summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkxl
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-03-06 21:58:15 +0000
committerKarl Berry <karl@freefriends.org>2021-03-06 21:58:15 +0000
commit974640d66e61e81cb197ad96fdff7b08343e4c5a (patch)
tree2e1f75f32f312b7f24ba82b4590ae230bcd6f399 /Master/texmf-dist/tex/context/base/mkxl
parentb4fa72e61230aca75f7f6fbf988821f71edfb6b2 (diff)
context
git-svn-id: svn://tug.org/texlive/trunk@58167 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl')
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-bar.mkxl220
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-bck.mklx775
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-pgr.lmt1325
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-pgr.mkxl444
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-pos.lmt1636
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-pos.mkxl506
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-snc.mkxl157
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/anch-tab.mkxl387
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-alt.lmt111
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-alt.mkxl55
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-col.lmt666
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-col.mkxl22
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-eff.mkxl60
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-ini.mkxl141
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-lay.lmt311
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-lay.mkxl107
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-mkr.mkxl25
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/attr-neg.mkxl28
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-mth.lmt742
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-ref.lmt261
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-tag.lmt846
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-exp.lmt2719
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-exp.mkxl361
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-ext.mkxl95
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-ini.lmt127
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-ini.mkxl74
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-lua.lmt366
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-lua.mkxl16
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-mps.lmt210
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-mps.mkxl16
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-pdf.lmt55
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-pdf.mkxl157
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-pdp.lmt291
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-res.lmt177
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-res.mkxl34
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/back-u3d.mkxl158
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/bibl-bib.mkxl983
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/bibl-tra.mkxl1508
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/blob-ini.lmt210
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/blob-ini.mkxl56
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/buff-ini.lmt900
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/buff-ini.mkxl233
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/buff-par.mklx186
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/buff-ver.mkxl1000
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/catc-act.mkxl51
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/catc-def.mkxl273
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/catc-ini.lmt44
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/catc-ini.mkxl274
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/catc-sym.mkxl96
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/char-act.mkxl99
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/char-enc.mkxl18
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/char-ini.mkxl126
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/char-tex.lmt994
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/char-utf.mkxl44
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/chem-ini.mkxl53
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/chem-str.mkxl751
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-bas.mkxl19
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-com.mkxl19
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-ini.mkxl45
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-int.lmt252
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-int.mkxl34
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-lmt.lmt949
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cldf-ver.mkxl18
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/colo-ext.mkxl99
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/colo-grp.mkxl163
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/colo-ini.mkxl1378
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/colo-nod.lmt40
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/colo-nod.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-cs.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-de.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-en.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-fil.mkxl152
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-fr.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-gb.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-it.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-log.mkxl306
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-new.mkxl54
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-nl.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-pe.mkxl22
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-ro.mkxl21
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-run.lmt311
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/cont-run.mkxl59
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/context.mkxl614
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-con.mkxl1002
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-ctx.mkxl29
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-dat.mkxl110
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-def.mkxl182
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-env.lmt213
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-env.mkxl807
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-ini.mkxl224
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-lmt.lmt95
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-lmt.mkxl28
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-sys.lmt83
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-sys.mkxl451
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-two.mkxl111
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-uti.lua470
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/core-uti.mkxl35
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/driv-ini.lmt375
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/driv-ini.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/driv-shp.lmt1358
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/driv-shp.mkxl18
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/enco-ini.mkxl488
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-ini.mklx231
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-job.lmt1283
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-job.mklx361
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-lib.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-mod.lmt309
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-mod.mklx262
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-res.mklx145
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/file-syn.mklx61
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-aux.mklx26
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-chk.lmt452
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-chk.mkxl56
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-col.lmt525
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-col.mklx150
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-con.lmt1508
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-ctx.lmt3231
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-def.lmt516
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-emp.mklx268
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-enh.lmt90
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-fbk.lmt350
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-fea.mklx389
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-fil.mklx384
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-fmp.lmt123
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-gds.mklx97
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-glf.mklx41
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-imp-math.lmt226
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-imp-tracing.lmt286
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-ini.lmt40
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-ini.mklx2693
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-lib.mklx156
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-mat.mklx595
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-mpf.lmt173
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-mps.lmt398
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-ogr.lmt785
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-ota.lmt580
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-otj.lmt2556
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-ots.lmt4475
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-pre.mkxl1025
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-sel.mklx282
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-set.mklx158
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-sol.mklx131
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-sty.mklx439
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-sym.mklx250
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-tex.lmt160
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-tex.mkxl121
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-tra.mkxl118
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-uni.mkxl27
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-unk.mkxl150
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-var.mklx57
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-vfc.lmt181
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-vir.lmt159
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/good-mth.lmt464
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-epd.mkxl93
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-fig.mkxl367
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-inc.lmt2160
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-inc.mkxl1073
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-pat.mkxl119
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-raw.mkxl64
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-rul.lmt124
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-rul.mkxl89
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-trf.lmt126
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/grph-trf.mkxl1003
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/hand-ini.mkxl113
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/java-ini.mkxl157
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-def.mkxl844
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-dis.lmt222
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-frd.mkxl143
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-frq.mkxl234
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-hyp.lmt1806
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-hyp.mkxl262
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-ini.lmt670
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-ini.mkxl787
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-lab.mkxl377
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-mis.mkxl586
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-rep.mkxl74
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-spa.mkxl74
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-tra.mkxl95
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-url.mkxl167
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lang-wrd.mkxl69
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/layo-ini.lmt115
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/layo-ini.mkxl30
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-curl.lmt296
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-ghostscript.lmt111
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-graphicsmagick.lmt77
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.lmt58
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.mkxl38
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.lmt126
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.mkxl121
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-mysql.lmt220
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-postgress.lmt200
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-sqlite.lmt187
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.lmt161
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.mkxl64
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/libs-ini.mkxl32
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-ano.lmt1401
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-aux.lmt153
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-col.lmt837
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-emb.lmt2233
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-enc.lmt157
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-epa.lmt1107
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-fld.lmt1501
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-fmt.lmt1020
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-fnt.lmt194
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-grp.lmt302
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-img.lmt1351
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-ini.lmt1363
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-lmt.lmt3019
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt639
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-mov.lmt68
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-pde.lmt1211
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-ren.lmt399
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-res.lmt41
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-rul.lmt388
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-tag.lmt743
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-u3d.lmt495
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-wid.lmt789
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lpdf-xmp.lmt311
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-bas.mkxl52
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-cbk.lmt287
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-cod.lmt308
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-cod.mkxl48
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-fio.lmt107
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-ini.lmt46
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-ini.mkxl299
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-lib.mkxl104
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-log.lmt739
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-soc.mkxl52
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-usr.lmt177
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/luat-usr.mkxl121
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lxml-css.mkxl76
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lxml-ctx.mkxl58
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lxml-ini.mkxl448
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/lxml-sor.mkxl99
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/m-mkivmkxl.mkxl3
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-acc.mklx153
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-act.lmt478
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-ali.mkxl1964
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-def.mkxl172
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-del.mkxl164
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-dis.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-fen.mkxl841
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-for.mkxl47
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-frc.mkxl833
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-inc.mkxl69
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-ini.mkxl3022
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-inl.mkxl22
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-int.mkxl141
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-mis.mkxl60
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-noa.lmt2412
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-pln.mkxl237
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-rad.mklx271
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-scr.mkxl58
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-stc.mklx1379
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-tag.lmt596
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-toy.mkxl54
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-vfu.lmt1153
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-blb.mkxl53
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-fig.mkxl69
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-fnt.lmt150
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-fnt.mkxl44
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-fun.mkxl25
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-grd.mkxl105
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-clock.mkxl141
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-demo.mkxl233
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-experiments.mkxl358
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-gamesymbols.mkxl349
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-punk.mkxl272
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-symbols.mkxl99
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-threesix.mkxl246
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-imp-txt.mkxl277
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-ini.lmt161
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-ini.mkxl1336
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-lua.mkxl95
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-mac.mkxl45
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-nod.mkxl248
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-pag.mkxl26
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-pdf.mkxl95
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-pdh.mkxl716
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-tex.mkxl235
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/meta-xml.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-cnt.lmt2022
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.lmt420
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.mkxl34
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-fio.lmt289
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-int.lmt160
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-lmp.lmt104
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-lmt.lmt199
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-lua.lmt210
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-mat.lmt139
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-mpf.lmt1260
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.lmt742
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.mkxl157
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-pps.lmt1647
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-pps.mkxl228
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-ran.lmt237
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt722
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-scn.lmt799
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mlib-svg.lmt3333
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-aux.mkxl1172
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-def.mkxl32
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-dim.mklx156
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-ini.lmt389
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-ini.mkxl804
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-prm.mkxl117
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/mult-sys.mkxl607
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-acc.lmt112
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-aux.lmt396
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-bck.lmt294
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-bck.mkxl80
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-cmp.lmt237
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-ext.lmt650
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-fin.lmt939
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-fin.mkxl78
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-fnt.lmt550
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-gcm.lmt114
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-ini.lmt200
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-ini.mkxl56
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-met.lmt633
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-mig.lmt134
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-mig.mkxl44
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-nut.lmt601
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-par.lmt101
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-pro.lmt219
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-ref.lmt831
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-res.lmt563
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-rul.lmt725
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-rul.mkxl610
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-scn.lmt314
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-ser.lmt284
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-shp.lmt115
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-snp.lmt69
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-syn.lmt782
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-tex.lmt39
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/node-tra.lmt739
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/norm-ctx.mkxl87
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-bar.mkxl94
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-bck.mklx245
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-box.mkxl1121
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-com.mkxl990
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-cut.mkxl164
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-fen.mkxl81
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-lyr.mkxl779
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-mis.mklx112
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-mrl.mkxl845
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-obj.lmt305
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-obj.mkxl168
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-ori.lmt70
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-ori.mkxl97
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-pos.mkxl160
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-rul.lmt258
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/pack-rul.mkxl3017
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-app.mkxl205
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-bck.mkxl708
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-box.mklx289
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-brk.mkxl483
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-col.mkxl165
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-com.mkxl180
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-cst.mkxl791
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-fac.mkxl87
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-ffl.mkxl247
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-flt.mkxl333
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-flw.mkxl178
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-imp.mkxl1404
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-inf.mkxl124
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-ini.mkxl376
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-inj.mklx228
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-ins.mkxl209
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-lay.mkxl1630
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-lin.lmt418
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-lin.mklx604
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-mak.mklx280
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-mbk.mklx243
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-mcl.mkxl542
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-mix.mkxl1086
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-mrk.mkxl229
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-not.mkxl32
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-one.mkxl709
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-otr.lmt14
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-otr.mklx333
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-par.mkxl73
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-pcl.mkxl853
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-plg.mkxl213
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-pst.mkxl112
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-sel.mklx420
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-sid.mkxl1019
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-smp.mkxl56
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-spr.mkxl129
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-str.mkxl128
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-txt.mklx959
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/page-var.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/phys-dim.mkxl817
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/publ-inc.mkxl63
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/publ-ini.mkxl2038
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/publ-old.mkxl22
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/publ-xml.mkxl111
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/regi-ini.mkxl37
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-bar.mklx404
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-but.mklx1049
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-fld.mklx1040
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-hlp.mklx160
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-ini.mklx254
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-pag.lmt56
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-pag.mklx324
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-ref.mklx107
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrn-wid.mklx757
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/scrp-ini.mkxl111
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/sort-ini.mkxl19
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-ali.lmt134
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-ali.mkxl1218
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-chr.lmt348
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-chr.mkxl78
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-def.mkxl91
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-flr.mkxl196
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-grd.mkxl377
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-hor.lmt38
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-hor.mkxl1218
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-lin.mkxl211
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-pag.mkxl170
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-par.lmt81
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-par.mkxl456
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-prf.lmt949
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-prf.mklx113
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-ver.lmt2516
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/spac-ver.mkxl2430
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-bkm.mkxl182
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-blk.mkxl171
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-con.mklx1186
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-def.mkxl338
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-des.mklx199
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-doc.mkxl85
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-enu.mklx372
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-flt.mklx2609
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-ind.mkxl128
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-ini.mklx81
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-itm.mklx1933
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-lab.mkxl199
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-lev.mklx98
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-lnt.mklx361
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-lst.mklx1574
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-mar.mkxl136
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-mat.mkxl1317
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-not.mklx1971
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-num.mkxl691
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-pag.mkxl487
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-ref.mklx2217
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-reg.mkxl1276
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-ren.mkxl820
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-sbe.mkxl137
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-sec.mkxl1322
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-syn.mkxl680
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-tag.lmt615
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-tag.mkxl531
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-tnt.mkxl117
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/strc-usr.mkxl169
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/supp-box.lmt1395
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/supp-box.mkxl2926
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/supp-dir.mkxl44
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/supp-mat.mkxl180
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/supp-ran.mkxl52
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/symb-emj.lmt76
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/symb-emj.mkxl26
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/symb-ini.lmt59
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/symb-ini.mkxl340
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-aux.lmt782
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-aux.mkxl6671
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-cmp.mkxl22
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-con.mkxl88
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-fnt.mkxl47
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-ini.mkxl1319
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-lua.lmt521
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-lua.mkxl104
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-mac.lmt117
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-pln.mkxl119
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/syst-rtp.mkxl18
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-com.mkxl96
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-frm.mkxl263
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-ltb.mkxl803
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-mis.mkxl299
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-ntb.mkxl2179
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-nte.mkxl107
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-tab.mkxl2267
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-tbl.mkxl2949
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-tsp.mkxl580
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-xnt.mklx142
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.mklx976
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/task-ini.mkxl22
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/toks-aux.lmt183
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/toks-aux.mkxl68
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/toks-ini.lmt332
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/toks-ini.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/toks-scn.lmt582
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/toks-scn.mkxl23
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-ctx.mkxl58
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-deb.lmt417
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-deb.mkxl45
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-inf.lmt425
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-jus.mkxl24
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-tex.mkxl70
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-tim.lmt178
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-vis.lmt1757
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/trac-vis.mkxl219
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-def.mkxl48
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-fbk.mkxl70
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-ini.lmt45
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-ini.mklx613
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-lua.mkxl86
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-set.mkxl139
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/type-siz.mkxl169
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-bld.lmt414
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-bld.mkxl62
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt498
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-brk.mkxl94
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-cap.lmt458
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-cap.mkxl271
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-chr.lmt291
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-chr.mkxl87
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-cln.mkxl32
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-del.mkxl925
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-dig.lmt181
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-dig.mkxl58
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-dir.mkxl205
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-drp.lmt341
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-drp.mkxl129
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-duc.lmt1048
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-fkr.mkxl38
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt406
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-fln.mkxl109
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-ini.lmt11
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-ini.mkxl49
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-inj.mkxl75
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt703
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-itc.mklx56
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-itm.mkxl273
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-krn.lmt630
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-krn.mkxl143
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-lan.mkxl64
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-lig.mkxl23
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-lin.lmt402
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-lin.mkxl25
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-mar.lmt995
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-mar.mkxl478
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-ovl.lmt184
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-ovl.mkxl116
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-pag.lmt206
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-pag.mkxl20
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-par.lmt96
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-par.mkxl60
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-plc.mkxl39
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-pnc.mkxl55
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-prc.mklx99
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-rep.mkxl42
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-rub.lmt397
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-rub.mkxl173
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-scr.mkxl266
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-shp.lmt267
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-shp.mkxl130
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-spa.lmt255
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-spa.mkxl78
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-sus.lmt308
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-sus.mkxl51
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-tal.lmt439
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-tal.mkxl303
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-txt.mklx231
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-wrp.mkxl58
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/unic-ini.mkxl36
566 files changed, 251816 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-bar.mkxl b/Master/texmf-dist/tex/context/base/mkxl/anch-bar.mkxl
new file mode 100644
index 00000000000..3587eb8a052
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-bar.mkxl
@@ -0,0 +1,220 @@
+%D \module
+%D [ file=anch-bar,
+%D version=2003.03.16,
+%D title=\CONTEXT\ Anchoring Macros,
+%D subtitle=Margin Bars and alike,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Anchoring Macros / Margin Bars}
+
+\unprotect
+
+%D This can be done better now ...
+
+%D We will implement a sidebar mechanism using the functionality from
+%D \type {core-pos}.
+%D
+%D \starttyping
+%D \definesidebar[whow][rulecolor=green,distance=0pt]
+%D
+%D \input tufte \par
+%D \startsidebar
+%D \input tufte \par
+%D \input tufte \par
+%D \startsidebar[whow]
+%D \input tufte \par
+%D \input tufte \par
+%D \input tufte
+%D \stopsidebar \par
+%D \input tufte \par
+%D \input tufte
+%D \stopsidebar \par
+%D \input tufte \par
+%D \input tufte \par
+%D \startsidebar
+%D \input tufte \par
+%D \input tufte \par
+%D \input tufte \par
+%D \input tufte \par
+%D \input tufte
+%D \stopsidebar \par
+%D \input tufte \par
+%D \input tufte \par
+%D \startsidebar
+%D \input tufte
+%D \input tufte
+%D \input tufte
+%D \input tufte
+%D \input tufte
+%D \stopsidebar
+%D \stoptyping
+
+\installcorenamespace{sidebar}
+
+\installcommandhandler \??sidebar {sidebar} \??sidebar
+
+\newcount\c_anch_sidebars_n
+\newcount\c_anch_sidebars_current % local
+\newdimen\d_anch_sidebars_distance
+\newcount\c_anch_sidebars_level
+
+% \setupMPvariables
+% [mpos:sidebar]
+% [linecolor=red,
+% linewidth=2pt,
+% distance=5pt]
+
+\setupsidebar
+ [\c!rulethickness=\dimexpr\bodyfontsize/6\relax, % 2pt default
+ \c!rulecolor=\s!black,
+ \c!alternative=0,
+ \c!topoffset=\zeropoint,
+ \c!bottomoffset=\zeropoint,
+ \c!distance=.5\bodyfontsize,
+ \c!level=,
+ \c!leftmargindistance=\zeropoint]
+
+\aliased\let\setupsidebars\setupsidebar
+
+\permanent\tolerant\protected\def\startsidebar[#1]#*[#2]%
+ {\bgroup
+ \dontleavehmode
+ \advance\c_anch_sidebars_level\plusone
+ \global\advance\c_anch_sidebars_n\plusone
+ \c_anch_sidebars_current\c_anch_sidebars_n\relax % relax needed
+ \ifhastok={#1}%
+ \edef\currentsidebar{\the\c_anch_sidebars_level}%
+ \checksidebarparent
+ \setupcurrentsidebar[#1]%
+ \else
+ \def\currentsidebar{#1}%
+ \setupcurrentsidebar[#2]%
+ \fi
+ \scratchdistance\sidebarparameter\c!distance\relax
+ \scratchdimen\sidebarparameter\c!leftmargindistance\relax
+ \edef\m_level{\sidebarparameter\c!level}%
+ \ifempty\m_level
+ \ifnum\c_anch_sidebars_level=\plusone
+ \ifzeropt\scratchdimen
+ \advance\d_anch_sidebars_distance\scratchdistance\relax
+ \else
+ \d_anch_sidebars_distance\scratchdimen
+ \fi
+ \else
+ \advance\d_anch_sidebars_distance\scratchdistance\relax
+ \fi
+ \else
+ \ifnum\m_level=\plusone
+ \ifzeropt\scratchdimen
+ \advance\d_anch_sidebars_distance\scratchdistance\relax
+ \else
+ \d_anch_sidebars_distance\scratchdimen
+ \fi
+ \else
+ \d_anch_sidebars_distance\dimexpr\scratchdimen+\numexpr\m_level-\plusone\relax\dimexpr\scratchdistance\relax\relax
+ \fi
+ \fi
+ \startpositionoverlay{\v!text-1}%
+ \normalexpanded{\setMPpositiongraphicrange % maybe expand in definition
+ {b:sidebar:\the\c_anch_sidebars_n}%
+ {e:sidebar:\the\c_anch_sidebars_n}%
+ {mpos:sidebar}%
+ {self=sidebar:\the\c_anch_sidebars_n,
+ linewidth=\sidebarparameter\c!rulethickness,
+ linecolor=\sidebarparameter\c!rulecolor,
+ alternative=\sidebarparameter\c!alternative,
+ topoffset=\the\dimexpr\sidebarparameter\c!topoffset,
+ bottomoffset=\the\dimexpr\sidebarparameter\c!bottomoffset,
+ distance=\the\d_anch_sidebars_distance}%
+ }%
+ \stoppositionoverlay
+ \bpos{sidebar:\the\c_anch_sidebars_current}%
+ \ignorespaces}
+
+\permanent\protected\def\stopsidebar
+ {\removelastspace
+ \epos{sidebar:\the\c_anch_sidebars_current}
+ \carryoverpar\egroup}
+
+%D Let's keep this nice and simple (okay, we could pass the 6 variables in
+%D one lua call).
+
+\startMPpositionmethod{mpos:sidebar}
+ \startMPpositiongraphic{mpos:sidebar}{linecolor,linewidth,distance,alternative}%
+ anch_sidebars_draw (
+ \MPp\MPbself,\MPp\MPeself,\MPy\MPbself,\MPy\MPeself,\MPh\MPbself,\MPd\MPeself,
+ \MPx{\textanchor},\MPy{\textanchor},\MPw{\textanchor},\MPh{\textanchor},
+ \MPvar{alternative},\MPvar{distance},\MPvar{linewidth},\MPvar{linecolor},
+ \MPvar{topoffset}, \MPvar{bottomoffset}
+ ) ;
+ \stopMPpositiongraphic
+ \MPpositiongraphic{mpos:sidebar}{}%
+\stopMPpositionmethod
+
+%D We now reimplement the \MKII\ margin rules handler in a more
+%D modern way.
+%D
+%D \setupmarginrules
+%D [rulecolor=darkred,
+%D rulethickness=2pt]
+%D
+%D \setupmarginrules % sidebar
+%D [2]
+%D [rulecolor=darkblue]
+%D
+%D \startmarginrule[1]
+%D \input ward
+%D \startmarginrule[2]
+%D \input ward
+%D \startmarginrule[3]
+%D \input ward
+%D \startmarginrule[level=6,rulecolor=darkgreen]
+%D \input ward
+%D \stopmarginrule
+%D \input ward
+%D \stopmarginrule
+%D \input ward
+%D \stopmarginrule
+%D \input ward
+%D \stopmarginrule
+%D
+%D Compared to the old mechanism we now can pass settings too.
+
+\definesidebar
+ [\v!margin]
+ [\c!leftmargindistance=\dimexpr\leftmargindistance+\sidebarparameter\c!rulethickness/2\relax]
+
+\dorecurse{5}{\definesidebar[\v!margin:#1][\v!margin]} % let's be nice and predefine 5 levels
+
+\permanent\tolerant\protected\def\setupmarginrule[#1]#*[#2]%
+ {\ifarguments\or
+ \setupsidebar[\v!margin][#1]%
+ \or
+ \setupsidebar[\v!margin:#1][#2]%
+ \fi}
+
+\aliased\let\setupmarginrules\setupmarginrule
+
+\permanent\tolerant\protected\def\startmarginrule[#1]% pretty inefficient checking
+ {\edef\m_anch_marginrules_kind{#1}%
+ \ifempty\m_anch_marginrules_kind
+ \startsidebar[\v!margin][]%
+ \orelse\ifhastok={\m_anch_marginrules_kind}%
+ \startsidebar[\v!margin][#1]%
+ \else
+ \anch_marginrules_check{#1}%
+ \startsidebar[\v!margin:#1][\c!level=#1]%
+ \fi}
+
+\def\anch_marginrules_check#1%
+ {\doifnotcommandhandler\??sidebar{\v!margin:#1}{\definesidebar[\v!margin:#1][\v!margin]}}
+
+\aliased\let\stopmarginrule\stopsidebar
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-bck.mklx b/Master/texmf-dist/tex/context/base/mkxl/anch-bck.mklx
new file mode 100644
index 00000000000..36416c93da3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-bck.mklx
@@ -0,0 +1,775 @@
+%D \module
+%D [ file=anch-bck, % moved from anch-pgr (1999.08.01)
+%D version=2011.12.19,
+%D title=\CONTEXT\ Anchoring Macros,
+%D subtitle=Backgrounds,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% when tested for a while: \MPgetmultipars -> getmultipars
+% when tested for a while: \MPvar -> mpvar
+
+\writestatus{loading}{ConTeXt Anchoring Macros / Backgrounds}
+
+%D This module is quite okay but at some point I might add methods that use
+%D attributes although not too much is to be expected, apart from better anchoring.
+%D In fact better anchoring can be done independent of that.
+
+\unprotect
+
+% This might be overloaded later on:
+
+% \defineoverlay[\v!text-2][\positionoverlay{\v!text-2}]
+% \defineoverlay[\v!text-1][\positionoverlay{\v!text-1}]
+% \defineoverlay[\v!text+1][\positionoverlay{\v!text+1}]
+% \defineoverlay[\v!text+2][\positionoverlay{\v!text+2}]
+
+% Paragraph positions:
+
+\newcount\c_anch_backgrounds_text_n
+
+\def\v_anch_backgrounds_text_current {tbg:0}
+\def\v_anch_backgrounds_anchor_current_b{b:\v_anch_backgrounds_text_current}
+\def\v_anch_backgrounds_anchor_current_e{e:\v_anch_backgrounds_text_current}
+
+\def\anch_backgrounds_text_initialize_next
+ {\global\advance\c_anch_backgrounds_text_n\plusone
+ \edef\v_anch_backgrounds_text_current{tbg:\number\c_anch_backgrounds_text_n}}
+
+% The first position can be used in the middle of a paragraph in which case we're
+% too late with initializing par positions. Therefore we check if positions are
+% used at all.
+%
+% tricky: we need to catch newly set! otherwise an old run can have positions
+
+\protected\def\anch_backgrounds_text_initialize
+ {\doifelsepositionsused\enableparpositions\donothing
+ \glet\anch_backgrounds_text_initialize\relax}
+
+\appendtoks
+ \anch_backgrounds_text_initialize
+\to \everystarttext
+
+%D Some MP
+
+\newcount\MPparcounter
+
+\permanent\def\MPself {\MPvar{self}}
+\permanent\def\MPbself {b:\MPvar{self}}
+\permanent\def\MPeself {e:\MPvar{self}}
+\permanent\def\MPparanchor{p:\number\MPparcounter}
+
+% \carryoverpar is needed for left/right floats
+
+% \definetextbackground[more][state=start,backgroundcolor=red] % location=paragraph
+% \definetextbackground[test][state=start,backgroundcolor=green]
+%
+% \page \placefigure[left]{}{}
+%
+% \starttextbackground[test]
+% \readfile{ward}{}{}
+% \starttextbackground[more]
+% \readfile{ward}{}{}
+% \stoptextbackground
+% \readfile{ward}{}{}
+% \stoptextbackground
+%
+% \page \placefigure[right]{}{}
+%
+% \starttextbackground[test]
+% \readfile{ward}{}{}
+% \starttextbackground[more]
+% \readfile{ward}{}{}
+% \stoptextbackground
+% \readfile{ward}{}{}
+% \stoptextbackground
+
+\newcount\c_anch_backgrounds_text_level
+
+\installcorenamespace{textbackground}
+\installcorenamespace{textbackgroundlevel}
+
+\installcommandhandler \??textbackground {textbackground} \??textbackground
+
+\appendtoks
+ \frozen\instance\setuevalue{\currenttextbackground}{\groupedcommand{\starttextbackground[\currenttextbackground]}{\stoptextbackground}}%
+ \frozen\instance\setuevalue{\e!start\currenttextbackground}{\starttextbackground[\currenttextbackground]}%
+ \frozen\instance\setuevalue{\e!stop \currenttextbackground}{\stoptextbackground}%
+\to \everydefinetextbackground
+
+\newconstant \c_anch_backgrounds_pos_state
+\newconditional\c_anch_backgrounds_pos_no_shape
+
+\def\anch_backgrounds_bpos
+ {\ifconditional\c_anch_backgrounds_pos_no_shape
+ \bposkind\v_anch_backgrounds_text_current\plusthree
+ \else
+ \bposkind\v_anch_backgrounds_text_current\c_anch_backgrounds_pos_state
+ \fi}
+
+\def\anch_backgrounds_epos
+ {\ifconditional\c_anch_backgrounds_pos_no_shape
+ \eposkind\v_anch_backgrounds_text_current\plusthree
+ \else
+ \eposkind\v_anch_backgrounds_text_current\c_anch_backgrounds_pos_state
+ \fi}
+
+% \permanent\tolerant\protected\def\starttextbackground[#tag]#spacer[#settings]%
+% {\begingroup
+% \advance\c_anch_backgrounds_text_level\plusone
+% \def\currenttextbackground{#tag}%
+% \anch_backgrounds_text_initialize_next
+% \ifparameter#settings\or
+% \setupcurrenttextbackground[#settings]%
+% \fi
+% \doifelse{\textbackgroundparameter\c!state}\v!start
+% \anch_backgrounds_text_preset_yes
+% \anch_backgrounds_text_preset_nop
+% \anch_backgrounds_text_start_indeed}
+
+% ugly hack to register usage
+
+\newcount \c_anch_backgrounds_text_count
+\newcount \c_anch_backgrounds_text_check
+\newconstant\c_anch_backgrounds_text_state
+
+\permanent\tolerant\protected\def\starttextbackground[#tag]#spacer[#settings]%
+ {\begingroup
+ \global\advance\c_anch_backgrounds_text_count\plusone
+ \advance\c_anch_backgrounds_text_level\plusone
+ \def\currenttextbackground{#tag}%
+ \anch_backgrounds_text_initialize_next
+ \ifparameter#settings\or
+ \setupcurrenttextbackground[#settings]%
+ \fi
+ \doifelse{\textbackgroundparameter\c!state}\v!start
+ \anch_backgrounds_text_preset_yes
+ \anch_backgrounds_text_preset_nop
+ \anch_backgrounds_text_start_indeed}
+
+\def\anch_backgrounds_text_level_start
+ {\c_anch_backgrounds_text_check\c_anch_backgrounds_text_count}
+
+\def\anch_backgrounds_text_level_stop
+ {\c_anch_backgrounds_text_state
+ \ifnum\c_anch_backgrounds_text_count>\c_anch_backgrounds_text_check
+ \plusone
+ \else
+ \zerocount
+ \fi}
+
+% todo \backgroundvariable\c!variant
+
+% criterium determines when we fall back on text
+% always is always forcing paragraphs
+
+\let\anch_backgrounds_text_start_indeed\relax
+\let\anch_backgrounds_text_stop_indeed \relax
+
+\setvalue{\??textbackgroundlevel\v!text}%
+ {\let\anch_backgrounds_text_start_indeed\anch_backgrounds_text_start_txt
+ \let\anch_backgrounds_text_stop_indeed \anch_backgrounds_text_stop_txt}
+
+\setvalue{\??textbackgroundlevel\v!paragraph}%
+ {\ifnum\c_anch_backgrounds_text_level>\textbackgroundparameter\c!criterium\relax
+ \let\anch_backgrounds_text_start_indeed\anch_backgrounds_text_start_txt
+ \let\anch_backgrounds_text_stop_indeed \anch_backgrounds_text_stop_txt
+ \else
+ \let\anch_backgrounds_text_start_indeed\anch_backgrounds_text_start_par
+ \let\anch_backgrounds_text_stop_indeed \anch_backgrounds_text_stop_par
+ \fi}
+
+\setvalue{\??textbackgroundlevel\v!always}%
+ {\let\anch_backgrounds_text_start_indeed\anch_backgrounds_text_start_par
+ \let\anch_backgrounds_text_stop_indeed \anch_backgrounds_text_stop_par}
+
+\setvalue{\??textbackgroundlevel\v!none}%
+ {\anch_backgrounds_text_preset_nop}
+
+\def\anch_backgrounds_text_preset_nop
+ {\let\anch_backgrounds_text_start_indeed\relax
+ \let\anch_backgrounds_text_stop_indeed \relax}
+
+\def\anch_backgrounds_text_preset_yes
+ {\anch_backgrounds_text_preset_nop
+ \csname\??textbackgroundlevel\textbackgroundparameter\c!location\endcsname
+ \edef\p_anch_backgrounds_text_frame {\textbackgroundparameter\c!frame}%
+ \edef\p_anch_backgrounds_text_corner {\textbackgroundparameter\c!corner}%
+ \edef\p_anch_backgrounds_text_background{\textbackgroundparameter\c!background}%
+ \ifx\p_anch_backgrounds_text_frame\v!on
+ \ifx\p_anch_backgrounds_text_corner\v!round
+ \let\p_anch_backgrounds_text_frame\!!plustwo
+ \else
+ \let\p_anch_backgrounds_text_frame\!!plusone
+ \fi
+ \else
+ \let\p_anch_backgrounds_text_frame\!!zerocount
+ \fi
+ \ifx\p_anch_backgrounds_text_background\v!color
+ \let\p_anch_backgrounds_text_background\!!plusone
+ \else
+ \let\p_anch_backgrounds_text_background\!!zerocount
+ \fi
+ \startpositionoverlay{\textbackgroundoverlay{\textbackgroundparameter\c!level}}%
+ \anch_backgrounds_text_meta_graphic % gets expanded directly
+ \stoppositionoverlay}
+
+\def\anch_backgrounds_text_meta_graphic
+ {\normalexpanded
+ {\setMPpositiongraphicrange % needs to be optimized
+ {\v_anch_backgrounds_anchor_current_b}%
+ {\v_anch_backgrounds_anchor_current_e}%
+ {\textbackgroundparameter\c!method}%
+ {self=\v_anch_backgrounds_text_current,
+ mp=\textbackgroundparameter\c!mp,
+ gridtype=\textbackgroundparameter\c!alternative,
+ filltype=\p_anch_backgrounds_text_background,
+ linetype=\p_anch_backgrounds_text_frame,
+ dashtype=\textbackgroundparameter\c!dash,
+ gridcolor=\textbackgroundparameter\c!framecolor,
+ linecolor=\textbackgroundparameter\c!framecolor,
+ lineoffset=\textbackgroundparameter\c!frameoffset,
+ fillcolor=\textbackgroundparameter\c!backgroundcolor,
+ filloffset=\textbackgroundparameter\c!backgroundoffset,
+ gridwidth=\textbackgroundparameter\c!rulethickness,
+ gridshift=\textbackgroundparameter\c!voffset,
+ linewidth=\textbackgroundparameter\c!rulethickness,
+ lineradius=\textbackgroundparameter\c!radius}}}
+
+\permanent\protected\def\stoptextbackground
+ {\anch_backgrounds_text_stop_indeed
+ \carryoverpar\endgroup} % why doesn't this work ?
+
+\permanent\protected\def\starttextbackgroundmanual
+ {\begingroup
+ \c_anch_backgrounds_pos_state\plusone
+ \usetextbackgroundstyleandcolor\c!style\c!color
+ \anch_backgrounds_bpos}
+
+\permanent\protected\def\stoptextbackgroundmanual
+ {\anch_backgrounds_epos
+ \carryoverpar\endgroup}
+
+\def\anch_backgrounds_text_start_txt
+ {\ifvmode \dontleavehmode \fi
+ \begingroup
+ \c_anch_backgrounds_pos_state\plusone
+ \usetextbackgroundstyleandcolor\c!style\c!color
+ \anch_backgrounds_bpos}
+
+\def\anch_backgrounds_text_stop_txt
+ {\anch_backgrounds_epos
+ \carryoverpar\endgroup}
+
+\newskip\textbackgroundskip
+
+% maybe we should have a resetter for such compensation struts
+
+\def\anch_backgrounds_reset_attributes
+ {\scratchcounter\c_attr_snapmethod
+ \resetallattributes % \c_attr_linenumber\attributeunsetvalue
+ \c_attr_snapmethod\scratchcounter}
+
+\def\anch_backgrounds_text_start_par % beware .. background shapes
+ {\endgraf % new
+ \textbackgroundparameter\c!before
+ \begingroup
+ \c_anch_backgrounds_pos_state\plustwo
+ \begingroup
+ \anch_backgrounds_reset_attributes
+ \noindent
+ \ifgridsnapping
+ \spac_helpers_assign_skip\textbackgroundskip{\textbackgroundparameter\c!topoffset}%
+ \ifdim\textbackgroundskip>\zeropoint
+ \struttedbox % not always ok (e.g. setups)
+ {\hpack{\raise\textbackgroundskip\hpack{\anch_backgrounds_bpos}}}%
+ \else
+ \anch_backgrounds_bpos
+ \fi
+ \else
+ \anch_backgrounds_bpos
+ \fi
+ \endgraf % we need a vertical nobreak - 29/06/2004
+ \endgroup
+ \nobreak
+ \vskip-\lineheight
+ \nobreak
+ \ifgridsnapping \else
+ \spac_helpers_assign_skip\textbackgroundskip{\textbackgroundparameter\c!topoffset}%
+ \ifdim\textbackgroundskip>\zeropoint
+ \kern\textbackgroundskip\nobreak
+ \fi
+ \fi
+ \dosetleftskipadaption{\textbackgroundparameter\c!leftoffset}%
+ \advance\leftskip\leftskipadaption
+ \dosetleftskipadaption{\textbackgroundparameter\c!rightoffset}%
+ \advance\rightskip\leftskipadaption
+ % new
+ \dosetraggedcommand{\textbackgroundparameter\c!align}%
+ \raggedcommand
+ %
+ \usetextbackgroundstyleandcolor\c!style\c!color
+ \nowhitespace
+ \seteffectivehsize
+ \doinhibitblank % \blank[\v!disable]% new
+ \par}
+
+\def\anch_backgrounds_text_stop_par
+ {\par
+ \removelastskip % new
+ \spac_helpers_assign_skip\textbackgroundskip{\textbackgroundparameter\c!bottomoffset}%
+ \ifdim\lastskip>\zeropoint
+ \advance\textbackgroundskip-\lastskip
+ \fi
+ \ifgridsnapping
+ \orelse\ifdim\textbackgroundskip>\zeropoint
+ \kern\textbackgroundskip\nobreak
+ \fi
+ \nobreak
+ \vskip-\dimexpr\lineheight+\parskip\relax % problem: we loose the hangindent
+ \nobreak
+ \endgroup
+ \begingroup
+ \forgeteverypar % NOT REALLY NEEDED, SAVES HASH/MEM
+ \anch_backgrounds_reset_attributes
+ \nobreak \noindent \strut \hfill \kern\zeropoint
+ % so far
+ \spac_helpers_assign_skip\textbackgroundskip{\textbackgroundparameter\c!bottomoffset}%
+ \ifgridsnapping % experimental, pascal (todo: topoffset in same way)
+ \ifdim\textbackgroundskip>\zeropoint
+ \struttedbox % not always ok (e.g. setups)
+ {\hpack{\lower\textbackgroundskip\hpack{\anch_backgrounds_epos}}}%
+ \else
+ \anch_backgrounds_epos
+ \fi
+ \else
+ \anch_backgrounds_epos
+ \fi
+ \endgraf
+ \carryoverpar\endgroup
+ \endgraf % new
+ \textbackgroundparameter\c!after}
+
+\permanent\protected\def\checkpositionoverlays % overloads \relax in anch-pgr
+ {\ifproductionrun
+ \enabletextarearegistration
+ \enablehiddenbackground
+ \enforced\glet\checkpositionoverlays\relax
+ \fi}
+
+% shape handling
+
+\definesystemattribute[textbackground][public]
+
+% \def\page_prepare_backgrounds#1%
+% {\clf_collectbackgrounds\realpageno#1\relax}
+
+\def\anch_backgrounds_bpos
+ {\ifconditional\c_anch_backgrounds_pos_no_shape
+ \c_attr_textbackground\attributeunsetvalue
+ \bposkind\v_anch_backgrounds_text_current\plusthree
+ \orelse\ifnum\c_anch_backgrounds_pos_state=\plusone
+ \bposkind\v_anch_backgrounds_text_current\c_anch_backgrounds_pos_state
+ \clf_registerbackground{\v_anch_backgrounds_text_current}%
+ \else
+ \bposkind\v_anch_backgrounds_text_current\c_anch_backgrounds_pos_state
+ \c_attr_textbackground\attributeunsetvalue
+ \fi}
+
+% plugs into other code
+
+\def\strc_floats_wrap_free_region
+ {\global\setbox\floatbox\hpack % we always need to wrap
+ {\ifconditional\c_strc_floats_trace_free
+ \strc_floats_show_free_region
+ \fi
+ \box\floatbox}}
+
+\startuseMPgraphic{floatfree}
+ draw_free_region(
+ \the\wd\floatbox,
+ \the\ht\floatbox,
+ \the\dp\floatbox,
+ \the\d_free_offset_left,
+ \the\d_free_offset_right,
+ \the\d_free_offset_top,
+ \the\d_free_offset_bottom
+ ) ;
+\stopuseMPgraphic
+
+\def\strc_floats_show_free_region
+ {\lower\dp\floatbox\hpack to \zeropoint {\useMPgraphic{floatfree}}}
+
+\newconditional\c_strc_floats_mark_as_free
+\newconditional\c_strc_floats_trace_free
+\newconstant \c_free_offset_kind
+\newdimen \d_free_offset_left
+\newdimen \d_free_offset_right
+\newdimen \d_free_offset_top
+\newdimen \d_free_offset_bottom
+
+\installtextracker{floats.freeregion}
+ {\settrue \c_strc_floats_trace_free}
+ {\setfalse\c_strc_floats_trace_free}
+
+\def\strc_floats_mark_as_free#1#2#3#4#5%
+ {\c_free_offset_kind #1%
+ \d_free_offset_left #2%
+ \d_free_offset_right #3%
+ \d_free_offset_top #4%
+ \d_free_offset_bottom#5%
+ \strc_floats_wrap_free_region
+ \anch_mark_tagged_box_free
+ \floatbox
+ \c_free_offset_kind
+ \d_free_offset_left
+ \d_free_offset_right
+ \d_free_offset_top
+ \d_free_offset_bottom}
+
+\ifrelax\strc_floats_mark_pag_as_free \else
+ \writestatus{error}{wrong place for pag_as_free}\wait
+\fi
+
+\ifrelax\strc_floats_mark_par_as_free \else
+ \writestatus{error}{wrong place for par_as_free}\wait
+\fi
+
+\def\strc_floats_mark_pag_as_free
+ {\ifpositioning
+ \ifconditional\c_strc_floats_mark_as_free
+ \strc_floats_mark_as_free
+ \plusone
+ \zeropoint
+ \zeropoint
+ \d_strc_floats_top
+ \d_strc_floats_bottom
+ \fi
+ \fi}
+
+\def\strc_floats_mark_par_as_free
+ {\ifpositioning
+ \ifconditional\c_strc_floats_mark_as_free
+ \ifcase\c_page_sides_float_type
+ \or % backspace
+ \strc_floats_mark_as_free
+ \plustwo
+ \zeropoint
+ \d_page_sides_rightoffset
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % leftedge
+ \strc_floats_mark_as_free
+ \plustwo
+ \zeropoint
+ \d_page_sides_rightoffset
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % leftmargin
+ \strc_floats_mark_as_free
+ \plustwo
+ \zeropoint
+ \d_page_sides_rightoffset
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % leftside
+ \strc_floats_mark_as_free
+ \plustwo
+ \d_page_sides_leftskip % maybe too
+ \d_page_sides_margin
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % rightside
+ \strc_floats_mark_as_free
+ \plusthree
+ \d_page_sides_margin
+ \d_page_sides_rightskip % maybe too
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % rightmargin
+ \strc_floats_mark_as_free
+ \plusthree
+ \d_page_sides_leftoffset
+ \zeropoint
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % rightedge
+ \strc_floats_mark_as_free
+ \plusthree
+ \d_page_sides_leftoffset
+ \zeropoint
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \or % cutspace
+ \strc_floats_mark_as_free
+ \plusthree
+ \d_page_sides_leftoffset
+ \zeropoint
+ \d_page_sides_topskip
+ \d_page_sides_bottomskip
+ \fi
+ \fi
+ \fi}
+
+% so far
+
+\setuptextbackground
+ [\c!mp=mpos:region:draw,
+ \c!method=mpos:region,
+ \c!state=\v!start,
+ \c!location=\v!text,
+ \c!leftoffset=\!!zeropoint, % 1em,
+ \c!rightoffset=\textbackgroundparameter\c!leftoffset,
+ \c!topoffset=\!!zeropoint, % \v!medium,
+ \c!bottomoffset=\textbackgroundparameter\c!topoffset,
+ \c!criterium=\plusone,
+ \c!level=-1,
+ \c!alternative=0,
+ \c!align=,
+ \c!dash=0, % to be internationalized
+ \c!background=\v!color,
+ \c!backgroundcolor=lightgray,
+ \c!backgroundoffset=\!!zeropoint,
+ \c!corner=\v!rectangular,
+ \c!radius=.5\bodyfontsize,
+ \c!voffset=\!!zeropoint,
+ \c!frame=\v!on,
+ \c!framecolor=blue,
+ \c!frameoffset=\!!zeropoint,
+ \c!rulethickness=\linewidth]
+
+%D The \METAPOST\ connection:
+
+% gridtype = 1 => baseline
+% gridtype = 2 => betweenline
+
+\setupMPvariables
+ [mpos:region]
+ [mp=mpos:region:unset,
+ gridtype=0,
+ linetype=1,
+ filltype=1,
+ dashtype=0, % 1 = dashed, 2 = dashed with background
+ gridcolor=red,
+ linecolor=blue,
+ fillcolor=lightgray,
+ filloffset=\!!zeropoint,
+ linewidth=\linewidth,
+ gridwidth=\linewidth,
+ gridshift=\!!zeropoint,
+ lineradius=.5\bodyfontsize,
+ lineoffset=\!!zeropoint,
+ dashtype=1]
+
+\startuseMPgraphic{mpos:region:setup}
+ boxgridtype := \MPvar{gridtype} ;
+ boxlinetype := \MPvar{linetype} ;
+ boxfilltype := \MPvar{filltype} ;
+ boxdashtype := \MPvar{dashtype} ;
+ boxfilloffset := \MPvar{filloffset} ;
+ boxlinewidth := \MPvar{linewidth} ;
+ boxgridwidth := \MPvar{gridwidth} ;
+ boxgridshift := \MPvar{gridshift} ;
+ boxlineradius := \MPvar{lineradius} ;
+ boxlineoffset := \MPvar{lineoffset} ;
+ %
+ def boxgridcolor = \MPvar{gridcolor} enddef ;
+ def boxlinecolor = \MPvar{linecolor} enddef ;
+ def boxfillcolor = \MPvar{fillcolor} enddef ;
+ %
+ def boxgridoptions = withcolor boxgridcolor enddef ;
+ def boxlineoptions = withcolor boxlinecolor enddef ;
+ def boxfilloptions = withcolor boxfillcolor enddef ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{mpos:region:extra}
+ % user stuff
+\stopuseMPgraphic
+
+\startuseMPgraphic{mpos:region:anchor}
+ setbounds currentpicture to multibox ;
+\stopuseMPgraphic
+
+\startMPpositiongraphic{mpos:region}{fillcolor,filloffset,linecolor,gridcolor,linewidth,gridwidth,gridshift,lineradius,lineoffset}
+ \includeMPgraphic{mpos:region:setup} ;
+ \includeMPgraphic{mpos:region:extra} ;
+ % \getmultipars("self","anchor") ;
+ \MPgetmultipars{\MPvar{self}}{\MPanchorid} ;
+ \includeMPgraphic{\MPvar{mp}} ;
+ \includeMPgraphic{mpos:region:anchor} ;
+\stopMPpositiongraphic
+
+%D For old times sake:
+
+\startMPpositiongraphic{mpos:box}{fillcolor,linecolor,linewidth}
+ string tag; tag := "\MPvar{self}" ;
+ path box ; box := positionbox(tag) ;
+
+ box := box enlarged \MPvar{filloffset} ;
+ fill box withcolor \MPvar{fillcolor} ;
+ draw box withcolor \MPvar{linecolor} withpen pencircle scaled \MPvar{linewidth} ;
+
+ positioninregion;
+\stopMPpositiongraphic
+
+\startMPpositionmethod{mpos:region}
+ \MPpositiongraphic{mpos:region}{}%
+\stopMPpositionmethod
+
+\startuseMPgraphic{mpos:region:draw}
+ draw_multi_pars
+\stopuseMPgraphic
+
+\startuseMPgraphic{mpos:region:show}
+ show_multi_pars
+\stopuseMPgraphic
+
+\startuseMPgraphic{mpos:region:kind}
+ show_multi_kind
+\stopuseMPgraphic
+
+\startuseMPgraphic{mpos:region:sideline}
+ draw_multi_side
+\stopuseMPgraphic
+
+\startuseMPgraphic{mpos:region:sideline:dashed}
+ draw_multi_side_path dashed evenly ;
+\stopuseMPgraphic
+
+%D As an example we define a grid background:
+
+\pushoverloadmode
+
+\definetextbackground
+ [\v!grid]
+ [\c!state=\v!stop,
+ \c!location=\v!paragraph,
+ \c!frame=\v!off,
+ \c!framecolor=red,
+ \c!background=,
+ \c!alternative=1]
+
+\ifdefined\basegrid
+ \aliased\letcsname\v!grid\endcsname\basegrid
+\fi
+
+\popoverloadmode
+
+%D Some examples (that might become modules anch-imp-whatever):
+
+\setupMPvariables
+ [mpos:encircle]
+ [fillcolor=lightgray,
+ filloffset=\!!zeropoint,
+ linecolor=blue,
+ lineoffset=5pt,
+ linewidth=1pt]
+
+% \startMPpositiongraphic{mpos:encircle}{linecolor,fillcolor,linewidth,lineoffset}
+% getposboxes("self","anchor") ;
+% if nofposboxes = 1 :
+% posboxes[1] := posboxes[1] enlarged \MPvar{lineoffset} cornered \MPvar{lineoffset} ;
+% fill posboxes[1] withcolor \MPvar{fillcolor} ;
+% draw posboxes[1] withpen pencircle scaled \MPvar{linewidth} withcolor \MPvar{linecolor} ;
+% fi ;
+% \stopMPpositiongraphic
+
+\startMPpositiongraphic{mpos:encircle}{linecolor,fillcolor,linewidth,lineoffset}
+ getposboxes("self","anchor") ;
+ if nofposboxes = 1 :
+ posboxes[1] := (posboxes[1] enlarged mpvar "lineoffset") cornered mpvar "lineoffset" ;
+ fill posboxes[1] withcolor mpvar "fillcolor" ;
+ draw posboxes[1] withpen pencircle scaled mpvar "linewidth" withcolor mpvar "linecolor" ;
+ fi ;
+\stopMPpositiongraphic
+
+\setupMPvariables
+ [mpos:connect]
+ [linecolor=red,
+ lineoffset=.25ex,
+ linewidth=1pt]
+
+% \startMPpositiongraphic{mpos:connect}{linecolor,lineoffset,linewidth}
+% boxlinewidth := \MPvar{linewidth} ;
+% boxlineoffset := \MPvar{lineoffset} ;
+% def boxlineoptions = withcolor \MPvar{linecolor} enddef ;
+% getposboxes("from,to","anchor") ;
+% connect_positions ;
+% \stopMPpositiongraphic
+
+\startMPpositiongraphic{mpos:connect}{linecolor,lineoffset,linewidth}
+ boxlinewidth := mpvar "linewidth" ;
+ boxlineoffset := mpvar "lineoffset" ;
+ def boxlineoptions = withcolor mpvar "linecolor" enddef ;
+ getposboxes("from,to","anchor") ;
+ connect_positions ;
+\stopMPpositiongraphic
+
+\protect \endinput
+
+\definetextbackground[underline] [location=text,alternative=1,background=,frame=off]
+\definetextbackground[overstrike] [location=text,alternative=2,background=,frame=off]
+\definetextbackground[exlines] [location=text,alternative=3,background=,frame=off]
+\definetextbackground[strikethrough][location=text,alternative=4,background=,frame=off]
+
+\definestartstop [underline]
+ [before={\starttextbackground[underline]},
+ after=\stoptextbackground]
+
+\definestartstop
+ [overstrike]
+ [before={\starttextbackground[overstrike]},
+ after=\stoptextbackground]
+
+\definestartstop
+ [exlines]
+ [before={\starttextbackground[exlines]},
+ after=\stoptextbackground]
+
+\definestartstop
+ [strikethrough]
+ [before={\starttextbackground[strikethrough]},
+ after=\stoptextbackground]
+
+\definetextbackground
+ [sideline]
+ [mp=mpos:region:sideline,
+ location=paragraph,
+ framecolor=red,
+ frameoffset=5mm]
+
+\definestartstop [sideline]
+ [before={\starttextbackground[sideline]},
+ after=\stoptextbackground]
+
+\starttext
+
+ \startunderline \input tufte \stopunderline \blank
+ \startoverstrike \input tufte \stopoverstrike \blank
+ \startexlines \input tufte \stopexlines \blank
+ \startstrikethrough \input tufte \stopstrikethrough \blank
+ \startsideline \input tufte \stopsideline \blank
+
+ \page
+
+ \startpositionoverlay{text-1}
+ \setMPpositiongraphic{connect-1-b}{mpos:connect}{from=connect-1-b,to=connect-1-e}
+ \setMPpositiongraphic{connect-1-e}{mpos:connect}{from=connect-1-b,to=connect-1-e}
+ \stoppositionoverlay
+
+ \startpositionoverlay{text-1}
+ \setMPpositiongraphic{encircle-1}{mpos:encircle}{self=encircle-1}
+ \stoppositionoverlay
+
+ test \hpos{connect-1-b}{START}
+ \dorecurse{10}{\input ward}
+ \hpos{encircle-1}{\strut HERE}
+ \dorecurse{10}{\input ward}
+ \hpos{connect-1-e}{STOP} test
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-pgr.lmt b/Master/texmf-dist/tex/context/base/mkxl/anch-pgr.lmt
new file mode 100644
index 00000000000..5b750d80e7a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-pgr.lmt
@@ -0,0 +1,1325 @@
+if not modules then modules = { } end modules ['anch-pgr'] = {
+ version = 1.001,
+ comment = "companion to anch-pgr.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is a bit messy module but backgrounds are messy anyway. Especially when we want to
+-- follow shapes. This will always be work in progress as it also depends on new features
+-- in context.
+--
+-- Alas, shapes and inline didn't work as expected end of 2016 so I had to pick up this
+-- thread again. But with regular excursions to listening to Brad Mehldau's Mehliana I
+-- could keep myself motivated. Some old stuff has been removed, some suboptimal code has
+-- been replaced. Background code is still not perfect, but some day ... the details manual
+-- will discuss this issue.
+
+local tonumber = tonumber
+local sort, concat = table.sort, table.concat
+local splitter = lpeg.splitat(":")
+local lpegmatch = lpeg.match
+
+local jobpositions = job.positions
+local formatters = string.formatters
+local setmetatableindex = table.setmetatableindex
+local settings_to_array = utilities.parsers.settings_to_array
+
+local enableaction = nodes.tasks.enableaction
+
+local commands = commands
+local context = context
+
+local implement = interfaces.implement
+
+local getmacro = tokens.getters.macro
+local scanmpstring = mp.scan.string
+local mpgnamespace = getmacro("??graphicvariable")
+
+local report_graphics = logs.reporter("backgrounds")
+local report_shapes = logs.reporter("backgrounds","shapes")
+local report_free = logs.reporter("backgrounds","free")
+
+local trace_shapes = false trackers.register("backgrounds.shapes", function(v) trace_shapes = v end)
+local trace_ranges = false trackers.register("backgrounds.shapes.ranges",function(v) trace_ranges = v end)
+local trace_free = false trackers.register("backgrounds.shapes.free", function(v) trace_free = v end)
+
+local f_b_tag = formatters["b:%s"]
+local f_e_tag = formatters["e:%s"]
+local f_p_tag = formatters["p:%s"]
+
+local f_tag_two = formatters["%s:%s"]
+
+local f_point = formatters["%p"]
+local f_pair = formatters["(%p,%p)"]
+local f_path = formatters["%--t--cycle"]
+local f_pair_i = formatters["(%r,%r)"] -- rounded
+
+graphics = graphics or { }
+local backgrounds = { }
+graphics.backgrounds = backgrounds
+
+-- -- -- these can go -- -- --
+
+backgrounds.point = f_point
+backgrounds.pair = f_pair
+backgrounds.path = f_path
+
+-- -- --
+
+local texsetattribute = tex.setattribute
+
+local a_textbackground = attributes.private("textbackground")
+
+local nuts = nodes.nuts
+
+local new_latelua = nuts.pool.latelua
+local new_rule = nuts.pool.rule
+local new_kern = nuts.pool.kern
+local new_hlist = nuts.pool.hlist
+
+local getbox = nuts.getbox
+local getid = nuts.getid
+----- getlist = nuts.getlist
+local setlink = nuts.setlink
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+
+local nodecodes = nodes.nodecodes
+local par_code = nodecodes.par
+
+local start_of_par = nuts.start_of_par
+local insert_before = nuts.insert_before
+local insert_after = nuts.insert_after
+
+local processranges = nuts.processranges
+
+local unsetvalue = attributes.unsetvalue
+
+local jobpositions = job.positions
+local getpos = jobpositions.getpos
+local getfree = jobpositions.getfree
+
+local data = { }
+local realpage = 1
+local recycle = 1000 -- only tables can overflow this
+local enabled = false
+
+-- Freeing the data is somewhat tricky as we can have backgrounds spanning
+-- many pages but for an arbitrary background shape that is not so common.
+
+local function check(specification)
+ local a = specification.attribute
+ local index = specification.index
+ local depth = specification.depth
+ local d = specification.data
+ local where = specification.where
+ local ht = specification.ht
+ local dp = specification.dp
+ -- this is not yet r2l ready
+ local w = d.shapes[realpage]
+ local x, y = getpos()
+ if trace_ranges then
+ report_shapes("attribute %i, index %i, depth %i, location %s, position (%p,%p)",
+ a,index,depth,where,x,y)
+ end
+ local n = #w
+ if d.index ~= index then
+ n = n + 1
+ d.index = index
+ d.depth = depth
+ -- w[n] = { x, x, y, ht, dp }
+ w[n] = { y, ht, dp, x, x }
+ else
+ local wn = w[n]
+ local wh = wn[2]
+ local wd = wn[3]
+ if depth < d.depth then
+ local wy = wn[1]
+ wn[1] = y
+ d.depth = depth
+ local dy = wy - y
+ wh = wh - dy
+ wd = wd - dy
+ end
+ if where == "r" then
+ if x > wn[5] then
+ wn[5] = x
+ end
+ else
+ if x < wn[4] then
+ wn[4] = x
+ end
+ end
+ if ht > wh then
+ wn[2] = ht
+ end
+ if dp > wd then
+ wn[3] = dp
+ end
+ end
+ -- inspect(w)
+end
+
+local index = 0
+
+local function flush(head,f,l,a,parent,depth)
+ local d = data[a]
+ if d then
+ local ix = index
+ local ht = getheight(parent)
+ local dp = getdepth(parent)
+ local ln = new_latelua { action = check, attribute = a, index = ix, depth = depth, data = d, where = "l", ht = ht, dp = dp }
+ local rn = new_latelua { action = check, attribute = a, index = ix, depth = depth, data = d, where = "r", ht = ht, dp = dp }
+ if trace_ranges then
+ ln = new_hlist(setlink(new_rule(65536,65536*4,0),new_kern(-65536),ln))
+ rn = new_hlist(setlink(new_rule(65536,0,65536*4),new_kern(-65536),rn))
+ end
+ if getid(f) == par_code and start_of_par(f) then -- we need to clean this mess
+ insert_after(head,f,ln)
+ else
+ head, f = insert_before(head,f,ln)
+ end
+ insert_after(head,l,rn)
+ end
+ return head, true
+end
+
+local function registerbackground(name)
+ local n = #data + 1
+ if n > recycle then
+ -- we could also free all e: that are beyond a page but we don't always
+ -- know the page so a recycle is nicer and the s lists are kept anyway
+ -- so the amount of kept data is not that large
+ n = 1
+ end
+ local b = jobpositions.tobesaved["b:"..name]
+ if b then
+ local s = setmetatableindex("table")
+ b.s = s
+ data[n] = {
+ bpos = b,
+ name = name,
+ n = n,
+ shapes = s,
+ count = 0,
+ sindex = 0,
+ }
+ texsetattribute(a_textbackground,n)
+ if not enabled then
+ enableaction("contributers", "nodes.handlers.textbackgrounds")
+ enabled = true
+ end
+ else
+ texsetattribute(a_textbackground,unsetvalue)
+ end
+end
+
+-- local function collectbackgrounds(r,n)
+-- if enabled then
+-- local parent = getbox(n)
+-- local head = getlist(parent)
+-- realpage = r
+-- processranges(a_textbackground,flush,head) -- ,parent)
+-- end
+-- end
+--
+-- interfaces.implement {
+-- name = "collectbackgrounds",
+-- actions = collectbackgrounds,
+-- arguments = { "integer", "integer" }
+-- }
+
+nodes.handlers.textbackgrounds = function(head,where,parent) -- we have hlistdir and local dir
+ -- todo enable action in register
+ index = index + 1
+ return processranges(a_textbackground,flush,head,parent)
+end
+
+interfaces.implement {
+ name = "registerbackground",
+ actions = registerbackground,
+ arguments = "string",
+}
+
+-- optimized already but we can assume a cycle i.e. prune the last point and then
+-- even less code .. we could merge some loops but his is more robust
+
+-- use idiv here
+
+local function topairs(t,n)
+ local r = { }
+ for i=1,n do
+ local ti = t[i]
+ r[i] = f_pair_i(ti[1]/65556,ti[2]/65536)
+ end
+ return concat(r," ")
+end
+
+local eps = 65536 / 4
+local pps = eps
+local nps = - pps
+
+local function unitvector(x,y)
+ if x < pps and x > nps then
+ x = 0
+ elseif x < 0 then
+ x = -1
+ else
+ x = 1
+ end
+ if y < pps and y > nps then
+ y = 0
+ elseif y < 0 then
+ y = -1
+ else
+ y = 1
+ end
+ return x, y
+end
+
+local function finish(t)
+ local tm = #t
+ if tm < 2 then
+ return
+ end
+ if trace_ranges then
+ report_shapes("initial list: %s",topairs(t,tm))
+ end
+ -- remove similar points
+ local n = 1
+ local tn = tm
+ local tf = t[1]
+ local tx = tf[1]
+ local ty = tf[2]
+ for i=2,#t do
+ local ti = t[i]
+ local ix = ti[1]
+ local iy = ti[2]
+ local dx = ix - tx
+ local dy = iy - ty
+ if dx > eps or dx < - eps or dy > eps or dy < - eps then
+ n = n + 1
+ t[n] = ti
+ tx = ix
+ ty = iy
+ end
+ end
+ if trace_shapes then
+ report_shapes("removing similar points: %s",topairs(t,n))
+ end
+ if n > 2 then
+ -- remove redundant points
+ repeat
+ tn = n
+ n = 0
+ local tm = t[tn]
+ local tmx = tm[1]
+ local tmy = tm[2]
+ local tp = t[1]
+ local tpx = tp[1]
+ local tpy = tp[2]
+ for i=1,tn do -- while and only step when done
+ local ti = tp
+ local tix = tpx
+ local tiy = tpy
+ if i == tn then
+ tp = t[1]
+ else
+ tp = t[i+1]
+ end
+ tpx = tp[1]
+ tpy = tp[2]
+
+ local vx1, vx2 = unitvector(tix - tmx,tpx - tix)
+ if vx1 ~= vx2 then
+ n = n + 1
+ t[n] = ti
+ else
+ local vy1, vy2 = unitvector(tiy - tmy,tpy - tiy)
+ if vy1 ~= vy2 then
+ n = n + 1
+ t[n] = ti
+ end
+ end
+
+ tmx = tix
+ tmy = tiy
+ end
+ until n == tn or n <= 2
+ if trace_shapes then
+ report_shapes("removing redundant points: %s",topairs(t,n))
+ end
+ -- remove spikes
+ if n > 2 then
+ repeat
+ tn = n
+ n = 0
+ local tm = t[tn]
+ local tmx = tm[1]
+ local tmy = tm[2]
+ local tp = t[1]
+ local tpx = tp[1]
+ local tpy = tp[2]
+ for i=1,tn do -- while and only step when done
+ local ti = tp
+ local tix = tpx
+ local tiy = tpy
+ if i == tn then
+ tp = t[1]
+ else
+ tp = t[i+1]
+ end
+ tpx = tp[1]
+ tpy = tp[2]
+
+ local vx1, vx2 = unitvector(tix - tmx,tpx - tix)
+ if vx1 ~= - vx2 then
+ n = n + 1
+ t[n] = ti
+ else
+ local vy1, vy2 = unitvector(tiy - tmy,tpy - tiy)
+ if vy1 ~= - vy2 then
+ n = n + 1
+ t[n] = ti
+ end
+ end
+
+ tmx = tix
+ tmy = tiy
+ end
+ until n == tn or n <= 2
+ if trace_shapes then
+ report_shapes("removing spikes: %s",topairs(t,n))
+ end
+ end
+ end
+ -- prune trailing points
+ if tm > n then
+ for i=tm,n+1,-1 do
+ t[i] = nil
+ end
+ end
+ if n > 1 then
+ local tf = t[1]
+ local tl = t[n]
+ local dx = tf[1] - tl[1]
+ local dy = tf[2] - tl[2]
+ if dx > eps or dx < - eps or dy > eps or dy < - eps then
+ -- different points
+ else
+ -- saves a point (as we -- cycle anyway)
+ t[n] = nil
+ n = n -1
+ end
+ if trace_shapes then
+ report_shapes("removing cyclic endpoints: %s",topairs(t,n))
+ end
+ end
+ return t
+end
+
+local eps = 65536
+
+-- The next function can introduce redundant points but these are removed later on
+-- in the unspiker. It makes checking easier.
+
+local function shape(kind,b,p,realpage,xmin,xmax,ymin,ymax,fh,ld)
+ local s = b.s
+ if not s then
+ if trace_shapes then
+ report_shapes("calculating %s area, no shape",kind)
+ end
+ return
+ end
+ s = s[realpage]
+ if not s then
+ if trace_shapes then
+ report_shapes("calculating %s area, no shape for page %s",kind,realpage)
+ end
+ return
+ end
+ local ns = #s
+ if ns == 0 then
+ if trace_shapes then
+ report_shapes("calculating %s area, empty shape for page %s",kind,realpage)
+ end
+ return
+ end
+ --
+ if trace_shapes then
+ report_shapes("calculating %s area, using shape for page %s",kind,realpage)
+ end
+ -- it's a bit inefficient to use the par values and later compensate for b and
+ -- e but this keeps the code (loop) cleaner
+ local ph = p and p.h or 0
+ local pd = p and p.d or 0
+ --
+ xmax = xmax + eps
+ xmin = xmin - eps
+ ymax = ymax + eps
+ ymin = ymin - eps
+ local ls = { } -- left shape
+ local rs = { } -- right shape
+ local pl = nil -- previous left x
+ local pr = nil -- previous right x
+ local n = 0
+ local xl = nil
+ local xr = nil
+ local mh = ph -- min
+ local md = pd -- min
+ for i=1,ns do
+ local si = s[i]
+ local y = si[1]
+ local ll = si[4] -- can be sparse
+ if ll then
+ xl = ll
+ local rr = si[5] -- can be sparse
+ if rr then
+ xr = rr
+ end
+ end
+ if trace_ranges then
+ report_shapes("original : [%02i] xl=%p xr=%p y=%p",i,xl,xr,y)
+ end
+ if xl ~= xr then -- could be catched in the finalizer
+ local xm = xl + (xr - xl)/2 -- midpoint should be in region
+ if xm >= xmin and xm <= xmax and y >= ymin and y <= ymax then
+ local ht = si[2] -- can be sparse
+ if ht then
+ ph = ht
+ local dp = si[3] -- can be sparse
+ if dp then
+ pd = dp
+ end
+ end
+ local h = y + (ph < mh and mh or ph)
+ local d = y - (pd < md and md or pd)
+ if pl then
+ n = n + 1
+ ls[n] = { pl, h }
+ rs[n] = { pr, h }
+ if trace_ranges then
+ report_shapes("paragraph : [%02i] xl=%p xr=%p y=%p",i,pl,pr,h)
+ end
+ end
+ n = n + 1
+ ls[n] = { xl, h }
+ rs[n] = { xr, h }
+ if trace_ranges then
+ report_shapes("height : [%02i] xl=%p xr=%p y=%p",i,xl,xr,h)
+ end
+ n = n + 1
+ ls[n] = { xl, d }
+ rs[n] = { xr, d }
+ if trace_ranges then
+ report_shapes("depth : [%02i] xl=%p xr=%p y=%p",i,xl,xr,d)
+ end
+ end
+ pl, pr = xl, xr
+ else
+ if trace_ranges then
+ report_shapes("ignored : [%02i] xl=%p xr=%p y=%p",i,xl,xr,y)
+ end
+ end
+ end
+ --
+ if true and n > 0 then
+ -- use height of b and depth of e, maybe check for weird border
+ -- cases here
+ if fh then
+ local lsf = ls[1]
+ local rsf = rs[1]
+ if lsf[2] < fh then
+ lsf[2] = fh
+ end
+ if rsf[2] < fh then
+ rsf[2] = fh
+ end
+ end
+ if fd then
+ local lsl = ls[n]
+ local rsl = rs[n]
+ if lsl[2] > fd then
+ lsl[2] = fd
+ end
+ if rsl[2] > fd then
+ rsl[2] = fd
+ end
+ end
+ end
+ --
+ for i=n,1,-1 do
+ n = n + 1 rs[n] = ls[i]
+ end
+ return rs
+end
+
+local function singlepart(b,e,p,realpage,r,left,right)
+ local bx = b.x
+ local by = b.y
+ local ex = e.x
+ local ey = e.y
+ local rx = r.x
+ local ry = r.y
+ local bh = by + b.h
+ local bd = by - b.d
+ local eh = ey + e.h
+ local ed = ey - e.d
+ local rh = ry + r.h
+ local rd = ry - r.d
+ local rw = rx + r.w
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ if ex == rx then
+ -- We probably have a strut at the next line so we force a width
+ -- although of course it is better to move up. But as we have whitespace
+ -- (at least visually) injected then it's best to stress the issue.
+ ex = rw
+ end
+ local area
+ if by == ey then
+ if trace_shapes then
+ report_shapes("calculating single area, partial line")
+ end
+ area = {
+ { bx, bh },
+ { ex, eh },
+ { ex, ed },
+ { bx, bd },
+ }
+ elseif b.k == 2 then
+ area = {
+ { rx, bh },
+ { rw, bh },
+ { rw, ed },
+ { rx, ed },
+ }
+ else
+ area = shape("single",b,p,realpage,rx,rw,rd,rh,bh,ed)
+ end
+ if not area then
+ area = {
+ { bx, bh },
+ { rw, bh },
+ { rw, eh },
+ { ex, eh },
+ { ex, ed },
+ { rx, ed },
+ { rx, bd },
+ { bx, bd },
+ }
+ end
+ return {
+ location = "single",
+ region = r,
+ area = finish(area),
+ }
+end
+
+local function firstpart(b,e,p,realpage,r,left,right)
+ local bx = b.x
+ local by = b.y
+ local rx = r.x
+ local ry = r.y
+ local bh = by + b.h
+ local bd = by - b.d
+ local rh = ry + r.h
+ local rd = ry - r.d
+ local rw = rx + r.w
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local area = shape("first",b,p,realpage,rx,rw,rd,rh,bh,false)
+ if not area then
+ if b.k == 2 then
+ area = {
+ { rx, bh },
+ { rw, bh },
+ { rw, rd },
+ { rx, rd },
+ }
+ else
+ area = {
+ { bx, bh },
+ { rw, bh },
+ { rw, rd }, -- { rw, eh },
+ { rx, rd }, -- { rx, ed },
+ { rx, bd },
+ { bx, bd },
+ }
+ end
+ end
+ return {
+ location = "first",
+ region = r,
+ area = finish(area),
+ }
+end
+
+local function middlepart(b,e,p,realpage,r,left,right)
+ local rx = r.x
+ local ry = r.y
+ local rh = ry + r.h
+ local rd = ry - r.d
+ local rw = rx + r.w
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local area = shape("middle",b,p,realpage,rx,rw,rd,rh,false,false)
+ if not area then
+ area = {
+ { rw, rh },
+ { rw, rd },
+ { rx, rd },
+ { rx, rh },
+ }
+ end
+ return {
+ location = "middle",
+ region = r,
+ area = finish(area),
+ }
+end
+
+local function lastpart(b,e,p,realpage,r,left,right)
+ local ex = e.x
+ local ey = e.y
+ local rx = r.x
+ local ry = r.y
+ local eh = ey + e.h
+ local ed = ey - e.d
+ local rh = ry + r.h
+ local rd = ry - r.d
+ local rw = rx + r.w
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local area = shape("last",b,p,realpage,rx,rw,rd,rh,false,ed)
+ if not area then
+ if b.k == 2 then
+ area = {
+ { rw, rh },
+ { rw, ed },
+ { rx, ed },
+ { rx, rh },
+ }
+ else
+ area = {
+ { rw, rh }, -- { rw, bh },
+ { rw, eh },
+ { ex, eh },
+ { ex, ed },
+ { rx, ed },
+ { rx, rh }, -- { rx, bd },
+ }
+ end
+ end
+ return {
+ location = "last",
+ region = r,
+ area = finish(area),
+ }
+end
+
+local function calculatemultipar(tag)
+ local collected = jobpositions.collected
+ local b = collected[f_b_tag(tag)]
+ local e = collected[f_e_tag(tag)]
+ if not b or not e then
+ report_shapes("invalid tag %a",tag)
+ return { }
+ end
+ local br = b.r
+ local er = e.r
+ if not br or not er then
+ report_shapes("invalid region for %a",tag)
+ return { }
+ end
+ local btag, bindex = lpegmatch(splitter,br)
+ local etag, eindex = lpegmatch(splitter,er)
+ if not bindex or not eindex or btag ~= etag then
+ report_shapes("invalid indices for %a",tag)
+ return { }
+ end
+ local bindex = tonumber(bindex)
+ local eindex = tonumber(eindex)
+ -- Here we compensate for columns (in tables): a table can have a set of column
+ -- entries and these are shared. We compensate left/right based on the columns
+ -- x and w but need to take the region into acount where the specification was
+ -- flushed and not the begin pos's region, because otherwise we get the wrong
+ -- compensation for asymetrical doublesided layouts.
+ local left = 0
+ local right = 0
+ local bc = b.c
+ local rc = bc and collected[bc]
+ if rc then
+ local tb = collected[rc.r]
+ if tb then
+ left = -(tb.x - rc.x)
+ right = (tb.w - rc.w - left)
+ end
+ end
+ -- Obeying intermediate changes of left/rightskip makes no sense as it will
+ -- look bad, so we only look at the begin situation.
+ local bn = b.n
+ local p = bn and collected[f_p_tag(bn)] -- par
+ if p then
+ left = left + (p.ls or 0)
+ right = right + (p.rs or 0)
+ end
+ --
+ local bp = b.p -- page
+ if trace_shapes then
+ report_shapes("tag %a, left %p, right %p, par %s, page %s, column %s",
+ tag,left,right,bn or "-",bp or "-",bc or "-")
+ end
+ --
+ if bindex == eindex then
+ return {
+ list = { [bp] = { singlepart(b,e,p,bp,collected[br],left,right) } },
+ bpos = b,
+ epos = e,
+ }
+ else
+ local list = {
+ [bp] = { firstpart(b,e,p,bp,collected[br],left,right) },
+ }
+ for i=bindex+1,eindex-1 do
+ br = f_tag_two(btag,i)
+ local r = collected[br]
+ if not r then
+ report_graphics("invalid middle for %a",br)
+ else
+ local rp = r.p -- page
+ local pp = list[rp]
+ local mp = middlepart(b,e,p,rp,r,left,right)
+ if pp then
+ pp[#pp+1] = mp
+ else
+ list[rp] = { mp }
+ end
+ end
+ end
+ local ep = e.p -- page
+ local pp = list[ep]
+ local lp = lastpart(b,e,p,ep,collected[er],left,right)
+ if pp then
+ pp[#pp+1] = lp
+ else
+ list[ep] = { lp }
+ end
+ return {
+ list = list,
+ bpos = b,
+ epos = e,
+ }
+ end
+end
+
+local pbg = { } -- will move to pending
+
+local multilocs = {
+ single = 1, -- maybe 0
+ first = 1,
+ middle = 2,
+ last = 3,
+}
+
+-- if unknown context_abck : input mp-abck.mpiv ; fi ;
+
+local f_template_a = formatters[ [[
+path multiregs[], multipars[], multibox ;
+string multikind[] ;
+numeric multilocs[], nofmultipars ;
+nofmultipars := %s ;
+multibox := unitsquare xyscaled (%p,%p) ;
+numeric par_strut_height, par_strut_depth, par_line_height ;
+par_strut_height := %p ;
+par_strut_depth := %p ;
+par_line_height := %p ;
+]] ]
+
+local f_template_b = formatters[ [[
+multilocs[%s] := %s ;
+multikind[%s] := "%s" ;
+multipars[%s] := (%--t--cycle) shifted - (%p,%p) ;
+]] ]
+
+-- unspiked(simplified(%--t--cycle)) shifted - (%p,%p) ;
+
+local f_template_c = formatters[ [[
+setbounds currentpicture to multibox ;
+]] ]
+
+local function freemultipar(pagedata,frees) -- ,k
+ -- if k == 3 then
+ -- -- tables have local regions
+ -- return
+ -- end
+ if not frees then
+ return
+ end
+ local nfree = #frees
+ if nfree == 0 then
+ return
+ end
+ for i=1,#pagedata do
+ local data = pagedata[i]
+ local area = data.area
+
+ if area then
+
+ local region = data.region
+ local y = 0 -- region.y
+ -- local x = region.x
+ local areas = { }
+ data.areas = areas
+
+ local f_1 = { }
+ local n_1 = 0
+ local f_2 = { }
+ local n_2 = 0
+ for i=1,#frees do
+ local f = frees[i]
+ local k = f.k
+ if k == 1 then -- pag
+ n_1 = n_1 + 1
+ f_1[n_1] = f
+ elseif k == 2 or k == 3 then -- par
+ n_2 = n_2 + 1
+ f_2[n_2] = f
+ end
+ end
+
+ local lineheight = tex.dimen.lineheight
+
+ -- page floats
+
+ local function check_one(free1,free2)
+ local temp = { }
+ local some = false
+ local top = (free2 and (y + free2.y + free2.h + (free2.to or 0))) or false
+ local bot = (free1 and (y + free1.y - free1.d - (free1.bo or 0))) or false
+ for i=1,#area do
+ local a = area[i]
+ local x = a[1]
+ local y = a[2]
+ if free2 and y <= top then
+ y = top
+ end
+ if free1 and y >= bot then
+ y = bot
+ end
+ if not some then
+ some = y
+ elseif some == true then
+ -- done
+ elseif y ~= some then
+ some = true
+ end
+ temp[i] = { x, y }
+ end
+ if some == true then
+ areas[#areas+1] = temp
+ end
+ end
+
+ if n_1 > 0 then
+ check_one(false,f_1[1])
+ for i=2,n_1 do
+ check_one(f_1[i-1],f_1[i])
+ end
+ check_one(f_1[n_1],false)
+ end
+
+ -- par floats
+
+ if #areas == 0 then
+ areas[1] = area
+ end
+
+ -- we can collect the coordinates first
+
+ local function check_two(area,frees)
+ local ul = area[1]
+ local ur = area[2]
+ local lr = area[3]
+ local ll = area[4]
+ local ulx = ul[1]
+ local uly = ul[2]
+ local urx = ur[1]
+ local ury = ur[2]
+ local lrx = lr[1]
+ local lry = lr[2]
+ local llx = ll[1]
+ local lly = ll[2]
+
+ local temp = { }
+ local n = 0
+ local done = false
+
+ for i=1,#frees do
+ local free = frees[i]
+ local fx = free.x
+ local fy = free.y
+ local ymax = y + fy + free.h + (free.to or 0)
+ local ymin = y + fy - free.d - (free.bo or 0)
+ local xmin = fx - (free.lo or 0)
+ local xmax = fx + free.w + (free.ro or 0)
+ if free.k == 3 then
+ if uly <= ymax and uly >= ymin and lly <= ymin then
+ if trace_free then
+ report_free("case 1, top, right") -- ok
+ end
+ n = n + 1 temp[n] = { xmin, ury }
+ n = n + 1 temp[n] = { xmin, ymin }
+ n = n + 1 temp[n] = { lrx, ymin }
+ n = n + 1 temp[n] = { lrx, lry }
+ done = true
+ elseif uly >= ymax and lly <= ymin then
+ if trace_free then
+ report_free("case 2, outside, right") -- ok
+ end
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmin, ury }
+ else
+ n = n + 1 temp[n] = { urx, ury }
+ n = n + 1 temp[n] = { urx, ymax }
+ end
+ n = n + 1 temp[n] = { xmin, ymax }
+ n = n + 1 temp[n] = { xmin, ymin }
+ n = n + 1 temp[n] = { lrx, ymin }
+ n = n + 1 temp[n] = { lrx, lry }
+ done = true
+ elseif lly <= ymax and lly >= ymin and uly >= ymax then
+ if trace_free then
+ report_free("case 3, bottom, right")
+ end
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmin, ury }
+ else
+ n = n + 1 temp[n] = { urx, ury }
+ n = n + 1 temp[n] = { urx, ymax }
+ end
+ n = n + 1 temp[n] = { xmin, ymax }
+ n = n + 1 temp[n] = { xmin, lry }
+ done = true
+ elseif uly <= ymax and lly >= ymin then
+ if trace_free then
+ report_free("case 4, inside, right")
+ end
+ n = n + 1 temp[n] = { xmin, uly }
+ n = n + 1 temp[n] = { xmin, lly }
+ done = true
+ else
+ -- case 0
+ if trace_free then
+ report_free("case 0, nothing")
+ end
+ end
+ end
+ end
+
+ if not done then
+ if trace_free then
+ report_free("no right shape")
+ end
+ n = n + 1 temp[n] = { urx, ury }
+ n = n + 1 temp[n] = { lrx, lry }
+ n = n + 1 temp[n] = { llx, lly }
+ else
+ done = false
+ end
+
+ for i=#frees,1,-1 do
+ local free = frees[i]
+ local fx = free.x
+ local fy = free.y
+ local ymax = y + fy + free.h + (free.to or 0)
+ local ymin = y + fy - free.d - (free.bo or 0)
+ local xmin = fx - (free.lo or 0)
+ local xmax = fx + free.w + (free.ro or 0)
+ if free.k == 2 then
+ if uly <= ymax and uly >= ymin and lly <= ymin then
+ if trace_free then
+ report_free("case 1, top, left") -- ok
+ end
+ n = n + 1 temp[n] = { ulx, ymin }
+ n = n + 1 temp[n] = { xmax, ymin }
+ n = n + 1 temp[n] = { xmax, uly }
+ done = true
+ elseif uly >= ymax and lly <= ymin then
+ if trace_free then
+ report_free("case 2, outside, left") -- ok
+ end
+ n = n + 1 temp[n] = { llx, lly }
+ n = n + 1 temp[n] = { llx, ymin }
+ n = n + 1 temp[n] = { xmax, ymin }
+ n = n + 1 temp[n] = { xmax, ymax }
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmax, uly }
+ else
+ n = n + 1 temp[n] = { llx, ymax }
+ n = n + 1 temp[n] = { llx, uly }
+ end
+ done = true
+ elseif lly <= ymax and lly >= ymin and uly >= ymax then
+ if trace_free then
+ report_free("case 3, bottom, left")
+ end
+ n = n + 1 temp[n] = { xmax, lly }
+ n = n + 1 temp[n] = { xmax, ymax }
+ if uly - ymax < lineheight then
+ n = n + 1 temp[n] = { xmax, uly }
+ else
+ n = n + 1 temp[n] = { llx, ymax }
+ n = n + 1 temp[n] = { llx, uly }
+ end
+ done = true
+ elseif uly <= ymax and lly >= ymin then
+ if trace_free then
+ report_free("case 4, inside, left")
+ end
+ n = n + 1 temp[n] = { xmax, lly }
+ n = n + 1 temp[n] = { xmax, uly }
+ done = true
+ else
+ -- case 0
+ end
+ end
+ end
+
+ if not done then
+ if trace_free then
+ report_free("no left shape")
+ end
+ n = n + 1 temp[n] = { llx, lly }
+ end
+ n = n + 1 temp[n] = { ulx, uly }
+
+ return temp
+ end
+
+ if n_2 > 0 then
+ for i=1,#areas do
+ local area = areas[i]
+ if #area == 4 then -- and also check type, must be pargaraph
+ areas[i] = check_two(area,f_2)
+ else
+ -- message that not yet supported
+ end
+ end
+ end
+
+ for i=1,#areas do
+ finish(areas[i]) -- again
+ end
+
+ end
+
+ end
+end
+
+local function fetchmultipar(n,anchor,page)
+ local a = jobpositions.collected[anchor]
+ if not a then
+ report_graphics("missing anchor %a",anchor)
+ else
+ local data = pbg[n]
+ if not data then
+ data = calculatemultipar(n)
+ pbg[n] = data -- can be replaced by register
+ -- register(data.list,n,anchor)
+ end
+ local list = data and data.list
+ if list then
+ local pagedata = list[page]
+ if pagedata then
+ local k = data.bpos.k
+ if k ~= 3 then
+ -- to be checked: no need in txt mode
+ freemultipar(pagedata,getfree(page))
+ end
+ local nofmultipars = #pagedata
+ if trace_shapes then
+ report_graphics("fetching %a at page %s using anchor %a containing %s multipars",
+ n,page,anchor,nofmultipars)
+ end
+ local x = a.x
+ local y = a.y
+ local w = a.w
+ local h = a.h
+ local d = a.d
+ local bpos = data.bpos
+ local bh = bpos.h
+ local bd = bpos.d
+ local result = { false } -- slot 1 will be set later
+ local n = 0
+ for i=1,nofmultipars do
+ local data = pagedata[i]
+ local location = data.location
+ local region = data.region
+ local areas = data.areas
+ if not areas then
+ areas = { data.area }
+ end
+ for i=1,#areas do
+ local area = areas[i]
+ for i=1,#area do
+ local a = area[i]
+ area[i] = f_pair(a[1],a[2])
+ end
+ n = n + 1
+ result[n+1] = f_template_b(n,multilocs[location],n,location,n,area,x,y)
+ end
+ end
+ data[page] = nil
+ result[1] = f_template_a(n,w,h+d,bh,bd,bh+bd) -- was delayed
+ result[n+2] = f_template_c()
+ return concat(result,"\n")
+ end
+ end
+ end
+ return f_template_a(0,0,0,0,0,0);
+end
+
+backgrounds.fetchmultipar = fetchmultipar
+
+local function getwhatever(action)
+ local tags = scanmpstring()
+ local anchor = scanmpstring()
+ local page = nil
+ if tags == "self" then
+ tags = getmacro(mpgnamespace .. getmacro("currentmpvariableclass") .. ":self",true) -- Isn't it already edef'd?
+ elseif type(tags) == "string" then
+ tags = settings_to_array(tags)
+ for i=1,#tags do
+ tags[i] = getmacro(mpgnamespace .. getmacro("currentmpvariableclass") .. ":" .. tags[i],true) -- Isn't it already edef'd?
+ end
+ end
+ if anchor == "anchor" then
+ anchor = getmacro("MPanchorid") -- brrr
+ end
+ if tags and anchor then
+ return action(tags,anchor,page)
+ end
+end
+
+metapost.registerscript("getmultipars", function()
+ return getwhatever(fetchmultipar)
+end)
+
+-- n anchor page
+
+implement {
+ name = "fetchmultipar",
+ actions = { fetchmultipar, context },
+ arguments = { "string", "string", "integer" }
+}
+
+-- todo: use inject
+
+do
+
+ local f_template_a = formatters[ [[
+ path posboxes[], posregions[] ;
+ numeric pospages[] ;
+ numeric nofposboxes ;
+ nofposboxes := %s ;
+ %t
+ ]] ]
+
+ local f_template_b = formatters[ [[
+ pospages[%s] := %s ;
+ posboxes[%s] := (%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle ;
+ posregions[%s] := (%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle ;
+ ]] ]
+
+ local function getposboxes(tags,anchor,page) -- no caching (yet) / page
+ local collected = jobpositions.collected
+ if type(tags) == "string" then
+ tags = settings_to_array(tags)
+ end
+ local list = { }
+ local nofboxes = 0
+ for i=1,#tags do
+ local tag= tags[i]
+ local c = collected[tag]
+ if c then
+ local r = c.r
+ if anchor ~= r then
+ r = anchor
+ end
+ if r then
+ r = collected[r]
+ if r then
+ local rx = r.x
+ local ry = r.y
+ local rw = r.w
+ local rh = r.h
+ local rd = r.d
+ local cx = c.x - rx
+ local cy = c.y - ry
+ local cw = cx + c.w
+ local ch = cy + c.h
+ local cd = cy - c.d
+ nofboxes = nofboxes + 1
+ list[nofboxes] = f_template_b(
+ nofboxes,c.p,
+ nofboxes,cx,ch,cw,ch,cw,cd,cx,cd,
+ nofboxes,0,rh,rw,rh,rw,rd,0,rd
+ )
+ end
+ end
+ else
+ -- print("\n missing",tag)
+ end
+ end
+ return f_template_a(nofboxes,list)
+ end
+
+ metapost.registerscript("getposboxes", function()
+ return getwhatever(getposboxes)
+ end)
+
+ implement {
+ name = "fetchposboxes",
+ arguments = { "string", "string", "integer" },
+ actions = { getposboxes, context },
+ }
+
+end
+
+do
+
+ local doifelse = commands.doifelse
+
+ implement {
+ name = "doifelserangeonpage",
+ arguments = { "string", "string", "integer" },
+ actions = function(first,last,page)
+ local c = jobpositions.collected
+ local f = c[first]
+ if f then
+ f = f.p
+ if f and f ~= true and page >= f then
+ local l = c[last]
+ if l then
+ l = l.p
+ doifelse(l and l ~= true and page <= l)
+ return
+ end
+ end
+ end
+ doifelse(false)
+ end
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-pgr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/anch-pgr.mkxl
new file mode 100644
index 00000000000..ec8f0e549f2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-pgr.mkxl
@@ -0,0 +1,444 @@
+%D \module
+%D [ file=anch-pgr, % split off core-pos
+%D version=1999.08.01,
+%D title=\CONTEXT\ Anchoring Macros,
+%D subtitle=Positioning Graphics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Anchoring Macros / Grapics}
+
+%D Before we come to graphics support, we have to make sure of the reference point
+%D on the page. The next macros do so and are hooked into the page building routine.
+
+\registerctxluafile{anch-pgr}{autosuffix}
+
+\unprotect
+
+%D A few more low level macros take care of defining and recalling actions. Actions
+%D are saved globally! The lists can become quite long because there can be lots of
+%D parameters passed on so we clean up the list afterwards.
+
+\newtoks\everypositionaction
+\newtoks\everyinsertpositionaction
+\newtoks\everycleanpositionaction
+
+\installcorenamespace{positionaction}
+\installcorenamespace{positioncleanup}
+
+\protected\def\anch_positions_set_action#1%
+ {\gdefcsname\??positionaction#1\endcsname} % nicely gobbles spaces
+
+\permanent\protected\def\doifpositionaction#1%
+ {\ifcsname\??positionaction#1\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifelsepositionaction#1%
+ {\ifcsname\??positionaction#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifpositionactionelse\doifelsepositionaction
+
+\permanent\protected\def\dopositionaction#1%
+ {\edef\currentpositionaction{#1}%
+ \ifcsname\??positionaction\currentpositionaction\endcsname
+ \anch_positions_action_indeed
+ \fi}
+
+\def\anch_positions_action_indeed
+ {\doifelseposition\currentpositionaction
+ \anch_positions_action_indeed_yes
+ \anch_positions_action_indeed_nop}
+
+\def\anch_positions_action_indeed_nop
+ {\anch_positions_trace_action_nop}
+
+\def\anch_positions_action_indeed_yes % we need a way to figure out if we have actions
+ {\begingroup
+ \setbox\scratchbox\hbox % \hpack
+ {\anch_positions_trace_action_yes
+ \the\everyinsertpositionaction
+ \the\everypositionaction
+ \begincsname\??positionaction\currentpositionaction\endcsname
+ \anch_positions_cleanup_action}%
+ \smashedbox\scratchbox % smashing is really needed else we get problems with too big overlays
+ \endgroup}
+
+\protected\def\anch_positions_trace_action_nop_indeed
+ {\anch_positions_trace\clap\darkred{<\currentpositionaction>}}
+
+\protected\def\anch_positions_trace_action_yes_indeed
+ {\anch_positions_trace\clap\darkgreen{<\currentpositionaction>}}
+
+\let\anch_positions_trace_action_nop\relax
+\let\anch_positions_trace_action_yes\relax
+
+\appendtoks
+ \let\anch_positions_trace_action_nop\anch_positions_trace_action_nop_indeed
+ \let\anch_positions_trace_action_yes\anch_positions_trace_action_yes_indeed
+\to \t_anch_positions_tracers
+
+%D Here the complication has to do with collecting actions for later execution. This
+%D collection is especially handy when we want to move actions to a specific layer.
+%D Such series of actions are stored in a macro that is cleaned up after each
+%D invocation.
+
+\def\anch_positions_cleanup_action % not in trialtypesetting
+ {\ifcsname\??positioncleanup\currentpositionaction\endcsname
+ \the\everycleanpositionaction
+ \xdefcsname\??positioncleanup\currentpositionaction\endcsname{\csname\??positioncleanup\currentpositionaction\endcsname}%
+ \fi}
+
+\permanent\protected\def\handlepositionaction#1\with#2\on#3% ugly, will change
+ {\begingroup
+ \edef\currentpositionanchor{\ifempty\currentpositionoverlay#3\else\currentpositionoverlay::\MPanchoridentifier\fi}%
+ \normalexpanded{\anch_positions_set_action{\currentpositionanchor}{\noexpand\csname\??positioncleanup\currentpositionanchor\endcsname}}%
+ \let#1\relax
+ \ifcsname\??positioncleanup\currentpositionanchor\endcsname
+ \xdefcsname\??positioncleanup\currentpositionanchor\endcsname{\csname\??positioncleanup\currentpositionanchor\endcsname#1#2}%
+ \else
+ \xdefcsname\??positioncleanup\currentpositionanchor\endcsname{#1#2}%
+ \fi
+ \endgroup}
+
+%D The first version of this module implemented head and tail anchors. Currently we
+%D stick to just one anchor and derive the head and tail anchors from this one. We
+%D set these anchors before and after each page.
+
+\newdimen\c_anch_page_width
+\newdimen\c_anch_page_height
+
+\protected\def\anch_positions_register_page#1% this one is flushed first ! ... can't we avoid this one
+ {\ifpositioning\ifcase\realpageno\or
+ \ifdim\c_anch_page_height=\paperheight
+ \ifdim\c_anch_page_width=\paperwidth
+ % no change
+ \else
+ \c_anch_page_width \paperwidth
+ \c_anch_page_height\paperheight
+ \anch_make_page_box{#1}% \ifvbox#1\setbox#1\hpack{\box#1}\fi
+ \fi
+ \else
+ \c_anch_page_width \paperwidth
+ \c_anch_page_height\paperheight
+ \anch_make_page_box{#1}% \ifvbox#1\setbox#1\hpack{\box#1}\fi
+ \fi
+ \fi\fi}
+
+\protected\def\anch_positions_place_anchors
+ {\ifpositioning
+ \anch_positions_place_anchors_yes
+ \else
+ \anch_positions_place_anchors_nop
+ \fi}
+
+\def\anch_positions_place_anchors_yes % todo : depth pagebox
+ {\begingroup
+ \setbox\scratchbox\emptyhbox
+ \ht\scratchbox\textheight
+ \dp\scratchbox\zeropoint % redundant
+ \wd\scratchbox\makeupwidth
+ \anch_mark_text_box\scratchbox
+ \box\scratchbox
+ \endgroup}
+
+\def\anch_positions_place_anchors_nop
+ {\vkern\textheight}
+
+%D \macros
+%D {positionoverlay,startpositionoverlay}
+%D
+%D As long as we're dealing with graphics it makes much sense to use the available
+%D overlay mechanism. For this purpose, we define some dedicated overlay extensions.
+%D
+%D \startbuffer[sample]
+%D \defineoverlay [sample] [\positionoverlay{sample}]
+%D
+%D \startpositionoverlay{sample}
+%D \setMPpositiongraphic{A-1}{connectcenter}{from=A-1,to=A-2}
+%D \stoppositionoverlay
+%D \stopbuffer
+%D
+%D \typebuffer[sample]
+%D
+%D \startbuffer[graphic]
+%D \startMPpositiongraphic{connectcenter}
+%D path pa, pb ; pair ca, cb ;
+%D initialize_box(\MPpos{\MPvar{from}}) ; pa := pxy ; ca := cxy ;
+%D initialize_box(\MPpos{\MPvar{to}}) ; pb := pxy ; cb := cxy ;
+%D draw pa withcolor red ;
+%D draw pb withcolor red ;
+%D draw ca -- cb withcolor blue ;
+%D anchor_box(\MPanchor{\MPvar{from}}) ;
+%D \stopMPpositiongraphic
+%D \stopbuffer
+%D
+%D We can best demonstrate this in an example, say:
+%D
+%D \startbuffer[text]
+%D \framed
+%D [backgroundachtergrond=sample,align=middle,width=7cm]
+%D {We want to connect \hpos {A-1} {this} word with its
+%D grammatical cousin \hpos {A-2} {that}.}
+%D \stopbuffer
+%D
+%D \typebuffer[text]
+%D
+%D \startlinecorrection
+%D %\getbuffer[graphic,sample,text]
+%D \stoplinecorrection
+%D
+%D The graphic is defined in the following way, using some macros defined in an
+%D auxiliary \METAPOST\ module that is preloaded.
+%D
+%D \typebuffer[graphic]
+
+\immutable\def\MPanchoridentifier{mpa} % {mp-anchor}
+\mutable \def\MPanchornumber {\the\realpageno}
+\mutable \let\MPanchorid \empty
+
+%D The rest of the definitions concerning such overlays may look complicated,
+
+\mutable\let\currentpositionoverlay\empty
+
+%D Position actions are automatically executed when a position is set.
+
+\immutable\def\textbackgroundoverlay#1{\v!text#1}
+
+\permanent\protected\def\positionoverlay % the test prevents too many redundant positions
+ {\ifpositioning % in (not used) text* position layers
+ \expandafter\anch_positions_overlay_indeed
+ \else % also \iftrialtypesetting test here?
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\anch_positions_overlay_indeed#1%
+ {\begingroup
+ \edef\currentpositionoverlay{#1}%
+ \ifcsname\??positionaction\currentpositionoverlay::\MPanchoridentifier\endcsname
+ \anch_positions_overlay_compose
+ \fi
+ \endgroup}
+
+\permanent\def\MPoverlayanchor#1{\MPpos\MPanchorid}
+
+\def\anch_positions_overlay_compose
+ {\vpack to \d_overlay_height
+ {%\writestatus{!!!}{\currentpositionoverlay/\MPanchoridentifier/\MPanchornumber}%
+ \edef\MPanchorid{\currentpositionoverlay::\MPanchoridentifier:\MPanchornumber}% realpageno
+ % \edef\MPanchor##1{\MPpos\MPanchorid}%
+ \enforced\let\MPanchor\MPoverlayanchor % no need to fetch it already, seldom used
+ \the\everyinsertpositionaction
+ \copyposition{\currentpositionoverlay::\MPanchoridentifier}\MPanchorid
+ \setbox\scratchbox\hbox to \d_overlay_width % \hpack
+ {\dopositionaction{\currentpositionoverlay::\MPanchoridentifier}\hss}%
+ \ht\scratchbox\d_overlay_height
+ \dp\scratchbox\zeropoint
+ \anch_mark_tagged_box\scratchbox\MPanchorid % needs an hbox
+ \box\scratchbox
+ \vfill}}
+
+\permanent\protected\def\positionregionoverlay % shares regions
+ {\ifpositioning
+ \expandafter\anch_positions_region_overlay_indeed
+ \else % also \iftrialtypesetting test here?
+ \expandafter\gobbletwoarguments
+ \fi}
+
+\let\currentpositionregion\empty
+
+\def\anch_positions_region_overlay_indeed#1#2%
+ {\begingroup
+ \edef\currentpositionregion {#1}%
+ \edef\currentpositionoverlay{#2}%
+ \ifcsname\??positionaction\currentpositionoverlay::\MPanchoridentifier\endcsname
+ \anch_positions_region_overlay_compose
+ \fi
+ \endgroup}
+
+\def\anch_positions_region_overlay_compose
+ {\vpack to \d_overlay_height
+ {\let\MPanchorid\currentpositionregion
+ \enforced\let\MPanchor\MPoverlayanchor % no need to fetch it already, seldom used
+ \the\everyinsertpositionaction
+ \copyposition{\currentpositionoverlay::\MPanchoridentifier}\MPanchorid
+ \setbox\scratchbox\hbox to \d_overlay_width % \hpack
+ {\dopositionaction{\currentpositionoverlay::\MPanchoridentifier}\hss}%
+ \ht\scratchbox\d_overlay_height
+ \dp\scratchbox\zeropoint
+ \box\scratchbox
+ \vfill}}
+
+% \let\anch_positions_overlay_nop\gobbleoneargument
+
+\permanent\protected\def\startpositionoverlay
+ {\iftrialtypesetting
+ \expandafter\anch_positions_overlay_start_nop
+ \else
+ \expandafter\anch_positions_overlay_start_yes
+ \fi}
+
+\def\anch_positions_overlay_start_nop#1\stoppositionoverlay
+ {}
+
+\ifdefined\checkpositionoverlays \else \let\checkpositionoverlays\relax \fi
+
+\mutable\let\currentpositionoverlay\empty
+
+\def\anch_positions_overlay_start_yes#1%
+ {\checkpositionoverlays
+ \edef\currentpositionoverlay{#1}}
+
+\permanent\protected\def\stoppositionoverlay
+ {\let\currentpositionoverlay\empty}
+
+%D A position graphic is a normal (non||reused) \METAPOST\ graphic, used
+%D immediately, with zero dimensions, so that a sequence of them does not harm.
+
+\installcorenamespace{positiongraphic}
+\installcorenamespace{positionmethod}
+%installcorenamespace{graphicvariable}
+
+\newbox\b_anch_positions_graphic
+
+\permanent\tolerant\protected\def\startMPpositiongraphic#=#*#=#:#3\stopMPpositiongraphic % tag list mpcode
+ {\gdefcsname\??positiongraphic#1\endcsname{\anch_positions_meta_graphic_use{#1}{#2}{#3}}}
+
+\aliased\let\stopMPpositiongraphic\relax
+
+\def\anch_positions_meta_graphic_prepare
+ {\ifcsname\??graphicvariable\currentmpvariableclass:self\endcsname \else
+ \letcsname\??graphicvariable\currentmpvariableclass:self\endcsname\currentposition
+ \fi
+ \ifcsname\??graphicvariable\currentmpvariableclass:from\endcsname \else
+ \letcsname\??graphicvariable\currentmpvariableclass:from\endcsname\currentposition
+ \fi}
+
+\def\anch_positions_meta_graphic_use#1#2#3%
+ {\begingroup
+ \meta_prepare_variables{#2}%
+ \anch_positions_meta_graphic_prepare
+ \startMPcode#3\stopMPcode
+ \endgroup}
+
+\permanent\tolerant\protected\def\MPpositiongraphic#=#*#=%
+ {\ifcsname\??positionmethod#1\endcsname % method
+ \expandafter\anch_positions_meta_graphic_direct_method
+ \orelse\ifcsname\??positiongraphic#1\endcsname
+ \expandafter\anch_positions_meta_graphic_direct_normal
+ \else
+ \expandafter\gobbletwoarguments
+ \fi{#1}{#2}}
+
+\def\anch_positions_meta_graphic_direct_method{\anch_positions_meta_graphic_direct\??positionmethod }
+\def\anch_positions_meta_graphic_direct_normal{\anch_positions_meta_graphic_direct\??positiongraphic}
+
+\def\anch_positions_meta_graphic_direct#1#2#3% what tag setups
+ {\begingroup
+ \setupMPvariables[#2][#3]%
+ \edef\currentmpvariableclass{#2}%
+ \anch_positions_meta_graphic_prepare
+ \obeyMPboxorigin % do we also set the size ? when needed this must be done in mp ... might change
+ \enforced\tolerant\protected\def\MPpositiongraphic##=##*##={\anch_positions_meta_graphic_nested{#3}{##1}{##2}}% takes two extra arguments
+ % \def\anch_positions_meta_graphic_direct{\anch_positions_meta_graphic_nested{#3}}% takes two extra arguments
+ \setbox\b_anch_positions_graphic\hbox % \hpack
+ {\ignorespaces\begincsname#1#2\endcsname\removelastspace}%
+ \smashbox\b_anch_positions_graphic
+ \box\b_anch_positions_graphic
+ \endgroup}
+
+\def\anch_positions_meta_graphic_nested#1#2#3% nesting used in prikkels / pascal (might go away)
+ {\begingroup
+ \setupMPvariables[#2][#1,#3]%
+ \edef\currentmpvariableclass{#2}%
+ \anch_positions_meta_graphic_prepare
+ \begincsname\??positiongraphic#2\endcsname
+ \endgroup}%
+
+\permanent\def\startMPpositionmethod#1#2\stopMPpositionmethod
+ {\gdefcsname\??positionmethod#1\endcsname{#2}} % todo: var list here
+
+\aliased\let\stopMPpositionmethod\relax
+
+%D Simple one position graphics.
+
+\permanent\tolerant\protected\def\setMPpositiongraphic#=#*#=#*#=%
+ {\ifempty\currentpositionoverlay
+ \anch_positions_set_action{#1}{\MPpositiongraphic{#2}{#3}}%
+ \else % silly can be one
+ \anch_positions_meta_graphic_handle{#1}{#2}{#3}%
+ \fi}
+
+\def\anch_positions_meta_graphic_handle#1#2#3% combine with boxes
+ {\handlepositionaction\anch_positions_meta_graphic_handle_indeed\with{#1}{#2}{#3}\on{#2}}
+
+\def\anch_positions_meta_graphic_insert#1#2#3% pos tag setups
+ {\ifnum\MPp{#1}=\realpageno\relax % extra saveguard
+ \def\currentposition{#1}\MPpositiongraphic{#2}{#3}%
+ \fi}
+
+\let\anch_positions_meta_graphic_handle_indeed\relax
+
+\appendtoks
+ \let\anch_positions_meta_graphic_handle_indeed\anch_positions_meta_graphic_insert
+\to \everyinsertpositionaction
+
+\def\anch_positions_meta_graphic_cleanup#1#2#3% pos tag setups
+ {\ifnum\MPp{#1}<\realpageno \else
+ \noexpand\anch_positions_meta_graphic_handle_indeed{#1}{#2}{#3}%
+ \fi}
+
+\appendtoks
+ \let\anch_positions_meta_graphic_handle_indeed\anch_positions_meta_graphic_cleanup
+\to \everycleanpositionaction
+
+%D Graphics that span two positions (beware, does not cross pages).
+
+\permanent\tolerant\protected\def\setMPpositiongraphicrange#=#*#=#*#=#*#=%
+ {\ifempty\currentpositionoverlay
+ \anch_positions_set_action{#1}{\MPpositiongraphic{#3}{#4}}%
+ \else
+ \anch_positions_meta_graphic_handle_range{#1}{#2}{#3}{#4}%
+ \fi}
+
+\def\anch_positions_meta_graphic_handle_range#1#2#3#4%
+ {\handlepositionaction\anch_positions_meta_graphic_handle_range_indeed\with{#1}{#2}{#3}{#4}\on{#2}}
+
+\def\anch_positions_meta_graphic_insert_range#1#2#3#4% pos pos tag setups
+ {\clf_doifelserangeonpage{#1}{#2}\realpageno
+ {\def\currentposition{#1}%
+ \MPpositiongraphic{#3}{#4}}%
+ {}}
+
+\appendtoks
+ \let\anch_positions_meta_graphic_handle_range_indeed\anch_positions_meta_graphic_insert_range
+\to \everyinsertpositionaction
+
+\def\anch_positions_meta_graphic_cleanup_range#1#2#3#4% pos tag setups
+ {\ifnum\MPp{#2}<\realpageno \else
+ \noexpand \anch_positions_meta_graphic_handle_range_indeed{#1}{#2}{#3}{#4}%
+ \fi}
+
+\appendtoks
+ \let\anch_positions_meta_graphic_handle_range_indeed\anch_positions_meta_graphic_cleanup_range
+\to \everycleanpositionaction
+
+\let\anch_positions_meta_graphic_handle_range_indeed\gobblefourarguments
+
+% Helpers:
+
+\permanent\def\MPgetposboxes #1#2{\clf_fetchposboxes{#1}{#2}\realpageno}
+\permanent\def\MPgetmultipars#1#2{\clf_fetchmultipar{#1}{#2}\realpageno}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-pos.lmt b/Master/texmf-dist/tex/context/base/mkxl/anch-pos.lmt
new file mode 100644
index 00000000000..67d1657f20d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-pos.lmt
@@ -0,0 +1,1636 @@
+if not modules then modules = { } end modules ['anch-pos'] = {
+ version = 1.001,
+ comment = "companion to anch-pos.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>We save positional information in the main utility table. Not only
+can we store much more information in <l n='lua'/> but it's also
+more efficient.</p>
+--ldx]]--
+
+-- plus (extra) is obsolete but we will keep it for a while
+--
+-- maybe replace texsp by our own converter (stay at the lua end)
+-- eventually mp will have large numbers so we can use sp there too
+--
+-- this is one of the first modules using scanners and we need to replace it by
+-- implement and friends
+--
+-- we could have namespaces, like p, page, region, columnarea, textarea but then
+-- we need virtual table accessors as well as have tag/id accessors ... we don't
+-- save much here (at least not now)
+--
+-- This was the last module that got rid of directly setting scanners, with a little
+-- performance degradation but not that noticeable.
+
+local tostring, next, setmetatable, tonumber = tostring, next, setmetatable, tonumber
+local sort = table.sort
+local format, gmatch = string.format, string.gmatch
+local lpegmatch = lpeg.match
+local insert, remove = table.insert, table.remove
+local allocate = utilities.storage.allocate
+
+local report = logs.reporter("positions")
+
+local scanners = tokens.scanners
+local scanstring = scanners.string
+local scaninteger = scanners.integer
+local scandimen = scanners.dimen
+
+local implement = interfaces.implement
+
+local commands = commands
+local context = context
+
+local ctx_latelua = context.latelua
+
+local tex = tex
+local texgetcount = tex.getcount
+local texgetinteger = tex.getintegervalue or tex.getcount
+local texsetcount = tex.setcount
+local texget = tex.get
+local texsp = tex.sp
+----- texsp = string.todimen -- because we cache this is much faster but no rounding
+
+local setmetatableindex = table.setmetatableindex
+local setmetatablenewindex = table.setmetatablenewindex
+
+local nuts = nodes.nuts
+
+local setlink = nuts.setlink
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getbox = nuts.getbox
+local getid = nuts.getid
+local getwhd = nuts.getwhd
+
+local hlist_code = nodes.nodecodes.hlist
+
+local find_tail = nuts.tail
+----- hpack = nuts.hpack
+
+local new_latelua = nuts.pool.latelua
+
+local variables = interfaces.variables
+local v_text = variables.text
+local v_column = variables.column
+
+local pt = number.dimenfactors.pt
+local pts = number.pts
+local formatters = string.formatters
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local jobpositions = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.positions = jobpositions
+
+local default = { -- not r and paragraphs etc
+ __index = {
+ x = 0, -- x position baseline
+ y = 0, -- y position baseline
+ w = 0, -- width
+ h = 0, -- height
+ d = 0, -- depth
+ p = 0, -- page
+ n = 0, -- paragraph
+ ls = 0, -- leftskip
+ rs = 0, -- rightskip
+ hi = 0, -- hangindent
+ ha = 0, -- hangafter
+ hs = 0, -- hsize
+ pi = 0, -- parindent
+ ps = false, -- parshape
+ dir = 0,
+ }
+}
+
+local f_b_tag = formatters["b:%s"]
+local f_e_tag = formatters["e:%s"]
+local f_p_tag = formatters["p:%s"]
+local f_w_tag = formatters["w:%s"]
+
+local f_region = formatters["region:%s"]
+
+local f_tag_three = formatters["%s:%s:%s"]
+local f_tag_two = formatters["%s:%s"]
+
+local nofregular = 0
+local nofspecial = 0
+local splitter = lpeg.splitat(":",true)
+
+local pagedata = { }
+local columndata = setmetatableindex("table") -- per page
+local freedata = setmetatableindex("table") -- per page
+
+local function initializer()
+ tobesaved = jobpositions.tobesaved
+ collected = jobpositions.collected
+ for tag, data in next, collected do
+ local prefix, rest = lpegmatch(splitter,tag)
+ if prefix == "p" then
+ nofregular = nofregular + 1
+ elseif prefix == "page" then
+ nofregular = nofregular + 1
+ pagedata[tonumber(rest) or 0] = data
+ elseif prefix == "free" then
+ nofspecial = nofspecial + 1
+ local t = freedata[data.p or 0]
+ t[#t+1] = data
+ elseif prefix == "columnarea" then
+ columndata[data.p or 0][data.c or 0] = data
+ end
+ setmetatable(data,default)
+ end
+ --
+ local pages = structures.pages.collected
+ if pages then
+ local last = nil
+ for p=1,#pages do
+ local region = "page:" .. p
+ local data = pagedata[p]
+ local free = freedata[p]
+ if free then
+ sort(free,function(a,b) return b.y < a.y end) -- order matters !
+ end
+ if data then
+ last = data
+ last.free = free
+ elseif last then
+ local t = setmetatableindex({ free = free, p = p },last)
+ if not collected[region] then
+ collected[region] = t
+ else
+ -- something is wrong
+ end
+ pagedata[p] = t
+ end
+ end
+ end
+ jobpositions.pagedata = pagedata
+end
+
+function jobpositions.used()
+ return next(collected) -- we can safe it
+end
+
+function jobpositions.getfree(page)
+ return freedata[page]
+end
+
+-- we can gain a little when we group positions but then we still have to
+-- deal with regions and cells so we either end up with lots of extra small
+-- tables pointing to them and/or assembling/disassembling so in the end
+-- it makes no sense to do it (now) and still have such a mix
+--
+-- proof of concept code removed ... see archive
+
+local function finalizer()
+ -- We make the (possible extensive) shape lists sparse working
+ -- from the end. We could also drop entries here that have l and
+ -- r the same which saves testing later on.
+ for k, v in next, tobesaved do
+ local s = v.s
+ if s then
+ for p, data in next, s do
+ local n = #data
+ if n > 1 then
+ local ph = data[1][2]
+ local pd = data[1][3]
+ local xl = data[1][4]
+ local xr = data[1][5]
+ for i=2,n do
+ local di = data[i]
+ local h = di[2]
+ local d = di[3]
+ local l = di[4]
+ local r = di[5]
+ if r == xr then
+ di[5] = nil
+ if l == xl then
+ di[4] = nil
+ if d == pd then
+ di[3] = nil
+ if h == ph then
+ di[2] = nil
+ else
+ ph = h
+ end
+ else
+ pd, ph = d, h
+ end
+ else
+ ph, pd, xl = h, d, l
+ end
+ else
+ ph, pd, xl, xr = h, d, l, r
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+job.register('job.positions.collected', tobesaved, initializer, finalizer)
+
+local regions = { }
+local nofregions = 0
+local region = nil
+
+local columns = { }
+local nofcolumns = 0
+local column = nil
+
+local nofpages = nil
+
+-- beware ... we're not sparse here as lua will reserve slots for the nilled
+
+local getpos, gethpos, getvpos, getrpos
+
+function jobpositions.registerhandlers(t)
+ getpos = t and t.getpos or function() return 0, 0 end
+ getrpos = t and t.getrpos or function() return 0, 0, 0 end
+ gethpos = t and t.gethpos or function() return 0 end
+ getvpos = t and t.getvpos or function() return 0 end
+end
+
+function jobpositions.getpos () return getpos () end
+function jobpositions.getrpos() return getrpos() end
+function jobpositions.gethpos() return gethpos() end
+function jobpositions.getvpos() return getvpos() end
+
+-------- jobpositions.getcolumn() return column end
+
+jobpositions.registerhandlers()
+
+local function setall(name,p,x,y,w,h,d,extra)
+ tobesaved[name] = {
+ p = p,
+ x = x ~= 0 and x or nil,
+ y = y ~= 0 and y or nil,
+ w = w ~= 0 and w or nil,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ e = extra ~= "" and extra or nil,
+ r = region,
+ c = column,
+ r2l = texgetinteger("inlinelefttoright") == 1 and true or nil,
+ }
+end
+
+local function enhance(data)
+ if not data then
+ return nil
+ end
+ if data.r == true then -- or ""
+ data.r = region
+ end
+ if data.x == true then
+ if data.y == true then
+ local x, y = getpos()
+ data.x = x ~= 0 and x or nil
+ data.y = y ~= 0 and y or nil
+ else
+ local x = gethpos()
+ data.x = x ~= 0 and x or nil
+ end
+ elseif data.y == true then
+ local y = getvpos()
+ data.y = y ~= 0 and y or nil
+ end
+ if data.p == true then
+ data.p = texgetcount("realpageno") -- we should use a variable set in otr
+ end
+ if data.c == true then
+ data.c = column
+ end
+ if data.w == 0 then
+ data.w = nil
+ end
+ if data.h == 0 then
+ data.h = nil
+ end
+ if data.d == 0 then
+ data.d = nil
+ end
+ return data
+end
+
+-- analyze some files (with lots if margindata) and then when one key optionally
+-- use that one instead of a table (so, a 3rd / 4th argument: key, e.g. "x")
+
+local function set(name,index,value) -- ,key
+ -- officially there should have been a settobesaved
+ local data = enhance(value or {})
+ if value then
+ container = tobesaved[name]
+ if not container then
+ tobesaved[name] = {
+ [index] = data
+ }
+ else
+ container[index] = data
+ end
+ else
+ tobesaved[name] = data
+ end
+end
+
+local function setspec(specification)
+ local name = specification.name
+ local index = specification.index
+ local value = specification.value
+ local data = enhance(value or {})
+ if value then
+ container = tobesaved[name]
+ if not container then
+ tobesaved[name] = {
+ [index] = data
+ }
+ else
+ container[index] = data
+ end
+ else
+ tobesaved[name] = data
+ end
+end
+
+local function get(id,index)
+ if index then
+ local container = collected[id]
+ return container and container[index]
+ else
+ return collected[id]
+ end
+end
+
+------------.setdim = setdim
+jobpositions.setall = setall
+jobpositions.set = set
+jobpositions.setspec = setspec
+jobpositions.get = get
+
+implement {
+ name = "dosaveposition",
+ public = true,
+ protected = true,
+ arguments = { "argument", "integerargument", "dimenargument", "dimenargument" },
+ actions = setall, -- name p x y
+}
+
+implement {
+ name = "dosavepositionwhd",
+ public = true,
+ protected = true,
+ arguments = { "argument", "integerargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument" },
+ actions = setall, -- name p x y w h d
+}
+
+implement {
+ name = "dosavepositionplus",
+ public = true,
+ protected = true,
+ arguments = { "argument", "integerargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "argument" },
+ actions = setall, -- name p x y w h d extra
+}
+
+-- will become private table (could also become attribute driven but too nasty
+-- as attributes can bleed e.g. in margin stuff)
+
+-- not much gain in keeping stack (inc/dec instead of insert/remove)
+
+local function b_column(specification)
+ local tag = specification.tag
+ local x = gethpos()
+ tobesaved[tag] = {
+ r = true,
+ x = x ~= 0 and x or nil,
+ -- w = 0,
+ }
+ insert(columns,tag)
+ column = tag
+end
+
+local function e_column()
+ local t = tobesaved[column]
+ if not t then
+ -- something's wrong
+ else
+ local x = gethpos() - t.x
+ t.w = x ~= 0 and x or nil
+ t.r = region
+ end
+ remove(columns)
+ column = columns[#columns]
+end
+
+jobpositions.b_column = b_column
+jobpositions.e_column = e_column
+
+implement {
+ name = "bposcolumn",
+ arguments = "string",
+ actions = function(tag)
+ insert(columns,tag)
+ column = tag
+ end
+}
+
+implement {
+ name = "bposcolumnregistered",
+ arguments = "string",
+ actions = function(tag)
+ insert(columns,tag)
+ column = tag
+ ctx_latelua { action = b_column, tag = tag }
+ end
+}
+
+implement {
+ name = "eposcolumn",
+ actions = function()
+ remove(columns)
+ column = columns[#columns]
+ end
+}
+
+implement {
+ name = "eposcolumnregistered",
+ actions = function()
+ ctx_latelua { action = e_column }
+ remove(columns)
+ column = columns[#columns]
+ end
+}
+
+-- regions
+
+local function b_region(specification)
+ local tag = specification.tag or specification
+ local last = tobesaved[tag]
+ local x, y = getpos()
+ last.x = x ~= 0 and x or nil
+ last.y = y ~= 0 and y or nil
+ last.p = texgetcount("realpageno")
+ insert(regions,tag) -- todo: fast stack
+ region = tag
+end
+
+local function e_region(specification)
+ local last = tobesaved[region]
+ local y = getvpos()
+ local x, y = getpos()
+ if specification.correct then
+ local h = (last.y or 0) - y
+ last.h = h ~= 0 and h or nil
+ end
+ last.y = y ~= 0 and y or nil
+ remove(regions) -- todo: fast stack
+ region = regions[#regions]
+end
+
+jobpositions.b_region = b_region
+jobpositions.e_region = e_region
+
+local lastregion
+
+local function setregionbox(n,tag,k,lo,ro,to,bo,column) -- kind
+ if not tag or tag == "" then
+ nofregions = nofregions + 1
+ tag = f_region(nofregions)
+ end
+ local box = getbox(n)
+ local w, h, d = getwhd(box)
+ tobesaved[tag] = {
+ -- p = texgetcount("realpageno"), -- we copy them
+ x = 0,
+ y = 0,
+ w = w ~= 0 and w or nil,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ k = k ~= 0 and k or nil,
+ lo = lo ~= 0 and lo or nil,
+ ro = ro ~= 0 and ro or nil,
+ to = to ~= 0 and to or nil,
+ bo = bo ~= 0 and bo or nil,
+ c = column or nil,
+ }
+ lastregion = tag
+ return tag, box
+end
+
+local function markregionbox(n,tag,correct,...) -- correct needs checking
+ local tag, box = setregionbox(n,tag,...)
+ -- todo: check if tostring is needed with formatter
+ local push = new_latelua { action = b_region, tag = tag }
+ local pop = new_latelua { action = e_region, correct = correct }
+ -- maybe we should construct a hbox first (needs experimenting) so that we can avoid some at the tex end
+ local head = getlist(box)
+ -- no, this fails with \framed[region=...] .. needs thinking
+ -- if getid(box) ~= hlist_code then
+ -- -- report("mark region box assumes a hlist, fix this for %a",tag)
+ -- head = hpack(head)
+ -- end
+ if head then
+ local tail = find_tail(head)
+ setlink(push,head)
+ setlink(tail,pop)
+ else -- we can have a simple push/pop
+ setlink(push,pop)
+ end
+ setlist(box,push)
+end
+
+jobpositions.markregionbox = markregionbox
+jobpositions.setregionbox = setregionbox
+
+function jobpositions.enhance(name)
+ enhance(tobesaved[name])
+end
+
+function jobpositions.gettobesaved(name,tag)
+ local t = tobesaved[name]
+ if t and tag then
+ return t[tag]
+ else
+ return t
+ end
+end
+
+function jobpositions.settobesaved(name,tag,data)
+ local t = tobesaved[name]
+ if t and tag and data then
+ t[tag] = data
+ end
+end
+
+local nofparagraphs = 0
+
+implement {
+ name = "parpos",
+ actions = function()
+ nofparagraphs = nofparagraphs + 1
+ texsetcount("global","c_anch_positions_paragraph",nofparagraphs)
+ local box = getbox("strutbox")
+ local w, h, d = getwhd(box)
+ local t = {
+ p = true,
+ c = true,
+ r = true,
+ x = true,
+ y = true,
+ h = h,
+ d = d,
+ hs = texget("hsize"), -- never 0
+ }
+ local leftskip = texget("leftskip",false)
+ local rightskip = texget("rightskip",false)
+ local hangindent = texget("hangindent")
+ local hangafter = texget("hangafter")
+ local parindent = texget("parindent")
+ local parshape = texget("parshape")
+ if leftskip ~= 0 then
+ t.ls = leftskip
+ end
+ if rightskip ~= 0 then
+ t.rs = rightskip
+ end
+ if hangindent ~= 0 then
+ t.hi = hangindent
+ end
+ if hangafter ~= 1 and hangafter ~= 0 then -- can not be zero .. so it needs to be 1 if zero
+ t.ha = hangafter
+ end
+ if parindent ~= 0 then
+ t.pi = parindent
+ end
+ if parshape and #parshape > 0 then
+ t.ps = parshape
+ end
+ local name = f_p_tag(nofparagraphs)
+ tobesaved[name] = t
+ ctx_latelua { action = enhance, specification = t }
+ end
+}
+
+implement {
+ name = "dosetposition",
+ arguments = "argument",
+ public = true,
+ protected = true,
+ actions = function(name)
+ local spec = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ r2l = texgetinteger("inlinelefttoright") == 1 or nil,
+ }
+ tobesaved[name] = spec
+ ctx_latelua { action = enhance, specification = spec }
+ end
+}
+
+implement {
+ name = "dosetpositionwhd",
+ arguments = { "argument", "dimenargument", "dimenargument", "dimenargument" },
+ public = true,
+ protected = true,
+ actions = function(name,w,h,d)
+ local spec = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ w = w ~= 0 and w or nil,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ r2l = texgetinteger("inlinelefttoright") == 1 or nil,
+ }
+ tobesaved[name] = spec
+ ctx_latelua { action = enhance, specification = spec }
+ end
+}
+
+implement {
+ name = "dosetpositionbox",
+ arguments = { "argument", "integerargument" },
+ public = true,
+ protected = true,
+ actions = function(name,n)
+ local box = getbox(n)
+ local w, h, d = getwhd(box)
+ local spec = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ w = w ~= 0 and w or nil,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ r2l = texgetinteger("inlinelefttoright") == 1 or nil,
+ }
+ tobesaved[name] = spec
+ ctx_latelua { action = enhance, specification = spec }
+ end
+}
+
+implement {
+ name = "dosetpositionplus",
+ arguments = { "argument", "dimenargument", "dimenargument", "dimenargument" },
+ public = true,
+ protected = true,
+ actions = function(name,w,h,d)
+ local spec = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ w = w ~= 0 and w or nil,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ e = scanstring(),
+ r2l = texgetinteger("inlinelefttoright") == 1 or nil,
+ }
+ tobesaved[name] = spec
+ ctx_latelua { action = enhance, specification = spec }
+ end
+}
+
+implement {
+ name = "dosetpositionstrut",
+ arguments = "argument",
+ public = true,
+ protected = true,
+ actions = function(name)
+ local box = getbox("strutbox")
+ local w, h, d = getwhd(box)
+ local spec = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ r2l = texgetinteger("inlinelefttoright") == 1 or nil,
+ }
+ tobesaved[name] = spec
+ ctx_latelua { action = enhance, specification = spec }
+ end
+}
+
+implement {
+ name = "dosetpositionstrutkind",
+ arguments = { "argument", "integerargument" },
+ public = true,
+ protected = true,
+ actions = function(name,kind)
+ local box = getbox("strutbox")
+ local w, h, d = getwhd(box)
+ local spec = {
+ k = kind,
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ r2l = texgetinteger("inlinelefttoright") == 1 or nil,
+ }
+ tobesaved[name] = spec
+ ctx_latelua { action = enhance, specification = spec }
+ end
+}
+
+function jobpositions.getreserved(tag,n)
+ if tag == v_column then
+ local fulltag = f_tag_three(tag,texgetcount("realpageno"),n or 1)
+ local data = collected[fulltag]
+ if data then
+ return data, fulltag
+ end
+ tag = v_text
+ end
+ if tag == v_text then
+ local fulltag = f_tag_two(tag,texgetcount("realpageno"))
+ return collected[fulltag] or false, fulltag
+ end
+ return collected[tag] or false, tag
+end
+
+function jobpositions.copy(target,source)
+ collected[target] = collected[source]
+end
+
+function jobpositions.replace(id,p,x,y,w,h,d)
+ collected[id] = { p = p, x = x, y = y, w = w, h = h, d = d } -- c g
+end
+
+local function getpage(id)
+ local jpi = collected[id]
+ return jpi and jpi.p
+end
+
+local function getcolumn(id)
+ local jpi = collected[id]
+ return jpi and jpi.c or false
+end
+
+local function getparagraph(id)
+ local jpi = collected[id]
+ return jpi and jpi.n
+end
+
+local function getregion(id)
+ local jpi = collected[id]
+ if jpi then
+ local r = jpi.r
+ if r then
+ return r
+ end
+ local p = jpi.p
+ if p then
+ return "page:" .. p
+ end
+ end
+ return false
+end
+
+jobpositions.page = getpage
+jobpositions.column = getcolumn
+jobpositions.paragraph = getparagraph
+jobpositions.region = getregion
+
+jobpositions.p = getpage -- not used, kind of obsolete
+jobpositions.c = getcolumn -- idem
+jobpositions.n = getparagraph -- idem
+jobpositions.r = getregion -- idem
+
+function jobpositions.x(id)
+ local jpi = collected[id]
+ return jpi and jpi.x
+end
+
+function jobpositions.y(id)
+ local jpi = collected[id]
+ return jpi and jpi.y
+end
+
+function jobpositions.width(id)
+ local jpi = collected[id]
+ return jpi and jpi.w
+end
+
+function jobpositions.height(id)
+ local jpi = collected[id]
+ return jpi and jpi.h
+end
+
+function jobpositions.depth(id)
+ local jpi = collected[id]
+ return jpi and jpi.d
+end
+
+function jobpositions.whd(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.h, jpi.h, jpi.d
+ end
+end
+
+function jobpositions.leftskip(id)
+ local jpi = collected[id]
+ return jpi and jpi.ls
+end
+
+function jobpositions.rightskip(id)
+ local jpi = collected[id]
+ return jpi and jpi.rs
+end
+
+function jobpositions.hsize(id)
+ local jpi = collected[id]
+ return jpi and jpi.hs
+end
+
+function jobpositions.parindent(id)
+ local jpi = collected[id]
+ return jpi and jpi.pi
+end
+
+function jobpositions.hangindent(id)
+ local jpi = collected[id]
+ return jpi and jpi.hi
+end
+
+function jobpositions.hangafter(id)
+ local jpi = collected[id]
+ return jpi and jpi.ha or 1
+end
+
+function jobpositions.xy(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.x, jpi.y
+ else
+ return 0, 0
+ end
+end
+
+function jobpositions.lowerleft(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.x, jpi.y - jpi.d
+ else
+ return 0, 0
+ end
+end
+
+function jobpositions.lowerright(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.x + jpi.w, jpi.y - jpi.d
+ else
+ return 0, 0
+ end
+end
+
+function jobpositions.upperright(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.x + jpi.w, jpi.y + jpi.h
+ else
+ return 0, 0
+ end
+end
+
+function jobpositions.upperleft(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.x, jpi.y + jpi.h
+ else
+ return 0, 0
+ end
+end
+
+function jobpositions.position(id)
+ local jpi = collected[id]
+ if jpi then
+ return jpi.p, jpi.x, jpi.y, jpi.w, jpi.h, jpi.d
+ else
+ return 0, 0, 0, 0, 0, 0
+ end
+end
+
+local splitter = lpeg.splitat(",")
+
+function jobpositions.extra(id,n,default) -- assume numbers
+ local jpi = collected[id]
+ if jpi then
+ local e = jpi.e
+ if e then
+ local split = jpi.split
+ if not split then
+ split = lpegmatch(splitter,jpi.e)
+ jpi.split = split
+ end
+ return texsp(split[n]) or default -- watch the texsp here
+ end
+ end
+ return default
+end
+
+local function overlapping(one,two,overlappingmargin) -- hm, strings so this is wrong .. texsp
+ one = collected[one]
+ two = collected[two]
+ if one and two and one.p == two.p then
+ if not overlappingmargin then
+ overlappingmargin = 2
+ end
+ local x_one = one.x
+ local x_two = two.x
+ local w_two = two.w
+ local llx_one = x_one - overlappingmargin
+ local urx_two = x_two + w_two + overlappingmargin
+ if llx_one > urx_two then
+ return false
+ end
+ local w_one = one.w
+ local urx_one = x_one + w_one + overlappingmargin
+ local llx_two = x_two - overlappingmargin
+ if urx_one < llx_two then
+ return false
+ end
+ local y_one = one.y
+ local y_two = two.y
+ local d_one = one.d
+ local h_two = two.h
+ local lly_one = y_one - d_one - overlappingmargin
+ local ury_two = y_two + h_two + overlappingmargin
+ if lly_one > ury_two then
+ return false
+ end
+ local h_one = one.h
+ local d_two = two.d
+ local ury_one = y_one + h_one + overlappingmargin
+ local lly_two = y_two - d_two - overlappingmargin
+ if ury_one < lly_two then
+ return false
+ end
+ return true
+ end
+end
+
+local function onsamepage(list,page)
+ for id in gmatch(list,"(, )") do
+ local jpi = collected[id]
+ if jpi then
+ local p = jpi.p
+ if not p then
+ return false
+ elseif not page then
+ page = p
+ elseif page ~= p then
+ return false
+ end
+ end
+ end
+ return page
+end
+
+local function columnofpos(realpage,xposition)
+ local p = columndata[realpage]
+ if p then
+ for i=1,#p do
+ local c = p[i]
+ local x = c.x or 0
+ local w = c.w or 0
+ if xposition >= x and xposition <= (x + w) then
+ return i
+ end
+ end
+ end
+ return 1
+end
+
+jobpositions.overlapping = overlapping
+jobpositions.onsamepage = onsamepage
+jobpositions.columnofpos = columnofpos
+
+-- interface
+
+implement {
+ name = "replacepospxywhd",
+ arguments = { "argument", "integerargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument", "dimenargument" },
+ public = true,
+ protected = true,
+ actions = function(name,page,x,y,w,h,d)
+ collected[name] = {
+ p = page,
+ x = x,
+ y = y,
+ w = w,
+ h = h,
+ d = d,
+ }
+ end
+}
+
+implement {
+ name = "copyposition",
+ arguments = "2 arguments",
+ public = true,
+ protected = true,
+ actions = function(target,source)
+ collected[target] = collected[source]
+ end
+}
+
+implement {
+ name = "MPp",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local p = jpi.p
+ if p and p ~= true then
+ context(p)
+ return
+ end
+ end
+ context('0')
+ end
+}
+
+implement {
+ name = "MPx",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local x = jpi.x
+ if x and x ~= true and x ~= 0 then
+ context("%.5Fpt",x*pt)
+ return
+ end
+ end
+ context('0pt')
+ end
+}
+
+implement {
+ name = "MPy",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local y = jpi.y
+ if y and y ~= true and y ~= 0 then
+ context("%.5Fpt",y*pt)
+ return
+ end
+ end
+ context('0pt')
+ end
+}
+
+implement {
+ name = "MPw",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local w = jpi.w
+ if w and w ~= 0 then
+ context("%.5Fpt",w*pt)
+ return
+ end
+ end
+ context('0pt')
+ end
+}
+
+implement {
+ name = "MPh",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local h = jpi.h
+ if h and h ~= 0 then
+ context("%.5Fpt",h*pt)
+ return
+ end
+ end
+ context('0pt')
+ end
+}
+
+implement {
+ name = "MPd",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local d = jpi.d
+ if d and d ~= 0 then
+ context("%.5Fpt",d*pt)
+ return
+ end
+ end
+ context('0pt')
+ end
+}
+
+implement {
+ name = "MPxy",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context('(%.5Fpt,%.5Fpt)',
+ jpi.x*pt,
+ jpi.y*pt
+ )
+ else
+ context('(0,0)')
+ end
+ end
+}
+
+implement {
+ name = "MPwhd",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local w = jpi.w or 0
+ local h = jpi.h or 0
+ local d = jpi.d or 0
+ if w ~= 0 or h ~= 0 or d ~= 0 then
+ context("%.5Fpt,%.5Fpt,%.5Fpt",w*pt,h*pt,d*pt)
+ return
+ end
+ end
+ context('0pt,0pt,0pt')
+ end
+}
+
+implement {
+ name = "MPll",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context('(%.5Fpt,%.5Fpt)',
+ jpi.x *pt,
+ (jpi.y-jpi.d)*pt
+ )
+ else
+ context('(0,0)') -- for mp only
+ end
+ end
+}
+
+implement {
+ name = "MPlr",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context('(%.5Fpt,%.5Fpt)',
+ (jpi.x + jpi.w)*pt,
+ (jpi.y - jpi.d)*pt
+ )
+ else
+ context('(0,0)') -- for mp only
+ end
+ end
+}
+
+implement {
+ name = "MPur",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context('(%.5Fpt,%.5Fpt)',
+ (jpi.x + jpi.w)*pt,
+ (jpi.y + jpi.h)*pt
+ )
+ else
+ context('(0,0)') -- for mp only
+ end
+ end
+}
+
+implement {
+ name = "MPul",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context('(%.5Fpt,%.5Fpt)',
+ jpi.x *pt,
+ (jpi.y + jpi.h)*pt
+ )
+ else
+ context('(0,0)') -- for mp only
+ end
+ end
+}
+
+local function MPpos(id)
+ local jpi = collected[id]
+ if jpi then
+ local p = jpi.p
+ if p then
+ context("%s,%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt",
+ p,
+ jpi.x*pt,
+ jpi.y*pt,
+ jpi.w*pt,
+ jpi.h*pt,
+ jpi.d*pt
+ )
+ return
+ end
+ end
+ context('0,0,0,0,0,0') -- for mp only
+end
+
+implement {
+ name = "MPpos",
+ arguments = "argument",
+ public = true,
+ actions = MPpos
+}
+
+implement {
+ name = "MPn",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local n = jpi.n
+ if n then
+ context(n)
+ return
+ end
+ end
+ context(0)
+ end
+}
+
+implement {
+ name = "MPc",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local c = jpi.c
+ if c and c ~= true then
+ context(c)
+ return
+ end
+ end
+ context('0') -- okay ?
+ end
+}
+
+implement {
+ name = "MPr",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ local r = jpi.r
+ if r and r ~= true then
+ context(r)
+ return
+ end
+ local p = jpi.p
+ if p and p ~= true then
+ context("page:" .. p)
+ end
+ end
+ end
+}
+
+local function MPpardata(id)
+ local t = collected[id]
+ if not t then
+ local tag = f_p_tag(id)
+ t = collected[tag]
+ end
+ if t then
+ context("%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt,%s,%.5Fpt",
+ t.hs*pt,
+ t.ls*pt,
+ t.rs*pt,
+ t.hi*pt,
+ t.ha,
+ t.pi*pt
+ )
+ else
+ context("0,0,0,0,0,0") -- for mp only
+ end
+end
+
+implement {
+ name = "MPpardata",
+ arguments = "argument",
+ public = true,
+ actions = MPpardata
+}
+
+implement {
+ name = "MPposset",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local b = f_b_tag(name)
+ local e = f_e_tag(name)
+ local w = f_w_tag(name)
+ local p = f_p_tag(getparagraph(b))
+ MPpos(b) context(",") MPpos(e) context(",") MPpos(w) context(",") MPpos(p) context(",") MPpardata(p)
+ end
+}
+
+implement {
+ name = "MPls",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context("%.5Fpt",jpi.ls*pt)
+ else
+ context("0pt")
+ end
+ end
+}
+
+implement {
+ name = "MPrs",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context("%.5Fpt",jpi.rs*pt)
+ else
+ context("0pt")
+ end
+ end
+}
+
+local splitter = lpeg.tsplitat(",")
+
+implement {
+ name = "MPplus",
+ arguments = { "argument", "integerargument", "argument" },
+ public = true,
+ actions = function(name,n,default)
+ local jpi = collected[name]
+ if jpi then
+ local e = jpi.e
+ if e then
+ local split = jpi.split
+ if not split then
+ split = lpegmatch(splitter,jpi.e)
+ jpi.split = split
+ end
+ context(split[n] or default)
+ return
+ end
+ end
+ context(default)
+ end
+}
+
+implement {
+ name = "MPrest",
+ arguments = { "argument", "argument" },
+ public = true,
+ actions = function(name,default)
+ local jpi = collected[name]
+ context(jpi and jpi.e or default)
+ end
+}
+
+implement {
+ name = "MPxywhd",
+ arguments = "argument",
+ public = true,
+ actions = function(name)
+ local jpi = collected[name]
+ if jpi then
+ context("%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt,%.5Fpt",
+ jpi.x*pt,
+ jpi.y*pt,
+ jpi.w*pt,
+ jpi.h*pt,
+ jpi.d*pt
+ )
+ else
+ context("0,0,0,0,0") -- for mp only
+ end
+ end
+}
+
+local doif = commands.doif
+local doifelse = commands.doifelse
+
+implement {
+ name = "doifelseposition",
+ arguments = "argument",
+ public = true,
+ protected = true,
+ actions = function(name)
+ doifelse(collected[name])
+ end
+}
+
+implement {
+ name = "doifposition",
+ arguments = "argument",
+ public = true,
+ protected = true,
+ actions = function(name)
+ doif(collected[name])
+ end
+}
+
+implement {
+ name = "doifelsepositiononpage",
+ arguments = { "string", "integerargument" },
+ public = true,
+ protected = true,
+ actions = function(name,p)
+ local c = collected[name]
+ doifelse(c and c.p == p)
+ end
+}
+
+implement {
+ name = "doifelseoverlapping",
+ arguments = { "argument", "argument" },
+ public = true,
+ protected = true,
+ actions = function(one,two)
+ doifelse(overlapping(one,two))
+ end
+}
+
+implement {
+ name = "doifelsepositionsonsamepage",
+ arguments = "argument", -- string
+ public = true,
+ protected = true,
+ actions = function(list)
+ doifelse(onsamepage(list))
+ end
+}
+
+implement {
+ name = "doifelsepositionsonthispage",
+ arguments = "argument", -- string
+ public = true,
+ protected = true,
+ actions = function(list)
+ doifelse(onsamepage(list,tostring(texgetcount("realpageno"))))
+ end
+}
+
+implement {
+ name = "doifelsepositionsused",
+ public = true,
+ protected = true,
+ actions = function()
+ doifelse(next(collected))
+ end
+}
+
+implement {
+ name = "markregionbox",
+ arguments = "integer",
+ actions = markregionbox
+}
+
+implement {
+ name = "setregionbox",
+ arguments = "integer",
+ actions = setregionbox
+}
+
+implement {
+ name = "markregionboxtagged",
+ arguments = { "integer", "string" },
+ actions = markregionbox
+}
+
+implement {
+ name = "markregionboxtaggedn",
+ arguments = { "integer", "string", "integer" },
+ actions = function(box,tag,n)
+ markregionbox(box,tag,nil,nil,nil,nil,nil,nil,n)
+ end
+}
+
+implement {
+ name = "setregionboxtagged",
+ arguments = { "integer", "string" },
+ actions = setregionbox
+}
+
+implement {
+ name = "markregionboxcorrected",
+ arguments = { "integer", "string", true },
+ actions = markregionbox
+}
+
+implement {
+ name = "markregionboxtaggedkind",
+ arguments = { "integer", "string", "integer", "dimen", "dimen", "dimen", "dimen" },
+ actions = function(box,tag,n,d1,d2,d3,d4)
+ markregionbox(box,tag,nil,n,d1,d2,d3,d4)
+ end
+}
+
+implement {
+ name = "reservedautoregiontag",
+ public = true,
+ actions = function()
+ nofregions = nofregions + 1
+ context(f_region(nofregions))
+ end
+}
+
+-- statistics (at least for the moment, when testing)
+
+-- statistics.register("positions", function()
+-- local total = nofregular + nofusedregions + nofmissingregions
+-- if total > 0 then
+-- return format("%s collected, %s regulars, %s regions, %s unresolved regions",
+-- total, nofregular, nofusedregions, nofmissingregions)
+-- else
+-- return nil
+-- end
+-- end)
+
+statistics.register("positions", function()
+ local total = nofregular + nofspecial
+ if total > 0 then
+ return format("%s collected, %s regular, %s special",total,nofregular,nofspecial)
+ else
+ return nil
+ end
+end)
+
+-- We support the low level positional commands too:
+
+local newsavepos = nodes.pool.savepos
+
+jobpositions.lastx = 0
+jobpositions.lasty = 0
+
+implement { name = "savepos", actions = function() context(newsavepos()) end }
+implement { name = "lastxpos", actions = function() context(jobpositions.lastx) end }
+implement { name = "lastypos", actions = function() context(jobpositions.lasty) end }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-pos.mkxl b/Master/texmf-dist/tex/context/base/mkxl/anch-pos.mkxl
new file mode 100644
index 00000000000..d217f2cac3f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-pos.mkxl
@@ -0,0 +1,506 @@
+%D \module
+%D [ file=anch-pos, % was core-pos
+%D version=1999.08.01,
+%D title=\CONTEXT\ Anchoring Macros,
+%D subtitle=Positioning Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Anchoring Macros / Positioning}
+
+%D In \MKIV\ there was already a different housekeeping model for positions quite
+%D early, but starting in 2012 more dramatic changes started to happen, especially
+%D in relation to background graphics. It will probably take some time to settle.
+
+\registerctxluafile{anch-pos}{autosuffix}
+
+\unprotect
+
+%D The first application of positional information was embedded graphics. Since we
+%D are interacting with text, it made sense to take the current line height and
+%D depth into account too. This is why we have position macros for simple positions
+%D and one boxes.
+%D
+%D \starttyping
+%D \dosetposition {identifier}
+%D \dosetpositionwhd {identifier} {width} {height} {depth}
+%D \dosetpositionplus {identifier} {width} {height} {depth} {list}
+%D \stoptyping
+
+% \dosaveposition #1#2#3#4 % defined at lua end
+% \dosavepositionwhd #1#2#3#4#5#6#7 % defined at lua end
+% \dosavepositionplus #1#2#3#4#5#6#7#8 % defined at lua end
+
+% \dosetposition #1 % defined at lua end
+% \dosetpositionwhd #1#2#3#4 % defined at lua end
+% \dosetpositionplus #1#2#3#4#5 % defined at lua end
+% \dosetpositionbox #1#2 % defined at lua end
+% \dosetpositionstrut #1 % defined at lua end
+% \dosetpositionstrutkind #1#2 % defined at lua end
+
+\newbox\b_anch_position
+\newif \ifpositioning % sort of public
+
+\mutable\let\currentposition \empty
+\mutable\let\currentpositionaction\empty
+\mutable\let\currentpositionanchor\empty
+\mutable\let\currentpositionregion\empty
+
+%D Sometimes we want to trick the position handler a bit:
+
+% \replacepospxywhd #1#2#3#4#5#6#7 % defined at lua end
+
+%D \macros
+%D {MPp, MPx, MPy, MPw, MPh, MPd, MPxy, MPll, MPlr, MPur, MPul, MPpos, MPanchor}
+%D
+%D Access to the positional information is provided by macros with short names
+%S that are clearly meant for \METAPOST\ but nowadays also used for other purposes.
+
+% \MPp : defined at lua end
+% \MPr : defined at lua end
+% \MPc : defined at lua end
+% \MPn : defined at lua end
+% \MPx : defined at lua end
+% \MPy : defined at lua end
+% \MPw : defined at lua end
+% \MPh : defined at lua end
+% \MPd : defined at lua end
+% \MPxy : defined at lua end
+% \MPwhd : defined at lua end
+% \MPll : defined at lua end
+% \MPlr : defined at lua end
+% \MPur : defined at lua end
+% \MPul : defined at lua end
+% \MPpos : defined at lua end
+% \MPls : defined at lua end
+% \MPrs : defined at lua end
+% \MPpardata : defined at lua end
+% \MPxywhd : defined at lua end
+% \MPposset : defined at lua end
+
+\aliased\let\MPpage \MPp
+\aliased\let\MPregion \MPr
+\aliased\let\MPcolumn \MPc
+\aliased\let\MPparagraph\MPn
+\aliased\let\MPanchor \MPpos % overloaded locally when needed (todo: LMTX)
+
+\aliased\let\MPleftskip \MPls % compatible feature
+\aliased\let\MPrightkip \MPrs % compatible feature
+
+%D \macros
+%D {MPplus, MPrest, MPv, MPvv}
+%D
+%D Since we will probably keep on extending, we provide a general extension
+%D macro. The plus alternative takes an extra argument, denoting what additional
+%D parameter to pick up. So, the third extra is fetched with,
+%D
+%D \starttyping
+%D \MPplus{identifier}{3}{default}
+%D \stoptyping
+%D
+%D All extras (comma separated) are fetched with:
+%D
+%D \starttyping
+%D \MPrest{identifier}
+%D \stoptyping
+%D
+%D The extra parameters are not treated.
+
+% \MPplus #1#2#3 % defined at lua end
+% \MPrest #1 % defined at lua end
+
+\aliased\let\MPv \MPplus
+\aliased\let\MPvv\MPrest
+
+%D There are two low level positioning macros. Both store the position as well
+%D as execute an action associated with that position.
+
+\let\dopositionaction\gobbleoneargument % implemented later
+
+\def\anch_positions_initialize
+ {\ifpositioning \else
+ \global\positioningtrue
+ \fi}
+
+\permanent\protected\def\setpositiononly
+ {\iftrialtypesetting
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\anch_positions_set_only_indeed
+ \fi}
+
+\def\anch_positions_set_only_indeed#1%
+ {\anch_positions_initialize
+ \edef\currentposition{#1}%
+ \dosetposition\currentposition}
+
+\permanent\protected\def\setposition
+ {\iftrialtypesetting
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\anch_positions_set_indeed
+ \fi}
+
+\def\anch_positions_set_indeed#1%
+ {\anch_positions_initialize
+ \edef\currentposition{#1}%
+ \dosetposition\currentposition
+ \anch_positions_trace_left
+ \dopositionaction\currentposition}
+
+\permanent\protected\def\setpositiondata
+ {\iftrialtypesetting
+ \expandafter\gobblefourarguments
+ \else
+ \expandafter\anch_positions_set_data_indeed
+ \fi}
+
+\def\anch_positions_set_data_indeed#1#2#3#4%
+ {\anch_positions_initialize
+ \hbox % \hpack
+ {\edef\currentposition{#1}%
+ \dosetpositionwhd\currentposition{#2}{#3}{#4}% already \the\dimexpr
+ \anch_positions_trace_left
+ \dopositionaction\currentposition
+ \hss}}
+
+\permanent\protected\def\setpositionbox
+ {\iftrialtypesetting
+ \expandafter\anch_positions_set_box_nop
+ \else
+ \expandafter\anch_positions_set_box_yes
+ \fi}
+
+\def\anch_positions_set_box_nop#1%
+ {\dowithnextboxcs\flushnextbox}
+
+\def\anch_positions_set_box_yes#1%
+ {\dowithnextbox{\anch_positions_set_box_finish{#1}}}
+
+\def\anch_positions_set_box_finish#1%
+ {\anch_positions_initialize
+ \hbox to \wd\nextbox % \hpack
+ {\edef\currentposition{#1}%
+ \dosetpositionbox\currentposition\nextbox
+ \anch_positions_trace_left
+ \setbox\b_anch_position\box\nextbox
+ \dopositionaction\currentposition
+ \box\b_anch_position
+ \hss}}
+
+\permanent\protected\def\setpositionstrut
+ {\iftrialtypesetting
+ \expandafter\anch_positions_set_strut_nop
+ \else
+ \expandafter\anch_positions_set_strut_yes
+ \fi}
+
+\def\anch_positions_set_strut_nop#1%
+ {\strut}
+
+\def\anch_positions_set_strut_yes#1%
+ {\anch_positions_initialize
+ \hbox to \zeropoint % \hpack
+ {\edef\currentposition{#1}%
+ \dosetpositionstrut\currentposition
+ \anch_positions_trace_left
+ \dopositionaction\currentposition
+ \strut
+ \hss}}
+
+\permanent\protected\def\setpositionstrutkind
+ {\iftrialtypesetting
+ \expandafter\anch_positions_set_strut_kind_nop
+ \else
+ \expandafter\anch_positions_set_strut_kind_yes
+ \fi}
+
+\def\anch_positions_set_strut_kind_yes#1#2%
+ {\anch_positions_initialize
+ \hbox to \zeropoint % \hpack
+ {\edef\currentposition{#1}%
+ \dosetpositionstrutkind\currentposition{#2}%
+ \anch_positions_trace_left
+ \dopositionaction\currentposition
+ \strut
+ \hss}}
+
+\def\anch_positions_set_strut_kind_nop#1#2%
+ {\strut}
+
+\permanent\protected\def\setpositiondataplus
+ {\iftrialtypesetting
+ \expandafter\gobblefivearguments
+ \else
+ \expandafter\anch_positions_set_plus_indeed
+ \fi}
+
+\def\anch_positions_set_plus_indeed#1#2#3#4#5%
+ {\anch_positions_initialize
+ \hbox % \hpack
+ {\edef\currentposition{#1}%
+ \dosetpositionplus\currentposition{#2}{#3}{#4}{#5}% already \the\dimexpr
+ \anch_positions_trace_right
+ \dopositionaction\currentposition
+ \hss}}
+
+\permanent\protected\def\setpositionplus
+ {\iftrialtypesetting
+ \expandafter\anch_positions_set_plus_nop
+ \else
+ \expandafter\anch_positions_set_plus_yes
+ \fi}
+
+\def\anch_positions_set_plus_nop#1#2%
+ {\dowithnextboxcs\flushnextbox}
+
+\def\anch_positions_set_plus_yes#1#2%
+ {\dowithnextbox{\anch_positions_set_plus_yes_finish{#1}{#2}}}
+
+\def\anch_positions_set_plus_yes_finish#1#2%
+ {\anch_positions_initialize
+ \hbox to \nextboxwd % \hpack
+ {\edef\currentposition{#1}%
+ \dosetpositionplus\currentposition{\wd\nextbox}{\ht\nextbox}{\dp\nextbox}{#2}%
+ \anch_positions_trace_right
+ \setbox\b_anch_position\flushnextbox
+ \dopositionaction\currentposition
+ \box\b_anch_position
+ \hss}}
+
+\let\currentposition\s!unknown
+
+%D A few special ones .. will be cleaned up
+
+\permanent\def\pageanchor {page:\the\realpageno} % for the moment only one pagesize
+\permanent\def\textanchor {text:\the\realpageno}
+\permanent\def\regionanchor{region:0}
+
+\newcount\c_anch_column % will be delegated to lua
+\newcount\c_anch_text % will be delegated to lua
+
+% beware we need to pass \somethingexpanded or { }
+
+\protected\def\anch_mark_column_box#1#2% box n
+ {\global\advance\c_anch_column\plusone
+ \clf_markregionboxtaggedn#1{columnarea:\the\c_anch_column}#2\relax} % extra height
+
+\protected\def\anch_mark_region_box
+ {\iftrialtypesetting
+ \expandafter\gobbleoneargument
+ \orelse\ifpositioning
+ \expandafter\anch_mark_region_box_indeed
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\protected\def\anch_mark_region_box_indeed#1%
+ {\clf_markregionbox#1\relax}
+
+\protected\def\anch_mark_flow_box#1% will be extended / renamed
+ {\hpack\bgroup
+ \global\advance\c_anch_text\plusone
+ \clf_markregionboxtagged#1{textarea:\the\c_anch_text}%
+ \box#1%
+ \egroup}
+
+\protected\def\anch_mark_tagged_box#1#2%
+ {\clf_markregionboxtagged#1{#2}}
+
+\protected\def\anch_mark_flow_only#1% will be extended / renamed
+ {\global\advance\c_anch_text\plusone
+ \clf_markregionboxcorrected#1{textarea:\the\c_anch_text}}
+
+\protected\def\anch_make_page_box#1% maybe like text
+ {\clf_setregionboxtagged#1{page:\the\realpageno}}
+
+\protected\def\anch_mark_text_box#1%
+ {\clf_markregionboxtagged#1{text:\the\realpageno}} % needs an hbox
+
+\newcount\c_anch_free
+
+\protected\def\anch_mark_tagged_box_free#1#2#3#4#5#6% only needed when positions
+ {\ifpositioning
+ \global\advance\c_anch_free\plusone % could be done at the lua end
+ \clf_markregionboxtaggedkind
+ #1%
+ {free:\number\c_anch_free}%
+ #2\space % kind
+ #3\space % leftoffset
+ #4\space % rightoffset
+ #5\space % topoffset
+ #6\relax % bottomoffset
+ \fi}
+
+% \reservedautoregiontag % define at lua end
+
+%D We can copy a position with:
+%D
+%D \starttyping
+%D \copyposition {to} {from}
+%D \stoptyping
+%D
+%D Again, this is a global operation.
+
+% \copyposition #1#2 % defined at lua end
+
+%D The fact that handling positions is a two pass operation, is one of the
+%D reasons why we need to be able to test for existence, using:
+%D
+%D \starttyping
+%D \doifpositionelse {identifier} {found action} {not found action}
+%D \stoptyping
+
+% \doifposition #1 % defined at lua end
+% \doifelseposition #1#2 % defined at lua end
+% \doifelsepositiononpage #1#2 % defined at lua end
+
+\aliased\let\doifpositionelse \doifelseposition
+\aliased\let\doifpositiononpageelse\doifelsepositiononpage
+
+%D \macros
+%D {xypos}
+%D
+%D We have several macros available to save positions. Later we will see
+%D applications.
+%D
+%D \starttabulate[|l|l||]
+%D \NC \type {\xypos} \NC \NC simple position with no dimensions \NC \NR
+%D \NC \type {\hpos} \NC \NC position and characteristics of a \type {\hbox} \NC \NR
+%D \NC \type {\vpos} \NC \NC position and characteristics of a \type {\vbox} \NC \NR
+%D \NC \type {\bpos} \NC b: \NC begin point in a line \NC \NR
+%D \NC \type {\epos} \NC e: \NC end point in a line \NC \NR
+%D \stoptabulate
+%D
+%D Each macro takes an identifier as argument, and the \type {\hpos} and
+%D \type {\vpos} also expect box content.
+
+\aliased\let\xypos\setpositiononly
+
+\permanent\protected\def\hpos #1{\dontleavehmode\setpositionbox{#1}\hbox}
+\permanent\protected\def\vpos #1{\setpositionbox{#1}\vbox}
+\permanent\protected\def\bpos #1{\dontleavehmode\setpositionstrut{b:#1}\ignorespaces}
+\permanent\protected\def\epos #1{\removeunwantedspaces\setpositionstrut{e:#1}}
+\permanent\protected\def\bposkind#1#2{\dontleavehmode\setpositionstrutkind{b:#1}{#2}\ignorespaces} % not public, used in backgrounds
+\permanent\protected\def\eposkind#1#2{\removeunwantedspaces\setpositionstrutkind{e:#1}{#2}} % not public, used in backgrounds
+
+%D When we want to calculate more complex backgrounds, we need to know what the
+%D current indentation scheme is. At the cost of many positions and memory, we
+%D can keep track of them. This mechanism is activated automatically based on
+%D information collected in the previous pass.
+
+\newtoks \t_anch_positions_tracers
+\newcount\c_anch_positions_paragraph
+
+\permanent\protected\def\tracepositions
+ {\the\t_anch_positions_tracers}
+
+\permanent\protected\def\enableparpositions % global
+ {\enforced\aliased\glet\registerparoptions\doregisterparoptions
+ \global\positioningtrue}
+
+\permanent\let\disableparpositions\relax
+
+\aliased\let\registerparoptions\relax
+
+\permanent\protected\def\doregisterparoptions
+ {\iftrialtypesetting \orelse\ifinpagebody \orelse\ifmmode \orelse\ifinformula \else
+ \anch_positions_register_par_options
+ \fi}
+
+\def\anch_positions_register_par_options_normal
+ {\dontleavehmode\clf_parpos}
+
+\def\anch_positions_register_par_options_traced
+ {\anch_positions_register_par_options_normal
+ \smashedhbox to \zeropoint
+ {\hss
+ \startcolor[blue]%
+ \llap{\infofont\number\c_anch_positions_paragraph}%
+ \vrule
+ \s!width 4\onepoint
+ \s!height2\onepoint
+ \s!depth 2\onepoint
+ \stopcolor
+ \hss}}
+
+\let\anch_positions_register_par_options\anch_positions_register_par_options_normal
+
+\appendtoks
+ \let\anch_positions_register_par_options\anch_positions_register_par_options_traced
+\to \t_anch_positions_tracers
+
+\protected\def\anch_positions_trace#1#2#3%
+ {\smashedhbox
+ {#1{\infofont#2#3}%
+ \kern-\onepoint
+ \vrule\s!width2\onepoint\s!height\halfapoint\s!depth\halfapoint}}
+
+\protected\def\anch_positions_trace_left_indeed
+ {\anch_positions_trace\llap\darkmagenta{\currentposition>}}
+
+\protected\def\anch_positions_trace_right_indeed
+ {\anch_positions_trace\rlap\darkcyan{<\currentposition}}
+
+\let\anch_positions_trace_left \relax
+\let\anch_positions_trace_right\relax
+
+\appendtoks
+ \let\anch_positions_trace_left \anch_positions_trace_left_indeed
+ \let\anch_positions_trace_right \anch_positions_trace_right_indeed
+\to \t_anch_positions_tracers
+
+% \appendtoks \registerparoptions \to \everypar
+
+%D \macros
+%D {doifoverlappingelse}
+%D
+%D A first application of positional information, is to determine if two boxes do
+%D overlap:
+%D
+%D \starttyping
+%D \doifoverlappingelse{point a}{point b}
+%D {action when overlapping}
+%D {action when not overlapping}
+%D \stoptyping
+
+% \doifelseoverlapping #1#2#3#4 % defined at lua end
+
+\aliased\let\doifoverlappingelse\doifelseoverlapping
+
+%D \macros
+%D {doifpositionsonsamepageelse,
+%D doifpositionsonthispageelse}
+%D
+%D Instead of letting the user handle fuzzy expansion, we provide a simple test on
+%D positions being on the same page.
+%D
+%D \starttyping
+%D \doifpositionsonsamepageelse{point a}{point b}
+%D {action when on same page}
+%D {action when not on same page}
+%D \doifpositionsonthispageelse{point a}{point b}
+%D {action when on this page}
+%D {action when not on this page}
+%D \stoptyping
+
+% \doifelsepositionsonsamepage #1 % defined at lua end
+% \doifelsepositionsonthispage #1 % defined at lua end
+% \doifelsepositionsused % defined at lua end
+
+\aliased\let\doifpositionsonsamepageelse\doifelsepositionsonsamepage
+\aliased\let\doifpositionsonthispageelse\doifelsepositionsonthispage
+\aliased\let\doifpositionsusedelse \doifelsepositionsused
+
+%D Moved here:
+
+\permanent\protected\def\savepos {\clf_savepos}
+\permanent\protected\def\lastxpos{\numexpr\clf_lastxpos\relax}
+\permanent\protected\def\lastypos{\numexpr\clf_lastypos\relax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-snc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/anch-snc.mkxl
new file mode 100644
index 00000000000..e186e024f96
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-snc.mkxl
@@ -0,0 +1,157 @@
+%D \module
+%D [ file=anch-snc,
+%D version=2003.12.01, % actually 1999 so real old
+%D title=\CONTEXT\ Anchoring Macros,
+%D subtitle=Synchronization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The original is in the mkii file. It does more at the \TEX\ end and
+%D has some more magic. If we really need that I'll add it. After all,
+%D in mkiv we can do things different.
+
+%D TODO: bleed : + left , minus right oro check if it touches page ... autobleed
+
+\writestatus{loading}{ConTeXt Anchoring Macros / Synchronization}
+
+\registerctxluafile{anch-snc}{}
+
+\unprotect
+
+\ifdefined\??syncposcounter \else \installcorenamespace{syncposcounter} \fi
+\ifdefined\s!syncpos \else \def\s!syncpos {syncpos} \fi
+
+\let\c_sync_n\relax
+
+\permanent\protected\def\definesyncpositions[#1]%
+ {\ifcsname\??syncposcounter:#1\endcsname \else
+ \expandafter\newcount\csname\??syncposcounter:#1\endcsname
+ \fi}
+
+\permanent\tolerant\protected\def\syncposition[#1]#*[#2]% we could actually use par positions
+ {\dontleavehmode
+ \ifcsname\??syncposcounter:#1\endcsname
+ \let\c_sync_n\lastnamedcs
+ \global\advance\c_sync_n\plusone
+ \enabletextarearegistration
+ \setpositionplus{\s!syncpos:#1:\the\c_sync_n}{#2}\hpack{\strut}%
+ \else
+ \strut
+ \fi
+ \ignorespaces}
+
+\protect
+
+\continueifinputfile{anch-snc.mkiv}
+
+\starttext
+
+% \setuppapersize[A4][A3]
+
+\setuplayout[location=middle]
+
+\setupbodyfont[dejavu]
+
+\definesyncpositions[1]
+\definesyncpositions[2]
+
+% \enabletrackers[metapost.lua]
+
+\startMPdefinitions
+ input "mp-asnc.mpiv" ;
+
+ SetSyncColor(1,0,"magenta") ;
+ SetSyncColor(1,1,"red") ;
+ SetSyncColor(1,2,"green") ;
+ SetSyncColor(1,3,"blue") ;
+ SetSyncColor(1,4,"yellow") ;
+
+ SetSyncColor(2,0,"magenta") ;
+ SetSyncColor(2,1,"red") ;
+ SetSyncColor(2,2,"green") ;
+ SetSyncColor(2,3,"blue") ;
+ SetSyncColor(2,4,"yellow") ;
+\stopMPdefinitions
+
+\startuseMPgraphic{sync1}
+ StartPage ;
+ StartSync(1) ;
+ SyncHOffset := 0 ;
+ SyncWidth := BackSpace - LeftMarginDistance;
+ CollectSyncDataPage ;
+ % ExtendSyncPaths ; % to top of text area
+ PruneSyncPaths ; % clip top / bottom
+ CollapseSyncPaths ;
+ MakeSyncPaths ;
+ % DrawSyncPaths ;
+ FillSyncPaths ;
+ StopSync ;
+ clip currentpicture to Page ;
+ setbounds currentpicture to Page ;
+ StopPage ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{sync2}
+ StartSync(2) ;
+ SyncHOffset := -1cm ;
+ SyncWidth := 1cm ;
+ CollectSyncDataRegion(OverlayRegion) ;
+ ExtendSyncPaths ; % to top of text area
+ MakeSyncPaths ;
+ FillSyncPaths ;
+ StopSync ;
+ clip currentpicture to OverlayBox leftenlarged 1cm;
+ setbounds currentpicture to OverlayBox ;
+\stopuseMPgraphic
+
+\defineoverlay[tempoverlay1][\useMPgraphic{sync1}]
+\defineoverlay[tempoverlay2][\useMPgraphic{sync2}]
+
+\setupbackgrounds[page][background=tempoverlay1]
+
+\framed[region=yes,background=tempoverlay2,width=14cm,align=normal]{
+ \syncposition[2][1]\samplefile{ward}\endgraf
+ \syncposition[2][2]\samplefile{ward}\endgraf
+ \syncposition[2][3]\samplefile{ward}\endgraf
+}
+
+
+\vskip1cm \hskip1cm \framed[region=yes,background=tempoverlay2,width=16cm,align=normal]{
+ \syncposition[2][1]\samplefile{ward}\endgraf
+ \syncposition[2][2]\samplefile{ward}\endgraf
+ \syncposition[2][3]\samplefile{ward}\endgraf
+}
+
+\vskip1cm \hskip1cm \framed[region=yes,background=tempoverlay2,width=10cm,align=normal]{
+ \syncposition[2][1]\samplefile{ward}\endgraf
+ \syncposition[2][2]\samplefile{ward}\endgraf
+ \syncposition[2][3]\samplefile{ward}\endgraf
+}
+
+
+\dorecurse {100} {
+% \dorecurse {1} {
+ \startchapter[title={Test #1}]
+ \syncposition[1][1,reset]\dorecurse{20}{\samplefile{ward}\endgraf}
+ \syncposition[1][2]\dorecurse {4}{\samplefile{ward}\endgraf}
+ \syncposition[1][3]\dorecurse {7}{\samplefile{ward}\endgraf}
+ \syncposition[1][4]\dorecurse {3}{\samplefile{ward}\endgraf}
+ \stopchapter
+}
+
+\dorecurse {100} {
+% \dorecurse {1} {
+ \startchapter[title={Test #1}]
+ \syncposition[1][1]\dorecurse{1}{\samplefile{ward}\endgraf}
+ \syncposition[1][2]\dorecurse{1}{\samplefile{ward}\endgraf}
+ \syncposition[1][3]\dorecurse{1}{\samplefile{ward}\endgraf}
+ \syncposition[1][4]\dorecurse{1}{\samplefile{ward}\endgraf}
+ \stopchapter
+}
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/anch-tab.mkxl b/Master/texmf-dist/tex/context/base/mkxl/anch-tab.mkxl
new file mode 100644
index 00000000000..11e9598d9c2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/anch-tab.mkxl
@@ -0,0 +1,387 @@
+%D \module
+%D [ file=anch-pgr, % moved from anch-pgr (1999.08.01)
+%D version=2011.12.19, % 1999.08.01,
+%D title=\CONTEXT\ Anchoring Macros,
+%D subtitle=Table Extensions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Anchoring Macros / Table Extensions}
+
+%D This is just a playground and functionality might change or even dissappear in
+%D favour of better solutions. Anyway, it now has been aroudn too long to bother.
+
+\unprotect
+
+\newcount\c_anch_tabs
+
+\protected\def\tabl_tabulate_hook_b
+ {\iftrialtypesetting\else
+ \ifpositioning
+ \ifconditional\tablehaspositions
+ \ifcase\c_tabl_tabulate_noflines % \ifnum\c_tabl_tabulate_noflines=\c_tabl_tabulate_totalnoflines
+ \tabl_tabulate_hook_b_first
+ \else
+ \tabl_tabulate_hook_b_next
+ \fi
+ \fi
+ \fi
+ \fi}
+
+\protected\def\tabl_tabulate_hook_e
+ {\iftrialtypesetting\else
+ \ifpositioning
+ \ifconditional\tablehaspositions
+ \ifcase\c_tabl_tabulate_noflines % \ifnum\c_tabl_tabulate_noflines=\c_tabl_tabulate_totalnoflines
+ \tabl_tabulate_hook_e_first
+ \else
+ \tabl_tabulate_hook_e_next
+ \fi
+ \fi
+ \fi
+ \fi}
+
+\protected\def\tabl_tabulate_hook_b_first{\clf_bposcolumnregistered{tabulate:\the\c_anch_tabs:\the\c_tabl_tabulate_column}}
+\protected\def\tabl_tabulate_hook_b_next {\clf_bposcolumn{tabulate:\the\c_anch_tabs:\the\c_tabl_tabulate_column}}
+\protected\def\tabl_tabulate_hook_e_first{\clf_eposcolumnregistered}
+\protected\def\tabl_tabulate_hook_e_next {\clf_eposcolumn}
+
+% \appendtoks \registerparoptions \to \everypar
+
+\appendtoks
+ \global\advance\c_anch_tabs\plusone
+\to \everytabulate
+
+% for text backgrounds
+
+\appendtoks
+ \settrue\c_anch_backgrounds_pos_no_shape
+\to \everytabulate
+
+%D Beware, the following code is somewhat weird and experimental and might be
+%D dropped or become a loadable module.
+
+%D \macros
+%D {GFC, GTC, GSC}
+%D
+%D The next macros extend tables and tabulation with backgrounds and position
+%D related features. Areas are specified with symbolic names, and symbolic
+%D references to the graphics involved. Each table has its own namespace.
+
+\newconditional\tablehaspositions
+\newcount \noftabpositions
+\newtoks \posXCtoks
+
+\protected\def\anch_tabulate_bpos{\bpos}
+\protected\def\anch_tabulate_epos{\epos}
+
+\installcorenamespace{positiontables}
+
+\permanent\def\tbPOSprefix{\??positiontables\number\noftabpositions:}
+
+\permanent\protected\def\tablepos
+ {\normalexpanded{\global\posXCtoks\emptytoks\the\posXCtoks}}
+
+\permanent\protected\def\tbXC {\anch_table_checked\anch_tables_indeed_XC }
+\permanent\protected\def\tbGSC{\anch_table_checked\anch_tables_indeed_GSC}
+\permanent\protected\def\tbGFC{\anch_table_checked\anch_tables_indeed_GFC}
+\permanent\protected\def\tbGTC{\anch_table_checked\anch_tables_indeed_GTC}
+
+\tolerant\def\anch_table_checked#1[#2]%
+ {\iftrialtypesetting
+ \global\settrue\tablehaspositions
+ \expandafter\anch_tables_indeed_NC
+ \orelse\ifparameter#2\or
+ \expandafter#1%
+ \else
+ \expandafter\anch_tables_indeed_NC
+ \fi[#2]}
+
+\def\anch_tables_indeed_NC[#1]%
+ {\NC}
+
+\def\anch_tables_indeed_XC[#1]%
+ {{\enforced\let\NC\relax\processcommalist[#1]\anch_tables_step_XC}}
+
+\def\anch_tables_step_XC#1%
+ {\anch_tables_step_indeed_XC[#1]}
+
+\def\anch_tables_step_indeed_XC[#1#2]%
+ {\if#1>\anch_tables_indeed_GFC[#2:#2]\orelse
+ \if#1+\anch_tables_indeed_GFC[#2:#2]\orelse
+ \if#1<\anch_tables_indeed_GTC[#2:#2]\orelse
+ \if#1-\anch_tables_indeed_GTC[#2:#2]\orelse
+ \if#1=\anch_tables_indeed_GSC[#2:#2]\orelse
+ \anch_tables_indeed_GSC[#1#2:#1#2]\fi}
+
+\def\anch_tables_indeed_GSC[#1]%
+ {\processcommalist[#1]\anch_tables_step_GSC}
+
+\def\anch_tables_step_GSC#1%
+ {\anch_tables_append_GSC[#1:#1]}
+
+\def\anch_tables_append_GSC[#1:#2:#3]%
+% {\doglobal\appendtoks\anch_tables_process_GSC[#1:#2]\to\posXCtoks\NC}
+ {\gtoksapp\posXCtoks{\anch_tables_process_GSC[#1:#2]}\NC}
+
+\def\anch_tables_process_GSC[#1:#2]%
+ {\remappositionframed{#2}{\tbPOSprefix#1}%
+ \anch_tabulate_bpos{\tbPOSprefix#1}%
+ \doglobal\appendtoks\expandafter\anch_tabulate_epos\expandafter{\tbPOSprefix#1}\to\posXCtoks}
+% \xtoksapp\posXCtoks{\anch_tabulate_epos{\tbPOSprefix#1}}}
+
+\def\anch_tables_indeed_GFC[#1]%
+ {\doglobal\appendtoks\anch_tables_delayed_GFC[#1]\to\posXCtoks\NC}
+% {\gtoksapp\posXCtoks{\anch_tables_delayed_GFC[#1]}\NC}
+
+\def\anch_tables_delayed_GFC[#1]%
+ {\processcommalist[#1]\anch_tables_step_GFC}
+
+\def\anch_tables_step_GFC#1%
+ {\anch_tables_process_GFC[#1:#1]}
+
+\def\anch_tables_process_GFC[#1:#2:#3]%
+ {\remappositionframed{#2}{\tbPOSprefix#1}%
+ \anch_tabulate_bpos{\tbPOSprefix#1}}
+
+\def\anch_tables_indeed_GTC[#1]%
+ {\doglobal\appendtoks\anch_tables_delayed_GTC[#1]\to\posXCtoks\NC}
+% {\gtoksapp\posXCtoks{\anch_tables_delayed_GTC[#1]}\NC}
+
+\def\anch_tables_delayed_GTC[#1]%
+ {\doglobal\appendtoks\anch_tables_process_GTC[#1]\to\posXCtoks}
+% {\gtoksapp\posXCtoks{\anch_tables_process_GTC[#1]}\NC}
+
+\def\anch_tables_process_GTC[#1]%
+ {\processcommalist[#1]\anch_tables_step_GTC}
+
+\def\anch_tables_step_GTC#1%
+ {\anch_tables_step_indeed_GTC[#1:#1]}
+
+\def\anch_tables_step_indeed_GTC[#1:#2]%
+ {\anch_tabulate_epos{\tbPOSprefix#1}}
+
+%D The amount of code to support tables and tabulation is rather minimalistic.
+
+\aliased\let\tabulatepos\tablepos
+
+\permanent\protected\def\tabulatenormalpos
+ {\iftrialtypesetting
+ % nothing
+ \orelse\ifconditional\tablehaspositions
+ \hss\tabulatepos\hss
+ \else
+ % nothing
+ \fi}
+
+\permanent\protected\def\tabulateequalpos
+ {\iftrialtypesetting
+ \tabulateEQ
+ \orelse\ifconditional\tablehaspositions
+ \tabulateEQpos
+ \else
+ \tabulateEQ
+ \fi}
+
+\permanent\protected\def\tabulateEQpos
+ {\setbox\scratchbox\hbox{\tabulateEQ}%
+ \hbox to \wd\scratchbox{\hss\kern\zeropoint\tabulatepos\hss}% hpack
+ \kern-\wd\scratchbox
+ \box\scratchbox}
+
+\appendtoks
+ \global\advance\noftabpositions\plusone
+ \global\setfalse\tablehaspositions
+\to \everytabulate
+
+% We need to handle paragraphs as well.
+
+\let\anch_tabulate_flush_epos\relax
+
+\protected\def\anch_tabulate_bpos_indeed
+ {\bpos}
+
+\protected\def\anch_tabulate_epos_indeed#1%
+ {\ifvoid\b_tabl_tabulate_current\c_tabl_tabulate_column
+ \epos{#1}%
+ \glet\anch_tabulate_flush_epos\relax
+ \else
+ \gdef\anch_tabulate_flush_epos{\epos{#1}}%
+ \fi}
+
+\permanent\def\flushtabulatesplitbox
+ {\box\b_tabl_tabulate
+ \iftrialtypesetting\orelse\ifconditional\tablehaspositions\anch_tabulate_flush_epos\fi}
+
+\appendtoks
+ \let\anch_tabulate_bpos\anch_tabulate_bpos_indeed % ?
+ \let\anch_tabulate_epos\anch_tabulate_epos_indeed % ?
+ \glet\anch_tabulate_flush_epos\relax
+\to \everytabulate
+
+%D In order to prevent potential clashes with abbreviations, postpone the mapping and
+%D make them mutable.
+
+\mutable\let\GSC\empty
+\mutable\let\GFC\empty
+\mutable\let\GTC\empty
+
+\appendtoks
+ \enforced\let\GSC\tbGSC
+ \enforced\let\GFC\tbGFC
+ \enforced\let\GTC\tbGTC
+ \enforced\let\XC \tbXC
+\to \everytabulate
+
+%D \macros
+%D {definepositionframed}
+%D
+%D The next example show how to provide backgrounds to table cells. First we define
+%D some framed backgrounds.
+%D
+%D \startbuffer
+%D \definepositionframed[x][background=color,backgroundcolor=red]
+%D \definepositionframed[y][background=color,backgroundcolor=green]
+%D \definepositionframed[z][background=color,backgroundcolor=blue]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D % \getbuffer
+%D
+%D \startbuffer
+%D \starttabulate[|c|c|c|]
+%D \GFC[f:x] this is a small \NC table \NC in which we \NC \FR
+%D \NC will demonstrate \GFC[g:z] that this \GTC[g] positioning \NC \MR
+%D \GSC[e:y] mechanism also \GTC[f] works quite well \NC in tables \NC \LR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D The table itself defines three areas (a, b and~c) using these frames.
+%D
+%D \typebuffer
+%D % \getbuffer
+%D
+%D Tables (based on \TABLE) are supported by:
+
+\permanent\def\normalTABLEsimplebar {\unskip\!ttRightGlue&\tablepos&} % |
+\permanent\def\normalTABLEcomplexbar{\unskip\!ttRightGlue&\omit\tablepos\!ttAlternateVrule} % \|
+\permanent\def\normalTABLEquote {\unskip\!ttRightGlue&\omit\tablepos&} % "
+
+\appendtoks
+ \global\advance\noftabpositions\plusone
+ \global\setfalse\tablehaspositions
+\to \everytable
+
+%D Since we don't want nameclashes:
+
+\appendtoks
+ \enforced\let\GSC\tbGSC
+ \enforced\let\GFC\tbGFC
+ \enforced\let\GTC\tbGTC
+ \enforced\let\XC \tbXC
+\to \everytable
+
+%D In the previous example, we could have provided an overlay to the framed definition.
+%D A more direct approach is demonstrated below:
+%D
+%D \startbuffer
+%D \def\cw#1{\color[white]{#1}}
+%D
+%D \startMPpositiongraphic{tableshade}
+%D initialize_area(\MPpos{\MPvar{from}},\MPpos{\MPvar{to}}) ;
+%D color c ; c := \MPvar{color} ;
+%D linear_shade(pxy,0,.4c,.9c) ;
+%D anchor_area(\MPanchor{\MPvar{from}}) ;
+%D \stopMPpositiongraphic
+%D
+%D \setMPpositiongraphic{b:x}{tableshade}{from=b:x,to=e:x,color=red}
+%D \setMPpositiongraphic{b:y}{tableshade}{from=b:y,to=e:y,color=green}
+%D \setMPpositiongraphic{b:z}{tableshade}{from=b:z,to=e:z,color=blue}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D The definition of the table looks about the same as the previous one:
+%D
+%D \startbuffer
+%D \starttable[|c|c|c|]
+%D \GFC[b:z] \cw{this is a small} \NC \cw{table} \NC in which we \NC \FR
+%D \NC \cw{will demonstrate} \GFC[c:y] \cw{that this} \GTC[c] \cw{positioning} \NC \MR
+%D \GSC[a:x] \cw{mechanism also} \GTC[b] \cw{works quite well} \NC in tables \NC \LR
+%D \stoptable
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+
+% \definepositionframed[w][background=color,backgroundcolor=yellow]
+% \definepositionframed[x][background=color,backgroundcolor=red]
+% \definepositionframed[y][background=color,backgroundcolor=green]
+% \definepositionframed[z][background=color,backgroundcolor=blue]
+%
+% \starttabulate[|c|c|c|]
+% \NC this is a small \NC table \NC in which we \NC \FR
+% \NC will demonstrate \NC that this \NC positioning \NC \MR
+% \NC mechanism also \NC works quite well \NC in tables \NC \LR
+% \stoptabulate
+%
+% \starttabulate[|c|c|c|]
+% \GFC[f:x] this is a small \GTC table \NC in which we \NC \FR
+% \NC will demonstrate \GFC[g:z] that this \GTC[g] positioning \NC \MR
+% \GSC[e:y] mechanism also \GTC[f] works quite well \NC in tables \NC \LR
+% \stoptabulate
+%
+% \starttabulate[|c|c|c|]
+% \GFC[f:x,d:w] this is a small \GTC[d] table \NC in which we \NC \FR
+% \NC will demonstrate \GFC[g:z] that this \GTC[g] positioning \NC \MR
+% \GSC[e:y] mechanism also \GTC[f] works quite well \NC in tables \NC \LR
+% \stoptabulate
+%
+% \starttabulate[|c|c|c|]
+% \XC[+f:x] this is a small \XC table \NC in which we \NC \FR
+% \NC will demonstrate \XC[+g:z] that this \XC[-g] positioning \NC \MR
+% \XC[=e:y] mechanism also \XC[-f] works quite well \NC in tables \NC \LR
+% \stoptabulate
+%
+% \starttabulate[|c|c|c|]
+% \XC[+f:x,+d:w] this is a small \XC[-d] table \NC in which we \NC \FR
+% \NC will demonstrate \XC[+g:z] that this \XC[-g] positioning \NC \MR
+% \XC[=e:y] mechanism also \XC[-f] works quite well \NC in tables \NC \LR
+% \stoptabulate
+
+% evt [b:x]
+%
+% \definepositionframed[x][background=color,fillcolor=red]
+% \definepositionframed[y][background=color,fillcolor=green]
+% \definepositionframed[z][background=color,fillcolor=blue]
+
+\permanent\protected\def\remappositionframed#1#2% from to
+ {\copyposition{b:#1}{b:#2}%
+ \copyposition{e:#1}{e:#2}%
+ \anch_positions_set_action{b:#2}{\dopositionaction{b:#1}}}
+
+\permanent\tolerant\protected\def\definepositionframed[#1]#*[#2]%
+ {\anch_positions_set_action{b:#1}{\anch_framed_handle[#1][#2]}}
+
+\protected\def\anch_framed_handle
+ {\bgroup
+ \positionframed}
+
+\permanent\tolerant\protected\def\positionframed[#1]#*[#2]%
+ {\setbox\scratchbox\hpack
+ {\scratchwidth \dimexpr\MPx{e:#1}-\MPx{b:#1}\relax
+ \scratchdepth \dimexpr\MPy{b:#1}-\MPy{e:#1}+\MPd{e:#1}\relax
+ \scratchheight\dimexpr\scratchdepth+\MPh{b:#1}\relax
+ \lower\scratchdepth\hpack
+ {\framed[\c!width=\scratchwidth,\c!height=\scratchheight,\c!offset=\v!overlay,#2]{}}}%
+ \smashedbox\scratchbox
+ \egroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-alt.lmt b/Master/texmf-dist/tex/context/base/mkxl/attr-alt.lmt
new file mode 100644
index 00000000000..9d85920741d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-alt.lmt
@@ -0,0 +1,111 @@
+if not modules then modules = { } end modules ['attr-eff'] = {
+ version = 1.001,
+ comment = "companion to attr-eff.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local attributes, nodes, backends, utilities = attributes, nodes, backends, utilities
+local tex = tex
+
+local states = attributes.states
+local enableaction = nodes.tasks.enableaction
+local nodeinjections = backends.nodeinjections
+local texsetattribute = tex.setattribute
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local interfaces = interfaces
+local implement = interfaces.implement
+
+attributes.alternates = attributes.alternates or { }
+local alternates = attributes.alternates
+
+local a_alternate = attributes.private('alternate')
+
+alternates.data = allocate()
+alternates.values = alternates.values or { }
+alternates.registered = alternates.registered or { }
+alternates.attribute = a_alternate
+
+local data = alternates.data
+local registered = alternates.registered
+local values = alternates.values
+
+storage.register("attributes/alternates/registered", registered, "attributes.alternates.registered")
+storage.register("attributes/alternates/values", values, "attributes.alternates.values")
+
+local function extender(alternates,key)
+ if key == "none" then
+ local d = nodeinjections.stopalternate()
+ alternates.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ local d = values[n] and nodeinjections.startalternate(values[n]) or nodeinjections.stopalternate()
+ data[n] = d
+ return d
+end
+
+setmetatableindex(alternates, extender)
+setmetatableindex(alternates.data, reviver)
+
+alternates.handler = nodes.installattributehandler {
+ name = "alternate",
+ namespace = alternates,
+ initializer = states.initialize,
+ finalizer = states.finalize,
+ processor = states.simple,
+}
+
+local function register(specification)
+ local text = specification.text or "unknown"
+ local n = registered[text]
+ if not n then
+ n = #values + 1
+ values[n] = text
+ registered[text] = n
+ end
+ return n
+end
+
+local enabled = false
+
+local function enable()
+ if not enabled then
+ enableaction("shipouts","attributes.alternates.handler")
+ enabled = true
+ end
+end
+
+alternates.register = register
+alternates.enable = enable
+
+-- interface
+
+implement {
+ name = "setalternate",
+ actions = function(specification)
+ if not enabled then
+ enable()
+ end
+ texsetattribute(a_alternate,register(specification))
+ end,
+ arguments = {
+ {
+ { "text", "string" },
+ }
+ }
+}
+
+implement {
+ name = "resetalternate",
+ actions = function()
+ if enabled then
+ texsetattribute(a_alternate)
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-alt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-alt.mkxl
new file mode 100644
index 00000000000..3881d929e1c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-alt.mkxl
@@ -0,0 +1,55 @@
+%D \module
+%D [ file=attr-alt,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Alternates,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Alternates}
+
+\registerctxluafile{attr-alt}{autosuffix}
+
+%D The main reason for providing this is that we need to make sure we have no
+%D dangling end markers when we cross a page. Public viewers don't seem to support
+%D actual text at all and acrobat has some funny selection issues.
+%D
+%D \starttyping
+%D test
+%D \startalternate[text={A}]
+%D \dorecurse{10}{A }
+%D \startalternate[text={B}]
+%D \dorecurse{10}{B }
+%D \stopalternate
+%D \dorecurse{10}{A }
+%D \stopalternate
+%D test \par
+%D \stoptyping
+%D
+%D \starttyping
+%D test \startalternate[text={e=mc^2}]!$e=mc^2$\stopalternate\par
+%D test \alternate{e=mc^2}{copy:}\nbsp$e=mc^2$\par
+%D \stoptyping
+
+\unprotect
+
+\permanent\protected\def\startalternate[#1]% maybe better/also: [text={...}]
+ {\begingroup
+ \getdummyparameters[\c!text=,#1]%
+ \clf_setalternate text {\dummyparameter\c!text}\relax}
+
+\permanent\protected\def\stopalternate
+ {\endgroup}
+
+\permanent\protected\def\alternate#1%
+ {\groupedcommand{\clf_setalternate text {#1}\relax}{}}
+
+\permanent\protected\def\setalternate #1{\clf_setalternate text {#1}\relax}
+\permanent\protected\def\resetalternate {\clf_resetalternate}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-col.lmt b/Master/texmf-dist/tex/context/base/mkxl/attr-col.lmt
new file mode 100644
index 00000000000..9c542a94293
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-col.lmt
@@ -0,0 +1,666 @@
+if not modules then modules = { } end modules ['attr-col'] = {
+ version = 1.001,
+ comment = "companion to attr-col.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this module is being reconstructed and code will move to other places
+-- we can also do the nsnone via a metatable and then also se index 0
+
+-- list could as well refer to the tables (instead of numbers that
+-- index into another table) .. depends on what we need
+
+local type, tonumber = type, tonumber
+local concat = table.concat
+local min, max, floor, mod = math.min, math.max, math.floor, math.mod
+
+local attributes = attributes
+local nodes = nodes
+local utilities = utilities
+local logs = logs
+local backends = backends
+local storage = storage
+local context = context
+local tex = tex
+
+local variables = interfaces.variables
+local v_yes = variables.yes
+local v_no = variables.no
+
+local p_split_comma = lpeg.tsplitat(",")
+local p_split_colon = lpeg.splitat(":")
+local lpegmatch = lpeg.match
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local report_attributes = logs.reporter("attributes","colors")
+local report_colors = logs.reporter("colors","support")
+local report_transparencies = logs.reporter("transparencies","support")
+
+-- todo: document this but first reimplement this as it reflects the early
+-- days of luatex / mkiv and we have better ways now
+
+-- nb: attributes: color etc is much slower than normal (marks + literals) but ...
+-- nb. too many "0 g"s
+
+local states = attributes.states
+local nodeinjections = backends.nodeinjections
+local registrations = backends.registrations
+local unsetvalue = attributes.unsetvalue
+
+local enableaction = nodes.tasks.enableaction
+local setaction = nodes.tasks.setaction
+
+local registerstorage = storage.register
+local formatters = string.formatters
+
+local interfaces = interfaces
+local implement = interfaces.implement
+
+local texgetattribute = tex.getattribute
+
+-- We can distinguish between rules and glyphs but it's not worth the trouble. A
+-- first implementation did that and while it saves a bit for glyphs and rules, it
+-- costs more resourses for transparencies. So why bother.
+
+--
+-- colors
+--
+
+-- we can also collapse the two attributes: n, n+1, n+2 and then
+-- at the tex end add 0, 1, 2, but this is not faster and less
+-- flexible (since sometimes we freeze color attribute values at
+-- the lua end of the game)
+--
+-- we also need to store the colorvalues because we need then in mp
+--
+-- This is a compromis between speed and simplicity. We used to store the
+-- values and data in one array, which made in neccessary to store the
+-- converters that need node constructor into strings and evaluate them
+-- at runtime (after reading from storage). Think of:
+--
+-- colors.strings = colors.strings or { }
+--
+-- if environment.initex then
+-- colors.strings[color] = "return colors." .. colorspace .. "(" .. concat({...},",") .. ")"
+-- end
+--
+-- registerstorage("attributes/colors/data", colors.strings, "attributes.colors.data") -- evaluated
+--
+-- We assume that only processcolors are defined in the format.
+
+attributes.colors = attributes.colors or { }
+local colors = attributes.colors
+
+local a_color = attributes.private('color')
+local a_selector = attributes.private('colormodel')
+
+colors.data = allocate()
+colors.values = colors.values or { }
+colors.registered = colors.registered or { }
+colors.weightgray = true
+colors.attribute = a_color
+colors.selector = a_selector
+colors.default = 1
+colors.main = nil
+colors.triggering = true
+colors.supported = true
+colors.model = "all"
+
+local data = colors.data
+local values = colors.values
+local registered = colors.registered
+
+local cmykrgbmode = 0 -- only for testing, already defined colors are not affected
+
+local numbers = attributes.numbers
+local list = attributes.list
+
+registerstorage("attributes/colors/values", values, "attributes.colors.values")
+registerstorage("attributes/colors/registered", registered, "attributes.colors.registered")
+
+directives.register("colors.cmykrgbmode", function(v) cmykrgbmode = tonumber(v) or 0 end)
+
+local f_colors = {
+ rgb = formatters["r:%s:%s:%s"],
+ cmyk = formatters["c:%s:%s:%s:%s"],
+ gray = formatters["s:%s"],
+ spot = formatters["p:%s:%s:%s:%s"],
+}
+
+local models = {
+ [interfaces.variables.none] = unsetvalue,
+ black = unsetvalue,
+ bw = unsetvalue,
+ all = 1,
+ gray = 2,
+ rgb = 3,
+ cmyk = 4,
+}
+
+local function rgbtocmyk(r,g,b) -- we could reduce
+ if not r then
+ return 0, 0, 0
+ else
+ return 1-r, 1-g, 1-b, 0
+ end
+end
+
+local function cmyktorgb(c,m,y,k)
+ if not c then
+ return 0, 0, 0, 1
+ elseif cmykrgbmode == 1 then
+ local d = 1.0 - k
+ return 1.0 - min(1.0,c*d+k), 1.0 - min(1.0,m*d+k), 1.0 - min(1.0,y*d+k)
+ else
+ return 1.0 - min(1.0,c +k), 1.0 - min(1.0,m +k), 1.0 - min(1.0,y +k)
+ end
+end
+
+local function rgbtogray(r,g,b)
+ if not r then
+ return 0
+ end
+ local w = colors.weightgray
+ if w == true then
+ return .30*r + .59*g + .11*b
+ elseif not w then
+ return r/3 + g/3 + b/3
+ else
+ return w[1]*r + w[2]*g + w[3]*b
+ end
+end
+
+local function cmyktogray(c,m,y,k)
+ return rgbtogray(cmyktorgb(c,m,y,k))
+end
+
+-- http://en.wikipedia.org/wiki/HSI_color_space
+-- http://nl.wikipedia.org/wiki/HSV_(kleurruimte)
+
+-- h /= 60; // sector 0 to 5
+-- i = floor( h );
+-- f = h - i; // factorial part of h
+
+local function hsvtorgb(h,s,v)
+ if s > 1 then
+ s = 1
+ elseif s < 0 then
+ s = 0
+ elseif s == 0 then
+ return v, v, v
+ end
+ if v > 1 then
+ s = 1
+ elseif v < 0 then
+ v = 0
+ end
+ if h < 0 then
+ h = 0
+ elseif h >= 360 then
+ h = mod(h,360)
+ end
+ local hd = h / 60
+ local hi = floor(hd)
+ local f = hd - hi
+ local p = v * (1 - s)
+ local q = v * (1 - f * s)
+ local t = v * (1 - (1 - f) * s)
+ if hi == 0 then
+ return v, t, p
+ elseif hi == 1 then
+ return q, v, p
+ elseif hi == 2 then
+ return p, v, t
+ elseif hi == 3 then
+ return p, q, v
+ elseif hi == 4 then
+ return t, p, v
+ elseif hi == 5 then
+ return v, p, q
+ else
+ print("error in hsv -> rgb",h,s,v)
+ return 0, 0, 0
+ end
+end
+
+local function rgbtohsv(r,g,b)
+ local offset, maximum, other_1, other_2
+ if r >= g and r >= b then
+ offset, maximum, other_1, other_2 = 0, r, g, b
+ elseif g >= r and g >= b then
+ offset, maximum, other_1, other_2 = 2, g, b, r
+ else
+ offset, maximum, other_1, other_2 = 4, b, r, g
+ end
+ if maximum == 0 then
+ return 0, 0, 0
+ end
+ local minimum = other_1 < other_2 and other_1 or other_2
+ if maximum == minimum then
+ return 0, 0, maximum
+ end
+ local delta = maximum - minimum
+ return (offset + (other_1-other_2)/delta)*60, delta/maximum, maximum
+end
+
+local function graytorgb(s) -- unweighted
+ return 1-s, 1-s, 1-s
+end
+
+local function hsvtogray(h,s,v)
+ return rgb_to_gray(hsv_to_rgb(h,s,v))
+end
+
+local function graytohsv(s)
+ return 0, 0, s
+end
+
+local function hwbtorgb(hue,black,white)
+ local r, g, b = hsvtorgb(hue,1,.5)
+ local f = 1 - white - black
+ return f * r + white, f * g + white , f * b + white
+end
+
+colors.rgbtocmyk = rgbtocmyk
+colors.rgbtogray = rgbtogray
+colors.cmyktorgb = cmyktorgb
+colors.cmyktogray = cmyktogray
+colors.rgbtohsv = rgbtohsv
+colors.hsvtorgb = hsvtorgb
+colors.hwbtorgb = hwbtorgb
+colors.hsvtogray = hsvtogray
+colors.graytohsv = graytohsv
+
+-- we can share some *data by using s, rgb and cmyk hashes, but
+-- normally the amount of colors is not that large; storing the
+-- components costs a bit of extra runtime, but we expect to gain
+-- some back because we have them at hand; the number indicates the
+-- default color space
+
+function colors.gray(s)
+ return { 2, s, s, s, s, 0, 0, 0, 1-s }
+end
+
+function colors.rgb(r,g,b)
+ local s = rgbtogray(r,g,b)
+ local c, m, y, k = rgbtocmyk(r,g,b)
+ return { 3, s, r, g, b, c, m, y, k }
+end
+
+function colors.cmyk(c,m,y,k)
+ local s = cmyktogray(c,m,y,k)
+ local r, g, b = cmyktorgb(c,m,y,k)
+ return { 4, s, r, g, b, c, m, y, k }
+end
+
+--~ function colors.spot(parent,f,d,p)
+--~ return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
+--~ end
+
+function colors.spot(parent,f,d,p)
+ -- inspect(parent) inspect(f) inspect(d) inspect(p)
+ if type(p) == "number" then
+ local n = list[numbers.color][parent] -- hard coded ref to color number
+ if n then
+ local v = values[n]
+ if v then
+ -- the via cmyk hack is dirty, but it scales better
+ local c, m, y, k = p*v[6], p*v[7], p*v[8], p*v[9]
+ local r, g, b = cmyktorgb(c,m,y,k)
+ local s = cmyktogray(c,m,y,k)
+ return { 5, s, r, g, b, c, m, y, k, parent, f, d, p }
+ end
+ end
+ else
+ -- todo, multitone (maybe p should be a table)
+ local ps = lpegmatch(p_split_comma,p)
+ local ds = lpegmatch(p_split_comma,d)
+ local c, m, y, k = 0, 0, 0, 0
+ local done = false
+ for i=1,#ps do
+ local p = tonumber(ps[i])
+ local d = ds[i]
+ if p and d then
+ local n = list[numbers.color][d] -- hard coded ref to color number
+ if n then
+ local v = values[n]
+ if v then
+ c = c + p*v[6]
+ m = m + p*v[7]
+ y = y + p*v[8]
+ k = k + p*v[9]
+ done = true
+ end
+ end
+ end
+ end
+ if done then
+ local r, g, b = cmyktorgb(c,m,y,k)
+ local s = cmyktogray(c,m,y,k)
+ local f = tonumber(f)
+ return { 5, s, r, g, b, c, m, y, k, parent, f, d, p }
+ end
+ end
+ return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
+end
+
+local function graycolor(...) graycolor = nodeinjections.graycolor return graycolor(...) end
+local function rgbcolor (...) rgbcolor = nodeinjections.rgbcolor return rgbcolor (...) end
+local function cmykcolor(...) cmykcolor = nodeinjections.cmykcolor return cmykcolor(...) end
+local function spotcolor(...) spotcolor = nodeinjections.spotcolor return spotcolor(...) end
+
+local function extender(colors,key)
+ if colors.supported and key == "none" then
+ local d = graycolor(0)
+ colors.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ if colors.supported then
+ local v = values[n]
+ local d
+ if not v then
+ local gray = graycolor(0)
+ d = { gray, gray, gray, gray }
+ report_attributes("unable to revive color %a",n)
+ else
+ local model = colors.forcedmodel(v[1])
+ if model == 2 then
+ local gray = graycolor(v[2])
+ d = { gray, gray, gray, gray }
+ elseif model == 3 then
+ local gray = graycolor(v[2])
+ local rgb = rgbcolor(v[3],v[4],v[5])
+ local cmyk = cmykcolor(v[6],v[7],v[8],v[9])
+ d = { rgb, gray, rgb, cmyk }
+ elseif model == 4 then
+ local gray = graycolor(v[2])
+ local rgb = rgbcolor(v[3],v[4],v[5])
+ local cmyk = cmykcolor(v[6],v[7],v[8],v[9])
+ d = { cmyk, gray, rgb, cmyk }
+ elseif model == 5 then
+ local spot = spotcolor(v[10],v[11],v[12],v[13])
+ -- d = { spot, gray, rgb, cmyk }
+ d = { spot, spot, spot, spot }
+ end
+ end
+ data[n] = d
+ return d
+ end
+end
+
+setmetatableindex(colors, extender)
+setmetatableindex(colors.data, reviver)
+
+function colors.filter(n)
+ return concat(data[n],":",5)
+end
+
+-- unweighted (flat) gray could be another model but a bit work as we need to check:
+--
+-- attr-col colo-ini colo-run
+-- grph-inc grph-wnd
+-- lpdf-col lpdf-fmt lpdf-fld lpdf-grp
+-- meta-pdf meta-pdh mlib-pps
+--
+-- but as we never needed it we happily delay that.
+
+function colors.setmodel(name,weightgray)
+ if weightgray == true or weightgray == v_yes then
+ weightgray = true
+ elseif weightgray == false or weightgray == v_no then
+ weightgray = false
+ else
+ local r, g, b = lpegmatch(p_split_colon,weightgray)
+ if r and g and b then
+ weightgray = { r, g, b }
+ else
+ weightgray = true
+ end
+ end
+ local default = models[name] or 1
+
+ colors.model = name -- global, not useful that way
+ colors.default = default -- global
+ colors.weightgray = weightgray -- global
+
+ -- avoid selective checking is no need for it
+
+ local forced = colors.forced
+
+ if forced == nil then
+ -- unset
+ colors.forced = default
+ elseif forced == false then
+ -- assumed mixed
+ elseif forced ~= default then
+ -- probably mixed
+ colors.forced = false
+ else
+ -- stil the same
+ end
+ return default
+end
+
+function colors.register(name, colorspace, ...) -- passing 9 vars is faster (but not called that often)
+ local stamp = f_colors[colorspace](...)
+ local color = registered[stamp]
+ if not color then
+ color = #values + 1
+ values[color] = colors[colorspace](...)
+ registered[stamp] = color
+ -- colors.reviver(color)
+ end
+ if name then
+ list[a_color][name] = color -- not grouped, so only global colors
+ end
+ return registered[stamp]
+end
+
+function colors.value(id)
+ return values[id]
+end
+
+attributes.colors.handler = nodes.installattributehandler {
+ name = "color",
+ namespace = colors,
+ initializer = states.initialize,
+ finalizer = states.finalize,
+ processor = states.selective,
+ resolver = function() return colors.main end,
+}
+
+function colors.enable(value)
+ setaction("shipouts","attributes.colors.handler",not (value == false or not colors.supported))
+end
+
+function colors.forcesupport(value) -- can move to attr-div
+ colors.supported = value
+ report_colors("color is %ssupported",value and "" or "not ")
+ colors.enable(value)
+end
+
+function colors.toattributes(name)
+ local mc = list[a_color][name]
+ local mm = texgetattribute(a_selector)
+ return (mm == unsetvalue and 1) or mm or 1, mc or list[a_color][1] or unsetvalue
+end
+
+-- transparencies
+
+local a_transparency = attributes.private('transparency')
+
+attributes.transparencies = attributes.transparencies or { }
+local transparencies = attributes.transparencies
+transparencies.registered = transparencies.registered or { }
+transparencies.data = allocate()
+transparencies.values = transparencies.values or { }
+transparencies.triggering = true
+transparencies.attribute = a_transparency
+transparencies.supported = true
+
+local registered = transparencies.registered -- we could use a 2 dimensional table instead
+local data = transparencies.data
+local values = transparencies.values
+local f_transparency = formatters["%s:%s"]
+
+registerstorage("attributes/transparencies/registered", registered, "attributes.transparencies.registered")
+registerstorage("attributes/transparencies/values", values, "attributes.transparencies.values")
+
+local function inject_transparency(...)
+ inject_transparency = nodeinjections.transparency
+ return inject_transparency(...)
+end
+
+local function register_transparency(...)
+ register_transparency = registrations.transparency
+ return register_transparency(...)
+end
+
+function transparencies.register(name,a,t,force) -- name is irrelevant here (can even be nil)
+ -- Force needed here for metapost converter. We could always force
+ -- but then we'd end up with transparencies resources even if we
+ -- would not use transparencies (but define them only). This is
+ -- somewhat messy.
+ local stamp = f_transparency(a,t)
+ local n = registered[stamp]
+ if not n then
+ n = #values + 1
+ values[n] = { a, t }
+ registered[stamp] = n
+ if force then
+ register_transparency(n,a,t)
+ end
+ elseif force and not data[n] then
+ register_transparency(n,a,t)
+ end
+ if name then
+ list[a_transparency][name] = n -- not grouped, so only global transparencies
+ end
+ return registered[stamp]
+end
+
+local function extender(transparencies,key)
+ if colors.supported and key == "none" then
+ local d = inject_transparency(0)
+ transparencies.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ if n and transparencies.supported then
+ local v = values[n]
+ local d
+ if not v then
+ d = inject_transparency(0)
+ else
+ d = inject_transparency(n)
+ register_transparency(n,v[1],v[2])
+ end
+ data[n] = d
+ return d
+ else
+ return ""
+ end
+end
+
+setmetatableindex(transparencies,extender)
+setmetatableindex(transparencies.data,reviver) -- register if used
+
+-- check if there is an identity
+
+function transparencies.value(id)
+ return values[id]
+end
+
+attributes.transparencies.handler = nodes.installattributehandler {
+ name = "transparency",
+ namespace = transparencies,
+ initializer = states.initialize,
+ finalizer = states.finalize,
+ processor = states.process,
+}
+
+function transparencies.enable(value) -- nil is enable
+ setaction("shipouts","attributes.transparencies.handler",not (value == false or not transparencies.supported))
+end
+
+function transparencies.forcesupport(value) -- can move to attr-div
+ transparencies.supported = value
+ report_transparencies("transparency is %ssupported",value and "" or "not ")
+ transparencies.enable(value)
+end
+
+function transparencies.toattribute(name)
+ return list[a_transparency][name] or unsetvalue
+end
+
+--- colorintents: overprint / knockout
+
+attributes.colorintents = attributes.colorintents or { }
+local colorintents = attributes.colorintents
+colorintents.data = allocate() -- colorintents.data or { }
+colorintents.attribute = attributes.private('colorintent')
+
+colorintents.registered = allocate {
+ overprint = 1,
+ knockout = 2,
+}
+
+local data, registered = colorintents.data, colorintents.registered
+
+local function extender(colorintents,key)
+ if key == "none" then
+ local d = data[2]
+ colorintents.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ if n == 1 then
+ local d = nodeinjections.overprint() -- called once
+ data[1] = d
+ return d
+ elseif n == 2 then
+ local d = nodeinjections.knockout() -- called once
+ data[2] = d
+ return d
+ end
+end
+
+setmetatableindex(colorintents, extender)
+setmetatableindex(colorintents.data, reviver)
+
+function colorintents.register(stamp)
+ return registered[stamp] or registered.overprint
+end
+
+colorintents.handler = nodes.installattributehandler {
+ name = "colorintent",
+ namespace = colorintents,
+ initializer = states.initialize,
+ finalizer = states.finalize,
+ processor = states.process,
+}
+
+function colorintents.enable()
+ enableaction("shipouts","attributes.colorintents.handler")
+end
+
+-- interface
+
+implement { name = "enablecolor", onlyonce = true, actions = colors.enable }
+implement { name = "enabletransparency", onlyonce = true, actions = transparencies.enable }
+implement { name = "enablecolorintents", onlyonce = true, actions = colorintents.enable }
+
+--------- { name = "registercolor", actions = { colors .register, context }, arguments = "string" }
+--------- { name = "registertransparency", actions = { transparencies.register, context }, arguments = "string" }
+implement { name = "registercolorintent", actions = { colorintents .register, context }, arguments = "string" }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-col.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-col.mkxl
new file mode 100644
index 00000000000..0c23f2ce384
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-col.mkxl
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=attr-col,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Color,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Color}
+
+\registerctxluafile{attr-col}{autosuffix}
+
+\unprotect
+
+% We implement this elsewhere but some code might end up here.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-eff.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-eff.mkxl
new file mode 100644
index 00000000000..42aadf9a278
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-eff.mkxl
@@ -0,0 +1,60 @@
+%D \module
+%D [ file=attr-eff,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Effects,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Effects}
+
+\registerctxluafile{attr-eff}{}
+
+\unprotect
+
+\installcorenamespace{effect}
+
+\installcommandhandler \??effect {effect} \??effect
+
+\setupeffect
+ [\c!method=\v!none,
+ \c!stretch=\zerocount,
+ \c!rulethickness=\zeropoint,
+ \c!alternative=\v!normal]
+
+\appendtoks
+ \edef\p_method{\effectparameter\c!method}%
+ \ifx\p_method\v!command
+ \frozen\protected\xdefcsname\e!start\currenteffect\endcsname{\starteffect[#1]}%
+ \frozen\protected\xdefcsname\e!stop \currenteffect\endcsname{\stopeffect}%
+ \fi
+\to \everydefineeffect
+
+\permanent\protected\def\starteffect[#1]%
+ {\clf_seteffect
+ alternative {\namedeffectparameter{#1}\c!alternative}
+ stretch \numexpr\namedeffectparameter{#1}\c!stretch\relax
+ rulethickness \dimexpr\namedeffectparameter{#1}\c!rulethickness\relax
+ \relax}
+
+\permanent\protected\def\stopeffect % can be special
+ {\clf_reseteffect} % v!normal 0 0
+
+\permanent\protected\def\effect[#1]%
+ {\groupedcommand{\starteffect[#1]}{\stopeffect}}
+
+% yes or no grouped
+
+\defineeffect [\v!inner] [\c!alternative=\v!inner,\c!rulethickness=.25pt]
+\defineeffect [\v!outer] [\c!alternative=\v!outer,\c!rulethickness=.25pt]
+\defineeffect [\v!both] [\c!alternative=\v!both, \c!rulethickness=.25pt]
+\defineeffect [\v!normal] [\c!alternative=\v!normal]
+\defineeffect [\v!hidden] [\c!alternative=\v!hidden]
+\defineeffect [\v!stretch] [\c!alternative=\v!stretch,\c!stretch=1]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-ini.mkxl
new file mode 100644
index 00000000000..e707a478f1f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-ini.mkxl
@@ -0,0 +1,141 @@
+%D \module
+%D [ file=attr-ini,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Initialization}
+
+\unprotect
+
+\registerctxluafile{attr-ini}{}
+
+\installcorenamespace{attributecount} % the counter representing the attribute (attrdef'd)
+\installcorenamespace{attributeid} % the internal number
+\installcorenamespace{attributestack} % the attribute specific stack
+\installcorenamespace{attributepickup}
+
+\permanent\protected\def\pushattribute#1%
+ {\global\advance\csname\??attributestack\string#1\endcsname\plusone
+ \gdefcsname\??attributestack\string#1:\number\csname\??attributestack\string#1\endcsname\endcsname{\number\attribute#1}}
+
+\permanent\protected\def\popattribute#1%
+ {\attribute#1\csname\??attributestack\string#1:\number\csname\??attributestack\string#1\endcsname\endcsname\relax
+ \global\advance\csname\??attributestack\string#1\endcsname\minusone}
+
+\permanent\protected\def\installattributestack#1%
+ {\ifcsname\??attributestack\string#1\endcsname \else
+ \expandafter\newcount\csname\??attributestack\string#1\endcsname
+ \fi}
+
+\newtoks \t_attr_list_global
+\newtoks \t_attr_list_local
+\newtoks \t_attr_list_pickup
+\newtoks \t_attr_list_nomath
+
+\ifdefined \s!global \else \immutable\def\s!global {global} \fi % for metatex % or hard check later
+\ifdefined \s!public \else \immutable\def\s!public {public} \fi % for metatex % or hard check later
+\ifdefined \s!private \else \immutable\def\s!private {private} \fi % for metatex % or hard check later
+\ifdefined \s!attribute \else \immutable\def\s!attribute{attribute} \fi % for metatex % or hard check later
+\ifdefined \s!pickup \else \immutable\def\s!pickup {pickup} \fi % for metatex % or hard check later
+\ifdefined \s!forget \else \immutable\def\s!forget {forget} \fi % for metatex % or hard check later
+
+\immutable\def\??attributeprefix{c_attr_}
+
+\permanent\protected\def\defineattribute {\attr_basics_define_indeed[\s!public ]}
+\permanent\protected\def\definesystemattribute{\attr_basics_define_indeed[\s!private]}
+
+\tolerant\def\attr_basics_define_indeed[#1]#*[#2]#*[#3]%
+ {\ifcsname\??attributeprefix#2\endcsname\else
+ \scratchcounter\clf_defineattribute{#2}{#1}\relax
+ %\writestatus\m!system{defining #1 attribute #2 with number \number\scratchcounter}%
+ \global\permanent\expandafter\attributedef\csname\??attributeprefix#2\endcsname\scratchcounter % \foo = 123
+ \global\permanent\expandafter\integerdef \csname\??attributeid #2\endcsname\scratchcounter % \attribute\foo = 123
+ % some attributes are always global
+ \doifelseinset\s!global{#3}%
+ {\xtoksapp\t_attr_list_global{\csname\??attributeprefix#2\endcsname\attributeunsetvalue}}% \c_attr_foo
+ {\xtoksapp\t_attr_list_local {\csname\??attributeprefix#2\endcsname\attributeunsetvalue}}%
+ \doifinset\s!nomath{#3}%
+ {\xtoksapp\t_attr_list_nomath{\csname\??attributeprefix#2\endcsname\attributeunsetvalue}}%
+ \doifinset\s!public{#3}%
+ {\aliased\gletcsname#2\s!attribute\expandafter\endcsname\csname\??attributeid#2\endcsname}% \attribute\fooattribute
+ % used to sync in the page builder .. used for directions and scripts ... i start forgetting these details .. (seldom used)
+ \doifinset\s!pickup{#3}%
+ {\global\expandafter\integerdef\csname\??attributepickup#2\endcsname\attributeunsetvalue
+ \xtoksapp\t_attr_list_pickup{\csname\??attributeprefix#2\endcsname\csname\??attributepickup#2\endcsname}%
+ \ifcsname#2\s!attribute\endcsname
+ \permanent\xdefcsname\s!pickup#2\s!attribute\endcsname{\expandafter\integerdef\csname\??attributepickup#2\endcsname\csname\??attributeprefix#2\endcsname}%
+ \permanent\xdefcsname\s!forget#2\s!attribute\endcsname{\expandafter\integerdef\csname\??attributepickup#2\endcsname\attributeunsetvalue}%
+ \fi}%
+ \fi}
+
+\permanent\protected\def\pickupattributes
+ {\the\t_attr_list_pickup\relax}
+
+\permanent\protected\def\newattribute#1%
+ {\attr_basics_define_indeed[\s!public][\csstring#1][]%
+ \expandafter\glet\expandafter#1\csname\??attributeid\csstring#1\endcsname}
+
+% expandable so we can \edef them for speed
+
+\permanent\def\dosetattribute#1#2{\csname\??attributeprefix#1\endcsname#2\relax}
+\permanent\def\doresetattribute#1{\csname\??attributeprefix#1\endcsname\attributeunsetvalue}
+\permanent\def\dogetattribute #1{\number\csname\??attributeprefix#1\endcsname}
+\permanent\def\dogetattributeid#1{\number\csname\??attributeid #1\endcsname}
+
+\aliased\let\dompattribute\gobbletwoarguments
+
+\permanent\protected\def\resetglobalattributes{\the\t_attr_list_global\glyphdatafield\zerocount\glyphscriptfield\zerocount}
+\permanent\protected\def\resetlocalattributes {\the\t_attr_list_local \glyphdatafield\zerocount\glyphscriptfield\zerocount}
+
+\aliased\let\resetallattributes\resetlocalattributes
+
+%D Rather special.
+
+\permanent\protected\def\savecurrentattributes #1{\clf_savecurrentattributes {#1}}
+\permanent\protected\def\restorecurrentattributes#1{\clf_restorecurrentattributes{#1}}
+
+%D For the moment we put this here. The order of definition matters a bit because
+%D performance is better when we put frequently accessed attributes at the front.
+%D So, we might move more here.
+
+%definesystemattribute [state] % nomath
+\definesystemattribute [color] [public] % global
+\definesystemattribute [colormodel] [public,global]
+%definesystemattribute [skip]
+%definesystemattribute [penalty]
+\definesystemattribute [transparency] [public]
+\definesystemattribute [reference] [public]
+\definesystemattribute [destination] [public]
+\definesystemattribute [case] [public]
+\definesystemattribute [visual] [public,global]
+\definesystemattribute [viewerlayer] [public]
+\definesystemattribute [background] [public]
+\definesystemattribute [alignbackground] [public]
+\definesystemattribute [colorintent] [public]
+\definesystemattribute [negative] [public]
+\definesystemattribute [effect] [public]
+\definesystemattribute [layoutcomponent] [public]
+\definesystemattribute [internal] [public]
+\definesystemattribute [ruled] [public]
+\definesystemattribute [shifted] [public]
+\definesystemattribute [checkedbreak] [public]
+\definesystemattribute [alternate] [public]
+\definesystemattribute [vboxtohboxseparator] [public]
+
+\permanent\protected\let\showattributes\clf_showattributes % maybe at lua end
+
+%D Todo:
+
+% \appendtoks
+% \clf_cleanupattributes
+% \to \everyafterpagebreak
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-lay.lmt b/Master/texmf-dist/tex/context/base/mkxl/attr-lay.lmt
new file mode 100644
index 00000000000..d0febb9eefb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-lay.lmt
@@ -0,0 +1,311 @@
+if not modules then modules = { } end modules ['attr-lay'] = {
+ version = 1.001,
+ comment = "companion to attr-lay.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- layers (ugly code, due to no grouping and such); currently we use exclusive layers
+-- but when we need it stacked layers might show up too; the next function based
+-- approach can be replaced by static (metatable driven) resolvers
+
+-- maybe use backends.registrations here too
+
+local type = type
+local insert, remove = table.insert, table.remove
+
+local attributes = attributes
+local nodes = nodes
+local utilities = utilities
+local logs = logs
+local backends = backends
+
+local context = context
+local interfaces = interfaces
+local tex = tex
+
+local implement = interfaces.implement
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+local report_viewerlayers = logs.reporter("viewerlayers")
+
+-- todo: document this but first reimplement this as it reflects the early
+-- days of luatex / mkiv and we have better ways now
+
+-- nb: attributes: color etc is much slower than normal (marks + literals) but ...
+-- nb. too many "0 g"s
+-- nb: more local tables
+
+attributes.viewerlayers = attributes.viewerlayers or { }
+local viewerlayers = attributes.viewerlayers
+
+local variables = interfaces.variables
+local v_local = variables["local"]
+local v_global = variables["global"]
+local v_start = variables["start"]
+local v_yes = variables["yes"]
+
+local a_viewerlayer = attributes.private("viewerlayer")
+
+viewerlayers = viewerlayers or { }
+viewerlayers.data = allocate()
+viewerlayers.registered = viewerlayers.registered or { }
+viewerlayers.values = viewerlayers.values or { }
+viewerlayers.scopes = viewerlayers.scopes or { }
+viewerlayers.listwise = allocate()
+viewerlayers.attribute = a_viewerlayer
+viewerlayers.supported = true
+viewerlayers.hasorder = true
+
+local states = attributes.states
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+local nodeinjections = backends.nodeinjections
+local codeinjections = backends.codeinjections
+
+local texsetattribute = tex.setattribute
+local texgetattribute = tex.getattribute
+local texsettokenlist = tex.settoks
+local unsetvalue = attributes.unsetvalue
+
+local data = viewerlayers.data
+local values = viewerlayers.values
+local listwise = viewerlayers.listwise
+local registered = viewerlayers.registered
+local scopes = viewerlayers.scopes
+
+local f_stamp = formatters["%s"]
+
+storage.register("attributes/viewerlayers/registered", registered, "attributes.viewerlayers.registered")
+storage.register("attributes/viewerlayers/values", values, "attributes.viewerlayers.values")
+storage.register("attributes/viewerlayers/scopes", scopes, "attributes.viewerlayers.scopes")
+
+local layerstacker = utilities.stacker.new("layers") -- experiment
+
+layerstacker.mode = "stack"
+layerstacker.unset = attributes.unsetvalue
+
+viewerlayers.resolve_reset = layerstacker.resolve_reset
+viewerlayers.resolve_begin = layerstacker.resolve_begin
+viewerlayers.resolve_step = layerstacker.resolve_step
+viewerlayers.resolve_end = layerstacker.resolve_end
+
+-- stacked
+
+local function startlayer(...) startlayer = nodeinjections.startlayer return startlayer(...) end
+local function stoplayer (...) stoplayer = nodeinjections.stoplayer return stoplayer (...) end
+
+local function extender(viewerlayers,key)
+ if viewerlayers.supported and key == "none" then
+ local d = stoplayer()
+ viewerlayers.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ if viewerlayers.supported then
+ local v = values[n]
+ if v then
+ local d = startlayer(v)
+ data[n] = d
+ return d
+ else
+ report_viewerlayers("error: unknown reference %a",tostring(n))
+ end
+ end
+end
+
+setmetatableindex(viewerlayers,extender)
+setmetatableindex(viewerlayers.data,reviver)
+
+-- !!!! TEST CODE !!!!
+
+layerstacker.start = function(...) local f = nodeinjections.startstackedlayer layerstacker.start = f return f(...) end
+layerstacker.stop = function(...) local f = nodeinjections.stopstackedlayer layerstacker.stop = f return f(...) end
+layerstacker.change = function(...) local f = nodeinjections.changestackedlayer layerstacker.change = f return f(...) end
+
+local function initializer(...)
+ return states.initialize(...)
+end
+
+attributes.viewerlayers.handler = nodes.installattributehandler {
+ name = "viewerlayer",
+ namespace = viewerlayers,
+ initializer = initializer,
+ finalizer = states.finalize,
+ processor = states.stacker,
+ -- processor = states.stacked,
+}
+
+local stack, enabled, global = { }, false, false
+
+function viewerlayers.enable(value)
+ if value == false or not viewerlayers.supported then
+ if enabled then
+ disableaction("shipouts","attributes.viewerlayers.handler")
+ end
+ enabled = false
+ else
+ if not enabled then
+ enableaction("shipouts","attributes.viewerlayers.handler")
+ end
+ enabled = true
+ end
+end
+
+function viewerlayers.forcesupport(value)
+ viewerlayers.supported = value
+ report_viewerlayers("viewerlayers are %ssupported",value and "" or "not ")
+ viewerlayers.enable(value)
+end
+
+local function register(name,lw) -- if not inimode redefine data[n] in first call
+ if not enabled then
+ viewerlayers.enable(true)
+ end
+ local stamp = f_stamp(name)
+ local n = registered[stamp]
+ if not n then
+ n = #values + 1
+ values[n] = name
+ registered[stamp] = n
+ listwise[n] = lw or false -- lw forces a used
+ end
+ return registered[stamp] -- == n
+end
+
+viewerlayers.register = register
+
+function viewerlayers.setfeatures(hasorder)
+ viewerlayers.hasorder = hasorder
+end
+
+local usestacker = true -- new, experimental
+
+function viewerlayers.start(name)
+ local a
+ if usestacker then
+ a = layerstacker.push(register(name) or unsetvalue)
+ else
+ insert(stack,texgetattribute(a_viewerlayer))
+ a = register(name) or unsetvalue
+ end
+ if global or scopes[name] == v_global then
+ scopes[a] = v_global -- messy but we don't know the attributes yet
+ texsetattribute("global",a_viewerlayer,a)
+ else
+ texsetattribute(a_viewerlayer,a)
+ end
+ -- or macro
+ texsettokenlist("currentviewerlayertoks",name)
+end
+
+function viewerlayers.stop()
+ local a
+ if usestacker then
+ a = layerstacker.pop()
+ else
+ a = remove(stack)
+ end
+ if not a then
+ -- error
+ elseif a >= 0 then
+ if global or scopes[a] == v_global then
+ texsetattribute("global",a_viewerlayer,a)
+ else
+ texsetattribute(a_viewerlayer,a)
+ end
+ texsettokenlist("currentviewerlayertoks",values[a] or "")
+ else
+ if global or scopes[a] == v_global then
+ texsetattribute("global",a_viewerlayer,unsetvalue)
+ else
+ texsetattribute(a_viewerlayer,unsetvalue)
+ end
+ texsettokenlist("currentviewerlayertoks","")
+ end
+end
+
+function viewerlayers.define(settings)
+ local tag = settings.tag
+ if not tag or tag == "" then
+ -- error
+ elseif not scopes[tag] then -- prevent duplicates
+ local title = settings.title
+ if not title or title == "" then
+ settings.title = tag
+ end
+ scopes[tag] = settings.scope or v_local
+ codeinjections.defineviewerlayer(settings)
+ end
+end
+
+function viewerlayers.definedlayoutcomponent(tag)
+ viewerlayers.define {
+ tag = tag,
+ title = utilities.strings.nice(tag),
+ visible = v_start,
+ editable = v_yes,
+ printable = v_yes,
+ }
+ return register(tag,true) -- true forces a use
+end
+
+function viewerlayers.cleanup()
+ layerstacker.clean()
+ -- todo
+end
+
+implement {
+ name = "cleanuplayers",
+ actions = viewerlayers.cleanup
+}
+
+implement {
+ name = "defineviewerlayer",
+ actions = viewerlayers.define,
+ arguments = {
+ {
+ { "tag" },
+ { "title" },
+ { "visible" },
+ { "editable" },
+ { "export" },
+ { "printable" },
+ { "scope" },
+ },
+ true
+ }
+}
+
+implement {
+ name = "definedlayoutcomponent",
+ actions = { viewerlayers.definedlayoutcomponent, context },
+ arguments = "string"
+}
+
+implement {
+ name = "startviewerlayer",
+ public = true,
+ protected = true,
+ actions = viewerlayers.start,
+ arguments = "optional",
+}
+
+implement {
+ name = "stopviewerlayer",
+ public = true,
+ protected = true,
+ actions = viewerlayers.stop
+}
+
+implement {
+ name = "registeredviewerlayer",
+ actions = { register, context },
+ arguments = { "string", true } -- true forces a use
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-lay.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-lay.mkxl
new file mode 100644
index 00000000000..00c6335337a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-lay.mkxl
@@ -0,0 +1,107 @@
+%D \module
+%D [ file=attr-lay,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Viewerlayers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Viewerlayers}
+
+%D Currently there is a limitation in mixed inline usage. This has to do with the fact
+%D that we have a stacked model but cannot determine where to revert to (as we can
+%D have AABBCCAA ranges). Maybe I'll solve that one day. It only affects nested inline
+%D layers and these make not much sense anyway. We'd have to store the complete nesting
+%D stack in the attribute in order to be able to cross pages and that demands a new
+%D mechanism.
+
+\unprotect
+
+\registerctxluafile{attr-lay}{autosuffix}
+
+% needs to work over stopitemize grouping etc
+
+\installcorenamespace{viewerlayer}
+
+\installcommandhandler \??viewerlayer {viewerlayer} \??viewerlayer
+
+\setupviewerlayer
+ [\c!state=\v!start,
+ \c!title=,
+ \c!export=\v!yes, % exportable is ugly
+ \c!printable=\v!yes,
+ \c!scope=\v!local, % maybe global but needs checking with layout
+ \c!method=\v!none]
+
+\appendtoks
+ \clf_defineviewerlayer
+ tag {\currentviewerlayer}%
+ title {\viewerlayerparameter\c!title}%
+ visible {\viewerlayerparameter\c!state}%
+ editable {\v!yes}%
+ export {\viewerlayerparameter\c!export}%
+ printable {\viewerlayerparameter\c!printable}%
+ scope {\viewerlayerparameter\c!scope}%
+ \relax
+ \doif{\viewerlayerparameter\c!method}\v!command
+ {\frozen\instance\setuxvalue{\e!start#1}{\startviewerlayer[\currentviewerlayer]}%
+ \frozen\instance\setuxvalue{\e!stop #1}{\stopviewerlayer}}%
+\to \everydefineviewerlayer
+
+% \startviewerlayer[#1] % defined at lua end
+% \stopviewerlayer % defined at lua end
+
+\permanent\protected\def\viewerlayer[#1]%
+ {\groupedcommand{\startviewerlayer[#1]}{\stopviewerlayer}}
+
+% some day we will keep this at the lua end as the info is only needed there
+
+\newtoks\currentviewerlayertoks % soon we can set macros at the lua end
+
+\def\currentviewerlayer{\the\currentviewerlayertoks}
+
+\appendtoks
+ \let\currentviewerlayer\empty
+\to \everybeforepagebody
+
+% layout components are implemented rather directly (speed)
+
+\installcorenamespace{layoutcomponentattribute}
+
+\mutable\let\layoutcomponentboxattribute\empty
+
+\permanent\def\attr_layoutcomponent_initialize#1%
+ {\edef\layoutcomponentboxattribute{\clf_definedlayoutcomponent{#1}}%
+ \edef\layoutcomponentboxattribute{attr \viewerlayerattribute \layoutcomponentboxattribute\relax}%
+ \gletcsname\??layoutcomponentattribute#1\endcsname\layoutcomponentboxattribute}
+
+\permanent\def\attr_layoutcomponent_set#1% make this faster
+ {\expandafter\let\expandafter\layoutcomponentboxattribute\csname\??layoutcomponentattribute#1\endcsname
+ \ifrelax\layoutcomponentboxattribute
+ \attr_layoutcomponent_initialize{#1}% get rid of { }
+ \fi}
+
+\permanent\def\attr_layoutcomponent_reset
+ {\let\layoutcomponentboxattribute\empty}
+
+\permanent\let\setlayoutcomponentattribute \gobbleoneargument
+\permanent\let\resetlayoutcomponentattribute\relax
+ \let\layoutcomponentboxattribute \empty
+
+\permanent\protected\def\showlayoutcomponents
+ {\enforced\let\setlayoutcomponentattribute \attr_layoutcomponent_set
+ \enforced\let\resetlayoutcomponentattribute\attr_layoutcomponent_reset}
+
+\protected\def\attr_layoutcomponent_cleanup
+ {\clf_cleanuplayers}
+
+\appendtoks
+ \attr_layoutcomponent_cleanup
+\to \everyshipout
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-mkr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-mkr.mkxl
new file mode 100644
index 00000000000..2209af24157
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-mkr.mkxl
@@ -0,0 +1,25 @@
+%D \module
+%D [ file=attr-mkr,
+%D version=2013.01.09,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Markers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Markers}
+
+\unprotect
+
+\registerctxluafile{attr-mkr}{}
+
+\permanent \protected\def\definemarker [#1]{\defineattribute[\s!marker:#1]}
+\permanent\tolerant\protected\def\setmarker [#1]#*[#2]{\dosetattribute{\s!marker:#1}{#2}}
+\permanent \protected\def\resetmarker [#1]{\dogetattribute{\s!marker:#1}}
+\permanent \def\boxmarker #1#2{attr \numexpr\dogetattributeid{\s!marker:#1}\numexpr \numexpr#2\relax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/attr-neg.mkxl b/Master/texmf-dist/tex/context/base/mkxl/attr-neg.mkxl
new file mode 100644
index 00000000000..c849e6bf6db
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/attr-neg.mkxl
@@ -0,0 +1,28 @@
+%D \module
+%D [ file=attr-neg,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Attribute Macros,
+%D subtitle=Negation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Attribute Macros / Negation}
+
+\registerctxluafile{attr-neg}{}
+
+\unprotect
+
+% positive and negative are preregistered
+
+\permanent\protected\def\startnegative{\clf_setnegative{\v!negative}}
+\permanent\protected\def\stopnegative {\clf_setnegative{\v!positive}}
+
+\permanent\protected\def\startpositive{\clf_setnegative{\v!positive}}
+\permanent\protected\def\stoppositive {\clf_setnegative{\v!negative}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-mth.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-mth.lmt
new file mode 100644
index 00000000000..73c09d79ecf
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-mth.lmt
@@ -0,0 +1,742 @@
+if not modules then modules = { } end modules ['back-exp-imp-mth'] = {
+ version = 1.001,
+ comment = "companion to back-exp.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local sub = string.sub
+local utfchar, utfvalues = utf.char, utf.values
+local setmetatableindex, concat = table.setmetatableindex, table.concat
+
+local structurestags = structures.tags
+local specifications = structurestags.specifications
+local locatedtag = structurestags.locatedtag
+
+local backend = structurestags.backend
+
+local setattribute = backend.setattribute
+local extras = backend.extras
+local checks = backend.checks
+local finalizers = backend.finalizers
+
+local implement = interfaces.implement
+
+do
+
+ local automathrows = true directives.register("export.math.autorows", function(v) automathrows = v end)
+ local automathapply = true directives.register("export.math.autoapply", function(v) automathapply = v end)
+ local automathnumber = true directives.register("export.math.autonumber", function(v) automathnumber = v end)
+ local automathstrip = true directives.register("export.math.autostrip", function(v) automathstrip = v end)
+
+ local functions = mathematics.categories.functions
+
+ local function collapse(di,i,data,ndata,detail,element)
+ local collapsing = di.data
+ if data then
+ di.element = element
+ di.detail = nil
+ i = i + 1
+ while i <= ndata do
+ local dn = data[i]
+ if dn.detail == detail then
+ collapsing[#collapsing+1] = dn.data[1]
+ dn.skip = "ignore"
+ i = i + 1
+ else
+ break
+ end
+ end
+ end
+ return i
+ end
+
+ local function collapse_mn(di,i,data,ndata)
+ -- this is tricky ... we need to make sure that we wrap in mrows if we want
+ -- to bypass this one
+ local collapsing = di.data
+ if data then
+ i = i + 1
+ while i <= ndata do
+ local dn = data[i]
+ local tg = dn.tg
+ if tg == "mn" then
+ collapsing[#collapsing+1] = dn.data[1]
+ dn.skip = "ignore"
+ i = i + 1
+ elseif tg == "mo" then
+ local d = dn.data[1]
+ if d == "." then
+ collapsing[#collapsing+1] = d
+ dn.skip = "ignore"
+ i = i + 1
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ end
+ return i
+ end
+
+ -- maybe delay __i__ till we need it
+
+ local apply_function = {
+ {
+ element = "mo",
+ -- comment = "apply function",
+ -- data = { utfchar(0x2061) },
+ data = { "&#x2061;" },
+ nature = "mixed",
+ }
+ }
+
+ local functioncontent = { }
+
+ setmetatableindex(functioncontent,function(t,k)
+ local v = { { content = k } }
+ t[k] = v
+ return v
+ end)
+
+ local dummy_nucleus = {
+ element = "mtext",
+ data = { content = "" },
+ nature = "inline",
+ comment = "dummy nucleus",
+ fulltag = "mtext>0"
+ }
+
+ local function accentchar(d)
+ for i=1,3 do
+ d = d.data
+ if not d then
+ return
+ end
+ d = d[1]
+ if not d then
+ return
+ end
+ local tg = d.tg
+ if tg == "mover" then
+ local s = specifications[d.fulltag]
+ local t = s.top
+ if t then
+ d = d.data[1]
+ local d1 = d.data[1]
+ d1.content = utfchar(t)
+ d.data = { d1 }
+ return d
+ end
+ elseif tg == "munder" then
+ local s = specifications[d.fulltag]
+ local b = s.bottom
+ if b then
+ d = d.data[1]
+ local d1 = d.data[1]
+ d1.content = utfchar(b)
+ d.data = { d1 }
+ return d
+ end
+ end
+ end
+ end
+
+ local no_mrow = {
+ mrow = true,
+ mfenced = true,
+ mfrac = true,
+ mroot = true,
+ msqrt = true,
+ mtable = true,
+ mi = true,
+ mo = true,
+ mn = true,
+ }
+
+ local function checkmath(root) -- we can provide utf.toentities as an option
+ local data = root.data
+ if data then
+ local ndata = #data
+ local roottg = root.tg
+ if roottg == "msubsup" then
+ -- kind of tricky: we have a diufferent order in display mode
+ local nucleus, superscript, subscript
+ if ndata > 3 then
+ -- error
+ else
+ for i=1,ndata do
+ local di = data[i]
+ if not di then
+ -- weird
+ elseif di.content then
+ -- text
+ else
+ local s = specifications[di.fulltag]
+ if s.subscript then
+ subscript = i
+ elseif s.superscript then
+ superscript = i
+ else
+ nucleus = i
+ end
+ end
+ end
+ if superscript or subscript then
+ -- we probably always have 3 anyway ... needs checking
+ local nuc = nucleus and data[nucleus]
+ local sub = subscript and data[subscript]
+ local sup = superscript and data[superscript]
+ local n = 0 -- play safe
+ if nuc then n = n + 1 ; data[n] = nuc end
+ if sub then n = n + 1 ; data[n] = sub end
+ if sup then n = n + 1 ; data[n] = sup end
+ end
+ end
+ -- elseif roottg == "msup" or roottg == "msub" then
+ -- -- m$^2$
+ -- if ndata == 1 then
+ -- local d = data[1]
+ -- data[2] = d
+ -- d.__i__ = 2
+ -- data[1] = dummy_nucleus
+ -- end
+ elseif roottg == "mfenced" then
+ local s = specifications[root.fulltag]
+ local l, m, r = s.left, s.middle, s.right
+ if l then
+ l = utfchar(l)
+ end
+ if m then
+ local t = { }
+ for i=1,#m do
+ t[i] = utfchar(m[i])
+ end
+ m = concat(t)
+ end
+ if r then
+ r = utfchar(r)
+ end
+ root.attributes = {
+ open = l,
+ separators = m,
+ close = r,
+ }
+ end
+ if ndata == 0 then
+ root.skip = "comment" -- get rid of weird artefacts
+ root.nota = "weird"
+ return
+ elseif ndata == 1 then
+ local d = data[1]
+ if not d or d == "" then
+ root.skip = "comment"
+ return
+ elseif d.content then
+ return
+ else -- if ndata == 1 then
+ local tg = d.tg
+ if automathrows and (roottg == "mrow" or roottg == "mtext") then
+ -- maybe just always ! check spec first
+ -- or we can have chesks.* for each as we then can flatten
+ if no_mrow[tg] then
+ root.skip = "comment"
+ end
+ elseif roottg == "mo" then
+ if tg == "mo" then
+ root.skip = "comment"
+ end
+ end
+ end
+ end
+ local i = 1
+ while i <= ndata do -- -- -- TOO MUCH NESTED CHECKING -- -- --
+ local di = data[i]
+ if di and not di.content then
+ local tg = di.tg
+ if tg == "math" then
+ -- di.element = "mrow" -- when properties
+ di.skip = "comment"
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mover" then
+ local s = specifications[di.fulltag]
+ if s.accent then
+ local t = s.top
+ local d = di.data
+ -- todo: accent = "false" (for scripts like limits)
+ di.attributes = {
+ accent = "true",
+ }
+ -- todo: p.topfixed
+ if t then
+ -- mover
+ d[1].data[1].content = utfchar(t)
+ di.data = { d[2], d[1] }
+ end
+ else
+ -- can't happen
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "munder" then
+ local s = specifications[di.fulltag]
+ if s.accent then
+ local b = s.bottom
+ local d = di.data
+ -- todo: accent = "false" (for scripts like limits)
+ di.attributes = {
+ accent = "true",
+ }
+ -- todo: p.bottomfixed
+ if b then
+ -- munder
+ d[2].data[1].content = utfchar(b)
+ end
+ else
+ -- can't happen
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "munderover" then
+ local s = specifications[di.fulltag]
+ if s.accent then
+ local t = s.top
+ local b = s.bottom
+ local d = di.data
+ -- todo: accent = "false" (for scripts like limits)
+ -- todo: accentunder = "false" (for scripts like limits)
+ di.attributes = {
+ accent = "true",
+ accentunder = "true",
+ }
+ -- todo: p.topfixed
+ -- todo: p.bottomfixed
+ if t and b then
+ -- munderover
+ d[1].data[1].content = utfchar(t)
+ d[3].data[1].content = utfchar(b)
+ di.data = { d[2], d[3], d[1] }
+ else
+ -- can't happen
+ end
+ else
+ -- can't happen
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mstacker" then
+ local d = di.data
+ local d1 = d[1]
+ local d2 = d[2]
+ local d3 = d[3]
+ local t1 = d1 and d1.tg
+ local t2 = d2 and d2.tg
+ local t3 = d3 and d3.tg
+ local m = nil -- d1.data[1]
+ local t = nil
+ local b = nil
+ -- only accent when top / bot have stretch
+ -- normally we flush [base under over] which is better for tagged pdf
+ if t1 == "mstackermid" then
+ m = accentchar(d1) -- or m
+ if t2 == "mstackertop" then
+ if t3 == "mstackerbot" then
+ t = accentchar(d2)
+ b = accentchar(d3)
+ di.element = "munderover"
+ di.data = { m or d1.data[1], b or d3.data[1], t or d2.data[1] }
+ else
+ t = accentchar(d2)
+ di.element = "mover"
+ di.data = { m or d1.data[1], t or d2.data[1] }
+ end
+ elseif t2 == "mstackerbot" then
+ if t3 == "mstackertop" then
+ b = accentchar(d2)
+ t = accentchar(d3)
+ di.element = "munderover"
+ di.data = { m or d1.data[1], t or d3.data[1], m, b or d2.data[1] }
+ else
+ b = accentchar(d2)
+ di.element = "munder"
+ di.data = { m or d1.data[1], b or d2.data[1] }
+ end
+ else
+ -- can't happen
+ end
+ else
+ -- can't happen
+ end
+ if t or b then
+ di.attributes = {
+ accent = t and "true" or nil,
+ accentunder = b and "true" or nil,
+ }
+ di.detail = nil
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mroot" then
+ local data = di.data
+ local size = #data
+ if size == 1 then
+ -- else firefox complains ... code in math-tag (for pdf tagging)
+ di.element = "msqrt"
+ elseif size == 2 then
+ data[1], data[2] = data[2], data[1]
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "break" then
+ di.skip = "comment"
+ i = i + 1
+ elseif tg == "mtext" then
+ -- this is only needed for unboxed mtexts ... all kind of special
+ -- tex border cases and optimizations ... trial and error
+ local data = di.data
+ if #data > 1 then
+ for i=1,#data do
+ local di = data[i]
+ local content = di.content
+ if content then
+ data[i] = {
+ element = "mtext",
+ nature = "inline",
+ data = { di },
+ n = 0,
+ }
+ elseif di.tg == "math" then
+ local di = di.data[1]
+ if di then
+ data[i] = di
+ checkmath(di)
+ end
+ end
+ end
+ di.element = "mrow"
+ -- di.tg = "mrow"
+ -- di.nature = "inline"
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mrow" and detail then -- hm, falls through
+ di.detail = nil
+ checkmath(di)
+ di = {
+ element = "maction",
+ nature = "display",
+ attributes = { actiontype = detail },
+ data = { di },
+ n = 0,
+ }
+ data[i] = di
+ i = i + 1
+ else
+ local category = di.mathcategory
+ if category then
+ -- no checkmath(di) here
+ if category == 1 then -- mo
+ i = collapse(di,i,data,ndata,detail,"mo")
+ elseif category == 2 then -- mi
+ i = collapse(di,i,data,ndata,detail,"mi")
+ elseif category == 3 then -- mn
+ i = collapse(di,i,data,ndata,detail,"mn")
+ elseif category == 4 then -- ms
+ i = collapse(di,i,data,ndata,detail,"ms")
+ elseif category >= 1000 then
+ local apply = category >= 2000
+ if apply then
+ category = category - 1000
+ end
+ if tg == "mi" then -- function
+ if roottg == "mrow" then
+ root.skip = "comment"
+ root.element = "function"
+ end
+ i = collapse(di,i,data,ndata,detail,"mi")
+ local tag = functions[category]
+ if tag then
+ di.data = functioncontent[tag]
+ end
+ if apply then
+ di.after = apply_function
+ elseif automathapply then -- make function
+ local following
+ if i <= ndata then
+ -- normally not the case
+ following = data[i]
+ else
+ local parent = di.__p__ -- == root
+ if parent.tg == "mrow" then
+ parent = parent.__p__
+ end
+ local index = parent.__i__
+ following = parent.data[index+1]
+ end
+ if following then
+ local tg = following.tg
+ if tg == "mrow" or tg == "mfenced" then -- we need to figure out the right condition
+ di.after = apply_function
+ end
+ end
+ end
+ else -- some problem
+ checkmath(di)
+ i = i + 1
+ end
+ else
+ checkmath(di)
+ i = i + 1
+ end
+ elseif automathnumber and tg == "mn" then
+ checkmath(di)
+ i = collapse_mn(di,i,data,ndata)
+ else
+ checkmath(di)
+ i = i + 1
+ end
+ end
+ else -- can be string or boolean
+ if parenttg ~= "mtext" and di == " " then
+ data[i] = false
+ end
+ i = i + 1
+ end
+ end
+ end
+ end
+
+ local function stripmath(di)
+ if not di then
+ --
+ elseif di.content then
+ return di
+ else
+ local tg = di.tg
+ if tg == "mtext" or tg == "ms" then
+ return di
+ else
+ local data = di.data
+ local ndata = #data
+ local n = 0
+ for i=1,ndata do
+ local d = data[i]
+ if d and not d.content then
+ d = stripmath(d)
+ end
+ if d then
+ local content = d.content
+ if not content then
+ n = n + 1
+ d.__i__ = n
+ data[n] = d
+ elseif content == " " or content == "" then
+ if di.tg == "mspace" then
+ -- we append or prepend a space to a preceding or following mtext
+ local parent = di.__p__
+ local index = di.__i__ -- == i
+ local data = parent.data
+ if index > 1 then
+ local d = data[index-1]
+ if d.tg == "mtext" then
+ local dd = d.data
+ local dn = dd[#dd]
+ local dc = dn.content
+ if dc then
+ dn.content = dc .. content
+ end
+ end
+ elseif index < ndata then
+ local d = data[index+1]
+ if d.tg == "mtext" then
+ local dd = d.data
+ local dn = dd[1]
+ local dc = dn.content
+ if dc then
+ dn.content = content .. dc
+ end
+ end
+ end
+ end
+ else
+ n = n + 1
+ data[n] = d
+ end
+ end
+ end
+ for i=ndata,n+1,-1 do
+ data[i] = nil
+ end
+ if #data > 0 then
+ return di
+ end
+ end
+ end
+ end
+
+ function checks.math(di)
+ if di.skip == "comment" then
+ -- already done, kind of weird, happens in mathmatrix, maybe some collapse
+ -- issue that i need to look into
+ else
+ local specification = specifications[di.fulltag]
+ local mode = specification and specification.mode == "display" and "block" or "inline"
+ di.attributes = {
+ ["display"] = mode,
+ ["xmlns:m"] = mathmlns,
+ }
+ -- can be option if needed:
+ if mode == "inline" then
+ -- di.nature = "mixed" -- else spacing problem (maybe inline)
+ di.nature = "inline" -- we need to catch x$X$x and x $X$ x
+ else
+ di.nature = "display"
+ end
+ if automathstrip then
+ stripmath(di)
+ end
+ checkmath(di)
+ end
+ end
+
+ -- this one can replace some of the previous code .. todo (test on mathmatrix)
+
+ -- ignore with no data can be removed
+
+ local function checked(d)
+ local n = #d
+ if n == 1 then
+ local di = d[1]
+ local tg = di.tg
+ if tg == "ignore" then
+ -- todo: we can move ignore's data one level up
+ return 1
+ elseif di.content then
+ return 1
+ else
+ local dd = di.data
+ if #dd > 0 and checked(dd) > 0 then
+ return 1
+ else
+ return 0
+ end
+ end
+ else
+ local m = 0
+ for i=1,n do
+ local di = d[i]
+ local tg = di.tg
+ if tg == "ignore" then
+ -- skip
+ elseif di.content then
+ m = m + 1
+ d[m] = di
+ else
+ local dd = di.data
+ if #dd > 0 and checked(dd) > 0 then
+ m = m + 1
+ d[m] = di
+ end
+ end
+ end
+ if m < n then
+ for i=n,m+1,-1 do
+ d[i] = nil
+ end
+ end
+ return m
+ end
+ end
+
+ function checks.mrow(di)
+ -- local d = di.data
+ -- if d then
+ -- checked(d)
+ -- end
+ end
+
+ -- we can move more checks here
+
+ local function flatten(di)
+ local r = di.__p__
+ while r do
+ local d = r.data
+ local n = #d
+ if d and n > 1 then
+ n = checked(d)
+ end
+ local tg = r.tg
+ if n == 1 and (tg == "mtext" or tg == "mrow") then
+ r.skip = "comment" -- weird error
+ r = r.__p__
+ else
+ break
+ end
+ end
+ end
+
+ function checks.mtable(di)
+ flatten(di)
+ local d = di.data
+ for i=1,#d do
+ local d = d[i]
+ if d.tg == "mtr" then
+ local d = d.data
+ for i=1,#d do
+ local d = d[i]
+ if d.tg == "mtd" then
+ -- okay
+ elseif d.content then
+ d.content = ""
+ else
+ d.skip = "comment" -- weird error
+ end
+ end
+ elseif d.content then
+ d.content = ""
+ else
+ d.skip = "comment" -- weird error
+ end
+ end
+ end
+
+ do
+
+ local a, z, A, Z = 0x61, 0x7A, 0x41, 0x5A
+
+ function extras.mi(di,element,n,fulltag) -- check with content
+ local str = di.data[1].content
+ if str and sub(str,1,1) ~= "&" then -- hack but good enough (maybe gsub op eerste)
+ for v in utfvalues(str) do
+ if (v >= a and v <= z) or (v >= A and v <= Z) then
+ local a = di.attributes
+ if a then
+ a.mathvariant = "normal"
+ else
+ di.attributes = { mathvariant = "normal" }
+ end
+ end
+ end
+ end
+ end
+
+ end
+
+ function extras.msub(di,element,n,fulltag)
+ -- m$^2$
+ local data = di.data
+ if #data == 1 then
+ local d = data[1]
+ data[2] = d
+ d.__i__ = 2
+ data[1] = dummy_nucleus
+ end
+ end
+
+ extras.msup = extras.msub
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-ref.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-ref.lmt
new file mode 100644
index 00000000000..25682f8edfa
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-ref.lmt
@@ -0,0 +1,261 @@
+if not modules then modules = { } end modules ['back-exp-imp-ref'] = {
+ version = 1.001,
+ comment = "companion to back-exp.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- quite some code deals with exporting references --
+
+-- links:
+--
+-- url :
+-- file :
+-- internal : automatic location
+-- location : named reference
+
+-- references:
+--
+-- implicit : automatic reference
+-- explicit : named reference
+
+local tonumber = tonumber
+local lpegmatch = lpeg.match
+local insert = table.insert
+
+local references = structures.references
+
+local structurestags = structures.tags
+local specifications = structurestags.specifications
+local locatedtag = structurestags.locatedtag
+
+local backend = structurestags.backend
+
+local setattribute = backend.setattribute
+local extras = backend.extras
+local fixes = backend.fixes
+local referencehash = backend.referencehash
+local destinationhash = backend.destinationhash
+
+local implement = interfaces.implement
+
+local evaluators = { }
+local specials = { }
+local explicits = { }
+
+evaluators.inner = function(di,var)
+ local inner = var.inner
+ if inner then
+ setattribute(di,"location",inner,true)
+ end
+end
+
+evaluators.outer = function(di,var)
+ local file, url = references.checkedfileorurl(var.outer,var.outer)
+ if url then
+ setattribute(di,"url",url,true)
+ elseif file then
+ setattribute(di,"file",file,true)
+ end
+end
+
+evaluators["outer with inner"] = function(di,var)
+ local file = references.checkedfile(var.f)
+ if file then
+ setattribute(di,"file",file,true)
+ end
+ local inner = var.inner
+ if inner then
+ setattribute(di,"inner",inner,true)
+ end
+end
+
+evaluators.special = function(di,var)
+ local handler = specials[var.special]
+ if handler then
+ handler(di,var)
+ end
+end
+
+do
+
+ evaluators["special outer with operation"] = evaluators.special
+ evaluators["special operation"] = evaluators.special
+ evaluators["special operation with arguments"] = evaluators.special
+
+ function specials.url(di,var)
+ local url = references.checkedurl(var.operation)
+ if url and url ~= "" then
+ setattribute(di,"url",url,true)
+ end
+ end
+
+ function specials.file(di,var)
+ local file = references.checkedfile(var.operation)
+ if file and file ~= "" then
+ setattribute(di,"file",file,true)
+ end
+ end
+
+ function specials.fileorurl(di,var)
+ local file, url = references.checkedfileorurl(var.operation,var.operation)
+ if url and url ~= "" then
+ setattribute(di,"url",url,true)
+ elseif file and file ~= "" then
+ setattribute(di,"file",file,true)
+ end
+ end
+
+ function specials.internal(di,var)
+ local internal = references.checkedurl(var.operation)
+ if internal then
+ setattribute(di,"location",internal)
+ end
+ end
+
+ local function adddestination(di,references) -- todo: specials -> exporters and then concat
+ if references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local prefix = references.prefix
+ if prefix and prefix ~= "" then
+ setattribute(di,"prefix",prefix,true)
+ end
+ setattribute(di,"destination",reference,true)
+ for i=1,#references do
+ local r = references[i]
+ local e = evaluators[r.kind]
+ if e then
+ e(di,r)
+ end
+ end
+ end
+ end
+ end
+
+ function extras.addimplicit(di,references)
+ if references then
+ local internal = references.internal
+ if internal then
+ setattribute(di,"implicit",internal)
+ end
+ end
+ end
+
+ function extras.addinternal(di,references)
+ if references then
+ local internal = references.internal
+ if internal then
+ setattribute(di,"internal",internal)
+ end
+ end
+ end
+
+ local p_firstpart = lpeg.Cs((1-lpeg.P(","))^0)
+
+ local function addreference(di,references)
+ if references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local prefix = references.prefix
+ if prefix and prefix ~= "" then
+ setattribute(di,"prefix",prefix)
+ end
+ setattribute(di,"reference",reference,true)
+ setattribute(di,"explicit",lpegmatch(p_firstpart,reference),true)
+ end
+ local internal = references.internal
+ if internal and internal ~= "" then
+ setattribute(di,"implicit",internal)
+ end
+ end
+ end
+
+ local function link(di,element,n,fulltag)
+ -- for instance in lists a link has nested elements and no own text
+ local reference = referencehash[fulltag]
+ if reference then
+ adddestination(di,structures.references.get(reference))
+ return true
+ else
+ local data = di.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ if di then
+ local fulltag = di.fulltag
+ if fulltag and link(di,element,n,fulltag) then
+ return true
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function reference(di,element,n,fulltag)
+ local destination = destinationhash[fulltag]
+ if destination then
+ local d = structures.references.internals[destination]
+ if d then
+ addreference(di,d.references)
+ return true
+ else
+ return false
+ end
+ else
+ local data = di.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ if di then
+ local fulltag = di.fulltag
+ if fulltag and reference(di,element,n,fulltag) then
+ return true
+ end
+ end
+ end
+ end
+ end
+ end
+
+ extras.adddestination = adddestination
+ extras.addreference = addreference
+
+ extras.link = link
+ extras.reference = reference
+
+end
+
+do
+
+ function fixes.linenumber(di,data,i)
+ local ni = data[i+1]
+ if ni then
+ if ni.data then
+ while true do
+ local d = ni.data[1]
+ if d then
+ local e = d.element
+ if e then
+ if e == "line" or e == "verbatimline" then
+ insert(d.data,1,di)
+ data[i] = false
+ return
+ else
+ ni = d
+ end
+ else
+ return
+ end
+ else
+ return
+ end
+ end
+ end
+ end
+ end
+
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-tag.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-tag.lmt
new file mode 100644
index 00000000000..73b7b5b47e6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-exp-imp-tag.lmt
@@ -0,0 +1,846 @@
+if not modules then modules = { } end modules ['back-exp-imp-tag'] = {
+ version = 1.001,
+ comment = "companion to back-exp.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Because we run into the 200 locals limit we now split the file into smaller
+-- parts.
+
+local tonumber = tonumber
+local todimen = number.todimen
+local sortedhash, setmetatableindex, concat, insert = table.sortedhash, table.setmetatableindex, table.concat, table.insert
+local settings_to_hash = utilities.parsers.settings_to_hash
+local lpegmatch = lpeg.match
+local formatters = string.formatters
+
+local references = structures.references
+local structurestags = structures.tags
+local taglist = structurestags.taglist
+local specifications = structurestags.specifications
+local properties = structurestags.properties
+local locatedtag = structurestags.locatedtag
+
+local backend = structurestags.backend
+
+local setattribute = backend.setattribute
+local extras = backend.extras
+local checks = backend.checks
+local fixes = backend.fixes
+local listdata = backend.listdata
+local finalizers = backend.finalizers
+local usedstyles = backend.usedstyles -- for now
+local usedimages = backend.usedimages -- for now
+local referencehash = backend.referencehash
+local destinationhash = backend.destinationhash
+
+local implement = interfaces.implement
+
+do
+
+ local itemgroups = { }
+
+ local function setitemgroup(packed,level,symbol)
+ itemgroups[locatedtag("itemgroup")] = {
+ packed = packed,
+ symbol = symbol,
+ level = level,
+ }
+ end
+
+ local function setitem(kind)
+ itemgroups[locatedtag("item")] = {
+ kind = kind,
+ }
+ end
+
+ function extras.itemgroup(di,element,n,fulltag)
+ local hash = itemgroups[fulltag]
+ if hash then
+ setattribute(di,"packed",hash.packed and "yes" or nil)
+ setattribute(di,"symbol",hash.symbol)
+ setattribute(di,"level",hash.level)
+ end
+ end
+
+ function extras.item(di,element,n,fulltag)
+ local hash = itemgroups[fulltag]
+ if hash then
+ local kind = hash.kind
+ if kind and kind ~= "" then
+ setattribute(di,"kind",kind)
+ end
+ end
+ end
+
+ implement {
+ name = "settagitemgroup",
+ actions = setitemgroup,
+ arguments = { "boolean", "integer", "string" }
+ }
+
+ implement {
+ name = "settagitem",
+ actions = setitem,
+ arguments = "string"
+ }
+
+ structurestags.setitemgroup = setitemgroup
+ structurestags.setitem = setitem
+
+end
+
+do
+
+ local registered = structures.sections.registered
+
+ local function resolve(di,element,n,fulltag)
+ local data = listdata[fulltag]
+ if data then
+ extras.addreference(di,data.references)
+ return true
+ else
+ local data = di.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ if di then
+ local ft = di.fulltag
+ if ft and resolve(di,element,n,ft) then
+ return true
+ end
+ end
+ end
+ end
+ end
+ end
+
+ function extras.section(di,element,n,fulltag)
+ local r = registered[specifications[fulltag].detail]
+ if r then
+ setattribute(di,"level",r.level)
+ end
+ resolve(di,element,n,fulltag)
+ end
+
+ local floats = { }
+
+ local function setfloat(options,method)
+ floats[locatedtag("float")] = {
+ options = options,
+ method = method,
+ }
+ end
+
+ function extras.float(di,element,n,fulltag)
+ local hash = floats[fulltag]
+ if hash then
+ local method = hash.method
+ if not method or method == "" then
+ method = "here"
+ end
+ setattribute(di,"method",method)
+ local options = hash.options
+ if options and options ~= "" then
+ options = settings_to_hash(options)
+ options[method] = nil
+ options = concat(sortedkeys(options),",")
+ if #options > 0 then
+ setattribute(di,"options",options)
+ end
+ end
+ end
+ resolve(di,element,n,fulltag)
+ end
+
+ implement {
+ name = "settagfloat",
+ actions = setfloat,
+ arguments = "2 strings",
+ }
+
+ structurestags.setfloat = setfloat
+
+end
+
+do
+
+ local registered = { }
+
+ local function setformulacontent(n)
+ registered[locatedtag("formulacontent")] = {
+ n = n,
+ }
+ end
+
+ function extras.formulacontent(di,element,n,fulltag)
+ local r = registered[fulltag]
+ if r then
+ setattribute(di,"n",r.n)
+ end
+ end
+
+ implement {
+ name = "settagformulacontent",
+ actions = setformulacontent,
+ arguments = "integer",
+ }
+
+ structurestags.setformulacontent = setformulacontent
+
+end
+
+do
+
+ local symbols = { }
+
+ local function settagdelimitedsymbol(symbol)
+ symbols[locatedtag("delimitedsymbol")] = {
+ symbol = symbol,
+ }
+ end
+
+ function extras.delimitedsymbol(di,element,n,fulltag)
+ local hash = symbols[fulltag]
+ if hash then
+ setattribute(di,"symbol",hash.symbol or nil)
+ end
+ end
+
+ implement {
+ name = "settagdelimitedsymbol",
+ actions = settagdelimitedsymbol,
+ arguments = "string"
+ }
+
+ structurestags.settagdelimitedsymbol = settagdelimitedsymbol
+
+end
+
+
+do
+
+ local symbols = { }
+
+ local function settagsubsentencesymbol(symbol)
+ symbols[locatedtag("subsentencesymbol")] = {
+ symbol = symbol,
+ }
+ end
+
+ function extras.subsentencesymbol(di,element,n,fulltag)
+ local hash = symbols[fulltag]
+ if hash then
+ setattribute(di,"symbol",hash.symbol or nil)
+ end
+ end
+
+ implement {
+ name = "settagsubsentencesymbol",
+ actions = settagsubsentencesymbol,
+ arguments = "string"
+ }
+
+ structurestags.settagsubsentencesymbol = settagsubsentencesymbol
+
+end
+
+do
+
+ local synonyms = { }
+ local sortings = { }
+
+ local function setsynonym(tag)
+ synonyms[locatedtag("synonym")] = tag
+ end
+
+ function extras.synonym(di,element,n,fulltag)
+ local tag = synonyms[fulltag]
+ if tag then
+ setattribute(di,"tag",tag)
+ end
+ end
+
+ local function setsorting(tag)
+ sortings[locatedtag("sorting")] = tag
+ end
+
+ function extras.sorting(di,element,n,fulltag)
+ local tag = sortings[fulltag]
+ if tag then
+ setattribute(di,"tag",tag)
+ end
+ end
+
+ implement {
+ name = "settagsynonym",
+ actions = setsynonym,
+ arguments = "string"
+ }
+
+ implement {
+ name = "settagsorting",
+ actions = setsorting,
+ arguments = "string"
+ }
+
+ structurestags.setsynonym = setsynonym
+ structurestags.setsorting = setsorting
+
+end
+
+do
+
+ local descriptions = { }
+ local symbols = { }
+ local linked = { }
+
+ -- we could move the notation itself to the first reference (can be an option)
+
+ local function setnotation(tag,n) -- needs checking (is tag needed)
+ -- we can also use the internals hash or list
+ local nd = structures.notes.get(tag,n)
+ if nd then
+ local references = nd.references
+ descriptions[references and references.internal] = locatedtag("description")
+ end
+ end
+
+ local function setnotationsymbol(tag,n) -- needs checking (is tag needed)
+ local nd = structures.notes.get(tag,n) -- todo: use listdata instead
+ if nd then
+ local references = nd.references
+ symbols[references and references.internal] = locatedtag("descriptionsymbol")
+ end
+ end
+
+ function finalizers.descriptions(tree)
+ local n = 0
+ for id, tag in sortedhash(descriptions) do
+ local sym = symbols[id]
+ if sym then
+ n = n + 1
+ linked[tag] = n
+ linked[sym] = n
+ end
+ end
+ end
+
+ function extras.description(di,element,n,fulltag)
+ local id = linked[fulltag]
+ if id then
+ setattribute(di,"insert",id)
+ end
+ end
+
+ function extras.descriptionsymbol(di,element,n,fulltag)
+ local id = linked[fulltag]
+ if id then
+ setattribute(di,"insert",id)
+ end
+ end
+
+ implement {
+ name = "settagnotation",
+ actions = setnotation,
+ arguments = { "string", "integer" }
+ }
+
+ implement {
+ name = "settagnotationsymbol",
+ actions = setnotationsymbol,
+ arguments = { "string", "integer" }
+ }
+
+ structurestags.setnotation = setnotation
+ structurestags.setnotationsymbol = setnotationsymbol
+
+end
+
+
+do
+
+ local strippedtag = structurestags.strip -- we assume global styles
+
+ local highlight = { }
+ local construct = { }
+
+ usedstyles.highlight = highlight
+ usedstyles.construct = construct
+
+ local function sethighlight(name,style,color,mode)
+ if not highlight[name] then
+ highlight[name] = {
+ style = style,
+ color = color,
+ mode = mode == 1 and "display" or nil,
+ }
+ end
+ end
+
+ local function setconstruct(name,style,color,mode)
+ if not construct[name] then
+ construct[name] = {
+ style = style,
+ color = color,
+ mode = mode == 1 and "display" or nil,
+ }
+ end
+ end
+
+ implement {
+ name = "settagconstruct",
+ actions = setconstruct,
+ arguments = { "string", "string", "integer", "integer" }
+ }
+
+ implement {
+ name = "settaghighlight",
+ actions = sethighlight,
+ arguments = { "string", "string", "integer", "integer" }
+ }
+
+ structurestags.sethighlight = sethighlight
+ structurestags.setconstruct = setconstruct
+
+end
+
+do
+
+ local f_id = formatters["%s-%s"]
+ local image = { }
+ usedimages.image = image
+
+ structurestags.usewithcare.images = image
+
+ local function setfigure(name,used,page,width,height,label)
+ local fulltag = locatedtag("image")
+ local spec = specifications[fulltag]
+ if spec then
+ local page = tonumber(page)
+ image[fulltag] = {
+ id = f_id(spec.tagname,spec.tagindex),
+ name = name,
+ used = used,
+ page = page and page > 1 and page or nil,
+ width = todimen(width, "cm","%0.3F%s"),
+ height = todimen(height,"cm","%0.3F%s"),
+ label = label,
+ }
+ else
+ -- we ignore images in layers in the background / pagebody
+ end
+ end
+
+ function extras.image(di,element,n,fulltag)
+ local data = image[fulltag]
+ if data then
+ setattribute(di,"name",data.name)
+ setattribute(di,"page",data.page)
+ setattribute(di,"id",data.id)
+ setattribute(di,"width",data.width)
+ setattribute(di,"height",data.height)
+ setattribute(di,"label",data.height)
+ end
+ end
+
+ implement {
+ name = "settagfigure",
+ actions = setfigure,
+ arguments = { "string", "string", "string", "dimen", "dimen", "string" }
+ }
+
+ structurestags.setfigure = setfigure
+
+end
+
+do
+
+ local combinations = { }
+
+ local function setcombination(nx,ny)
+ combinations[locatedtag("combination")] = {
+ nx = nx,
+ ny = ny,
+ }
+ end
+
+ function extras.combination(di,element,n,fulltag)
+ local data = combinations[fulltag]
+ if data then
+ setattribute(di,"nx",data.nx)
+ setattribute(di,"ny",data.ny)
+ end
+ end
+
+ implement {
+ name = "settagcombination",
+ actions = setcombination,
+ arguments = { "integer", "integer" }
+ }
+
+ structurestags.setcombination = setcombination
+
+end
+
+do
+
+ local function hascontent(data)
+ for i=1,#data do
+ local di = data[i]
+ if not di or di.tg == "ignore" then
+ --
+ else
+ local content = di.content
+ if content == " " then
+ --
+ elseif content then
+ return true
+ else
+ local d = di.data
+ if d and #d > 0 and hascontent(d) then
+ return true
+ end
+ end
+ end
+ end
+ end
+
+ local tabledata = { }
+
+ local function settablecell(rows,columns,align)
+ if align > 0 or rows > 1 or columns > 1 then -- or kind > 0
+ tabledata[locatedtag("tablecell")] = {
+ rows = rows,
+ columns = columns,
+ align = align,
+ }
+ end
+ end
+
+ local function gettablecell(fulltag)
+ return tabledata[fulltag]
+ end
+
+ function extras.tablecell(di,element,n,fulltag)
+ local hash = tabledata[fulltag]
+ if hash then
+ local columns = hash.columns
+ if columns and columns > 1 then
+ setattribute(di,"columns",columns)
+ end
+ local rows = hash.rows
+ if rows and rows > 1 then
+ setattribute(di,"rows",rows)
+ end
+ local align = hash.align
+ if not align or align == 0 then
+ -- normal
+ elseif align == 1 then -- use numbertoalign here
+ setattribute(di,"align","flushright")
+ elseif align == 2 then
+ setattribute(di,"align","middle")
+ elseif align == 3 then
+ setattribute(di,"align","flushleft")
+ end
+ end
+ end
+
+ local tabulatedata = { }
+
+ local function settabulatecell(align,kind)
+ if align > 0 or kind > 0 then
+ tabulatedata[locatedtag("tabulatecell")] = {
+ align = align,
+ kind = kind, -- 1 = bold head
+ }
+ end
+ end
+
+ local function gettabulatecell(fulltag)
+ return tabulatedata[fulltag]
+ end
+
+ function extras.tabulate(di,element,n,fulltag)
+ local data = di.data
+ for i=1,#data do
+ local di = data[i]
+ if di.tg == "tabulaterow" and not hascontent(di.data) then
+ di.element = "" -- or simply remove
+ end
+ end
+ end
+
+ function extras.tabulatecell(di,element,n,fulltag)
+ local hash = tabulatedata[fulltag]
+ if hash then
+ local align = hash.align
+ if not align or align == 0 then
+ -- normal
+ elseif align == 1 then
+ setattribute(di,"align","flushleft")
+ elseif align == 2 then
+ setattribute(di,"align","flushright")
+ elseif align == 3 then
+ setattribute(di,"align","middle")
+ end
+ local kind = hash.kind
+ if kind == 1 then
+ setattribute(di,"kind","strong")
+ elseif kind == 2 then
+ setattribute(di,"kind","equals")
+ end
+ end
+ end
+
+ implement {
+ name = "settagtablecell",
+ actions = settablecell,
+ arguments = { "integer", "integer", "integer" }
+ }
+
+ implement {
+ name = "settagtabulatecell",
+ actions = settabulatecell,
+ arguments = { "integer", "integer" },
+ }
+
+ structurestags.settablecell = settablecell
+ structurestags.gettablecell = gettablecell
+ structurestags.settabulatecell = settabulatecell
+ structurestags.gettabulatecell = gettabulatecell
+
+end
+
+do
+
+ -- todo: internal is already hashed
+
+ local p_stripper = lpeg.patterns.stripper
+
+ local function setregister(tag,n) -- check if tag is needed
+ local data = structures.registers.get(tag,n)
+ if data then
+ referencehash[locatedtag("registerlocation")] = data
+ end
+ end
+
+ function extras.registerlocation(di,element,n,fulltag)
+ local data = referencehash[fulltag]
+ if type(data) == "table" then
+ extras.addinternal(di,data.references)
+ return true
+ else
+ -- needs checking, probably bookmarks
+ end
+ end
+
+ function extras.registerpages(di,element,n,fulltag) -- ignorebreaks
+ local data = di.data
+ for i=1,#data do
+ local d = data[i]
+ if d.content == " " then
+ d.content = ""
+ end
+ end
+ end
+
+ function extras.registerseparator(di,element,n,fulltag) -- ignorespaces
+ local data = di.data
+ for i=1,#data do
+ local d = data[i]
+ local c = d.content
+ if type(c) == "string" then
+ d.content = lpegmatch(p_stripper,c)
+ end
+ end
+ end
+
+ implement {
+ name = "settagregister",
+ actions = setregister,
+ arguments = { "string", "integer" }
+ }
+
+ structurestags.setregister = setregister
+
+end
+
+do
+
+ -- todo: internal is already hashed
+
+ local function setlist(n)
+ local data = structures.lists.getresult(n)
+ if data then
+ referencehash[locatedtag("listitem")] = data
+ end
+ end
+
+ function extras.listitem(di,element,n,fulltag)
+ local data = referencehash[fulltag]
+ if data then
+ extras.addinternal(di,data.references)
+ return true
+ end
+ end
+
+ implement {
+ name = "settaglist",
+ actions = setlist,
+ arguments = "integer"
+ }
+
+ structurestags.setlist = setlist
+
+end
+
+do
+
+ local usedpublications = { }
+ local tagsindatasets = setmetatableindex("table")
+ local serialize = false
+
+ local function setpublication(dataset,tag,rendering)
+ usedpublications[locatedtag("publication")] = {
+ dataset = dataset,
+ tag = tag,
+ rendering = rendering
+ }
+ tagsindatasets[dataset][tag] = true
+ if not serialize then
+ structures.tags.registerextradata("btx",function()
+ local t = { "<btxdata>"}
+ for dataset, used in sortedhash(tagsindatasets) do
+ t[#t+1] = publications.converttoxml(dataset,true,false,true,false,true,true)
+ end
+ t[#t+1] = "</btxdata>"
+ return concat(t,"\n")
+ end)
+ end
+ end
+
+ function extras.publication(di,element,n,fulltag)
+ local hash = usedpublications[fulltag]
+ if hash then
+ setattribute(di,"dataset",hash.dataset)
+ setattribute(di,"tag",hash.tag)
+ end
+ end
+
+ implement {
+ name = "settagpublication",
+ actions = setpublication,
+ arguments = "2 strings"
+ }
+
+ structurestags.setpublication = setpublication
+
+end
+
+do
+
+ local usedparagraphs = { }
+
+ local function setparagraph(align)
+ if align ~= "" then
+ usedparagraphs[locatedtag("paragraph")] = {
+ align = align,
+ }
+ end
+ end
+
+ function extras.paragraph(di,element,n,fulltag)
+ local hash = usedparagraphs[fulltag]
+ if hash then
+ setattribute(di,"align",hash.align)
+ end
+ end
+
+ implement {
+ name = "settagparagraph",
+ actions = setparagraph,
+ arguments = "string"
+ }
+
+ structurestags.setparagraph = setparagraph
+
+end
+
+do
+
+ local marginanchors = { }
+ local margincontent = { }
+
+ function checks.margintext(di)
+ local i = marginanchors[di.fulltag]
+ margincontent[i] = di
+ end
+
+ function checks.marginanchor(di)
+ local i = marginanchors[di.fulltag]
+ local d = margincontent[i]
+ --
+ di.attribute = d.attribute
+ di.data = d.data
+ di.detail = d.detail
+ di.element = d.element
+ di.fulltag = d.fulltag
+ di.nature = d.nature
+ di.samepar = true
+ di.tg = d.tg
+ --
+ d.skip = "ignore"
+ end
+
+ implement {
+ name = "settagmargintext",
+ arguments = "integer",
+ actions = function(n)
+ marginanchors[locatedtag("margintext")] = n
+ end
+ }
+
+ implement {
+ name = "settagmarginanchor",
+ arguments = "integer",
+ actions = function(n)
+ marginanchors[locatedtag("marginanchor")] = n
+ end
+ }
+
+end
+
+do
+
+ function fixes.linenumber(di,data,i)
+ local ni = data[i+1]
+ if ni then
+ if ni.data then
+ while true do
+ local d = ni.data[1]
+ if d then
+ local e = d.element
+ if e then
+ if e == "line" or e == "verbatimline" then
+ insert(d.data,1,di)
+ data[i] = false
+ return
+ else
+ ni = d
+ end
+ else
+ return
+ end
+ else
+ return
+ end
+ end
+ end
+ end
+ end
+
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-exp.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-exp.lmt
new file mode 100644
index 00000000000..1375d2655c2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-exp.lmt
@@ -0,0 +1,2719 @@
+if not modules then modules = { } end modules ['back-exp'] = {
+ version = 1.001,
+ comment = "companion to back-exp.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Todo: share properties more with tagged pdf (or thge reverse)
+
+-- Because we run into the 200 local limit we quite some do .. end wrappers .. not always
+-- that nice but it has to be.
+
+-- Experiments demonstrated that mapping to <div> and classes is messy because we have to
+-- package attributes (some 30) into one set of (space seperatated but prefixed classes)
+-- which only makes things worse .. so if you want something else, use xslt to get there.
+
+-- language -> only mainlanguage, local languages should happen through start/stoplanguage
+-- tocs/registers -> maybe add a stripper (i.e. just don't flush entries in final tree)
+-- footnotes -> css 3
+-- bodyfont -> in styles.css
+
+-- Because we need to look ahead we now always build a tree (this was optional in
+-- the beginning). The extra overhead in the frontend is neglectable.
+--
+-- We can optimize the code ... currently the overhead is some 10% for xml + html so
+-- there is no hurry.
+
+-- todo: move critital formatters out of functions
+-- todo: delay loading (apart from basic tag stuff)
+
+-- problem : too many local variables
+
+-- check setting __i__
+
+local next, type, tonumber = next, type, tonumber
+local sub, gsub, match = string.sub, string.gsub, string.match
+local validstring = string.valid
+local lpegmatch = lpeg.match
+local utfchar, utfvalues, utflen = utf.char, utf.values, utf.len
+local concat, merge, sort, setmetatableindex = table.concat, table.merge, table.sort, table.setmetatableindex
+local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys
+local formatters = string.formatters
+local todimen = number.todimen
+local replacetemplate = utilities.templates.replace
+local settings_to_array = utilities.parsers.settings_to_array
+
+local addsuffix, joinfile, nameonly, basename, filesuffix = file.addsuffix, file.join, file.nameonly, file.basename, file.suffix
+
+local trace_export = false trackers.register ("export.trace", function(v) trace_export = v end)
+local trace_spacing = false trackers.register ("export.trace.spacing", function(v) trace_spacing = v end)
+local trace_details = false trackers.register ("export.trace.details", function(v) trace_details = v end)
+
+local less_state = false directives.register("export.lessstate", function(v) less_state = v end)
+local show_comment = true directives.register("export.comment", function(v) show_comment = v end)
+
+-- maybe we will also support these:
+--
+-- local css_hyphens = false directives.register("export.css.hyphens", function(v) css_hyphens = v end)
+-- local css_textalign = false directives.register("export.css.textalign", function(v) css_textalign = v end)
+-- local css_bodyfontsize = false directives.register("export.css.bodyfontsize", function(v) css_bodyfontsize = v end)
+-- local css_textwidth = false directives.register("export.css.textwidth", function(v) css_textwidth = v end)
+
+local report_export = logs.reporter("backend","export")
+
+local nodes = nodes
+local attributes = attributes
+
+local variables = interfaces.variables
+local v_yes = variables.yes
+local v_no = variables.no
+local v_xml = variables.xml
+local v_hidden = variables.hidden
+
+local implement = interfaces.implement
+
+local included = backends.included
+
+local tasks = nodes.tasks
+local fontchar = fonts.hashes.characters
+local fontquads = fonts.hashes.quads
+local languagenames = languages.numbers
+
+local texgetcount = tex.getcount
+
+local references = structures.references
+local structurestags = structures.tags
+local taglist = structurestags.taglist
+local specifications = structurestags.specifications
+local properties = structurestags.properties
+local locatedtag = structurestags.locatedtag
+
+structurestags.usewithcare = { }
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local characterdata = characters.data
+local overloads = fonts.mappings.overloads
+
+-- todo: more locals (and optimize)
+
+local exportversion = "0.35"
+local mathmlns = "http://www.w3.org/1998/Math/MathML"
+local contextns = "http://www.contextgarden.net/context/export" -- whatever suits
+local cssnamespaceurl = "@namespace context url('%namespace%') ;"
+local cssnamespace = "context|"
+----- cssnamespacenop = "/* no namespace */"
+
+local usecssnamespace = false
+
+local nofcurrentcontent = 0 -- so we don't free (less garbage collection)
+local currentcontent = { }
+local currentnesting = nil
+local currentattribute = nil
+local last = nil
+local currentparagraph = nil
+
+local noftextblocks = 0
+
+----- hyphencode = 0xAD
+local hyphen = utfchar(0xAD) -- todo: also emdash etc
+local tagsplitter = structurestags.patterns.splitter
+----- colonsplitter = lpeg.splitat(":")
+----- dashsplitter = lpeg.splitat("-")
+local threshold = 65536
+local indexing = false
+local keephyphens = false
+local exportproperties = false
+
+local finetuning = { }
+
+local treestack = { }
+local nesting = { }
+local currentdepth = 0
+
+local wrapups = { }
+
+local tree = { data = { }, fulltag == "root" } -- root
+local treeroot = tree
+local treehash = { }
+local extras = { }
+local checks = { }
+local fixes = { }
+local finalizers = { }
+local nofbreaks = 0
+local used = { }
+local exporting = false
+local restart = false
+local specialspaces = { [0x20] = " " } -- for conversion
+local somespace = { [0x20] = true, [" "] = true } -- for testing
+local entities = { ["&"] = "&amp;", [">"] = "&gt;", ["<"] = "&lt;" }
+local attribentities = { ["&"] = "&amp;", [">"] = "&gt;", ["<"] = "&lt;", ['"'] = "quot;" }
+
+local p_entity = lpeg.replacer(entities) -- was: entityremapper = utf.remapper(entities)
+local p_attribute = lpeg.replacer(attribentities)
+local p_escaped = lpeg.patterns.xml.escaped
+
+local f_tagid = formatters["%s-%04i"]
+
+-- local alignmapping = {
+-- flushright = "right",
+-- middle = "center",
+-- flushleft = "left",
+-- }
+
+local defaultnature = "mixed" -- "inline"
+
+setmetatableindex(used, function(t,k)
+ if k then
+ local v = { }
+ t[k] = v
+ return v
+ end
+end)
+
+local f_entity = formatters["&#x%X;"]
+local f_attribute = formatters[" %s=%q"]
+local f_property = formatters[" %s%s=%q"]
+
+setmetatableindex(specialspaces, function(t,k)
+ local v = utfchar(k)
+ t[k] = v
+ entities[v] = f_entity(k)
+ somespace[k] = true
+ somespace[v] = true
+ return v
+end)
+
+
+local namespaced = {
+ -- filled on
+}
+
+local namespaces = {
+ msubsup = "m",
+ msub = "m",
+ msup = "m",
+ mn = "m",
+ mi = "m",
+ ms = "m",
+ mo = "m",
+ mtext = "m",
+ mrow = "m",
+ mfrac = "m",
+ mroot = "m",
+ msqrt = "m",
+ munderover = "m",
+ munder = "m",
+ mover = "m",
+ merror = "m",
+ math = "m",
+ mrow = "m",
+ mtable = "m",
+ mtr = "m",
+ mtd = "m",
+ mfenced = "m",
+ maction = "m",
+ mspace = "m",
+ -- only when testing
+ mstacker = "m",
+ mstackertop = "m",
+ mstackermid = "m",
+ mstackerbot = "m",
+}
+
+setmetatableindex(namespaced, function(t,k)
+ if k then
+ local namespace = namespaces[k]
+ local v = namespace and namespace .. ":" .. k or k
+ t[k] = v
+ return v
+ end
+end)
+
+local function attribute(key,value)
+ if value and value ~= "" then
+ return f_attribute(key,lpegmatch(p_attribute,value))
+ else
+ return ""
+ end
+end
+
+local function setattribute(di,key,value,escaped)
+ if value and value ~= "" then
+ local a = di.attributes
+ if escaped then
+ value = lpegmatch(p_escaped,value)
+ end
+ if not a then
+ di.attributes = { [key] = value }
+ else
+ a[key] = value
+ end
+ end
+end
+
+local listdata = { } -- this has to be done otherwise: each element can just point back to ...
+
+function wrapups.hashlistdata()
+ local c = structures.lists.collected
+ for i=1,#c do
+ local ci = c[i]
+ local tag = ci.references.tag
+ if tag then
+ local m = ci.metadata
+ local t = m.kind .. ">" .. tag -- todo: use internal (see strc-lst.lua where it's set)
+ listdata[t] = ci
+ end
+ end
+end
+
+function structurestags.setattributehash(attr,key,value) -- public hash
+ local specification = taglist[attr]
+ if specification then
+ specification[key] = value
+ else
+ -- some kind of error
+ end
+end
+
+local usedstyles = { }
+local usedimages = { }
+local referencehash = { } -- move ?
+local destinationhash = { } -- move ?
+
+structurestags.backend = {
+ setattribute = setattribute,
+ extras = extras,
+ checks = checks,
+ fixes = fixes,
+ listdata = listdata,
+ finalizers = finalizers,
+ usedstyles = usedstyles,
+ usedimages = usedimages,
+ referencehash = referencehash,
+ destinationhash = destinationhash,
+}
+
+local namespacetemplate = [[
+/* %what% for file %filename% */
+
+%cssnamespaceurl%
+]]
+
+do
+
+ -- experiment: styles and images
+ --
+ -- officially we should convert to bp but we round anyway
+
+ -- /* padding : ; */
+ -- /* text-justify : inter-word ; */
+ -- /* text-align : justify ; */
+
+local documenttemplate = [[
+document,
+%namespace%div.document {
+ font-size : %size% !important ;
+ max-width : %width% !important ;
+ text-align : %align% !important ;
+ hyphens : %hyphens% !important ;
+}]]
+
+local styletemplate = [[
+%element%[detail="%detail%"],
+%namespace%div.%element%.%detail% {
+ display : inline ;
+ font-style : %style% ;
+ font-variant : %variant% ;
+ font-weight : %weight% ;
+ font-family : %family% ;
+ color : %color% ;
+}]]
+
+ local numbertoallign = {
+ [0] = "justify", ["0"] = "justify", [variables.normal ] = "justify",
+ [1] = "right", ["1"] = "right", [variables.flushright] = "right",
+ [2] = "center", ["2"] = "center", [variables.middle ] = "center",
+ [3] = "left", ["3"] = "left", [variables.flushleft ] = "left",
+ }
+
+ function wrapups.allusedstyles(filename)
+ local result = { replacetemplate(namespacetemplate, {
+ what = "styles",
+ filename = filename,
+ namespace = contextns,
+ -- cssnamespaceurl = usecssnamespace and cssnamespaceurl or cssnamespacenop,
+ cssnamespaceurl = cssnamespaceurl,
+ },false,true) }
+ --
+ local bodyfont = finetuning.bodyfont
+ local width = finetuning.width
+ local hyphen = finetuning.hyphen
+ local align = finetuning.align
+ --
+ if type(bodyfont) == "number" then
+ bodyfont = todimen(bodyfont)
+ else
+ bodyfont = "12pt"
+ end
+ if type(width) == "number" then
+ width = todimen(width) or "50em"
+ else
+ width = "50em"
+ end
+ if hyphen == v_yes then
+ hyphen = "manual"
+ else
+ hyphen = "inherited"
+ end
+ if align then
+ align = numbertoallign[align]
+ end
+ if not align then
+ align = hyphen and "justify" or "inherited"
+ end
+ --
+ result[#result+1] = replacetemplate(documenttemplate,{
+ size = bodyfont,
+ width = width,
+ align = align,
+ hyphens = hyphen
+ })
+ --
+ local colorspecification = xml.css.colorspecification
+ local fontspecification = xml.css.fontspecification
+ for element, details in sortedhash(usedstyles) do
+ for detail, data in sortedhash(details) do
+ local s = fontspecification(data.style)
+ local c = colorspecification(data.color)
+ detail = gsub(detail,"[^A-Za-z0-9]+","-")
+ result[#result+1] = replacetemplate(styletemplate,{
+ namespace = usecssnamespace and cssnamespace or "",
+ element = element,
+ detail = detail,
+ style = s.style or "inherit",
+ variant = s.variant or "inherit",
+ weight = s.weight or "inherit",
+ family = s.family or "inherit",
+ color = c or "inherit",
+ display = s.display and "block" or nil,
+ })
+ end
+ end
+ return concat(result,"\n\n")
+ end
+
+end
+
+do
+
+local imagetemplate = [[
+%element%[id="%id%"], %namespace%div.%element%[id="%id%"] {
+ display : block ;
+ background-image : url('%url%') ;
+ background-size : 100%% auto ;
+ background-repeat : no-repeat ;
+ width : %width% ;
+ height : %height% ;
+}]]
+
+ local f_svgname = formatters["%s.svg"]
+ local f_svgpage = formatters["%s-page-%s.svg"]
+ local collected = { }
+
+ local function usedname(name,page)
+ if filesuffix(name) == "pdf" then
+ -- temp hack .. we will have a remapper
+ if page and page > 1 then
+ name = f_svgpage(nameonly(name),page)
+ else
+ name = f_svgname(nameonly(name))
+ end
+ end
+ local scheme = url.hasscheme(name)
+ if not scheme or scheme == "file" then
+ -- or can we just use the name ?
+ return joinfile("../images",basename(url.filename(name)))
+ else
+ return name
+ end
+ end
+
+ function wrapups.allusedimages(filename)
+ local result = { replacetemplate(namespacetemplate, {
+ what = "images",
+ filename = filename,
+ namespace = contextns,
+ -- cssnamespaceurl = usecssnamespace and cssnamespaceurl or "",
+ cssnamespaceurl = cssnamespaceurl,
+ },false,true) }
+ for element, details in sortedhash(usedimages) do
+ for detail, data in sortedhash(details) do
+ local name = data.name
+ local page = tonumber(data.page) or 1
+ local spec = {
+ element = element,
+ id = data.id,
+ name = name,
+ page = page,
+ url = usedname(name,page),
+ width = data.width,
+ height = data.height,
+ used = data.used,
+ namespace = usecssnamespace and cssnamespace or "",
+ }
+ result[#result+1] = replacetemplate(imagetemplate,spec)
+ collected[detail] = spec
+ end
+ end
+ return concat(result,"\n\n")
+ end
+
+ function wrapups.uniqueusedimages() -- todo: combine these two
+ return collected
+ end
+
+end
+
+--
+
+properties.vspace = { export = "break", nature = "display" }
+----------------- = { export = "pagebreak", nature = "display" }
+
+local function makebreaklist(list)
+ nofbreaks = nofbreaks + 1
+ local t = { }
+ local l = list and list.taglist
+ if l then
+ for i=1,#list do
+ t[i] = l[i]
+ end
+ end
+ t[#t+1] = "break>" .. nofbreaks -- maybe no number or 0
+ return { taglist = t }
+end
+
+local breakattributes = {
+ type = "collapse"
+}
+
+local function makebreaknode(attributes) -- maybe no fulltag
+ nofbreaks = nofbreaks + 1
+ return {
+ tg = "break",
+ fulltag = "break>" .. nofbreaks,
+ n = nofbreaks,
+ element = "break",
+ nature = "display",
+ attributes = attributes or nil,
+ -- data = { }, -- not needed
+ -- attribute = 0, -- not needed
+ -- parnumber = 0,
+ }
+end
+
+do
+
+ local fields = { "title", "subtitle", "author", "keywords", "url", "version" }
+
+ local ignoredelements = false
+
+ local function checkdocument(root)
+ local data = root.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ local tg = di.tg
+ if tg == "noexport" then
+ local s = specifications[di.fulltag]
+ local u = s and s.userdata
+ if u then
+ local comment = u.comment
+ if comment then
+ di.element = "comment"
+ di.data = { { content = comment } }
+ u.comment = nil
+ else
+ data[i] = false
+ end
+ else
+ data[i] = false
+ end
+ elseif di.content then
+ -- okay
+ elseif tg == "ignore" then
+ di.element = ""
+ checkdocument(di)
+ elseif ignoredelements and ignoredelements[tg] then
+ di.element = ""
+ checkdocument(di)
+ else
+ checkdocument(di) -- new, else no noexport handling
+ end
+ end
+ end
+ end
+
+ function extras.document(di,element,n,fulltag)
+ setattribute(di,"language",languagenames[texgetcount("mainlanguagenumber")])
+ if not less_state then
+ setattribute(di,"file",tex.jobname)
+ if included.date then
+ setattribute(di,"date",os.fulltime())
+ end
+ setattribute(di,"context",environment.version)
+ setattribute(di,"version",exportversion)
+ setattribute(di,"xmlns:m",mathmlns)
+ local identity = interactions.general.getidentity()
+ for i=1,#fields do
+ local key = fields[i]
+ local value = identity[key]
+ if value and value ~= "" then
+ setattribute(di,key,value)
+ end
+ end
+ end
+ checkdocument(di)
+ end
+
+ implement {
+ name = "ignoretagsinexport",
+ arguments = "string",
+ actions = function(list)
+ for tag in string.gmatch(list,"[a-z]+") do
+ if ignoredelements then
+ ignoredelements[tag] = true
+ else
+ ignoredelements = { [tag] = true }
+ end
+ end
+ end,
+ }
+
+end
+
+-- flusher
+
+do
+
+ local f_detail = formatters[' detail="%s"']
+ local f_chain = formatters[' chain="%s"']
+ local f_index = formatters[' n="%s"']
+ local f_spacing = formatters['<c p="%s">%s</c>']
+
+ local f_empty_inline = formatters["<%s/>"]
+ local f_empty_mixed = formatters["%w<%s/>\n"]
+ local f_empty_display = formatters["\n%w<%s/>\n"]
+ local f_empty_inline_attr = formatters["<%s%s/>"]
+ local f_empty_mixed_attr = formatters["%w<%s%s/>"]
+ local f_empty_display_attr = formatters["\n%w<%s%s/>\n"]
+
+ local f_begin_inline = formatters["<%s>"]
+ local f_begin_mixed = formatters["%w<%s>"]
+ local f_begin_display = formatters["\n%w<%s>\n"]
+ local f_begin_inline_attr = formatters["<%s%s>"]
+ local f_begin_mixed_attr = formatters["%w<%s%s>"]
+ local f_begin_display_attr = formatters["\n%w<%s%s>\n"]
+
+ local f_end_inline = formatters["</%s>"]
+ local f_end_mixed = formatters["</%s>\n"]
+ local f_end_display = formatters["%w</%s>\n"]
+
+ local f_begin_inline_comment = formatters["<!-- %s --><%s>"]
+ local f_begin_mixed_comment = formatters["%w<!-- %s --><%s>"]
+ local f_begin_display_comment = formatters["\n%w<!-- %s -->\n%w<%s>\n"]
+ local f_begin_inline_attr_comment = formatters["<!-- %s --><%s%s>"]
+ local f_begin_mixed_attr_comment = formatters["%w<!-- %s --><%s%s>"]
+ local f_begin_display_attr_comment = formatters["\n%w<!-- %s -->\n%w<%s%s>\n"]
+
+ local f_comment_begin_inline = formatters["<!-- begin %s -->"]
+ local f_comment_begin_mixed = formatters["%w<!-- begin %s -->"]
+ local f_comment_begin_display = formatters["\n%w<!-- begin %s -->\n"]
+
+ local f_comment_end_inline = formatters["<!-- end %s -->"]
+ local f_comment_end_mixed = formatters["<!-- end %s -->\n"]
+ local f_comment_end_display = formatters["%w<!-- end %s -->\n"]
+
+ local f_metadata_begin = formatters["\n%w<metadata>\n"]
+ local f_metadata = formatters["%w<metavariable name=%q>%s</metavariable>\n"]
+ local f_metadata_end = formatters["%w</metadata>\n"]
+
+ local function attributes(a)
+ local r = { }
+ local n = 0
+ for k, v in next, a do
+ n = n + 1
+ r[n] = f_attribute(k,tostring(v)) -- tostring because of %q
+ end
+ sort(r)
+ return concat(r,"")
+ end
+
+ local function properties(a)
+ local r = { }
+ local n = 0
+ for k, v in next, a do
+ n = n + 1
+ r[n] = f_property(exportproperties,k,tostring(v)) -- tostring because of %q
+ end
+ sort(r)
+ return concat(r,"")
+ end
+
+ local depth = 0
+ local inline = 0
+
+ local function emptytag(result,element,nature,di) -- currently only break but at some point
+ local a = di.attributes -- we might add detail etc
+ if a then -- happens seldom
+ if nature == "display" then
+ result[#result+1] = f_empty_display_attr(depth,namespaced[element],attributes(a))
+ elseif nature == "mixed" then
+ result[#result+1] = f_empty_mixed_attr(depth,namespaced[element],attributes(a))
+ else
+ result[#result+1] = f_empty_inline_attr(namespaced[element],attributes(a))
+ end
+ else
+ if nature == "display" then
+ result[#result+1] = f_empty_display(depth,namespaced[element])
+ elseif nature == "mixed" then
+ result[#result+1] = f_empty_mixed(depth,namespaced[element])
+ else
+ result[#result+1] = f_empty_inline(namespaced[element])
+ end
+ end
+ end
+
+ -- local function stripspaces(di)
+ -- local d = di.data
+ -- local n = #d
+ -- local m = 0
+ -- for i=1,n do
+ -- local di = d[i]
+ -- if di.tg then
+ -- m = m + 1
+ -- d[m] = di
+ -- end
+ -- end
+ -- for i=n,m+1,-1 do
+ -- d[i] = nil
+ -- end
+ -- end
+ --
+ -- -- simpler:
+
+ local function stripspaces(di)
+ local d = di.data
+ for i=1,#d do
+ local di = d[i]
+ if not di.tg then
+ di.content = ""
+ end
+ end
+ end
+
+ local function begintag(result,element,nature,di,skip)
+ local index = di.n
+ local fulltag = di.fulltag
+ local specification = specifications[fulltag] or { } -- we can have a dummy
+ local comment = di.comment
+ local detail = specification.detail
+ if skip == "comment" then
+ if show_comment then
+ if nature == "inline" or inline > 0 then
+ result[#result+1] = f_comment_begin_inline(namespaced[element])
+ inline = inline + 1
+ elseif nature == "mixed" then
+ result[#result+1] = f_comment_begin_mixed(depth,namespaced[element])
+ depth = depth + 1
+ inline = 1
+ else
+ result[#result+1] = f_comment_begin_display(depth,namespaced[element])
+ depth = depth + 1
+ end
+ end
+ elseif skip then
+ -- ignore
+ else
+
+ local n = 0
+ local r = { } -- delay this
+ if detail then
+ detail = gsub(detail,"[^A-Za-z0-9]+","-")
+ specification.detail = detail -- we use it later in for the div
+ n = n + 1
+ r[n] = f_detail(detail)
+ end
+ local parents = specification.parents
+ if parents then
+ parents = gsub(parents,"[^A-Za-z0-9 ]+","-")
+ specification.parents = parents -- we use it later in for the div
+ n = n + 1
+ r[n] = f_chain(parents)
+ end
+ if indexing and index then
+ n = n + 1
+ r[n] = f_index(index)
+ end
+ --
+ local extra = extras[element]
+ if extra then
+ extra(di,element,index,fulltag)
+ end
+ --
+ if di.record then
+ stripspaces(di)
+ end
+ --
+ if exportproperties then
+ local p = specification.userdata
+ if not p then
+ -- skip
+ elseif exportproperties == v_yes then
+ n = n + 1
+ r[n] = attributes(p)
+ else
+ n = n + 1
+ r[n] = properties(p)
+ end
+ end
+ local a = di.attributes
+ if a then
+ if trace_spacing then
+ a.p = di.parnumber or 0
+ end
+ n = n + 1
+ r[n] = attributes(a)
+ elseif trace_spacing then
+ n = n + 1
+ r[n] = attributes { p = di.parnumber or 0 }
+ end
+ if n == 0 then
+ if nature == "inline" or inline > 0 then
+ if show_comment and comment then
+ result[#result+1] = f_begin_inline_comment(comment,namespaced[element])
+ else
+ result[#result+1] = f_begin_inline(namespaced[element])
+ end
+ inline = inline + 1
+ elseif nature == "mixed" then
+ if show_comment and comment then
+ result[#result+1] = f_begin_mixed_comment(depth,comment,namespaced[element])
+ else
+ result[#result+1] = f_begin_mixed(depth,namespaced[element])
+ end
+ depth = depth + 1
+ inline = 1
+ else
+ if show_comment and comment then
+ result[#result+1] = f_begin_display_comment(depth,comment,depth,namespaced[element])
+ else
+ result[#result+1] = f_begin_display(depth,namespaced[element])
+ end
+ depth = depth + 1
+ end
+ else
+ r = concat(r,"",1,n)
+ if nature == "inline" or inline > 0 then
+ if show_comment and comment then
+ result[#result+1] = f_begin_inline_attr_comment(comment,namespaced[element],r)
+ else
+ result[#result+1] = f_begin_inline_attr(namespaced[element],r)
+ end
+ inline = inline + 1
+ elseif nature == "mixed" then
+ if show_comment and comment then
+ result[#result+1] = f_begin_mixed_attr_comment(depth,comment,namespaced[element],r)
+ else
+ result[#result+1] = f_begin_mixed_attr(depth,namespaced[element],r)
+ end
+ depth = depth + 1
+ inline = 1
+ else
+ if show_comment and comment then
+ result[#result+1] = f_begin_display_attr_comment(depth,comment,depth,namespaced[element],r)
+ else
+ result[#result+1] = f_begin_display_attr(depth,namespaced[element],r)
+ end
+ depth = depth + 1
+ end
+ end
+ end
+ used[element][detail or ""] = { nature, specification.parents } -- for template css
+ -- also in last else ?
+ local metadata = specification.metadata
+ if metadata then
+ result[#result+1] = f_metadata_begin(depth)
+ for k, v in table.sortedpairs(metadata) do
+ if v ~= "" then
+ result[#result+1] = f_metadata(depth+1,k,lpegmatch(p_entity,v))
+ end
+ end
+ result[#result+1] = f_metadata_end(depth)
+ end
+ end
+
+ local function endtag(result,element,nature,di,skip)
+ if skip == "comment" then
+ if show_comment then
+ if nature == "display" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_comment_end_display(depth,namespaced[element])
+ inline = 0
+ elseif nature == "mixed" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_comment_end_mixed(namespaced[element])
+ inline = 0
+ else
+ inline = inline - 1
+ result[#result+1] = f_comment_end_inline(namespaced[element])
+ end
+ end
+ elseif skip then
+ -- ignore
+ else
+ if nature == "display" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_end_display(depth,namespaced[element])
+ inline = 0
+ elseif nature == "mixed" and (inline == 0 or inline == 1) then
+ depth = depth - 1
+ result[#result+1] = f_end_mixed(namespaced[element])
+ inline = 0
+ else
+ inline = inline - 1
+ result[#result+1] = f_end_inline(namespaced[element])
+ end
+ end
+ end
+
+ local function flushtree(result,data,nature)
+ local nofdata = #data
+ for i=1,nofdata do
+ local di = data[i]
+ if not di then -- hm, di can be string
+ -- whatever
+ else
+ local content = di.content
+ -- also optimize for content == "" : trace that first
+ if content then
+ -- already has breaks
+ local content = lpegmatch(p_entity,content)
+ if i == nofdata and sub(content,-1) == "\n" then -- move check
+ -- can be an end of line in par but can also be the last line
+ if trace_spacing then
+ result[#result+1] = f_spacing(di.parnumber or 0,sub(content,1,-2))
+ else
+ result[#result+1] = sub(content,1,-2)
+ end
+ result[#result+1] = " "
+ else
+ if trace_spacing then
+ result[#result+1] = f_spacing(di.parnumber or 0,content)
+ else
+ result[#result+1] = content
+ end
+ end
+ elseif not di.collapsed then -- ignore collapsed data (is appended, reconstructed par)
+ local element = di.element
+ if not element then
+ -- skip
+ elseif element == "break" then -- or element == "pagebreak"
+ emptytag(result,element,nature,di)
+ elseif element == "" or di.skip == "ignore" then
+ -- skip
+ else
+ if di.before then
+ flushtree(result,di.before,nature)
+ end
+ local natu = di.nature
+ local skip = di.skip
+ if di.breaknode then
+ emptytag(result,"break","display",di)
+ end
+ begintag(result,element,natu,di,skip)
+ flushtree(result,di.data,natu)
+ endtag(result,element,natu,di,skip)
+ if di.after then
+ flushtree(result,di.after,nature)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function breaktree(tree,parent,parentelement) -- also removes double breaks
+ local data = tree.data
+ if data then
+ local nofdata = #data
+ local prevelement
+ local prevnature
+ local prevparnumber
+ local newdata = { }
+ local nofnewdata = 0
+ for i=1,nofdata do
+ local di = data[i]
+ if not di then
+ -- skip
+ elseif di.skip == "ignore" then
+ -- skip (new)
+elseif di.tg == "ignore" then
+ -- skip (new)
+ elseif di.content then
+ if di.samepar then
+ prevparnumber = false
+ else
+ local parnumber = di.parnumber
+ if prevnature == "inline" and prevparnumber and prevparnumber ~= parnumber then
+ nofnewdata = nofnewdata + 1
+ if trace_spacing then
+ newdata[nofnewdata] = makebreaknode { type = "a", p = prevparnumber, n = parnumber }
+ else
+ newdata[nofnewdata] = makebreaknode()
+ end
+ end
+ prevelement = nil
+ prevparnumber = parnumber
+ end
+ prevnature = "inline"
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
+ elseif not di.collapsed then
+ local element = di.element
+ if element == "break" then -- or element == "pagebreak"
+ if prevelement == "break" then
+ di.element = ""
+ end
+ prevelement = element
+ prevnature = "display"
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
+ elseif element == "" or di.skip == "ignore" then
+ -- skip
+ else
+ if di.samepar then
+ prevnature = "inline"
+ prevparnumber = false
+ else
+ local nature = di.nature
+ local parnumber = di.parnumber
+ if prevnature == "inline" and nature == "inline" and prevparnumber and prevparnumber ~= parnumber then
+ nofnewdata = nofnewdata + 1
+ if trace_spacing then
+ newdata[nofnewdata] = makebreaknode { type = "b", p = prevparnumber, n = parnumber }
+ else
+ newdata[nofnewdata] = makebreaknode()
+ end
+ end
+ prevnature = nature
+ prevparnumber = parnumber
+ end
+ prevelement = element
+ breaktree(di,tree,element)
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
+ end
+ else
+ if di.samepar then
+ prevnature = "inline"
+ prevparnumber = false
+ else
+ local nature = di.nature
+ local parnumber = di.parnumber
+ if prevnature == "inline" and nature == "inline" and prevparnumber and prevparnumber ~= parnumber then
+ nofnewdata = nofnewdata + 1
+ if trace_spacing then
+ newdata[nofnewdata] = makebreaknode { type = "c", p = prevparnumber, n = parnumber }
+ else
+ newdata[nofnewdata] = makebreaknode()
+ end
+ end
+ prevnature = nature
+ prevparnumber = parnumber
+ end
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
+ end
+ end
+ tree.data = newdata
+ end
+ end
+
+ -- also tabulaterow reconstruction .. maybe better as a checker
+ -- i.e cell attribute
+
+ local function collapsetree(tree)
+-- for tag, trees in sortedhash(treehash) do
+ for tag, trees in next, treehash do
+ local d = trees[1].data
+-- print("!!!!!!!!",tag)
+-- inspect(trees)
+ if d then
+ local nd = #d
+ if nd > 0 then
+ for i=2,#trees do
+ local currenttree = trees[i]
+ local currentdata = currenttree.data
+ local currentpar = currenttree.parnumber
+ local previouspar = trees[i-1].parnumber
+ currenttree.collapsed = true
+ -- is the next ok?
+ if previouspar == 0 or not (di and di.content) then
+ previouspar = nil -- no need anyway so no further testing needed
+ end
+ for j=1,#currentdata do
+ local cd = currentdata[j]
+ if not cd or cd == "" then
+ -- skip
+ elseif cd.skip == "ignore" then
+ -- skip
+ elseif cd.content then
+ if not currentpar then
+ -- add space ?
+ elseif not previouspar then
+ -- add space ?
+ elseif currentpar ~= previouspar then
+ nd = nd + 1
+ if trace_spacing then
+ d[nd] = makebreaknode { type = "d", p = previouspar, n = currentpar }
+ else
+ d[nd] = makebreaknode()
+ end
+ end
+ previouspar = currentpar
+ nd = nd + 1
+ d[nd] = cd
+ else
+ nd = nd + 1
+ d[nd] = cd
+ end
+ currentdata[j] = false
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function finalizetree(tree)
+ for _, finalizer in next, finalizers do
+ finalizer(tree)
+ end
+ end
+
+ -- local function showtree(data,when,where)
+ -- if data then
+ -- for i=1,#data do
+ -- local d = data[i]
+ -- if type(d) == "table" and d.element then
+ -- print(when,where,i,d.element,d.parnumber or 0)
+ -- end
+ -- end
+ -- end
+ -- end
+
+ local function indextree(tree)
+ local data = tree.data
+ if data then
+ local n, new = 0, { }
+ -- showtree(data,"before","index")
+ for i=1,#data do
+ local d = data[i]
+ if not d then
+ -- skip
+ elseif d.content then
+ n = n + 1
+ new[n] = d
+ elseif not d.collapsed then
+ n = n + 1
+ d.__i__ = n
+ d.__p__ = tree
+ indextree(d)
+ new[n] = d
+ end
+ end
+ tree.data = new
+ -- showtree(new,"after","index")
+ end
+ end
+
+ local function checktree(tree)
+ local data = tree.data
+ if data then
+ -- showtree(data,"before","check")
+ for i=1,#data do
+ local d = data[i]
+ if type(d) == "table" then
+ local check = checks[d.tg]
+ if check then
+ check(d,data,i)
+ end
+ checktree(d) -- so parts can pass twice
+ end
+ end
+ -- showtree(data,"after","check")
+ end
+ end
+
+ local function fixtree(tree)
+ local data = tree.data
+ if data then
+ -- showtree(data,"before","fix")
+ for i=1,#data do
+ local d = data[i]
+ if type(d) == "table" then
+ local fix = fixes[d.tg]
+ if fix then
+ fix(d,data,i)
+ end
+ fixtree(d) -- so parts can pass twice
+ end
+ end
+ -- showtree(data,"after","fix")
+ end
+ end
+
+ wrapups.flushtree = flushtree
+ wrapups.breaktree = breaktree
+ wrapups.collapsetree = collapsetree
+ wrapups.finalizetree = finalizetree
+ wrapups.indextree = indextree
+ wrapups.checktree = checktree
+ wrapups.fixtree = fixtree
+
+end
+
+-- collector code
+
+local function push(fulltag,depth)
+ local tg, n, detail, element, nature, record
+ local specification = specifications[fulltag]
+ if specification then
+ tg = specification.tagname
+ n = specification.tagindex
+ detail = specification.detail
+ else
+ -- a break (more efficient if we don't store those in specifications)
+ tg, n = lpegmatch(tagsplitter,fulltag)
+ n = tonumber(n) -- to tonumber in tagsplitter
+ end
+ local p = properties[tg]
+ if p then
+ element = p.export or tg
+ nature = p.nature or "inline" -- defaultnature
+ record = p.record
+ end
+ local treedata = tree.data
+ local t = { -- maybe we can use the tag table
+ tg = tg,
+ fulltag = fulltag,
+ detail = detail,
+ n = n, -- already a number
+ element = element,
+ nature = nature,
+ data = { },
+ attribute = currentattribute,
+ parnumber = currentparagraph,
+ record = record, -- we can consider storing properties
+ }
+ treedata[#treedata+1] = t
+ currentdepth = currentdepth + 1
+ nesting[currentdepth] = fulltag
+ treestack[currentdepth] = tree
+ if trace_export then
+ if detail and detail ~= "" then
+ report_export("%w<%s trigger=%q n=%q paragraph=%q index=%q detail=%q>",currentdepth-1,tg,n,currentattribute or 0,currentparagraph or 0,#treedata,detail)
+ else
+ report_export("%w<%s trigger=%q n=%q paragraph=%q index=%q>",currentdepth-1,tg,n,currentattribute or 0,currentparagraph or 0,#treedata)
+ end
+ end
+ tree = t
+ if tg == "break" then
+ -- no need for this
+ else
+ local h = treehash[fulltag]
+ if h then
+ h[#h+1] = t
+ else
+ treehash[fulltag] = { t }
+ end
+ end
+end
+
+local function pop()
+ if currentdepth > 0 then
+ local top = nesting[currentdepth]
+ tree = treestack[currentdepth]
+ currentdepth = currentdepth - 1
+ if trace_export then
+ if top then
+ report_export("%w</%s>",currentdepth,match(top,"[^>]+"))
+ else
+ report_export("</BAD>")
+ end
+ end
+ else
+ report_export("%w<!-- too many pops -->",currentdepth)
+ end
+end
+
+local function continueexport()
+ if nofcurrentcontent > 0 then
+ if trace_export then
+ report_export("%w<!-- injecting pagebreak space -->",currentdepth)
+ end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " " -- pagebreak
+ end
+end
+
+local function pushentry(current)
+ if not current then
+ -- bad news
+ return
+ end
+ current = current.taglist
+ if not current then
+ -- even worse news
+ return
+ end
+ if restart then
+ continueexport()
+ restart = false
+ end
+ local newdepth = #current
+ local olddepth = currentdepth
+ if trace_export then
+ report_export("%w<!-- moving from depth %s to %s (%s) -->",currentdepth,olddepth,newdepth,current[newdepth])
+ end
+ if olddepth <= 0 then
+ for i=1,newdepth do
+ push(current[i],i)
+ end
+ else
+ local difference
+ if olddepth < newdepth then
+ for i=1,olddepth do
+ if current[i] ~= nesting[i] then
+ difference = i
+ break
+ end
+ end
+ else
+ for i=1,newdepth do
+ if current[i] ~= nesting[i] then
+ difference = i
+ break
+ end
+ end
+ end
+ if difference then
+ for i=olddepth,difference,-1 do
+ pop()
+ end
+ for i=difference,newdepth do
+ push(current[i],i)
+ end
+ elseif newdepth > olddepth then
+ for i=olddepth+1,newdepth do
+ push(current[i],i)
+ end
+ elseif newdepth < olddepth then
+ for i=olddepth,newdepth,-1 do
+ pop()
+ end
+ elseif trace_export then
+ report_export("%w<!-- staying at depth %s (%s) -->",currentdepth,newdepth,nesting[newdepth] or "?")
+ end
+ end
+ return olddepth, newdepth
+end
+
+local function pushcontent(oldparagraph,newparagraph)
+ if nofcurrentcontent > 0 then
+ if oldparagraph then
+ if currentcontent[nofcurrentcontent] == "\n" then
+ if trace_export then
+ report_export("%w<!-- removing newline -->",currentdepth)
+ end
+ nofcurrentcontent = nofcurrentcontent - 1
+ end
+ end
+ local content = concat(currentcontent,"",1,nofcurrentcontent)
+ if content == "" then
+ -- omit; when oldparagraph we could push, remove spaces, pop
+ elseif somespace[content] and oldparagraph then
+ -- omit; when oldparagraph we could push, remove spaces, pop
+ else
+ local olddepth, newdepth
+ local list = taglist[currentattribute]
+ if list then
+ olddepth, newdepth = pushentry(list)
+ end
+ if tree then
+ local td = tree.data
+ local nd = #td
+ td[nd+1] = { parnumber = oldparagraph or currentparagraph, content = content }
+ if trace_export then
+ report_export("%w<!-- start content with length %s -->",currentdepth,utflen(content))
+ report_export("%w%s",currentdepth,(gsub(content,"\n","\\n")))
+ report_export("%w<!-- stop content -->",currentdepth)
+ end
+ if olddepth then
+ for i=newdepth-1,olddepth,-1 do
+ pop()
+ end
+ end
+ end
+ end
+ nofcurrentcontent = 0
+ end
+ if oldparagraph then
+ pushentry(makebreaklist(currentnesting))
+ if trace_export then
+ report_export("%w<!-- break added between paragraph %a and %a -->",currentdepth,oldparagraph,newparagraph)
+ end
+ end
+end
+
+local function finishexport()
+ if trace_export then
+ report_export("%w<!-- start finalizing -->",currentdepth)
+ end
+ if nofcurrentcontent > 0 then
+ if somespace[currentcontent[nofcurrentcontent]] then
+ if trace_export then
+ report_export("%w<!-- removing space -->",currentdepth)
+ end
+ nofcurrentcontent = nofcurrentcontent - 1
+ end
+ pushcontent()
+ end
+ for i=currentdepth,1,-1 do
+ pop()
+ end
+ currentcontent = { } -- we're nice and do a cleanup
+ if trace_export then
+ report_export("%w<!-- stop finalizing -->",currentdepth)
+ end
+end
+
+-- inserts ?
+
+local collectresults do -- too many locals otherwise
+
+ local nodecodes = nodes.nodecodes
+ local gluecodes = nodes.gluecodes
+ local listcodes = nodes.listcodes
+ local whatsitcodes = nodes.whatsitcodes
+
+ local subtypes = nodes.subtypes
+
+ local hlist_code = nodecodes.hlist
+ local vlist_code = nodecodes.vlist
+ local glyph_code = nodecodes.glyph
+ local glue_code = nodecodes.glue
+ local kern_code = nodecodes.kern
+ local disc_code = nodecodes.disc
+ local whatsit_code = nodecodes.whatsit
+ local par_code = nodecodes.par
+
+ local userskip_code = gluecodes.userskip
+ local rightskip_code = gluecodes.rightskip
+ local parfillskip_code = gluecodes.parfillskip
+ local spaceskip_code = gluecodes.spaceskip
+ local xspaceskip_code = gluecodes.xspaceskip
+
+ local linelist_code = listcodes.line
+
+ local userdefinedwhatsit_code = whatsitcodes.userdefined
+
+ local privateattribute = attributes.private
+ local a_image = privateattribute('image')
+ local a_reference = privateattribute('reference')
+ local a_destination = privateattribute('destination')
+ local a_characters = privateattribute('characters')
+ local a_exportstatus = privateattribute('exportstatus')
+ local a_tagged = privateattribute('tagged')
+ local a_taggedpar = privateattribute("taggedpar")
+ local a_textblock = privateattribute("textblock")
+
+ local inline_mark = nodes.pool.userids["margins.inline"]
+
+ local nuts = nodes.nuts
+
+ local getnext = nuts.getnext
+ local getdisc = nuts.getdisc
+ local getlist = nuts.getlist
+ local getid = nuts.getid
+ local getattr = nuts.getattr
+ local setattr = nuts.setattr -- maybe use properties
+ local isglyph = nuts.isglyph
+ local getkern = nuts.getkern
+ local getwidth = nuts.getwidth
+
+ local start_of_par = nuts.start_of_par
+
+ local nexthlist = nuts.traversers.hlist
+ local nextnode = nuts.traversers.node
+
+ local function addtomaybe(maybewrong,c,case)
+ if trace_export then
+ report_export("%w<!-- possible paragraph mixup at %C case %i -->",currentdepth,c,case)
+ else
+ local s = formatters["%C"](c)
+ if maybewrong then
+ maybewrong[#maybewrong+1] = s
+ else
+ maybewrong = { s }
+ end
+ return maybewrong
+ end
+ end
+
+ local function showmaybe(maybewrong)
+ if not trace_export then
+ report_export("fuzzy paragraph: % t",maybewrong)
+ end
+ end
+
+ local function showdetail(n,id,subtype)
+ local a = getattr(n,a_tagged)
+ local t = taglist[a]
+ local c = nodecodes[id]
+ local s = subtypes[id][subtype]
+ if a and t then
+ report_export("node %a, subtype %a, tag %a, element %a, tree '% t'",c,s,a,t.tagname,t.taglist)
+ else
+ report_export("node %a, subtype %a, untagged",c,s)
+ end
+ end
+
+ local function collectresults(head,list,pat,pap) -- is last used (we also have currentattribute)
+ local p
+ local paragraph
+ local maybewrong
+ local pid
+ for n, id, subtype in nextnode, head do
+ if trace_details then
+ showdetail(n,id,subtype)
+ end
+ if id == glyph_code then
+ local c, f = isglyph(n)
+ local at = getattr(n,a_tagged) or pat
+ if not at then
+ -- we need to tag the pagebody stuff as being valid skippable
+ --
+ -- report_export("skipping character: %C (no attribute)",n.char)
+ else
+ if last ~= at then
+ local tl = taglist[at]
+ local ap = getattr(n,a_taggedpar) or pap
+ if paragraph and (not ap or ap < paragraph) then
+ maybewrong = addtomaybe(maybewrong,c,1)
+ end
+ pushcontent()
+ currentnesting = tl
+ currentparagraph = ap
+ currentattribute = at
+ last = at
+ pushentry(currentnesting)
+ if trace_export then
+ report_export("%w<!-- processing glyph %C tagged %a -->",currentdepth,c,at)
+ end
+ -- We need to intercept this here; maybe I will also move this
+ -- to a regular setter at the tex end.
+ local r = getattr(n,a_reference)
+ if r then
+ local t = tl.taglist
+ referencehash[t[#t]] = r -- fulltag
+ end
+ local d = getattr(n,a_destination)
+ if d then
+ local t = tl.taglist
+ destinationhash[t[#t]] = d -- fulltag
+ end
+ --
+ elseif last then
+ -- we can consider tagging the pars (lines) in the parbuilder but then we loose some
+ -- information unless we inject a special node (but even then we can run into nesting
+ -- issues)
+ local ap = getattr(n,a_taggedpar) or pap
+ if ap ~= currentparagraph then
+ pushcontent(currentparagraph,ap)
+ pushentry(currentnesting)
+ currentattribute = last
+ currentparagraph = ap
+ end
+ if paragraph and (not ap or ap < paragraph) then
+ maybewrong = addtomaybe(maybewrong,c,2)
+ end
+ if trace_export then
+ report_export("%w<!-- processing glyph %C tagged %a -->",currentdepth,c,last)
+ end
+ else
+ if trace_export then
+ report_export("%w<!-- processing glyph %C tagged %a -->",currentdepth,c,at)
+ end
+ end
+ local s = getattr(n,a_exportstatus)
+ if s then
+ c = s
+ end
+ if c == 0 then
+ if trace_export then
+ report_export("%w<!-- skipping last glyph -->",currentdepth)
+ end
+ elseif c == 0x20 then
+ local a = getattr(n,a_characters)
+ nofcurrentcontent = nofcurrentcontent + 1
+ if a then
+ if trace_export then
+ report_export("%w<!-- turning last space into special space %U -->",currentdepth,a)
+ end
+ currentcontent[nofcurrentcontent] = specialspaces[a] -- special space
+ else
+ currentcontent[nofcurrentcontent] = " "
+ end
+ else
+ local fc = fontchar[f]
+ if fc then
+ fc = fc and fc[c]
+ if fc then
+ local u = fc.unicode
+ if not u then
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ elseif type(u) == "table" then
+ for i=1,#u do
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(u[i])
+ end
+ else
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(u)
+ end
+ elseif c > 0 then
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ else
+ -- we can have -1 as side effect of an explicit hyphen (unless we expand)
+ end
+ elseif c > 0 then
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ else
+ -- we can have -1 as side effect of an explicit hyphen (unless we expand)
+ end
+ end
+ end
+ elseif id == glue_code then
+ -- we need to distinguish between hskips and vskips
+ local ca = getattr(n,a_characters)
+ if ca == 0 then
+ -- skip this one ... already converted special character (node-acc)
+ elseif ca then
+ local a = getattr(n,a_tagged) or pat
+ if a then
+ local c = specialspaces[ca]
+ if last ~= a then
+ local tl = taglist[a]
+ if trace_export then
+ report_export("%w<!-- processing space glyph %U tagged %a case 1 -->",currentdepth,ca,a)
+ end
+ pushcontent()
+ currentnesting = tl
+ currentparagraph = getattr(n,a_taggedpar) or pap
+ currentattribute = a
+ last = a
+ pushentry(currentnesting)
+ -- no reference check (see above)
+ elseif last then
+ local ap = getattr(n,a_taggedpar) or pap
+ if ap ~= currentparagraph then
+ pushcontent(currentparagraph,ap)
+ pushentry(currentnesting)
+ currentattribute = last
+ currentparagraph = ap
+ end
+ if trace_export then
+ report_export("%w<!-- processing space glyph %U tagged %a case 2 -->",currentdepth,ca,last)
+ end
+ end
+ -- if somespace[currentcontent[nofcurrentcontent]] then
+ -- if trace_export then
+ -- report_export("%w<!-- removing space -->",currentdepth)
+ -- end
+ -- nofcurrentcontent = nofcurrentcontent - 1
+ -- end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = c
+ end
+ elseif subtype == userskip_code then
+ if getwidth(n) > threshold then
+ if last and not somespace[currentcontent[nofcurrentcontent]] then
+ local a = getattr(n,a_tagged) or pat
+ if a == last then
+ if trace_export then
+ report_export("%w<!-- injecting spacing 5a -->",currentdepth)
+ end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ elseif a then
+ -- e.g LOGO<space>LOGO
+ if trace_export then
+ report_export("%w<!-- processing glue > threshold tagged %s becomes %s -->",currentdepth,last,a)
+ end
+ pushcontent()
+ if trace_export then
+ report_export("%w<!-- injecting spacing 5b -->",currentdepth)
+ end
+ last = a
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ currentnesting = taglist[last]
+ pushentry(currentnesting)
+ currentattribute = last
+ end
+ end
+ end
+ elseif subtype == spaceskip_code or subtype == xspaceskip_code then
+ if not somespace[currentcontent[nofcurrentcontent]] then
+ local a = getattr(n,a_tagged) or pat
+ if a == last then
+ if trace_export then
+ report_export("%w<!-- injecting spacing 7 (stay in element) -->",currentdepth)
+ end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ else
+ if trace_export then
+ report_export("%w<!-- injecting spacing 7 (end of element) -->",currentdepth)
+ end
+ last = a
+ pushcontent()
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ currentnesting = taglist[last]
+ pushentry(currentnesting)
+ currentattribute = last
+ end
+ end
+ elseif subtype == rightskip_code then
+ -- a line
+ if nofcurrentcontent > 0 then
+ local r = currentcontent[nofcurrentcontent]
+ if r == hyphen then
+ if not keephyphens then
+ nofcurrentcontent = nofcurrentcontent - 1
+ end
+ elseif pid == disc_code then
+ -- go on .. tricky: we should mark the glyhs as coming from a disc
+ elseif not somespace[r] then
+ local a = getattr(n,a_tagged) or pat
+ if a == last then
+ if trace_export then
+ report_export("%w<!-- injecting spacing 1 (end of line, stay in element) -->",currentdepth)
+ end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ else
+ if trace_export then
+ report_export("%w<!-- injecting spacing 1 (end of line, end of element) -->",currentdepth)
+ end
+ last = a
+ pushcontent()
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ currentnesting = taglist[last]
+ pushentry(currentnesting)
+ currentattribute = last
+ end
+ end
+ end
+ elseif subtype == parfillskip_code then
+ -- deal with paragraph endings (crossings) elsewhere and we quit here
+ -- as we don't want the rightskip space addition
+ if maybewrong then
+ showmaybe(maybewrong)
+ end
+ return
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local ai = getattr(n,a_image)
+ if ai then
+ local at = getattr(n,a_tagged) or pat
+ if nofcurrentcontent > 0 then
+ pushcontent()
+ pushentry(currentnesting) -- ??
+ end
+ pushentry(taglist[at]) -- has an index, todo: flag empty element
+ if trace_export then
+ report_export("%w<!-- processing image tagged %a",currentdepth,last)
+ end
+ last = nil
+ currentparagraph = nil
+ else
+ -- we need to determine an end-of-line
+ local list = getlist(n)
+ if list then
+ -- todo: no par checking needed in math
+ local at = getattr(n,a_tagged) or pat
+ collectresults(list,n,at)
+ end
+ end
+ elseif id == kern_code then
+ local kern = getkern(n)
+ if kern > 0 then
+local a = getattr(n,a_tagged) or pat
+local t = taglist[a]
+if not t or t.tagname ~= "ignore" then -- maybe earlier on top)
+ local limit = threshold
+ if p then
+ local c, f = isglyph(p)
+ if c then
+ limit = fontquads[f] / 4
+ end
+ end
+ if kern > limit then
+ if last and not somespace[currentcontent[nofcurrentcontent]] then
+-- local a = getattr(n,a_tagged) or pat
+ if a == last then
+ if not somespace[currentcontent[nofcurrentcontent]] then
+ if trace_export then
+ report_export("%w<!-- injecting spacing 8 (kern %p) -->",currentdepth,kern)
+ end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+ end
+ elseif a then
+ -- e.g LOGO<space>LOGO
+ if trace_export then
+ report_export("%w<!-- processing kern, threshold %p, tag %s => %s -->",currentdepth,limit,last,a)
+ end
+ last = a
+ pushcontent()
+ if trace_export then
+ report_export("%w<!-- injecting spacing 9 (kern %p) -->",currentdepth,kern)
+ end
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = " "
+-- currentnesting = taglist[last]
+currentnesting = t
+ pushentry(currentnesting)
+ currentattribute = last
+ end
+ end
+ end
+end
+ end
+ elseif id == whatsit_code then
+ if subtype == userdefinedwhatsit_code then
+ -- similar to images, see above
+ local at = getattr(n,a_tagged)
+ if nofcurrentcontent > 0 then
+ pushcontent()
+ pushentry(currentnesting) -- ??
+ end
+ pushentry(taglist[at])
+ if trace_export then
+ report_export("%w<!-- processing anchor tagged %a",currentdepth,last)
+ end
+ last = nil
+ currentparagraph = nil
+ end
+ elseif not paragraph and id == par_code and start_of_par(n) then
+ paragraph = getattr(n,a_taggedpar)
+ elseif id == disc_code then
+ -- very unlikely because we stripped them
+ local pre, post, replace = getdisc(n)
+ if keephyphens then
+ if pre and not getnext(pre) and isglyph(pre) == 0xAD then -- hyphencode then
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = hyphen
+ end
+ end
+ if replace then
+ collectresults(replace,nil)
+ end
+ end
+ p = n
+ pid = id
+ end
+ if maybewrong then
+ showmaybe(maybewrong)
+ end
+ end
+
+ function nodes.handlers.export(head) -- hooks into the page builder
+ starttiming(treehash)
+ if trace_export then
+ report_export("%w<!-- start flushing page -->",currentdepth)
+ end
+ -- continueexport()
+ restart = true
+ collectresults(head)
+ if trace_export then
+ report_export("%w<!-- stop flushing page -->",currentdepth)
+ end
+ stoptiming(treehash)
+ return head
+ end
+
+ function nodes.handlers.checkparcounter(p)
+ setattr(p,a_taggedpar,texgetcount("tagparcounter") + 1)
+ return p
+ end
+
+ function builders.paragraphs.tag(head)
+ noftextblocks = noftextblocks + 1
+ for n, subtype in nexthlist, head do
+ if subtype == linelist_code then
+ setattr(n,a_textblock,noftextblocks)
+ elseif subtype == glue_code or subtype == kern_code then -- no need to set fontkerns
+ setattr(n,a_textblock,0)
+ end
+ end
+ return false
+ end
+
+end
+
+do
+
+ local xmlcollected = xml.collected
+ local xmlsetcomment = xml.setcomment
+
+local xmlpreamble = [[
+<?xml version="1.0" encoding="UTF-8" standalone="%standalone%" ?>
+
+<!--
+
+ input filename : %filename%
+ processing date : %date%
+ context version : %contextversion%
+ exporter version : %exportversion%
+
+-->
+
+]]
+
+ local flushtree = wrapups.flushtree
+
+ local function wholepreamble(standalone)
+ return replacetemplate(xmlpreamble, {
+ standalone = standalone and "yes" or "no",
+ filename = tex.jobname,
+ date = included.date and os.fulltime(),
+ contextversion = environment.version,
+ exportversion = exportversion,
+ })
+ end
+
+
+local csspreamble = [[
+<?xml-stylesheet type="text/css" href="%filename%" ?>
+]]
+
+local cssheadlink = [[
+<link type="text/css" rel="stylesheet" href="%filename%" />
+]]
+
+ local function allusedstylesheets(cssfiles,files,path)
+ local done = { }
+ local result = { }
+ local extras = { }
+ for i=1,#cssfiles do
+ local cssfile = cssfiles[i]
+ if type(cssfile) ~= "string" then
+ -- error
+ elseif cssfile == "export-example.css" then
+ -- ignore
+ elseif not done[cssfile] then
+ cssfile = joinfile(path,basename(cssfile))
+ report_export("adding css reference '%s'",cssfile)
+ files[#files+1] = cssfile
+ result[#result+1] = replacetemplate(csspreamble, { filename = cssfile })
+ extras[#extras+1] = replacetemplate(cssheadlink, { filename = cssfile })
+ done[cssfile] = true
+ end
+ end
+ return concat(result), concat(extras)
+ end
+
+local elementtemplate = [[
+/* element="%element%" detail="%detail%" chain="%chain%" */
+
+%element%,
+%namespace%div.%element% {
+ display: %display% ;
+}]]
+
+local detailtemplate = [[
+/* element="%element%" detail="%detail%" chain="%chain%" */
+
+%element%[detail=%detail%],
+%namespace%div.%element%.%detail% {
+ display: %display% ;
+}]]
+
+-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd" >
+
+local htmltemplate = [[
+%preamble%
+
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
+
+ <head>
+
+ <meta charset="utf-8"/>
+
+ <title>%title%</title>
+
+%style%
+
+ </head>
+ <body>
+ <div class="document" xmlns="http://www.pragma-ade.com/context/export">
+
+<div class="warning">Rendering can be suboptimal because there is no default/fallback css loaded.</div>
+
+%body%
+
+ </div>
+ </body>
+</html>
+]]
+
+ local displaymapping = {
+ inline = "inline",
+ display = "block",
+ mixed = "inline",
+ }
+
+ local function allusedelements(filename)
+ local result = { replacetemplate(namespacetemplate, {
+ what = "template",
+ filename = filename,
+ namespace = contextns,
+ -- cssnamespaceurl = usecssnamespace and cssnamespaceurl or "",
+ cssnamespaceurl = cssnamespaceurl,
+ },false,true) }
+ for element, details in sortedhash(used) do
+ if namespaces[element] then
+ -- skip math
+ else
+ for detail, what in sortedhash(details) do
+ local nature = what[1] or "display"
+ local chain = what[2]
+ local display = displaymapping[nature] or "block"
+ if detail == "" then
+ result[#result+1] = replacetemplate(elementtemplate, {
+ element = element,
+ display = display,
+ chain = chain,
+ namespace = usecssnamespace and namespace or "",
+ })
+ else
+ result[#result+1] = replacetemplate(detailtemplate, {
+ element = element,
+ display = display,
+ detail = detail,
+ chain = chain,
+ namespace = usecssnamespace and cssnamespace or "",
+ })
+ end
+ end
+ end
+ end
+ return concat(result,"\n\n")
+ end
+
+ local function allcontent(tree,embed)
+ local result = { }
+ flushtree(result,tree.data,"display") -- we need to collect images
+ result = concat(result)
+ -- no need to lpeg .. fast enough
+ result = gsub(result,"\n *\n","\n")
+ result = gsub(result,"\n +([^< ])","\n%1")
+ return result
+ end
+
+ -- local xhtmlpreamble = [[
+ -- <!DOCTYPE html PUBLIC
+ -- "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
+ -- "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd"
+ -- >
+ -- ]]
+
+ local function cleanxhtmltree(xmltree)
+ if xmltree then
+ local implicits = { }
+ local explicits = { }
+ local overloads = { }
+ for e in xmlcollected(xmltree,"*") do
+ local at = e.at
+ if at then
+ local explicit = at.explicit
+ local implicit = at.implicit
+ if explicit then
+ if not explicits[explicit] then
+ explicits[explicit] = true
+ at.id = explicit
+ if implicit then
+ overloads[implicit] = explicit
+ end
+ end
+ else
+ if implicit and not implicits[implicit] then
+ implicits[implicit] = true
+ at.id = "aut:" .. implicit
+ end
+ end
+ end
+ end
+ for e in xmlcollected(xmltree,"*") do
+ local at = e.at
+ if at then
+ local internal = at.internal
+ local location = at.location
+ if internal then
+ if location then
+ local explicit = overloads[location]
+ if explicit then
+ at.href = "#" .. explicit
+ else
+ at.href = "#aut:" .. internal
+ end
+ else
+ at.href = "#aut:" .. internal
+ end
+ else
+ if location then
+ at.href = "#" .. location
+ else
+ local url = at.url
+ if url then
+ at.href = url
+ else
+ local file = at.file
+ if file then
+ at.href = file
+ end
+ end
+ end
+ end
+ end
+ end
+ return xmltree
+ else
+ return xml.convert('<?xml version="1.0"?>\n<error>invalid xhtml tree</error>')
+ end
+ end
+
+ -- maybe the reverse: be explicit about what is permitted
+
+ local private = {
+ destination = true,
+ prefix = true,
+ reference = true,
+ --
+ id = true,
+ href = true,
+ --
+ implicit = true,
+ explicit = true,
+ --
+ url = true,
+ file = true,
+ internal = true,
+ location = true,
+ --
+ name = true, -- image name
+ used = true, -- image name
+ page = true, -- image name
+ width = true,
+ height = true,
+ --
+ }
+
+ local addclicks = true
+ local f_onclick = formatters[ [[location.href='%s']] ]
+ local f_onclick = formatters[ [[location.href='%s']] ]
+
+ local p_cleanid = lpeg.replacer { [":"] = "-" }
+ local p_cleanhref = lpeg.Cs(lpeg.P("#") * p_cleanid)
+
+ local p_splitter = lpeg.Ct ( (
+ lpeg.Carg(1) * lpeg.C((1-lpeg.P(" "))^1) / function(d,s) if not d[s] then d[s] = true return s end end
+ * lpeg.P(" ")^0 )^1 )
+
+
+ local classes = table.setmetatableindex(function(t,k)
+ local v = concat(lpegmatch(p_splitter,k,1,{})," ")
+ t[k] = v
+ return v
+ end)
+
+ local function makeclass(tg,at)
+ local detail = at.detail
+ local chain = at.chain
+ local extra = nil
+ local classes = { }
+ local nofclasses = 0
+ at.detail = nil
+ at.chain = nil
+ for k, v in next, at do
+ if not private[k] then
+ nofclasses = nofclasses + 1
+ classes[nofclasses] = k .. "-" .. v
+ end
+ end
+ if detail and detail ~= "" then
+ if chain and chain ~= "" then
+ if chain ~= detail then
+ extra = classes[tg .. " " .. chain .. " " .. detail]
+ elseif tg ~= detail then
+ extra = detail
+ end
+ elseif tg ~= detail then
+ extra = detail
+ end
+ elseif chain and chain ~= "" then
+ if tg ~= chain then
+ extra = chain
+ end
+ end
+ -- in this order
+ if nofclasses > 0 then
+ sort(classes)
+ classes = concat(classes," ")
+ if extra then
+ return tg .. " " .. extra .. " " .. classes
+ else
+ return tg .. " " .. classes
+ end
+ else
+ if extra then
+ return tg .. " " .. extra
+ else
+ return tg
+ end
+ end
+ end
+
+ -- Some elements are not supported (well) in css so we need to retain them. For
+ -- instance, tablecells have no colspan so basically that renders css table div
+ -- elements quite useless. A side effect is that we nwo can have conflicts when
+ -- we mix in with other html (as there is no reset). Of course, when it eventually
+ -- gets added, there is a change then that those not using the div abstraction
+ -- will be rediculed.
+ --
+ -- a table tr td th thead tbody tfoot
+ --
+
+ local crappycss = {
+ table = "table", tabulate = "table",
+ tablehead = "thead", tabulatehead = "thead",
+ tablebody = "tbody", tabulatebody = "tbody",
+ tablefoot = "tfoot", tabulatefoot = "tfoot",
+ tablerow = "tr", tabulaterow = "tr",
+ tablecell = "td", tabulatecell = "td",
+ }
+
+ local cssmapping = false
+
+ directives.register("export.nativetags", function(v)
+ cssmapping = v and crappycss or false
+ end)
+
+ local function remap(specification,source,target)
+ local comment = nil -- share comments
+ for c in xmlcollected(source,"*") do
+ if not c.special then
+ local tg = c.tg
+ local ns = c.ns
+ if ns == "m" then
+ if false then -- yes or no
+ c.ns = ""
+ c.at["xmlns:m"] = nil
+ end
+ -- elseif tg == "a" then
+ -- c.ns = ""
+ else
+ local dt = c.dt
+ local nt = #dt
+ if nt == 0 or (nt == 1 and dt[1] == "") then
+ if comment then
+ c.dt = comment
+ else
+ xmlsetcomment(c,"empty")
+ comment = c.dt
+ end
+ end
+ local at = c.at
+ local class = nil
+ local label = nil
+ if tg == "document" then
+ at.href = nil
+ at.detail = nil
+ at.chain = nil
+ elseif tg == "metavariable" then
+ label = at.name
+ at.detail = "metaname-" .. label
+ class = makeclass(tg,at)
+ else
+ class = makeclass(tg,at)
+ end
+ local id = at.id
+ local href = at.href
+ local attr = nil
+ if id then
+ id = lpegmatch(p_cleanid, id) or id
+ if href then
+ href = lpegmatch(p_cleanhref,href) or href
+ attr = {
+ class = class,
+ id = id,
+ href = href,
+ onclick = addclicks and f_onclick(href) or nil,
+ }
+ else
+ attr = {
+ class = class,
+ id = id,
+ }
+ end
+ else
+ if href then
+ href = lpegmatch(p_cleanhref,href) or href
+ attr = {
+ class = class,
+ href = href,
+ onclick = addclicks and f_onclick(href) or nil,
+ }
+ else
+ attr = {
+ class = class,
+ }
+ end
+ end
+ c.at = attr
+ if label then
+ attr.label = label
+ end
+ c.tg = cssmapping and cssmapping[tg] or "div"
+ end
+ end
+ end
+ end
+
+ -- local cssfile = nil directives.register("backend.export.css", function(v) cssfile = v end)
+
+ local embedfile = false directives.register("export.embed",function(v) embedfile = v end)
+
+ function structurestags.finishexport()
+
+ if exporting then
+ exporting = false
+ else
+ return
+ end
+
+ local onlyxml = finetuning.export == v_xml
+
+ starttiming(treehash)
+ --
+ finishexport()
+ --
+ report_export("")
+ if onlyxml then
+ report_export("exporting xml, no other files")
+ else
+ report_export("exporting xml, xhtml, html and css files")
+ end
+ report_export("")
+ --
+ wrapups.fixtree(tree)
+ wrapups.collapsetree(tree)
+ wrapups.indextree(tree)
+ wrapups.checktree(tree)
+ wrapups.breaktree(tree)
+ wrapups.finalizetree(tree)
+ --
+ wrapups.hashlistdata()
+ --
+ local askedname = finetuning.file
+ --
+ -- we use a dedicated subpath:
+ --
+ -- ./jobname-export
+ -- ./jobname-export/images
+ -- ./jobname-export/styles
+ -- ./jobname-export/styles
+ -- ./jobname-export/jobname-export.xml
+ -- ./jobname-export/jobname-export.xhtml
+ -- ./jobname-export/jobname-export.html
+ -- ./jobname-export/jobname-specification.lua
+ -- ./jobname-export/styles/jobname-defaults.css
+ -- ./jobname-export/styles/jobname-styles.css
+ -- ./jobname-export/styles/jobname-images.css
+ -- ./jobname-export/styles/jobname-templates.css
+
+ if type(askedname) ~= "string" or askedname == "" then
+ askedname = tex.jobname
+ end
+
+ local usedname = nameonly(askedname)
+ local basepath = usedname .. "-export"
+ local imagepath = joinfile(basepath,"images")
+ local stylepath = joinfile(basepath,"styles")
+
+ local function validpath(what,pathname)
+ if lfs.isdir(pathname) then
+ report_export("using existing %s path %a",what,pathname)
+ return pathname
+ end
+ lfs.mkdir(pathname)
+ if lfs.isdir(pathname) then
+ report_export("using cretated %s path %a",what,basepath)
+ return pathname
+ else
+ report_export("unable to create %s path %a",what,basepath)
+ return false
+ end
+ end
+
+ if not (validpath("export",basepath) and validpath("images",imagepath) and validpath("styles",stylepath)) then
+ return
+ end
+
+ -- we're now on the dedicated export subpath so we can't clash names
+ --
+ -- a xhtml suffix no longer seems to be work well with browsers
+
+ local xmlfilebase = addsuffix(usedname .. "-raw","xml" )
+ local xhtmlfilebase = addsuffix(usedname .. "-tag","xhtml")
+ local htmlfilebase = addsuffix(usedname .. "-div","html")
+ local specificationfilebase = addsuffix(usedname .. "-pub","lua" )
+
+ local xmlfilename = joinfile(basepath, xmlfilebase )
+ local xhtmlfilename = joinfile(basepath, xhtmlfilebase )
+ local htmlfilename = joinfile(basepath, htmlfilebase )
+ local specificationfilename = joinfile(basepath, specificationfilebase)
+ --
+ local defaultfilebase = addsuffix(usedname .. "-defaults", "css")
+ local imagefilebase = addsuffix(usedname .. "-images", "css")
+ local stylefilebase = addsuffix(usedname .. "-styles", "css")
+ local templatefilebase = addsuffix(usedname .. "-templates","css")
+ --
+ local defaultfilename = joinfile(stylepath,defaultfilebase )
+ local imagefilename = joinfile(stylepath,imagefilebase )
+ local stylefilename = joinfile(stylepath,stylefilebase )
+ local templatefilename = joinfile(stylepath,templatefilebase)
+
+ local cssfile = finetuning.cssfile
+
+ -- we keep track of all used files
+
+ local files = {
+ }
+
+ -- we always load the defaults and optionally extra css files; we also copy the example
+ -- css file so that we always have the latest version
+
+ local cssfiles = {
+ defaultfilebase,
+ imagefilebase,
+ stylefilebase,
+ }
+
+ local cssextra = cssfile and table.unique(settings_to_array(cssfile)) or { }
+
+ -- at this point we're ready for the content; the collector also does some
+ -- housekeeping and data collecting; at this point we still have an xml
+ -- representation that uses verbose element names and carries information in
+ -- attributes
+
+ local data = tree.data
+ for i=1,#data do
+ if data[i].tg ~= "document" then
+ data[i] = { }
+ end
+ end
+
+ local result = allcontent(tree,embedmath) -- embedfile is for testing
+
+ -- ugly but so be it:
+
+ local extradata = structures.tags.getextradata()
+ if extradata then
+ local t = { "" }
+ t[#t+1] = "<extradata>"
+ for name, action in sortedhash(extradata) do
+ t[#t+1] = action()
+ end
+ t[#t+1] = "</extradata>"
+ t[#t+1] = "</document>"
+ -- we use a function because otherwise we can have a bad capture index
+ result = gsub(result,"</document>",function()
+ return concat(t,"\n")
+ end)
+ end
+
+ -- done with ugly
+
+ if onlyxml then
+
+ os.remove(defaultfilename)
+ os.remove(imagefilename)
+ os.remove(stylefilename)
+ os.remove(templatefilename)
+
+ for i=1,#cssextra do
+ os.remove(joinfile(stylepath,basename(source)))
+ end
+
+ -- os.remove(xmlfilename)
+
+ os.remove(imagefilename)
+ os.remove(stylefilename)
+ os.remove(templatefilename)
+ os.remove(xhtmlfilename)
+ os.remove(specificationfilename)
+ os.remove(htmlfilename)
+
+ result = concat {
+ wholepreamble(true),
+ "<!-- This export file is used for filtering runtime only! -->\n",
+ result,
+ }
+
+ report_export("saving xml data in %a",xmlfilename)
+ io.savedata(xmlfilename,result)
+
+ return
+
+ end
+
+ local examplefilename = resolvers.findfile("export-example.css")
+ if examplefilename then
+ local data = io.loaddata(examplefilename)
+ if not data or data == "" then
+ data = "/* missing css file */"
+ elseif not usecssnamespace then
+ data = gsub(data,cssnamespace,"")
+ end
+ io.savedata(defaultfilename,data)
+ end
+
+ if cssfile then
+ for i=1,#cssextra do
+ local source = addsuffix(cssextra[i],"css")
+ local target = joinfile(stylepath,basename(source))
+ cssfiles[#cssfiles+1] = source
+ if not lfs.isfile(source) then
+ source = joinfile("../",source)
+ end
+ if lfs.isfile(source) then
+ report_export("copying %s",source)
+ file.copy(source,target)
+ end
+ end
+ end
+
+ local x_styles, h_styles = allusedstylesheets(cssfiles,files,"styles")
+
+ local attach = backends.nodeinjections.attachfile
+
+ if embedfile and attach then
+ -- only for testing
+ attach {
+ data = concat{ wholepreamble(true), result },
+ name = basename(xmlfilename),
+ registered = "export",
+ title = "raw xml export",
+ method = v_hidden,
+ mimetype = "application/mathml+xml",
+ }
+ end
+
+ result = concat {
+ wholepreamble(true),
+ x_styles, -- adds to files
+ result,
+ }
+
+ cssfiles = table.unique(cssfiles)
+
+ -- we're now ready for saving the result in the xml file
+
+ report_export("saving xml data in %a",xmlfilename)
+ io.savedata(xmlfilename,result)
+
+ report_export("saving css image definitions in %a",imagefilename)
+ io.savedata(imagefilename,wrapups.allusedimages(usedname))
+
+ report_export("saving css style definitions in %a",stylefilename)
+ io.savedata(stylefilename,wrapups.allusedstyles(usedname))
+
+ report_export("saving css template in %a",templatefilename)
+ io.savedata(templatefilename,allusedelements(usedname))
+
+ -- additionally we save an xhtml file; for that we load the file as xml tree
+
+ report_export("saving xhtml variant in %a",xhtmlfilename)
+
+ local xmltree = cleanxhtmltree(xml.convert(result))
+
+ -- local xmltree = xml.convert(result)
+ -- for c in xml.collected(xmltree,"m:mtext[lastindex()=1]/m:mrow") do
+ -- print(c)
+ -- end
+ -- for c in xml.collected(xmltree,"mtext/mrow") do
+ -- print(c)
+ -- end
+ -- local xmltree = cleanxhtmltree(xmltree)
+
+ xml.save(xmltree,xhtmlfilename)
+
+ -- now we save a specification file that can b eused for generating an epub file
+
+ -- looking at identity is somewhat redundant as we also inherit from interaction
+ -- at the tex end
+
+ local identity = interactions.general.getidentity()
+ local metadata = structures.tags.getmetadata()
+
+ local specification = {
+ name = usedname,
+ identifier = os.uuid(),
+ images = wrapups.uniqueusedimages(),
+ imagefile = joinfile("styles",imagefilebase),
+ imagepath = "images",
+ stylepath = "styles",
+ xmlfiles = { xmlfilebase },
+ xhtmlfiles = { xhtmlfilebase },
+ htmlfiles = { htmlfilebase },
+ styles = cssfiles,
+ htmlroot = htmlfilebase,
+ language = languagenames[texgetcount("mainlanguagenumber")],
+ title = validstring(finetuning.title) or validstring(identity.title),
+ subtitle = validstring(finetuning.subtitle) or validstring(identity.subtitle),
+ author = validstring(finetuning.author) or validstring(identity.author),
+ firstpage = validstring(finetuning.firstpage),
+ lastpage = validstring(finetuning.lastpage),
+ metadata = metadata,
+ }
+
+ report_export("saving specification in %a",specificationfilename,specificationfilename)
+
+ xml.wipe(xmltree,"metadata") -- maybe optional
+
+ io.savedata(specificationfilename,table.serialize(specification,true))
+
+ -- the html export for epub is different in the sense that it uses div's instead of
+ -- specific tags
+
+ report_export("saving div based alternative in %a",htmlfilename)
+
+ remap(specification,xmltree)
+
+ -- believe it or not, but a <title/> can prevent viewing in browsers
+
+ local title = specification.title
+
+ if not title or title == "" then
+ title = metadata.title
+ if not title or title == "" then
+ title = usedname -- was: "no title"
+ end
+ end
+
+ local variables = {
+ style = h_styles,
+ body = xml.tostring(xml.first(xmltree,"/div")),
+ preamble = wholepreamble(false),
+ title = title,
+ }
+
+ io.savedata(htmlfilename,replacetemplate(htmltemplate,variables,"xml"))
+
+ -- finally we report how an epub file can be made (using the specification)
+
+ report_export("")
+ report_export('create epub with: mtxrun --script epub --make "%s" [--purge --rename --svgmath]',usedname)
+ report_export("")
+
+ stoptiming(treehash)
+ end
+
+ local enableaction = nodes.tasks.enableaction
+
+ function structurestags.initializeexport()
+ if not exporting then
+ report_export("enabling export to xml")
+ enableaction("shipouts","nodes.handlers.export")
+ enableaction("shipouts","nodes.handlers.accessibility")
+ enableaction("math", "noads.handlers.tags")
+ enableaction("everypar","nodes.handlers.checkparcounter")
+ luatex.registerstopactions(structurestags.finishexport)
+ exporting = true
+ end
+ end
+
+ function structurestags.setupexport(t)
+ merge(finetuning,t)
+ keephyphens = finetuning.hyphen == v_yes
+ exportproperties = finetuning.properties
+ if exportproperties == v_no then
+ exportproperties = false
+ end
+ end
+
+ statistics.register("xml exporting time", function()
+ if exporting then
+ return string.format("%s seconds, version %s", statistics.elapsedtime(treehash),exportversion)
+ end
+ end)
+
+end
+
+-- These are called at the tex end:
+
+implement {
+ name = "setupexport",
+ actions = structurestags.setupexport,
+ arguments = {
+ {
+ { "align" },
+ { "bodyfont", "dimen" },
+ { "width", "dimen" },
+ { "properties" },
+ { "hyphen" },
+ { "title" },
+ { "subtitle" },
+ { "author" },
+ { "firstpage" },
+ { "lastpage" },
+ { "svgstyle" },
+ { "cssfile" },
+ { "file" },
+ { "export" },
+ }
+ }
+}
+
+implement {
+ name = "finishexport",
+ actions = structurestags.finishexport,
+}
+
+implement {
+ name = "initializeexport",
+ actions = structurestags.initializeexport,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-exp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-exp.mkxl
new file mode 100644
index 00000000000..5aebd302fdc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-exp.mkxl
@@ -0,0 +1,361 @@
+%D \module
+%D [ file=back-exp,
+%D version=2010.08.22,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=XML export,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% see t:/export/todo-fonts.tex for (future) experiment
+
+\writestatus{loading}{ConTeXt Backend Macros / XML export}
+
+\registerctxluafile{back-exp} {autosuffix}
+\registerctxluafile{back-exp-imp-tag}{autosuffix}
+\registerctxluafile{back-exp-imp-mth}{autosuffix}
+\registerctxluafile{back-exp-imp-ref}{autosuffix}
+
+%D This is an experimental exporter and a logical follow up on tagging. The
+%D exporter assumes a properly tagged document. Some elements get a couple
+%D of attributes because otherwise rendering information would get lost. In
+%D general we assume that when the \XML\ is converted to \HTML\ some stylesheet
+%D is applied anyway.
+
+\unprotect
+
+% we can replace this by a more generic attributeset mechanism where we bind
+% to any element (needed anyway, see userdata thingies)
+
+\definesystemattribute[taggedpar][public]
+
+\permanent\tolerant\protected\def\setelementexporttag[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \clf_settagproperty{#1}{export}{#2}%
+ \or
+ \clf_settagproperty{#1}{#2}{#3}%
+ \fi}
+
+% todo: no need for calls when trialtypesetting
+
+\permanent\def\taggedctxcommand
+ {\iftrialtypesetting
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\ctxcommand
+ \fi}
+
+\newcount\tagparcounter
+
+\aliased\let\dotagsetparcounter\relax
+\aliased\let\doresetparcounter \relax
+
+\appendtoks
+ \doresetparcounter
+\to \everyflushatnextpar
+
+% \appendtoks
+% \dotagsetparcounter
+% \to \everypar
+
+\prependtoks
+ \dotagsetparcounter
+\to \everypar
+
+% \appendtoks
+% \dotagsetparcounter
+% \to \neverypar
+
+\prependtoks
+ \dotagsetparcounter
+\to \neverypar
+
+\appendtoks
+ \dotagsetparcounter
+\to \everytabulatepar % tricky, maybe this should be neverypar
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagTABLEcell
+ {\iftrialtypesetting\else
+ \clf_settagtablecell
+ \numexpr\tablecellrows\relax
+ \numexpr\tablecellcolumns\relax
+ \numexpr\raggedstatus\relax
+ \fi}%
+ % brrr, we need to tag empty cells (unless we start numbering)
+ \enforced\permanent\protected\def\dotagTABLEsignal
+ {\signalcharacter}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagtabulatecell
+ {\iftrialtypesetting\else
+ \clf_settagtabulatecell\c_tabl_tabulate_align\c_tabl_tabulate_kind
+ \fi}%
+ \enforced\permanent\protected\def\dotagtabulatesignal
+ {\dontleavehmode\signalcharacter\ignorespaces}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsynonym
+ {\iftrialtypesetting\else\clf_settagsynonym{\currentsynonymtag}\fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsorting
+ {\iftrialtypesetting\else\clf_settagsorting{\currentsortingtag}\fi}%
+\to \everyenableelements
+
+\appendtoks % frozen and assumed global per highlight class
+ \enforced\permanent\protected\def\dotagconstruct
+ {\iftrialtypesetting\else
+ \clf_settagconstruct
+ {\currentstartstop}%
+ {\startstopparameter\c!style}%
+ \c_attr_color
+ \ifvmode\plusone\else\zerocount\fi
+ \relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks % frozen and assumed global per highlight class
+ \enforced\permanent\protected\def\dotaghighlight
+ {\iftrialtypesetting\else
+ \clf_settaghighlight
+ {\currenthighlight}%
+ {\highlightparameter\c!style}
+ \c_attr_color
+ \ifvmode\plusone\else\zerocount\fi
+ \relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks % we can have differently scaled images
+ \enforced\permanent\protected\def\dotagfigure
+ {\iftrialtypesetting\else
+ \clf_settagfigure
+ {\figurefileoriginal}%
+ {\figurefullname}%
+ {\figurefilepage}%
+ \dimexpr\figurewidth\relax
+ \dimexpr\figureheight\relax
+ {\figurelabel}%
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagcombination
+ {\iftrialtypesetting\else
+ \clf_settagcombination
+ \numexpr\combinationparameter\c!nx\relax
+ \numexpr\combinationparameter\c!ny\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsetparcounter
+ {\global\advance\tagparcounter\plusone\c_attr_taggedpar\tagparcounter}%
+ \enforced\permanent\protected\def\doresetparcounter
+ {\c_attr_taggedpar\attributeunsetvalue}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsetitemgroup
+ {\iftrialtypesetting\else
+ \clf_settagitemgroup
+ \ifconditional\c_strc_itemgroups_pack true\else false\fi\space
+ \numexpr\currentitemlevel\relax
+ {\currentitemgroupsymbol}%
+ \fi}%
+ \enforced\permanent\protected\def\dotagsetitem#1%
+ {\iftrialtypesetting\else
+ \clf_settagitem{#1}%
+ \fi}%
+\to \everyenableelements
+
+% \appendtoks
+% \enforced\permanent\protected\def\dotagsetdescription
+% {\iftrialtypesetting\else
+% \clf_settagdescription{\currentdescription}\currentdescriptionnumberentry\relax
+% \fi}%
+% \to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsetnotation
+ {\iftrialtypesetting\else
+ \clf_settagnotation{\currentnote}\currentnotenumber\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsetnotesymbol
+ {\iftrialtypesetting\else
+ \clf_settagnotationsymbol{\currentnote}\currentnotenumber\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagregisterlocation
+ {\iftrialtypesetting\else
+ \clf_settagregister{\currentregister}\currentregisternumber\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotaglistlocation
+ {\iftrialtypesetting\else
+ \clf_settaglist\currentlistindex\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsetdelimitedsymbol#1%
+ {\iftrialtypesetting\else
+ \clf_settagdelimitedsymbol{#1}\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagsetsubsentencesymbol#1%
+ {\iftrialtypesetting\else
+ \clf_settagsubsentencesymbol{#1}\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagregisterfloat#1#2%
+ {\iftrialtypesetting\else
+ \clf_settagfloat{#1}{#2}\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\let\specialfixedspace \explicitfixedspace
+ \enforced\let\specialobeyedspace \explicitobeyedspace
+ \enforced\let\specialstretchedspace\explicitstretchedspace
+ \enforced\let\specialcontrolspace \explicitcontrolspace
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagregisterformula#1%
+ {\iftrialtypesetting\else
+ \clf_settagformulacontent#1\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagmarginanchor#1%
+ {\iftrialtypesetting\else\clf_settagmarginanchor#1\relax\fi}%
+ \enforced\permanent\protected\def\dotagmargintext#1%
+ {\iftrialtypesetting\else\clf_settagmargintext#1\relax\fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagpublication#1#2%
+ {\iftrialtypesetting\else
+ \clf_settagpublication{#1}{#2}\relax
+ \fi}%
+\to \everyenableelements
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagparagraph#1%
+ {\iftrialtypesetting\else
+ \clf_settagparagraph{#1}\relax
+ \fi}%
+\to \everyenableelements
+
+% The action: \setupbackend[export=yes] % or filename
+
+% maybe xhtml css settings will move to setupexport
+
+% maybe some day a definer
+
+\installcorenamespace{export}
+
+\installparameterhandler \??export {export}
+\installsetuphandler \??export {export}
+
+\setupexport
+ [\c!align=\number\raggedstatus,
+ \c!bodyfont=\bodyfontsize,
+ \c!width=\textwidth,
+ \c!title={\directinteractionparameter\c!title},
+ \c!subtitle={\directinteractionparameter\c!subtitle},
+ \c!author={\directinteractionparameter\c!author},
+ % \c!firstpage=, % imagename
+ % \c!lastpage=, % imagename
+ \c!properties=\v!no, % no: ignore, yes: as attribute, otherwise: use as prefix
+ \c!hyphen=\v!no,
+ \c!svgstyle=,
+ \c!cssfile=,
+ \c!file=]
+
+\resetsystemmode\v!export
+
+\permanent\protected\def\doinitializeexport
+ {\edef\p_export{\backendparameter\c!export}%
+ \ifempty\p_export \else
+ % yes | xml
+ \setuptagging[\c!state=\v!start]%
+ \clf_initializeexport
+ \setsystemmode\v!export
+ \exportingtrue
+ \writestatus\m!backend\empty % so messages will stand out a bit
+ \the\everyinitializeexport
+ \writestatus\m!backend\empty % so messages will stand out a bit
+ \enforced\glet\doinitializeexport\relax
+ \fi}
+
+\permanent\protected\def\dostartexport
+ {%\glet\dostartexport\relax
+ \let\currentexport\empty
+ \clf_setupexport
+ align {\exportparameter\c!align}%
+ bodyfont \dimexpr\exportparameter\c!bodyfont\relax
+ width \dimexpr\exportparameter\c!width\relax
+ properties {\exportparameter\c!properties}%
+ hyphen {\exportparameter\c!hyphen}%
+ title {\exportparameter\c!title}%
+ subtitle {\exportparameter\c!subtitle}%
+ author {\exportparameter\c!author}%
+ firstpage {\exportparameter\c!firstpage}%
+ lastpage {\exportparameter\c!lastpage}%
+ svgstyle {\exportparameter\c!svgstyle}%
+ cssfile {\exportparameter\c!cssfile}%
+ file {\exportparameter\c!file}%
+ export {\backendparameter\c!export}%
+ \relax}
+
+\permanent\protected\def\dostopexport
+ {\enforced\glet\dostopexport\relax
+ \clf_finishexport}
+
+\appendtoks
+ \doinitializeexport
+\to \everysetupbackend
+
+\appendtoks
+ \ifexporting
+ \dostartexport
+ \fi
+\to \everystarttext
+
+% better (before pdf gets closed, so we can embed), but it needs testing:
+
+\appendtoks
+ \ifexporting
+ \dostopexport
+ \fi
+\to \everystoptext
+
+\appendtoks
+ \ifexporting
+ \dostartexport % in case it is done inside \starttext
+ \fi
+\to \everystartdocument
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-ext.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-ext.mkxl
new file mode 100644
index 00000000000..a3d9d2633e8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-ext.mkxl
@@ -0,0 +1,95 @@
+%D \module
+%D [ file=back-ext,
+%D version=2019.02.08, % and later, merged file
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=Output,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% \registerctxluafile{back-ext}{autosuffix}
+
+\unprotect
+
+% \let\normalopenout \openout
+% \let\normalwrite \write
+% \let\normalcloseout\closeout
+% \let\normallatelua \latelua
+% \let\normalspecial \special
+
+% rotation
+
+\permanent\protected\def\dostartrotation#1%
+ {\forcecolorhack
+ \clf_startrotation#1\relax} % todo: implement without Q q
+
+\permanent\protected\def\dostoprotation
+ {\clf_stoprotation
+ \forcecolorhack}
+
+% scaling
+
+\permanent\protected\def\dostartscaling#1#2%
+ {\forcecolorhack
+ \clf_startscaling rx #1 ry #2\relax}
+
+\permanent\protected\def\dostopscaling
+ {\clf_stopscaling
+ \forcecolorhack}
+
+% mirroring
+
+\permanent\protected\def\dostartmirroring
+ {\clf_startmirroring}
+
+\permanent\protected\def\dostopmirroring
+ {\clf_stopmirroring}
+
+% transform
+
+\permanent\protected\def\dotransformnextbox#1#2#3#4#5#6%
+ {\dowithnextbox{\dodotransformnextbox{#1}{#2}{#3}{#4}{#5}{#6}}}
+
+\permanent\protected\def\dodotransformnextbox#1#2#3#4#5#6%
+ {\hpack
+ {\kern #5\onebasepoint
+ \raise#6\onebasepoint
+ \hpack
+ {\clf_startmatrix rx #1 sx #2 sy #3 ry #4\relax
+ \box\nextbox
+ \clf_stopmatrix}}}
+
+%D \macros
+%D {dostartclipping,dostopclipping}
+%D
+%D Clipping is implemented in such a way that an arbitrary code can be fed.
+%D
+%D \starttyping
+%D \dostartclipping {pathname} {width} {height}
+%D \dostopclipping
+%D \stoptyping
+
+\mutable\let\MPclippath\empty
+
+\permanent\protected\def\dostartclipping#1#2#3% we can move this to lua and only set a box here
+ {\forcecolorhack
+ \meta_grab_clip_path{#1}{#2}{#3}%
+ \clf_startclipping{\MPclippath}%
+ \glet\MPclippath\empty}
+
+\permanent\protected\def\dostopclipping
+ {\clf_stopclipping}
+
+%D This wrapper is needed when you use code that messes with e.g. local color
+%D directives, invisible for the rest of the machinery. It's only needed in very
+%D special cases, like around the above scaler directives:
+
+\permanent\protected\edef\hcontainer{\hpack \s!direction\directionlefttoright \s!container\space}
+\permanent\protected\edef\vcontainer{\vpack \s!direction\directionlefttoright \s!container\space}
+\permanent\protected\edef\tcontainer{\tpack \s!direction\directionlefttoright \s!container\space}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-ini.lmt
new file mode 100644
index 00000000000..92270574249
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-ini.lmt
@@ -0,0 +1,127 @@
+if not modules then modules = { } end modules ['back-ini'] = {
+ version = 1.001,
+ comment = "companion to back-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, type = next, type
+local format = string.format
+
+backends = backends or { }
+local backends = backends
+
+local context = context
+
+local trace = false trackers.register("backend", function(v) trace = v end)
+local report = logs.reporter("backend")
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local setaction = nodes.tasks.setaction
+
+local implement = interfaces.implement
+local variables = interfaces.variables
+
+local texset = tex.set
+
+local nodeinjections = { }
+local codeinjections = { }
+local registrations = { }
+local tables = allocate()
+
+local function nothing()
+ return nil
+end
+
+backends.nothing = nothing
+
+local function donothing(t,k)
+ t[k] = nothing
+ return nothing
+end
+
+setmetatableindex(nodeinjections, donothing)
+setmetatableindex(codeinjections, donothing)
+setmetatableindex(registrations, donothing)
+
+local defaults = {
+ nodeinjections = nodeinjections,
+ codeinjections = codeinjections,
+ registrations = registrations,
+ tables = tables,
+}
+
+backends.defaults = defaults
+
+backends.nodeinjections = { } setmetatableindex(backends.nodeinjections, nodeinjections)
+backends.codeinjections = { } setmetatableindex(backends.codeinjections, codeinjections)
+backends.registrations = { } setmetatableindex(backends.registrations, registrations)
+backends.tables = { } setmetatableindex(backends.tables, tables)
+
+backends.current = "unknown"
+
+function backends.install(what)
+ if type(what) == "string" then
+ local backend = backends[what]
+ if backend then
+ if trace then
+ if backend.comment then
+ report("initializing backend %a, %a",what,backend.comment)
+ else
+ report("initializing backend %a",what)
+ end
+ end
+ backends.current = what
+ for category, default in next, defaults do
+ local target = backends[category]
+ local plugin = backend [category]
+ setmetatableindex(plugin, default)
+ setmetatableindex(target, plugin)
+ end
+ elseif trace then
+ report("no backend named %a",what)
+ end
+ end
+end
+
+statistics.register("used backend", function()
+ local bc = backends.current
+ if bc ~= "unknown" then
+ return format("%s (%s)",bc,backends[bc].comment or "no comment")
+ else
+ return nil
+ end
+end)
+
+-- can best be here
+
+interfaces.implement {
+ name = "setrealspaces",
+ arguments = "string",
+ actions = function(v)
+ setaction("shipouts","nodes.handlers.accessibility",v == variables.yes)
+ end
+}
+
+-- moved to here
+
+local included = table.setmetatableindex( {
+ context = true,
+ id = true,
+ metadata = true,
+ date = true,
+ id = true,
+ pdf = true,
+}, function(t,k)
+ return true
+end)
+
+backends.included = included
+
+-- could also be codeinjections
+
+function backends.getcallbackstate()
+ return { count = status.late_callbacks or 0 }
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-ini.mkxl
new file mode 100644
index 00000000000..fad9c1e709d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-ini.mkxl
@@ -0,0 +1,74 @@
+%D \module
+%D [ file=back-ini,
+%D version=2009.04.15,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Backend Macros / Initialization}
+
+\registerctxluafile{back-ini}{autosuffix}
+
+\unprotect
+
+%D Right from the start \CONTEXT\ had a backend system based on runtime pluggable
+%D code. As most backend issues involved specials and since postprocessors had not
+%D that much in common, we ended up with a system where we could switch backend as
+%D well as output code for multiple backends at the same time.
+%D
+%D Because \LUATEX\ has the backend built in, and since some backend issues have
+%D been moved to the frontend I decided to provide new backend code for \MKIV,
+%D starting with what was actually used.
+%D
+%D At this moment \DVI\ is no longer used for advanced document output and we
+%D therefore dropped support for this format. Future versions might support more
+%D backends again, but this has a low priority.
+
+%D The exact page model depends on the backend so we just define some variables that
+%D are used. A helper at the \LUA\ end will synchronize with the internal variables.
+%D We store these in the format.
+
+\ifdefined\everybackendshipout \else \newtoks\everybackendshipout \fi
+\ifdefined\everylastbackendshipout \else \newtoks\everylastbackendshipout \fi
+\ifdefined\everybackendlastinshipout \else \newtoks\everybackendlastinshipout \fi
+
+\installcorenamespace{backend}
+
+\installsetuponlycommandhandler \??backend {backend}
+
+%D \starttyping
+%D \setupbackend[space=yes]
+%D \stoptyping
+
+\appendtoks
+ \clf_setrealspaces{\backendparameter\c!space}%
+\to \everysetupbackend
+
+%D For the moment this is an experiment (defauls is pdf, but we also
+%D have lua, json and mps).
+%D
+%D \starttyping
+%D % \setupoutput[lua]
+%D % \setupoutput[json]
+%D % \setupoutput[mps]
+%D % \setupoutput[none] % for testing only
+%D % \setupoutput[empty] % for testing only
+%D
+%D \starttext
+%D \dorecurse{1000}{
+%D {\tf \red \samplefile{tufte}} \par
+%D {\bf \green \samplefile {ward}} \par
+%D {\sl \blue \samplefile{davis}} \par
+%D }
+%D \stoptext
+%D \stoptyping
+
+\permanent\protected\def\setupoutput[#1]{\clf_enabledriver{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-lua.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-lua.lmt
new file mode 100644
index 00000000000..dc836f12bb3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-lua.lmt
@@ -0,0 +1,366 @@
+if not modules then modules = { } end modules ['back-lua'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- we can remap fonts
+
+local fontproperties = fonts.hashes.properties
+local fontparameters = fonts.hashes.parameters
+local fontshapes = fonts.hashes.shapes
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local bpfactor = number.dimenfactors.bp
+local texgetbox = tex.getbox
+
+local rulecodes = nodes.rulecodes
+local normalrule_code = rulecodes.normal
+----- boxrule_code = rulecodes.box
+----- imagerule_code = rulecodes.image
+----- emptyrule_code = rulecodes.empty
+----- userrule_code = rulecodes.user
+----- overrule_code = rulecodes.over
+----- underrule_code = rulecodes.under
+----- fractionrule_code = rulecodes.fraction
+----- radicalrule_code = rulecodes.radical
+local outlinerule_code = rulecodes.outline
+
+-- todo : per instance
+
+local pages = { }
+local fonts = { }
+local names = { }
+local mapping = { }
+local used = { }
+local shapes = { }
+local glyphs = { }
+local buffer = { }
+local b = 0
+local converter = nil
+
+local compact = false -- true
+local shapestoo = true
+
+local x, y, d, f, c, w, h, t, r, o
+
+local function reset()
+ buffer = { }
+ b = 0
+ t = nil
+ x = nil
+ y = nil
+ d = nil
+ f = nil
+ c = nil
+ w = nil
+ h = nil
+ r = nil
+ o = nil
+end
+
+local function result()
+ -- todo: we're now still in the pdf backend but need different codeinjections
+ local codeinjections = backends.pdf.codeinjections
+ local getvariable = codeinjections.getidentityvariable or function() end
+ local jobname = environment.jobname or tex.jobname or "unknown"
+ return {
+ metadata = {
+ unit = "bp",
+ jobname = jobname,
+ title = getvariable("title") or jobname,
+ subject = getvariable("subject"),
+ author = getvariable("author"),
+ keywords = getvariable("keywords"),
+ time = os.date("%Y-%m-%d %H:%M"),
+ engine = environment.luatexengine .. " " .. environment.luatexversion,
+ context = environment.version,
+ },
+ fonts = fonts,
+ pages = pages,
+ shapes = shapes,
+ }
+end
+
+-- actions
+
+local function outputfilename(driver)
+ return tex.jobname .. "-output.lua"
+end
+
+local function save() -- might become a driver function that already is plugged into stopactions
+ local filename = outputfilename()
+ drivers.report("saving result in %a",filename)
+ starttiming(drivers)
+ local data = result()
+ if data then
+ io.savedata(filename,table.serialize(data))
+ end
+ stoptiming(drivers)
+end
+
+local function prepare(driver)
+ converter = drivers.converters.lmtx
+ luatex.registerstopactions(1,function()
+ save()
+ end)
+end
+
+local function initialize(driver,details)
+ reset()
+end
+
+local function finalize(driver,details)
+ local n = details.pagenumber
+ local b = details.boundingbox
+ pages[n] = {
+ number = n,
+ content = buffer,
+ boundingbox = {
+ b[1] * bpfactor,
+ b[2] * bpfactor,
+ b[3] * bpfactor,
+ b[4] * bpfactor,
+ },
+ }
+end
+
+local function wrapup(driver)
+end
+
+local function cleanup(driver)
+ reset()
+end
+
+local function convert(driver,boxnumber,pagenumber)
+ converter(driver,texgetbox(boxnumber),"page",pagenumber)
+end
+
+-- flushers
+
+local function updatefontstate(id)
+ if not mapping[id] then
+ local fn = #fonts + 1
+ mapping[id] = fn
+ local properties = fontproperties[id]
+ local parameters = fontparameters[id]
+ local filename = file.basename(properties.filename)
+ local fontname = properties.fullname or properties.fontname
+ if shapestoo then
+ if not names[fontname] then
+ local sn = #shapes+1
+ names[fontname] = sn
+ shapes[sn] = { }
+ glyphs[sn] = fontshapes[id].glyphs
+ end
+ end
+ fonts[fn] = {
+ filename = filename,
+ name = fontname,
+ size = parameters.size * bpfactor,
+ shapes = shapestoo and names[fontname] or nil,
+ }
+ end
+end
+
+local function flushcharacter(current, pos_h, pos_v, pos_r, font, char)
+ local fnt = mapping[font]
+ b = b + 1
+ buffer[b] = {
+ t = "glyph" ~= t and "glyph" or nil,
+ f = font ~= f and fnt or nil,
+ c = char ~= c and char or nil,
+ x = pos_h ~= x and (pos_h * bpfactor) or nil,
+ y = pos_v ~= y and (pos_v * bpfactor) or nil,
+ d = pos_r ~= d and (pos_r == 1 and "r2l" or "l2r") or nil,
+ }
+ t = "glyph"
+ f = font
+ c = char
+ x = pos_h
+ y = pos_v
+ d = pos_r
+ if shapestoo then
+ -- can be sped up if needed
+ local sn = fonts[fnt].shapes
+ local shp = shapes[sn]
+ if not shp[char] then
+ shp[char] = glyphs[sn][char]
+ end
+ end
+end
+
+local function rule(pos_h, pos_v, pos_r, size_h, size_v, rule_s, rule_o)
+ b = b + 1
+ buffer[b] = {
+ t = "rule" ~= t and "rule" or nil,
+ r = rule_s ~= r and rule_s or nil,
+ o = rule_s == "outline" and rule_o ~= o and (rule_o * bpfactor) or nil,
+ w = size_h ~= w and (size_h * bpfactor) or nil,
+ h = size_v ~= h and (size_v * bpfactor) or nil,
+ x = pos_h ~= x and (pos_h * bpfactor) or nil,
+ y = pos_v ~= y and (pos_v * bpfactor) or nil,
+ d = pos_r ~= d and (pos_r == 1 and "r2l" or "l2r") or nil,
+ }
+ t = "rule"
+ w = size_h
+ h = size_v
+ x = pos_h
+ y = pos_v
+ d = pos_r
+end
+
+local function flushrule(current, pos_h, pos_v, pos_r, size_h, size_v, subtype)
+ local rule_s, rule_o
+ if subtype == normalrule_code then
+ rule_s = "normal"
+ elseif subtype == outlinerule_code then
+ rule_s = "outline"
+ rule_o = getdata(current)
+ else
+ return
+ end
+ return rule(pos_h, pos_v, pos_r, size_h, size_v, rule_s, rule_o)
+end
+
+local function flushsimplerule(pos_h, pos_v, pos_r, size_h, size_v)
+ return rule(pos_h, pos_v, pos_r, size_h, size_v, "normal", nil)
+end
+
+local function flushspecialrule(pos_h, pos_v, pos_r, w, h, d, l, outline)
+ -- no l support yet
+ return rule(pos_h, pos_v - d, pos_r, w, h + d, outline and "outline" or "normal")
+end
+
+-- file stuff too
+-- todo: default flushers
+-- also color (via hash)
+
+-- installer
+
+drivers.install {
+ name = "lua",
+ actions = {
+ prepare = prepare,
+ initialize = initialize,
+ finalize = finalize,
+ wrapup = wrapup,
+ cleanup = cleanup,
+ convert = convert,
+ outputfilename = outputfilename,
+ },
+ flushers = {
+ updatefontstate = updatefontstate,
+ character = flushcharacter,
+ rule = flushrule,
+ simplerule = flushsimplerule,
+ specialrule = flushspecialrule,
+ }
+}
+
+-- actions
+
+local function outputfilename(driver)
+ return tex.jobname .. "-output.json"
+end
+
+local function save() -- might become a driver function that already is plugged into stopactions
+ local filename = outputfilename()
+ drivers.report("saving result in %a",filename)
+ starttiming(drivers)
+ local data = result()
+ if data then
+ if not utilities.json then
+ require("util-jsn")
+ end
+ io.savedata(filename,utilities.json.tostring(data,not compact))
+ end
+ stoptiming(drivers)
+end
+
+local function prepare(driver)
+ converter = drivers.converters.lmtx
+ luatex.registerstopactions(1,function()
+ save()
+ end)
+end
+
+-- installer
+
+drivers.install {
+ name = "json",
+ actions = {
+ prepare = prepare,
+ initialize = initialize,
+ finalize = finalize,
+ wrapup = wrapup,
+ cleanup = cleanup,
+ convert = convert,
+ outputfilename = outputfilename,
+ },
+ flushers = {
+ updatefontstate = updatefontstate,
+ character = flushcharacter,
+ rule = flushrule,
+ simplerule = flushsimplerule,
+ specialrule = flushspecialrule,
+ }
+}
+
+-- actions
+
+local function outputfilename(driver)
+ return tex.jobname .. "-output.js"
+end
+
+local function save() -- might become a driver function that already is plugged into stopactions
+ local filename = outputfilename()
+ drivers.report("saving result in %a",filename)
+ starttiming(drivers)
+ local data = result()
+ if data then
+ if not utilities.json then
+ require("util-jsn")
+ end
+ io.savedata(filename,
+ "const JSON_CONTEXT = JSON.parse ( `" ..
+ utilities.json.tostring(data,not compact) ..
+ "` ) ;\n"
+ )
+ end
+ stoptiming(drivers)
+end
+
+local function prepare(driver)
+ converter = drivers.converters.lmtx
+ luatex.registerstopactions(1,function()
+ save()
+ end)
+end
+
+-- installer
+
+drivers.install {
+ name = "js",
+ actions = {
+ prepare = prepare,
+ initialize = initialize,
+ finalize = finalize,
+ wrapup = wrapup,
+ cleanup = cleanup,
+ convert = convert,
+ outputfilename = outputfilename,
+ },
+ flushers = {
+ updatefontstate = updatefontstate,
+ character = flushcharacter,
+ rule = flushrule,
+ simplerule = flushsimplerule,
+ specialrule = flushspecialrule,
+ }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-lua.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-lua.mkxl
new file mode 100644
index 00000000000..0fb1bcc88ef
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-lua.mkxl
@@ -0,0 +1,16 @@
+%D \module
+%D [ file=back-lua,
+%D version=2018.07.26,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=\LUA,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{back-lua}{autosuffix,optimize}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-mps.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-mps.lmt
new file mode 100644
index 00000000000..4a2a573c459
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-mps.lmt
@@ -0,0 +1,210 @@
+if not modules then modules = { } end modules ['back-mps'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local fontproperties = fonts.hashes.properties
+local fontparameters = fonts.hashes.parameters
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local bpfactor = number.dimenfactors.bp
+local texgetbox = tex.getbox
+local formatters = string.formatters
+
+local rulecodes = nodes.rulecodes
+local normalrule_code = rulecodes.normal
+----- boxrule_code = rulecodes.box
+----- imagerule_code = rulecodes.image
+----- emptyrule_code = rulecodes.empty
+----- userrule_code = rulecodes.user
+----- overrule_code = rulecodes.over
+----- underrule_code = rulecodes.under
+----- fractionrule_code = rulecodes.fraction
+----- radicalrule_code = rulecodes.radical
+local outlinerule_code = rulecodes.outline
+
+local fonts = { }
+local pages = { }
+local buffer = { }
+local b = 0
+local converter = nil
+
+local function reset()
+ buffer = { }
+ b = 0
+end
+
+local f_font = formatters[ "\\definefont[%s][file:%s*none @ %sbp]\n" ]
+
+local f_glyph = formatters[ [[draw textext.drt("\%s\char%i\relax") shifted (%N,%N);]] ]
+local f_rule = formatters[ [[fill unitsquare xscaled %N yscaled %N shifted (%N,%N);]] ]
+local f_outline = formatters[ [[draw unitsquare xscaled %N yscaled %N shifted (%N,%N);]] ]
+
+-- actions
+
+local function outputfilename(driver)
+ return tex.jobname .. "-output.tex"
+end
+
+local function save() -- might become a driver function that already is plugged into stopactions
+ starttiming(drivers)
+ if #pages > 0 then
+ local filename = outputfilename()
+ drivers.report("saving result in %a",filename)
+ reset()
+ b = b + 1
+ buffer[b] = "\\starttext\n"
+ for k, v in table.sortedhash(fonts) do
+ b = b + 1
+ buffer[b] = f_font(v.name,v.filename,v.size)
+ end
+ for i=1,#pages do
+ b = b + 1
+ buffer[b] = pages[i]
+ end
+ b = b + 1
+ buffer[b] = "\\stoptext\n"
+ io.savedata(filename,table.concat(buffer,"",1,b))
+ end
+ stoptiming(drivers)
+end
+
+local function prepare(driver)
+ converter = drivers.converters.lmtx
+ luatex.registerstopactions(1,function()
+ save()
+ end)
+end
+
+local function initialize(driver,details)
+ reset()
+ b = b + 1
+ buffer[b] = "\n\\startMPpage"
+end
+
+local function finalize(driver,details)
+ b = b + 1
+ buffer[b] = "\\stopMPpage\n"
+ pages[details.pagenumber] = table.concat(buffer,"\n",1,b)
+end
+
+local function wrapup(driver)
+end
+
+local function cleanup(driver)
+ reset()
+end
+
+local function convert(driver,boxnumber,pagenumber)
+ converter(driver,texgetbox(boxnumber),"page",pagenumber)
+end
+
+-- flushers
+
+local last
+
+local function updatefontstate(id)
+ if fonts[id] then
+ last = fonts[id].name
+ else
+ last = "MPSfont" .. converters.Characters(id)
+ fonts[id] = {
+ filename = file.basename(fontproperties[id].filename),
+ size = fontparameters[id].size * bpfactor,
+ name = last,
+ }
+ end
+end
+
+local function flushcharacter(current, pos_h, pos_v, pod_r, font, char)
+ b = b + 1
+ buffer[b] = f_glyph(last,char,pos_h*bpfactor,pos_v*bpfactor)
+end
+
+local function flushrule(current, pos_h, pos_v, pod_r, size_h, size_v, subtype)
+ if subtype == normalrule_code then
+ b = b + 1
+ buffer[b] = f_rule(size_h*bpfactor,size_v*bpfactor,pos_h*bpfactor,pos_v*bpfactor)
+ elseif subtype == outlinerule_code then
+ b = b + 1
+ buffer[b] = f_outline(size_h*bpfactor,size_v*bpfactor,pos_h*bpfactor,pos_v*bpfactor)
+ end
+end
+
+local function flushsimplerule(pos_h, pos_v, pod_r, size_h, size_v)
+ flush_rule(false,pos_h,pos_v,pod_r,size_h,size_v,normalrule_code)
+end
+
+local function flushspecialrule(pos_h, pos_v, pod_r, w, h, d, l, outline)
+ flush_rule(false,pos_h,pos_v-d,pod_r,w,h+d,outline and outlinerule_code or normalrule_code)
+end
+
+-- installer
+
+drivers.install {
+ name = "mps",
+ actions = {
+ prepare = prepare,
+ initialize = initialize,
+ finalize = finalize,
+ wrapup = wrapup,
+ cleanup = cleanup,
+ convert = convert,
+ outputfilename = outputfilename,
+ },
+ flushers = {
+ updatefontstate = updatefontstate,
+ character = flushcharacter,
+ rule = flushrule,
+ simplerule = flushsimplerule,
+ specialrule = flushspecialrule,
+ }
+}
+
+-- extras
+
+-- if not mp then
+-- return
+-- end
+--
+-- local mpprint = mp.print
+-- local formatters = string.formatters
+--
+-- local f_glyph = formatters[ [[draw textext.drt("\setfontid%i\relax\char%i\relax") shifted (%N,%N);]] ]
+-- local f_rule = formatters[ [[fill unitsquare xscaled %N yscaled %N shifted (%N,%N);]] ]
+--
+-- local current = nil
+-- local size = 0
+--
+-- function mp.place_buffermake(box)
+-- drivers.convert("mps",box)
+-- current = drivers.action("mps","fetch")
+-- size = #current
+-- end
+--
+-- function mp.place_buffersize()
+-- mpprint(size)
+-- end
+--
+-- function mp.place_bufferslot(i)
+-- if i > 0 and i <= size then
+-- local b = buffer[i]
+-- local t = b[1]
+-- if t == "glyph" then
+-- mpprint(f_glyph(b[2],b[3],b[4]*bpfactor,b[5]*bpfactor))
+-- elseif t == "rule" then
+-- mpprint(f_rule(b[2]*bpfactor,b[3]*bpfactor,b[4]*bpfactor,b[5]*bpfactor))
+-- end
+-- end
+-- end
+--
+-- function mp.place_bufferwipe()
+-- current = nil
+-- size = 0
+-- end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-mps.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-mps.mkxl
new file mode 100644
index 00000000000..d4d428d4203
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-mps.mkxl
@@ -0,0 +1,16 @@
+%D \module
+%D [ file=back-mps,
+%D version=2018.07.26,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=\METAPOST,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{back-mps}{autosuffix,optimize}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-pdf.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-pdf.lmt
new file mode 100644
index 00000000000..44d0230bdd6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-pdf.lmt
@@ -0,0 +1,55 @@
+if not modules then modules = { } end modules ['back-pdf'] = {
+ version = 1.001,
+ comment = "companion to back-pdf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We hide the pdf table from users so that we can guarantee no interference with
+-- the way we manage resources, info, etc. Users should use the \type {lpdf}
+-- interface instead. If needed I will provide replacement functionality.
+
+local setmetatableindex = table.setmetatableindex
+
+local pdfsetcompression
+local pdfimmediateobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfsetcompression = lpdf.setcompression
+ pdfimmediateobject = lpdf.immediateobject
+end)
+
+interfaces.implement {
+ name = "setpdfcompression",
+ arguments = { "integer", "integer" },
+ actions = function(...) pdfsetcompression(...) end,
+}
+
+do
+
+ local dummy = function() end
+ local report = logs.reporter("backend")
+
+ local function unavailable(t,k)
+ report("calling unavailable pdf.%s function",k)
+ t[k] = dummy
+ return dummy
+ end
+
+ updaters.register("backend.update",function()
+ --
+ -- For now we keep this for tikz. If really needed some more can be made
+ -- accessible but it has to happen in a controlled way then, for instance
+ -- by first loading or enabling some compatibility layer so that we can
+ -- trace possible interferences.
+ --
+ pdf = {
+ immediateobj = pdfimmediateobject
+ }
+ setmetatableindex(pdf,unavailable)
+ end)
+
+end
+
+backends.install("pdf")
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-pdf.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-pdf.mkxl
new file mode 100644
index 00000000000..d59c29bc01c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-pdf.mkxl
@@ -0,0 +1,157 @@
+%D \module
+%D [ file=back-pdf,
+%D version=2009.04.15,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=\PDF,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The less there is here, the better. After a decade it is time to remove the \type
+%D {\pdf*} ones completely. For the moment I keep them commented but even that will
+%D go away.
+
+\writestatus{loading}{ConTeXt Backend Macros / PDF}
+
+\registerctxluafile{lpdf-ini}{autosuffix,optimize}
+\registerctxluafile{lpdf-lmt}{autosuffix,optimize}
+\registerctxluafile{lpdf-col}{autosuffix}
+\registerctxluafile{lpdf-xmp}{autosuffix}
+\registerctxluafile{lpdf-ano}{autosuffix}
+\registerctxluafile{lpdf-res}{autosuffix}
+\registerctxluafile{lpdf-mis}{autosuffix}
+\registerctxluafile{lpdf-ren}{autosuffix}
+\registerctxluafile{lpdf-grp}{autosuffix}
+\registerctxluafile{lpdf-wid}{autosuffix}
+\registerctxluafile{lpdf-fld}{autosuffix}
+\registerctxluafile{lpdf-mov}{autosuffix}
+\registerctxluafile{lpdf-u3d}{autosuffix} % this will become a module
+%registerctxluafile{lpdf-swf}{} % this will become a module
+\registerctxluafile{lpdf-tag}{autosuffix}
+\registerctxluafile{lpdf-fmt}{autosuffix}
+\registerctxluafile{lpdf-pde}{autosuffix}
+\registerctxluafile{lpdf-img}{autosuffix,optimize}
+\registerctxluafile{lpdf-epa}{autosuffix}
+\registerctxluafile{lpdf-emb}{autosuffix,optimize}
+\registerctxluafile{lpdf-fnt}{autosuffix}
+\registerctxluafile{lpdf-rul}{autosuffix}
+
+\registerctxluafile{back-pdp}{autosuffix}
+\registerctxluafile{back-pdf}{autosuffix} % some code will move to lpdf-*
+
+\loadmkxlfile{back-u3d} % this will become a module
+%loadmkxlfile{back-swf} % this will become a module
+
+\unprotect
+
+%D We will minimize the number of calls to \PDF\ specific primitives and delegate
+%D all management and injection of code to the backend.
+%D
+%D Because we do a lot in \LUA\ and don't want interferences, we nil most of the
+%D \PDFTEX\ primitives. Of course one can always use the \type {\pdfvariable},
+%D \type {\pdfextension} and \type {\pdffeedback} primitives but it will probably
+%D have bad side effects.
+
+\permanent\protected\def\pdfextension{\clf_pdfextension}
+\permanent \def\pdffeedback {\clf_pdffeedback}
+\permanent \def\pdfvariable {\clf_pdfvariable}
+
+%D For the moment we keep this for tikz but hopefully it will at some point use
+%D the proper ones. Consider them obsolete:
+
+\permanent\protected\def\pdfliteral {\clf_pdfliteral}
+\permanent\protected\def\pdfobj {\clf_pdfobj}%
+\permanent\protected\def\pdflastobj {\numexpr\clf_pdflastobj\relax}
+\permanent\protected\def\pdfrefobj {\clf_pdfrefobj}
+
+\permanent\protected\def\pdfrestore {\pdfextension restore}
+\permanent\protected\def\pdfsave {\pdfextension save}
+\permanent\protected\def\pdfsetmatrix{\pdfextension setmatrix}
+
+\permanent\let\pdfxform \saveboxresource
+\permanent\let\pdflastxform\lastsavedboxresourceindex
+\permanent\let\pdfrefxform \useboxresource
+
+%D Here are quick and dirty compression flippers, mostly used when testing something
+%D as one can best stick to the defaults that also adapt to specific standards.
+
+\permanent\protected\def\nopdfcompression {\clf_setpdfcompression\zerocount\zerocount}
+\permanent\protected\def\onlypdfobjectcompression{\clf_setpdfcompression\zerocount\plusthree}
+\permanent\protected\def\maximumpdfcompression {\clf_setpdfcompression\plusnine \plusnine }
+\permanent\protected\def\normalpdfcompression {\clf_setpdfcompression\plusthree\plusthree}
+
+%D PDF/X (maybe combine the two lua calls)
+
+\setupbackend
+ [xmpfile=]
+
+\appendtoks
+ \edef\p_file{\backendparameter{xmpfile}}%
+ \ifempty\p_file\else
+ \clf_setxmpfile{\p_file}%
+ \fi
+\to \everysetupbackend
+
+%D This will change:
+
+\appendtoks
+ \clf_setformat
+ format {\backendparameter\c!format}%
+ level {\backendparameter\c!level}%
+ option {\backendparameter\c!option}%
+ profile {\backendparameter\c!profile}%
+ intent {\backendparameter\c!intent}%
+ file {\backendparameter\c!file}%
+ \relax
+\to \everysetupbackend
+
+%D These are the only official methods to add stuff to the resources. If more is
+%D needed for third party low level code, it can be added.
+
+\permanent\protected\def\pdfbackendsetcatalog #1#2{\clf_lpdf_addtocatalog{#1}{#2}}
+\permanent\protected\def\pdfbackendsetinfo #1#2{\clf_lpdf_addtoinfo{#1}{#2}}
+\permanent\protected\def\pdfbackendsetname #1#2{\clf_lpdf_addtonames{#1}{#2}}
+
+\permanent\protected\def\pdfbackendsetpageattribute #1#2{\clf_lpdf_addtopageattributes{#1}{#2}}
+\permanent\protected\def\pdfbackendsetpagesattribute#1#2{\clf_lpdf_addtopagesattributes{#1}{#2}}
+\permanent\protected\def\pdfbackendsetpageresource #1#2{\clf_lpdf_addtopageresources{#1}{#2}}
+
+\permanent\protected\def\pdfbackendsetextgstate #1#2{\clf_lpdf_adddocumentextgstate{#1}{#2}}
+\permanent\protected\def\pdfbackendsetcolorspace #1#2{\clf_lpdf_adddocumentcolorspace{#1}{#2}}
+\permanent\protected\def\pdfbackendsetpattern #1#2{\clf_lpdf_adddocumentpattern{#1}{#2}}
+\permanent\protected\def\pdfbackendsetshade #1#2{\clf_lpdf_adddocumentshade{#1}{#2}}
+
+\permanent \def\pdfbackendcurrentresources {\clf_lpdf_collectedresources}
+\permanent \def\pdfcolor #1{\clf_lpdf_color\numexpr\rawcolorattribute{#1}\relax}
+
+%D This is a temporary hack mthat will be removed, improved or somehow can become
+%D default.
+
+\permanent\def\TransparencyHack{\setupcolors[\c!pagecolormodel=\v!auto]}
+
+%D Just in case one needs this \unknown:
+%D
+%D \starttyping
+%D text \pdfbackendactualtext{Meier}{Müller} text
+%D \stoptyping
+
+\permanent\protected\def\pdfbackendactualtext#1#2% not interfaced
+ {\clf_startactualtext{#2}%
+ #1%
+ \clf_stopactualtext}
+
+\aliased\let\pdfactualtext\pdfbackendactualtext
+
+%D Bah, this is also needed for tikz:
+
+\ifdefined\pdfsavepos\else
+ \permanent\let\pdfsavepos \savepos
+ \permanent\let\pdflastxpos\lastxpos
+ \permanent\let\pdflastypos\lastypos
+\fi
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-pdp.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-pdp.lmt
new file mode 100644
index 00000000000..1b3a1700790
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-pdp.lmt
@@ -0,0 +1,291 @@
+if not modules then modules = { } end modules ['back-pdp'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is temporary ... awaiting a better test .. basically we can
+-- always use this: pdf primitives.
+
+local context = context
+local lpdf = lpdf
+
+local pdfreserveobject
+local pdfcompresslevel
+local pdfobject
+local pdfpagereference
+local pdfgetxformname
+local pdfminorversion
+local pdfmajorversion
+
+updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+ pdfcompresslevel = lpdf.compresslevel
+ pdfobject = lpdf.object
+ pdfpagereference = lpdf.pagereference
+ pdfgetxformname = lpdf.getxformname
+ pdfminorversion = lpdf.minorversion
+ pdfmajorversion = lpdf.majorversion
+end)
+
+local tokenscanners = tokens.scanners
+local scanword = tokenscanners.word
+local scankeyword = tokenscanners.keyword
+local scanstring = tokenscanners.string
+local scaninteger = tokenscanners.integer
+local scanwhd = tokenscanners.whd
+
+local trace = false trackers.register("backend", function(v) trace = v end)
+local report = logs.reporter("backend")
+
+local nodepool = nodes.pool
+local newliteral = nodepool.literal
+local newsave = nodepool.save
+local newrestore = nodepool.restore
+local newsetmatrix = nodepool.setmatrix
+
+local implement = interfaces.implement
+local constants = interfaces.constants
+local variables = interfaces.variables
+
+-- literals
+
+local function pdf_literal()
+ context(newliteral(scanword() or "origin",scanstring()))
+end
+
+-- objects
+
+local lastobjnum = 0
+
+local function pdf_obj()
+ if scankeyword("reserveobjnum") then
+ lastobjnum = pdfreserveobject()
+ if trace then
+ report("\\pdfobj reserveobjnum: object %i",lastobjnum)
+ end
+ else
+ local immediate = true
+ local objnum = scankeyword("useobjnum") and scaninteger() or pdfreserveobject()
+ local uncompress = scankeyword("uncompressed") or pdfcompresslevel() == 0
+ local streamobject = scankeyword("stream")
+ local attributes = scankeyword("attr") and scanstring() or nil
+ local fileobject = scankeyword("file")
+ local content = scanstring()
+ local object = streamobject and {
+ type = "stream",
+ objnum = objnum,
+ immediate = immediate,
+ attr = attributes,
+ compresslevel = uncompress and 0 or nil,
+ } or {
+ type = "raw",
+ objnum = objnum,
+ immediate = immediate,
+ }
+ if fileobject then
+ object.file = content
+ -- object.filename = content
+ else
+ object.string = content
+ end
+ pdfobject(object)
+ lastobjnum = objnum
+ if trace then
+ report("\\pdfobj: object %i",lastobjnum)
+ end
+ end
+end
+
+local function pdf_lastobj()
+ context("%i",lastobjnum)
+ if trace then
+ report("\\lastobj: object %i",lastobjnum)
+ end
+end
+
+local function pdf_refobj()
+ local objnum = scaninteger()
+ if trace then
+ report("\\refobj: object %i (todo)",objnum)
+ end
+end
+
+-- annotations
+
+local lastobjnum = 0
+
+local function pdf_annot()
+ if scankeyword("reserveobjnum") then
+ lastobjnum = pdfreserveobject()
+ if trace then
+ report("\\pdfannot reserveobjnum: object %i",lastobjnum)
+ end
+ else
+ local width = false
+ local height = false
+ local depth = false
+ local data = false
+ local object = false
+ local attr = false
+ --
+ if scankeyword("useobjnum") then
+ object = scancount()
+ report("\\pdfannot useobjectnum is not (yet) supported")
+ end
+ local width, height, depth = scanwhd()
+ if scankeyword("attr") then
+ attr = scanstring()
+ end
+ data = scanstring()
+ context(backends.nodeinjections.annotation(width or 0,height or 0,depth or 0,data or ""))
+ end
+end
+
+local function pdf_dest()
+ local name = false
+ local zoom = false
+ local view = false
+ local width = false
+ local height = false
+ local depth = false
+ if scankeyword("num") then
+ report("\\pdfdest num is not (yet) supported")
+ elseif scankeyword("name") then
+ name = scanstring()
+ end
+ if scankeyword("xyz") then
+ view = "xyz"
+ if scankeyword("zoom") then
+ report("\\pdfdest zoom is ignored")
+ zoom = scancount() -- will be divided by 1000 in the backend
+ end
+ elseif scankeyword("fitbh") then
+ view = "fitbh"
+ elseif scankeyword("fitbv") then
+ view = "fitbv"
+ elseif scankeyword("fitb") then
+ view = "fitb"
+ elseif scankeyword("fith") then
+ view = "fith"
+ elseif scankeyword("fitv") then
+ view = "fitv"
+ elseif scankeyword("fitr") then
+ view = "fitr"
+ width, height, depth = scanwhd()
+ elseif scankeyword("fit") then
+ view = "fit"
+ end
+ context(backends.nodeinjections.destination(width or 0,height or 0,depth or 0,{ name or "" },view or "fit"))
+end
+
+-- management
+
+local function pdf_save()
+ context(newsave())
+end
+
+local function pdf_restore()
+ context(newrestore())
+end
+
+local function pdf_setmatrix()
+ context(newsetmatrix(scanstring()))
+end
+
+-- extras
+
+-- extensions: literal dest annot save restore setmatrix obj refobj colorstack
+-- startlink endlink startthread endthread thread outline glyphtounicode fontattr
+-- mapfile mapline includechars catalog info names trailer
+
+local extensions = {
+ literal = pdf_literal,
+ obj = pdf_obj,
+ refobj = pdf_refobj,
+ dest = pdf_dest,
+ annot = pdf_annot,
+ save = pdf_save,
+ restore = pdf_restore,
+ setmatrix = pdf_setmatrix,
+}
+
+local function pdf_extension()
+ local w = scanword()
+ if w then
+ local e = extensions[w]
+ if e then
+ e()
+ else
+ report("\\pdfextension: unsupported %a",w)
+ end
+ end
+end
+
+-- feedbacks: colorstackinit creationdate fontname fontobjnum fontsize lastannot
+-- lastlink lastobj pageref retval revision version xformname
+
+local feedbacks = {
+ lastobj = pdf_lastobj,
+ pageref = function() context(pdfpagereference()) end,
+ xformname = function() context(pdfgetxformname ()) end,
+}
+
+local function pdf_feedback()
+ local w = scanword()
+ if w then
+ local f = feedbacks[w]
+ if f then
+ f()
+ else
+ report("\\pdffeedback: unsupported %a",w)
+ end
+ end
+end
+
+-- variables: (integers:) compresslevel decimaldigits gamma gentounicode
+-- ignoreunknownimages imageaddfilename imageapplygamma imagegamma imagehicolor
+-- imageresolution inclusioncopyfonts inclusionerrorlevel majorversion minorversion
+-- objcompresslevel omitcharset omitcidset pagebox pkfixeddpi pkresolution
+-- recompress suppressoptionalinfo uniqueresname (dimensions:) destmargin horigin
+-- linkmargin threadmargin vorigin xformmargin (tokenlists:) pageattr pageresources
+-- pagesattr pkmode trailerid xformattr xformresources
+
+local variables = {
+ minorversion = function() context(pdfminorversion()) end,
+ majorversion = function() context(pdfmajorversion()) end,
+}
+
+local function pdf_variable()
+ local w = scanword()
+ if w then
+ local f = variables[w]
+ if f then
+ f()
+ else
+ report("\\pdfvariable: unsupported %a",w)
+ end
+ else
+ print("missing variable")
+ end
+end
+
+-- kept:
+
+implement { name = "pdfextension", actions = pdf_extension }
+implement { name = "pdffeedback", actions = pdf_feedback }
+implement { name = "pdfvariable", actions = pdf_variable }
+
+-- for the moment (tikz)
+
+implement { name = "pdfliteral", actions = pdf_literal }
+implement { name = "pdfobj", actions = pdf_obj }
+implement { name = "pdflastobj", actions = pdf_lastobj }
+implement { name = "pdfrefobj", actions = pdf_refobj }
+--------- { name = "pdfannot", actions = pdf_annot }
+--------- { name = "pdfdest", actions = pdf_dest }
+--------- { name = "pdfsave", actions = pdf_save }
+--------- { name = "pdfrestore", actions = pdf_restore }
+--------- { name = "pdfsetmatrix", actions = pdf_setmatrix }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-res.lmt b/Master/texmf-dist/tex/context/base/mkxl/back-res.lmt
new file mode 100644
index 00000000000..3157b37ef61
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-res.lmt
@@ -0,0 +1,177 @@
+if not modules then modules = { } end modules ['back-res'] = {
+ version = 1.001,
+ comment = "companion to back-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local context = context
+
+local trace = false trackers.register("backend", function(v) trace = v end)
+local report = logs.reporter("backend")
+
+local scanners = tokens.scanners
+local scankeyword = scanners.keyword
+local scaninteger = scanners.integer
+local scanstring = scanners.string
+local scandimension = scanners.dimension
+local scanword = scanners.word
+local scanwhd = scanners.whd
+
+local implement = interfaces.implement
+local constants = interfaces.constants
+local variables = interfaces.variables
+
+-- A box resource has an index. This happens to be an object number due to the pdf
+-- backend but in fact it's an abstraction. This is why we have explicit fetchers.
+-- The internal number (as in \Fm123) is yet another number.
+
+local tex_saveboxresource = tex.saveboxresource
+local tex_useboxresource = tex.useboxresource
+local tex_getboxresourcebox = tex.getboxresourcebox
+local tex_getboxresourcedimensions = tex.getboxresourcedimensions
+
+updaters.register("backend.update",function()
+ tex_saveboxresource = tex.saveboxresource
+ tex_useboxresource = tex.useboxresource
+ tex_getboxresourcebox = tex.getboxresourcebox
+ tex_getboxresourcedimensions = tex.getboxresourcedimensions
+end)
+
+local savebox = function(...) return tex_saveboxresource(...) end
+local usebox = function(...) return tex_useboxresource(...) end
+local getbox = function(...) return tex_getboxresourcebox(...) end
+local getwhd = function(...) return tex_getboxresourcedimensions(...) end
+
+local boxresources = {
+ save = savebox,
+ use = usebox,
+ getbox = getbox,
+ getdimensions = getwhd,
+}
+
+tex.boxresources = boxresources
+
+local lastindex = 0
+
+local function saveboxresource()
+ local immediate = true
+ local kind = scankeyword("type") and scaninteger() or 0
+ local attributes = scankeyword("attr") and scanstring() or nil
+ local resources = scankeyword("resources") and scanstring() or nil
+ local margin = scankeyword("margin") and scandimension() or 0 -- register
+ local boxnumber = scaninteger()
+ --
+ lastindex = savebox(boxnumber,attributes,resources,immediate,kind,margin)
+ if trace then
+ report("\\saveboxresource: index %i",lastindex)
+ end
+end
+
+local function lastsavedboxresourceindex()
+ if trace then
+ report("\\lastsaveboxresource: index %i",lastindex)
+ end
+ context("%i",lastindex)
+end
+
+local function useboxresource()
+ local width, height, depth = scanwhd()
+ local index = scaninteger()
+ local node = usebox(index,width,height,depth)
+ if trace then
+ report("\\useboxresource: index %i",index)
+ end
+ context(node)
+end
+
+implement { name = "saveboxresource", actions = saveboxresource }
+implement { name = "lastsavedboxresourceindex", actions = lastsavedboxresourceindex }
+implement { name = "useboxresource", actions = useboxresource }
+
+-- image resources
+
+local imageresources = { }
+local lastindex = 0
+local lastpages = 1
+
+local function saveimageresource()
+ local width, height, depth = scanwhd()
+ local page = 1
+ local immediate = true
+ local margin = 0 -- or dimension
+ local attributes = scankeyword("attr") and scanstring() or nil
+ if scankeyword("named") then
+ scanstring() -- ignored
+ elseif scankeyword("page") then
+ page = scaninteger()
+ end
+ local userpassword = scankeyword("userpassword") and scanstring() or nil
+ local ownerpassword = scankeyword("ownerpassword") and scanstring() or nil
+ local visiblefilename = scankeyword("visiblefilename") and scanstring() or nil
+ local colorspace = scankeyword("colorspace") and scaninteger() or nil
+ local pagebox = scanword() or nil
+ local filename = scanstring()
+-- pcall
+ context.getfiguredimensions( { filename }, {
+ [constants.userpassword] = userpassword,
+ [constants.ownerpassword] = ownerpassword,
+ [constants.page] = page or 1,
+ [constants.size] = pagebox,
+ })
+ context.relax()
+ lastindex = lastindex + 1
+ lastpages = 1
+ imageresources[lastindex] = {
+ filename = filename,
+ page = page or 1,
+ size = pagebox,
+ width = width,
+ height = height,
+ depth = depth,
+ attr = attributes,
+ -- margin = margin,
+ }
+end
+
+local function lastsavedimageresourceindex()
+ context("%i",lastindex or 0)
+end
+
+local function lastsavedimageresourcepages()
+ context("%i",lastpages or 0) -- todo
+end
+
+local function useimageresource()
+ local width, height, depth = scanwhd()
+ if scankeyword("keepopen") then
+ -- ignored
+ end
+ local index = scaninteger()
+ local l = imageresources[index]
+ if l then
+ if not (width or height or depth) then
+ width = l.width
+ height = l.height
+ depth = l.depth
+ end
+ -- pcall / we could use a dedicated call instead:
+ context.externalfigure( { l.filename }, {
+ [constants.userpassword] = l.userpassword,
+ [constants.ownerpassword] = l.ownerpassword,
+ [constants.width] = width and (width .. "sp") or nil,
+ [constants.height] = height and (height .. "sp") or nil,
+ [constants.page] = l.page or 1,
+ [constants.size] = pagebox,
+ })
+ context.relax()
+ else
+ report("no valid image resource %a",index)
+ end
+end
+
+implement { name = "saveimageresource", actions = saveimageresource }
+implement { name = "lastsavedimageresourceindex", actions = lastsavedimageresourceindex }
+implement { name = "lastsavedimageresourcepages", actions = lastsavedimageresourcepages }
+implement { name = "useimageresource", actions = useimageresource }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-res.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-res.mkxl
new file mode 100644
index 00000000000..8a18eef991f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-res.mkxl
@@ -0,0 +1,34 @@
+%D \module
+%D [ file=back-res,
+%D version=2019.05.23, % 2009.04.15,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=Resources,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Backend Macros / Resources}
+
+\registerctxluafile{back-res}{autosuffix}
+
+\unprotect
+
+%D We overload the primitives with our own but use a bit of indirection for the
+%D purpose of tracing. Some of these are needed for packages like tikz.
+
+% todo: public implementors (we can update the lua code anyway)
+
+\permanent\protected\def\saveboxresource {\clf_saveboxresource}
+\permanent\protected\def\lastsavedboxresourceindex {\numexpr\clf_lastsavedboxresourceindex\relax}
+\permanent\protected\def\useboxresource {\clf_useboxresource}
+
+\permanent\protected\def\saveimageresource {\clf_saveimageresource}
+\permanent\protected\def\lastsavedimageresourceindex{\numexpr\clf_lastsavedimageresourceindex\relax}
+\permanent\protected\def\lastsavedimageresourcepages{\numexpr\clf_lastsavedimageresourcepages\relax}
+\permanent\protected\def\useimageresource {\clf_useimageresource}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/back-u3d.mkxl b/Master/texmf-dist/tex/context/base/mkxl/back-u3d.mkxl
new file mode 100644
index 00000000000..297dd554579
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/back-u3d.mkxl
@@ -0,0 +1,158 @@
+%D \module
+%D [ file=back-u3d,
+%D version=2009.04.15,
+%D title=\CONTEXT\ Backend Macros,
+%D subtitle=U3D Experiment,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is only a placeholder that demonstrates the usage of u3d resources. The
+%D user interface is rather messy an might be improved. The files and setup is
+%D derived from an example by Michael Vidiassov.
+
+\endinput
+
+\starttext
+
+\startluaparameterset [u3d:myset:controls:1]
+ view = {
+ name = 'default',
+ bg = {1,1,1},
+ mag = 100,
+ coo = {0,0,0},
+ c2c = {0,0,1},
+ rot = {40,0,60},
+ roo = 6,
+ lights = 'CAD'
+ },
+ js = 'cloudq.js'
+\stopluaparameterset
+
+\startluaparameterset [u3d:myset:controls:2]
+ views = {
+ {
+ name = 'AnglePositioning',
+ bg = {1,1,1},
+ azimuth = 45,
+ altitude = 45,
+ roo = 50,
+ aac = 2.5,
+ lights = 'Artwork'
+ },
+ {
+ name = 'RotationPositioning',
+ bg = {1,1,1},
+ rot = {0,45,45},
+ roo = 50,
+ aac = 2.5,
+ lights = 'Artwork'
+ },
+ {
+ name = 'VectorPositioning',
+ bg = {1,0,0},
+ c2c = {1,1,math.sqrt(2)},
+ roo = 50,
+ aac = 2.5,
+ lights = 'CAD'
+ },
+ {
+ name = 'PositionPositioning',
+ bg = {1,0,0},
+ pos = {1+25,1+25,1+50/math.sqrt(2)},
+ aac = 2.5,
+ lights = 'CAD'
+ },
+ {
+ name = 'ortho',
+ bg = {1,1,1},
+ mag = 300,
+ lights = 'CAD',
+ crossection = {}
+ }
+ },
+ view = {
+ name = 'default',
+ bg = {1,1,1},
+ c2c = {-1,-1,0},
+ roo = 50,
+ aac = 2.5,
+ roll = 45,
+ lights = 'CAD',
+ crossection = {
+ normal = {-1,-1,-1},
+ transparent = true
+ },
+ nodes = {
+ {
+ name = 'xlabel',
+ visible = false
+ },
+ {
+ name = 'ylabel',
+ opacity = 0.5
+ },
+ {
+ name = 'zlabel',
+ rendermode = 'Wireframe'
+ }
+ }
+ }
+\stopluaparameterset
+
+\useexternalfigure
+ [cloudq]
+ [cloudq.u3d]
+ [width=0.7\textwidth,
+ height=.7\textwidth,
+ display=u3d:myset:display:1,
+ controls=u3d:myset:controls:1]
+
+\useexternalfigure
+ [axes]
+ [axes.u3d]
+ [width=0.7\textwidth,
+ height=.7\textwidth,
+ controls=u3d:myset:controls:1]
+
+\startluaparameterset[u3d:myset:display:2]
+ toolbar = true,
+ preview = 'cloudq.png'
+\stopluaparameterset
+
+\startluaparameterset[u3d:myset:display:3]
+ toolbar = true,
+ tree = false,
+ preview = 'axes.png'
+\stopluaparameterset
+
+\startluaparameterset[u3d:myset:display:4]
+ toolbar = true,
+ tree = false,
+ view = {
+ name = 'view',
+ bg = {0.1,0.1,0.1},
+ c2c = {-1,-1,0},
+ roo = 50,
+ aac = 2.5,
+ roll = 45,
+ lights = 'Red'
+ }
+\stopluaparameterset
+
+\startluaparameterset[u3d:myset:display:5]
+ toolbar = true,
+ tree = false,
+ view = 'ortho'
+\stopluaparameterset
+
+\placefigure[here]{none}{\externalfigure[cloudq][display=u3d:myset:display:2]}
+\placefigure[here]{none}{\externalfigure[axes] [display=u3d:myset:display:3]}
+\placefigure[here]{none}{\externalfigure[axes] [display=u3d:myset:display:4]}
+\placefigure[here]{none}{\externalfigure[axes] [display=u3d:myset:display:5,width=0.5\textwidth,height=.5\textwidth]}
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/bibl-bib.mkxl b/Master/texmf-dist/tex/context/base/mkxl/bibl-bib.mkxl
new file mode 100644
index 00000000000..20aea7bb612
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/bibl-bib.mkxl
@@ -0,0 +1,983 @@
+%D \module
+%D [ file=bibl-bib,
+%D version=2007.08.17,
+%D title=\CONTEXT\ Bibliography Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen \& Taco Hoekwater,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is really obsolete in \LMTX. For now I keep it but it will either go
+%D away. The code is not upgraded, we only deal with macro protection. I will
+%D only update the bits that annoy me in grepping for possible optimizations.
+
+\writestatus{loading}{ConTeXt Bibliography Support / Experimental BibTeX}
+
+\registerctxluafile{bibl-bib}{}
+
+\unprotect
+
+% todo: et al limiters
+% todo: split: citationvariant and publicationvariant
+
+%D This interface is under development. As I don't use \BIBTEX\ myself I need some
+%D motivation to spend time on it, and an occasional question on the list can be a
+%D reason. A few examples. As \BIBTEX\ databases can be poluted by local commands,
+%D we need to catch:
+%D
+%D \startbuffer
+%D \defbibtexcommand\MF {MF}
+%D \defbibtexcommand\MP {MP}
+%D \defbibtexcommand\TUB {TUGboat}
+%D \defbibtexcommand\Mc {Mac}
+%D \defbibtexcommand\sltt{\tt}
+%D \defbibtexcommand\<#1>{\type{#1}}
+%D \defbibtexcommand\acro#1{#1}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Let's define a session and load a few databases. We convert to \UTF\ and strip
+%D commands.
+%D
+%D \startbuffer
+%D \definebibtexsession [somebibtex]
+%D \registerbibtexfile [somebibtex] [tugboat.bib]
+%D \registerbibtexfile [somebibtex] [komoedie.bib]
+%D \preparebibtexsession [somebibtex] [convert,strip]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This loads an mapping (work in progress):
+%D
+%D \startbuffer
+%D \def\currentbibtexformat{apa} \input bxml-\currentbibtexformat.mkiv
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D There are several ways to handle the \XML. It helps if you're a bit familiar with
+%D \XML\ processing in \MKIV.
+%D
+%D Here we regular setups. Three elements are mapped but only one is actually used
+%D and applied to root element \type {/bibtex}.
+%D
+%D \startbuffer
+%D \startxmlsetups bibtex
+%D \xmlregistereddocumentsetups{#1}{}
+%D \xmlsetsetup{#1}{bibtex|entry|field}{bibtex:*}
+%D \xmlmain{#1}
+%D \stopxmlsetups
+%D
+%D \startxmlsetups bibtex:bibtex
+%D \xmlfilter{#1}{
+%D /entry[@category='article']
+%D /field[@name='author' and (find(text(),'Hagen') or find(text(),'Hoekwater'))]
+%D /../command(bibtex:format)
+%D }
+%D \stopxmlsetups
+%D
+%D \applytobibtexsession[somebibtex][bibtex]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D A simpler setup is given next. Here we just apply a setup to the root element
+%D directly:
+%D
+%D \startbuffer
+%D \startxmlsetups bibtex:list
+%D \xmlfilter{#1}{/bibtex/entry/command(bibtex:format)}
+%D \stopxmlsetups
+%D
+%D \applytobibtexsession[somebibtex][bibtex:list]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D A slightly more complex expression:
+%D
+%D \startbuffer
+%D \startxmlsetups bibtex:filter
+%D \xmlfilter{#1}{
+%D /bibtex
+%D /entry[@category='article']
+%D /field[@name='author' and (find(text(),'Hagen') or find(text(),'Hoekwater'))]
+%D /../command(bibtex:format)
+%D }
+%D \stopxmlsetups
+%D
+%D \applytobibtexsession[somebibtex][bibtex:filter]
+%D \stopbuffer
+%D
+%D \typebuffer
+
+\newtoks \everydefinebibtexsession
+\newtoks \everypreparebibtexsession
+\newtoks \everysetupbibtexsession
+\setfalse \tracebibtexformat
+
+\protected\def\definebibtexsession {\dosingleargument\dodefinebibtexsession}
+ \def\preparebibtexsession{\dodoubleempty \dopreparebibtexsession}
+\protected\def\setupbibtexsession {\dodoubleargument\dosetupbibtexsession}
+
+\def\dodefinebibtexsession [#1]{\edef\currentbibtexsession{#1}%
+ \ctxcommand{definebibtexsession("#1")}%
+ \the\everydefinebibtexsession}
+
+\def\dopreparebibtexsession[#1][#2]{\edef\currentbibtexsession{#1}%
+ \ctxcommand{preparebibtexsession("#1","bibtex:#1","#2")}%
+ \the\everypreparebibtexsession}
+
+\def\dosetupbibtexsession [#1][#2]{\edef\currentbibtexsession{#1}%
+ \getparameters[\??pb#1][#2]%
+ \the\everysetupbibtexsession}
+
+\def\registerbibtexfile {\dodoubleargument\doregisterbibtexfile}
+\def\registerbibtexentry {\dodoubleargument\doregisterbibtexentry}
+\def\applytobibtexsession {\dodoubleargument\doapplytobibtexsession}
+
+\def\doregisterbibtexfile [#1][#2]{\ctxcommand{registerbibtexfile("#1","#2")}}
+\def\doregisterbibtexentry [#1][#2]{\ctxcommand{registerbibtexentry("#1","#2")}}
+\def\doapplytobibtexsession[#1][#2]{\xmlprocess{bibtex:#1}{#2}{#2}}
+
+\protected\def\bibtexcommand#1%
+ {\ifcsname\??pb:c:#1\endcsname \else
+ \fakebibtexcommand{#1}%
+ \fi
+ \csname\??pb:c:#1\endcsname}
+
+\def\fakebibtexcommand#1%
+ {\ifcsname#1\endcsname
+ \writestatus{bibtex}{unknown command: #1, using built-in context variant}%
+ \setugvalue{\??pb:c:#1}{\dosomebibtexcommand{#1}}%
+ \else
+ \writestatus{bibtex}{unknown command: #1}%
+ \setugvalue{\??pb:c:#1}{\dofakebibtexcommand{#1}}%
+ \fi}
+
+\let\dosomebibtexcommand \getvalue
+\def\dofakebibtexcommand#1{{\tttf#1}}
+\def\defbibtexcommand #1{\setuvalue{\strippedcsname#1}}
+
+\def\bibxmldoifelse#1{\xmldoifelse\currentbibxmlnode{/field[@name='#1']}}
+\def\bibxmldoif #1{\xmldoif \currentbibxmlnode{/field[@name='#1']}}
+\def\bibxmldoifnot #1{\xmldoifnot \currentbibxmlnode{/field[@name='#1']}}
+\def\bibxmlflush #1{\xmlcontext \currentbibxmlnode{/field[@name='#1']}}
+\def\bibxmlsetup {\xmlsetup \currentbibxmlnode} % {#1}
+
+\def\currentbibtexformat{apa} % how to interface this, maybe split loading and key
+\def\currentbibxmlnode {unset}
+\def\currentbibxmltag {unset}
+
+\startxmlsetups bibtex
+ \xmlregistereddocumentsetups{#1}{}
+ \xmlsetsetup{#1}{bibtex|entry|field}{bibtex:*}
+ \xmlmain{#1}
+\stopxmlsetups
+
+\startxmlsetups bibtex:format
+ \bibtexpublicationsparameter\c!before\relax % prevents lookahead
+ \edef\currentbibxmlnode {#1}
+ \edef\currentbibxmltag {\xmlatt{#1}{tag}}
+ \edef\currentbibxmlcategory{\xmlatt{#1}{category}}
+ \ifconditional\tracebibtexformat
+ \tracedbibxmlintro\currentbibxmltag
+ \tracedbibxmlintro\currentbibxmlcategory
+ \fi
+ \ignorespaces
+ \xmlcommand{#1}{.}{bibtex:\currentbibtexformat:\currentbibxmlcategory}
+ \removeunwantedspaces
+ \bibtexpublicationsparameter\c!after\relax % prevents lookahead
+\stopxmlsetups
+
+\startxmlsetups bibtex:list
+ \xmlfilter{#1}{/bibtex/entry/command(bibtex:format)}
+\stopxmlsetups
+
+\startxmlsetups bibtex:bibtex
+ \xmlfilter{#1}{/entry/command(bibtex:format)}
+\stopxmlsetups
+
+% formatters
+
+\let\normalbibxmlflush\bibxmlflush
+
+\definecolor[bibtextracecolor:field] [darkred]
+\definecolor[bibtextracecolor:crossref][darkblue]
+\definecolor[bibtextracecolor:key] [darkgreen]
+
+\def\tracedbibxmlintro #1{{\tttf#1 -> }}
+\def\tracedbibxmlflush #1{\color[bibtextracecolor:field] {\tttf[#1]}}
+\def\tracedbibxmltexts #1{\color[bibtextracecolor:field] {\tttf<#1>}}
+\def\tracedbibxmlcrossref#1{\color[bibtextracecolor:crossref]{\tttf#1}}
+\def\tracedbibxmlkey #1{\color[bibtextracecolor:key] {\tttf#1}}
+
+\def\tracedbibxmltext
+ {\ifconditional\tracebibtexformat
+ \expandafter\tracedbibxmltexts % plural
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\def\bibxmlflush
+ {\ifconditional\tracebibtexformat
+ \expandafter\tracedbibxmlflush
+ \else
+ \expandafter\normalbibxmlflush
+ \fi}
+
+\startxmlsetups bibtex:format:crossref
+ \ifconditional\tracebibtexformat
+ \tracedbibxmlcrossref{\xmlfirst\currentbibxmlnode{/field[@name='crossref']/lower()}}
+ \else
+ \cite[\xmlfirst\currentbibxmlnode{/field[@name='crossref']/lower()}]
+ \fi
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:key
+ \ifconditional\tracebibtexformat
+ \tracedbibxmlkey{\normalbibxmlflush{key}}
+ \else
+ \bibxmlflush{key}
+ \fi
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:common:author
+ \ifconditional\tracebibtexformat
+ \bibxmlflush\currentbibtexvariant
+ \else
+ \xmlfilter{#1}{/field[@name='\currentbibtexvariant']/bibtexconcat('\currentbibtexvariant')}
+ \fi
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:author
+ \begingroup
+ \def\currentbibtexvariant{author}
+ \xmlsetup{#1}{bibtex:format:common:author}
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:artauthor
+ \begingroup
+ \def\currentbibtexvariant{artauthor}
+ \xmlsetup{#1}{bibtex:format:common:author}
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:editor
+ \begingroup
+ \def\currentbibtexvariant{editor}
+ \xmlsetup{#1}{bibtex:format:common:author}
+ \endgroup
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:doi
+% \bibdoifelse{\@@pb@doi}{#1\expanded{\bibgotoDOI{\@@pb@thekey}{\@@pb@doi}}#2}{#3}
+ *doi*
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:doi
+% \bibdoifelse{\@@pb@biburl}{#1\expanded{\bibgotoURL{\@@pb@thekey}{\@@pb@biburl}}#2}{#3}
+ *url*
+\stopxmlsetups
+
+\startxmlsetups bibtex:format:month
+% {\bibdoifelse\@@pb@month
+% {#1\doifnumberelse\@@pb@month
+% {\doifconversiondefinedelse\@@pbmonthconversion
+% {\convertnumber\@@pbmonthconversion\@@pb@month}{\@@pb@month}}%
+% {\@@pb@month}#2}%
+% {#3}
+ *month*
+\stopxmlsetups
+
+% lists
+
+\def\bibtexlistprocessor
+ {\ctxlua{bibtex.hacks.add(structures.lists.uservalue("\currentlist",\currentlistindex,"bibref"),\currentlistindex)}}
+
+\appendtoks
+ \definelist[\currentbibtexsession]%
+ \setuplist[\currentbibtexsession][\c!state=\v!start,\c!width=]%
+ \installstructurelistprocessor{\currentbibtexsession:userdata}{\bibtexlistprocessor}%
+\to \everydefinebibtexsession
+
+% \def\installbibtexsorter#1#2%
+% {\setvalue{\??pb:\c!sort:#1}{#2}}
+
+% \installbibtexsorter\v!no {no}
+% \installbibtexsorter\v!author {au}
+% \installbibtexsorter\v!title {ti}
+% \installbibtexsorter\v!short {ab}
+% \installbibtexsorter\empty {no}
+% \installbibtexsorter\s!default{no}
+
+% \setupbibtex
+% [\c!sorttype=\v!cite,
+% \c!sort=no]
+
+% \protected\def\startpublication#1\stoppublication
+% {\blank
+% todo
+% \blank}
+
+% \let\stoppublication\relax
+
+\protected\def\bibtexspace {\removeunwantedspaces\space}
+\protected\def\bibtexperiod {\removeunwantedspaces.\space}
+\protected\def\bibtexcomma {\removeunwantedspaces,\space}
+\protected\def\bibtexlparent {\removeunwantedspaces\space(}
+\protected\def\bibtexrparent {\removeunwantedspaces)\space}
+\protected\def\bibtexlbracket{\removeunwantedspaces\space[}
+\protected\def\bibtexrbracket{\removeunwantedspaces]\space}
+
+% interfacing
+
+% todo : lang en language
+% todo : directions
+
+% variables
+
+\ifdefined\bibtexblock \else \newcount\bibtexblock \fi \bibtexblock\plusone
+\ifdefined\bibtexcounter \else \newcount\bibtexcounter \fi
+
+\newtoks \everysetupbibtexpublications
+\newtoks \everysetupbibtexcitations
+
+\def\bibtexrefprefix{\number\bibtexblock:}
+
+\let\currentbibtexsession\s!default
+\let\currentbibtexvariant\s!default
+
+% parameters: session+variant variant session shared
+
+\def\bibtexpublicationsparameter#1%
+ {\csname
+ \ifcsname\??pb\currentbibtexsession:\currentbibtexvariant#1\endcsname
+ \??pb\currentbibtexsession:\currentbibtexvariant#1%
+ \orelse\ifcsname\??pb:\currentbibtexvariant#1\endcsname
+ \??pb:\currentbibtexvariant#1%
+ \orelse\ifcsname\??pb\currentbibtexsession#1\endcsname
+ \??pb\currentbibtexsession#1%
+ \orelse\ifcsname\??pb#1\endcsname
+ \??pb#1%
+ \else
+ \s!empty
+ \fi
+ \endcsname}
+
+\def\bibtexcitationparameter#1%
+ {\csname
+ \ifcsname\??pv\currentbibtexsession:\currentbibtexvariant#1\endcsname
+ \??pv\currentbibtexsession:\currentbibtexvariant#1%
+ \orelse\ifcsname\??pv:\currentbibtexvariant#1\endcsname
+ \??pv:\currentbibtexvariant#1%
+ \orelse\ifcsname\??pv\currentbibtexsession#1\endcsname
+ \??pv\currentbibtexsession#1%
+ \orelse\ifcsname\??pv#1\endcsname
+ \??pv#1%
+ \else
+ \s!empty
+ \fi
+ \endcsname}
+
+% setup commands
+
+\protected\def\setupbibtexpublications
+ {\let\currentpublicationclass\??pb
+ \let\everysetupbibtexwhatever\everysetupbibtexpublications
+ \dosetupbibtexwhatever}
+
+\protected\def\setupbibtexcitations
+ {\let\currentpublicationclass\??pv
+ \let\everysetupbibtexwhatever\everysetupbibtexcitations
+ \dosetupbibtexwhatever}
+
+\protected\def\setupbibtexpublicationvariants
+ {\let\currentpublicationclass\??pb
+ \let\everysetupbibtexwhatever\everysetupbibtexpublications
+ \dosetupbibtexwhatevervariant}
+
+\protected\def\setupbibtexcitationvariants
+ {\let\currentpublicationclass\??pv
+ \let\everysetupbibtexwhatever\everysetupbibtexcitations
+ \dosetupbibtexwhatevervariant}
+
+\tolerant\def\dosetupbibtexwhatever[#1]#*[#2]% [sessionlist] [setup]
+ {\ifarguments\or
+ % setups
+ \getparameters[\currentpublicationclass][#1]%
+ \the\everysetupbibtexwhatever
+ \or
+ % sessions setups
+ \def\dobtxcommand##1{\getparameters[\currentpublicationclass##1][#2]}%
+ \processcommalist[#1]\dobtxcommand
+ \fi}
+
+\tolerant\def\dosetupbibtexwhatevervariant[#1]#*[#2]#*[#3]% [sessionlist] [variantlist] [setup]
+ {\ifarguments\or
+ % setups
+ \getparameters[\currentpublicationclass][#1]%
+ \the\everysetupbibtexwhatever
+ \or
+ % variants setups
+ \def\dobtxcommand##1{\getparameters[\currentpublicationclass:##1][#2]}%
+ \processcommalist[#1]\dobtxcommand
+ \or
+ % sessions variants setups
+ \def\dobtxcommand##1%
+ {\def\dodobtxcommand####1{\getparameters[\currentpublicationclass##1:####1][#3]}%
+ \processcommalist[#2]\dodobtxcommand}%
+ \processcommalist[#1]\docbtxommand
+ \fi}
+
+% some initializations
+
+\setupbibtexcitationvariants
+ [author,authoryear,authoryears]
+ [\c!namesep={, }]
+
+% loading alternatives (apa etc)
+
+\def\doloadbibtexpublicationalternative
+ {\ifproductionrun
+ \edef\bibtexpublicationsalternative{\@@pbalternative}% parent
+ \ifempty\bibtexpublicationsalternative \else
+ \processcommacommand[\bibtexpublicationsalternative]\dodoloadbibtexpublicationalternative
+ \let\@@pbalternative\empty
+ \fi
+ \fi}
+
+\def\dodoloadbibtexpublicationalternative#1%
+ {\doonlyonce{#1}
+ {\startreadingfile
+ \readsysfile{bxml-#1.mkiv}
+ {\showmessage\m!publications{6}{bxml-#1}}
+ {\showmessage\m!publications{1}{bxml-#1}}%
+ \stopreadingfile}}
+
+\appendtoks
+ \doloadbibtexpublicationalternative
+\to \everysetupbibtexpublications
+
+% we expect at least one invocation of the setup commands
+% because otherwise we always load the apa style even if
+% no publications are used
+%
+% \appendtoks
+% \doloadbibtexpublicationalternative
+% \to \everystarttext
+
+% whatever, should be key
+
+\def\bibtexleftnumber#1{#1\hfill~}
+
+% testing
+
+% \showmessage\m!publications{5}{#1 is unknown}\secondoftwoarguments}
+
+\let\doifbibreferencefoundelse\secondofthreearguments
+
+% lists
+
+\newtoks\everysetupbibtexlistplacement
+
+% this will change as we need it too often .. we will use context.thebibtexnamesep
+
+\appendtoks
+ \ctxlua {bibtex.authors.setsettings {
+ namesep = \!!bs\bibtexpublicationsparameter\c!namesep\!!es,
+ lastnamesep = \!!bs\bibtexpublicationsparameter\c!lastnamesep\!!es,
+ finalnamesep = \!!bs\bibtexpublicationsparameter\c!finalnamesep\!!es,
+ firstnamesep = \!!bs\bibtexpublicationsparameter\c!firstnamesep\!!es,
+ juniorsep = \!!bs\bibtexpublicationsparameter\c!juniorsep\!!es,
+ vonsep = \!!bs\bibtexpublicationsparameter\c!vonsep\!!es,
+ surnamesep = \!!bs\bibtexpublicationsparameter\c!surnamesep\!!es,
+ namesep = \!!bs\bibtexpublicationsparameter\c!namesep\!!es,
+ lastnamesep = \!!bs\bibtexpublicationsparameter\c!lastnamesep\!!es,
+ finalnamesep = \!!bs\bibtexpublicationsparameter\c!finalnamesep\!!es,
+ author = {
+ etallimit = \!!bs\bibtexpublicationsparameter\c!authoretallimit\!!es,
+ etaldisplay = \!!bs\bibtexpublicationsparameter\c!authoretaldisplay\!!es,
+ etaltext = \!!bs\bibtexpublicationsparameter\c!authoretaltext\!!es,
+ },
+ editor = {
+ etallimit = \!!bs\bibtexpublicationsparameter\c!editoretallimit\!!es,
+ etaldisplay = \!!bs\bibtexpublicationsparameter\c!editoretaldisplay\!!es,
+ etaltext = \!!bs\bibtexpublicationsparameter\c!editoretaltext\!!es,
+ },
+ artauthor = {
+ etallimit = \!!bs\bibtexpublicationsparameter\c!artauthoretallimit\!!es,
+ etaldisplay = \!!bs\bibtexpublicationsparameter\c!artauthoretaldisplay\!!es,
+ etaltext = \!!bs\bibtexpublicationsparameter\c!artauthoretaltext\!!es,
+ },
+ } }%
+\to \everysetupbibtexlistplacement
+
+\def\completebibtexpublications{\dodoubleempty\docompletebibtexpublications}
+\protected\def\placebibtexpublications {\dodoubleempty\doplacebibtexpublications}
+
+\def\docompletebibtexpublications[#1][#2]% title might become obsolete, just headtext
+ {\begingroup
+ \edef\currentbibtexsession{#1}%
+ \let\currentlist\currentbibtexsession
+ \setuplist[\currentbibtexsession][\c!criterium=\v!previous,#2]
+ \edef\currentbibtexsessiontitle{\namedlistparameter\currentbibtexsession\c!title}%
+ \ifempty\currentbibtexsessiontitle
+ \normalexpanded{\startnamedsection[\v!chapter][\c!reference=\currentbibtexsession,\c!title={\headtext{\currentbibtexsession}}]}%
+ \else
+ \normalexpanded{\startnamedsection[\v!chapter][\c!reference=\currentbibtexsession,\c!title={\currentbibtexsessiontitle}]}%
+ \fi
+ \dodoplacebibtexpublications
+ \stopnamedsection
+ \endgroup}
+
+\def\doplacebibtexpublications[#1][#2]%
+ {\begingroup
+ \edef\currentbibtexsession{#1}%
+ \let\currentlist\currentbibtexsession
+ \setuplist[\currentbibtexsession][\c!criterium=\v!previous,#2]%
+ \dodoplacebibtexpublications
+ \endgroup}
+
+\def\dodoplacebibtexpublications
+ {\determinelistcharacteristics[\currentbibtexsession]%
+ \the\everysetupbibtexlistplacement
+ \forgetall
+ \typesetbibtexlist
+ \global\advance\bibtexblock\plusone}
+
+\setvalue{\??pb:\c!numbering:\v!short}#1% todo var s -> short tag
+ {\bibtexlistnumberbox{\bibtexpublicationsparameter\c!numbercommand{\bibtexgetshort\currentpublicationtag}}}
+
+\setvalue{\??pb:\c!numbering:\v!bib}#1% todo var n -> number
+ {\bibtexlistnumberbox{\bibtexpublicationsparameter\c!numbercommand{\bibtexgetnumber\currentpublicationtag}}}
+
+\setvalue{\??pb:\c!numbering:\s!unknown}#1%
+ {\bibtexlistnumberbox{\bibtexpublicationsparameter\c!numbercommand{#1}}}
+
+\def\@@pblimitednumber % name
+ {\csname\??pb:\c!numbering:%
+ \ifcsname\??pb:\c!numbering:\currentbibtexnumbering\endcsname
+ \currentbibtexnumbering
+ \else
+ \s!unknown
+ \fi
+ \endcsname}
+
+\appendtoks
+ \edef\currentbibtexnumbering{\bibtexpublicationsparameter\c!numbering}%
+ \ifx\currentbibtexnumbering\v!no
+ \setuplist[\currentbibtexsession][\c!numbercommand=,\c!symbol=\v!none,\c!textcommand=\outdented]%
+ \else
+ \setuplist[\currentbibtexsession][\c!numbercommand=\@@pblimitednumber]%
+ \fi
+\to \everysetupbibtexlistplacement
+
+\newdimen\bibtexnumberwidth
+
+\def\bibtexlistnumberbox{\hbox \ifcase\bibtexnumberwidth\else to \bibtexnumberwidth\fi}
+
+\appendtoks
+ \doifelse{\bibtexpublicationsparameter\c!autohang}\v!yes
+ {\ifx\currentbibtexnumbering\v!short
+ \setbox\scratchbox\hbox{\bibtexpublicationsparameter\c!numbercommand{\bibtexpublicationsparameter\c!samplesize}}%
+ \else
+ \setbox\scratchbox\hbox{\bibtexpublicationsparameter\c!numbercommand{\ctxlua{tex.write(structures.lists.size())}}}%
+ \fi
+ \bibtexnumberwidth\wd\scratchbox
+ \setuplist[\currentbibtexsession][\c!distance=\zeropoint]}
+ {\doifelsenothing{\bibtexpublicationsparameter\c!width}
+ {\bibtexnumberwidth\zeropoint}
+ {\bibtexnumberwidth\bibtexpublicationsparameter\c!width}}%
+ \setuplist[\currentbibtexsession][\c!width=\bibtexnumberwidth]%
+\to \everysetupbibtexlistplacement
+
+\appendtoks
+ \let\maybeyear\gobbleoneargument
+ \let\noopsort \gobbleoneargument
+\to \everysetupbibtexlistplacement
+
+\appendtoks
+ \doifelse{\bibtexpublicationsparameter\c!maybeyear}\v!off
+ {\let\maybeyear\gobbleoneargument}
+ {\let\maybeyear\firstofoneargument}%
+\to \everysetupbibtexlistplacement
+
+\appendtoks
+ \doifnot{\bibtexpublicationsparameter\c!option}\v!continue
+ {\global\bibtexcounter\zerocount}%
+\to \everysetupbibtexlistplacement
+
+\appendtoks
+ \edef\currentbibtexcriterium{\namedlistparameter\currentbibtexsession\c!criterium}%
+\to \everysetupbibtexlistplacement
+
+\def\typesetbibtexlist
+ {\begingroup
+ \startpacked[\v!blank]%
+ \doif{\namedlistparameter\currentbibtexsession\c!criterium}\v!cite
+ {\setuplist[\currentbibtexsession][\c!criterium=\v!here]}%
+ \doifelse{\bibtexpublicationsparameter\c!method}\v!local
+ {\ctxlua{bibtex.hacks.reset(1)}}% function can take method
+ {\ctxlua{bibtex.hacks.reset(2)}}%
+ \strc_lists_place_current
+ {\currentbibtexsession}
+ {\currentbibtexcriterium}
+ {}%
+ {\namedlistparameter\currentbibtexsession\c!extras}%
+ {\namedlistparameter\currentbibtexsession\c!order}%
+ \ctxlua{bibtex.hacks.flush("\bibtexpublicationsparameter\c!sorttype")}%
+ \stoppacked
+ \endgroup}
+
+\protected\def\typesetbibtexpublication#1%
+ {\edef\currentbibtexsessiontag{#1}%
+ \ifempty\currentbibtexsessiontag
+ % can't really happen
+ \orelse\ifx\currentbibtexcriterium\v!all
+ \dotypesetbibtexpublication % was \doplacepublicationindeed
+ \else
+ \ctxlua{bibtex.hacks.doifalreadyplaced("\currentbibtexsessiontag")}
+ \donothing
+ \dotypesetbibtexpublication
+ \fi}
+
+\def\dotypesetbibtexpublication
+ {\doifelsebibreferencefound\currentbibtexsessiontag
+ {\global\advance\bibtexcounter\plusone
+ \ctxlua{bibtex.hacks.registerplaced("\currentbibtexsessiontag")}%
+ \let\currentlist\currentbibtexsession
+ \let\currentlistentrynumber \bibtexcounter
+ \let\currentlistentrytitle \thebibtexpublicationlistelement
+ \let\currentlistentrypagenumber\empty
+ \strc_lists_apply_renderingsetup}
+ {}} % invalid
+
+\def\thebibtexpublicationlistelement
+ {\strut
+ \expanded{\reference[\bibtexrefprefix\currentbibtexsessiontag]{\number\bibtexcounter}}%
+ \dotypesetabibtexpublication\currentbibtexsessiontag
+ \strut}
+
+\def\dotypesetabibtexpublication#1%
+ {\begingroup
+ \ignorespaces
+ \xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/command(bibtex:format)}%
+ \removeunwantedspaces
+% \ignorespaces
+% \bibalternative{\bibgetvart{#1}}%
+% \removeunwantedspaces
+ \endgroup}
+
+\def\doprocessbibtexentry#1{\typesetbibtexpublication{#1}}
+
+% citations
+
+\protected\def\bibtexcitation[#1]%
+ {\edef\currentbibtexsession{#1}%
+ \strictdoifelsenextoptional\dobibtexcitation\dobibtexref}
+
+\def\dobibtexref#1%
+ {\dodobibtexcitation[#1][]}
+
+\def\dobibtexcitation[#1]%
+ {\strictdoifelsenextoptional{\dodobibtexcitation[#1]}{\dodobibtexcitation[#1][]}}
+
+\def\dodobibtexcitation[#1][#2]%
+ {\dontleavehmode
+ \begingroup
+ \doifelsenothing{#2}\secondargumentfalse\secondargumenttrue
+ \ifsecondargument
+ \dowhateverbibtexcitation{#1}{#2}%
+ \else
+ \donumberedbibtexcitation{#1}%
+ \fi
+ \endgroup}
+
+\def\dowhatevercitation#1#2%
+ {\processcommalist[#2]\dobibtexcitationindeed
+ \setupinteraction[\c!style=]% use flag instead
+ \doifelseassignment{#1}
+ {\getparameters[\??pb\??pb][\c!alternative=,\c!extras=,#1]%
+ \edef\currentbibtexvariant{\@@pb@@pbalternative}%
+ \ifempty\currentbibtexvariant
+ \edef\currentbibtexvariant{\bibtexpublicationparameter\c!refcommand}%
+ \fi
+ \ifempty\@@pb@@pbextras
+ \setupcite[\currentbibtexvariant][#1]%
+ \else
+ \edef\@@pb@@pbextras{{\@@pb@@pbextras\ifdefined\@@pb@@pbright\@@pb@@pbright\else\bibtexpublicationparameter\c!right\fi}}%
+ \expanded{\setupcite[\currentbibtexvariant][#1,\c!right=\@@pb@@pbextras]}%
+ \fi}%
+ {\def\currentbibtexvariant{#1}}%
+ \getvalue{bibtex\currentbibtexvariant ref}[#2]}
+
+\def\donumberedbibtexcitation#1%
+ {\processcommalist[#1]\dobibtexcitationindeed
+ \setupinteraction[\c!style=]%
+ \edef\currentbibtexvariant{\bibtexcitationparameter\c!refcommand}%
+ \getvalue{bibtex\currentbibtexvariant ref}[#1]}
+
+\def\dobibtexcitationindeed#1%
+ {\iftrialtypesetting \else
+ \expanded{\writedatatolist[\currentbibtexsession][bibref=#1]}%
+ \fi}
+
+\def\nobibtexcitation[#1]%
+ {\processcommalist[#1]\dobibtexcitationindeed}
+
+\def\bibtexnumref[#1]%
+ {\dontleavehmode
+ \begingroup
+ \bibtexcitationparameter\v!left
+ \penalty\plustenthousand
+ \ctxlua{bibtex.hacks.resolve("","\number\bibtexblock","#1")}%
+ \bibtexcitationparameter\v!right
+ \endgroup}
+
+\def\dowithbibtexnumrefconnector#1#2%
+ {\ifnum#1>\plusone
+ \ifnum#2>\plusone
+ \ifnum#2=#1\relax
+ \bibtexpublicationsparameter\c!lastpubsep
+ \else
+ \bibtexpublicationsparameter\c!pubsep
+ \fi
+ \fi
+ \fi}
+
+\def\dowithbibtexnumref#1#2#3#4#5% n, i, prefix block ref
+ {\dowithbibtexnumrefconnector{#1}{#2}%
+ \def\bibtexrefprefix{#4:}%
+ \inbiblink[#5]}
+
+\def\dowithbibtexnumrefrange#1#2#3#4#5#6#7% n, i, prefix block ref
+ {\dowithbibtexnumrefconnector{#1}{#2}%
+ \def\bibtexrefprefix{#4:}%
+ \inbiblink[#5]%
+ \endash
+ \def\bibtexrefprefix{#6:}%
+ \inbiblink[#7]}
+
+\def\nobibtexnumref#1%
+ {[#1]}
+
+% hm
+
+% \def\@@pbinumbercommand{\executeifdefined{\??pb:\c!numbercommand:\@@pbnumbering}\firstofoneargument}
+
+% \letvalue{\??pb:\c!numbercommand:\v!yes }\firstofoneargument
+% \letvalue{\??pb:\c!numbercommand:\v!no }\gobbleoneargument
+% \setvalue{\??pb:\c!numbercommand:\v!short}{\bibtexgetshort\currentpublicationtag\gobbleoneargument}
+% \setvalue{\??pb:\c!numbercommand:\v!bib }{\bibtexgetnumber\currentpublicationtag\gobbleoneargument}
+
+% \def\bibalternative#1{\csname\??pv\@@currentalternative#1\endcsname}
+
+% basic setup
+
+% parent -> publicationlist
+%
+% \setuplist
+% [\currentbibtexsession]
+% [\c!samplesize={AA99},
+% \c!alternative=a,
+% \c!interaction=,
+% \c!pagenumber=\v!no,
+% #1,
+% \c!command=]
+
+% \setuppublicationlist
+% [\c!title=,
+% \c!command=\dospecialbibinsert,
+% \c!maybeyear=\v!on]
+
+\setupbibtexpublications
+ [\c!monthconversion=,
+ \c!alternative=apa,
+ \c!method=\v!global,
+ \c!refcommand=num,
+ \c!numbercommand=\bibtexleftnumber]
+
+\setupbibtexcitations % command ?
+ [\c!refcommand=num]
+
+% helpers
+
+\protected\def\doifelsebibtexinteraction
+ {\iflocation
+ \edef\temp{\bibtexcitationparameter\c!interaction}%
+ \ifx\temp\v!stop
+ \doubleexpandafter\secondoftwoarguments
+ \else
+ \doubleexpandafter\firstoftwoarguments
+ \fi
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifbibtexinteractionelse\doifelsebibtexinteraction
+
+% variants
+
+% todo: lastsep here
+
+\newconditional\firstbibtexrefsep
+
+\def\bibtexresetrefsep
+ {\settrue\firstbibtexrefsep}
+
+\def\bibtexinsertrefsep
+ {\ifconditional\firstbibtexrefsep
+ \setfalse\firstbibtexrefsep
+ \else
+ \bibtexcitationparameter\c!pubsep
+ \fi}
+
+\def\inbibtexlink#1#2%
+ {\doifelsereferencefound{\bibtexrefprefix#1}
+ {\goto{#2}[\bibtexrefprefix#1]}
+ {!#1!}}
+
+\def\dobibtexgotolink#1#2%
+ {\doifelsereferencefound{\bibtexrefprefix#1}
+ {\goto{#2}[\bibtexrefprefix#1]}
+ {!#1!}}
+
+\def\dobibattexlink#1#2%
+ {\doifelsereferencefound{\bibtexrefprefix#1}
+ {\at{#2}[\bibtexrefprefix#1]}
+ {!#1!}}
+
+\def\dobibtexurllink#1#2%
+ {\expanded{\useURL[bibtex:url:#1][#2]}%
+ \doifelsebibtexinteraction
+ {\goto{\url[bibtex:url:#1]}[url(bibtex:url:#1)]}
+ {\url[bibtex:url:#1]}}
+
+% todo: style, color
+
+\protected\def\bibtexdataref {\dodoubleargument\dobibtexdataref}
+\protected\def\bibtextyperef {\dodoubleargument\dobibtextyperef}
+\protected\def\bibtexkeyref {\dodoubleargument\dobibtexkeyref}
+\protected\def\bibtexserialref {\dodoubleargument\dobibtexserialref}
+\protected\def\bibtexurlref {\dodoubleargument\dobibtexurlref}
+\protected\def\bibtexdoiref {\dodoubleargument\dobibtexdoiref}
+\protected\def\bibtexpageref {\dodoubleargument\dobibtexpageref}
+\protected\def\bibtexnoneref {\dodoubleargument\dobibtexnoneref}
+\protected\def\bibtexshortref {\dodoubleargument\dobibtexshortref}
+\protected\def\bibtexyearref {\dodoubleargument\dobibtexyearref}
+\protected\def\bibtexauthorref {\dodoubleargument\dobibtexauthorref}
+\protected\def\bibtexauthoryearref {\dodoubleargument\dobibtexauthoryearref}
+\protected\def\bibtexauthoryearsref{\dodoubleargument\dobibtexauthoryearsref}
+
+\def\dobibtexdataref {\doprocessbibtexref\dodobibtexdataref {ref}} % [#1][#2]
+\def\dobibtextyperef {\doprocessbibtexref\dodobibtextyperef {type}} % [#1][#2]
+\def\dobibtexkeyref {\doprocessbibtexref\dodobibtexkeyref {key}} % [#1][#2]
+\def\dobibtexserialref {\doprocessbibtexref\dodobibtexserialref {serial}} % [#1][#2]
+\def\dobibtexurlref {\doprocessbibtexref\dodobibtexurlref {url}} % [#1][#2]
+\def\dobibtexdoiref {\doprocessbibtexref\dodobibtexdoiref {doi}} % [#1][#2]
+\def\dobibtexpageref {\doprocessbibtexref\dodobibtexpageref {page}} % [#1][#2]
+\def\dobibtexnoneref {\doprocessbibtexref\dodobibtexnoneref {none}} % [#1][#2]
+\def\dobibtexshortref {\doprocessbibtexref\dodobibtexshortref {short}} % [#1][#2]
+\def\dobibtexyearref {\doprocessbibtexref\dodobibtexyearref {year}} % [#1][#2]
+\def\dobibtexauthorref {\doprocessbibtexref\dodobibtexauthorref {author}} % [#1][#2]
+\def\dobibtexauthoryearref {\doprocessbibtexref\dodobibtexauthoryearref {authoryear}} % [#1][#2]
+\def\dobibtexauthoryearsref{\doprocessbibtexref\dodobibtexauthoryearsref{authoryears}} % [#1][#2]
+
+\def\doprocessbibtexref#1#2[#3][#4]%
+ {\edef\currentbibtexsession{#3}%
+ \edef\currentbibtexvariant{#2}%
+ \def\dodoprocessbibtexref##1%
+ {% test for existence
+ \edef\currentbibtextag{##1}%
+ \bibtexinsertrefsep
+ #1{##1}}%
+ \bibtexresetrefsep
+ \bibtexcitationparameter\v!left
+ \processcommalist[#4]\dodoprocessbibtexref\relax
+ \bibtexcitationparameter\v!right}
+
+\def\dodobibtexdataref#1%
+ {\dotypesetabibtexpublication{#1}}
+
+\def\dodobibtextyperef#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/attribute('category')}}%
+ \bibtexrefcontent}
+
+\def\dodobibtexkeyref#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='key']/context()}}%
+ \dobibtexgotolink{#1}{\bibtexrefcontent}}
+
+\def\dodobibtexserialref#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/match()}}%
+ \dobibtexgotolink{#1}{\bibtexrefcontent}}
+
+\def\dodobibtexurlref#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/field[@name='url']/context()}}%
+ \dobibtexurllink{#1}{\bibtexrefcontent}}
+
+\def\dodobibtexdoiref#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/field[@name='doi']/context()}}%
+ \dobibtexurllink{#1}{http://dx.doi.org/\bibtexrefcontent}}
+
+\def\dodobibtexpageref#1%
+ {\dobibtexatlink{#1}{}} % second argument can become 'page'
+
+\def\dodobibtexnoneref#1%
+ {}
+
+\def\dodobibtexshortref#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/bibtexshort()}}%
+ \dobibtexgotolink{#1}{\bibtexrefcontent}}
+
+\def\dodobibtexyearref#1%
+ {\edef\bibtexrefcontent{\xmlfilter{bibtex:\currentbibtexsession}{/bibtex/entry[@tag='#1']/field[@name='year']/context()}}%
+ \bibtexrefcontent}
+
+% \def\bibmaybeinteractive#1#2%
+% {\doifelsevalue{@@pv\@@currentalternative\c!compress}
+% {\ifbibinteractionelse{\gotobiblink{#2}[#1]}{#2}}
+% {#2}}
+
+% \def\bibauthornumref[#1]%
+% {\getcommalistsize[#1]%
+% \global\bibitemcounter\commalistsize
+% \bibresetrefsep
+% \processcommalist[#1]\dobibauthornumref }
+%
+% \def\dobibauthornumref#1%
+% {\bibinsertrefsep
+% \doifbibreferencefoundelse{#1}
+% {\begingroup
+% \bibgetvara{#1}%
+% \bibalternative\c!inbetween
+% \setuppublications[\c!refcommand=num]%
+% \cite[#1]%
+% \endgroup}
+% {}}
+
+% compress years
+% andtext namesep
+% otherstext authoretallimit
+
+% we will use context.* instead at the lua end because it saves us passing settings
+
+% \def\thebibtexpubsep {\bibtexpublicationsparameter\c!pubsep}
+% \def\thebibtexlastpubsep {\bibtexpublicationsparameter\c!lastpubsep}
+% \def\thebibtexfinalpubseparator{\bibtexpublicationsparameter\c!lastpubsep}
+
+\def\dodobibtexauthorref #1{\ctxlua{bibtex.authorref ("bibtex:\currentbibtexsession","#1","normal","author")}}
+\def\dodobibtexauthoryearref #1{\ctxlua{bibtex.authoryearref ("bibtex:\currentbibtexsession","#1","normal","author")}}
+\def\dodobibtexauthoryearsref#1{\ctxlua{bibtex.authoryearsref("bibtex:\currentbibtexsession","#1","normal","author")}}
+
+\protected\def\bibtexsingularplural#1#2{\ctxlua{bibtex.singularorplural(\!!bs#1\!!es,\!!bs#2\!!es)}}
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/bibl-tra.mkxl b/Master/texmf-dist/tex/context/base/mkxl/bibl-tra.mkxl
new file mode 100644
index 00000000000..536de8d31c9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/bibl-tra.mkxl
@@ -0,0 +1,1508 @@
+%D \module
+%D [ file=bibl-tra,
+%D version=2009.08.22,
+%D title=\CONTEXT\ Publication Module,
+%D subtitle=Publications,
+%D author=Taco Hoekwater,
+%D date=\currentdate,
+%D copyright={Public Domain}]
+%C
+%C Donated to the public domain.
+
+%D This is really obsolete in \LMTX. For now I keep it but it will either go
+%D away. The code is not upgraded, we only deal with macro protection. I will
+%D only update the bits that annoy me in grepping for possible optimizations.
+
+% % % % watch out ... bibl-tra-new.mkiv is work in progress % % % %
+
+% % % % mlbibtex also supports context and we can run that instead of bibtex % % % %
+
+%D This module has been adapted to \MKIV\ by Hans Hagen so if things go wrong, he is
+%D to blame. The changes concern references and lists but teh rendering itself is
+%D unchanged. Future versions might provide variants as we have plans for an
+%D upgrade.
+%D
+%D We use a still somewhat experimental extension to the list mechanism. Eventually
+%D the bibtex module will use the bibl loader and access the data by means of lpath
+%D expressions. In that case we don't need to process the bibliography but still
+%D need to track usage as done here.
+%D
+%D A bit ongoing: make more local macros prefixed with bib, i.e. the bib namespace
+%D is reserved.
+
+%D Todo: commandhandler
+
+\writestatus{loading}{ConTeXt Bibliography Support / BibTeX}
+
+\definefilesynonym[bib][obsolete]
+
+\registerctxluafile{bibl-tra}{}
+
+%D The original was developed independantly by Taco Hoekwater while still working
+%D for Kluwer Academic publishers (it still used the dutch interface then).
+%D Development continued after he left Kluwer, and in Januari 2005, the then already
+%D internationalized file was merged with the core distribution by Hans Hagen. The
+%D current version is once again by Taco.
+%D
+%D More documentation and additional resources can be found on the contextgarden:
+%D \hyphenatedurl {http://wiki.contextgarden.net//Bibliography}.
+
+%D \subject{DONE (dd/mm/yyyy)}
+%D
+%D \startitemize
+%D \item add author definition (and associated system variable) (26/05/2005)
+%D \item add finalnamesep support for Oxford comma (17/09/2005)
+%D \item add \type{\insert...} for: doi, eprint, howpublished (19/09/2005)
+%D \item allow a defaulted \type{\setupcite} (19/11/2005)
+%D \item renamed citation type 'number' to 'serial' (19/11/2005)
+%D \item better definition of \type{\inverted...author} (19/11/2005)
+%D \item don't reset [numbercommand] in \type {\setuppublication} by default (20/11/2005)
+%D \item don't disable other \type {\setuppublication} keys if alternative is present (20/11/2005)
+%D \item drop \type{\sanitizeaccents} (20/11/2005)
+%D \item added \type{\nocite} and \type{\cite[none]} (21/11/2005)
+%D \item added headtext for it (23/11/2005)
+%D \item make \type{\cite[url]} and \type{\cite[doi]} interactive (23/11/2005)
+%D \item make right-aligned labels in the list work even when autohang=no
+%D \item use 'et al.' instead of 'et.al.'. Pointed out by Peter M\"unster (30/12/2005)
+%D \item added headtext for cz (31/12/2005)
+%D \item Keep whitespace after \type{\cite} with single argument (31/12/2005)
+%D \item Fix broken \type{\cite{}} support (31/12/2005)
+%D \item Use \type{\readfile} inside \type{\usepublications} instead of \type{\readsysfile} (12/01/2006)
+%D \item Use \type{\currentbibyear} and \type{\currentbibauthor} instead of \type{\YR} and \type{\AU} (05/02/2006)
+%D \item Fix compressed version of authoryear style (05/02/2006)
+%D \item Rename the clashing data fields \type{\url} and \type{\type} to \type{\biburl} and \type{\bibtype} (05/02/2006)
+%D \item Added two french bibl files from Renaud Aubin (06/02/2006)
+%D \item Five new bib class and eight extra bib fields, for IEEEtran (07/02/2006)
+%D \item French keyword translation, provided by Renaud (08/02/2006)
+%D \item fix underscores in undefined keys (22/02/2006)
+%D \item Destroy interactivity in labels of the publication list (13/03/2006)
+%D \item fix multi-cite list compression (11/4/2006)
+%D \item fix \type{\getcitedata} (11/4/2006)
+%D \item magic for chapter bibs (18-25/4/2006)
+%D \item language setting (25/4/2006)
+%D \item use \type{\hyphenatedurl} for \type{\inserturl} (25/4/2006)
+%D \item Add \type{\docitation} to \type{\nocite}(26/4/2006)
+%D \item patents can have numbers, added to bst files (26/4/2006)
+%D \item \type{\docitation} needs a \type{\iftrialtypesetting} (27/4/2006)
+%D \item \type{\filllocalpublist}'s loop is bound by definedness, not resolvedness (27/4/2006)
+%D \item \type{\setuppublications[monthconversion=]} added (15/5/2006)
+%D \item use \type{\undefinedreference} instead of bare question marks (15/5/2006)
+%D \item add grouping around \type{\placepublications} commands (16/5/2006)
+%D \item fix a bug in \type{\cite{<item>}} (17/5/2006)
+%D \item support \type{\cite[authornum]} (18/5/2006)
+%D \item make \type{\cite} unexpandable (20/6/2006)
+%D \item allow hyperlinks in author\&year combo's
+%D (cite list compression has to be off) (20/6/2006)
+%D \item fix duplicate labels for per-chapter style (20/6/2006)
+%D \item allow \type{\setupcite[interaction=(start|stop)]}
+%D \item fix the item number in the publication list with 'numbering=yes' (22/6/2006)
+%D \item make the default criterium for \type{\placepublications} be \type{previous} (23/6/2006)
+%D \item fix \type{\normalauthor} and \type{\normalshortauthor} spacing (29/6/2006)
+%D \item do not typeset empty arguments to \type{\typesetapublication} (29/6/2006)
+%D \item add \type{symbol=none} to \type{\setuplist} in unnumbered
+%D mode to prevent typesetting of bare numbers (29/6/2006)
+%D \item remove two incorrect spaces from bibl-num.tex (1/7/2006)
+%D \item reset font styles within \type{\cite}, so that font switches
+%D in \type{left} stay in effect (12/7/2006)
+%D \item guard added against loading bbl files multiple times (13/7/2006)
+%D \item fix \type{\cite[num]} with compression is on. (14/7/2006)
+%D \item test \type{\iflocation} before deciding to use the
+%D interactive version of cite (18/7/2006)
+%D \item support \type{\setupcite[authoretallimit=1]} (18/7/2006)
+%D \item support use of \type{\cite} within titles and captions by
+%D saveguarding the list item extraction and reference placement
+%D code (19/7/2006)
+%D \item support \type{\setuppublicationlist[title=\chapter]} (4/8/2006)
+%D \item use the expansion of \type{\headtext{pubs}} (4/8/2006)
+%D \item hook added for repeated authors in publication list
+%D \type{\setuppublicationlist[artauthorcommand=\mythreeargscommand]}
+%D (4/8/2006)
+%D \item make the bracketed arguments of \type{\artauthor}, \type{\author}
+%D and \type{\editor} (bbl commands) optional (4/8/2006)
+%D \item the constants \type{sorttype}, \type{compress} and
+%D \type{autohang} have moved to the core (8/8/2006)
+%D \item bibtex is now registered as a program to be run by texexec (8/8/2006)
+%D \item fix a bug in \type{\setupcite[authoretallimit=1]} (9/8/2006)
+%D \item fix a bug inside citations that prevented lastpubsep from ever being
+%D used due to a volatile \type{\commalistsize} (25/8/2006).
+%D \item added the possibility of \type{\placepublications[option=continue]}
+%D (6/9/2006)
+%D \item Mojca translated Master's Thesis to Masterarbeit (bibl-apa-de.tex)
+%D (12/9/2006)
+%D \item Added \type{\setuppublicationlist[maybeyear=off]} by request from
+%D Thomas Schmitz (15/9/2006)
+%D \item Removed some spurious spaces pointed out by willi egger (19/9/2006)
+%D \item Add configuration of bibtex executable name (4/11/2006)
+%D \item Fix numbering=short and numbering=bib (spotted by Matthias W\"achter) (4/11/2006)
+%D \item third attempt to get a correct release (5/11/2006)
+%D \item fix a few missing dots in bibl-num.tex (7/12/2006)
+%D \item Patch for DOI's by Tobias Burnus (17/4/2007)
+%D \item Patch for \type{\insertbiburl} and \type{\insertdoi} for Tobias Burnus (18/4/2007)
+%D \item Added a missing \type{\relax} in \type{\dospecialbibinsert},
+%D that made the space before the {\it et al.} text disappear. (18/4/2007)
+%D \item Attempt to fix percent signs in bbl files. As a side-effect,
+%D this prohibits comments in \tex{startpublication} blocks! (17/4/2008)
+%D \item Patch from Matthias W\"achter that allows arbitrary .bst
+%D files to be used with \tex{setupbibtex} (25/9/2008)
+%D \item Extended for the new multilingual setups for the Oct 2008 current of ConTeXt (23/10/2008)
+%D \item Multilingual setups needed another fix (27/10/2008)
+%D \item Two fixes for bibl-apa by Michael Green (27/10/2008)
+%D \item Catalan translation of 'References' (10/11/2008)
+%D \item 'chapter' -> 'chapitre' in bibl-apa-fr (27/11/2008)
+%D \item Run bibtex via os.execute in mkiv modee (01/12/2008)
+%D \item Small correction in bibl-apa's placement of volume
+%D information in articles (05/01/2009)
+%D \item Handle multi-author (more than two) cases in \type{\cite}
+%D (02/03/2009)
+%D \item Suppress a syntax error in \type{cont-xp} mode. The output is
+%D probably not right, though (02/03/2009)
+%D \item Added a \tex{loadmarkfile} at the end, and two new files
+%D from Hans. The \type{t-bib.mkiv} is needed to make the module
+%D work with the new structure code (17/04/2009)
+%D \item Added a patch to \type{t-bib.mkiv} from Hans to make the
+%D cross referencing between multiple citations an
+%D bibliographies work (27/04/2009)
+%D \item Remove a superfluous \type{\unprotect} in t-bib.mkiv (11/05/2009).
+%D \item Patch of incollection in bibl-ams.tex from Xan (08/06/2009).
+%D \item Patch of unpublished in bibl-ams.tex from Xan (22/07/2009).
+%D \item Modified \type{\bibdogetupsometextprefix} so it works for undefined
+%D language labels, from Hans (13/08/2009).
+%D \item Adapt referencing and list insertion to \MKIV. Update some code
+%D to the latest \CONTEXT. Change some names in order to avoid conflicts
+%D with existing core names (like \type {\insertpages}).
+%D \item All constants, variables, message etc.\ are now in the core.
+%D \item Added key: \type {method} (when \type {global}, previous shown entries are
+%D not shown again, when \type {local} they are repeated).
+%D \stopitemize
+%D
+%D \subject{WISHLIST}
+%D
+%D \startitemize
+%D \item link back from publication list to citation
+%D \item export \type {\citation{<cited item>}}
+%D \item support mlbibtex
+%D \item don't load the whole lot, but filter entries instead
+%D \item 9 vs 10, 19 vs 20 ... prevent extra runs when only subtle changes in wd of reference
+%D \stopitemize
+
+\unprotect
+
+\def\biblistname{pubs} % for compatibility
+
+\definelist
+ [pubs]
+
+\setuplist
+ [pubs]
+ [\c!state=\v!start,
+ \c!criterium=\@@pbcriterium,
+ \c!headnumber=\v!always, % needed as we provide our own and need to force
+ \c!width=]
+
+\installstructurelistprocessor{pubs:userdata}%
+ {\ctxlua{bibtex.hacks.add(structures.lists.uservalue("\currentlist",\currentlistindex,"bibref"),\currentlistindex)}}
+
+\ifdefined\bibtexblock \else \newcount\bibtexblock \fi \bibtexblock\plusone
+\ifdefined\bibtexcounter \else \newcount\bibtexcounter \fi
+
+%D \macros{bibdoif,bibdoifnot,bibdoifelse}
+%D
+%D Here are a few small helpers that are used a lot in all the typesetting commands
+%D (\type{\bibinsert...}) we will encounter later.
+
+\protected\def\bibdoifelse#1%
+ {\expandafter\def\expandafter\!!stringa\expandafter{#1}%
+ \ifempty\!!stringa
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\protected\def\bibdoifnot#1%
+ {\expandafter\def\expandafter\!!stringa\expandafter{#1}%
+ \ifempty\!!stringa
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\protected\def\bibdoif#1%
+ {\expandafter\def\expandafter\!!stringa\expandafter{#1}%
+ \ifempty\!!stringa
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D Unfortunately, \BIBTEX\ is not the best configurable program around. The names of
+%D the commands it parses as well as the \type {.aux} extension to the file name are
+%D both hardwired.
+%D
+%D This means \CONTEXT\ has to write a \LATEX-style auxiliary file, yuk! The good
+%D news is that it can be rather short. We'll just ask \BIBTEX\ to output the entire
+%D database(s) into the \type {bbl} file.
+%D
+%D The \type {\bibstyle} command controls how the \type {bbl} file will be sorted.
+%D The possibilities are:
+%D
+%D \startitemize[packed]
+%D \item by author (+year, title): cont-au.bst
+%D \item by title (+author, year): cont-ti.bst
+%D \item by short key as in abbrev.bst: cont-ab.bst
+%D \item not sorted at all: cont-no.bst
+%D \stopitemize
+
+\newtoks\everysetupbibtex
+
+\protected\def\setupbibtex
+ {\dosingleempty\dosetupbibtex}
+
+\protected\def\dosetupbibtex[#1]%
+ {\let\@@pbdatabase\empty
+ \getparameters[\??pb][#1]%
+ \the\everysetupbibtex}
+
+\protected\def\installbibtexsorter#1#2%
+ {\setvalue{\??pb:\c!sort:#1}{#2}}
+
+\installbibtexsorter\v!no {no}
+\installbibtexsorter\v!author {au}
+\installbibtexsorter\v!title {ti}
+\installbibtexsorter\v!short {ab}
+\installbibtexsorter\empty {no}
+\installbibtexsorter\s!default{no}
+
+\def\thebibtexsorter{\executeifdefined{\??pb:\c!sort:\@@pbsort}\@@pbsort}
+
+\appendtoks
+ \ifempty\@@pbdatabase\else
+ \doifmode{*\v!first}{\ctxlua{bibtex.hacks.process { style="\thebibtexsorter", database="\@@pbdatabase" }}}%
+ \fi
+\to \everysetupbibtex
+
+\setupbibtex
+ [\c!sorttype=\v!cite,
+ \c!sort=no]
+
+%D \macros{iftypesetall,ifbibcitecompress}
+%D
+%D The module needs some new \type{\if} statements.
+
+\newtoks\everysetuppublications
+
+\protected\def\setuppublications
+ {\dosingleargument\dosetuppublications}
+
+\protected\def\dosetuppublications[#1]%
+ {\getparameters[\??pb][\c!alternative=,#1]%
+ \doifsomething\@@pbalternative
+ {\readsysfile{bibl-\@@pbalternative.tex}
+ {\showmessage\m!publications{6}{bibl-\@@pbalternative}}
+ {\showmessage\m!publications{1}{bibl-\@@pbalternative}}%
+ \let\@@pbalternative\empty}%
+ \let\setuppublicationlayout\normalsetuppublicationlayout % overloaded in bibl-num ... vadjust needs to be done with option
+ \getparameters[\??pb][#1]% as bibl-* can have set things back
+ \the\everysetuppublications
+ \ignorespaces}
+
+%D We can omit already shown references (\v!global) or use fresh lists each time
+%D (\v!local).
+
+\setnewconstant\bibtexoncemode\plusone % 0=disable, 1=local, 2=global
+
+\appendtoks
+ \doifelse\@@pbmethod\v!local
+ {\bibtexoncemode\plusone}%
+ {\bibtexoncemode\plustwo}%
+\to \everysetuppublications
+
+%D Cite lists are compressed, if possible. This is set later on.
+
+\newif\ifbibcitecompress\bibcitecompresstrue
+
+\def\@@pbinumbercommand{\executeifdefined{\??pb:\c!numbercommand:\@@pbnumbering}\firstofoneargument}
+
+\setvalue{\??pb:\c!numbercommand:\v!yes }#1{#1}%
+\setvalue{\??pb:\c!numbercommand:\v!no }#1{}
+\setvalue{\??pb:\c!numbercommand:\v!short}#1{\bibgetvars\currentpublicationkey}
+\setvalue{\??pb:\c!numbercommand:\v!bib }#1{\bibgetvarn\currentpublicationkey}
+
+% to be tested
+%
+% \setvalue{\??pb:\c!numbercommand:\v!short}{\bibgetvars\currentpublicationkey\firstofoneargument}
+% \setvalue{\??pb:\c!numbercommand:\v!bib }{\bibgetvarn\currentpublicationkey\firstofoneargument}
+
+\appendtoks
+ \processaction
+ [\@@pbrefcommand]
+ [\s!default=>\edef\@@citedefault{\@@pbrefcommand},
+ \s!unknown=>\edef\@@citedefault{\@@pbrefcommand}]%
+\to \everysetuppublications
+
+\protected\def\bibleftnumber#1{#1\hfill~}
+
+%D \macros{usepublications}
+%D
+%D After discussing it with Thomas Schmitz it became clear that using external
+%D references makes no sense as one needs to refer to it in special ways and because
+%D similar numbers can be confusing. So, for the moment this is not supported in
+%D \MKIV. (So no: see reference [3-5,9] in "some other document")
+
+\protected\def\usepublications[#1]%
+ {\processcommalist[#1]\dousepublications}
+
+\protected\def\dousepublications#1%
+ {\doonlyonce{#1.\f!bibextension}{\dodousepublications{#1}}}
+
+\pushoverloadmode
+
+\protected\def\dodousepublications#1% brr, this par stuff
+ {\enforced\let\par\ignorespaces
+ \ifhmode\kern\zeropoint\fi
+ \pushcatcodetable
+ \setcatcodetable\ctxcatcodes
+ \readfile{#1.\f!bibextension}
+ {\showmessage\m!publications{4}{#1.\f!bibextension}}
+ {\showmessage\m!publications{2}{#1.\f!bibextension}}%
+ \popcatcodetable
+ \ifhmode\removeunwantedspaces\fi
+ \enforced\let\par\normalpar}
+
+\popoverloadmode
+
+%D \macros{setuppublicationlist}
+%D
+%D This will be the first command in (\BIBTEX-generated) \type {bbl} files. \quote
+%D {samplesize} is a sample value (in case of \BIBTEX|-|generated files, this will
+%D be the longest \quote {short} key). Here \quote {totalnumber} is the total number
+%D of entries that will follow in this file.
+%D
+%D Both values are only needed for the label calculation if \quote {autohang} is
+%D \quote {true}, so by default the command is not even needed, and therefore I saw
+%D no need to give it it's own system variable and it just re-uses \type {pb}.
+
+\def\publicationlistparameter#1{\csname\??pv:l:#1\endcsname}
+
+\protected\def\setuppublicationlist
+ {\dosingleempty\dosetuppublicationlist}
+
+\protected\def\dosetuppublicationlist[#1]%
+ {\getparameters[\??pv:l:][#1]%
+ \setuplist[pubs][\c!samplesize={AA99},\c!alternative=a,\c!interaction=,\c!pagenumber=\v!no,#1,\c!command=]}
+
+\protected\def\setuppublicationlayout[#1]#2%
+ {\setvalue{\??pv:l:#1}{#2}}
+
+\let\normalsetuppublicationlayout\setuppublicationlayout
+
+\setuppublicationlist
+ [\c!title=,
+ \c!command=\dospecialbibinsert,
+ \c!maybeyear=\v!on]
+
+%D \macros{bibalternative}
+%D
+%D A nice little shorthand that will be used so we don't have to key in the weird
+%D \type {\@@pv} parameter names all the time.
+
+\def\bibalternative#1%
+ {\csname\??pv\@@currentalternative#1\endcsname}
+
+%D \macros{simplebibdef,bibcommandlist}
+%D
+%D \type {\simplebibdef} defines \type {bib@#1}, which in turn will use one argument
+%D that is stored in \type {@@pb@#1}.
+%D
+%D \type {\simplebibdef} also defines \type {bibinsert#1}, which can be used in the
+%D argument of \type {\setuppublicationlayout} to fetch one of the \type {@@pb@}
+%D data entries. \type {bibinsert#1} then has three arguments: \type {#1} are
+%D commands to be executed before the data, \type {#2} are commands to be executed
+%D after the data, and \type {#3} are commands to be executed if the data is not
+%D found.
+%D
+%D \type {\bibcommandlist} is the list of commands that is affected by this
+%D approach. Later on, it will be used to do a series of assignments from \type {#1}
+%D to \type {bib@#1}: e.g \type {\title} becomes \type {\bib@title} when used within
+%D a publication.
+
+\newtoks\initializebibdefinitions % we need to prevent clashes
+
+\protected\def\simplebibdef#1% hh: funny expansion ?
+ {\expandafter\def\csname bib@#1\endcsname##1%
+ {\setvalue{\??pb @#1}{##1}\ignorespaces}%
+ \expandafter \appendtoks
+ \expandafter\let\csname insert#1\expandafter\endcsname\csname bibinsert#1\endcsname
+ \to \initializebibdefinitions
+ \expandafter\protected\expandafter\def\csname bibinsert#1\endcsname##1##2##3%
+ {\expandafter\bibdoifelse\expandafter{\csname\??pb @#1\endcsname}{##1\csname\??pb @#1\endcsname##2}{##3}}}
+
+\def\bibcommandlist
+ {abstract, annotate, arttitle, assignee, bibnumber, bibtype, biburl, chapter, city,
+ comment, country, day, dayfiled, doi, edition, eprint, howpublished, isbn, issn,
+ issue, journal, keyword, keywords, lastchecked, month, monthfiled, names, nationality,
+ note, notes, organization, pages, pubname, pubyear, revision, series, size, thekey,
+ title, volume, yearfiled}
+
+\processcommacommand[\bibcommandlist]\simplebibdef
+
+\protected\def\bibinsertdoi#1#2#3% let's see how this fails
+ {\bibdoifelse{\@@pb@doi}{#1\expanded{\bibgotoDOI{\@@pb@thekey}{\@@pb@doi}}#2}{#3}}
+
+\protected\def\bibinsertbiburl#1#2#3% let's see how this fails
+ {\bibdoifelse{\@@pb@biburl}{#1\expanded{\bibgotoURL{\@@pb@thekey}{\@@pb@biburl}}#2}{#3}}
+
+\protected\def\bibinsertmonth#1#2#3%
+ {\bibdoifelse\@@pb@month
+ {#1\doifelsenumber\@@pb@month
+ {\doifelseconversiondefined\@@pbmonthconversion
+ {\convertnumber\@@pbmonthconversion\@@pb@month}{\@@pb@month}}%
+ {\@@pb@month}#2}%
+ {#3}}
+
+\appendtoks
+ \let\inserturl \bibinsertbiburl % for backward compat.
+ \let\inserttype\bibinsertbibtype % for backward compat.
+\to\initializebibdefinitions
+
+\protected\def\newbibfield[#1]%
+ {\simplebibdef{#1}%
+ \edef\bibcommandlist{\bibcommandlist,#1}}
+
+%D \macros{complexbibdef,specialbibinsert}
+%D
+%D The commands \type {\artauthor}, \type {\author} and \type {\editor} are more
+%D complex than the other commands. Their argument lists have this form:
+%D
+%D \type{\author[junior]{firstnames}[inits]{von}{surname}}
+%D
+%D (bracketed stuff is optional)
+%D
+%D And not only that, but there also might be more than one of each of these
+%D commands. This is why a special command is needed to insert them, as well as one
+%D extra counter for each command.
+
+% todo: instead of \getvalue {bla@num} in specs we should do \bibentrynum {bla} so
+% that we can create a better namespace
+
+%D All of these \type {\expandafter}'s and \type {\csnames} make this code look far
+%D more complex than it really is. For example, the argument \type {author} defines
+%D the macro \type {\bib@author} to do two things: increment the counter \type
+%D {\author@num} (let's say to 2) and next store it's arguments in the macro \type
+%D {\@@pb@author2}. And it defines \type {\bibinsertauthors} to expand into
+%D
+%D \starttyping
+%D \specialbibinsert{author}{\author@num}{<before>}{<after>}{<not>}
+%D \stoptyping
+
+\protected\def\docomplexbibdef#1%
+ {\dodoubleempty\dodocomplexbibdef[#1]}
+
+\protected\def\dodocomplexbibdef[#1][#2]#3%
+ {\doquadrupleempty\dododocomplexbibdef[#1][#2][#3]}
+
+\protected\def\dododocomplexbibdef[#1][#2][#3][#4]#5#6%
+ {\expandafter\increment\csname#1@num\endcsname % todo: bib in name
+ \setevalue{\??pb @#1\csname#1@num\endcsname}{{#3}{#5}{#6}{#4}{#2}}\ignorespaces}
+
+\protected\def\complexbibdef#1%
+ {\expandafter\newcounter\csname #1@num\endcsname
+ \expandafter\def\csname bib@#1\endcsname{\docomplexbibdef{#1}}%
+ \expandafter \appendtoks
+ \expandafter\let\csname insert#1s\expandafter\endcsname\csname bibinsert#1s\endcsname
+ \to \initializebibdefinitions
+ \expandafter\def\csname bibinsert#1s\endcsname##1##2##3{\specialbibinsert{#1}{\csname #1@num\endcsname}{##1}{\unskip ##2}{##3}}}
+
+\processcommalist[author,artauthor,editor]\complexbibdef
+
+%D Another level of indirection is needed to control the typesetting of all of these
+%D arguments.
+
+\newcount\etallimitcounter
+\newcount\etaldisplaycounter
+\newcount\todocounter
+
+\protected\def\specialbibinsert#1#2#3#4#5%
+ {\bgroup
+ \ifnum#2>\zerocount
+ \etallimitcounter =0\bibalternative{#1etallimit}\relax
+ \etaldisplaycounter=0\bibalternative{#1etaldisplay}\relax
+ \ifnum #2>\etallimitcounter
+ \todocounter\etaldisplaycounter
+ % just in case ...
+ \ifnum\todocounter>\etallimitcounter
+ \todocounter\etallimitcounter
+ \fi
+ \else
+ \todocounter#2\relax
+ \fi
+ \ifnum\todocounter>\zerocount
+ % find the current author list
+ \let\templist\empty
+ \dorecurse{#2}
+ {\scratchtoks\doubleexpandafter{\csname\??pb @#1\recurselevel\endcsname}%
+ \edef\templist{\ifempty\templist\else\templist,\fi\the\scratchtoks}}%
+ #3\publicationlistparameter\c!command{#1}{\todocounter}{\templist}#4\relax
+ \else
+ #5%
+ \fi
+ \else
+ #5%
+ \fi
+ \egroup}
+
+%D This macro does the hard work of inserting a list of people in the output, with
+%D proper regard of all the inbetween strings that can arise depending on length of
+%D the list of people.
+
+%D \#1 = type
+%D \#2 = number of items to be typeset
+%D \#3 = commacommand containing authors
+
+\protected\def\doprocessauthoritem#1#2#3%
+ {\advance\scratchcounter\plusone
+ \ifnum\numexpr\scratchcounter-\plusone\relax<#2\relax
+ \publicationlistparameter{#1}#3%
+ \ifnum\scratchcounter=#2\relax
+ \ifnum\etallimitcounter<\commalistsize\relax
+ \bibalternative{#1etaltext}%
+ \fi
+ \orelse\ifnum\numexpr\scratchcounter+\plusone\relax=#2\relax
+ \ifnum\commalistsize>\plustwo
+ \bibalternative\c!finalnamesep
+ \else
+ \bibalternative\c!lastnamesep
+ \fi
+ \else
+ \bibalternative\c!namesep
+ \fi
+ \fi}
+
+\protected\def\dospecialbibinsert#1#2#3%
+ {\getcommacommandsize[#3]%
+ \scratchcounter\zerocount
+ \processcommacommand[#3]{\doprocessauthoritem{#1}{#2}}}
+
+%D \macros{invertedauthor,normalauthor,invertedshortauthor,normalshortauthor}
+%D
+%D Just some commands that can be used in \type {\setuppublicationparameters}
+%D If you want to write an extension to the styles, you might as well define
+%D some of these commands yourself.
+%D
+%D The argument list has been reordered here, and the meanings are:
+%D
+%D \startlines
+%D \type{#1} firstnames
+%D \type{#2} von
+%D \type{#3} surname
+%D \type{#4} inits
+%D \type{#5} junior
+%D \stoplines
+
+\protected\def\normalauthor#1#2#3#4#5%
+ {\bibdoif{#1}{#1\bibalternative\c!firstnamesep}%
+ \bibdoif{#2}{#2\bibalternative\c!vonsep}%
+ #3%
+ \bibdoif{#5}{\bibalternative\c!surnamesep#5\unskip}}
+
+\protected\def\normalshortauthor#1#2#3#4#5%
+ {\bibdoif{#4}{#4\bibalternative\c!firstnamesep}%
+ \bibdoif{#2}{#2\bibalternative\c!vonsep}%
+ #3%
+ \bibdoif{#5}{\bibalternative\c!surnamesep#5\unskip}}
+
+\protected\def\invertedauthor#1#2#3#4#5%
+ {\bibdoif{#2}{#2\bibalternative\c!vonsep}%
+ #3%
+ \bibdoif{#5}{\bibalternative\c!juniorsep#5}%
+ \bibdoif{#1}{\bibalternative\c!surnamesep#1\unskip}}
+
+\protected\def\invertedshortauthor#1#2#3#4#5%
+ {\bibdoif{#2}{#2\bibalternative\c!vonsep}%
+ #3%
+ \bibdoif{#5}{\bibalternative\c!juniorsep#5}%
+ \bibdoif{#4}{\bibalternative\c!surnamesep#4\unskip}}
+
+%D \macros{clearbibitem,clearbibitemtwo,bibitemdefs}
+%D
+%D These are used in \type {\typesetapublication} to do initializations and
+%D cleanups.
+
+\protected\def\clearbibitem#1{\setvalue{\??pb @#1}{}}%
+
+\protected\def\clearbibitemtwo#1% is this reset really needed? after all we reset the counter and we are local
+ {%\dofastrecurse\plusone{\csname#1@num\endcsname}\plusone{\expandafter\let\csname\??pb @#1\recurselevel\undefined}%
+ \letvalue{#1@num}\!!zerocount}
+
+\protected\def\bibitemdefs#1%
+ {\expandafter\let\csname#1\expandafter\endcsname\csname bib@#1\endcsname}
+
+\protected\def\presetbibvariables % make a fast resetter (toks)
+ {\processcommacommand[\bibcommandlist,crossref]\clearbibitem
+ \processcommalist [artauthor,author,editor]\clearbibitemtwo
+ \processcommacommand[\bibcommandlist]\bibitemdefs
+ \processcommalist [artauthor,author,editor,crossref]\bibitemdefs}
+
+%D \macros{startpublication}
+%D
+%D We are coming to the end of this module, to the macros that do typesetting and
+%D read the \type {bbl} file.
+%D
+%D Just a \type {\dosingleempty} is the most friendly of doing this: there need not
+%D even be an argument to \type {\startpublication}. Of course, then there is no key
+%D either, and it had better be an article (otherwise the layout will be all screwed
+%D up).
+%D
+%D Only specifying the key in the argument is also legal. In storing this stuff into
+%D macros, some trickery with token registers is needed to fix the expansion
+%D problems. Even so, this appears to not always be 100\% safe, so people are
+%D urgently advised to use \ETEX\ instead of traditional \TEX.
+%D
+%D In \ETEX, all expansion problems are conveniently solved by the primitive \type
+%D {\protected}. To put that another way:
+%D
+%D It's not a bug in this module if it does not appear in \ETEX!
+%D
+%D Now prohibits comments, so % can be used for urls
+
+\pushoverloadmode
+
+\overloaded \protected\def\startpublication
+ {\dosingleempty\dostartpublication}
+
+\overloaded\let\stoppublication\relax
+
+\popoverloadmode
+
+%D This is rather memory hungry; some day i will rewrite this so that we use the
+%D database instead.
+
+%D \macros{doifbibreferencefoundelse}
+%D
+%D Some macros to fetch the information provided by \type {\startpublication}.
+%D
+%D We can consider a faster variant in the bbl file; we can also consider storing
+%D the keys in lua (and then do more in lua) and use calls to fetch the variables.
+
+% hm, we can store at the lua end ...
+
+\protected\def\dostartpublication[#1]%
+ {\begingroup
+ \doifelseassignment{#1}%
+ {\getparameters[\??pb][k=\s!unknown,t=article,n=,s=,a=,y=,o=,u=,#1]}%
+ {\getparameters[\??pb][k=#1,t=article,n=,s=,a=,y=,o=,u=]}%
+ \ctxlua{bibtex.hacks.register(\!!bs\@@pbk\!!es,\!!bs\@@pbs\!!es)}%
+ \catcode\commentasciicode\othercatcode
+ \dodostartpublication}
+
+\protected\def\dodostartpublication#1\stoppublication
+ {\setxvalue{pbd:\@@pbk}##1{\noexpand\ifcase##1\noexpand\or
+ \@@pbk\noexpand\or
+ \@@pba\noexpand\or
+ \@@pby\noexpand\or
+ \@@pbs\noexpand\or
+ \@@pbn\noexpand\or
+ \@@pbt\noexpand\or
+ \@@pbo\noexpand\or
+ \@@pbu\noexpand\or
+ \normalunexpanded{#1}\noexpand\fi}%
+ \endgroup
+ \ignorespaces}
+
+\def\bibgetvark#1{\csname pbd:#1\endcsname\plusone }
+\def\bibgetvara#1{\csname pbd:#1\endcsname\plustwo }
+\def\bibgetvary#1{\csname pbd:#1\endcsname\plusthree}
+\def\bibgetvars#1{\csname pbd:#1\endcsname\plusfour }
+\def\bibgetvarn#1{\csname pbd:#1\endcsname\plusfive }
+\def\bibgetvart#1{\csname pbd:#1\endcsname\plussix }
+\def\bibgetvaro#1{\csname pbd:#1\endcsname\plusseven}
+\def\bibgetvaru#1{\csname pbd:#1\endcsname\pluseight}
+\def\bibgetvard#1{\csname pbd:#1\endcsname\plusnine }
+
+\protected\def\doifelsebibreferencefound#1%
+ {\preloadbiblist
+ \doifelsedefined{pbd:#1}
+ \firstoftwoarguments
+ {\showmessage\m!publications{5}{#1,\the\inputlineno}\secondoftwoarguments}}
+
+\let\doifbibreferencefoundelse\doifelsebibreferencefound
+
+%D \macros{bib@crossref}
+%D
+%D \type {\crossref} is used in database files to point to another entry. Because of
+%D this special situation, it has to be defined separately. Since this command will
+%D not be seen until at \type {\placepublications}, it may force extra runs. The
+%D same is true for \type {\cite} commands inside of publications.
+
+% used in bib self
+
+\protected\def\bib@crossref#1% called via \csname \endcsname
+ {\setvalue{\??pb @crossref}{#1}\ignorespaces}
+
+\protected\def\bibinsertcrossref#1#2#3%
+ {\bibdoifelse\@@pb@crossref{#1\cite[\@@pb@crossref]#2}{#3}}
+
+\let\insertcrossref\gobblethreearguments
+
+\appendtoks\let\insertcrossref\bibinsertcrossref\to\initializebibdefinitions
+
+%D The next macro is needed because the number command of the publist sometimes
+%D needs to fetch something from the current item (like the 'short' key). For this,
+%D the ID of the current item is passed in the implict parameter \type
+%D {\currentpublicationkey}.
+
+\protected\def\doprocessbibtexentry#1{\typesetapublication{#1}}
+
+\protected\def\typesetpubslist
+ {\begingroup
+ \startpacked[\v!blank]%
+ \preloadbiblist
+ % \the\initializebibdefinitions % COMMENTED
+ \edef\currentlist{pubs}%
+ \ctxlua{bibtex.hacks.reset(\number\bibtexoncemode)}%
+ \doifelse{\listparameter\c!criterium}\v!all
+ {\showmessage\m!publications{7}{}%
+ \ctxlua{bibtex.hacks.filterall()}}
+ {\doif{\listparameter\c!criterium}\v!cite
+ {\setuplist[pubs][\c!criterium=\v!here]}%
+ \strc_lists_place_current
+ {pubs}%
+ {\listparameter\c!criterium}%
+ {}%
+ {\listparameter\c!extras}%
+ {\listparameter\c!order}}%
+ \ctxlua{bibtex.hacks.flush("\@@pbsorttype")}%
+ \stoppacked
+ \endgroup}
+
+\newif\ifinpublist
+
+\protected\def\initializepubslist
+ {\def\currentlist{pubs}%
+ \edef\@@pbnumbering{\@@pbnumbering}%
+ \doifelse\@@pbautohang\v!yes
+ {\ifx\@@pbnumbering\v!short
+ \setbox\scratchbox\hbox{\@@pbnumbercommand{\listparameter\c!samplesize}}%
+ \else
+ \doifelse{\listparameter\c!criterium}\v!all
+ {\setbox\scratchbox\hbox{\@@pbnumbercommand{\ctxlua{tex.write{bibtex.hacks.nofregistered()}}}}}
+ {\determinelistcharacteristics[pubs]%
+ \setbox\scratchbox\hbox{\@@pbnumbercommand{\structurelistsize}}}%
+ \fi
+ \edef\publistnumberbox{\hbox to \the\wd\scratchbox}%
+ \expanded{\setuplist[pubs][\c!width=\the\wd\scratchbox,\c!distance=\zeropoint]}%
+ \ifx\@@pbnumbering\v!short
+ \def\@@pblimitednumber##1{\publistnumberbox{\@@pbnumbercommand{\bibgetvars\currentpublicationkey}}}%
+ \orelse\ifx\@@pbnumbering\v!bib
+ \def\@@pblimitednumber##1{\publistnumberbox{\@@pbnumbercommand{\bibgetvarn\currentpublicationkey}}}%
+ \else
+ \def\@@pblimitednumber##1{\publistnumberbox{\@@pbnumbercommand{##1}}}%
+ \fi}
+ {\doifelsenothing{\listparameter\c!width}
+ {\let \publistnumberbox \hbox}
+ {\edef\publistnumberbox{\hbox to \listparameter\c!width}}%
+ \ifx\@@pbnumbering\v!short
+ \def\@@pblimitednumber##1{\publistnumberbox{\@@pbnumbercommand{\bibgetvars\currentpublicationkey}}}%
+ \orelse\ifx\@@pbnumbering\v!bib
+ \def\@@pblimitednumber##1{\publistnumberbox{\@@pbnumbercommand{\bibgetvarn\currentpublicationkey}}}%
+ \else
+ \def\@@pblimitednumber##1{\publistnumberbox{\@@pbnumbercommand{##1}}}%
+ \fi}%
+ \ifx\@@pbnumbering\v!no
+ \setuplist[pubs][\c!numbercommand=,\c!symbol=\v!none,\c!textcommand=\outdented]%
+ \else
+ \setuplist[pubs][\c!numbercommand=\@@pblimitednumber]%
+ \fi
+ \doifelse{\publicationlistparameter\c!maybeyear}{\v!off}{\def\maybeyear##1{}}{\def\maybeyear##1{##1}}%
+ \forgetall}
+
+%D The full list of publications
+
+\protected\def\completepublications
+ {\dosingleempty\docompletepublications}
+
+\protected\def\docompletepublications[#1]%
+ {\begingroup
+ \setuplist[pubs][#1]%
+ \edef\currentbibtexsessiontitle{\publicationlistparameter\c!title}%
+ \ifempty\currentbibtexsessiontitle
+ \normalexpanded{\startnamedsection[\v!chapter][\c!reference=pubs,\c!title={\headtext{pubs}}]}%
+ \else
+ \normalexpanded{\startnamedsection[\v!chapter][\c!reference=pubs,\c!title={\currentbibtexsessiontitle}]}%
+ \fi
+ \dodoplacepublications
+ \stopnamedsection
+ \endgroup}
+
+%D And the portion with the entries only.
+
+\def\bibrefprefix{\number\bibtexblock:}
+
+\protected\def\placepublications
+ {\dosingleempty\doplacepublications}
+
+\protected\def\doplacepublications[#1]%
+ {\begingroup
+ \setuplist[pubs][#1]%
+ \dodoplacepublications
+ \endgroup}
+
+\protected\def\dodoplacepublications
+ {\determinelistcharacteristics[pubs]%
+ \initializepubslist
+ \doifnot{\namedlistparameter{pubs}\c!option}\v!continue
+ {\global\bibtexcounter\zerocount}%
+ \inpublisttrue
+ \typesetpubslist
+ \inpublistfalse
+ \global\advance\bibtexblock\plusone}
+
+%D \subsubject{What's in a publication}
+%D
+%D Watch out: here all means all publications in database, so use text when you want
+%D text only.
+
+\protected\def\typesetapublication#1%
+ {\doifsomething{#1}
+ {\doifelse{\namedlistparameter{pubs}\c!criterium}\v!all
+ {\doplacepublicationindeed{#1}}%
+ {\ctxlua{bibtex.hacks.doifalreadyplaced("#1")}
+ {}
+ {\doplacepublicationindeed{#1}}}%
+ }}
+
+%D For the moment we don't access the data directly but we will do that later when
+%D we get away from storing the data and only deal with references.
+
+% we'll define proper handlers later
+
+\protected\def\doplacepublicationindeed#1%
+ {\doifelsebibreferencefound{#1}
+ {\global\advance\bibtexcounter\plusone
+ \def\currentpublicationkey{#1}%
+ \ctxlua{bibtex.hacks.registerplaced("#1")}%
+ \def \currentlist {pubs}%
+ \edef\currentlistentrynumber {\number\bibtexcounter}%
+ \let \currentlistentrytitle \bibtexpubtext
+ \let \currentlistentrypagenumber\empty
+ \strc_lists_apply_renderingsetup}
+ {}} % invalid
+
+\protected\def\bibtexpubtext
+ {\expanded{\reference[\bibrefprefix\currentpublicationkey]{\number\bibtexcounter}}%
+ \strut\dotypesetapublication\currentpublicationkey\strut}
+
+\protected\def\dotypesetapublication#1%
+ {\bgroup
+ \the\initializebibdefinitions % NEW
+ \def\@@currentalternative{:l:}%
+ \presetbibvariables
+ \let\biblanguage\empty
+ \ignorespaces
+ \bibgetvard{#1}%
+ \removeunwantedspaces
+ \ignorespaces
+ \bibalternative{\bibgetvart{#1}}%
+ \removeunwantedspaces
+ \egroup}
+
+%D An few afterthoughts:
+
+\let\maybeyear\gobbleoneargument
+\let\noopsort \gobbleoneargument
+
+%D This is the result of bibtex's `language' field.
+
+\protected\def\setbiblanguage#1#2{\setvalue{\??pb\s!language#1}{#2}}
+
+\protected\def\lang#1%
+ {\edef\biblanguage{#1}%
+ \ifcsname\??pb\s!language#1\endcsname
+ \language[\getvalue{\??pb\s!language#1}]%
+ \fi
+ \ignorespaces}
+
+%D \subject{Citations}
+
+%D \macros{cite,bibref}
+%D
+%D The indirection with \type {\dobibref} allows \LATEX\ style \type {\cite}
+%D commands with a braced argument (these might appear in included data from the
+%D \type {.bib} file).
+
+\pushoverloadmode
+
+\protected\def\cite
+ {\strictdoifelsenextoptional\dodocite\dobibref}
+
+\popoverloadmode
+
+\protected\def\dobibref#1%
+ {\docite[#1][]}
+
+\protected\def\dodocite[#1]%
+ {\strictdoifelsenextoptional{\docite[#1]}{\docite[#1][]}}
+
+\protected\def\docite[#1][#2]%
+ {\begingroup
+ \doifelsesomething{#2}
+ {\dowhatevercite{#1}{#2}}%
+ {\donumberedcite{#1}}%
+ \endgroup}
+
+\protected\def\dowhatevercite#1#2%
+ {\processcommalist[#2]\docitation
+ \setupinteraction[\c!style=]%
+ \doifelseassignment
+ {#1}%
+ {\getparameters[LO][\c!alternative=,\c!extras=,#1]%
+ \edef\@@currentalternative{\LOalternative}%
+ \ifempty\@@currentalternative
+ \edef\@@currentalternative{\@@citedefault}%
+ \fi
+ \ifempty\LOextras
+ \setupcite[\@@currentalternative][#1]%
+ \else
+ \expandafter\ifrelax\csname \??pv \@@currentalternative\c!right\endcsname
+ % avoids tail recursion
+ \expandafter\let\csname \??pv \@@currentalternative\c!right\endcsname\empty
+ \fi
+ \expandafter\ifrelax\csname LOright\endcsname
+ \edef\LOextras{{\LOextras\bibalternative\c!right}}%
+ \else
+ \edef\LOextras{{\LOextras\LOright}}%
+ \fi
+ \expanded{\setupcite[\@@currentalternative][#1,\c!right=\LOextras]}%
+ \fi}%
+ {\def\@@currentalternative{#1}}%
+ \doifelsevalue{@@pv\@@currentalternative\c!compress}\v!no\bibcitecompressfalse\bibcitecompresstrue
+ \getvalue{bib\@@currentalternative ref}[#2]}
+
+\protected\def\donumberedcite#1%
+ {\processcommalist[#1]\docitation
+ \setupinteraction[\c!style=]%
+ \edef\@@currentalternative{\@@citedefault}%
+ \doifelsevalue{@@pv\@@currentalternative\c!compress}\v!no\bibcitecompressfalse\bibcitecompresstrue
+ \getvalue{bib\@@citedefault ref}[#1]}
+
+%D \macros{nocite}
+
+\pushoverloadmode
+
+\protected\def\nocite[#1]%
+ {\processcommalist[#1]\docitation}
+
+\popoverloadmode
+
+%D \macros{setupcite}
+
+\permanent\tolerant\protected\def\setupcite[#1]#*[#2]%
+ {\ifparameter#2\or
+ \def\dodosetupcite##1{\getparameters[\??pv##1][#2]}%
+ \processcommalist[#1]\dodosetupcite
+ \else % default case
+ \getparameters[\??pv\@@citedefault][#1]%
+ \fi}
+
+%D Low-level stuff
+
+\protected\def\getcitedata#1[#2]#*[#3]#*to#4%
+ {\bgroup
+ \dofetchapublication{#3}%
+ \doifelsedefined{\??pb @bib#2}%
+ {\xdef#4{\getvalue{\??pb @bib#2}}}%
+ {\xdef#4{\getvalue{\??pb @#2}}}%
+ \egroup}
+
+\protected\def\dofetchapublication#1%
+ {\def\currentpublicationkey{#1}%
+ \presetbibvariables
+ \ignorespaces\bibgetvard{#1}}
+
+\protected\def\docitation#1%
+ {\iftrialtypesetting \else
+ \expanded{\writedatatolist[pubs][bibref=#1]}%
+ \fi}
+
+\let\addthisref\gobbleoneargument % keep this for compatibility
+
+%D \macros{ixbibauthoryear,thebibauthors,thebibyears}
+%D
+%D If compression of \type {\cite}'s argument expansion is on, the macros that deal
+%D with authors and years call this internal command to do the actual typesetting.
+%D
+%D Two entries with same author but with different years may be condensed into
+%D ``Author (year1,year2)''. This is about the only optimization that makes sense
+%D for the (author,year) style of citations (years within one author have to be
+%D unique anyway so no need to test for that, and ``Author1, Author2 (year)''
+%D creates more confusion than it does good).
+%D
+%D In the code below, the macro \type {\thebibauthors} holds the names of the
+%D alternative author info fields for the current list. This is a commalist, and
+%D \type {\thebibyears} holds the (collection of) year(s) that go with this author
+%D (possibly as a nested commalist).
+%D
+%D There had better be an author for all cases, but there does not have to be year
+%D info always. \type{\thebibyears} is pre|-|initialized because this makes the
+%D insertion macros simpler.
+%D
+%D In normal \TEX, of course there are expansion problems again.
+
+%D Delegate this to \LUA.
+
+% \let\ixlastcommand \relax
+% \let\ixsecondcomman \relax
+% \let\ixfirstcommand \relax
+% \let\thebibauthors \empty
+% \let\thebibyears \empty
+% \let\authorcount \!!zerocount
+
+\let\currentbibauthor\empty
+
+\protected\def\ixbibauthoryear#1#2#3#4%
+ {\bgroup
+ \gdef\ixlastcommand {#4}%
+ \gdef\ixsecondcommand{#3}%
+ \gdef\ixfirstcommand {#2}%
+ \glet\thebibauthors \empty
+ \glet\thebibyears \empty
+ \getcommalistsize[#1]%
+ \ifbibcitecompress
+ \dorecurse\commalistsize{\xdef\thebibyears{\thebibyears,}}%
+ \processcommalist[#1]\docompressbibauthoryear
+ \else
+ \processcommalist[#1]\donormalbibauthoryear
+ \fi
+ \egroup
+ \dobibauthoryear}
+
+%D \macros{dodobibauthoryear}
+%D
+%D This macro only has to make sure that the lists \type {\thebibauthors} and \type
+%D {\thebibyears} are printed.
+
+\protected\def\dobibauthoryear
+ {\scratchcounter\zerocount
+ \getcommacommandsize[\thebibauthors]%
+ \edef\authorcount{\commalistsize}%
+ \expandafter\processcommalist\expandafter[\thebibauthors]\dodobibauthoryear}
+
+\protected\def\dodobibauthoryear#1%
+ {\advance\scratchcounter\plusone
+ \edef\wantednumber{\the\scratchcounter}%
+ \getfromcommacommand[\thebibyears][\wantednumber]%
+ \expandafter\def\expandafter\currentbibyear\expandafter{\commalistelement}%
+ \setcurrentbibauthor{#1}%
+ \ifnum\scratchcounter=\plusone
+ \ixfirstcommand
+ \orelse\ifnum \scratchcounter=\authorcount\relax
+ \ixlastcommand
+ \else
+ \ixsecondcommand
+ \fi}
+
+\protected\def\setcurrentbibauthor#1% sensitive for empty entries but I don't want to touch this
+ {\getcommacommandsize[#1]%
+ \ifcase\commalistsize
+ % anonymous?
+ \let\currentbibauthor\empty
+ \or
+ \def\currentbibauthor{#1}%
+ \or
+ \expanded{\docurrentbibauthor#1}%
+ \else
+ \handlemultiplebibauthors{\commalistsize}{#1}%
+ \fi}
+
+\newcount\citescratchcounter
+
+\protected\def\handlemultiplebibauthors#1#2%
+ {\citescratchcounter\zerocount
+ \let\currentbibauthor\empty
+ \protected\def\bibprocessauthoritem##1%
+ {\advance\citescratchcounter\plusone
+ \ifnum \citescratchcounter=#1\relax
+ \edef\currentbibauthor{\currentbibauthor##1}%
+ \orelse\ifnum\numexpr\citescratchcounter+\plusone\relax=#1\relax
+ \edef\currentbibauthor{\currentbibauthor##1\bibalternative{andtext}}%
+ \else
+ \edef\currentbibauthor{\currentbibauthor##1\bibalternative{namesep}}%
+ \fi}%
+ \processcommalist[#2]\bibprocessauthoritem}
+
+\setupcite
+ [author,authoryear,authoryears]
+ [\c!namesep={, }]
+
+%D This discovery of authoretallimit is not the best one,
+%D but it will do for now.
+
+\protected\def\docurrentbibauthor#1,#2%
+ {\doifelseempty{#2}
+ {\def\currentbibauthor{#1\bibalternative{otherstext}}}
+ {\expandafter\ifrelax\csname\??pv\@@currentalternative authoretallimit\endcsname
+ \edef\currentbibauthor{#1\bibalternative{andtext}#2}%
+ \else
+ \edef\currentbibauthor{#1%
+ \ifcase0\bibalternative{authoretallimit}\relax\or
+ \bibalternative{otherstext}\else\bibalternative{andtext}#2\fi}%
+ \fi}}
+
+%D This is not the one Hans made for me, because I need a global
+%D edef, and the \type {\robustdoifinsetelse} doesn't listen to
+%D \type {\doglobal}.
+
+\pushoverloadmode
+
+ \protected\def\robustaddtocommalist#1#2% {item} \cs
+ {\robustdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\xdef#2{\ifempty#2\else#2,\fi#1}}}
+
+\popoverloadmode
+
+%D \macros{donormalbibauthoryear}
+%D
+%D Now we get to the macros that fill the two lists. The \quote {simple} one really
+%D is quite simple.
+
+\protected\def\donormalbibauthoryear#1%
+ {\def\myauthor{Xxxxxxxxxx}%
+ \def\myyear{0000}%
+ \doifelsebibreferencefound{#1}
+ {\def\myauthor{{\bibgetvara{#1}}}%
+ \def\myyear {\bibgetvary{#1}}}%
+ {}%
+ \expandafter\doglobal\expandafter\appendtocommalist\expandafter{\myauthor}\thebibauthors
+ \expandafter\doglobal\expandafter\appendtocommalist\expandafter{\myyear }\thebibyears}
+
+%D \macros{docompressbibauthoryear}
+%D
+%D So much for the easy parts. Nothing at all will be done if the reference is not
+%D found or the reference does not contain author data. No questions marks o.s.s.
+%D (to be fixed later).
+
+\protected\def\docompressbibauthoryear#1%
+ {\def\myauthor{Xxxxxxxxxx}%
+ \def\myyear {0000}%
+ \doifelsebibreferencefound{#1}
+ {\xdef\myauthor{\bibgetvara{#1}}%
+ \xdef\myyear {\bibgetvary{#1}}}
+ {}%
+ \ifempty\myauthor\else
+ \checkifmyauthoralreadyexists
+ \findmatchingyear
+ \fi}
+
+%D Two temporary counters. One of these two can possibly be replaced by \type
+%D {\scratchcounter}.
+
+\newcount\bibitemcounter
+\newcount\bibitemwanted
+
+%D The first portion is simple enough: if this is the very first author it is quite
+%D straightforward to add it. \type {\bibitemcounter} and \type {\bibitemwanted} are
+%D needed later to insert the year information in the correct item of \type
+%D {\thebibyears}.
+
+\protected\def\checkifmyauthoralreadyexists
+ {\doifelseemptyvalue{thebibauthors}
+ {\global\bibitemwanted \plusone
+ \global\bibitemcounter\plusone
+ \xdef\thebibauthors{{\myauthor}}}
+ {% the next weirdness is because according to \getcommalistsize,
+ % the length of \type{[{{},{}}]} is 2.
+ \expandafter\getcommalistsize\expandafter[\thebibauthors,]%
+ \global\bibitemcounter\numexpr\commalistsize+\minusone\relax
+ \global\bibitemwanted \zerocount
+ \processcommacommand[\thebibauthors]\docomparemyauthor}}
+
+%D The outer \type {\ifnum} accomplishes the addition of a new author to \type
+%D {\thebibauthors}. The messing about with the two counters is again to make sure
+%D that \type {\thebibyears} will be updated correctly.If the author {\it was}
+%D found, the counters will stay at their present values and everything will be
+%D setup properly to insert the year info.
+
+\protected\def\docomparemyauthor#1%
+ {\global\advance\bibitemwanted \plusone
+ \def\mytempc{#1}%
+ \ifx\mytempc\myauthor
+ \quitcommalist
+ \orelse\ifnum\bibitemwanted=\bibitemcounter\relax
+ \global\advance\bibitemwanted \plusone
+ \global\bibitemcounter\bibitemwanted\relax
+ \expandafter\doglobal\expandafter\robustaddtocommalist\expandafter{{\myauthor}}\thebibauthors
+ \fi}
+
+%D This macro should be clear now.
+
+\protected\def\findmatchingyear
+ {\edef\wantednumber{\the\bibitemwanted}%
+ \getfromcommacommand[\thebibyears][\wantednumber]%
+ \ifempty\commalistelement
+ \edef\myyear{{\myyear}}%
+ \else
+ \edef\myyear{{\commalistelement,\myyear}}%
+ \fi
+ \edef\newcommalistelement{\myyear}%
+ \doglobal\replaceincommalist \thebibyears \wantednumber}
+
+%D \macros{gotobiblink,inbiblink,atbiblink}
+%D
+%D The final task is looping over that list until a match is found.
+
+%D Beware, we can have cites without reference match.
+
+\protected\def\gotobiblink#1[#2]{\doifelsereferencefound{\bibrefprefix#2}{\goto{#1}[\bibrefprefix#2]}{#1}}
+\protected\def\atbiblink [#1]{\doifelsereferencefound{\bibrefprefix#1}{\at [\bibrefprefix#1]}{#1}}
+\protected\def\inbiblink [#1]{\doifelsereferencefound{\bibrefprefix#1}{\expanded{\goto{\currentreferencetext}}[\bibrefprefix#1]}{#1}}
+
+%D \macros{bibauthoryearref,bibauthoryearsref,bibauthorref,bibyearref}
+%D
+%D Now that all the hard work has been done, these are simple. \type
+%D {\ixbibauthoryearref} stores the data in the macros \type {\currentbibauthor} and
+%D \type {\currentbibyear}.
+
+\protected\def\doifelsebibinteraction
+ {\iflocation
+ \edef\test{\bibalternative\c!interaction}%
+ \ifx\test\v!stop
+ \doubleexpandafter\secondoftwoarguments
+ \else
+ \doubleexpandafter\firstoftwoarguments
+ \fi
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifbibinteractionelse\doifelsebibinteraction
+
+\protected\def\bibmaybeinteractive#1#2%
+ {\doifelsebibinteraction{\gotobiblink{#2}[#1]}{#2}}
+
+\protected\def\bibauthoryearref[#1]%
+ {\ixbibauthoryear{#1}%
+ {\bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween
+ \bibalternative\v!left{\currentbibyear}\bibalternative\v!right}}
+ {\bibalternative\c!pubsep
+ \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween
+ \bibalternative\v!left {\currentbibyear}\bibalternative\v!right}}
+ {\bibalternative\c!lastpubsep
+ \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween
+ \bibalternative\v!left {\currentbibyear}\bibalternative\v!right}}}
+
+\protected\def\bibauthoryearsref[#1]%
+ {\bibalternative\v!left
+ \ixbibauthoryear{#1}
+ {\bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween{\currentbibyear}}}
+ {\bibalternative\c!pubsep
+ \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween{\currentbibyear}}}
+ {\bibalternative\c!lastpubsep
+ \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween{\currentbibyear}}}%
+ \bibalternative\v!right}
+
+\protected\def\bibauthorref[#1]%
+ {\bibalternative\v!left
+ \ixbibauthoryear{#1}%
+ {\bibmaybeinteractive{#1}{{\currentbibauthor}}}
+ {\bibalternative\c!pubsep \bibmaybeinteractive{#1}{{\currentbibauthor}}}
+ {\bibalternative\c!lastpubsep\bibmaybeinteractive{#1}{{\currentbibauthor}}}%
+ \bibalternative\v!right}
+
+\protected\def\bibyearref[#1]%
+ {\bibalternative\v!left
+ \ixbibauthoryear{#1}%
+ {\bibmaybeinteractive{#1}{{\currentbibyear}}}
+ {\bibalternative\c!pubsep \bibmaybeinteractive{#1}{{\currentbibyear}}}
+ {\bibalternative\c!lastpubsep\bibmaybeinteractive{#1}{{\currentbibyear}}}%
+ \bibalternative\v!right}
+
+%D \macros{bibshortref,bibkeyref,bibpageref,bibtyperef,bibserialref}
+%D
+%D There is hardly any point in trying to compress these. The only thing that needs
+%D to be done is making sure that the separations are inserted correctly. And that
+%D is what \type {\bibinsertrefsep} does.
+
+\newconditional\firstbibrefsep
+
+\protected\def\bibresetrefsep
+ {\settrue\firstbibrefsep}
+
+\protected\def\bibinsertrefsep
+ {\ifconditional\firstbibrefsep
+ \setfalse\firstbibrefsep
+ \else
+ \bibalternative\c!pubsep
+ \fi}
+
+\protected\def\bibshortref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibshortref
+ \bibalternative\v!right}
+
+\protected\def\dobibshortref#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\gotobiblink{\bibgetvars{#1}}[#1]}
+ {}}
+
+\protected\def\bibserialref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibserialref
+ \bibalternative\v!right}
+
+\protected\def\dobibserialref#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\gotobiblink{\bibgetvarn{#1}}[#1]}
+ {}}
+
+\protected\def\bibkeyref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibkeyref
+ \bibalternative\v!right}
+
+\protected\def\dobibkeyref#1%
+ {\bibinsertrefsep
+ \gotobiblink{#1}[#1]}
+
+\protected\def\bibgotoDOI#1#2%
+ {\doifelsebibinteraction
+ {\useURL[bibfooDoi#1][#2]%
+ \useURL[bibfoo#1][http://dx.doi.org/#2]%
+ \goto{\url[bibfooDoi#1]}[url(bibfoo#1)]}
+ {\hyphenatedurl{#2}}}
+
+\protected\def\bibdoiref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibdoiref
+ \bibalternative\v!right}
+
+\protected\def\dobibdoiref#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\expanded{\bibgotoDOI{#1}{\bibgetvaro{#1}}}}
+ {}}
+
+\protected\def\biburlref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobiburlref
+ \bibalternative\v!right}
+
+\protected\def\bibgotoURL#1#2%
+ {\doifelsebibinteraction
+ {\useURL[bibfoo#1][#2]\goto{\url[bibfoo#1]}[url(bibfoo#1)]}
+ {\hyphenatedurl{#2}}}
+
+\protected\def\dobiburlref#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\expanded{\bibgotoURL{#1}{\bibgetvaru{#1}}}}
+ {}}
+
+\protected\def\bibtyperef[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibtyperef
+ \bibalternative\v!right}
+
+\protected\def\dobibtyperef#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\gotobiblink{\bibgetvart{#1}}[#1]}
+ {}}
+
+\protected\def\bibpageref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibpageref
+ \bibalternative\v!right}
+
+\protected\def\dobibpageref#1%
+ {\bibinsertrefsep
+ \doifelsebibinteraction
+ {\atbiblink[#1]}
+ {{\referencingfalse\at[#1]}}}
+
+\protected\def\bibdataref[#1]%
+ {\bibalternative\v!left
+ \bibresetrefsep\processcommalist[#1]\dobibdata
+ \bibalternative\v!right}
+
+\protected\def\dobibdata#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\dotypesetapublication{#1}}
+ {}}
+
+\let\bibnoneref\nocite
+
+%D \macros{bibnumref}
+
+\protected\def\bibnumref[#1]%
+ {\begingroup
+ \bibalternative\v!left
+ \penalty\plustenthousand
+ \ctxlua{bibtex.hacks.resolve("","\number\bibtexblock","#1")}%
+ \bibalternative\v!right
+ \endgroup}
+
+\protected\def\dowithbibtexnumrefconnector#1#2%
+ {\ifnum#1>\plusone
+ \ifnum#2>\plusone
+ \ifnum#2=#1\relax
+ \bibalternative{lastpubsep}%
+ \else
+ \bibalternative{pubsep}%
+ \fi
+ \fi
+ \fi}
+
+\protected\def\dowithbibtexnumref#1#2#3#4#5% n, i, prefix block ref
+ {\dowithbibtexnumrefconnector{#1}{#2}%
+ \def\bibrefprefix{#4:}%
+ \inbiblink[#5]}
+
+\protected\def\dowithbibtexnumrefrange#1#2#3#4#5#6#7% n, i, prefix block ref
+ {\dowithbibtexnumrefconnector{#1}{#2}%
+ \def\bibrefprefix{#4:}%
+ \inbiblink[#5]%
+ \endash
+ \def\bibrefprefix{#6:}%
+ \inbiblink[#7]}
+
+%D By request from Sanjoy. This makes it easier to implement \type {\citeasnoun}.
+
+\protected\def\bibauthornumref[#1]%
+ {\getcommalistsize[#1]%
+ \global\bibitemcounter\commalistsize
+ \bibresetrefsep
+ \processcommalist[#1]\dobibauthornumref}
+
+\protected\def\dobibauthornumref#1%
+ {\bibinsertrefsep
+ \doifelsebibreferencefound{#1}
+ {\begingroup
+ \cite[\c!left=,\c!right=,\c!alternative=\v!author][#1]%
+ \bibalternative\c!inbetween
+ \cite[num][#1]%
+ \endgroup}
+ {}}
+
+%D And some defaults are loaded from bibl-apa:
+
+\setuppublications
+ [\c!monthconversion=,
+ \c!alternative=apa,
+ \c!method=\v!global,
+ %\c!criterium=\v!previous,
+ \c!criterium=\v!cite, % mojca wants this so bother her, not me
+ \c!refcommand=num,
+ \c!numbercommand=\bibleftnumber]
+
+\protected\def\preloadbiblist
+ {\glet\preloadbiblist\relax
+ \dousepublications\jobname}
+
+% \appendtoks \preloadbiblist \to \everysetuppublications
+% \appendtoks \preloadbiblist \to \everystarttext
+
+\let\ifbibinteractionelse\doifbibinteractionelse
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/blob-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/blob-ini.lmt
new file mode 100644
index 00000000000..42a18fddde0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/blob-ini.lmt
@@ -0,0 +1,210 @@
+if not modules then modules = { } end modules ['blob-ini'] = {
+ version = 1.001,
+ comment = "companion to blob-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This module is just a playground. Occasionally we need to typeset at the lua and and
+-- this is one method. In principle we can construct pages this way too which sometimes
+-- makes sense in dumb cases. Actually, if one only needs this, one does not really need
+-- tex, okay maybe the parbuilder but that one can be simplified as well then.
+
+-- set fonts, attributes
+-- rest already done in packers etc
+-- add local par whatsit (or wait till cleaned up)
+-- collapse or new pars
+-- interline spacing etc
+
+-- blob.char
+-- blob.line
+-- blob.paragraph
+-- blob.page
+
+local type, tostring = type, tostring
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+
+local report_blobs = logs.reporter("blobs")
+
+local flush_node_list = node.flush_list
+local hpack_node_list = node.hpack
+----- vpack_node_list = node.vpack
+local write_node = node.write
+
+local typesetters = nodes.typesetters
+local tonodes = typesetters.tonodes
+local tohpack = typesetters.tohpack
+local tovpack = typesetters.tovpack
+
+local implement = interfaces.implement
+
+-- provide copies here (nicer for manuals)
+
+blobs = blobs or { }
+local blobs = blobs
+
+blobs.tonodes = tonodes
+blobs.tohpack = tohpack
+blobs.tovpack = tovpack
+
+-- end of helpers
+
+local newline = lpeg.patterns.newline
+local space = lpeg.patterns.spacer
+local newpar = (space^0*newline*space^0)^2
+
+local ctxtextcapture = lpeg.Ct ( ( space^0 * ( newpar + lpeg.Cs(((space^1/" " + 1)-newpar)^1) ) )^0)
+
+function blobs.new()
+ return {
+ list = { },
+ }
+end
+
+function blobs.dispose(t)
+ local list = t.list
+ for i=1,#list do
+ local li = list[i]
+ local pack = li.pack
+ if pack then
+ flush_node_list(pack)
+ li.pack = nil
+ end
+ end
+end
+
+function blobs.append(t,str) -- compare concat and link
+ local typ = type(str)
+ local dummy = nil
+ if typ == "number" then
+ str = tostring(str)
+ typ = "string"
+ end
+ if typ == "string" then
+ local pars = lpegmatch(ctxtextcapture,str)
+ local list = t.list
+ for p=1,#pars do
+ local head, tail = tonodes(pars[p],nil,nil)
+ list[#list+1] = { head = head, tail = tail }
+ end
+ end
+end
+
+function blobs.pack(t,how)
+ local list = t.list
+ for i=1,#list do
+ local pack = list[i].pack
+ if pack then
+ flush_node_list(node.pack)
+ end
+ if how == "vertical" then
+ -- we need to prepend a local par node
+ -- list[i].pack = vpack_node_list(list[i].head,"exactly")
+ report_blobs("vpack not yet supported")
+ else
+ list[i].pack = hpack_node_list(list[i].head,"exactly")
+ end
+ end
+end
+
+function blobs.write(t)
+ local list = t.list
+ for i=1,#list do
+ local li = list[i]
+ local pack = li.pack
+ if pack then
+ write_node(pack)
+ flush_node_list(pack)
+ li.pack = nil
+ end
+ end
+end
+
+function blobs.dimensions(t)
+ local list = t.list
+ local first = list and list[1]
+ if first then
+ local pack = first.pack
+ return pack.width, pack.height, pack.depth
+ else
+ return 0, 0, 0
+ end
+end
+
+-- blob.char
+-- blob.line: head, tail
+-- blob.paragraph
+-- blob.page
+
+-- local lineblob = {
+-- type = "line",
+-- head = false,
+-- tail = false,
+-- pack = false,
+-- properties = { },
+-- end
+
+-- local parblob = {
+-- type = "line",
+-- head = false,
+-- tail = false,
+-- pack = false,
+-- properties = { },
+-- end
+
+-- for the moment here:
+
+local function strwd(str)
+ local l = tohpack(str)
+ local w = l.width
+ flush_node_list(l)
+ return w
+end
+
+local function strht(str)
+ local l = tohpack(str)
+ local h = l.height
+ flush_node_list(l)
+ return h
+end
+
+local function strdp(str)
+ local l = tohpack(str)
+ local d = l.depth
+ flush_node_list(l)
+ return d
+end
+
+local function strhd(str)
+ local l = tohpack(str)
+ local s = l.height + l.depth
+ flush_node_list(l)
+ return s
+end
+
+blobs.strwd = strwd
+blobs.strht = strht
+blobs.strdp = strdp
+blobs.strhd = strhd
+
+-- upgraded
+
+local dimension_code = tokens.values.dimension
+local scan_hbox = tokens.scanners.hbox
+
+local function action(field,what)
+ local l = scan_hbox()
+ local d = l[field]
+ flush_node_list(l)
+ if what == "value" then
+ return dimension_code, d
+ else
+ context("%p",d)
+ end
+end
+
+implement { name = "widthofstring", public = true, usage = "value", actions = function(w) return action("width", w) end }
+implement { name = "heightofstring", public = true, usage = "value", actions = function(w) return action("height",w) end }
+implement { name = "depthofstring", public = true, usage = "value", actions = function(w) return action("depth", w) end }
+implement { name = "totalofstring", public = true, usage = "value", actions = function(w) return action("total", w) end }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/blob-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/blob-ini.mkxl
new file mode 100644
index 00000000000..1dcd5597254
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/blob-ini.mkxl
@@ -0,0 +1,56 @@
+%D \module
+%D [ file=blob-ini,
+%D version=2010.04.06,
+%D title=\CONTEXT\ \LUA\ Typesetting,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Typesetting / Initialization}
+
+%D This is a prelude to typesetting at the \LUA\ end. The code
+%D is already quite old but will only get nice when we are further
+%D down the road (close to version 1.00 of \LUATEX). Typesetting in
+%D pure \LUA\ sometimes makes sense.
+
+\registerctxluafile{node-typ}{} % experimental
+\registerctxluafile{blob-ini}{autosuffix}
+
+\unprotect
+
+% \widthofstring % defined in lua
+% \heightofstring % defined in lua
+% \depthofstring % defined in lua
+% \heightanddepthofstring % defined in lua
+
+\permanent\def\wdofstring#1{\dimexpr\widthofstring {#1}\relax} % assured an dimen
+\permanent\def\htofstring#1{\dimexpr\heightofstring{#1}\relax} % assured an dimen
+\permanent\def\dpofstring#1{\dimexpr\depthofstring {#1}\relax} % assured an dimen
+\permanent\def\hdofstring#1{\dimexpr\totalofstring {#1}\relax} % assured an dimen
+
+\aliased\let\heightanddepthofstring\totalofstring
+\aliased\let\htdpofstring \hdofstring
+
+% \let\hd\htdp % if yes then move this
+
+% This one takes anything that can be typeset
+
+\permanent\protected\def\setwidthof#1\to#2{\edef#2{\widthofstring{#1}}}
+
+\protect \endinput
+
+% \starttext
+%
+% \startluacode
+% local b = blobs.new()
+% blobs.append(b,"Hello world.\n Here we are.\n\n And Again!")
+% blobs.pack(b)
+% blobs.write(b)
+% \stopluacode
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/buff-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/buff-ini.lmt
new file mode 100644
index 00000000000..41e8a50785e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/buff-ini.lmt
@@ -0,0 +1,900 @@
+if not modules then modules = { } end modules ['buff-ini'] = {
+ version = 1.001,
+ comment = "companion to buff-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local concat = table.concat
+local type, next, load = type, next, load
+local sub, format = string.sub, string.format
+local splitlines, validstring, replacenewlines = string.splitlines, string.valid, string.replacenewlines
+local P, Cs, patterns, lpegmatch = lpeg.P, lpeg.Cs, lpeg.patterns, lpeg.match
+local utfchar = utf.char
+local nameonly = file.nameonly
+local totable = string.totable
+local md5hex = md5.hex
+local isfile = lfs.isfile
+local savedata = io.savedata
+
+local trace_run = false trackers.register("buffers.run", function(v) trace_run = v end)
+local trace_grab = false trackers.register("buffers.grab", function(v) trace_grab = v end)
+local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end)
+
+local report_buffers = logs.reporter("buffers","usage")
+local report_typeset = logs.reporter("buffers","typeset")
+----- report_grabbing = logs.reporter("buffers","grabbing")
+
+local context = context
+local commands = commands
+
+local implement = interfaces.implement
+
+local scanners = tokens.scanners
+local scanstring = scanners.string
+local scancsname = scanners.csname
+local scaninteger = scanners.integer
+local scanboolean = scanners.boolean
+local scancode = scanners.code
+local scantokencode = scanners.tokencode
+----- scantoken = scanners.token
+
+local getters = tokens.getters
+local gettoken = getters.token
+
+local getcommand = token.get_command
+----- getcsname = token.get_csname
+local getnextchar = token.scan_next_char or token.get_next_char
+
+local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+local formatters = string.formatters
+local addsuffix = file.addsuffix
+local replacesuffix = file.replacesuffix
+
+local registertempfile = luatex.registertempfile
+
+local v_yes = variables.yes
+
+local eol = patterns.eol
+local space = patterns.space
+local whitespace = patterns.whitespace
+local blackspace = whitespace - eol
+local whatever = (1-eol)^1 * eol^0
+local emptyline = space^0 * eol
+
+local catcodenumbers = catcodes.numbers
+
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local txtcatcodes = catcodenumbers.txtcatcodes
+
+local setdata = job.datasets.setdata
+local getdata = job.datasets.getdata
+
+local ctx_viafile = context.viafile
+local ctx_getbuffer = context.getbuffer
+local ctx_pushcatcodetable = context.pushcatcodetable
+local ctx_popcatcodetable = context.popcatcodetable
+local ctx_setcatcodetable = context.setcatcodetable
+local ctx_printlines = context.printlines
+
+buffers = buffers or { }
+local buffers = buffers
+
+local cache = { }
+
+local function erase(name)
+ cache[name] = nil
+end
+
+local function assign(name,str,catcodes)
+ cache[name] = {
+ data = str,
+ catcodes = catcodes,
+ typeset = false,
+ }
+end
+
+local function combine(name,str,prepend)
+ local buffer = cache[name]
+ if buffer then
+ buffer.data = prepend and (str .. buffer.data) or (buffer.data .. str)
+ buffer.typeset = false
+ else
+ cache[name] = {
+ data = str,
+ typeset = false,
+ }
+ end
+end
+
+local function prepend(name,str)
+ combine(name,str,true)
+end
+
+local function append(name,str)
+ combine(name,str)
+end
+
+local function exists(name)
+ return cache[name]
+end
+
+local function getcontent(name)
+ local buffer = name and cache[name]
+ return buffer and buffer.data or ""
+end
+
+local function getlines(name)
+ local buffer = name and cache[name]
+ return buffer and splitlines(buffer.data)
+end
+
+local function getnames(name)
+ if type(name) == "string" then
+ return settings_to_array(name)
+ else
+ return name
+ end
+end
+
+local function istypeset(name)
+ local names = getnames(name)
+ if #names == 0 then
+ return false
+ end
+ for i=1,#names do
+ local c = cache[names[i]]
+ if c and not c.typeset then
+ return false
+ end
+ end
+ return true
+end
+
+local function markastypeset(name)
+ local names = getnames(name)
+ for i=1,#names do
+ local c = cache[names[i]]
+ if c then
+ c.typeset = true
+ end
+ end
+end
+
+local function collectcontent(name,separator) -- no print
+ local names = getnames(name)
+ local nnames = #names
+ if nnames == 0 then
+ return getcontent("") -- default buffer
+ elseif nnames == 1 then
+ return getcontent(names[1])
+ else
+ local t = { }
+ local n = 0
+ for i=1,nnames do
+ local c = getcontent(names[i])
+ if c ~= "" then
+ n = n + 1
+ t[n] = c
+ end
+ end
+ -- the default separator was \r, then \n and is now os.newline because buffers
+ -- can be loaded in other applications
+ return concat(t,separator or os.newline)
+ end
+end
+
+local function loadcontent(name) -- no print
+ local content = collectcontent(name,"\n") -- tex likes \n hm, elsewhere \r
+ local ok, err = load(content)
+ if ok then
+ return ok()
+ else
+ report_buffers("invalid lua code in buffer %a: %s",name,err or "unknown error")
+ end
+end
+
+buffers.raw = getcontent
+buffers.erase = erase
+buffers.assign = assign
+buffers.prepend = prepend
+buffers.append = append
+buffers.exists = exists
+buffers.getcontent = getcontent
+buffers.getlines = getlines
+buffers.collectcontent = collectcontent
+buffers.loadcontent = loadcontent
+
+-- the context interface
+
+implement {
+ name = "assignbuffer",
+ actions = assign,
+ arguments = { "string", "string", "integer" }
+}
+
+implement {
+ name = "erasebuffer",
+ actions = erase,
+ arguments = "string"
+}
+
+-- local anything = patterns.anything
+-- local alwaysmatched = patterns.alwaysmatched
+-- local utf8character = patterns.utf8character
+--
+-- local function countnesting(b,e)
+-- local n
+-- local g = P(b) / function() n = n + 1 end
+-- + P(e) / function() n = n - 1 end
+-- -- + anything
+-- + utf8character
+-- local p = alwaysmatched / function() n = 0 end
+-- * g^0
+-- * alwaysmatched / function() return n end
+-- return p
+-- end
+
+local counters = { }
+local nesting = 0
+local autoundent = true
+local continue = false
+
+-- Beware: the first character of bufferdata has to be discarded as it's there to
+-- prevent gobbling of newlines in the case of nested buffers. The last one is
+-- a newlinechar and is removed too.
+--
+-- An \n is unlikely to show up as \r is the endlinechar but \n is more generic
+-- for us.
+
+-- This fits the way we fetch verbatim: the indentation before the sentinel
+-- determines the stripping.
+
+-- str = [[
+-- test test test test test test test
+-- test test test test test test test
+-- test test test test test test test
+--
+-- test test test test test test test
+-- test test test test test test test
+-- test test test test test test test
+-- ]]
+
+-- local function undent(str)
+-- local margin = match(str,"[\n\r]( +)[\n\r]*$") or ""
+-- local indent = #margin
+-- if indent > 0 then
+-- local lines = splitlines(str)
+-- local ok = true
+-- local pattern = "^" .. margin
+-- for i=1,#lines do
+-- local l = lines[i]
+-- if find(l,pattern) then
+-- lines[i] = sub(l,indent+1)
+-- else
+-- ok = false
+-- break
+-- end
+-- end
+-- if ok then
+-- return concat(lines,"\n")
+-- end
+-- end
+-- return str
+-- end
+
+-- how about tabs
+
+local strippers = { }
+local nofspaces = 0
+
+local normalline = space^0 / function(s) local n = #s if n < nofspaces then nofspaces = n end end
+ * whatever
+
+local getmargin = (emptyline + normalline)^1
+
+local function undent(str) -- new version, needs testing: todo: not always needed, like in xtables
+ nofspaces = #str
+ local margin = lpegmatch(getmargin,str)
+ if nofspaces == #str or nofspaces == 0 then
+ return str
+ end
+ local stripper = strippers[nofspaces]
+ if not stripper then
+ stripper = Cs(((space^-nofspaces)/"" * whatever + emptyline)^1)
+ strippers[nofspaces] = stripper
+ end
+ return lpegmatch(stripper,str) or str
+end
+
+buffers.undent = undent
+
+-- function commands.grabbuffer(name,begintag,endtag,bufferdata,catcodes,doundent) -- maybe move \\ to call
+-- local dn = getcontent(name)
+-- if dn == "" then
+-- nesting = 0
+-- continue = false
+-- end
+-- if trace_grab then
+-- if #bufferdata > 30 then
+-- report_grabbing("%s => |%s..%s|",name,sub(bufferdata,1,10),sub(bufferdata,-10,#bufferdata))
+-- else
+-- report_grabbing("%s => |%s|",name,bufferdata)
+-- end
+-- end
+-- local counter = counters[begintag]
+-- if not counter then
+-- counter = countnesting(begintag,endtag)
+-- counters[begintag] = counter
+-- end
+-- nesting = nesting + lpegmatch(counter,bufferdata)
+-- local more = nesting > 0
+-- if more then
+-- dn = dn .. sub(bufferdata,2,-1) .. endtag
+-- nesting = nesting - 1
+-- continue = true
+-- else
+-- if continue then
+-- dn = dn .. sub(bufferdata,2,-2) -- no \r, \n is more generic
+-- elseif dn == "" then
+-- dn = sub(bufferdata,2,-2)
+-- else
+-- dn = dn .. "\n" .. sub(bufferdata,2,-2) -- no \r, \n is more generic
+-- end
+-- local last = sub(dn,-1)
+-- if last == "\n" or last == "\r" then -- \n is unlikely as \r is the endlinechar
+-- dn = sub(dn,1,-2)
+-- end
+-- if doundent or (autoundent and doundent == nil) then
+-- dn = undent(dn)
+-- end
+-- end
+-- assign(name,dn,catcodes)
+-- commands.doifelse(more)
+-- end
+
+local split = table.setmetatableindex(function(t,k)
+ local v = totable(k)
+ t[k] = v
+ return v
+end)
+
+local tochar = {
+ [ 0] = "\\",
+ [ 1] = "{",
+ [ 2] = "}",
+ [ 3] = "$",
+ [ 4] = "&",
+ [ 5] = "\n",
+ [ 6] = "#",
+ [ 7] = "^",
+ [ 8] = "_",
+ [10] = " ",
+ [14] = "%",
+}
+
+local experiment = false
+local experiment = scantokencode and true
+
+local function pickup(start,stop)
+ local stoplist = split[stop] -- totable(stop)
+ local stoplength = #stoplist
+ local stoplast = stoplist[stoplength]
+ local startlist = split[start] -- totable(start)
+ local startlength = #startlist
+ local startlast = startlist[startlength]
+ local list = { }
+ local size = 0
+ local depth = 0
+-- local done = 32
+ local scancode = experiment and scantokencode or scancode
+ while true do -- or use depth
+ local char = scancode()
+ if char then
+-- if char < done then
+-- -- we skip leading control characters so that we can use them to
+-- -- obey spaces (a dirty trick)
+-- else
+-- done = 0
+ char = utfchar(char)
+ size = size + 1
+ list[size] = char
+ if char == stoplast and size >= stoplength then
+ local done = true
+ local last = size
+ for i=stoplength,1,-1 do
+ if stoplist[i] ~= list[last] then
+ done = false
+ break
+ end
+ last = last - 1
+ end
+ if done then
+ if depth > 0 then
+ depth = depth - 1
+ else
+ break
+ end
+ char = false -- trick: let's skip the next (start) test
+ end
+ end
+ if char == startlast and size >= startlength then
+ local done = true
+ local last = size
+ for i=startlength,1,-1 do
+ if startlist[i] ~= list[last] then
+ done = false
+ break
+ end
+ last = last - 1
+ end
+ if done then
+ depth = depth + 1
+ end
+ end
+-- end
+ else
+ -- local t = scantoken()
+ local t = gettoken()
+ if t then
+ -- we're skipping leading stuff, like obeyedlines and relaxes
+ if experiment and size > 0 then
+ -- we're probably in a macro
+ local char = tochar[getcommand(t)]
+ if char then
+ size = size + 1 ; list[size] = char
+ else
+-- local csname = getcsname(t)
+ local csname = scancsname(t)
+ if csname == stop then
+ stoplength = 0
+ break
+ else
+ size = size + 1 ; list[size] = "\\"
+ size = size + 1 ; list[size] = csname
+ size = size + 1 ; list[size] = " "
+ end
+ end
+ else
+ -- ignore and hope for the best
+ end
+ else
+ break
+ end
+ end
+ end
+ local start = 1
+ local stop = size - stoplength - 1
+ -- not good enough: only empty lines, but even then we miss the leading
+ -- for verbatim
+ --
+ -- the next is not yet adapted to the new scanner ... we don't need lpeg here
+ --
+ for i=start,stop do
+ local li = list[i]
+ if lpegmatch(blackspace,li) then
+ -- keep going
+ elseif lpegmatch(eol,li) then
+ -- okay
+ start = i + 1
+ else
+ break
+ end
+ end
+ for i=stop,start,-1 do
+ if lpegmatch(whitespace,list[i]) then
+ stop = i - 1
+ else
+ break
+ end
+ end
+ --
+ if start <= stop then
+ return concat(list,"",start,stop)
+ else
+ return ""
+ end
+end
+
+-- -- lmtx:
+--
+-- local function pickup(start,stop)
+-- local stoplist = split[stop] -- totable(stop)
+-- local stoplength = #stoplist
+-- local stoplast = stoplist[stoplength]
+-- local startlist = split[start] -- totable(start)
+-- local startlength = #startlist
+-- local startlast = startlist[startlength]
+-- local list = { }
+-- local size = 0
+-- local depth = 0
+-- getnextchar() -- we start with a \relax
+-- while true do -- or use depth
+-- local char = getnextchar()
+-- if char then
+-- size = size + 1
+-- list[size] = char
+-- if char == stoplast and size >= stoplength then
+-- local done = true
+-- local last = size
+-- for i=stoplength,1,-1 do
+-- if stoplist[i] ~= list[last] then
+-- done = false
+-- break
+-- end
+-- last = last - 1
+-- end
+-- if done then
+-- if depth > 0 then
+-- depth = depth - 1
+-- else
+-- break
+-- end
+-- char = false -- trick: let's skip the next (start) test
+-- end
+-- end
+-- if char == startlast and size >= startlength then
+-- local done = true
+-- local last = size
+-- for i=startlength,1,-1 do
+-- if startlist[i] ~= list[last] then
+-- done = false
+-- break
+-- end
+-- last = last - 1
+-- end
+-- if done then
+-- depth = depth + 1
+-- end
+-- end
+-- else
+-- local t = gettoken()
+-- if t then
+-- -- we're skipping leading stuff, like obeyedlines and relaxes
+-- if experiment and size > 0 then
+-- -- we're probably in a macro
+-- local char = tochar[getcommand(t)]
+-- if char then
+-- size = size + 1 ; list[size] = char
+-- else
+-- local csname = getcsname(t)
+ local csname = scancsname(t)
+-- if csname == stop then
+-- stoplength = 0
+-- break
+-- else
+-- size = size + 1 ; list[size] = "\\"
+-- size = size + 1 ; list[size] = csname
+-- size = size + 1 ; list[size] = " "
+-- end
+-- end
+-- else
+-- -- ignore and hope for the best
+-- end
+-- else
+-- break
+-- end
+-- end
+-- end
+-- local start = 1
+-- local stop = size - stoplength - 1
+-- -- not good enough: only empty lines, but even then we miss the leading
+-- -- for verbatim
+-- --
+-- -- the next is not yet adapted to the new scanner ... we don't need lpeg here
+-- --
+-- for i=start,stop do
+-- local li = list[i]
+-- if lpegmatch(blackspace,li) then
+-- -- keep going
+-- elseif lpegmatch(eol,li) then
+-- -- okay
+-- start = i + 1
+-- else
+-- break
+-- end
+-- end
+-- for i=stop,start,-1 do
+-- if lpegmatch(whitespace,list[i]) then
+-- stop = i - 1
+-- else
+-- break
+-- end
+-- end
+-- --
+-- if start <= stop then
+-- return concat(list,"",start,stop)
+-- else
+-- return ""
+-- end
+-- end
+
+-- function buffers.pickup(name,start,stop,finish,catcodes,doundent)
+-- local data = tokens.pickup(start,stop)
+-- if doundent or (autoundent and doundent == nil) then
+-- data = buffers.undent(data)
+-- end
+-- buffers.assign(name,data,catcodes)
+-- context(finish)
+-- end
+
+-- commands.pickupbuffer = buffers.pickup
+
+tokens.pickup = pickup
+
+implement {
+ name = "pickupbuffer",
+ actions = function()
+ -- let's pickup all here (no arguments)
+ local name = scanstring()
+ local start = scanstring()
+ local stop = scanstring()
+ local finish = scancsname()
+ local catcodes = scaninteger()
+ local doundent = scanboolean()
+ -- could be a scanner:
+ local data = pickup(start,stop)
+ if doundent or (autoundent and doundent == nil) then
+ data = undent(data)
+ end
+ buffers.assign(name,data,catcodes)
+ context[finish]()
+ end
+}
+
+local function savebuffer(list,name,prefix) -- name is optional
+ if not list or list == "" then
+ list = name
+ end
+ if not name or name == "" then
+ name = list
+ end
+ local content = collectcontent(list,nil) or ""
+ if content == "" then
+ content = "empty buffer"
+ end
+ if prefix == v_yes then
+ name = addsuffix(tex.jobname .. "-" .. name,"tmp")
+ end
+ io.savedata(name,replacenewlines(content))
+end
+
+implement {
+ name = "savebuffer",
+ actions = savebuffer,
+ arguments = "3 strings",
+}
+
+-- we can consider adding a size to avoid unlikely clashes
+
+local olddata = nil
+local newdata = nil
+local getrunner = sandbox.getrunner
+
+local runner = sandbox.registerrunner {
+ name = "run buffer",
+ program = "context",
+ method = "execute",
+ template = jit and "--purgeall --jit %filename%" or "--purgeall %filename%",
+ reporter = report_typeset,
+ checkers = {
+ filename = "readable",
+ }
+}
+
+local function runbuffer(name,encapsulate,runnername,suffixes)
+ if not runnername or runnername == "" then
+ runnername = "run buffer"
+ end
+ local suffix = "pdf"
+ if type(suffixes) == "table" then
+ suffix = suffixes[1]
+ elseif type(suffixes) == "string" and suffixes ~= "" then
+ suffix = suffixes
+ suffixes = { suffix }
+ else
+ suffixes = { suffix }
+ end
+ local runner = getrunner(runnername)
+ if not runner then
+ report_typeset("unknown runner %a",runnername)
+ return
+ end
+ if not olddata then
+ olddata = getdata("buffers","runners") or { }
+ local suffixes = olddata.suffixes
+ local hashes = olddata.hashes
+ if hashes and suffixes then
+ for k, hash in next, hashes do
+ for h, v in next, hash do
+ for s, v in next, suffixes do
+ local tmp = addsuffix(h,s)
+ -- report_typeset("mark for deletion: %s",tmp)
+ registertempfile(tmp)
+ end
+ end
+ end
+ end
+ end
+ if not newdata then
+ newdata = {
+ version = environment.version,
+ suffixes = { },
+ hashes = { },
+ }
+ setdata {
+ name = "buffers",
+ tag = "runners",
+ data = newdata,
+ }
+ end
+ local oldhashes = olddata.hashes or { }
+ local newhashes = newdata.hashes or { }
+ local old = oldhashes[suffix]
+ local new = newhashes[suffix]
+ if not old then
+ old = { }
+ oldhashes[suffix] = old
+ for hash, n in next, old do
+ local tag = formatters["%s-t-b-%s"](tex.jobname,hash)
+ local tmp = addsuffix(tag,"tmp")
+ -- report_typeset("mark for deletion: %s",tmp)
+ registertempfile(tmp) -- to be sure
+ end
+ end
+ if not new then
+ new = { }
+ newhashes[suffix] = new
+ end
+ local names = getnames(name)
+ local content = collectcontent(names,nil) or ""
+ if content == "" then
+ content = "empty buffer"
+ end
+ if encapsulate then
+ content = formatters["\\starttext\n%s\n\\stoptext\n"](content)
+ end
+ --
+ local hash = md5hex(content)
+ local tag = formatters["%s-t-b-%s"](nameonly(tex.jobname),hash) -- make sure we run on the local path
+ --
+ local filename = addsuffix(tag,"tmp")
+ local resultname = addsuffix(tag,suffix)
+ --
+ if new[tag] then
+ -- done
+ elseif not old[tag] or olddata.version ~= newdata.version or not isfile(resultname) then
+ if trace_run then
+ report_typeset("changes in %a, processing forced",name)
+ end
+ savedata(filename,content)
+ report_typeset("processing saved buffer %a\n",filename)
+ runner { filename = filename }
+ end
+ new[tag] = (new[tag] or 0) + 1
+ report_typeset("no changes in %a, processing skipped",name)
+ registertempfile(filename)
+ -- report_typeset("mark for persistence: %s",filename)
+ for i=1,#suffixes do
+ local suffix = suffixes[i]
+ newdata.suffixes[suffix] = true
+ local tmp = addsuffix(tag,suffix)
+ -- report_typeset("mark for persistance: %s",tmp)
+ registertempfile(tmp,nil,true)
+ end
+ --
+ return resultname -- first result
+end
+
+local f_getbuffer = formatters["buffer.%s"]
+
+local function getbuffer(name)
+ local str = getcontent(name)
+ if str ~= "" then
+ -- characters.showstring(str)
+ ctx_viafile(str,f_getbuffer(validstring(name,"noname")))
+ end
+end
+
+local function getbuffermkvi(name) -- rather direct !
+ ctx_viafile(resolvers.macros.preprocessed(getcontent(name)),formatters["buffer.%s.mkiv"](validstring(name,"noname")))
+end
+
+local function gettexbuffer(name)
+ local buffer = name and cache[name]
+ if buffer and buffer.data ~= "" then
+ ctx_pushcatcodetable()
+ if buffer.catcodes == txtcatcodes then
+ ctx_setcatcodetable(txtcatcodes)
+ else
+ ctx_setcatcodetable(ctxcatcodes)
+ end
+ -- context(function() ctx_viafile(buffer.data) end)
+ ctx_getbuffer { name } -- viafile flushes too soon
+ ctx_popcatcodetable()
+ end
+end
+
+buffers.get = getbuffer
+buffers.getmkiv = getbuffermkiv
+buffers.gettexbuffer = gettexbuffer
+buffers.run = runbuffer
+
+implement { name = "getbufferctxlua", actions = loadcontent, arguments = "string" }
+implement { name = "getbuffer", actions = getbuffer, arguments = "string" }
+implement { name = "getbuffermkvi", actions = getbuffermkvi, arguments = "string" }
+implement { name = "gettexbuffer", actions = gettexbuffer, arguments = "string" }
+
+interfaces.implement {
+ name = "getbuffercontent",
+ arguments = "string",
+ actions = { getcontent, context },
+}
+
+implement {
+ name = "typesetbuffer",
+ actions = { runbuffer, context },
+ arguments = { "string", true }
+}
+
+implement {
+ name = "runbuffer",
+ actions = { runbuffer, context },
+ arguments = { "string", false, "string" }
+}
+
+implement {
+ name = "doifelsebuffer",
+ actions = { exists, commands.doifelse },
+ arguments = "string"
+}
+
+-- This only used for mp buffers and is a kludge. Don't change the
+-- texprint into texsprint as it fails because "p<nl>enddef" becomes
+-- "penddef" then.
+
+implement {
+ name = "feedback", -- bad name, maybe rename to injectbuffercontent
+ actions = { collectcontent, ctx_printlines },
+ arguments = "string"
+}
+
+do
+
+ local context = context
+ local ctxcore = context.core
+
+ local ctx_startbuffer = ctxcore.startbuffer
+ local ctx_stopbuffer = ctxcore.stopbuffer
+
+ local ctx_startcollecting = context.startcollecting
+ local ctx_stopcollecting = context.stopcollecting
+
+ function ctxcore.startbuffer(...)
+ ctx_startcollecting()
+ ctx_startbuffer(...)
+ end
+
+ function ctxcore.stopbuffer()
+ ctx_stopbuffer()
+ ctx_stopcollecting()
+ end
+
+end
+
+-- moved here:
+
+function buffers.samplefile(name)
+ if not buffers.exists(name) then
+ buffers.assign(name,io.loaddata(resolvers.findfile(name)))
+ end
+ buffers.get(name)
+end
+
+implement {
+ name = "samplefile", -- bad name, maybe rename to injectbuffercontent
+ actions = buffers.samplefile,
+ arguments = "string"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/buff-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/buff-ini.mkxl
new file mode 100644
index 00000000000..ed62743ad89
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/buff-ini.mkxl
@@ -0,0 +1,233 @@
+%D \module
+%D [ file=buff-ini,
+%D version=2011.11.22, % previous big effort 2000.01.05,
+%D title=\CONTEXT\ Buffer Macros,
+%D subtitle=Buffers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Buffer Macros / Buffers}
+
+\registerctxluafile{buff-ini}{autosuffix}
+
+\unprotect
+
+%D There have been several iterations in \MKIV\ but here we only show the currently
+%D used one. One can always look back (and maybe learn some). It will never look
+%D pretty and never be efficient but it has served us for ages (conceptually it's
+%D as in \MKII).
+
+\mutable\let\currentbuffer\empty
+
+\permanent\def\doifelsebuffer#1{\clf_doifelsebuffer{#1}} % expandable, todo: use public implementor
+
+\aliased\let\doifbufferelse\doifelsebuffer
+
+\permanent\tolerant\protected\def\resetbuffer[#1]{\clf_erasebuffer{#1}} % todo: use public implementor
+
+\permanent\setuvalue{\e!start\v!buffer}%
+ {\begingroup % (3)
+ \obeylines
+ \buff_start}
+
+\tolerant\def\buff_start[#1]#*[#2]%
+ {\buff_start_indeed{}{#1}{\e!start\v!buffer}{\e!stop\v!buffer}}
+
+\def\buff_start_indeed#1#2#3#4%
+ {\edef\p_strip{\namedbufferparameter{#1}\c!strip}% for aditya
+ \normalexpanded{\buff_pickup
+ {#2}%
+ {#3}%
+ {#4}%
+ {}%
+ {\buff_stop{#4}}%
+ \ifx\p_strip\v!no\zerocount\else\plusone\fi}}
+
+\permanent\protected\def\grabbufferdatadirect % name start stop
+ {\begingroup % (6)
+ \buff_start_indeed\empty}
+
+\permanent\protected\def\grabbufferdata % was: \dostartbuffer
+ {\begingroup % (4)
+ \obeylines
+ \buff_grab_direct}
+
+\tolerant\def\buff_grab_direct[#1]#*[#2]#*[#3]#*[#4]% [category] [name] [start] [stop]
+ {\ifnum\lastarguments=\plusfour
+ \expandafter\buff_start_indeed
+ \else
+ \expandafter\buff_start_indeed_default
+ \fi{#1}{#2}{#3}{#4}}
+
+\def\buff_start_indeed_default#1#2#3#4{\buff_start_indeed\empty{#1}{#2}{#3}}
+
+\let\buff_finish\relax
+%let\buff_gobble\relax
+
+\protected\def\buff_pickup#1#2#3#4#5#6% name, startsequence, stopsequence, before, after, undent
+ {\begingroup % (1)
+ #4%
+ \begingroup % (2)
+ \scratchcounter\catcodetable
+ \clf_erasebuffer{#1}%
+ \setcatcodetable\vrbcatcodes
+ \protected\def\buff_finish
+ {\endgroup % (1)
+ \endgroup % (2)
+ #5}%
+ % todo: we need to skip the first lineending which is an active character
+ % but sometimes we have something different ... this is a side effect of
+ % checking for optional arguments i.e. the next token is already tokenized
+ % and for that reason we have the \relax as well as the \string
+ \clf_pickupbuffer
+ {#1}%
+ {#2}%
+ {#3}%
+% {\string\dofinishpickupbuffer}%
+ \buff_finish
+ \scratchcounter
+ \ifnum#6=\plusone\s!true\else\s!false\fi
+ % \relax}
+ \expandafter\relax\string} % maybe \normalexpanded{\relax\utfchar{7}}} signal
+
+\protected\def\buff_stop#1%
+ {\endgroup % (3 & 4 & 5 & 6)
+ \begincsname#1\endcsname}
+
+\aliased\let\endbuffer\relax
+
+\permanent\tolerant\protected\def\setbuffer[#1]#:#2\endbuffer % seldom used so we just pass #2
+ {\clf_assignbuffer{#1}{\detokenize{#2}}\catcodetable\relax}
+
+% beware, never adapt the global buffer settings, actually we might introduce
+% a broken parent chain for this purpose but on the other hand it's not that
+% different from framed cum suis
+
+\installcorenamespace{buffer}
+
+\installcommandhandler \??buffer {buffer} \??buffer
+
+\setupbuffer
+ [\c!before=,
+ \c!after=]
+
+\newcount\c_buff_n_of_defined
+
+\mutable\let\currentdefinedbuffer\s!dummy
+
+\appendtoks
+ \global\advance\c_buff_n_of_defined\plusone
+ \setexpandedbufferparameter\c!number{\number\c_buff_n_of_defined}%
+ \edef\currentdefinedbuffer{def-\number\c_buff_n_of_defined}%
+ \frozen\instance\setuevalue{\e!start\currentbuffer}{\buff_start_defined{\currentbuffer}{\currentdefinedbuffer}{\e!start\currentbuffer}{\e!stop\currentbuffer}}%
+ \frozen\instance\setuevalue{\e!get \currentbuffer}{\buff_get_stored {\currentbuffer}{\currentdefinedbuffer}}%
+\to \everydefinebuffer
+
+\protected\def\buff_start_defined
+ {\begingroup % (5)
+ \buff_start_indeed}
+
+\permanent\def\thebuffernumber #1{\namedbufferparameter{#1}\c!number}
+\permanent\def\thedefinedbuffer#1{def-\namedbufferparameter{#1}\c!number}
+
+\permanent\tolerant\protected\def\getbuffer[#1]% [name]
+ {\namedbufferparameter\empty\c!before\relax
+ \doifelsenothing{#1}
+ {\buff_get_stored_indeed\empty}
+ {\processcommalist[#1]\buff_get_stored_indeed}%
+ \namedbufferparameter\empty\c!after\relax}
+
+\protected\def\buff_get_stored#1#2%
+ {\namedbufferparameter{#1}\c!before\relax
+ \buff_get_stored_indeed{#2}%
+ \namedbufferparameter{#1}\c!after\relax}
+
+\protected\def\buff_get_stored_indeed#1%
+ {\clf_getbuffer{#1}}
+
+\permanent\protected\def\getdefinedbuffer[#1]%
+ {\buff_get_stored{#1}{\thedefinedbuffer{#1}}}%
+
+\permanent\tolerant\protected\def\inlinebuffer[#1]% [name]
+ {\doifelsenothing{#1}
+ {\buff_get_stored_inline_indeed\empty}
+ {\processcommalist[#1]\buff_get_stored_inline_indeed}}
+
+\protected\def\buff_get_stored_inline_indeed#1%
+ {\ignorespaces\clf_getbuffer{#1}\removeunwantedspaces}
+
+\permanent\def\rawbuffer#1% expandable
+ {\clf_getbuffer{#1}}
+
+\definebuffer
+ [\v!hiding]
+
+\setupbuffer
+ [\v!hiding]
+ [\c!before=,
+ \c!after=]
+
+\permanent\protected\protected\def\processTEXbuffer[#1]% keep case, maybe also lower
+ {\pushcatcodetable
+ \catcodetable\ctxcatcodes % \setcatcodetable
+ \buff_get_stored_indeed{#1}%
+ \popcatcodetable}
+
+% only mkiv:
+%
+% \startbuffer[x]
+% x
+% \stopbuffer
+%
+% \savebuffer[x] [temp] % gets name: jobname-temp.tmp
+% \savebufferinfile[x][temp.log] % gets name: temp.log
+
+\installcorenamespace{savebuffer}
+
+\installsetuponlycommandhandler \??savebuffer {savebuffer}
+
+\setupsavebuffer
+ [\c!list=,
+ \c!file=,
+ \c!prefix=\v!yes]
+
+\permanent\tolerant\protected\def\savebuffer[#1]#*[#2]%
+ {\begingroup
+ \ifhastok={#1}%
+ \setupcurrentsavebuffer[#1]%
+ \else
+ \setupcurrentsavebuffer[\c!list={#1},\c!file=#2]%
+ \fi
+ \clf_savebuffer{\directsavebufferparameter\c!list}{\directsavebufferparameter\c!file}{\directsavebufferparameter\c!prefix}%
+ \endgroup}
+
+%D Experimental: no expansion of commands in buffer!
+
+% \startbuffer[what]
+% context("WHAT")
+% \stopbuffer
+% \startbuffer
+% context("JOBNAME")
+% \stopbuffer
+%
+% \ctxluabuffer[what] \ctxluabuffer
+
+\permanent\tolerant\protected\def\ctxluabuffer[#1]{\clf_getbufferctxlua{#1}} % todo: use public implementor
+\permanent\tolerant\protected\def\mkvibuffer [#1]{\clf_getbuffermkvi {#1}} % todo: use public implementor
+
+% maybe still used elsewhere
+
+% \aliased\doprocesstexbuffer\mkvibuffer
+
+\aliased\let\dostartbuffer\grabbufferdata % for old times sake, this will go away
+
+% new (expandable):
+
+\permanent\def\getbufferdata[#1]{\buff_get_stored_indeed{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/buff-par.mklx b/Master/texmf-dist/tex/context/base/mkxl/buff-par.mklx
new file mode 100644
index 00000000000..b6ffa3f534e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/buff-par.mklx
@@ -0,0 +1,186 @@
+%D \module
+%D [ file=buff-par,
+%D version=2010.12.05,
+%D title=\CONTEXT\ Buffer Macros,
+%D subtitle=Parallel,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Buffer Macros / Parallel}
+
+\registerctxluafile{buff-par}{}
+
+%D This module is made for Thomas Schmitz as part of a project. There is
+%D no documentation yet. Also, this code is unfinished and not tested well.
+%D
+%D \starttyping
+%D \defineparallel[main][one,two]
+%D
+%D \startmain
+%D \startone
+%D first 0
+%D [reference] first 1
+%D first 2
+%D first 3
+%D \stopone
+%D \starttwo
+%D second 1
+%D second 2
+%D \stoptwo
+%D \stopmain
+%D
+%D \startmain
+%D \startone
+%D first 4
+%D first 4
+%D \stopone
+%D \starttwo
+%D second 3
+%D second 4
+%D \stoptwo
+%D \stopmain
+%D
+%D \placeparallel[main][one][criterium=all]
+%D
+%D \placeparallel[main][two][criterium=all]
+%D \stoptyping
+
+%D criterium=all start=<number> n=<number>
+
+\unprotect
+
+\installcorenamespace{parallel}
+\installcorenamespace{parallelhandler}
+
+\installcommandhandler \??parallel {parallel} \??parallel
+
+\setupparallel
+ [\c!setups=parallel:place:default]
+
+\pushoverloadmode
+
+\let\buff_parallel_define_saved \defineparallel
+\let\buff_process_parallel_instance\relax
+
+\permanent\tolerant\protected\overloaded\def\defineparallel[#name]#spacer[#instances]%
+ {\buff_parallel_define_saved[#name][\c!list={#instances}]% list is internal
+ \clf_defineparallel{#name}{#instances}%
+ \expandafter\newtoks\csname\??parallelhandler#name\endcsname
+ \processcommacommand[#instances]{\buff_parallel_define_instance{#name}}%
+ \frozen\instance\setuevalue{\e!start#name}{\buff_parallel_start{#name}}%
+ \frozen\instance\setuevalue{\e!stop #name}{\buff_parallel_stop}}
+
+\popoverloadmode
+
+\def\buff_process_parallel_instances
+ {\the\csname\??parallelhandler\currentparallel\endcsname}
+
+\def\buff_parallel_define_instance#name#instance%
+ {\normalexpanded{\buff_parallel_define_saved[#instance][#name]}%
+ \definebuffer[#instance]%
+ \toksapp
+ \csname\??parallelhandler#name\endcsname
+ {\edef\currentparallelinstance{#instance}\buff_process_parallel_instance\relax}%
+ \setuevalue{\e!stop#instance}{\buff_parallel_save{#instance}}}
+
+\pushoverloadmode
+
+\let\buff_parallel_setup_saved \setupparallel
+
+\permanent\tolerant\protected\def\setupparallel[#name]#spacer[#instances]#spacer[#setups]%
+ {\processcommalist[#instances]{\buff_parallel_setup_instance{#name}{#setups}}}
+
+\popoverloadmode
+
+\def\buff_parallel_setup_instance#name#setups#instance%
+ {\buff_parallel_setup_saved[#name:#instance][#setups]}
+
+\protected\def\buff_parallel_start#name%
+ {\pushmacro\currentparallel
+ \edef\currentparallel{#name}%
+ \clf_nextparallel{\currentparallel}}
+
+\protected\def\buff_parallel_stop
+ {\popmacro\currentparallel}
+
+\protected\def\buff_parallel_save#instance% defined moet ook aan de lua kant kunnen
+ {\clf_saveparallel
+ {\currentparallel}%
+ {#instance}%
+ {\thedefinedbuffer{#instance}}%
+ \relax}
+
+\permanent\tolerant\protected\def\placeparallel[#name]#spacer[#instance]#spacer[#settings]%
+ {\begingroup
+ \edef\currentparallel{#name}%
+ \setupcurrentparallel[#settings]%
+ \clf_placeparallel
+ {\currentparallel}%
+ {#instance}%
+ {%
+ % setups {\parallelparameter\c!setups}%
+ start {\parallelparameter\c!start}%
+ n {\parallelparameter\c!n}%
+ criterium {\parallelparameter\c!criterium}%
+ }%
+ \relax
+ \endgroup}
+
+\mutable\let\currentparallelinstance\empty
+\mutable\let\currentparallelnumber \empty
+\mutable\let\currentparallelline \empty
+\mutable\let\currentparallellabel \empty
+\mutable\let\currentparallelcontent \empty
+
+\permanent\protected\def\doflushparallel#instance#status#line#label#content% called at lua end
+ {\begingroup
+ \def\currentparallelinstance{#instance}%
+ \def\currentparallelnumber {#status}%
+ \def\currentparallelline {#line}%
+ \def\currentparallellabel {#label}%
+ \def\currentparallelcontent {#content}% can be kep at the lua end .. no need to use a virtual file
+ \ifcase#status\relax
+ \expandafter\buff_parallel_flush_nop
+ \or
+ \expandafter\buff_parallel_flush_yes
+ \fi
+ \endgroup}
+
+\def\buff_parallel_flush_nop
+ {}
+
+\def\buff_parallel_flush_yes
+ %{\directsetup{\namedparallelparameter{\currentparallel:\currentparallelinstance}\c!setups}}
+ {\directsetup{\namedparallelparameter{\currentparallelinstance}\c!setups}}
+
+\permanent\protected\def\doifelseparallel#name#instance%
+ {\clf_doifelseparallel{#name}{#instance}}
+
+\aliased\let\doifparallelelse\doifelseparallel
+
+\permanent\tolerant\protected\def\resetparallel[#name]#spacer[#instance]%
+ {\clf_resetparallel{#name}{#instance}}
+
+\startsetups parallel:place:default
+ \hangafter\plusone
+ \hangindent4\emwidth
+ \dontleavehmode
+ \hbox to 3\emwidth \bgroup
+ \hss
+ \bf
+ \doifsomething \currentparallellabel {
+ \textreference[\currentparallellabel]{\currentparallelline}
+ }
+ \currentparallelline
+ \quad
+ \egroup
+ \currentparallelcontent
+ \par
+\stopsetups
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/buff-ver.mkxl b/Master/texmf-dist/tex/context/base/mkxl/buff-ver.mkxl
new file mode 100644
index 00000000000..68a07df60be
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/buff-ver.mkxl
@@ -0,0 +1,1000 @@
+%D \module
+%D [ file=buff-ver, % was core-ver
+%D version=2000.05.09,
+%D title=\CONTEXT\ Buffer Macros,
+%D subtitle=Verbatim,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% check after-first and before-last breaks
+
+\writestatus{loading}{ConTeXt Buffer Macros / Verbatim}
+
+\registerctxluafile{buff-ver}{}
+
+\unprotect
+
+\startcontextdefinitioncode
+
+\definesystemattribute[verbatimline][public]
+
+\appendtoksonce
+ \c_attr_verbatimline\attributeunsetvalue
+\to \everyforgetall
+
+%D Initializations.
+
+\installcorenamespace{typinglines}
+\installcorenamespace{typingspace}
+\installcorenamespace{typingblank} % needs checking ... used?
+
+\newtoks\everyinitializeverbatim
+
+\appendtoks
+ \the\everyresettypesetting
+ \resetfontfeature
+ \resetcharacterspacing
+ \resetbreakpoints
+\to \everyinitializeverbatim
+
+\permanent\protected\def\setverbatimspaceskip % to be checked: must happen after font switch
+ {\spaceskip\fontcharwd\font`x\relax
+ \xspaceskip\spaceskip}
+
+\defcsname\??typinglines\v!no \endcsname{\buff_verbatim_ignore_hyphens}
+\defcsname\??typinglines\v!normal \endcsname{\buff_verbatim_ignore_hyphens}
+\defcsname\??typinglines\v!yes \endcsname{\buff_verbatim_obey_breakpoints}
+\defcsname\??typinglines\v!hyphenated\endcsname{\buff_verbatim_obey_hyphens}
+
+\defcsname\??typingspace\v!on \endcsname{\enforced\let\obeyedspace\specialcontrolspace}
+\defcsname\??typingspace\v!stretch \endcsname{\enforced\let\obeyedspace\specialstretchedspace}
+\defcsname\??typingspace\v!normal \endcsname{}
+\defcsname\??typingspace\v!fixed \endcsname{\enforced\let\obeyedspace\specialfixedspace}
+
+\defcsname\??typingblank\v!standard \endcsname{\s_spac_whitespace_parskip}
+\defcsname\??typingblank\v!small \endcsname{\smallskipamount}
+\defcsname\??typingblank\v!medium \endcsname{\medskipamount}
+\defcsname\??typingblank\v!big \endcsname{\bigskipamount}
+\defcsname\??typingblank\v!halfline \endcsname{.5\baselineskip}
+\defcsname\??typingblank\v!line \endcsname{\baselineskip}
+\defcsname\??typingblank\v!none \endcsname{\zeropoint}
+
+\protected\def\buff_verbatim_obey_hyphens
+ {}
+
+\protected\def\buff_verbatim_obey_breakpoints
+ {\nohyphens
+ \veryraggedright}
+
+\protected\def\buff_verbatim_ignore_hyphens
+ {\nohyphens}
+
+\def\buff_verbatim_initialize_type_one
+ {\enforced\let\obeylines\ignorelines
+ \edef\p_buff_option{\typeparameter\c!option}%
+ \ifx\p_buff_option\v!none
+ \usetypestyleandcolor\c!style\c!color
+ \orelse\ifempty\p_buff_option
+ \usetypestyleandcolor\c!style\c!color
+ \else
+ \usetypestyleparameter\c!style % no color
+ \fi
+ \setcatcodetable\vrbcatcodes}
+
+\def\buff_verbatim_initialize_type_two
+ {\spaceskip.5\emwidth\relax
+ \enforced\let\obeyedspace\specialobeyedspace
+ \enforced\let\controlspace\specialcontrolspace
+ \edef\p_buff_lines{\typeparameter\c!lines}%
+ \begincsname\??typinglines\p_buff_lines\endcsname
+ \edef\p_buff_space{\typeparameter\c!space}%
+ \begincsname\??typingspace\p_buff_space\endcsname
+ \relax\the\everyinitializeverbatim\relax}
+
+\permanent\protected\def\doinitializeverbatim % for use elsewhere .. temp hack (see lxml-ini)
+ {\buff_verbatim_initialize_type_one
+ \buff_verbatim_initialize_type_two}
+
+\let\buff_verbatim_set_line_margin\relax
+
+\def\buff_verbatim_set_line_margin_indeed
+ {\hskip\doifelseoddpage{\typingparameter\c!oddmargin}{\typingparameter\c!evenmargin}\relax}
+
+\def\buff_verbatim_initialize_typing_one
+ {\usebodyfontparameter\typingparameter
+ \scratchskip\typingparameter\c!oddmargin\relax
+ \ifzeropt\scratchskip \else
+ \let\buff_verbatim_set_line_margin\buff_verbatim_set_line_margin_indeed
+ \fi
+ \scratchskip\typingparameter\c!evenmargin\relax
+ \ifzeropt\scratchskip \else
+ \let\buff_verbatim_set_line_margin\buff_verbatim_set_line_margin_indeed
+ \fi
+ \ifrelax\buff_verbatim_set_line_margin
+ \doadaptleftskip{\typingparameter\c!margin}%
+ \fi
+ \edef\p_buff_option{\typingparameter\c!option}%
+ \ifx\p_buff_option\v!none
+ \usetypingstyleandcolor\c!style\c!color
+ \orelse\ifempty\p_buff_option
+ \usetypingstyleandcolor\c!style\c!color
+ \else
+ \usetypingstyleparameter\c!style % no color !
+ \fi
+ \usealignparameter\typingparameter}
+
+\def\buff_verbatim_initialize_typing_two
+ {\spaceskip.5\emwidth\relax
+ \enforced\let\obeyedspace\specialobeyedspace
+ \enforced\let\controlspace\specialcontrolspace
+ \edef\p_buff_lines{\typingparameter\c!lines}%
+ \begincsname\??typinglines\p_buff_lines\endcsname
+ \edef\p_buff_space{\typingparameter\c!space}%
+ \begincsname\??typingspace\p_buff_space\endcsname
+ \relax\the\everyinitializeverbatim\relax}
+
+%D \macros
+%D {definetype,setuptype}
+%D
+%D Specific inline verbatim commands can be defined with the following command.
+
+\installcorenamespace{type}
+
+\installcommandhandler \??type {type} \??type
+
+\appendtoks
+ \frozen\instance\setuevalue{\currenttype}{\buff_verbatim_type{\currenttype}}%
+\to \everydefinetype
+
+\appendtoks
+ \buff_verbatim_initialize_visualizer{\typeparameter\c!option}%
+\to \everysetuptype
+
+%D \macros
+%D {definetyping,setuptyping}
+%D
+%D For most users the standard \type {\start}||\type {\stop}||pair will suffice, but
+%D for documentation purposes the next definition command can be of use:
+%D
+%D \starttyping
+%D \definetyping[extratyping][margin=3em]
+%D
+%D \startextratyping
+%D these extra ones are indented by 1 em
+%D \stopextratyping
+%D \stoptyping
+%D
+%D The definitions default to the standard typing values.
+
+\installcorenamespace{typing}
+
+\installcommandhandler \??typing {typing} \??typing
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!start\currenttyping}{\buff_verbatim_typing_start{\currenttyping}}%
+ \frozen\instance\setuevalue{\e!stop \currenttyping}{\buff_verbatim_typing_stop {\currenttyping}}%
+ \ifempty\currenttypingparent
+ \normalexpanded{\definelinenumbering[\currenttyping]}%
+ \else
+ \normalexpanded{\definelinenumbering[\currenttyping][\currenttypingparent]}%
+ \fi
+\to \everydefinetyping
+
+\appendtoks
+ \buff_verbatim_initialize_visualizer{\typingparameter\c!option}% we can check at the tex end
+\to \everysetuptyping
+
+\def\buff_verbatim_initialize_visualizer#1%
+ {\ifproductionrun\clf_loadvisualizer{#1}\fi}
+
+\permanent\protected\def\doifelsevisualizer#1%
+ {\clf_doifelsevisualizer{#1}}
+
+\aliased\let\doifvisualizerelse\doifelsevisualizer
+
+%D Verbatim command are very sensitive to argument processing, which is a direct
+%D result of the \CATCODES\ being fixed at reading time.
+%D
+%D Typesetting inline verbatim can be accomplished by \type {\type}, which in this
+%D sentence was typeset by saying just \type {\type {\type}}, which in turn was
+%D typeset by \unknown. Using the normal grouping characters \type {{}} is the most
+%D natural way of using this command.
+%D
+%D A second, more or less redundant, alternative is delimiting the argument with an
+%D own character. This method was implemented in the context of a publication in the
+%D \MAPS, where this way of delimiting is recognized by \LATEX\ users.
+%D
+%D The third, more original alternative, is the one using \type {<<} and \type {>>}
+%D as delimiters. This alternative can be used in situations where slanted
+%D typeseting is needed.
+%D
+%D \macros
+%D {type}
+%D
+%D We define \type {\type} as a protected command. This command has several
+%D invocations: grouped, wirt boundary characters, and with font switches.
+%D
+%D \starttyping
+%D normal: \par \type{xx<<..xx..<<xx <<xx>> >>..>>xx} \par \type<<....>> \par \type<<..<<xx>>..>> \par
+%D normal: \par \type{xx<..xx..<xx <slanted> >..>xx} \par \type{<....>} \par \type{<..<xx>..>}
+%D \setuptype[option=slanted]
+%D slanted: \par \type{xx<<..sl..<<xx <<sl>> xx>>..sl..>>xx} \par \type<<..xx..>> \par \type<<..<<sl>>..>> \par
+%D slanted: \par \type{xx<<..sl..<xx <sl> xx>..sl..>>xx} \par \type<<..xx..>> \par \type<<..<sl>..>> \par
+%D \setuptype[option=none]
+%D none: \par \type{xx<<..xx..<<xx <<xx>> >>..>>xx} \par \type<<....>> \par \type<<..<<xx>>..>> \par
+%D \stoptyping
+%D
+%D When writing the manual to \CONTEXT\ and documenting this source we needed to
+%D typeset \type {<<} and \type {>>}. Because we wanted to do this in the natural
+%D way, we've adapted the original definition a bit. This implementation went
+%D through several live cycles. The final implementation looks a bit further and
+%D treats the lone \type {<<} and \type {>>} a bit different. The \type {\null}
+%D prevents ligatures, which unfortunately turn up in Lucida fonts.
+%D
+%D The following lines show what happens when we set \type {option=commands}.
+%D
+%D \startbuffer
+%D \starttyping
+%D test//test test/BTEX \footnote{test test test}/ETEX test
+%D test//test test/BTEX \footnote{test test test}/ETEX test
+%D test test test/BTEX \bf(nota bene)/ETEX test
+%D test test test /BTEX \bf(nota bene)/ETEX test
+%D \stoptyping
+%D \stopbuffer
+%D
+%D % \bgroup\setuptyping[option=commands]\getbuffer\egroup
+%D
+%D this was keyed in as:
+%D
+%D \typebuffer
+
+% Beware, the command is supposed to do something like \framed and not manipulate
+% content i.e. it gets no argument but something b/egrouped instead:
+
+\def\buff_verbatim_left_of_type
+ {\typeparameter\c!left
+ \typeparameter\c!command\bgroup}
+
+\def\buff_verbatim_right_of_type
+ {\egroup
+ \typeparameter\c!right}
+
+\permanent\protected\def\type{\buff_verbatim_type\empty}
+\permanent\protected\def\typ {\buff_verbatim_typ \empty}
+
+\protected\def\buff_verbatim_type#1%
+ {\dontleavehmode
+ \bgroup
+ \edef\currenttype{#1}%
+ \buff_verbatim_type_indeed}
+
+\def\buff_verbatim_typ#1%
+ {\dontleavehmode
+ \bgroup
+ \edef\currenttype{#1}%
+ \lettypeparameter\c!lines\v!hyphenated
+ \enforced\let\specialobeyedspace\specialstretchedspace
+ \buff_verbatim_type_indeed}
+
+\tolerant\def\buff_verbatim_type_indeed[#1]%
+ {\ifarguments\or\setupcurrenttype[#1]\fi
+ \buff_verbatim_left_of_type
+ \futureexpand\bgroup\buff_verbatim_type_a\buff_verbatim_type_one}
+
+\def\buff_verbatim_type_one
+ {\catcode`<=\othercatcode % old precaution
+ \catcode`>=\othercatcode % old precaution
+ \futureexpandis<\buff_verbatim_type_b\buff_verbatim_type_c}
+
+\def\buff_verbatim_type_three
+ {\if\next<%
+ \expandafter\buff_verbatim_type_b
+ \else
+ \expandafter\buff_verbatim_type_c
+ \fi}
+
+\def\buff_verbatim_type_a
+ {\buff_verbatim_initialize_type_one
+ \setcatcodetable\tpacatcodes
+ \buff_verbatim_type_normal}
+
+\def\buff_verbatim_type_b#1%
+ {\buff_verbatim_initialize_type_one
+ \setcatcodetable\tpbcatcodes
+ \buff_verbatim_type_nested}
+
+\def\buff_verbatim_type_c#1%
+ {\buff_verbatim_initialize_type_one
+ \setcatcodetable\vrbcatcodes
+ \def\next##1#1{\buff_verbatim_type_normal{##1}}%
+ \next}
+
+\def\buff_verbatim_type_normal#1%
+ {\buff_verbatim_initialize_type_two
+ \dostarttaggedchained\t!verbatim\currenttype\??type
+ \clf_type
+ data {\detokenize{#1}}%
+ tab {\typeparameter\c!tab}%
+ method {\p_buff_option}%
+ compact {\typeparameter\c!compact}% % none | all | last (all needed in tabulate etc for manuals)
+ escape {\typeparameter\c!escape}% % new but rather useless imo (escaping in general is not used much)
+ % nature {inline}% is default
+ \relax
+ \dostoptagged
+ \buff_verbatim_right_of_type
+ \egroup}
+
+\def\buff_verbatim_type_nested#1%
+ {\buff_verbatim_initialize_type_two
+ \dostarttaggedchained\t!verbatim\currenttype\??type
+ \clf_type
+ data {\detokenize{#1}}%
+ tab {\typeparameter\c!tab}%
+ method {\p_buff_option}% % extra visualizer (maybe: nested,\typeparameter\c!option)
+ escape {\typeparameter\c!escape}% % new but rather useless imo (escaping in general is not used much)
+ % nature {inline}% is default
+ method {nested}%
+ \relax
+ \dostoptagged
+ \buff_verbatim_right_of_type
+ \egroup
+ \gobbleoneargument} % grab last >
+
+%D The \type {compact} option can come in handy in the case of inline argument passing
+%D
+%D \starttyping
+%D \definetype[TeXcodeA][option=TEX]
+%D \definetype[TeXcodeB][option=TEX,compact=all]
+%D \definetype[TeXcodeC][option=TEX,compact=absolute]
+%D
+%D \def\argA#1{\TeXcodeA{{#1}}}
+%D \def\argB#1{\TeXcodeB{{#1}}}
+%D \def\argC#1{\TeXcodeC{{#1}}}
+%D \stoptyping
+
+%D \macros
+%D {obeyhyphens,obeybreakpoints}
+%D
+%D Although it's not clear from the macros, one character trait of this macros,
+%D which are build on top of the support module, is that they don't hyphenate. We
+%D therefore offer the alternative \type{\typ}. The current implementation works all
+%D right, but a decent hyphenation support of \type{\tt} text will be implemented
+%D soon.
+
+\permanent\protected\def\specialfixedspace {\kern\interwordspace\relax}
+\permanent\protected\def\specialobeyedspace {\hskip\interwordspace\relax} % better than spaceskip
+\permanent\protected\def\specialstretchedspace {\hskip.5\interwordspace\s!plus.125\interwordspace\relax} % \interwordstretch can be zero
+\permanent\protected\def\specialcontrolspace {\normalcontrolspace\allowbreak} % uses fallback
+
+\permanent\protected\def\explicitfixedspace {\asciispacechar}
+\permanent\protected\def\explicitobeyedspace {\asciispacechar\allowbreak}
+\permanent\protected\def\explicitstretchedspace{\asciispacechar\hskip\zeropoint\s!plus.125\interwordspace\relax}%
+\permanent\protected\def\explicitcontrolspace {\optionalcontrolspace\allowbreak} % uses asciispace
+
+\appendtoks
+ \enforced\protected\def\obeyedspace{\hskip\zeropoint\relax\asciispacechar\hskip\zeropoint\relax}%
+\to \everyenableelements
+
+\permanent\protected\def\obeyhyphens
+ {\enforced\let\obeyedspace \specialobeyedspace % maybe \specialstretchedspace
+ \enforced\let\controlspace\specialcontrolspace
+ \spaceskip.25\emwidth\relax} % hm a bit of stretch !
+
+\permanent\protected\def\obeybreakpoints
+ {\ignorehyphens
+ \veryraggedright}
+
+\permanent\protected\def\ignorehyphens
+ {\nohyphens
+ \enforced\let\obeyedspace \specialobeyedspace
+ \enforced\let\controlspace\specialcontrolspace
+ \spaceskip.5\emwidth\relax}
+
+%D \macros
+%D {tex,arg,mat,dis,astype}
+%D
+%D Sometimes, for instance when we pass verbatim text as an argument, the fixed
+%D \CATCODES\ interfere with our wishes. An experimental implementation of character
+%D by character processing of verbatim text did overcome this limitation, but we've
+%D decided not to use that slow and sometimes troublesome solution. Instead we stick
+%D to some 'old' \CONTEXT\ macros for typesetting typical \TEX\ characters.
+
+\immutable\def\lesscharacter {<} % obsolete
+\immutable\def\morecharacter {>} % obsolete
+
+\immutable\let\texescape \textbackslash
+\immutable\let\leftargument \textbraceleft
+\immutable\let\rightargument \textbraceright
+\immutable\let\inlinemathmarker \textdollar
+\immutable\def\displaymathmarker{\textdollar\textdollar}
+
+\def\buff_verbatim_special_type#1#2#% # gobbles spaces
+ {\dontleavehmode\bgroup
+ \buff_verbatim_initialize_type_one
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+ #1%
+ \bgroup % else wrong font for #2
+ \aftergroup#2%
+ \aftergroup\egroup
+ \let\nexttoken}
+
+\mutable\protected\def\tex{\buff_verbatim_special_type\texescape \relax}
+\mutable\protected\def\arg{\buff_verbatim_special_type\leftargument \rightargument}
+\mutable\protected\def\mat{\buff_verbatim_special_type\inlinemathmarker \inlinemathmarker}
+\mutable\protected\def\dis{\buff_verbatim_special_type\displaymathmarker\displaymathmarker}
+
+\aliased\let\normaltexttex\tex
+\aliased\let\normaltextarg\arg
+\aliased\let\normaltextmat\mat
+\aliased\let\normaltextdis\dis
+
+\permanent\protected\def\astype
+ {\dontleavehmode
+ \bgroup\usetypestyleandcolor\c!style\c!color\let\nexttoken}
+
+\permanent\protected\def\asciistr#1% used in some old styles
+ {\dontleavehmode\begingroup
+ \dostarttagged\t!verbatim\empty
+ \usetypestyleandcolor\c!style\c!color\detokenize{#1}%
+ \dostoptagged
+ \endgroup}
+
+%D The basic display verbatim commands are defined in an indirect way. As we
+%D will see, they are a specific case of a more general mechanism.
+
+\mutable\let\beginofverbatimlines\relax % hooks
+\mutable\let\endofverbatimlines \relax % hooks
+
+\protected\def\buff_verbatim_typing_start#1% tricky non standard lookahead
+ {\begingroup
+ \edef\currenttyping{#1}%
+ \obeylines
+ \futureexpandis[\buff_verbatim_typing_start_yes\buff_verbatim_typing_start_nop}
+
+\def\buff_verbatim_typing_start_nop
+ {\typingparameter\c!before\relax
+ \startpacked[\v!blank]%
+ \buff_verbatim_setup_line_numbering
+ \buff_verbatim_initialize_typing_one
+ \buff_verbatim_setup_keep_together
+ \normalexpanded{\buff_verbatim_type_block{\e!start\currenttyping}{\e!stop\currenttyping}}}
+
+\def\buff_verbatim_typing_start_yes[#1]%
+ {\typingparameter\c!before\relax
+ \startpacked[\v!blank]%
+ \ifcondition\validassignment{#1}%
+ \setupcurrenttyping[#1]%
+ \else
+ \doif\v!continue{#1}{\lettypingparameter\c!continue\v!yes}%
+ \fi
+ \buff_verbatim_setup_line_numbering
+ \buff_verbatim_initialize_typing_one
+ \buff_verbatim_setup_keep_together
+ \normalexpanded{\buff_verbatim_type_block{\e!start\currenttyping}{\e!stop\currenttyping}}}
+
+% \startnamedtyping[#1]
+
+\protected\def\buff_verbatim_setup_keep_together
+ {\edef\p_keeptogether{\typingparameter\c!keeptogether}%
+ \ifx\p_keeptogether\v!yes
+ \settrue \c_buff_optimize_linebreaks
+ \else
+ \setfalse\c_buff_optimize_linebreaks
+ \fi}
+
+\protected\def\buff_verbatim_type_block#1#2%
+ {\edef\p_strip{\typingparameter\c!strip}%
+ \normalexpanded{\buff_pickup
+ {_typing_}%
+ {#1}%
+ {#2}%
+ {}%
+ {\buff_verbatim_type_block_verbatim_indeed{#1}{#2}}%
+ \ifx\p_strip\v!no\zerocount\else\plusone\fi}}
+
+\protected\def\buff_verbatim_type_block_verbatim_indeed#1#2%
+ {\buff_verbatim_initialize_typing_two
+ \dostarttaggedchained\t!verbatimblock\currenttyping\??typing
+ \beginofverbatimlines
+ \dostarttagged\t!verbatimlines\empty
+ \clf_typebuffer
+ name {_typing_}%
+ strip {\typingparameter\c!strip}%
+ range {\typingparameter\c!range}%
+ tab {\typingparameter\c!tab}%
+ method {\p_buff_option}%
+ escape {\typingparameter\c!escape}%
+ nature {display}%
+ \relax
+ \dostoptagged
+ \endofverbatimlines
+ \dostoptagged
+ \begincsname#2\endcsname}
+
+\protected\def\buff_verbatim_typing_stop#1% hm, currenttyping
+ {\stoppacked
+ \typingparameter\c!after\relax
+ \useindentnextparameter\typingparameter
+ \endgroup
+ \dorechecknextindentation}
+
+%D Line numbering for files is combined with filtering, while display verbatim has
+%D the ability to continue.
+%D
+%D \starttyping
+%D \typefile[numbering=file,start=10,stop=12]{test.tex}
+%D
+%D \definetyping[code][numbering=line]
+%D
+%D \starttext
+%D \startcode
+%D ...
+%D ...
+%D \stopcode
+%D
+%D \startcode[start=continue]
+%D ...
+%D ...
+%D \stopcode
+%D
+%D \startcode[start=10]
+%D ...
+%D \stopcode
+%D \stoptyping
+
+%D Ranges:
+%D
+%D \starttyping
+%D % label:start:one
+%D
+%D \def\MyMacro
+%D {just an example}
+%D
+%D % label:stop:one
+%D
+%D \starttext
+%D
+%D % \typefile[file][range={3,6}] {whatever.tex}
+%D % \typefile[file][range={3,+2}]{whatever.tex}
+%D
+%D \typefile[file][range={label:start:one,label:stop:one}]{whatever.tex}
+%D
+%D \stoptext
+%D \stoptyping
+
+%D \macros
+%D {typefile}
+%D
+%D Typesetting files verbatim (for the moment) only supports colorization of \TEX\
+%D sources as valid option. The other setup values are inherited from display
+%D verbatim. The implementation of \type {\typefile} is straightforward:
+
+% [category] [settings] {name} % for historic reasons, all filenames are {}
+
+\mutable\let\currenttypingfile\empty
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!type\currenttyping\v!file}{\typefile[\currenttyping]}%
+\to \everydefinetyping
+
+\permanent\tolerant\protected\def\typefile[#1]#*[#2]#:#3%
+ {\begingroup
+ \ifparameter#2\or
+ \setuptyping[#1][#2]%
+ \buff_verbatim_type_file_checked{#1}{#3}%
+ \orelse\ifparameter#1\or
+ \ifhastok={#1}%
+ \setuptyping[\v!file][#1]%
+ \buff_verbatim_type_file_checked\v!file{#3}%
+ \else
+ \buff_verbatim_type_file_checked{#1}{#3}%
+ \fi
+ \else
+ \buff_verbatim_type_file_checked\v!file{#3}%
+ \fi
+ \useindentnextparameter\typingparameter % needs checking
+ \endgroup
+ \dorechecknextindentation} % needs checking
+
+\def\buff_verbatim_type_file_checked#1#2%
+ {\doifelsetypingfile{#2}
+ {\buff_verbatim_type_file_indeed{#1}\currenttypingfile}
+ {\showmessage\m!verbatims1{#2}}}
+
+\permanent\protected\def\doifelsetypingfile#1%
+ {\edef\currenttypingfile{\locfilename{#1}}%
+ \ifempty\currenttypingfile
+ \edef\currenttypingfile{\locfilename{#1.tex}}% downward compatible
+ \fi
+ \ifempty\currenttypingfile
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doiftypingfileelse\doifelsetypingfile
+
+\def\buff_verbatim_type_file_indeed#1#2% category name
+ {\edef\currenttyping{#1}%
+ \typingparameter\c!before\relax
+ \startpacked[\v!blank]
+ \buff_verbatim_setup_line_numbering
+ \buff_verbatim_initialize_typing_one
+ \buff_verbatim_initialize_typing_two
+ \dostarttaggedchained\t!verbatimblock\currenttyping\??typing
+ \beginofverbatimlines
+ \dostarttagged\t!verbatimlines\empty
+ \clf_typefile
+ name {#2}%
+ strip {\typingparameter\c!strip}%
+ range {\typingparameter\c!range}%
+ regime {\currentregime}%
+ tab {\typingparameter\c!tab}%
+ method {\p_buff_option}%
+ nature {display}%
+ \relax
+ \dostoptagged
+ \endofverbatimlines
+ \dostoptagged
+ \stoppacked
+ \typingparameter\c!after\relax}
+
+%D Line numbering:
+
+\newconditional\verbatimnumberinglines
+
+\permanent\protected\def\beginofverbatimlinenumbering
+ {\startlinenumbering
+ [\currenttyping]%
+ [\c!continue=\typingparameter\c!continue,
+ \c!method=\v!type,
+ \c!start=\typingparameter\c!start,
+ \c!stop=\typingparameter\c!stop, % ?
+ \c!step=\typingparameter\c!step]}
+
+\permanent\protected\def\endofverbatimlinenumbering
+ {\stoplinenumbering}
+
+\def\buff_verbatim_setup_line_numbering
+ {\ifdefined\startlinenumbering
+ \edef\p_numbering{\typingparameter\c!numbering}%
+ \ifx\p_numbering\v!file
+ \setuplinenumbering[\currenttyping][\c!method=\v!file]%
+ \settrue\verbatimnumberinglines
+ \orelse\ifx\p_numbering\v!line
+ \settrue\verbatimnumberinglines
+ \else
+ \setfalse\verbatimnumberinglines
+ \fi
+ \ifconditional\verbatimnumberinglines
+ \enforced\let\beginofverbatimlines\beginofverbatimlinenumbering
+ \enforced\let\endofverbatimlines \endofverbatimlinenumbering
+ \fi
+ \fi}
+
+%D \macros
+%D {filename}
+%D
+%D Typesetting filenames in monospaced fonts is possible with
+%D
+%D \starttyping
+%D \filename{here/there/filename.suffix}
+%D \stoptyping
+%D
+%D The definition is not that spectacular.
+
+\permanent\protected\def\filename#1{{\tttf\hyphenatedfilename{#1}}}
+
+%D \macros
+%D {verbatim}
+%d
+%D And a bonus macro, an unexpanded detokenize:
+
+\permanent\protected\def\verbatim#1{\detokenize{#1}}
+
+%D The setups for display verbatim and file verbatim are shared. One can adapt the
+%D extra defined typing environments, but they also default to the values below.
+%D Watch the alternative escape character.
+
+% \tttf gives problems with {\tx \type...}
+% \tt\tf does not adapt to e.g. \bf
+
+\setuptyping
+ [\c!before=\blank,
+ \c!after=\blank,
+ %\c!keeptogether=yes, % this will become default (when tested enough)
+ %\c!bodyfont=,
+ %\c!color=,
+ \c!space=\v!off,
+ \c!lines=\v!no,
+ \c!page=\v!no,
+ \c!tab=\v!yes, % what is this: \s!ascii ?
+ \c!option=\v!none,
+ \c!text=\v!no,
+ \c!style=\tt,
+ \c!indentnext=\v!yes,
+ \c!margin=\zeropoint,
+ \c!evenmargin=\zeropoint,
+ \c!oddmargin=\zeropoint,
+ \c!blank=\v!line,
+ %\c!escape=, % yes | no | {START,STOP} | default when yes: {BTEX,ETEX}
+ \c!numbering=\v!no,
+ %\c!range=,
+ \c!start=1,
+ %\c!stop=,
+ \c!step=1,
+ %\c!continue=,
+ \c!strip=\v!no] % auto or number
+
+\definetyping[\v!typing]
+
+% the \zeropoint forces the number to the margin but also works ok in text
+
+\setuplinenumbering[\v!typing][\c!location=\v!left,\c!width=\zeropoint]
+
+\setuptyping [\v!file] [\s!parent=\??typing \v!typing] % we don't want \start..\stop overload
+\setuplinenumbering[\v!file] [\s!parent=\??linenumbering\v!typing]
+
+\setuptyping [\v!buffer][\s!parent=\??typing \v!file] % we don't want \start..\stop overload
+\setuplinenumbering[\v!buffer][\s!parent=\??linenumbering\v!file]
+
+%D The setups for inline verbatim default to:
+
+\setuptype
+ [\c!space=\v!off,
+ \c!lines=\v!no,
+ %\c!color=,
+ \c!style=\tt,
+ %\c!option=\v!normal,
+ \c!page=\v!no,
+ \c!tab=\v!yes,
+ \c!compact=\v!all]
+
+%D Buffers
+
+% [name] [settings] | [name] | [settings]
+
+\permanent\protected\def\typebuffer
+ {\begingroup
+ \let\currenttyping\v!buffer
+ \buff_verbatim_type_buffer}
+
+\permanent\protected\def\typedefinedbuffer[#1]%
+ {\buff_verbatim_type_defined_buffer[\v!buffer][\thedefinedbuffer{#1}]}%
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!type\currentbuffer}{\buff_verbatim_type_defined_buffer[\v!buffer][\currentdefinedbuffer]}%
+\to \everydefinebuffer
+
+\appendtoks % \e!buffer
+ \frozen\instance\setuevalue{\e!type\currenttyping\v!buffer}{\buff_verbatim_type_buffer_class{\currenttyping}}%
+\to \everydefinetyping
+
+\tolerant\protected\def\buff_verbatim_type_buffer[#1]#*[#2]%
+ {\ifarguments
+ \buff_verbatim_type_buffer_indeed\currenttyping\empty% []
+ \or
+ \ifhastok={#1}%
+ \setupcurrenttyping[#1]%
+ \buff_verbatim_type_buffer_indeed\currenttyping\empty% [settings]
+ \else
+ \processcommalist[#1]{\buff_verbatim_type_buffer_indeed\currenttyping}% [name]
+ \fi
+ \or
+ \setupcurrenttyping[#2]%
+ \processcommalist[#1]{\buff_verbatim_type_buffer_indeed\currenttyping}% [name] [settings]
+ \fi
+ \useindentnextparameter\typingparameter
+ \endgroup
+ \dorechecknextindentation}
+
+\protected\def\buff_verbatim_type_buffer_class#1%
+ {\begingroup
+ \edef\currenttyping{#1}%
+ \buff_verbatim_type_buffer}
+
+\tolerant\protected\def\buff_verbatim_type_defined_buffer[#1]#*[#2]#*[#3]% category name settings
+ {\begingroup
+ \ifarguments\or\or\or
+ \setuptyping[#1][#3]%
+ \fi
+ \buff_verbatim_type_buffer_indeed{#1}{#2}%
+ \useindentnextparameter\typingparameter
+ \endgroup
+ \dorechecknextindentation}
+
+\protected\def\buff_verbatim_type_buffer_indeed#1#2% category name
+ {\edef\currenttyping{#1}%
+ \typingparameter\c!before\relax
+ \startpacked[\v!blank]
+ \buff_verbatim_setup_line_numbering
+ \buff_verbatim_initialize_typing_one
+ \buff_verbatim_initialize_typing_two
+ \dostarttaggedchained\t!verbatimblock{#1}\??typing
+ \beginofverbatimlines
+ \dostarttagged\t!verbatimlines\empty
+ \clf_typebuffer
+ name {#2}%
+ strip {\typingparameter\c!strip}%
+ range {\typingparameter\c!range}%
+ regime {\currentregime}%
+ tab {\typingparameter\c!tab}%
+ method {\p_buff_option}%
+ escape {\typingparameter\c!escape}%
+ nature {display}%
+ \relax
+ \dostoptagged
+ \endofverbatimlines
+ \dostoptagged
+ \stoppacked
+ \typingparameter\c!after\relax}
+
+\permanent\protected\def\typeinlinebuffer
+ {\dontleavehmode
+ \begingroup
+ \enforced\let\buff_verbatim_type_buffer_indeed\buff_verbatim_type_buffer_indeed_inline
+ \enforced\let\setupcurrenttyping\setupcurrenttype % a terrible hack but it saves code
+ \let\currenttype\empty
+ \buff_verbatim_type_buffer}
+
+\protected\def\buff_verbatim_type_buffer_indeed_inline#1#2% category name
+ {\edef\currenttype{#1}%
+ \buff_verbatim_initialize_type_one
+ \dostarttaggedchained\t!verbatim\currenttype\??type
+ \clf_typebuffer
+ name {#2}%
+ strip {\v!yes}%
+ regime {\currentregime}%
+ method {\p_buff_option}%
+ nature {inline}% is default anyway
+ \relax
+ \dostoptagged}
+
+% get : before/after of buffer
+% typing : before/after of typing
+% process : no before/after (special case anyway)
+
+% the next one is obsolete (bad name and used nowhere) so if it stays
+% the name will change
+
+\permanent\tolerant\protected\def\processbuffer[#1]#*[#2]%
+ {\writestatus{nota bene}{\string\processbuffer\space is obsolete}%
+ \begingroup
+ \ifarguments
+ \buff_verbatim_process_indeed\v!buffer\empty% []
+ \or
+ %\ifcondition\validassignment{#1}%
+ \ifhastok={#1}%
+ \setuptyping[\v!buffer][#1]%
+ \buff_verbatim_process_indeed\v!buffer\empty% [settings]
+ \else
+ \processcommalist[#1]{\buff_verbatim_process_indeed\v!buffer}% [name]
+ \fi
+ \or
+ \setuptyping[\v!buffer][#2]%
+ \processcommalist[#1]{\buff_verbatim_process_indeed\v!buffer}% [name] [settings]
+ \fi
+ \endgroup}
+
+\def\buff_verbatim_process_indeed#1#2%
+ {\edef\currenttyping{#1}%
+ \clf_processbuffer
+ name {#2}%
+ strip {\typingparameter\c!strip}%
+ tab {\typingparameter\c!tab}%
+ % method {\p_buff_option}%
+ nature {direct}%
+ \relax}
+
+% so far for obsolete
+
+% line numbering, keep broken lines together
+
+\newcount\nofverbatimlines
+
+\protected\def\buff_verbatim_begin_of_line
+ {\ifconditional\verbatimnumberinglines
+ \global\advance\nofverbatimlines\plusone
+ \c_attr_verbatimline\nofverbatimlines
+ \fi
+ \noindent
+ \buff_verbatim_set_line_margin
+ \the\everyline % maybe also after starttagged
+ \strut % after starttagged, else break !
+ \dostarttagged\t!verbatimline\empty}
+
+\protected\def\buff_verbatim_end_of_line
+ {\dostoptagged
+ \obeyedline % still needed?
+ \par
+ \ifconditional\verbatimnumberinglines
+ \c_attr_verbatimline\attributeunsetvalue
+ \fi}
+
+\protected\def\buff_verbatim_empty_line
+ {\dostoptagged
+ \ifconditional\verbatimnumberinglines
+ \par\strut\par % this will be an option where we use a signal instead of a strut
+ \else
+ \blank[\typingparameter\c!blank]%
+ \fi
+ \dostarttagged\t!verbatimlines\empty}
+
+% hooks (todo: frozen):
+
+\newcount \c_buff_verbatim_noflines
+\newcount \c_buff_verbatim_current
+\newconditional\c_buff_optimize_linebreaks
+
+\permanent\protected\def\doverbatimspace {\obeyedspace}
+
+\permanent\protected\def\doinlineverbatimstart {}
+\permanent\protected\def\doinlineverbatimstop {}
+\permanent\protected\def\doinlineverbatimbeginline {}
+\permanent\protected\def\doinlineverbatimnewline {\obeyedspace}
+\permanent\protected\def\doinlineverbatimemptyline {\obeyedspace}
+
+\permanent\protected\def\dodisplayverbatimstart {\advance\c_buff_verbatim_current\plusone
+ \buff_verbatim_begin_of_line}
+\permanent\protected\def\dodisplayverbatimstop {\buff_verbatim_end_of_line}
+\permanent\protected\def\dodisplayverbatimbeginline{\advance\c_buff_verbatim_current\plusone
+ \buff_verbatim_begin_of_line}
+\permanent\protected\def\dodisplayverbatimnewline {\buff_verbatim_end_of_line
+ \par
+ \ifconditional\c_buff_optimize_linebreaks
+ \buff_verbatim_inject_breaks
+ \fi}
+\permanent\protected\def\dodisplayverbatimemptyline{\buff_verbatim_empty_line}
+
+\protected\def\buff_verbatim_inject_breaks
+ {\ifnum\c_buff_verbatim_current=\plusone
+ \nobreak
+ \orelse\ifnum\c_buff_verbatim_noflines=\plusthree
+ \nobreak
+ \orelse\ifnum\c_buff_verbatim_current=\numexpr\c_buff_verbatim_noflines-\plusone\relax
+ \nobreak
+ \fi}
+
+\permanent\protected\def\dodisplayverbatiminitialize#1%
+ {\forgetparindent % maybe more
+ \c_buff_verbatim_noflines#1\relax
+ \c_buff_verbatim_current\zerocount}
+
+\loadmarkfile{buff-imp-default} % preloaded as otherwise spurious spaces inline due to loading
+\loadmarkfile{buff-imp-nested} % preloaded as otherwise spurious spaces inline due to loading
+\loadmarkfile{buff-imp-escaped} % for completeness
+
+%D To be considered:
+
+% \installcorenamespace{visualizer}
+%
+% \installcommandhandler \??visualizer {visualizer} \??visualizer
+%
+% \appendtoks
+% \frozen\instance\setuevalue{\currentvisualizer}{\buff_verbatim_visualize{\currentvisualizer}}%
+% \to \everydefinevisualizer
+%
+% \protected\def\buff_verbatim_visualize#1%
+% {\bgroup
+% \def\currentvisualizer{#1}%
+% \usevisualizerstyleandcolor\c!style\c!color
+% \let\next}
+
+\appendtoks
+ \enforced\def\type#1{\detokenize\expandafter{\csstring#1}}% or maybe detokenize
+ \enforced\def\tex #1{\letterbackslash#1}%
+\to \everysimplifycommands
+
+\stopcontextdefinitioncode
+
+%D This is \type {\asciimode} without the double comment hackery:
+
+\permanent\protected\def\literalmode{\setcatcodetable\txtcatcodes}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/catc-act.mkxl b/Master/texmf-dist/tex/context/base/mkxl/catc-act.mkxl
new file mode 100644
index 00000000000..4ccf22a7f68
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/catc-act.mkxl
@@ -0,0 +1,51 @@
+%D \module
+%D [ file=catc-act,
+%D version=2006.09.18,
+%D title=\CONTEXT\ Catcode Macros,
+%D subtitle=Default Catcode Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D \macros
+%D {installactivecharacter, defineactivecharacter}
+%D
+%D This module deals with some active character handling and I still need to
+%D update it to use \typ {\letcharcode <number> = \something}. This module
+%D will be simplified stepwise. Look into the \MKIV\ variant for originals.
+%D
+%D There was a time when we had active double quotes and such but we no longer
+%D do that. In fact, only the tilde and bar can be active in \CONTEXT.
+%D
+%D For those who love the more obscure features of \TEX: whenever you see
+%D uppercase magic with active characters, keep in mind that this is something
+%D deep down in the engine: handling of that specific case. The code here is
+%D not used and not really tested.
+
+\permanent\protected\def\installactivecharacter#1 %
+ {\ifchknum#1\or
+ \scratchcounter #1\edef\scratchmacro{\expandtoken\othercatcode\scratchcounter}%
+ \else
+ \scratchcounter`#1\let \scratchmacro #1%
+ \fi
+ \normalexpanded{\startextendcatcodetable\ctxcatcodes\catcode\the\scratchcounter\activecatcode\stopextendcatcodetable}%
+ \letcatcodecommand \ctxcatcodes \scratchcounter \scratchmacro
+ \ifnum\currentcatcodetable=\ctxcatcodes \setcatcodetable\ctxcatcodes \fi}
+
+\permanent\protected\def\defineactivecharacter #1 #2%
+ {\ifchknum#1\or\letcharcode#1=#2\relax\else\letcharcode`#1=#2\relax\fi}
+
+\permanent\protected\def\makecharacteractive #1 %
+ {\catcode`#1\activecatcode}
+
+\permanent\protected\def\installanddefineactivecharacter #1 #2%
+ {\normalexpanded{\installactivecharacter \utfchar{#1} }%
+ \defineactivecharacter #1 #2}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/catc-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/catc-def.mkxl
new file mode 100644
index 00000000000..a7d48714a5c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/catc-def.mkxl
@@ -0,0 +1,273 @@
+%D \module
+%D [ file=catc-def,
+%D version=2006.09.18,
+%D title=\CONTEXT\ Catcode Macros,
+%D subtitle=Default Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Contrary to \MKIV\ we define the catcode tables in one file. The original split
+%D was a prelude to a split approach where context would run on top of a basic layer
+%D (so that we could make smaller alternative formats) but that idea (from the early
+%D days of \LUATEX) was dropped when I realized that there is not common ground to
+%D cover between formats. It is simply not worth the trouble.
+
+\ifdefined\nilcatcodes \else \newcatcodetable \nilcatcodes \fi
+\ifdefined\texcatcodes \else \newcatcodetable \texcatcodes \fi
+\ifdefined\luacatcodes \else \newcatcodetable \luacatcodes \fi
+\ifdefined\notcatcodes \else \newcatcodetable \notcatcodes \fi
+\ifdefined\rlncatcodes \else \newcatcodetable \rlncatcodes \fi
+\ifdefined\vrbcatcodes \else \newcatcodetable \vrbcatcodes \fi
+\ifdefined\prtcatcodes \else \newcatcodetable \prtcatcodes \fi
+\ifdefined\ctxcatcodes \else \newcatcodetable \ctxcatcodes \fi
+\ifdefined\txtcatcodes \else \newcatcodetable \txtcatcodes \fi
+\ifdefined\tpacatcodes \else \newcatcodetable \tpacatcodes \fi % { }
+\ifdefined\tpbcatcodes \else \newcatcodetable \tpbcatcodes \fi % < >
+\ifdefined\ctdcatcodes \else \newcatcodetable \ctdcatcodes \fi % context definitions
+
+\startcatcodetable \nilcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+\stopcatcodetable
+
+\startcatcodetable \texcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\circumflexasciicode\superscriptcatcode
+ \catcode\underscoreasciicode\subscriptcatcode
+ \catcode\ampersandasciicode \alignmentcatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+ \catcode\dollarasciicode \mathshiftcatcode
+ \catcode\hashasciicode \parametercatcode
+ \catcode\commentasciicode \commentcatcode
+\stopcatcodetable
+
+\startcatcodetable \luacatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\circumflexasciicode\othercatcode
+ \catcode\underscoreasciicode\othercatcode
+ \catcode\ampersandasciicode \othercatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\commentasciicode \othercatcode
+ \catcode\hashasciicode \othercatcode
+ \catcode\barasciicode \othercatcode
+ \catcode\leftbraceasciicode \othercatcode
+ \catcode\rightbraceasciicode\othercatcode
+ \catcode\tildeasciicode \othercatcode
+ \catcode\dollarasciicode \othercatcode
+\stopcatcodetable
+
+\startcatcodetable \notcatcodes % probably less needed
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\circumflexasciicode \othercatcode
+ \catcode\underscoreasciicode \othercatcode
+ \catcode\ampersandasciicode \othercatcode
+ \catcode\tildeasciicode \othercatcode
+ \catcode\hashasciicode \othercatcode
+ \catcode\dollarasciicode \othercatcode
+ \catcode\commentasciicode \othercatcode
+ \catcode\lessthanasciicode \othercatcode
+ \catcode\morethanasciicode \othercatcode
+ \catcode\leftbraceasciicode \othercatcode
+ \catcode\rightbraceasciicode \othercatcode
+ \catcode\doublequoteasciicode \othercatcode
+ \catcode\singlequoteasciicode \othercatcode
+ \catcode\forwardslashasciicode\othercatcode
+ \catcode\backslashasciicode \othercatcode
+ \catcode\barasciicode \othercatcode
+\stopcatcodetable
+
+\startcatcodetable \rlncatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \spacecatcode % maybe we also need a vector with space being other
+ \catcode\endoffileasciicode \othercatcode
+ \catcode\circumflexasciicode \othercatcode
+ \catcode\underscoreasciicode \othercatcode
+ \catcode\ampersandasciicode \othercatcode
+ \catcode\tildeasciicode \othercatcode
+ \catcode\hashasciicode \othercatcode
+ \catcode\dollarasciicode \othercatcode
+ \catcode\commentasciicode \othercatcode
+ \catcode\lessthanasciicode \othercatcode
+ \catcode\morethanasciicode \othercatcode
+ \catcode\leftbraceasciicode \othercatcode
+ \catcode\rightbraceasciicode \othercatcode
+ \catcode\doublequoteasciicode \othercatcode
+ \catcode\singlequoteasciicode \othercatcode
+ \catcode\forwardslashasciicode\othercatcode
+ \catcode\backslashasciicode \othercatcode
+ \catcode\barasciicode \othercatcode
+\stopcatcodetable
+
+\startcatcodetable \vrbcatcodes % probably less needed
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode\othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\endoffileasciicode\othercatcode
+\stopcatcodetable
+
+\startcatcodetable \prtcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\circumflexasciicode \superscriptcatcode
+ %catcode\underscoreasciicode \subscriptcatcode
+ \catcode\underscoreasciicode \lettercatcode
+ \catcode\ampersandasciicode \alignmentcatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode \endgroupcatcode
+ \catcode\dollarasciicode \mathshiftcatcode
+ \catcode\hashasciicode \parametercatcode
+ \catcode\commentasciicode \commentcatcode
+ \catcode\atsignasciicode \lettercatcode
+ \catcode\exclamationmarkasciicode\lettercatcode
+ \catcode\questionmarkasciicode \lettercatcode
+ \catcode\tildeasciicode \activecatcode
+ \catcode\barasciicode \activecatcode
+\stopcatcodetable
+
+\startcatcodetable \ctxcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ % \catcode\circumflexasciicode\superscriptcatcode
+ % \catcode\underscoreasciicode\subscriptcatcode
+ % \catcode\ampersandasciicode \alignmentcatcode
+ \catcode\underscoreasciicode\othercatcode
+ \catcode\circumflexasciicode\othercatcode
+ \catcode\ampersandasciicode \othercatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+ \catcode\dollarasciicode \mathshiftcatcode
+ \catcode\hashasciicode \parametercatcode
+ \catcode\commentasciicode \commentcatcode
+ \catcode\tildeasciicode \activecatcode
+ \catcode\barasciicode \activecatcode
+\stopcatcodetable
+
+\startcatcodetable \tpacatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\endoffileasciicode \othercatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+\stopcatcodetable
+
+\startcatcodetable \tpbcatcodes
+ \catcode\tabasciicode \othercatcode
+ \catcode\endoflineasciicode \othercatcode
+ \catcode\formfeedasciicode \othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\endoffileasciicode \othercatcode
+ \catcode\lessthanasciicode \begingroupcatcode
+ \catcode\morethanasciicode \endgroupcatcode
+\stopcatcodetable
+
+\setnewconstant \doublecommentsignal "10FF25 %% < 110000 (last valid range)
+
+\startcatcodetable \txtcatcodes
+ \catcode\tabasciicode \spacecatcode
+ \catcode\endoflineasciicode \endoflinecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\spaceasciicode \spacecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode\endgroupcatcode
+ \catcode\doublecommentsignal\commentcatcode
+\stopcatcodetable
+
+\startcatcodetable \ctdcatcodes
+ \catcode\tabasciicode \ignorecatcode
+ \catcode\endoflineasciicode \ignorecatcode
+ \catcode\formfeedasciicode \ignorecatcode
+ \catcode\spaceasciicode \ignorecatcode
+ \catcode\endoffileasciicode \ignorecatcode
+ \catcode\circumflexasciicode \superscriptcatcode % candidate
+ \catcode\underscoreasciicode \lettercatcode
+ \catcode\ampersandasciicode \alignmentcatcode
+ % \catcode\colonasciicode \lettercatcode % candidate
+ \catcode\backslashasciicode \escapecatcode
+ \catcode\leftbraceasciicode \begingroupcatcode
+ \catcode\rightbraceasciicode \endgroupcatcode
+ \catcode\dollarasciicode \mathshiftcatcode
+ \catcode\hashasciicode \parametercatcode
+ \catcode\commentasciicode \commentcatcode
+ \catcode\atsignasciicode \lettercatcode
+ \catcode\exclamationmarkasciicode\lettercatcode
+ \catcode\questionmarkasciicode \lettercatcode
+ \catcode\tildeasciicode \activecatcode
+ \catcode\barasciicode \activecatcode
+\stopcatcodetable
+
+%D From now on we can use the protection mechanisms.
+
+\permanent\protected\def\unprotect {\pushcatcodetable\catcodetable\prtcatcodes}
+\permanent\protected\def\protect {\popcatcodetable}
+
+\permanent\protected\def\starttexcode {\pushcatcodetable\catcodetable\prtcatcodes}
+\permanent\protected\def\stoptexcode {\popcatcodetable}
+
+\permanent\protected\def\startcontextcode {\pushcatcodetable\catcodetable\ctxcatcodes}
+\permanent\protected\def\stopcontextcode {\popcatcodetable}
+
+\permanent\protected\def\startcontextdefinitioncode {\pushcatcodetable\catcodetable\ctdcatcodes}
+\permanent\protected\def\stopcontextdefinitioncode {\popcatcodetable}
+
+%D We have a few active characters (left):
+
+\letcatcodecommand \ctxcatcodes \barasciicode \relax
+\letcatcodecommand \ctxcatcodes \tildeasciicode \relax
+
+%D Because some characters have a special meaning, we provide shortcuts to their
+%D character representation. Some will be overloaded (which might change).
+
+\permanent\chardef \^ = \circumflexasciicode
+\permanent\chardef \_ = \underscoreasciicode
+\permanent\chardef \& = \ampersandasciicode
+\permanent\chardef \% = \commentasciicode
+\permanent\chardef \# = \hashasciicode
+\permanent\chardef \$ = \dollarasciicode
+\permanent\chardef \{ = \leftbraceasciicode
+\permanent\chardef \} = \rightbraceasciicode
+\permanent\chardef \\ = \backslashasciicode
+\permanent\chardef \| = \barasciicode
+
+\pushoverloadmode
+ \catcodetable \ctxcatcodes
+ \aliased\let\defaultcatcodetable\ctxcatcodes
+\popoverloadmode
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/catc-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/catc-ini.lmt
new file mode 100644
index 00000000000..ede8216a640
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/catc-ini.lmt
@@ -0,0 +1,44 @@
+if not modules then modules = { } end modules ['catc-ini'] = {
+ version = 1.001,
+ comment = "companion to catc-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+catcodes = catcodes or { }
+catcodes.numbers = catcodes.numbers or { }
+catcodes.names = catcodes.names or { }
+
+storage.register("catcodes/numbers", catcodes.numbers, "catcodes.numbers")
+storage.register("catcodes/names", catcodes.names, "catcodes.names")
+
+local numbers = catcodes.numbers
+local names = catcodes.names
+
+-- We register the catcode tables in the \type {tex} namespace as we have been doing
+-- right from the start (when there wasn't much in that namespace). Normally
+-- registration of a table only happens when we're in so called ini mode. Also, the
+-- number of tables is rather small.
+
+function catcodes.register(name,number)
+ numbers[name] = number
+ local cnn = names[number]
+ if cnn then
+ cnn[#cnn+1] = name
+ else
+ names[number] = { name }
+ end
+ tex[name] = number -- downward compatible
+end
+
+-- At runtime we need to populate the \type {tex} namespace again.
+
+for name, number in next, numbers do
+ tex[name] = number -- downward compatible
+end
+
+-- We catch wrong usage.
+
+table.setmetatableindex(numbers,function(t,k) if type(k) == "number" then t[k] = k return k end end)
+table.setmetatableindex(names, function(t,k) if type(k) == "string" then t[k] = k return k end end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/catc-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/catc-ini.mkxl
new file mode 100644
index 00000000000..5b33db981d6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/catc-ini.mkxl
@@ -0,0 +1,274 @@
+%D \module
+%D [ file=catc-ini,
+%D version=2006.09.18,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Catcode Handling,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{catc-ini}{autosuffix}
+
+\unprotect
+
+%D A long standing wish has been the availability of catcode arrays. Because
+%D traditional \TEX\ does not provide this we implement a fake method in the \MKII\
+%D file. There is some overlap in code with \MKII\ but we take that for granted.
+%D Also, in \MKIV\ less active characters are used.
+
+% \begingroup
+% \letcharcode\newlineasciicode\relax \xdef\outputnewlinechar{\Uchar\newlineasciicode}
+% \endgroup
+
+% \endlinechar = \endoflineasciicode % appended to input lines
+% \newlinechar = \newlineasciicode % can be used in write
+
+% rather special and used in writing to file: \let\par\outputnewlinechar
+
+% \permanent\protected\def\initializenewlinechar % operating system dependent
+% {\begingroup
+% \enforced\letcharcode\newlineasciicode\relax
+% \newlinechar\newlineasciicode
+% \xdef\outputnewlinechar{\Uchar\newlineasciicode}%
+% \endgroup}
+
+%D We predefine some prefixes ahead of syst-aux and mult-sys. We reserve 8 slots for
+%D catcodes. (This active mess probably needs an update some day.)
+
+\installsystemnamespace{catcodelet} % let : \let
+\installsystemnamespace{catcodedef} % def : \def
+\installsystemnamespace{catcodeued} % ued : \protected\def
+\installsystemnamespace{catcodeget} % \meaning
+
+\installsystemnamespace{catcodetablet}
+\installsystemnamespace{catcodetablen}
+
+\newcount\c_syst_catcodes_n \c_syst_catcodes_n\zerocount % 0 = signal, so advance before allocate
+\newcount\c_syst_catcodes_a
+\newcount\c_syst_catcodes_b
+\newcount\c_syst_catcodes_c
+
+\permanent\protected\def\newcatcodetable#1% we could move the cctdefcounter to lua
+ {\global\advance\c_syst_catcodes_n\plusone
+ \gdefcsname\??catcodetablen\number\c_syst_catcodes_n\endcsname{\string#1}% logging
+ \immutable\integerdef#1\c_syst_catcodes_n
+ \ctxlua{catcodes.register("\expandafter\gobbleoneargument\string#1",\number#1)}}
+
+\newtoks\everysetdefaultcatcodes
+
+\everysetdefaultcatcodes % this might get dropped
+ {\catcode\backslashasciicode\othercatcode
+ \catcode\endoflineasciicode\othercatcode
+ \catcode\spaceasciicode \othercatcode
+ \catcode\commentasciicode \othercatcode
+ \catcode\delasciicode \othercatcode}
+
+\permanent\protected\def\startcatcodetable#1#2\stopcatcodetable
+ {\begingroup
+ \catcodetable\inicatcodes
+ \the\everysetdefaultcatcodes
+ #2%
+ \savecatcodetable#1\relax
+ \endgroup}
+
+\permanent\let\stopcatcodetable\relax
+
+\permanent\protected\def\startextendcatcodetable#1#2\stopextendcatcodetable
+ {\begingroup
+ \catcodetable#1\relax
+ \globaldefs\plusone
+ #2%
+ \globaldefs\zerocount
+ \endgroup}
+
+\permanent\let\stopextendcatcodetable\relax
+
+\permanent\protected\def\permitcircumflexescape % to be used grouped
+ {\catcode\circumflexasciicode\superscriptcatcode}
+
+\aliased\let\permitcaretescape\permitcircumflexescape
+
+% ==
+%
+% \protected\def\startextendcatcodetable#1#2\stopextendcatcodetable
+% {\bgroup
+% \scratchcounter\the\catcodetable
+% \catcodetable #1 #2
+% \catcodetable\scratchcounter
+% \egroup}
+
+%D The next command can be defined in a cleaner way in the MkIV way but we want
+%D to have a fast one with a minimal chance for interference. Do we still need
+%D this complex mechanism? Probably not. Future versions of \MKIV\ might only use
+%D active characters for very special cases.
+
+\setnewconstant\c_syst_catcodes_hack\tildeasciicode
+
+%D Once a catcode is assigned, the next assignments will happen faster. However,
+%D redefinitions probably happen seldom so it's sort of overkill.
+
+\permanent\protected\def\letcatcodecommand{\afterassignment\syst_catcodes_let_a\c_syst_catcodes_a}
+\permanent\protected\def\defcatcodecommand{\afterassignment\syst_catcodes_def_a\c_syst_catcodes_a}
+\permanent\protected\def\uedcatcodecommand{\afterassignment\syst_catcodes_ued_a\c_syst_catcodes_a}
+
+\def\syst_catcodes_let_a{\afterassignment\syst_catcodes_let_b\c_syst_catcodes_b}
+\def\syst_catcodes_def_a{\afterassignment\syst_catcodes_def_b\c_syst_catcodes_b}
+\def\syst_catcodes_ued_a{\afterassignment\syst_catcodes_ued_b\c_syst_catcodes_b}
+
+\def\syst_catcodes_let_b % each time
+ {\ifcsname\??catcodelet\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\syst_catcodes_let_c
+ \fi}
+
+\def\syst_catcodes_def_b % each time
+ {\ifcsname\??catcodedef\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\syst_catcodes_def_c
+ \fi}
+
+\def\syst_catcodes_ued_b % each time
+ {\ifcsname\??catcodeued\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\syst_catcodes_ued_c
+ \fi}
+
+\def\syst_catcodes_let_c % only first time
+ {\frozen\enforced\gdefcsname\??catcodelet\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\expandafter\endcsname\expandafter
+ {\enforced\letcsname\??catcodeget\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname}%
+ \syst_catcodes_reinstate_unexpanded
+ \csname\??catcodelet\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname}
+
+\def\syst_catcodes_def_c % only first time (we could use \normalexpanded here)
+ {\frozen\enforced\gdefcsname\??catcodedef\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\expandafter\endcsname
+ \expandafter##\expandafter1\expandafter
+ {\frozen\enforced\defcsname\??catcodeget\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname{##1}}%
+ \syst_catcodes_reinstate_normal
+ \csname\??catcodedef\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname}
+
+\def\syst_catcodes_ued_c % only first time
+ {\frozen\enforced\gdefcsname\??catcodeued\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\expandafter\endcsname
+ \expandafter##\expandafter1\expandafter
+ {\frozen\enforced\protected\defcsname\??catcodeget\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname{##1}}%
+ \syst_catcodes_reinstate_unexpanded
+ \csname\??catcodeued\number\c_syst_catcodes_a:\number\c_syst_catcodes_b\endcsname}
+
+\permanent\def\reinstatecatcodecommand{\afterassignment\syst_catcodes_reinstate_normal\c_syst_catcodes_b}
+
+%D This can be used when a direct definition has been done and the selector has been
+%D lost. A problem is that \type {\next} needs to be unique (as it gets bound) (still?).
+
+\def\syst_catcodes_reinstate_normal
+ {\begingroup
+ \edef\temp{\noexpand\catcodecommand{\number\c_syst_catcodes_b}}%
+ \global\letcharcode\c_syst_catcodes_b\temp
+ \endgroup}
+
+\def\syst_catcodes_reinstate_unexpanded
+ {\begingroup
+ \protected\edef\temp{\noexpand\catcodecommand{\number\c_syst_catcodes_b}}%
+ \global\letcharcode\c_syst_catcodes_b\temp
+ \endgroup}
+
+\newconstant\defaultcatcodetable
+
+\permanent\def\catcodecommand#1%
+ {\csname\??catcodeget\number
+ \ifcsname\??catcodeget\number\currentcatcodetable:\number#1\endcsname
+ \currentcatcodetable \else \defaultcatcodetable
+ \fi
+ :\number#1\endcsname}
+
+%D \macros
+%D {restorecatcodes,pushcatcodetable,popcatcodetable}
+%D
+%D We're not finished dealing \CATCODES\ yet. In \CONTEXT\ we use only one auxiliary
+%D file, which deals with tables of contents, registers, two pass tracking,
+%D references etc. This file, as well as files concerning graphics, is processed
+%D when needed, which can be in the mid of typesetting verbatim. However, when
+%D reading in data in verbatim mode, we should temporary restore the normal
+%D \CATCODES, and that's exactly what the next macros do. Saving the catcodes can be
+%D disabled by saying \type {\localcatcodestrue}. In \MKIV\ instead we can push and
+%D pop catcode tables and as we keep track of used tables users seldom need to deal
+%D with this themselves.
+
+\newcount\c_syst_catcodes_level
+
+\permanent\protected\def\pushcatcodetable
+ {\advance\c_syst_catcodes_level\plusone
+ \syst_catcodes_trace_push
+ \expandafter\integerdef\csname\??catcodetablet\number\c_syst_catcodes_level\endcsname\currentcatcodetable}
+
+\permanent\protected\def\popcatcodetable
+ {\ifcase\c_syst_catcodes_level
+ \syst_catcodes_trace_nesting_error
+ \else
+ \catcodetable\csname\??catcodetablet\number\c_syst_catcodes_level\endcsname
+ \syst_catcodes_trace_pop
+ \advance\c_syst_catcodes_level\minusone
+ \fi}
+
+\protected\def\syst_catcodes_trace_nesting_error
+ {\immediate\write\statuswrite{}%
+ \immediate\write\statuswrite{Fatal error: catcode push/pop mismatch. Fix this! (restore level: \number\c_syst_catcodes_level)}\wait\end
+ \immediate\write\statuswrite{}}
+
+\permanent\protected\def\restorecatcodes % takes previous level
+ {\ifnum\c_syst_catcodes_level>\plusone
+ \catcodetable\csname\??catcodetablet\number\numexpr\c_syst_catcodes_level-1\relax\endcsname
+ \fi}
+
+\permanent\protected\def\setcatcodetable#1%
+ {\catcodetable#1%
+ \syst_catcodes_trace_set}
+
+%D Handy for debugging (not that we ever use(d) it):
+%D
+%D \starttyping
+%D \tracecatcodetables
+%D \stoptyping
+
+\permanent\protected\def\tracecatcodetables
+ {\def\syst_catcodes_trace_set {\syst_catcodes_trace{set \catcodetablename\space at \number\c_syst_catcodes_level}}%
+ \def\syst_catcodes_trace_push{\syst_catcodes_trace{push \catcodetablename\space from \syst_catcodes_prev\space at \number\c_syst_catcodes_level}}%
+ \def\syst_catcodes_trace_pop {\syst_catcodes_trace{pop \catcodetablename\space to \syst_catcodes_prev\space at \number\c_syst_catcodes_level}}}
+
+\def\syst_catcodes_trace#1{\immediate\write\statuswrite{[#1]}}
+
+\def\syst_catcodes_prev
+ {\ifnum\numexpr\c_syst_catcodes_level-1\relax>\zerocount
+ \csname\??catcodetablen\number\csname\??catcodetablet\number\numexpr\c_syst_catcodes_level-1\relax\endcsname\endcsname
+ \else
+ -%
+ \fi}
+
+\permanent\def\catcodetablename
+ {\ifnum\currentcatcodetable>\zerocount
+ \csname\??catcodetablen\number\currentcatcodetable\endcsname
+ \else
+ -%
+ \fi}
+
+\let\syst_catcodes_trace_set \empty
+\let\syst_catcodes_trace_push\empty
+\let\syst_catcodes_trace_pop \empty
+
+% \tracecatcodetables
+
+\protect
+
+%D We still have to define these so let's do that now:
+
+\newcatcodetable \inicatcodes
+\initcatcodetable\inicatcodes
+
+\mutable\let\currentcatcodetable\catcodetable
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/catc-sym.mkxl b/Master/texmf-dist/tex/context/base/mkxl/catc-sym.mkxl
new file mode 100644
index 00000000000..a96ffb865c0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/catc-sym.mkxl
@@ -0,0 +1,96 @@
+%D \module
+%D [ file=catc-sym,
+%D version=1997.01.03, % moved code
+%D title=\CONTEXT\ Catcode Macros,
+%D subtitle=Some Handy Constants,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The following definitions can be used for mapping special characters onto
+%D letters, although we cheat a bit: they actually are of category other but
+%D for users that distinction doesn't matter here. The definitions in \MKII\
+%D and \MKIV\ look more impressive but here we use a \LUAMETATEX\ feature
+%D that permits a more direct definition (that also looks nice with the
+%D \CONTEXT\ syntax highlighting):
+
+\immutable\edef\letterleftbrace {\expandtoken \othercatcode \leftbraceasciicode}
+\immutable\edef\letterrightbrace {\expandtoken \othercatcode \rightbraceasciicode}
+\immutable\edef\letterampersand {\expandtoken \othercatcode \ampersandasciicode}
+\immutable\edef\letterless {\expandtoken \othercatcode \lessthanasciicode}
+\immutable\edef\lettermore {\expandtoken \othercatcode \morethanasciicode}
+\immutable\edef\letterhash {\expandtoken \othercatcode \hashasciicode}
+\immutable\edef\letterdoublequote {\expandtoken \othercatcode \doublequoteasciicode}
+\immutable\edef\lettersinglequote {\expandtoken \othercatcode \singlequoteasciicode}
+\immutable\edef\letterdollar {\expandtoken \othercatcode \dollarasciicode}
+\immutable\edef\letterpercent {\expandtoken \othercatcode \percentasciicode}
+\immutable\edef\letterhat {\expandtoken \othercatcode \circumflexasciicode}
+\immutable\edef\letterunderscore {\expandtoken \othercatcode \underscoreasciicode}
+\immutable\edef\letterbar {\expandtoken \othercatcode \barasciicode}
+\immutable\edef\lettertilde {\expandtoken \othercatcode \tildeasciicode}
+\immutable\edef\letterbackslash {\expandtoken \othercatcode \backslashasciicode}
+\immutable\edef\letterslash {\expandtoken \othercatcode \forwardslashasciicode}
+\immutable\edef\letterquestionmark {\expandtoken \othercatcode \questionmarkasciicode}
+\immutable\edef\letterexclamationmark {\expandtoken \othercatcode \exclamationmarkasciicode}
+\immutable\edef\letterat {\expandtoken \othercatcode \atsignasciicode}
+\immutable\edef\lettercolon {\expandtoken \othercatcode \colonasciicode}
+\immutable\edef\letterhyphen {\expandtoken \othercatcode \hyphenasciicode}
+
+\immutable\edef\letterleftparenthesis {\expandtoken \othercatcode \leftparentasciicode}
+\immutable\edef\letterrightparenthesis {\expandtoken \othercatcode \rightparentasciicode}
+\immutable\edef\letterleftbracket {\expandtoken \othercatcode \leftbracketasciicode}
+\immutable\edef\letterrightbracket {\expandtoken \othercatcode \rightbracketasciicode}
+
+\aliased\let\letterescape \letterbackslash
+\aliased\let\letterbgroup \letterleftbrace
+\aliased\let\letteregroup \letterrightbrace
+\aliased\let\letteropenbrace \letterleftbrace
+\aliased\let\letterclosebrace\letterrightbrace
+
+\unprotect
+
+%immutable\edef\s!n_u_l_token{\expandtoken \othercatcode \zerocount} % nul(l)
+%immutable\edef\s!s_o_h_token{\expandtoken \othercatcode \plusone } % start of header ^^^^0001
+%immutable\edef\s!s_t_x_token{\expandtoken \othercatcode \plustwo } % start of text ^^^^0002
+%immutable\edef\s!e_t_x_token{\expandtoken \othercatcode \plusthree} % end of text ^^^^0003
+\immutable\edef\s!e_o_t_token{\expandtoken \othercatcode \plusfour } % end of transmission ^^^^0004
+%immutable\edef\s!e_n_q_token{\expandtoken \othercatcode \plusfive } % enquiry
+%immutable\edef\s!a_c_k_token{\expandtoken \othercatcode \plussix } % aknowledgement
+
+\protect
+
+%D \macros
+%D {uncatcodespecials,setnaturalcatcodes,setnormalcatcodes,
+%D uncatcodecharacters,uncatcodeallcharacters,
+%D uncatcodespacetokens}
+%D
+%D The following macros are more or less replaced by switching to a catcode table
+%D (which we simulate in \MKII) but we keep them for convenience and compatibility.
+%D Some old engine code has been removed. A few ar still used a few times so I need
+%D to clean that up.
+
+%permanent\protected\def\uncatcodespecials {\setcatcodetable\nilcatcodes \uncatcodespacetokens}
+%permanent\protected\def\setnaturalcatcodes {\setcatcodetable\nilcatcodes}
+\permanent\protected\def\setnormalcatcodes {\setcatcodetable\ctxcatcodes} % maybe \texcatcodes
+%permanent\protected\def\uncatcodecharacters {\setcatcodetable\nilcatcodes} % was fast version, gone now
+%permanent\protected\def\uncatcodeallcharacters{\setcatcodetable\nilcatcodes} % was slow one, with restore
+
+\permanent\protected\def\uncatcodespacetokens
+ {\catcode\spaceasciicode \spacecatcode
+ \catcode\tabasciicode \spacecatcode
+ \catcode\formfeedasciicode \endoflinecatcode
+ \catcode\endoflineasciicode\endoflinecatcode
+ \catcode\delasciicode \ignorecatcode}
+
+%D These two are probably no longer needed, but we keep them for a while. Some more
+%D explanation can be foun din the mkiv variant of this module, where we also
+%D discuss side effects. It's time to move on, so these might go away some day.
+
+\aliased \let\rescan \scantextokens
+\permanent\def\rescanwithsetup#1#2{\begingroup\directsetup{#1}\scantextokens{#2}\endgroup}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/char-act.mkxl b/Master/texmf-dist/tex/context/base/mkxl/char-act.mkxl
new file mode 100644
index 00000000000..3fb4d2da65d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/char-act.mkxl
@@ -0,0 +1,99 @@
+%D \module
+%D [ file=char-act,
+%D version=2006.12.05,
+%D title=\CONTEXT\ Character Support,
+%D subtitle=Active,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Character Support / Active}
+
+\unprotect
+
+\ifdefined\page_otr_fill_and_eject_page \else \let\page_otr_fill_and_eject_page\relax \fi % forward reference
+
+%D \macros
+%D {obeyedspace, obeyedtab, obeyedline, obeyedpage}
+%D
+%D We have followed Knuth in naming macros that make \SPACE, \NEWLINE\ and \NEWPAGE\
+%D active and assigning them \type {\obeysomething}, but first we set some default
+%D values.
+%D
+%D These are expandable:
+
+\permanent\def\obeyedspace{\space}
+\permanent\def\obeyedtab {\obeyedspace}
+\permanent\def\obeyedline {\par}
+\permanent\def\obeyedpage {\page_otr_fill_and_eject_page}
+
+%D \macros
+%D {controlspace,setcontrolspaces}
+%D
+%D First we define \type {\obeyspaces}. When we want visible spaces (control spaces)
+%D we only have to adapt the definition of \type {\obeyedspace} to:
+
+\immutable\chardef\asciispacechar\spaceasciicode % a real space character
+
+\permanent\protected\def\naturalspace{\asciispacechar}
+\permanent\protected\def\controlspace{\hbox{\asciispacechar}} % rather tex, we need the unicode value
+\permanent\protected\def\normalspaces{\catcode\spaceasciicode\spacecatcode}
+
+% \bgroup
+% \catcode\spaceasciicode\activecatcode
+% \protected\gdef\obeyspaces {\catcode\spaceasciicode\activecatcode\def {\obeyedspace }}
+% \protected\gdef\setcontrolspaces{\catcode\spaceasciicode\activecatcode\def {\controlspace}}
+% \egroup
+
+%permanent\protected\def\obeyspaces {\catcode\spaceasciicode\activecatcode\letcharcode\spaceasciicode\obeyedspace }
+%permanent\protected\def\setcontrolspaces{\catcode\spaceasciicode\activecatcode\letcharcode\spaceasciicode\controlspace}
+
+%D \macros
+%D {obeytabs, obeylines, obeypages,ignoretabs, ignorelines, ignorepages}
+%D
+%D Next we take care of \NEWLINE\ and \NEWPAGE\ and because we want to be able to
+%D typeset listings that contain \TAB, we have to handle those too. Because we have
+%D to redefine the \NEWPAGE\ character locally, we redefine the meaning of this
+%D (often already) active character.
+
+% \expandafter\def\activeformfeedtoken{\par}
+
+\letcharcode\formfeedasciicode\par
+
+%D The following indirect definitions enable us to implement all kind of \type
+%D {\obeyed} handlers.
+
+% \protected\def\obeytabs {\catcode\tabasciicode \activecatcode\expandafter\def\activetabtoken {\obeyedtab }}
+% \protected\def\obeylines {\catcode\endoflineasciicode\activecatcode\expandafter\def\activeendoflinetoken{\obeyedline}}
+% \protected\def\obeypages {\catcode\formfeedasciicode \activecatcode\expandafter\def\activeformfeedtoken {\obeyedpage}}
+
+% \protected\def\ignoretabs {\catcode\tabasciicode \activecatcode\expandafter\def\activetabtoken {\obeyedspace}}
+% \protected\def\ignorelines{\catcode\endoflineasciicode\activecatcode\expandafter\def\activeendoflinetoken{\obeyedspace}}
+% \protected\def\ignorepages{\catcode\formfeedasciicode \ignorecatcode}
+% \protected\def\ignoreeofs {\catcode\endoffileasciicode\ignorecatcode}
+
+% but ... as we don't want to freeze to \obeyedspace etc which can be set after
+% \obeyspaces, we use an idirectness
+
+\def\syst_obeyed_space{\obeyedspace}
+\def\syst_obeyed_tab {\obeyedtab}
+\def\syst_obeyed_line {\obeyedline}
+\def\syst_obeyed_page {\obeyedpage}
+
+\permanent\protected\def\obeyspaces {\catcode\spaceasciicode \activecatcode\enforced\letcharcode\spaceasciicode \syst_obeyed_space}
+\permanent\protected\def\obeytabs {\catcode\tabasciicode \activecatcode\enforced\letcharcode\tabasciicode \syst_obeyed_tab }
+\permanent\protected\def\obeylines {\catcode\endoflineasciicode\activecatcode\enforced\letcharcode\endoflineasciicode\syst_obeyed_line }
+\permanent\protected\def\obeypages {\catcode\formfeedasciicode \activecatcode\enforced\letcharcode\formfeedasciicode \syst_obeyed_page }
+
+\permanent\protected\def\ignoretabs {\catcode\tabasciicode \activecatcode\enforced\letcharcode\tabasciicode \syst_obeyed_space}
+\permanent\protected\def\ignorelines{\catcode\endoflineasciicode\activecatcode\enforced\letcharcode\endoflineasciicode\syst_obeyed_space}
+\permanent\protected\def\ignorepages{\catcode\formfeedasciicode \ignorecatcode}
+\permanent\protected\def\ignoreeofs {\catcode\endoffileasciicode\ignorecatcode}
+
+\permanent\protected\def\setcontrolspaces{\catcode\spaceasciicode\activecatcode\enforced\letcharcode\spaceasciicode\controlspace}
+
+\endinput \protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/char-enc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/char-enc.mkxl
new file mode 100644
index 00000000000..d0dd3d20a5d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/char-enc.mkxl
@@ -0,0 +1,18 @@
+%D \module
+%D [ file=char-enc,
+%D version=2006.08.20,
+%D title=\CONTEXT\ Character Support,
+%D subtitle=Encodings,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Character Support / Encodings}
+
+\registerctxluafile{char-enc}{}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/char-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/char-ini.mkxl
new file mode 100644
index 00000000000..6965598a960
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/char-ini.mkxl
@@ -0,0 +1,126 @@
+%D \module
+%D [ file=char-ini,
+%D version=2006.08.20,
+%D title=\CONTEXT\ Character Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Character Support / Initialization}
+
+\registerctxluafile{char-fio}{}
+\registerctxluafile{char-map}{} % maybe we will load this someplace else
+\registerctxluafile{char-tex}{autosuffix}
+\registerctxluafile{char-ent}{}
+
+\unprotect
+
+% ¨äëïöüÿ
+% ´áćéíĺńóŕśúýź
+% ˙ċėġiż
+% ¯āēīōū
+% ˝őű
+% ˆâĉêĝĥîĵôŝûŵŷ
+% `àèìòùỳ
+% ¸çķļņŗşţ
+% ˛ąęįų
+% ˚åů
+% ˘ăĕğĭŏŭ
+% ˇčďěľňřšťž
+% ˜ãĩñõũ
+
+% \def\checkedchar#1% #2%
+% {\relax\iffontchar\font#1 \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi{\char#1}}
+%
+% impossible in math mode so there always fallback (till we have gyre):
+%
+% use \normalUchar when possible .. the next one is nice for documents and it also accepts
+% 0x prefixed numbers
+
+\permanent\def\utfchar #1{\clf_utfchar {#1}} % todo: define public at lua end
+\permanent\def\safechar#1{\clf_safechar{#1}} % todo: define public at lua end
+
+\permanent\protected\def\Ux #1{\Uchar\numexpr"#1\relax} % used in xml (sometimes overloaded)
+\permanent \def\eUx#1{\Uchar\numexpr"#1\relax} % used in xml (sometimes overloaded)
+
+\permanent\def\expandUx{\enforced\let\Ux\eUx}
+
+\permanent \def\checkedchar {\relax\ifmmode\expandafter\checkedmathchar\else\expandafter\checkedtextchar\fi} % #1#2
+\permanent \def\checkedmathchar#1#2{#2}
+\permanent\protected\def\checkedtextchar #1{\clf_doifelsecharinfont\numexpr#1\relax{\char#1}} % {#2}
+
+\newconditional\prefermathovertextchar
+
+\permanent\protected\def\textormathchar#1%
+ {\begingroup
+ \scratchcounter#1\relax
+ \ifmmode
+ \char\scratchcounter
+ \else
+ \ifconditional\prefermathovertextchar
+ % dirty trick .. maybe let this adapt to bf too ?
+ \setfontid\fontid\textfont\c_font_fam_mr
+ \fi
+ \iffontchar\font\scratchcounter
+ \char\scratchcounter
+ \else
+ \normalstartimath\char\scratchcounter\normalstopimath
+ \fi
+ \fi
+ \endgroup}
+
+\permanent\protected\def\textormathchars#1%
+ {{\font_text_or_mathchars#1\relax}}
+
+\protected\def\font_text_or_mathchars#1#2\relax
+ {\relax\ifmmode
+ #1#2%
+ \orelse\iffontchar\font`#1\relax
+ #1#2\relax
+ \else
+ \normalstartimath#1#2\normalstopimath
+ \fi}
+
+%D The codes are stored in the format, so we don't need to reinitialize them (unless
+%D of course we have adapted the table). It is on the agenda to do this with \type
+%D {tex.lccode} cum suis once they're available.
+
+% Is setting up vrb tpa and tpb needed?
+
+% move to lua side
+
+\clf_setlettercatcodes\texcatcodes
+\clf_setlettercatcodes\ctxcatcodes
+\clf_setlettercatcodes\notcatcodes
+%clf_setlettercatcodes\mthcatcodes
+\clf_setlettercatcodes\vrbcatcodes % hm ... we need to hyphemate
+\clf_setlettercatcodes\prtcatcodes
+\clf_setlettercatcodes\tpacatcodes % hm
+\clf_setlettercatcodes\tpbcatcodes % hm
+\clf_setlettercatcodes\txtcatcodes
+
+\clf_setothercatcodes \rlncatcodes
+
+\clf_setactivecatcodes\ctxcatcodes
+\clf_setactivecatcodes\notcatcodes
+\clf_setactivecatcodes\prtcatcodes
+
+\clf_setcharacternames\ctxcatcodes
+
+\permanent\def\chardescription#1{\clf_chardescription#1\relax}
+
+% experiment (watch out: this is global and very font dependent when used with
+% casing mechanisms)
+%
+% \overloaduppercase{0xDF}{0x1E9E}}
+% \overloaduppercase{0xDF}{0x53,0x53}
+
+\permanent\protected\def\overloaduppercase{\clf_overloaduppercase} % todo: define public at lua end
+\permanent\protected\def\overloadlowercase{\clf_overloadlowercase} % todo: define public at lua end
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/char-tex.lmt b/Master/texmf-dist/tex/context/base/mkxl/char-tex.lmt
new file mode 100644
index 00000000000..3d8f6a2598e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/char-tex.lmt
@@ -0,0 +1,994 @@
+if not modules then modules = { } end modules ['char-tex'] = {
+ version = 1.001,
+ comment = "companion to char-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local lpeg = lpeg
+local tonumber, next, type = tonumber, next, type
+local format, find, gmatch, match, gsub = string.format, string.find, string.gmatch, string.match, string.gsub
+local utfchar, utfbyte = utf.char, utf.byte
+local concat, tohash = table.concat, table.tohash
+local P, C, R, S, V, Cs, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.V, lpeg.Cs, lpeg.Cc
+
+local lpegpatterns = lpeg.patterns
+local lpegmatch = lpeg.match
+local utfchartabletopattern = lpeg.utfchartabletopattern
+
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+
+local context = context
+local commands = commands
+
+local characters = characters
+local texcharacters = { }
+characters.tex = texcharacters
+local utffilters = characters.filters.utf
+
+local allocate = utilities.storage.allocate or function() return { } end
+local mark = utilities.storage.mark or allocate
+
+local is_character = characters.is_character
+local is_letter = characters.is_letter
+local is_command = characters.is_command
+local is_spacing = characters.is_spacing
+local is_mark = characters.is_mark
+local is_punctuation = characters.is_punctuation
+
+local data = characters.data if not data then return end
+local blocks = characters.blocks
+
+local trace_defining = false trackers.register("characters.defining", function(v) characters_defining = v end)
+
+local report_defining = logs.reporter("characters")
+
+--[[ldx--
+<p>In order to deal with 8-bit output, we need to find a way to go from <l n='utf'/> to
+8-bit. This is handled in the <l n='luatex'/> engine itself.</p>
+
+<p>This leaves us problems with characters that are specific to <l n='tex'/> like
+<type>{}</type>, <type>$</type> and alike. We can remap some chars that tex input files
+are sensitive for to a private area (while writing to a utility file) and revert then
+to their original slot when we read in such a file. Instead of reverting, we can (when
+we resolve characters to glyphs) map them to their right glyph there. For this purpose
+we can use the private planes 0x0F0000 and 0x100000.</p>
+--ldx]]--
+
+local low = allocate()
+local high = allocate()
+local escapes = allocate()
+local special = "~#$%^&_{}\\|" -- "~#$%{}\\|"
+
+local private = {
+ low = low,
+ high = high,
+ escapes = escapes,
+}
+
+utffilters.private = private
+
+for ch in gmatch(special,".") do
+ local cb
+ if type(ch) == "number" then
+ cb, ch = ch, utfchar(ch)
+ else
+ cb = utfbyte(ch)
+ end
+ if cb < 256 then
+ escapes[ch] = "\\" .. ch
+ low[ch] = utfchar(0x0F0000 + cb)
+ if ch == "%" then
+ ch = "%%" -- nasty, but we need this as in replacements (also in lpeg) % is interpreted
+ end
+ high[utfchar(0x0F0000 + cb)] = ch
+ end
+end
+
+local tohigh = lpeg.replacer(low) -- frozen, only for basic tex
+local tolow = lpeg.replacer(high) -- frozen, only for basic tex
+
+lpegpatterns.utftohigh = tohigh
+lpegpatterns.utftolow = tolow
+
+function utffilters.harden(str)
+ return lpegmatch(tohigh,str)
+end
+
+function utffilters.soften(str)
+ return lpegmatch(tolow,str)
+end
+
+private.escape = utf.remapper(escapes) -- maybe: ,"dynamic"
+private.replace = utf.remapper(low) -- maybe: ,"dynamic"
+private.revert = utf.remapper(high) -- maybe: ,"dynamic"
+
+--[[ldx--
+<p>We get a more efficient variant of this when we integrate
+replacements in collapser. This more or less renders the previous
+private code redundant. The following code is equivalent but the
+first snippet uses the relocated dollars.</p>
+
+<typing>
+[󰀤x󰀤] [$x$]
+</typing>
+--ldx]]--
+
+-- using the tree-lpeg-mapper would be nice but we also need to deal with end-of-string
+-- cases: "\"\i" and don't want "\relax" to be seen as \r e lax" (for which we need to mess
+-- with spaces
+
+local accentmapping = allocate {
+ ['"'] = { [""] = "¨",
+ A = "Ä", a = "ä",
+ E = "Ë", e = "ë",
+ I = "Ï", i = "ï", ["ı"] = "ï", ["\\i"] = "ï",
+ O = "Ö", o = "ö",
+ U = "Ü", u = "ü",
+ Y = "Ÿ", y = "ÿ",
+ },
+ ["'"] = { [""] = "´",
+ A = "Á", a = "á",
+ C = "Ć", c = "ć",
+ E = "É", e = "é",
+ I = "Í", i = "í", ["ı"] = "í", ["\\i"] = "í",
+ L = "Ĺ", l = "ĺ",
+ N = "Ń", n = "ń",
+ O = "Ó", o = "ó",
+ R = "Ŕ", r = "ŕ",
+ S = "Ś", s = "ś",
+ U = "Ú", u = "ú",
+ Y = "Ý", y = "ý",
+ Z = "Ź", z = "ź",
+ },
+ ["."] = { [""] = "˙",
+ C = "Ċ", c = "ċ",
+ E = "Ė", e = "ė",
+ G = "Ġ", g = "ġ",
+ I = "İ", i = "i", ["ı"] = "i", ["\\i"] = "i",
+ Z = "Ż", z = "ż",
+ },
+ ["="] = { [""] = "¯",
+ A = "Ā", a = "ā",
+ E = "Ē", e = "ē",
+ I = "Ī", i = "ī", ["ı"] = "ī", ["\\i"] = "ī",
+ O = "Ō", o = "ō",
+ U = "Ū", u = "ū",
+ },
+ ["H"] = { [""] = "˝",
+ O = "Ő", o = "ő",
+ U = "Ű", u = "ű",
+ },
+ ["^"] = { [""] = "ˆ",
+ A = "Â", a = "â",
+ C = "Ĉ", c = "ĉ",
+ E = "Ê", e = "ê",
+ G = "Ĝ", g = "ĝ",
+ H = "Ĥ", h = "ĥ",
+ I = "Î", i = "î", ["ı"] = "î", ["\\i"] = "î",
+ J = "Ĵ", j = "ĵ",
+ O = "Ô", o = "ô",
+ S = "Ŝ", s = "ŝ",
+ U = "Û", u = "û",
+ W = "Ŵ", w = "ŵ",
+ Y = "Ŷ", y = "ŷ",
+ },
+ ["`"] = { [""] = "`",
+ A = "À", a = "à",
+ E = "È", e = "è",
+ I = "Ì", i = "ì", ["ı"] = "ì", ["\\i"] = "ì",
+ O = "Ò", o = "ò",
+ U = "Ù", u = "ù",
+ Y = "Ỳ", y = "ỳ",
+ },
+ ["c"] = { [""] = "¸",
+ C = "Ç", c = "ç",
+ K = "Ķ", k = "ķ",
+ L = "Ļ", l = "ļ",
+ N = "Ņ", n = "ņ",
+ R = "Ŗ", r = "ŗ",
+ S = "Ş", s = "ş",
+ T = "Ţ", t = "ţ",
+ },
+ ["k"] = { [""] = "˛",
+ A = "Ą", a = "ą",
+ E = "Ę", e = "ę",
+ I = "Į", i = "į",
+ U = "Ų", u = "ų",
+ },
+ ["r"] = { [""] = "˚",
+ A = "Å", a = "å",
+ U = "Ů", u = "ů",
+ },
+ ["u"] = { [""] = "˘",
+ A = "Ă", a = "ă",
+ E = "Ĕ", e = "ĕ",
+ G = "Ğ", g = "ğ",
+ I = "Ĭ", i = "ĭ", ["ı"] = "ĭ", ["\\i"] = "ĭ",
+ O = "Ŏ", o = "ŏ",
+ U = "Ŭ", u = "ŭ",
+ },
+ ["v"] = { [""] = "ˇ",
+ C = "Č", c = "č",
+ D = "Ď", d = "ď",
+ E = "Ě", e = "ě",
+ L = "Ľ", l = "ľ",
+ N = "Ň", n = "ň",
+ R = "Ř", r = "ř",
+ S = "Š", s = "š",
+ T = "Ť", t = "ť",
+ Z = "Ž", z = "ž",
+ },
+ ["~"] = { [""] = "˜",
+ A = "Ã", a = "ã",
+ I = "Ĩ", i = "ĩ", ["ı"] = "ĩ", ["\\i"] = "ĩ",
+ N = "Ñ", n = "ñ",
+ O = "Õ", o = "õ",
+ U = "Ũ", u = "ũ",
+ },
+}
+
+texcharacters.accentmapping = accentmapping
+
+local accent_map = allocate { -- incomplete
+ ['~'] = "̃" , -- ̃ Ẽ
+ ['"'] = "̈" , -- ̈ Ë
+ ["`"] = "̀" , -- ̀ È
+ ["'"] = "́" , -- ́ É
+ ["^"] = "̂" , -- ̂ Ê
+ -- ̄ Ē
+ -- ̆ Ĕ
+ -- ̇ Ė
+ -- ̉ Ẻ
+ -- ̌ Ě
+ -- ̏ Ȅ
+ -- ̑ Ȇ
+ -- ̣ Ẹ
+ -- ̧ Ȩ
+ -- ̨ Ę
+ -- ̭ Ḙ
+ -- ̰ Ḛ
+}
+
+-- local accents = concat(table.keys(accentmapping)) -- was _map
+
+local function remap_accent(a,c,braced)
+ local m = accentmapping[a]
+ if m then
+ local n = m[c]
+ if n then
+ return n
+ end
+ end
+-- local m = accent_map[a]
+-- if m then
+-- return c .. m
+-- elseif braced then -- or #c > 0
+ if braced then -- or #c > 0
+ return "\\" .. a .. "{" .. c .. "}"
+ else
+ return "\\" .. a .. " " .. c
+ end
+end
+
+local commandmapping = allocate {
+ ["aa"] = "å", ["AA"] = "Å",
+ ["ae"] = "æ", ["AE"] = "Æ",
+ ["cc"] = "ç", ["CC"] = "Ç",
+ ["i"] = "ı", ["j"] = "ȷ",
+ ["ij"] = "ij", ["IJ"] = "IJ",
+ ["l"] = "ł", ["L"] = "Ł",
+ ["o"] = "ø", ["O"] = "Ø",
+ ["oe"] = "œ", ["OE"] = "Œ",
+ ["sz"] = "ß", ["SZ"] = "SZ", ["ss"] = "ß", ["SS"] = "ß", -- uppercase: ẞ
+}
+
+texcharacters.commandmapping = commandmapping
+
+local ligaturemapping = allocate {
+ ["''"] = "”",
+ ["``"] = "“",
+ ["--"] = "–",
+ ["---"] = "—",
+}
+
+-- Older accent handling code can be found in char-def.lua but in the meantime
+-- we moved on. First the one with commands:
+
+local untex, pattern
+
+local function toutfpattern()
+ if not untex then
+ local hash = { }
+ for k, v in next, accentmapping do
+ for kk, vv in next, v do
+ if (k >= "a" and k <= "z") or (k >= "A" and k <= "Z") then
+ hash[ "\\"..k.." "..kk ] = vv
+ hash["{\\"..k.." "..kk.."}"] = vv
+ else
+ hash["\\" ..k ..kk ] = vv
+ hash["{\\"..k ..kk.."}"] = vv
+ end
+ hash["\\" ..k.."{"..kk.."}" ] = vv
+ hash["{\\"..k.."{"..kk.."}}"] = vv
+ end
+ end
+ for k, v in next, commandmapping do
+ hash["\\"..k.." "] = v
+ hash["{\\"..k.."}"] = v
+ hash["{\\"..k.." }"] = v
+ end
+ for k, v in next, ligaturemapping do
+ hash[k] = v
+ end
+ untex = utfchartabletopattern(hash) / hash
+ end
+ return untex
+end
+
+local function prepare()
+ pattern = Cs((toutfpattern() + P(1))^0)
+ return pattern
+end
+
+local function textoutf(str,strip)
+ if str == "" then
+ return str
+ elseif not find(str,"\\",1,true) then
+ return str
+ -- elseif strip then
+ else
+ return lpegmatch(pattern or prepare(),str)
+ end
+end
+
+texcharacters.toutfpattern = toutfpattern
+texcharacters.toutf = textoutf
+
+-- print(texcharacters.toutf([[\~{Z}]],true))
+-- print(texcharacters.toutf([[\'\i]],true))
+-- print(texcharacters.toutf([[\'{\i}]],true))
+-- print(texcharacters.toutf([[\"{e}]],true))
+-- print(texcharacters.toutf([[\" {e}]],true))
+-- print(texcharacters.toutf([[{\"{e}}]],true))
+-- print(texcharacters.toutf([[{\" {e}}]],true))
+-- print(texcharacters.toutf([[{\l}]],true))
+-- print(texcharacters.toutf([[{\l }]],true))
+-- print(texcharacters.toutf([[\v{r}]],true))
+-- print(texcharacters.toutf([[fo{\"o}{\ss}ar]],true))
+-- print(texcharacters.toutf([[H{\'a}n Th\^e\llap{\raise 0.5ex\hbox{\'{\relax}}} Th{\'a}nh]],true))
+
+-- Next the ones without backslash
+
+local untex, pattern
+
+local function toutfpattern()
+ if not untex then
+ local hash = { }
+ for k, v in next, accentmapping do
+ for kk, vv in next, v do
+ hash[k..kk] = vv
+ end
+ end
+ for k, v in next, commandmapping do
+ hash[k] = v
+ end
+ for k, v in next, ligaturemapping do
+ hash[k] = v
+ end
+ untex = utfchartabletopattern(hash) / hash
+ end
+ return untex
+end
+
+local function prepare()
+ pattern = Cs((toutfpattern() + P(1))^0)
+ return pattern
+end
+
+local function textoutf(str)
+ return lpegmatch(pattern or prepare(),str)
+end
+
+texcharacters.strtoutfpattern = toutfpattern
+texcharacters.strtextoutf = textoutf
+
+local collapse = utffilters.collapse
+local combine = utffilters.combine
+
+if not interfaces then return end
+
+local implement = interfaces.implement
+
+local pattern
+
+local verbosemarks = characters.verbosemarks
+
+if verbosemarks then
+
+ mark(verbosemarks)
+
+else
+
+ verbosemarks = allocate {
+ ["stroke"] = utfchar(0x02F), ["slash"] = utfchar(0x02F),
+ ["middle dot"] = utfchar(0x0B7),
+
+ ["grave"] = utfchar(0x300),
+ ["acute"] = utfchar(0x301),
+ ["circumflex"] = utfchar(0x302),
+ ["tilde"] = utfchar(0x303),
+ ["macron"] = utfchar(0x304), ["line"] = utfchar(0x304),
+ ["overline"] = utfchar(0x305),
+ ["breve"] = utfchar(0x306),
+ ["dot"] = utfchar(0x307),
+ ["dieresis"] = utfchar(0x308), ["diaeresis"] = utfchar(0x308),
+ ["hook"] = utfchar(0x309),
+ ["ring"] = utfchar(0x30A),
+ ["double acute"] = utfchar(0x30B), ["hungarumlaut"] = utfchar(0x30B), -- tex speak
+ ["caron"] = utfchar(0x30C),
+ ["vertical line"] = utfchar(0x30D),
+ ["double vertical line"] = utfchar(0x30E),
+ ["double grave"] = utfchar(0x30F),
+ ["inverted breve"] = utfchar(0x311),
+ ["dot below"] = utfchar(0x323),
+ ["ring below"] = utfchar(0x325),
+ ["cedilla"] = utfchar(0x327), ["comma below"] = utfchar(0x327),
+ ["ogonek"] = utfchar(0x328),
+ ["caron below"] = utfchar(0x32C),
+ ["circumflex below"] = utfchar(0x32D),
+ ["tilde below"] = utfchar(0x330),
+ ["macron below"] = utfchar(0x331), ["line below"] = utfchar(0x331),
+
+ ["hook below"] = utfchar(0x1FA9D),
+ }
+
+ characters.verbosemarks = verbosemarks
+
+ if storage then
+ storage.register("characters/verbosemarks", verbosemarks, "characters.verbosemarks")
+ end
+
+end
+
+local function prepare()
+ pattern = Cs((utfchartabletopattern(verbosemarks) / verbosemarks + lpegpatterns.space/"" + lpegpatterns.utf8character)^0)
+ return pattern
+end
+
+local hash = table.setmetatableindex(function(t,k)
+ local f = ""
+ k = lpegmatch(pattern or prepare(),k) or k
+ local v = collapse(k) or k -- char specials
+-- print("collapse",k,v)
+ if k ~= v then
+ goto DONE
+ end
+ v = combine(k) or k -- with specials
+-- print("combine",k,v)
+ if k ~= v then
+ goto DONE
+ end
+ v = commandmapping[k] or k
+-- print("command",k,v)
+ if k ~= v then
+ f = "\\"
+ goto DONE
+ end
+ v = textoutf(k) or k
+-- print("utf",k,v)
+ if k ~= v then
+ f = "\\"
+ goto DONE
+ end
+ ::DONE::
+ report_defining("instead of old school '%s%s' you can input the utf sequence %s",f,k,v)
+ t[k] = v
+ return v
+end)
+
+implement {
+ name = "chr",
+ arguments = "argument",
+ public = true,
+ actions = function(str)
+ context(hash[str]) -- expandable
+ end
+}
+
+function texcharacters.safechar(n) -- was characters.safechar
+ local c = data[n]
+ if c and c.contextname then
+ return "\\" .. c.contextname
+ else
+ return utfchar(n)
+ end
+end
+
+if not context or not commands then
+ -- used in e.g. mtx-bibtex
+ return
+end
+
+-- all kind of initializations
+
+local tex = tex
+local texsetlccode = tex.setlccode
+local texsetsfcode = tex.setsfcode
+local texsetcatcode = tex.setcatcode
+
+local contextsprint = context.sprint
+local ctxcatcodes = catcodes.numbers.ctxcatcodes
+
+local texsetmacro = tokens.setters.macro
+local texsetchar = tokens.setters.char
+
+-- function texcharacters.defineaccents()
+-- local ctx_dodefineaccentcommand = context.dodefineaccent
+-- local ctx_dodefineaccent = context.dodefineaccent
+-- local ctx_dodefinecommand = context.dodefinecommand
+-- for accent, group in next, accentmapping do
+-- ctx_dodefineaccentcommand(accent)
+-- for character, mapping in next, group do
+-- ctx_dodefineaccent(accent,character,mapping)
+-- end
+-- end
+-- for command, mapping in next, commandmapping do
+-- ctx_dodefinecommand(command,mapping)
+-- end
+-- os.exit()
+-- end
+
+function texcharacters.defineaccents()
+ local ctx_dodefinecombine = context.dodefinecombine
+ local ctx_dodefinecommand = context.dodefinecommand
+ for verbose, mark in next, verbosemarks do
+ ctx_dodefinecombine((gsub(verbose," ","")),mark)
+ end
+ for command, mapping in next, commandmapping do
+ ctx_dodefinecommand(command,mapping)
+ end
+end
+
+implement { -- a waste of scanner but consistent
+ name = "defineaccents",
+ actions = texcharacters.defineaccents
+}
+
+--[[ldx--
+<p>Instead of using a <l n='tex'/> file to define the named glyphs, we
+use the table. After all, we have this information available anyway.</p>
+--ldx]]--
+
+local function to_number(s)
+ local n = tonumber(s)
+ if n then
+ return n
+ end
+ return tonumber(match(s,'^"(.*)$'),16) or 0
+end
+
+implement {
+ name = "utfchar",
+ actions = { to_number, utfchar, contextsprint },
+ arguments = "string"
+}
+
+implement {
+ name = "safechar",
+ actions = { to_number, texcharacters.safechar, contextsprint },
+ arguments = "string"
+}
+
+implement {
+ name = "uchar",
+ arguments = { "integer", "integer" },
+ actions = function(h,l)
+ context(utfchar(h*256+l))
+ end
+}
+
+tex.uprint = commands.utfchar
+
+-- in contect we don't use lc and uc codes (in fact in luatex we should have a hf code)
+-- so at some point we might drop this
+
+-- The following get set at the TeX end:
+
+local forbidden = tohash {
+ 0x000A0, -- zs nobreakspace <self>
+ 0x000AD, -- cf softhyphen <self>
+ -- 0x00600, -- cf arabicnumber <self>
+ -- 0x00601, -- cf arabicsanah <self>
+ -- 0x00602, -- cf arabicfootnotemarker <self>
+ -- 0x00603, -- cf arabicsafha <self>
+ -- 0x00604, -- cf arabicsamvat <self>
+ -- 0x00605, -- cf arabicnumberabove <self>
+ -- 0x0061C, -- cf arabiclettermark <self>
+ -- 0x006DD, -- cf arabicendofayah <self>
+ -- 0x008E2, -- cf arabicdisputedendofayah <self>
+ 0x02000, -- zs enquad <self>
+ 0x02001, -- zs emquad <self>
+ 0x02002, -- zs enspace \kern .5\emwidth
+ 0x02003, -- zs emspace \hskip \emwidth
+ 0x02004, -- zs threeperemspace <self>
+ 0x02005, -- zs fourperemspace <self>
+ 0x02006, -- zs sixperemspace <self>
+ 0x02007, -- zs figurespace <self>
+ 0x02008, -- zs punctuationspace <self>
+ 0x02009, -- zs breakablethinspace <self>
+ 0x0200A, -- zs hairspace <self>
+ 0x0200B, -- cf zerowidthspace <self>
+ 0x0200C, -- cf zwnj <self>
+ 0x0200D, -- cf zwj <self>
+ 0x0202F, -- zs narrownobreakspace <self>
+ 0x0205F, -- zs medspace \textormathspace +\medmuskip 2
+ -- 0x03000, -- zs ideographicspace <self>
+ -- 0x0FEFF, -- cf zerowidthnobreakspace \penalty \plustenthousand \kern \zeropoint
+}
+
+local csletters = characters.csletters -- also a signal that we have initialized
+local activated = { }
+local sfmode = "unset" -- unset, traditional, normal
+local block_too = false
+
+directives.register("characters.blockstoo",function(v) block_too = v end)
+
+-- If this is something that is not documentwide and used a lot, then we
+-- need a more clever approach (trivial but not now).
+
+local function setuppersfcodes(v,n)
+ if sfstate ~= "unset" then
+ report_defining("setting uppercase sf codes to %a",n)
+ for u, chr in next, data do
+ if chr.category == "lu" then
+ texsetsfcode(u,n)
+ end
+ end
+ end
+ sfstate = v
+end
+
+directives.register("characters.spaceafteruppercase",function(v)
+ if v == "traditional" then
+ setuppersfcodes(v,999)
+ elseif v == "normal" then
+ setuppersfcodes(v,1000)
+ end
+end)
+
+if not csletters then
+
+ csletters = allocate()
+ characters.csletters = csletters
+
+ report_defining("setting up character related codes and commands")
+
+ if sfstate == "unset" then
+ sfstate = "traditional"
+ end
+
+ local traditional = sfstate == "traditional"
+
+ for u, chr in next, data do -- will move up
+ local contextname = chr.contextname
+ local category = chr.category
+ local isletter = is_letter[category]
+ if contextname then
+ if is_character[category] then
+ if chr.unicodeslot < 128 then
+ if isletter then
+ local c = utfchar(u)
+ csletters[c] = u
+ end
+ else
+ local c = utfchar(u)
+ if isletter and u >= 32 and u <= 65536 then
+ csletters[c] = u
+ end
+ end
+ if isletter then
+ local lc = chr.lccode
+ local uc = chr.uccode
+ if not lc then
+ chr.lccode = u
+ lc = u
+ elseif type(lc) == "table" then
+ lc = u
+ end
+ if not uc then
+ chr.uccode = u
+ uc = u
+ elseif type(uc) == "table" then
+ uc = u
+ end
+ texsetlccode(u,lc,uc)
+ if traditional and category == "lu" then
+ texsetsfcode(code,999)
+ end
+ end
+ elseif is_command[category] and not forbidden[u] then
+ -- skip
+ elseif is_mark[category] then
+ texsetlccode(u,u,u) -- for hyphenation
+ end
+ elseif isletter then
+ csletters[utfchar(u)] = u
+ local lc, uc = chr.lccode, chr.uccode
+ if not lc then
+ chr.lccode = u
+ lc = u
+ elseif type(lc) == "table" then
+ lc = u
+ end
+ if not uc then
+ chr.uccode = u
+ uc = u
+ elseif type(uc) == "table" then
+ uc = u
+ end
+ texsetlccode(u,lc,uc)
+ if traditional and category == "lu" then
+ texsetsfcode(code,999)
+ end
+ elseif is_mark[category] then
+ texsetlccode(u,u,u) -- for hyphenation
+ end
+ end
+
+ if blocks_too then
+ -- this slows down format generation by over 10 percent
+ for k, v in next, blocks do
+ if v.catcode == "letter" then
+ local first = v.first
+ local last = v.last
+ local gaps = v.gaps
+ if first and last then
+ for u=first,last do
+ csletters[utfchar(u)] = u
+ --
+ -- texsetlccode(u,u,u) -- self self
+ --
+ end
+ end
+ if gaps then
+ for i=1,#gaps do
+ local u = gaps[i]
+ csletters[utfchar(u)] = u
+ --
+ -- texsetlccode(u,u,u) -- self self
+ --
+ end
+ end
+ end
+ end
+ end
+
+ if storage then
+ storage.register("characters/csletters", csletters, "characters.csletters")
+ end
+
+ function characters.setcharacternames(ctt)
+ for u, chr in next, data do -- will move up
+ local contextname = chr.contextname
+ local category = chr.category
+ local isletter = is_letter[category]
+ if contextname then
+ if is_character[category] then
+ if chr.unicodeslot < 128 then
+ if isletter then
+ texsetmacro(contextname,utfchar(u),"immutable")
+ else
+ texsetchar(contextname,u,"immutable")
+ end
+ else
+ texsetmacro(contextname,utfchar(u),"immutable")
+ end
+ elseif is_command[category] and not forbidden[u] then
+ texsetmacro(contextname,utfchar(u),"immutable")
+ end
+ end
+ end
+ end
+
+else
+ mark(csletters)
+end
+
+lpegpatterns.csletter = utfchartabletopattern(csletters)
+
+-- The engine presets the letters to 11 (always).
+
+function characters.setlettercatcodes(cct)
+ if trace_defining then
+ report_defining("assigning letter catcodes to catcode table %a",cct)
+ end
+ local saved = tex.catcodetable
+ tex.catcodetable = cct
+ texsetcatcode(0x200C,11) -- non-joiner
+ texsetcatcode(0x200D,11) -- joiner
+ for c, u in next, csletters do
+ texsetcatcode(u,11)
+ end
+ tex.catcodetable = saved
+end
+
+function characters.setothercatcodes(cct)
+ if trace_defining then
+ report_defining("assigning other catcodes to catcode table %a",cct)
+ end
+ local saved = tex.catcodetable
+ tex.catcodetable = cct
+ for u=65,90 do
+ texsetcatcode(u,12)
+ end
+ for u=97,122 do
+ texsetcatcode(u,12)
+ end
+ tex.catcodetable = saved
+end
+
+function characters.setactivecatcodes(cct)
+ local saved = tex.catcodetable
+ tex.catcodetable = cct
+ for i=1,#activated do
+ local u = activated[i]
+ texsetcatcode(u,13)
+ if trace_defining then
+ report_defining("character %U (%s) is active in set %a",u,data[u].description,cct)
+ end
+ end
+ tex.catcodetable = saved
+end
+
+--[[ldx--
+<p>Setting the lccodes is also done in a loop over the data table.</p>
+--ldx]]--
+
+implement {
+ name = "chardescription",
+ arguments = "integer",
+ actions = function(slot)
+ local d = data[slot]
+ if d then
+ context(d.description)
+ end
+ end,
+}
+
+if characters.setcharacternames then -- only in ini mode
+
+ implement { name = "setlettercatcodes", scope = "private", actions = characters.setlettercatcodes, arguments = "integer" }
+ implement { name = "setothercatcodes", scope = "private", actions = characters.setothercatcodes, arguments = "integer" }
+ implement { name = "setactivecatcodes", scope = "private", actions = characters.setactivecatcodes, arguments = "integer" }
+ implement { name = "setcharacternames", scope = "private", actions = characters.setcharacternames, arguments = "integer" }
+
+end
+
+-- experiment (some can move to char-ini.lua)
+
+local function overload(c,u,code,codes)
+ local c = tonumber(c)
+ if not c then
+ return
+ end
+ local u = utilities.parsers.settings_to_array(u)
+ local n = #u
+ if n == 0 then
+ return
+ end
+ local t = nil
+ if n == 1 then
+ t = tonumber(u[1])
+ else
+ t = { }
+ for i=1,n do
+ t[#t+1] = tonumber(u[i])
+ end
+ end
+ if t then
+ data[c][code] = t
+ characters[codes][c] = nil
+ end
+end
+
+implement {
+ name = "overloaduppercase",
+ arguments = "2 strings",
+ actions = function(c,u)
+ overload(c,u,"uccode","uccodes")
+ end
+}
+
+implement {
+ name = "overloadlowercase",
+ arguments = "2 strings",
+ actions = function(c,u)
+ overload(c,u,"lccode","lccodes")
+ end
+}
+
+-- Just for fun we support keywords:
+--
+-- \startTEXpage[offset=10pt]
+-- abg"
+-- \sl \showboxes
+-- \accent `" h%
+-- \accent `" x%
+-- \accent yoffset .2ex `" x
+-- \accent yoffset 1.1ex `x x%
+-- \stopTEXpage
+--
+-- We could do this:
+--
+-- \startTEXpage[offset=10pt]
+-- abg"
+-- \sl \showboxes
+-- \withaccent `" h%
+-- \withaccent `" x%
+-- \withaccent yoffset .2ex `" x
+-- \withaccent yoffset 1.1ex accent `x base `x%
+-- \stopTEXpage
+--
+-- But only when users demand it:
+--
+-- do
+--
+-- local new_glyph = nodes.pool.glyph
+--
+-- local scankeyword = tokens.scanners.keyword
+-- local scaninteger = tokens.scanners.integer
+-- local scandimension = tokens.scanners.dimension
+-- local scantoken = tokens.scanners.token
+--
+-- implement {
+-- name = "withaccent",
+-- public = true,
+-- protected = true,
+-- actions = function()
+-- local xoffset = 0
+-- local yoffset = 0
+-- local accent = false
+-- local base = false
+-- local zwj = 0x200D
+-- while true do
+-- if scankeyword("xoffset") then
+-- xoffset = scandimension()
+-- elseif scankeyword("yoffset") then
+-- yoffset = scandimension()
+-- elseif scankeyword("accent") then
+-- accent = scaninteger()
+-- elseif scankeyword("base") then
+-- base = scaninteger()
+-- else
+-- break
+-- end
+-- end
+-- if not accent then
+-- accent = scaninteger()
+-- end
+-- if not base then
+-- local nxttok = scantoken()
+-- base = nxttok.cmdname == "char_number" and scaninteger() or nxttok.index
+-- end
+-- if base and accent and base > 0 and accent > 0 then
+-- base = new_glyph(true,base)
+-- zwj = new_glyph(true,zwj)
+-- accent = new_glyph(true,accent)
+-- local slant = fonts.hashes.parameters[true].slant / 65536 -- a la tex
+-- local xheight = fonts.hashes.parameters[true].xheight -- hm, compensated for glyphscale?
+-- accent.xoffset = xoffset - .5*(base.width -accent.width) + .5*(base.height-accent.height) * slant
+-- accent.yoffset = yoffset - (xheight - accent.height)
+-- accent.left = accent.width
+-- accent.options = accent.options | 0x40 | 0x80
+-- context.dontleavehmode()
+-- context(base)
+-- context(zwj)
+-- context(accent)
+-- end
+-- end,
+-- }
+--
+-- end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/char-utf.mkxl b/Master/texmf-dist/tex/context/base/mkxl/char-utf.mkxl
new file mode 100644
index 00000000000..a7cbab34522
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/char-utf.mkxl
@@ -0,0 +1,44 @@
+%D \module
+%D [ file=char-utf,
+%D version=2006.12.05,
+%D title=\CONTEXT\ Character Support,
+%D subtitle=Unicode UTF,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Character Support / Unicode UTF}
+
+%D After a bit of experimenting we reached a clean state where \UTF\ 8, 16 and 32
+%D were supported as well as collapsing (combining sequences). Writing the code was
+%D a relaxed experience, not in the last place because it was accompanied by
+%D listening to those nice Vienna Teng cd's (who decided that making music was more
+%D fun than programming, but then, she may not know \TEX\ and \LUA).
+
+\unprotect
+
+\registerctxluafile{char-def}{}
+\registerctxluafile{char-ini}{}
+\registerctxluafile{char-utf}{}
+\registerctxluafile{char-cjk}{}
+
+%D We enable collapsing (combining characters) by default, but since the source
+%D files are rather simple, we postpone the initialization till runtime.
+
+% \appendtoks
+% \clf_enableutf % not needed when we create a format so we do it now
+% \to \everyjob
+
+%D The next one influences input parsing.
+%D
+%D \starttyping
+%D \definecomposedutf 318 108 126 % lcaron
+%D \stoptyping
+
+\permanent\protected\def\definecomposedutf#1 #2 #3 {\clf_addgrapheme{#1}{#2}{#3}}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/chem-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/chem-ini.mkxl
new file mode 100644
index 00000000000..b88d4d8346e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/chem-ini.mkxl
@@ -0,0 +1,53 @@
+%D \module
+%D [ file=chem-ini,
+%D version=2008.03.06,
+%D subtitle=Chemistry,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: \mathscriptsmode
+
+\writestatus{loading}{ConTeXt Chemistry Macros / Initialization} % might become Inline
+
+\registerctxluafile{chem-ini}{}
+
+\unprotect
+
+% todo: use \mathscriptsmode if needed
+
+%D \macros
+%D {molecule}
+%D
+%D Quick and dirty:
+%D
+%D \starttyping
+%D \permanent\protected\def\molecule#1%
+%D {$\enablesupersub\tf#1$}
+%D \stoptyping
+%D
+%D Using \LUA:
+%D
+%D \startbuffer
+%D \molecule{H_2SO_4^-2}
+%D \molecule{H_2SO_4^{-2}}
+%D \molecule{H_2SO_4^{-2{x}}}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\protected\def\molecule#1% todo: can become a public implementor
+ {\clf_molecule{#1}}
+
+%D For old times sake:
+
+\permanent\protected\def\chem#1#2#3%
+ {\dontleavehmode\begingroup#1\lohi{#2}{#3}\endgroup}
+
+\protect \endinput
+
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/chem-str.mkxl b/Master/texmf-dist/tex/context/base/mkxl/chem-str.mkxl
new file mode 100644
index 00000000000..6c591225e33
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/chem-str.mkxl
@@ -0,0 +1,751 @@
+%D \module
+%D [ file=chem-ini,
+%D version=2009.05.13,
+%D subtitle=Chemistry,
+%D author=Hans Hagen \& Alan Braslau,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The original \PPCHTEX\ code was written in pure \TEX\, although later we made
+%D the move from \PICTEX\ to \METAPOST\. The current implementation is a mix between
+%D \TEX\, \LUA\ and \METAPOST. Although the first objective is to get a compatible
+%D but better implementation, later versions might provide more,
+
+%D NOT YET LMTX'd
+
+\writestatus{loading}{ConTeXt Chemistry Macros / Structure}
+
+\registerctxluafile{chem-str}{}
+
+% We have a slightly different interface. This is unchanged:
+%
+% \startchemical[axis=on]
+% \chemical[SIX,ROT2,B,R6,SUB1,FIVE,ROT1,B][1]
+% \stopchemical
+%
+% Here we use chemicalformula instead, so no longer a mix:
+%
+% \startchemicalformula
+% \chemical{2H_2}{top}{bottom}
+% \chemical{PLUS}{top}{bottom}
+% \chemical{O_2}{top}{bottom}
+% \chemical{GIVES}{top}{bottom}
+% \chemical{2H_2O}{top}{bottom}
+% \stopchemicalformula
+%
+% \startchemicalformula
+% \chemical{2H_2}
+% \chemical{PLUS}
+% \chemical{O_2}
+% \chemical{GIVES}
+% \chemical{2H_2O}
+% \stopchemicalformula
+%
+% The inline variant has only one argument:
+%
+% \chemical{2H_2,PLUS,O_2,GIVES,2H_2O}
+
+\unprotect
+
+\installcorenamespace{chemical}
+\installcorenamespace{chemicalsymbol}
+\installcorenamespace{chemicalframed}
+\installcorenamespace{chemicalsize}
+
+% \installsimplecommandhandler \??chemical {chemical} \??chemical % no \define...
+\installcommandhandler \??chemical {chemical} \??chemical % no \define...
+
+\aliased\let\setupchemicals \setupchemical
+\aliased\let\definechemicals\definechemical
+
+%D We use a dedicated framed macro instead of inheriting one. This is both
+%D a historical and practical reason (like shared keys with different meaning
+%D that could clash, e.g.\ align).
+
+\defineMPinstance % not really needed as we set in lua
+ [chemistry]
+ [\s!format=metafun,
+ %\s!extensions=\v!yes, % Should we add extensions and initializations?
+ %\s!initializations=\v!yes, % Would this give EmWidth, etc.?
+ \c!method=\s!double]
+
+\startMPdefinitions{chemistry}
+ loadmodule "chem" ;
+\stopMPdefinitions
+
+\defineframed
+ [\??chemicalframed]
+ [\c!align=\v!normal,
+ \c!strut=\v!no]
+
+\mutable\let\currentdefinedchemical\empty
+
+\permanent\protected\def\setupchemicalframed
+ {\setupframed[\??chemicalframed]}
+
+\permanent\protected\tolerant\overloaded\def\definechemical[#1]#:#2% is global (so we don't use the commandhandler)
+ {\startnointerference
+ \edef\currentdefinedchemical{#1}%
+ \enforced\let\chemical\chem_chemical_nested
+ \clf_undefinechemical{#1}%
+ #2% flush
+ \stopnointerference}
+
+\permanent\tolerant\protected\def\chem_chemical_nested[#1]#*[#2]%
+ {\clf_definechemical{\currentdefinedchemical}{#1}{\detokenize{#2}}}
+
+% chemical symbols
+
+\permanent\tolerant\protected\def\definechemicalsymbol[#1]#*[#2]%
+ {\ifarguments\else\defcsname\??chemicalsymbol#1\endcsname{#2}\fi}
+
+\permanent\protected\def\chemicalsymbol[#1]%
+ {\csname\??chemicalsymbol\ifcsname\??chemicalsymbol#1\endcsname#1\else\s!unknown\fi\endcsname}
+
+\definechemicalsymbol[\s!unknown][] % \char"FFFD empty
+
+% size (small medium big)
+
+% \edef\chemicaltoplocation{t}
+% \edef\chemicalbotlocation{b}
+
+\permanent\protected\def\chemicaltext#1%
+ {\mathematics
+ {\usechemicalstyleandcolor\c!style\c!color
+ \strut
+ \ifcase\currentxfontsize\or\scriptstyle\or\scriptscriptstyle\fi
+ #1}}
+
+\setvalue{\??chemicalsize\v!small }{\txx}
+\setvalue{\??chemicalsize\v!medium}{\tx}
+\setvalue{\??chemicalsize\v!big }{}
+
+\newtoks \everychemical
+\newtoks \everystructurechemical
+%newconditional\indisplaychemical
+
+\newtoks \t_chem_every_box
+\newbox \b_chem_result
+\newconditional\c_chem_some_text
+\newdimen \d_chem_width
+\newdimen \d_chem_height
+\newdimen \d_chem_depth
+
+\permanent\tolerant\protected\def\startchemical[#1]#*[#2]%
+ {\ifmmode\vcenter\else\vbox\fi % vpack ?
+ \bgroup
+ \synchronizestrut{\chemicalparameter\c!strut}%
+ \dontcomplain
+ %\settrue\indisplaychemical
+ \forgetall
+ \ifparameter#2\or
+ \ifhastok={#1}%
+ \setupcurrentchemical[#1]% same as \currentchemical
+ \else
+ \edef\currentchemical{#1}%
+ \setupcurrentchemical[#2]%
+ \fi
+ \orelse\ifparameter#1\or
+ \ifhastok={#1}%
+ \setupcurrentchemical[#1]% same as \currentchemical
+ \else
+ \edef\currentchemical{#1}%
+ \fi
+ \fi
+ \the\everystructurechemical
+ \setbox\b_chem_result\hpack\bgroup
+ \clf_startchemical
+ width {\chemicalparameter\c!width}%
+ height {\chemicalparameter\c!height}%
+ left {\chemicalparameter\c!left}%
+ right {\chemicalparameter\c!right}%
+ top {\chemicalparameter\c!top}%
+ bottom {\chemicalparameter\c!bottom}%
+ scale {\chemicalparameter\c!scale}%
+ rotation {\chemicalparameter\c!rotation}%
+ symalign {\chemicalparameter\c!symalign}%
+ axis {\chemicalparameter\c!axis}% was \MPcolor{...}
+ framecolor {\chemicalparameter\c!framecolor}%
+ rulethickness {\number\dimexpr\chemicalparameter\c!rulethickness}%
+ offset {\number\dimexpr\chemicalparameter\c!offset}%
+ unit {\number\dimexpr\chemicalparameter\c!unit}%
+ factor {\number\chemicalparameter\c!factor}%
+ \relax
+ \startnointerference}
+
+\permanent\protected\def\stopchemical
+ {\stopnointerference
+ \clf_stopchemical
+ \egroup
+ \d_chem_width \wd\b_chem_result
+ \d_chem_height\ht\b_chem_result
+ \d_chem_depth \dp\b_chem_result
+ \the\t_chem_every_box
+ \doifelsenothing{\chemicalparameter\c!frame}\chem_framed_nop\chem_framed_yes
+ \egroup}
+
+\protected\def\chem_framed_yes
+ {\localframedwithsettings
+ [\??chemicalframed]%
+ [\c!frame=\chemicalparameter\c!frame,
+ \c!rulethickness=\chemicalparameter\c!rulethickness,
+ \c!framecolor=\chemicalparameter\c!framecolor]%
+ {\vpack{\box\b_chem_result\vss}}} % remove depth
+
+\protected\def\chem_framed_nop
+ {\directlocalframed
+ [\??chemicalframed]%
+ {\vpack{\box\b_chem_result\vss}}} % remove depth
+
+\aliased\let\startstructurechemical\startchemical
+\aliased\let\stopstructurechemical \stopchemical
+
+\appendtoks
+ \enforced\let\chemical\structurechemical
+\to\everystructurechemical
+
+\permanent\tolerant\protected\def\structurechemical[#1]#*[#2]#*[#3]%
+ {\ifparameter#3\or
+ \expandafter\strc_chem_indeed_three
+ \else
+ \expandafter\strc_chem_indeed_two
+ \fi[#1][#2][#3]}
+
+\def\strc_chem_indeed_three[#1][#2][#3]%
+ {\writestatus\m!chemicals{hyperlinked chemicals not yet supported}% todo reference, for the moment ignored
+ \clf_chemicalcomponent
+ {#2}%
+ {\detokenize{#3}}%
+ {\the\dimexpr\chemicalparameter\c!rulethickness}% todo: scaled points
+ {\chemicalparameter\c!rulecolor}%
+ \relax
+ \ignorespaces}
+
+\def\strc_chem_indeed_two[#1][#2]% why no [#3] here
+ {\clf_chemicalcomponent
+ {#1}%
+ {\detokenize{#2}}%
+ {\the\dimexpr\chemicalparameter\c!rulethickness}% todo: scaled points
+ {\chemicalparameter\c!rulecolor}%
+ \relax
+ \ignorespaces}
+
+\appendtoks
+ \setbox\b_chem_result\hpack{\raise\MPlly\box\b_chem_result}%
+ \d_chem_width \wd\b_chem_result
+ \d_chem_height\ht\b_chem_result
+ \d_chem_depth \dp\b_chem_result
+\to \t_chem_every_box
+
+% kind of compatible, but text sizes instead of math sizes (i.e. tx is larger than scriptsize)
+
+\appendtoks
+ \edef\m_bodyfont{\chemicalparameter\c!bodyfont}%
+ \ifempty\m_bodyfont
+ \switchtobodyfont[\m_bodyfont]%
+ \fi
+ \begincsname\??chemicalsize\chemicalparameter\c!size\endcsname
+% \to \everystructurechemical
+\to \everychemical
+
+\permanent\protected\def\chemicaltoptext#1{\global\settrue\c_chem_some_text\gdef\m_chem_top_text{#1}\ignorespaces}
+\permanent\protected\def\chemicalbottext#1{\global\settrue\c_chem_some_text\gdef\m_chem_bot_text{#1}\ignorespaces}
+\permanent\protected\def\chemicalmidtext#1{\global\settrue\c_chem_some_text\gdef\m_chem_mid_text{#1}\ignorespaces}
+
+\mutable\let\toptext\empty
+\mutable\let\bottext\empty
+\mutable\let\midtext\empty
+
+\appendtoks
+ \let\toptext\chemicaltoptext \glet\m_chem_top_text\empty
+ \let\bottext\chemicalbottext \glet\m_chem_bot_text\empty
+ \let\midtext\chemicalmidtext \glet\m_chem_mid_text\empty
+ \global\setfalse\c_chem_some_text
+\to \everystructurechemical
+
+\def\chem_add_texts
+ {\setbox2\hpack to \d_chem_width{\strut\hss\hbox{\strut\m_chem_mid_text}\hss}%
+ \setbox4\hpack to \d_chem_width{\strut\hss\hbox{\strut\m_chem_top_text}\hss}%
+ \setbox6\hpack to \d_chem_width{\strut\hss\hbox{\strut\m_chem_bot_text}\hss}%
+ \setbox\b_chem_result\hpack \bgroup
+ \box\b_chem_result
+ \hskip-\d_chem_width
+ \raise\d_chem_height\hpack{\lower\ht4\box4}%
+ \hskip-\d_chem_width
+ \lower.5\dimexpr\ht2-\dp2\relax\box2%
+ \hskip-\d_chem_width
+ \lower\d_chem_depth \hpack{\raise\dp6\box6}%
+ \hss
+ \egroup} % text on top of chemicals
+
+\appendtoks
+ \ifconditional\c_chem_some_text
+ \chem_add_texts
+ \d_chem_width \wd\b_chem_result
+ \d_chem_height\ht\b_chem_result
+ \d_chem_depth \dp\b_chem_result
+ \fi
+\to \t_chem_every_box
+
+% todo: enspace or emspace
+
+\definechemicalsymbol[space] [\enspace\quad\enspace]
+\definechemicalsymbol[plus] [\enspace+\enspace]
+\definechemicalsymbol[minus] [\enspace-\enspace]
+\definechemicalsymbol[gives] [\chem_arrow_construct\xrightarrow]
+\definechemicalsymbol[equilibrium] [\chem_arrow_construct\xrightoverleftarrow]
+\definechemicalsymbol[mesomeric] [\chem_arrow_construct\xleftrightarrow]
+\definechemicalsymbol[opencomplex] [\mathematics{\Bigg[}] % not yet ok
+\definechemicalsymbol[closecomplex][\mathematics{\Bigg]}] % not yet ok
+
+\definechemicalsymbol[SPACE] [{\chemicalsymbol[space]}]
+\definechemicalsymbol[PLUS] [{\chemicalsymbol[plus]}]
+\definechemicalsymbol[MINUS] [{\chemicalsymbol[minus]}]
+\definechemicalsymbol[GIVES] [{\chemicalsymbol[gives]}]
+\definechemicalsymbol[EQUILIBRIUM] [{\chemicalsymbol[equilibrium]}]
+\definechemicalsymbol[MESOMERIC] [{\chemicalsymbol[mesomeric]}]
+\definechemicalsymbol[OPENCOMPLEX] [{\chemicalsymbol[opencomplex]}]
+\definechemicalsymbol[CLOSECOMPLEX][{\chemicalsymbol[closecomplex]}]
+
+\def\chem_arrow_construct#1#2#3%
+ {\enspace
+ \mathematics{#1%
+ {\strut\hbox \s!spread 2\emwidth{\hss\clf_inlinechemical{#3}\hss}}% {\strut\hbox \s!spread 2em{\hss#3\hss}}}%
+ {\strut\hbox \s!spread 2\emwidth{\hss\clf_inlinechemical{#2}\hss}}}% {\strut\hbox \s!spread 2em{\hss#2\hss}}%
+ \enspace}
+
+% special macros (probably needs some more work)
+
+\let\chem_box_normal_yes\hbox
+\let\chem_box_visual_yes\hbox
+\let\chem_box_visual_nop\relax
+
+\installtextracker
+ {chemistry.boxes}
+ {\let\chem_box_visual_yes\ruledhbox \let\chem_box_visual_nop\ruledhbox}
+ {\let\chem_box_visual_yes\hbox \let\chem_box_visual_nop\relax }
+
+\def\chem_top_construct#1#2#3#4%
+ {\hpack\bgroup
+ \setstrut
+ \setbox\scratchboxone\chem_box_visual_yes{\strut#3}%
+ \setbox\scratchboxtwo\chem_box_visual_yes{\strut\molecule{#4}}%
+ \setbox\scratchboxone\chem_box_normal_yes{\raise\dimexpr\dp\scratchboxone+\ht\scratchboxtwo\relax\hbox to \wd\scratchboxtwo{#1\box\scratchboxone#2}}%
+ \smashbox\scratchboxone
+ \box\scratchboxone
+ \box\scratchboxtwo
+ \egroup}
+
+\def\chem_bottom_construct#1#2#3#4%
+ {\hpack\bgroup
+ \setstrut
+ \setbox\scratchboxone\chem_box_visual_yes{\strut#3}%
+ \setbox\scratchboxtwo\chem_box_visual_yes{\strut\molecule{#4}}%
+ \setbox\scratchboxone\chem_box_normal_yes{\lower\dimexpr\dp\scratchboxtwo+\ht\scratchboxone\relax\hbox to \wd\scratchboxtwo{#1\box\scratchboxone#2}}%
+ \smashbox\scratchboxone
+ \box\scratchboxone
+ \box\scratchboxtwo
+ \egroup}
+
+\permanent\protected\def\chemicalleft#1#2% redundant boxes thanks to visual
+ {\hbox\bgroup % hpack ?
+ \setstrut
+ \llap{\chem_box_visual_nop{\strut#1}}%
+ \chem_box_visual_nop{\strut#2}%
+ \egroup}
+
+\permanent\protected\def\chemicalright#1#2% redundant boxes thanks to visual
+ {\hbox\bgroup % hpack ?
+ \setstrut
+ \chem_box_visual_yes{\strut#2}%
+ \rlap{\chem_box_visual_nop{\strut#1}}%
+ \egroup}
+
+\permanent\protected\def\chemicaltop {\chem_top_construct \hss \hss }
+\permanent\protected\def\chemicallefttop {\chem_top_construct \relax\hss }
+\permanent\protected\def\chemicalrighttop {\chem_top_construct \hss \relax}
+\permanent\protected\def\chemicalbottom {\chem_bottom_construct\hss \hss }
+\permanent\protected\def\chemicalleftbottom {\chem_bottom_construct\relax\hss }
+\permanent\protected\def\chemicalrightbottom{\chem_bottom_construct\hss \relax}
+
+\permanent\protected\def\chemicaltopleft #1{\chemicalleft {\chemicalrighttop {#1}{}}}
+\permanent\protected\def\chemicalbottomleft #1{\chemicalleft {\chemicalrightbottom{#1}{}}}
+\permanent\protected\def\chemicaltopright #1{\chemicalright{\chemicallefttop {#1}{}}}
+\permanent\protected\def\chemicalbottomright#1{\chemicalright{\chemicalleftbottom {#1}{}}}
+
+% \protected\def\chemicalcentered #1{\hbox to \fontcharwd\font`C{\setstrut\strut\hss#1\hss}}
+% \protected\def\chemicalleftcentered #1{\hbox to \fontcharwd\font`C{\setstrut\strut #1\hss}}
+% \protected\def\chemicalrightcentered#1{\hbox to \fontcharwd\font`C{\setstrut\strut\hss#1}}
+
+% \let\chemicalsmashedmiddle\chemicalcentered
+% \let\chemicalsmashedleft \chemicalleftcentered
+% \let\chemicalsmashedright \chemicalrightcentered
+
+\permanent\protected\def\chemicalalignedtext
+ {\ifmmode
+ \expandafter\chem_aligned_text_math
+ \else
+ \expandafter\chem_aligned_text_text
+ \fi}
+
+\aliased\let\chemicaltighttext\relax % maybe smaller strut
+
+\def\chem_aligned_text_text#1#2#3%
+ {\dontleavehmode
+ \begingroup
+ \usechemicalstyleandcolor\c!style\c!color
+ \chem_box_visual_yes to \fontcharwd\font`C\bgroup
+ \setstrut\strut
+ #1\molecule{#3}#2%
+ \egroup
+ \endgroup}
+
+\def\chem_aligned_text_math#1#2#3%
+ {\dontleavehmode
+ \begingroup
+ \scratchcounter\normalmathstyle
+ \usechemicalstyleandcolor\c!style\c!color
+ \chem_box_visual_yes to \fontcharwd\font`C\bgroup
+ \setstrut\strut
+ #1\mathematics{\tf\triggermathstyle\scratchcounter\molecule{#3}}#2%
+ \egroup
+ \endgroup}
+
+\permanent\protected\def\chemicalcentered {\chemicalalignedtext\hss \hss }
+\permanent\protected\def\chemicalleftcentered {\chemicalalignedtext\relax\hss }
+\permanent\protected\def\chemicalrightcentered{\chemicalalignedtext\hss \relax}
+
+\aliased\let\chemicalsmashedmiddle\chemicalcentered
+\aliased\let\chemicalsmashedleft \chemicalleftcentered
+\aliased\let\chemicalsmashedright \chemicalrightcentered
+
+\permanent\protected\def\chemicaloxidation#1#2#3%
+ {\chemicaltop{\txx\ifcase#2\relax0\else#1\convertnumber{I}{#2}\fi}{#3}}
+
+\permanent\protected\def\chemicaloxidationplus {\dotriplegroupempty\chemicaloxidation{\textplus }} % {} needed!
+\permanent\protected\def\chemicaloxidationminus{\dotriplegroupempty\chemicaloxidation{\textminus}} % {} needed!
+\permanent\protected\def\chemicalforeveropen {\dotriplegroupempty\chemicalleft {$\big[$}} % {} needed!
+\permanent\protected\def\chemicalforeverclose {\dotriplegroupempty\chemicalright {$\big]$}} % {} needed!
+\permanent\protected\def\chemicaloxidationone {\chemicaloxidation\relax1}
+\permanent\protected\def\chemicaloxidationtwo {\chemicaloxidation\relax2}
+\permanent\protected\def\chemicaloxidationthree{\chemicaloxidation\relax3}
+\permanent\protected\def\chemicaloxidationfour {\chemicaloxidation\relax4}
+\permanent\protected\def\chemicaloxidationfive {\chemicaloxidation\relax5}
+\permanent\protected\def\chemicaloxidationsix {\chemicaloxidation\relax6}
+\permanent\protected\def\chemicaloxidationseven{\chemicaloxidation\relax7}
+
+\permanent\protected\def\chemicalbar
+ {\hpack \s!spread .5\emwidth \bgroup
+ \hss
+ \vrule \s!height .9\strutht \s!depth .65\strutdp \s!width .1\exheight
+ \hss
+ \egroup}
+
+\permanent\let\X\relax
+\permanent\let\T\relax
+\permanent\let\B\relax
+\permanent\let\L\relax
+\permanent\let\M\relax
+\permanent\let\R\relax
+
+\appendtoks
+ \enforced\let |\chemicalbar % \SR{N|NH}
+ \enforced\let \+\chemicaloxidationplus
+ \enforced\let \-\chemicaloxidationminus
+ \enforced\let \[\chemicalforeveropen
+ \enforced\let \]\chemicalforeverclose
+ \enforced\let \1\chemicaloxidationone
+ \enforced\let \2\chemicaloxidationtwo
+ \enforced\let \3\chemicaloxidationthree
+ \enforced\let \4\chemicaloxidationfour
+ \enforced\let \5\chemicaloxidationfive
+ \enforced\let \6\chemicaloxidationsix
+ \enforced\let \7\chemicaloxidationseven
+ \enforced\let \X\chemicaltighttext
+ \enforced\let \T\chemicaltop
+ \enforced\let \B\chemicalbottom
+ \enforced\let \L\chemicalleft
+ \enforced\let\LC\chemicalleftcentered
+ \enforced\let \R\chemicalright
+ \enforced\let\RC\chemicalrightcentered
+ \enforced\let\TL\chemicaltopleft
+ \enforced\let\BL\chemicalbottomleft
+ \enforced\let\TR\chemicaltopright
+ \enforced\let\BR\chemicalbottomright
+ \enforced\let\LT\chemicallefttop
+ \enforced\let\LB\chemicalleftbottom
+ \enforced\let\RT\chemicalrighttop
+ \enforced\let\RB\chemicalrightbottom
+ \enforced\let\SL\chemicalsmashedleft
+ \enforced\let\SM\chemicalsmashedmiddle
+ \enforced\let\SR\chemicalsmashedright
+\to \everychemical
+
+% Should these also be defined in lower case, so as to be case independent?
+
+\appendtoks
+ \the\everychemical
+\to \everystructurechemical
+
+% inline
+
+\permanent\protected\def\chemical
+ {\ifinformula
+ \expandafter\indisplaychemical
+ \else
+ \expandafter\inlinechemical
+ \fi}
+
+\permanent\protected\def\indisplaychemical
+ {\mathstylecommand\displaychemical\inlinechemical\inlinechemical}
+
+% \permanent\protected\def\inlinechemical#1%
+% {\dontleavehmode
+% \begingroup
+% \scratchcounter\normalmathstyle
+% \usechemicalstyleandcolor\c!style\c!color
+% \hbox{\mathematics{\tf\triggermathstyle\scratchcounter\clf_inlinechemical{#1}}}%
+% \endgroup}
+
+\permanent\tolerant\protected\def\displaychemical#=#=#=%
+ {\the\everychemical
+ \everychemical\emptytoks
+ \quad
+ \vcenter\bgroup
+ \usechemicalstyleandcolor\c!style\c!color
+ \ifparameter#3\or
+ \ifparameter#2\or
+ \halign{\aligntab\hss\alignmark\alignmark\hss\cr#2\cr\molecule{#1}\cr#3\cr}%
+ \else
+ \halign{\aligntab\hss\alignmark\alignmark\hss \cr\molecule{#1}\cr#2\cr}%
+ \fi
+ \else
+ \hbox{\molecule{#1}}%
+ \fi
+ \egroup
+ \quad}
+
+\permanent\protected\def\inlinechemical#1%
+ {\dontleavehmode
+ \hbox{\usechemicalstyleandcolor\c!style\c!color\clf_inlinechemical{#1}}}
+
+\permanent\protected\def\chemicalbondrule
+ {\hpack{\vrule\s!height.75\exheight\s!depth-\dimexpr.75\exheight-\linewidth\relax\s!width\emwidth\relax}}
+
+\definechemicalsymbol[i:space] [\enspace\quad\enspace]
+\definechemicalsymbol[i:plus] [\enspace\mathematics{+}\enspace]
+\definechemicalsymbol[i:minus] [\enspace\mathematics{-}\enspace]
+\definechemicalsymbol[i:equals] [\enspace\mathematics{=}\enspace]
+\definechemicalsymbol[i:gives] [\enspace\mathematics{\xrightarrow{}{}}\enspace]
+\definechemicalsymbol[i:equilibrium] [\enspace\mathematics{\xrightoverleftarrow{}{}}\enspace]
+\definechemicalsymbol[i:mesomeric] [\enspace\mathematics{\xleftrightarrow{}{}}\enspace]
+\definechemicalsymbol[i:single] [\chemicalbondrule]
+\definechemicalsymbol[i:double] [\hpack{\lower.5ex\chemicalbondrule\hskip-1em\raise.5ex\chemicalbondrule}]
+\definechemicalsymbol[i:triple] [\hpack{\chemicalbondrule\hskip-1em\lower.5ex\chemicalbondrule\hskip-1em\raise.5ex\chemicalbondrule}]
+
+\permanent\protected\def\chemicalsinglebond {\chemicalsymbol[i:single]}
+\permanent\protected\def\chemicaldoublebond {\chemicalsymbol[i:double]}
+\permanent\protected\def\chemicaltriplebond {\chemicalsymbol[i:triple]}
+\permanent\protected\def\chemicalgives {\chemicalsymbol[i:gives]}
+\permanent\protected\def\chemicalmesomeric {\chemicalsymbol[i:mesomeric]}
+\permanent\protected\def\chemicalequilibrium{\chemicalsymbol[i:equilibrium]}
+\permanent\protected\def\chemicalplus {\chemicalsymbol[i:plus]}
+\permanent\protected\def\chemicalminus {\chemicalsymbol[i:minus]}
+\permanent\protected\def\chemicalequals {\chemicalsymbol[i:equals]}
+\permanent\protected\def\chemicalspace {\chemicalsymbol[i:space]}
+\permanent\protected\def\chemicalinline #1{#1}
+
+% display
+
+\newconditional\c_chem_has_top
+\newconditional\c_chem_has_bot
+
+\newtoks\t_chem_top
+\newtoks\t_chem_mid
+\newtoks\t_chem_bot
+
+\newif\ifinchemicalformula
+
+\permanent\protected\def\startchemicalformula
+ {\mathortext\vcenter\vbox\bgroup
+ \forgetall
+ \inchemicalformulatrue
+ \the\everychemical
+ \everychemical\emptytoks
+ \t_chem_top\emptytoks % not needed
+ \t_chem_mid\emptytoks % not needed
+ \t_chem_bot\emptytoks % not needed
+ \enforced\let\chemical\formulachemical
+ \setfalse\c_chem_has_top
+ \setfalse\c_chem_has_bot}
+
+\permanent\protected\def\stopchemicalformula
+ {\tabskip\emwidth\relax
+ \nointerlineskip
+ \ifconditional\c_chem_has_top
+ \ifconditional\c_chem_has_bot
+ \halign{\aligntab\hss\usechemicalstyleandcolor\c!style\c!color\alignmark\alignmark\hss\cr\the\t_chem_top\cr\the\t_chem_mid\cr\the\t_chem_bot\cr}%
+ \else
+ \halign{\aligntab\hss\usechemicalstyleandcolor\c!style\c!color\alignmark\alignmark\hss\cr\the\t_chem_top\cr\the\t_chem_mid\cr}%
+ \fi
+ \else
+ \ifconditional\c_chem_has_bot
+ \halign{\aligntab\hss\usechemicalstyleandcolor\c!style\c!color\alignmark\alignmark\hss\cr\the\t_chem_mid\cr\the\t_chem_bot\cr}%
+ \else
+ \halign{\aligntab\hss\usechemicalstyleandcolor\c!style\c!color\alignmark\alignmark\hss\cr\the\t_chem_mid\cr}%
+ \fi
+ \fi
+ \egroup}
+
+% for the moment we have a special set
+
+\definechemicalsymbol[d:space] [\enspace\quad\enspace]
+\definechemicalsymbol[d:plus] [\enspace+\enspace]
+\definechemicalsymbol[d:minus] [\enspace-\enspace]
+\definechemicalsymbol[d:equals] [\enspace=\enspace]
+\definechemicalsymbol[d:gives] [\rightarrowfill] % \chem_arrow_construct\xrightarrow
+\definechemicalsymbol[d:equilibrium] [\rightoverleftarrowfill] % \chem_arrow_construct\xrightoverleftarrow
+\definechemicalsymbol[d:mesomeric] [\leftarrowfill] % \chem_arrow_construct\xleftrightarrow
+\definechemicalsymbol[d:single] [\chemicalbondrule]
+\definechemicalsymbol[d:double] [\hpack{\lower.5ex\chemicalbondrule\hskip-1em\raise.5ex\chemicalbondrule}]
+\definechemicalsymbol[d:triple] [\hpack{\chemicalbondrule\hskip-1em\lower.5ex\chemicalbondrule\hskip-1em\raise.5ex\chemicalbondrule}]
+\definechemicalsymbol[d:opencomplex] [\mathematics{\Bigg[}] % not yet ok
+\definechemicalsymbol[d:closecomplex][\mathematics{\Bigg]}] % not yet ok
+
+\definechemicalsymbol[d:SPACE] [{\chemicalsymbol[d:space]}]
+\definechemicalsymbol[d:PLUS] [{\chemicalsymbol[d:plus]}]
+\definechemicalsymbol[d:MINUS] [{\chemicalsymbol[d:minus]}]
+\definechemicalsymbol[d:EQUALS] [{\chemicalsymbol[d:equals]}]
+\definechemicalsymbol[d:GIVES] [{\chemicalsymbol[d:gives]}]
+\definechemicalsymbol[d:EQUILIBRIUM] [{\chemicalsymbol[d:equilibrium]}]
+\definechemicalsymbol[d:MESOMERIC] [{\chemicalsymbol[d:mesomeric]}]
+\definechemicalsymbol[d:SINGLE] [{\chemicalsymbol[d:single]}]
+\definechemicalsymbol[d:DOUBLE] [{\chemicalsymbol[d:double]}]
+\definechemicalsymbol[d:TRIPLE] [{\chemicalsymbol[d:triple]}]
+\definechemicalsymbol[d:OPENCOMPLEX] [{\chemicalsymbol[d:opencomplex]}]
+\definechemicalsymbol[d:CLOSECOMPLEX][{\chemicalsymbol[d:closecomplex]}]
+
+\permanent\tolerant\protected\def\formulachemical#=#=#=%
+ {\relax
+ \ifarguments\or
+ \chem_formula_top_nop
+ \chem_formula_bot_nop
+ \or
+ \chem_formula_top_nop
+ \doifelsenothing{#2}\chem_formula_bot_nop{\chem_formula_bot_yes{#2}}%
+ \or
+ \doifelsenothing{#2}\chem_formula_top_nop{\chem_formula_top_yes{#2}}%
+ \doifelsenothing{#3}\chem_formula_bot_nop{\chem_formula_bot_yes{#3}}%
+ \fi
+ \ifcsname\??chemicalsymbol d:\detokenize{#1}\endcsname
+ \toksapp\t_chem_mid{\chemicalsymbol[d:#1]\aligntab}%
+ \else
+ \toksapp\t_chem_mid{\molecule{#1}\aligntab}%
+ \fi}
+
+\def\chem_formula_mid#1%
+ {\csname\??chemicalsymbol\detokenize{#1}\endcsname}
+
+\def\chem_formula_top_nop {\toksapp\t_chem_top{\aligntab}}
+\def\chem_formula_bot_nop {\toksapp\t_chem_bot{\aligntab}}
+\def\chem_formula_top_yes#1{\toksapp\t_chem_top{\chem_formula_top_indeed{#1}\aligntab}\settrue\c_chem_has_top}
+\def\chem_formula_bot_yes#1{\toksapp\t_chem_bot{\chem_formula_bot_indeed{#1}\aligntab}\settrue\c_chem_has_bot}
+
+\def\chem_formula_top_indeed#1{\strut#1}
+\def\chem_formula_bot_indeed#1{\strut#1}
+
+% Experimental: defaults might change.
+
+\definefloat
+ [\v!chemical]
+ [\v!chemicals]
+
+\setuplabeltext
+ [\v!chemical=]
+
+\setupfloat
+ [\v!chemical]
+ [\c!location=\v!here,
+ \c!inner=\hsize.8\textwidth\dontleavehmode, % brr
+ \c!align={\v!flushleft,\v!lohi}]
+
+\setupcaption
+ [\v!chemical]
+ [\c!location=\v!right,
+ \c!distance=\zeropoint,
+ \c!width=.2\textwidth,
+ \c!align=\v!flushright]
+
+% Can be used as for displayed math: \startplaceformula... to display a chemical formula
+% or a chemical structure:
+%
+% \startplacechemical
+% \startchemicalformula
+% \chemical{2H_2}
+% \chemical{PLUS}
+% \chemical{O_2}
+% \chemical{GIVES}
+% \chemical{2H_2O}
+% \stopchemicalformula
+% \stopplacechemical
+
+% gone: state option resolution offset (now frame offset) alternative
+
+\setupchemicalframed
+ [\c!align=\v!normal,
+ \c!strut=\v!no,
+ \c!offset=\v!overlay,
+ \c!frame=\v!off]
+
+\definecolor % private color
+ [chemicalframecolor]
+ [r=.75,g=.85,b=.95]
+
+\setupchemical
+ [\c!frame=,
+ \c!width=\v!fit, % or unitless number, multiplies scale*unit
+ \c!height=\v!fit, % or unitless number, multiplies scale*unit
+ \c!left=\v!fit, % or unitless number, multiplies scale*unit
+ \c!right=\v!fit, % or unitless number, multiplies scale*unit
+ \c!top=\v!fit, % or unitless number, multiplies scale*unit
+ \c!bottom=\v!fit, % or unitless number, multiplies scale*unit
+ \c!bodyfont=,
+ \c!scale=\v!normal, % small, normal or medium, big, or unitless number (multiplies unit)
+ \c!size=\v!medium,
+ \c!textsize=\v!big, % how is textsize used??
+ \c!axis=\v!off,
+ \c!style=\rm,
+ \c!rotation=0, % unitless number (interpreted as degrees)
+ \c!symalign=\v!auto,
+ \c!location=, % not yet used (was interaction related in mkii)
+ \c!offset=.25\emwidth,
+ \c!unit=\emwidth,
+ \c!factor=3,
+ \c!color=,
+ \c!strut=\v!yes,
+ \c!framecolor=chemicalframecolor,
+ \c!rulethickness=0.6pt, %1.5\linewidth,
+ \c!rulecolor=]
+
+%D Compatibility:
+
+\definechemical[+R] {\chemical[RR]}
+\definechemical[-R] {\chemical[LR]}
+
+\definechemical[CARBON:CB] {\chemical[NEWMANSTAGGER,C,SB]}
+\definechemical[NEWMANSTAGGER:CB] {\chemical[NEWMANSTAGGER,C,SB]}
+\definechemical[NEWMANECLIPSED:CB]{\chemical[NEWMANECLIPSED,C,SB]}
+\definechemical[CARBON:CB1] {\chemical[CARBON,C,SB,Z234,1.5MOV1,MIR0,C,SB,Z234]}
+
+\definechemical[NEWMAN] {\chemical[]}
+\definechemical[STAGGER] {\chemical[NEWMANSTAGGER]}
+\definechemical[ECLIPSE] {\chemical[NEWMANECLIPSED]}
+\definechemical[ECLIPSED] {\chemical[NEWMANECLIPSED]}
+\definechemical[SIX:FRONT] {\chemical[SIXFRONT]}
+\definechemical[FIVE:FRONT] {\chemical[FIVEFRONT]}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-bas.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cldf-bas.mkxl
new file mode 100644
index 00000000000..962db5209f5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-bas.mkxl
@@ -0,0 +1,19 @@
+%D \module
+%D [ file=cldf-bas,
+%D version=2010.10.19,,
+%D title=\CONTEXT\ \LUA\ Document Functions,
+%D subtitle=Basics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Documents / Basics}
+
+\registerctxluafile{cldf-bas}{}
+\registerctxluafile{cldf-prs}{}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-com.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cldf-com.mkxl
new file mode 100644
index 00000000000..7a8f7bf5c0d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-com.mkxl
@@ -0,0 +1,19 @@
+%D \module
+%D [ file=cldf-com,
+%D version=2010.10.19,,
+%D title=\CONTEXT\ \LUA\ Document Functions,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Documents / Functions}
+
+\registerctxluafile{cldf-com}{}
+%registerctxluafile{cldf-ver}{}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cldf-ini.mkxl
new file mode 100644
index 00000000000..b393eb9b572
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-ini.mkxl
@@ -0,0 +1,45 @@
+%D \module
+%D [ file=cldf-int,
+%D version=2019.01.01,
+%D title=\CONTEXT\ Data Macros,
+%D subtitle=Integer,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Documents / Initialization}
+
+\newcount\trialtypesettingstate % gets aliased at the Lua end
+
+\registerctxluafile{cldf-ini}{autosuffix}
+
+%D With each new update of \MKIV\ we can join Within Temptation in singing:
+%D
+%D \startbuffer
+%D \startluacode
+%D context("I go faster%s",string.rep(" and faster",6))
+%D \stopluacode
+%D
+%D \cldcontext{"I go faster\letterpercent s",
+%D string.rep(" and faster",6)}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Anyway \unknown\ the following are {\em not} user commands:
+
+% \catcode`=\activecatcode \let\luafunction % saves 10% on the call
+
+% todo: these can become implementers
+
+\permanent\protected\def\cldprocessfile#1{\directlua{context.runfile("#1")}}
+\permanent \def\cldloadfile #1{\directlua{context.loadfile("#1")}}
+\permanent \def\cldloadviafile#1{\directlua{context.loadviafile("#1")}}
+\permanent \def\cldcontext #1{\directlua{context(#1)}}
+\permanent \def\cldcommand #1{\directlua{context.#1}}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-int.lmt b/Master/texmf-dist/tex/context/base/mkxl/cldf-int.lmt
new file mode 100644
index 00000000000..4a60b469a38
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-int.lmt
@@ -0,0 +1,252 @@
+if not modules then modules = { } end modules ['cldf-int'] = {
+ version = 1.001,
+ comment = "companion to cldf-int.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- another experiment
+-- needs upgrading
+-- needs checking
+-- todo: multilingual
+
+local byte = string.byte
+local insert, remove, concat = table.insert, table.remove, table.concat
+local unpack, type = unpack or table.unpack, type
+
+local ctxcatcodes = catcodes.numbers.ctxcatcodes
+local context = context
+local contextsprint = context.sprint
+
+local trace_define = false trackers.register("context.define", function(v) trace_define = v end)
+
+interfaces = interfaces or { }
+local implement = interfaces.implement
+local estart = interfaces.elements.start
+local estop = interfaces.elements.stop
+
+local scanners = tokens.scanners
+local shortcuts = tokens.shortcuts
+
+local peekchar = scanners.peekchar
+local scankey = scanners.key
+local scanvalue = scanners.value
+local scanskip = scanners.skip
+
+local open = byte('[')
+local close = byte(']')
+local equal = byte('=')
+local comma = byte(',')
+
+local function scanhash(scanners)
+ if peekchar() == open then
+ local data = { }
+ scanskip()
+ while true do
+ local c = peekchar()
+ if c == comma then
+ scanskip()
+ elseif c == close then
+ scanskip()
+ break
+ else
+ local key = scankey(equal)
+ if key then
+ if peekchar() == equal then
+ scanskip()
+ if scanners then
+ local scanner = scanners[key]
+ if scanner then
+ data[key] = scanner()
+ else
+ data[key] = scanvalue(comma,close) or ""
+ end
+ else
+ data[key] = scanvalue(comma,close) or ""
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ end
+ return data
+ end
+end
+
+local function scanarray()
+ if peekchar() == open then
+ local data = { }
+ local d = 0
+ scanskip()
+ while true do
+ local c = peekchar()
+ if c == comma then
+ scanskip()
+ elseif c == close then
+ scanskip()
+ break
+ else
+ local v = scanvalue(comma,close) or ""
+ d = d + 1
+ data[d] = v
+ end
+ end
+ return data
+ end
+end
+
+shortcuts.scanhash = scanhash
+shortcuts.scanarray = scanarray
+
+scanners.hash = scanhash
+scanners.array = scanarray
+
+local function remap(arguments)
+ -- backward compatibility
+ if type(arguments) == "table" then
+ for i=1,#arguments do
+ local a = arguments[i]
+ if type(a) == "table" then
+ local t = a[2]
+ arguments[i] = t == "list" and "array" or t
+ end
+ end
+ return arguments
+ end
+end
+
+function interfaces.definecommand(name,specification) -- name is optional
+ if type(name) == "table" then
+ specification = name
+ name = specification.name
+ end
+ if name and specification then
+ local environment = specification.environment
+ local arguments = remap(specification.arguments)
+ if environment then
+ local starter = specification.starter
+ local stopper = specification.stopper
+ if starter and stopper then
+ implement {
+ name = estart .. name,
+ arguments = arguments,
+ public = true,
+ protected = true,
+ actions = starter,
+ }
+ implement {
+ name = estop .. name,
+ public = true,
+ protected = true,
+ actions = stopper,
+ }
+ else
+ -- message
+ end
+ end
+ if not environment or environment == "both" then
+ local macro = specification.macro
+ if macro then
+ implement {
+ name = name,
+ arguments = arguments,
+ public = true,
+ protected = true,
+ actions = macro,
+ }
+ else
+ -- message
+ end
+ end
+ else
+ -- message
+ end
+end
+
+function interfaces.tolist(t)
+ if t then
+ local r = { }
+ for i=1,#t do
+ r[i] = t[i]
+ end
+ local n = #r
+ for k,v in table.sortedhash(t) do
+ if type(k) ~= "number" then
+ n = n + 1
+ r[n] = k .. "=" .. v
+ end
+ end
+ return concat(r,", ")
+ else
+ return ""
+ end
+end
+
+-- \startluacode
+-- function test(opt_1, opt_2, arg_1)
+-- context.startnarrower()
+-- context("options 1: %s",interfaces.tolist(opt_1))
+-- context.par()
+-- context("options 2: %s",interfaces.tolist(opt_2))
+-- context.par()
+-- context("argument 1: %s",arg_1)
+-- context.stopnarrower()
+-- end
+--
+-- interfaces.definecommand {
+-- name = "test",
+-- arguments = {
+-- { "option", "list" },
+-- { "option", "hash" },
+-- { "content", "string" },
+-- },
+-- macro = test,
+-- }
+-- \stopluacode
+--
+-- test: \test[1][a=3]{whatever}
+--
+-- \startluacode
+-- local function startmore(opt_1)
+-- context.startnarrower()
+-- context("start more, options: %s",interfaces.tolist(opt_1))
+-- context.startnarrower()
+-- end
+--
+-- local function stopmore()
+-- context.stopnarrower()
+-- context("stop more")
+-- context.stopnarrower()
+-- end
+--
+-- interfaces.definecommand ( "more", {
+-- environment = true,
+-- arguments = {
+-- { "option", "list" },
+-- },
+-- starter = startmore,
+-- stopper = stopmore,
+-- } )
+-- \stopluacode
+--
+-- more: \startmore[1] one \startmore[2] two \stopmore one \stopmore
+--
+-- More modern (no need for option or content):
+--
+-- \startluacode
+-- interfaces.definecommand {
+-- name = "test",
+-- arguments = {
+-- "array", -- or list
+-- "hash",
+-- "string",
+-- "number",
+-- },
+-- macro = test,
+-- }
+-- \stopluacode
+--
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-int.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cldf-int.mkxl
new file mode 100644
index 00000000000..a564afc80ad
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-int.mkxl
@@ -0,0 +1,34 @@
+%D \module
+%D [ file=cldf-int,
+%D version=2019.01.01,
+%D title=\CONTEXT\ Data Macros,
+%D subtitle=Integer,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Multilingual Macros / Lua}
+
+\registerctxluafile{cldf-int}{autosuffix}
+
+%D These are user commands so we don't add any protection against overloading
+%D here.
+
+\unprotect
+
+\permanent\protected\def\newluatable#1{\clf_newluatable#1\atendofgrouped{\disposeluatable#1}}
+\permanent\protected\def\useluatable#1{\clf_useluatable#1\atendofgrouped{\disposeluatable#1}}
+
+\installcorenamespace{datavalue}
+
+\permanent\protected\def\setdatavalue#1#2% #2 is a number (expression)
+ {\expandafter\integerdef\csname\??datavalue#1\endcsname\numexpr#2\relax}
+
+\permanent\def\getdatavalue#1{\begincsname\??datavalue#1\endcsname}
+\permanent\def\thedatavalue#1{\the\begincsname\??datavalue#1\endcsname}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-lmt.lmt b/Master/texmf-dist/tex/context/base/mkxl/cldf-lmt.lmt
new file mode 100644
index 00000000000..f3121eadc02
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-lmt.lmt
@@ -0,0 +1,949 @@
+if not modules then modules = { } end modules ['cldf-lmt'] = {
+ version = 1.001,
+ comment = "companion to toks-scn.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, load = next, load
+local gmatch, gsub, byte = string.gmatch, string.gsub, string.byte
+
+local setmetatableindex = table.setmetatableindex
+local setmetatablenewindex = table.setmetatablenewindex
+local serialize = table.serialize
+local concat = table.concat
+
+local random = math.random
+local randomseed = math.randomseed
+local round = math.round
+local abs = math.abs
+
+local implement = interfaces.implement
+
+local scanners = tokens.scanners
+local scanword = scanners.word
+local scanstring = scanners.string
+local scanboolean = scanners.boolean
+local scandimen = scanners.dimen
+local scanfloat = scanners.float
+local scaninteger = scanners.integer
+local scancardinal = scanners.cardinal
+local scannumber = scanners.luanumber
+local scanluainteger = scanners.luainteger
+local scanluacardinal = scanners.luacardinal
+local scanluanumber = scanners.luanumber
+local scanargument = scanners.argument
+local scantoken = scanners.token
+local scancsname = scanners.csname
+local scankeyword = scanners.keyword
+
+local peekchar = scanners.peekchar
+local skipnext = scanners.skip
+
+local getindex = token.get_index
+
+local texsetdimen = tex.setdimen
+local texsetcount = tex.setcount
+local texgetcount = tex.getcount
+local texget = tex.get
+
+local values = tokens.values
+local none_code = values.none
+local integer_code = values.integer
+local cardinal_code = values.cardinal
+local dimension_code = values.dimension
+local skip_code = values.skip
+local boolean_code = values.boolean
+local float_code = values.float
+
+local global_code = tex.flagcodes.global
+
+local context = context
+
+-- variables --
+
+local floats = { }
+local integers = { }
+local cardinals = { }
+local numbers = { }
+
+implement {
+ name = "luafloat",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local n = scanword()
+ if b == "value" then
+ context("%.99g",floats[n] or 0)
+ else
+ floats[n] = scanluanumber(true)
+ -- floats[n] = scanfloat(true)
+ end
+ end,
+}
+
+implement {
+ name = "luainteger",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local n = scanword()
+ if b == "value" then
+ context("%i",integers[n] or 0)
+ else
+ integers[n] = scanluainteger(true)
+ end
+ end,
+}
+
+implement {
+ name = "luacount",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local n = scanword()
+ if b == "value" then
+ return integer_code, integers[n] or 0
+ else
+ integers[n] = scaninteger(true)
+ end
+ end,
+}
+
+implement {
+ name = "luadimen",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local n = scanword()
+ if b == "value" then
+ return dimension_code, integers[n] or 0
+ else
+ integers[n] = scandimen(false,false,true)
+ end
+ end,
+}
+
+implement {
+ name = "luacardinal",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local n = scanword()
+ if b == "value" then
+ context("%1.0f",cardinals[n] or 0)
+ else
+ cardinals[n] = scanluacardinal(true)
+ end
+ end,
+}
+
+implement {
+ name = "luanumber",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local n = scanword()
+ if b == "value" then
+ context("%N",floats[n] or integers[n] or cardinals[n] or 0) -- maybe %N
+ else
+ -- floats[n] = scanfloat(true)
+ floats[n] = scanluanumber(true)
+ end
+ end,
+}
+
+implement {
+ name = "luarandom",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ if b == "value" then
+ return integer_code, random(scanluainteger(),scanluainteger())
+ else
+ randomseed(scanluainteger(true))
+ end
+ end,
+}
+
+interfaces.floats = floats
+interfaces.integers = integers
+interfaces.cardinals = cardinals
+
+interfaces.numbers = table.setmetatableindex(function(t,k)
+ return floats[k] or integers[k] or cardinals[k]
+end)
+
+-- arrays --
+
+local arrays = { }
+
+interfaces.arrays = arrays
+
+local newindex = lua.newindex
+
+implement {
+ name = "newarray",
+ public = true,
+ protected = true,
+ arguments = { {
+ { "name", "string" },
+ { "nx", "integer" },
+ { "ny", "integer" },
+ { "type", "string" },
+ } },
+ actions = function(t)
+ local name = t.name
+ if t.name then
+ local nx = t.nx
+ local ny = t.ny
+ local ty = t.type or "integer"
+ local df = nil
+ if ty == "integer" or ty == "float" or ty == "dimension" then
+ df = 0
+ elseif ty == "boolean" then
+ df = false
+ else
+ ty = nil
+ end
+ if nx and ty ~= nil then
+ local data
+ if ny then
+ data = newindex(t.ny)
+ for i=1,ny do
+ data[i] = newindex(nx,df)
+ end
+ else
+ data = newindex(nx,df)
+ end
+ arrays[name] = data
+ data.nx = nx
+ data.ny = ny
+ data.type = ty
+ if ty == "integer" then
+ data.scanner = scaninteger
+ elseif ty == "boolean" then
+ data.scanner = scanboolean
+ elseif ty == "dimension" then
+ data.scanner = scandimen
+ elseif ty == "float" then
+ data.scanner = scanfloat
+ end
+ if ty == "integer" then
+ data.code = integer_code
+ elseif ty == "boolean" then
+ data.code = boolean_code
+ elseif ty == "dimension" then
+ data.code = dimension_code
+ elseif ty == "float" then
+ data.code = float_code
+ end
+ end
+ end
+ end,
+}
+
+implement {
+ name = "arrayvalue",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local name = scanstring()
+ if name then
+ local a = arrays[name]
+ if a then
+ local nx = a.nx
+ local ny = a.ny
+ local d = a
+ if ny then
+ d = d[scaninteger()]
+ end
+ local x = scaninteger()
+ if b == "value" then
+ local code = a.code
+ if code == float_code then
+ context("%.99g",d[x])
+ else
+ return code, d[x]
+ end
+ else
+ d[x] = a.scanner()
+ end
+ end
+ end
+ end,
+}
+
+implement {
+ name = "arrayequals",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local name = scanstring()
+ if name then
+ local a = arrays[name]
+ if a then
+ local nx = a.nx
+ local ny = a.ny
+ local d = a
+ if ny then
+ d = d[scaninteger()]
+ end
+ local x = scaninteger()
+ if b == "value" then
+ return boolean_code, a.scanner() == d[x]
+ end
+ end
+ end
+ end,
+}
+
+implement {
+ name = "arraycompare",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ local name = scanstring()
+ if name then
+ local a = arrays[name]
+ if a then
+ local nx = a.nx
+ local ny = a.ny
+ local d = a
+ if ny then
+ d = d[scaninteger()]
+ end
+ local x = scaninteger()
+ if b == "value" then
+ local v = a.scanner()
+ local d = d[x]
+ if d < v then
+ return integer_code, 0
+ elseif d == v then
+ return integer_code, 1
+ else
+ return integer_code, 2
+ end
+ end
+ end
+ end
+ end,
+}
+
+implement {
+ name = "showarray",
+ public = true,
+ protected = true,
+ actions = function()
+ local name = scanstring()
+ if name then
+ inspect(arrays[name])
+ end
+ end,
+}
+
+-- expressions --
+
+local cache = table.setmetatableindex(function(t,k)
+ local code = "return function() local n = interfaces.numbers local a = interfaces.arrays return " .. k .. " end"
+ code = loadstring(code)
+ if code then
+ code = code()
+ end
+ t[k] = code or false
+ return code
+end)
+
+table.makeweak(cache)
+
+implement {
+ name = "luaexpression",
+ public = true,
+ actions = function()
+ local how = scanword()
+ local code = cache[scanargument()]
+ if code then
+ local result = code()
+ if result then
+ if not how then
+ context(tostring(code()))
+ elseif how == "float" then
+ context("%.99g",result)
+ elseif how == "integer" then
+ context("%i",round(result))
+ elseif how == "cardinal" then
+ context("%d",abs(round(result)))
+ elseif how == "dimen" then
+ context("%p",result)
+ elseif how == "boolean" then
+ context("%d",result and 1 or 0)
+ elseif how == "lua" then
+ context("%q",result)
+ else
+ context(tostring(code()))
+ end
+ end
+ end
+ end
+}
+
+local dimenfactors = number.dimenfactors
+
+implement {
+ name = "nodimen",
+ public = true,
+ usage = "value",
+ actions = function(b)
+ if b == "value" then
+ local how = scanword()
+ local what = scandimen()
+ if how then
+ local factor = dimenfactors[how]
+ if factor then
+ context("%.6N%s",factor*what,how)
+ else
+ return dimension_code, what
+ end
+ else
+ return dimension_code, what
+ end
+ else
+ local t = scantoken()
+ if t then
+ local i = getindex(t)
+ if i then
+ local d = scandimen(false,false,true)
+ texsetdimen(i,d)
+ end
+ end
+ end
+ end,
+}
+
+-- experimental:
+
+local grouped = { }
+local slots = { }
+local nofslots = 0
+local nofgrouped = 0
+
+local getinteger = tokens.getinteger
+local setinteger = tokens.setinteger
+
+local report = logs.reporter("lua table")
+
+local ctxsprint = context.sprint
+
+-- we could have an extra one that collects all end of grouped actions
+-- so that we dispose more in one go but it doesn's pay off
+
+local function newluatable(name,mt,dispose)
+ local g = { }
+ local t = slots[nofslots]
+ slots[nofslots] = false
+ nofslots = nofslots - 1
+ if not t then
+ nofgrouped = nofgrouped + 1
+ t = nofgrouped
+ end
+ if mt then
+ mt = getinteger(name)
+ if mt then
+ mt = grouped[mt]
+ if mt then
+ setmetatableindex(g,mt)
+ end
+ end
+ end
+ grouped[t] = g
+ setinteger(name,t)
+ -- This is the slow part. Doing this at the TeX end saved 10% runtime. I'll
+ -- think of something that we can set it at the Lua end.
+ if dispose then
+ ctxsprint("\\atendofgrouped{\\disposeluatable\\" .. name .. "}")
+ end
+end
+
+local function disposeluatable(name)
+ local t = getinteger(name)
+ local g = grouped[t]
+ if g then
+ grouped[t] = false
+ nofslots = nofslots + 1
+ slots[nofslots] = t
+ end
+end
+
+local function setluatable(name,kv)
+ local t = getinteger(name)
+ local g = grouped[t]
+ if g and kv then
+ for k, v in next, kv do
+ g[k] = v
+ end
+ end
+end
+
+local function getfromluatable(name,k)
+ local t = getinteger(name)
+ local g = grouped[t]
+ if g then
+ local v = g[k]
+ if v then
+ context(v)
+ else
+ local n = tonumber(k)
+ if n then
+ local v = g[n]
+ if v then
+ context(v)
+ end
+ end
+ end
+ end
+end
+
+local function idxfromluatable(name,k)
+ local t = getinteger(name)
+ local g = grouped[t]
+ if g then
+ local v = g[k]
+ if v then
+ context(v)
+ end
+ end
+end
+
+local function getluatable(name,k)
+ local t = getinteger(name)
+ local g = grouped[t]
+ if g then
+ return g
+ end
+end
+
+local function inspectluatable(name)
+ local t = getinteger(name)
+ local g = grouped[t]
+ if g then
+ report("%s", serialize(g,'[grouped slot ' .. t .. ']'))
+ end
+end
+
+local function showluatables()
+ report("nofgrouped %i, nofslots %i",nofgrouped,nofslots)
+ for t=1,nofgrouped do
+ local g = grouped[t]
+ if g then
+ report("%s", serialize(g,'[grouped slot ' .. t .. ']'))
+ end
+ end
+end
+
+implement {
+ name = "newluatable",
+ protected = true,
+ arguments = "csname",
+ actions = newluatable,
+}
+
+implement {
+ name = "useluatable",
+ protected = true,
+ arguments = { "csname", true },
+ actions = newluatable,
+}
+
+implement {
+ name = "disposeluatable",
+ protected = true,
+ public = true,
+ arguments = "csname",
+ actions = disposeluatable,
+}
+
+implement {
+ name = "inspectluatable",
+ protected = true,
+ public = true,
+ arguments = "csname",
+ actions = inspectluatable,
+}
+
+implement {
+ name = "showluatables",
+ protected = true,
+ public = true,
+ actions = showluatables,
+}
+
+implement {
+ name = "setluatable",
+ protected = true,
+ public = true,
+ arguments = { "csname", "argument" },
+ actions = function(name,data)
+ data = load("return {" .. data .. "}")
+ if data then
+ data = data()
+ if data then
+ setluatable(name,data)
+ end
+ end
+ end
+}
+
+implement {
+ name = "getfromluatable",
+ protected = false,
+ public = true,
+ arguments = { "csname", "argument" },
+ actions = getfromluatable,
+}
+
+implement {
+ name = "idxfromluatable",
+ protected = false,
+ public = true,
+ arguments = { "csname", "integer" },
+ actions = idxfromluatable,
+}
+
+context.luatables = {
+ new = function(name) newluatable(name,false,true) end,
+ use = function(name) useluatable(name,true, true) end,
+ dispose = disposeluatable,
+ set = setluatable,
+ get = getluatable,
+ getfrom = getfromluatable,
+ idxfrom = idxfromluatable,
+ inspect = inspectluatable,
+ show = showluatables,
+}
+
+-- Here's another mechanism ...
+
+local tables = { }
+local stack = setmetatableindex("table")
+
+interfaces.implement {
+ name = "droptablegroup",
+ public = true,
+ actions = function()
+ local g = texget("currentgrouplevel") -- todo: tex.getgrouplevel()
+ local s = stack[g]
+ if s then
+ for t, data in next, s do
+ for k, v in next, data do
+ t[k] = v
+ end
+ end
+ stack[g] = { }
+ end
+ end,
+}
+
+local ctx_atendofgroup = context.core.cs.atendofgroup
+local ctx_droptablegroup = context.core.cs.droptablegroup
+
+local function handletable(t,b,array)
+ if b == "value" then
+ local k = array and scaninteger() or scanargument()
+ local v = t[k]
+ if v then
+ context(v)
+ end
+ else
+ local data = scanargument()
+ data = load("return {" .. data .. "}")
+ if data then
+ data = data()
+ if data then
+ local l = t.level
+ local g = texget("currentgrouplevel") -- todo: tex.getgrouplevel()
+ local s = stack[g]
+ local d = s[t]
+ if not d then
+ d = { }
+ s[t] = d
+ ctx_atendofgroup()
+ ctx_droptablegroup()
+ end
+ for k, v in next, data do
+ if not d[k] then
+ d[k] = t[k]
+ end
+ t[k] = v
+ end
+ -- if b == "global" then
+ if b and (b & 0x04) ~= 0 then -- or just band(b,0x04)
+ for k, v in next, stack do
+ local t = s[t]
+ if t then
+ for k, v in next, data do
+ if t[k] then
+ t[k] = nil
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function newtable(array)
+ local name = scancsname(true)
+ if not tables[name] then
+ local t = { }
+ tables[name] = t
+ interfaces.implement {
+ name = name,
+ public = true,
+ usage = "value",
+ actions = function(b)
+ handletable(t,b,array)
+ end,
+ }
+ else
+ -- already defined
+ end
+end
+
+implement {
+ name = "newhashedtable",
+ protected = true,
+ public = true,
+ actions = newtable,
+}
+
+implement {
+ name = "newindexedtable",
+ protected = true,
+ public = true,
+ actions = function() newtable(true) end,
+}
+
+context.hashedtables = setmetatableindex(function(t,k) return tables[k] end)
+context.indexedtables = context.hashedtables
+
+-- I played with adding bitwise operator to the expr scanners but in the end didn't
+-- keep the code ... the problem is in what symbols ... so for now a more verbose
+-- and/or/xor is okay. Extending the expression scanner would add a little overhead
+-- to all expressions while an logical operator would gain less that 50% so for now
+-- we don't bother. Providing \ifbitwiseand makes sense for performance reasons as
+-- if a bit beter than testing the bitwise and with an \ifcase.
+
+local reporterror = logs.texerrormessage
+
+local function youcant(cmd)
+ reporterror("you can't use \\%s this way, it's an integer value", cmd)
+end
+
+implement {
+ name = "bitwiseset",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ if what == "value" then
+ return cardinal_code, a
+ else
+ youcant("bitwiseset")
+ end
+ end
+}
+
+implement {
+ name = "bitwiseand",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ scankeyword("with")
+ local b = scancardinal()
+ if what == "value" then
+ return cardinal_code, a & b
+ else
+ youcant("bitwiseand")
+ end
+ end
+}
+
+implement {
+ name = "bitwiseor",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ scankeyword("with")
+ local b = scancardinal()
+ if what == "value" then
+ return cardinal_code, a | b
+ else
+ youcant("bitwiseor")
+ end
+ end
+}
+
+implement {
+ name = "bitwisexor",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ scankeyword("with")
+ local b = scancardinal()
+ if what == "value" then
+ return cardinal_code, a ~ b
+ else
+ youcant("bitwisexor")
+ end
+ end
+}
+
+implement {
+ name = "bitwisenot",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ if what == "value" then
+ return cardinal_code, ~a & 0xFFFFFFFF
+ else
+ youcant("bitwisenot")
+ end
+ end
+}
+
+implement {
+ name = "bitwisenil",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ scankeyword("with")
+ local b = scancardinal()
+ if what == "value" then
+ return cardinal_code, a & (~b & 0xFFFFFFFF)
+ else
+ youcant("bitwisenil")
+ end
+ end
+}
+
+implement {
+ name = "bitwiseshift",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local a = scancardinal()
+ scankeyword("by")
+ local b = scaninteger()
+ if what == "value" then
+ return cardinal_code, (b < 0) and (a << -b) or (a >> b)
+ else
+ youcant("bitwiseshift")
+ end
+ end
+}
+
+implement {
+ name = "bitwiseflip",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local t = scancsname()
+ local b = scaninteger()
+ local c = texgetcount(t)
+ if c then
+ if b > 0 then
+ c = c | b
+ elseif b < 0 then
+ c = c & (~(-b) & 0xFFFFFFFF)
+ end
+ if what == "value" then
+ return cardinal_code, c
+ elseif what and (what & global_code) ~= 0 then
+ texsetcount("global",t,c)
+ else
+ texsetcount(t,c)
+ end
+ end
+ end
+}
+
+implement {
+ name = "ifbitwiseand",
+ public = true,
+ usage = "condition",
+ actions = function(what)
+ local a = scancardinal()
+ local b = scancardinal()
+ return boolean_code, (a & b) ~= 0
+ end
+}
+
+interfaces.implement {
+ name = "bitwise",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ if what == "value" then
+ local b = 0
+ while true do
+ local c = peekchar()
+ if c == 48 or c == 49 then
+ skipnext()
+ b = (b << 1) + (c - 48)
+ if b == 0xFFFFFFFF then
+ break
+ end
+ else
+ break
+ end
+ end
+ return 1, b
+ else
+ local b = scancardinal()
+ local t = nil
+ local n = 0
+ for i=1,32 do
+ if (b & (0x100000000 >> i)) == 0 then
+ if n > 0 then
+ n = n + 1
+ t[n] = "0"
+ end
+ elseif t then
+ n = n + 1
+ t[n] = "1"
+ else
+ n = 1
+ t = { "1" }
+ end
+ end
+ context(n > 0 and table.concat(t) or "0")
+ end
+ end,
+}
+
+-- something to play with, this might move to syst-aux.lmt when we have that
+
+local escape = function(s) return "\\" .. byte(s) end
+
+interfaces.implement {
+ name = "ctxluamatch",
+ public = true,
+ usage = "value",
+ actions = function()
+ local command = context[scancsname()]
+ local pattern = gsub(scanstring(),"\\.",escape)
+ local input = gsub(scanstring(),"\\.",escape)
+ for a, b, c, d, e, f, g, h, i in gmatch(input,pattern) do
+ command(a, b or "", c or "", d or "", e or "", f or "", g or "", h or "", i or "")
+ end
+ return none_code
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cldf-ver.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cldf-ver.mkxl
new file mode 100644
index 00000000000..0ff63810ead
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cldf-ver.mkxl
@@ -0,0 +1,18 @@
+%D \module
+%D [ file=cldf-com,
+%D version=2010.10.19,
+%D title=\CONTEXT\ \LUA\ Document Functions,
+%D subtitle=Verbatim,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Documents / Verbatim}
+
+\registerctxluafile{cldf-ver}{}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/colo-ext.mkxl b/Master/texmf-dist/tex/context/base/mkxl/colo-ext.mkxl
new file mode 100644
index 00000000000..df2d9d768a1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/colo-ext.mkxl
@@ -0,0 +1,99 @@
+%D \module
+%D [ file=colo-ext, % mostof thsi code used to be in colo-ini.tex
+%D version=1997.04.01,
+%D title=\CONTEXT\ Color Macros,
+%D subtitle=Extras,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Color Macros / Extras}
+
+\unprotect
+
+%D \macros
+%D {negatecolorcomponent, negativecolorbox}
+%D
+%D Sometimes, especially when we deal with typesetting devices, we want to reverse
+%D the color scheme. Instead of recalculating all those colors, we use a quick and
+%D dirty approach:
+%D
+%D \starttyping
+%D \negativecolorbox0
+%D \stoptyping
+%D
+%D will negate the colors in box zero.
+
+\permanent\protected\def\negatecolorbox#1% or just set attr of #1
+ {\setbox#1\hpack to \wd#1%
+ {\scratchdimen\wd#1\relax
+ \startnegative % might change
+ \blackrule[\c!color=\s!white,\c!height=\ht#1,\c!depth=\dp#1,\c!width=\scratchdimen]%
+ \hskip-\scratchdimen
+ \leaders\box#1\hfill% this triggers application to the box .. a real dirty hack!
+ \stopnegative}}
+
+%D There are in principle two ways to handle overprint: bound to colors or
+%D independent. For the moment we only support independent overprint handling. Here
+%D we deal with a per-document setting.
+
+%D We can move the stack and attribute setting to the \LUA\ end.
+
+\installcorenamespace{colorintent}
+
+\permanent\protected\def\registercolorintent#1#2% \relax is needed !
+ {\setevalue{\??colorintent#1}{\c_attr_colorintent\clf_registercolorintent{#2}\relax}}
+
+\protected\def\colo_intents_set
+ {\clf_enablecolorintents
+ \protected\gdef\colo_intents_set##1{\csname\??colorintent##1\endcsname}%
+ \colo_intents_set}
+
+\registercolorintent{knockout} {knockout}
+\registercolorintent{overprint}{overprint}
+
+\installattributestack\colorintentattribute
+
+\setevalue{\??colorintent\v!none}{\c_attr_colorintent\attributeunsetvalue} % or reset? used at all?
+
+\permanent\protected\def\startcolorintent[#1]%
+ {\pushattribute\colorintentattribute
+ \colo_intents_set{#1}}
+
+\permanent\protected\def\stopcolorintent
+ {\popattribute\colorintentattribute}
+
+\permanent\protected\def\startoverprint{\startcolorintent[\v!overprint]}
+\permanent\protected\def\startknockout {\startcolorintent[\v!knockout ]}
+
+\aliased\let\stopoverprint\stopcolorintent
+\aliased\let\stopknockout \stopcolorintent
+
+\aliased\let\starttextcolorintent\relax
+\aliased\let\stoptextcolorintent \relax
+
+\appendtoks
+ \colo_intents_set_text
+\to \everysetupcolors
+
+\let\p_colo_intent\empty
+
+\def\colo_intents_set_text
+ {\edef\p_colo_intent{\colorsparameter\c!intent}%
+ \ifx\p_colo_intent\v!none \else
+ \enforced\protected\xdef\starttextcolorintent{\colo_intents_set{\p_colo_intent}}%
+ \glet\colo_intents_set_text\relax
+ \colo_intents_set\p_colo_intent
+ \fi}
+
+\appendtoks \starttextcolorintent \to \everystarttextproperties
+\appendtoks \stoptextcolorintent \to \everystoptextproperties
+
+\setupcolors
+ [\c!intent=\v!none]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/colo-grp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/colo-grp.mkxl
new file mode 100644
index 00000000000..9237952d750
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/colo-grp.mkxl
@@ -0,0 +1,163 @@
+%D \module
+%D [ file=colo-grp,
+%D version=2011.12.27, % moved from colo-ini
+%D title=\CONTEXT\ Color Macros,
+%D subtitle=Groups,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Regular colors and palets are used most, contrary to groups which is why we
+%D define their support in a separate module.
+
+\writestatus{loading}{ConTeXt Color Macros / Groups}
+
+\unprotect
+
+%D \macros
+%D {definecolorgroup}
+%D
+%D The naming of the colors in this palet suggests some ordening, which in turn is
+%D suported by color grouping.
+%D
+%D \starttyping
+%D \definecolorgroup
+%D [red]
+%D [1.00:0.90:0.90,
+%D 1.00:0.80:0.80,
+%D 1.00:0.70:0.70,
+%D 1.00:0.55:0.55,
+%D 1.00:0.40:0.40,
+%D 1.00:0.25:0.25,
+%D 1.00:0.15:0.15,
+%D 0.90:0.00:0.00]
+%D \stoptyping
+%D
+%D In such a color group colors are numbered from~$1$ to~$n$.
+%D
+%D \showsetup{definecolorgroup}
+%D
+%D This kind of specification is not only more compact than defining each color
+%D separate, it also loads faster and takes less bytes.
+
+\installcorenamespace{colorgroup}
+\installcorenamespace{colorgroupsetter}
+
+\newcount\c_colo_groups_n % scratch counter
+
+\permanent\tolerant\protected\def\definecolorgroup[#1]#*[#2]#*[#3]% % sort of obsolete, just use palets directly
+ {\ifarguments\or\or
+ \ifhastok:{#2}%
+ \colo_groups_define_normal[#1][\v!rgb][#2]
+ \else
+ \colo_groups_define_checked[#1][#2][]%
+ \fi
+ \else
+ \colo_groups_define_normal[#1][#2][#3]%
+ \fi}
+
+\def\colo_groups_define_normal[#1][#2][#3]%
+ {\c_colo_groups_n\zerocount
+ \processcommalist[#3]{\colo_groups_define_entry{#1}{#2}}}
+
+\def\colo_groups_define_checked[#1][#2][#3]%
+ {\doloop % inherited
+ {\ifcsname#2:##1\endcsname
+ \definecolor[#1:##1][#2:##1]%
+ \else
+ \exitloop
+ \fi}}
+
+\def\colo_groups_define_entry#1#2#3% name mode specification
+ {\advance\c_colo_groups_n\plusone
+ \csname\??colorgroupsetter\ifcsname\??colorgroupsetter#2\endcsname#2\else\s!rgb\fi\endcsname[#1:\the\c_colo_groups_n][#3:0:0:0:0]}
+
+\setvalue{\??colorgroupsetter\s!gray}[#1][#2:#3]{\definecolor[#1][s=#2]}
+\setvalue{\??colorgroupsetter\s!rgb }[#1][#2:#3:#4:#5]{\definecolor[#1][r=#2,g=#3,b=#4]}
+\setvalue{\??colorgroupsetter\s!cmyk}[#1][#2:#3:#4:#5:#6]{\definecolor[#1][c=#2,m=#3=,y=#4,k=#5]}
+\setvalue{\??colorgroupsetter\s!spot}[#1][#2:#3:#4]{\definespotcolor[#1][#2][p=#3]}
+
+%D \macros
+%D {showcolorgroup}
+%D
+%D We can show the group by:
+%D
+%D \startbuffer
+%D \showcolorgroup [blue] [horizontal,name,number,value]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D or in color:
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D which uses:
+%D
+%D \showsetup{showcolorgroup}
+
+\fetchmodulecommand \showcolorgroup \f!colo_run
+
+%D There are ten predefined color groups, like \color [green] {\em groen}, \color
+%D [red] {\em rood}, \color [blue] {\em blauw}, \color [cyan] {\em cyaan}, \color
+%D [magenta] {\em magenta} and \color [yellow] {\em geel}.
+%D
+%D \startlinecorrection
+%D \hbox to \hsize
+%D {\hss
+%D \showcolorgroup [red] [vertical,name,number]\hss
+%D \showcolorgroup [green] [vertical,name]\hss
+%D \showcolorgroup [blue] [vertical,name]\hss
+%D \showcolorgroup [cyan] [vertical,name]\hss
+%D \showcolorgroup [magenta][vertical,name]\hss
+%D \showcolorgroup [yellow] [vertical,name]\hss}
+%D \stoplinecorrection
+%D
+%D These groups are used to define palets {\em alfa} upto {\em zeta}. As long as we
+%D don't use colors from the same row, we get ourselves distinctive palets. By
+%D activating such a palet one gains access to its members {\em top} to {\em charm}
+%D (of course one should use more suitable names than these).
+%D
+%D \startlinecorrection
+%D \hbox to \hsize
+%D {\showpalet [alfa] [vertical,name,number]\hss
+%D \showpalet [beta] [vertical,name]\hss
+%D \showpalet [gamma] [vertical,name]\hss
+%D \showpalet [delta] [vertical,name]\hss
+%D \showpalet [epsilon] [vertical,name]\hss
+%D \showpalet [zeta] [vertical,name]}
+%D \stoplinecorrection
+%D
+%D By using the keyword \type {value} the individual color components are shown too.
+%D When printed in color, these showcases show both the colors and the gray value.
+
+%D \macros
+%D {comparecolorgroup}
+%D
+%D The similar command:
+%D
+%D \startbuffer
+%D \comparecolorgroup [blue]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D shows color groups:
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D this commands are defined as:
+%D
+%D \showsetup{comparecolorgroup}
+
+\fetchmodulecommand \comparecolorgroup \f!colo_run
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/colo-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/colo-ini.mkxl
new file mode 100644
index 00000000000..c096fe90f2c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/colo-ini.mkxl
@@ -0,0 +1,1378 @@
+%D \module
+%D [ file=colo-ini,
+%D version=2007.08.08,
+%D title=\CONTEXT\ Color Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Color Macros / Initialization}
+
+%D Todo: move color space in color attribute (last two bits)
+
+%D This module implements color. Since \MKII\ and \MKIV\ use a completely
+%D different approach, this module only implements a few generic mechanisms.
+
+\installcorenamespace{color}
+\installcorenamespace{colorattribute}
+\installcorenamespace{transparencyattribute}
+\installcorenamespace{colorsetter}
+\installcorenamespace{transparencysetter}
+\installcorenamespace{colorpaletspecification}
+\installcorenamespace{colorpalet}
+\installcorenamespace{colorstack}
+\installcorenamespace{colorconversions}
+\installcorenamespace{colornumber}
+
+\registerctxluafile{colo-ini}{}
+\registerctxluafile{colo-icc}{}
+
+\unprotect
+
+%D In \MKIV\ we have independent color, colorspace and transparency but I'm still
+%D not sure if I keep it that way as it's probably more efficient to combine them
+%D (less attributes and finalizing). If it becomes a bottleneck we can set up a more
+%D complex system with one shared attribute for colorspace, color and transparency.
+%D
+%D When typesetting for paper, we prefer using the \CMYK\ color space, but for
+%D on||screen viewing we prefer \RGB\ (the previous implementation supported only
+%D this scheme). Independant of such specifications, we support some automatic
+%D conversions:
+%D
+%D \startitemize[packed]
+%D \item convert all colors to \RGB
+%D \item convert all colors to \CMYK
+%D \item convert all colors to gray scales
+%D \stopitemize
+%D
+%D These options are communicated by means of:
+
+\newconditional\c_colo_rgb_supported
+\newconditional\c_colo_cmyk_supported
+\newconditional\c_colo_spot_supported % backend driven
+\newconditional\c_colo_convert_gray \settrue\c_colo_convert_gray
+\newconditional\c_colo_enabled
+\newconditional\c_colo_expanded
+
+\let\m_colo_weight_gray\v!yes
+
+\mutable\let\currentcolormodel \empty
+\mutable\let\currentcolorname \empty
+\mutable\let\currentcolorpalet \empty
+\mutable\let\currentcolorprefix\empty % \currentcolorpalet:
+
+%D \macros
+%D {definecolor,defineglobalcolor,definenamedcolor,definespotcolor,definemultitonecolor,
+%D definetransparency}
+%D
+%D \starttyping
+%D \definecolor [blue] [c=1,m=.38,y=0,k=.64] % pantone pms 2965 uncoated m
+%D \definecolor [yellow] [c=0,m=.28,y=1,k=.06] % pantone pms 124 uncoated m
+%D
+%D \definespotcolor [blue-100] [blue] [p=1]
+%D \definespotcolor [yellow-100] [yellow] [p=1]
+%D
+%D \definemultitonecolor [pdftoolscolor] [blue=.12,yellow=.28] [c=.1,m=.1,y=.3,k=.1]
+%D
+%D \defineprocesscolor[myred][r=.5]
+%D \defineprocesscolor[myredish][red][a=1,t=.5]
+%D
+%D \blackrule[color=myred,width=\hsize,height=1cm] \par
+%D \blackrule[color=myredish,width=\hsize,height=1cm]
+%D
+%D \useexternalfigure[demofig][mill.png][object=no]
+%D
+%D \startcombination[4*1]
+%D {\externalfigure[demofig]} {no color}
+%D {\externalfigure[demofig][color=pdftoolscolor]} {indexed duotone}
+%D {\externalfigure[demofig][color=blue-100]} {spot color}
+%D {\externalfigure[demofig][color=yellow-100]} {spot color}
+%D \stopcombination
+%D \stoptyping
+
+\permanent\protected\def\loadfoundcolorsetfile#1#2% name foundname / not a user command
+ {\startreadingfile
+ \startcolorset[#1]%
+ \input{#2}%
+ \stopcolorset
+ \showmessage\m!colors4{#1}%
+ \stopreadingfile}
+
+%D \macros
+%D {startcolor,stopcolor,color,graycolor}
+%D
+%D The local and global commands are here just for compatibility with \MKII.
+%D
+%D \showsetup{startcolor}
+%D
+%D The simple color commands are:
+%D
+%D \showsetup{color}
+%D \showsetup{graycolor}
+
+%D We expect sane behaviour in \MKIV\ so we don't used grouped command any
+%D longer.
+
+% \testfeatureonce{100000}{\color[red]{}} % 1.046 => 0.541
+
+\permanent\protected\def\switchtocolor[#1]{\begincsname#1\endcsname}
+
+% transparency
+
+\permanent\protected\def\transparent[#1]%
+ {\bgroup
+ \edef\scratchstringone{#1}%
+ % the \relax catches a non existent csname
+ \ifx\scratchstringone\v!reset
+ \c_attr_transparency\attributeunsetvalue
+ \else
+ \begincsname\??transparencysetter\scratchstringone\endcsname\relax
+ \fi
+ \let\nexttoken}
+
+\permanent\protected\def\starttransparent[#1]%$
+ {\begingroup
+ \edef\scratchstringone{#1}%
+ \ifx\scratchstringone\v!reset
+ \c_attr_transparency\attributeunsetvalue
+ \else
+ \begincsname\??transparencysetter\scratchstringone\endcsname\relax
+ \fi}
+
+\permanent\protected\def\stoptransparent
+ {\endgroup}
+
+% color
+
+\permanent\protected\def\coloronly[#1]%
+ {\bgroup
+ \edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop_only
+ \else
+ \colo_helpers_activate_yes_only
+ \fi
+ \let\nexttoken}
+
+\permanent\protected\def\startcoloronly[#1]%$
+ {\begingroup
+ \edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop_only
+ \else
+ \colo_helpers_activate_yes_only
+ \fi}
+
+\permanent\protected\def\stopcoloronly
+ {\endgroup}
+
+% color + transparency
+
+\permanent\protected\def\color[#1]%
+ {\bgroup
+ \edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop
+ \else
+ \colo_helpers_activate_yes
+ \fi
+ \let\nexttoken}
+
+\permanent\protected\def\graycolor[#1]%
+ {\bgroup
+ \colo_helpers_set_model\s!gray\colo_helpers_activate{#1}%
+ \let\nexttoken}
+
+\permanent\protected\def\startcolor[#1]%$
+ {\begingroup
+ \edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop
+ \else
+ \colo_helpers_activate_yes
+ \fi}
+
+\permanent\protected\def\stopcolor
+ {\endgroup}
+
+\permanent\protected\def\colored[#1]%
+ {\bgroup
+ \colo_basics_defined_and_activated{#1}%
+ \let\nexttoken}
+
+\permanent\protected\def\fastcolored[#1]#2%
+ {\begingroup % is this command still needed?
+ \colo_basics_defined_and_activated{#1}%
+ #2%
+ \endgroup}
+
+\permanent\protected\def\directcolored[#1]%
+ {\colo_basics_defined_and_activated{#1}}
+
+\permanent\protected\def\fastcolor[#1]#2%
+ {\begingroup % is this command still needed?
+ \edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop
+ \else
+ \colo_helpers_activate_yes
+ \fi
+ #2%
+ \endgroup}
+
+\permanent\protected\def\directcolor[#1]%
+ {\edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop
+ \else
+ \colo_helpers_activate_yes
+ \fi}
+
+%D The following command is obsolete:
+
+\permanent\protected\def\startcolorpage{\startcolor[\ifempty\maintextcolor\defaulttextcolor\else\maintextcolor\fi]}
+\permanent\protected\def\stopcolorpage {\stopcolor}
+
+\permanent\protected\def\getcolorattributevalue#1#2% obsolete, use \thecolorattribute instead ...
+ {\begingroup
+ \colo_helpers_activate{#1}%
+ \normalexpanded{\endgroup\edef\noexpand#2{\ifnum\c_attr_color=\attributeunsetvalue\else\number\c_attr_color\fi}}}
+
+% \aliased\let\grey\graycolor % these macros are only used in tracing
+
+%D \macros
+%D {startcurrentcolor,stopcurrentcolor}
+
+\permanent\protected\def\startcurrentcolor{\startcolor[\outercolorname]}
+\permanent\protected\def\stopcurrentcolor {\stopcolor}
+
+%D \macros
+%D {setupcolor}
+%D
+%D Color definitions can be grouped in files with the name:
+%D
+%D \starttyping
+%D colo-imp-tag.tex
+%D \stoptyping
+%D
+%D Loading such a file is done by:
+%D
+%D \showsetup{usecolors}
+%D
+%D Some default colors are specified in \type {colo-imp-rgb}, which is loaded into
+%D the format by default.
+%D
+%D Some demo palets and groups are defined in \type {colo-imp-dem} which can be
+%D loaded with:
+%D
+%D \starttyping
+%D \usecolors[dem]
+%D \stoptyping
+
+\permanent\protected\def\startcolorset[#1]{\clf_startcolorset{#1}}
+\permanent\protected\def\stopcolorset {\clf_stopcolorset}
+\permanent\protected\def\usecolors [#1]{\clf_usecolors{#1}}
+
+\aliased\let\setupcolor\usecolors
+
+\installsetuponlycommandhandler \??color {colors}
+
+\let\colo_helpers_show_message\gobblethreearguments
+
+% \newtoks\everysetupcolors
+
+\let\v_colo_freeze_state\s!false
+
+\setvalue{\??colorconversions\v!yes}%
+ {\settrue \c_colo_convert_gray}
+
+\setvalue{\??colorconversions\v!always}%
+ {\settrue \c_colo_convert_gray
+ \setfalse\c_colo_rgb_supported
+ \setfalse\c_colo_cmyk_supported}
+
+\appendtoks
+ %
+ \doifelse{\directcolorsparameter\c!spot }\v!yes \settrue \setfalse\c_colo_spot_supported
+ \doifelse{\directcolorsparameter\c!expansion}\v!yes \settrue \setfalse\c_colo_expanded
+ \doifelse{\directcolorsparameter\c!rgb }\v!yes \settrue \setfalse\c_colo_rgb_supported
+ \doifelse{\directcolorsparameter\c!cmyk }\v!yes \settrue \setfalse\c_colo_cmyk_supported
+ \doifelse{\directcolorsparameter\c!state }\v!start\settrue \setfalse\c_colo_enabled
+ %
+ \edef\m_colo_weight_gray{\directcolorsparameter\c!factor}%
+ %
+ \ifconditional\c_colo_expanded
+ \let\v_colo_freeze_state\s!true
+ \else
+ \let\v_colo_freeze_state\s!false
+ \fi
+ %
+ \setfalse\c_colo_convert_gray
+ \begincsname\??colorconversions\directcolorsparameter\c!conversion\endcsname
+ % too often:
+ \ifconditional\c_colo_rgb_supported \colo_helpers_show_message\m!colors{10}\v!rgb \fi
+ \ifconditional\c_colo_cmyk_supported\colo_helpers_show_message\m!colors{10}\v!cmyk\fi
+ \colo_helpers_set_current_model
+ \ifproductionrun
+ \edef\p_pagecolormodel{\directcolorsparameter\c!pagecolormodel}%
+ \clf_setpagecolormodel{\ifx\p_pagecolormodel\v!auto\currentcolormodel\else\p_pagecolormodel\fi}%
+ \fi
+\to \everysetupcolors
+
+\appendtoks
+ \setupcolors[\c!state=\v!start]%
+ \clf_enablecolor % this can as well happen when
+ \clf_enabletransparency % the handler is defined in lua
+ \let\colo_helpers_show_message\showmessage
+\to \everyjob
+
+%D We provide stacking independent of grouping.
+
+\newcount\c_colo_nesting
+
+\permanent\protected\def\pushcolor[#1]%
+ {\global\advance\c_colo_nesting\plusone
+ \edefcsname\??colorstack\number\c_colo_nesting\endcsname
+ {\c_attr_colormodel \the\c_attr_colormodel
+ \c_attr_color \the\c_attr_color
+ \c_attr_transparency\the\c_attr_transparency
+ \relax}% stack
+ \colo_helpers_activate{#1}}
+
+\permanent\protected\def\popcolor
+ {\csname\??colorstack\number\c_colo_nesting\endcsname
+ \global\advance\c_colo_nesting\minusone}
+
+% a simple one: assumes grouping
+
+\newcount\c_colo_saved_attribute_color
+\newcount\c_colo_saved_attribute_transparency
+
+\permanent\protected\def\savecolor
+ {\c_colo_saved_attribute_color \c_attr_color
+ \c_colo_saved_attribute_transparency\c_attr_transparency}
+
+\permanent\protected\def\restorecolor
+ {\c_attr_color \c_colo_saved_attribute_color
+ \c_attr_transparency\c_colo_saved_attribute_transparency}
+
+%D In this documentation we will not go into too much details on palets. Curious
+%D users can find more information on this topic in \from[use of color].
+%D
+%D At the moment we implemented color in \CONTEXT\ color printing was not yet on the
+%D desktop. In spite of this lack our graphics designer made colorfull
+%D illustrations. When printed on a black and white printer, distinctive colors can
+%D come out equally gray. We therefore decided to use only colors that were
+%D distinctive in colors as well as in black and white print.
+%D
+%D Although none of the graphic packages we used supported logical colors and global
+%D color redefition, we build this support into \CONTEXT. This enabled us to
+%D experiment and also prepared us for the future.
+
+%D \macros
+%D {definepalet}
+%D
+%D Colors are grouped in palets. The colors in such a palet can have colorful names,
+%D but best is to use names that specify their use, like {\em important} or {\em
+%D danger}. As a sort of example \CONTEXT\ has some palets predefined,
+%D like:\footnote{At the time I wrote the palet support, I was reading 'A hort
+%D history of time' of S.~Hawkins, so that's why we stuck to quarks.}
+%D
+%D \starttyping
+%D \definepalet
+%D [alfa]
+%D [ top=rood:7,
+%D bottom=groen:6,
+%D up=blauw:5,
+%D down=cyaan:4,
+%D strange=magenta:3,
+%D charm=geel:2]
+%D \stoptyping
+%D
+%D It's formal definition is:
+%D
+%D \showsetup{definepalet}
+%D
+%D Visualized, such a palet looks like:
+%D
+%D \startbuffer[palet]
+%D \showpalet [alfa] [horizontal,name,number,value]
+%D \stopbuffer
+%D
+%D \startlinecorrection
+%D \getbuffer[palet]
+%D \stoplinecorrection
+%D
+%D This bar shows both the color and gray alternatives of the palet components (not
+%D visible in black and white print).
+%D
+%D When needed, one can copy a palet by saying:
+%D
+%D \starttyping
+%D \definepalet [TEXcolorpretty] [colorpretty]
+%D \stoptyping
+%D
+%D This saves us some typing in for instance the modules that deal with pretty
+%D verbatim typesetting.
+
+\installcorenamespace{paletlist}
+\installcorenamespace{paletsize}
+
+\let\m_colo_palet\relax
+\let\c_colo_palet\relax
+
+\def\colo_palet_allocate#1%
+ {\letcsname\??paletlist#1\endcsname\empty
+ \ifcsname\??paletsize#1\endcsname\else
+ \expandafter\newcount\csname\??paletsize#1\endcsname
+ \fi}
+
+\def\colo_palet_prepare#1%
+ {\edef\colo_palet_name{#1}%
+ \ifcsname\??paletlist\colo_palet_name\endcsname
+ \csname\??paletsize\colo_palet_name\endcsname\zerocount
+ \else
+ \colo_palet_allocate\colo_palet_name
+ \fi
+ \enforced\expandafter\let\expandafter\c_colo_palet\csname\??paletsize\colo_palet_name\endcsname
+ \edef\m_colo_palet{\begincsname\??paletlist\colo_palet_name\endcsname}}
+
+\def\colo_palet_extend#1%
+ {\addtocommalist{#1}\m_colo_palet
+ \letcsname\??paletlist\colo_palet_name\endcsname\m_colo_palet
+ %\advance\csname\??paletsize\colo_palet_name\endcsname\plusone
+ \advance\c_colo_palet\plusone}
+
+\permanent\protected\def\doifelsecolorpalet#1%
+ {\ifcsname\??paletlist#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\mutable\let\paletlist\empty
+\mutable\let\paletsize\!!zerocount
+
+\permanent\protected\def\getpaletlist[#1]%
+ {\edef\paletlist{\begincsname\??paletlist#1\endcsname}}
+
+\permanent\protected\def\getpaletsize[#1]%
+ {\ifcsname\??paletsize#1\endcsname
+ \edef\paletsize{\the\lastnamedcs}%
+ \else
+ \let\paletsize\!!zerocount
+ \fi}
+
+\permanent\tolerant\protected\def\definepalet[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ \expandafter\gobblethreearguments
+ \or
+ \expandafter\gobblethreearguments
+ \or
+ \expandafter\colo_palets_define_a
+ \or
+ \expandafter\colo_palets_define_b
+ \fi{#1}{#2}{#3}}
+
+\protected\def\colo_palets_define_a#1#2#3%
+ {\colo_palet_prepare{#1}%
+ \ifcondition\validassignment{#2}%
+ \edefcsname\??colorpaletspecification#1\endcsname{#2}%
+ \processcommalist[#2]{\colo_palets_define_one{#1}}%
+ \orelse\ifcsname\??colorpaletspecification#2\endcsname
+ \normalexpanded{\colo_palets_define_b{#1}{\lastnamedcs}\empty}%
+ \fi}
+
+\protected\def\colo_palets_define_b#1#2#3%
+ {\colo_palet_prepare{#1}%
+ \edefcsname\??colorpaletspecification#1\endcsname{#2}%
+ \colo_palet_extend{#2}%
+ \colo_palets_define_assign{#1}{#2}{#3}}
+
+% % or, it being not that critital, just this
+%
+% \permanent\tolerant\protected\def\definepalet[#1]#*[#2]#*[#3]%
+% {\ifarguments
+% \or
+% \or
+% \colo_palet_prepare{#1}%
+% \ifcondition\validassignment{#2}%
+% \setevalue{\??colorpaletspecification#1}{#2}%
+% \processcommalist[#2]{\colo_palets_define_one{#1}}%
+% \orelse\ifcsname\??colorpaletspecification#2\endcsname
+% \normalexpanded{\colo_palets_define_b{#1}{\lastnamedcs}{}}%
+% \fi
+% \or
+% \colo_palet_prepare{#1}%
+% \setevalue{\??colorpaletspecification#1}{#2}%
+% \colo_palet_extend{#2}%
+% \colo_palets_define_assign{#1}{#2}{#3}%
+% \fi}
+
+\def\colo_palets_define_one#1#2% get rid of { }
+ {\ifcondition\validassignment{#2}% catch empty entries
+ \colo_palets_define_two{#1}[#2]%
+ \else
+ \colo_palets_define_three{#1}{#2}%
+ \fi}
+
+\def\colo_palets_define_two#1[#2=#3]%
+ {\colo_palet_extend{#2}%
+ \colo_palets_define_set{#1}{#2}{#3}}%
+
+\def\colo_palets_define_three#1#2%
+ {\ifcsname\??colorpaletspecification#2\endcsname
+ \processcommacommand[\lastnamedcs]{\colo_palets_define_one{#1}}%
+ \fi}
+
+%D Instead of refering to colors, one can also directly specify a color:
+%D
+%D \starttyping
+%D \definepalet[test][xx=green]
+%D \definepalet[test][xx={y=.4}]
+%D \stoptyping
+
+%D \macros
+%D {setuppalet}
+%D
+%D Colors are taken from the current palet, if defined. Setting the current palet is
+%D done by:
+%D
+%D \showsetup{setuppalet}
+
+\newtoks\everysetuppalet
+
+% \newtoks\t_colo_prefix % used in mp interface
+
+\permanent\tolerant\protected\def\setuppalet[#1]%
+ {\edef\currentcolorpalet{#1}%
+ \ifempty\currentcolorpalet
+ % seems to be a reset
+ \let\currentcolorprefix\empty
+ %\t_colo_prefix\emptytoks
+ \orelse\ifcsname\??paletlist\currentcolorpalet\endcsname
+ \edef\currentcolorprefix{#1:}%
+ %\t_colo_prefix\expandafter{\currentcolorprefix}%
+ \else
+ \colo_helpers_show_message\m!colors7\currentcolorpalet
+ \let\currentcolorpalet\empty
+ \let\currentcolorprefix\empty
+ %\t_colo_prefix\emptytoks
+ \fi
+ \the\everysetuppalet
+ \colo_helpers_initialize_maintextcolor}
+
+%D \macros
+%D {showpalet}
+%D
+%D The previous visualization was typeset with:
+%D
+%D \typebuffer[palet]
+%D
+%D This commands is defined as:
+%D
+%D \showsetup{showpalet}
+
+\fetchmodulecommand \showpalet \f!colo_run
+
+%D \macros
+%D {showcolorcomponents}
+%D
+%D \starttyping
+%D \showcolorcomponents[color-1,color-2]
+%D \stoptyping
+
+\fetchmodulecommand \showcolorcomponents \f!colo_run
+
+%D \macros
+%D {comparepalet}
+%D
+%D There are some more testing macros available:
+%D
+%D \startbuffer
+%D \comparepalet [alfa]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D shows the palet colors against a background:
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D The formal definition is:
+%D
+%D \showsetup{comparepalet}
+
+\fetchmodulecommand \comparepalet \f!colo_run
+
+%D \macros
+%D {showcolor}
+%D
+%D But let's not forget that we also have the more traditional non||related colors.
+%D These show up after:
+%D
+%D \starttyping
+%D \showcolor [name]
+%D \stoptyping
+%D
+%D Where \type{name} for instance can be \type{rgb}.
+%D
+%D \showsetup{showcolor}
+
+\fetchmodulecommand \showcolor \f!colo_run
+
+%D It would make sense to put the following code in \type {colo-mps}, but it it
+%D rather low level.
+
+%D \macros
+%D {negatecolorcomponent,negatedcolorcomponent}
+%D
+%D These speak for themselves. See \type {colo-ext} for usage.
+
+\permanent\protected\def\negatecolorcomponent #1{\edef#1{\clf_negatedcolorcomponent{#1}}}
+\permanent \def\negatedcolorcomponent#1{\clf_negatedcolorcomponent{#1}}
+
+%D \macros
+%D {MPcolor}
+%D
+%D A very special macro is \type{\MPcolor}. This one can be used to pass a \CONTEXT\
+%D color to \METAPOST.
+%D
+%D \starttyping
+%D \MPcolor{my own red}
+%D \stoptyping
+%D
+%D This macro returns \METAPOST\ code that represents the
+%D color.
+%D
+%D For the moment we keep the next downward compatibility switch, i.e.\ expanded
+%D colors. However, predefined colors and palets are no longer expanded (which is
+%D what I wanted in the first place).
+%D
+%D In \MKIV\ we don't support color separation as we might now assume that printing
+%D houses have the right programs to do it themselves. If it's ever needed in \MKIV\
+%D It is relatively easy to support it in the backend code.
+
+% todo: palets in definecolor
+% todo: {\red xx} vs \red{xx}
+
+% \def\mptexcolor#1{"\dogetattributeid\s!color \somecolorattribute{#1} A"}
+%
+% \startMPpage
+% fill fullcircle scaled 10cm ;
+% fill fullcircle scaled 5cm withprescript \mptexcolor{red} withpostscript \mptexcolor{black} ;
+% fill fullcircle scaled 3cm ;
+% draw btex test etex withprescript \mptexcolor{blue} ;
+% \stopMPpage
+
+\permanent\protected\def\setcolormodel[#1]% % beware, \setupcolors will overload this, so this one is
+ {\colo_helpers_set_model{#1}} % only for local usage
+
+\def\colo_helpers_set_model#1% direct
+ {\edef\currentcolormodel{#1}%
+ \clf_setcolormodel{\currentcolormodel}{\m_colo_weight_gray}} % sets attribute at lua end
+
+\colo_helpers_set_model\s!all
+
+\def\colo_helpers_set_current_model
+ {\ifconditional\c_colo_enabled
+ \ifconditional\c_colo_rgb_supported
+ \ifconditional\c_colo_cmyk_supported
+ \colo_helpers_set_model\s!all
+ \else
+ \colo_helpers_set_model\s!rgb
+ \fi
+ \orelse\ifconditional\c_colo_cmyk_supported
+ \colo_helpers_set_model\s!cmyk
+ \orelse\ifconditional\c_colo_convert_gray
+ \colo_helpers_set_model\s!gray
+ \else
+ \colo_helpers_set_model\s!none
+ \fi
+ \orelse\ifconditional\c_colo_convert_gray
+ \colo_helpers_set_model\s!gray
+ \else
+ \colo_helpers_set_model\s!none
+ \fi}
+
+%D Currently in mkiv transparency is implemented independent of color. This costs a
+%D bit more processing time but gives the possibility to apply transparency
+%D independently in the future. Is this useful? If not we may as well combine them
+%D again in the future. By coupling we are downward compatible. When we decouple we
+%D need to do more tricky housekeeping (e.g. persist color independent
+%D transparencies when color bound ones are nil.
+
+% Since we couple definitions, we could stick to one test. Todo. Same for mpcolor.
+
+\def\v_colo_dummy_name{c_o_l_o_r}
+
+\letvalue{\??colorattribute \v_colo_dummy_name}\empty
+\letvalue{\??transparencyattribute\v_colo_dummy_name}\empty
+\letvalue{\??colorsetter \v_colo_dummy_name}\empty
+\letvalue{\??transparencysetter \v_colo_dummy_name}\empty
+
+\letvalue{\??colorsetter -}\empty % used?
+\letvalue{\??transparencysetter-}\empty % used?
+
+% new: expandable (see tbl preamble)
+
+\def\colo_helpers_direct_activate
+ {\ifempty\currentcolorprefix
+ \expandafter\colo_helpers_direct_activate_nop
+ \else
+ \expandafter\colo_helpers_direct_activate_yes
+ \fi}
+
+\def\colo_helpers_direct_activate_yes#1%
+ {\ifcsname\??colorsetter\currentcolorprefix#1\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter\currentcolorprefix#1\endcsname
+ \orelse\ifcsname\??colorsetter#1\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter#1\endcsname
+ \fi}
+
+\def\colo_helpers_direct_activate_nop#1%
+ {\ifcsname\??colorsetter#1\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter#1\endcsname
+ \fi}
+
+\def\colo_helpers_activate_dummy
+ {\begincsname\??colorsetter \v_colo_dummy_name\endcsname
+ \begincsname\??transparencysetter\v_colo_dummy_name\endcsname}
+
+% so far
+
+\permanent\protected\def\colo_helpers_activate#1% two-step is not that much faster but less tracing
+ {\edef\currentcolorname{#1}%
+ \ifempty\currentcolorprefix
+ \colo_helpers_activate_nop
+ \else
+ \colo_helpers_activate_yes
+ \fi}
+
+\def\colo_helpers_activate_yes
+ {\ifcsname\??colorsetter\currentcolorprefix\currentcolorname\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter\currentcolorprefix\currentcolorname\endcsname
+ \orelse\ifcsname\??colorsetter\currentcolorname\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter\currentcolorname\endcsname
+ \fi}
+
+\def\colo_helpers_activate_nop
+ {\ifcsname\??colorsetter\currentcolorname\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter\currentcolorname\endcsname
+ \fi}
+
+\def\colo_helpers_activate_yes_only
+ {\ifcsname\??colorsetter\currentcolorprefix\currentcolorname\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??colorsetter\currentcolorname\endcsname
+ \lastnamedcs
+ \fi}
+
+\def\colo_helpers_activate_nop_only
+ {\ifcsname\??colorsetter\currentcolorname\endcsname
+ \lastnamedcs
+ \fi}
+
+\aliased\let\dousecolorparameter\colo_helpers_activate
+
+\permanent\protected\def\dousecolorhashparameter#1#2%
+ {\ifcsname#1#2\endcsname
+ \expandafter\dousecolorparameter\csname#1#2\endcsname
+ \fi}
+
+\permanent\protected\def\dousecurrentcolorparameter
+ {\let\currentcolorname\currentcolorparameter % maybe only when success
+ \ifcsname\??colorsetter\currentcolorprefix\currentcolorparameter\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter\currentcolorprefix\currentcolorparameter\endcsname
+ \orelse\ifcsname\??colorsetter\currentcolorparameter\endcsname
+ \lastnamedcs
+ \begincsname\??transparencysetter\currentcolorparameter\endcsname
+ \fi}
+
+\permanent\protected\def\deactivatecolor % sort of public but a bad name
+ {\let\currentcolorname\s!black
+ \c_attr_color \attributeunsetvalue
+ \c_attr_transparency\attributeunsetvalue}
+
+% todo: check if color is overloading a non-color command
+
+% \let\colo_basics_synchronize\gobbleoneargument % used in mp interface
+% \let\colo_basics_inherit \gobbletwoarguments % used in mp interface
+
+\def\colo_basics_allocate#1%
+ {\expandafter\newcount\csname\??colornumber#1\endcsname
+ \clf_synccolorcount{#1}\c_syst_last_allocated_count}
+
+\def\colo_basics_synchronize#1%
+ {\ifcsname\??colornumber#1\endcsname\else
+ \colo_basics_allocate{#1}%
+ \fi
+ \clf_synccolor{#1}%
+ %\csname\??colornumber#1\endcsname\csname\??colorattribute#1\endcsname
+ \lastnamedcs\csname\??colorattribute#1\endcsname}
+
+\let\colo_basics_inherit\clf_synccolorclone
+
+\newcount\c_colo_protection
+
+\permanent\protected\def\startprotectedcolors{\advance\c_colo_protection\plusone }
+\permanent\protected\def\stopprotectedcolors {\advance\c_colo_protection\minusone}
+
+\permanent\tolerant\protected\def\definecolor[#1]#*[#2]%
+ {\iftok{#1}{#2}%
+ % maybe a warning
+ \else
+ \clf_defineprocesscolorlocal{#1}{#2}\v_colo_freeze_state\relax
+ \colo_basics_synchronize{#1}%
+ \ifcase\c_colo_protection
+ \protected\instance\defcsname#1\endcsname{\colo_helpers_activate{#1}}%
+ \fi
+ \fi}
+
+\permanent\tolerant\protected\def\defineglobalcolor[#1]#*[#2]%
+ {\iftok{#1}{#2}%
+ % maybe a warning
+ \else
+ \clf_defineprocesscolorglobal{#1}{#2}\v_colo_freeze_state\relax
+ \colo_basics_synchronize{#1}%
+ \ifcase\c_colo_protection
+ \protected\instance\setgvalue{#1}{\colo_helpers_activate{#1}}%
+ \fi
+ \fi}
+
+\aliased\let\definenamedcolor\definecolor
+
+\def\colo_basics_defined_and_activated#1%
+ {\clf_defineprocesscolordummy{#1}% we could pass dummy here too
+ \colo_basics_synchronize{\v_colo_dummy_name}%
+ \colo_helpers_activate_dummy}
+
+\permanent\tolerant\protected\def\defineprocesscolor[#1]#*[#2]#*[#3]%
+ {\ifarguments\or
+ \colo_basics_define_process_wrapup{#1}\empty
+ \or
+ \colo_basics_define_process_wrapup{#1}{#2}%
+ \or % we could just pass #3 to clf_... also when it's empty ... todo after split
+ \colo_basics_define_process_wrapup{#1}{\processcolorcomponents{#2},#3}%
+ \fi}
+
+\def\colo_basics_define_process_wrapup#1#2%
+ {\clf_defineprocesscolorlocal{#1}{#2}\v_colo_freeze_state\relax
+ \colo_basics_synchronize{#1}%
+ \ifcase\c_colo_protection
+ \protected\instance\defcsname#1\endcsname{\colo_helpers_activate{#1}}%
+ \fi}
+
+%D Spotcolors used setxvalue but that messes up currentcolor and probably no global
+%D is needed either but they are global at the lua end (true argument) so we keep
+%D that if only because spot colors often are a document wide property
+
+\permanent\tolerant\protected\def\definespotcolor[#1]#*[#2]#*[#3]%
+ {\clf_definespotcolorglobal{#1}{#2}{#3}%
+ \colo_basics_synchronize{#1}%
+ \ifcase\c_colo_protection
+ \protected\instance\setgvalue{#1}{\colo_helpers_activate{#1}}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemultitonecolor[#1]#*[#2]#*[#3]#*[#4]%
+ {\clf_definemultitonecolorglobal{#1}{#2}{#3}{#4}%
+ \colo_basics_synchronize{#1}%
+ \ifcase\c_colo_protection
+ \protected\instance\setgvalue{#1}{\colo_helpers_activate{#1}}%
+ \fi}
+
+%D Transparencies (only):
+
+\permanent\tolerant\protected\def\definetransparency[#1]#*[#2]%
+ {\clf_definetransparency{#1}{#2}}
+
+\permanent\tolerant\protected\def\defineglobaltransparency[#1]#*[#2]%
+ {\clf_definetransparencyglobal{#1}{#2}}
+
+% A goodie that replaces the startMPcolor hackery
+
+% \definecolor[red-t] [r=1,t=0.5,a=1]
+% \definecolor[green-t][g=1,t=0.5,a=1]
+%
+% \defineintermediatecolor[mycolora][0.5,red,green]
+% \defineintermediatecolor[mycolorb][0.5,red-t,green-t]
+%
+% \definecolor[mycolorc][.5(blue,red)]
+% \definecolor[mycolord][.5(blue)]
+% \definecolor[mycolord][-.5(blue,red)] % complement
+% \definecolor[mycolord][-(blue)] % complement
+%
+% \enabledirectives[colors.pgf]
+% \definecolor[mycolorx][magenta!50!yellow]
+%
+% \starttext
+% test {\mycolora OEPS} test
+% test {\mycolorb OEPS} test
+% test {\mycolorc OEPS} test
+% test {\mycolord OEPS} test
+% test {\mycolorx OEPS} test
+% \stoptext
+%
+% Beware: if we say:
+%
+% \definecolor[one][two]
+%
+% Only color one is actually defined and two is not seen at the \LUA\ end. This
+% means that this doesn't work:
+%
+% \definecolor[ColorA][red]
+% \definecolor[ColorB][.5(ColorA)]
+% \definecolor[ColorC][.5(ColorB,white)]
+%
+% But this does work:
+%
+% \definecolor[ColorA][1.0(red)]
+% \definecolor[ColorB][0.5(ColorA)]
+% \definecolor[ColorC][0.5(ColorB,white)]
+%
+% because the fractional definition results in a new definition.
+
+\permanent\tolerant\protected\def\defineintermediatecolor[#1]#*[#2]#*[#3]
+ {\colo_basics_define_intermediate_indeed[#1][#2][#3]}
+
+\def\colo_basics_define_intermediate_indeed[#1][#2,#3,#4][#5]%
+ {\clf_defineintermediatecolor % not global
+ {#1}%
+ {#2}%
+ \rawcolorattribute{#3} %
+ \rawcolorattribute{#4} %
+ \rawtransparencyattribute{#3} %
+ \rawtransparencyattribute{#4} %
+ {#5}%
+ \v_colo_freeze_state
+ \relax
+ \colo_basics_synchronize{#1}%
+ \protected\instance\defcsname#1\endcsname{\colo_helpers_activate{#1}}}
+
+%D Here is a more efficient helper for pgf:
+%D
+%D \starttyping
+%D \startluacode
+%D function commands.pgfxcolorspec(ca) -- {}{}{colorspace}{list}
+%D local cv = attributes.colors.values[ca]
+%D if cv then
+%D local model = cv[1]
+%D if model == 2 then
+%D context("{}{}{gray}{%1.3f}",cv[2])
+%D elseif model == 3 then
+%D context("{}{}{rgb}{%1.3f,%1.3f,%1.3f}",cv[3],cv[4],cv[5])
+%D elseif model == 4 then
+%D context("{}{}{cmyk}{%1.3f,%1.3f,%1.3f,%1.3f}",cv[6],cv[7],cv[8],cv[9])
+%D else
+%D context("{}{}{gray}{%1.3f}",cv[2])
+%D end
+%D else
+%D context("{}{}{gray}{0}")
+%D end
+%D end
+%D \stopluacode
+%D
+%D \def\pgfutil@registercolor#1%
+%D {\setevalue{\string\color@#1}{\noexpand\xcolor@\ctxcommand{pgfxcolorspec(\thecolorattribute{#1})}}}
+%D
+%D \definecolor [darkblue] [r=0,g=0,b=0.5]
+%D
+%D \starttikzpicture
+%D \fill [blue] (0,0) circle(1);
+%D \fill [darkblue] (0,0) circle(1);
+%D \stoptikzpicture
+%D \stoptyping
+%D
+%D \stopmode
+%D
+%D All this will move to m-tikz.mkxl as it doesn't belong in the core. They are
+%D mutable because \quote {who knows what gets redefined}.
+
+\mutable\let\color@ \relax
+\mutable\let\xcolor@\relax
+
+\mutable\protected\def\pgf@context@registercolor#1{\edefcsname\string\color@#1\endcsname{\noexpand\xcolor@{}{}\clf_pgfxcolorspec\zerocount\thecolorattribute{#1}}}
+\mutable\protected\def\pgf@context@registergray #1{\edefcsname\string\color@#1\endcsname{\noexpand\xcolor@{}{}\clf_pgfxcolorspec\plustwo \thecolorattribute{#1}}}
+\mutable\protected\def\pgf@context@registerrgb #1{\edefcsname\string\color@#1\endcsname{\noexpand\xcolor@{}{}\clf_pgfxcolorspec\plusthree\thecolorattribute{#1}}}
+\mutable\protected\def\pgf@context@registercmyk #1{\edefcsname\string\color@#1\endcsname{\noexpand\xcolor@{}{}\clf_pgfxcolorspec\plusfour \thecolorattribute{#1}}}
+
+%D \starttyping
+%D \ifdefined\pgf@context@registercolor
+%D \let\pgfutil@registercolor\pgf@context@registercolor
+%D \fi
+%D \stoptyping
+
+%D \macros
+%D {doifcolorelse, doifcolor}
+%D
+%D Switching to a color is done by means of the following command. Later on we will
+%D explain the use of palets. We define ourselves a color conditional first.
+
+\permanent\def\doifelsecolor#1%
+ {\ifcsname\??colorattribute\currentcolorprefix#1\endcsname
+ \expandafter\firstoftwoarguments
+ \orelse\ifcsname\??colorattribute#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifcolorelse\doifelsecolor
+
+\permanent\def\doifcolor#1%
+ {\ifcsname\??colorattribute\currentcolorprefix#1\endcsname
+ \expandafter\firstofoneargument
+ \orelse\ifcsname\??colorattribute#1\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+%D \macros
+%D {startregistercolor,stopregistercolor,permitcolormode}
+%D
+%D If you only want to register a color, the switch \type {\ifpermitcolormode} can
+%D be used. That way the nested colors know where to go back to.
+%D
+%D We use these macros for implementing text colors (actually, the first application
+%D was in foreground colors).
+%D
+%D \starttyping
+%D \starttextcolor[red]
+%D \dorecurse{10}{\input tufte \color[green]{oeps} \par}
+%D \stoptextcolor
+%D \stoptyping
+%D
+%D This is more efficient than the alternative:
+%D
+%D \starttyping
+%D \setupbackgrounds[text][foregroundcolor=red]
+%D \startregistercolor[red]
+%D \dorecurse{10}{\input tufte \color[green]{oeps} \par}
+%D \stopregistercolor
+%D \stoptyping
+
+\mutable\let\maintextcolor \empty
+\mutable\def\defaulttextcolor{black}
+
+\permanent\protected\def\inheritmaintextcolor
+ {\ifempty\maintextcolor\else\colo_helpers_activate\maintextcolor\fi}
+
+\permanent\protected\def\onlyinheritmaintextcolor
+ {\ifempty\maintextcolor
+ \deactivatecolor
+ \else
+ \colo_helpers_activate\maintextcolor
+ \fi}
+
+\appendtoks
+ \deactivatecolor % public?
+ \inheritmaintextcolor
+\to \everybeforeoutput
+
+\def\colo_helpers_switch_to_maintextcolor#1%
+ {\let\maintextcolor\s!themaintextcolor
+ \definecolor[\maintextcolor][#1]% can be fast one
+ \colo_helpers_activate\maintextcolor
+ \clf_registermaintextcolor\thecolorattribute\maintextcolor\relax}
+
+\permanent\protected\def\starttextcolor[#1]%
+ {\doifsomething{#1}{\colo_helpers_switch_to_maintextcolor{#1}}}
+
+\aliased\let\stoptextcolor\relax
+
+\let\p_colo_textcolor\empty
+
+\def\colo_helpers_initialize_maintextcolor
+ {\edef\p_colo_textcolor{\directcolorsparameter\c!textcolor}%
+ \ifempty\p_colo_textcolor
+ \colo_helpers_switch_to_maintextcolor\defaulttextcolor
+ \else
+ \colo_helpers_switch_to_maintextcolor\p_colo_textcolor
+ \fi}
+
+\appendtoks \colo_helpers_initialize_maintextcolor \to \everyjob
+\appendtoks \colo_helpers_initialize_maintextcolor \to \everysetupcolors
+
+\letvalue{\??colorsetter }\empty \letvalue{\??colorattribute }\!!zerocount
+\letvalue{\??transparencysetter}\empty \letvalue{\??transparencyattribute}\!!zerocount
+
+\def\colo_helpers_inherited_direct_cs#1{\ifcsname\??colorsetter #1\endcsname\lastnamedcs\fi}
+\def\colo_helpers_inherited_direct_ca#1{\ifcsname\??colorattribute #1\endcsname\lastnamedcs\else\!!zerocount\fi}
+\def\colo_helpers_inherited_direct_ts#1{\ifcsname\??transparencysetter #1\endcsname\lastnamedcs\fi}
+\def\colo_helpers_inherited_direct_ta#1{\ifcsname\??transparencyattribute#1\endcsname\lastnamedcs\else\!!zerocount\fi}
+
+\let\colo_defcsname\defcsname
+\let\colo_endcsname\endcsname
+
+\appendtoks
+ \ifconditional\c_colo_expanded
+ \let\colo_defcsname\edefcsname
+ \else
+ \let\colo_defcsname\defcsname
+ \fi
+\to \everysetupcolors
+
+\def\colo_palets_define_set#1#2#3%
+ {\ifcondition\validassignment{#3}% \definepalet[test][xx={y=.4}]
+ \expandafter\colo_palets_define_assign
+ \orelse\ifcsname\??colorsetter#3\endcsname
+ % \definepalet[test][xx=green]
+ \expandafter\colo_palets_define_inherit
+ \else
+ % not entered when making format
+ \expandafter\colo_palets_define_undefine
+ \fi
+ {#1}{#2}{#3}}
+
+\def\colo_palets_define_inherit#1#2#3%
+ {\colo_basics_inherit{#1:#2}{#3}%
+ \colo_defcsname\??colorsetter #1:#2\colo_endcsname{\colo_helpers_inherited_direct_cs{#3}}%
+ \colo_defcsname\??colorattribute #1:#2\colo_endcsname{\colo_helpers_inherited_direct_ca{#3}}%
+ \colo_defcsname\??transparencysetter #1:#2\colo_endcsname{\colo_helpers_inherited_direct_ts{#3}}%
+ \colo_defcsname\??transparencyattribute#1:#2\colo_endcsname{\colo_helpers_inherited_direct_ta{#3}}}
+
+\def\colo_palets_define_undefine#1#2#3%
+ {\letcsname\??colorsetter #1:#2\endcsname\undefined
+ \letcsname\??colorattribute #1:#2\endcsname\undefined
+ \letcsname\??transparencysetter #1:#2\endcsname\undefined
+ \letcsname\??transparencyattribute#1:#2\endcsname\undefined}
+
+\def\colo_palets_define_assign#1#2#3%
+ {\definecolor[\??colorpalet#1:#2][#3]%
+ \colo_defcsname\??colorsetter #1:#2\colo_endcsname{\colo_helpers_inherited_direct_cs{\??colorpalet#1:#2}}%
+ \colo_defcsname\??colorattribute #1:#2\colo_endcsname{\colo_helpers_inherited_direct_ca{\??colorpalet#1:#2}}%
+ \colo_defcsname\??transparencysetter #1:#2\colo_endcsname{\colo_helpers_inherited_direct_ts{\??colorpalet#1:#2}}%
+ \colo_defcsname\??transparencyattribute#1:#2\colo_endcsname{\colo_helpers_inherited_direct_ta{\??colorpalet#1:#2}}}
+
+\setvalue{\??colorattribute currentcolor}{\the\c_attr_color} % for mpcolor
+\setvalue{\??transparencyattribute currentcolor}{\the\c_attr_transparency} % for mpcolor
+
+\def\colo_helpers_inherited_current_ca#1{\ifcsname\??colorattribute \currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??colorattribute #1\endcsname\lastnamedcs\else\!!zerocount\fi}
+\def\colo_helpers_inherited_current_cs#1{\ifcsname\??colorsetter \currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??colorsetter #1\endcsname\lastnamedcs\fi}
+\def\colo_helpers_inherited_current_ta#1{\ifcsname\??transparencyattribute\currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??transparencyattribute#1\endcsname\lastnamedcs\else\!!zerocount\fi}
+\def\colo_helpers_inherited_current_ts#1{\ifcsname\??transparencysetter \currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??transparencysetter #1\endcsname\lastnamedcs\fi}
+
+%D Low level defs:
+
+\permanent\protected\def\colordefalc#1#2{\edefcsname\??colorattribute #1\endcsname{#2}%
+ \defcsname \??colorsetter #1\endcsname{\c_attr_color #2 }}
+\permanent\protected\def\colordefagc#1#2{\xdefcsname\??colorattribute #1\endcsname{#2}%
+ \gdefcsname\??colorsetter #1\endcsname{\c_attr_color #2 }} % was not g
+\permanent\protected\def\colordefalt#1#2{\edefcsname\??transparencyattribute#1\endcsname{#2}%
+ \gdefcsname\??transparencysetter #1\endcsname{\c_attr_transparency#2 }}
+\permanent\protected\def\colordefagt#1#2{\xdefcsname\??transparencyattribute#1\endcsname{#2}%
+ \gdefcsname\??transparencysetter #1\endcsname{\c_attr_transparency#2 }}
+
+\permanent\protected\def\colordefflc#1#2{\defcsname \??colorattribute #1\endcsname{\colo_helpers_inherited_current_ca{#2}}%
+ \defcsname \??colorsetter #1\endcsname{\colo_helpers_inherited_current_cs{#2}}}
+\permanent\protected\def\colordeffgc#1#2{\gdefcsname\??colorattribute #1\endcsname{\colo_helpers_inherited_current_ca{#2}}%
+ \defcsname \??colorsetter #1\endcsname{\colo_helpers_inherited_current_cs{#2}}}
+\permanent\protected\def\colordefflt#1#2{\defcsname \??transparencyattribute#1\endcsname{\colo_helpers_inherited_current_ta{#2}}%
+ \defcsname \??transparencysetter #1\endcsname{\colo_helpers_inherited_current_ts{#2}}}
+\permanent\protected\def\colordeffgt#1#2{\gdefcsname\??transparencyattribute#1\endcsname{\colo_helpers_inherited_current_ta{#2}}%
+ \defcsname \??transparencysetter #1\endcsname{\colo_helpers_inherited_current_ts{#2}}}
+
+\permanent\protected\def\colordefrlc #1{\letcsname \??colorattribute #1\endcsname\undefined
+ \letcsname \??colorsetter #1\endcsname\undefined}
+\permanent\protected\def\colordefrgc #1{\gletcsname\??colorattribute #1\endcsname\undefined
+ \gletcsname\??colorsetter #1\endcsname\undefined}
+\permanent\protected\def\colordefrlt #1{\letcsname \??transparencyattribute#1\endcsname\undefined
+ \letcsname \??transparencysetter #1\endcsname\undefined}
+\permanent\protected\def\colordefrgt #1{\gletcsname\??transparencyattribute#1\endcsname\undefined
+ \gletcsname\??transparencysetter #1\endcsname\undefined}
+
+%D \macros
+%D {colorvalue, grayvalue}
+%D
+%D We can typeset the color components using \type {\colorvalue} and \type
+%D {\grayvalue}. The commands:
+%D
+%D \startbuffer
+%D color value of SomeKindOfRed: \colorvalue{SomeKindOfRed} \crlf
+%D gray value of SomeKindOfRed: \grayvalue{SomeKindOfRed}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D show us:
+%D
+%D \startexample
+%D \getbuffer
+%D \stopexample
+
+\mutable\let\currentcolorname\s!black % todo
+\mutable\let\outercolorname \s!black % todo
+
+\mutable\let\colorformatseparator\space
+
+\permanent\def\MPcolor#1%
+ {\clf_mpcolor
+ \c_attr_colormodel
+ \colo_helpers_inherited_current_ca{#1} %
+ \colo_helpers_inherited_current_ta{#1} }
+
+\permanent\def\MPcoloronly#1%
+ {\clf_mpcolor
+ \c_attr_colormodel
+ \colo_helpers_inherited_current_ca{#1} %
+ \zerocount}
+
+\permanent\def\MPtransparency#1%
+ {\clf_mpcolor
+ \zerocount
+ \zerocount
+ \colo_helpers_inherited_current_ta{#1} }
+
+\permanent\def\MPoptions#1%
+ {\clf_mpoptions
+ \c_attr_colormodel
+ \colo_helpers_inherited_current_ca{#1} %
+ \colo_helpers_inherited_current_ta{#1} }
+
+\permanent\def\rawcolormodelattribute {\c_attr_colormodel}
+\permanent\def\thecolormodelattribute {\the\c_attr_colormodel}
+
+\permanent\def\rawcolorattribute #1{\ifcsname\??colorattribute \currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??colorattribute #1\endcsname\lastnamedcs\else\zerocount\fi}
+\permanent\def\rawtransparencyattribute#1{\ifcsname\??transparencyattribute\currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??transparencyattribute#1\endcsname\lastnamedcs\else\zerocount\fi}
+
+\permanent\def\thecolorattribute #1{\number\ifcsname\??colorattribute \currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??colorattribute #1\endcsname\lastnamedcs\else\zerocount\fi}
+\permanent\def\thetransparencyattribute#1{\number\ifcsname\??transparencyattribute\currentcolorprefix#1\endcsname\lastnamedcs\orelse\ifcsname\??transparencyattribute#1\endcsname\lastnamedcs\else\zerocount\fi}
+
+\permanent\def\internalspotcolorname #1{\clf_spotcolorname \rawcolorattribute{#1}}
+\permanent\def\internalspotcolorparent #1{\clf_spotcolorparent\rawcolorattribute{#1}}
+\permanent\def\internalspotcolorsize #1{\clf_spotcolorvalue \rawcolorattribute{#1}}
+
+\permanent\def\colorcomponents #1{\clf_colorcomponents \rawcolorattribute {#1}}
+\permanent\def\transparencycomponents #1{\clf_transparencycomponents\rawtransparencyattribute{#1}}
+\permanent\def\processcolorcomponents #1{\clf_processcolorcomponents\rawcolorattribute {#1}}
+
+\permanent\def\colorvalue #1{\clf_formatcolor\rawcolorattribute{#1}{\colorformatseparator}}
+\permanent\def\grayvalue #1{\clf_formatgray \rawcolorattribute{#1}{\colorformatseparator}}
+
+\permanent\def\doifelseblack #1{\clf_doifelseblack\rawcolorattribute{#1}}
+\permanent\def\doifelsedrawingblack {\clf_doifelsedrawingblack}
+
+\aliased\let\doifblackelse \doifelseblack
+\aliased\let\doifdrawingblackelse \doifelsedrawingblack
+
+%D \macros
+%D {forcecolorhack}
+%D
+%D We can out this in front of (for instance) a special and so force color to be
+%D applied (only glyphs, rules and leaders are handled).
+%D
+%D \startbuffer
+%D \framed
+%D [background=color,backgroundcolor=yellow,framecolor=red,corner=round]
+%D {test}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+% ignores in attribute handler
+%
+% \def\forcecolorhack{\vrule\s!width\zeropoint\s!height\zeropoint\s!depth\zeropoint}
+
+% \normal added else fails in metafun manual (leaders do a hard scan)
+
+% \protected\def\forcecolorhack{\leaders\hrule\hskip\zeropoint\relax} % relax is needed !
+%
+% I really need to sort this out!
+
+\permanent\protected\def\forcecolorhack{\leaders\hrule\s!height\zeropoint\s!depth\zeropoint\hskip\zeropoint\relax} % relax is needed !
+
+%D We default to the colors defined in \type {colo-imp-rgb} and support both \RGB\
+%D and \CMYK\ output. Transparencies are defined here:
+
+\definecolor[black][s=0]
+\definecolor[white][s=1]
+
+\definetransparency [none] [0]
+\definetransparency [normal] [1]
+\definetransparency [multiply] [2]
+\definetransparency [screen] [3]
+\definetransparency [overlay] [4]
+\definetransparency [softlight] [5]
+\definetransparency [hardlight] [6]
+\definetransparency [colordodge] [7]
+\definetransparency [colorburn] [8]
+\definetransparency [darken] [9]
+\definetransparency [lighten] [10]
+\definetransparency [difference] [11]
+\definetransparency [exclusion] [12]
+\definetransparency [hue] [13]
+\definetransparency [saturation] [14]
+\definetransparency [color] [15]
+\definetransparency [luminosity] [16]
+
+%D Some common ones:
+
+\definecolor[trace:0][s=.4]
+\definecolor[trace:1][r=.6]
+\definecolor[trace:2][g=.6]
+\definecolor[trace:3][b=.6]
+\definecolor[trace:4][r=.6,g=.6]
+\definecolor[trace:5][r=.6,b=.6]
+\definecolor[trace:6][g=.6,b=.6]
+\definecolor[trace:7][r=.8,g=.4]
+\definecolor[trace:8][r=.8,b=.4]
+\definecolor[trace:9][g=.4,b=.8]
+
+\definecolor[trace:w][s=1]
+\definecolor[trace:r][r=.75,t=.5,a=1]
+\definecolor[trace:g][g=.75,t=.5,a=1]
+\definecolor[trace:b][b=.75,t=.5,a=1]
+%definecolor[trace:c][c=.75,t=.5,a=1]
+%definecolor[trace:m][m=.75,t=.5,a=1]
+%definecolor[trace:y][y=.75,t=.5,a=1]
+\definecolor[trace:c][g=.75,b=.75,t=.5,a=1]
+\definecolor[trace:m][r=.75,b=.75,t=.5,a=1]
+\definecolor[trace:y][r=.75,g=.75,t=.5,a=1]
+\definecolor[trace:s][s=.75,t=.5,a=1]
+\definecolor[trace:d][s=.25,t=.5,a=1]
+\definecolor[trace:o][r=1,g=.6,b=.1,t=.5,a=1]
+
+\definecolor[trace:dw][s=1]
+\definecolor[trace:dr][r=.75,t=.75,a=1]
+\definecolor[trace:dg][g=.75,t=.75,a=1]
+\definecolor[trace:db][b=.75,t=.75,a=1]
+%definecolor[trace:dc][c=.75,t=.75,a=1]
+%definecolor[trace:dm][m=.75,t=.75,a=1]
+%definecolor[trace:dy][y=.75,t=.75,a=1]
+\definecolor[trace:dc][g=.75,b=.75,t=.75,a=1]
+\definecolor[trace:dm][r=.75,b=.75,t=.75,a=1]
+\definecolor[trace:dy][r=.75,g=.75,t=.75,a=1]
+\definecolor[trace:ds][s=.75,t=.75,a=1]
+\definecolor[trace:dd][s=.25,t=.75,a=1]
+\definecolor[trace:do][r=1,g=.6,b=.1,t=.75,a=1]
+
+\setupcolors
+ [\c!state=\v!stop, % color will be enabled later on
+ \c!conversion=\v!yes,
+ \c!rgb=\v!yes,
+ \c!cmyk=\v!yes,
+ \c!spot=\v!yes,
+ \c!pagecolormodel=\v!none,
+ \c!expansion=\v!no,
+ \c!textcolor=,
+ \c!factor=\v!yes]
+
+\setupcolor
+ [\v!rgb]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/colo-nod.lmt b/Master/texmf-dist/tex/context/base/mkxl/colo-nod.lmt
new file mode 100644
index 00000000000..67487fee522
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/colo-nod.lmt
@@ -0,0 +1,40 @@
+if not modules then modules = { } end modules ['node-tra'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local nuts = nodes.nuts
+
+local setattrs = nuts.setattrs
+
+local privateattributes = attributes.private
+
+local a_colormodel = privateattributes('colormodel')
+local a_color = privateattributes('color')
+local a_transparency = privateattributes('transparency')
+
+local colors = { }
+nuts.colors = colors
+
+function colors.set(n,ma,ca,ta) -- we could also do layers here
+ if ca then
+ if ca > 0 then
+ if not ma or ma == 0 then
+ ma = 1
+ end
+ if ta then
+ setattrs(n,a_colormodel,ma,a_color,ca,a_transparency,ta)
+ else
+ setattrs(n,a_colormodel,ma,a_color,ca)
+ end
+ end
+ elseif ta then
+ if ta > 0 then
+ setattr(n,a_transparency,ta)
+ end
+ end
+ return n
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/colo-nod.mkxl b/Master/texmf-dist/tex/context/base/mkxl/colo-nod.mkxl
new file mode 100644
index 00000000000..3cc3db03366
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/colo-nod.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=colo-nod,
+%D version=2020.12.03,
+%D title=\CONTEXT\ Color Macros,
+%D subtitle=Nodes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Color Macros / Nodes}
+
+%D In \LMTX\ we moved some code here.
+
+\registerctxluafile{colo-nod}{}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-cs.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-cs.mkxl
new file mode 100644
index 00000000000..2167b568f92
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-cs.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-cs,
+%D version=1998.12.02,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ Czech Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {czech}
+\immutable\def\defaultlanguagetag{cs}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-de.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-de.mkxl
new file mode 100644
index 00000000000..1f54bbb30a0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-de.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-de,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ German Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {german}
+\immutable\def\defaultlanguagetag{de}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-en.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-en.mkxl
new file mode 100644
index 00000000000..5dd02f7d472
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-en.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-en,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ English Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {english}
+\immutable\def\defaultlanguagetag{en}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-fil.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-fil.mkxl
new file mode 100644
index 00000000000..0ca3bb05acd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-fil.mkxl
@@ -0,0 +1,152 @@
+%D \module
+%D [ file=cont-fil,
+%D version=1997.11.15,
+%D title=\CONTEXT\ Miscellaneous Macros,
+%D subtitle=File Synonyms,
+%D author=J. Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Todo: use full names and remove calls at the tex end (now integrated in \LUA\
+%D library code .. then we can load it at runtime when needed. We can also remove
+%D some of the names that no longer make sense for \LMTX.
+
+\writestatus{loading}{ConTeXt File Synonyms}
+
+\definefilesynonym [chemics] [chemic]
+
+\definefilesynonym [unit] [units]
+
+\definefilesynonym [pstric] [pstricks]
+\definefilesynonym [pstrick] [pstricks]
+
+\definefilesynonym [finance] [financ]
+
+\definefilesynonym [dir-make] [dir-01]
+\definefilesynonym [dir-identify] [dir-05]
+
+\definefilesynonym [int-load] [set-11]
+\definefilesynonym [int-make] [set-12]
+
+ \definefilesynonym [fig-base] [fig-00]
+ \definefilesynonym [fig-make] [fig-01]
+ \definefilesynonym [fig-fake] [fig-02]
+ \definefilesynonym [fig-missing] [fig-06]
+
+ \definefilesynonym [exi-interface] [exi-21]
+
+ \definefilesynonym [res-make] [res-01]
+ \definefilesynonym [res-base] [res-04]
+ \definefilesynonym [res-crop] [res-07]
+ \definefilesynonym [res-trace] [res-08]
+ \definefilesynonym [res-log] [res-09]
+ \definefilesynonym [res-identify] [res-12]
+
+ \definefilesynonym [med-show] [res-50]
+
+\definefilesynonym [pre-general] [pre-00]
+\definefilesynonym [pre-original] [present-original]
+\definefilesynonym [pre-green] [present-green]
+\definefilesynonym [pre-funny] [present-funny]
+\definefilesynonym [pre-colorful] [present-colorful]
+\definefilesynonym [pre-fuzzy] [present-fuzzy]
+\definefilesynonym [pre-polish] [pre-06]
+\definefilesynonym [pre-spider] [pre-07]
+\definefilesynonym [pre-wonder] [pre-08]
+\definefilesynonym [pre-windows] [present-windows]
+\definefilesynonym [pre-grow] [present-grow]
+\definefilesynonym [pre-stack] [present-stack]
+\definefilesynonym [pre-split] [present-split]
+\definefilesynonym [pre-balls] [present-balls]
+\definefilesynonym [pre-weird] [present-weird]
+\definefilesynonym [pre-stepwise] [present-stepwise]
+\definefilesynonym [pre-stepper] [present-stepper]
+\definefilesynonym [pre-punk] [present-punk]
+\definefilesynonym [pre-random] [present-random]
+
+\definefilesynonym [pre-arrows] [pre-12]
+\definefilesynonym [pre-writing] [pre-13]
+\definefilesynonym [pre-knot] [pre-16]
+\definefilesynonym [pre-shade] [pre-18]
+\definefilesynonym [pre-organic] [pre-19]
+\definefilesynonym [pre-speckle] [pre-20]
+\definefilesynonym [pre-zoom] [pre-21]
+\definefilesynonym [pre-cycle] [pre-22]
+\definefilesynonym [pre-super] [pre-23]
+\definefilesynonym [pre-more] [pre-26]
+
+\definefilesynonym [pre-01] [present-original]
+\definefilesynonym [pre-02] [present-green]
+\definefilesynonym [pre-03] [present-funny]
+\definefilesynonym [pre-04] [present-colorful]
+\definefilesynonym [pre-05] [present-fuzzy]
+\definefilesynonym [pre-09] [present-windows]
+\definefilesynonym [pre-10] [present-grow]
+\definefilesynonym [pre-11] [present-stack]
+\definefilesynonym [pre-14] [present-split]
+\definefilesynonym [pre-15] [present-balls]
+\definefilesynonym [pre-17] [present-weird]
+\definefilesynonym [pre-41] [present-tiles]
+\definefilesynonym [pre-60] [present-stepwise]
+\definefilesynonym [pre-61] [present-stepper]
+\definefilesynonym [pre-62] [present-overlap]
+\definefilesynonym [pre-69] [present-wobbling]
+\definefilesynonym [pre-70] [present-punk]
+\definefilesynonym [pre-71] [present-random]
+
+\definefilesynonym [abr-01] [abbreviations-pseudocaps]
+\definefilesynonym [abr-02] [abbreviations-smallcaps]
+\definefilesynonym [abr-03] [abbreviations-words]
+\definefilesynonym [abr-04] [abbreviations-mixed]
+
+\definefilesynonym [art-01] [article-basic]
+\definefilesynonym [article] [article-basic]
+\definefilesynonym [mag-01] [magazine-basic]
+\definefilesynonym [magazine] [magazine-basic]
+\definefilesynonym [mod-01] [module-basic]
+\definefilesynonym [module] [module-basic]
+
+\definefilesynonym [map-10] [maps] % for a while
+
+\definefilesynonym [mml] [mathml]
+\definefilesynonym [cml] [chemml]
+
+\definefilesynonym [letter] [cor-01]
+\definefilesynonym [memo] [cor-02]
+\definefilesynonym [resume] [cor-03]
+
+\definefilesynonym [fnt-10] [fonts-complete]
+\definefilesynonym [fnt-11] [fonts-system]
+\definefilesynonym [fnt-20] [fonts-steps]
+\definefilesynonym [fnt-21] [fonts-steps]
+\definefilesynonym [fnt-22] [fonts-engines]
+\definefilesynonym [fnt-23] [fonts-shapes]
+\definefilesynonym [fnt-24] [fonts-cjk]
+\definefilesynonym [fnt-25] [math-characters]
+\definefilesynonym [fnt-28] [fonts-goodies]
+\definefilesynonym [fnt-29] [fonts-shapes]
+\definefilesynonym [fnt-31] [fonts-coverage]
+\definefilesynonym [fnt-33] [math-coverage]
+
+\definefilesynonym [mat-10] [math-characters]
+\definefilesynonym [mat-11] [math-characters]
+\definefilesynonym [mat-12] [math-parameters]
+\definefilesynonym [mat-20] [math-parameters]
+
+\definefilesynonym [syn-01] [syntax]
+
+\definefilesynonym [reg-01] [regimes-list]
+
+\definefilesynonym [set-11] [setups-basics]
+\definefilesynonym [set-12] [setups-overview]
+%definefilesynonym [set-13] [setups-proofing]
+%definefilesynonym [set-15] [setups-generate]
+
+\definefilesynonym [ecmascript] [libs-imp-mujs]
+\definefilesynonym [zint] [libs-imp-zint]
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-fr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-fr.mkxl
new file mode 100644
index 00000000000..baee1ec71c8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-fr.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-de,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ French Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {french}
+\immutable\def\defaultlanguagetag{fr}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-gb.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-gb.mkxl
new file mode 100644
index 00000000000..0c3197a3e66
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-gb.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-uk,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ English Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {english}
+\immutable\def\defaultlanguagetag{gb}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-it.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-it.mkxl
new file mode 100644
index 00000000000..0491c2efd7a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-it.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-it,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ Italian Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {italian}
+\immutable\def\defaultlanguagetag{it}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-log.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-log.mkxl
new file mode 100644
index 00000000000..4df509f608f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-log.mkxl
@@ -0,0 +1,306 @@
+%D \module
+%D [ file=cont-log,
+%D version=1995.10.10,
+%D title=\CONTEXT\ Miscellaneous Macros,
+%D subtitle=\TEX\ Logos,
+%D author=J. Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt TeX Logos}
+
+%D The system that is used to typeset this text is called \TEX, typeset with an
+%D lowered~E. From te beginning of \TEX, authors of macro packages adapted this
+%D raising and lowering style. In this module we define some of those logos.
+
+%D We define the uppercase logo commands as (unfrozen) instances because they can be
+%D redefined in the synonyms modules.
+
+\unprotect
+
+\frozen\instance\protected\def\TeX
+ {\dontleavehmode
+ \begingroup
+ T%
+ \setbox\scratchbox\hbox{M}%
+ \kern-.1667\wd\scratchbox
+ \lower.5\exheight\hbox{E}%
+ \kern-.125\wd\scratchbox
+ X%
+ \endgroup}
+
+\frozen\instance\protected\def\TeXsuffix{\wordboundary\TeX}
+\frozen\instance\protected\def\TeXprefix{\TeX\wordboundary}
+
+\frozen\instance\protected\def\ConTeXt
+ {\dontleavehmode
+ \begingroup
+ Con%
+ \wordboundary
+ \setbox\scratchboxone\hbox{T\kern\zeropoint e}%
+ \setbox\scratchboxtwo\hbox{Te}%
+ \discretionary{-}{}{\kern\dimexpr\wd\scratchboxtwo-\wd\scratchboxone\relax}%
+ \TeX t%
+ \endgroup}
+
+\frozen\instance\protected\def\PPCHTeX{ppch\TeX}
+
+\frozen\instance\protected\def\LaTeX % requested by erik frambach
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox{L}%
+ \scratchdimen\ht\scratchbox
+ \setbox\scratchbox\hbox{\doresetattribute{case}\txx A}%
+ L\kern-.55\wd\scratchbox
+ \raise\scratchdimen\hbox{\lower\ht\scratchbox\copy\scratchbox}%
+ \kern-.2\wd\scratchbox\TeX
+ \endgroup}
+
+\frozen\instance\protected\def\TaBlE
+ {T%
+ \kern-.27\emwidth\lower.5\exheight\hbox{A}%
+ \kern-.18\emwidth B%
+ \kern-.1\emwidth\lower.5\exheight\hbox{L}%
+ \kern-.075\emwidth E}
+
+\frozen\instance\protected\def\PiCTeX
+ {P%
+ \kern-.12\emwidth\lower.5\exheight\hbox{I}%
+ \kern-.075\emwidth C%
+ \kern-.11\emwidth\TeX}
+
+\def\syst_logos_ams_script#1%
+ {\normalstartimath
+ \cal\ifdim\bodyfontsize>1.1\emwidth\scriptstyle\fi#1%
+ \normalstopimath}
+
+\frozen\instance\protected\def\AmSTeX
+ {\dontleavehmode
+ \syst_logos_ams_script A%
+ \kern-.1667\emwidth\lower.5\exheight\hbox{\syst_logos_ams_script M}%
+ \kern-.125\emwidth\syst_logos_ams_script S%
+ -\TeX}
+
+\frozen\instance\protected\def\LamSTeX
+ {L%
+ \kern-.4\emwidth\raise.3\exheight\hbox{\syst_logos_ams_script A}%
+ \kern-.25\emwidth\lower.4\exheight\hbox{\syst_logos_ams_script M}%
+ \kern-.1\emwidth{\syst_logos_ams_script S}%
+ -\TeX}
+
+%D Alternative \CONTEXT\ logo, first Idris S.~Hamid's version:
+%D
+%D \def\Context
+%D {{\sc C\kern -.0667emo\kern -.0667emn\kern -.0549emt\kern
+%D -.1667em\lower.5ex\hbox {e}\kern -.125emx\kern -.0549emt}}
+%D
+%D I changed this into one that adapts itself:
+
+\frozen\instance\protected\def\Context % wrong usage of \getscaledglyph
+ {\dontleavehmode
+ \begingroup
+ C%
+ \kern -.0667\emwidth
+ \getscaledglyph{.8}\empty{%
+ O\kern-.0667\emwidth
+ N%
+ \discretionary{-}{}{\kern-.0549\emwidth}%
+ T%
+ \doifelseitalic{\kern-.1\emwidth}{\kern-.1667\emwidth}%
+ \lower.5\exheight\hbox{E}%
+ \doifelseitalic\empty{\kern-.11em}%
+ X%
+ \kern-.055\emwidth
+ T%
+ }%
+ \endgroup}
+
+%D The \METAFONT\ and \METAPOST\ logos adapt themselves to the current fontsize, an
+%D ugly but usefull hack.
+
+\mutable\let\logofont\nullfont
+
+% \loadmapfile[original-base.map] % stil needed? not if we assume afm
+
+\permanent\protected\def\setMFPfont% more sensitive for low level changes
+ {\font\logofont=logo%
+ \ifx\fontalternative\s!bf\s!bf\orelse
+ \ifx\fontalternative\s!it\s!sl\orelse
+ \ifx\fontalternative\s!sl\s!sl\orelse
+ \ifx\fontalternative\s!bi\s!bf\orelse
+ \ifx\fontalternative\s!bs\s!bf\fi
+ 10 at \currentfontscale\bodyfontsize % there is no afm in the minimals yet
+ \logofont}
+
+\def\syst_logos_meta_hyphen % there is no hyphenchar in this font
+ {\discretionary{\vrule\s!height.33\emwidth\s!depth-.27\emwidth\s!width.33\emwidth}{}{}}
+
+\frozen\instance\protected\def\MetaFontLogo
+ {\dontleavehmode
+ \begingroup
+ \setMFPfont META\syst_logos_meta_hyphen FONT%
+ \endgroup}
+
+\protected\def\MetaPostLogo
+ {\dontleavehmode
+ \begingroup
+ \setMFPfont META\syst_logos_meta_hyphen POST%
+ \endgroup}
+
+% \protected\def\MetaFunLogo
+% {\dontleavehmode
+% \begingroup
+% \setMFPfont META\syst_logos_meta_hyphen FUN%
+% \endgroup}
+
+% As decided on the ConText Meeting 2013 the logo has been simplified:
+
+\instance\protected\def\MetaFont{MetaFont}
+\instance\protected\def\MetaPost{MetaPost}
+\instance\protected\def\MetaFun {MetaFun}
+
+%D \macros
+%D {TEX, METAFONT, METAPOST, METAFUN,
+%D PICTEX, TABLE,
+%D CONTEXT, PPCHTEX,
+%D AMSTEX, LATEX, LAMSTEX}
+%D
+%D We define the funny written ones as well as the less error prone upper case names
+%D (in \CONTEXT\ we tend to write all user defined commands, like abbreviations, in
+%D uppercase.)
+
+\instance\protected\def\METAFONT{\MetaFont}
+\instance\protected\def\METAPOST{\MetaPost}
+\instance\protected\def\PPCHTEX {\PPCHTeX}
+\instance\protected\def\CONTEXT {\ConTeXt}
+\instance\protected\def\METAFUN {\MetaFun}
+
+\instance\protected\def\TEX {\TeX}
+\instance\protected\def\LATEX {\LaTeX}
+\instance\protected\def\PICTEX {\PiCTeX}
+\instance\protected\def\TABLE {\TaBlE}
+\instance\protected\def\AMSTEX {\AmSTeX}
+\instance\protected\def\LAMSTEX {\LamSTeX}
+\instance\protected\def\INRSTEX {inrs\TeX}
+
+\instance\protected\def\PRAGMA {Pragma ADE}
+
+%D And this is how they show up: \TeX, \MetaFont, \MetaPost, \PiCTeX, \TaBlE, \ConTeXt,
+%D \PPCHTeX, \AmSTeX, \LaTeX, \LamSTeX.
+
+%D Some placeholders:
+
+%frozen\instance\protected\def\eTeX {\mathematics{\varepsilon}-\TeX}
+\frozen\instance\protected\def\eTeX {{\tf \nocap $\varepsilon$}-\TeX} % compact mode hack for epsilon
+\frozen\instance\protected\def\pdfTeX {pdf\wordboundary\TeX}
+\frozen\instance\protected\def\pdfeTeX {pdfe-\wordboundary\TeX}
+\frozen\instance\protected\def\luaTeX {lua\wordboundary\TeX}
+\frozen\instance\protected\def\Lua {Lua}
+\frozen\instance\protected\def\luajitTeX {lua\wordboundary jit\wordboundary\TeX}
+\frozen\instance\protected\def\luametaTeX{lua\wordboundary meta\wordboundary\TeX}
+\frozen\instance\protected\def\XeTeX {X\lower.5\exheight\hbox{\kern-.15\emwidth\mirror{E}}\kern-.1667\emwidth\TeX}
+
+% Adapted from a patch by Mojca:
+
+\def\syst_logos_xetex_e
+ {\setbox\scratchbox\hbox{E}%
+ \raise\dimexpr\ht\scratchbox+\dp\scratchbox\relax\hbox{\rotate[\c!rotation=180]{\box\scratchbox}}}
+
+\frozen\instance\protected\def\XeTeX
+ {X\lower.5\exheight
+ \hbox
+ {\kern-.15\emwidth
+ \iffontchar\font"018E\relax
+ \char"018E%
+ \else
+ \ifx\fontalternative\s!bf \mirror{E}\orelse
+ \ifx\fontalternative\s!it\syst_logos_xetex_e\orelse
+ \ifx\fontalternative\s!sl\syst_logos_xetex_e\orelse
+ \ifx\fontalternative\s!bi\syst_logos_xetex_e\orelse
+ \ifx\fontalternative\s!bs\syst_logos_xetex_e\else
+ \mirror{E}\fi
+ \fi}%
+ \kern-.1667\emwidth\TeX}
+
+\instance\protected\def\ETEX {\eTeX}
+\instance\protected\def\PDFTEX {\pdfTeX}
+\instance\protected\def\PDFETEX {\pdfeTeX}
+\instance\protected\def\LUATEX {\luaTeX}
+\instance\protected\def\LUAJITTEX {\luajitTeX}
+\instance\protected\def\LuaTeX {\luaTeX}
+\instance\protected\def\LuaMetaTeX{\luametaTeX}
+\instance\protected\def\LUAMETATEX{\luametaTeX}
+\instance\protected\def\XETEX {\XeTeX}
+
+\frozen\instance\protected\def\LuaTeX
+ {\dontleavehmode
+ \begingroup
+ Lua%
+ % hope for kerning, try aT
+ \setbox0\hbox{aT}%
+ \setbox2\hbox{a\kern\zeropoint T}%
+ \ifdim\wd0=\wd2 % kerns can go two ways
+ % no aT kerning, try oT as a is not symmetrical
+ \setbox0\hbox{oT}%
+ \setbox2\hbox{o\kern\zeropoint T}%
+ \ifdim\wd0=\wd2 % kerns can go two ways
+ % no aT and oT kerning, try To
+ \setbox0\hbox{To}%
+ \setbox2\hbox{T\kern\zeropoint o}%
+ % maybe we need to compensate for the angle (sl/it/bs/bi)
+ \fi
+ \ifdim\wd0=\wd2\else
+ \kern\dimexpr\wd0-\wd2\relax
+ \fi
+ \fi
+ \TeX
+ \endgroup}
+
+\frozen\instance\protected\def\LuajitTeX
+ {\dontleavehmode
+ \begingroup
+ % at this moment there is no real need for kerning tT
+ Luajit\kern\zeropoint\TeX
+ \endgroup}
+
+\frozen\instance\let\luaTeX \LuaTeX
+\frozen\instance\let\luajitTeX\LuajitTeX
+
+\instance\protected\def\LUATEX {\LuaTeX}
+\instance\protected\def\LUAJITTEX{\LuajitTeX}
+
+\instance\protected\def\MKII{MkII} % MarkTwo
+\instance\protected\def\MKIV{MkIV} % MarkFour : MKIV
+\instance\protected\def\MKVI{MkVI} % MarkSix : MKIV
+\instance\protected\def\MKXL{MkXL} % MarkFourty : LMTX
+\instance\protected\def\MKLX{MkLX} % MarkSixty : LMTX
+\instance\protected\def\MKIX{MkIX} % : MKIV / LMTX
+\instance\protected\def\MKXI{MkXI} % : MKIV / LMTX
+
+\instance\protected\def\MPII{MpII} % MarkTwo
+\instance\protected\def\MPIV{MpIV} % MarkFour : MetaFun
+\instance\protected\def\MPVI{MpVI} % MarkSix : MetaFun
+\instance\protected\def\MPXL{MpXL} % MarkFourty : LuaMetaFun
+\instance\protected\def\MPLX{MpLX} % MarkSixty : LuaMetaFun
+
+\instance\protected\def\LMTX{LMTX} % for LuaMetaTeX
+
+\appendtoks
+ \enforced\instance\def\ConTeXt {ConTeXt}%
+ \enforced\instance\def\MetaPost {MetaPost}%
+ \enforced\instance\def\MetaFont {MetaFont}%
+ \enforced\instance\def\MetaFun {MetaFun}%
+ \enforced\instance\def\TeX {TeX}%
+ \enforced\instance\let\TeXsuffix \TeX
+ \enforced\instance\let\TeXprefix \TeX
+ \enforced\instance\def\LuaTeX {LuaTeX}%
+ \enforced\instance\def\LuajitTeX {LuajitTeX}%
+ \enforced\instance\def\LuaMetaTeX{LuaMetaTeX}%
+ %\let\wordboundary\empty % primitive so we need to promote it to frozen (orjust preroll)
+\to \everysimplifycommands
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-new.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-new.mkxl
new file mode 100644
index 00000000000..b60dd2dddd4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-new.mkxl
@@ -0,0 +1,54 @@
+%D \module
+%D [ file=cont-new,
+%D version=1995.10.10,
+%D title=\CONTEXT\ Miscellaneous Macros,
+%D subtitle=New Macros,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% \normalend % uncomment this to get the real base runtime
+
+\newcontextversion{2021.03.05 19:11}
+
+%D This file is loaded at runtime, thereby providing an excellent place for hacks,
+%D patches, extensions and new features. There can be local overloads in cont-loc
+%D (which has always been the case) and experimental code in cont-exp (which is also
+%D so old that I need to remind myself to check it occasionally, so here is the
+%D reminder).
+
+% \enabletrackers[pages.timing]
+% \enabletrackers[system.usage=summary]
+
+\unprotect
+
+\writestatus\m!system{beware: some patches loaded from cont-new.mkiv}
+
+%D I need to verify if this indeed will make it into \type {math-ini.mkiv}, that is:
+%D does Alan use it?
+
+\ifdefined\t \else \permanent\protected\def\t{\mathortext\text\mathtext} \fi
+\ifdefined\w \else \permanent\protected\def\w{\mathortext\word\mathword} \fi
+
+\appendtoks
+ \enforced\aliased\let\t\mathtext
+ \enforced\aliased\let\w\mathword
+\to \everymathematics
+
+% so we need to be able to load modules in ini mode
+
+\pushoverloadmode
+ % charts needs these
+ \overloaded\unletfrozen\name % for now
+ \mutable\let\name\name
+ \mutable\let\text\text
+ \overloaded\frozen\let\type\type
+\popoverloadmode
+
+%D We're done.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-nl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-nl.mkxl
new file mode 100644
index 00000000000..5e74114e5b6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-nl.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-nl,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ Dutch Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {dutch}
+\immutable\def\defaultlanguagetag{nl}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-pe.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-pe.mkxl
new file mode 100644
index 00000000000..2242c3e0dda
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-pe.mkxl
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=cont-en,
+%D version=1997.08.19,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ English Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {persian}
+\immutable\def\messageinterface {english}
+\immutable\def\defaultlanguagetag{pe}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-ro.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-ro.mkxl
new file mode 100644
index 00000000000..ced28ba3d95
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-ro.mkxl
@@ -0,0 +1,21 @@
+%D \module
+%D [ file=cont-ro,
+%D version=2000.01.09,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ Romanian Format Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\catcode`\{=1 \catcode`\}=2
+
+\immutable\def\defaultinterface {romanian}
+\immutable\def\defaultlanguagetag{ro}
+
+\input context.mkxl
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-run.lmt b/Master/texmf-dist/tex/context/base/mkxl/cont-run.lmt
new file mode 100644
index 00000000000..76fd6ba249b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-run.lmt
@@ -0,0 +1,311 @@
+if not modules then modules = { } end modules ['cont-run'] = {
+ version = 1.001,
+ comment = "companion to cont-yes.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- When a style is loaded there is a good change that we never enter
+-- this code.
+
+
+local type, tostring = type, tostring
+
+local report_sandbox = logs.reporter("sandbox","call")
+local report_system = logs.reporter("system")
+local fastserialize = table.fastserialize
+local quoted = string.quoted
+local possiblepath = sandbox.possiblepath
+
+local context = context
+local implement = interfaces.implement
+
+local qualified = { }
+local writeable = { }
+local readable = { }
+local blocked = { }
+local trace_files = false
+local trace_calls = false
+local nofcalls = 0
+local nofrejected = 0
+local logfilename = "sandbox.log"
+
+local function registerstats()
+ statistics.register("sandboxing", function()
+ if trace_files then
+ return string.format("%i calls, %i rejected, logdata in '%s'",nofcalls,nofrejected,logfilename)
+ else
+ return string.format("%i calls, %i rejected",nofcalls,nofrejected)
+ end
+ end)
+ registerstats = false
+end
+
+local function logsandbox(details)
+ local comment = details.comment
+ local result = details.result
+ local arguments = details.arguments
+ for i=1,#arguments do
+ local argument = arguments[i]
+ local t = type(argument)
+ if t == "string" then
+ arguments[i] = quoted(argument)
+ if trace_files and possiblepath(argument) then
+ local q = qualified[argument]
+ if q then
+ local c = q[comment]
+ if c then
+ local r = c[result]
+ if r then
+ c[result] = r + 1
+ else
+ c[result] = r
+ end
+ else
+ q[comment] = {
+ [result] = 1
+ }
+ end
+ else
+ qualified[argument] = {
+ [comment] = {
+ [result] = 1
+ }
+ }
+ end
+ end
+ elseif t == "table" then
+ arguments[i] = fastserialize(argument)
+ else
+ arguments[i] = tostring(argument)
+ end
+ end
+ if trace_calls then
+ report_sandbox("%s(%,t) => %l",details.comment,arguments,result)
+ end
+ nofcalls = nofcalls + 1
+ if not result then
+ nofrejected = nofrejected + 1
+ end
+end
+
+local ioopen = sandbox.original(io.open) -- dummy call
+
+local function logsandboxfiles(name,what,asked,okay)
+ -- we're only interested in permitted access
+ if not okay then
+ blocked [asked] = blocked [asked] or 0 + 1
+ elseif what == "*" or what == "w" then
+ writeable[asked] = writeable[asked] or 0 + 1
+ else
+ readable [asked] = readable [asked] or 0 + 1
+ end
+end
+
+function sandbox.logcalls()
+ if not trace_calls then
+ trace_calls = true
+ sandbox.setlogger(logsandbox)
+ if registerstats then
+ registerstats()
+ end
+ end
+end
+
+function sandbox.logfiles()
+ if not trace_files then
+ trace_files = true
+ sandbox.setlogger(logsandbox)
+ sandbox.setfilenamelogger(logsandboxfiles)
+ luatex.registerstopactions(function()
+ table.save(logfilename,{
+ calls = {
+ nofcalls = nofcalls,
+ nofrejected = nofrejected,
+ filenames = qualified,
+ },
+ checkednames = {
+ readable = readable,
+ writeable = writeable,
+ blocked = blocked,
+ },
+ })
+ end)
+ if registerstats then
+ registerstats()
+ end
+ end
+end
+
+trackers.register("sandbox.tracecalls",sandbox.logcalls)
+trackers.register("sandbox.tracefiles",sandbox.logfiles)
+
+local sandboxing = environment.arguments.sandbox
+local debugging = environment.arguments.debug
+
+if sandboxing then
+
+ report_system("enabling sandbox")
+
+ sandbox.enable()
+
+ if type(sandboxing) == "string" then
+ sandboxing = utilities.parsers.settings_to_hash(sandboxing)
+ if sandboxing.calls then
+ sandbox.logcalls()
+ end
+ if sandboxing.files then
+ sandbox.logfiles()
+ end
+ end
+
+ -- Nicer would be if we could just disable write 18 and keep os.execute
+ -- which in fact we can do by defining write18 as macro instead of
+ -- primitive ... todo ... well, it has been done now.
+
+ -- We block some potential escapes from protection.
+
+ context [[\let\primitive\relax\let\normalprimitive\relax]]
+
+ debug = {
+ traceback = debug.traceback,
+ }
+
+ package.loaded.debug = debug
+
+elseif debugging then
+
+ -- we keep debug
+
+else
+
+ debug = {
+ traceback = debug.traceback,
+ getinfo = debug.getinfo,
+ sethook = debug.sethook,
+ }
+
+ package.loaded.debug = debug
+
+end
+
+local function processjob()
+
+ tokens.setters.macro("processjob","","permanent")
+
+ environment.initializefilenames() -- todo: check if we really need to pre-prep the filename
+
+ local arguments = environment.arguments
+ local suffix = environment.suffix
+ local filename = environment.filename -- hm, not inputfilename !
+
+ environment.lmtxmode = CONTEXTLMTXMODE -- should be true
+
+ if arguments.nosynctex then
+ luatex.synctex.setup {
+ state = interfaces.variables.never,
+ }
+ elseif arguments.synctex then
+ luatex.synctex.setup {
+ state = interfaces.variables.start,
+ method = interfaces.variables.max,
+ }
+ end
+
+ local overloadmode = arguments.overloadmode
+
+ if overloadmode == "warning" then
+ overloadmode = 3 -- 5
+ elseif overloadmode == "error" then
+ overloadmode = 4 -- 6
+ else
+ overloadmode = tonumber(overloadmode)
+ end
+
+ if overloadmode then
+ tex.set("overloadmode",overloadmode)
+ end
+
+ if not filename or filename == "" then
+ -- skip
+ elseif suffix == "xml" or arguments.forcexml then
+
+ -- Maybe we should move the preamble parsing here as it
+ -- can be part of (any) loaded (sub) file. The \starttext
+ -- wrapping might go away.
+
+ report_system("processing as xml: %s",filename)
+
+ context.starttext()
+ context.xmlprocess("main",filename,"")
+ context.stoptext()
+
+ elseif suffix == "cld" or arguments.forcecld then
+
+ report_system("processing as cld: %s",filename)
+
+ context.runfile(filename)
+
+ elseif suffix == "lua" or arguments.forcelua then
+
+ -- The wrapping might go away. Why is is it there in the
+ -- first place.
+
+ report_system("processing as lua: %s",filename)
+
+ context.starttext()
+ context.ctxlua(string.format('dofile("%s")',filename))
+ context.stoptext()
+
+ elseif suffix == "mp" or arguments.forcemp then
+
+ report_system("processing as metapost: %s",filename)
+
+ context.starttext()
+ context.processMPfigurefile(filename)
+ context.stoptext()
+
+ -- elseif suffix == "prep" then
+ --
+ -- -- Why do we wrap here. Because it can be xml? Let's get rid
+ -- -- of prepping in general.
+ --
+ -- context.starttext()
+ -- context.input(filename)
+ -- context.stoptext()
+
+ elseif suffix == "mps" or arguments.forcemps then
+
+ report_system("processing metapost output: %s",filename)
+
+ context.starttext()
+ context.startTEXpage()
+ context.externalfigure { filename }
+ context.stopTEXpage()
+ context.stoptext()
+
+ else
+
+ -- \writestatus{system}{processing as tex}
+ -- We have a regular tex file so no \starttext yet as we can
+ -- load fonts.
+ -- context.enabletrackers { "resolvers.*" }
+ context.input(filename)
+ -- context.disabletrackers { "resolvers.*" }
+
+ end
+
+ context.finishjob()
+
+end
+
+implement {
+ name = "processjob",
+ -- protected = true
+ public = true,
+ onlyonce = true,
+ actions = processjob,
+}
+
+texconfig.firstline = "\\processjob " -- experiment, yet undocumented
diff --git a/Master/texmf-dist/tex/context/base/mkxl/cont-run.mkxl b/Master/texmf-dist/tex/context/base/mkxl/cont-run.mkxl
new file mode 100644
index 00000000000..8944a0359e7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/cont-run.mkxl
@@ -0,0 +1,59 @@
+%D \module
+%D [ file=cont-run,
+%D version=2014.12.26,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Runner,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Runner}
+
+\registerctxluafile{cont-run}{autosuffix}
+
+% \enabletrackers[system.synctex.visualize]
+% \enabletrackers[system.synctex.xml]
+% \enabledirectives[system.synctex.details]
+% \setupsynctex[state=start]
+
+\unprotect
+
+\permanent\let\synctexsetfilename \clf_synctexsetfilename % can be public implementors
+\permanent\let\synctexresetfilename\clf_synctexresetfilename % idem
+\permanent\let\synctexblockfilename\clf_synctexblockfilename % idem
+
+\aliased\let\synctexpause \donothing
+\aliased\let\synctexresume \donothing
+\aliased\let\synctexpushline\donothing
+\aliased\let\synctexpopline \donothing
+
+\appendtoks\synctexpause \to\everybeforeoutput
+\appendtoks\synctexresume\to\everyafteroutput
+
+\permanent\protected\def\setupsynctex[#1]%
+ {\begingroup
+ \getdummyparameters[\c!state=\v!stop,\c!method=\v!max,#1]%
+ \edef\p_state{\dummyparameter\c!state}%
+ \clf_setupsynctex
+ state {\p_state}%
+ method {\dummyparameter\c!method}%
+ \relax
+ \ifx\p_state\v!start
+ % when public implementor, use conditional
+ \enforced\glet\synctexpause \clf_synctexpause
+ \enforced\glet\synctexresume \clf_synctexresume
+ \enforced\glet\synctexpushline\clf_synctexpushline
+ \enforced\glet\synctexpopline \clf_synctexpopline
+ \fi
+ \endgroup}
+
+\permanent\protected\def\blocksynctexfile[#1]%
+ {\processcommacommand{#1}\synctexblockfilename}
+
+\aliased\let\continuewhenlmtxmode\relax
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/context.mkxl b/Master/texmf-dist/tex/context/base/mkxl/context.mkxl
new file mode 100644
index 00000000000..88b99e7d4a4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/context.mkxl
@@ -0,0 +1,614 @@
+%D \module
+%D [ file=context,
+%D version=2019.07.24, % 2008.28.10, % 1995.10.10,
+%D title=\CONTEXT,
+%D subtitle=\CONTEXT\ Format Generation,
+%D author=Hans Hagen, % ɦɑns ɦɑˈχən
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% http://build.contextgarden.net/waterfall?tag=c/lua(meta)tex
+
+%D Welcome to context, pronounced as kontekst (rather dutch) and not as conτεχt.
+
+\catcode`\{=1 \catcode`\}=2 \catcode`\#=6
+
+%D From the next string (which is set by the script that assembles the distribution)
+%D later on we will calculate a number that can be used by use modules to identify
+%D the feature level. Starting with version 2004.8.30 the low level interface is
+%D english. Watch out and adapt your styles an modules.
+
+% \everypar{\writestatus{!!!!}{some spurious input in line \the\inputlineno}\wait}
+
+%D The order of loading will change when all modules have been cleaned up and the
+%D dependencies are more consistent. Beware, the version number has to match \type
+%D {YYYY.MM.DD HH:MM} format.
+
+\immutable\edef\contextformat {\jobname}
+\immutable\edef\contextversion{2021.03.05 19:11}
+
+%overloadmode 1 % check frozen / warning
+%overloadmode 2 % check frozen / error
+%overloadmode 3 % check frozen, primitive, permanent / warning
+\overloadmode 4 % check frozen, primitive, permanent / error
+
+%D Kind of special:
+
+\immutable\chardef\contextlmtxmode\directlua{tex.print(CONTEXTLMTXMODE or 0)}\relax
+
+%D For those who want to use this:
+
+\aliased\let\fmtname \contextformat
+\aliased\let\fmtversion\contextversion
+
+%D Loading:
+
+\immutable\edef\mksuffix {mkiv}
+\immutable\edef\contextmark{LMTX}
+
+\ifdefined\normalinput \else \aliased\let\normalinput\input \fi
+
+\permanent\protected\def\loadmarkfile#1{\normalinput{#1.\mksuffix}}
+\permanent\protected\def\loadmkivfile#1{\normalinput{#1.mkiv}}
+\permanent\protected\def\loadmkvifile#1{\normalinput{#1.mkvi}}
+\permanent\protected\def\loadmkxlfile#1{\normalinput{#1.mkxl}}
+\permanent\protected\def\loadmklxfile#1{\normalinput{#1.mklx}}
+
+%D First we load the system modules. These implement a lot of manipulation macros.
+%D We start with setting up some basic \TEX\ machinery.
+
+\loadmkxlfile{syst-ini}
+
+%D Some checking (more primitives are now defined):
+
+\ifdefined\defaultinterface \else \immutable\def\defaultinterface {english} \fi
+%ifdefined\messageinterface \else \immutable\let\messageinterface \defaultinterface \fi
+\ifdefined\defaultlanguagetag \else \immutable\def\defaultlanguagetag{en} \fi
+
+%D There is only this way to pass the version info to \LUA\ (currently). Hm, we could
+%D now put it into the environment.
+
+\newtoks\contextversiontoks \contextversiontoks\expandafter{\contextversion}
+
+% \normaleverypar{\wait} % uncomment for test of funny injections
+
+%D Now the more fundamental code gets defined.
+
+\loadmkxlfile{norm-ctx}
+\loadmkxlfile{syst-pln}
+%loadmkxlfile{syst-mes}
+
+\loadmkxlfile{luat-cod}
+\loadmkxlfile{luat-bas}
+\loadmkxlfile{luat-lib}
+\loadmkxlfile{luat-soc}
+
+\loadmkxlfile{catc-ini}
+\loadmkxlfile{catc-act}
+\loadmkxlfile{catc-def}
+\loadmkxlfile{catc-sym}
+
+\loadmkxlfile{toks-ini}
+\loadmkxlfile{cldf-ini}
+
+% \tracecatcodetables
+
+%D From here on we have \unexpanded being \normalprotected, as we already had
+%D \type {\unexpanded} long before etex came around.
+
+\loadmkxlfile{luat-ini}
+\loadmkxlfile{toks-scn}
+
+\loadmkxlfile{syst-aux}
+\loadmkxlfile{syst-lua}
+\loadmkxlfile{syst-con}
+
+\loadmkxlfile{syst-fnt}
+\loadmkxlfile{syst-rtp}
+
+\loadmkxlfile{luat-usr}
+
+\loadmklxfile{file-ini}
+\loadmklxfile{file-res}
+\loadmkxlfile{file-lib}
+
+\loadmkxlfile{core-lmt}
+
+%D This needs more checking for clashes:
+%D
+%D \starttyping
+%D \doifelsefileexists{l-macro-imp-codes-luametatex.lua}{\registerctxluafile{l-macro-imp-codes-luametatex}{}}{}
+%D \stoptyping
+
+\loadmkxlfile{supp-dir}
+
+\loadmkxlfile{char-utf}
+\loadmkxlfile{char-ini}
+\loadmkxlfile{char-act}
+
+\loadmkxlfile{mult-ini}
+\loadmkxlfile{mult-sys}
+\loadmkxlfile{mult-aux}
+\loadmkxlfile{mult-def}
+%loadmarkfile{mult-chk}
+\loadmklxfile{mult-dim}
+
+\loadmkxlfile{cldf-int} % interface
+
+\loadmkxlfile{trac-ctx} % maybe move up
+
+\loadmkxlfile{toks-aux}
+
+\loadmkxlfile{attr-ini}
+
+\loadmkxlfile{core-ini}
+\loadmkxlfile{core-env}
+
+\loadmkxlfile{layo-ini}
+
+\loadmkxlfile{node-ini}
+
+% \loadmkxlfile{cldf-bas} % basics / depends on nodes
+
+\loadmkxlfile{node-fin}
+\loadmkxlfile{node-mig}
+%loadmkxlfile{node-pag}
+
+\loadmkxlfile{driv-ini}
+
+\loadmkxlfile{back-ini}
+\loadmkxlfile{back-res}
+\loadmkxlfile{back-ext}
+
+\loadmkxlfile{cldf-bas} % basics / depends on nodes
+
+\loadmkxlfile{attr-col}
+\loadmkxlfile{attr-lay}
+\loadmkxlfile{attr-neg}
+\loadmkxlfile{attr-eff}
+\loadmkxlfile{attr-alt}
+\loadmkxlfile{attr-mkr}
+
+\loadmkxlfile{trac-tex}
+\loadmkxlfile{trac-deb} % will move up
+
+\loadmkxlfile{supp-box}
+
+\loadmkxlfile{supp-ran}
+\loadmkxlfile{supp-mat}
+
+\loadmkxlfile{core-uti}
+\loadmklxfile{file-job}
+
+\loadmkxlfile{anch-pos}
+
+\loadmkxlfile{typo-ini}
+\loadmkxlfile{typo-lin}
+\loadmkxlfile{typo-bld} % par builders
+
+\loadmkxlfile{typo-inj}
+
+\loadmklxfile{file-syn}
+\loadmklxfile{file-mod}
+
+\loadmkxlfile{core-sys}
+\loadmkxlfile{core-con}
+
+\loadmkxlfile{cont-fil}
+
+\loadmkxlfile{regi-ini}
+\loadmkxlfile{enco-ini}
+\loadmkxlfile{hand-ini}
+
+\loadmkxlfile{lang-ini}
+\loadmkxlfile{lang-hyp} % also loads dis
+\loadmkxlfile{lang-lab}
+
+\loadmkxlfile{unic-ini}
+
+\loadmkxlfile{core-two}
+\loadmkxlfile{core-dat}
+
+\loadmkxlfile{colo-ini}
+\loadmkxlfile{colo-nod}
+\loadmkxlfile{colo-grp} % optional
+\loadmkxlfile{colo-ext}
+
+\loadmkxlfile{node-bck} % overloads anch-pgr (experimental and undocumented)
+
+\loadmkxlfile{pack-cut}
+
+\loadmkxlfile{lang-mis}
+\loadmkxlfile{lang-url}
+\loadmkxlfile{lang-def}
+
+% \loadmkvifile{file-job} % why so late?
+
+\loadmkxlfile{symb-ini} % brrr depends on fonts
+
+\loadmkxlfile{sort-ini}
+
+\loadmklxfile{pack-mis}
+\loadmkxlfile{pack-ori}
+\loadmkxlfile{pack-rul}
+\loadmkxlfile{pack-mrl}
+\loadmklxfile{pack-bck}
+\loadmkxlfile{pack-fen}
+
+\loadmkxlfile{lxml-ini}
+\loadmkxlfile{lxml-sor}
+
+\loadmklxfile{typo-prc}
+\loadmkxlfile{typo-plc}
+
+\loadmklxfile{strc-ini}
+\loadmkxlfile{strc-tag}
+\loadmkxlfile{strc-doc}
+\loadmkxlfile{strc-num}
+\loadmkxlfile{strc-mar}
+\loadmkxlfile{strc-sbe}
+\loadmklxfile{strc-lst}
+\loadmkxlfile{strc-sec}
+\loadmkxlfile{strc-pag} % hm, depends on core-num
+\loadmkxlfile{strc-ren}
+%loadmarkfile{strc-xml}
+\loadmkxlfile{strc-def} % might happen later
+\loadmklxfile{strc-ref}
+%loadmarkfile{strc-reg}
+\loadmklxfile{strc-lev} % experiment
+
+\loadmkxlfile{spac-hor}
+\loadmkxlfile{spac-ali}
+\loadmkxlfile{spac-flr}
+\loadmkxlfile{spac-ver} % beware, hooks into strc-sec
+\loadmkxlfile{spac-lin}
+\loadmkxlfile{spac-pag}
+\loadmkxlfile{spac-par}
+\loadmkxlfile{spac-def}
+\loadmklxfile{spac-prf}
+\loadmkxlfile{spac-grd}
+
+\loadmklxfile{scrn-ini}
+\loadmklxfile{scrn-ref}
+
+\loadmkxlfile{pack-obj}
+
+\loadmklxfile{strc-itm}
+
+\loadmklxfile{strc-con}
+\loadmklxfile{strc-des}
+\loadmklxfile{strc-enu}
+
+\loadmkxlfile{strc-ind}
+\loadmkxlfile{strc-lab}
+\loadmkxlfile{strc-syn}
+
+% \loadmarkfile{core-sys}
+
+\loadmkxlfile{page-var}
+\loadmklxfile{page-otr}
+\loadmkxlfile{page-ini}
+\loadmkxlfile{page-ins}
+\loadmkxlfile{page-fac}
+\loadmkxlfile{page-brk}
+\loadmkxlfile{page-col}
+\loadmkxlfile{page-inf}
+\loadmkxlfile{page-flt}
+\loadmkxlfile{page-bck}
+\loadmkxlfile{page-not}
+\loadmkxlfile{page-one}
+\loadmkxlfile{page-lay}
+\loadmklxfile{page-box}
+\loadmklxfile{page-txt}
+\loadmkxlfile{page-sid}
+
+\loadmklxfile{strc-flt}
+
+\loadmkxlfile{page-pst}
+\loadmklxfile{page-mbk}
+%loadmarkfile{page-mul} % \usecolumns[old-multicolumns]
+\loadmkxlfile{page-mix}
+\loadmkxlfile{page-smp}
+\loadmkxlfile{page-cst}
+\loadmkxlfile{page-pcl} % new
+\loadmkxlfile{pack-lyr}
+\loadmkxlfile{pack-pos}
+\loadmklxfile{page-mak}
+\loadmkxlfile{page-mcl}
+
+\loadmkxlfile{strc-reg} % uses mixed columns
+
+\loadmklxfile{page-lin}
+\loadmkxlfile{page-par}
+\loadmkxlfile{typo-pag}
+\loadmkxlfile{typo-mar}
+\loadmkxlfile{typo-itm}
+
+\loadmkxlfile{buff-ini}
+\loadmkxlfile{buff-ver}
+\loadmklxfile{buff-par}
+
+\loadmarkfile{buff-imp-tex} % optional as also runtime if not loaded
+\loadmarkfile{buff-imp-mp} % optional as also runtime if not loaded
+\loadmarkfile{buff-imp-lua} % optional as also runtime if not loaded
+\loadmarkfile{buff-imp-xml} % optional as also runtime if not loaded
+
+\loadmarkfile{buff-imp-parsed-xml} % optional
+%loadmarkfile{buff-imp-parsed-lua} % optional
+
+\loadmkxlfile{strc-blk}
+
+\loadmkxlfile{page-imp}
+\loadmklxfile{page-sel} % optional
+\loadmklxfile{page-inj} % optional
+
+\loadmklxfile{scrn-pag}
+\loadmklxfile{scrn-wid}
+\loadmklxfile{scrn-but}
+\loadmklxfile{scrn-bar}
+
+\loadmkxlfile{page-com} % optional (after scrn-pag)
+
+\loadmkxlfile{strc-bkm} % bookmarks
+
+\loadmkxlfile{java-ini}
+
+\loadmklxfile{scrn-fld}
+\loadmklxfile{scrn-hlp}
+
+\loadmkxlfile{char-enc}
+
+\loadmklxfile{font-lib} % way too late but after language
+\loadmklxfile{font-fil}
+\loadmklxfile{font-var}
+\loadmklxfile{font-fea}
+\loadmklxfile{font-mat}
+\loadmklxfile{font-ini}
+\loadmklxfile{font-glf}
+\loadmklxfile{font-sym}
+\loadmklxfile{font-sty}
+\loadmklxfile{font-set}
+\loadmklxfile{font-emp}
+\loadmkxlfile{font-pre}
+\loadmkxlfile{font-unk}
+\loadmkxlfile{font-tra}
+\loadmkxlfile{font-chk}
+\loadmkxlfile{font-uni}
+\loadmklxfile{font-col}
+\loadmklxfile{font-gds}
+\loadmklxfile{font-aux}
+\loadmklxfile{font-sel}
+\loadmkxlfile{font-tex}
+
+\loadmkxlfile{typo-tal}
+\loadmkxlfile{typo-par} % par builders (uses fonts)
+
+\loadmkxlfile{tabl-com}
+%loadmarkfile{tabl-pln}
+
+\loadmkxlfile{tabl-tab} % thrd-tab stripped and merged
+
+\loadmkxlfile{tabl-tbl}
+\loadmkxlfile{tabl-ntb}
+\loadmkxlfile{tabl-nte}
+\loadmkxlfile{tabl-ltb}
+\loadmkxlfile{tabl-frm}
+\loadmkxlfile{tabl-tsp}
+\loadmklxfile{tabl-xtb}
+\loadmkxlfile{tabl-mis}
+
+\loadmkxlfile{typo-lan}
+
+\loadmkxlfile{lxml-css}
+
+\loadmkxlfile{spac-chr} % depends on fonts
+
+\loadmkxlfile{blob-ini}
+
+\loadmkxlfile{trac-vis}
+\loadmkxlfile{trac-jus}
+
+\loadmkxlfile{typo-cln}
+\loadmkxlfile{typo-wrp}
+\loadmkxlfile{typo-spa}
+\loadmkxlfile{typo-krn}
+\loadmklxfile{typo-itc}
+\loadmkxlfile{typo-dir}
+\loadmkxlfile{typo-brk}
+\loadmkxlfile{typo-cap}
+\loadmkxlfile{typo-dig}
+\loadmkxlfile{typo-rep}
+\loadmklxfile{typo-txt}
+\loadmkxlfile{typo-drp}
+\loadmkxlfile{typo-fln}
+\loadmkxlfile{typo-sus}
+\loadmkxlfile{typo-lig}
+\loadmkxlfile{typo-chr}
+\loadmkxlfile{typo-rub}
+\loadmkxlfile{typo-fkr}
+\loadmkxlfile{typo-pnc}
+
+\loadmklxfile{type-ini}
+\loadmkxlfile{type-set}
+
+\loadmkxlfile{scrp-ini}
+
+\loadmkxlfile{symb-emj}
+
+\loadmkxlfile{lang-tra} % can be optional (discussion with mm sideeffect)
+\loadmkxlfile{lang-wrd} % can be optional (discussion with mm sideeffect)
+\loadmkxlfile{lang-rep} % can be optional (bt 2013 side effect)
+
+\loadmkxlfile{mlib-ctx} % messy order
+
+\loadmkxlfile{meta-ini}
+\loadmkxlfile{meta-tex}
+\loadmkxlfile{meta-fun}
+\loadmkxlfile{meta-pag}
+\loadmkxlfile{meta-grd}
+\loadmkxlfile{meta-fnt}
+\loadmkxlfile{meta-nod}
+\loadmkxlfile{meta-lua}
+\loadmkxlfile{meta-mac}
+
+\loadmkxlfile{page-mrk} % depends on mp
+
+\loadmkxlfile{page-flw}
+\loadmkxlfile{page-spr}
+\loadmkxlfile{page-plg}
+\loadmkxlfile{page-str}
+
+\loadmkxlfile{anch-pgr} % can be moved up (nicer for dependencies)
+\loadmklxfile{anch-bck}
+\loadmkxlfile{anch-tab} % overloads tabl-tbl
+\loadmkxlfile{anch-bar}
+%loadmkxlfile{anch-snc} % when needed this one will be redone
+
+\loadmkxlfile{math-ini} % way after font-pre !
+\loadmkxlfile{math-pln}
+\loadmkxlfile{math-for}
+\loadmkxlfile{math-def} % also saves some meanings
+\loadmkxlfile{math-ali}
+\loadmklxfile{math-stc}
+\loadmkxlfile{math-frc}
+\loadmkxlfile{math-mis}
+\loadmkxlfile{math-scr}
+\loadmkxlfile{math-int}
+\loadmkxlfile{math-del}
+\loadmkxlfile{math-fen}
+\loadmklxfile{math-acc}
+\loadmklxfile{math-rad}
+\loadmkxlfile{math-inl}
+\loadmkxlfile{math-dis}
+%loadmkxlfile{math-lan}
+\loadmkxlfile{math-toy}
+
+\loadmkxlfile{strc-mat}
+
+\loadmkxlfile{chem-ini}
+\loadmkxlfile{chem-str}
+
+\loadmkxlfile{typo-scr}
+\loadmkxlfile{phys-dim}
+
+\loadmkxlfile{node-rul} % beware, defined \underbar so after math
+\loadmklxfile{font-sol} % font solutions
+
+\loadmklxfile{strc-not}
+\loadmklxfile{strc-lnt}
+\loadmkxlfile{strc-tnt}
+\loadmkxlfile{strc-usr}
+
+\loadmkxlfile{pack-com}
+\loadmkxlfile{typo-del}
+
+\loadmkxlfile{grph-trf}
+\loadmkxlfile{grph-inc}
+\loadmkxlfile{grph-fig}
+\loadmkxlfile{grph-raw}
+\loadmkxlfile{grph-rul}
+\loadmkxlfile{grph-pat}
+
+\loadmkxlfile{pack-box}
+\loadmkxlfile{pack-bar}
+\loadmkxlfile{page-app}
+\loadmkxlfile{meta-fig}
+
+\loadmkxlfile{page-ffl}
+
+\loadmkxlfile{lang-spa} % will become obsolete
+
+\loadmkxlfile{typo-ovl} % fuzzy project (tracing) code
+
+% old bibtex support: (will be m-oldbibtex.mkiv)
+
+% \loadmarkfile{bibl-bib}
+% \loadmarkfile{bibl-tra}
+
+% new bibtex support:
+
+\loadmkxlfile{publ-ini}
+%loadmkxlfile{publ-tra}
+\loadmkxlfile{publ-xml}
+\loadmkxlfile{publ-old}
+
+%loadmarkfile{x-xtag} % no longer preloaded
+
+\loadmkxlfile{meta-xml}
+
+\loadmkxlfile{cont-log}
+
+\loadmkxlfile{cldf-ver} % verbatim, this can come late
+\loadmkxlfile{cldf-com} % commands, this can come late
+
+\loadmkxlfile{core-ctx} % this order might change but we need to check depedencies / move to another namespace
+
+\loadmkxlfile{core-def}
+
+%usemodule[x][res-04] % xml resource libraries
+%usemodule[x][res-08] % rlx runtime conversion
+%usemodule[x][res-12] % rli external indentification
+
+% now we hook in backend code (needs checking)
+
+\loadmkxlfile{driv-shp}
+
+\loadmkxlfile{back-exp}
+\loadmkxlfile{back-pdf}
+\loadmkxlfile{back-mps}
+\loadmkxlfile{back-lua}
+
+\loadmkxlfile{mlib-pdf}
+\loadmkxlfile{mlib-pps}
+\loadmkxlfile{meta-pdf}
+\loadmkxlfile{meta-blb}
+\loadmkxlfile{grph-epd}
+
+\loadmkxlfile{typo-shp}
+
+\loadmkxlfile{math-inc} % an experiment
+\loadmkxlfile{publ-inc} % an experiment
+
+\loadmkxlfile{task-ini}
+
+\loadmkxlfile{syst-cmp} % compatibility stuff moved here
+
+\loadmkxlfile{libs-ini}
+
+\loadmkxlfile{cont-run} % the main runner (used in cont-yes.mkiv)
+
+\setupcurrentlanguage[\defaultlanguagetag]
+
+\prependtoks
+ \ctxlua{statistics.starttiming(statistics)}%
+\to \everyjob
+
+\appendtoks
+ \ctxlua{statistics.stoptiming(statistics)}%
+\to \everyjob
+
+% \appendtoks
+% \ctxlua{job.prepare()}%
+% \to \everyjob
+
+% \appendtoks
+% \enabletrackers[*]%
+% \to \everyjob
+
+\appendtoks
+ \ctxlua{statistics.savefmtstatus("\jobname","\contextversion","context.mkxl","\contextbanner")}% can become automatic
+\to \everydump
+
+% some day:
+%
+% \prependtoks
+% \overloadmode 255
+% \to \everyjob
+
+\overloadmode 0 % for now
+
+\errorstopmode \dump \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-con.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-con.mkxl
new file mode 100644
index 00000000000..df505cfee5f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-con.mkxl
@@ -0,0 +1,1002 @@
+%D \module
+%D [ file=core-con,
+%D version=1997.26.08,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Conversion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Conversion}
+
+\registerctxluafile{core-con}{}
+
+% todo: iso date ranges (from/to)
+
+\unprotect
+
+\ifdefined\currentlanguage \else \let\currentlanguage\empty \fi
+\ifdefined\labeltext \else \let\labeltext \firstofoneargument \fi
+
+%D This module deals with all kind of conversions from numbers and dates. I
+%D considered splitting this module in a support one and a core one, but to keep
+%D things simple as well as preserve the overview, I decided against splitting.
+
+\aliased\let\spr\firstofoneargument % separator
+\aliased\let\stp\firstofoneargument % stopper
+
+% needed for arab (but can disappear as no ons used it, experiment for idris)
+
+\permanent\protected\def\isolateseparators % even works with list separator overloading
+ {\enforced\permanent\def\spr##1{{##1}}% % we can probably best mess around with zwj
+ \enforced\permanent\def\stp##1{{##1}}} % and friends
+
+%D \macros
+%D {numbers}
+%D
+%D First we deal with the dummy conversion of numbers using the \TEX\ primitive
+%D \type{\number}. The uppercase alternative is only there for compatibility with
+%D the other conversion macros. We could do without \type{#1} but this way we get
+%D rid of unwanted braces. For the savety we also define a non||sence uppercase
+%D alternative.
+%D
+%D \showsetup{numbers}
+%D
+%D \starttyping
+%D \def\numbers#1{\number#1}
+%D \def\Numbers#1{\number#1}
+%D \stoptyping
+%D
+%D Due to read ahead, as in \type{[\pagenumber\space]} the space will disappear,
+%D unless we use:
+
+\permanent\def\numbers#1{\purenumber{#1}}
+\permanent\def\Numbers#1{\purenumber{#1}}
+
+%D \macros
+%D {romannumerals,Romannumerals}
+%D
+%D \TEX\ the program uses a rather tricky conversion from numbers to their roman
+%D counterparts. This conversion could of course be programmed in \TEX\ itself, but
+%D I guess Knuth found the programming trick worth presenting.
+%D
+%D \showsetup{romannumerals}
+%D \showsetup{Romannumerals}
+
+\permanent\def\romannumerals#1{\clf_romannumerals\numexpr#1\relax}
+\permanent\def\Romannumerals#1{\clf_Romannumerals\numexpr#1\relax}
+
+%D Arabic etc:
+
+\permanent\def\abjadnumerals #1{\clf_abjadnumerals \numexpr#1\relax}
+\permanent\def\abjadnodotnumerals#1{\clf_abjadnodotnumerals\numexpr#1\relax}
+\permanent\def\abjadnaivenumerals#1{\clf_alphabetic \numexpr#1\relax{arabic}} % okay?
+
+\permanent\def\hebrewnumerals #1{\clf_hebrewnumerals\numexpr#1\relax}
+\aliased \let\yiddishnumerals \hebrewnumerals
+
+\permanent\def\languagecharacters#1{\clf_alphabetic\numexpr#1\relax{\currentlanguage}} % new
+\permanent\def\languageCharacters#1{\clf_Alphabetic\numexpr#1\relax{\currentlanguage}} % new
+
+\permanent\def\alphabeticnumerals#1{\clf_alphabetic\numexpr#1\relax{}}
+\permanent\def\Alphabeticnumerals#1{\clf_Alphabetic\numexpr#1\relax{}}
+
+\permanent\def\thainumerals #1{\clf_alphabetic\numexpr#1\relax{thai}}
+\permanent\def\devanagarinumerals#1{\clf_alphabetic\numexpr#1\relax{devanagari}}
+\permanent\def\gurmurkhinumerals #1{\clf_alphabetic\numexpr#1\relax{gurmurkhi}}
+\permanent\def\gujaratinumerals #1{\clf_alphabetic\numexpr#1\relax{gujarati}}
+\permanent\def\tibetannumerals #1{\clf_alphabetic\numexpr#1\relax{tibetan}}
+\permanent\def\greeknumerals #1{\clf_alphabetic\numexpr#1\relax{greek}}
+\permanent\def\Greeknumerals #1{\clf_Alphabetic\numexpr#1\relax{greek}}
+\permanent\def\arabicnumerals #1{\clf_alphabetic\numexpr#1\relax{arabic}}
+\permanent\def\persiannumerals #1{\clf_alphabetic\numexpr#1\relax{persian}}
+\permanent\def\arabicdecimals #1{\clf_decimals \numexpr#1\relax{arabic}}
+\permanent\def\persiandecimals #1{\clf_decimals \numexpr#1\relax{persian}}
+
+\aliased \let\arabicexnumerals \persiannumerals
+
+\permanent\def\koreannumerals #1{\clf_alphabetic\numexpr#1\relax{korean}}
+\permanent\def\koreannumeralsp #1{\clf_alphabetic\numexpr#1\relax{korean-parenthesis}}
+\permanent\def\koreannumeralsc #1{\clf_alphabetic\numexpr#1\relax{korean-circle}}
+
+\aliased \let\koreanparentnumerals \koreannumeralsp
+\aliased \let\koreanparenthesisnumerals\koreannumeralsp
+\aliased \let\koreancirclenumerals \koreannumeralsc
+
+\permanent\def\chinesenumerals #1{\clf_chinesenumerals\numexpr#1\relax{normal}}
+\permanent\def\chinesecapnumerals#1{\clf_chinesenumerals\numexpr#1\relax{cap}}
+\permanent\def\chineseallnumerals#1{\clf_chinesenumerals\numexpr#1\relax{all}}
+
+%D \macros
+%D {character,Character}
+%D
+%D Converting a number into a character can of course only be done with numbers
+%D less or equal to~26. At the cost of much more macros a faster conversion is
+%D possible, using:
+%D
+%D \starttyping
+%D \setvalue{char1}{a} \def\character#1{\getvalue{char#1}}
+%D \stoptyping
+%D
+%D But we prefer a simpel \type{\case}.
+%D
+%D \showsetup{character}
+%D \showsetup{Character}
+
+\permanent\def\character#1{\clf_character\numexpr#1\relax}
+\permanent\def\Character#1{\clf_Character\numexpr#1\relax}
+
+%D \macros
+%D {characters,Characters}
+%D
+%D Converting large numbers is supported by the next two macros. This time we
+%D just count on: $\cdots$~x, y, z, aa, ab, ac~$\cdots$.
+%D
+%D \showsetup{characters}
+%D \showsetup{Characters}
+
+\permanent\def\characters#1{\clf_characters\numexpr#1\relax}
+\permanent\def\Characters#1{\clf_Characters\numexpr#1\relax}
+
+%D \macros
+%D {greeknumerals,Greeknumerals}
+%D
+%D Why should we only honour the romans, and not the greek?
+
+% \let\greeknumerals\gobbleoneargument
+% \let\Greeknumerals\gobbleoneargument
+
+%D \macros
+%D {oldstylenumerals,oldstyleromannumerals}
+%D
+%D These conversions are dedicated to Frans Goddijn.
+
+\permanent\protected\def\oldstylenumerals#1%
+ {\begingroup
+ \os\number#1%
+ \endgroup}
+
+\permanent\protected\def\oldstyleromannumerals#1% will become obsolete
+ {\dontleavehmode
+ \hbox\bgroup
+ \ss\txx
+ \setbox\scratchbox\hbox \s!spread .15\emwidth{\hss\uppercased{\romannumerals{#1}}\hss}%
+ \scratchwidth \wd\scratchbox
+ \scratchheight\ht\scratchbox
+ \scratchdimen .1\exheight
+ \vrule\s!width\scratchwidth\s!height\dimexpr\scratchheight+\scratchdimen\relax\s!depth-\dimexpr\scratchheight-+\scratchdimen\relax
+ \hskip-\scratchwidth
+ \vrule\s!width\scratchwidth\s!height\scratchdimen\s!depth\scratchdimen
+ \hskip-\scratchwidth
+ \box\scratchbox
+ \egroup}
+
+%D \macros
+%D {normaltime,normalyear,normalmonth,normalday}
+%D
+%D The last part of this module is dedicated to converting dates. Because we want to
+%D use as meaningful commands as possible, and because \TEX\ already uses up some of
+%D those, we save the original meanings.
+
+% \savenormalmeaning\time
+% \savenormalmeaning\year
+% \savenormalmeaning\month
+% \savenormalmeaning\day
+
+%D \macros
+%D {month,MONTH}
+%D
+%D Converting the month number into a month name is done using a case statement,
+%D abstact values and the label mechanism. This way users can easily redefine a
+%D label from for instance german into austrian.
+%D
+%D \starttyping
+%D \setuplabeltext [de] [january=J\"anner]
+%D \stoptyping
+%D
+%D Anyhow, the conversion looks like:
+
+\permanent\protected\def\monthlong #1{\clf_monthname\numexpr#1\relax}
+\permanent\protected\def\monthshort#1{\clf_monthmnem\numexpr#1\relax}
+\permanent\protected\def\daylong #1{\clf_dayname\numexpr#1\relax}
+\permanent\protected\def\dayshort #1{\clf_daymnem\numexpr#1\relax}
+\aliased \let\convertmonth\monthlong % for old times sake
+
+%D We redefine the \TEX\ primitive \type{\month} as:
+%D
+%D \showsetup{month}
+%D \showsetup{MONTH}
+
+\pushoverloadmode
+
+\aliased\let\month\monthlong
+
+\popoverloadmode
+
+\permanent\protected\def\MONTH #1{\WORD{\clf_monthname\numexpr#1\relax}}
+\permanent\protected\def\MONTHLONG #1{\WORD{\clf_monthname\numexpr#1\relax}}
+\permanent\protected\def\MONTHSHORT#1{\WORD{\clf_monthmnem\numexpr#1\relax}}
+
+%D We never explicitly needed this, but Tobias Burnus pointed out that it would be
+%D handy to convert to the day of the week. In doing so, we have to calculate the
+%D total number of days, taking leapyears into account. For those who are curious:
+%D
+%D \startitemize[packed]
+%D \item years that can be divided by 4 are leapyears
+%D \item exept years that can be divided by 100
+%D \item unless years can be divided by 400
+%D \stopitemize
+
+%D \macros
+%D {weekday,WEEKDAY}
+%D
+%D The first one is sort of redundant. It takes the day number argument.
+%D
+%D \showsetup{weekday}
+%D \showsetup{WEEKDAY}
+
+\permanent\protected\def\weekday #1{\clf_dayname\numexpr#1\relax}
+\permanent\protected\def\WEEKDAY #1{\WORD{\clf_dayname\numexpr#1\relax}}
+\permanent\protected\def\DAYLONG #1{\WORD{\clf_dayname\numexpr#1\relax}}
+\permanent\protected\def\DAYSHORT#1{\WORD{\clf_daymnem\numexpr#1\relax}}
+
+%D \macros
+%D {getdayoftheweek, dayoftheweek}
+
+\newcount\normalweekday
+
+\permanent \def\dayoftheweek #1#2#3{\clf_weekdayname\numexpr#1\relax\numexpr#2\relax\numexpr#3\relax} % name
+\permanent\protected\def\getdayoftheweek#1#2#3{\normalweekday\clf_weekday\numexpr#1\relax\numexpr#2\relax\numexpr#3\relax\relax} % number
+
+%D Using this macro in
+%D
+%D \startbuffer
+%D monday: \dayoftheweek {4} {5} {1992}
+%D friday: \dayoftheweek {16} {6} {1995}
+%D monday: \dayoftheweek {25} {8} {1997}
+%D saturday: \dayoftheweek {30} {8} {1997}
+%D tuesday: \dayoftheweek {2} {1} {1996}
+%D tuesday: \dayoftheweek {7} {1} {1997}
+%D tuesday: \dayoftheweek {13} {1} {1998}
+%D friday: \dayoftheweek {1} {1} {2000}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D gives
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D The macro \type {\getdayoftheweek} can be used to calculate the number \type
+%D {\normalweekday}.
+
+%D \macros
+%D {doifleapyearelse,
+%D getdayspermonth}
+%D
+%D Sometimes we need to know if we're dealing with a leapyear, so here is a
+%D testmacro:
+%D
+%D \starttyping
+%D \doifleapyearelse{year}{yes}{no}
+%D \stoptyping
+%D
+%D An example of its use can be seen in the macro
+%D
+%D \starttyping
+%D \getdayspermonth{year}{month}
+%D \stoptyping
+%D
+%D The number of days is available in the macro \type {\numberofdays}.
+
+\permanent\def\doifelseleapyear#1% expandable check
+ {\clf_doifelseleapyear\numexpr#1\relax}
+
+\aliased\let\doifleapyearelse\doifelseleapyear
+
+\mutable\let\numberofdays\!!zerocount
+
+\permanent\protected\def\getdayspermonth#1#2%
+ {\edef\numberofdays{\clf_nofdays\numexpr#1\relax\numexpr#2\relax}}
+
+\permanent\def\dayspermonth#1#2%
+ {\clf_nofdays\numexpr#1\relax\numexpr#2\relax}
+
+% \dayoftheweek{2006}{9}{15}
+% \doifleapyearelse{2000}{OK}{NOT OK}
+% \doifleapyearelse{2100}{NOT OK}{OK}
+% \doifleapyearelse{2004}{OK}{NOT OK}
+% \doifleapyearelse{2003}{NOT OK}{OK}
+% \dayspermonth{2000}{2}
+% [\the\normaltime=\the\time]
+
+%D \macros
+%D {currentdate, rawdate, date}
+%D
+%D We use these conversion macros in the date formatting macro:
+%D
+%D \showsetup{currentdate}
+%D
+%D This macro takes care of proper spacing and delivers for instance:
+%D
+%D \startbuffer
+%D \currentdate[weekday,day,month,year]
+%D \currentdate[WEEKDAY,day,MONTH,year]
+%D \stopbuffer
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D depending of course on the keywords. Here we gave:
+%D
+%D \typebuffer
+%D
+%D If needed one can also add non||keywords, like in
+%D
+%D \startbuffer
+%D \currentdate[dd,--,mm,--,yy]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D or typeset: \getbuffer.
+%D
+%D When no argument is passed, the current date is given as specified per
+%D language (using \type{\installlanguage}).
+%D
+%D \showsetup{currentdate}
+%D
+%D \startbuffer
+%D \date
+%D \date[d=12,m=12,y=1998][weekday]
+%D \date[d=12,m=12,y=1998]
+%D \stopbuffer
+%D
+%D We can also typeset arbitrary dates, using the previous
+%D command.
+%D
+%D \typebuffer
+%D
+%D The date is specified by one character keys. When no date is given, we get the
+%D current date.
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+%D \starttabulate[|l|l|]
+%D \HL
+%D \NC year \NC (\currentdate[year]) \NC\NR
+%D \NC yy \NC (\currentdate[yy]) \NC\NR
+%D \NC y \NC (\currentdate[y]) \NC\NR
+%D \NC Y \NC (\currentdate[Y]) \NC\NR
+%D \HL
+%D \NC month \NC (\currentdate[month]) \NC\NR
+%D \NC mm \NC (\currentdate[mm]) \NC\NR
+%D \NC m \NC (\currentdate[m]) \NC\NR
+%D \NC M \NC (\currentdate[M]) \NC\NR
+%D \HL
+%D \NC day \NC (\currentdate[day]) \NC\NR
+%D \NC dd \NC (\currentdate[dd]) \NC\NR
+%D \NC d \NC (\currentdate[d]) \NC\NR
+%D \NC D \NC (\currentdate[D]) \NC\NR
+%D \HL
+%D \NC weekday \NC (\currentdate[weekday]) \NC\NR
+%D \NC w \NC (\currentdate[w]) \NC\NR
+%D \NC W \NC (\currentdate[W]) \NC\NR
+%D \HL
+%D \NC referral \NC (\currentdate[referral]) \NC\NR
+%D \HL
+%D \NC day:mnem \NC (\currentdate[day:mnem]) \NC\NR
+%D \NC dd:mnem \NC (\currentdate[dd:mnem]) \NC\NR
+%D \NC d:mnem \NC (\currentdate[d:mnem]) \NC\NR
+%D \NC D:mnem \NC (\currentdate[D:mnem]) \NC\NR
+%D \HL
+%D \stoptabulate
+%D
+%D \startbuffer
+%D (\currentdate[D,.,M,.,Y])
+%D (\currentdate[day,month,year])
+%D (\currentdate[day,+,month,+,year])
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Some indirectness and abstraction:
+%D
+%D \starttyping
+%D \definedate[crap][year]
+%D \currentdate[crap]
+%D \stoptyping
+
+\installcorenamespace{date}
+
+\permanent\tolerant\protected\def\definedate[#1]#*[#2]%
+ {\ifarguments\else\setvalue{\??date#1}{#2}\fi}
+
+\def\syst_converters_check_date_specification#1%
+ {\ifcsname\??date#1\endcsname
+ \edef#1{\lastnamedcs}%
+ \fi}
+
+\newtoks\everycurrentdate
+
+\permanent\tolerant\protected\def\currentdate[#1]%
+ {\dontleavehmode
+ \begingroup
+ \the\everycurrentdate
+ \edef\m_syst_converters_date{#1}%
+ \syst_converters_check_date_specification\m_syst_converters_date
+ \syst_converters_check_date_specification\currentdatespecification
+ \clf_currentdate
+ {\m_syst_converters_date}{\currentdatespecification}{\labellanguage}%
+ \normalyear\normalmonth\normalday
+ \endgroup}
+
+\permanent\tolerant\protected\def\setdate[#1]% sets the date !
+ {\begingroup
+ \letdummyparameter\c!d\normalday
+ \letdummyparameter\c!m\normalmonth
+ \letdummyparameter\c!y\normalyear
+ \getdummyparameters[#1]%
+ \normalexpanded
+ {\endgroup
+ \iftok{\directdummyparameter\c!d}\emptytoks\else\normalday \number\directdummyparameter\c!d\relax\fi
+ \iftok{\directdummyparameter\c!m}\emptytoks\else\normalmonth\number\directdummyparameter\c!m\relax\fi
+ \iftok{\directdummyparameter\c!y}\emptytoks\else\normalyear \number\directdummyparameter\c!y\relax\fi}}
+
+\permanent\tolerant\protected\def\date[#1]#*[#2]% sets the date !
+ {\dontleavehmode
+ \begingroup
+ \setdate[#1]% sets the date !
+ \the\everycurrentdate
+ \edef\m_syst_converters_date{#2}%
+ \syst_converters_check_date_specification\m_syst_converters_date
+ \syst_converters_check_date_specification\currentdatespecification
+ \clf_currentdate
+ {\m_syst_converters_date}{\currentdatespecification}{\labellanguage}%
+ \normalyear\normalmonth\normalday
+ \endgroup}
+
+\permanent\def\rawdate[#1]% expandable and no labels
+ {\clf_currentdate
+ {#1}{\currentdatespecification}{}%
+ \normalyear\normalmonth\normalday}
+
+%D \macros
+%D {currenttime}
+%D
+%D The currenttime is actually the jobtime. You can specify a pattern similar
+%D to the previous date macro using the keys \type {h}, \type {m} and a separator.
+
+\mutable\let\currenthour \!!plusone
+\mutable\let\currentminute\!!plusone
+\mutable\let\currentsecond\!!plusone
+
+\permanent\protected\def\calculatecurrenttime
+ {\edef\currenthour {\clf_hour }%
+ \edef\currentminute{\clf_minute}%
+ \edef\currentsecond{\clf_second}}
+
+% \def\currenttimespecification{h,:,m}
+
+\permanent\protected\def\currenttime
+ {\doifelsenextoptional\syst_converters_current_time_yes\syst_converters_current_time_nop}
+
+\protected\def\syst_converters_current_time_yes[#1]%
+ {\calculatecurrenttime
+ \processallactionsinset[#1][h=>\currenthour,m=>\currentminute,\s!unknown=>\commalistelement]}
+
+\protected\def\syst_converters_current_time_nop
+ {\normalexpanded{\syst_converters_current_time_yes[\currenttimespecification]}}
+
+%D Because we're dealing with dates, we also introduce a few day loops:
+%D
+%D \starttyping
+%D \processmonth{year}{month}{command}
+%D \processyear{year}{command}{before}{after}
+%D \stoptyping
+%D
+%D The counters \type {\normalyear}, \type {\normalmonth} and \type {\normalday}
+%D can be used for for date manipulations.
+
+\permanent\protected\def\processmonth#1#2#3% year month command
+ {\begingroup
+ \getdayspermonth{#1}{#2}%
+ \dostepwiserecurse\plusone\numberofdays\plusone
+ {\normalyear #1\relax
+ \normalmonth#2\relax
+ \normalday \recurselevel\relax
+ #3}%
+ \endgroup}
+
+\permanent\protected\def\processyear#1#2#3#4% year command before after
+ {\begingroup
+ \dorecurse\plustwelve
+ {\normalyear #1\relax
+ \normalmonth\recurselevel\relax
+ #3\processmonth\normalyear\normalmonth{#2}#4}%
+ \endgroup}
+
+%D \macros
+%D {defineconversion, convertnumber}
+%D
+%D Conversion involves the macros that we implemented earlier in this module.
+%D
+%D \showsetup{defineconversion}
+%D \showsetup{convertnumber}
+%D
+%D We can feed this command with conversion macros as well as a set of conversion
+%D symbols. Both need a bit different treatment.
+%D
+%D \starttyping
+%D \defineconversion [roman] [\romannumerals]
+%D \defineconversion [set 1] [$\star$,$\bullet$,$\ast$]
+%D \stoptyping
+%D
+%D You can define a language dependent conversion with:
+%D
+%D \starttyping
+%D \defineconversion [en] [whatever] [\something]
+%D \stoptyping
+
+\installcorenamespace {conversion}
+\installcorenamespace {conversionarguments}
+\installcorenamespace {conversionwords}
+
+%D It might be better to move more to \LUA\ as we also need conversion there and
+%D doublicating logic doesn't make things cleaner. It means that all conversions
+%D will get a language argument too. However, we permit definitions at the \TEX\ end
+%D so we have to provide some hybrid method.
+
+% checkedconversion(method,n,language)
+
+\permanent\tolerant\protected\def\defineconversion[#1]#*[#2]#*[#3]% from now on global (maybe local again some day)
+ {\ifparameter#3\or
+ \syst_converters_define_conversion_indeed{#1#2}{#1:#2}{#3}%
+ \else
+ \syst_converters_define_conversion_indeed{#1}{#1}{#2}%
+ \fi}
+
+\def\syst_converters_define_conversion_indeed#1#2#3%
+ {\doifelseinstring{,}{\detokenize{#3}}
+ {\clf_defineconversion{#2}{\detokenize{#3}}% a set e.g. of symbols
+ \setgvalue{\??conversion#1}{\syst_converters_checked_conversion{#2}}}
+ {\setgvalue{\??conversion#1}{#3}}}
+
+\def\syst_converters_checked_conversion#1#2%
+ {\clf_checkedconversion{#1}\numexpr#2\relax}
+
+%D If a conversion is just a font switch then we need to make sure that the number
+%D is indeed ends up as number in the input, so we need to handle the second
+%D argument.
+
+\permanent\def\convertnumber#1#2% expandable
+ {\csname\??conversionarguments
+ \ifcsname\??conversion\currentlanguage#1\endcsname1\else
+ \ifcsname\??conversion #1\endcsname2\else
+ 3\fi\fi
+ \endcsname{#1}{\number#2}}
+
+\permanent\protected\def\uconvertnumber % unexpandable
+ {\convertnumber}
+
+\setvalue{\??conversionarguments1}#1{\csname\??conversion\currentlanguage#1\endcsname}
+\setvalue{\??conversionarguments2}#1{\csname\??conversion #1\endcsname}
+\letvalue{\??conversionarguments3}\syst_converters_checked_conversion
+
+% we can also add a \ctxcommand{doifelseconversion("#1","\currentlanguage")} to check
+% if we have definitions that are not matched at the lua end .. eventually we might do
+% that when more shows up
+
+\permanent\def\doifelseconversiondefined#1% expandable
+ {\ifcsname\??conversion\currentlanguage#1\endcsname
+ \expandafter\firstoftwoarguments
+ \orelse\ifcsname\??conversion#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\def\doifelseconversionnumber#1#2% expandable
+ {\ifnum#2>\numexpr\clf_nofconversions{#1}\relax
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doifconversiondefinedelse\doifelseconversiondefined
+\aliased\let\doifconversionnumberelse \doifelseconversionnumber
+
+%D Handy.
+
+\setvalue{\??conversionwords\v!one }{1}
+\setvalue{\??conversionwords\v!two }{2}
+\setvalue{\??conversionwords\v!three}{3}
+\setvalue{\??conversionwords\v!four }{4}
+\setvalue{\??conversionwords\v!five }{5}
+
+%permanent\def\wordtonumber#1#2{\ifcsname\??conversionwords#1\endcsname\csname\??conversionwords#1\endcsname\else#2\fi}
+\permanent\def\wordtonumber#1#2{\ifcsname\??conversionwords#1\endcsname\lastnamedcs\else#2\fi}
+
+% \defineconversion[ctx][c,o,n,t,e,x,t]
+%
+% \doloop{\doifelseconversionnumber{ctx}{\recurselevel}{[\recurselevel]}{\exitloop}}
+
+%D \macros
+%D {ordinalnumber, highordinalstr, ordinalstr}
+%D
+%D Efficient general ordinal number converters are sometimes difficult to
+%D implement. Fortunately dates never exceed the number~31.
+
+\permanent\def\highordinalstr#1{\high{\notsmallcapped{#1}}}
+\permanent\def\ordinalstr #1{\notsmallcapped{#1}}
+\permanent\def\ordinalnumber #1{\clf_ordinal\numexpr#1\relax{\currentlanguage}}
+\permanent\def\Ordinalnumber #1{\Words{\clf_ordinal\numexpr#1\relax{\currentlanguage}}}
+
+\mutable\let\verbosenumberconnector\space
+
+\permanent\def\verbosenumber#1{\clf_verbose\numexpr#1\relax{\currentlanguage}{\verbosenumberconnector}}
+\permanent\def\VerboseNumber#1{\Words{\clf_verbose\numexpr#1\relax{\currentlanguage}{\verbosenumberconnector}}}
+
+%D As longs as symbols are linked to levels or numbers, we can also use the
+%D conversion mechanism, but in for instance the itemization macros, we prefer
+%D symbols because they can more easier be (partially) redefined. Symbols are
+%D implemented in another module.
+
+\permanent\def\smallcappedromannumerals#1{\smallcapped{\romannumerals{#1}}}
+\permanent\def\smallcappedcharacters #1{\smallcapped{\characters {#1}}}
+
+\defineconversion [] [\numbers] % the default conversion
+\defineconversion [\v!empty] [\gobbleoneargument]
+\defineconversion [\v!none] [\numbers]
+\defineconversion [\s!default] [\numbers]
+
+\defineconversion [month] [\monthlong]
+\defineconversion [month:mnem] [\monthshort]
+
+\defineconversion [\v!character] [\character]
+\defineconversion [\v!Character] [\Character]
+
+\defineconversion [\v!characters] [\characters]
+\defineconversion [\v!Characters] [\Characters]
+
+\defineconversion [a] [\alphabeticnumerals]
+\defineconversion [A] [\Alphabeticnumerals]
+\defineconversion [AK] [\smallcappedcharacters]
+\defineconversion [KA] [\smallcappedcharacters]
+
+\defineconversion [\v!alphabetic] [\alphabeticnumerals]
+\defineconversion [\v!Alphabetic] [\Alphabeticnumerals]
+
+\defineconversion [\v!number] [\numbers]
+\defineconversion [\v!numbers] [\numbers]
+\defineconversion [\v!Numbers] [\Numbers]
+\defineconversion [\v!mediaeval] [\mediaeval]
+
+\defineconversion [\v!word] [\verbosenumber]
+\defineconversion [\v!words] [\verbosenumber]
+
+\defineconversion [\v!Word] [\VerboseNumber]
+\defineconversion [\v!Words] [\VerboseNumber]
+
+\defineconversion[\v!ordinal] [\ordinalnumber]
+\defineconversion[\v!Ordinal] [\Ordinalnumber]
+
+\defineconversion [n] [\numbers]
+\defineconversion [N] [\Numbers]
+\defineconversion [m] [\mediaeval]
+\defineconversion [o] [\oldstylenumerals]
+\defineconversion [O] [\oldstylenumerals]
+\defineconversion [or] [\oldstyleromannumerals]
+
+\defineconversion [\v!romannumerals] [\romannumerals]
+\defineconversion [\v!Romannumerals] [\Romannumerals]
+
+\defineconversion [i] [\romannumerals]
+\defineconversion [I] [\Romannumerals]
+\defineconversion [r] [\romannumerals]
+\defineconversion [R] [\Romannumerals]
+
+\defineconversion [KR] [\smallcappedromannumerals]
+\defineconversion [RK] [\smallcappedromannumerals]
+
+\defineconversion [\v!greek] [\greeknumerals]
+\defineconversion [\v!Greek] [\Greeknumerals]
+
+\defineconversion [g] [\greeknumerals]
+\defineconversion [G] [\Greeknumerals]
+
+%defineconversion [ñ] [\spanishnumerals]
+%defineconversion [Ñ] [\Spanishnumerals]
+
+\defineconversion [abjadnumerals] [\abjadnumerals]
+\defineconversion [abjadnodotnumerals] [\abjadnodotnumerals]
+\defineconversion [abjadnaivenumerals] [\abjadnaivenumerals]
+
+\defineconversion [hebrewnumerals] [\hebrewnumerals]
+\defineconversion [yiddishnumerals] [\yiddishnumerals]
+
+% users can do: \defineconversion [he] [\hebrewnumerals]
+% users can do: \defineconversion [yi] [\hebrewnumerals]
+
+\defineconversion [thainumerals] [\thainumerals]
+\defineconversion [devanagarinumerals] [\devanagarinumerals]
+\defineconversion [gurmurkhinumerals] [\gurmurkhinumerals]
+\defineconversion [gujaratinumerals] [\gujaratinumerals]
+\defineconversion [tibetannumerals] [\tibetannumerals]
+\defineconversion [greeknumerals] [\greeknumerals]
+\defineconversion [Greeknumerals] [\Greeknumerals]
+\defineconversion [arabicnumerals] [\arabicnumerals]
+\defineconversion [persiannumerals] [\persiannumerals]
+\defineconversion [arabicexnumerals] [\arabicexnumerals]
+\defineconversion [arabicdecimals] [\arabicdecimals]
+\defineconversion [persiandecimals] [\persiandecimals]
+
+\defineconversion [koreannumerals] [\koreannumerals]
+\defineconversion [koreanparennumerals] [\koreanparenthesisnumerals] % for old times sake (for a while)
+\defineconversion [koreanparenthesisnumerals]
+ [\koreanparenthesisnumerals]
+\defineconversion [koreancirclenumerals] [\koreancirclenumerals]
+
+\defineconversion [kr] [\koreannumerals]
+\defineconversion [kr-p] [\koreanparentnumerals]
+\defineconversion [kr-c] [\koreancirclenumerals]
+
+\defineconversion [chinesenumerals] [\chinesenumerals]
+\defineconversion [chinesecapnumerals] [\chinesecapnumerals]
+\defineconversion [chineseallnumerals] [\chineseallnumerals]
+
+\defineconversion [cn] [\chinesenumerals]
+\defineconversion [cn-c] [\chinesecapnumerals]
+\defineconversion [cn-a] [\chineseallnumerals]
+
+%D Moved from lang-def.mkiv:
+%D
+%D Define these as the general character enumeration when language is Slovenian. If
+%D you feel uncomfortable with this, mail Mojca, since she promised to to take the
+%D heat. Pablo was next to request this. We changed characters to numerals for this
+%D feature. We do need these definitions for mechanisms like itemize that check
+%D for converters.
+
+\permanent\def\sloveniannumerals#1{\clf_alphabetic\numexpr#1\relax{sl}}
+\permanent\def\slovenianNumerals#1{\clf_Alphabetic\numexpr#1\relax{sl}}
+
+\permanent\def\spanishnumerals #1{\clf_alphabetic\numexpr#1\relax{es}}
+\permanent\def\spanishNumerals #1{\clf_Alphabetic\numexpr#1\relax{es}}
+
+\permanent\def\russiannumerals #1{\clf_alphabetic\numexpr#1\relax{ru}}
+\permanent\def\russianNumerals #1{\clf_Alphabetic\numexpr#1\relax{ru}}
+
+%defineconversion [\s!sl] [character] [\sloveniannumerals]
+%defineconversion [\s!sl] [Character] [\slovenianNumerals]
+%defineconversion [\s!sl] [characters] [\sloveniannumerals]
+%defineconversion [\s!sl] [Characters] [\slovenianNumerals]
+
+\defineconversion [\s!sl] [alphabetic] [\sloveniannumerals]
+\defineconversion [\s!sl] [Alphabetic] [\slovenianNumerals]
+
+\defineconversion [\s!sl] [a] [\sloveniannumerals]
+\defineconversion [\s!sl] [A] [\slovenianNumerals]
+\defineconversion [\s!sl] [AK] [\smallcapped\sloveniannumerals]
+\defineconversion [\s!sl] [KA] [\smallcapped\sloveniannumerals]
+
+%defineconversion [\s!es] [character] [\spanishnumerals]
+%defineconversion [\s!es] [Character] [\spanishNumerals]
+%defineconversion [\s!es] [characters] [\spanishnumerals]
+%defineconversion [\s!es] [Characters] [\spanishNumerals]
+
+\defineconversion [\s!es] [alphabetic] [\spanishnumerals]
+\defineconversion [\s!es] [Alphabetic] [\spanishNumerals]
+
+\defineconversion [\s!es] [a] [\spanishnumerals]
+\defineconversion [\s!es] [A] [\spanishNumerals]
+\defineconversion [\s!es] [AK] [\smallcapped\spanishnumerals]
+\defineconversion [\s!es] [KA] [\smallcapped\spanishnumerals]
+
+%defineconversion [\s!ru] [character] [\russiannumerals]
+%defineconversion [\s!ru] [Character] [\russianNumerals]
+%defineconversion [\s!ru] [characters] [\russiannumerals]
+%defineconversion [\s!ru] [Characters] [\russianNumerals]
+
+\defineconversion [\s!ru] [alphabetic] [\russiannumerals]
+\defineconversion [\s!ru] [Alphabetic] [\russianNumerals]
+
+\defineconversion [\s!ru] [a] [\russiannumerals]
+\defineconversion [\s!ru] [A] [\russianNumerals]
+\defineconversion [\s!ru] [AK] [\smallcapped\russiannumerals]
+\defineconversion [\s!ru] [KA] [\smallcapped\russiannumerals]
+
+\defineconversion [sloveniannumerals] [\sloveniannumerals]
+\defineconversion [slovenianNumerals] [\slovenianNumerals]
+
+\defineconversion [spanishnumerals] [\spanishnumerals]
+\defineconversion [spanishNumerals] [\spanishNumerals]
+
+\defineconversion [russiannumerals] [\russiannumerals]
+\defineconversion [russianNumerals] [\russianNumerals]
+
+%D In case a font has no greek (WS):
+
+\defineconversion [mathgreek]
+ [\m{α},\m{β},\m{γ},\m{δ},\m{ε},\m{ζ},
+ \m{η},\m{θ},\m{ι},\m{κ},\m{λ},\m{μ},
+ \m{ν},\m{ξ},\m{ο},\m{π},\m{ρ},\m{σ},
+ \m{τ},\m{υ},\m{φ},\m{χ},\m{ψ},\m{ω}]
+
+%D Handy too (expanded!):
+
+\permanent\def\unihex#1{\clf_unihex\numexpr#1\relax}
+
+%D Symbol sets:
+
+\ifdefined\symbol \else \def\symbol[#1]{#1} \fi % todo
+
+% \defineconversion
+% [set 0]
+% [{\symbol[bullet]},
+% {\symbol[dash]},
+% {\symbol[star]},
+% {\symbol[triangle]},
+% {\symbol[circle]},
+% {\symbol[medcircle]},
+% {\symbol[bigcircle]},
+% {\symbol[square]},
+% {\symbol[checkmark]}]
+
+% \defineconversion
+% [set 1]
+% [\mathematics{\star},
+% \mathematics{\star\star},
+% \mathematics{\star\star\star},
+% \mathematics{\ddagger},
+% \mathematics{\ddagger\ddagger},
+% \mathematics{\ddagger\ddagger\ddagger},
+% \mathematics{\ast},
+% \mathematics{\ast\ast},
+% \mathematics{\ast\ast\ast}]
+%
+% \defineconversion
+% [set 2]
+% [\mathematics{\ast},
+% \mathematics{\dag},
+% \mathematics{\ddag},
+% \mathematics{\ast\ast},
+% \mathematics{\dag\dag},
+% \mathematics{\ddag\ddag},
+% \mathematics{\ast\ast\ast},
+% \mathematics{\dag\dag\dag},
+% \mathematics{\ddag\ddag\ddag},
+% \mathematics{\ast\ast\ast\ast},
+% \mathematics{\dag\dag\dag\dag},
+% \mathematics{\ddag\ddag\ddag\ddag}]
+%
+% \defineconversion
+% [set 3]
+% [\mathematics{\star},
+% \mathematics{\star\star},
+% \mathematics{\star\star\star},
+% \mathematics{\ddagger},
+% \mathematics{\ddagger\ddagger},
+% \mathematics{\ddagger\ddagger\ddagger},
+% \mathematics{\P},
+% \mathematics{\P\P},
+% \mathematics{\P\P\P},
+% \mathematics{\S},
+% \mathematics{\S\S},
+% \mathematics{\S\S\S},
+% \mathematics{\ast},
+% \mathematics{\ast\ast},
+% \mathematics{\ast\ast\ast}]
+
+\defineconversion
+ [set 0]
+ [\symbol{bullet},
+ \symbol{dash},
+ \symbol{star},
+ \symbol{triangle},
+ \symbol{circle},
+ \symbol{medcircle},
+ \symbol{bigcircle},
+ \symbol{square},
+ \symbol{checkmark}]
+
+\defineconversion
+ [set 1]
+ [\textormathchars{⋆},
+ \textormathchars{⋆⋆},
+ \textormathchars{⋆⋆⋆},
+ \textormathchars{‡},
+ \textormathchars{‡‡},
+ \textormathchars{‡‡‡},
+ \textormathchars{∗},
+ \textormathchars{∗∗},
+ \textormathchars{∗∗∗}]
+
+\defineconversion
+ [set 2]
+ [\textormathchars{∗},
+ \textormathchars{†},
+ \textormathchars{‡},
+ \textormathchars{∗∗},
+ \textormathchars{††},
+ \textormathchars{‡‡},
+ \textormathchars{∗∗∗},
+ \textormathchars{†††},
+ \textormathchars{‡‡‡},
+ \textormathchars{∗∗∗∗},
+ \textormathchars{††††},
+ \textormathchars{‡‡‡‡}]
+
+\defineconversion
+ [set 3]
+ [\textormathchars{⋆},
+ \textormathchars{⋆⋆},
+ \textormathchars{⋆⋆⋆},
+ \textormathchars{‡},
+ \textormathchars{‡‡},
+ \textormathchars{‡‡‡},
+ \textormathchars{¶},
+ \textormathchars{¶¶},
+ \textormathchars{¶¶¶},
+ \textormathchars{§},
+ \textormathchars{§§},
+ \textormathchars{§§§},
+ \textormathchars{∗},
+ \textormathchars{∗∗},
+ \textormathchars{∗∗∗}]
+
+%D Iteration of suggestion by WS on mailinglist 2010.12.22:
+%D
+%D \starttyping
+%D \setupfloatsplitting[conversion=continued]
+%D \stoptyping
+
+\permanent\protected\def\continuednumber#1%
+ {\labeltext{\ifcase#1\or\else\v!continued\fi}}
+
+\defineconversion
+ [\v!continued]
+ [\continuednumber]
+
+%D Taken from x-asciimath (see digits-001.tex for an example):
+
+\mutable\let\spaceddigitsmethod \empty % alternative methods : 1 default, 2 and 3 only when > 4
+\mutable\let\spaceddigitssymbol \empty % extra splitter symbol : {,}
+\mutable\let\spaceddigitsseparator\empty % separator symbol : {\Uchar{"2008}}
+
+\permanent\protected\def\spaceddigits#1%
+ {\clf_spaceddigits
+ method {\spaceddigitsmethod}%
+ symbol {\spaceddigitssymbol}%
+ separator {\spaceddigitsseparator}%
+ data {#1}%
+ \relax}
+
+%D For those who sart counting at zero:
+%D
+%D \starttyping
+%D \defineconversionset [zero] [n,zero] [n]
+%D
+%D \setuphead [sectionconversionset=zero]
+%D
+%D \starttext
+%D \startchapter [title=Introduction]
+%D \startsection [title=First topic] \stopsection
+%D \startsection [title=Second topic] \stopsection
+%D \stopchapter
+%D \stoptext
+%D \stoptyping
+
+\permanent\def\zeronumberconversion#1{\number\numexpr#1-\plusone\relax}
+
+\defineconversion [zero] [\zeronumberconversion]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-ctx.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-ctx.mkxl
new file mode 100644
index 00000000000..8837640d442
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-ctx.mkxl
@@ -0,0 +1,29 @@
+%D \module
+%D [ file=core-ctx,
+%D version=2006.08.16, % old stuff
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Job Control,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Job Control}
+
+\registerctxluafile{core-ctx}{}
+
+\unprotect
+
+% called directly in core-def.mkiv
+%
+% \protected\def\job_options_get_commandline {\clf_setdocumentcommandline}
+% \protected\def\job_options_get_ctxfile {\clf_setdocumentctxfile}
+% \protected\def\job_options_set_modes {\clf_setdocumentmodes}
+% \protected\def\job_options_set_modules {\clf_setdocumentmodules}
+% \protected\def\job_options_set_environments{\clf_setdocumentenvironments}
+% \protected\def\job_options_set_filenames {\clf_setdocumentfilenames}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-dat.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-dat.mkxl
new file mode 100644
index 00000000000..93d0bbb7ae8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-dat.mkxl
@@ -0,0 +1,110 @@
+%D \module
+%D [ file=core-dat,
+%D version=20122.04.17, % replaces core-two from 1997.03.31,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Multipass Datasets,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Multipass Datasets}
+
+%D \starttyping
+%D \definedataset[test-1]
+%D \definedataset[test-2][delay=yes]
+%D
+%D \startlines
+%D set 1: \setdataset[test-1][whatever=this-or-that-1]
+%D set 2: \setdataset[test-2][whatever=this-or-that-2]
+%D set 3: \setdataset[test-2][whatever=this-or-that-3]
+%D \stoplines
+%D
+%D \startlines
+%D get 1: \datasetvariable{test-1}{1}{whatever} / \datasetvariable{test-1}{1}{realpage}
+%D get 2: \datasetvariable{test-2}{1}{whatever} / \datasetvariable{test-2}{1}{realpage}
+%D get 3: \datasetvariable{test-2}{2}{whatever} / \datasetvariable{test-2}{2}{realpage}
+%D \stoplines
+%D \stoptyping
+
+\unprotect
+
+\registerctxluafile{core-dat}{}
+
+\installcorenamespace{dataset}
+
+\installcommandhandler \??dataset {dataset} \??dataset
+
+\permanent\tolerant\protected\def\setdataset[#1]#*[#2]#*[#3]%
+ {\begingroup
+ \edef\currentdataset{#1}%
+ \ifparameters\or\or
+ \clf_setdataset
+ name {\currentdataset}%
+ delay {\datasetparameter\c!delay}%
+ data {#2}%
+ \relax
+ \or
+ \edef\currentdataset{#1}%
+ \clf_setdataset
+ name {\currentdataset}%
+ tag {#2}%
+ delay {\datasetparameter\c!delay}%
+ data {#3}%
+ \relax
+ \fi
+ \endgroup}
+
+\permanent\def\datasetvariable#1#2#3%
+ {\clf_datasetvariable{#1}{#2}{#3}}
+
+\installcorenamespace{pagestate}
+\installcorenamespace{pagestatecounter}
+
+\installcommandhandler \??pagestate {pagestate} \??pagestate
+
+\def\syst_pagestates_allocate
+ {\expandafter\newcount\csname\??pagestatecounter\currentpagestate\endcsname}
+
+\appendtoks
+ \syst_pagestates_allocate
+\to \everydefinepagestate
+
+\setuppagestate
+ [\c!delay=\v!yes]
+
+\permanent\tolerant\protected\def\setpagestate[#1]#*[#2]%
+ {\begingroup
+ \edef\currentpagestate{#1}%
+ \ifcsname\??pagestatecounter\currentpagestate\endcsname
+ \scratchcounter\lastnamedcs
+ \advance\scratchcounter\plusone
+ \else
+ \scratchcounter\plusone
+ \syst_pagestates_allocate
+ \fi
+ \global\csname\??pagestatecounter\currentpagestate\endcsname\scratchcounter
+ \clf_setpagestate
+ name {\currentpagestate}%
+ tag {\ifparameter#2\or#2\else\number\scratchcounter\fi}%
+ delay {\pagestateparameter\c!delay}%
+ \relax
+ \endgroup}
+
+\permanent\protected\def\autosetpagestate#1%
+ {\setpagestate[#1]\relax}
+
+\permanent\def\autopagestatenumber#1{\begincsname\??pagestatecounter#1\endcsname}
+
+\permanent\def\pagestaterealpage #1#2{\clf_pagestaterealpage {#1}{#2}}
+\permanent\def\setpagestaterealpageno#1#2{\clf_setpagestaterealpageno{#1}{#2}}
+\permanent\def\pagestaterealpageorder#1#2{\clf_pagestaterealpageorder{#1}#2\relax}
+
+\permanent\def\autopagestaterealpage #1{\clf_pagestaterealpage {#1}{\number\autopagestatenumber{#1}}}
+\permanent\def\setautopagestaterealpageno#1{\clf_setpagestaterealpageno{#1}{\number\autopagestatenumber{#1}}}
+\permanent\def\autopagestaterealpageorder#1{\clf_pagestaterealpageorder{#1}\numexpr\autopagestatenumber{#1}\relax}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-def.mkxl
new file mode 100644
index 00000000000..96570bc366d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-def.mkxl
@@ -0,0 +1,182 @@
+%D \module
+%D [ file=core-def,
+%D version=2002.05.07,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Defaults,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Defaults}
+
+%D Here we collect settings that cannot be done earlier due to
+%D depedencies. More code will moved to this module later.
+
+\unprotect
+
+\usesymbols[mis,nav] % no longer mvs preloaded
+
+\setupsymbolset[navigation 1]
+
+\setupinteraction[\c!symbolset=navigation 1]
+
+% what a mess
+
+\everypar \emptytoks
+\neverypar \emptytoks
+
+\appendtoks
+ % \flushnotes
+ \page_otr_command_synchronize_side_floats % an otr specific command
+ \checkindentation
+ \showparagraphnumber
+ \restoreinterlinepenalty
+ % \flushmargincontents
+ % \flushcommentanchors
+ \flushnotes
+ % \synchronizenotes
+ % \OTRSETshowstatus
+ \registerparoptions
+ % \flushsyncpositions
+ \flushpostponednodedata
+ \typo_delimited_repeat
+ \spac_paragraphs_flush_intro
+ \typo_initial_handle
+ \typo_firstline_handle
+ %
+ \spac_paragraph_wrap
+ \spac_paragraph_freeze
+\to \everypar
+
+\appendtoks
+ \flushnotes
+\to \everydisplay
+
+% \appendtoks
+% \adjustsidefloatdisplaylines
+% \to \everydisplay
+
+% \appendtoks
+% \flushpostponednodedata
+% \to \neverypar
+
+% \appendtoks
+% \flushsyncpositions
+% \to \everyheadstart
+
+% \appendtoks
+% \flushsyncresets
+% \to \everyendoftextbody
+
+\appendtoks
+ \ignorespaces
+\to \everybeginofpar
+
+\appendtoks
+ \removeunwantedspaces
+ % \strut % option ?
+ % \flushsyncresets
+ % \setlastlinewidth % gone, will be done in lua
+ \endgraf
+\to \everyendofpar
+
+% initialization order:
+
+\ifdefined\font_preloads_at_every_job \else \let\font_preloads_at_every_job \relax \fi
+\ifdefined\font_preloads_at_start_text \else \let\font_preloads_at_start_text\relax \fi
+\ifdefined\font_preloads_at_stop_text \else \let\font_preloads_at_stop_text \relax \fi
+
+\appendtoks
+ \font_preloads_at_start_text
+\to \everystarttext
+
+\appendtoks
+ \font_preloads_at_stop_text
+\to \everystoptext
+
+% We made \loadoptionfile obsolete: we pass options via the command line to
+% luatex now and handle them directly instead of via a file. This also makes
+% the next obsolete:
+%
+% \directsetup{*runtime:options}
+% \directsetup{*runtime:modules}
+
+\appendtoks
+ \showcontextbanner
+ %\initializenewlinechar
+ \calculatecurrenttime
+ \syst_files_load
+ % for the moment here (before doc env)
+ \setupoutput[pdf]%
+ %
+ \clf_setdocumentcommandline
+ \clf_setdocumentctxfile
+ \clf_setdocumentfilenames
+ \font_preloads_at_every_job
+ \settopskip % brrr
+ \initializemainlanguage
+ \initializepagebackgrounds
+ \initializepagecounters
+ \clf_setdocumentmodes
+ \clf_setdocumentmodules
+ \clf_setdocumentenvironments
+\to \everyjob
+
+\appendtoks
+ \ifarrangingpages\poparrangedpages\fi
+\to \everybye
+
+\prependtoks
+ \resetallattributes
+\to \everybeforeoutput
+
+\appendtoks
+ \the\everybackendshipout
+\to \everyshipout
+
+\prependtoks
+ \the\everylastbackendshipout
+\to \everylastshipout
+
+\prependtoks
+ \lefttoright
+\to \everybeforeoutput
+
+% temporary here:
+
+\pushoverloadmode
+ \frozen\protected\def\arg{\mathortext\normalmatharg\normaltextarg} % might be overloaded
+\popoverloadmode
+
+% might move to \everydump or even disappear:
+
+\nonknuthmode
+
+% brrr
+
+\appendtoks
+ \synchronizegloballinespecs
+ \synchronizelocallinespecs
+\to \everysetupbodyfont
+
+\appendtoks
+ \synchronizelocallinespecs
+\to \everyswitchtobodyfont
+
+% who knows
+
+% \appendtoks
+% \resetcharacterspacing
+% \to \everyhyphenatedurl
+
+% \setbreakpoints[compound]
+
+%D Till we fixed all styles:
+
+\enforced\let\\\crlf % frozen or permanent?
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-env.lmt b/Master/texmf-dist/tex/context/base/mkxl/core-env.lmt
new file mode 100644
index 00000000000..388e96787a0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-env.lmt
@@ -0,0 +1,213 @@
+if not modules then modules = { } end modules ['core-env'] = {
+ version = 1.001,
+ comment = "companion to core-env.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- maybe this will move to the context name space although the
+-- plurals are unlikely to clash with future tex primitives
+--
+-- if tex.modes['xxxx'] then .... else .... end
+
+local rawset = rawset
+
+local P, C, S, lpegmatch, patterns = lpeg.P, lpeg.C, lpeg.S, lpeg.match, lpeg.patterns
+
+local context = context
+local ctxcore = context.core
+
+local texgetcount = tex.getcount
+local texgetintegervalue = token.get_integer -- todo: in tex namespace
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local setmetatablenewindex = table.setmetatablenewindex
+local setmetatablecall = table.setmetatablecall
+
+local createtoken = token.create
+local isdefined = tokens.isdefined
+
+texmodes = allocate { } tex.modes = texmodes
+texsystemmodes = allocate { } tex.systemmodes = texsystemmodes
+texconstants = allocate { } tex.constants = texconstants
+texconditionals = allocate { } tex.conditionals = texconditionals
+texifs = allocate { } tex.ifs = texifs
+texisdefined = allocate { } tex.isdefined = texisdefined
+
+local modes = { }
+local systemmodes = { }
+
+-- we could use the built-in tex.is[count|dimen|skip|toks] here but caching
+-- at the lua end is not that bad (and we need more anyway)
+
+local cache = tokens.cache
+
+-- we can have a modes cache too
+
+local commandcodes = tokens.commands
+local iftrue = cache["iftrue"].index
+
+-- local dimencode = cache["scratchdimen"] .command -- tokens.commands.register_dimen
+-- local countcode = cache["scratchcounter"].command -- tokens.commands.register_int
+-- local tokencode = cache["scratchtoks"] .command -- tokens.commands.register_toks
+-- local skipcode = cache["scratchskip"] .command -- tokens.commands.register_glue
+-- local muskipcode = cache["scratchmuskip"] .command -- tokens.commands.register_mu_glue
+-- local conditioncode = cache["iftrue"] .command -- tokens.commands.if_test
+
+-- local types = {
+-- [dimencode] = "dimen",
+-- [countcode] = "count",
+-- [tokencode] = "token",
+-- [skipcode] = "skip",
+-- [muskipcode] = "muskip",
+-- -- [attributecode] = "attribute",
+-- [conditioncode] = "condition"
+-- }
+
+local dimencode = commandcodes.register_dimen -- cache["scratchdimen"] .command -- tokens.commands.register_dimen
+local countcode = commandcodes.register_int -- cache["scratchcounter"].command -- tokens.commands.register_int
+local tokencode = commandcodes.register_toks -- ["scratchtoks"] .command -- tokens.commands.register_toks
+local skipcode = commandcodes.register_glue -- cache["scratchskip"] .command -- tokens.commands.register_glue
+local muskipcode = commandcodes.register_mu_glue -- cache["scratchmuskip"] .command -- tokens.commands.register_mu_glue
+local conditioncode = commandcodes.if_test -- cache["iftrue"] .command -- tokens.commands.if_test
+local integercode = commandcodes.integer
+
+local types = {
+ [dimencode] = "dimen",
+ [countcode] = "count",
+ [tokencode] = "token",
+ [skipcode] = "skip",
+ [muskipcode] = "muskip",
+ -- [attributecode] = "attribute",
+ [conditioncode] = "condition",
+ [integercode] = "integer",
+}
+
+setmetatableindex(texmodes, function(t,k)
+ local m = systemmodes[k]
+ if not m then
+ local n = "mode>" .. k
+ m = function() return texgetintegervalue(n) == 1 end
+ rawset(modes,k,m)
+ end
+ return m()
+end)
+
+setmetatableindex(texsystemmodes, function(t,k)
+ local m = systemmodes[k]
+ if not m then
+ local n = "mode>*" .. k
+ m = function() return texgetintegervalue(n) == 1 end
+ rawset(modes,k,m)
+ end
+ return m()
+end)
+
+-- also a call method
+
+context.settrialtypesettingmethod(function() return texsystemmodes.trialtypesetting end)
+
+setmetatablenewindex(texmodes, function(t,k) report_mode("you cannot set the %s named %a this way","mode", k) end)
+setmetatablenewindex(texsystemmodes, function(t,k) report_mode("you cannot set the %s named %a this way","systemmode", k) end)
+setmetatablenewindex(texconstants, function(t,k) report_mode("you cannot set the %s named %a this way","constant", k) end)
+setmetatablenewindex(texconditionals, function(t,k) report_mode("you cannot set the %s named %a this way","conditional",k) end)
+setmetatablenewindex(texifs, function(t,k) end)
+
+-- if we really need performance we can have a dedicated cache for each
+-- kind of variable ... maybe we no longer have to cache anyway (in lmtx)
+
+setmetatableindex(texconstants, function(t,k)
+ -- return cache[k].command == countcode and texgetcount(k) or 0
+ return cache[k].command == integercode and texgetintegervalue(k) or 0
+end)
+
+setmetatableindex(texconditionals, function(t,k) -- 0 == true
+ -- return cache[k].command == countcode and texgetcount(k) == 0
+ return cache[k].command == integercode and texgetintegervalue(k) == 0
+end)
+
+setmetatableindex(texifs, function(t,k)
+ local c = cache[k]
+ return c.command == conditioncode and c.index == iftrue
+end)
+
+tex.isdefined = isdefined
+
+function tex.isdimen(name)
+ local hit = cache[name]
+ return hit.command == dimencode and hit.index or true
+end
+
+function tex.iscount(name)
+ local hit = cache[name]
+ return hit.command == countcode and hit.index or true
+end
+
+function tex.istoken(name)
+ local hit = cache[name]
+ return hit.command == tokencode and hit.index or true
+end
+
+function tex.isskip(name)
+ local hit = cache[name]
+ return hit.command == skipcode and hit.index or true
+end
+
+function tex.ismuskip(name)
+ local hit = cache[name]
+ return hit.command == muskipcode and hit.index or true
+end
+
+function tex.type(name)
+ return types[cache[name].command] or "macro"
+end
+
+function context.setconditional(name,value)
+ if value then
+ ctxcore.settruevalue(name)
+ else
+ ctxcore.setfalsevalue(name)
+ end
+end
+
+function context.setmode(name,value)
+ if value then
+ ctxcore.setmode(name)
+ else
+ ctxcore.resetmode(name)
+ end
+end
+
+function context.setsystemmode(name,value)
+ if value then
+ ctxcore.setsystemmode(name)
+ else
+ ctxcore.resetsystemmode(name)
+ end
+end
+
+context.modes = texmodes
+context.systemmodes = texsystemmodes
+context.conditionals = texconditionals
+-------.constants = texconstants
+-------.ifs = texifs
+
+local sep = S("), ")
+local str = C((1-sep)^1)
+local tag = P("(") * C((1-S(")" ))^1) * P(")")
+local arg = P("(") * C((1-S("){"))^1) * P("{") * C((1-P("}"))^0) * P("}") * P(")")
+
+local pattern = (
+ P("lua") * tag / ctxcore.luasetup
+ + P("xml") * arg / ctxcore.setupwithargument -- or xmlw as xmlsetup has swapped arguments
+ + (P("tex") * tag + str) / ctxcore.texsetup
+ + sep^1
+)^1
+
+interfaces.implement {
+ name = "autosetups",
+ actions = function(str) lpegmatch(pattern,str) end,
+ arguments = "string"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-env.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-env.mkxl
new file mode 100644
index 00000000000..62e4106c574
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-env.mkxl
@@ -0,0 +1,807 @@
+%D \module
+%D [ file=core-env, % was core-new
+%D version=1995.01.01, % wrong
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=New ones,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Environments}
+
+\registerctxluafile{core-env}{autosuffix}
+
+\unprotect
+
+%D Modes:
+%D
+%D \starttyping
+%D \enablemode[screen,paper,bound]
+%D
+%D \doifmodeelse {paper} {this} {that}
+%D \doifmode {paper,screen} {this}
+%D \doifnotmode {paper,bound} {that}
+%D
+%D \startmode [list]
+%D \stopmode
+%D
+%D \startnotmode [list]
+%D \stopnotmode
+%D \stoptyping
+%D
+%D system modes have a * as prefix
+%D
+%D Sometimes, we want to prevent a mode for being set. Think of situations where a
+%D style enables a mode, but an outer level style does not want that. Preventing can
+%D be considered a permanent disabling on forehand.
+
+\aliased\let\systemmodeprefix\wildcardsymbol
+
+% we will have \installcorenamespace{mode} but need some hackery at the lua end then
+
+% TODO: make them mutable, also at the lua end
+
+\installcorenamespace{modestack}
+
+\immutable\def\??mode{mode>} % special namespace, also used at lua end
+
+\permanent\protected\def\newmode #1{\ifcsname \??mode#1\endcsname\else\resetmode{#1}\fi}
+\permanent\protected\def\setmode #1{\expandafter \integerdef\csname\??mode#1\endcsname\plusone}
+\permanent\protected\def\resetmode #1{\expandafter \integerdef\csname\??mode#1\endcsname\zerocount}
+\permanent\protected\def\globalsetmode #1{\global\expandafter\integerdef\csname\??mode#1\endcsname\plusone}
+\permanent\protected\def\globalresetmode #1{\global\expandafter\integerdef\csname\??mode#1\endcsname\zerocount}
+
+\permanent\protected\def\newsystemmode #1{\ifcsname \??mode\systemmodeprefix#1\endcsname\else\resetsystemmode{#1}\fi}
+\permanent\protected\def\setsystemmode #1{\expandafter \integerdef\csname\??mode\systemmodeprefix#1\endcsname\plusone}
+\permanent\protected\def\resetsystemmode #1{\expandafter \integerdef\csname\??mode\systemmodeprefix#1\endcsname\zerocount}
+\permanent\protected\def\globalsetsystemmode #1{\global\expandafter\integerdef\csname\??mode\systemmodeprefix#1\endcsname\plusone}
+\permanent\protected\def\globalresetsystemmode#1{\global\expandafter\integerdef\csname\??mode\systemmodeprefix#1\endcsname\zerocount}
+
+\expandafter \appendtoks
+ \expandafter\integerdef\csname\??mode\systemmodeprefix trialtypesetting\endcsname\plusone
+ \luacopyinputnodes\plusone
+\to \everysettrialtypesetting
+
+\expandafter \appendtoks
+ \expandafter\integerdef\csname\??mode\systemmodeprefix trialtypesetting\endcsname\zerocount
+ \luacopyinputnodes\zerocount
+\to \everyresettrialtypesetting
+
+\pushoverloadmode \newsystemmode{trialtypesetting} \popoverloadmode
+
+\the\everyresettrialtypesetting
+
+% user ones
+
+\mutable\let\syst_mode_prefix\relax
+
+\permanent\protected\def\preventmode{\unprotect\syst_modes_prevent}
+\permanent\protected\def\enablemode {\unprotect\syst_modes_enable }
+\permanent\protected\def\disablemode{\unprotect\syst_modes_disable}
+
+\permanent\protected\def\globalpreventmode{\let\syst_mode_prefix\global\unprotect\syst_modes_prevent}
+\permanent\protected\def\globalenablemode {\let\syst_mode_prefix\global\unprotect\syst_modes_enable }
+\permanent\protected\def\globaldisablemode{\let\syst_mode_prefix\global\unprotect\syst_modes_disable}
+
+\def\syst_modes_prevent[#1]{\protect\fastprocesscommacommand[#1]\syst_modes_prevent_indeed\let\syst_mode_prefix\relax}
+\def\syst_modes_enable [#1]{\protect\fastprocesscommacommand[#1]\syst_modes_enable_indeed \let\syst_mode_prefix\relax}
+\def\syst_modes_disable[#1]{\protect\fastprocesscommacommand[#1]\syst_modes_disable_indeed\let\syst_mode_prefix\relax}
+
+\def\syst_modes_prevent_indeed#1%
+ {\ifcsname\??mode#1\endcsname\else\setmode{#1}\fi
+ \syst_mode_prefix\expandafter\integerdef\lastnamedcs\plustwo}
+
+\def\syst_modes_enable_indeed#1% we can speed it up by moving the new outside
+ {\ifcsname\??mode#1\endcsname\else\setmode{#1}\fi
+ \ifnum\lastnamedcs=\plustwo\else
+ \syst_mode_prefix\expandafter\integerdef\lastnamedcs\plusone
+ \fi}
+
+\def\syst_modes_disable_indeed#1%
+ {\ifcsname\??mode#1\endcsname\else\setmode{#1}\fi
+ \ifnum\lastnamedcs=\plustwo\else
+ \syst_mode_prefix\expandafter\integerdef\lastnamedcs\zerocount
+ \fi}
+
+%D If you do a lot of mode testing, it makes sense to define modes (or disable them
+%D explicitly if unset. This makes testing twice as fast. Often one enables modes
+%D beforehand, in which case \type {\definemode} would reset the mode. The optional
+%D second argument \type {keep} will prevent changing the already set mode but defines
+%D it when undefined.
+
+\permanent\protected\def\definemode
+ {\unprotect
+ \syst_modes_define}
+
+\tolerant\def\syst_modes_define[#1]#*[#2]%
+ {\protect
+ \edef\m_modes_asked{#2}%
+ \fastprocesscommacommand[#1]\syst_modes_define_indeed}
+
+\def\syst_modes_define_indeed#1%
+ {\ifcsname\??mode#1\endcsname
+ % already set
+ \else
+ \newmode{#1}%
+ \fi
+ \ifx\m_modes_asked\v!keep
+ % not changes, disabled when undefined
+ \orelse\ifx\m_modes_asked\v!yes
+ \setmode{#1}%
+ \else
+ \resetmode{#1}%
+ \fi}
+
+% handy for mp
+
+\permanent\def\booleanmodevalue#1%
+ {\ifcsname\??mode#1\endcsname
+ \ifcase\lastnamedcs
+ \s!false
+ \or
+ \s!true
+ \else
+ \s!false
+ \fi
+ \else
+ \s!false
+ \fi}
+
+% check macros
+
+\newconditional\c_checked_mode
+
+% one
+
+\def\syst_modes_check_indeed#1%
+ {\ifcsname\??mode#1\endcsname
+ \ifcase\lastnamedcs\else
+ \let\syst_modes_check_step\gobbleoneargument
+ \fi
+ \fi}
+
+\def\syst_modes_check_nop#1#2#3%
+ {\let\syst_modes_check_step\syst_modes_check_indeed
+ \fastprocesscommacommand[#3]\syst_modes_check_step
+ \ifx\syst_modes_check_step\gobbleoneargument
+ \expandafter#1%
+ \else
+ \expandafter#2%
+ \fi}
+
+\def\syst_modes_check_yes#1#2#3%
+ {\ifcase\lastnamedcs
+ \expandafter#2%
+ \or
+ \expandafter#1%
+ \else
+ \expandafter#2%
+ \fi}
+
+\def\syst_modes_check_lr#1#2#3%
+ {\ifcsname\??mode#3\endcsname
+ \expandafter\syst_modes_check_yes
+ \else
+ \expandafter\syst_modes_check_nop
+ \fi#1#2{#3}}
+
+\def\syst_modes_check_ss#1#2[#3]%
+ {\ifcsname\??mode#3\endcsname
+ \expandafter\syst_modes_check_yes
+ \else
+ \expandafter\syst_modes_check_nop
+ \fi#1#2{#3}}
+
+% all
+
+\let\syst_modes_check_all_step\gobbleoneargument
+
+\def\syst_modes_check_all_indeed#1%
+ {\ifcsname\??mode#1\endcsname
+ \ifcase\lastnamedcs
+ \let\syst_modes_check_all_step\gobbleoneargument
+ \or
+ % enabled
+ \else
+ \let\syst_modes_check_all_step\gobbleoneargument
+ \fi
+ \else
+ \let\syst_modes_check_all_step\gobbleoneargument
+ \fi}
+
+\def\syst_modes_check_all_lr#1#2#3%
+ {\let\syst_modes_check_all_step\syst_modes_check_all_indeed
+ \fastprocesscommacommand[#3]\syst_modes_check_all_step
+ \ifx\syst_modes_check_all_step\gobbleoneargument
+ \expandafter#2%
+ \else
+ \expandafter#1%
+ \fi}
+
+\def\syst_modes_check_all_ss#1#2[#3]%
+ {\let\syst_modes_check_all_step\syst_modes_check_all_indeed
+ \fastprocesscommacommand[#3]\syst_modes_check_all_step
+ \ifx\syst_modes_check_all_step\gobbleoneargument
+ \expandafter#2%
+ \else
+ \expandafter#1%
+ \fi}
+
+\permanent\protected\def\doifelsemode {\syst_modes_check_lr \firstoftwoarguments \secondoftwoarguments}
+\permanent\protected\def\doifmode {\syst_modes_check_lr \firstofoneargument \gobbleoneargument}
+\permanent\protected\def\doifnotmode {\syst_modes_check_lr \gobbleoneargument \firstofoneargument}
+\permanent\protected\def\startmode {\syst_modes_check_ss \donothing \syst_modes_stop_yes}
+\permanent\protected\def\startnotmode {\syst_modes_check_ss \syst_modes_stop_nop \donothing}
+\permanent\protected\def\doifelseallmodes{\syst_modes_check_all_lr\firstoftwoarguments \secondoftwoarguments}
+\permanent\protected\def\doifallmodes {\syst_modes_check_all_lr\firstofoneargument \gobbleoneargument}
+\permanent\protected\def\doifnotallmodes {\syst_modes_check_all_lr\gobbleoneargument \firstofoneargument}
+\permanent\protected\def\startallmodes {\syst_modes_check_all_ss\donothing \syst_modes_stop_all_yes}
+\permanent\protected\def\startnotallmodes{\syst_modes_check_all_ss\syst_modes_stop_all_nop\donothing}
+
+\aliased\let\doifmodeelse \doifelsemode
+\aliased\let\doifallmodeselse\doifelseallmodes
+
+\permanent\protected\def\stopmode {} % no relax
+\permanent\protected\def\stopnotmode {} % no relax
+\permanent\protected\def\stopallmodes {} % no relax
+\permanent\protected\def\stopnotallmodes{} % no relax
+
+\def\syst_modes_stop_yes #1\stopmode {}
+\def\syst_modes_stop_nop #1\stopnotmode {}
+\def\syst_modes_stop_all_yes#1\stopallmodes {}
+\def\syst_modes_stop_all_nop#1\stopnotallmodes{}
+
+%D Pushing/popping:
+
+\permanent\protected\def\pushmode[#1]{\expandafter\pushmacro\csname\??mode#1\endcsname}
+\permanent\protected\def\popmode [#1]{\expandafter\popmacro \csname\??mode#1\endcsname}
+
+\permanent\protected\def\pushsystemmode#1{\expandafter\pushmacro\csname\??mode\systemmodeprefix#1\endcsname}
+\permanent\protected\def\popsystemmode #1{\expandafter\popmacro \csname\??mode\systemmodeprefix#1\endcsname}
+
+%D Here is a relatively new variant of mode checking:
+%D
+%D \starttyping
+%D \enablemode[two]
+%D
+%D \startmodeset
+%D [one] {1}
+%D [two] {2}
+%D [two] {2}
+%D [three] {3}
+%D [default] {?}
+%D \stopmodeset
+%D
+%D \startmodeset
+%D [one] {1}
+%D [three] {3}
+%D [default] {?}
+%D \stopmodeset
+%D
+%D \startmodeset
+%D [one] {
+%D \input tufte
+%D }
+%D [two] {
+%D \startmodeset
+%D [one] {1}
+%D [two] {2}
+%D [two] {2}
+%D [three] {3}
+%D [default] {?}
+%D \stopmodeset
+%D }
+%D [three] {
+%D \input zapf
+%D }
+%D [default] {
+%D \input ward
+%D }
+%D \stopmodeset
+%D \stoptyping
+
+\newconditional\c_syst_modes_set_done % conditionals can be pushed/popped
+
+\installmacrostack\c_syst_modes_set_done
+
+% \permanent\protected\def\startmodeset
+% {\push_macro_c_syst_modes_set_done
+% \setfalse\c_syst_modes_set_done
+% \doifelsenextoptionalcs\syst_modes_set_start\syst_modes_set_quit}
+%
+% \aliased\let\stopmodeset\relax
+%
+% \def\syst_modes_set_start[#1]%
+% {\edef\m_mode_case{#1}%
+% \unless\ifx\m_mode_case\s!default
+% \expandafter\syst_modes_set_check
+% \orelse\ifconditional\c_syst_modes_set_done
+% \expandafter\syst_modes_set_quit
+% \else
+% \expandafter\syst_modes_set_yes
+% \fi}
+%
+% \def\syst_modes_set_check
+% {\syst_modes_check_lr\syst_modes_set_yes\syst_modes_set_nop\m_mode_case}
+%
+% \def\syst_modes_set_yes#1%
+% {\settrue\c_syst_modes_set_done
+% #1%
+% \doifelsenextoptionalcs\syst_modes_set_start\syst_modes_set_quit}
+%
+% \def\syst_modes_set_nop#1%
+% {\doifelsenextoptionalcs\syst_modes_set_start\syst_modes_set_quit}
+%
+% \def\syst_modes_set_quit#-\stopmodeset
+% {\pop_macro_c_syst_modes_set_done}
+
+\permanent\protected\def\startmodeset
+ {\push_macro_c_syst_modes_set_done
+ \setfalse\c_syst_modes_set_done
+ \syst_modes_set_start}
+
+\aliased\let\stopmodeset\relax
+
+\tolerant\def\syst_modes_set_start#.[#1]% skip spaces and pars
+ {\edef\m_mode_case{#1}%
+ \ifempty\m_mode_case
+ \expandafter\syst_modes_set_quit
+ \orunless\ifx\m_mode_case\s!default
+ \expandafter\syst_modes_set_check
+ \orelse\ifconditional\c_syst_modes_set_done
+ \expandafter\syst_modes_set_quit
+ \else
+ \expandafter\syst_modes_set_yes
+ \fi}
+
+\def\syst_modes_set_check
+ {\syst_modes_check_lr\syst_modes_set_yes\syst_modes_set_nop\m_mode_case}
+
+\tolerant\def\syst_modes_set_yes#1%
+ {\settrue\c_syst_modes_set_done
+ #1%
+ \syst_modes_set_start}
+
+\tolerant\def\syst_modes_set_nop#1%
+ {\syst_modes_set_start}
+
+\def\syst_modes_set_quit#-\stopmodeset
+ {\pop_macro_c_syst_modes_set_done}
+
+%D Lets now set a mode:
+
+\enablemode[mkiv] \newsystemmode{mkiv} \setsystemmode{mkiv}
+
+%D Setups:
+
+\installcorenamespace{setup} % we can probably get rid of some :'s later on
+
+% \protected\def\startsetups{} % to please dep checker
+% \protected\def\stopsetups {} % to please dep checker
+%
+% \protected\def\setups{\doifelsenextbgroup\syst_setups_a\syst_setups_b} % {..} or [..]
+% \protected\def\setup {\doifelsenextbgroup\syst_setups \syst_setups_c} % {..} or [..]
+%
+% \def\syst_setups_a #1{\processcommacommand[#1]\syst_setups} % {..}
+% \def\syst_setups_b[#1]{\processcommacommand[#1]\syst_setups} % [..]
+% \def\syst_setups_c[#1]{\syst_setups{#1}} % [..]
+
+\permanent\protected\tolerant\def\setups[#1]#;#2{\processcommacommand[#1#2]\syst_setups} % {..} or [..]
+\permanent\protected\tolerant\def\setup [#1]#;#2{\syst_setups{#1#2}} % {..} or [..]
+
+\letvalue{\??setup:\??empty}\gobbleoneargument
+
+\permanent\def\syst_setups#1% the grid option will be extended to other main modes
+ {\csname\??setup
+ \ifgridsnapping
+ \ifcsname\??setup\v!grid:#1\endcsname\v!grid:#1\else:\ifcsname\??setup:#1\endcsname#1\else\??empty\fi\fi
+ \else
+ :\ifcsname\??setup:#1\endcsname#1\else\??empty\fi
+ \fi
+ \endcsname\empty} % takes one argument
+
+% no checking and we assume it being defined:
+
+\permanent\def\fastsetup #1{\csname\??setup:#1\endcsname\empty}
+\permanent\def\fastsetupwithargument #1{\csname\??setup:#1\endcsname} % swapped per 2015-08-30
+\permanent\def\fastsetupwithargumentswapped#1#2{\csname\??setup:#2\endcsname{#1}}% swapped per 2015-09-05
+
+% the next one is meant for \c!setups situations, hence the check for
+% a shortcut
+
+\let\m_syst_setups_asked\empty
+
+\permanent\protected\def\doprocesslocalsetups#1% sort of public, fast local variant
+ {\edef\m_syst_setups_asked{#1}%
+ \ifempty\m_syst_setups_asked\else
+ \expandafter\syst_setups_process_local
+ \fi}
+
+\permanent\protected\def\usesetupsparameter#1%
+ {\edef\m_syst_setups_asked{#1\c!setups}%
+ \ifempty\m_syst_setups_asked\else
+ \expandafter\syst_setups_process_local
+ \fi}
+
+% setups=S1
+% setups=lua(S2)
+% setups=S3
+% setups={S1,lua(S2),xml(test{123}),S3}
+
+\def\syst_setups_process_local
+ {\clf_autosetups{\m_syst_setups_asked}%
+ \relax} % let's prevent lookahead
+
+\permanent\def\autosetups#1{\clf_autosetups{#1}} % todo: public implementor
+
+\permanent\edef\setupwithargument#1% saves a few expansions
+ {\noexpand\csname\??setup:\noexpand\ifcsname\??setup:#1\endcsname#1\noexpand\else\??empty\noexpand\fi\endcsname}
+
+\permanent\edef\setupwithargumentswapped#1#2% saves a few expansions (can be \let)
+ {\noexpand\csname\??setup:\noexpand\ifcsname\??setup:#2\endcsname#2\noexpand\else\??empty\noexpand\fi\endcsname{#1}}
+
+\aliased\let\directsetup\syst_setups
+\aliased\let\texsetup \syst_setups % nicer than \directsetup and more en par with xmlsetup and luasetup
+
+\permanent\protected\def\doifelsesetups#1% to be done: grid
+ {\ifcsname\??setup:#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifsetupselse\doifelsesetups
+
+\permanent\protected\def\doifsetups#1% to be done: grid
+ {\ifcsname\??setup:#1\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotsetups#1% to be done: grid
+ {\ifcsname\??setup:#1\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+% maybe some day:
+%
+% \protected\def\fastsetupfallback#1#2%
+% {\ifcsname\??setup:#1\endcsname
+% \expandafter\lastnamedcs
+% \else
+% \csname\??setup:#2\expandafter\endcsname
+% \fi}
+%
+% or
+%
+% \protected\def\fastsetupfallback#1#2#3% prefix preferred fallback
+% {\ifcsname\??setup:#1#2\endcsname
+% \expandafter\lastnamedcs
+% \else
+% \csname\??setup:#1#3\expandafter\endcsname
+% \fi}
+
+% \startluasetups oeps
+% context("DONE")
+% a = 1
+% b = 1
+% \stopluasetups
+%
+% \luasetup{oeps}
+%
+% \startsetups xxx
+% ziezo
+% \stopsetups
+%
+% \directsetup{xxx}
+%
+% \startxmlsetups zzz
+% [[#1]]
+% \stopxmlsetups
+%
+% \xmlsetup{123}{zzz}
+%
+% \startbuffer[what]
+% context("DONE")
+% \stopbuffer
+%
+% \startbuffer
+% context("MORE")
+% \stopbuffer
+%
+% \ctxluabuffer[what]
+%
+% \ctxluabuffer
+
+\newtoks\t_syst_setups_tex \appendtoks
+ \catcode\endoflineasciicode \ignorecatcode
+\to \t_syst_setups_tex
+
+\newtoks\t_syst_setups_loc \appendtoks
+ \catcode\endoflineasciicode \ignorecatcode
+\to \t_syst_setups_loc
+
+\newtoks\t_syst_setups_raw \appendtoks
+ % nothing
+\to \t_syst_setups_raw
+
+\newtoks\t_syst_setups_xml \appendtoks
+ \catcode\endoflineasciicode\ignorecatcode
+ \catcode\barasciicode \othercatcode
+\to \t_syst_setups_xml
+
+\newtoks\t_syst_setups_lua \appendtoks
+ \obeylualines
+ \obeyluatokens
+\to \t_syst_setups_lua
+
+% Is doglobal still relevant? Maybe always global? Or never? Anyway, it will become obsolete.
+
+\permanent\protected\def\startluasetups {\begingroup\doifelsenextoptionalcs\syst_setups_start_lua_yes\syst_setups_start_lua_nop} \aliased\let\stopluasetups \relax
+\permanent\protected\def\startxmlsetups {\begingroup\doifelsenextoptionalcs\syst_setups_start_xml_yes\syst_setups_start_xml_nop} \aliased\let\stopxmlsetups \relax
+\permanent\protected\def\startrawsetups {\begingroup\doifelsenextoptionalcs\syst_setups_start_raw_yes\syst_setups_start_raw_nop} \aliased\let\stoprawsetups \relax
+\permanent\protected\def\startlocalsetups{\begingroup\doifelsenextoptionalcs\syst_setups_start_loc_yes\syst_setups_start_loc_nop} \aliased\let\stoplocalsetups\relax
+\permanent\protected\def\startsetups {\begingroup\doifelsenextoptionalcs\syst_setups_start_tex_yes\syst_setups_start_tex_nop} \aliased\let\stopsetups \relax
+
+\def\syst_setups_start_lua_two#1#2#+\stopluasetups {\endgroup\dodoglobal\instance\defcsname\??setup#1:#2\endcsname##1{#3}}
+\def\syst_setups_start_xml_two#1#2#+\stopxmlsetups {\endgroup\dodoglobal\instance\defcsname\??setup#1:#2\endcsname##1{#3}}
+\def\syst_setups_start_raw_two#1#2#+\stoprawsetups {\endgroup\dodoglobal\instance\defcsname\??setup#1:#2\endcsname##1{#3}}
+\def\syst_setups_start_loc_two#1#2#+\stoplocalsetups{\endgroup\dodoglobal\instance\defcsname\??setup#1:#2\endcsname##1{#3}}
+\def\syst_setups_start_tex_two#1#2#+\stopsetups {\endgroup\dodoglobal\instance\defcsname\??setup#1:#2\endcsname##1{#3}}
+
+\tolerant\def\syst_setups_start_lua_yes[#1]#*[#2]%
+ {\the\t_syst_setups_lua
+ \ifarguments\expandafter\syst_setups_start_lua_one
+ \or \expandafter\syst_setups_start_lua_one
+ \else \expandafter\syst_setups_start_lua_two
+ \fi{#1}{#2}}
+
+\tolerant\def\syst_setups_start_xml_yes[#1]#*[#2]%
+ {\the\t_syst_setups_xml
+ \ifarguments\expandafter\syst_setups_start_xml_one
+ \or \expandafter\syst_setups_start_xml_one
+ \else \expandafter\syst_setups_start_xml_two
+ \fi{#1}{#2}}
+
+\tolerant\def\syst_setups_start_raw_yes[#1]#*[#2]%
+ {\the\t_syst_setups_raw
+ \ifarguments\expandafter\syst_setups_start_raw_one
+ \or \expandafter\syst_setups_start_raw_one
+ \else \expandafter\syst_setups_start_raw_two
+ \fi{#1}{#2}}
+
+\tolerant\def\syst_setups_start_loc_yes[#1]#*[#2]%
+ {\the\t_syst_setups_loc
+ \ifarguments\expandafter\syst_setups_start_loc_one
+ \or \expandafter\syst_setups_start_loc_one
+ \else \expandafter\syst_setups_start_loc_two
+ \fi{#1}{#2}}
+
+\tolerant\def\syst_setups_start_tex_yes[#1]#*[#2]%
+ {\the\t_syst_setups_tex
+ \ifarguments\expandafter\syst_setups_start_tex_one
+ \or \expandafter\syst_setups_start_tex_one
+ \else \expandafter\syst_setups_start_tex_two
+ \fi{#1}{#2}}
+
+\def\syst_setups_start_lua_nop#1 {\the\t_syst_setups_lua\syst_setups_start_lua_two{}{#1}} % space delimited
+\def\syst_setups_start_xml_nop#1 {\the\t_syst_setups_xml\syst_setups_start_xml_two{}{#1}} % space delimited
+\def\syst_setups_start_raw_nop#1 {\the\t_syst_setups_raw\syst_setups_start_raw_two{}{#1}} % space delimited
+\def\syst_setups_start_loc_nop#1 {\the\t_syst_setups_loc\syst_setups_start_loc_two{}{#1}} % space delimited
+\def\syst_setups_start_tex_nop#1 {\the\t_syst_setups_tex\syst_setups_start_tex_two{}{#1}} % space delimited
+
+\def\syst_setups_start_lua_one#1#2{\syst_setups_start_lua_two{}{#1}} % [..]
+\def\syst_setups_start_xml_one#1#2{\syst_setups_start_xml_two{}{#1}} % [..]
+\def\syst_setups_start_raw_one#1#2{\syst_setups_start_raw_two{}{#1}} % [..]
+\def\syst_setups_start_loc_one#1#2{\syst_setups_start_loc_two{}{#1}} % [..]
+\def\syst_setups_start_tex_one#1#2{\syst_setups_start_tex_two{}{#1}} % [..]
+
+\permanent\def\luasetup#1{\ctxlua{\syst_setups{#1}}}
+
+%D System setups:
+
+\aliased\let\systemsetupsprefix\wildcardsymbol
+
+\permanent\def\systemsetups#1{\syst_setups{\systemsetupsprefix#1}}
+
+\permanent\protected\def\resetsetups[#1]% see x-fo for usage
+ {\ifcsname\??setup\ifgridsnapping\v!grid\fi:#1\endcsname
+ \dodoglobal\undefinevalue{\??setup\ifgridsnapping\v!grid\fi:#1}%
+ \else
+ \dodoglobal\undefinevalue{\??setup:#1}%
+ \fi}
+
+\permanent\tolerant\protected\def\copysetups[#1]#*[#2]%
+ {\ifcsname\??setup:#2\endcsname
+ \letcsname\??setup:#1\expandafter\endcsname\lastnamedcs
+ \fi}
+
+\permanent\protected\def\showsetupsdefinition[#1]%
+ {\showvalue{\??setup:#1}} % temp hack for debugging
+
+%D \macros
+%D {setvariables,getvariable,getvariabledefault}
+%D
+%D \starttyping
+%D \setvariables[xx][title=]
+%D \setvariables[xx][title=test test]
+%D \setvariables[xx][title=test $x=1$ test] % fatal error reported
+%D \setvariables[xx][title=test {$x=1$} test]
+%D \setvariables[xx][title] % fatal error reported
+%D \setvariables[xx][titletitel=e]
+%D \stoptyping
+
+\installcorenamespace{variables}
+
+\permanent\protected\def\setvariables {\syst_variables_set[\getrawparameters ]}
+\permanent\protected\def\setevariables{\syst_variables_set[\getraweparameters]}
+\permanent\protected\def\setgvariables{\syst_variables_set[\getrawgparameters]}
+\permanent\protected\def\setxvariables{\syst_variables_set[\getrawxparameters]}
+
+\mutable\let\currentvariableclass\empty
+
+\tolerant\def\syst_variables_set[#1]#*[#2]#*[#3]% tricky, test on s-pre-60
+ {\doifelse{#2}\currentvariableclass
+ {#1[\??variables#2:][#3]}%
+ {\pushmacro\currentvariableclass
+ \def\currentvariableclass{#2}%
+ \getvariable{#2}\s!reset
+ #1[\??variables#2:][#3]%
+ \getvariable{#2}\s!set
+ \popmacro\currentvariableclass}}
+
+\permanent\protected\def\setvariable #1#2#3{\defcsname \??variables#1:#2\endcsname{#3}}
+\permanent\protected\def\setevariable#1#2#3{\edefcsname\??variables#1:#2\endcsname{#3}}
+\permanent\protected\def\setgvariable#1#2#3{\defcsname \??variables#1:#2\endcsname{#3}}
+\permanent\protected\def\setxvariable#1#2#3{\xdefcsname\??variables#1:#2\endcsname{#3}}
+
+\permanent \def\getvariable #1#2{\begincsname\??variables#1:#2\endcsname}
+\permanent\protected\def\showvariable#1#2{\showvalue{\begincsname\??variables#1:#2\endcsname}}
+
+\let\currentvariableclass\empty
+
+%D \macros
+%D {checkvariables}
+%D
+%D I'll probably forget that this on exists.
+
+\let\m_syst_variables_temp\empty
+
+\permanent\tolerant\def\checkvariables[#1]#*[#2]%
+ {\dogetparameters\syst_variables_check_value[#1][#2]}
+
+\def\syst_variables_check_value#1#2#3%
+ {\ifcsname\??variables#1:#2\endcsname
+ \edef\m_syst_variables_temp{\lastnamedcs}%
+ \ifempty\m_syst_variables_temp
+ \defcsname\??variables#1:#2\endcsname{#3}%
+ \fi
+ \else
+ \defcsname\??variables#1:#2\endcsname{#3}%
+ \fi}
+
+%D \macros
+%D {doifelsevariable,doifvariable,doifnotvariable}
+%D
+%D A few trivial macros:
+
+\permanent\protected\def\doifelsevariable#1#2%
+ {\ifcsname\??variables#1:#2\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifvariableelse\doifelsevariable
+
+\permanent\protected\def\doifvariable#1#2%
+ {\ifcsname\??variables#1:#2\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotvariable#1#2%
+ {\ifcsname\??variables#1:#2\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D A few more (we could use a public test variable so that we only need to expand
+%D once, assuming expandable variables):
+
+\letvalue{\??variables:}\empty
+
+\permanent\protected\def\doifelseemptyvariable#1#2%
+ {\edef\m_syst_string_one{\begincsname\??variables#1:#2\endcsname}%
+ \ifempty\m_syst_string_one
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifemptyvariableelse\doifelseemptyvariable
+
+\permanent\protected\def\doifemptyvariable#1#2%
+ {\edef\m_syst_string_one{\begincsname\??variables#1:#2\endcsname}%
+ \ifempty\m_syst_string_one
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotemptyvariable#1#2%
+ {\edef\m_syst_string_one{\begincsname\??variables#1:#2\endcsname}%
+ \ifempty\m_syst_string_one
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\def\getvariabledefault#1#2% #3% can be command
+ {\ifcsname\??variables#1:#2\endcsname
+ \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\tolerant\protected\def\setupenv[#1]%
+ {\syst_variables_set[\getrawparameters][\s!environment][#1]}
+
+\permanent\protected\def\doifelseenv{\doifelsevariable \s!environment}
+\permanent\protected\def\doifenv {\doifvariable \s!environment}
+\permanent\protected\def\doifnotenv {\doifnotvariable \s!environment}
+\permanent \def\env {\getvariable \s!environment}
+\permanent \def\envvar {\getvariabledefault\s!environment}
+
+\aliased\let\doifenvelse\doifelseenv
+
+%D \macros
+%D {defineselector,setupselector,select,directselect}
+%D
+%D \starttyping
+%D \defineselector[caption][max=2,n=2]
+%D
+%D \start
+%D \setupselector[caption][n=1]
+%D \placelist[figure][criterium=all]
+%D \stop
+%D
+%D \starttext
+%D \placefigure
+%D {\select{caption}{zapf}{\input zapf \relax}}
+%D {}
+%D \stoptext
+%D \stoptyping
+
+\installcorenamespace{selector}
+
+\permanent\tolerant\protected\def\defineselector[#1]#*[#2]{\getparameters[\??selector#1][\c!max=\plusone,\c!n=\plusone,#2]}
+\permanent\tolerant\protected\def\setupselector [#1]#*[#2]{\getparameters[\??selector#1][#2]}
+
+% \def\directselect#1% expandable
+% {\filterfromnext % maybe add an \expanded
+% {\csname\??selector\ifcsname\??selector#1\c!max\endcsname#1\fi\c!max\endcsname}%
+% {\csname\??selector\ifcsname\??selector#1\c!n \endcsname#1\fi\c!n \endcsname}}
+%
+% this is more efficient when the arguments are used a few times (or passed along):
+
+\permanent\def\directselect#1% expandable
+ {\expandafter\filterfromnext
+ \csname\??selector\ifcsname\??selector#1\c!max\endcsname#1\fi\c!max\expandafter\endcsname
+ \csname\??selector\ifcsname\??selector#1\c!n \endcsname#1\fi\c!n \endcsname}
+
+\permanent\protected\let\select\directselect % we promote to protected
+
+\letcsname\??selector\c!max\endcsname\plusone
+\letcsname\??selector\c!n \endcsname\plusone
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-ini.mkxl
new file mode 100644
index 00000000000..de7c645b425
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-ini.mkxl
@@ -0,0 +1,224 @@
+%D \module
+%D [ file=core-ini,
+%D version=2003.12.01,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Additional Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Additional Initialization}
+
+\unprotect
+
+%D We introduce a couple of variables that are used all over \CONTEXT. Alternatively
+%D we could define them in each module but as they are part of the bigger picture we
+%D prefer to do it here. Ideally we should hav ea proper dependency tree but it might
+%D be that we want to make versions with a smaller footprints in which case one would
+%D still need to define the token list registers (unless we could do that runtime).
+
+%D \macros
+%D {every...}
+%D
+%D A few every's.
+
+%D Output routine:
+
+\newtoks \everybeforeoutput
+\newtoks \everyafteroutput
+
+%D Shipout:
+
+\newtoks \everyshipout
+\newtoks \everybeforeshipout
+\newtoks \everyaftershipout
+\newtoks \everyfirstshipout
+\newtoks \everylastshipout
+
+%D End of run:
+
+\newtoks \everybye
+\newtoks \everygoodbye
+\newtoks \everynotabene
+
+%D Document:
+
+\newtoks \everyendoftextbody
+
+\newtoks \everystarttext
+\newtoks \everystoptext
+
+\newtoks \everystartdocument
+\newtoks \everystopdocument
+
+%D Purity:
+
+\newtoks \everyforgetall
+\newtoks \everycleanupfeatures
+\newtoks \everysimplifycommands
+%newtoks \everypreroll
+
+\aliased\let\simplifiedcommands\everysimplifycommands % backward compatible, will stay as it's used in styles
+
+\newconditional\simplifyingcommands % public
+
+\permanent\protected\def\forgetall {\the\everyforgetall}
+\permanent\protected\def\cleanupfeatures {\the\everycleanupfeatures}
+\permanent\protected\def\simplifycommands{\the\everysimplifycommands}
+
+\appendtoks
+ \settrue\simplifyingcommands
+\to \everysimplifycommands
+
+\appendtoks
+ \everypar\emptytoks % pretty important
+\to \everyforgetall
+
+%D Page building:
+
+\newtoks \everybeforepagebody
+\newtoks \everyafterpagebody
+
+\aliased\let\everypagebody\everybeforepagebody % backward compatible, will become obsolete
+
+%D Floats:
+
+\newtoks \everyinsidefloat
+
+%D Exporting:
+
+\newtoks \everyinitializeexport
+
+%D Sectioning:
+
+%newtoks \everyheadstart
+
+%D Par building (experimental, used in xml <p> .. </p>)
+
+\newtoks \everybeginofpar
+\newtoks \everyendofpar
+%newtoks \everyparflush
+
+\protected\def\bpar{\the\everybeginofpar\ignorespaces} % may interfere with \everypar
+\protected\def\epar{\ifhmode\removeunwantedspaces\the\everyendofpar\fi} % test prevents problems with \bpar\epar
+
+%D Lists:
+
+\newtoks \everylistentry
+\newtoks \everysavesortkeys
+
+%D Marks:
+
+%newtoks \everymarking
+
+%D Fonts:
+
+\newtoks \everyfont
+\newtoks \everyglobalbodyfont
+\newtoks \everydefinedfont
+
+\newtoks \everybodyfont
+\newtoks \everyfontswitch
+
+\newtoks \everysetupbodyfont
+\newtoks \everyswitchtobodyfont
+
+%D Math:
+
+\newtoks \everybeforedisplayformula
+\newtoks \everymathematics
+
+\prependtoks \the\everymathematics \to \everymath
+\prependtoks \the\everymathematics \to \everydisplay
+
+%D Tables:
+
+%newtoks \everytable % we need to disstinguish kinds
+
+%D More generic (used to be pushcolor etc)
+
+\newtoks\everystarttextproperties
+\newtoks\everystoptextproperties
+
+\permanent\protected\def\starttextproperties{\the\everystarttextproperties}
+\permanent\protected\def\stoptextproperties {\the\everystoptextproperties}
+
+%D \macros
+%D {trialtypesetting}
+%D
+%D We disable trial typesetting in the output routine,
+%D just to be sure.
+
+\prependtoks
+ \resettrialtypesetting
+\to \everybeforepagebody
+
+%D \macros
+%D {ifinpagebody,ifinsidecolumns,ifdoublesided,ifsinglesided}
+%D
+%D These will become system modes and conditionals
+
+\newif \ifinpagebody
+\newif \ifinsidecolumns
+\newif \ifinsidemulticolumns % simple mixed-in-text columns
+\newif \ifdoublesided \doublesidedfalse
+\newif \ifsinglesided \singlesidedtrue
+\newif \ifinsidefloat
+\newif \ifdoingblocks
+\newif \ifgridsnapping
+\newif \ifexporting
+
+\newconstant\pageduplexmode % 0 single 1 double 2 mix
+\newconstant\pagebodymode % 0 not 1 normal pagebody 2 spread
+
+\newcount\nofcolumns \nofcolumns \plusone
+\newcount\nofmulticolumns \nofmulticolumns\plusone
+
+%D \macros
+%D {ifproductionrun}
+%D
+%D This boolean can be used to bypass certain initializations.
+
+% \newif\ifproductionrun % already defined
+
+\appendtoks
+ \productionruntrue
+\to \everydump
+
+%D \macros
+%D {everyboxedcontent, ifboxedcontent,
+%D startboxedcontent, stopboxedcontent}
+%D
+%D This one is relatively new and will be used as a more robust test for inner
+%D situations.
+
+\newif \ifboxedcontent
+\newtoks\everyboxedcontent
+
+\appendtoks
+ \boxedcontenttrue
+\to \everyboxedcontent
+
+\permanent\protected\def\startboxedcontent{\bgroup\the\everyboxedcontent}
+
+\aliased\let\stopboxedcontent\egroup
+
+% %D We store some original meanings, maybe in \type {math-ini}.
+%
+% \let\normalat \at
+% \let\normalin \in
+% \let\normalfrom \from
+% \let\normalover \over
+% \let\normalabout\about
+
+%D This will be implemented way later:
+
+\let\setlayoutcomponentattribute \gobbleoneargument
+\let\resetlayoutcomponentattribute\relax
+\let\layoutcomponentboxattribute \empty
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-lmt.lmt b/Master/texmf-dist/tex/context/base/mkxl/core-lmt.lmt
new file mode 100644
index 00000000000..ccbdcc0dff6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-lmt.lmt
@@ -0,0 +1,95 @@
+if not modules then modules = { } end modules ['core-lmt'] = {
+ version = 1.001,
+ comment = "companion to core-lmt.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Kind of obsolete ... old school directions.
+
+local implement = interfaces.implement
+local scankeyword = tokens.scanners.keyword
+local scaninteger = tokens.scanners.integer
+
+local settextdir = tex.settextdir
+local setlinedir = tex.setlinedir
+local setpardir = tex.setpardir
+local setboxdir = tex.setboxdir
+
+local gettextdir = tex.gettextdir
+local getlinedir = tex.getlinedir
+local getpardir = tex.getpardir
+local getboxdir = tex.getboxdir
+
+local none_code = tokens.values.none
+
+local context = context
+
+local function scandir()
+ if scankeyword("tlt") then
+ return 0
+ elseif scankeyword("trt") then
+ return 1
+ else
+ return 0
+ end
+end
+
+local function showdir(d)
+ context(d == 1 and "TRT" or "LTL")
+ return none_code
+end
+
+implement {
+ name = "textdir",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ if what == "value" then
+ showdir(gettextdir())
+ else
+ settextdir(scandir())
+ end
+ end
+}
+
+implement {
+ name = "linedir",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ if what == "value" then
+ return showdir(getlinedir())
+ else
+ setlinedir(scandir())
+ end
+ end
+}
+
+implement {
+ name = "pardir",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ if what == "value" then
+ return showdir(getpardir())
+ else
+ setpardir(scandir())
+ end
+ end
+}
+
+implement {
+ name = "boxdir",
+ public = true,
+ usage = "value",
+ actions = function(what)
+ local n = scaninteger()
+ if what == "value" then
+ return showdir(getboxdir(n))
+ else
+ setboxdir(n,scandir())
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-lmt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-lmt.mkxl
new file mode 100644
index 00000000000..77ef179669a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-lmt.mkxl
@@ -0,0 +1,28 @@
+%D \module
+%D [ file=core-lmt,
+%D version=2018.08.2,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Primitives,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt System Macros / Primitives}
+
+\registerctxluafile{core-lmt}{autosuffix}
+
+\unprotect
+
+% nothing here
+
+\protect \endinput
+
+% \starttext
+% abc{\textdir TRTdef}ghi
+% abc{\textdir trtdef}ghi
+% \boxdirection0=1
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-sys.lmt b/Master/texmf-dist/tex/context/base/mkxl/core-sys.lmt
new file mode 100644
index 00000000000..0d471133d3f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-sys.lmt
@@ -0,0 +1,83 @@
+if not modules then modules = { } end modules ['core-sys'] = {
+ version = 1.001,
+ comment = "companion to core-sys.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local lower, format, gsub = string.lower, string.format, string.gsub
+local suffixonly, basename, removesuffix = file.suffix, file.basename, file.removesuffix
+
+local environment = environment
+local context = context
+local implement = interfaces.implement
+
+local report_files = logs.reporter("system","files")
+
+function environment.initializefilenames()
+
+ local arguments = environment.arguments
+
+ local jobname = arguments.jobname or tex.jobname
+ local fulljobname = arguments.fulljobname or jobname
+ local inputfilename = arguments.input or fulljobname
+ local outputfilename = arguments.result or removesuffix(jobname)
+
+ local inputfilename = suffixonly(inputfilename) == "tex" and removesuffix(inputfilename) or inputfilename or ""
+
+ local filename = fulljobname
+ local suffix = suffixonly(filename)
+
+ local filename = ctxrunner.resolve(filename) -- in case we're prepped
+
+ local jobfilename = jobname or inputfilename or tex.jobname or ""
+ local inputfilename = inputfilename or ""
+
+ local jobfilebase = basename(jobfilename)
+ local inputfilebase = basename(inputfilename)
+
+ -- jobfilename = gsub(jobfilename, "^./","")
+ -- inputfilename = gsub(inputfilename,"^./","")
+
+ environment.jobfilefullname = fulljobname
+ environment.jobfilename = jobfilebase
+ environment.jobfilesuffix = lower(suffixonly(jobfilebase))
+
+ environment.inputfilename = inputfilename -- so here we keep e.g. ./ or explicit paths
+ environment.inputfilebarename = removesuffix(inputfilebase)
+ environment.inputfilesuffix = lower(suffixonly(inputfilebase))
+
+ environment.outputfilename = outputfilename or environment.inputfilebarename or ""
+
+ environment.filename = filename
+ environment.suffix = suffix
+
+ -- if tex then
+ -- tex.jobname = jobfilename
+ -- end
+
+ report_files("jobname %a, input %a, result %a",jobfilename,inputfilename,outputfilename)
+
+ function environment.initializefilenames() end
+end
+
+-- we could set a macro (but will that work when we're expanding? needs testing!)
+
+implement { name = "operatingsystem", public = true, actions = function() context(os.platform) end }
+implement { name = "jobfilefullname", public = true, actions = function() context(environment.jobfilefullname) end }
+implement { name = "jobfilename", public = true, actions = function() context(environment.jobfilename) end }
+implement { name = "jobfilesuffix", public = true, actions = function() context(environment.jobfilesuffix) end }
+implement { name = "inputfilebarename", public = true, actions = function() context(environment.inputfilebarename) end }
+implement { name = "inputfilerealsuffix", public = true, actions = function() context(environment.inputfilerealsuffix) end }
+implement { name = "inputfilesuffix", public = true, actions = function() context(environment.inputfilesuffix) end }
+implement { name = "inputfilename", public = true, actions = function() context(environment.inputfilename) end }
+implement { name = "outputfilename", public = true, actions = function() context(environment.outputfilename) end }
+
+implement {
+ name = "systemlog",
+ arguments = "3 strings",
+ actions = function(whereto,category,text)
+ logs.system(whereto,"context",tex.jobname,category,text)
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-sys.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-sys.mkxl
new file mode 100644
index 00000000000..c5093588eb4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-sys.mkxl
@@ -0,0 +1,451 @@
+%D \module
+%D [ file=core-sys, % moved from main-001
+%D version=1997.03.31,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=System,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% we need to mkiv-ize this file !
+
+\writestatus{loading}{ConTeXt Core Macros / System}
+
+\registerctxluafile{core-sys}{autosuffix}
+
+\unprotect
+
+%D Version checking:
+
+\permanent\protected\def\newcontextversion#1%
+ {\doifelse{#1}\contextversion
+ {\aliased\let\newcontextversion\gobbleoneargument}
+ {\writeline
+ \writestatus{Fatal Error}{Your format does not match the base files!}%
+ \writeline
+ \writestatus{Format Version}{\contextversion\space\contextmark}%
+ \writestatus{Files Version}{#1}%
+ \batchmode
+ \normalend}}
+
+%D End of lines to the output. \TEX\ will map this onto the platform specific
+%D line ending. I hate this mess.
+
+
+% \operatingsystem % defined at lua end
+
+%D The jobname is what gets loaded by the cont-yes stub file. This name also
+%D determines the name of tuc etc files.
+
+% \jobfilefullname % defined at lua end
+% \jobfilename % defined at lua end
+% \jobfilesuffix % defined at lua end
+
+%D However, that one can itself load another file.
+
+% \inputfilebarename % defined at lua end
+% \inputfilerealsuffix % defined at lua end
+% \inputfilesuffix % defined at lua end
+% \inputfilename % defined at lua end
+
+%D The output name is only used for some checking.
+
+% \outputfilename % defined at lua end
+
+\installcorenamespace{system}
+
+\installdirectcommandhandler \??system {system}
+
+\appendtoks
+% \edef\outputfilename {\directsystemparameter\c!file }%
+% \edef\inputfilename {\directsystemparameter\c!inputfile }%
+\to \everysetupsystem
+
+\appendtoks
+ \ifcase\directsystemparameter\c!n\relax
+ % % 0 : unknown
+ \or
+ \setsystemmode\v!first % 1 : first run
+ \or
+ % % 2 : successive run
+ \or
+ \setsystemmode\v!first % 3 : first and only run
+ \or
+ \setsystemmode\v!last % 4 : (extra) last run
+ \fi
+\to \everysetupsystem
+
+\newconditional\prerollrun % when true it means that we have a forced number of runs
+
+% Some mechanisms (see x-res-01) use either \jobfilename or
+% \jobfilename.somesuffix, in which case we need to use the full name if given or a
+% default (like \jobfilename.xml); this comes down to replacing the default tex
+% suffix.
+
+\permanent\def\jobfullname{\jobfilename.\jobfilesuffix}
+
+\permanent\protected\def\setjobfullname#1% #1 = default if not given
+ {\doifelsenothing\jobfilename
+ {\let\jobfullname\empty}
+ {\doif\jobfilesuffix\c!tex{\edef\jobfullname{\jobfilename.#1}}}}
+
+%D There are a couple of system states avaiable:
+%D
+%D \starttabulate [|T|T|]
+%D \NC \type{\jobname} \NC \jobname \NC \NR
+%D \NC \type{\jobfilename} \NC \jobfilename \NC \NR
+%D \NC \type{\jobfilesuffix} \NC \jobfilesuffix \NC \NR
+%D \NC \type{\inputfilename} \NC \inputfilename \NC \NR
+%D \NC \type{\inputfilebarename} \NC \inputfilebarename \NC \NR
+%D \NC \type{\inputfilesuffix} \NC \inputfilesuffix \NC \NR
+%D \NC \type{\outputfilename} \NC \outputfilename \NC \NR
+%D \NC \type{\operatingsystem} \NC \operatingsystem \NC \NR
+%D \stoptabulate
+
+% \appendtoks
+% \edef\outputresolution{\directsystemparameter\c!resolution}%
+% \to \everysetupsystem
+
+%D The system modes set by the setup command can be used in situations like:
+%D
+%D \starttyping
+%D \startmode[*first]
+%D \executesystemcommand{cleanupxml text.xml clean-text.xml}
+%D \stopmode
+%D
+%D \starttext
+%D \typefile{clean-text.xml}
+%D \stoptext
+%D \stoptyping
+
+\permanent\protected\def\setuprandomize[#1]%
+ {\doifsomething{#1}
+ {\begingroup
+ % tex's time is in minutes
+ \scratchcounter\normaltime
+ \processaction
+ [#1]
+ [ \v!small=>\divide\scratchcounter 15, % 900,
+ \v!medium=>\divide\scratchcounter 30, % 1800,
+ \v!big=>\divide\scratchcounter 60, % 3600,
+ \v!normal=>\scratchcounter\newrandomseed,
+ \s!default=>\scratchcounter\newrandomseed,
+ \s!unknown=>\scratchcounter#1]%
+ \setrandomseed\scratchcounter
+ % \writestatus\m!system{randomseed: \the\scratchcounter}%
+ \endgroup}}
+
+\setupsystem
+ [\c!directory=,
+ \c!n=0, % 0:unknown 1: one run 2: first 3: successive 4: final run
+ % \c!resolution=600,% in dpi, no unit in mkiv, not used in lmtx
+ % \c!random=, % obsolete here
+ % \c!file=\jobname,
+ % \c!inputfile=\outputfilename,
+ \c!type=unix, % windows is normally less sensitive to handle
+ \c!bodyfont=\normalizedlocalbodyfontsize] % of iets anders
+
+%D Remark: windows programs normally handle \type {cr|lf|crlf} but unix is more
+%D picky, so we default to the \type {cr}. I never understood why \type {crlf} was
+%D not used in all systems, since it makes most sense. But anyway, in \MKIV\ we
+%D avoid most of the complications anyway as we deal with much at the \LUA\ end.
+
+\permanent\tolerant\protected\def\start[#1]%
+ {\bgroup
+ \ifparameters
+ \expandafter\syst_start_nop
+ \else
+ \edef\m_syst_start_stop{#1}%
+ \expandafter\syst_start_yes
+ \fi}
+
+\let\syst_stop_indeed\donothing
+
+\def\syst_start_yes
+ {\ifempty\m_syst_start_stop
+ \let\syst_stop_indeed\donothing
+ \orelse\ifcsname\e!start\m_syst_start_stop\endcsname
+ \expandafter\let\expandafter\syst_stop_indeed\csname\e!stop\m_syst_start_stop\endcsname
+ \csname\e!start\m_syst_start_stop\expandafter\expandafter\expandafter\endcsname
+ \else
+ \let\syst_stop_indeed\donothing
+ \fi}
+
+\def\syst_start_nop
+ {\let\syst_stop_indeed\donothing}
+
+\permanent\protected\def\stop
+ {\syst_stop_indeed
+ \egroup}
+
+% \c!before \c!after \c!inbetween \c!commands \c!style \c!color
+
+\installcorenamespace{startstop}
+
+\installcommandhandler \??startstop {startstop} \??startstop
+
+\appendtoks
+ \frozen\setuevalue{\e!start\currentstartstop}{\syst_startstop_start {\currentstartstop}}%
+ \frozen\setuevalue{\e!stop \currentstartstop}{\syst_startstop_stop {\currentstartstop}}%
+ \frozen\setuevalue {\currentstartstop}{\syst_startstop_indeed{\currentstartstop}}%
+\to \everydefinestartstop
+
+\ifdefined\dotagconstruct \else \aliased\let\dotagconstruct\relax \fi
+
+\protected\def\syst_startstop_start#1%
+ {\namedstartstopparameter{#1}\c!before\relax
+ \bgroup
+ \def\currentstartstop{#1}%
+ % we will keep this for a while:
+ \startstopparameter\c!commands\relax % obsolete
+ % this is the new method:
+ \usesetupsparameter\startstopparameter\relax % only in the display version
+ \dostarttagged\t!construct\currentstartstop
+ \usestartstopstyleandcolor\c!style\c!color
+ \dotagconstruct}
+
+\protected\def\syst_startstop_stop#1%
+ {\dostoptagged
+ \egroup
+ \namedstartstopparameter{#1}\c!after\relax}
+
+\protected\def\syst_startstop_indeed#1%
+ {\groupedcommand
+ {\def\currentstartstop{#1}%
+ \startstopparameter\c!commands\relax % better: setups so that will show op soon
+ \dostarttagged\t!construct\currentstartstop
+ \usestartstopstyleandcolor\c!style\c!color
+ \startstopparameter\c!left\relax}
+ {\def\currentstartstop{#1}% safeguard, not really needed
+ \startstopparameter\c!right\relax
+ \dostoptagged
+ \startstopparameter\c!inbetween\relax}}
+
+% \definestartstop[tracing][\c!style=\tt]
+
+% \protected\def\ignorestartstop[#1]%
+% {\protected\defcsname\e!start#1\expandafter\endcsname\expandafter
+% {\expandafter\gobbleuntil\csname\e!stop#1\endcsname}}
+%
+% \ignorestartstop[bagger]
+
+\installcorenamespace{highlight}
+
+\installcommandhandler \??highlight {highlight} \??highlight % we could do with less
+
+\setuphighlight
+ [\c!define=\v!yes]
+
+\appendtoks
+ \ifcstok{\highlightparameter\c!define}\v!yes
+ \frozen\instance\setuevalue\currenthighlight{\typo_highlights_indeed{\currenthighlight}}%
+ \fi
+\to \everydefinehighlight
+
+\ifdefined\dotaghighlight \else \aliased\let\dotaghighlight\relax \fi
+
+\permanent\protected\def\typo_highlights_indeed#1% inline style/color switch
+ {\dontleavehmode\groupedcommand % otherwise wrong par number in tags
+ {\def\currenthighlight{#1}%
+ \dostarttagged\t!highlight\currenthighlight
+ \usehighlightstyleandcolor\c!style\c!color
+ \dotaghighlight}
+ {\dostoptagged}}
+
+\permanent\protected\def\highlight[#1]%
+ {\typo_highlights_indeed{#1}}
+
+\permanent\protected\def\starthighlight[#1]%
+ {\begingroup
+ \def\currenthighlight{#1}%
+ \dostarttagged\t!highlight\currenthighlight
+ \usehighlightstyleandcolor\c!style\c!color
+ \dotaghighlight}
+
+\permanent\protected\def\stophighlight
+ {\dostoptagged
+ \endgroup}
+
+\aliased\let\directhighlight\typo_highlights_indeed
+
+\permanent\protected\def\defineexpandable
+ {\doifelsenextoptional
+ {\syst_basics_define_yes\def}%
+ {\syst_basics_define_nop\def}}
+
+\permanent\protected\def\define
+ {\doifelsenextoptional
+ {\syst_basics_define_yes{\protected\def}}%
+ {\syst_basics_define_nop{\protected\def}}}
+
+\protected\def\syst_basics_define_yes#1[#2]#3#4%
+ {\ifdefined#3%
+ \showmessage\m!system4{\string#3}%
+ \fi
+ \ifcase0#2\relax
+ #1#3{#4}\or
+ #1#3##1{#4}\or
+ #1#3##1##2{#4}\or
+ #1#3##1##2##3{#4}\or
+ #1#3##1##2##3##4{#4}\or
+ #1#3##1##2##3##4##5{#4}\or
+ #1#3##1##2##3##4##5##6{#4}\or
+ #1#3##1##2##3##4##5##6##7{#4}\or
+ #1#3##1##2##3##4##5##6##7##8{#4}\or
+ #1#3##1##2##3##4##5##6##7##8##9{#4}\else
+ #1#3{#4}\fi}
+
+\protected\def\syst_basics_define_nop#1#2#3%
+ {\ifdefined#2%
+ \showmessage\m!system4{\string#2}%
+ \fi
+ #1#2{#3}}
+
+\aliased\let\redefine\define
+
+% new but maybe not needed:
+%
+% \checked\def \whatever#alpha#beta{#alpha + #beta}
+% \checked\edef\whatever#alpha#beta{#alpha + #beta}
+
+\mutable\let\gobbleddefinition\relax % only for diagnostics
+
+\permanent\protected\def\unique#1#2%
+ {\ifdefined#2%
+ \showmessage\m!system4{\string#2}%
+ \expandafter#1\expandafter\gobbleddefinition
+ \else
+ \expandafter#1%
+ \fi#2}
+
+\permanent\protected\def\checked#1#2%
+ {\ifdefined#2%
+ \showmessage\m!system4{\string#2}%
+ \fi
+ #1#2}
+
+% \startluacode
+% local formatters = string.formatters
+% local contextsprint, ctxcatcodes, prtcatcodes = context.sprint, tex.ctxcatcodes, tex.prtcatcodes
+% local match, gmatch, rep = string.match, string.gmatch, string.rep
+% local empty = {
+% "single",
+% "double",
+% "triple",
+% "quadruple",
+% "quintuple",
+% }
+% local check = {
+% "first",
+% "second",
+% "third",
+% "fourth",
+% "fifth",
+% }
+% function commands.define(str)
+% -- we could store the defaults in lua and call lua instead but why bother
+% local arg, cmd = match(str,"(.*)\\(.-)$")
+% local a = { }
+% for s in gmatch(arg,"%[(.-)%]") do
+% a[#a+1] = s
+% end
+% local n = tonumber(a[#a])
+% if n then
+% a[#a] = nil
+% else
+% n = 0
+% end
+% contextsprint(ctxcatcodes,formatters["\\protected\\def\\%s"](cmd))
+% if #a > 0 then
+% contextsprint(prtcatcodes,formatters["{\\do%sempty\\user_defined_%s}"](empty[#a],cmd))
+% contextsprint(prtcatcodes,formatters["\\def\\user_defined_%s"](cmd))
+% for i=1,#a do
+% contextsprint(ctxcatcodes,formatters["[#%s]"](i))
+% end
+% contextsprint(ctxcatcodes,"{")
+% for i=#a,1,-1 do
+% contextsprint(ctxcatcodes,formatters["\\if%sargument"](check[i]))
+% contextsprint(prtcatcodes,formatters["\\def\\next{\\user_defined_indeed_%s"](cmd))
+% for j=1,#a-i do
+% contextsprint(ctxcatcodes,formatters["[%s]"](a[j]))
+% end
+% for j=1,i do
+% contextsprint(ctxcatcodes,formatters["[#%s]"](j))
+% end
+% contextsprint(ctxcatcodes,"}")
+% if i == 1 then
+% contextsprint(ctxcatcodes,rep("\\fi",#a))
+% else
+% contextsprint(ctxcatcodes,"\\else")
+% end
+% end
+% contextsprint(ctxcatcodes,"\\next}")
+% contextsprint(prtcatcodes,formatters["\\def\\user_defined_indeed_%s"](cmd))
+% for i=1,#a do
+% contextsprint(ctxcatcodes,formatters["[#%s]"](i))
+% end
+% end
+% for i=1,n do
+% contextsprint(ctxcatcodes,formatters["#%s"](#a+i))
+% end
+% end
+% \stopluacode
+%
+% \protected\def\define#1#{\ctxcommand{define([[\detokenize{#1}]])}}
+%
+% \starttext
+% \define[2]\whatevera{#1+#2}
+% \whatevera{A}{B}
+% \define[me][too][2]\whateverb{#1+#2+#3+#4}
+% \whateverb[A]{B}{C}
+% \whateverb[A][B]{C}{D}
+% \define[alpha][beta][gamma][delta]\whateverc{#1+#2+#3+#4}
+% \whateverc[P][Q]
+% \stoptext
+
+%D This is a checked variant of \type {\getvalue}.
+
+\permanent\protected\def\macroname#1% brrr
+ {\begincsname#1\endcsname}
+
+% %D A weird one that I probably needed once, so it might as well become
+% %D obsolete.
+%
+% \protected\def\usecommands#1%
+% {\begingroup
+% \def\docommand##1{\setbox\scratchbox\hbox{\csname\string##1\endcsname##1}}%
+% \processcommalist[#1]\docommand
+% \endgroup}
+
+\permanent\protected\def\syst_log_indeed#1#2#3%
+ {\ctxcommand{systemlog("#1","#2",\!!bs#3\!!es)}}
+
+\aliased\let\systemlog\syst_log_indeed
+
+\permanent\protected\def\systemlogfirst
+ {\ifcase\directsystemparameter\c!n\relax
+ \expandafter\syst_log_indeed
+ \or
+ \expandafter\syst_log_indeed
+ \else
+ \expandafter\gobblethreearguments
+ \fi}
+
+\permanent\protected\def\systemloglast
+ {\ifcase\directsystemparameter\c!n\relax
+ \expandafter\syst_log_indeed
+ \or
+ \expandafter\gobblethreearguments
+ \or
+ \expandafter\gobblethreearguments
+ \or
+ \expandafter\gobblethreearguments
+ \or
+ \expandafter\syst_log_indeed
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-two.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-two.mkxl
new file mode 100644
index 00000000000..cadd9d05907
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-two.mkxl
@@ -0,0 +1,111 @@
+%D \module
+%D [ file=core-two, % moved from core-uti
+%D version=1997.03.31,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Two Pass Data,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Two Pass Data}
+
+%D This is a rather old mechanism which has not changed much over time, apart from
+%D adding a few more selectors. This code used to be part of \type {core-uti}. The
+%D following examples demonstrate the interface.
+%D
+%D \startbuffer
+%D \definetwopasslist{test-1}
+%D
+%D \gettwopassdatalist{test-1} [\twopassdatalist=]
+%D \checktwopassdata {test-1} [\twopassdata=]
+%D \checktwopassdata {test-1} [\twopassdata=]
+%D \gettwopassdata {test-1} [\twopassdata=]
+%D \gettwopassdata {test-1} [\twopassdata=]
+%D
+%D \definetwopasslist{test-2}
+%D
+%D \lazysavetwopassdata{test-2}{1}{x}
+%D \lazysavetwopassdata{test-2}{2}{y}
+%D \lazysavetwopassdata{test-2}{3}{z}
+%D
+%D \gettwopassdatalist{test-2} [\twopassdatalist=x,y,z]
+%D \checktwopassdata {test-2} [\twopassdata=x]
+%D \checktwopassdata {test-2} [\twopassdata=x]
+%D \gettwopassdata {test-2} [\twopassdata=x]
+%D \gettwopassdata {test-2} [\twopassdata=y]
+%D \gettwopassdata {test-2} [\twopassdata=z]
+%D \gettwopassdata {test-2} [\twopassdata=]
+%D
+%D \definetwopasslist{test-3}
+%D
+%D \lazysavetaggedtwopassdata{test-3}{1}{x}{a}
+%D \lazysavetaggedtwopassdata{test-3}{2}{y}{b}
+%D \lazysavetaggedtwopassdata{test-3}{3}{z}{c}
+%D
+%D \findtwopassdata{test-3}{x} [\twopassdata=a]
+%D \findtwopassdata{test-3}{y} [\twopassdata=b]
+%D \findtwopassdata{test-3}{z} [\twopassdata=c]
+%D \findtwopassdata{test-3}{w} [\twopassdata=]
+%D
+%D \definetwopasslist{test-4}
+%D
+%D \lazysavetwopassdata{test-4}{1}{A}
+%D \lazysavetwopassdata{test-4}{2}{B}
+%D \lazysavetwopassdata{test-4}{3}{C}
+%D
+%D \getfirsttwopassdata{test-4} [\twopassdata=A]
+%D \getlasttwopassdata {test-4} [\twopassdata=C]
+%D \getfirsttwopassdata{test-4} [\twopassdata=A]
+%D \getlasttwopassdata {test-4} [\twopassdata=C]
+%D \getfromtwopassdata {test-4}{1} [\twopassdata=A]
+%D \getfromtwopassdata {test-4}{3} [\twopassdata=C]
+%D \getfromtwopassdata {test-4}{2} [\twopassdata=B]
+%D \stopbuffer
+%D
+%D \getbuffer \typebuffer
+
+\unprotect
+
+\registerctxluafile{core-two}{}
+
+\permanent\def\immediatesavetwopassdata #1#2#3{\normalexpanded{\noexpand\clf_savetwopassdata{#1}{#3}}}
+\permanent\def \lazysavetwopassdata #1#2#3{\normalexpanded{\noexpand\ctxlatecommand{savetwopassdata("#1","#3")}}}
+\permanent\let \savetwopassdata \lazysavetwopassdata
+\permanent\def \savetaggedtwopassdata#1#2#3#4{\normalexpanded{\noexpand\clf_savetaggedtwopassdata{#1}{#3}{#4}}}
+\permanent\def\lazysavetaggedtwopassdata#1#2#3#4{\normalexpanded{\noexpand\ctxlatecommand{savetaggedtwopassdata("#1",'#3',"#4")}}}
+
+% temp hack: needs a proper \starteverytimeluacode
+
+\setfalse\twopassdatafound
+
+\mutable\let\twopassdata \empty
+\mutable\let\twopassdatalist\empty
+\mutable\let\noftwopassitems\!!zeropoint
+
+\def\syst_twopass_check % can be delegated to lua once obsolete is gone
+ {\ifempty\twopassdata
+ \setfalse\twopassdatafound
+ \else
+ \settrue\twopassdatafound
+ \fi}
+
+\permanent\protected\def\definetwopasslist #1{\clf_definetwopasslist{#1}}
+\permanent\protected\def\gettwopassdata #1{\edef\twopassdata {\clf_gettwopassdata {#1}}\syst_twopass_check}
+\permanent\protected\def\checktwopassdata #1{\edef\twopassdata {\clf_checktwopassdata {#1}}\syst_twopass_check}
+\permanent\protected\def\findtwopassdata #1#2{\edef\twopassdata {\clf_findtwopassdata {#1}{#2}}\syst_twopass_check}
+\permanent\protected\def\getfirsttwopassdata #1{\edef\twopassdata {\clf_getfirsttwopassdata {#1}}\syst_twopass_check}
+\permanent\protected\def\getlasttwopassdata #1{\edef\twopassdata {\clf_getlasttwopassdata {#1}}%
+ \edef\noftwopassitems{\clf_counttwopassdata {#1}}\syst_twopass_check}
+\permanent\protected\def\getnamedtwopassdatalist#1#2{\edef #1{\clf_gettwopassdatalist {#2}}}
+\permanent\protected\def\gettwopassdatalist #1{\edef\twopassdatalist{\clf_gettwopassdatalist {#1}}}
+
+\permanent\protected\def\doifelseintwopassdata #1#2{\clf_doifelseintwopassdata{#1}{#2}}
+
+\aliased\let\doifintwopassdataelse\doifelseintwopassdata
+\aliased\let\getfromtwopassdata \findtwopassdata
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-uti.lua b/Master/texmf-dist/tex/context/base/mkxl/core-uti.lua
new file mode 100644
index 00000000000..887ef9a7536
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-uti.lua
@@ -0,0 +1,470 @@
+if not modules then modules = { } end modules ['core-uti'] = {
+ version = 1.001,
+ comment = "companion to core-uti.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: keep track of changes here (hm, track access, and only true when
+-- accessed and changed)
+
+--[[ldx--
+<p>A utility file has always been part of <l n='context'/> and with
+the move to <l n='luatex'/> we also moved a lot of multi-pass info
+to a <l n='lua'/> table. Instead of loading a <l n='tex'/> based
+utility file under different setups, we now load a table once. This
+saves much runtime but at the cost of more memory usage.</p>
+--ldx]]--
+
+local math = math
+local format, match = string.format, string.match
+local next, type, tostring, tonumber = next, type, tostring, tonumber
+local concat = table.concat
+
+local definetable = utilities.tables.definetable
+local accesstable = utilities.tables.accesstable
+local migratetable = utilities.tables.migratetable
+local serialize = table.serialize
+local packers = utilities.packers
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+
+local getrandom = utilities.randomizer.get
+local setrandomseedi = utilities.randomizer.setseedi
+local getrandomseed = utilities.randomizer.getseed
+
+local implement = interfaces.implement
+
+local texgetcount = tex.getcount
+
+local report_passes = logs.reporter("job","passes")
+
+job = job or { }
+local job = job
+
+job.version = 1.32
+job.packversion = 1.02
+
+-- some day we will implement loading of other jobs and then we need
+-- job.jobs
+
+--[[ldx--
+<p>Variables are saved using in the previously defined table and passed
+onto <l n='tex'/> using the following method. Of course one can also
+directly access the variable using a <l n='lua'/> call.</p>
+--ldx]]--
+
+local savelist, comment = { }, { }
+
+function job.comment(key,value)
+ if type(key) == "table" then
+ for k, v in next, key do
+ comment[k] = v
+ end
+ else
+ comment[key] = value
+ end
+end
+
+job.comment("version",job.version)
+
+local enabled = true
+local initialized = false
+
+directives.register("job.save",function(v) enabled = v end)
+
+function job.disablesave()
+ enabled = false -- for instance called when an error
+end
+
+function job.initialize(loadname,savename)
+ if not initialized then
+ if not loadname or loadname == "" then
+ loadname = tex.jobname .. ".tuc"
+ end
+ if not savename or savename == "" then
+ savename = tex.jobname .. ".tua"
+ end
+ job.load(loadname) -- has to come after structure is defined !
+ luatex.registerstopactions(function()
+ if enabled then
+ job.save(savename)
+ end
+ end)
+ initialized = true
+ end
+end
+
+function job.register(collected, tobesaved, initializer, finalizer, serializer)
+ savelist[#savelist+1] = { collected, tobesaved, initializer, finalizer, serializer }
+end
+
+-- as an example we implement variables
+
+local tobesaved, collected, checksums = allocate(), allocate(), allocate()
+
+local jobvariables = {
+ collected = collected,
+ tobesaved = tobesaved,
+ checksums = checksums,
+}
+
+-- if not checksums.old then checksums.old = md5.HEX("old") end -- used in experiment
+-- if not checksums.new then checksums.new = md5.HEX("new") end -- used in experiment
+
+job.variables = jobvariables
+
+local function initializer()
+ checksums = jobvariables.checksums
+end
+
+job.register('job.variables.checksums', 'job.variables.checksums', initializer)
+
+local rmethod, rvalue
+local collectedmacros, tobesavedmacros
+
+local ctx_setxvalue = context.setxvalue
+
+local function initializer()
+ tobesaved = jobvariables.tobesaved
+ collected = jobvariables.collected
+ --
+ rvalue = collected.randomseed
+ if not rvalue then
+ rvalue = getrandom("initialize")
+ setrandomseedi(rvalue)
+ rmethod = "initialized"
+ else
+ setrandomseedi(rvalue)
+ rmethod = "resumed"
+ end
+ tobesaved.randomseed = rvalue
+ --
+ collectedmacros = collected.macros
+ tobesavedmacros = tobesaved.macros
+ if not collectedmacros then
+ collectedmacros = { }
+ collected.macros = collectedmacros
+ end
+ if not tobesavedmacros then
+ tobesavedmacros = { }
+ tobesaved.macros = tobesavedmacros
+ end
+ -- will become collected.macros
+ for cs, value in next, collectedmacros do
+ if type(value) == "string" then -- safeguard
+ ctx_setxvalue(cs,value)
+ end
+ end
+end
+
+job.register('job.variables.collected', tobesaved, initializer)
+
+function jobvariables.save(cs,value)
+ tobesavedmacros[cs] = value
+end
+
+function jobvariables.restore(cs)
+ return collectedmacros[cs] or tobesavedmacros[cs]
+end
+
+function job.getrandomseed()
+ return tobesaved.randomseed or getrandomseed()
+end
+
+-- checksums
+
+function jobvariables.getchecksum(tag)
+ return checksums[tag] -- no default
+end
+
+function jobvariables.makechecksum(data)
+ return data and md5.HEX(data) -- no default
+end
+
+function jobvariables.setchecksum(tag,checksum)
+ checksums[tag] = checksum
+end
+
+--
+
+local packlist = {
+ "numbers",
+ "ownnumbers",
+ "metadata",
+ "sectiondata",
+ "prefixdata",
+ "numberdata",
+ "pagedata",
+ "directives",
+ "specification",
+ "processors", -- might become key under directives or metadata
+-- "references", -- we need to rename of them as only one packs (not structures.lists.references)
+}
+
+local skiplist = {
+ "datasets",
+ "userdata",
+ "positions",
+}
+
+-- not ok as we can have arbitrary keys in userdata and dataset so some day we
+-- might need a bit more granularity, like skippers
+
+local jobpacker = packers.new(packlist,job.packversion,skiplist) -- jump number when changs in hash
+
+job.pack = true
+-- job.pack = false
+
+directives.register("job.pack",function(v) job.pack = v end)
+
+local _save_, _load_, _others_ = { }, { }, { } -- registers timing
+
+function job.save(filename) -- we could return a table but it can get pretty large
+ statistics.starttiming(_save_)
+ local f = io.open(filename,'w')
+ if f then
+ f:write("local utilitydata = { }\n\n")
+ f:write(serialize(comment,"utilitydata.comment",true),"\n\n")
+ for l=1,#savelist do
+ -- f:write("do\n\n") -- no solution for the jit limitatione either
+ local list = savelist[l]
+ local target = format("utilitydata.%s",list[1])
+ local data = list[2]
+ local finalizer = list[4]
+ local serializer = list[5]
+ if type(data) == "string" then
+ data = utilities.tables.accesstable(data)
+ end
+ if type(finalizer) == "function" then
+ finalizer()
+ end
+ if job.pack then
+ packers.pack(data,jobpacker,true)
+ end
+ local definer, name = definetable(target,true,true) -- no first and no last
+ if serializer then
+ f:write(definer,"\n\n",serializer(data,name,true),"\n\n")
+ else
+ f:write(definer,"\n\n",serialize(data,name,true),"\n\n")
+ end
+ -- f:write("end\n\n")
+ end
+ if job.pack then
+ packers.strip(jobpacker)
+ -- f:write("do\n\n")
+ f:write(serialize(jobpacker,"utilitydata.job.packed",true),"\n\n")
+ -- f:write("end\n\n")
+ end
+ f:write("return utilitydata")
+ f:close()
+ end
+ statistics.stoptiming(_save_)
+end
+
+local function load(filename)
+ if lfs.isfile(filename) then
+
+ local function dofile(filename)
+ local result = loadstring(io.loaddata(filename))
+ if result then
+ return result()
+ else
+ return nil
+ end
+ end
+
+ local okay, data = pcall(dofile,filename)
+ if okay and type(data) == "table" then
+ local jobversion = job.version
+ local datacomment = data.comment
+ local dataversion = datacomment and datacomment.version or "?"
+ if dataversion ~= jobversion then
+ report_passes("version mismatch: %s <> %s",dataversion,jobversion)
+ else
+ return data
+ end
+ else
+ os.remove(filename) -- probably a bad file (or luajit overflow as it cannot handle large tables well)
+ report_passes("removing stale job data file %a, restart job, message: %s%s",filename,tostring(data),
+ jit and " (try luatex instead of luajittex)" or "")
+ os.exit(true) -- trigger second run
+ end
+ end
+end
+
+function job.load(filename)
+ statistics.starttiming(_load_)
+ local utilitydata = load(filename)
+ if utilitydata then
+ local jobpacker = utilitydata.job.packed
+ local handlers = { }
+ for i=1,#savelist do
+ local list = savelist[i]
+ local target = list[1]
+ local initializer = list[3]
+ local result = accesstable(target,utilitydata)
+ if result then
+ local done = packers.unpack(result,jobpacker,true)
+ if done then
+ migratetable(target,mark(result))
+ if type(initializer) == "function" then
+ handlers[#handlers+1] = { initializer, result }
+ end
+ else
+ report_passes("pack version mismatch")
+ end
+ end
+ end
+ -- so we have all tables available (unpacked)
+ for i=1,#handlers do
+ local handler = handlers[i]
+ handler[1](handler[2])
+ end
+ end
+ statistics.stoptiming(_load_)
+end
+
+function job.loadother(filename)
+ statistics.starttiming(_load_)
+ _others_[#_others_+1] = file.nameonly(filename)
+ local utilitydata = load(filename)
+ if utilitydata then
+ local jobpacker = utilitydata.job.packed
+ local unpacked = { }
+ for l=1,#savelist do
+ local list = savelist[l]
+ local target = list[1]
+ local result = accesstable(target,utilitydata)
+ local done = packers.unpack(result,jobpacker,true)
+ if done then
+ migratetable(target,result,unpacked)
+ end
+ end
+ unpacked.job.packed = nil -- nicer in inspecting
+ return unpacked
+ end
+ statistics.stoptiming(_load_)
+end
+
+-- function job.keep(filename)
+-- local suffix = file.suffix(filename)
+-- local base = file.removesuffix(filename)
+-- if suffix == "" then
+-- suffix = "tuc"
+-- end
+-- for i=1,10 do
+-- local tmpname = format("%s-%s-%02d.tmp",base,suffix,i)
+-- if lfs.isfile(tmpname) then
+-- os.remove(tmpname)
+-- report_passes("removing %a",tmpname)
+-- end
+-- end
+-- if lfs.isfile(filename) then
+-- local tmpname = format("%s-%s-%02d.tmp",base,suffix,environment.currentrun or 1)
+-- report_passes("copying %a into %a",filename,tmpname)
+-- file.copy(filename,tmpname)
+-- else
+-- report_passes("no file %a, nothing kept",filename)
+-- end
+-- end
+
+-- eventually this will end up in strc-ini
+
+statistics.register("startup time", function()
+ return statistics.elapsedseconds(statistics,"including runtime option file processing")
+end)
+
+statistics.register("jobdata time",function()
+ if enabled then
+ if #_others_ > 0 then
+ return format("%s seconds saving, %s seconds loading, other files: %s",statistics.elapsedtime(_save_),statistics.elapsedtime(_load_),concat(_others_," "))
+ else
+ return format("%s seconds saving, %s seconds loading",statistics.elapsedtime(_save_),statistics.elapsedtime(_load_))
+ end
+ else
+ if #_others_ > 0 then
+ return format("nothing saved, %s seconds loading, other files: %s",statistics.elapsedtime(_load_),concat(_others_," "))
+ else
+ return format("nothing saved, %s seconds loading",statistics.elapsedtime(_load_))
+ end
+ end
+end)
+
+statistics.register("callbacks", function()
+ local c_internal = status.callbacks or 0
+ local c_file = status.indirect_callbacks or 0
+ local c_direct = status.direct_callbacks or 0
+ local c_late = backends.getcallbackstate().count
+ local c_function = status.function_callbacks or 0
+ local c_total = c_internal + c_file + c_direct + c_late + c_function
+ local n_pages = structures.pages.nofpages or 0
+ local c_average = n_pages > 0 and math.round(c_total/n_pages) or 0
+ local result = format (
+ "internal: %s, file: %s, direct: %s, late: %s, function %s, total: %s (%s per page)",
+ c_internal, c_file, c_direct, c_late, c_function, c_total, c_average
+ )
+ statistics.callbacks = function()
+ return result
+ end
+ return result
+end)
+
+statistics.register("randomizer", function()
+ if rmethod and rvalue then
+ return format("%s with value %s",rmethod,rvalue)
+ end
+end)
+
+-- a sort of joke (for ctx meeting)
+
+-- local kg_per_watt_per_second = 1 / 15000000
+-- local watts_per_core = 50
+-- local speedup_by_other_engine = 1.2
+-- local used_wood_factor = watts_per_core * kg_per_watt_per_second / speedup_by_other_engine
+-- local used_wood_factor = (50 / 15000000) / 1.2
+
+
+function statistics.formatruntime(runtime)
+ if not environment.initex then -- else error when testing as not counters yet
+ -- stoptiming(statistics) -- to be sure
+ local shipped = texgetcount('nofshipouts')
+ local pages = texgetcount('realpageno')
+ if pages > shipped then
+ pages = shipped
+ end
+ runtime = tonumber(runtime)
+ if shipped > 0 or pages > 0 then
+ local persecond = (runtime > 0) and (shipped/runtime) or pages
+ if pages == 0 then
+ pages = shipped
+ end
+ return format("%0.3f seconds, %i processed pages, %i shipped pages, %.3f pages/second",runtime,pages,shipped,persecond)
+ else
+ return format("%0.3f seconds",runtime)
+ end
+ end
+end
+
+implement {
+ name = "savevariable",
+ actions = job.variables.save,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "setjobcomment",
+ actions = job.comment,
+ arguments = { { "*" } }
+}
+
+implement {
+ name = "initializejob",
+ actions = job.initialize
+}
+
+implement {
+ name = "disablejobsave",
+ actions = job.disablesave
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/core-uti.mkxl b/Master/texmf-dist/tex/context/base/mkxl/core-uti.mkxl
new file mode 100644
index 00000000000..b8d3bf7ebed
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/core-uti.mkxl
@@ -0,0 +1,35 @@
+%D \module
+%D [ file=core-uti,
+%D version=1997.03.31, % 2006.09.19 mkiv
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Utility File Handling,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Utility File Handling}
+
+\unprotect
+
+\registerctxluafile{core-uti}{}
+
+% savecurrentvalue#1#2% immediate, expanded, defined at lua end
+
+\appendtoks
+ \clf_setjobcomment
+ file {\jobname}%
+ format {\contextformat}%
+ stamp {\contextversion}%
+ escape {\!!bs\space...\space\!!es}%
+ \relax
+\to \everystarttext
+
+\appendtoks
+ \clf_initializejob
+\to \everyjob
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/driv-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/driv-ini.lmt
new file mode 100644
index 00000000000..729196e870d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/driv-ini.lmt
@@ -0,0 +1,375 @@
+if not modules then modules = { } end modules ['driv-ini'] = {
+ version = 1.001,
+ comment = "companion to driv-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type = type
+local addsuffix = file.addsuffix
+
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local report = logs.reporter("drivers")
+
+local instances = { }
+local helpers = { }
+local converters = { }
+local prepared = { }
+local wrappedup = { }
+local cleanedup = { }
+local currentdriver = "default"
+local currentinstance = nil
+
+local shipout = tex.shipout
+local texgetbox = tex.getbox
+local texgetcount = tex.getcount
+
+function converters.engine(driver,boxnumber,mode,number,specification)
+ return shipout(boxnumber)
+end
+
+local prepare = nil
+local convert = nil
+local wrapup = nil
+local cleanup = nil
+local outputfilename = nil
+
+drivers = drivers or {
+ instances = instances,
+ helpers = helpers,
+ converters = converters,
+ lmtxversion = 0.10,
+ report = report,
+}
+
+local dummy = function() end
+
+local defaulthandlers = {
+ prepare = dummy,
+ initialize = dummy,
+ finalize = dummy,
+ updatefontstate = dummy,
+ wrapup = dummy,
+ cleanup = dummy,
+ convert = dummy,
+ outputfilename = dummy,
+}
+
+local installwhatsits do
+
+ local leaderlevel = 0
+ local backends = backends
+
+ local function pushleaderlevel()
+ leaderlevel = leaderlevel + 1
+ end
+
+ local function popleaderlevel()
+ leaderlevel = leaderlevel - 1
+ end
+
+ local function flushlatelua(current,h,v)
+ -- Here we assume maganement by the lua function so currently we don't
+ -- check for leaderlevel.
+ return backends.latelua(current,h,v)
+ end
+
+ local function flushwriteout(current)
+ if leaderlevel == 0 then
+ backends.writeout(current)
+ end
+ end
+
+ local function flushopenout(current)
+ if leaderlevel == 0 then
+ backends.openout(current)
+ end
+ end
+
+ local function flushcloseout(current)
+ if leaderlevel == 0 then
+ backends.closeout(current)
+ end
+ end
+
+ local function flushsavepos(current,pos_h,pos_v)
+ local jp = job.positions
+ jp.lastx = pos_h
+ jp.lasty = pos_v
+ end
+
+ local function flushuserdefined()
+ -- nothing here
+ end
+
+ local whatsitcodes = nodes.whatsitcodes
+
+ local trace = true trackers.register("backends.whatsits",function(v) trace = v end)
+
+ installwhatsits = function(name,flushers)
+
+ -- latelua : specific
+ -- userdefined : special purpose, handled in callbacks
+ -- savepos : only used by generic packages
+ -- open : generic
+ -- close : generic
+ -- write : generic
+
+ -- An alternative is to have a first time setter.
+
+ local function checkagain(t, k)
+ local v = rawget(flushers,k) -- in case it's registered later
+ if not v then
+ if trace then
+ report("unsupported whatsit %a in driver %a",whatsitcodes[k] or k,name)
+ end
+ v = function() end
+ end
+ t[k] = v
+ return v
+ end
+
+ -- flushers.whatsit = setmetatableindex ( {
+ -- [whatsitcodes.literal] = flushers.literal,
+ -- [whatsitcodes.latelua] = flushlatelua,
+ -- [whatsitcodes.userdefined] = flushuserdefined,
+ -- [whatsitcodes.savepos] = flushsavepos,
+ -- [whatsitcodes.save] = flushers.save,
+ -- [whatsitcodes.restore] = flushers.restore,
+ -- [whatsitcodes.setmatrix] = flushers.setmatrix,
+ -- [whatsitcodes.open] = flushopenout,
+ -- [whatsitcodes.close] = flushcloseout,
+ -- [whatsitcodes.write] = flushwriteout,
+ -- [whatsitcodes.startmatrix] = flushers.startmatrix,
+ -- [whatsitcodes.stopmatrix] = flushers.stopmatrix,
+ -- [whatsitcodes.startscaling] = flushers.startscaling,
+ -- [whatsitcodes.stopscaling] = flushers.stopscaling,
+ -- [whatsitcodes.startrotation] = flushers.startrotation,
+ -- [whatsitcodes.stoprotation] = flushers.stoprotation,
+ -- [whatsitcodes.startmirroring] = flushers.startmirroring,
+ -- [whatsitcodes.stopmirroring] = flushers.stopmirroring,
+ -- [whatsitcodes.startclipping] = flushers.startclipping,
+ -- [whatsitcodes.stopclipping] = flushers.stopclipping,
+ -- [whatsitcodes.setstate] = flushers.setstate,
+ -- }, checkagain )
+
+ -- -- sparse
+
+ -- flushers.whatsit = setmetatableindex ( {
+ -- [whatsitcodes.latelua] = flushlatelua,
+ -- [whatsitcodes.userdefined] = flushuserdefined,
+ -- [whatsitcodes.savepos] = flushsavepos,
+ -- [whatsitcodes.open] = flushopenout,
+ -- [whatsitcodes.close] = flushcloseout,
+ -- [whatsitcodes.write] = flushwriteout,
+ -- }, checkagain )
+
+ -- -- delayed
+
+ local whatsit ; whatsit = setmetatableindex ( {
+ [whatsitcodes.literal] = flushers.literal or function(...) return checkagain(whatsit,whatsitcodes.literal )(...) end,
+ [whatsitcodes.latelua] = flushlatelua,
+ [whatsitcodes.userdefined] = flushuserdefined,
+ [whatsitcodes.savepos] = flushsavepos,
+ [whatsitcodes.save] = flushers.save or function(...) return checkagain(whatsit,whatsitcodes.save )(...) end,
+ [whatsitcodes.restore] = flushers.restore or function(...) return checkagain(whatsit,whatsitcodes.restore )(...) end,
+ [whatsitcodes.setmatrix] = flushers.setmatrix or function(...) return checkagain(whatsit,whatsitcodes.setmatrix )(...) end,
+ [whatsitcodes.open] = flushopenout,
+ [whatsitcodes.close] = flushcloseout,
+ [whatsitcodes.write] = flushwriteout,
+ [whatsitcodes.startmatrix] = flushers.startmatrix or function(...) return checkagain(whatsit,whatsitcodes.startmatrix )(...) end,
+ [whatsitcodes.stopmatrix] = flushers.stopmatrix or function(...) return checkagain(whatsit,whatsitcodes.stopmatrix )(...) end,
+ [whatsitcodes.startscaling] = flushers.startscaling or function(...) return checkagain(whatsit,whatsitcodes.startscaling )(...) end,
+ [whatsitcodes.stopscaling] = flushers.stopscaling or function(...) return checkagain(whatsit,whatsitcodes.stopscaling )(...) end,
+ [whatsitcodes.startrotation] = flushers.startrotation or function(...) return checkagain(whatsit,whatsitcodes.startrotation )(...) end,
+ [whatsitcodes.stoprotation] = flushers.stoprotation or function(...) return checkagain(whatsit,whatsitcodes.stoprotation )(...) end,
+ [whatsitcodes.startmirroring] = flushers.startmirroring or function(...) return checkagain(whatsit,whatsitcodes.startmirroring)(...) end,
+ [whatsitcodes.stopmirroring] = flushers.stopmirroring or function(...) return checkagain(whatsit,whatsitcodes.stopmirroring )(...) end,
+ [whatsitcodes.startclipping] = flushers.startclipping or function(...) return checkagain(whatsit,whatsitcodes.startclipping )(...) end,
+ [whatsitcodes.stopclipping] = flushers.stopclipping or function(...) return checkagain(whatsit,whatsitcodes.stopclippin )(...) end,
+ [whatsitcodes.setstate] = flushers.setstate or function(...) return checkagain(whatsit,whatsitcodes.setstate )(...) end,
+ }, checkagain)
+
+ flushers.whatsit = whatsit
+
+ flushers.pushleaderlevel = pushleaderlevel
+ flushers.popleaderlevel = popleaderlevel
+
+ end
+
+end
+
+function drivers.install(specification)
+ local name = specification.name
+ if not name then
+ report("missing driver name")
+ return
+ end
+ local actions = specification.actions
+ if not actions then
+ report("no actions for driver %a",name)
+ return
+ end
+ local flushers = specification.flushers
+ if not flushers then
+ report("no flushers for driver %a",name)
+ return
+ end
+ installwhatsits(name,flushers)
+ -- report("driver %a is installed",name)
+ setmetatableindex(actions, defaulthandlers)
+ setmetatableindex(flushers, function() return dummy end)
+ instances[name] = specification
+end
+
+function drivers.convert(boxnumber)
+ if currentinstance then
+ callbacks.functions.start_page_number()
+ starttiming(drivers)
+ convert(currentinstance,boxnumber,texgetcount("realpageno"))
+ stoptiming(drivers)
+ callbacks.functions.stop_page_number()
+ end
+end
+
+function drivers.outputfilename()
+ if currentinstance then
+ return outputfilename(currentinstance)
+ end
+end
+
+luatex.wrapup(function()
+ if currentinstance and wrapup and not wrappedup[currentdriver] then
+ starttiming(drivers)
+ wrapup(currentinstance)
+ stoptiming(drivers)
+ wrappedup[currentdriver] = true
+ cleanedup[currentdriver] = true
+ end
+end)
+
+luatex.cleanup(function()
+ if currentinstance and cleanup and not cleanedup[currentdriver] then
+ starttiming(drivers)
+ cleanup(currentinstance)
+ stoptiming(drivers)
+ wrappedup[currentdriver] = true
+ cleanedup[currentdriver] = true
+ end
+end)
+
+function drivers.enable(name)
+ if name ~= currentdriver then
+ if currentinstance then
+ starttiming(drivers)
+ cleanup(currentinstance)
+ stoptiming(drivers)
+ end
+ currentdriver = name or "default"
+ currentinstance = instances[currentdriver]
+ if currentinstance then
+ local actions = currentinstance.actions
+ prepare = actions.prepare
+ wrapup = actions.wrapup
+ cleanup = actions.cleanup
+ convert = actions.convert
+ outputfilename = actions.outputfilename
+ --
+ if prepare and not prepared[currentdriver] then
+ starttiming(drivers)
+ prepare(currentinstance)
+ stoptiming(drivers)
+ prepared[currentdriver] = true
+ end
+ else
+ report("bad driver")
+ end
+ end
+end
+
+statistics.register("driver time",function()
+ return statistics.elapsedseconds(drivers)
+end)
+
+interfaces.implement {
+ name = "shipoutpage",
+ arguments = "integer",
+ actions = drivers.convert,
+}
+
+interfaces.implement {
+ name = "enabledriver",
+ arguments = "string",
+ actions = drivers.enable,
+}
+
+-- The default driver:
+
+do
+
+ local filename = nil
+
+ drivers.install {
+ name = "default",
+ actions = {
+ convert = drivers.converters.engine,
+ outputfilename = function(driver)
+ if not filename then
+ filename = addsuffix(tex.jobname,"pdf")
+ end
+ return filename
+ end,
+ },
+ flushers = {
+ -- we always need this entry
+ },
+ }
+
+end
+
+-- No driver:
+
+do
+
+ drivers.install {
+ name = "none",
+ actions = { },
+ flushers = { },
+ }
+
+end
+
+do
+
+ local function prepare(driver)
+ converter = drivers.converters.lmtx
+ end
+
+ local function convert(driver,boxnumber,pagenumber)
+ converter(driver,texgetbox(boxnumber),"page",pagenumber)
+ end
+
+ drivers.install {
+ name = "empty",
+ actions = {
+ prepare = prepare,
+ convert = convert,
+ },
+ flushers = { },
+ }
+
+end
+
+--
+
+setmetatableindex(instances,function() return instances.default end)
+
+-- for now:
+
+drivers.enable("default")
diff --git a/Master/texmf-dist/tex/context/base/mkxl/driv-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/driv-ini.mkxl
new file mode 100644
index 00000000000..947eb37779d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/driv-ini.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=driv-ini,
+%D version=2018.07.26,
+%D title=\CONTEXT\ Driver Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Driver Macros / Initialization}
+
+\registerctxluafile{driv-ini}{autosuffix}
+
+\unprotect
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/driv-shp.lmt b/Master/texmf-dist/tex/context/base/mkxl/driv-shp.lmt
new file mode 100644
index 00000000000..d3a47bf0d2e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/driv-shp.lmt
@@ -0,0 +1,1358 @@
+if not modules then modules = { } end modules ['driv-shp'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to driv-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next = type, next
+
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+local concat = table.concat
+local keys = table.keys
+local sortedhash = table.sortedhash
+local splitstring = string.split
+local find = string.find
+local stripstring = string.strip
+local idiv = number.idiv
+local sequenced = table.sequenced
+local round = math.round
+local extract = bit32.extract
+local nuts = nodes.nuts
+
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local getdirection = nuts.getdirection
+local getlist = nuts.getlist
+local getoffsets = nuts.getoffsets
+local getorientation = nuts.getorientation
+local getfield = nuts.getfield
+local getwhd = nuts.getwhd
+local getkern = nuts.getkern
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+----- getwidth = nuts.getwidth
+local getnext = nuts.getnext
+local getsubtype = nuts.getsubtype
+local getid = nuts.getid
+local getleader = nuts.getleader
+----- getglue = nuts.getglue
+local getshift = nuts.getshift
+local getdata = nuts.getdata
+----- getexpansion = nuts.getexpansion
+local getreplace = nuts.getreplace
+local setreplace = nuts.setreplace
+local getfont = nuts.getfont
+local getkerndimension = nuts.getkerndimension
+
+local setdirection = nuts.setdirection
+local setfield = nuts.setfield
+local setlink = nuts.setlink
+
+local isglyph = nuts.isglyph
+local findtail = nuts.tail
+local nextdir = nuts.traversers.dir
+local nextnode = nuts.traversers.node
+
+----- rangedimensions = node.direct.rangedimensions -- nuts ?
+local effectiveglue = nuts.effective_glue
+local start_of_par = nuts.start_of_par
+local dirdimensions = nuts.dirdimensions
+
+local texget = tex.get
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local characters = fonthashes.characters
+local parameters = fonthashes.parameters
+
+local nodecodes = nodes.nodecodes
+local whatsitcodes = nodes.whatsitcodes
+local gluecodes = nodes.gluecodes
+local dircodes = nodes.dircodes
+local dirvalues = nodes.dirvalues
+local subtypes = nodes.subtypes
+
+local lefttoright_code = dirvalues.lefttoright
+local righttoleft_code = dirvalues.righttoleft
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local dir_code = nodecodes.dir
+local disc_code = nodecodes.disc
+local math_code = nodecodes.math
+local rule_code = nodecodes.rule
+local whatsit_code = nodecodes.whatsit
+----- penalty_code = nodecodes.penalty
+----- boundary_code = nodecodes.boundary
+
+local leaders_code = gluecodes.leaders
+local cleaders_code = gluecodes.cleaders
+local xleaders_code = gluecodes.xleaders
+local gleaders_code = gluecodes.gleaders
+
+local spaceskip_code = gluecodes.spaceskip
+
+local getpagedimensions = layouts.getpagedimensions
+
+local drivers = drivers
+local instances = drivers.instances
+
+local report = logs.reporter("drivers")
+
+---------------------------------------------------------------------------------------
+
+local lastfont = nil
+local fontcharacters = nil
+
+local magicconstants = tex.magicconstants
+local trueinch = magicconstants.trueinch
+local maxdimen = magicconstants.maxdimen
+local running = magicconstants.running
+
+local pos_h = 0
+local pos_v = 0
+local pos_r = lefttoright_code
+local shippingmode = "none"
+
+local abs_max_v = 0
+local abs_max_h = 0
+
+local shipbox_h = 0
+local shipbox_v = 0
+local page_size_h = 0
+local page_size_v = 0
+----- page_h_origin = 0 -- trueinch
+----- page_v_origin = 0 -- trueinch
+
+local initialize
+local finalize
+local updatefontstate
+local pushorientation
+local poporientation
+local flushcharacter
+local flushfontchar
+local flushrule
+local flushliteral
+local flushwhatsit
+
+-- make local
+
+function drivers.getpos () return round(pos_h), round(pos_v) end
+function drivers.getrpos() return round(pos_h), round(pos_v), pos_r end
+function drivers.gethpos() return round(pos_h) end
+function drivers.getvpos() return round(pos_v) end
+
+-- characters
+
+local flush_character do
+
+ local stack = setmetatableindex("table")
+ local level = 0
+ local nesting = 0
+ local main = 0
+
+ -- experiment (smaller page stream but might be fragile)
+
+ local tospace = false directives.register("backends.spaces", function(v) tospace = v end)
+
+ -- todo: cache streams
+
+ local default = 16384 * number.dimenfactors.bp -- 65536 // 4
+
+ local vfinjectors = fonts.helpers.vfinjectors
+
+ local function flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,factor,vfcommands,sx,sy)
+
+ if nesting > 100 then
+ return
+ elseif nesting == 0 then
+ main = font
+ end
+
+ nesting = nesting + 1
+
+ local saved_h = pos_h
+ local saved_v = pos_v
+ local saved_r = pos_r
+ pos_r = lefttoright_code
+
+ local data = fontdata[font]
+ local fnt = font
+ local fonts = data.fonts
+ local siz = (data.parameters.factor or 1)/65536
+
+ local function flushchar(font,char,fnt,chr,f,e)
+ if fnt then
+ local nest = char ~= chr or font ~= fnt
+ if fnt == 0 then
+ fnt = main
+ end
+ return flush_character(current,fnt,chr,factor,nest,pos_h,pos_v,pos_r,f,e)
+ else
+ return 0
+ end
+ end
+
+ -- we assume resolved fonts: id mandate but maybe also size
+
+ -- we could map left, right, up, down -> offset
+ -- we could map char, font, slot to -> slot
+
+ for i=1,#vfcommands do
+ local packet = vfcommands[i]
+ local command = packet[1]
+ if command == "char" then
+ local chr = packet[2]
+ local f = packet[3]
+ local e = packet[4]
+ pos_h = pos_h + flushchar(font,char,fnt,chr,f,e)
+ elseif command == "slot" then
+ local index = packet[2]
+ local chr = packet[3]
+ local f = packet[4]
+ local e = packet[5]
+ if index == 0 then
+ pos_h = pos_h + flushchar(font,char,font,chr,f,e)
+ else
+ local okay = fonts and fonts[index]
+ if okay then
+ local fnt = okay.id
+ if fnt then
+ pos_h = pos_h + flushchar(font,char,fnt,chr,f,e)
+ end
+ else
+ -- safeguard, we assume the font itself (often index 1)
+ pos_h = pos_h + flushchar(font,char,font,chr,f,e)
+ end
+ end
+ elseif command == "use" then
+ local index = packet[2]
+ if index then
+ local fnt
+ if index == 0 then
+ fnt = font
+ else
+ local okay = fonts and fonts[index]
+ if okay then
+ fnt = okay.id
+ end
+ end
+ if fnt then
+ -- not efficient but ok for now as experiment
+ local d = characters[fnt]
+ if d then
+ for i=3,#packet do
+ local chr = packet[i]
+ local dat = d[chr]
+ if dat then
+ flushfontchar(fnt,chr,dat)
+ end
+ end
+ end
+ end
+ end
+ elseif command == "right" then
+ local h = packet[2] -- already scaled
+ if h ~= 0 then
+ if factor ~= 0 then
+ h = h + h * factor / 1000 -- expansion
+ end
+ pos_h = pos_h + h * sx
+ end
+ elseif command == "left" then
+ local h = packet[2] -- already scaled
+ if h ~= 0 then
+ if factor ~= 0 then
+ h = h + h * factor / 1000 -- expansion
+ end
+ pos_h = pos_h - h * sx
+ end
+ elseif command == "down" then
+ local v = packet[2] -- already scaled
+ if v and v ~= 0 then
+ pos_v = pos_v - v * sy
+ end
+ elseif command == "up" then
+ local v = packet[2] -- already scaled
+ if v and v ~= 0 then
+ pos_v = pos_v + v * sy
+ end
+ elseif command == "offset" then
+ local ph = pos_h
+ local pv = pos_v
+ local h = packet[2] or 0
+ local v = packet[3] or 0
+ local c = packet[4]
+ if h ~= 0 then
+ if factor ~= 0 then
+ h = h + h * factor / 1000 -- expansion
+ end
+ pos_h = pos_h + h * sx
+ end
+ if v and v ~= 0 then
+ pos_v = pos_v + v * sy
+ end
+ if c then
+ flushchar(font,char,fnt,c)
+ pos_h = ph
+ pos_v = pv
+ end
+ elseif command == "compose" then
+ local ph = pos_h
+ local pv = pos_v
+ local h = packet[2] or 0
+ local v = packet[3] or 0
+ local c = packet[4]
+ if h ~= 0 then
+ if factor ~= 0 then
+ h = h + h * factor / 1000 -- expansion
+ end
+ pos_h = pos_h + h * sx
+ end
+ if v and v ~= 0 then
+ pos_v = pos_v + v * sy
+ end
+ if c then
+ flushchar(font,char,fnt,c)
+ pos_h = ph
+ pos_v = pv
+ end
+ elseif command == "push" then
+ level = level + 1
+ local s = stack[level]
+ s[1] = pos_h
+ s[2] = pos_v
+ elseif command == "pop" then
+ if level > 0 then
+ local s = stack[level]
+ pos_h = s[1]
+ pos_v = s[2]
+ level = level - 1
+ end
+ elseif command == "rule" then
+ local size_v = packet[2]
+ local size_h = packet[3]
+ if size_h > 0 and size_v > 0 then
+ if factor ~= 0 then
+ size_h = size_h + size_h * factor / 1000
+ end
+ if size_h > 0 then
+ size_h = size_h * sx
+ size_v = size_v * sy
+ flushsimplerule(pos_h,pos_v,pos_r,size_h,size_v)
+ pos_h = pos_h + size_h
+ end
+ end
+ elseif command == "frame" then
+ -- d:width d:height d:depth d:rulethickness b:outline b:advance b:baseline s:color
+ local width = packet[2]
+ if width > 0 then
+ local height = packet[3] or 0
+ local depth = packet[4] or 0
+ local total = height + depth
+ if total > 0 then
+ if factor ~= 0 then
+ width = width + width * factor / 1000
+ end
+ if width > 0 then
+ local line = packet[5] or default
+ local outline = not packet[6]
+ local advance = not packet[7]
+ local baseline = outline and packet[8]
+ local color = packet[9] -- no longer needed probably
+ if color then
+ vfinjectors.startcolor(pos_h,pos_v,color) -- takes packet or string
+ end
+ width = width * sx
+ height = height * sy
+ depth = depth * sy
+ flushspecialrule(pos_h,pos_v,pos_r,width,height,depth,line,outline,baseline)
+ if color then
+ vfinjectors.stopcolor()
+ end
+ if advance then
+ pos_h = pos_h + width
+ end
+ end
+ end
+ end
+ elseif command == "font" then
+ local index = packet[2]
+ local okay = fonts and fonts[index]
+ if okay then
+ fnt = okay.id or fnt -- or maybe just return
+ end
+ elseif command == "lua" then
+ local code = packet[2]
+ local kind = type(code)
+ if kind ~= "function" then
+ code = loadstring(code)
+ kind = type(code)
+ end
+ if kind == "function" then
+ code(font,char,pos_h,pos_v,sx,sy)
+ end
+ elseif command == "node" then -- obsolete
+ local h = packet[2]
+ hlist_out(h,getlist(h))
+ else
+ local injector = vfinjectors[command]
+ if injector then
+ injector(pos_h,pos_v,packet)
+ end
+ end
+ -- image : not needed, maybe some day
+ -- pdfmode : not used
+ -- special : makes no sense
+ -- nop : the official ignore
+ -- scale : not supported
+ end
+
+ pos_h = saved_h
+ pos_v = saved_v
+ pos_r = saved_r
+
+ nesting = nesting - 1
+ end
+
+ local onetimemessage -- could be defined later (todo: make plug for this)
+
+ local getxyscales = nuts.getxyscales
+
+ flush_character = function(current,font,char,factor,vfcommands,pos_h,pos_v,pos_r,f,e)
+
+ if font ~= lastfont then
+ lastfont = font
+ fontcharacters = characters[font]
+ updatefontstate(font)
+ end
+
+ local data = fontcharacters[char]
+ if not data then
+ if char > 0 then
+ if not onetimemessage then
+ onetimemessage = fonts.loggers.onetimemessage
+ end
+ onetimemessage(font,char,"missing")
+ end
+ return 0, 0, 0
+ end
+ if vfcommands then
+ vfcommands = data.commands
+ end
+ local width, height, depth, naturalwidth, sx, sy
+ if current then
+ naturalwidth, height, depth, factor = getwhd(current,true) -- also get corrected width
+ sx, sy = getxyscales(current) -- maybe: getwhdfs
+ if factor == 0 then
+ width = naturalwidth
+ else
+ -- width = (1.0 + factor/1000000.0) * naturalwidth
+ width = naturalwidth + naturalwidth * factor/1000000.0
+ -- width = naturalwidth + naturalwidth * 0.000001 * factor
+ end
+ else
+ width = data.width or 0
+ height = data.height or 0
+ depth = data.depth or 0
+ naturalwidth = width
+ if not factor then
+ factor = 0
+ end
+ sx = 1
+ sy = 1
+ end
+ if pos_r == righttoleft_code then
+ pos_h = pos_h - width -- here ?
+ end
+ if vfcommands then
+ flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,factor,vfcommands,sx,sy) -- also f ?
+ else
+ -- kind of messy that we do orientation here and offsets elsewhere .. this might change
+ local orientation = data.orientation
+ if orientation and (orientation == 1 or orientation == 3) then
+ local x = data.xoffset
+ local y = data.yoffset
+ if x then
+ pos_h = pos_h + x
+ end
+ if y then
+ pos_v = pos_v + y
+ end
+ pushorientation(orientation,pos_h,pos_v)
+ flushcharacter(current,pos_h,pos_v,pos_r,font,char,data,f,e,factor,sx,sy) -- ,naturalwidth,width)
+ poporientation(orientation,pos_h,pos_v)
+ else
+ flushcharacter(current,pos_h,pos_v,pos_r,font,char,data,f,e,factor,sx,sy) -- ,naturalwidth,width)
+ end
+ end
+ return width, height, depth
+ end
+
+end
+
+-- end of characters
+
+local function reset_state()
+ pos_h = 0
+ pos_v = 0
+ pos_r = lefttoright_code
+ shipbox_h = 0
+ shipbox_v = 0
+ shippingmode = "none"
+ page_size_h = 0
+ page_size_v = 0
+ -- page_h_origin = 0 -- trueinch
+ -- page_v_origin = 0 -- trueinch
+end
+
+-- local function dirstackentry(t,k)
+-- local v = {
+-- cur_h = 0,
+-- cur_v = 0,
+-- ref_h = 0,
+-- ref_v = 0,
+-- }
+-- t[k] = v
+-- return v
+-- end
+--
+-- local dirstack = setmetatableindex(dirstackentry)
+--
+-- local function reset_dir_stack()
+-- dirstack = setmetatableindex(dirstackentry)
+-- end
+
+local dirstack = { }
+
+local function reset_dir_stack()
+ dirstack = { }
+end
+
+local hlist_out, vlist_out do
+
+ local function applyanchor(orientation,x,y,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
+ local ot = extract(orientation, 0,4)
+ local ay = extract(orientation, 4,4)
+ local ax = extract(orientation, 8,4)
+ local of = extract(orientation,12,4)
+ if ot == 4 then
+ ot, ay = 0, 1
+ elseif ot == 5 then
+ ot, ay = 0, 2
+ end
+ if ot == 0 or ot == 2 then
+ if ax == 1 then x = x - width
+ elseif ax == 2 then x = x + width
+ elseif ax == 3 then x = x - width/2
+ elseif ax == 4 then x = x + width/2
+ end
+ if ot == 2 then
+ doffset, hoffset = hoffset, doffset
+ end
+ if ay == 1 then y = y - doffset
+ elseif ay == 2 then y = y + hoffset
+ elseif ay == 3 then y = y + (doffset + hoffset)/2 - doffset
+ end
+ elseif ot == 1 or ot == 3 then
+ if ay == 1 then y = y - height
+ elseif ay == 2 then y = y + height
+ elseif ay == 3 then y = y - height/2
+ end
+ if ot == 1 then
+ doffset, hoffset = hoffset, doffset
+ end
+ if ax == 1 then x = x - width
+ elseif ax == 2 then x = x + width
+ elseif ax == 3 then x = x - width/2
+ elseif ax == 4 then x = x + width/2
+ elseif ax == 5 then x = x - hoffset
+ elseif ax == 6 then x = x + doffset
+ end
+ end
+ return ot, x + xoffset, y - yoffset
+ end
+
+ -- to be checked: begin- or enddir kan nil zijn, weird
+
+ -- local function calculate_width_to_enddir(this_box,begindir) -- can be a helper
+ -- local dir_nest = 1
+ -- local enddir = begindir
+ -- for current, subtype in nextdir, getnext(begindir) do
+ -- if subtype == normaldir_code then -- todo
+ -- dir_nest = dir_nest + 1
+ -- else
+ -- dir_nest = dir_nest - 1
+ -- end
+ -- if dir_nest == 0 then -- does the type matter
+ -- enddir = current
+ -- local width = rangedimensions(this_box,begindir,enddir)
+ -- return enddir, width
+ -- end
+ -- end
+ -- if enddir == begindir then
+ -- local width = rangedimensions(this_box,begindir) -- ,enddir)
+ -- return enddir, width
+ -- end
+ -- return enddir, 0
+ -- end
+
+ -- check frequencies of nodes
+
+ hlist_out = function(this_box,current)
+ local ref_h = pos_h
+ local ref_v = pos_v
+ local ref_r = pos_r
+ pos_r = getdirection(this_box)
+ local boxwidth,
+ boxheight,
+ boxdepth = getwhd(this_box)
+
+ local cur_h = 0
+ local cur_v = 0
+
+ -- if not current then
+ -- current = getlist(this_box)
+ -- end
+
+ -- we can encounter par, boundary and penalty nodes but a special
+ -- iterator over content nodes won't save much
+ for current, id, subtype in nextnode, current do
+ if id == glyph_code then
+ local char, font = isglyph(current)
+ local x_offset, y_offset, left, right, raise = getoffsets(current)
+ if x_offset ~= 0 or y_offset ~= 0 then
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - (cur_h + x_offset)
+ else
+ pos_h = ref_h + (cur_h + x_offset)
+ end
+ pos_v = ref_v - (cur_v - y_offset)
+ -- synced
+ end
+ pos_v = pos_v + raise
+ pos_h = pos_h - left
+ local wd = flush_character(current,font,char,false,true,pos_h,pos_v,pos_r)
+ cur_h = cur_h + wd - right
+ elseif id == glue_code then
+ local gluewidth = effectiveglue(current,this_box)
+ if gluewidth ~= 0 then
+ if subtype >= leaders_code then
+ local leader = getleader(current)
+ if leader then
+ local width, height, depth = getwhd(leader)
+ if getid(leader) == rule_code then
+ if gluewidth > 0 then
+ if height == running then
+ height = boxheight
+ end
+ if depth == running then
+ depth = boxdepth
+ end
+ local total = height + depth
+ if total > 0 then
+ if pos_r == righttoleft_code then
+ pos_h = pos_h - gluewidth
+ end
+ pos_v = pos_v - depth
+ flushrule(leader,pos_h,pos_v,pos_r,gluewidth,total,getsubtype(leader))
+ end
+ cur_h = cur_h + gluewidth
+ end
+ elseif width > 0 and gluewidth > 0 then
+ local boxdir = getdirection(leader) or lefttoright_code
+ gluewidth = gluewidth + 10
+ local edge = cur_h + gluewidth
+ local lx = 0
+ if subtype == gleaders_code then
+ local save_h = cur_h
+ if pos_r == righttoleft_code then
+ cur_h = ref_h - shipbox_h - cur_h
+ cur_h = width * (cur_h / width)
+ cur_h = ref_h - shipbox_h - cur_h
+ else
+ cur_h = cur_h + ref_h - shipbox_h
+ cur_h = width * (cur_h / width)
+ cur_h = cur_h - ref_h - shipbox_h
+ end
+ if cur_h < save_h then
+ cur_h = cur_h + width
+ end
+ elseif subtype == leaders_code then
+ local save_h = cur_h
+ cur_h = width * (cur_h / width)
+ if cur_h < save_h then
+ cur_h = cur_h + width
+ end
+ else
+ lq = gluewidth / width
+ lr = gluewidth % width
+ if subtype == cleaders_code then
+ cur_h = cur_h + lr / 2
+ else
+ lx = lr / (lq + 1)
+ cur_h = cur_h + (lr - (lq - 1) * lx) / 2
+ end
+ end
+ local shift = getshift(leader)
+ pushleaderlevel()
+ while cur_h + width <= edge do
+ local basepoint_h = 0
+ -- local basepoint_v = shift
+ if boxdir ~= pos_r then
+ basepoint_h = boxwidth
+ end
+ -- synch_pos_with_cur(ref_h,ref_v,cur_h + basepoint_h,shift)
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - (cur_h + basepoint_h)
+ else
+ pos_h = ref_h + (cur_h + basepoint_h)
+ end
+ pos_v = ref_v - shift
+ -- synced
+ if getid(leader) == vlist_code then
+ vlist_out(leader,getlist(leader))
+ else
+ hlist_out(leader,getlist(leader))
+ end
+ cur_h = cur_h + width + lx
+ end
+ popleaderlevel()
+ cur_h = edge - 10
+ else
+ cur_h = cur_h + gluewidth
+ end
+ else
+ cur_h = cur_h + gluewidth
+ end
+ else
+ if tospace and subtype == spaceskip_code then
+ -- todo: flush_space
+ flush_character(false,getfont(current),32,false,true,pos_h,pos_v,pos_r) -- we need tp pass current for scale?
+ end
+ cur_h = cur_h + gluewidth
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local width, height, depth = getwhd(current)
+ local list = getlist(current)
+ if list then
+ local boxdir = getdirection(current) or lefttoright_code
+ local shift, orientation = getshift(current)
+ if not orientation then
+ local basepoint_h = boxdir ~= pos_r and width or 0
+ -- local basepoint_v = shift
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - (cur_h + basepoint_h)
+ else
+ pos_h = ref_h + (cur_h + basepoint_h)
+ end
+ pos_v = ref_v - shift
+ -- synced
+ if id == vlist_code then
+ vlist_out(current,list)
+ else
+ hlist_out(current,list)
+ end
+ elseif orientation == 0x1000 then
+ local orientation, xoffset, yoffset = getorientation(current)
+ local basepoint_h = boxdir ~= pos_r and width or 0
+ -- local basepoint_v = shift
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - (cur_h + basepoint_h + xoffset)
+ else
+ pos_h = ref_h + (cur_h + basepoint_h + xoffset)
+ end
+ pos_v = ref_v - (shift - yoffset)
+ -- synced
+ if id == vlist_code then
+ vlist_out(current,list)
+ else
+ hlist_out(current,list)
+ end
+ else
+ local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
+ local orientation, basepoint_h, basepoint_v = applyanchor(orientation,0,shift,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
+ if orientation == 1 then
+ basepoint_h = basepoint_h + doffset
+ if boxdir == pos_r then
+ basepoint_v = basepoint_v - height
+ end
+ elseif orientation == 2 then
+ if boxdir == pos_r then
+ basepoint_h = basepoint_h + width
+ end
+ elseif orientation == 3 then
+ basepoint_h = basepoint_h + hoffset
+ if boxdir ~= pos_r then
+ basepoint_v = basepoint_v - height
+ end
+ end
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - (cur_h + basepoint_h)
+ else
+ pos_h = ref_h + (cur_h + basepoint_h)
+ end
+ pos_v = ref_v - (cur_v + basepoint_v)
+ -- synced
+ pushorientation(orientation,pos_h,pos_v,pos_r)
+ if id == vlist_code then
+ vlist_out(current,list)
+ else
+ hlist_out(current,list)
+ end
+ poporientation(orientation,pos_h,pos_v,pos_r)
+ end
+ end
+ cur_h = cur_h + width
+ elseif id == kern_code then
+ -- we can use getkerndimension(current) but then we get rounded values so for
+ -- now we calculate ourselves
+ local kern, factor = getkern(current,true)
+ if kern ~= 0 then
+ if factor ~= 0 then
+ cur_h = cur_h + (1.0 + factor/1000000.0) * kern
+ else
+ cur_h = cur_h + kern
+ end
+ end
+ elseif id == rule_code then
+ local width, height, depth = getwhd(current)
+ if width > 0 then
+ if height == running then
+ height = boxheight
+ end
+ if depth == running then
+ depth = boxdepth
+ end
+ local total = height + depth
+ if total > 0 then
+ local xoffset, yoffset, left, right = getoffsets(current) -- top bottom
+ if left ~= 0 then
+ pos_v = pos_v + left
+ total = total - left
+ end
+ if right ~= 0 then
+ depth = depth - right
+ total = total - right
+ end
+ if pos_r == righttoleft_code then
+ pos_h = pos_h - width
+ xoffset = - xoffset
+ end
+ pos_v = pos_v - depth
+ flushrule(current,pos_h + xoffset,pos_v + yoffset,pos_r,width,total,subtype)
+ end
+ end
+ cur_h = cur_h + width
+ elseif id == math_code then
+ -- local kern = getkern(current)
+ -- if kern ~= 0 then
+ -- cur_h = cur_h + kern
+ -- else
+ cur_h = cur_h + effectiveglue(current,this_box)
+ -- end
+ elseif id == dir_code then
+ -- We normally have proper begin-end pairs. A begin without end is (silently) handled
+ -- and an end without a begin will be (silently) skipped we only need to move forward
+ -- so we then have a faster calculation.
+ local dir, cancel = getdirection(current)
+ if cancel then
+ local ds = dirstack[current]
+ if ds then
+ ref_h = ds.ref_h
+ ref_v = ds.ref_v
+ cur_h = ds.cur_h
+ cur_v = ds.cur_v
+ else
+ -- pardir
+ end
+ pos_r = dir
+ else
+ local width, enddir = dirdimensions(this_box,current)
+ local new_h = cur_h + width
+ if dir ~= pos_r then
+ cur_h = new_h
+ end
+ if enddir ~= current then
+ dirstack[enddir] = {
+ cur_h = new_h,
+ cur_v = cur_v,
+ ref_h = ref_h,
+ ref_v = ref_v,
+ }
+ setdirection(enddir,pos_r)
+ end
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - cur_h
+ else
+ pos_h = ref_h + cur_h
+ end
+ pos_v = ref_v - cur_v
+ -- synced
+ ref_h = pos_h
+ ref_v = pos_v
+ cur_h = 0
+ cur_v = 0
+ pos_r = dir
+ goto synced
+ end
+ elseif id == whatsit_code then
+ flushwhatsit[subtype](current,pos_h,pos_v)
+ elseif id == disc_code then
+ local replace, tail = getreplace(current)
+ -- actually, we no longer have these select discs in the packaged list ... it's about
+ -- time to clean up the disc code a bit further
+ if replace then -- and subtype ~= select_disc_code then
+ setlink(tail,getnext(current))
+ setlink(current,replace)
+ setreplace(current)
+ end
+ -- elseif id == par_code and start_of_par(current) then
+ -- local pardir = getdirection(current) or lefttoright_code
+ -- if pardir == righttoleft_code then
+ -- end
+ -- end
+ else
+ -- penalty, boundary ... no dimensions
+ goto synced
+ end
+ -- There is no gain in skipping over this when we have zero progression
+ -- and such.
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - cur_h
+ else
+ pos_h = ref_h + cur_h
+ end
+ pos_v = ref_v - cur_v
+ ::synced::
+ end
+ pos_h = ref_h
+ pos_v = ref_v
+ pos_r = ref_r
+ end
+
+ vlist_out = function(this_box,current)
+ local ref_h = pos_h
+ local ref_v = pos_v
+ local ref_r = pos_r
+ pos_r = getdirection(this_box)
+
+ local boxwidth,
+ boxheight,
+ boxdepth = getwhd(this_box)
+
+ local cur_h = 0
+ local cur_v = - boxheight
+ local top_edge = cur_v
+
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - cur_h
+ else
+ pos_h = ref_h + cur_h
+ end
+ pos_v = ref_v - cur_v
+ -- synced
+
+ -- if not current then
+ -- current = getlist(this_box)
+ -- end
+
+ -- while current do
+ -- local id = getid(current)
+ for current, id, subtype in nextnode, current do
+ if id == glue_code then
+ local glueheight = effectiveglue(current,this_box)
+ if glueheight ~= 0 then
+ if subtype >= leaders_code then
+ local leader = getleader(current)
+ if leader then
+ local width, height, depth = getwhd(leader)
+ local total = height + depth
+ if getid(leader) == rule_code then
+ depth = 0 -- hm
+ total = glueheight -- forgotten ... needs testing
+ if total > 0 then
+ if width == running then
+ width = boxwidth
+ end
+ if width > 0 then
+ if pos_r == righttoleft_code then
+ cur_h = cur_h - width
+ end
+ flushrule(leader,pos_h,pos_v - total,pos_r,width,total,getsubtype(leader))
+ end
+ cur_v = cur_v + total
+ end
+ elseif total > 0 and glueheight > 0 then
+ glueheight = glueheight + 10
+ local edge = cur_v + glueheight
+ local ly = 0
+ if subtype == gleaders_code then
+ save_v = cur_v
+ cur_v = ref_v - shipbox_v - cur_v
+ cur_v = total * (cur_v / total)
+ cur_v = ref_v - shipbox_v - cur_v
+ if cur_v < save_v then
+ cur_v = cur_v + total
+ end
+ elseif subtype == leaders_code then -- aleader
+ save_v = cur_v
+ cur_v = top_edge + total * ((cur_v - top_edge) / total)
+ if cur_v < save_v then
+ cur_v = cur_v + total
+ end
+ else
+ lq = glueheight / total
+ lr = glueheight % total
+ if subtype == cleaders_code then
+ cur_v = cur_v + lr / 2
+ else
+ ly = lr / (lq + 1)
+ cur_v = cur_v + (lr - (lq - 1) * ly) / 2
+ end
+ end
+ local shift = getshift(leader)
+ pushleaderlevel()
+ while cur_v + total <= edge do -- todo: <= edge - total
+ -- synch_pos_with_cur(ref_h, ref_v, getshift(leader), cur_v + height)
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - shift
+ else
+ pos_h = ref_h + shift
+ end
+ pos_v = ref_v - (cur_v + height)
+ -- synced
+ if getid(leader) == vlist_code then
+ vlist_out(leader,getlist(leader))
+ else
+ hlist_out(leader,getlist(leader))
+ end
+ cur_v = cur_v + total + ly
+ end
+ popleaderlevel()
+ cur_v = edge - 10
+ else
+ cur_v = cur_v + glueheight
+ end
+ end
+ else
+ cur_v = cur_v + glueheight
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local width, height, depth = getwhd(current)
+ local list = getlist(current)
+ if list then
+ local boxdir = getdirection(current) or lefttoright_code
+ local shift, orientation = getshift(current)
+ if not orientation then
+ -- local basepoint_h = shift
+ -- local basepoint_v = height
+ if boxdir ~= pos_r then
+ shift = shift + width
+ end
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - shift
+ else
+ pos_h = ref_h + shift
+ end
+ pos_v = ref_v - (cur_v + height)
+ -- synced
+ if id == vlist_code then
+ vlist_out(current,list)
+ else
+ hlist_out(current,list)
+ end
+ elseif orientation == 0x1000 then
+ local orientation, xoffset, yoffset = getorientation(current)
+ -- local basepoint_h = shift
+ -- local basepoint_v = height
+ if boxdir ~= pos_r then
+ shift = shift + width
+ end
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - (shift + xoffset)
+ else
+ pos_h = ref_h + (shift + xoffset)
+ end
+ pos_v = ref_v - (cur_v + height - yoffset)
+ -- synced
+ if id == vlist_code then
+ vlist_out(current,list)
+ else
+ hlist_out(current,list)
+ end
+ else
+ local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
+ local orientation, basepoint_h, basepoint_v = applyanchor(orientation,shift,height,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
+ if orientation == 1 then
+ basepoint_h = basepoint_h + width - height
+ basepoint_v = basepoint_v - height
+ elseif orientation == 2 then
+ basepoint_h = basepoint_h + width
+ basepoint_v = basepoint_v + depth - height
+ elseif orientation == 3 then -- weird
+ basepoint_h = basepoint_h + height
+ end
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - basepoint_h
+ else
+ pos_h = ref_h + basepoint_h
+ end
+ pos_v = ref_v - (cur_v + basepoint_v)
+ -- synced
+ pushorientation(orientation,pos_h,pos_v,pos_r)
+ if id == vlist_code then
+ vlist_out(current,list)
+ else
+ hlist_out(current,list)
+ end
+ poporientation(orientation,pos_h,pos_v,pos_r)
+ end
+ end
+ cur_v = cur_v + height + depth
+ elseif id == kern_code then
+ cur_v = cur_v + getkern(current)
+ elseif id == rule_code then
+ local width, height, depth = getwhd(current)
+ local total = height + depth
+ if total > 0 then
+ if width == running then
+ width = boxwidth
+ end
+ if width > 0 then
+ local xoffset, yoffset, left, right = getoffsets(current)
+ if left ~= 0 then
+ width = width - left
+ xoffset = left
+ end
+ if right ~= 0 then
+ width = width - right
+ end
+ if pos_r == righttoleft_code then
+ xoffset = - xoffset - width
+ end
+ flushrule(current,pos_h + xoffset,pos_v - total - yoffset,pos_r,width,total,subtype)
+ end
+ end
+ cur_v = cur_v + total
+ elseif id == whatsit_code then
+ flushwhatsit[subtype](current,pos_h,pos_v)
+ else
+ -- penalty
+ goto synced
+ end
+ if pos_r == righttoleft_code then
+ pos_h = ref_h - cur_h
+ else
+ pos_h = ref_h + cur_h
+ end
+ pos_v = ref_v - cur_v
+ ::synced::
+ end
+ pos_h = ref_h
+ pos_v = ref_v
+ pos_r = ref_r
+ end
+
+end
+
+function drivers.converters.lmtx(driver,box,smode,objnum,specification)
+
+ if not driver then
+ report("error in converter, no driver")
+ return
+ end
+
+ if box then
+ box = tonut(box)
+ else
+ report("error in converter, no box")
+ return
+ end
+
+ local actions = driver.actions
+ local flushers = driver.flushers
+
+ initialize = actions.initialize
+ finalize = actions.finalize
+
+ updatefontstate = flushers.updatefontstate
+
+ pushorientation = flushers.pushorientation
+ poporientation = flushers.poporientation
+
+ pushleaderlevel = flushers.pushleaderlevel
+ popleaderlevel = flushers.popleaderlevel
+
+ flushcharacter = flushers.character
+ flushfontchar = flushers.fontchar
+ flushrule = flushers.rule
+ flushsimplerule = flushers.simplerule
+ flushspecialrule = flushers.specialrule
+ flushliteral = flushers.literal
+ flushwhatsit = flushers.whatsit
+
+ reset_dir_stack()
+ reset_state()
+
+ shippingmode = smode
+
+ local details = nil -- must be outside labels
+
+ local width, height, depth = getwhd(box)
+
+ local total = height + depth
+
+ ----- v_offset_par = 0
+ ----- h_offset_par = 0
+
+ local max_v = total -- + v_offset_par
+ local max_h = width -- + h_offset_par
+
+ if height > maxdimen or depth > maxdimen or width > maxdimen then
+ goto DONE
+ end
+
+ if max_v > maxdimen then
+ goto DONE
+ elseif max_v > abs_max_v then
+ abs_max_v = max_v
+ end
+
+ if max_h > maxdimen then
+ goto DONE
+ elseif max_h > abs_max_h then
+ abs_max_h = max_h
+ end
+
+ if shippingmode == "page" then
+
+ -- We have zero offsets in ConTeXt.
+
+ local pagewidth, pageheight = getpagedimensions()
+
+ pos_r = lefttoright_code
+
+ if pagewidth > 0 then
+ page_size_h = pagewidth
+ else
+ page_size_h = width
+ end
+
+ if page_size_h == 0 then
+ page_size_h = width
+ end
+
+ if pageheight > 0 then
+ page_size_v = pageheight
+ else
+ page_size_v = total
+ end
+
+ if page_size_v == 0 then
+ page_size_v = total
+ end
+
+ local refpoint_h = 0
+ local refpoint_v = page_size_v
+
+ pos_h = refpoint_h
+ pos_v = refpoint_v - height
+
+ else
+
+ page_size_h = width
+ page_size_v = total
+ pos_r = getdirection(box)
+ pos_v = depth
+ pos_h = pos_r == righttoleft_code and width or 0
+
+ end
+
+ shipbox_ref_h = pos_h
+ shipbox_ref_v = pos_v
+
+ details = {
+ shippingmode = smode, -- target
+ boundingbox = { 0, 0, page_size_h, page_size_v },
+ objectnumber = smode ~= "page" and objnum or nil,
+ pagenumber = smode == "page" and objnum or nil,
+ specification = specification,
+ }
+
+ initialize(driver,details)
+
+ lastfont = nil -- this forces a sync each page / object
+
+ if getid(box) == vlist_code then
+ vlist_out(box,getlist(box))
+ else
+ hlist_out(box,getlist(box))
+ end
+
+ ::DONE::
+
+ finalize(driver,details)
+
+ shippingmode = "none"
+end
+
+-- This will move to back-out.lua eventually.
+
+do
+
+ ----- sortedhash = table.sortedhash
+
+ ----- tonut = nodes.tonut
+ local properties = nodes.properties.data
+ local flush = texio.write
+ local flushline = texio.write_nl
+
+ local periods = utilities.strings.newrepeater(".")
+
+ local function showdetails(n,l)
+ local p = properties[tonut(n)]
+ if p then
+ for k, v in sortedhash(p) do
+ local t = type(v)
+ if t == "string" then
+ if find(v,"[\n\r]") then
+ v = "\n" .. stripstring(v) .. "\n"
+ end
+ elseif t == "number" or t == "boolean" then
+ v = tostring(v)
+ elseif t == "table" then
+ v = sequenced(v)
+ else
+ v = "<" .. tostring(v) .. ">"
+ end
+ flushline(periods[l+1] .. " " .. k .. " = " .. v)
+ end
+ end
+ end
+
+ local whatsittracers = {
+ latelua = showdetails,
+ literal = showdetails,
+ }
+
+ callback.register("show_whatsit",function(n,l)
+ local s = nodes.whatsitcodes[n.subtype]
+ flush(" [" .. s .. "]")
+ local w = whatsittracers[s]
+ if w then
+ w(n,l)
+ end
+ end)
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/driv-shp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/driv-shp.mkxl
new file mode 100644
index 00000000000..199215a59ea
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/driv-shp.mkxl
@@ -0,0 +1,18 @@
+%D \module
+%D [ file=driv-shp,
+%D version=2018.07.26,
+%D title=\CONTEXT\ Driver Macros,
+%D subtitle=Shipout,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{driv-shp}{autosuffix,optimize}
+
+\unprotect
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/enco-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/enco-ini.mkxl
new file mode 100644
index 00000000000..c93ef7f6185
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/enco-ini.mkxl
@@ -0,0 +1,488 @@
+%D \module
+%D [ file=enco-ini,
+%D version=2007.02.19, % 2000.12.27, % 1998.12.03,
+%D title=\CONTEXT\ Encoding Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is stripped down version of the original enco-ini.tex file. For more details
+%D you might want to study the \MKII\ file but since \LUATEX\ is unicode inside we
+%D need less code. When we moved to \LMTX\ again some disappeared and the composition
+%D macros were upgraded in the process.
+%D
+%D An historic note: When dealing with characters we have four cases to take into
+%D account when moving from \MKII\ to \MKIV:
+%D
+%D \starttabulate[|l|l|]
+%D \NC \type {<byte 200>} \NC ref to slot 200 in current font \NC \NR
+%D \NC \type {\char 200} \NC ref to slot 200 in current font \NC \NR
+%D \NC \type {<active 200>} \NC can (e.g.) map to another slot in current font \NC \NR
+%D \NC \type {\namedglyph} \NC can map to some slot in some font \NC \NR
+%D \stoptabulate
+%D
+%D Using case 2 for special characters is doomed to fail because we are not going to
+%D intercept these on the fly as happens automatically with traditional font
+%D encoding handling. We could do that in a node pass but it's not worth the effort
+%D because we seldom use this case in a document source.
+
+\writestatus{loading}{ConTeXt Encoding Macros / Initialization}
+
+\unprotect
+
+%D This one is obsolete but maybe sometimes still used in styles:
+
+\immutable\let\defaultencoding\s!default
+
+%D \macros
+%D {defineaccent, definecharacter, definecommand}
+%D
+%D Some of these are used at the \LUA\ end but the names will change.
+
+\installcorenamespace{accents}
+
+%D These might go away:
+
+\permanent\protected\def\defineaccent#1 #2 #3 %
+ {\dodefineaccentcommand{#1}%
+ \dodefineaccent{#1}{#2}{#3}}
+
+\permanent\protected\def\dodefineaccentcommand#1%
+ {\ifcsname\string#1\endcsname\else
+ \edefcsname\string#1\endcsname{\noexpand\dohandleaccent{\string#1}}%
+ \fi}
+
+\permanent\protected\def\dodefineaccent#1#2#3% no spaces, used low level
+ {\defcsname\??accents\string#1\string#2\empty\endcsname{#3}}
+
+\permanent\protected\def\dohandleaccent#1#2% expandable because we want them in the tuc file
+ {\csname\??accents
+ \ifcsname\??accents\string#1#2\empty\endcsname
+ \string#1#2\empty
+ \orelse\ifcsname\??accents\string#1\string#2\empty\endcsname
+ \string#1\string#2\empty
+ \else
+ \endcsname#2\csname\??accents % very dirty trick: ignore accent but keep char
+ \fi
+ \endcsname}
+
+\immutable\letvalue{\??accents}\empty
+
+%D Because now have this (\type {\chr} issues a one|-|time warning):
+
+\permanent\protected\def\dodefinecombine#1#2%
+ {\ifcsname\string#1\endcsname\else
+ \permanent\protected\defcsname with#1\endcsname##1{\chr{##1#2}}%
+ \fi}
+
+%D The following dirty trick is needed to catch \asciimath{\^{1/5}log}, although:
+%D do we?
+
+\permanent\protected\def\definecharacter#1 #2 %
+ {\doifelsenumber{\string#2}
+ {\edefcsname\string#1\endcsname{\Uchar#2}}
+ {\protected\defcsname\string#1\endcsname{#2}}}
+
+%D specials: \aa \ae \cc \i \ij \l \o \oe \sz \par
+%D SPECIALS: \AA \AE \CC \j \IJ \L \O \OE \SZ \par
+
+\permanent\protected\def\definecommand#1 #2 %
+ {\protected\defcsname\string#1\endcsname{#2}}
+
+\permanent\protected\def\dodefinecommand#1#2% \O \L \AE ...
+ {% not permanent as these are kind of obsolete
+ \frozen\protected\defcsname\string#1\endcsname{#2}}
+
+%D We set up some things in \LUA:
+
+\clf_defineaccents % one time
+
+%D This defined commands like \type {\withgrave} and \type {\withcaronbelow} and
+%D some of these have aliases. Below ee also alias the traditional one character
+%D accent commands.
+%D
+%D \starttabulate[||||]
+%D \NC \getvalue{agrave} \NC \withgrave {a} \NC \`{a} \NC \NR
+%D \NC \getvalue{aacute} \NC \withacute {a} \NC \'{a} \NC \NR
+%D \NC \getvalue{acircumflex} \NC \withcircumflex {a} \NC \^{a} \NC \NR
+%D \NC \getvalue{atilde} \NC \withtilde {a} \NC \~{a} \NC \NR
+%D \NC \getvalue{amacron} \NC \withmacron {a} \NC \={a} \NC \NR
+%D \NC \getvalue{ebreve} \NC \withbreve {e} \NC \u{e} \NC \NR
+%D \NC \getvalue{cdotaccent} \NC \withdot {c} \NC \.{c} \NC \NR
+%D \NC \getvalue{ediaeresis} \NC \withdieresis {e} \NC \"{e} \NC \NR
+%D \NC \getvalue{uring} \NC \withring {u} \NC \r{u} \NC \NR
+%D \NC \getvalue{uhungarumlaut} \NC \withhungarumlaut{u} \NC \H{u} \NC \NR
+%D \NC \getvalue{ecaron} \NC \withcaron {e} \NC \v{e} \NC \NR
+%D \NC \getvalue{ecedilla} \NC \withcedilla {e} \NC \c{e} \NC \NR
+%D \NC \getvalue{eogonek} \NC \withogonek {e} \NC \k{e} \NC \NR
+%D \stoptabulate
+%D
+%D Now we alias these traditional shortcuts:
+
+\pushoverloadmode
+
+\suggestedalias \` \withgrave
+\suggestedalias \' \withacute
+\suggestedalias \^ \withcircumflex
+\suggestedalias \~ \withtilde
+\suggestedalias \= \withmacron
+\suggestedalias \u \withbreve
+\suggestedalias \. \withdot
+\suggestedalias \" \withdieresis
+\suggestedalias \r \withring
+\suggestedalias \H \withdoubleacute
+\suggestedalias \v \withcaron
+\suggestedalias \c \withcedilla
+\suggestedalias \k \withogonek
+\suggestedalias \d \withdotbelow
+
+\popoverloadmode
+
+% From \type {enco-com} we had these, maybe that's better:
+
+% \aliased\let\AE\AEligature \aliased\let\ae\aeligature
+% \aliased\let\OE\OEligature \aliased\let\oe\oeligature
+% \aliased\let\IJ\IJligature \aliased\let\ij\ijligature
+% \aliased\let\AA\textAngstrom \aliased\let\aa\aring
+% \aliased\let\CC\Ccedilla \aliased\let\cc\ccedilla
+% \aliased\let\L \Lslash \aliased\let\l \lslash
+% \aliased\let\O \Oslash \aliased\let\o \oslash
+% \aliased\let\SZ\Eszett \aliased\let\sz\eszett % \aliased\let\SS\Ssharp
+
+% %D \macros
+% %D {everyuppercase, everylowercase, everysanitize}
+%
+% \newtoks \everyuppercase
+% \newtoks \everylowercase
+% \newtoks \everysanitize
+
+%D Accent handling (try to avoid this):
+
+% \buildtextaccent\greekdasia\greekalphamacron
+% \buildtextaccent\textacute q
+
+%D We can use offsets in \LMTX\ but even that makes no sense because we have
+%D a virtual feature already in \MKIV. These builders are depricated:
+
+\newbox\b_enco_accent
+
+\permanent\protected\def\buildmathaccent#1%
+ {\mathaccent#1 }
+
+\permanent\protected\def\buildtextaccent#1#2% we could do all at the lua end
+ {\dontleavehmode\begingroup % but that's no fun (yet)
+ \setbox\scratchboxone\hbox{#1}% accent
+ \setbox\scratchboxtwo\hbox{#2}% character
+ \scratchheight\dimexpr\ht\scratchboxtwo-\ht\scratchboxone\relax
+ \scratchdepth \dimexpr\dp\scratchboxtwo-\dp\scratchboxone\relax
+ \scratchwidth \wd\scratchboxtwo
+ \hbox to \wd\ifdim\wd\scratchboxone>\wd\scratchboxtwo\scratchboxone\else\scratchboxtwo\fi\bgroup
+ \hss\box\scratchboxtwo\hss
+ \hskip-\scratchwidth
+ \hss
+ \ifdim\ht\scratchboxone>\exheight
+ % top accent
+ \raise\dimexpr\scratchheight+\exheight/3\relax
+ \else
+ \lower-\dimexpr\scratchdepth+\exheight/3\relax
+ \fi
+ \box\scratchboxone
+ \hss
+ \egroup
+ \endgroup}
+
+\permanent\protected\def\bottomaccent#1#2#3#4#5% down right slantcorrection accent char
+ {\dontleavehmode % why this align mess
+ \vtop
+ {\forgetall
+ \baselineskip\zeropoint
+ \lineskip#1%
+ \everycr\emptytoks
+ \tabskip\zeropoint
+ \lineskiplimit\zeropoint
+ \setbox0\hbox{#4}%
+ \halign
+ {##\crcr\hbox{#5}\crcr
+ \hidewidth
+ \hskip#2\wd0
+ \hskip-#3\slantperpoint % in plain 1ex * dimenless value
+ \vpack to .2\exheight{\box0\vss}\hidewidth
+ \crcr}}}
+
+\permanent\protected\def\buildtextmacron {\bottomaccent{.25ex}{0}{15}{\textmacron}}
+\permanent\protected\def\buildtextbottomdot {\bottomaccent{.25ex}{0}{5}{\textbottomdot}}
+\permanent\protected\def\buildtextcedilla {\bottomaccent{0ex}{0}{5}{\textcedilla}}
+\permanent\protected\def\buildtextogonek {\bottomaccent{-.1ex}{.5}{0}{\textogonek}}
+\permanent\protected\def\buildtextbottomcomma{\bottomaccent{.15ex}{0}{5}{\tx,}}
+
+\permanent\protected\def\topaccent#1#2#3#4#5% down right slantcorrection accent char
+ {\dontleavehmode
+ \bgroup
+ \setbox0\hbox{#4}%
+ \setbox2\hbox{#5}%
+ \hbox to \wd2 \bgroup
+ \hss\copy2\hss
+ \hskip-\wd2
+ \hss\hskip#2\wd0\hskip-#3\slantperpoint\raise#1\hbox{#4}\hss
+ \egroup
+ \egroup}
+
+\permanent\protected\def\buildtextgrave
+ {\topaccent{0pt}{0}{15}{\textgrave}} % e.g.
+
+\permanent\protected\def\definemathaccent#1 #2%
+ {\setvalue{#1}{\mathaccent#2 }}
+
+% from enco-def:
+
+% \aliased\let\i\dotlessi
+% \aliased\let\j\dotlessj
+
+% \aliased\let\P\paragraphmark \aliased\let\textP\paragraphmark % obsolete (surfaced in bibliographic files)
+% \aliased\let\S\sectionmark \aliased\let\textS\sectionmark % obsolete (surfaced in bibliographic files)
+
+\immutable\def\eszett {ß} \immutable\def\Eszett {SS} \permanent\def\Ssharp{SS}
+\immutable\def\lslash {ł} \immutable\def\Lslash {Ł}
+\immutable\def\dslash {đ} \immutable\def\Dslash {Đ}
+%immutable\def\oslash {ø} %immutable\def\Oslash {Ø} % clashes with math: use \Ostroke
+\immutable\def\dcroat {đ} \immutable\def\Dcroat {Đ}
+\immutable\def\kcedilla{ķ} \immutable\def\Kcedilla{Ķ}
+\immutable\def\lcedilla{ļ} \immutable\def\Lcedilla{Ļ}
+\immutable\def\ncedilla{ņ} \immutable\def\Ncedilla{Ņ}
+\immutable\def\rcedilla{ŗ} \immutable\def\Rcedilla{Ŗ}
+\immutable\def\aumlaut {ä} \immutable\def\Aumlaut {Ä}
+\immutable\def\eumlaut {ë} \immutable\def\Eumlaut {Ë}
+\immutable\def\iumlaut {ï} \immutable\def\Iumlaut {Ï}
+\immutable\def\oumlaut {ö} \immutable\def\Oumlaut {Ö}
+\immutable\def\uumlaut {ü} \immutable\def\Uumlaut {Ü}
+
+%D For old times sake we keep these (obsolete):
+
+\immutable\def\textflorin{ƒ} \immutable\def\florin {ƒ}
+\immutable\def\pound {£} \immutable\def\sterling{£}
+\immutable\def\promille {‰} \immutable\def\permille{‰}
+
+%D These are kind of \TEX\ specific
+
+\pushoverloadmode
+
+\permanent\protected\def\ampersand{\mathortext\mathampersand\textampersand}
+
+\immutable\let\percent\textpercent
+\immutable\let\procent\textpercent
+\immutable\let\dollar \textdollar
+\immutable\let\hash \texthash
+
+\popoverloadmode
+
+%D some more: what with freezing here?
+
+\ifdefined\softhyphen \else
+ \aliased\let\softhyphen\explicitdiscretionary
+\fi
+
+%D The softhyhen is never used but we keep the definition below:
+
+\aliased\let\hyphen \softhyphen % never used
+
+% But we ditch these:
+
+% \def\hyphen {\softhyphen} % never used
+% \def\compoundwordmark {\hyphen}
+% \def\cwm {\hyphen}
+% \def\nonbreakinghyphen{\hyphen}
+% \def\breakinghyphen {\hyphen\prewordbreak}
+
+%D Quotes \unknown\ we keep these funny names because they indicate where we come
+%D from \unknown\ long long ago when we had no real useful names for them. Some
+%D ancient styles might still use them.
+
+\aliased\let\lowerleftsingleninequote \quotesinglebase % ‚
+\aliased\let\lowerleftdoubleninequote \quotedblbase % „
+\aliased\let\lowerrightsingleninequote\quotesinglebase % ‚
+\aliased\let\lowerrightdoubleninequote\quotedblbase % „
+
+\aliased\let\upperleftsingleninequote \quoteright % ’
+\aliased\let\upperleftdoubleninequote \quotedblright % ”
+\aliased\let\upperrightsingleninequote\quoteright % ’
+\aliased\let\upperrightdoubleninequote\quotedblright % ”
+
+\aliased\let\upperleftsinglesixquote \quoteleft % ‘
+\aliased\let\upperleftdoublesixquote \quotedblleft % “
+\aliased\let\upperrightsinglesixquote \quoteleft % ‘
+\aliased\let\upperrightdoublesixquote \quotedblleft % “
+
+\aliased\let\leftsubguillemot \guilsingleleft % ‹
+\aliased\let\rightsubguillemot \guilsingleright % ›
+
+% Some left-overs that will go away!
+
+\permanent\protected\def\textblacksquare{\dontleavehmode\hpack{\vrule\s!width.3\s!em\s!height.4\s!em\s!depth-.1\s!em}}
+%permanent\protected\def\schwa {\hbox{\rotate[\c!rotation=180,\c!location=\v!high]{\hbox{e}}}}
+\permanent\protected\def\schwagrave {\buildtextgrave\schwa}
+
+\installcorenamespace{controlspace}
+
+\permanent\protected\def\fallbackcontrolspace % beware, current font, we also need to honor color
+ {\hbox to \interwordspace \bgroup
+ \hss
+ \ifcsname\??controlspace\number\interwordspace\endcsname
+ \csname\??controlspace\number\interwordspace\endcsname
+ \else
+ \enco_fast_control_space_define % only regular
+ \fi
+ \textcontrolspace
+ \hss
+ \egroup}
+
+\protected\def\enco_fast_control_space_define
+ {\scratchdimen\interwordspace
+ \definedfont[LMTypewriter-Regular at \the\dimexpr\currentfontbodyscale\dimexpr\fontbody]% see font-sym.mkiv
+ \gletcsname\??controlspace\number\scratchdimen\endcsname\lastrawfontcall}
+
+\permanent\protected\def\normalcontrolspace
+ {\iffontchar\font\numexpr\expandafter`\textcontrolspace\relax
+ \textcontrolspace
+ \else
+ \fallbackcontrolspace
+ \fi}
+
+\aliased\let\textvisiblespace\normalcontrolspace
+
+\permanent\protected\def\optionalcontrolspace
+ {\iffontchar\font\numexpr\expandafter`\textcontrolspace\relax
+ \textcontrolspace
+ \else
+ \asciispacechar % used for export !
+ \fi}
+
+%D A smaller and bolder variant, more like the math and monospaced ones.
+
+\permanent\protected\def\fakeunderscore
+ {\relax\ifmmode
+ \vrule\s!depth .12\fontexheight\mathstylefont\normalmathstyle\s!width \fontinterwordspace\mathstylefont\normalmathstyle\s!height\zeropoint\relax
+ \else
+ \dontleavehmode\hbox{\vrule\s!depth .12\fontexheight\font\s!width \fontinterwordspace\font\s!height\zeropoint}%
+ \fi}
+
+\permanent\protected\def\fakeunderscores{\enforced\let\_\fakeunderscore}
+\permanent\protected\def\textunderscores{\enforced\let\_\textunderscore}
+
+\textunderscores
+
+\ifdefined\mathunderscore \else \aliased\let\mathunderscore\fakeunderscore \fi
+\ifdefined\textunderscore \else \aliased\let\textunderscore\fakeunderscore \fi
+
+\permanent\protected\def\normalunderscore{\ifmmode\mathunderscore\else\textunderscore\fi}
+
+\pushoverloadmode
+ \enforced\let\_\normalunderscore
+\popoverloadmode
+
+%D To be sorted out:
+
+\pushoverloadmode
+
+\permanent\protected\def\textminus
+ {\char
+ \iffontchar\font"2012 "2012 \orelse % figuredash
+ \iffontchar\font"2013 "2013 \orelse % endash
+ \iffontchar\font"2212 "2212 \else % math minus
+ "002D \fi} % hyphen
+
+\permanent\protected\def\textplus
+ {\char"002B } % plus
+
+\popoverloadmode
+
+%D Moved from core-mis:
+
+% \permanent\protected\def\celsius #1{#1\mathematics{^\circ}C}
+% \permanent\protected\def\inch {\mathematics{\prime\prime}} % was: \hbox{\rm\char125\relax}
+% \permanent\protected\def\fraction#1#2{\mathematics{#1\over#2}}
+
+\permanent\protected\def\celsius #1{#1\iffontchar\font`℃℃\else\mathematics{^\circ}C\fi} % 0x2103
+\permanent\protected\def\inch {\iffontchar\font`″″\else\mathematics{\prime\prime}\fi} % 0x2103
+\permanent\protected\def\fraction#1#2{\mathematics{\Uabove exact .125\exheight {#1}{#2}}}
+
+%D \startbuffer
+%D \startlines
+%D x\periods x
+%D x\periods[10]x
+%D x\periods[n=10,symbol={,}]x
+%D x\periods[n=4,symbol={!!},width=1em]x
+%D x\periods[n=4,symbol={!!},width=fit]x
+%D x\periods[n=4,symbol={!!},width=fit,distance=1em]x
+%D x\unknown x
+%D \stoplines
+%D \stopbuffer
+%D
+%D \typbuffer \getbuffer
+
+\installcorenamespace {periods}
+
+\installsetuponlycommandhandler \??periods {periods}
+
+\setupperiods
+ [\c!n=3,
+ \c!width=.5\emwidth, % can also be \v!fit
+ \c!distance=.25\emwidth,
+ \c!symbol=.]
+
+\permanent\protected\def\periods
+ {\dontleavehmode
+ \hbox\bgroup
+ \doifelsenextoptional\enco_periods_yes\enco_periods_nop}
+
+\protected\def\enco_periods_yes[#1]%
+ {\doifelseassignment{#1}
+ {\setupcurrentperiods[#1]%
+ \scratchcounter\periodsparameter\c!n}
+ {\doifelsenothing{#1}
+ {\scratchcounter\periodsparameter\c!n}%
+ {\scratchcounter#1}}%
+ \enco_periods_finish}
+
+\protected\def\enco_periods_nop
+ {\scratchcounter\periodsparameter\c!n
+ \enco_periods_finish}
+
+\protected\def\enco_periods_finish
+ {\edef\p_width{\periodsparameter\c!width}%
+ \ifx\p_width\v!fit
+ \enco_periods_finish_fit
+ \else
+ \enco_periods_finish_width
+ \fi
+ \egroup}
+
+\protected\def\enco_periods_finish_width
+ {\setbox\scratchbox\hbox to \p_width
+ {\hss\periodsparameter\c!symbol\hss}%
+ \dorecurse\scratchcounter{\copy\scratchbox}}
+
+\protected\def\enco_periods_finish_fit
+ {\edef\p_symbol{\periodsparameter\c!symbol}%
+ \scratchdistance\periodsparameter\c!distance
+ \hskip\scratchdistance
+ \dorecurse\scratchcounter{\p_symbol\hskip\scratchdistance}}
+
+\permanent\protected\def\unknown
+ {\dontleavehmode
+ \hbox\bgroup
+ \enco_periods_nop}
+
+%D Left-overs:
+
+\appendtoks
+ \enforced\let\buildtextaccent\secondoftwoarguments
+\to \everysimplifycommands
+
+%D See enco-ini.mkivc for a definition of the obsolete \type {\t}.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-ini.mklx b/Master/texmf-dist/tex/context/base/mkxl/file-ini.mklx
new file mode 100644
index 00000000000..35a49c72dd4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-ini.mklx
@@ -0,0 +1,231 @@
+%D \module
+%D [ file=file-ini, % was supp-fil,
+%D version=20110701, % 1995.10.10,
+%D title=\CONTEXT\ File Macros,
+%D subtitle=Helpers,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D \TEX\ operates on files, so one wouldn't wonder that there is a separate module
+%D for file commands. In \CONTEXT\ files are used for several purposes:
+%D
+%D \startitemize[packed]
+%D \item general textual input
+%D \item logging status information
+%D \item saving registers, lists and references
+%D \item buffering defered textual input
+%D \stopitemize
+%D
+%D When dealing with files we can load them as a whole, using the \type {\input}
+%D primitive or load them on a line||by||line basis, using \type {\read}. Writing is
+%D always done line by line, using \type {\write}.
+
+\writestatus{loading}{ConTeXt File Macros / Helpers}
+
+\registerctxluafile{file-ini}{}
+
+\unprotect
+
+%D \macros
+%D {scratchread, scratchwrite}
+%D
+%D We define a scratch file for reading. Keep in mind that the number of files is
+%D limited to~16, so use this one when possible. We also define a scratch output
+%D file. In \MKIV\ and \LMTX\ we never use these file handles.
+
+\ifdefined\scratchread \else \newread \scratchread \fi
+\ifdefined\scratchwrite \else \newwrite\scratchwrite \fi
+
+%D Seldom needed:
+
+\permanent\def\openinputfile #handle#name{\immediate\openin #handle={#name}\relax}
+\permanent\def\openoutputfile #handle#name{\immediate\openout#handle={#name}\relax}
+
+\permanent\def\closeinputfile #handle{\immediate\closein #handle\relax}
+\permanent\def\closeoutputfile#handle{\immediate\closeout#handle\relax}
+
+%D \macros
+%D {writeln}
+%D
+%D This saves a few tokens:
+
+\permanent\def\writeln#handle{\write#handle{}}
+
+%D \macros
+%D {pushendofline,popendofline}
+%D
+%D When we are loading files in the middle of the typesetting process, for instance
+%D when we load references, we have to be sure that the reading process does not
+%D generate so called 'spurious spaces'. This can be prevented by assigning the line
+%D ending character the \CATCODE\ comment. This is accomplished by
+%D
+%D \starttyping
+%D \pushendofline
+%D ... reading ...
+%D \popendofline
+%D \stoptyping
+
+\installsystemnamespace{eolstack}
+
+\newcount\c_system_files_eol_level
+
+\permanent\protected\def\pushendofline
+ {\advance\c_system_files_eol_level\plusone
+ \expandafter\chardef\csname\??eolstack\number\c_system_files_eol_level\endcsname\catcode\endoflineasciicode
+ \catcode\endoflineasciicode\commentcatcode}
+
+\permanent\protected\def\popendofline
+ {\catcode\endoflineasciicode\csname\??eolstack\number\c_system_files_eol_level\endcsname
+ \advance\c_system_files_eol_level\minusone}
+
+\permanent\protected\def\restoreendofline
+ {\catcode\endoflineasciicode\endoflinecatcode}
+
+%D \macros
+%D {startreadingfile,stopreadingfile}
+%D
+%D A low level capsule:
+
+\newcount\readingfilelevel % no longer needed
+\newtoks \everystartreadingfile
+\newtoks \everystopreadingfile
+
+\permanent\protected\def\startreadingfile% beter een every en \setnormalcatcodes
+ {\global\advance\readingfilelevel\plusone
+ \the\everystartreadingfile
+ \pushcatcodetable % saveguard
+ \setcatcodetable\ctxcatcodes
+ \clf_pushregime}% temporarily this way
+
+\permanent\protected\def\stopreadingfile
+ {\popcatcodetable % saveguard
+ \clf_popregime % temporarily this way
+ \the\everystopreadingfile
+ \global\advance\readingfilelevel\minusone}
+
+%D \macros
+%D {input, normalinput}
+%D
+%D Sometimes we run into troubles when \type {\input} wants to get expanded, e.g. in
+%D a \type {\write} (which happens in the metafun manual when we permit long MP
+%D lines). So, instead of fixing that, we go for a redefinition of \type {\input}.
+%D Of course it's better to use \type {\readfile} or \type {\processfile}.
+
+\permanent\def\inputgivenfile#name{\normalinput{#name}} % expands
+
+%D \macros
+%D {doifelsefile}
+%D
+%D The next alternative only looks if a file is present. No loading is done. This
+%D one obeys the standard \TEX\ lookup.
+%D
+%D \starttyping
+%D \doiffileelse {filename} {found} {not found}
+%D \stoptyping
+
+\permanent\protected\def\doifelsefile {\clf_doifelsefileexist}
+\permanent\protected\def\doifelsepath {\clf_doifelsepathexist}
+\permanent\protected\def\doiffile #name{\clf_doifelsefileexist{#name}\firstofoneargument\gobbleoneargument}
+\permanent\protected\def\doifnotfile #name{\clf_doifelsefileexist{#name}\gobbleoneargument\firstofoneargument}
+
+\aliased\let\doiffileelse\doifelsefile
+\aliased\let\doifpathelse\doifelsepath
+
+\aliased\let\doifelsefileexists\doifelsefile
+\aliased\let\doifelsepathexists\doifelsepath
+
+\aliased\let\doiffileexistselse\doifelsefileexists
+\aliased\let\doifpathexistselse\doifelsepathexists
+
+%D \macros
+%D {doifparentfileelse}
+%D
+%D \starttyping
+%D \doifparentfileelse{filename}{yes}{no}
+%D \stoptyping
+
+\ifdefined\outputfilename \else \def\outputfilename{\jobname} \fi
+
+\permanent\protected\def\doifelseparentfile{\clf_doifelseparentfile}
+
+\aliased\let\doifparentfileelse\doifelseparentfile
+
+%D \macros
+%D {splitfilename}
+%D
+%D \startbuffer
+%D \def\showfilesplit
+%D {\bgroup \tttf
+%D \hbox{(full: \splitofffull)}\space
+%D \hbox{(path: \splitoffpath)}\space
+%D \hbox{(base: \splitoffbase)}\space
+%D \hbox{(name: \splitoffname)}\space
+%D \hbox{(type: \splitofftype)}\space
+%D \egroup}
+%D
+%D \splitfilename{c:/aa/bb/cc/dd.ee.ff} \showfilesplit \endgraf
+%D \splitfilename{c:/aa/bb/cc/dd.ee} \showfilesplit \endgraf
+%D \splitfilename{c:/aa/bb/cc/dd} \showfilesplit \endgraf
+%D
+%D \splitfilename{dd.ee.ff} \showfilesplit \endgraf
+%D \splitfilename{dd.ee} \showfilesplit \endgraf
+%D \splitfilename{dd} \showfilesplit \endgraf
+%D \stopbuffer
+%D
+%D \start \typebuffer \getbuffer \stop
+
+\newconstant\kindoffile % 0=normal 1=full path spec (or http) / set at the lua end
+\newconstant\splitoffkind
+
+\mutable\def\splitoffroot{.}
+\mutable\let\splitofffull\empty
+\mutable\let\splitoffpath\empty
+\mutable\let\splitoffbase\empty
+\mutable\let\splitoffname\empty
+\mutable\let\splitofftype\empty
+
+\permanent\protected\def\splitfilename{\clf_splitfilename}
+
+%D \macros
+%D {doonlyonce, doinputonce, doendinputonce}
+%D
+%D Especially macropackages need only be loaded once. Repetitive loading not only
+%D costs time, relocating registers often leads to abortion of the processing
+%D because \TEX's capacity is limited. One can prevent multiple execution and
+%D loading by using one of both:
+%D
+%D \starttyping
+%D \doonlyonce{actions}
+%D \doinputonce{filename}
+%D \doendinputonce{filename}
+%D \stoptyping
+%D
+%D This command obeys the standard method for locating files. We could move this
+%D function to the \LUA\ end.
+
+\installsystemnamespace{fileonce}
+
+\permanent\protected\def\doonlyonce#whatever%
+ {\ifcsname\??fileonce#whatever\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \letgvalue{\??fileonce#whatever}\relax
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\doinputonce#name%
+ {\doonlyonce{#name}{\doifelsefile{#name}{\inputgivenfile{#name}}\donothing}}
+
+\permanent\protected\def\doendinputonce#name%
+ {\ifcsname\??fileonce#name\endcsname
+ \expandafter\endinput
+ \fi}
+
+\permanent\protected\def\forgetdoingonce#whatever%
+ {\global\undefinevalue{\??fileonce#whatever}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-job.lmt b/Master/texmf-dist/tex/context/base/mkxl/file-job.lmt
new file mode 100644
index 00000000000..95ad48e824b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-job.lmt
@@ -0,0 +1,1283 @@
+if not modules then modules = { } end modules ['file-job'] = {
+ version = 1.001,
+ comment = "companion to file-job.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- in retrospect dealing it's not that bad to deal with the nesting
+-- and push/poppign at the tex end
+
+local next, rawget, tostring, tonumber = next, rawget, tostring, tonumber
+local gsub, match, find = string.gsub, string.match, string.find
+local insert, remove, concat = table.insert, table.remove, table.concat
+local validstring, formatters = string.valid, string.formatters
+local sortedhash = table.sortedhash
+local setmetatableindex, setmetatablenewindex = table.setmetatableindex, table.setmetatablenewindex
+
+local commands = commands
+local resolvers = resolvers
+local context = context
+
+local ctx_doifelse = commands.doifelse
+
+local implement = interfaces.implement
+
+local trace_jobfiles = false trackers.register("system.jobfiles", function(v) trace_jobfiles = v end)
+
+local report = logs.reporter("system")
+local report_jobfiles = logs.reporter("system", "jobfiles")
+local report_functions = logs.reporter("system", "functions")
+
+local texsetcount = tex.setcount
+local elements = interfaces.elements
+local constants = interfaces.constants
+local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+local allocate = utilities.storage.allocate
+
+local nameonly = file.nameonly
+local suffixonly = file.suffix
+local basename = file.basename
+local addsuffix = file.addsuffix
+local removesuffix = file.removesuffix
+local dirname = file.dirname
+local is_qualified_path = file.is_qualified_path
+
+local cleanpath = resolvers.cleanpath
+local toppath = resolvers.toppath
+local resolveprefix = resolvers.resolve
+
+local hasscheme = url.hasscheme
+
+local jobresolvers = resolvers.jobs
+
+local registerextrapath = resolvers.registerextrapath
+local resetextrapaths = resolvers.resetextrapaths
+local getextrapaths = resolvers.getextrapath
+local pushextrapath = resolvers.pushextrapath
+local popextrapath = resolvers.popextrapath
+
+----- v_outer = variables.outer
+local v_text = variables.text
+local v_project = variables.project
+local v_environment = variables.environment
+local v_product = variables.product
+local v_component = variables.component
+local v_yes = variables.yes
+
+-- main code .. there is some overlap .. here we have loc://
+
+local function findctxfile(name) -- loc ? any ?
+ if is_qualified_path(name) then -- maybe when no suffix do some test for tex
+ return name
+ elseif not hasscheme(name) then
+ return resolvers.finders.byscheme("loc",name) or ""
+ else
+ return resolvers.findtexfile(name) or ""
+ end
+end
+
+resolvers.findctxfile = findctxfile
+
+implement {
+ name = "processfile",
+ arguments = "string",
+ public = true,
+ protected = true,
+ actions = function(name)
+ name = findctxfile(name)
+ if name ~= "" then
+ context.input(name)
+ end
+ end
+}
+
+implement {
+ name = "doifelseinputfile",
+ arguments = "string",
+ public = true,
+ protected = true,
+ actions = function(name)
+ ctx_doifelse(findctxfile(name) ~= "")
+ end
+}
+
+implement {
+ name = "locatefilepath",
+ arguments = "string",
+ actions = function(name)
+ context(dirname(findctxfile(name)))
+ end
+}
+
+implement {
+ name = "usepath",
+ arguments = "optional",
+ public = true,
+ protected = true,
+ actions = function(paths)
+ report_jobfiles("using path: %s",paths)
+ registerextrapath(paths)
+ end
+}
+
+implement {
+ name = "pushpath",
+ arguments = "optional",
+ public = true,
+ protected = true,
+ actions = function(paths)
+ report_jobfiles("pushing path: %s",paths)
+ pushextrapath(paths)
+ end
+}
+
+implement {
+ name = "poppath",
+ public = true,
+ protected = true,
+ actions = function(paths)
+ popextrapath()
+ report_jobfiles("popping path")
+ end
+}
+
+implement {
+ name = "usesubpath",
+ arguments = "optional",
+ public = true,
+ protected = true,
+ actions = function(subpaths)
+ report_jobfiles("using subpath: %s",subpaths)
+ registerextrapath(nil,subpaths)
+ end
+}
+
+implement {
+ name = "resetpath",
+ public = true,
+ protected = true,
+ actions = function()
+ report_jobfiles("resetting path")
+ resetextrapaths()
+ end
+}
+
+implement {
+ name = "allinputpaths",
+ public = true,
+ actions = function()
+ context(concat(getextrapaths(),","))
+ end
+}
+
+implement {
+ name = "usezipfile",
+ public = true,
+ protected = true,
+ arguments = { "optional", "optional" },
+ actions = function(name,tree)
+ if tree and tree ~= "" then
+ resolvers.usezipfile(formatters["zip:///%s?tree=%s"](name,tree))
+ else
+ resolvers.usezipfile(formatters["zip:///%s"](name))
+ end
+ end
+}
+
+-- moved from tex to lua:
+
+local texpatterns = { "%s.mkvi", "%s.mkiv", "%s.mklx", "%s.mkxl", "%s.tex" }
+local luapatterns = { "%s" .. utilities.lua.suffixes.luc, "%s.lua", "%s.lmt" }
+local cldpatterns = { "%s.cld" }
+local xmlpatterns = { "%s.xml" }
+
+local uselibrary = resolvers.uselibrary
+local input = context.input
+
+-- status
+--
+-- these need to be synced with input stream:
+
+local processstack = { }
+local processedfile = ""
+local processedfiles = { }
+
+implement {
+ name = "processedfile",
+ actions = function()
+ context(processedfile)
+ end
+}
+
+implement {
+ name = "processedfiles",
+ actions = function()
+ context(concat(processedfiles,","))
+ end
+}
+
+implement {
+ name = "dostarttextfile",
+ public = true,
+ protected = true,
+ arguments = "string",
+ actions = function(name)
+ insert(processstack,name)
+ processedfile = name
+ insert(processedfiles,name)
+ end
+}
+
+implement {
+ name = "dostoptextfile",
+ public = true,
+ protected = true,
+ actions = function()
+ processedfile = remove(processstack) or ""
+ end
+}
+
+local function startprocessing(name,notext)
+ if not notext then
+ -- report("begin file %a at line %a",name,status.linenumber or 0)
+ context.dostarttextfile(name)
+ end
+end
+
+local function stopprocessing(notext)
+ if not notext then
+ context.dostoptextfile()
+ -- report("end file %a at line %a",name,status.linenumber or 0)
+ end
+end
+
+--
+
+local typestack = { }
+local currenttype = v_text
+local nofmissing = 0
+local missing = {
+ tex = setmetatableindex("number"),
+ lua = setmetatableindex("number"),
+ cld = setmetatableindex("number"),
+ xml = setmetatableindex("number"),
+}
+
+local function reportfailure(kind,name)
+ nofmissing = nofmissing + 1
+ missing[kind][name] = true
+ report_jobfiles("unknown %s file %a",kind,name)
+end
+
+--
+
+local function action(name,foundname)
+ input(foundname)
+end
+local function failure(name,foundname)
+ reportfailure("tex",name)
+end
+local function usetexfile(name,onlyonce,notext)
+ startprocessing(name,notext)
+ uselibrary {
+ name = name,
+ patterns = texpatterns,
+ action = action,
+ failure = failure,
+ onlyonce = onlyonce,
+ }
+ stopprocessing(notext)
+end
+
+local function action(name,foundname)
+ dofile(foundname)
+end
+local function failure(name,foundname)
+ reportfailure("lua",name)
+end
+local function useluafile(name,onlyonce,notext)
+ uselibrary {
+ name = name,
+ patterns = luapatterns,
+ action = action,
+ failure = failure,
+ onlyonce = onlyonce,
+ }
+end
+
+local function action(name,foundname)
+ dofile(foundname)
+end
+local function failure(name,foundname)
+ reportfailure("cld",name)
+end
+local function usecldfile(name,onlyonce,notext)
+ startprocessing(name,notext)
+ uselibrary {
+ name = name,
+ patterns = cldpatterns,
+ action = action,
+ failure = failure,
+ onlyonce = onlyonce,
+ }
+ stopprocessing(notext)
+end
+
+local function action(name,foundname)
+ context.xmlprocess(foundname,"main","")
+end
+local function failure(name,foundname)
+ reportfailure("xml",name)
+end
+local function usexmlfile(name,onlyonce,notext)
+ startprocessing(name,notext)
+ uselibrary {
+ name = name,
+ patterns = xmlpatterns,
+ action = action,
+ failure = failure,
+ onlyonce = onlyonce,
+ }
+ stopprocessing(notext)
+end
+
+local suffixes = {
+ mkvi = usetexfile,
+ mkiv = usetexfile,
+ mklx = usetexfile,
+ mkxl = usetexfile,
+ tex = usetexfile,
+ luc = useluafile,
+ lua = useluafile,
+ cld = usecldfile,
+ xml = usexmlfile,
+ [""] = usetexfile,
+}
+
+local function useanyfile(name,onlyonce)
+ local s = suffixes[suffixonly(name)]
+ context(function() resolvers.pushpath(name) end)
+ if s then
+ -- s(removesuffix(name),onlyonce)
+ s(name,onlyonce) -- so, first with suffix, then without
+ else
+ usetexfile(name,onlyonce) -- e.g. ctx file
+ -- resolvers.readfilename(name)
+ end
+ context(resolvers.poppath)
+end
+
+implement { name = "loadtexfile", public = true, protected = true, actions = usetexfile, arguments = "optional" }
+implement { name = "loadluafile", public = true, protected = true, actions = useluafile, arguments = "optional" }
+implement { name = "loadcldfile", public = true, protected = true, actions = usecldfile, arguments = "optional" }
+implement { name = "loadxmlfile", public = true, protected = true, actions = usexmlfile, arguments = "optional" }
+
+implement { name = "loadtexfileonce", public = true, protected = true, actions = usetexfile, arguments = { "optional", true } }
+implement { name = "loadluafileonce", public = true, protected = true, actions = useluafile, arguments = { "optional", true } }
+implement { name = "loadcldfileonce", public = true, protected = true, actions = usecldfile, arguments = { "optional", true } }
+implement { name = "loadxmlfileonce", public = true, protected = true, actions = usexmlfile, arguments = { "optional", true } }
+
+implement { name = "useanyfile", actions = useanyfile, arguments = "string" }
+implement { name = "useanyfileonce", actions = useanyfile, arguments = { "string", true } }
+
+function jobresolvers.usefile(name,onlyonce,notext)
+ local s = suffixes[suffixonly(name)]
+ if s then
+ -- s(removesuffix(name),onlyonce,notext)
+ s(name,onlyonce,notext) -- so, first with suffix, then without
+ end
+end
+
+-- document structure
+
+local textlevel = 0 -- inaccessible for user, we need to define counter textlevel at the tex end
+
+local function dummyfunction() end
+
+local function startstoperror()
+ report("invalid \\%s%s ... \\%s%s structure",elements.start,v_text,elements.stop,v_text)
+ startstoperror = dummyfunction
+end
+
+local stopped
+
+local function starttext()
+ if textlevel == 0 then
+ if trace_jobfiles then
+ report_jobfiles("starting text")
+ end
+ context.dostarttext()
+ end
+ textlevel = textlevel + 1
+ texsetcount("global","textlevel",textlevel)
+end
+
+local function stoptext()
+ if not stopped then
+ if textlevel == 0 then
+ startstoperror()
+ elseif textlevel > 0 then
+ textlevel = textlevel - 1
+ end
+ texsetcount("global","textlevel",textlevel)
+ if textlevel <= 0 then
+ if trace_jobfiles then
+ report_jobfiles("stopping text")
+ end
+ context.dostoptext()
+ stopped = true
+ end
+ end
+end
+
+implement {
+ name = "starttext",
+ public = true,
+ protected = true,
+ actions = starttext
+}
+
+implement {
+ name = "stoptext",
+ public = true,
+ protected = true,
+ actions = stoptext
+}
+
+implement {
+ name = "forcequitjob",
+ arguments = "string",
+ public = true,
+ protected = true,
+ actions = function(reason)
+ if reason then
+ report("forcing quit: %s",reason)
+ else
+ report("forcing quit")
+ end
+ context.batchmode()
+ while textlevel >= 0 do
+ context.stoptext()
+ end
+ end
+}
+
+implement {
+ name = "forceendjob",
+ public = true,
+ protected = true,
+ actions = function()
+ report([[don't use \end to finish a document]])
+ context.stoptext()
+ end
+}
+
+implement {
+ name = "autostarttext",
+ public = true,
+ protected = true,
+ actions = function()
+ if textlevel == 0 then
+ report([[auto \starttext ... \stoptext]])
+ end
+ context.starttext()
+ end
+}
+
+implement {
+ name = "autostoptext",
+ public = true,
+ protected = true,
+ actions = stoptext
+}
+
+-- project structure
+
+implement {
+ name = "processfilemany",
+ public = true,
+ protected = true,
+ arguments = { "string", false },
+ actions = useanyfile
+}
+
+implement {
+ name = "processfileonce",
+ public = true,
+ protected = true,
+ arguments = { "string", true },
+ actions = useanyfile
+}
+
+implement {
+ name = "processfilenone",
+ arguments = "string",
+ public = true,
+ protected = true,
+ actions = dummyfunction,
+}
+
+local tree = { type = "text", name = "", branches = { } }
+local treestack = { }
+local top = tree.branches
+local root = tree
+
+local project_stack = { }
+local product_stack = { }
+local component_stack = { }
+local environment_stack = { }
+
+local stacks = {
+ [v_project ] = project_stack,
+ [v_product ] = product_stack,
+ [v_component ] = component_stack,
+ [v_environment] = environment_stack,
+}
+
+--
+
+local function pushtree(what,name)
+ local t = { }
+ top[#top+1] = { type = what, name = name, branches = t }
+ insert(treestack,top)
+ top = t
+end
+
+local function poptree()
+ top = remove(treestack)
+ -- inspect(top)
+end
+
+do
+
+ local function log_tree(report,top,depth)
+ report("%s%s: %s",depth,top.type,top.name)
+ local branches = top.branches
+ if #branches > 0 then
+ depth = depth .. " "
+ for i=1,#branches do
+ log_tree(report,branches[i],depth)
+ end
+ end
+ end
+
+ logs.registerfinalactions(function()
+ root.name = environment.jobname
+ --
+ logs.startfilelogging(report,"used files")
+ log_tree(report,root,"")
+ logs.stopfilelogging()
+ --
+ if nofmissing > 0 and logs.loggingerrors() then
+ logs.starterrorlogging(report,"missing files")
+ for kind, list in sortedhash(missing) do
+ for name in sortedhash(list) do
+ report("%w%s %s",6,kind,name)
+ end
+ end
+ logs.stoperrorlogging()
+ end
+ end)
+
+end
+
+local jobstructure = job.structure or { }
+job.structure = jobstructure
+jobstructure.collected = jobstructure.collected or { }
+jobstructure.tobesaved = root
+jobstructure.components = { }
+
+local function initialize()
+ local function collect(root,result)
+ local branches = root.branches
+ if branches then
+ for i=1,#branches do
+ local branch = branches[i]
+ if branch.type == "component" then
+ result[#result+1] = branch.name
+ end
+ collect(branch,result)
+ end
+ end
+ return result
+ end
+ jobstructure.components = collect(jobstructure.collected,{})
+end
+
+job.register('job.structure.collected',root,initialize)
+
+-- component: small unit, either or not components itself
+-- product : combination of components
+
+local ctx_processfilemany = context.processfilemany
+local ctx_processfileonce = context.processfileonce
+local ctx_processfilenone = context.processfilenone
+
+-- we need a plug in the nested loaded, push pop pseudo current dir
+
+local function processfilecommon(name,action)
+ -- experiment, might go away
+-- if not hasscheme(name) then
+-- local path = dirname(name)
+-- if path ~= "" then
+-- registerextrapath(path)
+-- report_jobfiles("adding search path %a",path)
+-- end
+-- end
+ -- till here
+ action(name)
+end
+
+local function processfilemany(name) processfilecommon(name,ctx_processfilemany) end
+local function processfileonce(name) processfilecommon(name,ctx_processfileonce) end
+local function processfilenone(name) processfilecommon(name,ctx_processfilenone) end
+
+local processors = utilities.storage.allocate {
+ -- [v_outer] = {
+ -- [v_text] = { "many", processfilemany },
+ -- [v_project] = { "once", processfileonce },
+ -- [v_environment] = { "once", processfileonce },
+ -- [v_product] = { "once", processfileonce },
+ -- [v_component] = { "many", processfilemany },
+ -- },
+ [v_text] = {
+ [v_text] = { "many", processfilemany },
+ [v_project] = { "once", processfileonce }, -- dubious
+ [v_environment] = { "once", processfileonce },
+ [v_product] = { "many", processfilemany }, -- dubious
+ [v_component] = { "many", processfilemany },
+ },
+ [v_project] = {
+ [v_text] = { "many", processfilemany },
+ [v_project] = { "none", processfilenone },
+ [v_environment] = { "once", processfileonce },
+ [v_product] = { "none", processfilenone },
+ [v_component] = { "none", processfilenone },
+ },
+ [v_environment] = {
+ [v_text] = { "many", processfilemany },
+ [v_project] = { "none", processfilenone },
+ [v_environment] = { "once", processfileonce },
+ [v_product] = { "none", processfilenone },
+ [v_component] = { "none", processfilenone },
+ },
+ [v_product] = {
+ [v_text] = { "many", processfilemany },
+ [v_project] = { "once", processfileonce },
+ [v_environment] = { "once", processfileonce },
+ [v_product] = { "many", processfilemany },
+ [v_component] = { "many", processfilemany },
+ },
+ [v_component] = {
+ [v_text] = { "many", processfilemany },
+ [v_project] = { "once", processfileonce },
+ [v_environment] = { "once", processfileonce },
+ [v_product] = { "none", processfilenone },
+ [v_component] = { "many", processfilemany },
+ }
+}
+
+local start = {
+ [v_text] = nil,
+ [v_project] = nil,
+ [v_environment] = context.startreadingfile,
+ [v_product] = context.starttext,
+ [v_component] = context.starttext,
+}
+
+local stop = {
+ [v_text] = nil,
+ [v_project] = nil,
+ [v_environment] = context.stopreadingfile,
+ [v_product] = context.stoptext,
+ [v_component] = context.stoptext,
+}
+
+jobresolvers.processors = processors
+
+local function topofstack(what)
+ local stack = stacks[what]
+ return stack and stack[#stack] or environment.jobname
+end
+
+local function productcomponent() -- only when in product
+ local product = product_stack[#product_stack]
+ if product and product ~= "" then
+ local component = component_stack[1]
+ if component and component ~= "" then
+ return component
+ end
+ end
+end
+
+local function justacomponent()
+ local product = product_stack[#product_stack]
+ if not product or product == "" then
+ local component = component_stack[1]
+ if component and component ~= "" then
+ return component
+ end
+ end
+end
+
+jobresolvers.productcomponent = productcomponent
+jobresolvers.justacomponent = justacomponent
+
+function jobresolvers.currentproject () return topofstack(v_project ) end
+function jobresolvers.currentproduct () return topofstack(v_product ) end
+function jobresolvers.currentcomponent () return topofstack(v_component ) end
+function jobresolvers.currentenvironment() return topofstack(v_environment) end
+
+local done = { }
+local tolerant = false -- too messy, mkii user with the wrong structure should adapt
+
+local function process(what,name)
+ local depth = #typestack
+ local process
+ --
+ name = resolveprefix(name)
+ --
+-- if not tolerant then
+ -- okay, would be best but not compatible with mkii
+ process = processors[currenttype][what]
+-- elseif depth == 0 then
+-- -- could be a component, product or (brr) project
+-- if trace_jobfiles then
+-- report_jobfiles("%s : %s > %s (case 1)",depth,currenttype,v_outer)
+-- end
+-- process = processors[v_outer][what]
+-- elseif depth == 1 and typestack[1] == v_text then
+-- -- we're still not doing a component or product
+-- if trace_jobfiles then
+-- report_jobfiles("%s : %s > %s (case 2)",depth,currenttype,v_outer)
+-- end
+-- process = processors[v_outer][what]
+-- else
+-- process = processors[currenttype][what]
+-- end
+ if process then
+ local method = process[1]
+ if method == "none" then
+ if trace_jobfiles then
+ report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"ignoring",what,name,currenttype,topofstack(currenttype))
+ end
+ elseif method == "once" and done[name] then
+ if trace_jobfiles then
+ report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"skipping",what,name,currenttype,topofstack(currenttype))
+ end
+ else
+ -- keep in mind that we also handle "once" at the file level
+ -- so there is a double catch
+ done[name] = true
+ local before = start[what]
+ local after = stop [what]
+ if trace_jobfiles then
+ report_jobfiles("%s : %s : %s %s %a in %s %a",depth,method,"processing",what,name,currenttype,topofstack(currenttype))
+ end
+ if before then
+ before()
+ end
+ process[2](name)
+ if after then
+ after()
+ end
+ end
+ else
+ if trace_jobfiles then
+ report_jobfiles("%s : %s : %s %s %a in %s %a",depth,"none","ignoring",what,name,currenttype,topofstack(currenttype))
+ end
+ end
+end
+
+local scan_delimited = tokens.scanners.delimited
+
+local function getname()
+ return scan_delimited(91,93) or scan_delimited(0,32) -- [name] or name<space>
+end
+
+implement { name = "project", public = true, protected = true, actions = function() process(v_project, getname()) end }
+implement { name = "environment", public = true, protected = true, actions = function() process(v_environment,getname()) end }
+implement { name = "product", public = true, protected = true, actions = function() process(v_product, getname()) end } -- will be overloaded
+implement { name = "component", public = true, protected = true, actions = function() process(v_component, getname()) end }
+
+implement { name = "useproject", public = true, protected = true, actions = function(name) process(v_project, name) end, arguments = "optional" }
+implement { name = "useenvironment", public = true, protected = true, actions = function(name) process(v_environment,name) end, arguments = "optional" }
+implement { name = "useproduct", public = true, protected = true, actions = function(name) process(v_product, name) end, arguments = "optional" } -- will be overloaded
+implement { name = "usecomponent", public = true, protected = true, actions = function(name) process(v_component, name) end, arguments = "optional" }
+
+-- todo: setsystemmode to currenttype
+-- todo: make start/stop commands at the tex end
+
+local start = {
+ [v_project] = context.startprojectindeed,
+ [v_product] = context.startproductindeed,
+ [v_component] = context.startcomponentindeed,
+ [v_environment] = context.startenvironmentindeed,
+}
+
+local stop = {
+ [v_project] = context.stopprojectindeed,
+ [v_product] = context.stopproductindeed,
+ [v_component] = context.stopcomponentindeed,
+ [v_environment] = context.stopenvironmentindeed,
+}
+
+local function gotonextlevel(what,name) -- todo: something with suffix name
+ insert(stacks[what],name)
+ insert(typestack,currenttype)
+ currenttype = what
+ pushtree(what,name)
+ if start[what] then
+ start[what]()
+ end
+end
+
+local function gotopreviouslevel(what)
+ if stop[what] then
+ stop[what]()
+ end
+ poptree()
+ currenttype = remove(typestack) or v_text
+ remove(stacks[what]) -- not currenttype ... weak recovery
+context.endinput() -- does not work
+-- context.signalendofinput(what)
+end
+
+local function autoname()
+ local name = scan_delimited(91,93) or scan_delimited(0,32) -- [name] or name<space>
+ if name == "*" then
+ name = nameonly(toppath() or name)
+ end
+ return name
+end
+
+implement { name = "startproject", public = true, protected = true, actions = function() gotonextlevel(v_project, autoname()) end }
+implement { name = "startproduct", public = true, protected = true, actions = function() gotonextlevel(v_product, autoname()) end }
+implement { name = "startcomponent", public = true, protected = true, actions = function() gotonextlevel(v_component, autoname()) end }
+implement { name = "startenvironment", public = true, protected = true, actions = function() gotonextlevel(v_environment,autoname()) end }
+
+implement { name = "stopproject", public = true, protected = true, actions = function() gotopreviouslevel(v_project ) end }
+implement { name = "stopproduct", public = true, protected = true, actions = function() gotopreviouslevel(v_product ) end }
+implement { name = "stopcomponent", public = true, protected = true, actions = function() gotopreviouslevel(v_component ) end }
+implement { name = "stopenvironment", public = true, protected = true, actions = function() gotopreviouslevel(v_environment) end }
+
+implement { name = "currentproject", public = true, actions = function() context(topofstack(v_project )) end }
+implement { name = "currentproduct", public = true, actions = function() context(topofstack(v_product )) end }
+implement { name = "currentcomponent", public = true, actions = function() context(topofstack(v_component )) end }
+implement { name = "currentenvironment", public = true, actions = function() context(topofstack(v_environment)) end }
+
+-- -- -- this will move -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- <?xml version='1.0' standalone='yes'?>
+-- <exa:variables xmlns:exa='htpp://www.pragma-ade.com/schemas/exa-variables.rng'>
+-- <exa:variable label='mode:pragma'>nee</exa:variable>
+-- <exa:variable label='mode:variant'>standaard</exa:variable>
+-- </exa:variables>
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+local report_examodes = logs.reporter("system","examodes")
+
+local function convertexamodes(str)
+ local x = xml.convert(str)
+ for e in xml.collected(x,"exa:variable") do
+ local label = e.at and e.at.label
+ if label and label ~= "" then
+ local data = xml.text(e)
+ local mode = match(label,"^mode:(.+)$")
+ if mode then
+ context.enablemode { formatters["%s:%s"](mode,data) }
+ end
+ context.setvariable("exa:variables",label,(gsub(data,"([{}])","\\%1")))
+ end
+ end
+end
+
+function environment.loadexamodes(filename)
+ if not filename or filename == "" then
+ filename = removesuffix(tex.jobname)
+ end
+ filename = resolvers.findfile(addsuffix(filename,'ctm')) or ""
+ if filename ~= "" then
+ report_examodes("loading %a",filename) -- todo: message system
+ convertexamodes(io.loaddata(filename))
+ else
+ report_examodes("no mode file %a",filename) -- todo: message system
+ end
+end
+
+implement {
+ name = "loadexamodes",
+ actions = environment.loadexamodes,
+ public = true,
+ protected = true,
+ arguments = "optional"
+}
+
+-- changed in mtx-context
+-- code moved from luat-ini
+
+-- todo: locals when mtx-context is changed
+
+document = document or {
+ arguments = allocate(),
+ files = allocate(),
+ variables = allocate(), -- for templates
+ options = {
+ commandline = {
+ environments = allocate(),
+ modules = allocate(),
+ modes = allocate(),
+ },
+ ctxfile = {
+ environments = allocate(),
+ modules = allocate(),
+ modes = allocate(),
+ },
+ },
+ functions = table.setmetatablenewindex(function(t,k,v)
+ if rawget(t,k) then
+ report_functions("overloading document function %a",k)
+ end
+ rawset(t,k,v)
+ return v
+ end),
+}
+
+function document.setargument(key,value)
+ document.arguments[key] = value
+end
+
+function document.setdefaultargument(key,default)
+ local v = document.arguments[key]
+ if v == nil or v == "" then
+ document.arguments[key] = default
+ end
+end
+
+function document.setfilename(i,name)
+ if name then
+ document.files[tonumber(i)] = name
+ else
+ document.files[#document.files+1] = tostring(i)
+ end
+end
+
+function document.getargument(key,default)
+ local v = document.arguments[key]
+ if type(v) == "boolean" then
+ v = (v and "yes") or "no"
+ document.arguments[key] = v
+ end
+ return v or default or ""
+end
+
+function document.getfilename(i)
+ return document.files[tonumber(i)] or ""
+end
+
+implement {
+ name = "setdocumentargument",
+ actions = document.setargument,
+ arguments = "2 strings"
+}
+
+implement {
+ name = "setdocumentdefaultargument",
+ actions = document.setdefaultargument,
+ arguments = "2 strings"
+}
+
+implement {
+ name = "setdocumentfilename",
+ actions = document.setfilename,
+ arguments = { "integer", "string" }
+}
+
+implement {
+ name = "getdocumentargument",
+ actions = { document.getargument, context },
+ arguments = "2 strings"
+}
+
+implement {
+ name = "getdocumentfilename",
+ actions = { document.getfilename, context },
+ arguments = "integer"
+}
+
+function document.setcommandline() -- has to happen at the tex end in order to expand
+
+ -- the document[arguments|files] tables are copies
+
+ local arguments = document.arguments
+ local files = document.files
+ local options = document.options
+
+ for k, v in next, environment.arguments do
+ k = gsub(k,"^c:","") -- already done, but better be safe than sorry
+ if arguments[k] == nil then
+ arguments[k] = v
+ end
+ end
+
+ -- in the new mtx=context approach we always pass a stub file so we need to
+ -- to trick the files table which actually only has one entry in a tex job
+
+ if arguments.timing then
+ context.usemodule { "timing" }
+ end
+
+ if arguments.usage then
+ trackers.enable("system.usage")
+ end
+
+ if arguments.batchmode then
+ context.batchmode(false)
+ end
+
+ if arguments.nonstopmode then
+ context.nonstopmode(false)
+ end
+
+ if arguments.nostatistics then
+ directives.enable("system.nostatistics")
+ end
+
+ if arguments.paranoid then
+ context.setvalue("maxreadlevel",1)
+ end
+
+ if validstring(arguments.path) then
+ context.usepath { arguments.path }
+ end
+
+ if arguments.export then
+ context.setupbackend { export = v_yes }
+ end
+
+ local inputfile = validstring(arguments.input)
+
+ if inputfile and dirname(inputfile) == "." and lfs.isfile(inputfile) then
+ -- nicer in checks
+ inputfile = basename(inputfile)
+ end
+
+ local forcedruns = arguments.forcedruns
+ local kindofrun = arguments.kindofrun
+ local currentrun = arguments.currentrun
+ local maxnofruns = arguments.maxnofruns or arguments.runs
+
+ -- context.setupsystem {
+ -- [constants.directory] = validstring(arguments.setuppath),
+ -- [constants.inputfile] = inputfile,
+ -- [constants.file] = validstring(arguments.result),
+ -- [constants.random] = validstring(arguments.randomseed),
+ -- -- old:
+ -- [constants.n] = validstring(kindofrun),
+ -- [constants.m] = validstring(currentrun),
+ -- }
+
+ context.setupsystem {
+ directory = validstring(arguments.setuppath),
+ inputfile = inputfile,
+ file = validstring(arguments.result),
+ random = validstring(arguments.randomseed),
+ -- old:
+ n = validstring(kindofrun),
+ m = validstring(currentrun),
+ }
+
+ forcedruns = tonumber(forcedruns) or 0
+ kindofrun = tonumber(kindofrun) or 0
+ maxnofruns = tonumber(maxnofruns) or 0
+ currentrun = tonumber(currentrun) or 0
+
+ local prerollrun = forcedruns > 0 and currentrun > 0 and currentrun < forcedruns
+
+ environment.forcedruns = forcedruns
+ environment.kindofrun = kindofrun
+ environment.maxnofruns = maxnofruns
+ environment.currentrun = currentrun
+ environment.prerollrun = prerollrun
+
+ context.setconditional("prerollrun",prerollrun)
+
+ if validstring(arguments.arguments) then
+ context.setupenv { arguments.arguments }
+ end
+
+ if arguments.once then
+ directives.enable("system.runonce")
+ end
+
+ if arguments.noarrange then
+ context.setuparranging { variables.disable }
+ end
+
+ --
+
+ local commandline = options.commandline
+
+ commandline.environments = table.append(commandline.environments,settings_to_array(validstring(arguments.environment)))
+ commandline.modules = table.append(commandline.modules, settings_to_array(validstring(arguments.usemodule)))
+ commandline.modes = table.append(commandline.modes, settings_to_array(validstring(arguments.mode)))
+
+ --
+
+ if #files == 0 then
+ local list = settings_to_array(validstring(arguments.files))
+ if list and #list > 0 then
+ files = list
+ end
+ end
+
+ if #files == 0 then
+ files = { validstring(arguments.input) }
+ end
+
+ --
+
+ document.arguments = arguments
+ document.files = files
+
+end
+
+-- commandline wins over ctxfile
+
+local function apply(list,action)
+ if list then
+ for i=1,#list do
+ action { list[i] }
+ end
+ end
+end
+
+function document.setmodes() -- was setup: *runtime:modes
+ apply(document.options.ctxfile .modes,context.enablemode)
+ apply(document.options.commandline.modes,context.enablemode)
+end
+
+function document.setmodules() -- was setup: *runtime:modules
+ apply(document.options.ctxfile .modules,context.usemodule)
+ apply(document.options.commandline.modules,context.usemodule)
+end
+
+function document.setenvironments() -- was setup: *runtime:environments
+ apply(document.options.ctxfile .environments,context.environment)
+ apply(document.options.commandline.environments,context.environment)
+end
+
+function document.setfilenames()
+ local initialize = environment.initializefilenames
+ if initialize then
+ initialize()
+ else
+ -- fatal error
+ end
+end
+
+implement { name = "setdocumentcommandline", actions = document.setcommandline, onlyonce = true }
+implement { name = "setdocumentmodes", actions = document.setmodes, onlyonce = true }
+implement { name = "setdocumentmodules", actions = document.setmodules, onlyonce = true }
+implement { name = "setdocumentenvironments", actions = document.setenvironments, onlyonce = true }
+implement { name = "setdocumentfilenames", actions = document.setfilenames, onlyonce = true }
+
+do
+
+ logs.registerfinalactions(function()
+ local foundintrees = resolvers.foundintrees()
+ if #foundintrees > 0 then
+ logs.startfilelogging(report,"used files")
+ for i=1,#foundintrees do
+ report("%4i: % T",i,foundintrees[i])
+ end
+ logs.stopfilelogging()
+ end
+ end)
+
+ logs.registerfinalactions(function()
+ local files = document.files -- or environment.files
+ local arguments = document.arguments -- or environment.arguments
+ --
+ logs.startfilelogging(report,"commandline options")
+ if arguments and next(arguments) then
+ for argument, value in sortedhash(arguments) do
+ report("%s=%A",argument,value)
+ end
+ else
+ report("no arguments")
+ end
+ logs.stopfilelogging()
+ --
+ logs.startfilelogging(report,"commandline files")
+ if files and #files > 0 then
+ for i=1,#files do
+ report("% 4i: %s",i,files[i])
+ end
+ else
+ report("no files")
+ end
+ logs.stopfilelogging()
+ end)
+
+end
+
+if environment.initex then
+
+ logs.registerfinalactions(function()
+ local startfilelogging = logs.startfilelogging
+ local stopfilelogging = logs.stopfilelogging
+ startfilelogging(report,"stored tables")
+ for k,v in sortedhash(storage.data) do
+ report("%03i %s",k,v[1])
+ end
+ stopfilelogging()
+ startfilelogging(report,"stored modules")
+ for k,v in sortedhash(lua.bytedata) do
+ report("%03i %s %s",k,v.name)
+ end
+ stopfilelogging()
+ startfilelogging(report,"stored attributes")
+ for k,v in sortedhash(attributes.names) do
+ report("%03i %s",k,v)
+ end
+ stopfilelogging()
+ startfilelogging(report,"stored catcodetables")
+ for k,v in sortedhash(catcodes.names) do
+ report("%03i % t",k,v)
+ end
+ stopfilelogging()
+ startfilelogging(report,"stored corenamespaces")
+ for k,v in sortedhash(interfaces.corenamespaces) do
+ report("%03i %s",k,v)
+ end
+ stopfilelogging()
+ end)
+
+end
+
+implement {
+ name = "continueifinputfile",
+ public = true,
+ protected = true,
+ arguments = "string",
+ actions = function(inpname,basetoo)
+ local inpnamefull = addsuffix(inpname,"tex")
+ local inpfilefull = addsuffix(environment.inputfilename,"tex")
+ local continue = inpnamefull == inpfilefull
+ -- if basetoo and not continue then
+ if not continue then
+ continue = inpnamefull == basename(inpfilefull)
+ end
+ if continue then
+ report("continuing input file %a",inpname)
+ else
+ context.endinput()
+ end
+-- ctx_doifelse(continue)
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-job.mklx b/Master/texmf-dist/tex/context/base/mkxl/file-job.mklx
new file mode 100644
index 00000000000..9fb14c9bfc2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-job.mklx
@@ -0,0 +1,361 @@
+%D \module
+%D [ file=file-job, % copied from main-001, later core-job
+%D version=1997.03.31,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Job Handling,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt File Macros / Job Handling}
+
+\unprotect
+
+%D This module delegates most of the work to \LUA\ and therefore also let it
+%D define the commands, which is more efficient.
+
+\registerctxluafile{file-job}{autosuffix}
+
+%D Here are some helpers for processing and path control. In the following example
+%D the lookup order is \type {.}, {/foo}, \type {foo/bar} as one can always
+%D explicitly prefix a wanted file.
+%D
+%D \starttyping
+%D \pushpath[foo]
+%D \pushpath[foo/bar]
+%D \input test
+%D \poppath
+%D \poppath
+%D \stoptyping
+
+% \usepath [#path] % defined at lua end
+% \usesubpath [#path] % defined at lua end
+% \pushpath [#path] % defined at lua end
+% \poppath % defined at lua end
+% \resetpath % defined at lua end (not the pushed paths)
+% \allinputpaths % defined at lua end
+% \doifelseinputfile #name % defined at lua end
+% \processfile #name % defined at lua end
+
+\aliased\let\doifinputfileelse\doifelseinputfile
+
+% helper (not really needed nowadays)
+
+\mutable\let\locatedfilepath\empty
+
+\permanent\protected\def\locatefilepath#name%
+ {\edef\locatedfilepath{\clf_locatefilepath{#name}}} % todo: set at the lua end, catcodes and such
+
+% % maybe:
+%
+% % \dirlist{c:/data/temporary/foo}{images/bar.jpg}
+% % \dirfile{c:/data/temporary/foo}{images/bar.jpg}
+%
+% \def\dirlist#1#2{dirlist://#1/**/#2}
+% \def\dirfile#1#2{dirlist://#1/**/#2?option=fileonly}
+
+% zip files are tree'd
+% exa stuff might become obsolete:
+
+% \usezipfile [#zipname]#spacer[#subtree] % defined at lua end
+% \loadexamodes [#name] % defined at lua end
+
+\protected\def\syst_files_load
+ {\syst_files_load_indeed\f!newfilename % new code, to be integrated at some point, plus fixes posted on the list
+ \syst_files_load_indeed\f!locfilename % new code, somewhat experimental, not distributed (outside the dev group)
+ \syst_files_load_indeed\f!expfilename % new code, very experimental, can be engine specific, mostly for me only
+ \syst_files_load_indeed\f!sysfilename % local settings, but probably not that good an idea to use
+ %\syst_files_load_indeed\f!fntfilename % maybe some day, can load goodies and patches
+ }
+
+\def\syst_files_load_indeed#name% from now on we assume a suffix to be part of the name
+ {\readsysfile{#name}{\showmessage\m!system2{#name}}\donothing}
+
+% document structure
+
+\ifdefined\textlevel \else \newcount\textlevel \fi % might go away
+\ifdefined\strc_pagenumbers_flush_final_page \else \let\strc_pagenumbers_flush_final_page\relax \fi % ugly
+
+\permanent\protected\def\dostarttext
+ {\enforced\glet\dostarttext\relax
+ \the\everystarttext
+ \global\everystarttext\emptytoks}
+
+\permanent\protected\def\dostoptext
+ {\enforced\glet\dostoptext\relax
+ \strc_pagenumbers_flush_final_page
+ \page % hm, bonus
+ \the\everystoptext
+ \global\everystoptext\emptytoks
+ \the\everybye
+ \global\everybye\emptytoks
+ \the\everygoodbye
+ \global\everygoodbye\emptytoks
+ \doifelsesometoks\everynotabene{\writeline\the\everynotabene\writeline}\donothing
+ % tex's \end:
+ \normalend}
+
+% \starttext % defined at lua end
+% \stoptext % defined at lua end
+% \forcequitjob % defined at lua end
+% \autostarttext % defined at lua end
+% \autostoptext % defined at lua end
+
+\pushoverloadmode
+ \aliased\let\end\forceendjob
+\popoverloadmode
+
+\aliased\let\finishjob\stoptext % nicer in luatex call commandline
+
+\newtoks\everystartnotext
+\newtoks\everystopnotext
+
+\permanent\protected\def\startnotext{\the\everystartnotext}
+\permanent\protected\def\stopnotext {\the\everystopnotext\normalend}
+
+% protect structure
+
+% \processfilemany {#name} % defined at lua end
+% \processfileonce {#name} % defined at lua end
+% \processfilenone {#name} % defined at lua end
+
+% \useproject [#name] % defined at lua end
+% \useproduct [#name] % defined at lua end
+% \usecomponent [#name] % defined at lua end
+% \useenvironment [#name] % defined at lua end
+
+% \project [#name] or #name<space> % defined at lua end
+% \product [#name] or #name<space> % defined at lua end
+% \component [#name] or #name<space> % defined at lua end
+% \environment [#name] or #name<space> % defined at lua end
+
+% \startproject [#name] or #name<space> % defined at lua end
+% \startproduct [#name] or #name<space> % defined at lua end
+% \startcomponent [#name] or #name<space> % defined at lua end
+% \startenvironment [#name] or #name<space> % defined at lua end
+
+\newconditional\endofinput % hack, needed because \endinput happens one level down in the input when we write from lua
+
+% \protected\def\signalendofinput#1% argument just for tracing
+% {\global\settrue\endofinput}
+%
+% \protected\def\honorendofinput
+% {\ifconditional\endofinput
+% \global\setfalse\endofinput
+% \endinput
+% \fi}
+
+% \protected\def\stopproject {\clf_stopproject \honorendofinput} % todo
+% \protected\def\stopproduct {\clf_stopproduct \honorendofinput} % todo
+% \protected\def\stopcomponent {\clf_stopcomponent \honorendofinput} % todo
+% \protected\def\stopenvironment {\clf_stopenvironment\honorendofinput} % todo
+
+% \currentproject % defined at lua end
+% \currentproduct % defined at lua end
+% \currentcomponent % defined at lua end
+% \currentenvironment % defined at lua end
+% \processedfile % defined at lua end
+% \processedfiles % defined at lua end
+%
+% \dostarttextfile #name % defined at lua end
+% \dostoptextfile % defined at lua end
+%
+% \loadtexfile [#name] % defined at lua end
+% \loadluafile [#name] % defined at lua end
+% \loadcldfile [#name] % defined at lua end
+% \loadanyfile [#name] % defined at lua end
+%
+% \loadtexfileonce [#name] % defined at lua end
+% \loadluafileonce [#name] % defined at lua end
+% \loadcldfileonce [#name] % defined at lua end
+% \loadanyfileonce [#name] % defined at lua end
+
+%D Handy for modules that have a test/demo appended (true added).
+
+% \permanent\protected\def\continueifinputfile#name{\clf_doifelsecontinuewithfile{#name}\relax\endinput} % we cannot do \endinput via lua
+
+% \continueifinputfile #name % defined at lua end
+
+% \startproject test
+% 1: \startmode[*project] project \stopmode \endgraf
+% 2: \startmode[*product] product \stopmode \endgraf
+% \stopproject
+
+% A bit weird place:
+
+\neverypar\emptytoks
+
+%D Wrappers (the signal is a synchronization with lua hack):
+
+\newsystemmode\v!project
+\newsystemmode\v!product
+\newsystemmode\v!component
+\newsystemmode\v!environment
+
+\permanent\protected\def\startprojectindeed
+ {%starttext
+ \pushsystemmode\v!project
+ \setsystemmode\v!project}
+
+\permanent\protected\def\stopprojectindeed
+ {\popsystemmode\v!project
+ %stoptext
+ }%\signalendofinput\v!project}
+
+\permanent\protected\def\startproductindeed
+ {\starttext
+ \pushsystemmode\v!product
+ \setsystemmode\v!product}
+
+\permanent\protected\def\stopproductindeed
+ {\popsystemmode\v!product
+ \stoptext
+ }%\signalendofinput\v!product}
+
+\permanent\protected\def\startcomponentindeed
+ {\starttext
+ \pushreferenceprefix\currentcomponent
+ \pushsystemmode\v!component
+ \setsystemmode\v!component}
+
+\permanent\protected\def\stopcomponentindeed
+ {\popsystemmode\v!component
+ \popreferenceprefix
+ \stoptext
+ }%\signalendofinput\v!component}
+
+\permanent\protected\def\startenvironmentindeed
+ {\pushsystemmode\v!environment
+ \setsystemmode\v!environment}
+
+\permanent\protected\def\stopenvironmentindeed
+ {\popsystemmode\v!environment
+ }%\signalendofinput\v!environment}
+
+%D Upgraded after the ctx 2020 meeting:
+%D
+%D \starttyping
+%D \definedocument[thesis]
+%D
+%D \setupdocument[a=b,e=f]
+%D \setupdocument[thesis][a=x,c=d]
+%D
+%D % \startsetups[thesis:start]
+%D \startsetups[document:start]
+%D START
+%D \stopsetups
+%D
+%D \startsetups[thesis:stop]
+%D % \startsetups[document:stop]
+%D STOP
+%D \stopsetups
+%D
+%D % \startdocument[thesis]
+%D \startthesis
+%D (\currentdocument/a): \documentvariable{a}\par
+%D (\currentdocument/c): \documentvariable{c}\par
+%D (\currentdocument/e): \documentvariable{e}\par
+%D \stopthesis
+%D % \stopdocument
+%D \stoptyping
+
+\installcorenamespace {document}
+
+\installcommandhandler \??document \s!document \??document
+
+\setupdocument
+ [\c!before=\directsetup{\s!document:\v!start},
+ \c!after =\directsetup{\s!document:\v!stop}]
+
+% to be discussed:
+%
+% \setupdocument
+% [metadata:title=\documentvariable\c!title,
+% metadata:subject=\documentvariable\c!subtitle,
+% metadata:subtitle=\documentvariable\c!subtitle,
+% metadata:author=\documentvariable\c!author,
+% metadata:authors=\documentvariable\c!author,
+% metadata:keyword=\documentvariable\c!keyword,
+% metadata:keywords=\documentvariable\c!keyword,
+% metadata:date=\documentvariable\c!date]
+
+\def\syst_document_setup#1%
+ {\directsetup{\doifelsesetups{\currentdocument:\v!start}\currentdocument\s!document:\v!start}}
+
+\appendtoks
+ \normalexpanded {%
+ \startsetups[\currentdocument:\v!start]\noexpand\directsetup{\ifempty\currentdocumentparent\s!document\else\currentdocumentparent\fi:\v!start}\stopsetups
+ \startsetups[\currentdocument:\v!stop ]\noexpand\directsetup{\ifempty\currentdocumentparent\s!document\else\currentdocumentparent\fi:\v!stop }\stopsetups
+ \permanent\setvalue{\e!start\currentdocument}{\startdocument[\currentdocument]}%
+ \permanent\setvalue{\e!stop \currentdocument}{\stopdocument}
+ }%
+\to \everydefinedocument
+
+% todo: dostarttagged\t!document
+
+\permanent\tolerant\protected\def\startdocument[#namespace]#spacer[#settings]%
+ {\pushmacro\currentdocument
+ \ifparameters
+ \or
+ \ifhastok={#namespace}%
+ \let\currentdocument\empty
+ \setupcurrentdocument[#namespace]%
+ \else
+ \edef\currentdocument{#namespace}%
+ \fi
+ \or
+ \edef\currentdocument{#namespace}%
+ \setupcurrentdocument[#settings]%
+ \fi
+ \ifempty\currentdocument\else\setmode\currentdocument\fi
+ \starttext
+ \the\everystartdocument
+ \documentvariable\c!before}
+
+\permanent\protected\def\stopdocument % todo: dostoptagged\t!document
+ {\documentvariable\c!after
+ \the\everystopdocument
+ \stoptext
+ \ifempty\currentdocument\else\resetmode\currentdocument\fi
+ \popmacro\currentdocument}
+
+\aliased \let\documentvariable \documentparameter
+\permanent\protected\def\unexpandeddocumentvariable{\documentparameter}
+
+\permanent\protected\def\doifelsedocumentvariable#name{\doifelsesomething{\documentvariable{#name}}}
+\permanent\protected\def\doifdocumentvariable #name{\doifsomething {\documentvariable{#name}}}
+\permanent\protected\def\doifnotdocumentvariable #name{\doifnot {\documentvariable{#name}}}
+
+\aliased\let\doifdocumentvariableelse\doifelsedocumentvariable
+
+\permanent\tolerant\protected\def\presetdocument[#namespace]#spacer[#settings]%
+ {\pushmacro\currentdocument
+ \ifparameters
+ \or
+ \ifhastok={#namespace}%
+ \let\currentdocument\empty
+ \setupcurrentdocument[#namespace]%
+ \fi
+ \or
+ \edef\currentdocument{#namespace}%
+ \setupcurrentdocument[#settings]%
+ \fi
+ \popmacro\currentdocument}
+
+% Bonus:
+
+% \installcorenamespace{samplefile}
+%
+% \protected\def\samplefile#1%
+% {\ifcsname\??samplefile#1\endcsname \else
+% \setxvalue{\??samplefile#1}{\cldloadfile{#1}}%
+% \fi
+% \lastnamedcs}
+
+\permanent\protected\def\samplefile#1{\clf_samplefile{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-lib.mkxl b/Master/texmf-dist/tex/context/base/mkxl/file-lib.mkxl
new file mode 100644
index 00000000000..c2ec555cf2f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-lib.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=file-lib, % was core-fil,
+%D version=20110701, % 1997.11.15,
+%D title=\CONTEXT\ File Macros,
+%D subtitle=Module Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt File Macros / Libraries}
+
+\unprotect
+
+\registerctxluafile{file-lib}{}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-mod.lmt b/Master/texmf-dist/tex/context/base/mkxl/file-mod.lmt
new file mode 100644
index 00000000000..6af5d8816e0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-mod.lmt
@@ -0,0 +1,309 @@
+if not modules then modules = { } end modules ['file-mod'] = {
+ version = 1.001,
+ comment = "companion to file-mod.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This module will be redone! For instance, the prefixes will move to data-*
+-- as they arr sort of generic along with home:// etc/.
+
+-- context is not defined yet! todo! (we need to load tupp-fil after cld)
+-- todo: move startreadingfile to lua and push regime there
+
+--[[ldx--
+<p>It's more convenient to manipulate filenames (paths) in
+<l n='lua'/> than in <l n='tex'/>. These methods have counterparts
+at the <l n='tex'/> side.</p>
+--ldx]]--
+
+local format, find, concat, tonumber = string.format, string.find, table.concat, tonumber
+local sortedhash = table.sortedhash
+local basename = file.basename
+
+local trace_modules = false trackers .register("modules.loading", function(v) trace_modules = v end)
+local permit_unprefixed = false directives.register("modules.permitunprefixed", function(v) permit_unprefixed = v end)
+
+local report = logs.reporter("modules")
+
+local commands = commands
+local context = context
+local implement = interfaces.implement
+
+local findbyscheme = resolvers.finders.byscheme -- use different one
+local iterator = utilities.parsers.iterator
+
+-- modules can have a specific suffix or can specify one
+
+local prefixes = {
+ "m", -- module, extends functionality
+ "p", -- private code
+ "s", -- styles
+ "x", -- xml specific modules
+ -- "v", -- an old internal one for examples
+ "t", -- third party extensions
+}
+
+-- the order might change and how about cld
+
+local suffixes = CONTEXTLMTXMODE > 0 and
+{
+ "mklx", -- preprocessed mkiv lmtx files
+ "mkxl", -- mkiv lmtx files
+ "mkvi", -- preprocessed mkiv files
+ "mkiv", -- mkiv files
+ "tex", -- normally source code files
+ "cld", -- context lua documents (often stand alone)
+ "lua", -- lua files
+}
+ or
+{
+ "mkvi",
+ "mkiv",
+ "tex",
+ "cld",
+ "lua",
+}
+
+-- This controls embedded overloads. Maybe at some point we will limit this to files in the
+-- tree but on the other hand we don't need to be that strict. It is just an extra level
+-- of protection.
+
+local setrunstate = tex.setrunstate
+local level = 0
+
+local function pushrunstate()
+ level = level + 1
+ if level == 1 then
+ setrunstate(1)
+ end
+end
+
+local function poprunstate()
+ if level == 1 then
+ setrunstate(3)
+ end
+ if level > 0 then
+ level = level - 1
+ end
+end
+
+luatex.pushrunstate = pushrunstate
+luatex.poprunstate = poprunstate
+
+tex.setrunstate = function() end
+
+implement { name = "pushrunstate", public = true, protected = true, actions = pushrunstate }
+implement { name = "poprunstate", public = true, protected = true, actions = poprunstate }
+
+-- Till here.
+
+local modstatus = { }
+local missing = false
+
+local function usemodule(name,hasscheme)
+ local foundname
+ if hasscheme then
+ -- no auto suffix as http will return a home page or error page
+ -- so we only add one if missing
+ local fullname = file.addsuffix(name,"tex")
+ if trace_modules then
+ report("checking url %a",fullname)
+ end
+ foundname = resolvers.findtexfile(fullname) or ""
+ elseif file.suffix(name) ~= "" then
+ if trace_modules then
+ report("checking file %a",name)
+ end
+ foundname = findbyscheme("any",name) or ""
+ else
+ for i=1,#suffixes do
+ local fullname = file.addsuffix(name,suffixes[i])
+ if trace_modules then
+ report("checking file %a",fullname)
+ end
+ foundname = findbyscheme("any",fullname) or ""
+ if foundname ~= "" then
+ break
+ end
+ end
+ end
+ if foundname ~= "" then
+ if trace_modules then
+ report("loading file %a",foundname)
+ end
+ context.pushrunstate()
+ context.startreadingfile()
+ resolvers.jobs.usefile(foundname,true) -- once, notext
+ -- context.input(foundname)
+ context.stopreadingfile()
+ context.poprunstate()
+ return true
+ else
+ return false
+ end
+end
+
+function environment.usemodules(prefix,askedname,truename)
+ local truename = truename or environment.truefilename(askedname) or askedname
+ if truename and truename ~= "" then
+ local hasprefix = prefix and prefix ~= ""
+ local hashname = ((hasprefix and prefix) or "*") .. "-" .. truename
+ local status = modstatus[hashname] or false -- yet unset
+ if status == 0 then
+ -- not found
+ elseif status == 1 then
+ status = status + 1
+ else
+ if trace_modules then
+ report("locating, prefix %a, askedname %a, truename %a",prefix,askedname,truename)
+ end
+ local hasscheme = url.hasscheme(truename)
+ if hasscheme then
+ -- no prefix and suffix done
+ if usemodule(truename,true) then
+ status = 1
+ else
+ status = 0
+ end
+ elseif hasprefix then
+ if usemodule(prefix .. "-" .. truename) then
+ status = 1
+ else
+ status = 0
+ end
+ else
+ for i=1,#prefixes do
+ -- todo: reconstruct name i.e. basename
+ local thename = prefixes[i] .. "-" .. truename
+ if usemodule(thename) then
+ status = 1
+ break
+ end
+ end
+ if status then
+ -- ok, don't change
+ elseif find(truename,"-",1,true) and usemodule(truename) then
+ -- assume a user namespace
+ report("using user prefixed file %a",truename)
+ status = 1
+ elseif permit_unprefixed and usemodule(truename) then
+ report("using unprefixed file %a",truename)
+ status = 1
+ else
+ status = 0
+ end
+ end
+ end
+ if status == 0 then
+ missing = true
+ report("%a is not found",askedname)
+ elseif status == 1 then
+ report("%a is loaded",trace_modules and truename or askedname)
+ else
+ report("%a is already loaded",trace_modules and truename or askedname)
+ end
+ modstatus[hashname] = status
+ end
+end
+
+statistics.register("loaded tex modules", function()
+ if next(modstatus) then
+ local t, f, nt, nf = { }, { }, 0, 0
+ for k, v in sortedhash(modstatus) do
+ local b = basename(k)
+ if v == 0 then
+ nf = nf + 1
+ f[nf] = b
+ else
+ nt = nt + 1
+ t[nt] = b
+ end
+ end
+ if nf == 0 then
+ return format("%s requested, all found (%s)",nt,concat(t," "))
+ elseif nt == 0 then
+ return format("%s requested, all missing (%s)",nf,concat(f," "))
+ else
+ return format("%s requested, %s found (%s), %s missing (%s)",nt+nf,nt,concat(t," "),nf,concat(f," "))
+ end
+ else
+ return nil
+ end
+end)
+
+logs.registerfinalactions(function()
+ logs.startfilelogging(report,"used modules")
+ for k, v in sortedhash(modstatus) do
+ report(v == 0 and "missing: %s" or "loaded : %s",basename(k))
+ end
+ logs.stopfilelogging()
+ if missing and logs.loggingerrors() then
+ logs.starterrorlogging(report,"missing modules")
+ for k, v in sortedhash(modstatus) do
+ if v == 0 then
+ report("%w%s",6,basename(k))
+ end
+ end
+ logs.stoperrorlogging()
+ end
+end)
+
+-- moved from syst-lua.lua:
+
+local lpegmatch = lpeg.match
+local splitter = lpeg.tsplitter(lpeg.S(". "),tonumber)
+
+function environment.comparedversion(one,two) -- one >= two
+ if not two or two == "" then
+ one, two = environment.version, one
+ elseif one == "" then
+ one = environment.version
+ end
+ one = lpegmatch(splitter,one)
+ two = lpegmatch(splitter,two)
+ one = (one[1] or 0) * 10000 + (one[2] or 0) * 100 + (one[3] or 0)
+ two = (two[1] or 0) * 10000 + (two[2] or 0) * 100 + (two[3] or 0)
+ if one < two then
+ return -1
+ elseif one > two then
+ return 1
+ else
+ return 0
+ end
+end
+
+environment.comparedversion = comparedversion
+
+
+function environment.useluamodule(list)
+ for filename in iterator(list) do
+ environment.loadluafile(filename)
+ end
+end
+
+implement {
+ name = "usemodules",
+ actions = environment.usemodules,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "doifelseolderversion",
+ actions = function(one,two) commands.doifelse(comparedversion(one,two) >= 0) end,
+ arguments = "2 strings"
+}
+
+implement {
+ name = "useluamodule",
+ actions = environment.useluamodule,
+ arguments = "string"
+}
+
+implement {
+ name = "loadluamodule",
+ actions = function(name) dofile(resolvers.findctxfile(name)) end, -- hack
+ arguments = "string"
+}
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-mod.mklx b/Master/texmf-dist/tex/context/base/mkxl/file-mod.mklx
new file mode 100644
index 00000000000..24471e30f00
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-mod.mklx
@@ -0,0 +1,262 @@
+%D \module
+%D [ file=file-mod, % was core-fil,
+%D version=20110701, % 1997.11.15,
+%D title=\CONTEXT\ File Macros,
+%D subtitle=Module Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt File Macros / Modules}
+
+\unprotect
+
+\registerctxluafile{file-mod}{autosuffix}
+
+%D \macros
+%D {usemodule}
+%D
+%D Most of \CONTEXT is preloaded in the format file. Some very domain specific
+%D typesetting topics are however dealt with in separate modules, e.g. typesetting
+%D of chemical structure formulas. These modules are loaded by:
+%D
+%D \showsetup{usemodule}
+%D
+%D More information on the specific modules can be found in their dedicated manuals.
+%D We use \type {\next} so that we can \type {\end} in modules.
+
+\installcorenamespace{module}
+
+\mutable\let\currentmodule \s!unknown
+\mutable\let\currentmodulecategory \empty
+\mutable\let\currentmoduleparameters\empty
+
+\installmacrostack\currentmodule
+\installmacrostack\currentmodulecategory
+\installmacrostack\currentmoduleparameters
+
+\permanent\tolerant\protected\def\usemodules[#category]#spacer[#name]#spacer[#parameters]% category=t|m|x|p|...
+ {\push_macro_currentmodule
+ \push_macro_currentmodulecategory
+ \push_macro_currentmoduleparameters
+ \ifparameters
+ \let\currentmodule\empty
+ \or
+ \let \currentmodulecategory \empty
+ \edef\currentmodule {#category}%
+ \let \currentmoduleparameters\empty
+ \or
+ \ifhastok={#name}%
+ \let\currentmodulecategory \empty
+ \edef\currentmodule {#category}%
+ \edef\currentmoduleparameters{#name}%
+ \else
+ \edef\currentmodulecategory {#category}%
+ \edef\currentmodule {#name}%
+ \let \currentmoduleparameters\empty
+ \fi
+ \or
+ \edef\currentmodulecategory {#category}%
+ \edef\currentmodule {#name}%
+ \def \currentmoduleparameters{#parameters}%
+ \fi
+ \processcommacommand[\currentmodule]{\strc_modules_use\currentmodulecategory}%
+ \pop_macro_currentmoduleparameters
+ \pop_macro_currentmodulecategory
+ \pop_macro_currentmodule}
+
+\def\strc_modules_use#category#name%
+ {\ifempty\currentmoduleparameters\else
+ \scratchtoks\expandafter{\currentmoduleparameters}%
+ \normalexpanded{\getparameters[\??module#name:][\the\scratchtoks]}%
+ \fi
+ \clf_usemodules{#category}{#name}}
+
+\aliased\let\usemodule \usemodules
+\aliased\let\usetexmodule\usemodules
+
+\newcount\c_syst_modules_nesting
+\newtoks \everysetupmodule
+
+\permanent\tolerant\protected\def\startmodule[#1]#;#2 %
+ {\global\advance\c_syst_modules_nesting\plusone
+ \push_macro_currentmodule
+ \push_macro_currentmoduleparameters
+ \def\currentmodule{#1#2}}
+
+\permanent\protected\def\stopmodule
+ {\ifcase\c_syst_modules_nesting
+ \writestatus\m!system{module wrapping error in '\currentmodule'}%
+ \else
+ \pop_macro_currentmoduleparameters
+ \pop_macro_currentmodule
+ \global\advance\c_syst_modules_nesting\minusone
+ \fi}
+
+\permanent\protected\def\setupmodule % to be lmtx'd
+ {\ifempty\currentmoduleparameters
+ \expandafter\syst_modules_setup_nop
+ \else
+ \expandafter\syst_modules_setup_yes
+ \fi}
+
+\tolerant\def\syst_modules_setup_nop[#name]#spacer[#parameters]%
+ {\ifarguments
+ % nothing
+ \or
+ \doifassignmentelse{#name}{\getparameters[\??module\currentmodule:][#name]}\donothing
+ \or
+ \getparameters[\??module#name:][#parameters]% internal (defaults)
+ \fi
+ \the\everysetupmodule}
+
+\tolerant\def\syst_modules_setup_yes[#name]#spacer[#parameters]%
+ {\scratchtoks\expandafter{\currentmoduleparameters}%
+ \ifparameters
+ \normalexpanded{\getparameters[\??module\currentmodule:][\the\scratchtoks]}%
+ \or
+ \ifhastok={#name}%
+ \getparameters[\??module\currentmodule:][#name]% internal (defaults)
+ \normalexpanded{\getparameters[\??module\currentmodule:][\the\scratchtoks]}% loadtime (user)
+ \else
+ \normalexpanded{\getparameters[\??module#1:][\the\scratchtoks]}% loadtime (user)
+ \fi
+ \or
+ \getparameters[\??module#name:][#parameters]% internal (defaults)
+ \normalexpanded{\getparameters[\??module#name:][\the\scratchtoks]}% loadtime (user)
+ \fi
+ \let\currentmoduleparameters\empty
+ \the\everysetupmodule}
+
+\permanent\def\moduleparameter#name#parameter% should have been \namedmoduleparameter
+ {\begincsname\??module#name:#parameter\endcsname}
+
+\letvalue\??module\empty % so we default to empty as with all parameters
+
+\permanent\def\currentmoduleparameter{\moduleparameter\currentmodule} % no need for inlining
+
+%permanent\protected\def\useluamodule[#name]{\clf_loadluamodule{#1}} % why not use useluamodule
+
+\permanent\protected\def\useluamodule [#name]{\clf_useluamodule{#1}}
+\permanent\protected\def\luaenvironment #name {\clf_loadluamodule{#1}}
+
+% \usemodule[newmml]
+% \usemodule[newmml][a=b]
+% \usemodule[x][newmml]
+% \usemodule[x][newmml][a=b]
+%
+% \startmodule [mathml]
+% \setupmodule[a=c] \relax [\currentmoduleparameter{a}] % user vars will be set afterwards
+% \setupmodule[a=c] \relax [\currentmoduleparameter{a}] % user vars are now forgotten
+% \stopmodule
+
+% one can introduce test sections with:
+%
+% \enablemode[newmml:test:\currentmoduleparameter{test}]
+% \startmode[newmml:test:yes} ... \stopmode
+%
+% these will be ignored unless test=yes
+%
+% however, a better way is:
+
+\permanent\protected\def\startmoduletestsection
+ {\begingroup
+ \setupmodule % we need to make sure that the vars are set
+ \doifelse{\currentmoduleparameter\v!test}\v!yes
+ {\endgroup
+ \writestatus\currentmodule{loading experimental code}}
+ {\endgroup
+ \writestatus\currentmodule{skipping experimental code}%
+ \gobbleuntil\stopmoduletestsection}}
+
+\aliased\let\stopmoduletestsection\donothing
+
+% will become file-run
+
+%D \macros
+%D {doifolderversionelse}
+%D
+%D We start with a macro specially for Aditya who wants to be able
+%D to use development versions of \MKIV\ for real documents.
+%D
+%D \starttyping
+%D \doifolderversionelse\contextversion{1010.10.10} {OLDER} {OKAY} => OLDER
+%D \doifolderversionelse\contextversion{2020.20.20} {OLDER} {OKAY} => OKAY
+%D \doifolderversionelse\contextversion{2020} {OLDER} {OKAY} => OKAY
+%D \stoptyping
+%D
+%D The version pattern is \type {yyyy.mm.dd} (with mm and dd being optional).
+
+\permanent\protected\def\doifelseolderversion#parent#child{\clf_doifelseolderversion{#parent}{#child}}
+\permanent\protected\def\doifelseoldercontext #child{\clf_doifelseolderversion{#child}{}}
+
+\aliased\let\doifolderversionelse\doifelseolderversion
+\aliased\let\doifoldercontextelse\doifelseoldercontext
+
+%D Relatively new (no need for a speedup here). Can't this now be done nicer?
+
+\permanent\protected\def\syst_modules_direct_lua#1#2%
+ {\edef\m_module_command_command {#1}%
+ \edef\m_module_command_function{#2}%
+ \directsetup{module:\m_module_command_command:start}%
+ \ctxlua{\m_module_command_function()}%
+ \directsetup{module:\m_module_command_command:stop}}
+
+\permanent\protected\def\syst_modules_single_lua#1#2%
+ {\edef\m_module_command_command {#1}%
+ \edef\m_module_command_function{#2}%
+ \dosingleempty\syst_modules_single_lua_indeed}
+
+\permanent\protected\def\syst_modules_single_lua_indeed[#1]%
+ {\directsetup{module:\m_module_command_command:start}%
+ \ctxlua{\m_module_command_function(\!!bs#1\!!es)}%
+ \directsetup{module:\m_module_command_command:stop}}
+
+\permanent\protected\def\syst_modules_double_lua#1#2%
+ {\edef\m_module_command_command {#1}%
+ \edef\m_module_command_function{#2}%
+ \dodoubleempty\syst_modules_double_lua_indeed}
+
+\permanent\protected\def\syst_modules_double_lua_indeed[#1][#2]%
+ {\directsetup{module:\m_module_command_command:start}%
+ \ctxlua{\m_module_command_function(\!!bs#1\!!es,\!!bs#2\!!es)}%
+ \directsetup{module:\m_module_command_command:stop}}
+
+\permanent\protected\def\installmodulecommandlua #1#2{\enforced\permanent\protected\def#1{\normalexpanded{\syst_modules_direct_lua{\csstring#1}{#2}}}}
+\permanent\protected\def\installmodulecommandluasingle#1#2{\enforced\permanent\protected\def#1{\normalexpanded{\syst_modules_single_lua{\csstring#1}{#2}}}}
+\permanent\protected\def\installmodulecommandluadouble#1#2{\enforced\permanent\protected\def#1{\normalexpanded{\syst_modules_double_lua{\csstring#1}{#2}}}}
+
+\protected\def\syst_modules_one_lua#1#2#3%
+ {\directsetup{module:#1:start}%
+ \ctxlua{#2(\!!bs#3\!!es)}%
+ \directsetup{module:#1:stop}}
+
+\protected\def\syst_modules_two_lua#1#2#3#4%
+ {\directsetup{module:#1:start}%
+ \ctxlua{#2(\!!bs#3\!!es,\!!bs#4\!!es)}%
+ \directsetup{module:#1:stop}}
+
+\permanent\protected\def\installmodulecommandluaone#1#2{\enforced\permanent\protected\def#1{\normalexpanded{\syst_modules_one_lua{\csstring#1}{#2}}}}
+\permanent\protected\def\installmodulecommandluatwo#1#2{\enforced\permanent\protected\def#1{\normalexpanded{\syst_modules_two_lua{\csstring#1}{#2}}}}
+
+%D This replaces \type {\fetchruntimecommand}:
+%D
+%D \starttyping
+%D \fetchmodulecommand \csname {module}
+%D \stoptyping
+
+\permanent\protected\def\fetchmodulecommand#1#2%
+ {\mutable\protected\def#1{\syst_fetch_module_command#1{#2}}}
+
+\def\syst_fetch_module_command#1#2% actually a test on #1 being define would be ok as well
+ {%writestatus\m!systems{fetching \string#1}%
+ \usemodule[#2]%
+ #1}
+
+\aliased\let\fetchruntimecommand\fetchmodulecommand % obsolete
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-res.mklx b/Master/texmf-dist/tex/context/base/mkxl/file-res.mklx
new file mode 100644
index 00000000000..a123b5e2213
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-res.mklx
@@ -0,0 +1,145 @@
+%D \module
+%D [ file=file-mod, % was supp-fil,
+%D version=20110701, % 1995.10.10,
+%D title=\CONTEXT\ File Macros,
+%D subtitle=Resolvers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D We could move some more to the lua end (implementors).
+
+\writestatus{loading}{ConTeXt File Macros / Resolvers}
+
+\unprotect
+
+\registerctxluafile{file-res}{}
+
+%D \macros
+%D {readfile,ReadFile}
+%D
+%D One cannot be sure if a file exists. When no file can be found, the \type
+%D {\input} primitive gives an error message and switches to interactive mode. The
+%D macro \type {\readfile} takes care of non||existing files. This macro has two
+%D faces.
+%D
+%D \starttyping
+%D \ReadFile {filename}
+%D \readfile {filename} {before loading} {not found}
+%D \stoptyping
+%D
+%D Many \TEX\ implementations have laid out some strategy for locating files. This
+%D can lead to unexpected results, especially when one loads files that are not
+%D found in the current directory. Let's give an example of this. In \CONTEXT\
+%D illustrations can be defined in an external file. The resizing macro first looks
+%D if an illustration is defined in the local definitions file. When no such file is
+%D found, it searches for a global file and when this file is not found either, the
+%D illustration itself is scanned for dimensions. One can imagine what happens if an
+%D adapted, localy stored illustration, is scaled according to dimensions stored
+%D somewhere else.
+%D
+%D When some \TEX\ implementation starts looking for a file, it normally first looks
+%D in the current directory. When no file is found, \TEX\ starts searching on the
+%D path where format and|/|or style files are stored. Depending on the
+%D implementation this can considerably slow down processing speed.
+%D
+%D In \CONTEXT, we support a project||wise ordening of files. In such an approach it
+%D seems feasible to store common files in a lower directory. When for instance
+%D searching for a general layout file, we therefore have to backtrack.
+%D
+%D These three considerations have lead to a more advanced approach for loading
+%D files.
+%D
+%D We first present an earlier implementation of \type {\readfile}. This command
+%D backtracks parent directories, upto a predefined level. Users can change this
+%D level (on the commandline using a directive); we default to~3.
+%D
+%D We use \type {\normalinput} instead of \type {\input} because we want to be able
+%D to redefine the original \type {\input} when needed, for instance when loading
+%D third party libraries.
+
+\mutable\let\readfilename\empty
+
+\def\syst_files_read_file#protocol#path#name% #true #false
+ {\edef\readfilename{\clf_getreadfilename{#protocol}{#path}{#name}}%
+ \ifempty\readfilename
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\syst_files_read_file_indeed
+ \fi}
+
+\def\syst_files_read_file_indeed#true#false%
+ {#true%
+ \relax
+ \normalinput{\readfilename}%
+ \relax}
+
+%D \macros
+%D {readjobfile,readlocfile,readsysfile,
+%D readfixfile,readsetfile}
+%D
+%D This implementation honnors the third situation, but we still can get unwanted
+%D files loaded and/or can get involved in extensive searching.
+%D
+%D Due to different needs, we decided to offer four alternative loading commands.
+%D With \type {\readjobfile} we load a local file and do no backtracking, while
+%D \type {\readlocfile} backtracks~\number \maxreadlevel\ directories, including the
+%D current one.
+%D
+%D System files can be anywhere and therefore \type {\readsysfile} is not bound to
+%D the current directory and obeys the \TEX\ implementation.
+%D
+%D Of the last two, \type {\readfixfile} searches on the directory specified and
+%D backtracks too, while \type {\readsetfile} does only search on the specified
+%D path.
+%D
+%D The most liberal is \type {\readfile}.
+
+\permanent\protected\def\readjobfile #name{\syst_files_read_file{job} {.}{#name}} % current path, no backtracking
+\permanent\protected\def\readlocfile #name{\syst_files_read_file{loc} {.}{#name}} % current path, backtracking
+\permanent\protected\def\readsysfile #name{\syst_files_read_file{sys} {.}{#name}} % current path, obeys tex search
+\permanent\protected\def\readfixfile#path#name{\syst_files_read_file{fix}{#path}{#name}} % specified path, backtracking
+\permanent\protected\def\readsetfile#path#name{\syst_files_read_file{set}{#path}{#name}} % specified path, no backtracking
+\permanent\protected\def\readfile #name{\syst_files_read_file{any} {.}{#name}}
+\permanent\protected\def\ReadFile #name{\syst_files_read_file{any} {.}{#name}\donothing\donothing}
+
+%D So now we've got ourselves five file loading commands:
+%D
+%D \starttyping
+%D \readfile {filename} {before loading} {not found}
+%D
+%D \readjobfile {filename} {before loading} {not found}
+%D \readlocfile {filename} {before loading} {not found}
+%D \readfixfile {filename} {before loading} {not found}
+%D \readsysfile {directory} {filename} {before loading} {not found}
+%D \stoptyping
+
+\permanent\protected\def\readtexfile#name#true#false%
+ {\pushcatcodetable
+ \catcodetable\ctxcatcodes
+ \readfile{#name}{#true}{#false}%
+ \popcatcodetable}
+
+% \permanent\protected\def\readxmlfile#name#true#false%
+% {\pushcatcodetable
+% \catcodetable\xmlcatcodes
+% \readfile{#name}{#true}{#false}%
+% \popcatcodetable}
+
+%D \macros
+%D {doiflocfileelse,locfilename}
+%D
+%D \starttyping
+%D \doiflocfileelse {filename} {before loading} {not found}
+%D \stoptyping
+
+\permanent\protected\def\doifelselocfile#name{\clf_doifelselocfile{#name}}
+\permanent \def\locfilename #name{\clf_locfilename {#name}}
+
+\aliased\let\doiflocfileelse\doifelselocfile
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/file-syn.mklx b/Master/texmf-dist/tex/context/base/mkxl/file-syn.mklx
new file mode 100644
index 00000000000..25f36f742fc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/file-syn.mklx
@@ -0,0 +1,61 @@
+%D \module
+%D [ file=file-syn, % was core-fil,
+%D version=20110701, % 1997.11.15,
+%D title=\CONTEXT\ File Macros,
+%D subtitle=Module Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt File Macros / Synonyms}
+
+\unprotect
+
+\registerctxluafile{file-syn}{}
+
+%D \macros
+%D {definefilesynonym,definefilefallback}
+%D
+%D One of the problems with loading files is that their names can depend on the
+%D interface language. We therefore need a method to define filesynonyms. The actual
+%D synonyms are defined elsewhere, but look like:
+%D
+%D \starttyping
+%D \definefilesynonym [chemic] [chemie]
+%D \definefilesynonym [einheit] [unit]
+%D \definefilesynonym [unit] [unit]
+%D \stoptyping
+%D
+%D So we can say in english:
+%D
+%D \starttyping
+%D \usemodules[pictex,chemic,unit]
+%D \stoptyping
+%D
+%D and in dutch:
+%D
+%D \starttyping
+%D \usemodules[pictex,chemie,unit]
+%D \stoptyping
+
+\permanent\tolerant\def\definefilesynonym [#name]#spacer[#realname]{\clf_definefilesynonym {#name}{#realname}}
+\permanent\tolerant\def\definefilefallback[#name]#spacer[#alternatives]{\clf_definefilefallback{#name}{#alternatives}}
+
+%D \macros
+%D {truefilename}
+%D
+%D At the system level such a filename can be called upon by saying:
+%D
+%D \starttyping
+%D \truefilename{filename/filesynonym}
+%D \stoptyping
+%D
+%D The implementation shows that nesting is supported.
+
+\permanent\def\truefilename#1{\clf_truefilename{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-aux.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-aux.mklx
new file mode 100644
index 00000000000..caf32510e5c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-aux.mklx
@@ -0,0 +1,26 @@
+%D \module
+%D [ file=font-gds,
+%D version=2012.06.26,
+%D title=\CONTEXT\ Font Support,
+%D subtitle=Helpers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Support / Helpers}
+
+%D Here we collect code moved from other files, like some of the
+%D support modules.
+
+\unprotect
+
+%D Leftovers:
+
+\permanent\protected\def\xbox {\bgroup\aftergroup\egroup\hbox\bgroup\tx \let\next=}
+\permanent\protected\def\xxbox{\bgroup\aftergroup\egroup\hbox\bgroup\txx\let\next=}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-chk.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-chk.lmt
new file mode 100644
index 00000000000..6f927e09764
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-chk.lmt
@@ -0,0 +1,452 @@
+if not modules then modules = { } end modules ['font-chk'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is kind of old but it makes no real sense to upgrade it to for instance
+-- using delayed type 3 fonts in order to be lean and mean and cut'n'paste
+-- compliant. When this kicks one needs to fix the choice of fonts anyway! So,
+-- instead we just keep the method we use but slightly adapted to the backend
+-- of lmtx.
+
+local next = next
+local floor = math.floor
+
+local context = context
+
+local formatters = string.formatters
+local bpfactor = number.dimenfactors.bp
+local fastcopy = table.fastcopy
+local sortedkeys = table.sortedkeys
+local sortedhash = table.sortedhash
+
+local report = logs.reporter("fonts")
+local report_checking = logs.reporter("fonts","checking")
+
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+
+fonts.checkers = fonts.checkers or { }
+local checkers = fonts.checkers
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontcharacters = fonthashes.characters
+
+local currentfont = font.current
+local addcharacters = font.addcharacters
+
+local helpers = fonts.helpers
+
+local addprivate = helpers.addprivate
+local hasprivate = helpers.hasprivate
+local getprivateslot = helpers.getprivateslot
+local getprivatecharornode = helpers.getprivatecharornode
+
+local otffeatures = fonts.constructors.features.otf
+local afmfeatures = fonts.constructors.features.afm
+
+local registerotffeature = otffeatures.register
+local registerafmfeature = afmfeatures.register
+
+local is_character = characters.is_character
+local chardata = characters.data
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+local disableaction = tasks.disableaction
+
+local implement = interfaces.implement
+
+local glyph_code = nodes.nodecodes.glyph
+
+local hpack_node = node.hpack
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local isglyph = nuts.isglyph
+local setchar = nuts.setchar
+
+local nextglyph = nuts.traversers.glyph
+
+local remove_node = nuts.remove
+local insert_node_after = nuts.insert_after
+local insert_node_before = nuts.insert_before
+local copy_node = nuts.copy
+
+local actions = false
+
+-- to tfmdata.properties ?
+
+local function onetimemessage(font,char,message) -- char == false returns table
+ local tfmdata = fontdata[font]
+ local shared = tfmdata.shared
+ if not shared then
+ shared = { }
+ tfmdata.shared = shared
+ end
+ local messages = shared.messages
+ if not messages then
+ messages = { }
+ shared.messages = messages
+ end
+ local category = messages[message]
+ if not category then
+ category = { }
+ messages[message] = category
+ end
+ if char == false then
+ return sortedkeys(category), category
+ end
+ local cc = category[char]
+ if not cc then
+ report_checking("char %C in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message)
+ category[char] = 1
+ else
+ category[char] = cc + 1
+ end
+end
+
+fonts.loggers.onetimemessage = onetimemessage
+
+local fakes = {
+ MissingLowercase = { width = .45, height = .55, depth = .20 },
+ MissingUppercase = { width = .65, height = .70, depth = .25 },
+ MissingMark = { width = .15, height = .70, depth = -.50 },
+ MissingPunctuation = { width = .15, height = .55, depth = .20 },
+ MissingUnknown = { width = .45, height = .20, depth = 0 },
+}
+
+local mapping = allocate {
+ lu = { "MissingUppercase", "darkred" },
+ ll = { "MissingLowercase", "darkred" },
+ lt = { "MissingUppercase", "darkred" },
+ lm = { "MissingLowercase", "darkred" },
+ lo = { "MissingLowercase", "darkred" },
+ mn = { "MissingMark", "darkgreen" },
+ mc = { "MissingMark", "darkgreen" },
+ me = { "MissingMark", "darkgreen" },
+ nd = { "MissingLowercase", "darkblue" },
+ nl = { "MissingLowercase", "darkblue" },
+ no = { "MissingLowercase", "darkblue" },
+ pc = { "MissingPunctuation", "darkcyan" },
+ pd = { "MissingPunctuation", "darkcyan" },
+ ps = { "MissingPunctuation", "darkcyan" },
+ pe = { "MissingPunctuation", "darkcyan" },
+ pi = { "MissingPunctuation", "darkcyan" },
+ pf = { "MissingPunctuation", "darkcyan" },
+ po = { "MissingPunctuation", "darkcyan" },
+ sm = { "MissingLowercase", "darkmagenta" },
+ sc = { "MissingLowercase", "darkyellow" },
+ sk = { "MissingLowercase", "darkyellow" },
+ so = { "MissingLowercase", "darkyellow" },
+}
+
+table.setmetatableindex(mapping, { "MissingUnknown", "darkgray" })
+
+checkers.mapping = mapping
+
+-- We provide access by (private) name for tracing purposes. We also need to make
+-- sure the dimensions are known at the lua and tex end. For previous variants see
+-- the mkiv files or older lmtx files. I decided to just drop the old stuff here.
+
+function checkers.placeholder(font,char)
+ local category = chardata[char].category or "lu" -- todo: unknown
+ local fakedata = mapping[category]
+ local tfmdata = fontdata[font]
+ local units = tfmdata.parameters.units or 1000
+ local slant = (tfmdata.parameters.slant or 0)/65536
+ local scale = units/1000
+ local rawdata = tfmdata.shared and tfmdata.shared.rawdata
+ local weight = (rawdata and rawdata.metadata and rawdata.metadata.pfmweight or 400)/400
+ local specification = {
+ code = "MissingGlyph",
+ scale = scale,
+ slant = slant,
+ weight = weight,
+ namespace = font,
+ shapes = { { shape = fakedata[1], color = fakedata[2] } },
+ }
+ fonts.helpers.setmetaglyphs("missing", font, char, specification)
+end
+
+function checkers.missing(head)
+ local lastfont = nil
+ local characters = nil
+ local found = nil
+ local addplaceholder = checkers.placeholder -- so we can overload
+ if actions.replace or actions.decompose then
+ for n, char, font in nextglyph, head do
+ if font ~= lastfont then
+ lastfont = font
+ characters = fontcharacters[font]
+ end
+ if font > 0 and not characters[char] and is_character[chardata[char].category or "unknown"] then
+ if actions.decompose then
+ local c = chardata[char]
+ if c then
+ local s = c.specials
+ if s and s[1] == "char" then
+ local l = #s
+ if l > 2 then
+ -- check first
+ local okay = true
+ for i=2,l do
+ if not characters[s[i]] then
+ okay = false
+ break
+ end
+ end
+ if okay then
+ -- we work backward
+ local o = n -- we're not supposed to change n (lua 5.4+)
+ onetimemessage(font,char,"missing (decomposed)")
+ setchar(n,s[l])
+ for i=l-1,2,-1 do
+ head, o = insert_node_before(head,o,copy_node(n))
+ setchar(o,s[i])
+ end
+ goto DONE
+ end
+ end
+ end
+ end
+ end
+ if actions.replace then
+ onetimemessage(font,char,"missing (replaced)")
+ local f, c = addplaceholder(font,char)
+ if f and c then
+ setchar(n, c, f)
+ end
+ goto DONE
+ end
+ if actions.remove then
+ onetimemessage(font,char,"missing (deleted)")
+ if not found then
+ found = { n }
+ else
+ found[#found+1] = n
+ end
+ goto DONE
+ end
+ onetimemessage(font,char,"missing (checked)")
+ ::DONE::
+ end
+ end
+ end
+ if found then
+ for i=1,#found do
+ head = remove_node(head,found[i],true)
+ end
+ end
+ return head
+end
+
+local relevant = {
+ "missing (decomposed)",
+ "missing (replaced)",
+ "missing (deleted)",
+ "missing (checked)",
+ "missing",
+}
+
+local function getmissing(id)
+ if id then
+ local list = getmissing(currentfont())
+ if list then
+ local _, list = next(getmissing(currentfont()))
+ return list
+ else
+ return { }
+ end
+ else
+ local t = { }
+ for id, d in next, fontdata do
+ local shared = d.shared
+ local messages = shared and shared.messages
+ if messages then
+ local filename = d.properties.filename
+ if not filename then
+ filename = tostring(d)
+ end
+ local tf = t[filename] or { }
+ for i=1,#relevant do
+ local tm = messages[relevant[i]]
+ if tm then
+ for k, v in next, tm do
+ tf[k] = (tf[k] or 0) + v
+ end
+ end
+ end
+ if next(tf) then
+ t[filename] = tf
+ end
+ end
+ end
+ local l = { }
+ for k, v in next, t do
+ l[k] = sortedkeys(v)
+ end
+ return l, t
+ end
+end
+
+checkers.getmissing = getmissing
+
+do
+
+ local reported = true
+
+ callback.register("glyph_not_found",function(font,char)
+ if font > 0 then
+ if char > 0 then
+ onetimemessage(font,char,"missing")
+ else
+ -- we have a special case
+ end
+ elseif not reported then
+ report("nullfont is used, maybe no bodyfont is defined")
+ reported = true
+ end
+ end)
+
+ local loaded = false
+
+ trackers.register("fonts.missing", function(v)
+ if v then
+ enableaction("processors","fonts.checkers.missing")
+ if v == true then
+ actions = { check = true }
+ else
+ actions = utilities.parsers.settings_to_set(v)
+ if not loaded and actions.replace then
+ metapost.simple("simplefun",'loadfile("mp-miss.mpxl");')
+ loaded = true
+ end
+ end
+ else
+ disableaction("processors","fonts.checkers.missing")
+ actions = false
+ end
+ end)
+
+ logs.registerfinalactions(function()
+ local collected, details = getmissing()
+ if next(collected) then
+ for filename, list in sortedhash(details) do
+ logs.startfilelogging(report,"missing characters",filename)
+ for u, v in sortedhash(list) do
+ report("%4i %U %c %s",v,u,u,chardata[u].description)
+ end
+ logs.stopfilelogging()
+ end
+ if logs.loggingerrors() then
+ for filename, list in sortedhash(details) do
+ logs.starterrorlogging(report,"missing characters",filename)
+ for u, v in sortedhash(list) do
+ report("%4i %U %c %s",v,u,u,chardata[u].description)
+ end
+ logs.stoperrorlogging()
+ end
+ end
+ end
+ end)
+
+end
+
+-- for the moment here
+
+local function expandglyph(characters,index,done)
+ done = done or { }
+ if not done[index] then
+ local data = characters[index]
+ if data then
+ done[index] = true
+ local d = fastcopy(data)
+ local n = d.next
+ if n then
+ d.next = expandglyph(characters,n,done)
+ end
+ local h = d.horiz_variants
+ if h then
+ for i=1,#h do
+ h[i].glyph = expandglyph(characters,h[i].glyph,done)
+ end
+ end
+ local v = d.vert_variants
+ if v then
+ for i=1,#v do
+ v[i].glyph = expandglyph(characters,v[i].glyph,done)
+ end
+ end
+ return d
+ end
+ end
+end
+
+helpers.expandglyph = expandglyph
+
+-- should not be needed as we add .notdef in the engine
+
+local dummyzero = {
+ -- width = 0,
+ -- height = 0,
+ -- depth = 0,
+ commands = { { "special", "" } },
+}
+
+local function adddummysymbols(tfmdata)
+ local characters = tfmdata.characters
+ if not characters[0] then
+ characters[0] = dummyzero
+ end
+ -- if not characters[1] then
+ -- characters[1] = dummyzero -- test only
+ -- end
+end
+
+local dummies_specification = {
+ name = "dummies",
+ description = "dummy symbols",
+ default = true,
+ manipulators = {
+ base = adddummysymbols,
+ node = adddummysymbols,
+ }
+}
+
+registerotffeature(dummies_specification)
+registerafmfeature(dummies_specification)
+
+--
+
+local function addvisualspace(tfmdata)
+ local spacechar = tfmdata.characters[32]
+ if spacechar and not spacechar.commands then
+ local w = spacechar.width
+ local h = tfmdata.parameters.xheight
+ local c = {
+ width = w,
+ commands = { { "rule", h, w } }
+ }
+ local u = addprivate(tfmdata, "visualspace", c)
+ end
+end
+
+local visualspace_specification = {
+ name = "visualspace",
+ description = "visual space",
+ default = true,
+ manipulators = {
+ base = addvisualspace,
+ node = addvisualspace,
+ }
+}
+
+registerotffeature(visualspace_specification)
+registerafmfeature(visualspace_specification)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-chk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/font-chk.mkxl
new file mode 100644
index 00000000000..34d67648270
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-chk.mkxl
@@ -0,0 +1,56 @@
+%D \module
+%D [ file=font-chk,
+%D version=2009.01.02, % moved from font-tra
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Checking,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Checking}
+
+\registerctxluafile{font-chk}{autosuffix}
+
+\tracinglostchars\zerocount
+
+% Use this instead:
+%
+% \definefontfeature[default][default][missing=yes]
+% \enabletrackers[fonts.missing=replace]
+
+\permanent\protected\def\checkmissingcharacters {\enabletrackers[fonts.missing]}
+\permanent\protected\def\removemissingcharacters {\enabletrackers[fonts.missing=remove]}
+\permanent\protected\def\replacemissingcharacters{\enabletrackers[fonts.missing=replace]}
+\permanent\protected\def\handlemissingcharacters {\enabletrackers[fonts.missing={decompose,replace}]}
+
+% \enableexperiments[fonts.compact]
+
+%D \starttyping
+%D \replacemissingcharacters
+%D
+%D \startbuffer
+%D \startlines[before=,after=]
+%D \strut {\tf test \char 12345\ test \char 12346\ test}
+%D \strut {\bf test \char 12345\ test \char 12346\ test}
+%D \strut {\it test \char 12345\ test \char 12346\ test}
+%D \strut {\bi test \char 12345\ test \char 12346\ test}
+%D \strut {test ὀ ρ φ α ν ῖ ο ς test}
+%D \stoplines
+%D \stopbuffer
+%D
+%D \starttext
+%D \startTEXpage[offset=2pt,strut=no]
+%D \getbuffer
+%D \switchtobodyfont[pagella]
+%D \getbuffer
+%D \switchtobodyfont[dejavu]
+%D \getbuffer
+%D \stopTEXpage
+%D \stoptext
+%D \stoptyping
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt
new file mode 100644
index 00000000000..41d3274008c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-col.lmt
@@ -0,0 +1,525 @@
+if not modules then modules = { } end modules ['font-col'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- possible optimization: delayed initialization of vectors
+-- we should also share equal vectors (math)
+
+local context, commands, trackers, logs = context, commands, trackers, logs
+local node, nodes, fonts, characters = node, nodes, fonts, characters
+local file, lpeg, table, string = file, lpeg, table, string
+
+local type, next, tonumber, toboolean = type, next, tonumber, toboolean
+local gmatch = string.gmatch
+local fastcopy = table.fastcopy
+local formatters = string.formatters
+
+local nuts = nodes.nuts
+
+local setfont = nuts.setfont
+
+----- traverse_char = nuts.traverse_char
+local nextchar = nuts.traversers.char
+local getscales = nuts.getscales
+local setscales = nuts.setscales
+local setprop = nuts.setprop
+
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local trace_collecting = false trackers.register("fonts.collecting", function(v) trace_collecting = v end)
+
+local report_fonts = logs.reporter("fonts","collections")
+
+local texconditionals = tex.conditionals
+
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+
+local collections = fonts.collections or { }
+fonts.collections = collections
+
+local definitions = collections.definitions or { }
+collections.definitions = definitions
+
+local vectors = collections.vectors or { }
+collections.vectors = vectors
+
+local helpers = fonts.helpers
+local charcommand = helpers.commands.char
+local rightcommand = helpers.commands.right
+local addprivate = helpers.addprivate
+local hasprivate = helpers.hasprivate
+local fontpatternhassize = helpers.fontpatternhassize
+
+local hashes = fonts.hashes
+local fontdata = hashes.identifiers
+local fontquads = hashes.quads
+local chardata = hashes.characters
+local propdata = hashes.properties
+local mathparameters = hashes.mathparameters
+
+local currentfont = font.current
+local addcharacters = font.addcharacters
+
+local implement = interfaces.implement
+
+local list = { }
+local current = 0
+local enabled = false
+
+local validvectors = table.setmetatableindex(function(t,k)
+ local v = false
+ if not mathparameters[k] then
+ v = vectors[k]
+ end
+ t[k] = v
+ return v
+end)
+
+local function checkenabled()
+ -- a bit ugly but nicer than a fuzzy state while defining math
+ if next(vectors) then
+ if not enabled then
+ enableaction("processors","fonts.collections.process")
+ enabled = true
+ end
+ else
+ if enabled then
+ disableaction("processors","fonts.collections.process")
+ enabled = false
+ end
+ end
+end
+
+collections.checkenabled = checkenabled
+
+function collections.reset(name,font)
+ if font and font ~= "" then
+ local d = definitions[name]
+ if d then
+ d[font] = nil
+ if not next(d) then
+ definitions[name] = nil
+ end
+ end
+ else
+ definitions[name] = nil
+ end
+end
+
+function collections.define(name,font,ranges,details)
+ -- todo: details -> method=force|conditional rscale=
+ -- todo: remap=name
+ local d = definitions[name]
+ if not d then
+ d = { }
+ definitions[name] = d
+ end
+ if name and trace_collecting then
+ report_fonts("extending collection %a using %a",name,font)
+ end
+ details = settings_to_hash(details)
+ -- todo, combine per font start/stop as arrays
+ local offset = details.offset
+ if type(offset) == "string" then
+ offset = characters.getrange(offset,true) or false
+ else
+ offset = tonumber(offset) or false
+ end
+ local target = details.target
+ if type(target) == "string" then
+ target = characters.getrange(target,true) or false
+ else
+ target = tonumber(target) or false
+ end
+ local rscale = tonumber (details.rscale) or 1
+ local force = toboolean(details.force,true)
+ local check = toboolean(details.check,true)
+ local factor = tonumber(details.factor)
+ local features = details.features
+ for s in gmatch(ranges,"[^, ]+") do
+ local start, stop, description, gaps = characters.getrange(s,true)
+ if start and stop then
+ if trace_collecting then
+ if description then
+ report_fonts("using range %a, slots %U - %U, description %a)",s,start,stop,description)
+ end
+ for i=1,#d do
+ local di = d[i]
+ if (start >= di.start and start <= di.stop) or (stop >= di.start and stop <= di.stop) then
+ report_fonts("overlapping ranges %U - %U and %U - %U",start,stop,di.start,di.stop)
+ end
+ end
+ end
+ d[#d+1] = {
+ font = font,
+ start = start,
+ stop = stop,
+ gaps = gaps,
+ offset = offset,
+ target = target,
+ rscale = rscale,
+ force = force,
+ check = check,
+ method = details.method,
+ factor = factor,
+ features = features,
+ }
+ end
+ end
+end
+
+-- todo: provide a lua variant (like with definefont)
+
+function collections.registermain(name)
+ local last = currentfont()
+ if trace_collecting then
+ report_fonts("registering font %a with name %a",last,name)
+ end
+ list[#list+1] = last
+end
+
+-- check: when true, only set when present in font
+-- force: when false, then not set when already set
+
+local uccodes = characters.uccodes
+local lccodes = characters.lccodes
+
+local methods = {
+ lowercase = function(oldchars,newchars,vector,start,stop,cloneid)
+ for k, v in next, oldchars do
+ if k >= start and k <= stop then
+ local lccode = lccodes[k]
+ if k ~= lccode and newchars[lccode] then
+ vector[k] = { cloneid, lccode }
+ end
+ end
+ end
+ end,
+ uppercase = function(oldchars,newchars,vector,start,stop,cloneid)
+ for k, v in next, oldchars do
+ if k >= start and k <= stop then
+ local uccode = uccodes[k]
+ if k ~= uccode and newchars[uccode] then
+ vector[k] = { cloneid, uccode }
+ end
+ end
+ end
+ end,
+}
+
+function collections.clonevector(name)
+ statistics.starttiming(fonts)
+ if trace_collecting then
+ report_fonts("processing collection %a",name)
+ end
+ local definitions = definitions[name]
+ local vector = { }
+ vectors[current] = vector
+ for i=1,#definitions do
+ local definition = definitions[i]
+ local name = definition.font
+ local start = definition.start
+ local stop = definition.stop
+ local check = definition.check
+ local force = definition.force
+ local offset = definition.offset or start
+ local remap = definition.remap -- not used
+ local target = definition.target
+ local method = definition.method
+ local cloneid = list[i]
+ local oldchars = fontdata[current].characters
+ local newchars = fontdata[cloneid].characters
+ local factor = definition.factor
+ local rscale = definition.rscale
+ if factor then
+ vector.factor = factor
+ end
+ if texconditionals["c_font_compact"] and rscale then
+ vector.rscale = rscale
+ end
+ if trace_collecting then
+ if target then
+ report_fonts("remapping font %a to %a for range %U - %U, offset %X, target %U",current,cloneid,start,stop,offset,target)
+ else
+ report_fonts("remapping font %a to %a for range %U - %U, offset %X",current,cloneid,start,stop,offset)
+ end
+ end
+ if method then
+ method = methods[method]
+ end
+ if method then
+ method(oldchars,newchars,vector,start,stop,cloneid)
+ elseif check then
+ if target then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if not newchars[target] then
+ -- not in font
+ elseif force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, target }
+ end
+ target = target + 1
+ end
+ elseif remap then
+ -- not used
+ else
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if not newchars[unicode] then
+ -- not in font
+ elseif force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = cloneid
+ end
+ end
+ end
+ else
+ if target then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, target }
+ end
+ target = target + 1
+ end
+ elseif remap then
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = { cloneid, remap[unicode] }
+ end
+ end
+ else
+ for unicode = start, stop do
+ local unic = unicode + offset - start
+ if force or (not vector[unic] and not oldchars[unic]) then
+ vector[unic] = cloneid
+ end
+ end
+ end
+ end
+ end
+ if trace_collecting then
+ report_fonts("activating collection %a for font %a",name,current)
+ end
+ statistics.stoptiming(fonts)
+ -- for WS: needs checking
+ if validvectors[current] then
+ checkenabled()
+ end
+end
+
+-- we already have this parser
+--
+-- local spec = (P("sa") + P("at") + P("scaled") + P("at") + P("mo")) * P(" ")^1 * (1-P(" "))^1 * P(" ")^0 * -1
+-- local okay = ((1-spec)^1 * spec * Cc(true)) + Cc(false)
+--
+-- if lpegmatch(okay,name) then
+
+function collections.prepare(name) -- we can do this in lua now .. todo
+ current = currentfont()
+ if vectors[current] then
+ return
+ end
+ local properties = propdata[current]
+ local mathsize = properties.mathsize
+ if mathsize == 1 or mathsize == 2 or mathsize == 3 then
+ return
+ end
+ local d = definitions[name]
+ if d then
+ if trace_collecting then
+ local filename = file.basename(properties.filename or "?")
+ report_fonts("applying collection %a to %a, file %a",name,current,filename)
+ end
+ list = { }
+ context.pushcatcodes("prt") -- context.unprotect()
+ context.font_fallbacks_start_cloning()
+ for i=1,#d do
+ local f = d[i]
+ local name = f.font
+ local scale = f.rscale or 1
+ if texconditionals["c_font_compact"] then
+ scale = 1
+ end
+ if fontpatternhassize(name) then
+ context.font_fallbacks_clone_unique(name,scale)
+ else
+ context.font_fallbacks_clone_inherited(name,scale)
+ end
+ context.font_fallbacks_register_main(name)
+ end
+ context.font_fallbacks_prepare_clone_vectors(name)
+ context.font_fallbacks_stop_cloning()
+ context.popcatcodes() -- context.protect()
+ end
+end
+
+function collections.report(message)
+ if trace_collecting then
+ report_fonts("tex: %s",message)
+ end
+end
+
+local function monoslot(font,char,parent,factor)
+ local tfmdata = fontdata[font]
+ local privatename = formatters["faked mono %s"](char)
+ local privateslot = hasprivate(tfmdata,privatename)
+ if privateslot then
+ return privateslot
+ else
+ local characters = tfmdata.characters
+ local properties = tfmdata.properties
+ local width = factor * fontquads[parent]
+ local character = characters[char]
+ if character then
+ -- runtime patching of the font (can only be new characters)
+ -- instead of messing with existing dimensions
+ local data = {
+ -- no features so a simple copy
+ width = width,
+ height = character.height,
+ depth = character.depth,
+ commands = {
+ rightcommand[(width - character.width or 0)/2],
+ charcommand[char],
+ }
+ }
+ local u = addprivate(tfmdata, privatename, data)
+ addcharacters(properties.id, { characters = { [u] = data } } )
+ return u
+ else
+ return char
+ end
+ end
+end
+
+function collections.register(font,char,handler)
+ if font and char and type(handler) == "function" then
+ local vector = vectors[font]
+ if not vector then
+ vector = { }
+ vectors[font] = vector
+ end
+ vector[char] = handler
+ end
+end
+
+-- todo: also general one for missing
+
+function collections.process(head) -- this way we keep feature processing
+ for n, char, font in nextchar, head do
+ local vector = validvectors[font]
+ if vector then
+ local vect = vector[char]
+ if vect then
+ local kind = type(vect)
+ local newfont, newchar
+ if kind == "table" then
+ newfont = vect[1]
+ newchar = vect[2]
+ if trace_collecting then
+ report_fonts("remapping character %C in font %a to character %C in font %a%s",
+ char,font,newchar,newfont,not chardata[newfont][newchar] and " (missing)" or ""
+ )
+ end
+ elseif kind == "function" then
+ newfont, newchar = vect(font,char,vector)
+ if not newfont then
+ newfont = font
+ end
+ if not newchar then
+ newchar = char
+ end
+ if trace_collecting then
+ report_fonts("remapping character %C in font %a to character %C in font %a%s",
+ char,font,newchar,newfont,not chardata[newfont][newchar] and " (missing)" or ""
+ )
+ end
+ vector[char] = { newfont, newchar }
+ else
+ local fakemono = vector.factor
+ if trace_collecting then
+ report_fonts("remapping font %a to %a for character %C%s",
+ font,vect,char,not chardata[vect][char] and " (missing)" or ""
+ )
+ end
+ newfont = vect
+ if fakemono then
+ newchar = monoslot(vect,char,font,fakemono)
+ else
+ newchar = char
+ end
+ end
+ local rscale = vector.rscale
+ if rscale and rscale ~= 1 then
+ local s, x, y = getscales(n)
+ setscales(n,s*rscale,x*rscale,y*rscale)
+ end
+ setfont(n,newfont,newchar)
+ setprop(n, "original", { font = font, char = char })
+ end
+ end
+ end
+ return head
+end
+
+function collections.found(font,char) -- this way we keep feature processing
+ if not char then
+ font, char = currentfont(), font
+ end
+ if chardata[font][char] then
+ return true -- in normal font
+ else
+ local v = vectors[font]
+ return v and v[char] and true or false
+ end
+end
+
+-- interface
+
+implement {
+ name = "fontcollectiondefine",
+ actions = collections.define,
+ arguments = "4 strings",
+}
+
+implement {
+ name = "fontcollectionreset",
+ actions = collections.reset,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "fontcollectionprepare",
+ actions = collections.prepare,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionreport",
+ actions = collections.report,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionregister",
+ actions = collections.registermain,
+ arguments = "string"
+}
+
+implement {
+ name = "fontcollectionclone",
+ actions = collections.clonevector,
+ arguments = "string"
+}
+
+implement {
+ name = "doifelsecharinfont",
+ actions = { collections.found, commands.doifelse },
+ arguments = "integer"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-col.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-col.mklx
new file mode 100644
index 00000000000..e6500dd7620
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-col.mklx
@@ -0,0 +1,150 @@
+%D \module
+%D [ file=font-col,
+%D version=2008.06.11,
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Fallbacks (collections),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo : missing only, force always, multiple fallbacks with test, scale
+% beware : only english key/values
+%
+% \resetfontfallback [whatever]
+%
+% \definefontfallback [whatever] [Slanted] [0x0060-0x007F] [force=yes]
+% \definefontfallback [whatever] [Bold] [0x0080-0x00FF,0x00A0-0x00AF] [rscale=1.2]
+% \definefontfallback [whatever] [BoldSlanted] [0x00C0-0x00C7] [check=yes,force=yes]
+%
+% \definefontfeature [emboldened] [effect={width=0.1,delta=0.4,factor=0.3}]
+% \definefontsynonym [SansEmboldened] [Sans] [features=emboldened]
+% \definefontfallback[FakeSansCaps] [SansEmboldened] [0x0000-0xFFFF] [rscale=.8,method=uppercase]
+% \definefontsynonym [SansCaps] [file:MyriadPro-Regular.otf] [fallbacks=FakeSansCaps]
+
+\writestatus{loading}{ConTeXt Font Macros / Collections}
+
+\registerctxluafile{font-col}{autosuffix}
+
+\unprotect
+
+\permanent\tolerant\protected\def\definefontfallback[#name]#spacer[#font]#spacer[#ranges]#spacer[#settings]%
+ {\let\mathsizesuffix\relax
+ \clf_fontcollectiondefine{#name}{#font}{#ranges}{#settings}%
+ \let\mathsizesuffix\empty}
+
+\permanent\tolerant\protected\def\resetfontfallback[#name]#spacer[#font]%
+ {\clf_fontcollectionreset{#name}{#font}}
+
+\def\font_fallbacks_prepare#name%
+ {\clf_fontcollectionprepare{#name}} % add fallbacks to last font
+
+% we might as well move the handling to lua but then we need to pass the
+% fallbacks, skewchar etc.
+
+\newconditional\c_font_fallbacks_in_progress
+
+\def\font_fallbacks_prepare_indeed#fallbacks%
+ {\begingroup
+ \settrue\c_font_fallbacks_in_progress
+ \lastrawfontcall % sets current font id
+ \font_fallbacks_prepare#fallbacks%
+ \endgroup}
+
+\let\m_font_fallbacks\empty % set in the font-ini
+
+\appendtoks
+ \ifconditional\c_font_fallbacks_in_progress\else
+ \ifempty\m_font_fallbacks
+ \ifempty\m_font_class_fallbacks\else
+ \font_fallbacks_prepare_indeed\m_font_class_fallbacks
+ \fi
+ \else
+ \font_fallbacks_prepare_indeed\m_font_fallbacks
+ \fi
+ \fi
+\to\everydefinefont
+
+\let\m_font_fallbacks_spec\empty
+
+\def\font_fallbacks_start_cloning
+ {\begingroup
+ \let\m_font_fallbacks_spec\somefontspec}
+
+\def\font_fallbacks_stop_cloning
+ {\endgroup}
+
+\def\font_fallbacks_clone_unique#specification#scale% kind of dododefinefont
+ {\clf_fontcollectionreport{defining #specification (relative scale: #scale)}% brrr
+ \setfalse\c_font_auto_size
+ \let\lastfontidentifier\s!dummy
+ \def\v_font_size_relative{#scale}%
+ \let\v_font_size_absolute\fontbody
+ \font_helpers_low_level_define{#specification}\v_font_identifier_basic
+ \csname\v_font_identifier_basic\endcsname
+ \setfalse\c_font_auto_size} % no \setfontcharacteristics and \the\everyfontswitch needed
+
+\def\font_fallbacks_clone_inherited#font% also a second argument
+ {\font_fallbacks_clone_unique{#font \m_font_fallbacks_spec}}
+
+\def\font_fallbacks_register_main #name{\clf_fontcollectionregister{#name}}
+\def\font_fallbacks_prepare_clone_vectors#name{\clf_fontcollectionclone{#name}}
+
+% math (experiment, todo clf_)
+
+\def\font_fallbacks_register_math#1#2#3#4%
+ {\doifelsenothing{#3}%
+ {\definedfont[#2 at #4sp]}%
+ {\definedfont[#2*#3\space at #4\scaledpoint]}%
+ \clf_registerfontfallbackid#1\space\fontid\font\space{#2}}
+
+% \def\font_fallbacks_finish_math
+% {\ctxlua{mathematics.finishfallbacks()}}
+
+% check : only replace when present in replacement font (default: no)
+% force : force replacent even when basefont has glyph (default: yes)
+
+% \definefontfallback [whatever] [Slanted] [0x0060-0x007F] [force=yes]
+% \definefontfallback [whatever] [Bold] [0x0080:0x00FF,0x00A0:0x00AF] [rscale=2.0]
+% \definefontfallback [whatever] [BoldSlanted] ["00C0-"00C7] [check=yes,force=yes]
+% \definefontfallback [whatever] [Mono] [latinextendeda] [force=yes] % 0x0100:0x017F
+% \definefontfallback [whatever] [Mono] [latin extended a] [force=yes] % 0x0100:0x017F
+%
+% \definefontsynonym[SerifPlus][Serif][fallbacks=whatever]
+%
+% \showfont[SerifPlus][all]
+
+% \definefontfeature[zh][mode=node,script=hang,lang=zhs]
+% \definefontfallback[serifwhatever] [lmroman10-regular] [0x0000-0x0400][force=yes]
+% \definefontfallback[serifboldwhatever] [lmroman10-bold] [0x0000-0x0400][force=yes]
+% \definefontfallback[serifitalicwhatever] [lmroman10-italic] [0x0000-0x0400][force=yes]
+% \definefontfallback[serifbolditalicwhatever][lmroman10-bolditalic][0x0000-0x0400][force=yes]
+%
+% \starttypescript [serif] [zhfont]
+% \definefontsynonym [zhserif] [AdobeSongStd-Light] [features=zh,fallbacks=serifwhatever]
+% \definefontsynonym [zhserifbold] [AdobeHeitiStd-Regular][features=zh,fallbacks=serifboldwhatever]
+% \definefontsynonym [zhserifitalic] [AdobeKaitiStd-Regular][features=zh,fallbacks=serifitalicwhatever]
+% \definefontsynonym [zhserifbolditalic][AdobeHeitiStd-Regular][features=zh,fallbacks=serifbolditalicwhatever]
+% \stoptypescript
+%
+% \starttypescript [serif][zhfont][name]
+% \definefontsynonym[Serif] [zhserif] % [fallbacks=serifwhatever]
+% \definefontsynonym[SerifBold] [zhserifbold] % [fallbacks=serifboldwhatever]
+% \definefontsynonym[SerifItalic] [zhserifitalic] % [fallbacks=serifitalicwhatever]
+% \definefontsynonym[SerifBoldItalic] [zhserifbolditalic] % [fallbacks=serifbolditalicwhatever]
+% \stoptypescript
+%
+% \starttypescript[myfont]
+% \definetypeface[myfont][rm][serif][zhfont]
+% \stoptypescript
+%
+% \usetypescript[myfont] \setupbodyfont[myfont,rm,12pt]
+%
+% \starttext
+% fonts {\bf fonts} {\bi fonts} {\it fonts}
+% \stoptext
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt
new file mode 100644
index 00000000000..bebc4e4984f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt
@@ -0,0 +1,1508 @@
+if not modules then modules = { } end modules ['font-con'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- some names of table entries will be changed (no _)
+
+local next, tostring, tonumber, rawget = next, tostring, tonumber, rawget
+local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find
+local sort, insert, concat = table.sort, table.insert, table.concat
+local sortedkeys, sortedhash, serialize, fastcopy = table.sortedkeys, table.sortedhash, table.serialize, table.fastcopy
+local derivetable = table.derive
+local ioflush = io.flush
+local round = math.round
+local setmetatable, getmetatable, rawget, rawset = setmetatable, getmetatable, rawget, rawset
+
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+local trace_scaling = false trackers.register("fonts.scaling", function(v) trace_scaling = v end)
+
+local report_defining = logs.reporter("fonts","defining")
+
+-- watch out: no negative depths and negative eights permitted in regular fonts
+
+--[[ldx--
+<p>Here we only implement a few helper functions.</p>
+--ldx]]--
+
+local fonts = fonts
+local constructors = fonts.constructors or { }
+fonts.constructors = constructors
+local handlers = fonts.handlers or { } -- can have preloaded tables
+fonts.handlers = handlers
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+-- will be directives
+
+constructors.version = 1.01
+constructors.cache = containers.define("fonts", "constructors", constructors.version, false)
+
+constructors.privateoffset = fonts.privateoffsets.textbase or 0xF0000
+
+-- This might become an interface:
+
+local designsizes = allocate()
+constructors.designsizes = designsizes
+local loadedfonts = allocate()
+constructors.loadedfonts = loadedfonts
+
+----- scalecommands = fonts.helpers.scalecommands
+
+--[[ldx--
+<p>We need to normalize the scale factor (in scaled points). This has to
+do with the fact that <l n='tex'/> uses a negative multiple of 1000 as
+a signal for a font scaled based on the design size.</p>
+--ldx]]--
+
+local factors = {
+ pt = 65536.0,
+ bp = 65781.8,
+}
+
+function constructors.setfactor(f)
+ constructors.factor = factors[f or 'pt'] or factors.pt
+end
+
+constructors.setfactor()
+
+function constructors.scaled(scaledpoints, designsize) -- handles designsize in sp as well
+ if scaledpoints < 0 then
+ local factor = constructors.factor
+ if designsize then
+ if designsize > factor then -- or just 1000 / when? mp?
+ return (- scaledpoints/1000) * designsize -- sp's
+ else
+ return (- scaledpoints/1000) * designsize * factor
+ end
+ else
+ return (- scaledpoints/1000) * 10 * factor
+ end
+ else
+ return scaledpoints
+ end
+end
+
+function constructors.getprivate(tfmdata)
+ local properties = tfmdata.properties
+ local private = properties.private
+ properties.private = private + 1
+ return private
+end
+
+function constructors.setmathparameter(tfmdata,name,value)
+ local m = tfmdata.mathparameters
+ local c = tfmdata.MathConstants
+ if m then
+ m[name] = value
+ end
+ if c and c ~= m then
+ c[name] = value
+ end
+end
+
+function constructors.getmathparameter(tfmdata,name)
+ local p = tfmdata.mathparameters or tfmdata.MathConstants
+ if p then
+ return p[name]
+ end
+end
+
+--[[ldx--
+<p>Beware, the boundingbox is passed as reference so we may not overwrite it
+in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to
+excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p>
+--ldx]]--
+
+-- The scaler is only used for otf and afm and virtual fonts. If a virtual font has italic
+-- correction make sure to set the hasitalics flag. Some more flags will be added in the
+-- future.
+
+--[[ldx--
+<p>The reason why the scaler was originally split, is that for a while we experimented
+with a helper function. However, in practice the <l n='api'/> calls are too slow to
+make this profitable and the <l n='lua'/> based variant was just faster. A days
+wasted day but an experience richer.</p>
+--ldx]]--
+
+-- experimental, sharing kerns (unscaled and scaled) saves memory
+-- local sharedkerns, basekerns = constructors.check_base_kerns(tfmdata)
+-- loop over descriptions (afm and otf have descriptions, tfm not)
+-- there is no need (yet) to assign a value to chr.tonunicode
+
+-- constructors.prepare_base_kerns(tfmdata) -- optimalization
+
+-- we have target.name=metricfile and target.fullname=RealName and target.filename=diskfilename
+-- when collapsing fonts, luatex looks as both target.name and target.fullname as ttc files
+-- can have multiple subfonts
+
+function constructors.calculatescale(tfmdata,scaledpoints)
+ -- implemented in font-ctx.lmt
+end
+
+function constructors.assignmathparameters(target,original)
+ -- implemented in math-act.lua
+end
+
+function constructors.beforecopyingcharacters(target,original)
+ -- can be used for additional tweaking
+end
+
+function constructors.aftercopyingcharacters(target,original)
+ -- can be used for additional tweaking
+end
+
+function constructors.trytosharefont(target,tfmdata)
+ -- implemented in font-def.lmt
+end
+
+local synonyms = {
+ exheight = "x_height",
+ xheight = "x_height",
+ ex = "x_height",
+ emwidth = "quad",
+ em = "quad",
+ spacestretch = "space_stretch",
+ stretch = "space_stretch",
+ spaceshrink = "space_shrink",
+ shrink = "space_shrink",
+ extraspace = "extra_space",
+ xspace = "extra_space",
+ slantperpoint = "slant",
+}
+
+function constructors.enhanceparameters(parameters)
+ local mt = getmetatable(parameters)
+ local getter = function(t,k)
+ if not k then
+ return nil
+ end
+ local s = synonyms[k]
+ if s then
+ return rawget(t,s) or (mt and mt[s]) or nil
+ end
+ if k == "spacing" then
+ return {
+ width = t.space,
+ stretch = t.space_stretch,
+ shrink = t.space_shrink,
+ extra = t.extra_space,
+ }
+ end
+ return mt and mt[k] or nil
+ end
+ local setter = function(t,k,v)
+ if not k then
+ return 0
+ end
+ local s = synonyms[k]
+ if s then
+ rawset(t,s,v)
+ elseif k == "spacing" then
+ if type(v) == "table" then
+ rawset(t,"space",v.width or 0)
+ rawset(t,"space_stretch",v.stretch or 0)
+ rawset(t,"space_shrink",v.shrink or 0)
+ rawset(t,"extra_space",v.extra or 0)
+ end
+ else
+ rawset(t,k,v)
+ end
+ end
+ setmetatable(parameters, {
+ __index = getter,
+ __newindex = setter,
+ })
+end
+
+local function mathkerns(v,vdelta)
+ local k = { }
+ for i=1,#v do
+ local entry = v[i]
+ local height = entry.height
+ local kern = entry.kern
+ k[i] = {
+ height = height and vdelta*height or 0,
+ kern = kern and vdelta*kern or 0,
+ }
+ end
+ return k
+end
+
+local psfake = 0
+
+local function fixedpsname(psname,fallback)
+ local usedname = psname
+ if psname and psname ~= "" then
+ if find(psname," ",1,true) then
+ usedname = gsub(psname,"[%s]+","-")
+ else
+ -- we assume that the name is sane enough (we might sanitize completely some day)
+ end
+ elseif not fallback or fallback == "" then
+ psfake = psfake + 1
+ psname = "fakename-" .. psfake
+ else
+ -- filenames can be a mess so we do a drastic cleanup
+ psname = fallback
+ usedname = gsub(psname,"[^a-zA-Z0-9]+","-")
+ end
+ return usedname, psname ~= usedname
+end
+
+function constructors.scale(tfmdata,specification)
+ local target = { } -- the new table
+ --
+ if tonumber(specification) then
+ specification = { size = specification }
+ end
+ target.specification = specification
+ --
+ local scalecommands = fonts.helpers.scalecommands -- defined later
+ --
+ local scaledpoints = specification.size
+ local relativeid = specification.relativeid
+ --
+ local properties = tfmdata.properties or { }
+ local goodies = tfmdata.goodies or { }
+ local resources = tfmdata.resources or { }
+ local descriptions = tfmdata.descriptions or { } -- bad news if empty
+ local characters = tfmdata.characters or { } -- bad news if empty
+ local changed = tfmdata.changed or { } -- for base mode
+ local shared = tfmdata.shared or { }
+ local parameters = tfmdata.parameters or { }
+ local mathparameters = tfmdata.mathparameters or { }
+ --
+ local targetcharacters = { }
+ local targetdescriptions = derivetable(descriptions)
+ local targetparameters = derivetable(parameters)
+ local targetproperties = derivetable(properties)
+ local targetgoodies = goodies -- we need to loop so no metatable
+ target.characters = targetcharacters
+ target.descriptions = targetdescriptions
+ target.parameters = targetparameters
+ -- target.mathparameters = targetmathparameters -- happens elsewhere
+ target.properties = targetproperties
+ target.goodies = targetgoodies
+ target.shared = shared
+ target.resources = resources
+ target.unscaled = tfmdata -- the original unscaled one
+ --
+ -- specification.mathsize : 1=text 2=script 3=scriptscript
+ -- specification.textsize : natural (text)size
+ -- parameters.mathsize : 1=text 2=script 3=scriptscript >1000 enforced size (feature value other than yes)
+ --
+ local mathsize = tonumber(specification.mathsize) or 0
+ local textsize = tonumber(specification.textsize) or scaledpoints
+ local forcedsize = tonumber(parameters.mathsize ) or 0 -- can be set by the feature "mathsize"
+ local extrafactor = tonumber(specification.factor ) or 1
+ if (mathsize == 2 or forcedsize == 2) and parameters.scriptpercentage then
+ scaledpoints = parameters.scriptpercentage * textsize / 100
+ elseif (mathsize == 3 or forcedsize == 3) and parameters.scriptscriptpercentage then
+ scaledpoints = parameters.scriptscriptpercentage * textsize / 100
+ elseif forcedsize > 1000 then -- safeguard
+ scaledpoints = forcedsize
+ else
+ -- in context x and xx also use mathsize
+ end
+ targetparameters.mathsize = mathsize
+ targetparameters.textsize = textsize
+ targetparameters.forcedsize = forcedsize
+ targetparameters.extrafactor = extrafactor
+ --
+ local defaultwidth = resources.defaultwidth or 0
+ local defaultheight = resources.defaultheight or 0
+ local defaultdepth = resources.defaultdepth or 0
+ local units = parameters.units or 1000
+ --
+ -- boundary keys are no longer needed as we now have a string 'right_boundary'
+ -- that can be used in relevant tables (kerns and ligatures) ... not that I ever
+ -- used them
+ --
+ -- boundarychar_label = 0, -- not needed
+ -- boundarychar = 65536, -- there is now a string 'right_boundary'
+ -- false_boundarychar = 65536, -- produces invalid tfm in luatex
+ --
+ targetproperties.language = properties.language or "dflt" -- inherited
+ targetproperties.script = properties.script or "dflt" -- inherited
+ targetproperties.mode = properties.mode or "base" -- inherited
+ targetproperties.method = properties.method
+ --
+ local askedscaledpoints = scaledpoints
+ local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints,nil,specification) -- no shortcut, dan be redefined
+ --
+ local hdelta = delta
+ local vdelta = delta
+ --
+ target.designsize = parameters.designsize -- not really needed so it might become obsolete
+ target.units = units
+ --
+ target.size = scaledpoints
+ --
+ target.subfont = properties.subfont
+ target.cidinfo = properties.cidinfo
+ target.format = properties.format
+ --
+ local original = properties.original or tfmdata.original
+ local fontname = properties.fontname or tfmdata.fontname
+ local fullname = properties.fullname or tfmdata.fullname
+ local filename = properties.filename or tfmdata.filename
+ local psname = properties.psname or tfmdata.psname
+ local name = properties.name or tfmdata.name
+ --
+ -- The psname used in pdf file as well as for selecting subfont in ttc although
+ -- we don't need that subfont look up here (mapfile stuff).
+ --
+ local psname, psfixed = fixedpsname(psname,fontname or fullname or file.nameonly(filename))
+ --
+ target.original = original
+ target.fontname = fontname
+ target.fullname = fullname
+ target.filename = filename
+ target.psname = psname
+ target.name = name
+ --
+ properties.fontname = fontname
+ properties.fullname = fullname
+ properties.filename = filename
+ properties.psname = psname
+ properties.name = name
+ -- expansion (hz)
+ local expansion = parameters.expansion
+ if expansion then
+ target.stretch = expansion.stretch
+ target.shrink = expansion.shrink
+ target.step = expansion.step
+ end
+ -- slanting
+ local slantfactor = parameters.slantfactor or 0
+ if slantfactor ~= 0 then
+ target.slant = slantfactor * 1000
+ else
+ target.slant = 0
+ end
+ -- widening
+ local extendfactor = parameters.extendfactor or 0
+ if extendfactor ~= 0 and extendfactor ~= 1 then
+ hdelta = hdelta * extendfactor
+ target.extend = extendfactor * 1000
+ else
+ target.extend = 1000 -- extent ?
+ end
+ -- squeezing
+ local squeezefactor = parameters.squeezefactor or 0
+ if squeezefactor ~= 0 and squeezefactor ~= 1 then
+ vdelta = vdelta * squeezefactor
+ target.squeeze = squeezefactor * 1000
+ else
+ target.squeeze = 1000 -- extent ?
+ end
+ -- effects
+ local mode = parameters.mode or 0
+ if mode ~= 0 then
+ target.mode = mode
+ end
+ local width = parameters.width or 0
+ if width ~= 0 then
+ target.width = width * delta * 1000 / 655360
+ end
+ --
+ targetparameters.factor = delta
+ targetparameters.hfactor = hdelta
+ targetparameters.vfactor = vdelta
+ targetparameters.size = scaledpoints
+ targetparameters.units = units
+ targetparameters.scaledpoints = askedscaledpoints
+ targetparameters.mode = mode
+ targetparameters.width = width
+ --
+ local hasquality = parameters.expansion or parameters.protrusion
+ local hasitalics = properties.hasitalics
+ local autoitalicamount = properties.autoitalicamount
+ local stackmath = not properties.nostackmath
+ local haskerns = properties.haskerns or properties.mode == "base" -- we can have afm in node mode
+ local hasligatures = properties.hasligatures or properties.mode == "base" -- we can have afm in node mode
+ local realdimensions = properties.realdimensions
+ local writingmode = properties.writingmode or "horizontal"
+ --
+ if changed and not next(changed) then
+ changed = false
+ end
+ --
+ target.writingmode = writingmode == "vertical" and "vertical" or "horizontal"
+ --
+ target.postprocessors = tfmdata.postprocessors
+ --
+ local targetslant = (parameters.slant or parameters[1] or 0) * factors.pt -- per point
+ local targetspace = (parameters.space or parameters[2] or 0) * hdelta
+ local targetspace_stretch = (parameters.space_stretch or parameters[3] or 0) * hdelta
+ local targetspace_shrink = (parameters.space_shrink or parameters[4] or 0) * hdelta
+ local targetx_height = (parameters.x_height or parameters[5] or 0) * vdelta
+ local targetquad = (parameters.quad or parameters[6] or 0) * hdelta
+ local targetextra_space = (parameters.extra_space or parameters[7] or 0) * hdelta
+ --
+ targetparameters.slant = targetslant -- slantperpoint
+ targetparameters.space = targetspace
+ targetparameters.space_stretch = targetspace_stretch
+ targetparameters.space_shrink = targetspace_shrink
+ targetparameters.x_height = targetx_height
+ targetparameters.quad = targetquad
+ targetparameters.extra_space = targetextra_space
+ --
+ local hshift = parameters.hshift
+ if hshift then
+ targetparameters.hshift = delta * hshift
+ end
+ local vshift = parameters.vshift
+ if vshift then
+ targetparameters.vshift = delta * vshift
+ end
+ --
+ local ascender = parameters.ascender
+ if ascender then
+ targetparameters.ascender = delta * ascender
+ end
+ local descender = parameters.descender
+ if descender then
+ targetparameters.descender = delta * descender
+ end
+ --
+ constructors.enhanceparameters(targetparameters) -- official copies for us, now virtual
+ --
+ local scaledwidth = defaultwidth * hdelta
+ local scaledheight = defaultheight * vdelta
+ local scaleddepth = defaultdepth * vdelta
+ --
+ local hasmath = (properties.hasmath or next(mathparameters)) and true
+ --
+ if hasmath then
+ constructors.assignmathparameters(target,tfmdata) -- does scaling and whatever is needed
+
+ properties.hasmath = true
+ target.nomath = false
+ target.MathConstants = target.mathparameters
+
+ properties.compactmath = true
+ target.compactmath = true
+
+ local textscale = parameters.textscale -- or 1000
+ local scriptscale = parameters.scriptscale -- or 700
+ local scriptscriptscale = parameters.scriptscriptscale -- or 500
+
+ parameters.textscale = textscale
+ parameters.scriptscale = scriptscale
+ parameters.scriptscriptscale = scriptscriptscale
+
+ target.textscale = textscale
+ target.scriptscale = scriptscale
+ target.scriptscriptscale = scriptscriptscale
+
+ targetparameters.textscale = textscale
+ targetparameters.scriptscale = scriptscale
+ targetparameters.scriptscriptscale = scriptscriptscale
+
+ -- todo: maybe retrofit to font-con.lua
+
+ local oldmath = properties.oldmath
+ targetproperties.oldmath = oldmath
+ target.oldmath = oldmath
+
+ else
+ properties.hasmath = false
+ target.nomath = true
+ target.mathparameters = nil -- nop
+ end
+ --
+ -- During the transition to opentype the engine had troubles with italics so we had some
+ -- additional code for fixing that. In node mode (text) we don't care much if italics gets
+ -- passed because the engine does nothing with them then.
+ --
+ if hasmath then
+ local mathitalics = properties.mathitalics
+ if mathitalics == false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics = false
+ autoitalicamount = false
+ end
+ else
+ local textitalics = properties.textitalics
+ if textitalics == false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics = false
+ autoitalicamount = false
+ end
+ end
+ --
+ -- end of context specific trickery
+ --
+ if trace_defining then
+ report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a",
+ name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta,
+ hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
+ end
+ --
+ constructors.beforecopyingcharacters(target,tfmdata)
+ --
+ local sharedkerns = { }
+ --
+ -- we can have a dumb mode (basemode without math etc) that skips most
+ --
+ for unicode, character in next, characters do
+ local chr, description, index
+ if changed then
+ local c = changed[unicode]
+ if c and c ~= unicode then
+ if c then
+ description = descriptions[c] or descriptions[unicode] or character
+ character = characters[c] or character
+ index = description.index or c
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ end
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ end
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ end
+ local width = description.width
+ local height = description.height
+ local depth = description.depth
+ local isunicode = description.unicode
+ if realdimensions then
+ -- this is mostly for checking issues
+ if not height or height == 0 then
+ local bb = description.boundingbox
+ local ht = bb[4]
+ if ht ~= 0 then
+ height = ht
+ end
+ if not depth or depth == 0 then
+ local dp = -bb[2]
+ if dp ~= 0 then
+ depth = dp
+ end
+ end
+ elseif not depth or depth == 0 then
+ local dp = -description.boundingbox[2]
+ if dp ~= 0 then
+ depth = dp
+ end
+ end
+ end
+ if width then width = hdelta*width else width = scaledwidth end
+ if height then height = vdelta*height else height = scaledheight end
+ -- if depth then depth = vdelta*depth else depth = scaleddepth end
+ if depth and depth ~= 0 then
+ depth = delta*depth
+ if isunicode then
+ chr = {
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ unicode = isunicode,
+ }
+ else
+ chr = {
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ }
+ end
+ else
+ if isunicode then
+ chr = {
+ index = index,
+ height = height,
+ width = width,
+ unicode = isunicode,
+ }
+ else
+ chr = {
+ index = index,
+ height = height,
+ width = width,
+ }
+ end
+ end
+ if hasquality then
+ -- we could move these calculations elsewhere (saves calculations)
+ local ve = character.expansion_factor
+ if ve then
+ chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere
+ end
+ local vl = character.left_protruding
+ if vl then
+ chr.left_protruding = width*vl
+ end
+ local vr = character.right_protruding
+ if vr then
+ chr.right_protruding = width*vr
+ end
+ end
+ --
+ if hasmath then
+ --
+ -- todo, just operate on descriptions.math
+ local vn = character.next
+ if vn then
+ chr.next = vn
+ else
+ local vv = character.vert_variants
+ if vv then
+ local t = { }
+ for i=1,#vv do
+ local vvi = vv[i]
+ local s = vvi["start"] or 0
+ local e = vvi["end"] or 0
+ local a = vvi["advance"] or 0
+ t[i] = { -- zero check nicer for 5.3
+ ["start"] = s == 0 and 0 or s * vdelta,
+ ["end"] = e == 0 and 0 or e * vdelta,
+ ["advance"] = a == 0 and 0 or a * vdelta,
+ ["extender"] = vvi["extender"],
+ ["glyph"] = vvi["glyph"],
+ }
+ end
+ chr.vert_variants = t
+ else
+ local hv = character.horiz_variants
+ if hv then
+ local t = { }
+ for i=1,#hv do
+ local hvi = hv[i]
+ local s = hvi["start"] or 0
+ local e = hvi["end"] or 0
+ local a = hvi["advance"] or 0
+ t[i] = { -- zero check nicer for 5.3
+ ["start"] = s == 0 and 0 or s * hdelta,
+ ["end"] = e == 0 and 0 or e * hdelta,
+ ["advance"] = a == 0 and 0 or a * hdelta,
+ ["extender"] = hvi["extender"],
+ ["glyph"] = hvi["glyph"],
+ }
+ end
+ chr.horiz_variants = t
+ end
+ end
+ -- todo also check mathitalics (or that one can go away)
+ end
+ local vi = character.vert_italic
+ if vi and vi ~= 0 then
+ chr.vert_italic = vi*hdelta
+ end
+ local va = character.accent
+ if va then
+ chr.top_accent = vdelta*va
+ end
+ if stackmath then
+ local mk = character.mathkerns
+ if mk then
+ local tr = mk.topright
+ local tl = mk.topleft
+ local br = mk.bottomright
+ local bl = mk.bottomleft
+ chr.mathkern = { -- singular -> should be patched in luatex !
+ top_right = tr and mathkerns(tr,vdelta) or nil,
+ top_left = tl and mathkerns(tl,vdelta) or nil,
+ bottom_right = br and mathkerns(br,vdelta) or nil,
+ bottom_left = bl and mathkerns(bl,vdelta) or nil,
+ }
+ end
+ end
+ if hasitalics then
+ local vi = character.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ end
+ elseif autoitalicamount then -- itlc feature
+ local vi = description.italic
+ if not vi then
+ local bb = description.boundingbox
+ if bb then
+ local vi = bb[3] - description.width + autoitalicamount
+ if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
+ chr.italic = vi*hdelta
+ end
+ else
+ -- report_defining("no boundingbox for character %C in font %a, fullname %a, filename %a",unicode,name,fullname,filename)
+ end
+ elseif vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ elseif hasitalics then -- unlikely
+ local vi = character.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ end
+ if haskerns then
+ local vk = character.kerns
+ if vk then
+ local s = sharedkerns[vk]
+ if not s then
+ s = { }
+ for k,v in next, vk do s[k] = v*hdelta end
+ sharedkerns[vk] = s
+ end
+ chr.kerns = s
+ end
+ end
+ if hasligatures then
+ local vl = character.ligatures
+ if vl then
+ if true then
+ chr.ligatures = vl -- shared
+ else
+ local tt = { }
+ for i, l in next, vl do
+ tt[i] = l
+ end
+ chr.ligatures = tt
+ end
+ end
+ end
+ local vc = character.commands
+ if vc then
+ chr.commands = scalecommands(vc,hdelta,vdelta)
+ end
+ targetcharacters[unicode] = chr
+ end
+ --
+ properties.setitalics = hasitalics -- for postprocessing
+ --
+ -- if hasmath then properties.mode = "none" end -- possible optimization but it needs testing
+ --
+ constructors.aftercopyingcharacters(target,tfmdata)
+ --
+ constructors.trytosharefont(target,tfmdata)
+ --
+ -- catch inconsistencies (for now)
+ --
+ local vfonts = target.fonts
+ if not vfonts or #vfonts == 0 then
+ target.fonts = { { id = 0 } }
+ end
+ --
+ return target
+end
+
+function constructors.finalize(tfmdata)
+ if tfmdata.properties and tfmdata.properties.finalized then
+ return
+ end
+ --
+ if not tfmdata.characters then
+ return nil
+ end
+ --
+ if not tfmdata.goodies then
+ tfmdata.goodies = { }
+ end
+ --
+ local parameters = tfmdata.parameters
+ if not parameters then
+ return nil
+ end
+ --
+ if not parameters.expansion then
+ parameters.expansion = {
+ stretch = tfmdata.stretch or 0,
+ shrink = tfmdata.shrink or 0,
+ step = tfmdata.step or 0,
+ }
+ end
+ --
+ if not parameters.size then
+ parameters.size = tfmdata.size
+ end
+ --
+ if not parameters.mode then
+ parameters.mode = 0
+ end
+ --
+ if not parameters.width then
+ parameters.width = 0
+ end
+ --
+ if not parameters.slantfactor then
+ parameters.slantfactor = (tfmdata.slant or 0)/1000
+ end
+ --
+ if not parameters.extendfactor then
+ parameters.extendfactor = (tfmdata.extend or 1000)/1000
+ end
+ --
+ if not parameters.squeezefactor then
+ parameters.squeezefactor = (tfmdata.squeeze or 1000)/1000
+ end
+ --
+ local designsize = parameters.designsize
+ if designsize then
+ parameters.minsize = tfmdata.minsize or designsize
+ parameters.maxsize = tfmdata.maxsize or designsize
+ else
+ designsize = factors.pt * 10
+ parameters.designsize = designsize
+ parameters.minsize = designsize
+ parameters.maxsize = designsize
+ end
+ parameters.minsize = tfmdata.minsize or parameters.designsize
+ parameters.maxsize = tfmdata.maxsize or parameters.designsize
+ --
+ if not parameters.units then
+ parameters.units = tfmdata.units or 1000
+ end
+ --
+ if not tfmdata.descriptions then
+ local descriptions = { } -- yes or no
+ setmetatableindex(descriptions, function(t,k) local v = { } t[k] = v return v end)
+ tfmdata.descriptions = descriptions
+ end
+ --
+ local properties = tfmdata.properties
+ if not properties then
+ properties = { }
+ tfmdata.properties = properties
+ end
+ --
+ properties.fontname = properties.fontname or tfmdata.fontname
+ properties.filename = properties.filename or tfmdata.filename
+ properties.fullname = properties.fullname or tfmdata.fullname
+ properties.name = properties.name or tfmdata.name
+ properties.psname = properties.psname or tfmdata.psname
+ --
+ properties.subfont = tfmdata.subfont or nil
+ properties.cidinfo = tfmdata.cidinfo or nil
+ properties.format = tfmdata.format or "type1"
+ properties.writingmode = tfmdata.writingmode or "horizontal"
+ properties.usedbitmap = tfmdata.usedbitmap
+ --
+ if not tfmdata.resources then
+ tfmdata.resources = { }
+ end
+ if not tfmdata.shared then
+ tfmdata.shared = { }
+ end
+ --
+ -- tfmdata.fonts
+ -- tfmdata.unscaled
+ --
+ if not properties.hasmath then
+ properties.hasmath = not tfmdata.nomath
+ end
+ --
+ tfmdata.MathConstants = nil
+ tfmdata.postprocessors = nil
+ --
+ tfmdata.fontname = nil
+ tfmdata.filename = nil
+ tfmdata.fullname = nil
+ tfmdata.name = nil -- most tricky part
+ tfmdata.psname = nil
+ --
+ tfmdata.subfont = nil
+ tfmdata.cidinfo = nil
+ tfmdata.format = nil
+ tfmdata.nomath = nil
+ tfmdata.designsize = nil
+ --
+ tfmdata.size = nil
+ tfmdata.stretch = nil
+ tfmdata.shrink = nil
+ tfmdata.step = nil
+ tfmdata.slant = nil
+ tfmdata.extend = nil
+ tfmdata.squeeze = nil
+ tfmdata.mode = nil
+ tfmdata.width = nil
+ tfmdata.units = nil
+ --
+ tfmdata.cache = nil
+ --
+ properties.finalized = true
+ --
+ return tfmdata
+end
+
+--[[ldx--
+<p>A unique hash value is generated by:</p>
+--ldx]]--
+
+local hashmethods = { }
+constructors.hashmethods = hashmethods
+
+function constructors.hashfeatures(specification) -- will be overloaded
+ local features = specification.features
+ if features then
+ local t, n = { }, 0
+ for category, list in sortedhash(features) do
+ if next(list) then
+ local hasher = hashmethods[category]
+ if hasher then
+ local hash = hasher(list)
+ if hash then
+ n = n + 1
+ t[n] = category .. ":" .. hash
+ end
+ end
+ end
+ end
+ if n > 0 then
+ return concat(t," & ")
+ end
+ end
+ return "unknown"
+end
+
+hashmethods.normal = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ if not k then
+ -- no need to add to hash
+ elseif k == "number" or k == "features" then
+ -- no need to add to hash (maybe we need a skip list)
+ else
+ n = n + 1
+ if type(v) == "table" then
+ -- table.sequenced
+ local t = { }
+ local m = 0
+ for k, v in next, v do
+ m = m + 1
+ t[m] = k .. '=' .. tostring(v)
+ end
+ sort(t)
+ s[n] = k .. '={' .. concat(t,",") .. "}"
+ else
+ s[n] = k .. '=' .. tostring(v)
+ end
+ end
+ end
+ if n > 0 then
+ sort(s)
+ return concat(s,"+")
+ end
+end
+
+--[[ldx--
+<p>In principle we can share tfm tables when we are in need for a font, but then
+we need to define a font switch as an id/attr switch which is no fun, so in that
+case users can best use dynamic features ... so, we will not use that speedup. Okay,
+when we get rid of base mode we can optimize even further by sharing, but then we
+loose our testcases for <l n='luatex'/>.</p>
+--ldx]]--
+
+function constructors.hashinstance(specification,force)
+ -- implemented in font-ctx.lmt
+end
+
+function constructors.setname(tfmdata,specification)
+ -- dummy (still called in generic font-otl)
+end
+
+function constructors.checkedfilename(data)
+ local foundfilename = data.foundfilename
+ if not foundfilename then
+ local askedfilename = data.filename or ""
+ if askedfilename ~= "" then
+ askedfilename = resolvers.resolve(askedfilename) -- no shortcut
+ foundfilename = resolvers.findbinfile(askedfilename,"") or ""
+ if foundfilename == "" then
+ report_defining("source file %a is not found",askedfilename)
+ foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or ""
+ if foundfilename ~= "" then
+ report_defining("using source file %a due to cache mismatch",foundfilename)
+ end
+ end
+ end
+ data.foundfilename = foundfilename
+ end
+ return foundfilename
+end
+
+local formats = allocate()
+fonts.formats = formats
+
+setmetatableindex(formats, function(t,k)
+ local l = lower(k)
+ if rawget(t,k) then
+ t[k] = l
+ return l
+ end
+ return rawget(t,file.suffix(l))
+end)
+
+do
+
+ local function setindeed(mode,source,target,group,name,position)
+ local action = source[mode]
+ if not action then
+ return
+ end
+ local t = target[mode]
+ if not t then
+ report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
+ os.exit()
+ elseif position then
+ -- todo: remove existing
+ insert(t, position, { name = name, action = action })
+ else
+ for i=1,#t do
+ local ti = t[i]
+ if ti.name == name then
+ ti.action = action
+ return
+ end
+ end
+ insert(t, { name = name, action = action })
+ end
+ end
+
+ local function set(group,name,target,source)
+ target = target[group]
+ if not target then
+ report_defining("fatal target error in setting feature %a, group %a",name,group)
+ os.exit()
+ end
+ local source = source[group]
+ if not source then
+ report_defining("fatal source error in setting feature %a, group %a",name,group)
+ os.exit()
+ end
+ local position = source.position
+ setindeed("node",source,target,group,name,position)
+ setindeed("base",source,target,group,name,position)
+ setindeed("plug",source,target,group,name,position)
+ end
+
+ local function register(where,specification)
+ local name = specification.name
+ if name and name ~= "" then
+ local default = specification.default
+ local description = specification.description
+ local initializers = specification.initializers
+ local processors = specification.processors
+ local manipulators = specification.manipulators
+ local modechecker = specification.modechecker
+ if default then
+ where.defaults[name] = default
+ end
+ if description and description ~= "" then
+ where.descriptions[name] = description
+ end
+ if initializers then
+ set('initializers',name,where,specification)
+ end
+ if processors then
+ set('processors', name,where,specification)
+ end
+ if manipulators then
+ set('manipulators',name,where,specification)
+ end
+ if modechecker then
+ where.modechecker = modechecker
+ end
+ end
+ end
+
+ constructors.registerfeature = register
+
+ function constructors.getfeatureaction(what,where,mode,name)
+ what = handlers[what].features
+ if what then
+ where = what[where]
+ if where then
+ mode = where[mode]
+ if mode then
+ for i=1,#mode do
+ local m = mode[i]
+ if m.name == name then
+ return m.action
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local newfeatures = { }
+ constructors.newfeatures = newfeatures -- downward compatible
+ constructors.features = newfeatures
+
+ local function setnewfeatures(what)
+ local handler = handlers[what]
+ local features = handler.features
+ if not features then
+ local tables = handler.tables -- can be preloaded
+ local statistics = handler.statistics -- can be preloaded
+ features = allocate {
+ defaults = { },
+ descriptions = tables and tables.features or { },
+ used = statistics and statistics.usedfeatures or { },
+ initializers = { base = { }, node = { }, plug = { } },
+ processors = { base = { }, node = { }, plug = { } },
+ manipulators = { base = { }, node = { }, plug = { } },
+ finalizers = { base = { }, node = { }, plug = { } },
+ }
+ features.register = function(specification) return register(features,specification) end
+ handler.features = features -- will also become hidden
+ end
+ return features
+ end
+
+ setmetatable(newfeatures, {
+ __call = function(t,k) local v = t[k] return v end,
+ __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end,
+ })
+
+end
+
+do
+
+ local newhandler = { }
+ constructors.handlers = newhandler -- downward compatible
+ constructors.newhandler = newhandler
+
+ local function setnewhandler(what) -- could be a metatable newindex
+ local handler = handlers[what]
+ if not handler then
+ handler = { }
+ handlers[what] = handler
+ end
+ return handler
+ end
+
+ setmetatable(newhandler, {
+ __call = function(t,k) local v = t[k] return v end,
+ __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end,
+ })
+
+end
+
+do
+ -- a pitty that we need to be generic as we have nicer mechanisms for this ...
+
+ local newenhancer = { }
+ constructors.enhancers = newenhancer
+ constructors.newenhancer = newenhancer
+
+ local function setnewenhancer(format)
+
+ local handler = handlers[format]
+ local enhancers = handler.enhancers
+
+ if not enhancers then
+
+ local actions = allocate() -- no need to allocate thee
+ local before = allocate()
+ local after = allocate()
+ local order = allocate()
+ local known = { }
+ local nofsteps = 0
+ local patches = { before = before, after = after }
+
+ local trace = false
+ local report = logs.reporter("fonts",format .. " enhancing")
+
+ trackers.register(format .. ".loading", function(v) trace = v end)
+
+ local function enhance(name,data,filename,raw)
+ local enhancer = actions[name]
+ if enhancer then
+ if trace then
+ report("apply enhancement %a to file %a",name,filename)
+ ioflush()
+ end
+ enhancer(data,filename,raw)
+ else
+ -- no message as we can have private ones
+ end
+ end
+
+ local function apply(data,filename,raw)
+ local basename = file.basename(lower(filename))
+ if trace then
+ report("%s enhancing file %a","start",filename)
+ end
+ ioflush() -- we want instant messages
+ for e=1,nofsteps do
+ local enhancer = order[e]
+ local b = before[enhancer]
+ if b then
+ for pattern, action in next, b do
+ if find(basename,pattern) then
+ action(data,filename,raw)
+ end
+ end
+ end
+ enhance(enhancer,data,filename,raw) -- we have one installed: check extra features
+ local a = after[enhancer]
+ if a then
+ for pattern, action in next, a do
+ if find(basename,pattern) then
+ action(data,filename,raw)
+ end
+ end
+ end
+ ioflush() -- we want instant messages
+ end
+ if trace then
+ report("%s enhancing file %a","stop",filename)
+ end
+ ioflush() -- we want instant messages
+ end
+
+ local function register(what,action)
+ if action then
+ if actions[what] then
+ -- overloading, e.g."check extra features"
+ else
+ nofsteps = nofsteps + 1
+ order[nofsteps] = what
+ known[what] = nofsteps
+ end
+ actions[what] = action
+ else
+ report("bad enhancer %a",what)
+ end
+ end
+
+ -- We used to have a lot of enhancers but no longer with the new font loader. The order of enhancers
+ -- is the order of definition. The before/after patches are there for old times sake and happen
+ -- before or after a (named) enhancer. An example of a set enhancer is "check extra features" so one
+ -- one set patches before or after that is applied. Unknown enhancers are auto-registered. It's a bit
+ -- messy but we keep it for compatibility reasons.
+ --
+ -- fonts.handlers.otf.enhancers.patches.register("before","some patches","somefont",function(data,filename)
+ -- print("!!!!!!!") -- before | after
+ -- end)
+ --
+ -- fonts.handlers.otf.enhancers.register("more patches",function(data,filename)
+ -- print("???????") -- enhance
+ -- end)
+
+ local function patch(what,where,pattern,action)
+ local pw = patches[what]
+ if pw then
+ local ww = pw[where]
+ if ww then
+ ww[pattern] = action
+ else
+ pw[where] = { [pattern] = action }
+ if not known[where] then
+ nofsteps = nofsteps + 1
+ order[nofsteps] = where
+ known[where] = nofsteps
+ end
+ end
+ end
+ end
+
+ enhancers = {
+ register = register,
+ apply = apply,
+ patch = patch,
+ report = report,
+ patches = {
+ register = patch,
+ report = report,
+ }, -- for old times sake
+ }
+
+ handler.enhancers = enhancers
+ end
+ return enhancers
+ end
+
+ setmetatable(newenhancer, {
+ __call = function(t,k) local v = t[k] return v end,
+ __index = function(t,k) local v = setnewenhancer(k) t[k] = v return v end,
+ })
+
+end
+
+--[[ldx--
+<p>We need to check for default features. For this we provide
+a helper function.</p>
+--ldx]]--
+
+function constructors.checkedfeatures(what,features)
+ local defaults = handlers[what].features.defaults
+ if features and next(features) then
+ features = fastcopy(features) -- can be inherited (mt) but then no loops possible
+ for key, value in next, defaults do
+ if features[key] == nil then
+ features[key] = value
+ end
+ end
+ return features
+ else
+ return fastcopy(defaults) -- we can change features in place
+ end
+end
+
+-- before scaling
+
+function constructors.initializefeatures(what,tfmdata,features,trace,report)
+ if features and next(features) then
+ local properties = tfmdata.properties or { } -- brrr
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatmodechecker = whatfeatures.modechecker
+ -- properties.mode can be enforces (for instance in font-otd)
+ local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
+ properties.mode = mode -- also status
+ features.mode = mode -- both properties.mode or features.mode can be changed
+ --
+ local done = { }
+ while true do
+ local redo = false
+ local initializers = whatfeatures.initializers[mode]
+ if initializers then
+ for i=1,#initializers do
+ local step = initializers[i]
+ local feature = step.name
+-- we could intercept mode here .. needs a rewrite of this whole loop then but it's cleaner that way
+ local value = features[feature]
+ if not value then
+ -- disabled
+ elseif done[feature] then
+ -- already done
+ else
+ local action = step.action
+ if trace then
+ report("initializing feature %a to %a for mode %a for font %a",feature,
+ value,mode,tfmdata.properties.fullname)
+ end
+ action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart
+ if mode ~= properties.mode or mode ~= features.mode then
+ if whatmodechecker then
+ properties.mode = whatmodechecker(tfmdata,features,properties.mode) -- force checking
+ features.mode = properties.mode
+ end
+ if mode ~= properties.mode then
+ mode = properties.mode
+ redo = true
+ end
+ end
+ done[feature] = true
+ end
+ if redo then
+ break
+ end
+ end
+ if not redo then
+ break
+ end
+ else
+ break
+ end
+ end
+ properties.mode = mode -- to be sure
+ return true
+ else
+ return false
+ end
+end
+
+-- while typesetting
+
+function constructors.collectprocessors(what,tfmdata,features,trace,report)
+ local processes = { }
+ local nofprocesses = 0
+ if features and next(features) then
+ local properties = tfmdata.properties
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatprocessors = whatfeatures.processors
+ local mode = properties.mode
+ local processors = whatprocessors[mode]
+ if processors then
+ for i=1,#processors do
+ local step = processors[i]
+ local feature = step.name
+ if features[feature] then
+ local action = step.action
+ if trace then
+ report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
+ end
+ if action then
+ nofprocesses = nofprocesses + 1
+ processes[nofprocesses] = action
+ end
+ end
+ end
+ elseif trace then
+ report("no feature processors for mode %a for font %a",mode,properties.fullname)
+ end
+ end
+ return processes
+end
+
+-- after scaling
+
+local function apply(key,what,tfmdata,features,trace,report)
+ if features and next(features) then
+ local properties = tfmdata.properties
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatactions = whatfeatures[key]
+ local mode = properties.mode
+ local actions = whatactions[mode]
+ if actions then
+ for i=1,#actions do
+ local step = actions[i]
+ local feature = step.name
+ local value = features[feature]
+ if value then
+ local action = step.action
+ if trace then
+ report("applying feature %s %a for mode %a for font %a",key,feature,mode,properties.fullname)
+ end
+ if action then
+ action(tfmdata,feature,value)
+ end
+ end
+ end
+ end
+ end
+end
+
+function constructors.applymanipulators(what,tfmdata,features,trace,report)
+ if features and next(features) then
+ apply("manipulators",what,tfmdata,features,trace,report)
+ end
+end
+
+function constructors.applyfinalizers(what,tfmdata,features,trace,report)
+ if features and next(features) then
+ apply("finalizers",what,tfmdata,features,trace,report)
+ end
+end
+
+function constructors.addcoreunicodes(unicodes) -- maybe make this a metatable if used at all
+ if not unicodes then
+ unicodes = { }
+ end
+ unicodes.space = 0x0020
+ unicodes.hyphen = 0x002D
+ unicodes.zwj = 0x200D
+ unicodes.zwnj = 0x200C
+ return unicodes
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-ctx.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-ctx.lmt
new file mode 100644
index 00000000000..b735e3d2f91
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-ctx.lmt
@@ -0,0 +1,3231 @@
+if not modules then modules = { } end modules ['font-ctx'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- At some point I will clean up the code here so that at the tex end
+-- the table interface is used.
+--
+-- Todo: make a proper 'next id' mechanism (register etc) or wait till 'true'
+-- in virtual fonts indices is implemented.
+--
+-- Olders code can be found in the mkiv lua variant.
+
+local tostring, next, type, rawget, tonumber = tostring, next, type, rawget, tonumber
+
+local format, gmatch, match, find, lower, upper, gsub, byte, topattern = string.format, string.gmatch, string.match, string.find, string.lower, string.upper, string.gsub, string.byte, string.topattern
+local concat, serialize, sort, fastcopy, mergedtable = table.concat, table.serialize, table.sort, table.fastcopy, table.merged
+local sortedhash, sortedkeys, sequenced = table.sortedhash, table.sortedkeys, table.sequenced
+local parsers = utilities.parsers
+local settings_to_hash, hash_to_string, settings_to_array = parsers.settings_to_hash, parsers.hash_to_string, parsers.settings_to_array
+local formatcolumns = utilities.formatters.formatcolumns
+local mergehashes = utilities.parsers.mergehashes
+local formatters = string.formatters
+local basename = file.basename
+
+local utfchar, utfbyte = utf.char, utf.byte
+local round = math.round
+
+local context, commands = context, commands
+
+local P, R, S, C, Cc, Cf, Cg, Ct, Cs, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.Cs, lpeg.match
+
+local trace_features = false trackers.register("fonts.features", function(v) trace_features = v end)
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+local trace_designsize = false trackers.register("fonts.designsize", function(v) trace_designsize = v end)
+local trace_usage = false trackers.register("fonts.usage", function(v) trace_usage = v end)
+local trace_mapfiles = false trackers.register("fonts.mapfiles", function(v) trace_mapfiles = v end)
+local trace_automode = false trackers.register("fonts.automode", function(v) trace_automode = v end)
+local trace_merge = false trackers.register("fonts.merge", function(v) trace_merge = v end)
+
+local report = logs.reporter("fonts")
+local report_features = logs.reporter("fonts","features")
+local report_cummulative = logs.reporter("fonts","cummulative")
+local report_defining = logs.reporter("fonts","defining")
+local report_status = logs.reporter("fonts","status")
+local report_mapfiles = logs.reporter("fonts","mapfiles")
+
+local setmetatableindex = table.setmetatableindex
+
+local implement = interfaces.implement
+
+local chardata = characters.data
+
+local fonts = fonts
+local handlers = fonts.handlers
+local otf = handlers.otf -- brrr
+local afm = handlers.afm -- brrr
+local tfm = handlers.tfm -- brrr
+local names = fonts.names
+local definers = fonts.definers
+local specifiers = fonts.specifiers
+local constructors = fonts.constructors
+local loggers = fonts.loggers
+local fontgoodies = fonts.goodies
+local helpers = fonts.helpers
+local hashes = fonts.hashes
+local currentfont = font.current
+local definefont = font.define
+
+local getprivateslot = helpers.getprivateslot
+
+local cleanname = names.cleanname
+
+local encodings = fonts.encodings
+----- aglunicodes = encodings.agl.unicodes
+local aglunicodes = nil -- delayed loading
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local nextchar = nuts.traversers.char
+
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getstate = nuts.getstate
+local setsubtype = nuts.setsubtype
+
+local texgetdimen = tex.getdimen
+local texsetcount = tex.setcount
+local texget = tex.get
+
+local texdefinefont = tex.definefont
+local texsp = tex.sp
+
+local fontdata = hashes.identifiers
+local characters = hashes.characters
+local descriptions = hashes.descriptions
+local properties = hashes.properties
+local resources = hashes.resources
+local unicodes = hashes.unicodes
+local csnames = hashes.csnames
+local lastmathids = hashes.lastmathids
+local exheights = hashes.exheights
+local emwidths = hashes.emwidths
+local parameters = hashes.parameters
+
+local designsizefilename = fontgoodies.designsizes.filename
+
+local ctx_char = context.char
+local ctx_safechar = context.safechar
+local ctx_getvalue = context.getvalue
+
+local otffeatures = otf.features
+local otftables = otf.tables
+
+local registerotffeature = otffeatures.register
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+specifiers.contextsetups = specifiers.contextsetups or { }
+specifiers.contextnumbers = specifiers.contextnumbers or { }
+specifiers.contextmerged = specifiers.contextmerged or { }
+specifiers.synonyms = specifiers.synonyms or { }
+
+local setups = specifiers.contextsetups
+local numbers = specifiers.contextnumbers
+local merged = specifiers.contextmerged
+local synonyms = specifiers.synonyms
+
+storage.register("fonts/setups" , setups , "fonts.specifiers.contextsetups" )
+storage.register("fonts/numbers", numbers, "fonts.specifiers.contextnumbers")
+storage.register("fonts/merged", merged, "fonts.specifiers.contextmerged")
+storage.register("fonts/synonyms", synonyms, "fonts.specifiers.synonyms")
+
+-- inspect(setups)
+
+setmetatableindex(setups, environment.initex and
+ function(t,k)
+ return type(k) == "number" and rawget(t,numbers[k]) or nil
+ end
+or
+ function(t,k)
+ local v = type(k) == "number" and rawget(t,numbers[k])
+ if v then
+ t[k] = v
+ return v
+ end
+ end
+)
+
+-- this will move elsewhere ...
+
+local function getfontname(tfmdata)
+ return basename(type(tfmdata) == "number" and properties[tfmdata].name or tfmdata.properties.name)
+end
+
+helpers.name = getfontname
+
+local addformatter = utilities.strings.formatters.add
+
+addformatter(formatters,"font:name", [["'"..fontname(%s).."'"]], { fontname = helpers.name })
+addformatter(formatters,"font:features",[["'"..sequenced(%s," ",true).."'"]],{ sequenced = table.sequenced })
+
+-- ... like font-sfm or so
+
+constructors.resolvevirtualtoo = true -- context specific (due to resolver)
+constructors.sharefonts = true -- experimental
+constructors.nofsharedfonts = 0
+constructors.nofsharedhashes = 0
+constructors.nofsharedvectors = 0
+constructors.noffontsloaded = 0
+
+-- we can get rid of the tfm instance when we have fast access to the
+-- scaled character dimensions at the tex end, e.g. a fontobject.width
+-- actually we already have some of that now as virtual keys in glyphs
+--
+-- flushing the kern and ligature tables from memory saves a lot (only
+-- base mode) but it complicates vf building where the new characters
+-- demand this data .. solution: functions that access them
+
+-- font.getcopy = font.getfont -- we always want the table that context uses
+
+do
+
+ -- Does this still make sense?
+
+ local shares = { }
+ local hashes = { }
+ local nofinstances = 0
+ local instances = setmetatableindex(function(t,k)
+ nofinstances = nofinstances + 1
+ t[k] = nofinstances
+ return nofinstances
+ end)
+
+ function constructors.trytosharefont(target,tfmdata)
+ constructors.noffontsloaded = constructors.noffontsloaded + 1
+ if constructors.sharefonts then
+ local fonthash = target.specification.hash
+ if fonthash then
+ local properties = target.properties
+ local fullname = target.fullname
+ local fontname = target.fontname
+ local psname = target.psname
+ -- for the moment here:
+ local instance = properties.instance
+ if instance then
+ local format = tfmdata.properties.format
+ if format == "opentype" then
+ target.streamprovider = 1
+ elseif format == "truetype" then
+ target.streamprovider = 2
+ else
+ target.streamprovider = 0
+ end
+ if target.streamprovider > 0 then
+ if fullname then
+ fullname = fullname .. ":" .. instances[instance]
+ target.fullname = fullname
+ end
+ if fontname then
+ fontname = fontname .. ":" .. instances[instance]
+ target.fontname = fontname
+ end
+ if psname then
+ -- this one is used for the funny prefix in font names in pdf
+ -- so it has to be kind of unique in order to avoid subset prefix
+ -- clashes being reported
+ psname = psname .. ":" .. instances[instance]
+ target.psname = psname
+ end
+ end
+ end
+ --
+ local sharedname = hashes[fonthash]
+ if sharedname then
+ -- this is ok for context as we know that only features can mess with font definitions
+ -- so a similar hash means that the fonts are similar too
+ if trace_defining then
+ report_defining("font %a uses backend resources of font %a (%s)",target.fullname,sharedname,"common hash")
+ end
+ target.fullname = sharedname
+ properties.sharedwith = sharedname
+ constructors.nofsharedfonts = constructors.nofsharedfonts + 1
+ constructors.nofsharedhashes = constructors.nofsharedhashes + 1
+ else
+ -- the one takes more time (in the worst case of many cjk fonts) but it also saves
+ -- embedding time .. haha, this is interesting: when i got a clash on subset tag
+ -- collision i saw in the source that these tags are also using a hash like below
+ -- so maybe we should have an option to pass it from lua
+ local characters = target.characters
+ local n = 1
+ local t = { target.psname }
+ -- for the moment here:
+ if instance then
+ n = n + 1
+ t[n] = instance
+ end
+ --
+ local u = sortedkeys(characters)
+ for i=1,#u do
+ local k = u[i]
+ n = n + 1 ; t[n] = k
+ n = n + 1 ; t[n] = characters[k].index or k
+ end
+ local checksum = md5.HEX(concat(t," "))
+ local sharedname = shares[checksum]
+ local fullname = target.fullname
+ if sharedname then
+ if trace_defining then
+ report_defining("font %a uses backend resources of font %a (%s)",fullname,sharedname,"common vector")
+ end
+ fullname = sharedname
+ properties.sharedwith = sharedname
+ constructors.nofsharedfonts = constructors.nofsharedfonts + 1
+ constructors.nofsharedvectors = constructors.nofsharedvectors + 1
+ else
+ shares[checksum] = fullname
+ end
+ target.fullname = fullname
+ hashes[fonthash] = fullname
+ end
+ end
+ end
+ end
+
+end
+
+directives.register("fonts.checksharing",function(v)
+ if not v then
+ report_defining("font sharing in backend is disabled")
+ end
+ constructors.sharefonts = v
+end)
+
+function definers.resetnullfont()
+ -- resetting is needed because tikz misuses nullfont
+ local parameters = fonts.nulldata.parameters
+ --
+ parameters.slant = 0 -- 1
+ parameters.space = 0 -- 2
+ parameters.space_stretch = 0 -- 3
+ parameters.space_shrink = 0 -- 4
+ parameters.x_height = 0 -- 5
+ parameters.quad = 0 -- 6
+ parameters.extra_space = 0 -- 7
+ parameters.designsize = 655360
+ --
+ constructors.enhanceparameters(parameters) -- official copies for us
+ --
+ definers.resetnullfont = function() end
+end
+
+-- This is some initialization code and we don't want (tracing) clutter in lmtx
+-- which is why we follow a different route there. I admit that is sounds freaky.
+-- Watch out: in lmtx the font dimen array is no longer resized automatically.
+
+implement {
+ name = "resetnullfont",
+ onlyonce = true,
+ permanent = false,
+ actions = function()
+ for i=1,7 do
+ font.setfontdimen(0,i,0)
+ end
+ definers.resetnullfont()
+ end
+}
+
+-- this cannot be a feature initializer as there is no auto namespace
+-- so we never enter the loop then; we can store the defaults in the tma
+-- file (features.gpos.mkmk = 1 etc)
+
+local needsnodemode = { -- we will have node mode by default anyway
+ -- gsub_single = true,
+ gsub_multiple = true,
+ -- gsub_alternate = true,
+ -- gsub_ligature = true,
+ gsub_context = true,
+ gsub_contextchain = true,
+ gsub_reversecontextchain = true,
+ -- chainsub = true,
+ -- reversesub = true,
+ gpos_mark2base = true,
+ gpos_mark2ligature = true,
+ gpos_mark2mark = true,
+ gpos_cursive = true,
+ -- gpos_single = true,
+ -- gpos_pair = true,
+ gpos_context = true,
+ gpos_contextchain = true,
+}
+
+otftables.scripts.auto = "automatic fallback to latn when no dflt present"
+
+-- setmetatableindex(otffeatures.descriptions,otftables.features)
+
+local function checkedscript(tfmdata,resources,features)
+ local latn = false
+ local script = false
+ if resources.features then
+ for g, list in next, resources.features do
+ for f, scripts in next, list do
+ if scripts.dflt then
+ script = "dflt"
+ break
+ elseif scripts.latn then
+ latn = true
+ end
+ end
+ end
+ end
+ if not script then
+ script = latn and "latn" or "dflt"
+ end
+ if trace_automode then
+ report_defining("auto script mode, using script %a in font %!font:name!",script,tfmdata)
+ end
+ features.script = script
+ return script
+end
+
+-- basemode combined with dynamics is somewhat tricky
+
+local function checkedmode(tfmdata,resources,features)
+ local sequences = resources.sequences
+ if sequences and #sequences > 0 then
+ local script = features.script or "dflt"
+ local language = features.language or "dflt"
+ for feature, value in next, features do
+ if value then
+ local found = false
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ local features = sequence.features
+ if features then
+ local scripts = features[feature]
+ if scripts then
+ local languages = scripts[script]
+ if languages and languages[language] then
+ if found then
+ -- more than one lookup
+ if trace_automode then
+ report_defining("forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s",
+ "node",tfmdata,feature,script,language,"multiple lookups")
+ end
+ features.mode = "node"
+ return "node"
+ elseif needsnodemode[sequence.type] then
+ if trace_automode then
+ report_defining("forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s",
+ "node",tfmdata,feature,script,language,"no base support")
+ end
+ features.mode = "node"
+ return "node"
+ else
+ -- at least one lookup
+ found = true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if trace_automode then
+ report_defining("forcing mode base, font %!font:name!",tfmdata)
+ end
+ features.mode = "base" -- new, or is this wrong?
+ return "base"
+end
+
+definers.checkedscript = checkedscript
+definers.checkedmode = checkedmode
+
+-- We only set the checker and leave other settings of the mode feature as
+-- they are.
+
+local function modechecker(tfmdata,features,mode) -- we cannot adapt features as they are shared!
+ if trace_features then
+ report_features("fontname %!font:name!, features %!font:features!",tfmdata,features)
+ end
+ local rawdata = tfmdata.shared.rawdata
+ local resources = rawdata and rawdata.resources
+ local script = features.script
+ if resources then
+ if script == "auto" then
+ script = checkedscript(tfmdata,resources,features)
+ end
+ if mode == "auto" then
+ mode = checkedmode(tfmdata,resources,features)
+ end
+ else
+ report_features("missing resources for font %!font:name!",tfmdata)
+ end
+ return mode
+end
+
+registerotffeature {
+ name = "mode",
+ modechecker = modechecker,
+}
+
+do
+
+ -- copying will end up here too
+
+ local beforecopyingcharacters = sequencers.new {
+ name = "beforecopyingcharacters",
+ arguments = "target,original",
+ }
+
+ appendgroup(beforecopyingcharacters,"before") -- user
+ appendgroup(beforecopyingcharacters,"system") -- private
+ appendgroup(beforecopyingcharacters,"after" ) -- user
+
+ function constructors.beforecopyingcharacters(original,target)
+ local runner = beforecopyingcharacters.runner
+ if runner then
+ runner(original,target)
+ end
+ end
+
+ local aftercopyingcharacters = sequencers.new {
+ name = "aftercopyingcharacters",
+ arguments = "target,original",
+ }
+
+ appendgroup(aftercopyingcharacters,"before") -- user
+ appendgroup(aftercopyingcharacters,"system") -- private
+ appendgroup(aftercopyingcharacters,"after" ) -- user
+
+ function constructors.aftercopyingcharacters(original,target)
+ local runner = aftercopyingcharacters.runner
+ if runner then
+ runner(original,target)
+ end
+ end
+
+end
+
+--[[ldx--
+<p>So far we haven't really dealt with features (or whatever we want
+to pass along with the font definition. We distinguish the following
+situations:</p>
+situations:</p>
+
+<code>
+name:xetex like specs
+name@virtual font spec
+name*context specification
+</code>
+--ldx]]--
+
+-- Currently fonts are scaled while constructing the font, so we have to do scaling
+-- of commands in the vf at that point using e.g. "local scale = g.parameters.factor
+-- or 1" after all, we need to work with copies anyway and scaling needs to be done
+-- at some point; however, when virtual tricks are used as feature (makes more
+-- sense) we scale the commands in fonts.constructors.scale (and set the factor
+-- there).
+
+local loadfont = definers.loadfont
+
+function definers.loadfont(specification,size,id) -- overloads the one in font-def
+ local variants = definers.methods.variants
+ local virtualfeatures = specification.features.virtual
+ if virtualfeatures and virtualfeatures.preset then
+ local variant = variants[virtualfeatures.preset]
+ if variant then
+ return variant(specification,size,id)
+ end
+ else
+ return loadfont(specification,size,id)
+ end
+end
+
+local function predefined(specification)
+ local variants = definers.methods.variants
+ local detail = specification.detail
+ if detail ~= "" and variants[detail] then
+ specification.features.virtual = { preset = detail }
+ end
+ return specification
+end
+
+definers.registersplit("@", predefined,"virtual")
+
+local normalize_features = otffeatures.normalize -- should be general
+
+local function definecontext(name,t) -- can be shared
+ local number = setups[name] and setups[name].number or 0 -- hm, numbers[name]
+ if number == 0 then
+ number = #numbers + 1
+ numbers[number] = name
+ end
+ t.number = number
+ setups[name] = t
+ return number, t
+end
+
+-- {a,b,c} as table (so we don' need to parse again when it gets applied)
+-- we will update this ... when we have foo={a,b,c} then we can keep the table
+
+-- \definefontfeature[demo][a={b,c}]
+-- \definefontfeature[demo][a={b=12,c={34,35}}]
+
+local h = setmetatableindex(function(t,k)
+ local v = "," .. k .. ","
+ t[k] = v
+ return v
+end)
+
+-- local function removefromhash(hash,key)
+-- local pattern = h[key]
+-- for k in next, hash do
+-- if k ~= key and find(h[k],pattern) then -- if find(k,",") and ...
+-- hash[k] = nil
+-- end
+-- end
+-- end
+
+local function presetcontext(name,parent,features) -- will go to con and shared
+ if features == "" and find(parent,"=",1,true) then
+ features = parent
+ parent = ""
+ end
+ if not features or features == "" then
+ features = { }
+ elseif type(features) == "string" then
+ features = normalize_features(settings_to_hash(features))
+ -- if type(value) == "string" and find(value,"[=:]") then
+ -- local t = settings_to_hash_colon_too(value) -- clashes with foo=file:bar
+ for key, value in next, features do
+ if type(value) == "string" and find(value,"[=]") then
+ local t = settings_to_hash(value)
+ if next(t) then
+ features[key] = sequenced(normalize_features(t,true),",")
+ end
+ end
+ end
+ else
+ features = normalize_features(features)
+ end
+ -- todo: synonyms, and not otf bound
+ if parent ~= "" then
+ for p in gmatch(parent,"[^, ]+") do
+ local s = setups[p]
+ if s then
+ for k, v in next, s do
+ -- no, as then we cannot overload: e.g. math,mathextra
+ -- reverted, so we only take from parent when not set
+ if features[k] == nil then
+ features[k] = v
+ end
+ end
+ else
+ -- just ignore an undefined one .. i.e. we can refer to not yet defined
+ end
+ end
+ end
+ -- these are auto set so in order to prevent redundant definitions
+ -- we need to preset them (we hash the features and adding a default
+ -- setting during initialization may result in a different hash)
+ --
+ -- for k,v in next, triggers do
+ -- if features[v] == nil then -- not false !
+ -- local vv = default_features[v]
+ -- if vv then features[v] = vv end
+ -- end
+ -- end
+ --
+ for feature,value in next, features do
+ if value == nil then -- not false !
+ local default = default_features[feature]
+ if default ~= nil then
+ features[feature] = default
+ end
+ end
+ end
+ -- sparse 'm so that we get a better hash and less test (experimental
+ -- optimization)
+ local t = { } -- can we avoid t ?
+ for k,v in next, features do
+ -- if v then t[k] = v end
+ t[k] = v
+ end
+ -- the number is needed for dynamic features; maybe number should always be
+ -- renewed as we can redefine features ... i need a test
+ local number = setups[name] and setups[name].number or 0
+ if number == 0 then
+ number = #numbers + 1
+ numbers[number] = name
+ end
+ --
+ t.number = number
+ -- there is the special case of combined features as we have in math but maybe
+ -- this has to change some day ... otherwise we mess up dynamics (ok, we could
+ -- impose a limit there: no combined features)
+ --
+ -- done elsewhere (!)
+ --
+ -- removefromhash(setups,name) -- can have changed (like foo,extramath)
+ --
+ setups[name] = t
+ return number, t
+end
+
+local function adaptcontext(pattern,features)
+ local pattern = topattern(pattern,false,true)
+ for name in next, setups do
+ if find(name,pattern) then
+ presetcontext(name,name,features)
+ end
+ end
+end
+
+-- See for old code in the lua file. We now share code.
+
+local function contextnumber(name)
+ local t = setups[name]
+ return t and t.number or 0
+end
+
+local function mergecontext(currentnumber,extraname,option) -- number string number (used in scrp-ini
+ local extra = setups[extraname]
+ if extra then
+ local current = setups[numbers[currentnumber]]
+ local mergedfeatures = { }
+ local mergedname = nil
+ if option < 0 then
+ if current then
+ for k, v in next, current do
+ if not extra[k] then
+ mergedfeatures[k] = v
+ end
+ end
+ end
+ mergedname = currentnumber .. "-" .. extraname
+ else
+ if current then
+ for k, v in next, current do
+ mergedfeatures[k] = v
+ end
+ end
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ mergedname = currentnumber .. "+" .. extraname
+ end
+ local number = #numbers + 1
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = option
+ setups[mergedname] = mergedfeatures
+ return number
+ else
+ return currentnumber
+ end
+end
+
+local extrasets = { }
+
+setmetatableindex(extrasets,function(t,k)
+ local v = mergehashes(setups,k)
+ t[k] = v
+ return v
+end)
+
+local function mergecontextfeatures(currentname,extraname,how,mergedname) -- string string
+ local extra = setups[extraname] or extrasets[extraname]
+ if extra then
+ local current = setups[currentname]
+ local mergedfeatures = { }
+ if how == "+" then
+ if current then
+ for k, v in next, current do
+ mergedfeatures[k] = v
+ end
+ end
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ if trace_merge then
+ report_features("merge %a, method %a, current %|T, extra %|T, result %|T",mergedname,"add",current or { },extra,mergedfeatures)
+ end
+ elseif how == "-" then
+ if current then
+ for k, v in next, current do
+ mergedfeatures[k] = v
+ end
+ end
+ for k, v in next, extra do
+ -- only boolean features
+ if v == true then
+ mergedfeatures[k] = false
+ end
+ end
+ if trace_merge then
+ report_features("merge %a, method %a, current %|T, extra %|T, result %|T",mergedname,"subtract",current or { },extra,mergedfeatures)
+ end
+ else -- =
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ if trace_merge then
+ report_features("merge %a, method %a, result %|T",mergedname,"replace",mergedfeatures)
+ end
+ end
+ local number = #numbers + 1
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = option
+ setups[mergedname] = mergedfeatures
+ return number
+ else
+ return numbers[currentname] or 0
+ end
+end
+
+local function registercontext(fontnumber,extraname,option)
+ local extra = setups[extraname]
+ if extra then
+ local mergedfeatures = { }
+ local mergedname = nil
+ if option < 0 then
+ mergedname = fontnumber .. "-" .. extraname
+ else
+ mergedname = fontnumber .. "+" .. extraname
+ end
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ local number = #numbers + 1
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = option
+ setups[mergedname] = mergedfeatures
+ return number
+ else
+ return 0
+ end
+end
+
+local function registercontextfeature(mergedname,extraname,how)
+ local extra = setups[extraname]
+ if extra then
+ local mergedfeatures = { }
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ local number = #numbers + 1 -- we somehow end up with steps of 2
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = how == "=" and 1 or 2 -- 1=replace, 2=combine
+ setups[mergedname] = mergedfeatures
+ return number
+ else
+ report_features("unknown feature %a cannot be merged into %a using method %a",extraname,mergedname,how)
+ return 0
+ end
+end
+
+specifiers.presetcontext = presetcontext
+specifiers.contextnumber = contextnumber
+specifiers.mergecontext = mergecontext
+specifiers.registercontext = registercontext
+specifiers.definecontext = definecontext
+
+constructors.hashmethods.normal = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ if not k then
+ -- no need to add to hash
+ elseif k == "number" or k == "features" then
+ -- no need to add to hash (maybe we need a skip list)
+ else
+ n = n + 1
+ if type(v) == "table" then
+ -- table.sequenced
+ local t = { }
+ local m = 0
+ for k, v in next, v do
+ m = m + 1
+ t[m] = format("%q=%q",k,v)
+ end
+ sort(t)
+ s[n] = format("%q={%s}",k,concat(t,","))
+ else
+ s[n] = format("%q=%q",k,v)
+ end
+ end
+ end
+ if n > 0 then
+ sort(s)
+ return concat(s,"+")
+ end
+end
+
+constructors.hashmethods.virtual = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ n = n + 1
+ s[n] = format("%q=%q",k,v)
+ end
+ if n > 0 then
+ sort(s)
+ return concat(s,"+")
+ end
+end
+
+-- end of redefine
+
+-- local withcache = { } -- concat might be less efficient than nested tables
+--
+-- local function withset(name,what)
+-- local zero = texgetattribute(0)
+-- local hash = zero .. "+" .. name .. "*" .. what
+-- local done = withcache[hash]
+-- if not done then
+-- done = mergecontext(zero,name,what)
+-- withcache[hash] = done
+-- end
+-- texsetattribute(0,done)
+-- end
+--
+-- local function withfnt(name,what,font)
+-- local font = font or currentfont()
+-- local hash = font .. "*" .. name .. "*" .. what
+-- local done = withcache[hash]
+-- if not done then
+-- done = registercontext(font,name,what)
+-- withcache[hash] = done
+-- end
+-- texsetattribute(0,done)
+-- end
+
+function specifiers.showcontext(name)
+ return setups[name] or setups[numbers[name]] or setups[numbers[tonumber(name)]] or { }
+end
+
+-- we need a copy as we will add (fontclass) goodies to the features and
+-- that is bad for a shared table
+
+-- local function splitcontext(features) -- presetcontext creates dummy here
+-- return fastcopy(setups[features] or (presetcontext(features,"","") and setups[features]))
+-- end
+
+-- local function splitcontext(features) -- presetcontext creates dummy here
+-- local sf = setups[features]
+-- if not sf then
+-- local n -- number
+-- if find(features,",") then
+-- -- let's assume a combination which is not yet defined but just specified (as in math)
+-- n, sf = presetcontext(features,features,"")
+-- else
+-- -- we've run into an unknown feature and or a direct spec so we create a dummy
+-- n, sf = presetcontext(features,"","")
+-- end
+-- end
+-- return fastcopy(sf)
+-- end
+
+local function splitcontext(features) -- presetcontext creates dummy here
+ local n, sf
+ -- We can have: "a=yes,b=yes" "a,b" "a" "a=yes" etc.
+ if find(features,"[,=]") then
+ --
+ -- from elsewhere (!)
+ --
+ -- this will become:
+ --
+ -- if find(features,"^reset," then
+ setups[features] = nil
+ -- end
+ -- let's assume a combination which is not yet defined but just specified (as in math)
+ n, sf = presetcontext(features,features,"")
+ else
+ sf = setups[features]
+ if not sf then
+ -- we've run into an unknown feature and or a direct spec so we create a dummy
+ n, sf = presetcontext(features,"","")
+ end
+ end
+ return fastcopy(sf)
+end
+
+-- local splitter = lpeg.splitat("=")
+--
+-- local function splitcontext(features)
+-- local setup = setups[features]
+-- if setup then
+-- return setup
+-- elseif find(features,",",1,true) then
+-- -- This is not that efficient but handy anyway for quick and dirty tests
+-- -- beware, due to the way of caching setups you can get the wrong results
+-- -- when components change. A safeguard is to nil the cache.
+-- local merge = nil
+-- for feature in gmatch(features,"[^, ]+") do
+-- if find(feature,"=",1,true) then
+-- local k, v = lpegmatch(splitter,feature)
+-- if k and v then
+-- if not merge then
+-- merge = { k = v }
+-- else
+-- merge[k] = v
+-- end
+-- end
+-- else
+-- local s = setups[feature]
+-- if not s then
+-- -- skip
+-- elseif not merge then
+-- merge = s
+-- else
+-- for k, v in next, s do
+-- merge[k] = v
+-- end
+-- end
+-- end
+-- end
+-- setup = merge and presetcontext(features,"",merge) and setups[features]
+-- -- actually we have to nil setups[features] in order to permit redefinitions
+-- setups[features] = nil
+-- end
+-- return setup or (presetcontext(features,"","") and setups[features]) -- creates dummy
+-- end
+
+specifiers.splitcontext = splitcontext
+
+function specifiers.contexttostring(name,kind,separator,yes,no,strict,omit) -- not used
+ return hash_to_string(
+ mergedtable(handlers[kind].features.defaults or {},setups[name] or {}),
+ separator, yes, no, strict, omit or { "number" }
+ )
+end
+
+local function starred(features) -- no longer fallbacks here
+ local detail = features.detail
+ if detail and detail ~= "" then
+ features.features.normal = splitcontext(detail)
+ else
+ features.features.normal = { }
+ end
+ return features
+end
+
+definers.registersplit('*',starred,"featureset")
+
+-- sort of xetex mode, but without [] and / as we have file: and name: etc
+
+local space = P(" ")
+local spaces = space^0
+local separator = S(";,")
+local equal = P("=")
+local sometext = C((1-equal-space-separator)^1)
+local truevalue = P("+") * spaces * sometext * Cc(true)
+local falsevalue = P("-") * spaces * sometext * Cc(false)
+local somevalue = sometext * spaces * Cc(true)
+local keyvalue = sometext * spaces * equal * spaces * sometext
+local pattern = Cf(Ct("") * (space + separator + Cg(falsevalue + truevalue + keyvalue + somevalue))^0, rawset)
+
+local function colonized(specification)
+ specification.features.normal = normalize_features(lpegmatch(pattern,specification.detail))
+ return specification
+end
+
+definers.registersplit(":",colonized,"direct")
+
+-- define (two steps)
+
+local sizepattern, splitpattern, specialscale do
+
+ -- todo: use lpeg.patterns
+
+ ----- space = P(" ")
+ ----- spaces = space^0
+ local digit = R('09')
+ local period = P(".")
+ local leftparent = (P"(")
+ local rightparent = (P")")
+ local leftbrace = (P"{")
+ local rightbrace = (P"}")
+ local withinparents = leftparent * (1-rightparent)^0 * rightparent
+ local withinbraces = leftbrace * (1-rightbrace )^0 * rightbrace
+ local value = C((withinparents + withinbraces + (1-space))^1)
+ -- local dimension = C((space/"" + P(1))^1)
+ local dimension = Cs((digit+period)^1 * (spaces/"") * (P(1)-digit)^0)
+-- local scaler = C((digit+period)^1)
+ local scaler = lpeg.patterns.unsigned/function(s) return round(tonumber(s)*1000) end
+ + C(lpeg.patterns.cardinal)
+ local rest = C(P(1)^0)
+ local scale_none = Cc(0)
+ local scale_at = (P("at") + P("@")) * Cc(1) * spaces * dimension -- dimension
+ local scale_sa = P("sa") * Cc(2) * spaces * dimension -- number
+ local scale_mo = P("mo") * Cc(3) * spaces * dimension -- number
+ local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- number
+ local scale_ht = P("ht") * Cc(5) * spaces * dimension -- dimension
+ local scale_cp = P("cp") * Cc(6) * spaces * dimension -- dimension
+
+ specialscale = { [5] = "ht", [6] = "cp" }
+
+ sizepattern = spaces * ((scale_at + scale_sa + scale_mo + scale_ht + scale_cp + scale_scaled) * (spaces * scaler)^0 + scale_none)
+ splitpattern = spaces * value * spaces * rest
+
+end
+
+function helpers.splitfontpattern(str)
+ local name, size = lpegmatch(splitpattern,str)
+ local kind, size = lpegmatch(sizepattern,size) -- sx, sy
+ return name, kind, size -- sx, sy
+end
+
+function helpers.fontpatternhassize(str)
+ local name, size = lpegmatch(splitpattern,str)
+ local kind, size = lpegmatch(sizepattern,size)
+ return size or false
+end
+
+local specification -- still needed as local ?
+
+local getspecification = definers.getspecification
+
+-- we can make helper macros which saves parsing (but normaly not
+-- that many calls, e.g. in mk a couple of 100 and in metafun 3500)
+
+local specifiers = { }
+
+do -- else too many locals
+
+ local starttiming = statistics.starttiming
+ local stoptiming = statistics.stoptiming
+
+ local setmacro = tokens.setters.macro
+ local ctxcatcodes = tex.ctxcatcodes
+ local texconditionals = tex.conditionals
+
+ local reported = setmetatableindex(function(t,k)
+ local v = setmetatableindex(function(t,k)
+ t[k] = true
+ return false
+ end)
+ t[k] = v
+ return v
+ end)
+
+ implement {
+ name = "definefont_one",
+ arguments = "string",
+ actions = function(str)
+ starttiming(fonts)
+ if trace_defining then
+ report_defining("memory usage before: %s",statistics.memused())
+ report_defining("start stage one: %s",str)
+ end
+ local fullname, size = lpegmatch(splitpattern,str)
+ local lookup, name, sub, method, detail = getspecification(fullname)
+ if not name then
+ report_defining("strange definition %a",str)
+ -- ctx_setdefaultfontname()
+ elseif name == "unknown" then
+ -- ctx_setdefaultfontname()
+ else
+ -- ctx_setsomefontname(name)
+ setmacro("somefontname",name,"global")
+ end
+ -- we can also use a count for the size
+ if size and size ~= "" then
+ local mode, size, sx, sy = lpegmatch(sizepattern,size)
+ if mode and size and size ~= "" then
+ texsetcount("scaledfontmode",mode)
+ -- ctx_setsomefontsize(size)
+ -- We use a catcodetable, just in case it's 1.2\exheight (a corner case that showed
+ -- up in the lmtx manual: marking that changed size in the mids of some verbatim).
+ setmacro(ctxcatcodes,"somefontsize",size)
+ if sx then
+ setmacro("somefontsizex",sx)
+ end
+ if sy then
+ setmacro("somefontsizey",sy)
+ end
+ else
+ texsetcount("scaledfontmode",0)
+ -- ctx_setemptyfontsize()
+ end
+ elseif true then
+ -- so we don't need to check in tex
+ texsetcount("scaledfontmode",2)
+ -- ctx_setemptyfontsize()
+ else
+ texsetcount("scaledfontmode",0)
+ -- ctx_setemptyfontsize()
+ end
+ specification = definers.makespecification(str,lookup,name,sub,method,detail,size)
+-- specification.original = str
+ if trace_defining then
+ report_defining("stop stage one")
+ end
+ end
+ }
+
+ local function nice_cs(cs)
+ return (gsub(cs,".->", ""))
+ end
+
+ local n = 0
+ local busy = false
+ local combinefeatures = false
+
+ directives.register("fonts.features.combine",function(v)
+ combinefeatures = v
+ end)
+
+ implement {
+ name = "definefont_two",
+ arguments = {
+ -- "boolean", "string", "string", "dimension", "integer", "string",
+ -- "string", "string", "string", "integer", "dimension", "string",
+ -- "string", "string", "string", "integer",
+ "boolean", "argument", "argument", "dimension", "integer",
+ "argument", "argument", "argument", "argument", "integer",
+ "dimension", "argument", "argument", "argument", "argument",
+ "integer",
+ },
+ actions = function (
+ global, -- \ifx\fontclass\empty\s!false\else\s!true\fi
+ cs, -- {#csname}%
+ str, -- \somefontfile
+ size, -- \d_font_scaled_font_size
+ inheritancemode, -- \c_font_feature_inheritance_mode
+ classfeatures, -- \m_font_class_features
+ fontfeatures, -- \m_font_features
+ classfallbacks, -- \m_font_class_fallbacks
+ fontfallbacks, -- \m_font_fallbacks
+ mathsize, -- \fontface
+ textsize, -- \d_font_scaled_text_face
+ classgoodies, -- \m_font_class_goodies
+ goodies, -- \m_font_goodies
+ classdesignsize, -- \m_font_class_designsize
+ fontdesignsize, -- \m_font_designsize
+ scaledfontmode -- \scaledfontmode
+ )
+ if trace_defining then
+ report_defining("start stage two: %s, size %s, features %a & %a, mode %a",str,size,classfeatures,fontfeatures,inheritancemode)
+ end
+ -- name is now resolved and size is scaled cf sa/mo
+ local lookup, name, sub, method, detail = getspecification(str or "")
+ -- new (todo: inheritancemode)
+ local designsize = fontdesignsize ~= "" and fontdesignsize or classdesignsize or ""
+ local designname = designsizefilename(name,designsize,size)
+ if designname and designname ~= "" then
+ if trace_defining or trace_designsize then
+ report_defining("remapping name %a, specification %a, size %a, designsize %a",name,designsize,size,designname)
+ end
+ -- we don't catch detail here
+ local o_lookup, o_name, o_sub, o_method, o_detail = getspecification(designname)
+ if o_lookup and o_lookup ~= "" then lookup = o_lookup end
+ if o_method and o_method ~= "" then method = o_method end
+ if o_detail and o_detail ~= "" then detail = o_detail end
+ name = o_name
+ sub = o_sub
+ end
+ if texconditionals["c_font_compact"] then
+ size = 655360
+ end
+ -- so far
+ -- some settings can have been overloaded
+ if lookup and lookup ~= "" then
+ specification.lookup = lookup
+ end
+ if relativeid and relativeid ~= "" then -- experimental hook
+ local id = tonumber(relativeid) or 0
+ specification.relativeid = id > 0 and id
+ end
+ --
+ specification.name = name
+ specification.size = size
+ specification.sub = (sub and sub ~= "" and sub) or specification.sub
+ specification.mathsize = mathsize
+ specification.textsize = textsize
+ specification.goodies = goodies
+ specification.cs = cs
+ specification.global = global
+ specification.scalemode = scaledfontmode -- context specific
+ if detail and detail ~= "" then
+ specification.method = method or "*"
+ specification.detail = detail
+ elseif specification.detail and specification.detail ~= "" then
+ -- already set
+ elseif inheritancemode == 0 then
+ -- nothing
+ elseif inheritancemode == 1 then
+ -- fontonly
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ end
+ elseif inheritancemode == 2 then
+ -- classonly
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ if classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 3 then
+ -- fontfirst
+ if combinefeatures then
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ if fontfeatures and fontfeatures ~= "" and fontfeatures ~= classfeatures then
+ specification.detail = classfeatures .. "," .. fontfeatures
+ else
+ specification.detail = classfeatures
+ end
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ else
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ elseif classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 4 then
+ -- classfirst
+ if combinefeatures then
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ if classfeatures and classfeatures ~= "" and classfeatures ~= fontfeatures then
+ specification.detail = fontfeatures .. "," .. classfeatures
+ else
+ specification.detail = fontfeatures
+ end
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ else
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ end
+ if classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ elseif fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ end
+ end
+ --
+ local tfmdata = definers.read(specification,size) -- id not yet known (size in spec?)
+ --
+ local lastfontid = 0
+ local tfmtype = type(tfmdata)
+ if tfmtype == "table" then
+ -- setting the extra characters will move elsewhere
+ local characters = tfmdata.characters
+ local parameters = tfmdata.parameters
+ local properties = tfmdata.properties
+ -- we use char0 as signal; cf the spec pdf can handle this (no char in slot)
+ characters[0] = nil
+ -- characters[0x00A0] = { width = parameters.space }
+ -- characters[0x2007] = { width = characters[0x0030] and characters[0x0030].width or parameters.space } -- figure
+ -- characters[0x2008] = { width = characters[0x002E] and characters[0x002E].width or parameters.space } -- period
+ --
+ local fallbacks = specification.fallbacks or ""
+ local mathsize = (mathsize == 1 or mathsize == 2 or mathsize == 3) and mathsize or nil -- can be unset so we test 1 2 3
+ if fallbacks ~= "" and mathsize and not busy then
+ busy = true
+ -- We need this ugly hack in order to resolve fontnames (at the \TEX end). Originally
+ -- math was done in Lua after loading (plugged into aftercopying).
+ --
+ -- After tl 2017 I'll also do text fallbacks this way (although backups there are done
+ -- in a completely different way.)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,1)
+ end
+ mathematics.resolvefallbacks(tfmdata,specification,fallbacks)
+ context(function()
+ -- We're doing math fallbacks.
+ busy = false
+ mathematics.finishfallbacks(tfmdata,specification,fallbacks)
+ tfmdata.original = specification.specification
+ local id = definefont(tfmdata,properties.id)
+ csnames[id] = specification.cs
+ properties.id = id -- already set
+ definers.register(tfmdata,id) -- to be sure, normally already done
+ texdefinefont(global,cs,id)
+ -- texdefinefont(cs,id,global and "global")
+ constructors.finalize(tfmdata)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,2)
+ end
+ -- resolved (when designsize is used):
+ local size = round(tfmdata.parameters.size or 655360)
+ setmacro("somefontsize",size.."sp")
+ -- ctx_setsomefontsize(size .. "sp")
+ texsetcount("scaledfontsize",size)
+ lastfontid = id
+ --
+ if trace_defining then
+ report_defining("memory usage after: %s",statistics.memused())
+ report_defining("stop stage two")
+ end
+ --
+ texsetcount("global","lastfontid",lastfontid)
+ specifiers[lastfontid] = { str, size }
+ if not mathsize then
+ -- forget about it (can't happen here)
+ elseif mathsize == 0 then
+ -- can't happen (here)
+ else
+ -- maybe only 1 2 3 (we already test for this)
+ lastmathids[mathsize] = lastfontid
+ end
+ stoptiming(fonts)
+ end)
+ return
+ else
+ tfmdata.original = specification.specification
+ local id = definefont(tfmdata,properties.id)
+ csnames[id] = specification.cs
+ properties.id = id -- already set
+ definers.register(tfmdata,id) -- to be sure, normally already done
+ texdefinefont(global,cs,id)
+ -- texdefinefont(cs,id,global and "global")
+ constructors.finalize(tfmdata)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,"-")
+ end
+ -- resolved (when designsize is used):
+ local size = round(tfmdata.parameters.size or 655360)
+ setmacro("somefontsize",size.."sp")
+ -- ctx_setsomefontsize(size .. "sp")
+ texsetcount("scaledfontsize",size)
+ lastfontid = id
+ end
+ elseif tfmtype == "number" then
+ if trace_defining then
+ report_defining("reusing %s, id %a, target %a, features %a / %a, fallbacks %a / %a, goodies %a / %a, designsize %a / %a",
+ name,tfmdata,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,classgoodies,goodies,classdesignsize,fontdesignsize)
+ end
+ csnames[tfmdata] = specification.cs
+ texdefinefont(global,cs,tfmdata)
+ -- texdefinefont(cs,tfmdata,global and "global")
+ -- resolved (when designsize is used):
+ local size = round(fontdata[tfmdata].parameters.size or 0)
+ -- ctx_setsomefontsize(size .. "sp")
+ setmacro("somefontsize",size.."sp")
+ texsetcount("scaledfontsize",size)
+ lastfontid = tfmdata
+ else
+ local nice = nice_cs(cs)
+ if not reported[name][nice] then
+ report_defining("unable to define %a as %a",name,nice)
+ end
+ lastfontid = -1
+ texsetcount("scaledfontsize",0)
+ -- ctx_letvaluerelax(cs) -- otherwise the current definition takes the previous one
+ end
+ if trace_defining then
+ report_defining("memory usage after: %s",statistics.memused())
+ report_defining("stop stage two")
+ end
+ --
+ texsetcount("global","lastfontid",lastfontid)
+ specifiers[lastfontid] = { str, size }
+ if not mathsize then
+ -- forget about it
+ elseif mathsize == 0 then
+ -- can't happen (here)
+ else
+ -- maybe only 1 2 3
+ lastmathids[mathsize] = lastfontid
+ end
+ --
+ stoptiming(fonts)
+ end
+ }
+
+ implement {
+ name = "specifiedfontspec",
+ arguments = "integer",
+ actions = function(id)
+ local f = specifiers[id]
+ if f then
+ context(f[1])
+ end
+ end
+ }
+
+ implement {
+ name = "specifiedfontsize",
+ arguments = "integer",
+ actions = function(id)
+ local f = specifiers[id]
+ if f then
+ context(f[2])
+ end
+ end
+ }
+
+ implement {
+ name = "specifiedfont",
+ arguments = { "integer", "number" },
+ actions = function(id,size)
+ local f = specifiers[id]
+ if f and size then
+ context("%s at %0.2p",f[1],size * f[2]) -- we round to 2 decimals (as at the tex end)
+ end
+ end
+ }
+ --
+
+ local function define(specification)
+ --
+ local name = specification.name
+ if not name or name == "" then
+ return -1
+ else
+ starttiming(fonts)
+ --
+ -- following calls expect a few properties to be set:
+ --
+ local lookup, name, sub, method, detail = getspecification(name or "")
+ --
+ specification.name = (name ~= "" and name) or specification.name
+ --
+ specification.lookup = specification.lookup or (lookup ~= "" and lookup) or "file"
+ specification.size = specification.size or 655260
+ specification.sub = specification.sub or (sub ~= "" and sub) or ""
+ specification.method = specification.method or (method ~= "" and method) or "*"
+ specification.detail = specification.detail or (detail ~= "" and detail) or ""
+ --
+ if type(specification.size) == "string" then
+ specification.size = texsp(specification.size) or 655260
+ end
+ --
+ specification.specification = "" -- not used
+ specification.resolved = ""
+ specification.forced = ""
+ specification.features = { } -- via detail, maybe some day
+ --
+ -- we don't care about mathsize textsize goodies fallbacks
+ --
+ local cs = specification.cs
+ if cs == "" then
+ cs = nil
+ specification.cs = nil
+ specification.global = false
+ elseif specification.global == nil then
+ specification.global = false
+ end
+ --
+ local tfmdata = definers.read(specification,specification.size)
+ if not tfmdata then
+ return -1, nil
+ elseif type(tfmdata) == "number" then
+ if cs then
+ texdefinefont(specification.global,cs,tfmdata)
+ -- texdefinefont(cs,tfmdata,specification.global and "global")
+ csnames[tfmdata] = cs
+ end
+ stoptiming(fonts)
+ return tfmdata, fontdata[tfmdata]
+ else
+ local id = definefont(tfmdata)
+ tfmdata.properties.id = id
+ definers.register(tfmdata,id)
+ if cs then
+ texdefinefont(specification.global,cs,id)
+ -- texdefinefont(cs,id,specification.global and "global")
+ csnames[id] = cs
+ end
+ constructors.finalize(tfmdata)
+ stoptiming(fonts)
+ return id, tfmdata
+ end
+ end
+ end
+
+ definers.define = define
+
+ -- local id, cs = fonts.definers.internal { }
+ -- local id, cs = fonts.definers.internal { number = 2 }
+ -- local id, cs = fonts.definers.internal { name = "dejavusans" }
+
+ local n = 0
+
+ function definers.internal(specification,cs)
+ specification = specification or { }
+ local name = specification.name
+ local size = tonumber(specification.size)
+ local number = tonumber(specification.number)
+ local id = nil
+ if not size then
+ size = texgetdimen("bodyfontsize")
+ end
+ if number then
+ id = number
+ elseif name and name ~= "" then
+ local cs = cs or specification.cs
+ if not cs then
+ n = n + 1 -- beware ... there can be many and they are often used once
+ -- cs = formatters["internal font %s"](n)
+ cs = "internal font " .. n
+ else
+ specification.cs = cs
+ end
+ id = define {
+ name = name,
+ size = size,
+ cs = cs,
+ }
+ end
+ if not id then
+ id = currentfont()
+ end
+ return id, csnames[id]
+ end
+
+ local function read(name,size)
+ return (define { name = name, size = size } or 0)
+ end
+
+ callbacks.register('define_font', read, "definition of fonts (tfmdata preparation)")
+
+ -- here
+
+ local infofont = 0
+
+ function fonts.infofont()
+ if infofont == 0 then
+ infofont = define { name = "dejavusansmono", size = texsp("6pt") }
+ end
+ return infofont
+ end
+
+ -- abstract interfacing
+
+ implement { name = "tf", actions = function() setmacro("fontalternative","tf") end }
+ implement { name = "bf", actions = function() setmacro("fontalternative","bf") end }
+ implement { name = "it", actions = function() setmacro("fontalternative","it") end }
+ implement { name = "sl", actions = function() setmacro("fontalternative","sl") end }
+ implement { name = "bi", actions = function() setmacro("fontalternative","bi") end }
+ implement { name = "bs", actions = function() setmacro("fontalternative","bs") end }
+
+end
+
+-- Not ok, we can best use a database for this. The problem is that we
+-- have delayed definitions and so we never know what style is taken
+-- as start.
+
+function constructors.calculatescale(tfmdata,scaledpoints,relativeid,specification)
+ local parameters = tfmdata.parameters
+ local units = parameters.units or 1000
+ if specification then
+ local scalemode = specification.scalemode
+ local special = scalemode and specialscale[scalemode]
+ if special == "ht" then
+ local height = parameters.ascender / units
+ scaledpoints = scaledpoints / height
+ elseif special == "cp" then
+ local glyph = tfmdata.descriptions[utfbyte("X")]
+ local height = (glyph and glyph.height or parameters.ascender) / units
+ scaledpoints = scaledpoints / height
+ end
+ end
+ if scaledpoints < 0 then
+ scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp
+ end
+ -- a temp hack till we have upgraded all mechanisms
+ local delta = round(scaledpoints/units)
+ local size = round(scaledpoints)
+ texsetcount("c_font_scaled_points",size)
+ --
+ return size, delta
+end
+
+local designsizes = constructors.designsizes
+
+-- called quite often when in mp labels
+-- otf.normalizedaxis
+
+function constructors.hashinstance(specification,force)
+ local hash = specification.hash
+ local size = specification.size
+ local fallbacks = specification.fallbacks
+ if force or not hash then
+ hash = constructors.hashfeatures(specification)
+ specification.hash = hash
+ end
+ if size < 1000 and designsizes[hash] then
+ size = round(constructors.scaled(size,designsizes[hash]))
+ else
+ size = round(size)
+ end
+ specification.size = size
+ if fallbacks then
+ return hash .. ' @ ' .. size .. ' @ ' .. fallbacks
+ else
+ local scalemode = specification.scalemode
+ local special = scalemode and specialscale[scalemode]
+ if special then
+ return hash .. ' @ ' .. size .. ' @ ' .. special
+ else
+ return hash .. ' @ ' .. size
+ end
+ end
+end
+
+-- We overload the (generic) resolver:
+
+local resolvers = definers.resolvers
+local hashfeatures = constructors.hashfeatures
+
+function definers.resolve(specification) -- overload function in font-con.lua
+ if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash
+ local r = resolvers[specification.lookup]
+ if r then
+ r(specification)
+ end
+ end
+ if specification.forced == "" then
+ specification.forced = nil
+ else
+ specification.forced = specification.forced
+ end
+ -- goodies are a context specific thing and are not always defined
+ -- as feature, so we need to make sure we add them here before
+ -- hashing because otherwise we get funny goodies applied
+ local goodies = specification.goodies
+ if goodies and goodies ~= "" then
+ -- this adapts the features table so it has best be a copy
+ local normal = specification.features.normal
+ if not normal then
+ specification.features.normal = { goodies = goodies }
+ elseif not normal.goodies then
+ local g = normal.goodies
+ if g and g ~= "" then
+ normal.goodies = formatters["%s,%s"](g,goodies)
+ else
+ normal.goodies = goodies
+ end
+ end
+ end
+ -- so far for goodie hacks
+ local hash = hashfeatures(specification)
+ local name = specification.name or "badfont"
+ local sub = specification.sub
+ if sub and sub ~= "" then
+ specification.hash = lower(name .. " @ " .. sub .. ' @ ' .. hash)
+ else
+ specification.hash = lower(name .. " @ " .. ' @ ' .. hash)
+ end
+ --
+ return specification
+end
+
+-- we need an 'do after the banner hook'
+
+-- => commands
+
+local pattern = P("P")
+ * (lpeg.patterns.hexdigit^4 / function(s) return tonumber(s,16) end)
+ * P(-1)
+
+local function nametoslot(name) -- also supports PXXXXX (4+ positions)
+ local t = type(name)
+ if t == "string" then
+ local unic = unicodes[true]
+ local slot = unic[name]
+ if slot then
+ return slot
+ end
+ --
+ local slot = unic[gsub(name,"_"," ")] or unic[gsub(name,"_","-")] or
+ unic[gsub(name,"-"," ")] or unic[gsub(name,"-","_")] or
+ unic[gsub(name," ","_")] or unic[gsub(name," ","-")]
+ if slot then
+ return slot
+ end
+ --
+ if not aglunicodes then
+ aglunicodes = encodings.agl.unicodes
+ end
+ local char = characters[true]
+ local slot = aglunicodes[name]
+ if slot and char[slot] then
+ return slot
+ end
+ local slot = lpegmatch(pattern,name)
+ if slot and char[slot] then
+ return slot
+ end
+ -- not in font
+ elseif t == "number" then
+ if characters[true][name] then
+ return slot
+ else
+ -- not in font
+ end
+ end
+end
+
+local found = { }
+
+local function descriptiontoslot(name)
+ local t = type(name)
+ if t == "string" then
+ -- slow
+ local list = sortedkeys(chardata) -- can be a cache with weak tables
+ local slot = found[name]
+ local char = characters[true]
+ if slot then
+ return char[slot] and slot or nil
+ end
+ local NAME = upper(name)
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local d = c.description
+ if d == NAME then
+ found[name] = slot
+ return char[slot] and slot or nil
+ end
+ end
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local s = c.synonyms
+ if s then
+ for i=1,#s do
+ local si = s[i]
+ if si == name then
+ found[name] = si
+ return char[slot] and slot or nil
+ end
+ end
+ end
+ end
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local d = c.description
+ if d and find(d,NAME) then
+ found[name] = slot
+ return char[slot] and slot or nil
+ end
+ end
+ for i=1,#list do
+ slot = list[i]
+ local c = chardata[slot]
+ local s = c.synonyms
+ if s then
+ for i=1,#s do
+ local si = s[i]
+ if find(s[i],name) then
+ found[name] = si
+ return char[slot] and slot or nil
+ end
+ end
+ end
+ end
+ -- not in font
+ elseif t == "number" then
+ if characters[true][name] then
+ return slot
+ else
+ -- not in font
+ end
+ end
+end
+
+local function indextoslot(font,index)
+ if not index then
+ index = font
+ font = true
+ end
+ local r = resources[font]
+ if r then
+ local indices = r.indices
+ if not indices then
+ indices = { }
+ local c = characters[font]
+ for unicode, data in next, c do
+ local di = data.index
+ if di then
+ indices[di] = unicode
+ end
+ end
+ r.indices = indices
+ end
+ return indices[tonumber(index)]
+ end
+end
+
+do -- else too many locals
+
+ local entities = characters.entities
+ local lowered = { } -- delayed initialization
+
+ setmetatableindex(lowered,function(t,k)
+ for k, v in next, entities do
+ local l = lower(k)
+ if not entities[l] then
+ lowered[l] = v
+ end
+ end
+ setmetatableindex(lowered,nil)
+ return lowered[k]
+ end)
+
+ local methods = {
+ -- entity
+ e = function(name)
+ return entities[name] or lowered[name] or name
+ end,
+ -- hexadecimal unicode
+ x = function(name)
+ local n = tonumber(name,16)
+ return n and utfchar(n) or name
+ end,
+ -- decimal unicode
+ d = function(name)
+ local n = tonumber(name)
+ return n and utfchar(n) or name
+ end,
+ -- hexadecimal index (slot)
+ s = function(name)
+ local n = tonumber(name,16)
+ local n = n and indextoslot(n)
+ return n and utfchar(n) or name
+ end,
+ -- decimal index
+ i = function(name)
+ local n = tonumber(name)
+ local n = n and indextoslot(n)
+ return n and utfchar(n) or name
+ end,
+ -- name
+ n = function(name)
+ local n = nametoslot(name)
+ return n and utfchar(n) or name
+ end,
+ -- unicode description (synonym)
+ u = function(name)
+ local n = descriptiontoslot(name,false)
+ return n and utfchar(n) or name
+ end,
+ -- all
+ a = function(name)
+ local n = nametoslot(name) or descriptiontoslot(name)
+ return n and utfchar(n) or name
+ end,
+ -- char
+ c = function(name)
+ return name
+ end,
+ }
+
+ -- -- nicer:
+ --
+ -- setmetatableindex(methods,function(t,k) return methods.c end)
+ --
+ -- local splitter = (C(1) * P(":") + Cc("c")) * C(P(1)^1) / function(method,name)
+ -- return methods[method](name)
+ -- end
+ --
+ -- -- more efficient:
+
+ local splitter = C(1) * P(":") * C(P(1)^1) / function(method,name)
+ local action = methods[method]
+ return action and action(name) or name
+ end
+
+ local function tochar(str)
+ local t = type(str)
+ if t == "number" then
+ return utfchar(str)
+ elseif t == "string" then
+ return lpegmatch(splitter,str) or str
+ else
+ return str
+ end
+ end
+
+ helpers.nametoslot = nametoslot
+ helpers.descriptiontoslot = descriptiontoslot
+ helpers.indextoslot = indextoslot
+ helpers.tochar = tochar
+
+ -- interfaces:
+
+ implement {
+ name = "fontchar",
+ actions = { nametoslot, ctx_char },
+ arguments = "string",
+ }
+
+ implement {
+ name = "fontcharbyindex",
+ actions = { indextoslot, ctx_char },
+ arguments = "integer",
+ }
+
+ implement {
+ name = "tochar",
+ actions = { tochar, ctx_safechar },
+ arguments = "string",
+ }
+
+end
+
+-- this will change ...
+
+function loggers.reportdefinedfonts()
+ if trace_usage then
+ local t, tn = { }, 0
+ for id, data in sortedhash(fontdata) do
+ local properties = data.properties or { }
+ local parameters = data.parameters or { }
+ tn = tn + 1
+ t[tn] = {
+ formatters["%03i"](id or 0),
+ formatters["%p" ](parameters.size or 0),
+ properties.format or "unknown",
+ properties.name or "",
+ properties.psname or "",
+ properties.fullname or "",
+ properties.sharedwith or "",
+ }
+ end
+ formatcolumns(t," ")
+ --
+ logs.startfilelogging(report,"defined fonts")
+ for k=1,tn do
+ report(t[k])
+ end
+ logs.stopfilelogging()
+ end
+end
+
+logs.registerfinalactions(loggers.reportdefinedfonts)
+
+function loggers.reportusedfeatures()
+ -- numbers, setups, merged
+ if trace_usage then
+ local t, n = { }, #numbers
+ for i=1,n do
+ local name = numbers[i]
+ local setup = setups[name]
+ local n = setup.number
+ setup.number = nil -- we have no reason to show this
+ t[i] = { i, name, sequenced(setup,false,true) } -- simple mode
+ setup.number = n -- restore it (normally not needed as we're done anyway)
+ end
+ formatcolumns(t," ")
+ logs.startfilelogging(report,"defined featuresets")
+ for k=1,n do
+ report(t[k])
+ end
+ logs.stopfilelogging()
+ end
+end
+
+logs.registerfinalactions(loggers.reportusedfeatures)
+
+-- maybe move this to font-log.lua:
+
+statistics.register("font engine", function()
+ local elapsed = statistics.elapsedseconds(fonts)
+ local nofshared = constructors.nofsharedfonts or 0
+ local nofloaded = constructors.noffontsloaded or 0
+ if nofshared > 0 then
+ return format("otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, %s shared in backend, %s common vectors, %s common hashes, load time %s",
+ otf.version,afm.version,tfm.version,nofloaded,
+ nofshared,constructors.nofsharedvectors,constructors.nofsharedhashes,
+ elapsed)
+ elseif nofloaded > 0 and elapsed then
+ return format("otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, load time %s",
+ otf.version,afm.version,tfm.version,nofloaded,
+ elapsed)
+ else
+ return format("otf %0.3f, afm %0.3f, tfm %0.3f",
+ otf.version,afm.version,tfm.version)
+ end
+end)
+
+-- experimental mechanism for Mojca:
+--
+-- fonts.definetypeface {
+-- name = "mainbodyfont-light",
+-- preset = "antykwapoltawskiego-light",
+-- }
+--
+-- fonts.definetypeface {
+-- name = "mojcasfavourite",
+-- preset = "antykwapoltawskiego",
+-- normalweight = "light",
+-- boldweight = "bold",
+-- width = "condensed",
+-- }
+
+local Shapes = {
+ serif = "Serif",
+ sans = "Sans",
+ mono = "Mono",
+}
+
+local ctx_startfontclass = context.startfontclass
+local ctx_stopfontclass = context.stopfontclass
+local ctx_definefontsynonym = context.definefontsynonym
+local ctx_dofastdefinetypeface = context.dofastdefinetypeface
+
+function fonts.definetypeface(name,t)
+ if type(name) == "table" then
+ -- {name=abc,k=v,...}
+ t = name
+ elseif t then
+ if type(t) == "string" then
+ -- "abc", "k=v,..."
+ t = settings_to_hash(name)
+ else
+ -- "abc", {k=v,...}
+ end
+ t.name = t.name or name
+ else
+ -- "name=abc,k=v,..."
+ t = settings_to_hash(name)
+ end
+ local p = t.preset and fonts.typefaces[t.preset] or { }
+ local name = t.name or "unknowntypeface"
+ local shortcut = t.shortcut or p.shortcut or "rm"
+ local size = t.size or p.size or "default"
+ local shape = t.shape or p.shape or "serif"
+ local fontname = t.fontname or p.fontname or "unknown"
+ local normalweight = t.normalweight or t.weight or p.normalweight or p.weight or "normal"
+ local boldweight = t.boldweight or t.weight or p.boldweight or p.weight or "normal"
+ local normalwidth = t.normalwidth or t.width or p.normalwidth or p.width or "normal"
+ local boldwidth = t.boldwidth or t.width or p.boldwidth or p.width or "normal"
+ Shape = Shapes[shape] or "Serif"
+ ctx_startfontclass { name }
+ ctx_definefontsynonym( { formatters["%s"] (Shape) }, { formatters["spec:%s-%s-regular-%s"] (fontname, normalweight, normalwidth) } )
+ ctx_definefontsynonym( { formatters["%sBold"] (Shape) }, { formatters["spec:%s-%s-regular-%s"] (fontname, boldweight, boldwidth ) } )
+ ctx_definefontsynonym( { formatters["%sBoldItalic"](Shape) }, { formatters["spec:%s-%s-italic-%s"] (fontname, boldweight, boldwidth ) } )
+ ctx_definefontsynonym( { formatters["%sItalic"] (Shape) }, { formatters["spec:%s-%s-italic-%s"] (fontname, normalweight, normalwidth) } )
+ ctx_stopfontclass()
+ local settings = sequenced({ features= t.features },",")
+ ctx_dofastdefinetypeface(name, shortcut, shape, size, settings)
+end
+
+implement {
+ name = "definetypeface",
+ actions = fonts.definetypeface,
+ arguments = "2 strings"
+}
+
+function fonts.current() -- todo: also handle name
+ return fontdata[currentfont()] or fontdata[0]
+end
+
+function fonts.currentid()
+ return currentfont() or 0
+end
+
+-- for the moment here, this will become a chain of extras that is
+-- hooked into the ctx registration (or scaler or ...)
+
+local dimenfactors = number.dimenfactors
+
+function helpers.dimenfactor(unit,id)
+ if unit == "ex" then
+ return id and exheights[id] or 282460 -- lm 10pt
+ elseif unit == "em" then
+ return id and emwidths [id] or 655360 -- lm 10pt
+ else
+ local du = dimenfactors[unit]
+ return du and 1/du or tonumber(unit) or 1
+ end
+end
+
+local function digitwidth(font) -- max(quad/2,wd(0..9))
+ local tfmdata = fontdata[font]
+ local parameters = tfmdata.parameters
+ local width = parameters.digitwidth
+ if not width then
+ width = round(parameters.quad/2) -- maybe tex.scale
+ local characters = tfmdata.characters
+ for i=48,57 do
+ local wd = round(characters[i].width)
+ if wd > width then
+ width = wd
+ end
+ end
+ parameters.digitwidth = width
+ end
+ return width
+end
+
+helpers.getdigitwidth = digitwidth
+helpers.setdigitwidth = digitwidth
+
+--
+
+function helpers.getparameters(tfmdata)
+ local p = { }
+ local m = p
+ local parameters = tfmdata.parameters
+ while true do
+ for k, v in next, parameters do
+ m[k] = v
+ end
+ parameters = getmetatable(parameters)
+ parameters = parameters and parameters.__index
+ if type(parameters) == "table" then
+ m = { }
+ p.metatable = m
+ else
+ break
+ end
+ end
+ return p
+end
+
+if environment.initex then
+
+ local function names(t)
+ local nt = #t
+ if nt > 0 then
+ local n = { }
+ for i=1,nt do
+ n[i] = t[i].name
+ end
+ return concat(n," ")
+ else
+ return "-"
+ end
+ end
+
+ statistics.register("font processing", function()
+ local l = { }
+ for what, handler in table.sortedpairs(handlers) do
+ local features = handler and handler.features
+ if features then
+ l[#l+1] = format("[%s (base initializers: %s) (base processors: %s) (base manipulators: %s) (node initializers: %s) (node processors: %s) (node manipulators: %s)]",
+ what,
+ names(features.initializers.base),
+ names(features.processors .base),
+ names(features.manipulators.base),
+ names(features.initializers.node),
+ names(features.processors .node),
+ names(features.manipulators.node)
+ )
+ end
+ end
+ return concat(l, " | ")
+ end)
+
+end
+
+-- redefinition
+
+-- local hashes = fonts.hashes
+-- local emwidths = hashes.emwidths
+-- local exheights = hashes.exheights
+
+setmetatableindex(dimenfactors, function(t,k)
+ if k == "ex" then
+ return 1/exheights[currentfont()]
+ elseif k == "em" then
+ return 1/emwidths[currentfont()]
+ elseif k == "pct" or k == "%" then
+ return 1/(texget("hsize")/100)
+ else
+ -- error("wrong dimension: " .. (s or "?")) -- better a message
+ return false
+ end
+end)
+
+dimenfactors.ex = nil
+dimenfactors.em = nil
+dimenfactors["%"] = nil
+dimenfactors.pct = nil
+
+--[[ldx--
+<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
+to scale virtual characters.</p>
+--ldx]]--
+
+do
+
+ -- can become luat-tex.lua
+
+ local texsetglyphdata = tex.setglyphdata
+ local texgetglyphdata = tex.getglyphdata
+
+ if not texsetglyphdata then
+
+ local texsetattribute = tex.setattribute
+ local texgetattribute = tex.getattribute
+
+ texsetglyphdata = function(n) return texsetattribute(0,n) end
+ texgetglyphdata = function() return texgetattribute(0) end
+
+ tex.setglyphdata = texsetglyphdata
+ tex.getglyphdata = texgetglyphdata
+
+ end
+
+ -- till here
+
+ local setmacro = tokens.setters.macro
+
+ function constructors.currentfonthasfeature(n)
+ local f = fontdata[currentfont()]
+ if not f then return end f = f.shared
+ if not f then return end f = f.rawdata
+ if not f then return end f = f.resources
+ if not f then return end f = f.features
+ return f and (f.gpos[n] or f.gsub[n])
+ end
+
+ local ctx_doifelse = commands.doifelse
+ local ctx_doif = commands.doif
+
+ implement {
+ name = "doifelsecurrentfonthasfeature",
+ actions = { constructors.currentfonthasfeature, ctx_doifelse },
+ arguments = "string"
+ }
+
+ local f_strip = formatters["%0.2fpt"] -- normally this value is changed only once
+ local stripper = lpeg.patterns.stripzeros
+
+ local cache = { }
+
+ local hows = {
+ ["+"] = "add",
+ ["-"] = "subtract",
+ ["="] = "replace",
+ }
+
+ local function setfeature(how,parent,name,font) -- 0/1 test temporary for testing
+ if not how or how == 0 then
+ if trace_features and texgetglyphdata() ~= 0 then
+ report_cummulative("font %!font:name!, reset",fontdata[font or true])
+ end
+ texsetglyphdata(0)
+ elseif how == true or how == 1 then
+ local hash = "feature > " .. parent
+ local done = cache[hash]
+ if trace_features and done then
+ report_cummulative("font %!font:name!, revive %a : %!font:features!",fontdata[font or true],parent,setups[numbers[done]])
+ end
+ texsetglyphdata(done or 0)
+ else
+ local full = parent .. how .. name
+ local hash = "feature > " .. full
+ local done = cache[hash]
+ if not done then
+ local n = setups[full]
+ if n then
+ -- already defined
+ else
+ n = mergecontextfeatures(parent,name,how,full)
+ end
+ done = registercontextfeature(hash,full,how)
+ cache[hash] = done
+ if trace_features then
+ report_cummulative("font %!font:name!, %s %a : %!font:features!",fontdata[font or true],hows[how],full,setups[numbers[done]])
+ end
+ end
+ texsetglyphdata(done)
+ end
+ end
+
+ local function resetfeature()
+ if trace_features and texgetglyphdata() ~= 0 then
+ report_cummulative("font %!font:name!, reset",fontdata[true])
+ end
+ texsetglyphdata(0)
+ end
+
+ local function setfontfeature(tag)
+ texsetglyphdata(contextnumber(tag))
+ end
+
+ local function resetfontfeature()
+ texsetglyphdata(0)
+ end
+
+ implement {
+ name = "nbfs",
+ arguments = "dimen",
+ actions = function(d)
+ context(lpegmatch(stripper,f_strip(d/65536)))
+ end
+ }
+
+ implement {
+ name = "featureattribute",
+ arguments = "string",
+ actions = { contextnumber, context }
+ }
+
+ implement {
+ name = "setfontfeature",
+ arguments = "string",
+ actions = setfontfeature,
+ }
+
+ implement {
+ name = "resetfontfeature",
+ -- arguments = { 0, 0 },
+ actions = resetfontfeature,
+ }
+
+ implement {
+ name = "setfontofid",
+ arguments = "integer",
+ actions = function(id)
+ ctx_getvalue(csnames[id])
+ end
+ }
+
+ implement {
+ name = "definefontfeature",
+ arguments = "3 strings",
+ actions = presetcontext,
+ }
+
+ implement {
+ name = "doifelsefontfeature",
+ arguments = "string",
+ actions = function(name) ctx_doifelse(contextnumber(name) > 1) end,
+ }
+
+ implement {
+ name = "doifunknownfontfeature",
+ arguments = "string",
+ actions = function(name) ctx_doif(contextnumber(name) == 0) end,
+ }
+
+ implement {
+ name = "adaptfontfeature",
+ arguments = "2 strings",
+ actions = adaptcontext
+ }
+
+ local function registerlanguagefeatures()
+ local specifications = languages.data.specifications
+ for i=1,#specifications do
+ local specification = specifications[i]
+ local language = specification.opentype
+ if language then
+ local script = specification.opentypescript or specification.script
+ if script then
+ local context = specification.context
+ if type(context) == "table" then
+ for i=1,#context do
+ definecontext(context[i], { language = language, script = script})
+ end
+ elseif type(context) == "string" then
+ definecontext(context, { language = language, script = script})
+ end
+ end
+ end
+ end
+ end
+
+ constructors.setfeature = setfeature
+ constructors.resetfeature = resetfeature
+
+ implement { name = "resetfeature", actions = resetfeature }
+ implement { name = "addfeature", actions = setfeature, arguments = { "'+'", "string", "string" } }
+ implement { name = "subtractfeature", actions = setfeature, arguments = { "'-'", "string", "string" } }
+ implement { name = "replacefeature", actions = setfeature, arguments = { "'='", "string", "string" } }
+ implement { name = "revivefeature", actions = setfeature, arguments = { true, "string" } }
+
+ implement {
+ name = "featurelist",
+ actions = { fonts.specifiers.contexttostring, context },
+ arguments = { "string", "'otf'", "string", "'yes'", "'no'", true }
+ }
+
+ implement {
+ name = "registerlanguagefeatures",
+ actions = registerlanguagefeatures,
+ }
+
+end
+
+-- a fontkern plug:
+
+-- nodes.injections.installnewkern(nuts.pool.fontkern)
+
+do
+
+ local report = logs.reporter("otf","variants")
+
+ local function replace(tfmdata,feature,value)
+ local characters = tfmdata.characters
+ local variants = tfmdata.resources.variants
+ if variants then
+ local t = { }
+ for k, v in sortedhash(variants) do
+ t[#t+1] = formatters["0x%X (%i)"](k,k)
+ end
+ value = tonumber(value) or 0xFE00 -- 917762
+ report("fontname : %s",tfmdata.properties.fontname)
+ report("available: % t",t)
+ local v = variants[value]
+ if v then
+ report("using : %X (%i)",value,value)
+ for k, v in next, v do
+ local c = characters[v]
+ if c then
+ characters[k] = c
+ end
+ end
+ else
+ report("unknown : %X (%i)",value,value)
+ end
+ end
+ end
+
+ registerotffeature {
+ name = 'variant',
+ description = 'unicode variant',
+ manipulators = {
+ base = replace,
+ node = replace,
+ }
+ }
+
+end
+
+-- here (todo: closure)
+
+-- make a closure (200 limit):
+
+do
+
+ local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
+
+ local analyzers = fonts.analyzers
+ local methods = analyzers.methods
+
+ local unsetvalue = attributes.unsetvalue
+
+ local a_color = attributes.private('color')
+ local a_colormodel = attributes.private('colormodel')
+ local m_color = attributes.list[a_color] or { }
+
+ local glyph_code = nodes.nodecodes.glyph
+
+ local states = analyzers.states
+
+ local colornames = {
+ [states.init] = "font:1",
+ [states.medi] = "font:2",
+ [states.fina] = "font:3",
+ [states.isol] = "font:4",
+ [states.mark] = "font:5",
+ [states.rest] = "font:6",
+ [states.rphf] = "font:1",
+ [states.half] = "font:2",
+ [states.pref] = "font:3",
+ [states.blwf] = "font:4",
+ [states.pstf] = "font:5",
+ }
+
+ -- todo: traversers
+ -- todo: check attr_list so that we can use the same .. helper: setcolorattr
+
+ local function markstates(head)
+ if head then
+ head = tonut(head)
+ local model = getattr(head,a_colormodel) or 1
+ for glyph in nextchar, head do
+ local a = getstate(glyph)
+ if a then
+ local name = colornames[a]
+ if name then
+ local color = m_color[name]
+ if color then
+ setattr(glyph,a_colormodel,model)
+ setattr(glyph,a_color,color)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function analyzeprocessor(head,font,attr)
+ local tfmdata = fontdata[font]
+ local script, language = otf.scriptandlanguage(tfmdata,attr)
+ local action = methods[script]
+ if not action then
+ return head, false
+ end
+ if type(action) == "function" then
+ local head, done = action(head,font,attr)
+ if done and trace_analyzing then
+ markstates(head)
+ end
+ return head, done
+ end
+ action = action[language]
+ if action then
+ local head, done = action(head,font,attr)
+ if done and trace_analyzing then
+ markstates(head)
+ end
+ return head, done
+ else
+ return head, false
+ end
+ end
+
+ registerotffeature { -- adapts
+ name = "analyze",
+ processors = {
+ node = analyzeprocessor,
+ }
+ }
+
+
+ function methods.nocolor(head,font,attr)
+ for n, c, f in nextchar, head do
+ if not font or f == font then
+ setattr(n,a_color,unsetvalue)
+ end
+ end
+ return head, true
+ end
+
+end
+
+
+local function purefontname(name)
+ if type(name) == "number" then
+ name = getfontname(name)
+ end
+ if type(name) == "string" then
+ return basename(name)
+ end
+end
+
+implement {
+ name = "purefontname",
+ actions = { purefontname, context },
+ arguments = "string",
+}
+
+local sharedstorage = storage.shared
+
+local list = sharedstorage.bodyfontsizes or { }
+local unknown = sharedstorage.unknownbodyfontsizes or { }
+
+sharedstorage.bodyfontsizes = list
+sharedstorage.unknownbodyfontsizes = unknown
+
+implement {
+ name = "registerbodyfontsize",
+ arguments = "string",
+ actions = function(size)
+ list[size] = true
+ end
+}
+
+interfaces.implement {
+ name = "registerunknownbodysize",
+ arguments = "string",
+ actions = function(size)
+ if not unknown[size] then
+ interfaces.showmessage("fonts",14,size)
+ end
+ unknown[size] = true
+ end,
+}
+
+implement {
+ name = "getbodyfontsizes",
+ arguments = "string",
+ actions = function(separator)
+ context(concat(sortedkeys(list),separator))
+ end
+}
+
+implement {
+ name = "processbodyfontsizes",
+ arguments = "string",
+ actions = function(command)
+ local keys = sortedkeys(list)
+ if command then
+ local action = context[command]
+ for i=1,#keys do
+ action(keys[i])
+ end
+ else
+ context(concat(keys,","))
+ end
+ end
+}
+
+implement {
+ name = "cleanfontname",
+ actions = { cleanname, context },
+ arguments = "string"
+}
+
+implement {
+ name = "fontlookupinitialize",
+ actions = names.lookup,
+ arguments = "string",
+}
+
+implement {
+ name = "fontlookupnoffound",
+ actions = { names.noflookups, context },
+}
+
+implement {
+ name = "fontlookupgetkeyofindex",
+ actions = { names.getlookupkey, context },
+ arguments = { "string", "integer"}
+}
+
+implement {
+ name = "fontlookupgetkey",
+ actions = { names.getlookupkey, context },
+ arguments = "string"
+}
+
+-- this might move to a runtime module:
+
+function commands.showchardata(n)
+ local tfmdata = fontdata[currentfont()]
+ if tfmdata then
+ if type(n) == "string" then
+ n = utfbyte(n)
+ end
+ local chr = tfmdata.characters[n]
+ if chr then
+ report_status("%s @ %s => %U => %c => %s",tfmdata.properties.fullname,tfmdata.parameters.size,n,n,serialize(chr,false))
+ end
+ end
+end
+
+function commands.showfontparameters(tfmdata)
+ -- this will become more clever
+ local tfmdata = tfmdata or fontdata[currentfont()]
+ if tfmdata then
+ local parameters = tfmdata.parameters
+ local mathparameters = tfmdata.mathparameters
+ local properties = tfmdata.properties
+ local hasparameters = parameters and next(parameters)
+ local hasmathparameters = mathparameters and next(mathparameters)
+ if hasparameters then
+ report_status("%s @ %s => text parameters => %s",properties.fullname,parameters.size,serialize(parameters,false))
+ end
+ if hasmathparameters then
+ report_status("%s @ %s => math parameters => %s",properties.fullname,parameters.size,serialize(mathparameters,false))
+ end
+ if not hasparameters and not hasmathparameters then
+ report_status("%s @ %s => no text parameters and/or math parameters",properties.fullname,parameters.size)
+ end
+ end
+end
+
+implement {
+ name = "currentdesignsize",
+ actions = function()
+ context(parameters[currentfont()].designsize)
+ end
+}
+
+implement {
+ name = "doifelsefontpresent",
+ actions = { names.exists, commands.doifelse },
+ arguments = "string"
+}
+
+-- we use 0xFE000+ and 0xFF000+ in math and for runtime (text) extensions we
+-- use 0xFD000+
+
+constructors.privateslots = constructors.privateslots or { }
+
+storage.register("fonts/constructors/privateslots", constructors.privateslots, "fonts.constructors.privateslots")
+
+do
+
+ local privateslots = constructors.privateslots
+ local lastprivateslot = 0xFD000
+
+ constructors.privateslots = setmetatableindex(privateslots,function(t,k)
+ local v = lastprivateslot
+ lastprivateslot = lastprivateslot + 1
+ t[k] = v
+ return v
+ end)
+
+ implement {
+ name = "getprivateglyphslot",
+ actions = function(name) context(privateslots[name]) end,
+ arguments = "string",
+ }
+
+end
+
+-- an extra helper
+
+function helpers.getcoloredglyphs(tfmdata)
+ if type(tfmdata) == "number" then
+ tfmdata = fontdata[tfmdata]
+ end
+ if not tfmdata then
+ tfmdata = fontdata[true]
+ end
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local collected = { }
+ for unicode, character in next, characters do
+ local description = descriptions[unicode]
+ if description and (description.colors or character.svg) then
+ collected[#collected+1] = unicode
+ end
+ end
+ table.sort(collected)
+ return collected
+end
+
+-- for the font manual
+
+statistics.register("body font sizes", function()
+ if next(unknown) then
+ return formatters["defined: % t, undefined: % t"](sortedkeys(list),sortedkeys(unknown))
+ end
+end)
+
+statistics.register("used fonts",function()
+ if trace_usage then
+ local filename = file.nameonly(environment.jobname) .. "-fonts-usage.lua"
+ if next(fontdata) then
+ local files = { }
+ local list = { }
+ for id, tfmdata in sortedhash(fontdata) do
+ local filename = tfmdata.properties.filename
+ if filename then
+ local filedata = files[filename]
+ if filedata then
+ filedata.instances = filedata.instances + 1
+ else
+ local rawdata = tfmdata.shared and tfmdata.shared.rawdata
+ local metadata = rawdata and rawdata.metadata
+ files[filename] = {
+ instances = 1,
+ filename = filename,
+ version = metadata and metadata.version,
+ size = rawdata and rawdata.size,
+ }
+ end
+ else
+ -- what to do
+ end
+ end
+ for k, v in sortedhash(files) do
+ list[#list+1] = v
+ end
+ table.save(filename,list)
+ else
+ os.remove(filename)
+ end
+ end
+end)
+
+-- new
+
+do
+
+ local settings_to_array = utilities.parsers.settings_to_array
+
+ implement {
+ name = "definefontcolorpalette",
+ arguments = "2 strings",
+ actions = function(name,set)
+ otf.registerpalette(name,settings_to_array(set))
+ end
+ }
+
+end
+
+do
+
+ local pattern = C((1-S("* "))^1) -- strips all after * or ' at'
+
+ implement {
+ name = "truefontname",
+ arguments = "string",
+ actions = function(s)
+ -- context(match(s,"[^* ]+") or s)
+ context(lpegmatch(pattern,s) or s)
+ end
+ }
+
+end
+
+do
+
+ local function getinstancespec(id)
+ local data = fontdata[id or true]
+ local shared = data.shared
+ local resources = shared and shared.rawdata.resources
+ if resources then
+ local instancespec = data.properties.instance
+ if instancespec then
+ local variabledata = resources.variabledata
+ if variabledata then
+ local instances = variabledata.instances
+ if instances then
+ for i=1,#instances do
+ local instance = instances[i]
+ if cleanname(instance.subfamily)== instancespec then
+ local values = table.copy(instance.values)
+ local axis = variabledata.axis
+ for i=1,#values do
+ for j=1,#axis do
+ if values[i].axis == axis[j].tag then
+ values[i].name = axis[j].name
+ break
+ end
+ end
+ end
+ return values
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ helpers.getinstancespec = getinstancespec
+
+ -- expandable
+
+ implement {
+ name = "currentfontinstancespec",
+ public = true,
+ actions = function()
+ local t = getinstancespec() -- current font
+ if t then
+ for i=1,#t do
+ if i > 1 then
+ context.space()
+ end
+ local ti = t[i]
+ context("%s=%s",ti.name,ti.value)
+ end
+ end
+ end
+ }
+
+end
+
+-- for the moment here (and not in font-con.lua):
+
+do
+
+ local identical = table.identical
+ local copy = table.copy
+ local fontdata = fonts.hashes.identifiers
+ local addcharacters = font.addcharacters
+
+ -- This helper is mostly meant to add last-resort (virtual) characters
+ -- or runtime generated fonts (so we forget about features and such). It
+ -- will probably take a while before it get used.
+
+ local trace_adding = false
+ local report_adding = logs.reporter("fonts","add characters")
+
+ trackers.register("fonts.addcharacters",function(v) trace_adding = v end)
+
+ function constructors.addcharacters(id,list)
+ local newchar = list.characters
+ if newchar then
+ local data = fontdata[id]
+ local newfont = list.fonts
+ local oldchar = data.characters
+ local oldfont = data.fonts
+ addcharacters(id, {
+ characters = newchar,
+ fonts = newfont,
+ nomath = not data.properties.hasmath,
+ })
+ -- this is just for tracing, as the assignment only uses the fonts list
+ -- and doesn't store it otherwise
+ if newfont then
+ if oldfont then
+ local oldn = #oldfont
+ local newn = #newfont
+ for n=1,newn do
+ local ok = false
+ local nf = newfont[n]
+ for o=1,oldn do
+ if identical(nf,oldfont[o]) then
+ ok = true
+ break
+ end
+ end
+ if not ok then
+ oldn = oldn + 1
+ oldfont[oldn] = newfont[i]
+ end
+ end
+ else
+ data.fonts = newfont
+ end
+ end
+ -- this is because we need to know what goes on and also might
+ -- want to access character data
+ for u, c in next, newchar do
+ if trace_adding then
+ report_adding("adding character %U to font %!font:name!",u,id)
+ end
+ oldchar[u] = c
+ end
+ end
+ end
+
+ implement {
+ name = "addfontpath",
+ arguments = "string",
+ actions = function(list)
+ names.addruntimepath(settings_to_array(list))
+ end
+ }
+
+end
+
+-- moved here
+
+do
+
+ local getfontoffamily = font.getfontoffamily
+ local new_glyph = nodes.pool.glyph
+ local fontproperties = fonts.hashes.properties
+
+ local function getprivateslot(id,name)
+ if not name then
+ name = id
+ id = currentfont()
+ end
+ local properties = fontproperties[id]
+ local privates = properties and properties.privates
+ return privates and privates[name]
+ end
+
+ local function getprivatenode(tfmdata,name)
+ if type(tfmdata) == "number" then
+ tfmdata = fontdata[tfmdata]
+ end
+ local properties = tfmdata.properties
+ local font = properties.id
+ local slot = getprivateslot(font,name)
+ if slot then
+ -- todo: set current attribibutes
+ local char = tfmdata.characters[slot]
+ local tonode = char.tonode
+ if tonode then
+ return tonode(font,char)
+ else
+ return new_glyph(font,slot)
+ end
+ end
+ end
+
+ local function getprivatecharornode(tfmdata,name)
+ if type(tfmdata) == "number" then
+ tfmdata = fontdata[tfmdata]
+ end
+ local properties = tfmdata.properties
+ local font = properties.id
+ local slot = getprivateslot(font,name)
+ if slot then
+ -- todo: set current attributes
+ local char = tfmdata.characters[slot]
+ local tonode = char.tonode
+ if tonode then
+ return "node", tonode(tfmdata,char)
+ else
+ return "char", slot
+ end
+ end
+ end
+
+ helpers.getprivateslot = getprivateslot
+ helpers.getprivatenode = getprivatenode
+ helpers.getprivatecharornode = getprivatecharornode
+
+ implement {
+ name = "getprivatechar",
+ arguments = "string",
+ actions = function(name)
+ local p = getprivateslot(name)
+ if p then
+ context(utfchar(p))
+ end
+ end
+ }
+
+ implement {
+ name = "getprivatemathchar",
+ arguments = "string",
+ actions = function(name)
+ local p = getprivateslot(getfontoffamily(0),name)
+ if p then
+ context(utfchar(p))
+ end
+ end
+ }
+
+ implement {
+ name = "getprivateslot",
+ arguments = "string",
+ actions = function(name)
+ local p = getprivateslot(name)
+ if p then
+ context(p)
+ end
+ end
+ }
+
+end
+
+-- handy, for now here:
+
+function fonts.helpers.collectanchors(tfmdata)
+
+ local resources = tfmdata.resources -- todo: use shared
+
+ if not resources or resources.anchors then
+ return resources.anchors
+ end
+
+ local anchors = { }
+
+ local function set(unicode,target,class,anchor)
+ local a = anchors[unicode]
+ if not a then
+ anchors[unicode] = { [target] = { anchor } }
+ return
+ end
+ local t = a[target]
+ if not t then
+ a[target] = { anchor }
+ return
+ end
+ local x = anchor[1]
+ local y = anchor[2]
+ for k, v in next, t do
+ if v[1] == x and v[2] == y then
+ return
+ end
+ end
+ t[#t+1] = anchor
+ end
+
+ local function getanchors(steps,target)
+ for i=1,#steps do
+ local step = steps[i]
+ local coverage = step.coverage
+ for unicode, data in next, coverage do
+ local class = data[1]
+ local anchor = data[2]
+ if anchor[1] ~= 0 or anchor[2] ~= 0 then
+ set(unicode,target,class,anchor)
+ end
+ end
+ end
+ end
+
+ local function getcursives(steps)
+ for i=1,#steps do
+ local step = steps[i]
+ local coverage = step.coverage
+ for unicode, data in next, coverage do
+ local class = data[1]
+ local en = data[2]
+ local ex = data[3]
+ if en then
+ set(unicode,"entry",class,en)
+ end
+ if ex then
+ set(unicode,"exit", class,ex)
+ end
+ end
+ end
+ end
+
+ local function collect(list)
+ if list then
+ for i=1,#list do
+ local entry = list[i]
+ local steps = entry.steps
+ local kind = entry.type
+ if kind == "gpos_mark2mark" then
+ getanchors(steps,"mark")
+ elseif kind == "gpos_mark2base" then
+ getanchors(steps,"base")
+ elseif kind == "gpos_mark2ligature" then
+ getanchors(steps,"ligature")
+ elseif kind == "gpos_cursive" then
+ getcursives(steps)
+ end
+ end
+ end
+ end
+
+ collect(resources.sequences)
+ collect(resources.sublookups)
+
+ local function sorter(a,b)
+ if a[1] == b[1] then
+ return a[2] < b[2]
+ else
+ return a[1] < b[1]
+ end
+ end
+
+ for unicode, old in next, anchors do
+ for target, list in next, old do
+ sort(list,sorter)
+ end
+ end
+
+ resources.anchors = anchors
+
+ return anchors
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-def.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-def.lmt
new file mode 100644
index 00000000000..614f9803606
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-def.lmt
@@ -0,0 +1,516 @@
+if not modules then modules = { } end modules ['font-def'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We can overload some of the definers.functions so we don't local them.
+
+local lower, gsub = string.lower, string.gsub
+local tostring, next = tostring, next
+local lpegmatch = lpeg.match
+local suffixonly, removesuffix, basename = file.suffix, file.removesuffix, file.basename
+local formatters = string.formatters
+local sortedhash, sortedkeys, setmetatableindex = table.sortedhash, table.sortedkeys, table.setmetatableindex
+
+local allocate = utilities.storage.allocate
+
+local trace_defining = false trackers .register("fonts.defining", function(v) trace_defining = v end)
+local directive_embedall = false directives.register("fonts.embedall", function(v) directive_embedall = v end)
+
+trackers.register("fonts.loading", "fonts.defining", "otf.loading", "afm.loading", "tfm.loading")
+
+local report_defining = logs.reporter("fonts","defining")
+
+--[[ldx--
+<p>Here we deal with defining fonts. We do so by intercepting the
+default loader that only handles <l n='tfm'/>.</p>
+--ldx]]--
+
+local nextfont = font.nextid
+
+local fonts = fonts
+local fontdata = fonts.hashes.identifiers
+local readers = fonts.readers
+local definers = fonts.definers
+local specifiers = fonts.specifiers
+local constructors = fonts.constructors
+local fontgoodies = fonts.goodies
+
+readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc
+
+local variants = allocate()
+specifiers.variants = variants
+
+definers.methods = definers.methods or { }
+
+local internalized = allocate() -- internal tex numbers (private)
+
+local loadedfonts = constructors.loadedfonts
+local designsizes = constructors.designsizes
+
+-- not in generic (some day I'll make two defs, one for context, one for generic)
+
+local resolvefile = fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end
+
+--[[ldx--
+<p>We hardly gain anything when we cache the final (pre scaled)
+<l n='tfm'/> table. But it can be handy for debugging, so we no
+longer carry this code along. Also, we now have quite some reference
+to other tables so we would end up with lots of catches.</p>
+--ldx]]--
+
+--[[ldx--
+<p>We can prefix a font specification by <type>name:</type> or
+<type>file:</type>. The first case will result in a lookup in the
+synonym table.</p>
+
+<typing>
+[ name: | file: ] identifier [ separator [ specification ] ]
+</typing>
+
+<p>The following function split the font specification into components
+and prepares a table that will move along as we proceed.</p>
+--ldx]]--
+
+-- beware, we discard additional specs
+--
+-- method:name method:name(sub) method:name(sub)*spec method:name*spec
+-- name name(sub) name(sub)*spec name*spec
+-- name@spec*oeps
+
+local function makespecification(specification,lookup,name,sub,method,detail,size)
+ size = size or 655360
+ if not lookup or lookup == "" then
+ lookup = definers.defaultlookup
+ end
+ if trace_defining then
+ report_defining("specification %a, lookup %a, name %a, sub %a, method %a, detail %a",
+ specification, lookup, name, sub, method, detail)
+ end
+ local t = {
+ lookup = lookup, -- forced type
+ specification = specification, -- full specification
+ size = size, -- size in scaled points or -1000*n
+ name = name, -- font or filename
+ sub = sub, -- subfont (eg in ttc)
+ method = method, -- specification method
+ detail = detail, -- specification
+ resolved = "", -- resolved font name
+ forced = "", -- forced loader
+ features = { }, -- preprocessed features
+ }
+ return t
+end
+
+definers.makespecification = makespecification
+
+do
+
+ local splitter, splitspecifiers = nil, "" -- not so nice
+
+ local P, C, S, Cc, Cs = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc, lpeg.Cs
+
+ local left = P("(")
+ local right = P(")")
+ local colon = P(":")
+ local space = P(" ")
+ local lbrace = P("{")
+ local rbrace = P("}")
+
+ definers.defaultlookup = "file"
+
+ local prefixpattern = P(false)
+
+ local function addspecifier(symbol)
+ splitspecifiers = splitspecifiers .. symbol
+ local method = S(splitspecifiers)
+ local lookup = C(prefixpattern) * colon
+ local sub = left * C(P(1-left-right-method)^1) * right
+ local specification = C(method) * C(P(1)^1)
+ local name = Cs((lbrace/"") * (1-rbrace)^1 * (rbrace/"") + (1-sub-specification)^1)
+ splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc("")))
+ end
+
+ local function addlookup(str)
+ prefixpattern = prefixpattern + P(str)
+ end
+
+ definers.addlookup = addlookup
+
+ addlookup("file")
+ addlookup("name")
+ addlookup("spec")
+
+ local function getspecification(str)
+ return lpegmatch(splitter,str or "") -- weird catch
+ end
+
+ definers.getspecification = getspecification
+
+ function definers.registersplit(symbol,action,verbosename)
+ addspecifier(symbol)
+ variants[symbol] = action
+ if verbosename then
+ variants[verbosename] = action
+ end
+ end
+
+ function definers.analyze(specification, size)
+ -- can be optimized with locals
+ local lookup, name, sub, method, detail = getspecification(specification or "")
+ return makespecification(specification, lookup, name, sub, method, detail, size)
+ end
+
+end
+
+--[[ldx--
+<p>We can resolve the filename using the next function:</p>
+--ldx]]--
+
+definers.resolvers = definers.resolvers or { }
+local resolvers = definers.resolvers
+
+-- todo: reporter
+
+function resolvers.file(specification)
+ local name = resolvefile(specification.name) -- catch for renames
+ local suffix = lower(suffixonly(name))
+ if fonts.formats[suffix] then
+ specification.forced = suffix
+ specification.forcedname = name
+ specification.name = removesuffix(name)
+ else
+ specification.name = name -- can be resolved
+ end
+end
+
+function resolvers.name(specification)
+ local resolve = fonts.names.resolve
+ if resolve then
+ local resolved, sub, subindex, instance = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = sub
+ specification.subindex = subindex
+ -- new, needed for experiments
+ if instance then
+ specification.instance = instance
+ local features = specification.features
+ if not features then
+ features = { }
+ specification.features = features
+ end
+ local normal = features.normal
+ if not normal then
+ normal = { }
+ features.normal = normal
+ end
+ normal.instance = instance
+ end
+ --
+ local suffix = lower(suffixonly(resolved))
+ if fonts.formats[suffix] then
+ specification.forced = suffix
+ specification.forcedname = resolved
+ specification.name = removesuffix(resolved)
+ else
+ specification.name = resolved
+ end
+ end
+ else
+ resolvers.file(specification)
+ end
+end
+
+function resolvers.spec(specification)
+ local resolvespec = fonts.names.resolvespec
+ if resolvespec then
+ local resolved, sub, subindex = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = sub
+ specification.subindex = subindex
+ specification.forced = lower(suffixonly(resolved))
+ specification.forcedname = resolved
+ specification.name = removesuffix(resolved)
+ end
+ else
+ resolvers.name(specification)
+ end
+end
+
+function definers.resolve(specification)
+ if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash
+ local r = resolvers[specification.lookup]
+ if r then
+ r(specification)
+ end
+ end
+ if specification.forced == "" then
+ specification.forced = nil
+ specification.forcedname = nil
+ end
+ specification.hash = lower(specification.name .. ' @ ' .. constructors.hashfeatures(specification))
+ if specification.sub and specification.sub ~= "" then
+ specification.hash = specification.sub .. ' @ ' .. specification.hash
+ end
+ return specification
+end
+
+--[[ldx--
+<p>The main read function either uses a forced reader (as determined by
+a lookup) or tries to resolve the name using the list of readers.</p>
+
+<p>We need to cache when possible. We do cache raw tfm data (from <l
+n='tfm'/>, <l n='afm'/> or <l n='otf'/>). After that we can cache based
+on specificstion (name) and size, that is, <l n='tex'/> only needs a number
+for an already loaded fonts. However, it may make sense to cache fonts
+before they're scaled as well (store <l n='tfm'/>'s with applied methods
+and features). However, there may be a relation between the size and
+features (esp in virtual fonts) so let's not do that now.</p>
+
+<p>Watch out, here we do load a font, but we don't prepare the
+specification yet.</p>
+--ldx]]--
+
+-- very experimental:
+
+function definers.applypostprocessors(tfmdata)
+ local postprocessors = tfmdata.postprocessors
+ if postprocessors then
+ local properties = tfmdata.properties
+ for i=1,#postprocessors do
+ local extrahash = postprocessors[i](tfmdata) -- after scaling etc
+ if type(extrahash) == "string" and extrahash ~= "" then
+ -- e.g. a reencoding needs this
+ extrahash = gsub(lower(extrahash),"[^a-z]","-")
+ properties.fullname = formatters["%s-%s"](properties.fullname,extrahash)
+ end
+ end
+ end
+ return tfmdata
+end
+
+-- function definers.applypostprocessors(tfmdata)
+-- return tfmdata
+-- end
+
+local function checkfeatures(tfmdata)
+ local resources = tfmdata.resources
+ local shared = tfmdata.shared
+ if resources and shared then
+ local features = resources.features
+ local usedfeatures = shared.features
+ if features and usedfeatures then
+ local usedlanguage = usedfeatures.language or "dflt"
+ local usedscript = usedfeatures.script or "dflt"
+ local function check(what)
+ if what then
+ local foundlanguages = { }
+ for feature, scripts in next, what do
+ if usedscript == "auto" or scripts["*"] then
+ -- ok
+ elseif not scripts[usedscript] then
+ -- report_defining("font %!font:name!, feature %a, no script %a",
+ -- tfmdata,feature,usedscript)
+ else
+ for script, languages in next, scripts do
+ if languages["*"] then
+ -- ok
+ elseif not languages[usedlanguage] then
+ report_defining("font %!font:name!, feature %a, script %a, no language %a",
+ tfmdata,feature,script,usedlanguage)
+ end
+ end
+ end
+ for script, languages in next, scripts do
+ for language in next, languages do
+ foundlanguages[language] = true
+ end
+ end
+ end
+ if false then
+ foundlanguages["*"] = nil
+ foundlanguages = sortedkeys(foundlanguages)
+ for feature, scripts in sortedhash(what) do
+ for script, languages in next, scripts do
+ if not languages["*"] then
+ for i=1,#foundlanguages do
+ local language = foundlanguages[i]
+ if not languages[language] then
+ report_defining("font %!font:name!, feature %a, script %a, no language %a",
+ tfmdata,feature,script,language)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ check(features.gsub)
+ check(features.gpos)
+ end
+ end
+end
+
+local reported = setmetatableindex(function(t,k)
+ t[k] = true
+ return false
+end)
+
+function definers.loadfont(specification)
+ local hash = constructors.hashinstance(specification)
+ -- todo: also hash by instance / factors
+ local tfmdata = loadedfonts[hash] -- hashes by size !
+ local name = specification.name
+ if not tfmdata then
+ -- normally context will not end up here often (if so there is an issue somewhere)
+ local forced = specification.forced or ""
+local id = nextfont(true)
+specification.id = id
+ if forced ~= "" then
+ local reader = readers[lower(forced)] -- normally forced is already lowered
+ tfmdata = reader and reader(specification)
+ if not tfmdata and not reported[name] then
+ report_defining("forced type %a of %a not found",forced,name)
+ end
+ else
+ local sequence = readers.sequence -- can be overloaded so only a shortcut here
+ for s=1,#sequence do
+ local reader = sequence[s]
+ if readers[reader] then -- we skip not loaded readers
+ if trace_defining then
+ report_defining("trying (reader sequence driven) type %a for %a with file %a",reader,specification.name,specification.filename)
+ end
+ tfmdata = readers[reader](specification)
+ if tfmdata then
+ break
+ else
+ specification.filename = nil
+ end
+ end
+ end
+ end
+ if tfmdata then
+ tfmdata = definers.applypostprocessors(tfmdata)
+ loadedfonts[hash] = tfmdata
+ designsizes[specification.hash] = tfmdata.parameters.designsize
+ checkfeatures(tfmdata)
+ end
+ end
+ if not tfmdata and not reported[name] then
+ report_defining("font with asked name %a is not found using lookup %a",name,specification.lookup)
+ end
+ return tfmdata
+end
+
+function constructors.readanddefine(name,size) -- no id -- maybe a dummy first
+ local specification = definers.analyze(name,size)
+ local method = specification.method
+ if method and variants[method] then
+ specification = variants[method](specification)
+ end
+ specification = definers.resolve(specification)
+ local hash = constructors.hashinstance(specification)
+ local id = definers.registered(hash)
+ if not id then
+ local tfmdata = definers.loadfont(specification)
+ if tfmdata then
+ tfmdata.properties.hash = hash
+ id = font.define(tfmdata)
+ definers.register(tfmdata,id)
+ else
+ id = 0 -- signal
+ end
+ end
+ return fontdata[id], id
+end
+
+--[[ldx--
+<p>So far the specifiers. Now comes the real definer. Here we cache
+based on id's. Here we also intercept the virtual font handler. Since
+it evolved stepwise I may rewrite this bit (combine code).</p>
+
+In the previously defined reader (the one resulting in a <l n='tfm'/>
+table) we cached the (scaled) instances. Here we cache them again, but
+this time based on id. We could combine this in one cache but this does
+not gain much. By the way, passing id's back to in the callback was
+introduced later in the development.</p>
+--ldx]]--
+
+function definers.registered(hash)
+ local id = internalized[hash]
+ return id, id and fontdata[id]
+end
+
+function definers.register(tfmdata,id)
+ if tfmdata and id then
+ local hash = tfmdata.properties.hash
+ if not hash then
+ report_defining("registering font, id %a, name %a, invalid hash",id,tfmdata.properties.filename or "?")
+ elseif not internalized[hash] then
+ internalized[hash] = id
+ if trace_defining then
+ report_defining("registering font, id %s, hash %a",id,hash)
+ end
+ fontdata[id] = tfmdata
+ end
+ end
+end
+
+function definers.read(specification,size,id) -- id can be optional, name can already be table
+ statistics.starttiming(fonts)
+ if type(specification) == "string" then
+ specification = definers.analyze(specification,size)
+ end
+ local method = specification.method
+ if method and variants[method] then
+ specification = variants[method](specification)
+ end
+ specification = definers.resolve(specification)
+ local hash = constructors.hashinstance(specification)
+ local tfmdata = definers.registered(hash) -- id
+ local name = specification.name
+ if tfmdata then
+ if trace_defining then
+ report_defining("already hashed: %s",hash)
+ end
+ else
+ tfmdata = definers.loadfont(specification) -- can be overloaded
+ if tfmdata then
+ tfmdata.original = specification.specification
+ if trace_defining then
+ report_defining("loaded and hashed: %s",hash)
+ end
+ tfmdata.properties.hash = hash
+ if id then
+ definers.register(tfmdata,id)
+ end
+ else
+ if trace_defining then
+ report_defining("not loaded and hashed: %s",hash)
+ end
+ end
+ end
+ if not tfmdata then -- or id?
+ if not reported[name] then
+ report_defining( "unknown font %a, loading aborted",name)
+ end
+ elseif trace_defining and type(tfmdata) == "table" then
+ local properties = tfmdata.properties or { }
+ local parameters = tfmdata.parameters or { }
+ report_defining("using %a font with id %a, name %a, size %a, fullname %a, filename %a",
+ properties.format or "unknown", id or "-", properties.name, parameters.size,
+ properties.fullname, basename(properties.filename))
+ end
+ statistics.stoptiming(fonts)
+ return tfmdata
+end
+
+function font.getfont(id)
+ return fontdata[id] -- otherwise issues
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-emp.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-emp.mklx
new file mode 100644
index 00000000000..1e30e701264
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-emp.mklx
@@ -0,0 +1,268 @@
+%D \module
+%D [ file=font-emp,
+%D version=20120106, % (moved from font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Emphasis,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Emphasis}
+
+\unprotect
+
+%D \macros
+%D {em,emphasistypeface,emphasisboldface}
+%D
+%D The next macro started as a copy of Donald Arseneau's \type {\em} (\TUGNEWS\
+%D Vol.~3, no.~1, 1994). His implementation was a bit more sophisticated version of
+%D the standard \LATEX\ one. We further enhanced the macro, so now it also adapts
+%D itself to boldface mode. Because we favor {\sl slanted} type over {\it italic},
+%D we made the emphasis adaptable, for instance:
+%D
+%D \starttyping
+%D \def\emphasistypeface {\it}
+%D \def\emphasisboldface {\bi}
+%D \stoptyping
+%D
+%D But we prefer:
+%D
+%D \starttyping
+%D \protected\def\emphasistypeface{\sl}
+%D \protected\def\emphasisboldface{\bs}
+%D \stoptyping
+%D
+%D or even better:
+
+\let\m_font_emphasized_typeface\empty
+
+\def\font_emphasis_checked_typeface#slanted#italic%
+ {\edef\m_font_emphasized_typeface{\font_bodyfontvariable\s!em}%
+ \ifx\m_font_emphasized_typeface\v!slanted
+ #slanted%
+ \orelse\ifx\m_font_emphasized_typeface\v!italic
+ #italic%
+ \orelse\ifempty\m_font_emphasized_typeface
+ #slanted%
+ \else
+ \csname\m_font_emphasized_typeface\endcsname
+ \fi}
+
+\permanent\protected\def\emphasistypeface{\font_emphasis_checked_typeface\sl\it}
+\permanent\protected\def\emphasisboldface{\font_emphasis_checked_typeface\bs\bi}
+
+\permanent\protected\def\normalboldface % public
+ {\relax
+ \ifx\fontalternative\s!it\bi\orelse
+ \ifx\fontalternative\s!sl\bs\else
+ \bf\fi}
+
+\permanent\protected\def\normalitalicface % public
+ {\relax
+ \ifx\fontalternative\s!tf\it\orelse
+ \ifx\fontalternative\s!bf\bi\else
+ \tf\fi}
+
+\permanent\protected\def\normalslantedface % public
+ {\relax \ifx\fontalternative\s!tf\sl
+ \orelse\ifx\fontalternative\s!bf\bs
+ \else \tf\fi}
+
+\permanent\protected\def\normaltypeface % public
+ {\relax
+ \ifx\fontalternative\s!bi \it \orelse
+ \ifx\fontalternative\s!bs \sl \else
+ \tf \fi}
+
+\permanent\protected\def\swaptypeface % public
+ {\relax
+ \ifx\fontalternative\s!it \tf \orelse
+ \ifx\fontalternative\s!sl \tf \orelse
+ \ifx\fontalternative\s!bf \emphasisboldface \orelse
+ \ifx\fontalternative\s!bs \bf \orelse
+ \ifx\fontalternative\s!bi \bf \else
+ \emphasistypeface \fi}
+
+\aliased\let\typeface \normaltypeface % public
+\aliased\let\boldface \normalboldface % public
+\aliased\let\slantedface\normalslantedface % public
+\aliased\let\italicface \normalitalicface % public
+\aliased\let\swapface \swaptypeface % public
+
+%D To be set with the default body font environment: \type {em} being \type
+%D {slanted} or \type {italic}.
+
+% maybe a \csname...\fontalternative\endcsname
+
+\newconditional\c_font_emphasis_needed
+
+\permanent\protected\def\em
+ {\relax
+ \ifdim\slantperpoint>\zeropoint
+ \settrue\c_font_emphasis_needed
+ \else
+ \setfalse\c_font_emphasis_needed
+ \fi
+ \font_emphasis_set_emphasis_boldface % new
+ \ifx\fontalternative\s!it
+ \enforced\permanent\protected\def\emphasistypeface{\it}\tf
+ \orelse\ifx\fontalternative\s!sl
+ \enforced\permanent\protected\def\emphasistypeface{\sl}\tf
+ \orelse\ifx\fontalternative\s!bf
+ \emphasisboldface
+ \orelse\ifx\fontalternative\s!bs
+ \enforced\permanent\protected\def\emphasisboldface{\bs}\bf
+ \orelse\ifx\fontalternative\s!bi
+ \enforced\permanent\protected\def\emphasisboldface{\bi}\bf
+ \else
+ \emphasistypeface
+ \fi
+ \ifconditional\c_font_emphasis_needed\else
+ \expandafter\aftergroup
+ \fi
+ \emphasiscorrection}
+
+%D The next feature was not present in previous versions. It takes care of \type
+%D {\em \bf ...} situations.
+
+\let\font_emphasis_saved_emphasis_boldface\relax
+
+\def\font_emphasis_set_emphasis_boldface
+ {\let\font_emphasis_saved_emphasis_boldface\bf
+ \let\font_emphasis_set_emphasis_boldface\relax
+ \enforced\let\bf\font_emphasis_bold_bold}
+
+\protected\def\font_emphasis_bold_bold
+ {%\relax
+ \enforced\let\bf\relax % new
+ \ifx\fontalternative\s!it
+ \bi
+ \orelse\ifx\fontalternative\s!sl
+ \bs
+ \else
+ \font_emphasis_saved_emphasis_boldface
+ \fi
+ \enforced\let\bf\font_emphasis_saved_emphasis_boldface}
+
+%D Donald's (adapted) macros take the next character into account when placing
+%D italic correction. As a bonus we also look for something that looks like a dash,
+%D in which case we don't correct.
+
+\aliased\let\normalitaliccorrection\/ % the tex primitive
+
+\let\italiccorrection\normalitaliccorrection % will be overloaded
+
+\permanent\protected\def\emphasiscorrection % public, not in raw alignment groups, else omit problem
+ {\ifhmode\ifnum\currentgrouptype=\aligngroupcode\else
+ \expandafter\expandafter\expandafter\font_emphasis_look
+ \fi\fi}
+
+\def\font_emphasis_look
+ {\begingroup
+ \futurelet\nexttoken\font_emphasis_test}
+
+\def\font_emphasis_test
+ {\ifcat\noexpand\nexttoken,% we can do better in luatex ... some day ...
+ \expandafter\font_emphasis_a
+ \else
+ \expandafter\font_emphasis_b
+ \fi}
+
+\def\font_emphasis_a
+ {\futurelet\nexttoken\font_emphasis_correction}
+
+\def\font_emphasis_correction
+ {\setbox\scratchbox\hbox{\nexttoken}% tricky as next can be a macro
+ \ifzeropt\ht\scratchbox % probably a space
+ \expandafter\font_emphasis_b
+ \orelse\ifdim\ht\scratchbox<.3\exheight
+ \expandafter\expandafter\expandafter\endgroup
+ \else
+ \expandafter\expandafter\expandafter\font_emphasis_b
+ \fi}
+
+\def\font_emphasis_b
+ {\scratchskip\lastskip
+ \ifzeropt\scratchskip
+ \italiccorrection\relax
+ \else
+ \unskip\italiccorrection\hskip\scratchskip
+ \fi
+ \endgroup}
+
+%D We end with some examples which show the behavior when some punctuation is met.
+%D We also show how the mechanism adapts itself to bold, italic and slanted typing.
+%D
+%D \startbuffer
+%D test {test}test \par
+%D test {\sl test}test \par
+%D test {\em test}test \par
+%D test {\em test}--test \par
+%D
+%D test {test}, test \par
+%D test {\em test}, test \par
+%D
+%D test {\em test {\em test {\em test} test} test} test \par
+%D test {\bf test {\em test {\em test} test} test} test \par
+%D test {\sl test {\em test {\em test} test} test} test \par
+%D test {\it test {\em test {\em test} test} test} test \par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D We get:
+%D
+%D \startpacked
+%D \getbuffer
+%D \stoppacked
+
+%D \macros
+%D {emphbf,emphit,emphsl,emphtf}
+%D
+%D The next emphasis alternatives are for \THANH. They adapt their style as good as
+%D possible. These macros are obsolete in \MKIV.
+
+% \protected\def\emphbf{\groupedcommand{\bf\def\emphit{\bi}\def\emphsl{\bs}}{}}
+% \protected\def\emphit{\groupedcommand{\it\def\emphbf{\bi}\def\emphsl{\sl}}{}}
+% \protected\def\emphsl{\groupedcommand{\sl\def\emphbf{\bs}\def\emphit{\it}}{}}
+% \protected\def\emphtf{\groupedcommand{\tf\def\emphbf{\bf}\def\emphit{\it}\def\emphsl{\sl}}{}}
+% \protected\def\emph {\groupedcommand{\em}{}}
+
+
+\permanent\protected\def\font_tf{\tf} \permanent\protected\def\font_bf{\bf}
+\permanent\protected\def\font_sl{\sl} \permanent\protected\def\font_bs{\bs}
+\permanent\protected\def\font_it{\it} \permanent\protected\def\font_bi{\bi}
+
+\protected\def\font_emphasis_bf{\bf\enforced\aliased\let\emphit\font_bi
+ \enforced\aliased\let\emphsl\font_bs}
+\protected\def\font_emphasis_it{\it\enforced\aliased\let\emphbf\font_bi
+ \enforced\aliased\let\emphsl\font_sl}
+\protected\def\font_emphasis_sl{\sl\enforced\aliased\let\emphbf\font_bs
+ \enforced\aliased\let\emphit\font_it}
+\protected\def\font_emphasis_tf{\tf\enforced\aliased\let\emphbf\font_bf
+ \enforced\aliased\let\emphit\font_it
+ \enforced\aliased\let\emphsl\font_sl}
+
+\permanent\protected\def\emphbf{\triggergroupedcommandcs\font_emphasis_bf}
+\permanent\protected\def\emphit{\triggergroupedcommandcs\font_emphasis_it}
+\permanent\protected\def\emphsl{\triggergroupedcommandcs\font_emphasis_sl}
+\permanent\protected\def\emphtf{\triggergroupedcommandcs\font_emphasis_tf}
+\permanent\protected\def\emph {\triggergroupedcommandcs\em}
+
+\permanent\protected\def\emphasized{\bgroup\em\let\nexttoken}
+
+%D \startbuffer
+%D TEXT {\emphbf text \emphit text \emphtf text \emphsl text} TEXT
+%D TEXT \emphbf{text \emphit{text} \emphtf{text} \emphsl{text}} TEXT
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-enh.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-enh.lmt
new file mode 100644
index 00000000000..1e532d4adb3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-enh.lmt
@@ -0,0 +1,90 @@
+if not modules then modules = { } end modules ['font-enh'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This module is already stripped from old stuff but more might go away
+-- in lmtx. Stay tuned.
+
+local next = next
+
+local trace_unicoding = false
+
+trackers.register("fonts.defining", function(v) trace_unicoding = v end)
+trackers.register("fonts.unicoding", function(v) trace_unicoding = v end)
+
+local report_unicoding = logs.reporter("fonts","unicoding")
+
+local fonts = fonts
+local constructors = fonts.constructors
+
+local afmfeatures = constructors.features.afm
+local otffeatures = constructors.features.otf
+
+local registerafmfeature = afmfeatures.register
+local registerotffeature = otffeatures.register
+
+local function initialize(tfmdata)
+ local goodies = tfmdata.goodies
+ local newcoding = nil
+ for i=1,#goodies do
+ local remapping = goodies[i].remapping
+ if remapping and remapping.unicodes then
+ newcoding = remapping.unicodes -- names to unicodes
+ end
+ end
+ if newcoding then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local oldcoding = tfmdata.resources.unicodes
+ local originals = { }
+ for name, newcode in next, newcoding do
+ local oldcode = oldcoding[name]
+ if characters[newcode] and not originals[newcode] then
+ originals[newcode] = {
+ character = characters [newcode],
+ description = descriptions[newcode],
+ }
+ end
+ if oldcode then
+ local original = originals[oldcode]
+ local character, description
+ if original then
+ character = original.character
+ description = original.description
+ else
+ character = characters [oldcode]
+ description = descriptions[oldcode]
+ end
+ characters [newcode] = character
+ descriptions[newcode] = description
+ character .unicode = newcode
+ description.unicode = newcode
+ else
+ oldcoding[name] = newcode
+ end
+ if trace_unicoding then
+ if oldcode then
+ report_unicoding("aliasing glyph %a from %U to %U",name,oldcode,newcode)
+ else
+ report_unicoding("aliasing glyph %a to %U",name,newcode)
+ end
+ end
+ end
+ end
+end
+
+local specification = {
+ name = "unicoding",
+ description = "adapt unicode table",
+ initializers = {
+ base = initialize,
+ node = initialize,
+ },
+}
+
+registerotffeature(specification)
+registerafmfeature(specification)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-fbk.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-fbk.lmt
new file mode 100644
index 00000000000..47d284c845c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-fbk.lmt
@@ -0,0 +1,350 @@
+if not modules then modules = { } end modules ['font-fbk'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local cos, tan, rad, format = math.cos, math.tan, math.rad, string.format
+local utfbyte, utfchar = utf.byte, utf.char
+local next = next
+
+--[[ldx--
+<p>This is very experimental code!</p>
+--ldx]]--
+
+local trace_visualize = false trackers.register("fonts.composing.visualize", function(v) trace_visualize = v end)
+local trace_define = false trackers.register("fonts.composing.define", function(v) trace_define = v end)
+
+local report = logs.reporter("fonts","combining")
+
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+local handlers = fonts.handlers
+local constructors = fonts.constructors
+local helpers = fonts.helpers
+
+local otf = handlers.otf
+local afm = handlers.afm
+local registerotffeature = otf.features.register
+local registerafmfeature = afm.features.register
+
+local addotffeature = otf.addfeature
+
+local unicodecharacters = characters.data
+local unicodefallbacks = characters.fallbacks
+
+local vfcommands = helpers.commands
+local charcommand = vfcommands.char
+local rightcommand = vfcommands.right
+local downcommand = vfcommands.down
+local upcommand = vfcommands.up
+local push = vfcommands.push
+local pop = vfcommands.pop
+
+local force_combining = false -- just for demo purposes (see mk)
+local fraction = 0.15 -- 30 units for lucida
+
+-- todo: we also need to update the feature hashes ... i'll do that when i'm in the mood
+-- and/or when i need it
+
+local function composecharacters(tfmdata)
+ -- this assumes that slot 1 is self, there will be a proper self some day
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local parameters = tfmdata.parameters
+ local properties = tfmdata.properties
+ local Xdesc = descriptions[utfbyte("X")]
+ local xdesc = descriptions[utfbyte("x")]
+ if Xdesc and xdesc then
+ local scale = parameters.factor or 1
+ local deltaxheight = scale * (Xdesc.boundingbox[4] - xdesc.boundingbox[4])
+ local extraxheight = fraction * deltaxheight -- maybe use compose value
+ local italicfactor = parameters.italicfactor or 0
+ local red, green, blue, black
+ if trace_visualize then
+ red = { "startcolor", "red" }
+ green = { "startcolor", "green" }
+ blue = { "startcolor", "blue" }
+ black = { "stopcolor" }
+ end
+ local compose = fonts.goodies.getcompositions(tfmdata)
+ if compose and trace_visualize then
+ report("using compose information from goodies file")
+ end
+ local done = false
+ for i, c in next, unicodecharacters do -- loop over all characters ... not that efficient but a specials hash takes memory
+ if force_combining or not characters[i] then
+ local s = c.specials
+ if s and s[1] == 'char' then
+ local chr = s[2]
+ local charschr = characters[chr]
+ if charschr then
+ local cc = c.category
+ if cc == 'll' or cc == 'lu' or cc == 'lt' then -- characters.is_letter[cc]
+ local acc = s[3]
+ local t = { }
+ for k, v in next, charschr do
+ if k ~= "commands" then
+ t[k] = v
+ end
+ end
+ local charsacc = characters[acc]
+ -- local ca = charsacc.category
+ -- if ca == "mn" then
+ -- -- mark nonspacing
+ -- elseif ca == "ms" then
+ -- -- mark spacing combining
+ -- elseif ca == "me" then
+ -- -- mark enclosing
+ -- else
+ if not charsacc then -- fallback accents
+ acc = unicodefallbacks[acc]
+ charsacc = acc and characters[acc]
+ end
+ local chr_t = charcommand[chr]
+ if charsacc then
+ if trace_define then
+ report("composed %C, base %C, accent %C",i,chr,acc)
+ end
+ local acc_t = charcommand[acc]
+ local cb = descriptions[chr].boundingbox
+ local ab = descriptions[acc].boundingbox
+ -- todo: adapt height
+ if cb and ab then
+ local c_llx = scale*cb[1]
+ local c_lly = scale*cb[2]
+ local c_urx = scale*cb[3]
+ local c_ury = scale*cb[4]
+ local a_llx = scale*ab[1]
+ local a_lly = scale*ab[2]
+ local a_urx = scale*ab[3]
+ local a_ury = scale*ab[4]
+ local done = false
+ if compose then
+ local i_compose = compose[i]
+ local i_anchored = i_compose and i_compose.anchored
+ if i_anchored then
+ local c_compose = compose[chr]
+ local a_compose = compose[acc]
+ local c_anchors = c_compose and c_compose.anchors
+ local a_anchors = a_compose and a_compose.anchors
+ if c_anchors and a_anchors then
+ local c_anchor = c_anchors[i_anchored]
+ local a_anchor = a_anchors[i_anchored]
+ if c_anchor and a_anchor then
+ local cx = c_anchor.x or 0
+ local cy = c_anchor.y or 0
+ local ax = a_anchor.x or 0
+ local ay = a_anchor.y or 0
+ local dx = cx - ax
+ local dy = cy - ay
+ if trace_define then
+ report("building %C from %C and %C",i,chr,acc)
+ report(" boundingbox:")
+ report(" chr: %3i %3i %3i %3i",unpack(cb))
+ report(" acc: %3i %3i %3i %3i",unpack(ab))
+ report(" anchors:")
+ report(" chr: %3i %3i",cx,cy)
+ report(" acc: %3i %3i",ax,ay)
+ report(" delta:")
+ report(" %s: %3i %3i",i_anchored,dx,dy)
+ end
+ local right = rightcommand[scale*dx]
+ local down = upcommand[scale*dy]
+ if trace_visualize then
+ t.commands = {
+ push, right, down,
+ green, acc_t, black,
+ pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, down,
+ acc_t, pop, chr_t,
+ }
+ end
+ done = true
+ end
+ end
+ end
+ end
+ if not done then
+ -- can be sped up for scale == 1
+ local dx = (c_urx - a_urx - a_llx + c_llx)/2
+ local dd = (c_urx - c_llx)*italicfactor
+ if a_ury < 0 then
+ local right = rightcommand[dx-dd]
+ if trace_visualize then
+ t.commands = {
+ push, right, red, acc_t,
+ black, pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, acc_t, pop,
+ chr_t,
+ }
+ end
+ t.depth = a_ury
+ elseif c_ury > a_lly then -- messy test
+ local dy
+ if compose then
+ -- experimental: we could use sx but all that testing
+ -- takes time and code
+ dy = compose[i]
+ if dy then
+ dy = dy.dy
+ end
+ if not dy then
+ dy = compose[acc]
+ if dy then
+ dy = dy and dy.dy
+ end
+ end
+ if not dy then
+ dy = compose.dy
+ end
+ if not dy then
+ dy = - deltaxheight + extraxheight
+ elseif dy > -1.5 and dy < 1.5 then
+ -- we assume a fraction of (percentage)
+ dy = - dy * deltaxheight
+ else
+ -- we assume fontunits (value smaller than 2 make no sense)
+ dy = - dy * scale
+ end
+ else
+ dy = - deltaxheight + extraxheight
+ end
+ t.height = a_ury-dy
+ local right = rightcommand[dx+dd]
+ local down = downcommand[dy]
+ if trace_visualize then
+ t.commands = {
+ push, right, down, green,
+ acc_t, black, pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, down, acc_t,
+ pop, chr_t,
+ }
+ end
+ else
+ local right = rightcommand[dx+dd]
+ if trace_visualize then
+ t.commands = {
+ push, right, blue, acc_t,
+ black, pop, chr_t,
+ }
+ else
+ t.commands = {
+ push, right, acc_t, pop,
+ chr_t,
+ }
+ end
+ t.height = a_ury
+ end
+ end
+ else
+ t.commands = {
+ chr_t, -- else index mess
+ }
+ end
+ else
+ if trace_define then
+ report("%C becomes simplified %C",i,chr)
+ end
+ t.commands = {
+ chr_t, -- else index mess
+ }
+ end
+ done = true
+ characters[i] = t
+ local d = { }
+ for k, v in next, descriptions[chr] do
+ d[k] = v
+ end
+ descriptions[i] = d
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local specification = {
+ name = "compose",
+ description = "additional composed characters",
+ manipulators = {
+ base = composecharacters,
+ node = composecharacters,
+ }
+}
+
+registerotffeature(specification)
+registerafmfeature(specification)
+
+addotffeature {
+ name = "char-ligatures",
+ type = "ligature",
+ data = characters.splits.char,
+ order = { "char-ligatures" },
+ prepend = true,
+}
+
+addotffeature {
+ name = "compat-ligatures",
+ type = "ligature",
+ data = characters.splits.compat,
+ order = { "compat-ligatures" },
+ prepend = true,
+}
+
+registerotffeature {
+ name = 'char-ligatures',
+ description = 'unicode char specials to ligatures',
+}
+
+registerotffeature {
+ name = 'compat-ligatures',
+ description = 'unicode compat specials to ligatures',
+}
+
+-- We now provide the composer in the helpers namespace (too):
+
+fonts.helpers.composecharacters = composecharacters
+
+-- We keep this just because we have a few demos but there often are other ways to achieve
+-- the same. This code installs the builder into the regular virtual font builder, which
+-- only makes sense as demo. It also shows a bit what the evolution is.
+
+do
+
+ local vf = handlers.vf
+ local commands = vf.combiner.commands
+
+ vf.helpers.composecharacters = composecharacters
+
+ commands["compose.trace.enable"] = function() trace_visualize = true end
+ commands["compose.trace.disable"] = function() trace_visualize = false end
+ commands["compose.force.enable"] = function() force_combining = true end
+ commands["compose.force.disable"] = function() force_combining = false end
+
+ commands["compose.trace.set"] = function(g,v)
+ if v[2] == nil then
+ trace_visualize = true
+ else
+ trace_visualize = v[2]
+ end
+ end
+
+ commands["compose.apply"] = function(g,v)
+ composecharacters(g)
+ end
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-fea.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-fea.mklx
new file mode 100644
index 00000000000..9f4d2119e99
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-fea.mklx
@@ -0,0 +1,389 @@
+%D \module
+%D [ file=font-sty,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=features,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Features}
+
+\unprotect
+
+%D Feature management.
+%D
+%D \starttyping
+%D \definefontfeature[smallcaps][smcp=yes,script=latn]
+%D \definefontfeature[smallcaps][SmallCapitals=yes,script=latn]
+%D \definefontfeature[smallcaps][Small Capitals=yes,script=latn]
+%D \definefontfeature[smallcaps][small capitals=yes,script=latn]
+%D \definefontfeature[smallcaps][smallcapitals=yes,script=latn]
+%D
+%D \definedfont[cambria*smallcaps] test
+%D
+%D \starttext
+%D \definefontfeature[basekerned][default][mode=base]
+%D \definefontfeature[nodekerned][default][mode=node]
+%D \definefontfeature[nonekerned][default][mode=base,kern=no]
+%D \setupcolors[state=start]
+%D \startoverlay
+%D {\vbox{\red \definedfont[Serif*nonekerned at 12pt]\input tufte }}
+%D {\vbox{\blue \definedfont[Serif*basekerned at 12pt]\input tufte }}
+%D {\vbox{\green\definedfont[Serif*nodekerned at 12pt]\input tufte }}
+%D \stopoverlay
+%D \stoptext
+%D
+%D \enabletrackers[otf.kerns]
+%D
+%D \definefontfeature[withkern][default][mode=node]
+%D \definefontfeature[nokern] [default][mode=node,kern=no]
+%D \definefontfeature[single] [default][mode=node,cpsp=yes]
+%D \definefontfeature[simple] [default][mode=node,cpsp=yes,kern=no]
+%D
+%D {\definedfont[Serif*default] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par}
+%D {\definedfont[Serif*nokern] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par}
+%D {\definedfont[Serif*single] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par}
+%D {\definedfont[Serif*simple] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par}
+%D \stoptyping
+
+% \definefontfeature[slanted] [default][slant=.25]
+% \definefontfeature[stretched][default][stretch=2]
+%
+% \start \definedfont[SerifBold*slanted at 20pt] \ruledhbox{Test!} \stop
+% \start \definedfont[SerifBold*stretched at 20pt] \ruledhbox{Test!} \stop
+%
+% \definefontfeature[default] [liga=yes,texligatures=yes,texquotes=yes]
+% \definefontfeature[default-caps][liga=yes,texligatures=yes,texquotes=yes,smcp=yes,script=latn]
+%
+% \starttypescript [serif] [palatino-nova-regular] [name]
+% \definefontsynonym[Serif] [palatinonova-regular][features=default]
+% \definefontsynonym[SerifCaps][palatinonova-regular][features=default-caps] % also sets Serif
+% \stoptypescript
+%
+% \starttypescript [serif] [palatino-nova-regular] [name]
+% \definefontsynonym[Serif] [palatinonova-regular*default]
+% \definefontsynonym[SerifCaps] [palatinonova-regular*default-caps]
+% \stoptypescript
+%
+% \definetypeface[mainface][rm][serif][palatino-nova-regular][default] \setupbodyfont[mainface]
+%
+% \starttext
+% ``Test'' -- --- ff fi fl \sc ``Test'' -- --- ff fi fl
+% \stoptext
+%
+% \definefontfeature
+% [default-base]
+% [script=latn,language=dflt,liga=yes,kern=yes,tlig=yes,trep=yes]
+% \definefontfeature
+% [default-node]
+% [script=latn,language=dflt,liga=yes,kern=yes,tlig=yes,trep=yes,mode=node]
+% \definefontfeature
+% [default-none]
+% [script=latn,language=dflt,liga=yes,kern=no, tlig=yes,trep=yes]
+%
+% \startoverlay
+% {\vtop{\color[red] {\font\test=name:lmroman12regular*default-node \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[green]{\font\test=name:lmroman12regular*default-base \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[blue] {\font\test=name:lmroman12regular*default-none \test \input ward \input zapf \input linden }}}
+% \stopoverlay
+%
+% \blank
+%
+% \startoverlay
+% {\vtop{\color[red] {\font\test=name:texgyrepagella*default-node \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[green]{\font\test=name:texgyrepagella*default-base \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[blue] {\font\test=name:texgyrepagella*default-none \test \input ward \input zapf \input linden }}}
+% \stopoverlay
+%
+% \blank
+%
+% \startoverlay
+% {\vtop{\color[red] {\font\test=name:palatinonovaregular*default-node \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[green]{\font\test=name:palatinonovaregular*default-base \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[blue] {\font\test=name:palatinonovaregular*default-none \test \input ward \input zapf \input linden }}}
+% \stopoverlay
+%
+% \startoverlay
+% {\vtop{\color[red] {\font\test=name:OfficinaSerifBookITC*default-node \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[green]{\font\test=name:OfficinaSerifBookITC*default-base \test \input ward \input zapf \input linden }}}
+% {\vtop{\color[blue] {\font\test=name:OfficinaSerifBookITC*default-none \test \input ward \input zapf \input linden }}}
+% \stopoverlay
+%
+% \definefontfeature[superdefault][default][compose=yes]
+%
+% {\font\oeps=name:lmroman10regular*default at 30pt \oeps test \char7683}
+% {\font\oeps=name:lmroman10regular*superdefault at 30pt \oeps test \char7683}
+
+\permanent\tolerant\protected\def\definefontfeature[#featureset]#spacer[#parent]#spacer[#settings]%
+ {\clf_definefontfeature{#featureset}{#parent}{#settings}}
+
+\permanent\tolerant\protected\def\adaptfontfeature[#pattern]#spacer[#settings]%
+ {\clf_adaptfontfeature{#pattern}{#settings}}
+
+\permanent\protected\def\fontfeatureslist[#name]#spacer[#separator]% todo: arg voor type
+ {\clf_featurelist{#name}{\detokenize{#separator}}}
+
+\attribute\zerocount\attributeunsetvalue
+
+\glyphdatafield\zerocount
+
+\let\font_dynamic_value\glyphdatafield
+
+% beware: these are global features on top of existing font features
+
+\mutable\let\currentfeature\s!current
+
+\let\m_font_feature_list \s!current
+\let\m_font_feature_asked\empty
+
+\newconstant\c_font_feature_state
+
+\newevery\everyfontfeatureswitch
+
+% hashing at this end is slower
+
+\permanent\tolerant\protected\def\addfeature [#1]#;#2{\edef\m_font_feature_asked{#1#2}\font_feature_add}
+\permanent\tolerant\protected\def\subtractfeature [#1]#;#2{\edef\m_font_feature_asked{#1#2}\font_feature_subtract}
+\permanent\tolerant\protected\def\replacefeature [#1]#;#2{\edef\m_font_feature_asked{#1#2}\font_feature_replace}
+\permanent\tolerant\protected\def\resetandaddfeature[#1]#;#2{\edef\m_font_feature_asked{#1#2}\font_feature_reset_add}
+\permanent\tolerant\protected\def\feature [#1]#;#2{\begincsname\??featureshortcut#1#2\endcsname}
+
+% for old times sake
+
+\permanent\protected\def\doaddfeature #1{\edef\m_font_feature_asked{#1}\font_feature_add}
+\permanent\protected\def\dosubtractfeature #1{\edef\m_font_feature_asked{#1}\font_feature_subtract}
+\permanent\protected\def\doreplacefeature #1{\edef\m_font_feature_asked{#1}\font_feature_replace}
+\permanent\protected\def\doresetandaddfeature#1{\edef\m_font_feature_asked{#1}\font_feature_reset_add}
+
+\protected\def\font_feature_add
+ {\ifnum\c_font_feature_state=\plusone
+ \ifx\m_font_feature_asked\currentfeature\else
+ \font_feature_add_indeed
+ \fi
+ \else
+ \font_feature_add_indeed
+ \fi}
+
+\protected\def\font_feature_add_indeed
+ {\clf_addfeature{\m_font_feature_list}{\m_font_feature_asked}%
+ \edef\m_font_feature_list{\m_font_feature_list+\m_font_feature_asked}% also + at the lua end
+ \c_font_feature_state\plusone
+ \let\currentfeature\m_font_feature_asked
+ \the\everyfontfeatureswitch}
+
+\protected\def\font_feature_subtract
+ {\ifnum\c_font_feature_state=\minusone
+ \ifx\m_font_feature_asked\currentfeature\else
+ \font_feature_subtract_indeed
+ \fi
+ \else
+ \font_feature_subtract_indeed
+ \fi}
+
+\protected\def\font_feature_subtract_indeed
+ {\clf_subtractfeature{\m_font_feature_list}{\m_font_feature_asked}%
+ \edef\m_font_feature_list{\m_font_feature_list-\m_font_feature_asked}% also - at the lua end
+ \c_font_feature_state\minusone
+ \let\currentfeature\m_font_feature_asked
+ \the\everyfontfeatureswitch}
+
+\protected\def\font_feature_replace
+ {\ifnum\c_font_feature_state=\zerocount
+ \ifx\m_font_feature_asked\currentfeature\else
+ \font_feature_replace_indeed
+ \fi
+ \else
+ \font_feature_replace_indeed
+ \fi}
+
+\protected\def\font_feature_replace_indeed
+ {\clf_replacefeature{\m_font_feature_list}{\m_font_feature_asked}%
+ \edef\m_font_feature_list{\m_font_feature_list=\m_font_feature_asked}% also = at the lua end
+ \c_font_feature_state\zerocount
+ \let\currentfeature\m_font_feature_asked
+ \the\everyfontfeatureswitch}
+
+\permanent\protected\def\resetfeature
+ {\ifx\currentfeature\s!current \else
+ \font_feature_reset_indeed
+ \fi}
+
+\protected\def\font_feature_reset_indeed
+ {\let\m_font_feature_asked\empty
+ \let\currentfeature \s!current
+ \let\m_font_feature_list \s!current
+ \clf_resetfeature}
+
+\permanent\protected\def\revivefeature
+ {\ifx\currentfeature\s!current \else
+ \font_feature_revive_indeed
+ \fi}
+
+\protected\def\font_feature_revive_indeed
+ {\clf_revivefeature{\m_font_feature_list}}
+
+\protected\def\font_feature_reset_add
+ {\ifnum\c_font_feature_state=\plusone
+ \ifx\m_font_feature_asked\currentfeature\else
+ \font_feature_reset_add_indeed
+ \fi
+ \else
+ \font_feature_reset_add_indeed
+ \fi}
+
+\protected\def\font_feature_reset_add_indeed
+ {\clf_addfeature{\s!current}{\m_font_feature_asked}%
+ \edef\m_font_feature_list{\s!current+\m_font_feature_asked}% also + at the lua end
+ \c_font_feature_state\plusone
+ \let\currentfeature\m_font_feature_asked
+ \the\everyfontfeatureswitch}
+
+\installcorenamespace{featureshortcut}
+
+\letcsname\??featureshortcut +\endcsname\addfeature
+\letcsname\??featureshortcut -\endcsname\subtractfeature
+\letcsname\??featureshortcut =\endcsname\replacefeature
+\letcsname\??featureshortcut !\endcsname\resetandaddfeature
+\letcsname\??featureshortcut >\endcsname\revivefeature
+\letcsname\??featureshortcut <\endcsname\resetfeature
+
+\letcsname\??featureshortcut\v!more \endcsname\addfeature % add set to previous set and combine with font set
+\letcsname\??featureshortcut\v!less \endcsname\subtractfeature % subtract set from previous set and combine with font set
+\letcsname\??featureshortcut\v!new \endcsname\replacefeature % replace font set
+\letcsname\??featureshortcut\v!reset \endcsname\resetfeature % forget sets and revert to font set
+\letcsname\??featureshortcut\v!default\endcsname\revivefeature % make sure the current set is used on top of the font set
+\letcsname\??featureshortcut\v!old \endcsname\revivefeature
+\letcsname\??featureshortcut\v!local \endcsname\resetandaddfeature
+\letcsname\??featureshortcut\s!unknown\endcsname\empty
+
+% experimental bonus:
+
+% \protected\def\addfflanguage
+% {\ifnum\c_font_feature_state=\plusone
+% \ifx\currentlanguage\currentfeature\else
+% \font_feature_add_language_indeed
+% \fi
+% \else
+% \font_feature_add_language_indeed
+% \fi}
+%
+% \protected\def\font_feature_add_language_indeed
+% {\clf_addfeature{\m_font_feature_list}{\currentlanguage}%
+% \edef\m_font_feature_list{\m_font_feature_list+\currentlanguage}% also + at the lua end
+% \c_font_feature_state\plusone
+% \let\currentfeature\currentlanguage}
+%
+% some 3% slower:
+
+% \protected\def\addfflanguage
+% {\let\m_font_feature_asked\currentlanguage
+% \font_feature_add}
+
+\let\m_font_feature_language\currentlanguage
+
+\permanent\protected\def\addfflanguage
+ {\ifx\currentlanguage\m_font_feature_language\else
+ \let\m_font_feature_language\currentlanguage
+ \let\m_font_feature_asked \currentlanguage
+ \font_feature_add
+ \fi}
+
+% just for old times sake:
+
+\permanent\protected\def\featureattribute#feature%
+ {\clf_featureattribute{#feature}}
+
+\permanent\protected\def\setfontfeature#feature%
+ {\edef\currentfeature{#feature}%
+ \let\m_font_feature_list\currentfeature
+ \clf_setfontfeature{\currentfeature}}
+
+% these are obsolete (don't use them any longer)
+
+\aliased\let\resetfontfeature\resetfeature % obsolete
+
+% these are obsolete (don't use them any longer)
+
+% \let\addfontfeaturetoset \font_feature_add_nop % obsolete
+% \let\subtractfontfeaturefromset \font_feature_subtract_nop % obsolete
+% \let\addfontfeaturetofont \font_feature_add_nop % obsolete
+% \let\subtractfontfeaturefromfont\font_feature_subtract_nop % obsolete
+
+% these are obsolete (don't use them any longer)
+
+\aliased\let\setff\setfontfeature % obsolete
+\aliased\let\addfs\doaddfeature % obsolete
+\aliased\let\subfs\dosubtractfeature % obsolete
+\aliased\let\addff\doaddfeature % obsolete
+\aliased\let\subff\dosubtractfeature % obsolete
+
+%D \macros
+%D {os}
+%D
+%D In good old \TEX, the old style numerals were often taken from the math fonts. No
+%D longer.
+
+\definefontfeature
+ [just-os]
+ [mode=node,onum=yes]
+
+%frozen\protected\def\sc{\setfontfeature{smallcaps}}
+\permanent\protected\def\os{\setfontfeature{just-os}}
+
+% \doifelsecurrentfonthasfeature{smcp}{YES}{NO}
+% \doifelsecurrentfonthasfeature{crap}{YES}{NO}
+% \doifelsecurrentfonthasfeature{kern}{YES}{NO}
+
+\permanent\def\doifelsecurrentfonthasfeature#feature{\clf_doifelsecurrentfonthasfeature{#feature}} % expandable
+\permanent\def\doifelsefontfeature #feature{\clf_doifelsefontfeature {#feature}} % expandable
+\permanent\def\doifunknownfontfeature #feature{\clf_doifunknownfontfeature {#feature}} % expandable
+
+\aliased\let\doifcurrentfonthasfeatureelse\doifelsecurrentfonthasfeature
+\aliased\let\doiffontfeatureelse \doifelsefontfeature
+
+% new:
+
+\clf_registerlanguagefeatures
+
+% also new
+
+\permanent\protected\def\useaddfontfeatureparameter#namespace% faster local variant
+ {\edef\m_font_feature_asked{#namespace\c!features}%
+ \ifempty\m_font_feature_asked\else
+ \font_feature_add
+ \fi}
+
+% let's put this here:
+
+\permanent\protected\def\slashedzero
+ {\dontleavehmode
+ \begingroup
+ %\addff{zero}
+ \doaddfeature{zero}%
+ 0%
+ \endgroup}
+
+% not nice but maybe handy
+
+% \starttyping
+% \blockligatures[fi,ff] \blockligatures[fl]
+%
+% \definefontfeature[default:b][default][blockligatures=yes]
+%
+% \setupbodyfont[pagella] \showfontkerns
+%
+% \definedfont[Serif*default:b]
+%
+% \startTEXpage[offset=1em]
+% fi ff fl
+% \stopTEXpage
+% \stoptyping
+
+\permanent\protected\def\blockligatures[#1]{\clf_blockligatures{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-fil.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-fil.mklx
new file mode 100644
index 00000000000..57ed47fa287
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-fil.mklx
@@ -0,0 +1,384 @@
+%D \module
+%D [ file=font-fil,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Classes and Files,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Classes and Files}
+
+\unprotect
+
+%D Personally I think that using \TEX\ macro packages is complicated by the way
+%D fonts are handled. Apart from the many encodings, we also deal with different
+%D naming schemes. Confronted with this problem, I decided to change the definitions
+%D into:
+%D
+%D \starttyping
+%D \definebodyfont [12pt] [rm] [tf=Times-Roman at 12pt]
+%D \stoptyping
+%D
+%D combined with for instance:
+%D
+%D \starttyping
+%D \definefontsynonym [Times-Roman] [tir]
+%D \stoptyping
+
+% \definetypeface[palatino][rm][serif][palatino,allbold][default]
+%
+% \startfontclass[palatino]
+% \definefontsynonym [Serif] [SerifBold]
+% \definefontsynonym [SerifItalic] [SerifBoldItalic]
+% \definefontsynonym [SerifSlanted] [SerifBoldSlanted]
+% \definefontsynonym [SerifCaps] [SerifBold]
+% \stopfontclass
+%
+% \setupbodyfont[palatino]
+
+\permanent\protected\tolerant\protected\def\startfontclass[#class]%
+ {\push_macro_fontclass
+ \doifelse{#class}\v!each
+ {\let\fontclass\empty}
+ {\doifsomething{#class}{\def\fontclass{#class}}}}
+
+\permanent\protected\def\stopfontclass
+ {\pop_macro_fontclass}
+
+\permanent\def\classfont#class#name{#class#name} % \definefont[whatever][\classfont{xx}{yy} at 10pt]
+
+%D \macros
+%D {definefontsynonym, doifelsefontsynonym,
+%D expandfontsynonym, truefontname, truefontdata}
+%D
+%D While looking for fonts, fontsynonyms are used for accessing the files!
+%D
+%D \starttyping
+%D \definefontsynonym[Serif][Lucida-Bright]
+%D \definefontsynonym[Lucida-Bright][lbr][encoding=texnansi]
+%D \stoptyping
+
+\installcorenamespace {fontfile} % file synonyms
+
+\let\m_font_name\empty
+\let\m_font_file\empty
+
+\mutable\def\defaultfontfile{\truefontname{Normal}} % was cmtt10, but that one is gone
+
+\permanent\protected\def\definefontsynonym[#name]#spacer[#file]%
+ {\edef\m_font_name{#name}%
+ \edef\m_font_file{#file}%
+ \ifempty\fontclass
+ \expandafter\font_basics_define_font_synonym_nop
+ \else
+ \expandafter\font_basics_define_font_synonym_yes
+ \fi}
+
+\protected\def\font_basics_define_font_synonym_nop
+ {\letcsname\??fontfile\m_font_name\endcsname\m_font_file
+ \doifelsenextoptionalcs\font_basics_define_font_synonym_nop_opt\font_basics_define_font_synonym_nop_nil}
+
+\protected\def\font_basics_define_font_synonym_yes
+ {\letcsname\??fontfile\fontclass\m_font_name\endcsname\m_font_file
+ \doifelsenextoptionalcs\font_basics_define_font_synonym_yes_opt\font_basics_define_font_synonym_yes_nil}
+
+\permanent\protected\def\edefinefontsynonym[#name]#spacer[#file]%
+ {\edef\m_font_name{#name}%
+ \edef\m_font_file{#file}%
+ \ifempty\fontclass
+ \expandafter\font_basics_define_font_synonym_nop_expanded
+ \else
+ \expandafter\font_basics_define_font_synonym_yes_expanded
+ \fi}
+
+\protected\def\font_basics_define_font_synonym_nop_expanded#ignore[#spec]%
+ {\letcsname\??fontfile\m_font_name\endcsname\m_font_file
+ \normalexpanded{\font_basics_define_font_synonym_nop_opt[#spec]}}
+
+\protected\def\font_basics_define_font_synonym_yes_expanded#ignore[#spec]%
+ {\letcsname\??fontfile\fontclass\m_font_name\endcsname\m_font_file
+ \normalexpanded{\font_basics_define_font_synonym_yes_opt[#spec]}}
+
+\protected\def\font_basics_define_font_synonym_nop_opt[#specification]%
+ {\let\p_features \undefined
+ \let\p_fallbacks \undefined
+ \let\p_goodies \undefined
+ \let\p_designsize\undefined
+ \expandafter\font_basics_get_font_parameter_nop#specification,\ignorearguments}
+
+\protected\def\font_basics_define_font_synonym_yes_opt[#specification]%
+ {\let\p_features \undefined
+ \let\p_fallbacks \undefined
+ \let\p_goodies \undefined
+ \let\p_designsize\undefined
+ \expandafter\font_basics_get_font_parameter_yes#specification,\ignorearguments}
+
+% todo: check if we can use \edef but then we need to protect \mathsizesuffix .. in fact that
+% can be default then: \let\mathsizesuffix\relax .. i need to play with it first
+
+\def\font_basics_get_font_parameter_nop#key=#value,%
+ {\ifarguments
+ \font_basics_get_font_parameter_nop_finish
+ \else
+ \defcsname p_#key\endcsname{#value}% % no edef as we need to keep \mathsizesuffix
+ \expandafter\font_basics_get_font_parameter_nop
+ \fi}
+
+\def\font_basics_get_font_parameter_yes#key=#value,%
+ {\ifarguments
+ \font_basics_get_font_parameter_yes_finish
+ \else
+ \defcsname p_#key\endcsname{#value}% % no edef as we need to keep \mathsizesuffix
+ \expandafter\font_basics_get_font_parameter_yes
+ \fi}
+
+% helpers, some day these will be toks and counts
+
+% \def\fntsetdefname {\glet\somefontname\defaultfontfile}
+% \def\fntsetsomename{\gdef\somefontname} % takes argument
+% \def\fntsetnopsize {\let\somefontsize\empty}
+% \def\fntsetsomesize{\def\somefontsize} % takes argument
+
+% happens later, else mkvi parsing gets into troubles
+
+% end of helpers
+
+% we could collect them in one macro (but no expansion)
+
+\def\font_basics_define_font_synonym_nop_nil
+ {\letcsname\??fontfile\m_font_name\s!features \endcsname\undefined
+ \letcsname\??fontfile\m_font_name\s!fallbacks \endcsname\undefined
+ \letcsname\??fontfile\m_font_name\s!goodies \endcsname\undefined
+ \letcsname\??fontfile\m_font_name\s!designsize\endcsname\undefined}
+
+\def\font_basics_define_font_synonym_yes_nil
+ {\gletcsname\??fontfile\fontclass\m_font_name\s!features \endcsname\undefined
+ \gletcsname\??fontfile\fontclass\m_font_name\s!fallbacks \endcsname\undefined
+ \gletcsname\??fontfile\fontclass\m_font_name\s!goodies \endcsname\undefined
+ \gletcsname\??fontfile\fontclass\m_font_name\s!designsize\endcsname\undefined}
+
+\def\font_basics_get_font_parameter_nop_finish
+ {\letcsname\??fontfile\m_font_name\s!features \endcsname\p_features
+ \letcsname\??fontfile\m_font_name\s!fallbacks \endcsname\p_fallbacks
+ \letcsname\??fontfile\m_font_name\s!goodies \endcsname\p_goodies
+ \letcsname\??fontfile\m_font_name\s!designsize\endcsname\p_designsize}
+
+\def\font_basics_get_font_parameter_yes_finish
+ {\gletcsname\??fontfile\fontclass\m_font_name\s!features \endcsname\p_features
+ \gletcsname\??fontfile\fontclass\m_font_name\s!fallbacks \endcsname\p_fallbacks
+ \gletcsname\??fontfile\fontclass\m_font_name\s!goodies \endcsname\p_goodies
+ \gletcsname\??fontfile\fontclass\m_font_name\s!designsize\endcsname\p_designsize}
+
+%\definefontsynonym [KopFont] [\fontclassname{officina}{SerifBold}]
+%\defineclassfontsynonym [KopFont] [officina] [SerifBold]
+%\definefontsynonym[#tag][\fontclassname{#class}{#fileortag}]}
+
+\permanent\tolerant\protected\def\defineclassfontsynonym[#tag]#spacer[#class]#spacer[#fileortag]% needs testing
+ {\defcsname\??fontfile\fontclass#tag\endcsname{\fontclassname{#class}{#fileortag}}%
+ \font_basics_define_font_synonym_yes_nil}
+
+\aliased\let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater
+
+\permanent\tolerant\protected\def\setupfontsynonym[#name]#spacer[#settings]% not yet supported, will do when needed
+ {}
+
+% todo: replace * by ... less messy with features
+
+% no \ifempty here as #1 can be empty
+
+\permanent\def\truefontname#name%
+ {\expandafter\font_helpers_true_fontname#name*\empty*\relax}
+
+\def\font_helpers_true_fontname#name*#first#rest*#ignore\relax
+ {\ifcsname\??fontfile\fontclass#name\endcsname
+ \ifx#first\empty
+ \expandafter\truefontname\lastnamedcs
+ \else
+ \expandafter\font_helpers_true_fontname_check\lastnamedcs*#first#rest%
+ \fi
+ \orelse\ifcsname\??fontfile\defaultfontclass#name\endcsname
+ \ifx#first\empty
+ \expandafter\truefontname\lastnamedcs
+ \else
+ \expandafter\font_helpers_true_fontname_check\lastnamedcs*#first#rest%
+ \fi
+ \orelse\ifcsname\??fontfile#name\endcsname
+ \ifx#first\empty
+ \expandafter\truefontname\lastnamedcs
+ \else
+ \expandafter\font_helpers_true_fontname_check\lastnamedcs*#first#rest%
+ \fi
+ \else
+ #name\ifx#first\empty\else*#first#rest\fi
+ \fi}
+
+\def\font_helpers_true_fontname_check#name%
+ {\expandafter\font_helpers_true_fontname_check_indeed#name*\relax}
+
+\def\font_helpers_true_fontname_check_indeed#name*#ignore\relax
+ {\ifcsname\??fontfile\fontclass#name\endcsname
+ \expandafter\font_helpers_true_fontname_check\lastnamedcs
+ \orelse\ifcsname\??fontfile\defaultfontclass#name\endcsname
+ \expandafter\font_helpers_true_fontname_check\lastnamedcs
+ \orelse\ifcsname\??fontfile#name\endcsname
+ \expandafter\font_helpers_true_fontname_check\lastnamedcs
+ \else
+ #name%
+ \fi}
+
+% ok when the last lookup is not stripped .. we need to be able to define synonyms for symbols
+
+\permanent\def\expandfontsynonym#command#name% one level expansion
+ {\ifcsname\??fontfile\fontclass#name\endcsname
+ \expandafter\normaldef\expandafter#command\expandafter{\lastnamedcs}%
+ \orelse\ifcsname\??fontfile\defaultfontclass#2\endcsname
+ \expandafter\normaldef\expandafter#command\expandafter{\lastnamedcs}%
+ \fi}
+
+\permanent\def\doifelsefontsynonym#name%
+ {\ifcsname\??fontfile\fontclass#name\endcsname
+ \expandafter\firstoftwoarguments
+ \orelse\ifcsname\??fontfile\defaultfontclass#name\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiffontsynonymelse\doifelsefontsynonym
+
+%D \macros
+%D {tracedfontname}
+%D
+%D A goody:
+
+\permanent\def\tracedfontname#name%
+ {#name\ifcsname\??fontfile\fontclass#name\endcsname
+ %\expandafter\tracedfontname\csname\??fontfile\fontclass#name\endcsname
+ \expandafter\tracedfontname\lastnamedcs
+ \orelse\ifcsname\??fontfile#name\endcsname
+ %\expandafter\tracedfontname\csname\??fontfile#name\endcsname
+ \expandafter\tracedfontname\lastnamedcs
+ \fi}
+
+%D \macros
+%D {fontclass, defaultfontclass}
+%D
+%D The fontclass model was introduced a while after we implement the basic font
+%D model and at that time we still defaulted to no model at all. Nowadays we default
+%D to the \type {modern} fontclass.
+
+\let\fontclass \empty
+\let\defaultfontclass\empty
+
+\permanent\def\fontclassname#class#name% tricky ... no lastnamedcs here due to nesting
+ {\ifcsname\??fontfile#class#name\endcsname
+ \fontclassname{#class}{\csname\??fontfile#class#name\endcsname}%
+ \orelse\ifcsname\??fontfile#name\endcsname
+ \fontclassname{#class}{\csname\??fontfile#name\endcsname}%
+ \else
+ #2%
+ \fi}
+
+\installmacrostack\fontclass
+
+%D Files or names can have properties and these need to be consulted at some point.
+%D They can inherit them.
+
+% Why these expanded and rscale not ... maybe not worth the trouble (delayed
+% expansion actually would be better i.e. macros in feature specs). Test without
+% pre-expansion.
+
+\def\font_helpers_update_font_class_parameters
+ {\edef\m_font_class_direction {\begincsname\??fontclass\fontclass\fontstyle\s!direction \endcsname}%
+ \edef\m_font_class_features {\begincsname\??fontclass\fontclass\fontstyle\s!features \endcsname}%
+ \edef\m_font_class_fallbacks {\begincsname\??fontclass\fontclass\fontstyle\s!fallbacks \endcsname}%
+ \edef\m_font_class_goodies {\begincsname\??fontclass\fontclass\fontstyle\s!goodies \endcsname}%
+ \edef\m_font_class_designsize{\begincsname\??fontclass\fontclass\fontstyle\s!designsize\endcsname}}
+
+% resolve
+
+\def\font_helpers_set_features_yes#name%
+ {\ifcsname\??fontfile\fontclass#name\s!features\endcsname \edef\m_font_features{\lastnamedcs}\orelse % class + symbolic_name
+ \ifcsname\??fontfile #name\s!features\endcsname \edef\m_font_features{\lastnamedcs}\orelse % symbolic_name
+ \ifcsname\??fontfile\fontclass#name\endcsname \expandafter\font_helpers_set_features_yes\lastnamedcs \orelse % class + parent_name
+ \ifcsname\??fontfile #name\endcsname \expandafter\font_helpers_set_features_yes\lastnamedcs \else % parent_name
+ \let\m_font_features\empty\fi}
+
+\def\font_helpers_set_fallbacks_yes#name%
+ {\ifcsname\??fontfile\fontclass#name\s!fallbacks\endcsname \edef\m_font_fallbacks{\lastnamedcs}\orelse
+ \ifcsname\??fontfile #name\s!fallbacks\endcsname \edef\m_font_fallbacks{\lastnamedcs}\orelse
+ \ifcsname\??fontfile\fontclass#name\endcsname \expandafter\font_helpers_set_fallbacks_yes\lastnamedcs \orelse
+ \ifcsname\??fontfile #name\endcsname \expandafter\font_helpers_set_fallbacks_yes\lastnamedcs \else
+ \let\m_font_fallbacks\empty\fi}
+
+\def\font_helpers_set_goodies_yes#name%
+ {\ifcsname\??fontfile\fontclass#name\s!goodies \endcsname \edef\m_font_goodies{\lastnamedcs}\orelse
+ \ifcsname\??fontfile #name\s!goodies \endcsname \edef\m_font_goodies{\lastnamedcs}\orelse
+ \ifcsname\??fontfile\fontclass#name\endcsname \expandafter\font_helpers_set_goodies_yes\lastnamedcs \orelse
+ \ifcsname\??fontfile #name\endcsname \expandafter\font_helpers_set_goodies_yes\lastnamedcs \else
+ \let\m_font_goodies\empty\fi}
+
+\def\font_helpers_set_designsize_yes#name%
+ {\ifcsname\??fontfile\fontclass#name\s!designsize\endcsname \edef\m_font_designsize{\lastnamedcs}\orelse
+ \ifcsname\??fontfile #name\s!designsize\endcsname \edef\m_font_designsize{\lastnamedcs}\orelse
+ \ifcsname\??fontfile\fontclass#name\endcsname \expandafter\font_helpers_set_designsize_yes\lastnamedcs \orelse
+ \ifcsname\??fontfile #name\endcsname \expandafter\font_helpers_set_designsize_yes\lastnamedcs \else
+ \let\m_font_designsize\empty\fi}
+
+\def\font_helpers_set_features_nop#name%
+ {\ifcsname\??fontfile#name\s!features\endcsname \edef\m_font_features{\lastnamedcs}\orelse
+ \ifcsname\??fontfile#name\endcsname \expandafter\font_helpers_set_features_nop\lastnamedcs \else
+ \let\m_font_features\empty\fi}
+
+\def\font_helpers_set_fallbacks_nop#name%
+ {\ifcsname\??fontfile#name\s!fallbacks\endcsname \edef\m_font_fallbacks{\lastnamedcs}\orelse
+ \ifcsname\??fontfile#name\endcsname \expandafter\font_helpers_set_fallbacks_nop\lastnamedcs \else
+ \let\m_font_fallbacks\empty\fi}
+
+\def\font_helpers_set_goodies_nop#name%
+ {\ifcsname\??fontfile#name\s!goodies\endcsname \edef\m_font_goodies{\lastnamedcs}\orelse
+ \ifcsname\??fontfile#name\endcsname \expandafter\font_helpers_set_goodies_nop\lastnamedcs \else
+ \let\m_font_goodies\empty\fi}
+
+\def\font_helpers_set_designsize_nop#name%
+ {\ifcsname\??fontfile#name\s!designsize\endcsname \edef\m_font_designsize{\lastnamedcs}\orelse
+ \ifcsname\??fontfile#name\endcsname \expandafter\font_helpers_set_designsize_nop\lastnamedcs \else
+ \let\m_font_designsize\empty\fi}
+
+\def\font_helpers_update_font_parameters_yes
+ {\font_helpers_set_features_yes \somefontname
+ \font_helpers_set_fallbacks_yes \somefontname
+ \font_helpers_set_goodies_yes \somefontname
+ \font_helpers_set_designsize_yes\somefontname}
+
+\def\font_helpers_update_font_parameters_nop
+ {\font_helpers_set_features_nop \somefontname
+ \font_helpers_set_fallbacks_nop \somefontname
+ \font_helpers_set_goodies_nop \somefontname
+ \font_helpers_set_designsize_nop\somefontname}
+
+\def\font_helpers_update_font_parameters
+ {\ifempty\fontclass\font_helpers_update_font_parameters_nop\else\font_helpers_update_font_parameters_yes\fi}
+
+\installcorenamespace{fontclass}
+
+% we can pack them (don't use \setxvalue!)
+
+\permanent\protected\def\savefontclassparameters#style#rscale#features#fallbacks#goodies#designsize#direction%
+ {\gletcsname\??fontclass\fontclass#style\s!rscale \endcsname#rscale%
+ \gletcsname\??fontclass\fontclass#style\s!features \endcsname#features%
+ \gletcsname\??fontclass\fontclass#style\s!fallbacks \endcsname#fallbacks%
+ \gletcsname\??fontclass\fontclass#style\s!goodies \endcsname#goodies%
+ \gletcsname\??fontclass\fontclass#style\s!designsize\endcsname#designsize%
+ \gletcsname\??fontclass\fontclass#style\s!direction \endcsname#direction}% math
+
+% bonus
+
+% \currentfontinstancespec % define ad the lua end
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-fmp.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-fmp.lmt
new file mode 100644
index 00000000000..f35c96f49ed
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-fmp.lmt
@@ -0,0 +1,123 @@
+if not modules then modules = { } end modules ['font-fmp'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We only need to pick up the filename and optionally the enc file as we only use
+-- them for old school virtual math fonts. We might as well drop this completely.
+-- This used to be a backend module but the code is rather generic so we just put it
+-- here now.
+
+local find, match, splitlines = string.find, string.match, string.splitlines
+
+local implement = interfaces.implement
+
+local mappings = { }
+
+local function setline(n)
+ if trace_fonts then
+ report_fonts("mapline: %s",n)
+ end
+ local name, fullname, encfile, pfbfile = match(n,"(%S+)%s+(%S+).-<(.-%.enc).-<(.-%.pfb)")
+ if name then
+ mappings[name] = { fullname, encfile, pfbfile }
+ end
+end
+
+local function setfile(n)
+ local okay, data = resolvers.loadbinfile(n,"map")
+ if okay and data then
+ data = splitlines(data)
+ for i=1,#data do
+ local d = data[i]
+ if d ~= "" and not find(d,"^[#%%]") then
+ setline(d)
+ end
+ end
+ end
+end
+
+local function getentry(n)
+ local n = file.nameonly(n)
+ local m = mappings[n]
+ if m then
+ local encfile = m[2]
+ local encoding = fonts.encodings.load(encfile)
+ if not encoding then
+ return
+ end
+ local pfbfile = resolvers.findfile(m[3],"pfb")
+ if not pfbfile or pfbfile == "" then
+ return
+ end
+ return encoding, pfbfile, encfile
+ end
+end
+
+-- soon to be obsolete:
+
+local mappings = fonts.mappings or { }
+fonts.mappings = mappings
+
+local loaded = { -- prevent loading (happens in cont-sys files)
+ -- ["original-base.map" ] = true,
+ -- ["original-ams-base.map" ] = true,
+ -- ["original-ams-euler.map"] = true,
+ -- ["original-public-lm.map"] = true,
+}
+
+function mappings.loadfile(name)
+ name = file.addsuffix(name,"map")
+ if not loaded[name] then
+ if trace_mapfiles then
+ report_mapfiles("loading map file %a",name)
+ end
+ setfile(name)
+ loaded[name] = true
+ end
+end
+
+local loaded = { -- prevent double loading
+}
+
+function mappings.loadline(how,line)
+ if line then
+ how = how .. " " .. line
+ elseif how == "" then
+ how = "= " .. line
+ end
+ if not loaded[how] then
+ if trace_mapfiles then
+ report_mapfiles("processing map line %a",line)
+ end
+ setline(how)
+ loaded[how] = true
+ end
+end
+
+function mappings.reset()
+ lpdf.setmapfile("") -- tricky ... backend related
+end
+
+mappings.getentry = getentry
+
+implement {
+ name = "loadmapfile",
+ actions = mappings.loadfile,
+ arguments = "string"
+}
+
+implement {
+ name = "loadmapline",
+ actions = mappings.loadline,
+ arguments = "string"
+}
+
+implement {
+ name = "resetmapfiles",
+ actions = mappings.reset,
+ arguments = "string"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-gds.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-gds.mklx
new file mode 100644
index 00000000000..897478273ff
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-gds.mklx
@@ -0,0 +1,97 @@
+%D \module
+%D [ file=font-gds,
+%D version=2009.10.14,
+%D title=\CONTEXT\ Font Support,
+%D subtitle=Colorschemes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Support / Colorschemes}
+
+%registerctxluafile{font-gds}{}
+
+\unprotect
+
+% this will become colorgroups and move to font-col or so
+
+\definecolor[colorscheme:1:1][s=.75]
+\definecolor[colorscheme:1:2][r=.75]
+\definecolor[colorscheme:1:3][g=.75]
+\definecolor[colorscheme:1:4][b=.75]
+\definecolor[colorscheme:1:5][c=.75]
+\definecolor[colorscheme:1:6][m=.75]
+\definecolor[colorscheme:1:7][y=.75]
+
+\definecolor[colorscheme:2:7][s=.75]
+\definecolor[colorscheme:2:6][r=.75]
+\definecolor[colorscheme:2:5][g=.75]
+\definecolor[colorscheme:2:4][b=.75]
+\definecolor[colorscheme:2:3][c=.75]
+\definecolor[colorscheme:2:2][m=.75]
+\definecolor[colorscheme:2:1][y=.75]
+
+\definecolor[colorscheme:3:1][r=.75]
+\definecolor[colorscheme:3:2][g=.75]
+\definecolor[colorscheme:3:3][b=.75]
+\definecolor[colorscheme:3:4][g=.75,b=.75]
+\definecolor[colorscheme:3:5][r=.75,b=.75]
+\definecolor[colorscheme:3:6][r=.75,g=.75]
+\definecolor[colorscheme:3:7][g=.75]
+
+\definecolor[colorscheme:4:1] [r=.6] % a
+\definecolor[colorscheme:4:5] [g=.6] % e
+\definecolor[colorscheme:4:9] [b=.6] % i
+\definecolor[colorscheme:4:15][g=.6,b=.6] % o
+\definecolor[colorscheme:4:21][r=.6,b=.6] % u
+\definecolor[colorscheme:4:25][r=.6,b=.6] % y
+
+\definesystemattribute[colorscheme][public]
+
+\permanent\protected\def\loadfontgoodies[#filename]{\clf_loadfontgoodies{#filename}} % todo: public implementor
+
+\permanent\protected\def\setfontcolorscheme % will move to the lua end
+ {\clf_enablefontcolorschemes
+ \enforced\permanent\protected\xdef\setfontcolorscheme[##1]{\c_attr_colorscheme##1\relax}%
+ \setfontcolorscheme}
+
+\permanent\protected\def\resetfontcolorscheme
+ {\c_attr_colorscheme\attributeunsetvalue}
+
+\protect \endinput
+
+% \definefontfeature[husayni-colored][goodies=husayni,colorscheme=default,featureset=default]
+%
+% \definedfont[husayni*husayni-colored at 36pt]
+%
+% \starttext \righttoleft
+%
+% \setfontcolorscheme[1]
+%
+% اَلْحَمْ‍دُ لِلّٰهِ حَمْدَ مُعْتَرِفٍ بِحَمْدِهٖ، مُغْتَرِفٌ مِنْ بِحَارِ
+% مَجْدِهٖ، بِلِسَانِ ٱلثَّنَاۤءِ شَاكِرًا، وَلِحُسْنِ اٰلاۤئِهٖ نَاشِرًا؛
+% اَلَّذِيْ خَلَقَ ٱلْمَوْتَ وَٱلْحَيٰوةَ، وَٱلْخَيْرَ وَٱلشَّرَّ،
+% وَٱلنَّفْعَ وَٱلضَّرَّ، وَٱلسُّكُوْنَ وَٱلْحَرَكَةَ، وَٱلْأَرْوَاحَ
+% وَٱلْأَجْسَامَ، وَٱلذِّكْرَ وَٱلنِّسْيَانَ.
+%
+% \setfontcolorscheme[2]
+%
+% اَلْحَمْ‍دُ لِلّٰهِ حَمْدَ مُعْتَرِفٍ بِحَمْدِهٖ، مُغْتَرِفٌ مِنْ بِحَارِ
+% مَجْدِهٖ، بِلِسَانِ ٱلثَّنَاۤءِ شَاكِرًا، وَلِحُسْنِ اٰلاۤئِهٖ نَاشِرًا؛
+% اَلَّذِيْ خَلَقَ ٱلْمَوْتَ وَٱلْحَيٰوةَ، وَٱلْخَيْرَ وَٱلشَّرَّ،
+% وَٱلنَّفْعَ وَٱلضَّرَّ، وَٱلسُّكُوْنَ وَٱلْحَرَكَةَ، وَٱلْأَرْوَاحَ
+% وَٱلْأَجْسَامَ، وَٱلذِّكْرَ وَٱلنِّسْيَانَ.
+%
+% \resetfontcolorscheme
+%
+% اَلْحَمْ‍دُ لِلّٰهِ حَمْدَ مُعْتَرِفٍ بِحَمْدِهٖ، مُغْتَرِفٌ مِنْ بِحَارِ
+% مَجْدِهٖ، بِلِسَانِ ٱلثَّنَاۤءِ شَاكِرًا، وَلِحُسْنِ اٰلاۤئِهٖ نَاشِرًا؛
+% اَلَّذِيْ خَلَقَ ٱلْمَوْتَ وَٱلْحَيٰوةَ، وَٱلْخَيْرَ وَٱلشَّرَّ،
+% وَٱلنَّفْعَ وَٱلضَّرَّ، وَٱلسُّكُوْنَ وَٱلْحَرَكَةَ، وَٱلْأَرْوَاحَ
+% وَٱلْأَجْسَامَ، وَٱلذِّكْرَ وَٱلنِّسْيَانَ.
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-glf.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-glf.mklx
new file mode 100644
index 00000000000..2c23f226eed
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-glf.mklx
@@ -0,0 +1,41 @@
+%D \module
+%D [ file=font-glf,
+%D version=2020.12.21,
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Glyph Scaling,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Glyph Scaling}
+
+\unprotect
+
+%D Experiment:
+
+\installcorenamespace{tweakedfont}
+
+\installcommandhandler \??tweakedfont {tweakedfont} \??tweakedfont
+
+\appendtoks
+ % we can optimize for zero and 1000 .. maybe also options?
+ \overloaded\frozen\protected\edefcsname\currenttweakedfont\endcsname
+ {\begincsname\tweakedfontparameter\c!style\endcsname
+ \glyphxscale \numexpr\numericscale\tweakedfontparameter\c!xscale*\glyphxscale/\plusthousand\relax
+ \glyphyscale \numexpr\numericscale\tweakedfontparameter\c!yscale*\glyphyscale/\plusthousand\relax
+ \glyphxoffset\dimexpr\glyphxoffset+\tweakedfontparameter\c!xoffset\relax
+ \glyphyoffset\dimexpr\glyphyoffset+\tweakedfontparameter\c!yoffset\relax}%
+\to \everydefinetweakedfont
+
+\setuptweakedfont
+ [\c!scale=\plusthousand,
+ \c!xscale=\tweakedfontparameter\c!scale,
+ \c!yscale=\tweakedfontparameter\c!scale,
+ \c!xoffset=\zeropoint,
+ \c!yoffset=\zeropoint]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-imp-math.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-imp-math.lmt
new file mode 100644
index 00000000000..277190f2410
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-imp-math.lmt
@@ -0,0 +1,226 @@
+if not modules then modules = { } end modules ['font-imp-math'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv and hand-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, type, tonumber = next, type, tonumber
+
+local fonts = fonts
+local helpers = fonts.helpers
+local registerotffeature = fonts.handlers.otf.features.register
+
+local setmetatableindex = table.setmetatableindex
+
+local texconditionals = tex.conditionals
+
+-- tfmdata.properties.mathnolimitsmode = tonumber(value) or 0
+
+local splitter = lpeg.splitat(",",tonumber)
+local lpegmatch = lpeg.match
+
+local function initialize(tfmdata,value)
+ local mathparameters = tfmdata.mathparameters
+ if mathparameters then
+ local sup, sub
+ if type(value) == "string" then
+ sup, sub = lpegmatch(splitter,value)
+ if not sup then
+ sub, sup = 0, 0
+ elseif not sub then
+ sub, sup = sup, 0
+ end
+ elseif type(value) == "number" then
+ sup, sub = 0, value
+ end
+ if sup then
+ mathparameters.NoLimitSupFactor = sup
+ end
+ if sub then
+ mathparameters.NoLimitSubFactor = sub
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathnolimitsmode",
+ description = "influence nolimits placement",
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+-- this will become a mode in the engine
+
+local function initialize(tfmdata,value)
+ tfmdata.properties.nostackmath = value and true
+end
+
+registerotffeature {
+ name = "nostackmath",
+ description = "disable math stacking mechanism",
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+-- A quick and dirty and low level implementation but okay for testing:
+
+function fonts.helpers.mathscriptslots(tfmdata,textcode)
+ local rawdata = tfmdata.shared.rawdata
+ local rawresources = rawdata and rawdata.resources
+ local rawfeatures = rawresources and rawresources.features
+ local basesubstitutions = rawfeatures and rawfeatures.gsub
+ local sequences = basesubstitutions and tfmdata.resources.sequences
+ if sequences then
+ local characters = tfmdata.characters
+ if characters[textcode] then
+ for s=1,#sequences do
+ local sequence = sequences[s]
+ local sfeatures = sequence.features
+ if sfeatures and sfeatures.ssty then
+ local steps = sequence.steps
+ for i=1,#steps do
+ local coverage = steps[i].coverage
+ if coverage then
+ local okay = coverage[textcode]
+ if okay then
+ return okay
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function manipulate(tfmdata,key,value)
+ if texconditionals["c_font_compact"] then
+ local rawdata = tfmdata.shared.rawdata
+ local rawresources = rawdata and rawdata.resources
+ local rawfeatures = rawresources and rawresources.features
+ local basesubstitutions = rawfeatures and rawfeatures.gsub
+ local sequences = basesubstitutions and tfmdata.resources.sequences
+
+ if sequences then
+ local characters = tfmdata.characters
+ for s=1,#sequences do
+ local sequence = sequences[s]
+ local sfeatures = sequence.features
+ if sfeatures and sfeatures.ssty then
+ local steps = sequence.steps
+ for i=1,#steps do
+ local coverage = steps[i].coverage
+ if coverage then
+ for textcode, v in next, coverage do
+ local textdata = characters[textcode]
+ if textdata then
+ local scriptcode = v[1]
+ local scriptscriptcode = v[2]
+ local scriptdata = characters[scriptcode]
+ local scriptscriptdata = characters[scriptscriptcode]
+ if scriptdata then
+ textdata.smaller = scriptcode
+ end
+ if scriptscriptdata then
+ scriptdata.smaller = scriptscriptcode
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function initialize(tfmdata,key,value)
+ if texconditionals["c_font_compact"] then
+ local rawdata = tfmdata.shared.rawdata
+ local rawresources = rawdata and rawdata.resources
+ local mathconstants = rawresources.mathconstants
+ if mathconstants then
+ local parameters = tfmdata.parameters
+ parameters.textscale = 1000
+ parameters.scriptscale = mathconstants.ScriptPercentScaleDown * 10
+ parameters.scriptscriptscale = mathconstants.ScriptScriptPercentScaleDown * 10
+ end
+ end
+end
+
+local specification = {
+ name = "compactmath",
+ description = "use one math font",
+ manipulators = {
+ base = manipulate,
+ node = manipulate,
+ },
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+registerotffeature(specification)
+
+-- The problem is that the traditional code path doesn't add an italic to the subscript,
+-- simply because it assumes that the width has that already subtracted. So, we cannot
+-- compensate in the following way. We're stuck with the fact that the texgyre fonts
+-- assume a traditional tex engine.
+
+-- local function manipulate(tfmdata,key,value)
+-- if value == "correct" then
+-- local chardata = characters.data
+-- for unicode, data in next, tfmdata.characters do
+-- local italic = data.italic
+-- if italic then
+-- -- todo: only letters
+-- data.width = (data.width or 0) + italic
+-- local cd = chardata[unicode]
+-- if cd then
+-- local visual = cd.visual
+-- if visual == "it" or visual == "bi" then
+-- italic = -italic
+-- else
+-- italic = 0
+-- end
+-- else
+-- italic = 0
+-- end
+-- data.italic = italic
+-- end
+-- end
+-- end
+-- end
+
+local function initialize(tfmdata,key,value)
+ if value then
+ local rawdata = tfmdata.shared.rawdata
+ local rawresources = rawdata and rawdata.resources
+ local mathconstants = rawresources.mathconstants
+ if mathconstants then
+ tfmdata.properties.oldmath = true
+ end
+ end
+end
+
+local specification = {
+ name = "oldmath",
+ description = "deal with fake opentype fonts",
+ -- manipulators = {
+ -- base = manipulate,
+ -- node = manipulate,
+ -- },
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+registerotffeature(specification)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-imp-tracing.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-imp-tracing.lmt
new file mode 100644
index 00000000000..59603c9ddd6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-imp-tracing.lmt
@@ -0,0 +1,286 @@
+if not modules then modules = { } end modules ['font-imp-tracing'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv and hand-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local abs = math.abs
+
+local next, type = next, type
+local concat = table.concat
+local formatters = string.formatters
+
+local fonts = fonts
+
+local handlers = fonts.handlers
+local registerotffeature = handlers.otf.features.register
+local registerafmfeature = handlers.afm.features.register
+
+local settings_to_array = utilities.parsers.settings_to_array
+local setmetatableindex = table.setmetatableindex
+
+local helpers = fonts.helpers
+local appendcommandtable = helpers.appendcommandtable
+local prependcommands = helpers.prependcommands
+local charcommand = helpers.commands.char
+
+local variables = interfaces.variables
+
+local v_background = variables.background
+local v_frame = variables.frame
+local v_empty = variables.empty
+local v_none = variables.none
+
+local function initialize(tfmdata,key,value)
+ if value then
+ local vfspecials = fonts.helpers.vfspecials
+ local vfcommands = fonts.helpers.commands
+ local backgrounds = vfspecials.backgrounds
+ local outlines = vfspecials.outlines
+ local characters = tfmdata.characters
+ local rulecache = backgrounds
+ local showchar = true
+ local color = "palegray"
+ if type(value) == "string" then
+ value = settings_to_array(value)
+ for i=1,#value do
+ local v = value[i]
+ if v == v_frame then
+ rulecache = outlines
+ elseif v == v_background then
+ rulecache = backgrounds
+ elseif v == v_empty then
+ showchar = false
+ elseif v == v_none then
+ color = nil
+ else
+ color = v
+ end
+ end
+ end
+ local gray = color and { "startcolor", color } or nil
+ local black = gray and { "stopcolor" } or nil
+ for unicode, character in next, characters do
+ local width = character.width or 0
+ local height = character.height or 0
+ local depth = character.depth or 0
+ local rule = rulecache[height][depth][width]
+ if showchar then
+ local commands = character.commands
+ if commands then
+ if gray then
+ character.commands = prependcommands (
+ commands, gray, rule, black
+ )
+ else
+ character.commands = prependcommands (
+ commands, rule
+ )
+ end
+ else
+ local char = charcommand[unicode]
+ if gray then
+ character.commands = {
+ gray, rule, black, char
+ }
+ else
+ character.commands = {
+ rule, char
+ }
+ end
+ end
+ else
+ if gray then
+ character.commands = {
+ gray, rule, black
+ }
+ else
+ character.commands = {
+ rule
+ }
+ end
+ end
+ end
+ end
+end
+
+local specification = {
+ name = "boundingbox",
+ description = "show boundingbox",
+ manipulators = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+registerotffeature(specification)
+registerafmfeature(specification)
+
+local function initialize(tfmdata,key,value)
+ if value then
+ local rawdata = tfmdata.shared.rawdata
+ local rawresources = rawdata and rawdata.resources
+ local mathconstants = rawresources.mathconstants
+ if mathconstants then
+ local vfspecials = fonts.helpers.vfspecials
+ local vfcommands = fonts.helpers.commands
+ local backgrounds = vfspecials.backgrounds
+ local outlines = vfspecials.outlines
+ local characters = tfmdata.characters
+ local rulecache = backgrounds
+ local push = vfcommands.push
+ local pop = vfcommands.pop
+ local black = { "stopcolor" }
+ local itcolor = { "startcolor", "trace:0" } -- s / dd (transparent)
+ local brcolor = { "startcolor", "trace:1" } -- r / dr
+ local trcolor = { "startcolor", "trace:2" } -- g / dg
+ local blcolor = { "startcolor", "trace:3" } -- b / db
+ local tlcolor = { "startcolor", "trace:4" } -- y / dy
+ for unicode, character in next, characters do
+ local mathkern = character.mathkern
+ local italic = character.vert_italic or character.italic
+ if mathkern or (italic and italic ~= 0) then
+ local width = character.width or 0
+ local height = character.height or 0
+ local depth = character.depth or 0
+ local list = { }
+ local count = 0
+ if italic and italic ~= 0 then
+ count = count + 1 list[count] = itcolor
+ count = count + 1 list[count] = push
+ count = count + 1 list[count] = { "offset", width + (italic < 0 and -italic or 0), 0 }
+ count = count + 1 list[count] = rulecache[height][depth][abs(-italic)]
+ count = count + 1 list[count] = pop
+ count = count + 1 list[count] = black
+ end
+ if mathkern then
+ local br = mathkern.bottom_right
+ local tr = mathkern.top_right
+ local bl = mathkern.bottom_left
+ local tl = mathkern.top_left
+ if br then
+ local done = false
+ for i=1,#br do
+ local l = br[i]
+ local h = l.height or 0
+ local k = l.kern or 0
+ if k ~= 0 then
+ if h == 0 then
+ h = height + depth -- todo max
+ end
+ if not done then
+ count = count + 1 list[count] = brcolor
+ done = true
+ end
+ count = count + 1 list[count] = push
+ count = count + 1 list[count] = { "offset", width + (k < 0 and k or 0), - depth }
+ count = count + 1 list[count] = rulecache[h][0][abs(k)]
+ count = count + 1 list[count] = pop
+ end
+ end
+ if done then
+ count = count + 1 list[count] = black
+ end
+ end
+ if tr then
+ local done = false
+ for i=1,#tr do
+ local l = tr[i]
+ local h = l.height or 0
+ local k = l.kern or 0
+ if k ~= 0 then
+ if h == 0 then
+ h = height + depth -- todo max
+ end
+ if not done then
+ count = count + 1 list[count] = trcolor
+ done = true
+ end
+ count = count + 1 list[count] = push
+ count = count + 1 list[count] = { "offset", width + (k < 0 and k or 0), height - h }
+ count = count + 1 list[count] = rulecache[h][0][abs(k)]
+ count = count + 1 list[count] = pop
+ end
+ end
+ if done then
+ count = count + 1 list[count] = black
+ end
+ end
+ if bl then
+ local done = false
+ for i=1,#bl do
+ local l = bl[i]
+ local h = l.height or 0
+ local k = l.kern or 0
+ if k ~= 0 then
+ if h == 0 then
+ h = height + depth -- todo max
+ end
+ if not done then
+ count = count + 1 list[count] = blcolor
+ done = true
+ end
+ count = count + 1 list[count] = push
+ count = count + 1 list[count] = { "offset", (k < 0 and k or 0), -depth }
+ count = count + 1 list[count] = rulecache[h][0][abs(k)]
+ count = count + 1 list[count] = pop
+ end
+ end
+ if done then
+ count = count + 1 list[count] = black
+ end
+ end
+ if tl then
+ local done = false
+ for i=1,#tl do
+ local l = tl[i]
+ local h = l.height or 0
+ local k = l.kern or 0
+ if k ~= 0 then
+ if h == 0 then
+ h = height + depth -- todo max
+ end
+ if not done then
+ count = count + 1 list[count] = tlcolor
+ done = true
+ end
+ count = count + 1 list[count] = push
+ count = count + 1 list[count] = { "offset", (k < 0 and k or 0), height - h }
+ count = count + 1 list[count] = rulecache[h][0][abs(k)]
+ count = count + 1 list[count] = pop
+ end
+ end
+ if done then
+ count = count + 1 list[count] = black
+ end
+ end
+ end
+ if count > 0 then
+ local commands = character.commands
+ if commands then
+ character.commands = appendcommandtable(commands,list)
+ else
+ list[#list+1] = charcommand[unicode]
+ character.commands = list
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local specification = {
+ name = "staircase",
+ description = "show staircase kerns",
+-- position = 1,
+ manipulators = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+registerotffeature(specification)
+registerafmfeature(specification)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-ini.lmt
new file mode 100644
index 00000000000..45e8f9e18a7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-ini.lmt
@@ -0,0 +1,40 @@
+if not modules then modules = { } end modules ['font-ini'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>Not much is happening here.</p>
+--ldx]]--
+
+local allocate = utilities.storage.allocate
+local sortedhash = table.sortedhash
+
+fonts = fonts or { }
+local fonts = fonts
+
+local identifiers = allocate()
+
+fonts.hashes = fonts.hashes or { identifiers = identifiers }
+fonts.tables = fonts.tables or { }
+fonts.helpers = fonts.helpers or { }
+fonts.tracers = fonts.tracers or { } -- for the moment till we have move to moduledata
+fonts.specifiers = fonts.specifiers or { } -- in format !
+
+fonts.analyzers = { } -- not needed here
+fonts.readers = { }
+fonts.definers = { methods = { } }
+fonts.loggers = { register = function() end }
+
+fonts.privateoffsets = {
+ textbase = 0xF0000, -- used for hidden (opentype features)
+ textextrabase = 0xFD000, -- used for visible by name
+ mathextrabase = 0xFE000, -- used for visible by code
+ mathbase = 0xFF000, -- used for hidden (virtual math)
+ keepnames = false, -- when set to true names are always kept (not for context)
+}
+
+-- Also here now:
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-ini.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-ini.mklx
new file mode 100644
index 00000000000..557ac3c49e2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-ini.mklx
@@ -0,0 +1,2693 @@
+%D \module
+%D [ file=font-ini,
+%D version=1998.09.11, % (second)
+%D version=2001.02.20, % (third)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: frozen tolerant iftok etc
+
+% todo: < 3 pt => 3pt
+% todo: check where more class usage
+% todo: split font-nam (style/alternative/size)
+% todo: split font-dim (scales etc)
+% todo: reconsider defaultfontclass
+
+%D Beware, we use a special set of parameters here:
+%D
+%D \starttabulate[|l|l|]
+%D \NC system variable (fixed) \NC \type {\s!text} \NC \NR
+%D \NC system variable (fixed) \NC \type {\s!script} \NC \NR
+%D \NC system variable (fixed) \NC \type {\s!scriptscript} \NC \NR
+%D \NC system variable (fixed) \NC \type {\s!x} \NC \NR
+%D \NC system variable (fixed) \NC \type {\s!xx} \NC \NR
+%D \NC variable (value) \NC \type {\v!big} \NC \NR
+%D \NC variable (value) \NC \type {\v!small} \NC \NR
+%D \NC constant (regular key) \NC \type {\c!interlinespace} \NC \NR
+%D \NC constant (regular key) \NC \type {\c!em} \NC \NR
+%D \stoptabulate
+%D
+%D The math related ones are similar to the ones used in \TEX\ itself,
+%D the size related ones show up as keywords in the user interface
+%D when switching sizes, and the two constants are used in key|/|value
+%D situations.
+
+%D We should consider design sizes ... maybe kick 'm out which removes
+%D the size code and simplifies things considerably. After all, there
+%D will be no latin modern math in sizes.
+
+\writestatus{loading}{ConTeXt Font Macros / Initialization}
+
+%D Documentation is somewhat messy as it contains bits and pieces of
+%D previous versions.
+
+\unprotect
+
+%D There are several ways to specify a font. Three of them are pure \TEX\ ones, the
+%D fourth one is new:
+%D
+%D \starttyping
+%D \font\name=cmr12
+%D \font\name=cmr12 at 10pt
+%D \font\name=cmr12 scaled 2
+%D \font\name=cmr12 sa 1.440
+%D \stoptyping
+%D
+%D The non||\TEX\ alternative \type{sa} stands for {\em scaled at}. This means as
+%D much as: scale the bodyfontsize with this factor. The scaled option is not that
+%D useful as one needs to know the design size.
+%D
+%D Because \type {sa} (scaled at) and \type {mo} (mapped on) are not low level \TEX\
+%D supported alternatives, we have to test for it ourselves. In doing so, we need an
+%D auxiliary \DIMENSION. We cannot use \type{\scratchdimen} because font loading can
+%D happen at any moment due to postponed loading. We could instead have used dirty
+%D grouping tricks, but this one works too.
+
+% \enableexperiments[fonts.autorscale]
+%
+% \starttypescript[mscore]
+% \definetypeface [mscore] [rm] [serif] [mscoretimes] [default]
+% \definetypeface [mscore] [ss] [sans] [mscorearial] [default] [rscale=auto] % 0.860]
+% \definetypeface [mscore] [tt] [mono] [mscorecourier] [default] [rscale=auto] % 1.065]
+% \definetypeface [mscore] [mm] [math] [times] [default] [rscale=auto] % 1.020]
+% \stoptypescript
+%
+% \starttext
+% \setupbodyfont[mscore,12pt]
+% \startTEXpage
+% test \ss test \tt test
+% \stopTEXpage
+% \stoptext
+
+% \definetypeface[one][rm][serif][computer-roman][default]
+% \definetypeface[two][rm][serif][computer-roman][default][rscale=.9]
+%
+% {\one \bf test \two test}
+% {\one \bf test \pushcurrentfont \two \popcurrentfont test}
+
+%D \macros
+%D {rm,ss,tt,hw,cg}
+%D
+%D Fonts are defined in separate files. When we define a font, we distinguish
+%D between several styles. In most cases we will use:
+%D
+%D \startlinecorrection
+%D \starttable[|l||]
+%D \HL
+%D \NC roman regular serif \NC \type{\rm} \NC\FR
+%D \NC sansserif sans support \NC \type{\ss} \NC\MR
+%D \NC type teletype mono \NC \type{\tt} \NC\LR
+%D \HL
+%D \stoptable
+%D \stoplinecorrection
+%D
+%D The number of styles is not limited to these three. When using Lucida Bright we
+%D can for instance also define:
+%D
+%D \startlinecorrection
+%D \starttable[|l||]
+%D \HL
+%D \NC handwritten \NC \type{\hw} \NC\FR
+%D \NC calligraphic \NC \type{\cg} \NC\LR
+%D \HL
+%D \stoptable
+%D \stoplinecorrection
+%D
+%D Within such a font set (\type{cmr}) and style (\type{\rm}) we can define a number
+%D of text font alternatives:
+%D
+%D \startlinecorrection
+%D \starttable[|l||]
+%D \HL
+%D \NC typeface \NC \type{\tf} \NC\FR
+%D \NC boldface \NC \type{\bf} \NC\MR
+%D \NC slanted \NC \type{\sl} \NC\MR
+%D \NC italic \NC \type{\it} \NC\MR
+%D \NC boldslanted \NC \type{\bs} \NC\MR
+%D \NC bolditalic \NC \type{\bi} \NC\MR
+%D \NC smallcaps \NC \type{\sc} \NC\LR
+%D \HL
+%D \stoptable
+%D \stoplinecorrection
+%D
+%D Internally fonts are stored as combination of size, style
+%D and alternative, e.g. \type{12pt}+\type{\ss}+\type{\bf}. Users are not confronted
+%D with sizes, but use the style or style+alternative to activate them.
+%D
+%D During the definition of a bodyfont one can also declare the available larger
+%D alternatives:
+%D
+%D \starttyping
+%D \tf \tfa \tfb \tfc ...
+%D \bf \bfa \bfb \bfc ...
+%D \sl \sla \slb \slc ...
+%D \stoptyping
+%D
+%D The smaller ones are automatically supplied and derived from
+%D the the bodyfont environment.
+%D
+%D \starttyping
+%D \tfx \tfxx
+%D \bfx \bfxx
+%D \slx \slxx
+%D \stoptyping
+%D
+%D There are only two smaller alternatives per style. The larger alternatives on the
+%D other hand have no limitations.
+%D
+%D These larger alternatives are mostly used in chapter and section titles or on
+%D title pages. When one switches to a larger alternative, the bold an other ones
+%D automatically adapt themselves:
+%D
+%D \startbuffer
+%D \tfd Hi \bf there\sl, here \tfb I \bf am
+%D \stopbuffer
+%D
+%S \startnarrower
+%D \typebuffer
+%S \stopnarrower
+%D
+%D therefore becomes:
+%D
+%D \startexample
+%D \getbuffer
+%D \stopexample
+%D
+%D Maybe this mechanism isn't always as logic, but as said before, we tried to make
+%D it as intuitive as possible.
+%D
+%D So a specific kind of glyph can be characterized by:
+%D
+%D \startnarrower
+%D family (cmr) + bodyfont (12pt) + style (rm) + alternative (bf) + size (a)
+%D \stopnarrower
+%D
+%D The last component (the size) is optional.
+%D
+%D We introduced \type {\tf} as command to call for the current normally sized
+%D typeface. This commands results in roman, sans serif, teletype or whatever style
+%D is in charge. Such rather massive switches of style sometimes take more
+%D processing time than comfortable. Of course there is a workaround for this: we
+%D can call fonts directly by means of commands like:
+%D
+%D \starttyping
+%D \rmtf \sssl \tttf \rmbsa
+%D \stoptyping
+%D
+%D One should realize that this fast calls have limitations, they lack for instance
+%D automatic super- and subscript support.
+%D
+%D This leaves us two more commands: \type {\tx} and \type {\txx}. These activate a
+%D smaller and even more smaller font than the current one and adapt themselves to
+%D the current alternative, so when \type {\bf} is active, \type {\tx} gives a
+%D smaller boldface, which in turn can be called directly by \type {\bfx}.
+%D
+%D These two smaller alternatives are specified by the bodyfont environment and
+%D therefore not necessarily have similar sizes as \type {\scriptsize} and \type
+%D {\scriptscriptsize}. The main reason for this incompatibility (which can easily
+%D be undone) lays in the fact that we often want a bit bigger characters than in
+%D math mode. In \CONTEXT\ for instance the \type {\tx} and \type {\txx} commands
+%D are used for surrogate \cap {smallcaps} which support both nesting and
+%D alternatives, like in {\bf \cap {a \cap {small} world}}, which was typeset by
+%D
+%D \starttyping
+%D \bf\cap{a \cap{small} world}
+%D \stoptyping
+%D
+%D And compare $\rm \scriptstyle THIS$ with the slightly larger \cap {THIS}:
+%D \ruledhbox {$\rm \scriptstyle scriptstyle: THIS$} or \ruledhbox {\cap {x style:
+%D THIS}} makes a big difference.
+%D
+%D The \type {x..d} sizes should be used grouped. If you don't group them, i.e. call
+%D them in a row, \CONTEXT\ will not be able to sort out your intention (\type {x}
+%D inside \type {d} inside \type {x}. etc.). The following table demonstrates this:
+%D
+%D \def\FontState{\setstrut\ruledhbox{\strut Hello}}
+%D
+%D \starttabulate[|||||]
+%D \HL
+%D \NC \rlap{\quad\bf grouped} \NC \NC \type {\tx} \NC \type {\txx} \NC \NR
+%D \HL
+%D \NC \type{\tfx} \NC \tfx \FontState \NC \tfx \tx \FontState \NC \tfx \txx \FontState \NC \NR
+%D \NC \type{\tfxx} \NC \tfxx \FontState \NC \tfxx\tx \FontState \NC \tfxx\txx \FontState \NC \NR
+%D \NC \type{\tf} \NC \tf \FontState \NC \tf \tx \FontState \NC \tf \txx \FontState \NC \NR
+%D \NC \type{\tfa} \NC \tfa \FontState \NC \tfa \tx \FontState \NC \tfa \txx \FontState \NC \NR
+%D \NC \type{\tfb} \NC \tfb \FontState \NC \tfb \tx \FontState \NC \tfb \txx \FontState \NC \NR
+%D \NC \type{\tfc} \NC \tfc \FontState \NC \tfc \tx \FontState \NC \tfc \txx \FontState \NC \NR
+%D \NC \type{\tfd} \NC \tfd \FontState \NC \tfd \tx \FontState \NC \tfd \txx \FontState \NC \NR
+%D \NC \type{\tfx} \NC \tfx \FontState \NC \tfx \tx \FontState \NC \tfx \txx \FontState \NC \NR
+%D \NC \type{\tfxx} \NC \tfxx \FontState \NC \tfxx\tx \FontState \NC \tfxx\txx \FontState \NC \NR
+%D \HL
+%D \stoptabulate
+%D
+%D \blank
+%D
+%D \starttabulate[|||||]
+%D \HL
+%D \NC \rlap{\quad\bf stacked} \NC \NC \type {\tx} \NC \type {\txx} \NC \NR
+%D \HL
+%D \NC \type{\tfx}
+%D \NC \tfx \FontState
+%D \NC \tfx \tx \FontState
+%D \NC \tfx \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfxx}
+%D \NC \tfx\tfxx \FontState
+%D \NC \tfx\tfxx \tx \FontState
+%D \NC \tfx\tfxx \txx \FontState
+%D \NC \NR
+%D \NC \type{\tf}
+%D \NC \tfx\tfxx\tf \FontState
+%D \NC \tfx\tfxx\tf \tx \FontState
+%D \NC \tfx\tfxx\tf \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfa}
+%D \NC \tfx\tfxx\tf\tfa \FontState
+%D \NC \tfx\tfxx\tf\tfa \tx \FontState
+%D \NC \tfx\tfxx\tf\tfa \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfb}
+%D \NC \tfx\tfxx\tf\tfa\tfb \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb \tx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfc}
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \tx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfd}
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \tx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfd \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfx}
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \tx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx \txx \FontState
+%D \NC \NR
+%D \NC \type{\tfxx}
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \tx \FontState
+%D \NC \tfx\tfxx\tf\tfa\tfb\tfc\tfx\tfxx \txx \FontState
+%D \NC \NR
+%D \HL
+%D \stoptabulate
+
+\fontslantperpoint \nullfont 0\scaledpoint
+\fontinterwordspace \nullfont 256377\scaledpoint
+\fontinterwordstretch\nullfont 128188\scaledpoint
+\fontinterwordshrink \nullfont 85459\scaledpoint
+\fontexheight \nullfont 338952\scaledpoint
+\fontemwidth \nullfont 786432\scaledpoint
+\fontextraspace \nullfont 85459\scaledpoint
+
+\appendtoks
+ \fontslantperpoint \nullfont 0\scaledpoint
+ \fontinterwordspace \nullfont 256377\scaledpoint
+ \fontinterwordstretch\nullfont 128188\scaledpoint
+ \fontinterwordshrink \nullfont 85459\scaledpoint
+ \fontexheight \nullfont 338952\scaledpoint
+ \fontemwidth \nullfont 786432\scaledpoint
+ \fontextraspace \nullfont 85459\scaledpoint
+\to \everyjob
+
+%D Tracing
+
+\newtoks\t_font_tracers_definitions
+
+\permanent\protected\def\tracefontdefinitions
+ {\the\t_font_tracers_definitions}
+
+%D Some housekeeping macros:
+
+\permanent\protected\def\setfontparameters
+ {\setfalse\c_font_synchronize
+ \the\everybodyfont
+ \settrue\c_font_synchronize}
+
+\mutable\let\savedfont\empty
+
+\installmacrostack\savedfont
+
+% \fontname\font : string
+% \fontid \font : integer
+% \the \font : token (reference to font)
+% \fontspecifiedname : string
+% \the \fontspecifiedsize : dimension
+
+\permanent\protected\def\savefont
+ {\edef\savedfont{\the\font}%
+ \push_macro_savedfont}
+
+\permanent\protected\def\restorefont
+ {\pop_macro_savedfont
+ \savedfont}
+
+\aliased\let\popcurrentfont\relax
+
+\permanent\protected\def\pushcurrentfont
+ {\enforced\permanent\protected\edef\popcurrentfont
+ {\def\noexpand\fontbody {\fontbody}%
+ \def\noexpand\fontstyle {\fontstyle}%
+ \def\noexpand\fontalternative{\fontalternative}%
+ \def\noexpand\fontsize {\fontsize}%
+ \font_helpers_check_big_math_synchronization
+ \font_helpers_synchronize_font}}
+
+%D \macros{definedfont}
+
+\mutable\let\thedefinedfont\relax % not to be confused with \everydefinefont
+
+\permanent\tolerant\protected\def\definedfont[#specification]%
+ {\ifarguments
+ \c_font_feature_inheritance_mode\c_font_feature_inheritance_fontonly
+ \or
+ \c_font_feature_inheritance_mode\c_font_feature_inheritance_fontonly
+ \font_basics_define_font_without_parameters{thedefinedfont}{#specification}%
+ \fi
+ \thedefinedfont
+ \the\everydefinedfont}
+
+%D \macros{startfont}
+
+\permanent\protected\def\startfont{\begingroup\definedfont}
+\permanent\protected\def\stopfont {\endgroup}
+
+%D \macros
+%D {everybodyfont,everyglobalbodyfont}
+%D
+%D Every change in bodyfont size has conseqences for the baseline distance and skips
+%D between paragraphs. These are initialized in other modules. Here we only provide
+%D the hooks that garantees their handling.
+%D
+%D At the system level one can initialize thing like:
+%D
+%D \starttyping
+%D \appendtoks \setupspacing \to \everybodyfont
+%D \stoptyping
+
+%D \macros
+%D {globalbodyfontsize,localbodyfontsize,bodyfontsize}
+%D
+%D Here we have to distinguish between the global (overal) bodyfont size and the
+%D local (sometimes in the textflow) size. We store these dimensions in two
+%D \DIMENSION\ registers. These registers are not to be misused in calculations.
+
+\ifdefined\globalbodyfontsize \else \newdimen\globalbodyfontsize \fi \globalbodyfontsize=12pt
+\ifdefined\localbodyfontsize \else \newdimen\localbodyfontsize \fi \localbodyfontsize =\globalbodyfontsize
+\ifdefined\bodyfontsize \else \newdimen\bodyfontsize \fi \bodyfontsize =\globalbodyfontsize
+
+%D When we assign for instance 12pt to a \DIMENSION\ register the \type {\the}'d
+%D value comes out as 12.0pt, which is often not the way users specify the bodyfont
+%D size. Therefore we use normalized values. They are cached to save overhead in
+%D \LUA\ calls.
+
+\installcorenamespace{fontnormalizedbody}
+
+\permanent\protected\def\normalizebodyfontsize#macro#body%
+ {\expandafter\let\expandafter#macro\csname\??fontnormalizedbody\number\dimexpr#body\endcsname
+ \ifrelax#macro%
+ \normalizebodyfontsize_indeed#macro{#body}%
+ \fi}
+
+\def\normalizebodyfontsize_indeed#macro#body%
+ {\edef#macro{\clf_nbfs\dimexpr#body\relax}%
+ \gletcsname\??fontnormalizedbody\number\dimexpr#body\endcsname#macro}
+
+\permanent\def\thenormalizedbodyfontsize#body%
+ {\clf_nbfs\dimexpr#body\relax}
+
+\mutable\edef\normalizedglobalbodyfontsize{\thenormalizedbodyfontsize\bodyfontsize}
+\mutable\edef\normalizedlocalbodyfontsize {\thenormalizedbodyfontsize\bodyfontsize}
+\mutable\edef\normalizedbodyfontsize {\thenormalizedbodyfontsize\bodyfontsize}
+
+%D \macros
+%D {mapfontsize}
+%D
+%D For special purposes, like in math, you may want to use slightly different sizes
+%D than the ones given. This happens for instance with the Math Times fonts. Mapped
+%D font sizes can be specified by using the \type {mo} key instead of \type {sa} in
+%D font definitions.
+%D
+%D \startbuffer
+%D \mapfontsize[10pt][11pt]
+%D \mapfontsize[11pt][12pt]
+%D \mapfontsize[12pt][13pt]
+%D
+%D \definefont[test][Serif]\test TEST \par
+%D \definefont[test][Serif sa 5]\test TEST \par
+%D \definefont[test][Serif mo 5]\test TEST \par
+%D \definefont[test][Serif sa d]\test TEST \par
+%D \definefont[test][Serif at 60pt]\test TEST \par
+%D \definefont[test][Serif scaled 6000]\test TEST \par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startpacked
+%D \getbuffer
+%D \stoppacked
+
+\installcorenamespace{mappedfontsize}
+
+\permanent\tolerant\protected\def\mapfontsize[#from]#spacer[#to]%
+ {\ifarguments\or\or\defcsname\??mappedfontsize\the\dimexpr#from\relax\endcsname{#to}\fi}
+
+\def\font_basics_set_mapped_fontsize#from%
+ {\ifcsname\??mappedfontsize\the\dimexpr#from\relax\endcsname
+ \lastnamedcs\else#from%
+ \fi}
+
+\installcorenamespace{fontbodyknown}
+\installcorenamespace{fontclassyes} % fontclass
+\installcorenamespace{fontclassnop} % nofontclass
+
+\def\font_helpers_process_relative_size_list#command% could be a toks
+ {#command\v!big
+ #command\v!small}
+
+\let\v_font_size_relative \plusone
+\def\v_font_size_absolute {\fontbody}
+
+\let\v_font_rscale_default\!!plusone
+\let\p_font_rscale \v_font_rscale_default
+
+\def\font_helpers_check_relative_font_id % can be plugged in later
+ {\let\p_font_rscale\minusone
+ \let\p_font_rscale\v_font_rscale_default}
+
+\def\font_helpers_check_relative_font_size#style%
+ {\edef\p_font_rscale
+ {\ifcsname\??fontclass\fontclass#style\s!rscale\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??fontclass\defaultfontclass#style\s!rscale\endcsname % brr
+ \lastnamedcs
+ \else
+ \v_font_rscale_default
+ \fi}%
+ % move elsewhere
+ \ifx\p_font_rscale\v!auto
+ \let\p_font_rscale\plusone
+ \font_helpers_check_relative_font_id
+ \fi}
+
+\def\font_rscale_xx#style%
+ {\ifcsname\??fontclass\fontclass#style\s!rscale\endcsname
+ \lastnamedcs
+ \else
+ \v_font_rscale_default
+ \fi}
+
+\def\font_rscale_mm
+ {\ifcsname\??fontclass\fontclass\s!mm\s!rscale\endcsname
+ \lastnamedcs
+ \else
+ \v_font_rscale_default
+ \fi}
+
+\def\font_helpers_register_fontbody#body%
+ {\letcsname\??fontbodyknown#body\endcsname\empty}
+
+%D \macros
+%D {definefontstyle,definefontsize,definefontalternative}
+%D
+%D When setting of switching the overall style we can use the short identifier like
+%D rm and ss, but when defined we can also use more verbose names like roman or
+%D sansserif. Such names are defined by:
+%D
+%D \starttyping
+%D \definefontstyle [serif,rm] [rm]
+%D \definefontstyle [sansserif,ss] [ss]
+%D \stoptyping
+
+\installcorenamespace{fontstyle} % full style prefix (roman etc)
+\installcorenamespace{fontshortstyle} % short style prefix (rm etc)
+
+\installcorenamespace{fontstyleknown}
+\installcorenamespace{fontalternativeknown}
+\installcorenamespace{fontsizeknown}
+
+\newtoks\t_font_style_commands
+\newtoks\t_font_size_commands
+\newtoks\t_font_alternative_commands
+
+\setnewmacro\m_font_style_command \gobbleoneargument
+\setnewmacro\m_font_size_command \gobbleoneargument
+\setnewmacro\m_font_alternative_command\gobbleoneargument
+
+\def\font_helpers_process_style_list #command{\def\m_font_style_command {#command}\the\t_font_style_commands}
+\def\font_helpers_process_size_list #command{\def\m_font_size_command {#command}\the\t_font_size_commands}
+\def\font_helpers_process_alternative_list#command{\def\m_font_alternative_command{#command}\the\t_font_alternative_commands}
+
+\def\font_helpers_register_style #style{\letcsname\??fontstyleknown #style\endcsname\empty}
+\def\font_helpers_register_size #size{\letcsname\??fontsizeknown #size\endcsname\empty}
+\def\font_helpers_register_alternative#alternative{\letcsname\??fontalternativeknown#alternative\endcsname\empty}
+
+\permanent\protected\def\definefontstyle[#commands]#spacer[#style]% style: rm ss tt ...
+ {\ifcsname\??fontstyleknown#style\endcsname \else % can be delayed till used (cg, hw)
+ \font_helpers_register_style{#style}%
+ \toksapp\t_font_style_commands{\m_font_style_command{#style}}%
+ \fi
+ \processcommalist[#commands]{\font_basics_define_fontstyle_indeed{#style}}}
+
+\def\font_basics_define_fontstyle_indeed#style#command%
+ {\defcsname\??fontshortstyle#command\endcsname{#style}%
+ \defcsname\??fontstyle #command\endcsname{\csname#style\endcsname}}
+
+\permanent\protected\def\definefontsize[#size]%
+ {\ifcsname\??fontsizeknown#size\endcsname \else
+ \font_helpers_register_size{#size}%
+ \toksapp\t_font_size_commands{\m_font_size_command{#size}}%
+ \fi
+ \font_helpers_check_fontname_combinations}
+
+\permanent\protected\def\definefontalternative[#alternative]%
+ {\ifcsname\??fontalternativeknown#alternative\endcsname \else
+ \font_helpers_register_alternative{#alternative}%
+ \toksapp\t_font_alternative_commands{\m_font_alternative_command{#alternative}}%
+ \fi
+ \font_helpers_check_fontname_combinations}
+
+\protected\def\font_helpers_check_fontname_combinations % we need to split math and text here ... todo (math only has mr and mb)
+ {\font_helpers_process_style_list\font_helpers_check_fontname_combinations_s}
+
+\def\font_helpers_check_fontname_combinations_s#style%
+ {\font_helpers_process_alternative_list{\font_helpers_check_fontname_combinations_indeed_s_a{#style}}}
+
+\def\font_helpers_check_fontname_combinations_indeed_s_a#style#alternative%
+ {\font_helpers_process_size_list{\font_basics_check_fontname_combination{#style}{#alternative}}}
+
+\definefontstyle [\s!mm] [\s!mm]
+\definefontstyle [\s!rm] [\s!rm]
+\definefontstyle [\s!ss] [\s!ss]
+\definefontstyle [\s!tt] [\s!tt]
+
+%D We define all the font switching commands globally. After all they are part of
+%D the formal font interface once defined. The size can be empty (so checking is
+%D needed as \type {\bf} is already defined).
+%D
+%D The \type {\normal..} variants are available as extras for cases where the \type
+%D {..} is overloaded.
+
+\newmacro\m_font_mm
+
+\def\font_basics_check_fontname_combination#style% alternative size
+ {\edef\m_font_mm{#style}%
+ \ifx\m_font_mm\s!mm % prevents \max and alike (re)defs
+ \expandafter\font_basics_check_math_bodyfont
+ \else
+ \expandafter\font_basics_check_text_bodyfont
+ \fi{#style}} % no \m_font_mm, not expanded later on
+
+\def\font_basics_check_math_bodyfont#style#alternative#size%
+ {}
+
+\def\font_basics_check_text_bodyfont_step#whatever#body% size can be empty (checking needed as \bf is already defined)
+ {\ifcsname#whatever\endcsname\else
+ \permanent\setugvalue{#whatever}{#body}%
+ \fi}
+
+\def\font_basics_check_text_bodyfont#style#alternative#size% size can be empty (checking needed as \bf is already defined)
+ {\font_basics_check_text_bodyfont_step{#style#size}{\font_helpers_set_current_font_style_size{#style}{#size}}% \rma
+ \font_basics_check_text_bodyfont_step{#alternative#size}{\font_helpers_set_current_font_alternative_size{#alternative}{#size}}% \sla
+ \font_basics_check_text_bodyfont_step{#style#alternative#size}{\font_helpers_set_current_font_style_alternative_size{#style}{#alternative}{#size}}% \rmsla
+ \font_basics_check_text_bodyfont_step{#style}{\font_helpers_set_current_font_style{#style}}% \rm
+ \font_basics_check_text_bodyfont_step{#alternative}{\font_helpers_set_current_font_alternative{#alternative}}% \sl
+ \font_basics_check_text_bodyfont_step{#style\s!x }{\font_helpers_set_current_font_x_style_alternative{#style}}% \rmx
+ \font_basics_check_text_bodyfont_step{#style\s!xx}{\font_helpers_set_current_font_xx_style_alternative{#style}}% \rmxx
+ \font_basics_check_text_bodyfont_step{#alternative\s!x }{\font_helpers_set_current_font_x_alternative{#alternative}}% \slx
+ \font_basics_check_text_bodyfont_step{#alternative\s!xx}{\font_helpers_set_current_font_xx_alternative{#alternative}}% \slxx
+ \font_basics_check_text_bodyfont_step{#style#alternative}{\font_helpers_set_current_font_style_alternative{#style}{#alternative}}}% \rmsl
+
+%D Scaling macros:
+%D
+%D This system is somewhat complicated by two (possible conflicting) demands:
+%D
+%D \startitemize
+%D \item We support wildcards like \type {sa *} which will adapt to the current
+%D size. This is also the default specification.
+%D \item We support named scales like \type {sa d}; beware: \type {x} and \type {xx}
+%D are valid scales but they are not alway the same as the ones used in for
+%D instance \type {\bfx} because there the sized come from the bodyfont
+%D environment. In the future there maybe a switch that also honors the
+%D environment in named scales.
+%D \stopitemize
+%D
+%D Keep in mind that the smaller sizes are just for text super and subscripts while
+%D larger sizes can be used in titles where for instance math follows the size.
+
+% b:x{\definedfont[SerifBold sa b]x}{\bfb x $x^x$}\par
+% 1:x{\definedfont[SerifBold sa 1]x}{\bf x $x^x$}\par
+% x:x{\definedfont[SerifBold sa x]x}{\bfx x $x^x$}\par
+% xx:x{\definedfont[SerifBold sa xx]x}{\bfxx x $x^x$}\par
+%
+% *:x{\definedfont[Serif sa *]x}\par
+% 1:x{\definedfont[Serif sa 1]x}\par
+% 2:x{\definedfont[Serif sa 2]x}\par
+% 3:x{\definedfont[Serif sa 3]x}\par
+% 4:x{\definedfont[Serif sa 4]x}\par
+% 5:x{\definedfont[Serif sa 5]x}\par
+
+\permanent\def\safontscale{\number\dimexpr\v_font_size_absolute\relax}
+\permanent\def\mofontscale{\number\dimexpr\font_basics_set_mapped_fontsize\v_font_size_absolute\relax}
+
+\mutable\let\somefontname \s!unknown
+\mutable\let\somefontspec \s!unknown
+\mutable\let\somefontfile \s!unknown
+\mutable\let\somefontsize \zerocount
+\mutable\let\somefontsizex\empty
+\mutable\let\somefontsizey\empty
+
+\newcount\scaledfontmode % also used at the lua end
+\newcount\scaledfontsize % also used at the lua end
+\newcount\lastfontid % also used at the lua end / tex end
+\newtoks \everydefinefont
+
+\aliased\let\c_font_feature_inheritance_fontnone \zerocount % none
+\aliased\let\c_font_feature_inheritance_fontonly \plusone % fontonly
+\aliased\let\c_font_feature_inheritance_classonly \plustwo % classonly
+\aliased\let\c_font_feature_inheritance_fontfirst \plusthree % fontfirst
+\aliased\let\c_font_feature_inheritance_classfirst\plusfour % classfirst
+
+\aliased\let\c_font_feature_inheritance_default \c_font_feature_inheritance_fontfirst
+
+\setnewconstant\c_font_feature_inheritance_mode \c_font_feature_inheritance_default
+
+\newdimen \d_font_scaled_text_face
+\newdimen \d_font_scaled_font_size
+\newconditional\c_font_body_scale
+\newfraction \f_font_body_scale
+
+\newconditional\c_font_compact
+\newcount \c_font_scaled_glyph_scale
+
+\immutable\dimensiondef\d_font_scaled_default 10pt
+
+\newcount\c_font_future_glyph_scale
+
+% todo: move all to lua: use localcall for resolving filename
+
+\newcount\c_font_future_glyph_scale
+\newcount\c_font_scaled_font_mode_saved
+\newcount\c_font_scaled_points
+
+\mutable\let\somefontspec \empty
+\mutable\let\lastrawfontcall \relax
+\mutable\let\lastglobalrawfontcall\relax
+
+\protected\def\font_helpers_low_level_define
+ {\ifconditional\c_font_compact
+ \expandafter\font_helpers_low_level_define_compact
+ \else
+ \expandafter\font_helpers_low_level_define_normal
+ \fi}
+
+\protected\def\font_helpers_low_level_define_normal#specification#csname%
+ {% we can now set more at the lua end
+ \glet\somefontname\defaultfontfile
+ \let\somefontsize\empty
+ \let\somefontsizex\empty
+ \let\somefontsizey\empty
+ \clf_definefont_one{\detokenize\expandafter{\normalexpanded{#specification}}}% the escapestring catches at \somedimen
+ % sets \scaledfontmode and \somefontname and \somefontsize
+ \ifcase\fontface\relax
+ % \let\v_font_size_absolute\textface % fontbody
+ \or
+ \let\v_font_size_absolute\textface
+ \or
+ \let\v_font_size_absolute\scriptface
+ \or
+ \let\v_font_size_absolute\scriptscriptface
+ \or
+ \let\v_font_size_absolute\xtextface
+ \or
+ \let\v_font_size_absolute\xxtextface
+ \fi
+ %
+ \ifcase\scaledfontmode\relax
+ % none, avoid the designsize if possible
+ \d_font_scaled_font_size-\plusthousand\scaledpoint
+ \or
+ % at
+ \d_font_scaled_font_size\somefontsize
+ \or
+ % sa
+ \d_font_scaled_font_size\v_font_size_absolute\relax
+ \d_font_scaled_font_size\currentfontbodysize\d_font_scaled_font_size % uses \somefontsize set by lua
+ \or
+ % mo
+ \d_font_scaled_font_size\font_basics_set_mapped_fontsize\v_font_size_absolute
+ \d_font_scaled_font_size\currentfontbodysize\d_font_scaled_font_size
+ \or
+ % scaled, don't use this one as it's unpredictable
+ \d_font_scaled_font_size-\somefontsize\scaledpoint
+ \else % ht cp
+ % experiment, yet undocumented
+ \d_font_scaled_font_size\somefontsize
+ \fi
+ \relax
+ \d_font_scaled_font_size\v_font_size_relative\d_font_scaled_font_size
+ \relax
+ %
+ \ifconditional\c_font_auto_size
+ \font_helpers_check_body_scale\fontsize
+ \ifconditional\c_font_body_scale
+ \d_font_scaled_font_size\f_font_body_scale\d_font_scaled_font_size
+ \d_font_scaled_text_face\f_font_body_scale\dimexpr\textface\relax
+ \else
+ \d_font_scaled_font_size\f_font_body_scale
+ \d_font_scaled_text_face\textface
+ \fi
+ \else
+ \d_font_scaled_text_face\textface
+ \fi
+ %
+ \edef\somefontspec{at \number\d_font_scaled_font_size sp}%
+ \edef\somefontfile{\truefontname\somefontname}%
+ \ifx\somefontfile\s!unknown
+ \edef\somefontfile{\defaultfontfile}%
+ \fi
+ \font_helpers_update_font_parameters
+ \font_helpers_update_font_class_parameters
+ %\writestatus{fonts}{low level define: #csname/\somefontfile/\number\d_font_scaled_font_size/\fontface/\number\d_font_scaled_text_face}%
+% \clf_definefont_two
+% \ifempty\fontclass\s!false\else\s!true\fi
+% {#csname}%
+% {\somefontfile}%
+% \d_font_scaled_font_size
+% \c_font_feature_inheritance_mode
+% {\m_font_class_features}%
+% {\m_font_features}%
+% {\m_font_class_fallbacks}%
+% {\m_font_fallbacks}%
+% \fontface
+% \d_font_scaled_text_face
+% {\m_font_class_goodies}%
+% {\m_font_goodies}%
+% {\m_font_class_designsize}%
+% {\m_font_designsize}%
+% \scaledfontmode
+ \clf_definefont_two
+ \ifempty\fontclass\s!false\else\s!true\fi
+ {#csname}%
+ \somefontfile
+ \d_font_scaled_font_size
+ \c_font_feature_inheritance_mode
+ \m_font_class_features
+ \m_font_features
+ \m_font_class_fallbacks
+ \m_font_fallbacks
+ \fontface
+ \d_font_scaled_text_face
+ \m_font_class_goodies
+ \m_font_goodies
+ \m_font_class_designsize
+ \m_font_designsize
+ \scaledfontmode
+ \relax
+ \ifcase\scaledfontsize
+ %\scaledfontsize\plusone
+ \let\somefontspec\empty
+ \let\lastrawfontcall\relax
+ \mutable\letcsname#csname\endcsname\relax
+ \else
+ \edef\somefontspec{at \number\scaledfontsize sp}% we need the resolved designsize (for fallbacks)
+ \expandafter\let\expandafter\lastrawfontcall\csname#csname\endcsname
+ \the\everydefinefont
+ \fi
+ \c_font_feature_inheritance_mode\c_font_feature_inheritance_default}
+
+\protected\def\font_helpers_low_level_define_compact#specification#csname%
+ {% we can now set more at the lua end
+ \glet\somefontname\defaultfontfile
+ \let\somefontsize\empty
+ \let\somefontsizex\empty
+ \let\somefontsizey\empty
+ \clf_definefont_one{\detokenize\expandafter{\normalexpanded{#specification}}}% the escapestring catches at \somedimen
+ % sets \scaledfontmode and \somefontname and \somefontsize
+ \ifcase\fontface\relax
+ % \let\v_font_size_absolute\textface % fontbody
+ \or
+ \let\v_font_size_absolute\textface
+ \or
+ \let\v_font_size_absolute\scriptface
+ \or
+ \let\v_font_size_absolute\scriptscriptface
+ \or
+ \let\v_font_size_absolute\xtextface
+ \or
+ \let\v_font_size_absolute\xxtextface
+ \fi
+ %
+ \ifcase\scaledfontmode\relax
+ % none, avoid the designsize if possible
+ \d_font_scaled_font_size-\plusthousand\scaledpoint
+ \or
+ % at
+ \d_font_scaled_font_size\somefontsize
+ \or
+ % sa
+ \d_font_scaled_font_size\v_font_size_absolute\relax
+ \d_font_scaled_font_size\currentfontbodysize\d_font_scaled_font_size % uses \somefontsize set by lua
+ \or
+ % mo
+ \d_font_scaled_font_size\font_basics_set_mapped_fontsize\v_font_size_absolute
+ \d_font_scaled_font_size\currentfontbodysize\d_font_scaled_font_size
+ \or
+ % scaled, don't use this one as it's unpredictable
+ \d_font_scaled_font_size-\somefontsize\scaledpoint
+ \else % ht cp
+ % experiment, yet undocumented
+ \d_font_scaled_font_size\somefontsize
+ \fi
+ \relax
+ \d_font_scaled_font_size\v_font_size_relative\d_font_scaled_font_size
+ \relax
+ %
+ \ifconditional\c_font_auto_size
+ \font_helpers_check_body_scale\fontsize
+ \ifconditional\c_font_body_scale
+ \d_font_scaled_font_size\f_font_body_scale\d_font_scaled_font_size
+ \d_font_scaled_text_face\f_font_body_scale\dimexpr\textface\relax
+ \else
+ \d_font_scaled_font_size\f_font_body_scale
+ \d_font_scaled_text_face\textface
+ \fi
+ \else
+ \d_font_scaled_text_face\textface
+ \fi
+ %
+ \ifnum\c_font_scaled_font_mode_saved>\plusfour
+ \c_font_scaled_font_mode_saved\scaledfontmode
+ \scaledfontmode\zerocount
+ \fi
+ \c_font_future_glyph_scale\numexpr\plushundred*\d_font_scaled_font_size/\maxcard\relax
+ \glyphscale\numexpr\plushundred*\d_font_scaled_font_size/\maxcard\relax % needed ? for math i guess
+ % \d_font_scaled_font_size\d_font_scaled_default
+ % \d_font_scaled_text_face\d_font_scaled_default
+ %
+ \edef\somefontspec{at \number\d_font_scaled_font_size sp}%
+ % this has to happen at the tex end ...
+ \edef\somefontfile{\truefontname\somefontname}%
+ \ifx\somefontfile\s!unknown
+ \edef\somefontfile{\defaultfontfile}%
+ \fi
+ \font_helpers_update_font_parameters
+ \font_helpers_update_font_class_parameters
+ % ... till here
+ %\writestatus{fonts}{low level define: #csname/\somefontfile/\number\d_font_scaled_font_size/\fontface/\number\d_font_scaled_text_face}%
+ \clf_definefont_two
+ \ifempty\fontclass\s!false\else\s!true\fi
+ {#csname}%
+ \somefontfile
+ \d_font_scaled_font_size
+ \c_font_feature_inheritance_mode
+ \m_font_class_features
+ \m_font_features
+ \m_font_class_fallbacks
+ \m_font_fallbacks
+ \fontface
+ \d_font_scaled_default
+ \m_font_class_goodies
+ \m_font_goodies
+ \m_font_class_designsize
+ \m_font_designsize
+ \scaledfontmode
+ \relax
+ \ifcase\scaledfontsize
+ %\scaledfontsize\plusone
+ \let\somefontspec\empty
+ \let\lastrawfontcall\relax
+ \mutable\letcsname#csname\endcsname\relax
+ \else
+ \edef\somefontspec{at \number\scaledfontsize sp}% we need the resolved designsize (for fallbacks)
+ %
+ \glyphscale
+ \ifnum\c_font_scaled_font_mode_saved>\plusfour
+ \numexpr\plusthousand*\dimexpr\d_font_scaled_default\relax/\c_font_scaled_points\relax
+ \else
+ \c_font_future_glyph_scale
+ \fi
+ \ifempty\somefontsizex\else
+ \edef\somefontsizex{\glyphxscale\somefontsizex\relax}% todo: inserts relax
+ \fi
+ \ifempty\somefontsizey\else
+ \edef\somefontsizey{\glyphyscale\somefontsizey\relax}%
+ \fi
+ \ifempty\fontclass\else\global\fi
+ \mutable\protected\edefcsname#csname\endcsname
+ {\setfontid\the\fontid\csname#csname\endcsname\relax
+ \glyphscale\the\glyphscale\relax
+ \somefontsizex
+ \somefontsizey}%
+ %
+ \expandafter\let\expandafter\lastrawfontcall\csname#csname\endcsname
+ \the\everydefinefont
+ \fi
+ \c_font_feature_inheritance_mode\c_font_feature_inheritance_default}
+
+\def\font_helpers_check_body_scale#fontsize% gets character (x xx a etc)
+ {\ifcsname\??fontenvironments\fontclass\fontbody#fontsize\endcsname
+ \expandafter\let\expandafter\f_font_body_scale\lastnamedcs
+ \setfalse\c_font_body_scale % !
+ \orelse\ifcsname\??fontenvironments\fontclass\s!default#fontsize\endcsname
+ \expandafter\let\expandafter\f_font_body_scale\lastnamedcs
+ \settrue\c_font_body_scale
+ \orelse\ifcsname\??fontenvironments\fontbody#fontsize\endcsname
+ \expandafter\let\expandafter\f_font_body_scale\lastnamedcs
+ \setfalse\c_font_body_scale % !
+ \orelse\ifcsname\??fontenvironments\s!default#fontsize\endcsname
+ \expandafter\let\expandafter\f_font_body_scale\lastnamedcs
+ \settrue\c_font_body_scale
+ \orelse\ifcsname\??fontenvironments\fontclass\s!default\s!text\endcsname
+ \expandafter\let\expandafter\f_font_body_scale\lastnamedcs
+ \settrue\c_font_body_scale
+ \orelse\ifcsname\??fontenvironments\s!default\s!text\endcsname
+ \expandafter\let\expandafter\f_font_body_scale\lastnamedcs
+ \settrue\c_font_body_scale
+ \else
+ \let\f_font_body_scale\plusone
+ \settrue\c_font_body_scale
+ \fi}
+
+\newif\ifskipfontcharacteristics \skipfontcharacteristicstrue
+
+\tracingfonts\plussix % <id: name @ size>
+
+%D When fontclasses are used, we define the font global, since namespaces are
+%D used. Otherwise we parse the specs each time.
+
+\mutable\let\fontfile\s!unknown
+
+%D Relatively new:
+
+\installcorenamespace{fonts}
+\installcorenamespace{fontslanguage}
+
+\installsetuponlycommandhandler \??fonts {fonts}
+
+\newconstant\c_fonts_auto_language
+
+\letvalue{\??fontslanguage\v!auto}\plusone % experimental
+%letvalue{\??fontslanguage\v!yes }\plustwo % less efficient, for experiments
+
+\appendtoks
+ \c_fonts_auto_language
+ \ifcsname\??fontslanguage\fontsparameter\c!language\endcsname
+ \lastnamedcs
+ \else
+ \zerocount
+ \fi
+\to \everysetupfonts
+
+\appendtoks
+ \ifcase\c_fonts_auto_language
+ % nothing
+ \or
+ \addfflanguage
+ % \or
+ % font
+ \fi
+\to \everylanguage
+
+% \setupfonts
+% [\c!language=\v!auto]
+
+%D \macros
+%D {everyfont,everyfontswitch}
+
+\ifdefined\everyfont \else \newtoks\everyfont \fi
+\ifdefined\everyfontswitch \else \newtoks\everyfontswitch \fi
+
+\permanent\def\setfontcharacteristics{\the\everyfont}
+
+% \appendtoks
+% \ifcase\c_fonts_auto_language
+% % nothing
+% \or
+% % auto
+% \or
+% \addfflanguage
+% \fi
+% \to \everyfont
+
+%D \macros
+%D {definefont}
+%D
+%D We also accept \type{sa a}||\type{sa d} as specification.
+%D
+%D Before we implement the main definition macro, we first show one for local use:
+%D
+%D \starttyping
+%D \definefont[Some][LucidaBright at 100pt] \Some some
+%D \definefont[More][LucidaBright scaled 3000] \More more
+%D \definefont[Nice][LucidaBright mo 2] \Nice nice
+%D \definefont[Text][LucidaBright sa 5.4] \Text last
+%D \stoptyping
+%D
+%D The implementation looks as follows:
+
+\permanent\tolerant\protected\def\definefont[#1]#*[#2]#*[#3]% [name][spec][1.6 | line=10pt | setup_id]
+ {\ifparameter#3\or
+ \expandafter\font_basics_define_font_a
+ \else
+ \expandafter\font_basics_define_font_b
+ \fi[#1][#2][#3]}
+
+\def\font_basics_define_font_a[#name][#specification][#settings]% [name][spec][1.6 | line=10pt | setup_id]
+ {\doifelsesetups{#settings}%
+ {\mutable\protected\defcsname#name\endcsname{\font_basics_define_font_with_setups {#name}{#specification}{#settings}}}
+ {\mutable\protected\defcsname#name\endcsname{\font_basics_define_font_with_parameters{#name}{#specification}{#settings}}}}
+
+\def\font_basics_define_font_b[#name][#specification][#dummy]%
+ {\mutable\protected\defcsname#name\endcsname{\font_basics_define_font_without_parameters{#name}{#specification}}}
+
+\def\font_basics_define_font_with_parameters#name#specification#settings%
+ {\font_basics_define_font_without_parameters{#name}{#specification}%
+ \setuplocalinterlinespace[#settings]%
+ \setupspacing\relax} % is this really needed ?
+
+\def\font_basics_define_font_with_setups#name#specification#settings%
+ {\font_basics_define_font_without_parameters{#name}{#specification}%
+ \setups[#settings]}
+
+%D Beware, in the frozen variants no settings are supported yet, but that might happen
+%D some day.
+
+\permanent\tolerant\protected\def\definefrozenfont[#name]#spacer[#specification]#spacer[#settings]%
+ {\ifparameter#name\or
+ \begingroup
+ \definefont[#name][#specification][#settings]%
+ \csname#name\endcsname
+ \glet\lastglobalrawfontcall\lastrawfontcall
+ \endgroup
+ \aliased\letcsname#name\endcsname\lastglobalrawfontcall
+ \fi}
+
+%D The instance namespace protection makes the switch local so that we can redefine a
+%D logical name and/or change the size in between.
+
+% todo: now mathsize twice in name (so it can go here)
+% todo: check when mathsize is needed
+
+\ifdefined\??fontinstanceready \else \installcorenamespace{fontinstanceready} \fi
+\ifdefined\??fontinstancebasic \else \installcorenamespace{fontinstancebasic} \fi
+\ifdefined\??fontinstanceclass \else \installcorenamespace{fontinstanceclass} \fi
+
+\newconditional\c_font_auto_size \settrue\c_font_auto_size
+
+\mutable\let\lastfontidentifier\empty
+
+\def\v_font_identifier_basic{\??fontinstancebasic \lastfontidentifier-\fontsize-\fontface}
+\def\v_font_identifier_class{\??fontinstanceclass\fontclass-\lastfontidentifier-\fontsize-\fontface}
+
+\let\v_font_identifier_basic_saved\v_font_identifier_basic
+\let\v_font_identifier_class_saved\v_font_identifier_class
+
+% \def\v_font_identifier_class{\??fontinstanceclass\fontclass-\lastfontidentifier-\fontstyle-\fontsize} % no \fontface
+
+\def\font_basics_define_font_without_parameters#identifier#2%
+ {\c_font_feature_inheritance_mode\c_font_feature_inheritance_fontonly
+ \edef\lastfontidentifier{#identifier}%
+ \let\v_font_size_relative\v_font_rscale_default
+ \let\v_font_size_absolute\fontbody
+ \font_helpers_low_level_define{#2}\v_font_identifier_basic
+ \csname\v_font_identifier_basic\endcsname
+ \setfalse\c_font_auto_size
+ \setfontcharacteristics
+ \the\everyfontswitch
+ \let\v_font_identifier_basic\v_font_identifier_basic_saved}
+
+\protected\def\font_helpers_trigger#identifier% make a traced variant
+ {\edef\lastfontidentifier{#identifier}%
+ \ifcsname\v_font_identifier_class\endcsname
+ % \writestatus{fonts}{trigger: reusing \v_font_identifier_class}%
+ \expandafter\font_helpers_trigger_reuse
+ \else
+ % \writestatus{fonts}{trigger: defining \v_font_identifier_class}%
+ \expandafter\font_helpers_trigger_define
+ \fi}
+
+\def\font_helpers_trigger_define#relative#absolute#specification%
+ {\def\v_font_size_relative{#relative}%
+ \def\v_font_size_absolute{#absolute}%
+ \font_helpers_low_level_define{#specification}\v_font_identifier_class
+ \csname\v_font_identifier_class\endcsname
+ \setfalse\c_font_auto_size
+ \ifskipfontcharacteristics \else
+ \setfontcharacteristics
+ \the\everyfontswitch
+ \fi
+ \let\v_font_identifier_class\v_font_identifier_class_saved}
+
+\def\font_helpers_trigger_reuse#relative#absolute#specification%
+ {\csname\v_font_identifier_class\endcsname
+ \setfalse\c_font_auto_size
+ \ifskipfontcharacteristics \else
+ \setfontcharacteristics
+ \the\everyfontswitch
+ \fi
+ \let\v_font_identifier_class\v_font_identifier_class_saved}
+
+%D \macros
+%D {currentfontbodyscale}
+%D
+%D Sometimes we need to have access to the font scale including the \type {a}||\type
+%D {d} sizes. The next macro returns the current scaling factor. Take a look at
+%D \type {cont-log.tex} for an example of its use.
+
+\installcorenamespace{fontenvironments}
+
+\permanent\def\currentfontbodysize % gets number (the normal sa 1 etc)
+ {\ifcsname\??fontenvironments\fontclass\s!default\somefontsize\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??fontenvironments\s!default\somefontsize\endcsname
+ \lastnamedcs
+ \else
+ \somefontsize
+ \fi}
+
+\permanent\def\currentfontbodyscale % gets character (x xx a etc)
+ {\csname\??fontenvironments
+ \ifcsname\??fontenvironments\fontclass\s!default\fontsize\endcsname\fontclass\s!default\fontsize\orelse
+ \ifcsname\??fontenvironments \s!default\fontsize\endcsname \s!default\fontsize\orelse
+ \ifcsname\??fontenvironments\fontclass\s!default\s!text \endcsname\fontclass\s!default\s!text \orelse
+ \ifcsname\??fontenvironments \s!default\s!text \endcsname \s!default\s!text \else
+ \s!default \fi
+ \endcsname}
+
+\def\font_currentfontbodyscale % gets character (x xx a etc)
+ {\ifcsname\??fontenvironments\fontclass\s!default\fontsize\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments \s!default\fontsize\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments\fontclass\s!default\s!text \endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments \s!default\s!text \endcsname\lastnamedcs\else
+ \csname\??fontenvironments \s!default \endcsname \fi}
+
+\permanent\def\currentfontscale % used in default definition
+ {\csname\??fontenvironments
+ \ifcsname\??fontenvironments\fontclass\s!default\xfontsize\endcsname\fontclass\s!default\fontsize\orelse
+ \ifcsname\??fontenvironments \s!default\xfontsize\endcsname \s!default\fontsize\orelse
+ \ifcsname\??fontenvironments\fontclass\s!default\s!text \endcsname\fontclass\s!default\s!text \orelse
+ \ifcsname\??fontenvironments \s!default\s!text \endcsname \s!default\s!text \else
+ \s!default \fi
+ \endcsname}
+
+\def\font_currentfontscale % used in default definition
+ {\ifcsname\??fontenvironments\fontclass\s!default\xfontsize\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments \s!default\xfontsize\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments\fontclass\s!default\s!text \endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments \s!default\s!text \endcsname\lastnamedcs\else
+ \csname\??fontenvironments \s!default \endcsname \fi}
+
+\setvalue{\??fontenvironments\s!default}{1}
+
+%D In the following macros we use \type {\currentxfontsize} to hold the current
+%D x||size of the font. This enables us to support for instance \type {\sl} inside a
+%D \type {\tx} switch.
+
+\newconstant\currentxfontsize
+
+\permanent\def\xfontsize{\ifcase\currentxfontsize\fontsize\or\s!x\else\s!xx\fi}
+
+%D Now we enter the area of font switching. The switching mechanism has to take care
+%D of several situations, like:
+%D
+%D \startitemize[packed]
+%D \item changing the overal document fonts (including margins, headers and footers)
+%D \item changing local fonts (only the running text)
+%D \item smaller and even more smaller alternatives (super- and subscripts)
+%D \stopitemize
+%D
+%D \TEX\ offers a powerful family mechanism for super- and subscripts in math mode.
+%D In text mode however, we don't use families for the smaller alternatives, and
+%D therefore have to take care of it otherwise.
+%D
+%D \macros
+%D {definebodyfontenvironment,setupbodyfontenvironment}
+%D
+%D The relationship between the several sizes of a font, is
+%D defined by:
+%D
+%D \showsetup{definebodyfontenvironment}
+%D
+%D Later on we will see how these parameters are used, so for the moment we stick
+%D with an example:
+%D
+%D \starttyping
+%D \definebodyfontenvironment
+%D [12pt]
+%D [ text=12pt,
+%D script=9pt,
+%D scriptscript=7pt,
+%D x=10pt,
+%D xx=8pt,
+%D big=12pt,
+%D small=10pt]
+%D \stoptyping
+%D
+%D The first argument specifies the bodyfont size to which the settings apply. All
+%D second parameters are specified in dimensions and tell us more about related
+%D sizes.
+%D
+%D Afterwards, one can change values with
+%D
+%D \showsetup{setupbodyfontenvironment}
+%D
+%D When instead of a size the keyword \type{unknown} is
+%D passed, fractions (relations) are used instead of fixed
+%D sizes.
+
+%D {\bf Remark:} We need to cover the following cases, otherwise users can get
+%D confused:
+%D
+%D \starttyping
+%D \setupbodyfont[23pt]
+%D
+%D \definebodyfontenvironment[23pt]
+%D \setupbodyfont[23pt]
+%D
+%D \definebodyfontenvironment[23pt]
+%D \definebodyfont[23pt][rm,ss,tt][default]
+%D \setupbodyfont[23pt]
+%D \stoptyping
+%D
+%D Beware: while some font defs can be global, the bodyfont environment checks
+%D local. This means that multiple local checks resulting in definitions are not
+%D that efficient. So, apart from an occasional switch, one should define an
+%D environment at the outer level.
+
+% \definebodyfontenvironment[33pt]
+% \definebodyfontenvironment[dejavu][default][1=.5]
+% \definebodyfontenvironment[dejavu][default][x=1.2]
+% \definebodyfontenvironment[dejavu][default][a=5]
+% \definebodyfontenvironment[dejavu][33pt][x=100pt]
+
+% the lookup order is:
+%
+% [class] [dimension] [parameters]
+% [class] [default] [parameters] % factors
+% [dimension] [parameters]
+% [default] [parameters] % factors
+%
+% with defaults providing factors
+
+% todo: class:size
+% todo: make assignments global
+
+\letvalue\??fontenvironments\empty % so we default to empty
+
+\permanent\def\bodyfontvariable#parameter%
+ {\csname\??fontenvironments
+ \ifcsname\??fontenvironments\fontclass\normalizedbodyfontsize#parameter\endcsname\fontclass\normalizedbodyfontsize#parameter\orelse
+ \ifcsname\??fontenvironments\fontclass #parameter\endcsname\fontclass #parameter\orelse
+ \ifcsname\??fontenvironments \normalizedbodyfontsize#parameter\endcsname \normalizedbodyfontsize#parameter\orelse
+ \ifcsname\??fontenvironments\s!default #parameter\endcsname\s!default #parameter\fi
+ \endcsname}
+
+\def\font_bodyfontvariable#parameter%
+ {\ifcsname\??fontenvironments\fontclass\normalizedbodyfontsize#parameter\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments\fontclass #parameter\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments \normalizedbodyfontsize#parameter\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments\s!default #parameter\endcsname\lastnamedcs\fi}
+
+\permanent\def\bodyfontsizevariable#size#parameter%
+ {\csname\??fontenvironments
+ \ifcsname\??fontenvironments\fontclass#size#parameter\endcsname\fontclass#size#parameter\orelse
+ \ifcsname\??fontenvironments\fontclass #parameter\endcsname\fontclass #parameter\orelse
+ \ifcsname\??fontenvironments #size#parameter\endcsname #size#parameter\orelse
+ \ifcsname\??fontenvironments\s!default #parameter\endcsname\s!default #parameter\fi
+ \endcsname}
+
+\def\font_bodyfontsizevariable#size#parameter%
+ {\ifcsname\??fontenvironments\fontclass#size#parameter\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments\fontclass #parameter\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments #size#parameter\endcsname\lastnamedcs\orelse
+ \ifcsname\??fontenvironments\s!default #parameter\endcsname\lastnamedcs\fi}
+
+\permanent\def\bodyfontinterlinespace{\bodyfontvariable\c!interlinespace} % used elsewhere
+
+\permanent\def\bodyfontdimension#class#size#parameter#body%
+ {\the\dimexpr
+ \ifcsname\??fontenvironments #class#size#parameter\endcsname \lastnamedcs \orelse
+ \ifcsname\??fontenvironments#class\s!default#parameter\endcsname \lastnamedcs\dimexpr#body\relax\orelse % factor
+ \ifcsname\??fontenvironments #size#parameter\endcsname \lastnamedcs \else
+ \lastnamedcs\dimexpr#body\relax\fi % factor
+ \relax}
+
+\installcorenamespace{fontenvironmentknown}
+
+\def\font_helpers_register_environment#class#body%
+ {\letcsname\??fontenvironmentknown#class#body\endcsname\empty}
+
+\newmacro\m_font_body
+\newmacro\m_font_body_normalized
+
+\permanent\tolerant\protected\def\definebodyfontenvironment[#1]#*[#2]#*[#3]% todo
+ {\ifarguments
+ \expandafter\font_basics_define_body_font_environment_unset
+ \or
+ \expandafter\font_basics_define_body_font_environment_unset
+ \or
+ \expandafter\font_basics_define_body_font_environment_empty
+ \or
+ \expandafter\font_basics_define_body_font_environment_class
+ \fi[#1][#2][#3]}
+
+\aliased\let\setupbodyfontenvironment\definebodyfontenvironment
+
+%D First we handle the class specific case. Beware: you can change values before
+%D a bodyfont is loaded but changing them afterwards can be sort of tricky as
+%D values are not consulted afterwards.
+
+\permanent\def\processbodyfontenvironmentlist#1% no \protected as then we cannot use it in alignments (still?)
+ {\clf_processbodyfontsizes{\strippedcsname#1}}
+
+\permanent\def\bodyfontenvironmentlist
+ {\clf_getbodyfontsizes}
+
+\def\font_basics_define_body_font_environment_class[#class][#body][#settings]%
+ {\edef\m_font_body{#body}%
+ \ifx\m_font_body\s!default
+ % these are the last resort within a class
+ \getrawparameters[\??fontenvironments#class\s!default][#settings]%
+ \else
+ \normalizebodyfontsize\m_font_body_normalized\m_font_body
+ \font_basics_define_body_font_environment_size[#class][\m_font_body_normalized][#settings]%
+ \clf_registerbodyfontsize{\m_font_body_normalized}%
+ \fi}
+
+%D The empty case uses the same code but needs to ignore the current class settings
+%D (just to be sure, as it's not really needed).
+
+\def\font_basics_define_body_font_environment_empty[#body][#settings][#dummy]%
+ {\push_macro_fontclass
+ \let\fontclass\empty
+ \font_basics_define_body_font_environment_class[][#body][#settings]%
+ \pop_macro_fontclass}
+
+\def\font_basics_define_body_font_environment_unset[#body][#dummya][#dummyb]%
+ {\push_macro_fontclass
+ \let\fontclass\empty
+ \font_basics_define_body_font_environment_class[][#body][]%
+ \pop_macro_fontclass}
+
+%D We don't check too soon as we can refer to later definitions.
+
+\newconditional\c_font_defining_environment_state % controls messages
+
+\def\font_basics_define_body_font_environment_size[#class][#normalizedbody][#settings]% normalized body
+ {\getrawparameters[\??fontenvironments#class#normalizedbody][#settings]%
+ \ifcsname\??fontenvironmentknown#class#normalizedbody\endcsname
+ % environment and size already defined
+ \orelse\ifproductionrun
+ \push_macro_fontclass
+ \edef\fontclass{#class}%
+ \font_helpers_register_environment{#class}{#normalizedbody}%
+ \settrue\c_font_defining_environment_state
+ \font_helpers_define_unknown_font{#normalizedbody}% current class
+ \setfalse\c_font_defining_environment_state
+ \pop_macro_fontclass
+ \fi
+ \font_helpers_register_fontbody{#normalizedbody}}
+
+%D Checking
+
+\def\font_helpers_check_bodyfont_environment#normalizedbody#body%
+ {\ifcsname\??fontenvironmentknown\fontclass#normalizedbody\endcsname
+ % already defined
+ \else
+ \font_helpers_check_bodyfont_environment_indeed{#normalizedbody}{#body}%
+ \fi}
+
+\def\font_helpers_check_bodyfont_environment_indeed#normalizedbody#body%
+ {\font_helpers_register_environment\fontclass{#normalizedbody}%
+ \ifcsname\??fontbodyknown#normalizedbody\endcsname
+ \else
+ \font_helpers_define_unknown_font{#normalizedbody}%
+ \fi}
+
+%D We default all parameters to the main bodyfont size, so the next setup is valid
+%D too:
+%D
+%D \starttyping
+%D \definebodyfontenvironment[24pt]
+%D \stoptyping
+%D
+%D All parameters can be redefined when needed, so one doesnot have to stick to the
+%D default ones.
+
+%D \macros
+%D {definebodyfont}
+%D
+%D The next step in defining a bodyfont involves the actual font files, which can be
+%D recognized by their extension \type {tfm}. Installing those file is often beyond
+%D the scope of the user and up to the system administrator.
+%D
+%D \showsetup{definebodyfont}
+%D
+%D This commands takes three arguments: a (series of) bodyfont size(s), the style
+%D group to which the definitions belong, and an alternative, as specified by the
+%D \TEX\ (math) families, extended with~a, b~\unknown.
+%D
+%D We show two examples, that show all the alternative scaling options. The \type
+%D {\tfa} alternatives can be extended with \type {\bfa}, \type {\slb}, etc. or even
+%D \type {e} and higher alternatives. The magic scaled values are derived from plain
+%D \TEX's \type {\magstep}:
+%D
+%D \starttyping
+%D \definebodyfont [12pt] [rm]
+%D [tf=cmr12,
+%D bf=cmbx12,
+%D it=cmti12,
+%D sl=cmsl12,
+%D bi=cmbxti10 at 12pt,
+%D bs=cmbxsl10 at 12pt,
+%D tfa=cmr12 scaled 1.200,
+%D tfb=cmr12 scaled 1.440,
+%D tfc=cmr12 scaled 1.728,
+%D tfd=cmr12 scaled 2.074,
+%D sc=cmcsc10 at 12pt]
+%D
+%D \definebodyfont [12pt,11pt,10pt,9pt,8pt] [rm]
+%D [tf=lbr sa 1,
+%D bf=lbd sa 1,
+%D it=lbi sa 1,
+%D sl=lbsl sa 1,
+%D bi=lbdi sa 1,
+%D bs=lbdi sa 1,
+%D tfa=lbr sa 1.200,
+%D tfb=lbr sa 1.440,
+%D tfc=lbr sa 1.728,
+%D tfd=lbr sa 2.074,
+%D sc=lbr sa 0.833]
+%D \stoptyping
+%D
+%D The second example shows that we can define more sizes at once. The main
+%D difference between these examples is that the Computer Modern Roman come in many
+%D design sizes. This means that there we cannot define them in bulk using \type
+%D {sa}. Instead of \type {rm} (roman) one can define \type {ss} (sans serif), \type
+%D {tt} (teletype), \type {hw} (hand written), \type {cg} (calygraphic) and whatever
+%D styles.
+%D
+%D The first argument may be a comma separated list. This, combined with
+%D specifications using \type{sa} can save a lot of typing. Although all arguments
+%D should be specified, we treat the second argument as optional.
+%D
+%D Defining a bodyfont involves two actions: defining the specific style related
+%D alternatives, like \type {\rma}, \type {\bfa} and \type {\rmsla}, and storing the
+%D definitions of their bodyfont size related fonts. The first step is bodyfont
+%D independant but executed every time. This permits user definitions like \type
+%D {\tfw} or \type {\bfq} for real large alternatives.
+%D
+%D If we move design size info to the lfg file (after all only lm has design sizes)
+%D we can get rid of much code .. 2012 or so.
+
+\installcorenamespace{fontdefinitions}
+
+% [class] [name] [rm,ss] [settings]
+% [class] [10pt,11pt] [rm,ss] [settings]
+% [class] [10pt,11pt] [rm,ss] [name]
+
+% [class] [name] [settings] == [name] [rm] [settings]
+% [class] [10pt,11pt] [settings] == [name] [rm] [settings]
+% [class] [10pt,11pt] [name] == [10pt,11pt] [rm] [name]
+
+\permanent\tolerant\protected\def\definebodyfont[#1]#*[#2]#*[#3]#*[#4]%
+ {\ifarguments
+ \or
+ \font_basics_define_body_font[#1][\s!rm][]%
+ \or
+ \font_basics_define_body_font[#1][\s!rm][#2]%
+ \or
+ \font_basics_define_body_font[#1][#2][#3]%
+ \or
+ \processcommacommand[#1]{\font_basics_define_body_font_class_given[#2][#3][#4]}%
+ \fi}
+
+\protected\def\font_basics_define_body_font[#whatever]%
+ {\doifelsenumber{#whatever}\font_basics_define_body_font_body\font_basics_define_body_font_name[#whatever]}
+
+\protected\def\font_basics_define_body_font_class_given[#1][#2][#3]#4%
+ {\push_macro_fontclass
+ \doifelse{#4}\s!default
+ {\let\fontclass\empty}
+ {\def\fontclass{#4}}%
+ \definebodyfont[#1][#2][#3]%
+ \pop_macro_fontclass}
+
+\protected\def\font_basics_define_body_font_body[#body][#style][#specification]%
+ {\ifcondition\validassignment{#specification}%
+ \expandafter\font_basics_define_body_font_body_assignment
+ \else
+ \expandafter\font_basics_define_body_font_body_identifier
+ \fi
+ [#body][#style][#specification]}%
+
+\protected\def\font_basics_define_body_font_name[#name][#style][#specification]%
+ {\ifcondition\validassignment{#specification}%
+ \expandafter\font_basics_define_body_font_name_assignment
+ \else
+ \expandafter\font_basics_define_body_font_name_identifier
+ \fi
+ [#name][#style][#specification]}%
+
+\protected\def\font_basics_define_body_font_body_assignment[#bodylist][#stylelist][#assignments]%
+ {\processcommalist[#bodylist]{\font_basics_define_body_font_body_assignment_a{#stylelist}{#assignments}}}
+
+\protected\def\font_basics_define_body_font_body_assignment_a#stylelist#assignments#body%
+ {\normalizebodyfontsize\m_font_asked_body{#body}%
+ % normally we define quite a lot in advance, i.e global defs
+ \font_helpers_check_bodyfont_environment\m_font_asked_body\m_font_asked_body % !!
+ \processcommalist[#stylelist]{\font_basics_define_body_font_body_assignment_b{#assignments}}}
+
+\protected\def\font_basics_define_body_font_body_assignment_b#assignments#style%
+ {\edef\m_font_asked_style{#style}%
+ \processcommalist[#assignments]\font_basics_define_body_font_defs}
+
+\protected\def\font_basics_define_body_font_defs
+ {\ifempty\fontclass
+ \expandafter\font_basics_define_body_font_defs_nop
+ \else
+ \expandafter\font_basics_define_body_font_defs_yes
+ \fi}
+
+\protected\def\font_basics_define_body_font_defs_yes_normal#assignment%
+ {\ifx\m_font_asked_style\s!mm
+ \expandafter\font_basics_define_body_font_yes_mm
+ \else
+ \expandafter\font_basics_define_body_font_yes_xx
+ \fi[#assignment]}
+
+\protected\def\font_basics_define_body_font_defs_nop_normal#assignment%
+ {\ifx\m_font_asked_style\s!mm
+ \expandafter\font_basics_define_body_font_nop_mm
+ \else
+ \expandafter\font_basics_define_body_font_nop_xx
+ \fi[#assignment]}
+
+\protected\def\font_basics_define_body_font_defs_yes_traced#assignment%
+ {\writestatus\m!fonts{[\fontclass] [\m_font_asked_body] [\m_font_asked_style] [#assignment]}%
+ \ifx\m_font_asked_style\s!mm
+ \expandafter\font_basics_define_body_font_yes_mm
+ \else
+ \expandafter\font_basics_define_body_font_yes_xx
+ \fi[#assignment]}
+
+\protected\def\font_basics_define_body_font_defs_nop_traced#assignment%
+ {\writestatus\m!fonts{[\fontclass] [\m_font_asked_body] [\m_font_asked_style] [#assignment]}%
+ \ifx\m_font_asked_style\s!mm
+ \expandafter\font_basics_define_body_font_nop_mm
+ \else
+ \expandafter\font_basics_define_body_font_nop_xx
+ \fi[#assignment]}
+
+\let\font_basics_define_body_font_defs_yes\font_basics_define_body_font_defs_yes_normal
+\let\font_basics_define_body_font_defs_nop\font_basics_define_body_font_defs_nop_normal
+
+\appendtoks
+ \let\font_basics_define_body_font_defs_yes\font_basics_define_body_font_defs_yes_traced
+ \let\font_basics_define_body_font_defs_nop\font_basics_define_body_font_defs_nop_traced
+\to \t_font_tracers_definitions
+
+%D We split into two characters (first part of spec) and the rest: the first two are
+%D the style and the rest is a size, although in practice one will seldom define the
+%D size directly. We might even drop that as it gives faster code.
+
+\protected\def\font_basics_define_body_font_nop_xx[#one#two#rest=#value]% local
+ {\ifcsname\m_font_asked_style#one#two#rest\endcsname\else\font_basics_check_fontname_combination\m_font_asked_style{#one#two}{#rest}\fi
+ \letcsname\??fontinstanceclass\m_font_asked_body-\m_font_asked_style-#one#two-#rest-0\endcsname\undefined
+ \protected\edefcsname\??fontinstanceready\m_font_asked_body-\m_font_asked_style-#one#two-#rest-0\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\m_font_asked_style-#one#two#rest}{\noexpand\font_rscale_xx{\m_font_asked_style}}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ \letcsname\??fontinstanceclass\m_font_asked_body-\m_font_asked_style-#one#two-#rest-4\endcsname\undefined
+ \protected\edefcsname\??fontinstanceready\m_font_asked_body-\m_font_asked_style-#one#two-#rest-4\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\m_font_asked_style-#one#two#rest}{\noexpand\font_rscale_xx{\m_font_asked_style}}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ \letcsname\??fontinstanceclass\m_font_asked_body-\m_font_asked_style-#one#two-#rest-5\endcsname\undefined
+ \protected\edefcsname\??fontinstanceready\m_font_asked_body-\m_font_asked_style-#one#two-#rest-5\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\m_font_asked_style-#one#two#rest}{\noexpand\font_rscale_xx{\m_font_asked_style}}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ }
+
+\protected\def\font_basics_define_body_font_yes_xx[#one#two#rest=#value]% global
+ {\ifcsname\m_font_asked_style#one#two#rest\endcsname\else\font_basics_check_fontname_combination\m_font_asked_style{#one#two}{#rest}\fi
+ \gletcsname\??fontinstanceclass\fontclass-\m_font_asked_body-\m_font_asked_style-#one#two-#rest-1\endcsname\undefined
+ \protected\xdefcsname\??fontinstanceready\fontclass-\m_font_asked_body-\m_font_asked_style-#one#two-#rest-0\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\m_font_asked_style-#one#two#rest-0}{\number\p_font_rscale}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ \gletcsname\??fontinstanceclass\fontclass-\m_font_asked_body-\m_font_asked_style-#one#two-#rest-2\endcsname\undefined
+ \protected\xdefcsname\??fontinstanceready\fontclass-\m_font_asked_body-\m_font_asked_style-#one#two-#rest-4\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\m_font_asked_style-#one#two#rest-4}{\number\p_font_rscale}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ \gletcsname\??fontinstanceclass\fontclass-\m_font_asked_body-\m_font_asked_style-#one#two-#rest-3\endcsname\undefined
+ \protected\xdefcsname\??fontinstanceready\fontclass-\m_font_asked_body-\m_font_asked_style-#one#two-#rest-5\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\m_font_asked_style-#one#two#rest-5}{\number\p_font_rscale}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ }
+
+% \writestatus{fonts}{define \m_asked_style\space yes: {\expandafter\meaning\csname\fontclass\m_font_asked_body\m_font_asked_style#one#two#rest\endcsname}
+
+%D Here the rest concerns rl or lr so in this case it is not a size specifier but
+%D a directional one.
+
+\protected\def\font_basics_define_body_font_nop_mm[#one#two#rest=#value]% local
+ {%\ifcsname\s!mm\endcsname\else\font_basics_check_fontname_combination\s!mm{#one#two}{#rest}\fi
+ \letcsname\??fontinstanceclass\m_font_asked_body-\s!mm-#one#two#rest-1\endcsname\undefined
+ % \letcsname\??fontinstanceclass\m_font_asked_body-\s!mm-#one#two#rest-2\endcsname\undefined
+ % \letcsname\??fontinstanceclass\m_font_asked_body-\s!mm-#one#two#rest-3\endcsname\undefined
+ \protected\xdefcsname\??fontinstanceready\m_font_asked_body-\s!mm-#one#two#rest\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\s!mm-#one#two#rest}{\noexpand\font_rscale_mm}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ }
+
+% \writestatus{fonts}{define \m_asked_style\space nop: \expandafter\meaning\csname\m_font_asked_body\m_font_asked_style#one#two#rest\endcsname}%
+
+\protected\def\font_basics_define_body_font_yes_mm[#one#two#rest=#value]% global
+ {%\ifcsname\s!mm\endcsname\else\font_basics_check_fontname_combination\s!mm{#one#two}{#rest}\fi
+ \gletcsname\??fontinstanceclass\fontclass-\m_font_asked_body-\s!mm-#one#two#rest-1\endcsname\undefined
+ % \gletcsname\??fontinstanceclass\fontclass-\m_font_asked_body-\s!mm-#one#two#rest-2\endcsname\undefined
+ % \gletcsname\??fontinstanceclass\fontclass-\m_font_asked_body-\s!mm-#one#two#rest-3\endcsname\undefined
+ \protected\xdefcsname\??fontinstanceready\fontclass-\m_font_asked_body-\s!mm-#one#two#rest\endcsname
+ {\font_helpers_trigger{\m_font_asked_body-\s!mm-#one#two#rest}{\number\p_font_rscale}{\m_font_asked_body}{\normalunexpanded{#value}}}%
+ }
+
+% \writestatus{fonts}{define \m_asked_style\space yes: \expandafter\meaning\csname\fontclass\m_font_asked_body\m_font_asked_style#one#two#rest\endcsname}%
+
+\protected\def\font_basics_define_body_font_body_identifier[#bodylist][#stylelist][#name]%
+ {\processcommalist[#bodylist]{\font_basics_define_body_font_body_identifier_a{#stylelist}{#name}}}
+
+\protected\def\font_basics_define_body_font_body_identifier_a#stylelist#name#body%
+ {\normalizebodyfontsize\m_font_asked_body{#body}%
+ \font_helpers_check_bodyfont_environment\m_font_asked_body\m_font_asked_body % !!
+ \processcommalist[#stylelist]{\font_basics_define_body_font_body_identifier_b{#name}}}
+
+\protected\def\font_basics_define_body_font_body_identifier_b#name#style%
+ {\edef\m_font_asked_style{#style}%
+ %\writestatus\m!fonts{[\fontclass] [\m_font_asked_body] [\m_font_asked_style] => [#name]}%
+ \csname\??fontdefinitions#name:\m_font_asked_style\endcsname} % no checking
+
+\protected\def\font_basics_define_body_font_name_assignment[#name][#stylelist][#assignments]%
+ {\processcommalist[#stylelist]{\font_basics_define_body_font_name_assignment_a{#name}{#assignments}}}
+
+\protected\def\font_basics_define_body_font_name_assignment_a#name#assignments#style%
+ {%\writestatus\m!fonts{[#name:#style] => [#assignments]}%
+ \edefcsname\??fontdefinitions#name:#style\endcsname{\font_basics_define_body_font_default{#assignments}}}
+
+\protected\def\font_basics_define_body_font_name_identifier[#name][#stylelist][#identifier]%
+ {\processcommalist[#stylelist]{\font_basics_define_body_font_name_identifier_a{#name}{#identifier}}}
+
+\protected\def\font_basics_define_body_font_name_identifier_a#name#identifier#style%
+ {%\writestatus\m!fonts{[#name:#style] => [##identifier:#style]}%
+ \ifcsname\??fontdefinitions#name:#style\endcsname
+ \letcsname\??fontdefinitions#name:#style\expandafter\endcsname\csname\??fontdefinitions#identifier:#style\endcsname
+ \else
+ \defcsname\??fontdefinitions#name:#style\endcsname{\csname\??fontdefinitions#identifier:#style\endcsname}%
+ \fi}
+
+%D The unknown:
+
+\newconditional\c_font_defining_unknown
+\newconditional\c_font_defining_state
+
+\protected\def\font_helpers_define_unknown_font#body% one level only
+ {\font_helpers_register_fontbody{#body}% prevents loop, can go
+ \setfalse\c_font_defining_state
+ \font_helpers_process_relative_size_list{\font_helpers_define_unknown_check_sizes{#body}}%
+ \ifconditional\c_font_defining_state
+ \setfalse\c_font_defining_state
+ \font_helpers_process_style_list{\font_helpers_define_unknown_check_definitions{#body}}%
+ \ifconditional\c_font_defining_state
+ \ifconditional\c_font_defining_environment_state\else
+ %\showmessage\m!fonts{14}{#body}% main
+ \clf_registerunknownbodysize{#body}%
+ \fi
+ \setfalse\c_font_defining_state
+ \font_helpers_register_fontbody{#body}%
+ % needed ?
+ \ifconditional\c_font_defining_unknown
+ \else
+ \settrue\c_font_defining_unknown
+ \font_helpers_process_relative_size_list{\font_helpers_define_unknown_check_relatives{#body}}%
+ \setfalse\c_font_defining_unknown
+ \fi
+ \fi
+ \fi}
+
+\def\font_helpers_define_unknown_check_sizes#body#relativesize%
+ {\ifcsname\??fontenvironments\s!default#relativesize\endcsname % fontclass ?
+ \expandafter\normalizebodyfontsize\csname\??fontenvironments#body#relativesize\endcsname{\csname\??fontenvironments\s!default#relativesize\endcsname\dimexpr#body\relax}%
+ \settrue\c_font_defining_state
+ \fi}
+
+\def\font_helpers_define_unknown_check_definitions#body#style%
+ {\ifcsname\??fontdefinitions\s!default:#style\endcsname
+ \edef\m_font_asked_body{#body}%
+ \edef\m_font_asked_style{#style}%
+ \lastnamedcs
+ \settrue\c_font_defining_state
+ \fi}
+
+\def\font_helpers_define_unknown_check_relatives#body#relativesize%
+ {\ifcsname\??fontbodyknown\csname\??fontenvironments#body#relativesize\endcsname\endcsname \else
+ \expandafter\font_helpers_define_unknown_font\csname\??fontenvironments#body#relativesize\endcsname
+ \settrue\c_font_defining_state
+ \fi}
+
+\protected\def\font_basics_define_body_font_default#assignments%
+ {\font_helpers_check_relative_font_size\m_font_asked_style % still needed here?
+ \ifcsname\m_font_asked_style\endcsname\else
+ \normalexpanded{\definefontstyle[\m_font_asked_style][\m_font_asked_style]}%
+ \fi
+ \processcommalist[#assignments]\font_basics_define_body_font_defs
+ \let\p_font_rscale\v_font_rscale_default}
+
+%D These macros show that quite some definitions take place. Fonts are not loaded
+%D yet! This means that at format generation time, no font files are present.
+
+\protected\def\font_basics_switch_points#body%
+ {\ifcsname\??fontbodyknown#body\endcsname \else
+ % we need to check the relative sizes for this body
+ \font_helpers_define_unknown_font{#body}%
+ \fi%
+ \ifcsname\??fontbodyknown#body\endcsname % always true now
+ \font_basics_complete_switch{#body}%
+ \localbodyfontsize#body\relax
+ \normalizebodyfontsize\normalizedbodyfontsize\localbodyfontsize
+ \font_helpers_check_bodyfont_environment\normalizedbodyfontsize\normalizedbodyfontsize % !!
+ \else
+ \showmessage\m!fonts4{#body}%
+ \fi
+ \ifconditional\c_font_compact
+ \glyphscale\numexpr\plushundred*\dimexpr\normalizedbodyfontsize\relax/\maxcard\relax
+ \fi}
+
+% \prependtoks
+% \ifconditional\c_font_compact
+% % better in lua ... accuracy
+% \glyphtextscale \numexpr\plusthousand*\dimexpr\textface \relax/\d_font_scaled_default\relax
+% \glyphscriptscale \numexpr\plusthousand*\dimexpr\scriptface \relax/\d_font_scaled_default\relax
+% \glyphscriptscriptscale \numexpr\plusthousand*\dimexpr\scriptscriptface\relax/\d_font_scaled_default\relax
+% \fi
+% \to \everymathematics
+
+\protected\def\font_basics_switch_style#style%
+ {\ifcsname\??fontstyle#style\endcsname
+ \lastnamedcs
+ \edef\fontstyle{#style}%
+ \ifmmode\mr\fi % in order to be compatible with \rm in math mode
+ % \the\everybodyfont % cleaner, in setting size as well as style
+ \else
+ \showmessage\m!fonts5{#style}%
+ \fi}
+
+%D Here comes the main font switching macros. These macros handle changes in size as
+%D well as returning to the global bodyfont size.
+
+\ifdefined\font_preloads_at_definition \else \let\font_preloads_at_definition\relax \fi
+
+\def\font_helpers_set_font#method#specification%
+ {\edef\m_font_specification{#specification}%
+ \ifempty\m_font_specification \else
+ \ifx\m_font_specification\v!global % we can have all kind of presets
+ \restoreglobalbodyfont
+ \else
+ \processcommacommand[\m_font_specification]{\font_helpers_set_font_check_size}%
+ \processcommacommand[\m_font_specification]{\font_helpers_set_font_set_font{#method}}%
+ \ifproductionrun
+ \font_preloads_at_definition
+ \font_basics_switch_points\normalizedbodyfontsize
+ \font_basics_switch_style\fontstyle
+ \ifempty\defaultfontclass
+ \let\defaultfontclass\fontclass
+ \fi
+ \fi
+ \fi
+ \currentxfontsize\zerocount
+ \fi}
+
+\def\font_helpers_set_font_check_size#option%
+ {\doifelsenumber{#option}{\font_helpers_check_bodyfont_environment{#option}{#option}}\donothing}
+
+\def\font_helpers_set_font_set_font#method#option% method=1: set, method=2: switch
+ {\doifsomething{#option}{\font_helpers_set_font_set_font_option{#method}{#option}}}
+
+\def\font_helpers_set_font_set_font_option#method#option%
+ {\doifelsenumber{#option}%
+ \font_helpers_set_font_set_font_option_body
+ \font_helpers_set_font_set_font_option_keyword
+ {#method}{#option}{#option}}
+
+\newmacro\m_font_keyword
+
+\protected\def\font_helpers_set_font_set_font_option_keyword#method#keyword#message%
+ {\edef\m_font_keyword{#keyword}%
+ \ifcsname\??fontenvironments\normalizedbodyfontsize\m_font_keyword\endcsname
+ \edef\m_font_step{\font_bodyfontvariable\m_font_keyword}%
+ \normalexpanded{\font_helpers_set_font_set_font_option_body{#method}{\m_font_step}{#message}}%
+ \orelse\ifx\m_font_keyword\v!reset
+ \let\fontstyle\empty % new 31/7/2006
+ \let\fontsize \empty
+ \orelse\ifcsname\??fontstyle\m_font_keyword\endcsname
+ \let\fontstyle\m_font_keyword
+ \else
+ \setcurrentfontclass\m_font_keyword
+ \ifcase#method\relax
+ \let\globalfontclass\globalfontclass % -)
+ \else
+ \let\globalfontclass\fontclass
+ \fi
+ \font_helpers_set_fontstyle_of_fontclass
+ \fi}
+
+\def\font_helpers_set_fontstyle_of_fontclass % will be overloaded later
+ {\let\fontstyle\s!rm}
+
+\protected\def\font_helpers_set_font_set_font_option_body#method#body#message%
+ {\normalizebodyfontsize\normalizedsetfont{#body}% redundant for some calls
+ \ifcsname\??fontbodyknown\normalizedsetfont\endcsname \else
+ \font_helpers_define_unknown_font\normalizedsetfont
+ \fi
+ \ifcsname\??fontbodyknown\normalizedsetfont\endcsname
+ \localbodyfontsize\normalizedsetfont
+ \let\normalizedbodyfontsize\normalizedsetfont
+ \else
+ \showmessage\m!fonts4{#message}%
+ %\font_helpers_set_font_set_font_option_body_fallbacks{#method}{#body}%
+ \fi}
+
+% we need to check the fontclass
+
+\permanent\def\registerfontclass#class%
+ {\letgvalue{\??fontclassyes#class}\v!yes} % global ?
+
+\permanent\def\setcurrentfontclass#class%
+ {\ifcsname\??fontclassyes#class\endcsname
+ \edef\fontclass{#class}%
+ \orelse\ifcsname\??fontclassnop#class\endcsname
+ % already tried
+ \else % too messy: \ifcase\currentgrouplevel % (unpredictable)
+ \fonts_helpers_try_currentfontclass{#class}%
+ \fi}
+
+\ifdefined\fonts_helpers_try_currentfontclass \else
+
+ \protected\def\fonts_helpers_try_currentfontclass#typeface%
+ {\letcsname\??fontclassnop#typeface\endcsname\empty}
+
+\fi
+
+% \let\defaultfontstyle \s!rm
+% \let\defaultfontalternative\s!tf
+% \let\defaultfontsize \empty
+% \let\defaultfontface \!!zerocount
+
+%D So far for synchronisation. (We can inline the following macros.)
+
+\permanent\protected\def\setcurrentfont#body#style#alternative#size% not used
+ {\edef\fontbody {#body}%
+ \edef\fontstyle {#style}%
+ \edef\fontalternative{#alternative}%
+ \edef\fontsize {#size}%
+ \font_helpers_check_big_math_synchronization
+ \font_helpers_synchronize_font}
+
+\permanent\protected\def\setcurrentfontbody#body% % not used
+ {\edef\fontbody{#body}%
+ \font_helpers_synchronize_font}
+
+% For Taco: optional fall backs:
+
+\ifdefined\font_typescripts_inherit_check \else
+ \let\font_typescripts_inherit_check\gobbleoneargument % implemented in type-ini
+\fi
+
+\protected\def\font_helpers_set_current_font_style#style%
+ {\edef\fontstyle{#style}%
+ \font_typescripts_inherit_check\fontstyle
+ \ifmmode\mr\fi % otherwise \rm not downward compatible ... not adapted yet
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_xsize_alternative#xsize#alternative%
+ {\edef\fontface{#xsize}%
+ \edef\fontalternative{#alternative}%
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_font_alternative#alternative%
+ {\edef\fontalternative{#alternative}%
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_font_size#size%
+ {\edef\fontsize{#size}%
+ \font_helpers_check_big_math_synchronization % double? better in everymath?
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_font_style_alternative#style#alternative% \rmsl
+ {\edef\fontstyle {#style}%
+ \edef\fontalternative{#alternative}%
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_font_style_size#style#size% \rma
+ {\edef\fontstyle{#style}%
+ \edef\fontsize {#size}%
+ \font_helpers_check_big_math_synchronization % double? better in everymath?
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_font_alternative_size#alternative#size% \sla
+ {\edef\fontalternative{#alternative}%
+ \edef\fontsize {#size}%
+ \font_helpers_check_big_math_synchronization % double? better in everymath?
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_set_current_font_style_alternative_size#style#alternative#size% \rmsla
+ {\edef\fontstyle {#style}%
+ \edef\fontalternative{#alternative}%
+ \edef\fontsize {#size}%
+ \font_helpers_check_big_math_synchronization % double? better in everymath?
+ \font_helpers_synchronize_font}
+
+\protected\def\font_helpers_synchronize_font % we can have dups i.e. no need to let fontstrategy
+ {\ifempty\fontclass
+ \applyfontstrategies
+ \else
+ \applyfontclassstrategies
+ \fi
+ \setfalse\c_font_auto_size
+ \ifskipfontcharacteristics
+ \setfontcharacteristics
+ \the\everyfontswitch
+ \fi}
+
+%D This is the resolver for special cases (sizes) and in practice it is not called
+%D that often so further optimization makes no sense.
+
+\def\font_helpers_check_strategy_class_a % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\fontclass-\fontbody-\fontstyle-\fontalternative-\fontsize-\fontface\endcsname
+ \setfalse\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_class_b
+ \fi}
+
+\def\font_helpers_check_strategy_class_b % --- --- --- def % pt tt bf
+ {\ifcsname\??fontinstanceready\fontclass-\fontbody-\fontstyle-\fontalternative-\defaultfontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_class_c
+ \fi}
+
+\def\font_helpers_check_strategy_class_c % --- --- def --- % pt tt tf a
+ {\ifcsname\??fontinstanceready\fontclass-\fontbody-\fontstyle-\defaultfontalternative-\fontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_class_d
+ \fi}
+
+\def\font_helpers_check_strategy_class_d % --- --- def def % pt tt tf
+ {\ifcsname\??fontinstanceready\fontclass-\fontbody-\fontstyle-\defaultfontalternative-\defaultfontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_class_e
+ \fi}
+
+\def\font_helpers_check_strategy_class_e % --- def def def % pt rm tf
+ {\ifcsname\??fontinstanceready\fontclass-\fontbody-\defaultfontstyle-\defaultfontalternative-\defaultfontsize-\fontface\endcsname
+ \setfalse\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_class_f
+ \fi}
+
+\def\font_helpers_check_strategy_class_f % def def def def % rm tf
+ {\ifcsname\??fontinstanceready\fontclass-\defaultfontbody-\defaultfontstyle-\defaultfontalternative-\defaultfontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_a
+ \fi}
+
+% no class
+
+\def\font_helpers_check_strategy_a % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\fontbody-\fontstyle-\fontalternative-\fontsize-\fontface\endcsname
+ \setfalse\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_b
+ \fi}
+
+\def\font_helpers_check_strategy_b % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\fontbody-\fontstyle-\fontalternative-\defaultfontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_c
+ \fi}
+
+\def\font_helpers_check_strategy_c % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\fontbody-\fontstyle-\defaultfontalternative-\fontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_d
+ \fi}
+
+\def\font_helpers_check_strategy_d % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\fontbody-\fontstyle-\defaultfontalternative-\defaultfontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_e
+ \fi}
+
+\def\font_helpers_check_strategy_e % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\fontbody-\defaultfontstyle-\defaultfontalternative-\defaultfontsize-\fontface\endcsname
+ \setfalse\c_font_auto_size
+ \lastnamedcs
+ \else
+ \expandafter\font_helpers_check_strategy_f
+ \fi}
+
+\def\font_helpers_check_strategy_f % --- --- --- --- % pt tt bf a
+ {\ifcsname\??fontinstanceready\defaultfontbody-\defaultfontstyle-\defaultfontalternative-\defaultfontsize-\fontface\endcsname
+ \settrue\c_font_auto_size
+ \lastnamedcs
+ \fi}
+
+\permanent\let\applyfontstrategies \font_helpers_check_strategy_a
+\permanent\let\applyfontclassstrategies\font_helpers_check_strategy_class_a
+
+%D Let's synchronize:
+
+\newconditional\c_font_synchronize \settrue\c_font_synchronize
+
+\prependtoks
+ \ifconditional\c_font_synchronize
+ \font_helpers_synchronize_math
+ \font_helpers_synchronize_font % problem: syncs last font
+ \fi
+\to \everybodyfont
+
+%D Setting the normal sizes as well as the x and xx smaller sizes is accomplished by
+%D the next set of macros. When in math mode, the commands \type {\tx} and \type
+%D {\txx} are just a switch to the script and double script styles, but in text mode
+%D the values defined by the bodyfontenvironment are used. Here we also set \type
+%D {\currentxfontsize}.
+
+\def\font_helpers_set_current_font_xxx_alternative#alternative#xsize#scriptstyle%
+ {\ifmmode
+ #scriptstyle%
+ \else
+ \font_helpers_set_current_xsize_alternative{#xsize}{#alternative}%
+ \fi}
+
+\def\font_helpers_reset_x_fontsize
+ {\ifcase\currentxfontsize\else
+ \currentxfontsize\zerocount
+ % also \sx and \sxx ?
+ \enforced\let\tx \normaltx
+ \enforced\let\txx\normaltxx
+ \fi}
+
+\let\font_helpers_check_nested_x_fontsize\relax
+
+\def\font_helpers_set_current_font_x_alternative#alternative%
+ {\font_helpers_check_nested_x_fontsize
+ \font_helpers_set_current_font_xxx_alternative{#alternative}{4}\scriptstyle
+ \currentxfontsize\plusone
+ \enforced\let\tx\txx}
+
+\def\font_helpers_set_current_font_xx_alternative#alternative%
+ {\font_helpers_check_nested_x_fontsize
+ \font_helpers_set_current_font_xxx_alternative{#alternative}{5}\scriptscriptstyle
+ \currentxfontsize\plustwo
+ \enforced\let\tx\empty
+ \enforced\let\txx\empty}
+
+%D This alterative is not really needed, but for old time's sake we keep it there.
+%D We can speed it up when needed.
+
+\def\font_helpers_set_current_font_x_style_alternative #alternative{\csname#alternative\endcsname\tx}
+\def\font_helpers_set_current_font_xx_style_alternative#alternative{\csname#alternative\endcsname\txx}
+
+%D These macros also show us that when we call for \type {\tx}, this macro is
+%D redefined to be \type {\txx}. Therefore calls like:
+%D
+%D \startbuffer
+%D {small \tx is \tx beautiful}
+%D {small \tx is \txx beautiful}
+%D {small \txx is \tx beautiful}
+%D {small \txx is \txx beautiful}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D result in:
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D Setting the main size involves the style list and therefore takes a bit more
+%D time. Keep in mind that the fontsize is represented by a character or empty.
+
+\installcorenamespace{fontscalex}
+\installcorenamespace{fontscalexx}
+
+\newconditional\c_font_inherit_scale
+
+\def\font_scale_inherit#1%
+ {\begingroup
+ \scratchcounterone\fontid\font\relax
+ \currentxfontsize\plusone
+ \normalexpanded{\definedfont[\clf_specifiedfont\scratchcounterone\font_currentfontscale\relax]}%
+ \scratchcountertwo\fontid\font\relax
+ \currentxfontsize\plustwo
+ \normalexpanded{\definedfont[\clf_specifiedfont\scratchcounterone\font_currentfontscale\relax]}%
+ \scratchcounterthree\fontid\font\relax
+ % parent -> x -> xx
+ % parent -> xx
+ %
+ % this can become \integerdef
+ %
+ \global\expandafter\chardef\csname\??fontscalex \number\scratchcounterone\endcsname\scratchcountertwo
+ \global\expandafter\chardef\csname\??fontscalexx\number\scratchcounterone\endcsname\scratchcounterthree
+ \global\expandafter\chardef\csname\??fontscalex \number\scratchcountertwo\endcsname\scratchcounterthree
+ \global\expandafter\chardef\csname\??fontscalexx\number\scratchcountertwo\endcsname\scratchcounterthree
+ \endgroup
+ \setfontid\csname#1\number\fontid\font\endcsname}
+
+\def\font_scale_inherit_x
+ {\ifcsname\??fontscalex\number\fontid\font\endcsname
+ \setfontid\lastnamedcs
+ \else
+ \font_scale_inherit\??fontscalex
+ \fi
+ \ifskipfontcharacteristics
+ \setfontcharacteristics
+ \the\everyfontswitch
+ \fi}
+
+\def\font_scale_inherit_xx
+ {\ifcsname\??fontscalexx\number\fontid\font\endcsname
+ \setfontid\lastnamedcs
+ \else
+ \font_scale_inherit\??fontscalexx
+ \fi
+ \ifskipfontcharacteristics
+ \setfontcharacteristics
+ \the\everyfontswitch
+ \fi}
+
+\def\font_scale_defined_x
+ {\let\fontface\!!plusfour
+ \let\fontalternative\fontalternative
+ \font_helpers_synchronize_font}
+
+\def\font_scale_defined_xx
+ {\let\fontface\!!plusfive
+ \let\fontalternative\fontalternative
+ \font_helpers_synchronize_font}
+
+% to freeze or not ...
+
+\permanent\protected\def\tx
+ {\currentxfontsize\plusone
+ \ifmmode
+ \scriptstyle
+ \orelse\ifconditional\c_font_inherit_scale
+ \font_scale_inherit_x
+ \else
+ \font_scale_defined_x
+ \fi
+ \enforced\let\tx\txx}
+
+\permanent\protected\def\txx
+ {\currentxfontsize\plustwo
+ \ifmmode
+ \scriptscriptstyle
+ \orelse\ifconditional\c_font_inherit_scale
+ \font_scale_inherit_xx
+ \else
+ \font_scale_defined_xx
+ \fi
+ \enforced\let\tx \empty
+ \enforced\let\txx\empty}
+
+\permanent\protected\def\sx
+ {\currentxfontsize\plusone
+ \ifmmode
+ \scriptstyle
+ \else
+ \font_scale_inherit_x
+ \fi
+ \enforced\let\tx\txx
+ \enforced\let\sx\sxx}
+
+\permanent\protected\def\sxx
+ {\currentxfontsize\plustwo
+ \ifmmode
+ \scriptscriptstyle
+ \else
+ \font_scale_inherit_xx
+ \fi
+ \enforced\let\tx \empty
+ \enforced\let\txx\empty
+ \enforced\let\sx \empty
+ \enforced\let\sxx\empty}
+
+\permanent\protected\def\useinheritxsizes{\settrue \c_font_inherit_scale} % not yet public, playground for WS and me
+\permanent\protected\def\usedefinedxsizes{\setfalse\c_font_inherit_scale} % not yet public, playground for WS and me
+
+\aliased\let\normaltx \tx
+\aliased\let\normaltxx\txx
+
+\aliased\let\normalsx \sx
+\aliased\let\normalsxx\sxx
+
+%D When asking for a complete font switch, for instance from 10 to 12~points, the
+%D next macro does the job. First we normalize the size, next we define the current
+%D range of text, script and scriptscript sizes, then we set the text fonts and the
+%D math families and finally we activate the default typeface and also set the font
+%D specific parameters assigned to \type {\everybodyfont}.
+
+\permanent\protected\def\textface {\currentbodyfontdimension\s!text }
+\permanent\protected\def\scriptface {\currentbodyfontdimension\s!script }
+\permanent\protected\def\scriptscriptface{\currentbodyfontdimension\s!scriptscript}
+\permanent\protected\def\xtextface {\currentbodyfontdimension\s!x }
+\permanent\protected\def\xxtextface {\currentbodyfontdimension\s!xx }
+
+\installcorenamespace{fontbodyfaces}
+
+\protected\def\font_basics_complete_switch#size%
+ {\bodyfontsize#size\relax
+ \normalizebodyfontsize\normalizedbodyfontsize\bodyfontsize
+ \expandafter\let\expandafter\font_basics_set_faces\csname\??fontbodyfaces\fontbody\endcsname
+ \ifrelax\font_basics_set_faces
+ \font_basics_set_faces_preset
+ \fi
+ \font_basics_set_faces}
+
+\def\font_basics_set_faces_preset
+ {\edef\font_basics_set_faces{% 0.2 sec on 10K \tfa
+ \enforced\permanent\protected\def\textface {\currentbodyfontdimension\s!text }%
+ \enforced\permanent\protected\def\scriptface {\currentbodyfontdimension\s!script }%
+ \enforced\permanent\protected\def\scriptscriptface{\currentbodyfontdimension\s!scriptscript}%
+ \enforced\permanent\protected\def\xtextface {\currentbodyfontdimension\s!x }%
+ \enforced\permanent\protected\def\xxtextface {\currentbodyfontdimension\s!xx }%
+ }%
+ \gletcsname\??fontbodyfaces\fontbody\endcsname\font_basics_set_faces}
+
+\permanent\def\currentbodyfontdimension#parameter% there can be factors here
+ {\the\dimexpr
+ \ifcsname\??fontenvironments\fontclass\normalizedbodyfontsize#parameter\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??fontenvironments\fontclass\s!default#parameter\endcsname
+ \lastnamedcs
+ \dimexpr\normalizedbodyfontsize\relax
+ \orelse\ifcsname\??fontenvironments\normalizedbodyfontsize#parameter\endcsname
+ \lastnamedcs
+ \else
+ \csname\??fontenvironments\s!default#parameter\endcsname
+ \dimexpr\normalizedbodyfontsize\relax
+ \fi
+ \relax}
+
+%D \macros
+%D {setupbodyfont,switchtobodyfont}
+%D
+%D The next two macros are user ones. With \type {\setupbodyfont} one can set the
+%D document bodyfont size, font family, style and/or options defined in files, for
+%D example:
+%D
+%D \starttyping
+%D \setupbodyfont[modern,12pt,roman]
+%D \stoptyping
+%D
+%D This command affects the document as a whole: text, headers and footers. The
+%D second macro however affects only the text:
+%D
+%D \starttyping
+%D \switchtobodyfont[10pt]
+%D \stoptyping
+%D
+%D So we've got:
+%D
+%D \showsetup{setupbodyfont}
+%D \showsetup{switchtobodyfont}
+%D
+%D Both macros look alike. The second one also has to take all kind of keywords into
+%D account.
+
+\ifdefined\saveinterlinespace \else \let\saveinterlinespace \relax \fi
+\ifdefined\restoreinterlinespace \else \let\restoreinterlinespace\relax \fi
+
+% \newtoks \everysetupbodyfont
+% \newtoks \everyswitchtobodyfont
+
+\permanent\protected\def\setupbodyfont
+ {\doifelsenextoptionalcs\font_basics_setupbodyfont_yes\font_basics_setupbodyfont_nop}
+
+\def\font_basics_setupbodyfont_nop
+ {\restoreglobalbodyfont
+ \saveinterlinespace}
+
+\def\font_basics_setupbodyfont_yes[#specification]%
+ {\doifsomething{#specification}
+ {\font_helpers_set_font\plusone{#specification}%
+ \globalbodyfontsize\localbodyfontsize
+ \normalizebodyfontsize\normalizedglobalbodyfontsize\globalbodyfontsize
+ \let\globalfontstyle\fontstyle
+ \ifproductionrun
+ \the\everybodyfont
+ \the\everyglobalbodyfont
+ \saveinterlinespace
+ \fi
+ \the\everysetupbodyfont}}
+
+\protected\def\font_basics_switchtobodyfont#specification%
+ {\edef\m_font_step{\font_bodyfontvariable{#specification}}%
+ \ifempty\m_font_step
+ \font_helpers_set_font\zerocount{#specification}%
+ \else
+ \font_helpers_switch_bodyfont_step % so we have a fast [small] switch
+ \fi
+ \the\everybodyfont
+ \the\everyswitchtobodyfont}
+
+\permanent\protected\def\switchtobodyfont[#specification]% could become an ifx
+ {\doifsomething{#specification}{\font_basics_switchtobodyfont{#specification}}}
+
+\permanent\protected\def\usebodyfontparameter#1%
+ {\edef\m_font_bodyfont_asked{#1\c!bodyfont}%
+ \ifempty\m_font_bodyfont_asked\else
+ \font_basics_switchtobodyfont\m_font_bodyfont_asked
+ \fi}
+
+\def\font_helpers_switch_bodyfont_step
+ {\font_basics_switch_points\m_font_step
+ \font_basics_switch_style \fontstyle}
+
+%D The following alternative is meant for math||to||text switching and will be
+%D optimized.
+
+\permanent\protected\def\fastswitchtobodyfont#name%
+ {\ifcsname\??fontenvironments\normalizedbodyfontsize#name\endcsname
+ \edef\p_bodyfont{\lastnamedcs}%
+ \ifcsname\??fontbodyknown\p_bodyfont\endcsname
+ \font_basics_complete_switch\p_bodyfont
+ \localbodyfontsize\p_bodyfont\relax
+ \fi
+ \fi
+ \csname\??fontstyle\fontstyle\endcsname
+ \the\everybodyfont}
+
+%D \starttyping
+%D $\cases{& \ccaron}$ $x=\hbox{\ccaron $x=\hbox{\ccaron}$}$
+%D \stoptyping
+
+%D \macros
+%D {usebodyfont}
+%D
+%D This looks nicer then a switch in the preamble
+%D
+%D \starttyping
+%D \usebodyfont[pagella,10pt]
+%D \usebodyfont[termes,10pt]
+%D \usebodyfont[dejavu,10pt]
+%D
+%D \setupbodyfont[dejavu]
+%D
+%D \starttext
+%D test
+%D \stoptext
+%D \stoptyping
+
+\permanent\protected\def\usebodyfont[#1]%
+ {\ifempty\fontclass
+ \setupbodyfont[#1]%
+ \else
+ \switchtobodyfont[#1]%
+ \fullrestoreglobalbodyfont
+ \fi}
+
+\fetchmodulecommand \showbodyfontstate \f!font_run
+
+%D Handy for manuals:
+
+%D The \type {\tochar} commmand takes a specification:
+%D
+%D \starttabulate[|l|l|l|]
+%D \NC e \NC entity \NC e:eacute \NC \NR
+%D \NC x \NC hexadecimal unicode \NC x:013D \NC \NR
+%D \NC d \NC decimal unicode \NC d:123 \NC \NR
+%D \NC s \NC hexadecimal index (slot) \NC s:210D \NC \NR
+%D \NC i \NC decimal index \NC i:456 \NC \NR
+%D \NC n \NC name \NC n:eight \NC \NR
+%D \NC c \NC name \NC c:x \NC \NR
+%D \NC u \NC unicode descriptions \NC u:dog \NC \NR
+%D \NC a \NC all (also descriptions) \NC a:rewind \NC \NR
+%D \stoptabulate
+%D
+%D This is an expandable command!
+
+\permanent\protected\def\fontchar #character{\clf_fontchar{#character}}
+\permanent\protected\def\fontcharbyindex #index{\clf_fontcharbyindex#index\relax}
+\permanent \def\tochar #specifications{\clf_tochar{#specifications}} % expanded (also used in edef)
+
+%D The next auxilliary macro is an alternative to \type {\fontname}.
+
+\permanent\def\purefontname#font{\clf_purefontname{\fontname#font}}
+
+%D \macros
+%D {switchstyleonly}
+%D
+%D For switching a style but keeping the alternative, there
+%D is:
+%D
+%D \starttyping
+%D {\bf text \switchstyleonly\ss text}
+%D {\bf text \switchstyleonly[ss]text}
+%D {\sl text \switchstyleonly[sansserif]text}
+%D \stoptyping
+
+\permanent\protected\def\switchstyleonly
+ {\doifelsenextoptionalcs\font_basics_switch_style_only_opt\font_basics_switch_style_only_arg}
+
+\def\font_basics_switch_style_only_arg#name% stupid version
+ {\font_helpers_set_current_font_style{\csname\??fontshortstyle\checkedstrippedcsname#name\endcsname}%
+ \the\everybodyfont} % needed ?
+
+\def\font_basics_switch_style_only_opt[#name]% todo : check
+ {\font_helpers_set_current_font_style{\csname\??fontshortstyle#name\endcsname}%
+ \the\everybodyfont} % needed ?
+
+%D \macros
+%D {definebodyfontswitch}
+%D
+%D \PLAIN\ \TEX\ defines some macro's like \type {\tenpoint} to switch to a specific
+%D bodyfontsize. Just for the sake of compatibility we can define them like:
+%D
+%D \starttyping
+%D \definebodyfontswitch [twelvepoint] [12pt]
+%D \stoptyping
+%D
+%D We don't support language specific synonyms here.
+
+\permanent\tolerant\protected\def\definebodyfontswitch[#command]#spacer[#specification]% no longer a commalist (not useful)
+ {\frozen\protected\defcsname#command\endcsname{\switchtobodyfont[#specification]}}%
+
+%D \macros
+%D {setsmallbodyfont,setmainbodyfont,setbigbodyfont}
+%D
+%D When we're typesetting at for instance 10pt, we can call for the \type {small} as
+%D well as the \type {big} alternative, related to this main size, using \type
+%D {\switchtobodyfont[small]}. The three alternatives can be activated by the next
+%D three system calls and are defined by the bodyfontenvironment.
+
+\newmacro\m_font_step
+
+\def\font_helpers_set_bodyfont_step#step%
+ {\edef\m_font_step{\font_bodyfontvariable{#step}}% not always \cs
+ \font_basics_switch_points\m_font_step
+ \font_basics_switch_style \fontstyle}
+
+\permanent\protected\def\setsmallbodyfont{\font_helpers_set_bodyfont_step\v!small\the\everybodyfont}
+\permanent\protected\def\setbigbodyfont {\font_helpers_set_bodyfont_step\v!big \the\everybodyfont}
+
+\permanent\protected\def\setmainbodyfont
+ {\font_basics_switch_points\normalizedbodyfontsize
+ \font_basics_switch_style\fontstyle
+ \the\everybodyfont
+ \the\everyglobalbodyfont
+ \saveinterlinespace}
+
+%D \macros
+%D {restoreglobalbodyfont}
+%D
+%D Users can set whatever font available while typesetting text. Pagenumbers,
+%D footers, headers etc. however must be typeset in the main bodyfont and style of
+%D the document. Returning to the global state can be done with the next macro:
+%D
+%D This macro has to be called when entering the pagebody handling routine as well
+%D as the footnote insert routine. Users can access this feature |<|for instance
+%D when one wants to typeset tables and alike in the main bodyfont and style while
+%D the running text is temporary set to a smaller one|>| by saying \type
+%D {\switchtobodyfont [global]}.
+
+\mutable\let\globalfontstyle\s!rm
+
+\permanent\protected\def\fullrestoreglobalbodyfont
+ {\let\fontsize\defaultfontsize
+ \let\fontbody\defaultfontbody
+ \let\fontface\defaultfontface
+ \currentxfontsize\zerocount
+ \let\fontclass\globalfontclass
+ \font_basics_switch_points\normalizedglobalbodyfontsize
+ \font_basics_switch_style\globalfontstyle
+ \redoconvertfont % just in case a pagebreak occurs
+ \tf
+ \the\everybodyfont
+ \the\everyglobalbodyfont
+ \saveinterlinespace}
+
+\permanent\protected\def\partialrestoreglobalbodyfont
+ {\let\fontsize\defaultfontsize
+ \let\fontbody\defaultfontbody
+ \let\fontface\defaultfontface
+ \currentxfontsize\zerocount
+ \redoconvertfont
+ \tf
+ \the\everybodyfont % indeed needed
+ \the\everyglobalbodyfont % indeed needed
+ \saveinterlinespace}
+
+\permanent\protected\def\restoreglobalbodyfont % ook style etc
+ {\ifx\fontclass\globalfontclass
+ \ifx\fontstyle\globalfontstyle
+ \ifx\normalizedbodyfontsize\normalizedglobalbodyfontsize
+ \partialrestoreglobalbodyfont
+ \else
+ \fullrestoreglobalbodyfont
+ \fi
+ \else
+ \fullrestoreglobalbodyfont
+ \fi
+ \else
+ \fullrestoreglobalbodyfont
+ \fi}
+
+% in case of troubles: \let\restorebodyfont\fullrestoreglobalbodyfont
+
+%D Here are some fast variants that can be used in cases where no font system is
+%D needed and where fonts are frozen:
+%D
+%D \starttyping
+%D \definefont [TestA][Serif at 10pt]
+%D \predefinefont[TestB][Serif at 20pt]
+%D
+%D \testfeatureonce{1000}{{\TestA}} % .312
+%D \testfeatureonce{1000}{{\TestB}} % < .016
+%D \testfeatureonce{1000}{{\definedfont[Serif at 30pt]}} % .312
+%D \testfeatureonce{1000}{{\predefinedfont[Serif at 40pt]}} % < .016
+%D \stoptyping
+
+\installcorenamespace{predefinedfont}
+
+\permanent\protected\def\predefinefont[#1]#*[#2]% global !
+ {\protected\gdefcsname#1\endcsname{\font_basics_predefine{#1}{#2}}}
+
+\permanent\protected\def\predefinedfont[#1]% global !
+ {\ifcsname\??predefinedfont#1\endcsname
+ \lastnamedcs
+ \else
+ \font_basics_predefined{#1}%
+ \fi}
+
+\protected\def\font_basics_predefine#1#2%
+ {\definedfont[#2]%
+ \gletcsname#1\expandafter\endcsname\csname\v_font_identifier_basic\endcsname}
+
+\protected\def\font_basics_predefined#1%
+ {\font_basics_predefine{\??predefinedfont#1}{#1}}
+
+%D Handy helper:
+
+\permanent\protected\def\savedefinedfont[#1]% hm, how about scales
+ {\bgroup
+ \definedfont[#1]%
+ \xdef\saveddefinedfontid {\number\fontid\font}%
+ \xdef\saveddefinedfontname{\fontname\font}%
+ \egroup}
+
+\mutable\def\saveddefinedfontid {\number\fontid\font}
+\mutable\def\saveddefinedfontname{\fontname\font}
+
+%D Ugly helper:
+
+\permanent\protected\def\saverunningstyleandcolor
+ {\enforced\permanent\protected\edef\restorerunningstyleandcolor
+ {\setfontid \number\fontid\font
+ \c_attr_colormodel \the\c_attr_colormodel
+ \c_attr_color \the\c_attr_color
+ \c_attr_transparency\the\c_attr_transparency
+ \relax}}
+
+\aliased\let\restorerunningstyleandcolor\relax
+
+%D Handy for defining additional glyphs:
+
+% todo: public implementors
+
+\aliased\let\getprivateglyphslot\clf_getprivateglyphslot % kind of private macro
+
+\aliased\let\getprivatechar \clf_getprivatechar % gives back a utf !
+\aliased\let\getprivatemathchar \clf_getprivatemathchar % gives back a utf !
+\aliased\let\getprivateslot \clf_getprivateslot % companion to fonts.helpers.addprivate
+
+% \protected\def\getprivatemathchar#1%
+% {\begingroup\the\textfont\zerocount\getprivatechar{#1}\endgroup}
+
+\permanent\protected\def\privatechar % the text variant gets expanded to utf
+ {\ifmmode
+ \expandafter\getprivatemathchar
+ \else
+ \expandafter\getprivatechar
+ \fi}
+
+%D Some fonts can have color specifiers:
+%D
+%D \starttyping
+%D \definefontfeature[seguiemj-cl][default][colr=yes,ccmp=yes,dist=yes]
+%D \definefontsynonym[emoji][seguiemj*seguiemj-cl]
+%D
+%D \definecolor[emoji-red] [r=.4]
+%D \definecolor[emoji-gray][s=1,t=.5,a=1]
+%D
+%D %definefontcolorpalette [emoji-r] [emoji-red,emoji-gray,textcolor] % bad
+%D \definefontcolorpalette [emoji-r] [emoji-red,emoji-gray] % okay
+%D
+%D \definefontfeature[seguiemj-r][ccmp=yes,dist=yes,colr=emoji-r]
+%D
+%D \definefont[MyEmojiR][seguiemj*seguiemj-r @ 100pt]
+%D
+%D \startTEXpage[offset=10pt]
+%D \MyEmojiR\resolvedemoji{triangular ruler}
+%D \stopTEXpage
+%D \stoptyping
+
+\permanent\tolerant\protected\def\definefontcolorpalette[#1]#*[#2]%
+ {\clf_definefontcolorpalette{#1}{#2}}
+
+%D \macros
+%D {addfontpath}
+%D
+%D A way to add a path at runtime (no need to generate database):
+
+\permanent\protected\def\usefontpath[#1]%
+ {\clf_addfontpath{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-lib.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-lib.mklx
new file mode 100644
index 00000000000..72c2d4ba9f5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-lib.mklx
@@ -0,0 +1,156 @@
+%D \module
+%D [ file=font-lib,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Libraries,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Libraries}
+
+\unprotect
+
+\registerctxluafile{font-ini}{autosuffix}
+\registerctxluafile{font-log}{}
+\registerctxluafile{font-con}{autosuffix} % kind of early
+%registerctxluafile{font-cft}{}
+\registerctxluafile{font-enc}{}
+\registerctxluafile{font-fmp}{autosuffix}
+\registerctxluafile{font-agl}{} % if needed we can comment this and delay loading
+\registerctxluafile{font-cid}{} % cid maps
+\registerctxluafile{font-map}{optimize}
+
+% helpers
+
+\registerctxluafile{font-otr}{optimize} % opentype fontloader
+\registerctxluafile{font-web}{} % opentype fontloader
+\registerctxluafile{font-cff}{optimize} % quadratic outlines
+\registerctxluafile{font-ttf}{optimize} % cubic outlines
+\registerctxluafile{font-dsp}{optimize} % ... for this one
+\registerctxluafile{font-hsh}{} % hashes used by context
+\registerctxluafile{font-vir}{}
+\registerctxluafile{font-vfc}{autosuffix}
+\registerctxluafile{font-prv}{} % needs hashes
+\registerctxluafile{font-nod}{optimize}
+\registerctxluafile{font-oti}{} % otf initialization
+\registerctxluafile{font-ott}{} % otf tables (first)
+\registerctxluafile{font-otl}{}
+\registerctxluafile{font-oto}{}
+\registerctxluafile{font-otj}{autosuffix,optimize}
+\registerctxluafile{font-oup}{}
+\registerctxluafile{font-ota}{autosuffix}
+% \registerctxluafile{font-ots-pre-scale}{autosuffix,optimize}
+\registerctxluafile{font-ots}{autosuffix,optimize}
+\registerctxluafile{font-otd}{optimize}
+\registerctxluafile{font-otc}{}
+\registerctxluafile{font-oth}{}
+\registerctxluafile{font-osd}{}
+%registerctxluafile{font-osm}{}
+\registerctxluafile{font-ogr}{autosuffix}
+
+% we use otf code for type one
+
+\registerctxluafile{font-onr}{optimize}
+\registerctxluafile{font-one}{optimize}
+\registerctxluafile{font-afk}{}
+
+% tfm
+
+\registerctxluafile{font-tpk}{optimize}
+\registerctxluafile{font-tfm}{}
+
+% name database
+
+\registerctxluafile{font-syn}{}
+\registerctxluafile{font-trt}{}
+
+% shapes
+
+\registerctxluafile{font-shp}{}
+
+% so far
+
+\registerctxluafile{font-pat}{} % patchers
+
+\registerctxluafile{node-fnt}{autosuffix} % here
+
+\registerctxluafile{font-mps}{autosuffix} % outline fun
+
+\registerctxluafile{font-lua}{}
+
+\registerctxluafile{font-enh}{autosuffix}
+
+\registerctxluafile{good-ini}{}
+\registerctxluafile{good-gen}{}
+\registerctxluafile{good-ctx}{}
+\registerctxluafile{good-mth}{autosuffix}
+
+\registerctxluafile{font-def}{autosuffix}
+\registerctxluafile{font-ctx}{autosuffix} % after def as it overloads
+
+% extensions, order matters
+
+\registerctxluafile{font-imp-ligatures}{}
+\registerctxluafile{font-imp-tex}{}
+\registerctxluafile{font-imp-reorder}{}
+\registerctxluafile{font-imp-properties}{}
+\registerctxluafile{font-imp-unicode}{}
+\registerctxluafile{font-imp-math}{autosuffix}
+\registerctxluafile{font-imp-notused}{}
+\registerctxluafile{font-imp-effects}{}
+\registerctxluafile{font-imp-quality}{}
+\registerctxluafile{font-imp-italics}{}
+\registerctxluafile{font-imp-dimensions}{}
+\registerctxluafile{font-imp-spacekerns}{}
+\registerctxluafile{font-imp-tweaks}{}
+\registerctxluafile{font-imp-combining}{}
+
+\doifelsefileexists{font-imp-scripts.lua} {
+ \registerctxluafile{font-imp-scripts}{}
+} {
+ % not yet, lmtx feature
+}
+
+\registerctxluafile{font-imp-tracing}{autosuffix} % comes last!
+
+\registerctxluafile{font-fbk}{autosuffix}
+
+\registerctxluafile{font-aux}{}
+
+\registerctxluafile{font-lig}{} % only for experiments so try to avoid it
+
+%D Some low level helpers
+%D
+%D \starttyping
+%D \def\TestLookup#specification%
+%D {\dolookupfontbyspec{#specification}
+%D pattern: #specification, found: \dolookupnoffound
+%D \blank
+%D \dorecurse {\dolookupnoffound} {%
+%D \recurselevel:~\dolookupgetkeyofindex{fontname}{\recurselevel}\quad
+%D }%
+%D \blank}
+%D
+%D \TestLookup{familyname=helveticaneue}
+%D \TestLookup{familyname=helveticaneue,weight=bold}
+%D \TestLookup{familyname=helveticaneue,weight=bold,style=italic}
+%D \stoptyping
+
+%D We can also move the lookups to the fonts.namespace (of commands) one can also
+%D directly use the clf calls when speed is needed.
+
+% todo: public implementors:
+
+\permanent \def\dolookupfontbyspec #1{\clf_fontlookupinitialize{#1}}
+\permanent \def\dolookupnoffound {\clf_fontlookupnoffound}
+\permanent \def\dolookupgetkeyofindex#1#2{\clf_fontlookupgetkeyofindex{#1}\numexpr#2\relax}
+\permanent \def\dolookupgetkey #1{\clf_fontlookupgetkey{#1}}
+\permanent \def\cleanfontname #1{\clf_cleanfontname{#1}}
+\permanent\protected\def\setfontofid #1{\clf_setfontofid\numexpr#1\relax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-mat.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-mat.mklx
new file mode 100644
index 00000000000..3fc6275a65d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-mat.mklx
@@ -0,0 +1,595 @@
+%D \module
+%D [ file=font-mat,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Math,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Math}
+
+\unprotect
+
+%D Be nice:
+
+\ifdefined\??fontinstanceready \else \installcorenamespace{fontinstanceready} \fi
+\ifdefined\??fontinstancebasic \else \installcorenamespace{fontinstancebasic} \fi
+\ifdefined\??fontinstanceclass \else \installcorenamespace{fontinstanceclass} \fi
+
+%D The order 3 2 1 of size matters: needed for math-fbk relative size storage!
+
+%D \macros
+%D {textonly}
+%D
+%D Traditionally math has a big impact on font definitions, mainly because we need
+%D to define alphabet variants using families and fonts. This means that one can
+%D easily get 10 fonts loaded per math size. In \MKIV\ we use a different approach:
+%D one family which has either a virtual font made of traditional fonts, or an
+%D \OPENTYPE\ font that has it all.
+%D
+%D We currently use only one math family but in the future we might consider using a
+%D second one for bold math. For the moment we keep the \MKII\ method of using a
+%D token register for definitions but we already dropped the text and symbols ones
+%D since they now live in the same family.
+
+\newtoks \t_font_math_strategies
+\newconditional\c_font_synchronize_math_fonts \settrue\c_font_synchronize_math_fonts
+
+\protected\def\font_helpers_synchronize_math % math stuff in mmode
+ {\ifconditional\c_font_synchronize_math_fonts\the\t_font_math_strategies\fi}
+
+\permanent\protected\def\textonly{\setfalse\c_font_synchronize_math_fonts} % document this
+
+%D The main math font definer. We have removed some optimized code simply because we
+%D now always have a fontclass. We could check for fontclass being default or empty
+%D and save a few tests but it does not help us when no math is defined.
+%D
+%D Because we want to keep mr=.. and mb=... settings (read: not break downward
+%D compatibility by enforcing mrlr etc) we need a bit more code that optimal.
+
+% todo: \c_font_fam_mr
+
+\aliased\let\c_font_fam_mr \zerocount % math regular
+\aliased\let\c_font_fam_mr_lr\plusone % math regular l2r
+\aliased\let\c_font_fam_mr_rl\plustwo % math regular r2l
+
+\aliased\let\c_font_fam_mb \plusthree % math bold
+\aliased\let\c_font_fam_mb_lr\plusfour % math bold l2r
+\aliased\let\c_font_fam_mb_rl\plusfive % math bold r2l
+
+\definesystemattribute[mathfamily][public]
+
+\newconditional\c_font_bidirectional_mathstrategy % can be default, not that much overhead: \settrue\c_font_bidirectional_mathstrategy
+\newconditional\c_font_complete_bold_mathstrategy \settrue\c_font_complete_bold_mathstrategy
+
+% \frozen ...
+
+\immutable\def\mathtextsuffix {-text}
+\immutable\def\mathscriptsuffix {-script}
+\immutable\def\mathscriptscriptsuffix{-scriptscript}
+
+%D Beware: truefontname also does a fallback on defaultfontclass so there
+%D can be some interference here, which is why we use a different method
+%D for bold.
+
+\def\font_helpers_set_math_family_a
+ {\ifcsname\??fontinstanceready\fontclass -\fontbody-\s!mm-\fontfamily-\fontsize\endcsname \setfalse\c_font_auto_size
+ \lastnamedcs \orelse
+ \ifcsname\??fontinstanceready\fontclass -\fontbody-\s!mm-\fontfamily \endcsname \settrue \c_font_auto_size
+ \lastnamedcs \else
+ \font_helpers_set_math_family_b
+ \fi}
+
+\def\font_helpers_set_math_family_b
+ {\ifcsname\??fontinstanceready\defaultfontclass-\fontbody-\s!mm-\fontfamily-\fontsize\endcsname \setfalse\c_font_auto_size
+ \lastnamedcs \orelse
+ \ifcsname\??fontinstanceready\defaultfontclass-\fontbody-\s!mm-\fontfamily \endcsname \settrue \c_font_auto_size
+ \lastnamedcs \else
+ \font_helpers_set_math_family_c
+ \fi}
+
+\def\font_helpers_set_math_family_c
+ {\ifcsname\??fontinstanceready \fontbody-\s!mm-\fontfamily-\fontsize\endcsname \setfalse\c_font_auto_size
+ \lastnamedcs \orelse
+ \ifcsname\??fontinstanceready \fontbody-\s!mm-\fontfamily \endcsname \settrue \c_font_auto_size
+ \lastnamedcs \else
+ \settrue \c_font_auto_size
+ \fi}
+
+%def\mathsizesuffix{\ifcase\fontface\or\mathtextsuffix\or\mathscriptsuffix\or\mathscriptscriptsuffix\fi}
+\mutable\let\mathsizesuffix\empty
+
+\mutable\let\fontfamily\relax % for now public but it is a helper
+
+\def\font_helpers_set_math_family_indeed_normal#mrtag#family% \fontface etc are also used later on
+ {\let\savedfontbody\fontbody
+ \let\fontfamily#family%
+ % the order is important as we depend on known id's when completing fonts
+ % enabling is needed when we have fallbacks which spoils the families
+ \let\mathsizesuffix\mathscriptscriptsuffix\let\fontface\!!plusthree
+ \font_helpers_set_math_family_a\scriptscriptfont#mrtag\font % defines
+ \font_helpers_set_math_family_a\scriptscriptfont#mrtag\font % enables
+ \let\mathsizesuffix\mathscriptsuffix \let\fontface\!!plustwo
+ \font_helpers_set_math_family_a\scriptfont #mrtag\font % defines
+ \font_helpers_set_math_family_a\scriptfont #mrtag\font % enables
+ \let\mathsizesuffix\mathtextsuffix \let\fontface\!!plusone
+ \font_helpers_set_math_family_a\textfont #mrtag\font % defines
+ \font_helpers_set_math_family_a\textfont #mrtag\font % enables
+ \let\mathsizesuffix\empty \let\fontface\!!zerocount
+ \let\fontbody\savedfontbody
+ \setfalse\c_font_auto_size}
+
+\def\font_helpers_set_math_family_set_scales_compact
+ {% these are used when no font setting is there, the settings come before setting the parameters
+ % and are stored with the family
+ \glyphtextscale \plusthousand
+ \glyphscriptscale \numexpr\plusthousand*\dimexpr\scriptface \relax/\dimexpr\textface\relax\relax
+ \glyphscriptscriptscale \numexpr\plusthousand*\dimexpr\scriptscriptface\relax/\dimexpr\textface\relax\relax}
+
+\def\font_helpers_set_math_family_set_scales_normal
+ {\glyphtextscale \plusthousand
+ \glyphscriptscale \plusthousand
+ \glyphscriptscriptscale\plusthousand}
+
+\def\font_helpers_set_math_family_indeed_compact#mrtag#family% \fontface etc are also used later on
+ {\let\savedfontbody\fontbody
+ \let\fontfamily#family%
+ \font_helpers_set_math_family_set_scales_compact
+ % the order is important as we depend on known id's when completing fonts
+ % enabling is needed when we have fallbacks which spoils the families
+ \let\mathsizesuffix\mathtextsuffix \let\fontface\!!plusone
+ \font_helpers_set_math_family_a\textfont #mrtag\font % defines
+ \font_helpers_set_math_family_a\textfont #mrtag\font % enables
+ \scriptfont #mrtag\font % reuses
+ \scriptscriptfont#mrtag\font % reuses
+ \let\mathsizesuffix\empty \let\fontface\!!zerocount
+ \let\fontbody\savedfontbody
+ \setfalse\c_font_auto_size}
+
+\def\font_helpers_set_math_family_indeed
+ {\ifconditional\c_font_compact
+ \expandafter\font_helpers_set_math_family_indeed_compact
+ \else
+ \expandafter\font_helpers_set_math_family_indeed_normal
+ \fi}
+
+\def\font_helpers_set_math_family_bold_indeed_normal#mbfam#familytag#mrfam% \c_font_fam_mb \s!mb \c_font_fam_mr
+ {\let\savedfontclass\defaultfontclass
+ \let\defaultfontclass\fontclass % else truefontname falls back on the wrong one
+ \let\savedfontbody\fontbody
+ \let\fontfamily#familytag%
+ \font_helpers_set_math_family_set_scales_normal
+ \let\mathsizesuffix\mathscriptscriptsuffix\let\fontface\!!plusthree
+ \font_helpers_set_math_family_bold_a\scriptscriptfont#mbfam#mrfam% defines
+ \font_helpers_set_math_family_bold_a\scriptscriptfont#mbfam#mrfam% enables
+ \let\mathsizesuffix\mathscriptsuffix \let\fontface\!!plustwo
+ \font_helpers_set_math_family_bold_a\scriptfont #mbfam#mrfam% defines
+ \font_helpers_set_math_family_bold_a\scriptfont #mbfam#mrfam% enables
+ \let\mathsizesuffix\mathtextsuffix \let\fontface\!!plusone
+ \font_helpers_set_math_family_bold_a\textfont #mbfam#mrfam% defines
+ \font_helpers_set_math_family_bold_a\textfont #mbfam#mrfam% enables
+ \let\mathsizesuffix\empty \let\fontface\!!zerocount
+ \let\fontbody\savedfontbody
+ \let\defaultfontclass\savedfontclass
+ \setfalse\c_font_auto_size}
+
+\def\font_helpers_set_math_family_bold_indeed_compact#mbfam#familytag#mrfam% \c_font_fam_mb \s!mb \c_font_fam_mr
+ {\let\savedfontclass\defaultfontclass
+ \let\defaultfontclass\fontclass % else truefontname falls back on the wrong one
+ \let\savedfontbody\fontbody
+ \let\fontfamily#familytag%
+ \let\mathsizesuffix\mathtextsuffix \let\fontface\!!plusone
+ \font_helpers_set_math_family_bold_a\textfont #mbfam#mrfam% defines
+ \font_helpers_set_math_family_bold_a\textfont #mbfam#mrfam% enables
+ \scriptfont #mbfam\scriptfont #mrfam% reuses
+ \scriptscriptfont#mbfam\scriptscriptfont#mrfam% reuses
+ \let\mathsizesuffix\empty \let\fontface\!!zerocount
+ \let\fontbody\savedfontbody
+ \let\defaultfontclass\savedfontclass
+ \setfalse\c_font_auto_size}
+
+\def\font_helpers_set_math_family_bold_indeed
+ {\ifconditional\c_font_compact
+ \expandafter\font_helpers_set_math_family_bold_indeed_compact
+ \else
+ \expandafter\font_helpers_set_math_family_bold_indeed_normal
+ \fi}
+
+\def\font_helpers_set_math_family_bold_a#font#mbfam#mrfam%
+ {\ifcsname\??fontinstanceready\fontclass-\fontbody-\s!mm-\fontfamily-\fontsize\endcsname \setfalse\c_font_auto_size
+ \lastnamedcs #font#mbfam\font \orelse
+ \ifcsname\??fontinstanceready\fontclass-\fontbody-\s!mm-\fontfamily \endcsname \settrue \c_font_auto_size
+ \lastnamedcs #font#mbfam\font \else
+ #font#mbfam#font#mrfam%
+ \fi}
+
+% optimized: math fonts are never changed (10K \bfa $x$: 3.2 => 2.5 (baseline 1.0))
+%
+% sort of tricky: we cannot reset in \everybeforedefinetypeface as we don't know
+% all sizes so we postpone the optimization to the first starttext
+%
+% pitfall: we should reset 'm when a fontclass name is reused
+
+\newconditional\optimizemathfontdefinitions \settrue\optimizemathfontdefinitions
+
+\def\font_helpers_set_math_family#mrfam#familytag%
+ {\ifconditional\optimizemathfontdefinitions
+ \ifcsname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-1\endcsname
+ % \writestatus{fonts}{math: reusing \fontclass\fontbody\s!mm#familytag\fontsize1}%
+ \font_helpers_preset_math_family_indeed#mrfam#familytag%
+ \else
+ % \writestatus{fonts}{math: defining \fontclass\fontbody\s!mm#familytag\fontsize1}%
+ \font_helpers_set_math_family_indeed#mrfam#familytag%
+ \fi
+ \else
+ \font_helpers_set_math_family_indeed#mrfam#familytag%
+ \fi}
+
+\def\font_helpers_set_math_family_bold#mbfam#familytag#mrfam%
+ {\ifconditional\optimizemathfontdefinitions
+ %\ifcsname\??fontinstanceclass\fontclass-\textface-\s!mm-#familytag-\fontsize-1\endcsname
+ \ifcsname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-1\endcsname
+ \font_helpers_preset_math_family_indeed#mbfam#familytag%
+ \else
+ \font_helpers_set_math_family_bold_indeed#mbfam#familytag#mrfam%
+ \fi
+ \else
+ \font_helpers_set_math_family_bold_indeed#mbfam#familytag#mrfam%
+ \fi}
+
+%D It can happen that we use a bodyfont with no math in which case we have a problem
+%D with setting the global bodyfont size in the page builder. For instance in:
+%D
+%D \starttext
+%D \definetypeface[test][rm][serif][pagella][default]
+%D \setupbodyfont[test]
+%D test
+%D \stoptext
+%D
+%D This is why we need the check. At the cost of some extra checking we gain a
+%D little in restoring global states and, what's more important, we get rid of large
+%D math parameter push/pop in tracingall when not needed.
+
+% \def\font_helpers_preset_math_family_indeed#fam#familytag%
+% {\expandafter\let\expandafter\v_font_math_one\csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-1\endcsname
+% \ifrelax\v_font_math_one
+% \font_helpers_preset_math_family_warning
+% \orelse\ifnum\fontid\textfont#fam=\fontid\v_font_math_one\else
+% \font_helpers_preset_math_family_indeed_changed#fam#familytag%
+% \fi}
+
+% \def\font_helpers_preset_math_family_warning
+% {\writestatus{fonts}{math: unset for global bodyfont \fontclass\space at \fontbody}}
+
+% \def\font_helpers_preset_math_family_indeed_changed_normal#fam#familytag%
+% {\font_helpers_set_math_family_set_scales_normal
+% \scriptscriptfont#fam\csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-3\endcsname
+% \scriptfont #fam\csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-2\endcsname
+% \textfont #fam\v_font_math_one}
+
+% \def\font_helpers_preset_math_family_indeed_changed_compact#fam#familytag%
+% {\font_helpers_set_math_family_set_scales_compact
+% \scriptscriptfont#fam\v_font_math_one
+% \scriptfont #fam\v_font_math_one
+% \textfont #fam\v_font_math_one}
+
+% \def\font_helpers_preset_math_family_indeed_changed
+% {\ifconditional\c_font_compact
+% \expandafter\font_helpers_preset_math_family_indeed_changed_compact
+% \else
+% \expandafter\font_helpers_preset_math_family_indeed_changed_normal
+% \fi}
+
+\def\font_helpers_preset_math_family_indeed_normal#fam#familytag%
+ {\expandafter\let\expandafter\font_math_last_font\csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-1\endcsname
+ \font_math_last_font
+ \ifnum\fontid\textfont#fam=\fontid\font\else
+ \font_helpers_preset_math_family_indeed_changed_normal#fam#familytag%
+ \fi}
+
+\def\font_helpers_preset_math_family_indeed_changed_normal#fam#familytag%
+ {\font_helpers_set_math_family_set_scales_normal
+ \csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-3\endcsname\scriptscriptfont#fam\font
+ \csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-2\endcsname\scriptfont #fam\font
+ % \csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-1\endcsname\textfont #fam\font}
+ \font_math_last_font \textfont #fam\font}
+
+\def\font_helpers_preset_math_family_indeed_compact#fam#familytag%
+ {\csname\??fontinstanceclass\fontclass-\fontbody-\s!mm-#familytag-\fontsize-1\endcsname
+ \ifnum\fontid\textfont#fam=\fontid\font\else
+ \font_helpers_preset_math_family_indeed_changed_compact#fam%
+ \fi}
+
+\def\font_helpers_preset_math_family_indeed_changed_compact#fam%
+ {\font_helpers_set_math_family_set_scales_compact
+ \scriptscriptfont#fam\font
+ \scriptfont #fam\font
+ \textfont #fam\font}
+
+\def\font_helpers_preset_math_family_indeed
+ {\ifconditional\c_font_compact
+ \expandafter\font_helpers_preset_math_family_indeed_compact
+ \else
+ \expandafter\font_helpers_preset_math_family_indeed_normal
+ \fi}
+
+\let\font_helpers_reset_fontclass_math_families\gobbleoneargument
+
+%D It would be nice if characters could be defined in a neutral way (say fam 255)
+%D and be mapped to a real family during noad list construction. However, this
+%D changes tex in critical places so for the moment we simulate this using
+%D manipulation.
+%D
+%D For tracing purposes we use three families but in l2r mode 1 and 2 are copies of
+%D 0 while in rl mode 0 is a copy of 1. There is no real overhead involved in this.
+%D This also permits different font definitions for normal and mixed.
+
+\let\m_font_class_direction\empty
+\let\m_font_class_features \empty
+\let\m_font_class_fallbacks\empty
+\let\m_font_class_goodies \empty
+
+\let\m_font_direction\empty
+\let\m_font_features \empty
+\let\m_font_fallbacks\empty
+\let\m_font_goodies \empty
+
+\appendtoks % can be analyzed once
+ % why here ..
+ %\edef\m_font_class_direction{\ifcsname\??fontclass\fontclass\s!mm\s!direction\endcsname\csname\??fontclass\fontclass\s!mm\s!direction\endcsname\fi}%
+ \edef\m_font_class_direction{\begincsname\??fontclass\fontclass\s!mm\s!direction\endcsname}%
+ % ...
+ \ifx\m_font_class_direction\v!both
+ \settrue\c_font_bidirectional_mathstrategy
+ \else
+ \setfalse\c_font_bidirectional_mathstrategy
+ \fi
+\to \t_font_math_strategies
+
+\def\font_helpers_bidirectional_mathstrategy_yes
+ {\font_helpers_set_math_family\c_font_fam_mr_lr\s!mrlr
+ \font_helpers_set_math_family\c_font_fam_mr_rl\s!mrrl
+ \ifnum\fontid\textfont\c_font_fam_mr=\fontid\textfont\c_font_fam_mr_lr\else
+ \font_helpers_bidirectional_mathstrategy_yes_changed
+ \fi}
+
+\def\font_helpers_bidirectional_mathstrategy_yes_changed
+ {\textfont \c_font_fam_mr\textfont \c_font_fam_mr_lr
+ \scriptfont \c_font_fam_mr\scriptfont \c_font_fam_mr_lr
+ \scriptscriptfont\c_font_fam_mr\scriptscriptfont\c_font_fam_mr_lr}
+
+\def\font_helpers_bidirectional_mathstrategy_nop
+ {\font_helpers_set_math_family\c_font_fam_mr\s!mr
+ \ifnum\fontid\textfont\c_font_fam_mr_rl=\fontid\textfont\c_font_fam_mr\else
+ \font_helpers_bidirectional_mathstrategy_nop_changed
+ \fi}
+
+\def\font_helpers_bidirectional_mathstrategy_nop_changed
+ {\textfont \c_font_fam_mr_lr\textfont \c_font_fam_mr
+ \scriptfont \c_font_fam_mr_lr\scriptfont \c_font_fam_mr
+ \scriptscriptfont\c_font_fam_mr_lr\scriptscriptfont\c_font_fam_mr
+ \textfont \c_font_fam_mr_rl\textfont \c_font_fam_mr
+ \scriptfont \c_font_fam_mr_rl\scriptfont \c_font_fam_mr
+ \scriptscriptfont\c_font_fam_mr_rl\scriptscriptfont\c_font_fam_mr}
+
+\appendtoks
+ \ifconditional\c_font_bidirectional_mathstrategy
+ \font_helpers_bidirectional_mathstrategy_yes
+ \else
+ \font_helpers_bidirectional_mathstrategy_nop
+ \fi
+\to \t_font_math_strategies
+
+\def\font_helpers_complete_bold_mathstrategy_yes_bidi
+ {\font_helpers_set_math_family_bold\c_font_fam_mb_lr\s!mblr\c_font_fam_mr_lr
+ \font_helpers_set_math_family_bold\c_font_fam_mb_rl\s!mbrl\c_font_fam_mr_rl
+ \ifnum\fontid\textfont\c_font_fam_mb=\fontid\textfont\c_font_fam_mb_lr\else
+ \font_helpers_complete_bold_mathstrategy_yes_bidi_changed
+ \fi}
+
+\def\font_helpers_complete_bold_mathstrategy_yes_bidi_changed
+ {\textfont \c_font_fam_mb\textfont \c_font_fam_mb_lr
+ \scriptfont \c_font_fam_mb\scriptfont \c_font_fam_mb_lr
+ \scriptscriptfont\c_font_fam_mb\scriptscriptfont\c_font_fam_mb_lr}
+
+\def\font_helpers_complete_bold_mathstrategy_yes
+ {\font_helpers_set_math_family_bold\c_font_fam_mb\s!mb\c_font_fam_mr\relax
+ \ifnum\fontid\textfont\c_font_fam_mb_rl=\fontid\textfont\c_font_fam_mb\else
+ \font_helpers_complete_bold_mathstrategy_yes_changed
+ \fi}
+
+\def\font_helpers_complete_bold_mathstrategy_yes_changed
+ {\textfont \c_font_fam_mb_rl\textfont \c_font_fam_mb
+ \scriptfont \c_font_fam_mb_rl\scriptfont \c_font_fam_mb
+ \scriptscriptfont\c_font_fam_mb_rl\scriptscriptfont\c_font_fam_mb
+ \textfont \c_font_fam_mb_lr\textfont \c_font_fam_mb
+ \scriptfont \c_font_fam_mb_lr\scriptfont \c_font_fam_mb
+ \scriptscriptfont\c_font_fam_mb_lr\scriptscriptfont\c_font_fam_mb}
+
+\def\font_helpers_complete_bold_mathstrategy_nop
+ {\ifnum\fontid\textfont\c_font_fam_mb=\fontid\textfont\c_font_fam_mr\else
+ \font_helpers_complete_bold_mathstrategy_nop_changed
+ \fi}
+
+\def\font_helpers_complete_bold_mathstrategy_nop_changed
+ {\textfont \c_font_fam_mb \textfont \c_font_fam_mr
+ \scriptfont \c_font_fam_mb \scriptfont \c_font_fam_mr
+ \scriptscriptfont\c_font_fam_mb \scriptscriptfont\c_font_fam_mr
+ \textfont \c_font_fam_mb_lr\textfont \c_font_fam_mr_lr
+ \scriptfont \c_font_fam_mb_lr\scriptfont \c_font_fam_mr_lr
+ \scriptscriptfont\c_font_fam_mb_lr\scriptscriptfont\c_font_fam_mr_lr
+ \textfont \c_font_fam_mb_rl\textfont \c_font_fam_mr_rl
+ \scriptfont \c_font_fam_mb_rl\scriptfont \c_font_fam_mr_rl
+ \scriptscriptfont\c_font_fam_mb_rl\scriptscriptfont\c_font_fam_mr_rl}
+
+\def\font_helpers_apply_complete_bold_mathstrategy
+ {\ifconditional\c_font_complete_bold_mathstrategy
+ \ifconditional\c_font_bidirectional_mathstrategy
+ \font_helpers_complete_bold_mathstrategy_yes_bidi
+ \else
+ \font_helpers_complete_bold_mathstrategy_yes
+ \fi
+ \else
+ \font_helpers_complete_bold_mathstrategy_nop
+ \fi}
+
+\appendtoks
+ \font_helpers_apply_complete_bold_mathstrategy
+\to \t_font_math_strategies
+
+\ifdefined\defaultmathfamily \else
+ \setnewconstant\defaultmathfamily\zerocount
+\fi
+
+\appendtoks
+ \fam\defaultmathfamily % all characters and symbols are in this family
+\to \everymathematics
+
+\protected\def\font_helpers_synchronize_math_family_mr
+ {\c_attr_mathfamily\ifconditional\c_font_bidirectional_mathstrategy
+ \ifconditional\c_math_right_to_left
+ \plustwo
+ \else
+ \plusone
+ \fi
+ \else
+ \zerocount
+ \fi}
+
+\protected\def\font_helpers_synchronize_math_family_mb
+ {\c_attr_mathfamily\ifconditional\c_font_bidirectional_mathstrategy
+ \ifconditional\c_math_right_to_left
+ \ifconditional\c_font_pseudo_bold_math_state\pluseight\else\plusfive\fi
+ \else
+ \ifconditional\c_font_pseudo_bold_math_state\plusseven\else\plusfour\fi
+ \fi
+ \else
+ \ifconditional\c_font_pseudo_bold_math_state\plussix\else\plusthree\fi
+ \fi}
+
+\installcorenamespace{fontmathsynchronizer}
+\installcorenamespace{fontmathstoredstrategy}
+
+\letvalue{\??fontmathsynchronizer\s!tf }\font_helpers_synchronize_math_family_mr
+\letvalue{\??fontmathsynchronizer\s!sl }\font_helpers_synchronize_math_family_mr
+\letvalue{\??fontmathsynchronizer\s!it }\font_helpers_synchronize_math_family_mr
+\letvalue{\??fontmathsynchronizer\s!bf }\font_helpers_synchronize_math_family_mb
+\letvalue{\??fontmathsynchronizer\s!bs }\font_helpers_synchronize_math_family_mb
+\letvalue{\??fontmathsynchronizer\s!bi }\font_helpers_synchronize_math_family_mb
+\letvalue{\??fontmathsynchronizer\empty}\font_helpers_synchronize_math_family_mr
+
+% \def\font_helpers_synchronize_math_family
+% {\csname\??fontmathsynchronizer\ifcsname\??fontmathsynchronizer\fontalternative\endcsname\fontalternative\fi\endcsname}
+
+\def\font_helpers_synchronize_math_family
+ {\ifcsname\??fontmathsynchronizer\fontalternative\endcsname
+ \lastnamedcs
+ \else
+ \font_helpers_synchronize_math_family_mr
+ \fi}
+
+\appendtoks
+ \ifnum\fontid\textfont\zerocount=\fontid\textfont\plusthree
+ \letcsname\??fontmathstoredstrategy\fontclass\endcsname\font_helpers_set_math_partial_bold_strategy
+ \else
+ \letcsname\??fontmathstoredstrategy\fontclass\endcsname\font_helpers_set_math_full_bold_strategy
+ \fi
+\to \t_font_math_strategies
+
+%def\font_helpers_synchronize_math_bold_strategy{\csname\??fontmathstoredstrategy\fontclass\endcsname}
+\def\font_helpers_synchronize_math_bold_strategy{\begincsname\??fontmathstoredstrategy\fontclass\endcsname}
+
+\newconditional\c_font_pseudo_bold_math_state
+
+\def\font_helpers_set_math_partial_bold_strategy{\settrue \c_font_pseudo_bold_math_state}
+\def\font_helpers_set_math_full_bold_strategy {\setfalse\c_font_pseudo_bold_math_state}
+
+\appendtoks
+ \font_helpers_synchronize_math_bold_strategy
+\to \everymathematics
+
+%D Bold is somewhat special as we might want both full-bold-math mixed regular-math,
+%D as well as automatic adaption to outer bold (in titles and inline text bold) so
+%D we will need explicit switches as well as an automatic one. (We will use lucida
+%D as an example.)
+
+\ifdefined\mathdefault \else \let\mathdefault\relax \fi
+
+\newconditional\c_math_bold
+
+\permanent\protected\def\mr % math regular
+ {\ifmmode
+ \font_helpers_synchronize_math_family_mr
+ \else
+ \font_helpers_set_current_font_alternative\s!mr
+ \fi
+ \mathdefault
+ \setfalse\c_math_bold}
+
+\permanent\protected\def\mb % math bold
+ {\ifmmode
+ \font_helpers_synchronize_math_family_mb
+ \else
+ \font_helpers_set_current_font_alternative\s!mb
+ \fi
+ \mathdefault
+ \settrue\c_math_bold}
+
+\appendtoks
+ \font_helpers_synchronize_math_family % auto bold
+\to \everymathematics
+
+\appendtoks
+ \ifconditional\c_math_bold\mb\fi
+\to \everymathematics
+
+%D \macros
+%D {bigmath,nobigmath}
+%D
+%D We can inhibit this slow||downer with:
+
+% these can best be combined
+
+% 0=never 1=everymath 2=always
+
+\setnewconstant\synchronizebigmathflag\plusone
+
+\appendtoks
+ \ifcase\synchronizebigmathflag
+ % never
+ \or
+ \synchronizebigmath
+ \or
+ % always
+ \fi
+\to \everymathematics
+
+\permanent\protected\def\nobigmath {\synchronizebigmathflag\zerocount}
+\permanent\protected\def\autobigmath{\synchronizebigmathflag\plusone\synchronizebigmath}
+\permanent\protected\def\bigmath {\synchronizebigmathflag\plustwo\synchronizebigmath}
+
+\mutable\let\bigmathfontsize\empty
+
+\permanent\protected\def\synchronizebigmath
+ {\ifx\bigmathfontsize\fontsize
+ % already in sync
+ \else
+ \let\bigmathfontsize\fontsize
+ \font_helpers_synchronize_math
+ \fi}
+
+\protected\def\font_helpers_check_big_math_synchronization
+ {\ifcase\synchronizebigmathflag
+ % never
+ \or
+ \ifmmode \synchronizebigmath \fi
+ \or
+ \synchronizebigmath
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-mpf.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-mpf.lmt
new file mode 100644
index 00000000000..b1e6a00dd55
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-mpf.lmt
@@ -0,0 +1,173 @@
+if not modules then modules = { } end modules ['font-mpf'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tonumber, unpack, type = tonumber, unpack, type
+
+local context = context
+local metapost = metapost
+
+local metafonts = { }
+metapost.metafonts = metafonts
+
+local sortedhash = table.sortedhash
+
+local addcharacters = fonts.constructors.addcharacters
+local fontdata = fonts.hashes.identifiers
+
+local otf = fonts.handlers.otf
+local otfregister = otf.features.register
+
+local getshapes = fonts.dropins.getshapes
+
+local setparameterset = metapost.setparameterset
+local simplemetapost = metapost.simple
+
+local register = fonts.collections.register
+local checkenabled = fonts.collections.checkenabled
+local newprivateslot = fonts.helpers.newprivateslot
+
+local currentfont = font.current
+
+local function setmetaglyphs(category,fontid,unicode,specification)
+ if unicode then
+ local tfmdata = fontdata[fontid]
+ local characters = tfmdata.characters
+ specification.unicode = unicode
+ specification.category = category
+ -- Generate one shape, make a use we pass the unicode as that is
+ -- the index in a picture list (normally). We could actually already
+ -- fetch the stream but for now we do that later (i.e. we delay the
+ -- serialization to pdf).
+ local code = specification.code
+ if code then
+ setparameterset("mpsfont",specification)
+ -- simplemetapost(instance,"begingroup;",true,true)
+ simplemetapost("simplefun",code)
+ -- simplemetapost(instance,"endgroup;",true,true)
+ end
+ -- We now know (hopefully) the dimensions of the image which is what
+ -- we need to pass to the engine in order to let it do its work.
+ local shapes = getshapes(category)
+ local units = shapes.parameters.units
+ local scale = tfmdata.parameters.size / units
+ local shape = shapes.glyphs[unicode]
+ --
+ local llx, lly, urx, ury = unpack(shape.boundingbox)
+ llx = llx * scale
+ urx = urx * scale
+ lly = lly * scale
+ ury = ury * scale
+ --
+ local newdata = { }
+ characters[unicode] = newdata -- so that we can register commands
+ fonts.dropins.swapone("mps",tfmdata,shape,unicode)
+ --
+ local olddata = characters[unicode]
+ newdata.width = urx - llx
+ newdata.height = ury
+ newdata.depth = -lly
+ newdata.unicode = unicode
+ -- commands = { { "offset", -llx, 0, newdata.commands[1][1], newdata.commands[1][2] } }
+ if llx ~= 0 then
+ newdata.commands = { { "offset", -llx, 0 }, newdata.commands[1] }
+ end
+ -- pass dimensions to lua
+ characters[unicode] = newdata
+ -- pass dimensions to tex
+ addcharacters(fontid, { characters = { [unicode] = newdata } })
+ return fontid, unicode
+ end
+end
+
+local function setmetaglyph(specification)
+ if specification then
+ local category = specification.category
+ local name = specification.name
+ if category and name then
+ local fontid = specification.fontid or currentfont()
+ local private = newprivateslot(name)
+ register(fontid,private,function(font,char)
+ return setmetaglyphs(category,font,char,specification) end
+ )
+ checkenabled()
+ end
+ end
+end
+
+local function initializempf(tfmdata,kind,value)
+ local metafont = metafonts[value]
+ if value then
+ local font = tfmdata.properties.id
+ for char, spec in sortedhash(metafont) do
+ if type(char) == "string" then
+ char = newprivateslot(char)
+ end
+ register(font,char,function(font,char)
+ return setmetaglyphs(value,font,char,spec)
+ end)
+ end
+ checkenabled()
+ end
+end
+
+fonts.helpers.setmetaglyphs = setmetaglyphs
+fonts.helpers.setmetaglyph = setmetaglyph
+
+otfregister {
+ name = "metafont",
+ description = "metafont glyphs",
+ manipulators = {
+ base = initializempf,
+ node = initializempf,
+ }
+}
+
+do
+
+ local scanners = tokens.scanners
+ local scanopen = scanners.scanopen
+ local scanclose = scanners.scanclose
+ local scanword = scanners.word
+ local scanstring = scanners.string
+
+ interfaces.implement {
+ name = "setmetaglyph",
+ public = true,
+ protected = true,
+ actions = function(t)
+ local t = { }
+ if scanopen() then
+ while not scanclose() do
+ local key = scanword()
+ if key == "shapes" then
+ if scanopen() then
+ local tt = { }
+ local nn = 0
+ while not scanclose() do
+ if scanopen() then
+ local ttt = { }
+ while not scanclose() do
+ local key = scanword()
+ ttt[key] = scanstring()
+ end
+ nn= nn + 1
+ tt[nn] = ttt
+ end
+ end
+ t[key] = tt
+ end
+ else
+ t[key] = scanstring()
+ end
+ end
+ end
+ setmetaglyph(t)
+ end
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-mps.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-mps.lmt
new file mode 100644
index 00000000000..8cab9cf5c8f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-mps.lmt
@@ -0,0 +1,398 @@
+if not modules then modules = { } end modules ['font-mps'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tostring = tostring
+local concat = table.concat
+local formatters = string.formatters
+
+-- QP0 [QP1] QP2 => CP0 [CP1 CP2] CP3
+
+-- CP0 = QP0
+-- CP3 = QP2
+--
+-- CP1 = QP0 + 2/3 *(QP1-QP0)
+-- CP2 = QP2 + 2/3 *(QP1-QP2)
+
+fonts = fonts or { }
+local metapost = fonts.metapost or { }
+fonts.metapost = metapost
+
+local f_moveto = formatters["(%N,%N)"]
+local f_lineto = formatters["--(%N,%N)"]
+local f_curveto = formatters["..controls(%N,%N)and(%N,%N)..(%N,%N)"]
+local s_cycle = "--cycle"
+
+local f_nofill = formatters["nofill %s;"]
+local f_dofill = formatters["fill %s;"]
+
+local f_draw_trace = formatters["drawpathonly %s;"]
+local f_draw = formatters["draw %s;"]
+
+local f_boundingbox = formatters["((%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--cycle)"]
+local f_vertical = formatters["((%N,%N)--(%N,%N))"]
+
+function metapost.boundingbox(d,factor)
+ local bounds = d.boundingbox
+ local factor = factor or 1
+ local llx = factor*bounds[1]
+ local lly = factor*bounds[2]
+ local urx = factor*bounds[3]
+ local ury = factor*bounds[4]
+ return f_boundingbox(llx,lly,urx,lly,urx,ury,llx,ury)
+end
+
+function metapost.widthline(d,factor)
+ local bounds = d.boundingbox
+ local factor = factor or 1
+ local lly = factor*bounds[2]
+ local ury = factor*bounds[4]
+ local width = factor*d.width
+ return f_vertical(width,lly,width,ury)
+end
+
+function metapost.zeroline(d,factor)
+ local bounds = d.boundingbox
+ local factor = factor or 1
+ local lly = factor*bounds[2]
+ local ury = factor*bounds[4]
+ return f_vertical(0,lly,0,ury)
+end
+
+function metapost.paths(d,xfactor,yfactor)
+ local sequence = d.sequence
+ local segments = d.segments
+ local list = { }
+ local path = { } -- recycled
+ local size = 0
+ local xfactor = xfactor or 1
+ local yfactor = yfactor or xfactor
+ if sequence then
+ local i = 1
+ local n = #sequence
+ while i < n do
+ local operator = sequence[i]
+ if operator == "m" then -- "moveto"
+ if size > 0 then
+ size = size + 1
+ path[size] = s_cycle
+ list[#list+1] = concat(path,"",1,size)
+ size = 1
+ else
+ size = size + 1
+ end
+ path[size] = f_moveto(xfactor*sequence[i+1],yfactor*sequence[i+2])
+ i = i + 3
+ elseif operator == "l" then -- "lineto"
+ size = size + 1
+ path[size] = f_lineto(xfactor*sequence[i+1],yfactor*sequence[i+2])
+ i = i + 3
+ elseif operator == "c" then -- "curveto"
+ size = size + 1
+ path[size] = f_curveto(xfactor*sequence[i+1],yfactor*sequence[i+2],xfactor*sequence[i+3],yfactor*sequence[i+4],xfactor*sequence[i+5],yfactor*sequence[i+6])
+ i = i + 7
+ elseif operator =="q" then -- "quadraticto"
+ size = size + 1
+ -- first is always a moveto
+ local l_x = xfactor*sequence[i-2]
+ local l_y = yfactor*sequence[i-1]
+ local m_x = xfactor*sequence[i+1]
+ local m_y = yfactor*sequence[i+2]
+ local r_x = xfactor*sequence[i+3]
+ local r_y = yfactor*sequence[i+4]
+ path[size] = f_curveto (
+ l_x + 2/3 * (m_x-l_x),
+ l_y + 2/3 * (m_y-l_y),
+ r_x + 2/3 * (m_x-r_x),
+ r_y + 2/3 * (m_y-r_y),
+ r_x, r_y
+ )
+ i = i + 5
+ else
+ -- weird
+ i = i + 1
+ end
+ end
+ elseif segments then
+ for i=1,#segments do
+ local segment = segments[i]
+ local operator = segment[#segment]
+ if operator == "m" then -- "moveto"
+ if size > 0 then
+ size = size + 1
+ path[size] = s_cycle
+ list[#list+1] = concat(path,"",1,size)
+ size = 1
+ else
+ size = size + 1
+ end
+ path[size] = f_moveto(xfactor*segment[1],yfactor*segment[2])
+ elseif operator == "l" then -- "lineto"
+ size = size + 1
+ path[size] = f_lineto(xfactor*segment[1],yfactor*segment[2])
+ elseif operator == "c" then -- "curveto"
+ size = size + 1
+ path[size] = f_curveto(xfactor*segment[1],yfactor*segment[2],xfactor*segment[3],yfactor*segment[4],xfactor*segment[5],yfactor*segment[6])
+ elseif operator =="q" then -- "quadraticto"
+ size = size + 1
+ -- first is always a moveto
+ local prev = segments[i-1]
+ local l_x = xfactor*prev[#prev-2]
+ local l_y = yfactor*prev[#prev-1]
+ local m_x = xfactor*segment[1]
+ local m_y = yfactor*segment[2]
+ local r_x = xfactor*segment[3]
+ local r_y = yfactor*segment[4]
+ path[size] = f_curveto (
+ l_x + 2/3 * (m_x-l_x),
+ l_y + 2/3 * (m_y-l_y),
+ r_x + 2/3 * (m_x-r_x),
+ r_y + 2/3 * (m_y-r_y),
+ r_x, r_y
+ )
+ else
+ -- weird
+ end
+ end
+ else
+ return
+ end
+ if size > 0 then
+ size = size + 1
+ path[size] = s_cycle
+ list[#list+1] = concat(path,"",1,size)
+ end
+ return list
+end
+
+function metapost.fill(paths)
+ local r = { }
+ local n = #paths
+ for i=1,n do
+ if i < n then
+ r[i] = f_nofill(paths[i])
+ else
+ r[i] = f_dofill(paths[i])
+ end
+ end
+ return concat(r)
+end
+
+function metapost.draw(paths,trace)
+ local r = { }
+ local n = #paths
+ for i=1,n do
+ if trace then
+ r[i] = f_draw_trace(paths[i])
+ else
+ r[i] = f_draw(paths[i])
+ end
+ end
+ return concat(r)
+end
+
+function metapost.maxbounds(data,index,factor)
+ local maxbounds = data.maxbounds
+ local factor = factor or 1
+ local glyphs = data.glyphs
+ local glyph = glyphs[index]
+ local boundingbox = glyph.boundingbox
+ local xmin, ymin, xmax, ymax
+ if not maxbounds then
+ xmin = 0
+ ymin = 0
+ xmax = 0
+ ymax = 0
+ for i=1,#glyphs do
+ local d = glyphs[i]
+ if d then
+ local b = d.boundingbox
+ if b then
+ if b[1] < xmin then xmin = b[1] end
+ if b[2] < ymin then ymin = b[2] end
+ if b[3] > xmax then xmax = b[3] end
+ if b[4] > ymax then ymax = b[4] end
+ end
+ end
+ end
+ maxbounds = { xmin, ymin, xmax, ymax }
+ data.maxbounds = maxbounds
+ else
+ xmin = maxbounds[1]
+ ymin = maxbounds[2]
+ xmax = maxbounds[3]
+ ymax = maxbounds[4]
+ end
+ local llx = boundingbox[1]
+ local lly = boundingbox[2]
+ local urx = boundingbox[3]
+ local ury = boundingbox[4]
+ local width = glyph.width
+ if llx > 0 then
+ llx = 0
+ end
+ if width > urx then
+ urx = width
+ end
+ return f_boundingbox(
+ factor*llx,factor*ymin,
+ factor*urx,factor*ymin,
+ factor*urx,factor*ymax,
+ factor*llx,factor*ymax
+ )
+end
+
+-- This is a nice example of tex, metapost and lua working in tandem. Each kicks in at the
+-- right time. It's probably why I like watching https://www.youtube.com/watch?v=c5FqpddnJmc
+-- so much: precisely (and perfectly) timed too.
+
+local texgetbox = tex.getbox
+
+local nodecodes = nodes.nodecodes -- no nuts yet
+local rulecodes = nodes.rulecodes
+
+local rule_code = nodecodes.rule
+
+local normalrule_code = rulecodes.normal
+local outlinerule_code = rulecodes.outline
+local userrule_code = rulecodes.user
+local emptyrule_code = rulecodes.empty
+
+local nuts = nodes.nuts
+local getwhd = nuts.getwhd
+local getexpansion = nuts.getexpansion
+local isglyph = nuts.isglyph
+
+local characters = fonts.hashes.characters
+local parameters = fonts.hashes.parameters
+local shapes = fonts.hashes.shapes
+local topaths = metapost.paths
+
+local f_text = formatters["mfun_do_outline_text_flush(%q,%i,%N,%N,%q)(%,t);"]
+local f_rule = formatters["mfun_do_outline_rule_flush(%q,%N,%N,%N,%N);"]
+local f_bounds = formatters["checkbounds(%N,%N,%N,%N);"]
+local s_nothing = "(origin scaled 10)"
+
+local sc = 10
+local fc = number.dimenfactors.bp
+
+local function glyph(kind,font,char,advance,shift,ex)
+ local character = characters[font][char]
+ if character then
+ local index = character.index
+ if index then
+ local shapedata = shapes[font]
+ local glyphs = shapedata.glyphs -- todo: subfonts fonts.shapes.indexed(font,sub)
+ if glyphs then
+ local glyf = glyphs[index]
+ if glyf then
+ local units = 1000 -- factor already takes shapedata.units into account
+ local yfactor = (sc/units) * parameters[font].factor / 655.36
+ local xfactor = yfactor
+ local shift = shift or 0
+ local advance = advance or 0
+ local exfactor = ex or 0
+ local wfactor = 1
+ local detail = kind == "p" and tostring(char) or ""
+ if exfactor ~= 0 then
+ wfactor = (1+(ex/units)/1000)
+ xfactor = xfactor * wfactor
+ end
+ local paths = topaths(glyf,xfactor,yfactor)
+ if paths then
+ return f_text(kind,#paths,advance,shift,detail,paths) -- , character.width * fc * wfactor
+ end
+ end
+ end
+ end
+ end
+end
+
+metapost.glyph = glyph
+
+local kind = ""
+local buffer = { }
+local b = 0
+
+local function reset()
+ buffer = { }
+ b = 0
+end
+
+local function flushcharacter(current, pos_h, pos_v, pod_r, font, char)
+ local char, font = isglyph(current)
+ local code = glyph(kind,font,char,pos_h*fc,pos_v*fc,getexpansion(current))
+ if code then
+ b = b + 1
+ buffer[b] = code
+ end
+end
+
+local function flushrule(current, pos_h, pos_v, pos_r, size_h, size_v, subtype)
+ if subtype == normalrule_code then
+ b = b + 1
+ buffer[b] = f_rule(kind,pos_h*fc,pos_v*fc,size_h*fc,size_v*fc)
+ elseif subtype == outlinerule_code then
+ b = b + 1
+ buffer[b] = f_rule("d",pos_h*fc,pos_v*fc,size_h*fc,size_v*fc)
+ elseif subtype == userrule_code then
+ -- print("USER RULE")
+ -- b = b + 1
+ -- buffer[b] = f_rule("d",size_h*fc,size_v*fc,pos_h*fc,pos_v*fc)
+ elseif subtype == emptyrule_code then
+ -- ignore
+ else
+ -- b = b + 1
+ -- buffer[b] = f_rule("f",pos_h*fc,pos_v*fc,size_h*fc,size_v*fc)
+ end
+end
+
+local function flushsimplerule(pos_h, pos_v, pos_r, size_h, size_v)
+ flushrule(false,pos_h,pos_v,pos_r,size_h,size_v,normalrule_code)
+end
+
+local function flushspecialrule(pos_h, pos_v, pos_r, w, h, d, l, outline)
+ flushrule(false,pos_h,pos_v-d,pos_r,w,h+d,outline and outlinerule_code or normalrule_code)
+end
+
+-- installer
+
+drivers.install {
+ name = "mpo",
+ actions = {
+ initialize = function()
+ reset()
+ end,
+ finalize = function(driver,details)
+ local bb = details.boundingbox
+ local llx = bb[1] * fc
+ local lly = bb[2] * fc
+ local urx = bb[3] * fc
+ local ury = bb[4] * fc
+ b = b + 1
+ buffer[b] = f_bounds(llx,lly,urx,ury)
+ -- inspect(buffer)
+ end,
+ },
+ flushers = {
+ updatefontstate = updatefontstate,
+ character = flushcharacter,
+ rule = flushrule,
+ simplerule = flushsimplerule,
+ specialrule = flushspecialrule,
+ }
+}
+
+function metapost.boxtomp(n,k)
+ kind = k
+ nodes.handlers.finalizebox(n)
+ drivers.converters.lmtx(drivers.instances.mpo,texgetbox(n),"box",1)
+ local result = concat(buffer,";")
+ reset()
+ return result
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-ogr.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-ogr.lmt
new file mode 100644
index 00000000000..698593ac7e0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-ogr.lmt
@@ -0,0 +1,785 @@
+if not modules then modules = { } end modules ['font-ogr'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Here we deal with graphic variants and for now also color support ends up here
+-- but that might change. It's lmtx only code.
+
+local tostring, tonumber, next, type, rawget = tostring, tonumber, next, type, rawget
+local round, max, mod, div = math.round, math.max, math.mod, math.div
+local find = string.find
+local concat, setmetatableindex, sortedhash = table.concat, table.setmetatableindex, table.sortedhash
+local utfbyte = utf.byte
+local formatters = string.formatters
+local settings_to_hash_strict, settings_to_array = utilities.parsers.settings_to_hash_strict, utilities.parsers.settings_to_array
+
+local otf = fonts.handlers.otf
+local otfregister = otf.features.register
+otf.svgenabled = true
+otf.pngenabled = true
+
+-- Just to remind me ... rewritten around the time this was posted on YT which
+-- was also around the 2019 ctx meeting:
+--
+-- Gavin Harrison - "Threatening War" by The Pineapple Thief
+-- https://www.youtube.com/watch?v=ENF9wth4kwM
+
+-- todo: svg color plugin
+-- todo: get rid of double cm in svg (tricky as also elsewhere)
+-- todo: png offsets (depth)
+-- todo: maybe collapse indices so that we have less files (harder to debug)
+-- todo: manage (read: assign) font id's in lua so we know in advance
+
+-- what here and what in backend ...
+
+do
+
+ -- This is a prelude to something better but I'm still experimenting.
+
+ local dropins = { }
+ fonts.dropins = dropins
+ local droppedin = 0
+ local identifiers = fonts.hashes.identifiers
+
+ function dropins.nextid()
+ droppedin = droppedin - 1
+ return droppedin
+ end
+
+ -- todo: pass specification table instead
+
+ function dropins.provide(method,t_tfmdata,indexdata,...)
+ local droppedin = dropins.nextid()
+ local t_characters = t_tfmdata.characters
+ local t_descriptions = t_tfmdata.descriptions
+ local t_properties = t_tfmdata.properties
+ local d_tfmdata = setmetatableindex({ },t_tfmdata)
+ local d_properties = setmetatableindex({ },t_properties)
+ d_properties.basefontname = "ContextRuntimeFont" .. droppedin
+ d_tfmdata.properties = d_properties
+ local d_characters = { } -- setmetatableindex({ },t_characters) -- hm, index vs unicode
+ local d_descriptions = { } -- setmetatableindex({ },t_descriptions) -- hm, index vs unicode
+ d_tfmdata.characters = d_characters
+ d_tfmdata.descriptions = d_descriptions
+ d_tfmdata.parentdata = t_tfmdata -- so we can access it if needed
+ d_properties.instance = - droppedin -- will become an extra element in the hash
+ identifiers[droppedin] = d_tfmdata
+ local fonts = t_tfmdata.fonts or { }
+ t_tfmdata.fonts = fonts
+ d_properties.format = "type3"
+ d_properties.method = method
+ d_properties.indexdata = { indexdata, ... } -- can take quite some memory
+ local slot = #fonts + 1
+ fonts[slot] = { id = droppedin }
+ return slot, droppedin, d_tfmdata, d_properties
+ end
+
+ -- todo: delay this, in which case we can be leaner and meaner
+
+ function dropins.clone(method,tfmdata,shapes,...) -- by index
+ if method and shapes then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local droppedin, tfmdrop, dropchars, dropdescs, colrshapes
+ local idx = 255
+ local slot = 0
+ for k, v in next, characters do
+ local index = v.index
+ if index then
+ local description = descriptions[k]
+ if description then
+ local shape = shapes[index]
+ if shape then
+ if idx >= 255 then
+ idx = 1
+ colrshapes = setmetatableindex({ },shapes)
+ slot, droppedin, tfmdrop = dropins.provide(method,tfmdata,colrshapes)
+ dropchars = tfmdrop.characters
+ dropdescs = tfmdrop.descriptions
+ else
+ idx = idx + 1
+ end
+ colrshapes[idx] = shape -- so not: description
+ -- todo: prepend
+ v.commands = { { "slot", slot, idx } }
+ -- hack to prevent that type 3 also gets 'use' flags .. todo
+ local c = { commands = false, index = idx, dropin = tfmdrop }
+ local d = { } -- { index = idx, dropin = tfmdrop }
+ setmetatableindex(c,v)
+ setmetatableindex(d,description)
+ dropchars[idx] = c
+ dropdescs[idx] = d -- not needed
+ end
+ end
+ end
+ end
+ else
+ -- error
+ end
+ end
+
+-- function dropins.swap(method,tfmdata,shapes,...) -- by unicode
+-- if method and shapes then
+-- local characters = tfmdata.characters
+-- local descriptions = tfmdata.descriptions
+-- local droppedin, tfmdrop, dropchars, dropdescs, colrshapes
+-- local idx = 255
+-- local slot = 0
+-- -- we can have a variant where shaped are by unicode and not by index
+-- for k, v in next, characters do
+-- local description = descriptions[k]
+-- if description then
+-- local shape = shapes[k]
+-- if shape then
+-- if idx >= 255 then
+-- idx = 1
+-- colrshapes = setmetatableindex({ },shapes)
+-- slot, droppedin, tfmdrop = dropins.provide(method,tfmdata,colrshapes)
+-- dropchars = tfmdrop.characters
+-- dropdescs = tfmdrop.descriptions
+-- else
+-- idx = idx + 1
+-- end
+-- colrshapes[idx] = shape -- so not: description
+-- -- todo: prepend
+-- v.commands = { { "slot", slot, idx } }
+-- -- hack to prevent that type 3 also gets 'use' flags .. todo
+-- local c = { commands = false, index = idx, dropin = tfmdrop }
+-- local d = { } -- index = idx, dropin = tfmdrop }
+-- setmetatableindex(c,v)
+-- setmetatableindex(d,description)
+-- dropchars[idx] = c
+-- dropdescs[idx] = d -- not needed
+-- end
+-- end
+-- end
+-- else
+-- -- error
+-- end
+-- end
+
+ function dropins.swap(method,tfmdata,shapes) -- by unicode
+ if method and shapes then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local droppedin = tfmdata.droppedin
+ local tfmdrop = tfmdata.tfmdrop
+ local dropchars = tfmdata.dropchars
+ local dropdescs = tfmdata.dropdescs
+ local colrshapes = tfmdata.colrshapes
+ local idx = tfmdata.dropindex or 255
+ local slot = tfmdata.dropslot or 0
+ -- we can have a variant where shaped are by unicode and not by index
+ for k, v in next, characters do
+ local description = descriptions[k]
+ if description then
+ local shape = shapes[k]
+ if shape then
+ if idx >= 255 then
+ idx = 1
+ colrshapes = setmetatableindex({ },shapes)
+ slot, droppedin, tfmdrop = dropins.provide(method,tfmdata,colrshapes)
+ dropchars = tfmdrop.characters
+ dropdescs = tfmdrop.descriptions
+ else
+ idx = idx + 1
+ end
+ colrshapes[idx] = shape -- so not: description
+ -- todo: prepend
+ v.commands = { { "slot", slot, idx } }
+ -- hack to prevent that type 3 also gets 'use' flags .. todo
+ local c = { commands = false, index = idx, dropin = tfmdrop }
+ local d = { } -- index = idx, dropin = tfmdrop }
+ setmetatableindex(c,v)
+ setmetatableindex(d,description)
+ dropchars[idx] = c
+ dropdescs[idx] = d -- not needed
+ end
+ end
+ end
+ tfmdata.droppedin = droppedin
+ tfmdata.tfmdrop = tfmdrop
+ tfmdata.dropchars = dropchars
+ tfmdata.dropdescs = dropdescs
+ tfmdata.colrshapes = colrshapes
+ tfmdata.dropindex = idx
+ tfmdata.dropslot = slot
+ else
+ -- error
+ end
+ end
+
+ function dropins.swapone(method,tfmdata,shape,unicode)
+ if method and shape then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local droppedin = tfmdata.droppedin
+ local tfmdrop = tfmdata.tfmdrop
+ local dropchars = tfmdata.dropchars
+-- local dropdescs = tfmdata.dropdescs
+ local colrshapes = tfmdata.colrshapes
+ local idx = tfmdata.dropindex or 255
+ local slot = tfmdata.dropslot or 0
+ local character = characters[unicode]
+-- local description = descriptions[unicode] or { }
+ if character then
+ if idx >= 255 then
+ idx = 1
+ colrshapes = setmetatableindex({ },shapes)
+ slot, droppedin, tfmdrop = dropins.provide(method,tfmdata,colrshapes)
+ dropchars = tfmdrop.characters
+ dropdescs = tfmdrop.descriptions
+ else
+ idx = idx + 1
+ end
+ colrshapes[idx] = shape.code -- so not: description
+ -- todo: prepend
+ character.commands = { { "slot", slot, idx } }
+ -- hack to prevent that type 3 also gets 'use' flags .. todo
+ local c = { commands = false, index = idx, dropin = tfmdrop }
+-- local d = { } -- index = idx, dropin = tfmdrop }
+ setmetatableindex(c,character)
+-- setmetatableindex(d,description)
+ dropchars[idx] = c
+-- dropdescs[idx] = d -- not needed
+ end
+ tfmdata.droppedin = droppedin
+ tfmdata.tfmdrop = tfmdrop
+ tfmdata.dropchars = dropchars
+-- tfmdata.dropdescs = dropdescs
+ tfmdata.colrshapes = colrshapes
+ tfmdata.dropindex = idx
+ tfmdata.dropslot = slot
+ end
+ end
+
+end
+
+do -- this will move to its own module
+
+ local dropins = fonts.dropins
+
+ local shapes = setmetatableindex(function(t,k)
+ local v = {
+ glyphs = { },
+ parameters = {
+ units = 10
+ },
+ }
+ t[k] = v
+ return v
+ end)
+
+ function dropins.getshape(name,n)
+ local s = shapes[name]
+ return s and s.glyphs and s.glyphs[n]
+ end
+
+ function dropins.getshapes(name)
+ return shapes[name]
+ end
+
+ function dropins.registerglyphs(parameters)
+ local category = parameters.name
+ local target = shapes[category].parameters
+ for k, v in next, parameters do
+ if k ~= "glyphs" then
+ target[k] = v
+ end
+ end
+ end
+
+ function dropins.registerglyph(parameters)
+ local category = parameters.category
+ local unicode = parameters.unicode
+ local private = parameters.private
+ local unichar = parameters.unichar
+ if private then
+ unicode = fonts.helpers.newprivateslot(private)
+ elseif type(unichar) == "string" then
+ unicode = utfbyte(unichar)
+ else
+ local unitype = type(unicode)
+ if unitype == "string" then
+ local uninumber = tonumber(unicode)
+ if uninumber then
+ unicode = round(uninumber)
+ else
+ unicode = utfbyte(unicode)
+ end
+ elseif unitype == "number" then
+ unicode = round(unicode)
+ end
+ end
+ if unicode then
+ parameters.unicode = unicode
+ -- print(category,unicode)
+ shapes[category].glyphs[unicode] = parameters
+ else
+ -- error
+ end
+ end
+
+ -- local function hascolorspec(t)
+ -- if (t.color or "") ~= "" then
+ -- return true
+ -- elseif (t.fillcolor or "") ~= "" then
+ -- return true
+ -- elseif (t.drawcolor or "") ~= "" then
+ -- return true
+ -- elseif (t.linecolor or "") ~= "" then
+ -- return true
+ -- else
+ -- return false
+ -- end
+ -- end
+
+ local function hascolorspec(t)
+ for k, v in next, t do
+ if find(k,"color") then
+ return true
+ end
+ end
+ return false
+ end
+
+ -- list of tonumber keywords
+
+ local function initializemps(tfmdata,kind,value)
+ if value then
+ local specification = settings_to_hash_strict(value)
+ if not specification or not next(specification) then
+ specification = { category = value }
+ end
+ -- todo: multiple categories but then maybe also different
+ -- clones because of the units .. for now we assume the same
+ -- units
+ local category = specification.category
+ if category and category ~= "" then
+ local categories = settings_to_array(category)
+ local usedshapes = nil
+ local index = 0
+ local spread = tonumber(specification.spread or 0) -- hm
+ local hascolor = hascolorspec(specification)
+
+ specification.spread = spread -- now a number, maybe also for slant, weight etc
+
+ local preroll = specification.preroll
+ if preroll then
+ metapost.simple(instance,"begingroup;",true,true)
+ metapost.setparameterset("mpsfont",specification)
+ metapost.simple("simplefun",preroll)
+ metapost.setparameterset("mpsfont")
+ metapost.simple(instance,"endgroup;",true,true)
+ end
+
+ for i=1,#categories do
+ local category = categories[i]
+ local mpsshapes = shapes[category]
+ if mpsshapes then
+ local properties = tfmdata.properties
+ local parameters = tfmdata.parameters
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local mpsparameters = mpsshapes.parameters
+ local units = mpsparameters.units or 1000
+ local defaultwidth = mpsparameters.width or 0
+ local defaultheight = mpsparameters.height or 0
+ local defaultdepth = mpsparameters.depth or 0
+ local usecolor = mpsparameters.usecolor
+ local spread = spread * units
+ local defaultcode = mpsparameters.code or ""
+ local scale = parameters.size / units
+ if hascolor then
+ -- the graphic has color
+ usecolor = false
+ else
+ -- do whatever is specified
+ end
+ usedshapes = usedshapes or {
+ instance = "simplefun",
+ units = units,
+ usecolor = usecolor,
+ specification = specification,
+ shapes = mpsshapes,
+ }
+ -- todo: deal with extensibles and more properties
+ for unicode, shape in sortedhash(mpsshapes.glyphs) do
+ index = index + 1 -- todo: somehow we end up with 2 as first entry after 0
+ local wd = shape.width or defaultwidth
+ local ht = shape.height or defaultheight
+ local dp = shape.depth or defaultdepth
+ local bb = shape.boundingbox
+ local uc = shape.tounicode
+ if uc then
+ uc = round(uc) -- brrr can be 123.0
+ end
+ if bb then
+ for i=1,4 do bb[i] = scale * bb[i] end
+ end
+ local newc = {
+ index = index, -- into usedshapes -- used?
+ width = scale * (wd + spread),
+ height = scale * ht,
+ depth = scale * dp,
+ boundingbox = bb,
+ unicode = uc or unicode,
+ -- shape = shape, -- maybe a copy
+ }
+ --
+ characters [unicode] = newc
+ descriptions[unicode] = newc
+ usedshapes [unicode] = shape.code or defaultcode
+ --
+ -- This is a way to get/use randomized shapes (see punk example).
+ --
+ if uc and uc ~= unicode then
+ local c = characters[uc]
+ if c then
+ local v = c.variants
+ if v then
+ v[#v + 1] = unicode
+ else
+ c.variants = { unicode }
+ end
+ end
+ end
+ end
+ end
+ end
+ if usedshapes then
+ -- todo: different font when units and usecolor changes, maybe move into loop
+ -- above
+ dropins.swap("mps",tfmdata,usedshapes)
+ end
+ end
+ end
+ end
+
+ -- This kicks in quite late, after features have been checked. So if needed
+ -- substitutions need to be defined with force.
+
+ otfregister {
+ name = "metapost",
+ description = "metapost glyphs",
+ manipulators = {
+ base = initializemps,
+ node = initializemps,
+ }
+ }
+
+end
+
+-- This sits here for historcal reasons so for now we keep it here.
+
+local startactualtext = nil
+local stopactualtext = nil
+
+function otf.getactualtext(s)
+ if not startactualtext then
+ startactualtext = backends.codeinjections.startunicodetoactualtextdirect
+ stopactualtext = backends.codeinjections.stopunicodetoactualtextdirect
+ end
+ return startactualtext(s), stopactualtext()
+end
+
+-- This is also somewhat specific.
+
+local color_direct = function() return false end
+local color_indirect = color_direct
+
+updaters.register("backend.update",function()
+ color_direct = lpdf.fonts.color_direct
+ color_indirect = lpdf.fonts.color_indirect
+end)
+
+local sharedpalettes = { } do
+
+ local register = attributes.colors.register
+
+ local colors = attributes.list[attributes.private('color')] or { }
+ local transparencies = attributes.list[attributes.private('transparency')] or { }
+
+ function otf.registerpalette(name,values)
+ sharedpalettes[name] = values
+ for i=1,#values do
+ local v = values[i]
+ if v == "textcolor" then
+ values[i] = false
+ else
+ local c = nil
+ local t = nil
+ if type(v) == "table" then
+ c = register(name,"rgb",
+ max(round((v.r or 0)*255),255)/255,
+ max(round((v.g or 0)*255),255)/255,
+ max(round((v.b or 0)*255),255)/255
+ )
+ else
+ c = colors[v]
+ t = transparencies[v]
+ end
+ values[i] = color_indirect(c,t)
+ end
+ end
+ end
+
+end
+
+local initializeoverlay do
+
+ local function autoconvert(t,k)
+ local v = { }
+ for i=1,#k do
+ local p = k[i]
+ v[i] = color_direct(p[1]/255, p[2]/255, p[3]/255)
+ end
+ t[k] = v
+ return v
+ end
+
+ initializeoverlay = function(tfmdata,kind,value) -- we really need the id ... todo
+ if value then
+ local resources = tfmdata.resources
+ local palettes = resources.colorpalettes
+ if palettes then
+ --
+ local converted = resources.converted
+ if not converted then
+ converted = setmetatableindex(autoconvert)
+ resources.converted = converted
+ end
+ local colorvalues = sharedpalettes[value]
+ local default = false -- so the text color (bad for icon overloads)
+ if colorvalues then
+ default = colorvalues[#colorvalues]
+ else
+ colorvalues = converted[palettes[tonumber(value) or 1] or palettes[1]] or { }
+ end
+ local classes = #colorvalues
+ if classes == 0 then
+ return
+ end
+ --
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local droppedin, tfmdrop, dropchars, dropdescs, colrshapes
+ local idx = 255
+ local slot = 0
+ --
+ -- maybe delay in which case we have less fonts as we can be sparse
+ --
+ for k, v in next, characters do
+ local index = v.index
+ if index then
+ local description = descriptions[k]
+ if description then
+ local colorlist = description.colors
+ if colorlist then
+ if idx >= 255 then
+ idx = 1
+ colrshapes = { }
+ slot, droppedin, tfmdrop = fonts.dropins.provide("color",tfmdata,colrshapes,colorvalues)
+ dropchars = tfmdrop.characters
+ dropdescs = tfmdrop.descriptions
+ else
+ idx = idx + 1
+ end
+ --
+ colrshapes[idx] = description
+ -- todo: use extender
+ local u = { "use", 0 }
+ for i=1,#colorlist do
+ u[i+2] = colorlist[i].slot
+ end
+ v.commands = { u, { "slot", slot, idx } }
+ -- hack to prevent that type 3 also gets 'use' flags .. todo
+ local c = { commands = false, index = idx, dropin = tfmdata }
+ local d = { } -- index = idx, dropin = tfmdrop
+ setmetatableindex(c,v)
+ setmetatableindex(d,description)
+ dropchars[idx] = c
+ dropdescs[idx] = d -- not needed
+ end
+ end
+ end
+ end
+ return true
+ end
+ end
+ end
+
+ fonts.handlers.otf.features.register {
+ name = "colr",
+ description = "color glyphs",
+ manipulators = {
+ base = initializeoverlay,
+ node = initializeoverlay,
+ }
+ }
+
+end
+
+local initializesvg do
+
+ local report_svg = logs.reporter("fonts","svg")
+
+ local cached = true -- maybe always false (after i've optimized the lot)
+
+ directives.register("fonts.svg.cached", function(v) cached = v end)
+
+ initializesvg = function(tfmdata,kind,value) -- hm, always value
+ if value then
+ local properties = tfmdata.properties
+ local svg = properties.svg
+ local hash = svg and svg.hash
+ local timestamp = svg and svg.timestamp
+ if not hash then
+ return
+ end
+ local shapes = nil
+ local method = nil
+ local enforce = attributes.colors.model == "cmyk"
+ if cached and not enforce then
+ -- we need a different hash than for mkiv, so we append:
+ local pdfhash = hash .. "-svg"
+ local pdffile = containers.read(otf.pdfcache,pdfhash)
+ local pdfshapes = pdffile and pdffile.pdfshapes
+ local pdftarget = file.join(otf.pdfcache.writable,file.addsuffix(pdfhash,"pdf"))
+ if not pdfshapes or pdffile.timestamp ~= timestamp or not next(pdfshapes) or not lfs.isfile(pdftarget) then
+ local svgfile = containers.read(otf.svgcache,hash)
+ local svgshapes = svgfile and svgfile.svgshapes
+ pdfshapes = svgshapes and metapost.svgshapestopdf(svgshapes,pdftarget,report_svg,tfmdata.parameters.units) or { }
+ -- look at ocl: we should store scale and x and y
+ containers.write(otf.pdfcache, pdfhash, {
+ pdfshapes = pdfshapes,
+ timestamp = timestamp,
+ })
+ end
+ shapes = pdfshapes
+ method = "pdf"
+ else
+ local mpsfile = containers.read(otf.mpscache,hash)
+ local mpsshapes = mpsfile and mpsfile.mpsshapes
+ if not mpsshapes or mpsfile.timestamp ~= timestamp or not next(mpsshapes) then
+ local svgfile = containers.read(otf.svgcache,hash)
+ local svgshapes = svgfile and svgfile.svgshapes
+ -- still suboptimal
+ mpsshapes = svgshapes and metapost.svgshapestomp(svgshapes,report_svg,tfmdata.parameters.units) or { }
+ if enforce then
+ -- cheap conversion, no black component generation
+ mpsshapes.preamble = "interim svgforcecmyk := 1;"
+ end
+ containers.write(otf.mpscache, hash, {
+ mpsshapes = mpsshapes,
+ timestamp = timestamp,
+ })
+ end
+ shapes = mpsshapes
+ method = "mps"
+ end
+ if shapes then
+ shapes.fixdepth = value == "fixdepth"
+ fonts.dropins.clone(method,tfmdata,shapes)
+ end
+ return true
+ end
+ end
+
+ otfregister {
+ name = "svg",
+ description = "svg glyphs",
+ manipulators = {
+ base = initializesvg,
+ node = initializesvg,
+ }
+ }
+
+end
+
+local initializepng do
+
+ -- If this is really critical we can also use a pdf file as cache but I don't expect
+ -- png fonts to remain used.
+
+ local colors = attributes.colors
+
+ local report_png = logs.reporter("fonts","png conversion")
+
+ initializepng = function(tfmdata,kind,value) -- hm, always value
+ if value then
+ local properties = tfmdata.properties
+ local png = properties.png
+ local hash = png and png.hash
+ local timestamp = png and png.timestamp
+ if not hash then
+ return
+ end
+ local pngfile = containers.read(otf.pngcache,hash)
+ local pngshapes = pngfile and pngfile.pngshapes
+ if pngshapes then
+ if colors.model == "cmyk" then
+ pngshapes.enforcecmyk = true
+ end
+ fonts.dropins.clone("png",tfmdata,pngshapes)
+ end
+ return true
+ end
+ end
+
+ otfregister {
+ name = "sbix",
+ description = "sbix glyphs",
+ manipulators = {
+ base = initializepng,
+ node = initializepng,
+ }
+ }
+
+ otfregister {
+ name = "cblc",
+ description = "cblc glyphs",
+ manipulators = {
+ base = initializepng,
+ node = initializepng,
+ }
+ }
+
+end
+
+do
+
+ -- I need to check jpeg and such but will do that when I run into
+ -- it.
+
+ local function initializecolor(tfmdata,kind,value)
+ if value == "auto" then
+ return
+ initializeoverlay(tfmdata,kind,value) or
+ initializesvg(tfmdata,kind,value) or
+ initializepng(tfmdata,kind,value)
+ elseif value == "overlay" then
+ return initializeoverlay(tfmdata,kind,value)
+ elseif value == "svg" then
+ return initializesvg(tfmdata,kind,value)
+ elseif value == "png" or value == "bitmap" then
+ return initializepng(tfmdata,kind,value)
+ else
+ -- forget about it
+ end
+ end
+
+ otfregister {
+ name = "color",
+ description = "color glyphs",
+ manipulators = {
+ base = initializecolor,
+ node = initializecolor,
+ }
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-ota.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-ota.lmt
new file mode 100644
index 00000000000..6475c57e64b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-ota.lmt
@@ -0,0 +1,580 @@
+if not modules then modules = { } end modules ['font-ota'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- context only
+
+local type = type
+local setmetatableindex = table.setmetatableindex
+
+if not trackers then trackers = { register = function() end } end
+
+----- trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
+
+local fonts, nodes, node = fonts, nodes, node
+
+local allocate = utilities.storage.allocate
+
+local otf = fonts.handlers.otf
+
+local analyzers = fonts.analyzers
+local initializers = allocate()
+local methods = allocate()
+
+analyzers.initializers = initializers
+analyzers.methods = methods
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getprev = nuts.getprev
+local getprop = nuts.getprop
+local setprop = nuts.setprop
+local getsubtype = nuts.getsubtype
+local getchar = nuts.getchar
+local ischar = nuts.ischar
+local isnextchar = nuts.isnextchar
+----- isprevchar = nuts.isprevchar
+
+local end_of_math = nuts.end_of_math
+
+local nodecodes = nodes.nodecodes
+local disc_code = nodecodes.disc
+local math_code = nodecodes.math
+
+local fontdata = fonts.hashes.identifiers
+local descriptiondata = fonts.hashes.descriptions
+local categories = characters and characters.categories or { } -- sorry, only in context
+local chardata = characters and characters.data
+
+local otffeatures = fonts.constructors.features.otf
+local registerotffeature = otffeatures.register
+
+--[[ldx--
+<p>Analyzers run per script and/or language and are needed in order to
+process features right.</p>
+--ldx]]--
+
+local setstate = nuts.setstate
+local getstate = nuts.getstate
+
+local classifiers = characters.classifiers
+
+-- never use these numbers directly
+
+local s_init = 1 local s_rphf = 7
+local s_medi = 2 local s_half = 8
+local s_fina = 3 local s_pref = 9
+local s_isol = 4 local s_blwf = 10
+local s_mark = 5 local s_pstf = 11
+local s_rest = 6
+
+local states = allocate {
+ init = s_init,
+ medi = s_medi,
+ med2 = s_medi,
+ fina = s_fina,
+ fin2 = s_fina,
+ fin3 = s_fina,
+ isol = s_isol,
+ mark = s_mark,
+ rest = s_rest,
+ rphf = s_rphf,
+ half = s_half,
+ pref = s_pref,
+ blwf = s_blwf,
+ pstf = s_pstf,
+}
+
+local features = allocate {
+ init = s_init,
+ medi = s_medi,
+ med2 = s_medi,
+ fina = s_fina,
+ fin2 = s_fina,
+ fin3 = s_fina,
+ isol = s_isol,
+ -- mark = s_mark,
+ -- rest = s_rest,
+ rphf = s_rphf,
+ half = s_half,
+ pref = s_pref,
+ blwf = s_blwf,
+ pstf = s_pstf,
+}
+
+local mappers = allocate {
+ l = s_init, -- left
+ d = s_medi, -- double
+ c = s_medi, -- joiner
+ r = s_fina, -- right
+ u = s_isol, -- nonjoiner
+}
+
+analyzers.states = states
+analyzers.features = features
+analyzers.useunicodemarks = false
+
+if not classifiers then
+
+ -- why not just always use categories[k] = "mn"
+
+ local f_arabic, l_arabic = characters.blockrange("arabic")
+ local f_syriac, l_syriac = characters.blockrange("syriac")
+ local f_mandiac, l_mandiac = characters.blockrange("mandiac")
+ local f_nko, l_nko = characters.blockrange("nko")
+ local f_ext_a, l_ext_a = characters.blockrange("arabicextendeda")
+
+ classifiers = setmetatableindex(function(t,k)
+ if type(k) == "number" then
+ local c = chardata[k]
+ local v = false
+ if c then
+ local arabic = c.arabic
+ if arabic then
+ v = mappers[arabic]
+ if not v then
+ log.report("analyze","error in mapping arabic %C",k)
+ -- error
+ v = false
+ end
+ elseif (k >= f_arabic and k <= l_arabic) or
+ (k >= f_syriac and k <= l_syriac) or
+ (k >= f_mandiac and k <= l_mandiac) or
+ (k >= f_nko and k <= l_nko) or
+ (k >= f_ext_a and k <= l_ext_a) then
+ if categories[k] == "mn" then
+ v = s_mark
+ else
+ v = s_rest
+ end
+ end
+ end
+ t[k] = v
+ return v
+ end
+ end)
+
+ characters.classifiers = classifiers
+
+end
+
+-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script
+-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace
+
+local is_letter = characters.is_letter
+local is_mark = characters.is_mark
+
+function analyzers.setstate(head,font) -- latin
+ local useunicodemarks = analyzers.useunicodemarks
+ local descriptions = descriptiondata[font]
+ local first = false
+ local last = false
+ local current = head
+ local done = false
+ -- only letters
+ while current do
+ -- local char, id = ischar(current,font)
+ local nxt, char, id = isnextchar(current,font)
+ if char then
+ if not getstate(current) then
+ -- local d = descriptions[char]
+ -- if d then
+ -- if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then
+ if is_mark[char] then
+ setstate(current,s_mark)
+ elseif is_letter[char] then
+ if first then
+ setstate(current,s_medi)
+ else
+ setstate(current,s_init)
+ first = current
+ done = true
+ end
+ last = current
+ else
+ goto PICKUP
+ end
+ goto NEXT
+ -- end
+ end
+ ::PICKUP::
+ if first then
+ setstate(last,first == last and s_isol or s_fina)
+ end
+ first = false
+ elseif char == false then
+ -- other font
+ if first then
+ setstate(last,first == last and s_isol or s_fina)
+ end
+ first = false
+ if id == math_code then
+ -- current = end_of_math(current)
+ nxt = getnext(end_of_math(current))
+ end
+ elseif id == disc_code then
+ -- always in the middle .. it doesn't make much sense to assign a property
+ -- here ... we might at some point decide to flag the components when present
+ -- but even then it's kind of bogus
+ setstate(current,s_medi)
+ last = current
+ else -- finish
+ if first then
+ setstate(last,first == last and s_isol or s_fina)
+ end
+ first = false
+ if id == math_code then
+ current = end_of_math(current)
+ end
+ end
+ ::NEXT::
+ -- current = getnext(current)
+ current = nxt
+ end
+ if first then
+ setstate(last,first == last and s_isol or s_fina)
+ end
+ return head, done
+end
+
+-- in the future we will use language/script attributes instead of the
+-- font related value, but then we also need dynamic features which is
+-- somewhat slower; and .. we need a chain of them
+
+local function analyzeinitializer(tfmdata,value) -- attr
+ local script, language = otf.scriptandlanguage(tfmdata) -- attr
+ local action = initializers[script]
+ if not action then
+ -- skip
+ elseif type(action) == "function" then
+ return action(tfmdata,value)
+ else
+ local action = action[language]
+ if action then
+ return action(tfmdata,value)
+ end
+ end
+end
+
+local function analyzeprocessor(head,font,attr)
+ local tfmdata = fontdata[font]
+ local script, language = otf.scriptandlanguage(tfmdata,attr)
+ local action = methods[script]
+ if not action then
+ -- skip
+ elseif type(action) == "function" then
+ return action(head,font,attr)
+ else
+ action = action[language]
+ if action then
+ return action(head,font,attr)
+ end
+ end
+ return head, false
+end
+
+registerotffeature {
+ name = "analyze",
+ description = "analysis of character classes",
+ default = true,
+ initializers = {
+ node = analyzeinitializer,
+ },
+ processors = {
+ position = 1,
+ node = analyzeprocessor,
+ }
+}
+
+-- latin
+
+methods.latn = analyzers.setstate
+-------.dflt = analyzers.setstate % can be an option or just the default
+
+local arab_warned = { }
+
+local function warning(current,what)
+ local char = getchar(current)
+ if not arab_warned[char] then
+ log.report("analyze","arab: character %C has no %a class",char,what)
+ arab_warned[char] = true
+ end
+end
+
+-- we can also use this trick for devanagari
+
+function methods.arab(head,font,attr)
+ local first, last, c_first, c_last
+ local current = head
+ local done = false
+ current = tonut(current)
+ while current do
+ -- local char, id = ischar(current,font)
+ local nxt, char, id = isnextchar(current,font)
+ if char and not getstate(current) then
+ done = true
+ local classifier = classifiers[char]
+ if not classifier then
+ if last then
+ if c_last == s_medi or c_last == s_fina then
+ setstate(last,s_fina)
+ else
+ warning(last,"fina")
+ setstate(last,s_error)
+ end
+ first = nil
+ last = nil
+ elseif first then
+ if c_first == s_medi or c_first == s_fina then
+ setstate(first,s_isol)
+ else
+ warning(first,"isol")
+ setstate(first,s_error)
+ end
+ first = nil
+ end
+ elseif classifier == s_mark then
+ setstate(current,s_mark)
+ elseif classifier == s_isol then
+ if last then
+ if c_last == s_medi or c_last == s_fina then
+ setstate(last,s_fina)
+ else
+ warning(last,"fina")
+ setstate(last,s_error)
+ end
+ first = nil
+ last = nil
+ elseif first then
+ if c_first == s_medi or c_first == s_fina then
+ setstate(first,s_isol)
+ else
+ warning(first,"isol")
+ setstate(first,s_error)
+ end
+ first = nil
+ end
+ setstate(current,s_isol)
+ elseif classifier == s_medi then
+ if first then
+ last = current
+ c_last = classifier
+ setstate(current,s_medi)
+ else
+ setstate(current,s_init)
+ first = current
+ c_first = classifier
+ end
+ elseif classifier == s_fina then
+ if last then
+ if getstate(last) ~= s_init then
+ setstate(last,s_medi)
+ end
+ setstate(current,s_fina)
+ first, last = nil, nil
+ elseif first then
+ -- if getstate(first) ~= s_init then
+ -- -- needs checking
+ -- setstate(first,s_medi)
+ -- end
+ setstate(current,s_fina)
+ first = nil
+ else
+ setstate(current,s_isol)
+ end
+ else -- classifier == s_rest
+ setstate(current,s_rest)
+ if last then
+ if c_last == s_medi or c_last == s_fina then
+ setstate(last,s_fina)
+ else
+ warning(last,"fina")
+ setstate(last,s_error)
+ end
+ first = nil
+ last = nil
+ elseif first then
+ if c_first == s_medi or c_first == s_fina then
+ setstate(first,s_isol)
+ else
+ warning(first,"isol")
+ setstate(first,s_error)
+ end
+ first = nil
+ end
+ end
+ else
+ if last then
+ if c_last == s_medi or c_last == s_fina then
+ setstate(last,s_fina)
+ else
+ warning(last,"fina")
+ setstate(last,s_error)
+ end
+ first = nil
+ last = nil
+ elseif first then
+ if c_first == s_medi or c_first == s_fina then
+ setstate(first,s_isol)
+ else
+ warning(first,"isol")
+ setstate(first,s_error)
+ end
+ first = nil
+ end
+ if id == math_code then -- a bit duplicate as we test for glyphs twice
+ -- current = end_of_math(current)
+ nxt = getnext(end_of_math(current))
+ end
+ end
+ -- current = getnext(current)
+ current = nxt
+ end
+ if last then
+ if c_last == s_medi or c_last == s_fina then
+ setstate(last,s_fina)
+ else
+ warning(last,"fina")
+ setstate(last,s_error)
+ end
+ elseif first then
+ if c_first == s_medi or c_first == s_fina then
+ setstate(first,s_isol)
+ else
+ warning(first,"isol")
+ setstate(first,s_error)
+ end
+ end
+ return head, done
+end
+
+methods.syrc = methods.arab
+methods.mand = methods.arab
+methods.nko = methods.arab
+
+-- a quick first attemp .. more later
+
+do
+
+ -- https://github.com/n8willis/opentype-shaping-documents/blob/master/opentype-shaping-mongolian.md#joining-properties
+ -- todo syrc
+
+ local joining = setmetatableindex(function(t,k)
+ if type(k) == "number" then
+ local c = chardata[k]
+ local v = false
+ if c then
+ local mongolian = c.mongolian
+ --
+ v = mongolian
+ end
+ t[k] = v
+ return v
+ end
+ end)
+
+ function methods.mong(head,font,attr)
+ local first, last
+ local current = head
+ local done = false
+ local prevjoin = nil
+ local prestate = nil
+ current = tonut(current)
+
+ local function wrapup()
+ if last then
+ if last ~= first then
+ local s = getstate(last)
+ if s == s_medi then
+ setstate(last,s_fina)
+ elseif s == s_init then
+ setstate(last,s_isol)
+ end
+ end
+ last = nil
+ first = nil
+ prevjoin = nil
+ prestate = nil
+ end
+ end
+
+ while current do
+ -- local char, id = ischar(current,font)
+ local nxt, char, id = isnextchar(current,font)
+ if char and not getstate(current) then
+ local currjoin = joining[char]
+ done = true
+ if not last then
+ setstate(current,s_isol)
+ prevjoin = currjoin
+ first = current
+ last = current
+ prevstate = s_isol
+ elseif currjoin == "t" then -- transparent
+ -- keep state
+ last = current
+ elseif prevjoin == "d" or prevjoin == "jc" or prevjoin == "l" then
+ if currjoin == "d" or prevjoin == "jc" or prevjoin == "r" then
+ local s = getstate(last)
+ if s == s_isol then
+ setstate(last,s_init)
+ elseif s == s_fina then
+ setstate(last,s_medi)
+ end
+ setstate(current,s_fina)
+ prevstate = s_fina
+ elseif prevjoin == "nj" or prevjoin == "l" then
+ local s = getstate(last)
+ if s == s_medi then
+ setstate(last,s_fina)
+ elseif s == s_init then
+ setstate(last,s_isol)
+ end
+ setstate(current,s_isol)
+ prevstate = s_isol
+ end
+ prevjoin = currjoin
+ last = current
+ elseif prevjoin == "nj" or prevjoin == "r" then
+ if s == s_medi then
+ setstate(last,s_fina)
+ elseif s == s_init then
+ setstate(last,s_isol)
+ end
+ setstate(current,s_isol)
+ prevjoin = currjoin
+ prevstate = s_isol
+ last = current
+ elseif last then
+ wrapup()
+ end
+ else
+ if last then
+ wrapup()
+ end
+ if id == math_code then -- a bit duplicate as we test for glyphs twice
+ -- current = end_of_math(current)
+ nxt = getnext(end_of_math(current))
+ end
+ end
+ -- current = getnext(current)
+ current = nxt
+ end
+ if last then
+ wrapup()
+ end
+ return head, done
+ end
+
+end
+
+directives.register("otf.analyze.useunicodemarks",function(v)
+ analyzers.useunicodemarks = v
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-otj.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-otj.lmt
new file mode 100644
index 00000000000..e9e9d524e4f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-otj.lmt
@@ -0,0 +1,2556 @@
+if not modules then modules = { } end modules ['font-otj'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to font-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- This property based variant is not faster but looks nicer than the attribute one. We
+-- need to use rawget (which is about 4 times slower than a direct access but we cannot
+-- get/set that one for our purpose! This version does a bit more with discretionaries
+-- (and Kai has tested it with his collection of weird fonts.)
+
+-- There is some duplicate code here (especially in the the pre/post/replace branches) but
+-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
+-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
+-- being too clever here is dangerous.
+
+-- As we have a rawget on properties we don't need one on injections.
+
+-- The use_advance code was just a test and is meant for testing and manuals. There is no
+-- performance (or whatever) gain and using kerns is somewhat cleaner (at least for now).
+
+-- An alternative is to have a list per base of all marks and then do a run over the node
+-- list that resolves the accumulated l/r/x/y and then do an inject pass.
+
+-- The thing with these positioning options is that it is not clear what Uniscribe does with
+-- the 2rl flag and we keep oscillating a between experiments.
+
+-- Beware: combining advance with cursive and marks can be a problem because marks have no
+-- dimensions .. maybe we need a special field for that ... the advance also sets the apply
+-- x offset flag!
+
+if not nodes.properties then return end
+
+local next, rawget, tonumber = next, rawget, tonumber
+local fastcopy = table.fastcopy
+
+local registertracker = trackers.register
+local registerdirective = directives.register
+
+local trace_injections = false registertracker("fonts.injections", function(v) trace_injections = v end)
+local trace_marks = false registertracker("fonts.injections.marks", function(v) trace_marks = v end)
+local trace_cursive = false registertracker("fonts.injections.cursive", function(v) trace_cursive = v end)
+local trace_spaces = false registertracker("fonts.injections.spaces", function(v) trace_spaces = v end)
+
+-- local fix_cursive_marks = false
+--
+-- registerdirective("fonts.injections.fixcursivemarks", function(v)
+-- fix_cursive_marks = v
+-- end)
+
+local report_injections = logs.reporter("fonts","injections")
+local report_spaces = logs.reporter("fonts","spaces")
+
+local attributes, nodes, node = attributes, nodes, node
+
+fonts = fonts
+local hashes = fonts.hashes
+local fontdata = hashes.identifiers
+local fontmarks = hashes.marks
+local parameters = fonts.hashes.parameters -- not in generic
+----- resources = fonts.hashes.resources -- not in generic
+
+nodes.injections = nodes.injections or { }
+local injections = nodes.injections
+
+local tracers = nodes.tracers
+local setcolor = tracers and tracers.colors.set
+local resetcolor = tracers and tracers.colors.reset
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+local tonode = nuts.tonode
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getfont = nuts.getfont
+local getchar = nuts.getchar
+local setchar = nuts.setchar
+----- getxoffset = nuts.getxoffset
+----- getyoffset = nuts.getyoffset
+local getoffsets = nuts.getoffsets
+local getxscale = nuts.getxscale
+local getyscale = nuts.getyscale
+local getxyscales = nuts.getxyscales
+local xscaled = nuts.xscaled
+local yscaled = nuts.yscaled
+local getboth = nuts.getboth
+local getdisc = nuts.getdisc
+local setdisc = nuts.setdisc
+local setoffsets = nuts.setoffsets
+local addxoffset = nuts.addxoffset
+local addyoffset = nuts.addyoffset
+local ischar = nuts.ischar
+local isnextchar = nuts.isnextchar
+local getkern = nuts.getkern
+local setkern = nuts.setkern
+local setlink = nuts.setlink
+local setwidth = nuts.setwidth
+local getwidth = nuts.getwidth
+local addxymargins = nuts.addxymargins -- we delegate scaling
+local copynode = nuts.copy
+
+local nextchar = nuts.traversers.char
+local nextglue = nuts.traversers.glue
+
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+
+local properties = nodes.properties.data
+
+local fontkern = nuts.pool and nuts.pool.fontkern
+local italickern = nuts.pool and nuts.pool.italickern
+
+local useitalickerns = false
+local useadvance = false
+local usezwjkerns = true -- when useadvance
+
+directives.register("fonts.injections.useitalics", function(v)
+ if v then
+ report_injections("using italics for space kerns (tracing only)")
+ end
+ useitalickerns = v
+end)
+
+registerdirective("fonts.injections.method", function(v)
+ useadvance = v == "advance"
+end)
+
+function injections.installnewkern() end -- obsolete
+
+local nofregisteredkerns = 0
+local nofregisteredpositions = 0
+local nofregisteredmarks = 0
+local nofregisteredcursives = 0
+local keepregisteredcounts = false
+
+function injections.keepcounts()
+ keepregisteredcounts = true
+end
+
+function injections.resetcounts()
+ nofregisteredkerns = 0
+ nofregisteredpositions = 0
+ nofregisteredmarks = 0
+ nofregisteredcursives = 0
+ keepregisteredcounts = false
+end
+
+-- We need to make sure that a possible metatable will not kick in unexpectedly.
+
+function injections.reset(n)
+ local p = rawget(properties,n)
+ if p then
+ p.injections = false -- { } -- nil should work too as we use rawget
+ else
+ properties[n] = false -- { injections = { } } -- nil should work too as we use rawget
+ end
+end
+
+function injections.copy(target,source)
+ local sp = rawget(properties,source)
+ if sp then
+ local tp = rawget(properties,target)
+ local si = sp.injections
+ if si then
+ si = fastcopy(si)
+ if tp then
+ tp.injections = si
+ else
+ properties[target] = {
+ injections = si,
+ }
+ end
+ elseif tp then
+ tp.injections = false -- { }
+ else
+ properties[target] = { injections = { } }
+ end
+ else
+ local tp = rawget(properties,target)
+ if tp then
+ tp.injections = false -- { }
+ else
+ properties[target] = false -- { injections = { } }
+ end
+ end
+end
+
+function injections.setligaindex(n,index) -- todo: don't set when 0
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections
+ if i then
+ i.ligaindex = index
+ else
+ p.injections = {
+ ligaindex = index
+ }
+ end
+ else
+ properties[n] = {
+ injections = {
+ ligaindex = index
+ }
+ }
+ end
+end
+
+function injections.getligaindex(n,default)
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections
+ if i then
+ return i.ligaindex or default
+ end
+ end
+ return default
+end
+
+function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag)
+
+ -- The standard says something about the r2lflag related to the first in a series:
+ --
+ -- When this bit is set, the last glyph in a given sequence to which the cursive
+ -- attachment lookup is applied, will be positioned on the baseline.
+ --
+ -- But it looks like we don't need to consider it.
+
+ local dx = factor*(exit[1]-entry[1])
+ local dy = -factor*(exit[2]-entry[2])
+ local ws = tfmstart.width
+ local wn = tfmnext.width
+ nofregisteredcursives = nofregisteredcursives + 1
+ if rlmode < 0 then
+ dx = -(dx + wn)
+ else
+ dx = dx - ws
+ end
+ if dx == 0 then
+ -- get rid of funny -0
+ dx = 0
+ end
+ --
+ local p = rawget(properties,start)
+ if p then
+ local i = p.injections
+ if i then
+ i.cursiveanchor = true
+ else
+ p.injections = {
+ cursiveanchor = true,
+ }
+ end
+ else
+ properties[start] = {
+ injections = {
+ cursiveanchor = true,
+ },
+ }
+ end
+ local p = rawget(properties,nxt)
+ if p then
+ local i = p.injections
+ if i then
+ i.cursivex = dx
+ i.cursivey = dy
+ else
+ p.injections = {
+ cursivex = dx,
+ cursivey = dy,
+ }
+ end
+ else
+ properties[nxt] = {
+ injections = {
+ cursivex = dx,
+ cursivey = dy,
+ },
+ }
+ end
+ return dx, dy, nofregisteredcursives
+end
+
+-- kind: 0=single 1=first of pair, 2=second of pair
+
+function injections.setposition(kind,current,factor,rlmode,spec,injection)
+ local x = factor * (spec[1] or 0)
+ local y = factor * (spec[2] or 0)
+ local w = factor * (spec[3] or 0)
+ local h = factor * (spec[4] or 0)
+ if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
+ local yoffset = y - h
+ local leftkern = x -- both kerns are set in a pair kern compared
+ local rightkern = w - x -- to normal kerns where we set only leftkern
+ if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
+ nofregisteredpositions = nofregisteredpositions + 1
+ if rlmode and rlmode < 0 then
+ leftkern, rightkern = rightkern, leftkern
+ end
+ if not injection then
+ injection = "injections"
+ end
+ local p = rawget(properties,current)
+ if p then
+ local i = p[injection]
+ if i then
+ if leftkern ~= 0 then
+ i.leftkern = (i.leftkern or 0) + leftkern
+ end
+ if rightkern ~= 0 then
+ i.rightkern = (i.rightkern or 0) + rightkern
+ end
+ if yoffset ~= 0 then
+ i.yoffset = (i.yoffset or 0) + yoffset
+ end
+ elseif leftkern ~= 0 or rightkern ~= 0 then
+ p[injection] = {
+ leftkern = leftkern,
+ rightkern = rightkern,
+ yoffset = yoffset,
+ }
+ else
+ p[injection] = {
+ yoffset = yoffset,
+ }
+ end
+ elseif leftkern ~= 0 or rightkern ~= 0 then
+ properties[current] = {
+ [injection] = {
+ leftkern = leftkern,
+ rightkern = rightkern,
+ yoffset = yoffset,
+ },
+ }
+ else
+ properties[current] = {
+ [injection] = {
+ yoffset = yoffset,
+ },
+ }
+ end
+ return x, y, w, h, nofregisteredpositions
+ end
+ end
+ return x, y, w, h -- no bound
+end
+
+-- The next one is used for simple kerns coming from a truetype kern table. The r2l
+-- variant variant needs checking but it is unlikely that a r2l script uses thsi
+-- feature.
+
+function injections.setkern(current,factor,rlmode,x,injection)
+ local dx = factor * x
+ if dx ~= 0 then
+ nofregisteredkerns = nofregisteredkerns + 1
+ local p = rawget(properties,current)
+ if not injection then
+ injection = "injections"
+ end
+ if p then
+ local i = p[injection]
+ if i then
+ i.leftkern = dx + (i.leftkern or 0)
+ else
+ p[injection] = {
+ leftkern = dx,
+ }
+ end
+ else
+ properties[current] = {
+ [injection] = {
+ leftkern = dx,
+ },
+ }
+ end
+ return dx, nofregisteredkerns
+ else
+ return 0, 0
+ end
+end
+
+-- This one is an optimization of pairs where we have only a "w" entry. This one is
+-- potentially different from the previous one wrt r2l. It needs checking. The
+-- optimization relates to smaller tma files.
+
+function injections.setmove(current,factor,rlmode,x,injection)
+ local dx = factor * x
+ if dx ~= 0 then
+ nofregisteredkerns = nofregisteredkerns + 1
+ local p = rawget(properties,current)
+ if not injection then
+ injection = "injections"
+ end
+ if rlmode and rlmode < 0 then
+ -- we need to swap with a single so then we also need to to it here
+ -- as move is just a simple single
+ if p then
+ local i = p[injection]
+ if i then
+ i.rightkern = dx + (i.rightkern or 0)
+ else
+ p[injection] = {
+ rightkern = dx,
+ }
+ end
+ else
+ properties[current] = {
+ [injection] = {
+ rightkern = dx,
+ },
+ }
+ end
+ else
+ if p then
+ local i = p[injection]
+ if i then
+ i.leftkern = dx + (i.leftkern or 0)
+ else
+ p[injection] = {
+ leftkern = dx,
+ }
+ end
+ else
+ properties[current] = {
+ [injection] = {
+ leftkern = dx,
+ },
+ }
+ end
+ end
+ return dx, nofregisteredkerns
+ else
+ return 0, 0
+ end
+end
+
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor
+ local dx = factor*(ba[1]-ma[1])
+ local dy = factor*(ba[2]-ma[2])
+ nofregisteredmarks = nofregisteredmarks + 1
+ if rlmode >= 0 then
+ dx = tfmbase.width - dx -- see later commented ox
+ end
+ local p = rawget(properties,start)
+ -- hm, dejavu serif does a sloppy mark2mark before mark2base
+ if p then
+ local i = p.injections
+ if i then
+ if i.markmark then
+ -- out of order mkmk: yes or no or option
+ else
+ -- if dx ~= 0 then
+ -- i.markx = dx
+ -- end
+ -- if y ~= 0 then
+ -- i.marky = dy
+ -- end
+ -- if rlmode then
+ -- i.markdir = rlmode
+ -- end
+ i.markx = dx
+ i.marky = dy
+ i.markdir = rlmode or 0
+ i.markbase = nofregisteredmarks
+ i.markbasenode = base
+ i.markmark = mkmk
+ i.checkmark = checkmark
+ end
+ else
+ p.injections = {
+ markx = dx,
+ marky = dy,
+ markdir = rlmode or 0,
+ markbase = nofregisteredmarks,
+ markbasenode = base,
+ markmark = mkmk,
+ checkmark = checkmark,
+ }
+ end
+ else
+ properties[start] = {
+ injections = {
+ markx = dx,
+ marky = dy,
+ markdir = rlmode or 0,
+ markbase = nofregisteredmarks,
+ markbasenode = base,
+ markmark = mkmk,
+ checkmark = checkmark,
+ },
+ }
+ end
+ return dx, dy, nofregisteredmarks
+end
+
+local function dir(n)
+ return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
+end
+
+local function showchar(n,nested)
+ local char = getchar(n)
+ report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
+end
+
+local function show(n,what,nested,symbol)
+ if n then
+ local p = rawget(properties,n)
+ if p then
+ local i = p[what]
+ if i then
+ local leftkern = i.leftkern or 0
+ local rightkern = i.rightkern or 0
+ local yoffset = i.yoffset or 0
+ local markx = i.markx or 0
+ local marky = i.marky or 0
+ local markdir = i.markdir or 0
+ local markbase = i.markbase or 0
+ local cursivex = i.cursivex or 0
+ local cursivey = i.cursivey or 0
+ local ligaindex = i.ligaindex or 0
+ local cursbase = i.cursiveanchor
+ local margin = nested and 4 or 2
+ --
+ if rightkern ~= 0 or yoffset ~= 0 then
+ report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
+ elseif leftkern ~= 0 then
+ report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
+ end
+ if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
+ report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no")
+ end
+ if cursivex ~= 0 or cursivey ~= 0 then
+ if cursbase then
+ report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
+ else
+ report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
+ end
+ elseif cursbase then
+ report_injections("%w%s curs: base",margin,symbol)
+ end
+ if ligaindex ~= 0 then
+ report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
+ end
+ end
+ end
+ end
+end
+
+local function showsub(n,what,where)
+ report_injections("begin subrun: %s",where)
+ for n in nextchar, n do
+ showchar(n,where)
+ show(n,what,where," ")
+ end
+ report_injections("end subrun")
+end
+
+local function trace(head,where)
+ report_injections()
+ report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered",
+ where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives)
+ local n = head
+ while n do
+ local id = getid(n)
+ if id == glyph_code then
+ showchar(n)
+ show(n,"injections",false," ")
+ show(n,"preinjections",false,"<")
+ show(n,"postinjections",false,">")
+ show(n,"replaceinjections",false,"=")
+ show(n,"emptyinjections",false,"*")
+ elseif id == disc_code then
+ local pre, post, replace = getdisc(n)
+ if pre then
+ showsub(pre,"preinjections","pre")
+ end
+ if post then
+ showsub(post,"postinjections","post")
+ end
+ if replace then
+ showsub(replace,"replaceinjections","replace")
+ end
+ show(n,"emptyinjections",false,"*")
+ end
+ n = getnext(n)
+ end
+ report_injections("end run")
+end
+
+local function show_result(head)
+ local current = head
+ local skipping = false
+ while current do
+ local id = getid(current)
+ if id == glyph_code then
+ local w = getwidth(current)
+ local x, y = getoffsets(current)
+ report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y)
+ skipping = false
+ elseif id == kern_code then
+ report_injections("kern: %p",getkern(current))
+ skipping = false
+ elseif not skipping then
+ report_injections()
+ skipping = true
+ end
+ current = getnext(current)
+ end
+ report_injections()
+end
+
+-- G +D-pre G
+-- D-post+
+-- +D-replace+
+--
+-- G +D-pre +D-pre
+-- D-post +D-post
+-- +D-replace +D-replace
+
+-- Beware! pre/post/replace can have non glyphs too
+
+-- local function inject_kerns_only(head,where)
+-- if trace_injections then
+-- trace(head,"kerns")
+-- end
+-- local current = head
+-- local prev -- = nil
+-- local prevdisc -- = nil
+-- local pre -- = nil -- saves a lookup
+-- local post -- = nil -- saves a lookup
+-- local replace -- = nil -- saves a lookup
+-- local pretail -- = nil -- saves a lookup
+-- local posttail -- = nil -- saves a lookup
+-- local replacetail -- = nil -- saves a lookup
+-- while current do
+-- local next, char, id = isnextchar(current)
+-- if char then
+-- local p = rawget(properties,current)
+-- if p then
+-- local i = p.injections
+-- if i then
+-- -- left|glyph|right
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- if useadvance then
+-- addmargins(current,-leftkern)
+-- elseif prev and getid(prev) == glue_code then
+-- if useitalickerns then
+-- head = insert_node_before(head,current,italickern(leftkern))
+-- else
+-- setwidth(prev, getwidth(prev) + leftkern)
+-- end
+-- else
+-- head = insert_node_before(head,current,fontkern(leftkern))
+-- end
+-- end
+-- end
+-- if prevdisc then
+-- local done = false
+-- if post then
+-- local i = p.postinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- if useadvance then
+-- addmargins(posttail,false,-leftkern)
+-- else
+-- setlink(posttail,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- if replace then
+-- local i = p.replaceinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- if useadvance then
+-- addmargins(replacetail,false,-leftkern)
+-- else
+-- setlink(replacetail,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- else
+-- local i = p.emptyinjections
+-- if i then
+-- -- glyph|disc|glyph (special case)
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- -- if useadvance then
+-- -- add zwj with offset
+-- -- else
+-- replace = fontkern(leftkern)
+-- done = true
+-- -- end
+-- end
+-- end
+-- end
+-- if done then
+-- setdisc(prevdisc,pre,post,replace)
+-- end
+-- end
+-- end
+-- prevdisc = nil
+-- -- prevglyph = current
+-- elseif char == false then
+-- -- other font
+-- prevdisc = nil
+-- -- prevglyph = current
+-- elseif id == disc_code then
+-- pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+-- local done = false
+-- if pre then
+-- -- left|pre glyphs|right
+-- for n in nextchar, pre do
+-- local p = rawget(properties,n)
+-- if p then
+-- local i = p.injections or p.preinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(n,leftkern)
+-- if useadvance then
+-- addmargins(n,-leftkern)
+-- else
+-- pre = insert_node_before(pre,n,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if post then
+-- -- left|post glyphs|right
+-- for n in nextchar, post do
+-- local p = rawget(properties,n)
+-- if p then
+-- local i = p.injections or p.postinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(n,leftkern)
+-- if useadvance then
+-- addmargins(n,-leftkern)
+-- else
+-- post = insert_node_before(post,n,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if replace then
+-- -- left|replace glyphs|right
+-- for n in nextchar, replace do
+-- local p = rawget(properties,n)
+-- if p then
+-- local i = p.injections or p.replaceinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(n,leftkern)
+-- if useadvance then
+-- addmargins(n,-leftkern)
+-- else
+-- replace = insert_node_before(replace,n,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if done then
+-- setdisc(current,pre,post,replace)
+-- end
+-- -- prevglyph = nil
+-- prevdisc = current
+-- else
+-- -- prevglyph = nil
+-- prevdisc = nil
+-- end
+-- prev = current
+-- current = next
+-- end
+-- --
+-- if keepregisteredcounts then
+-- keepregisteredcounts = false
+-- else
+-- nofregisteredkerns = 0
+-- end
+-- if trace_injections then
+-- show_result(head)
+-- end
+-- return head
+-- end
+
+local function inject_kerns_only_kerns(head,where)
+ if trace_injections then
+ trace(head,"kerns")
+ end
+ local current = head
+ local prev -- = nil
+ local prevdisc -- = nil
+ local pre -- = nil -- saves a lookup
+ local post -- = nil -- saves a lookup
+ local replace -- = nil -- saves a lookup
+ local pretail -- = nil -- saves a lookup
+ local posttail -- = nil -- saves a lookup
+ local replacetail -- = nil -- saves a lookup
+ while current do
+ local next, char, id = isnextchar(current)
+ if char then
+ local p = rawget(properties,current)
+ if p then
+ local i = p.injections
+ if i then
+ -- left|glyph|right
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ leftkern = xscaled(current,leftkern)
+ if prev and getid(prev) == glue_code then
+ if useitalickerns then
+ head = insert_node_before(head,current,italickern(leftkern))
+ else
+ setwidth(prev,getwidth(prev) + leftkern)
+ end
+ else
+ head = insert_node_before(head,current,fontkern(leftkern))
+ end
+ end
+ end
+ if prevdisc then
+ local done = false
+ if post then
+ local i = p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ setlink(posttail,fontkern(xscaled(current,leftkern)))
+ done = true
+ end
+ end
+ end
+ if replace then
+ local i = p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ setlink(replacetail,fontkern(xscaled(current,leftkern)))
+ done = true
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ -- glyph|disc|glyph (special case)
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ replace = fontkern(xscaled(current,leftkern))
+ done = true
+ end
+ end
+ end
+ if done then
+ setdisc(prevdisc,pre,post,replace)
+ end
+ end
+ end
+ prevdisc = nil
+ -- prevglyph = current
+ elseif char == false then
+ -- other font
+ prevdisc = nil
+ -- prevglyph = current
+ elseif id == disc_code then
+ pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+ local done = false
+ if pre then
+ -- left|pre glyphs|right
+ for n in nextchar, pre do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.preinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ pre = insert_node_before(pre,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if post then
+ -- left|post glyphs|right
+ for n in nextchar, post do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ post = insert_node_before(post,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if replace then
+ -- left|replace glyphs|right
+ for n in nextchar, replace do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ replace = insert_node_before(replace,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if done then
+ setdisc(current,pre,post,replace)
+ end
+ -- prevglyph = nil
+ prevdisc = current
+ else
+ -- prevglyph = nil
+ prevdisc = nil
+ end
+ prev = current
+ current = next
+ end
+ --
+ if keepregisteredcounts then
+ keepregisteredcounts = false
+ else
+ nofregisteredkerns = 0
+ end
+ if trace_injections then
+ show_result(head)
+ end
+ return head
+end
+
+local function inject_kerns_only_margins(head,where)
+ if trace_injections then
+ trace(head,"kerns")
+ end
+ local current = head
+ local prevdisc -- = nil
+ local pre -- = nil -- saves a lookup
+ local post -- = nil -- saves a lookup
+ local replace -- = nil -- saves a lookup
+ local pretail -- = nil -- saves a lookup
+ local posttail -- = nil -- saves a lookup
+ local replacetail -- = nil -- saves a lookup
+ while current do
+ local next, char, id = isnextchar(current)
+ if char then
+ local p = rawget(properties,current)
+ if p then
+ if prevdisc then
+ if post then
+ local i = p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(posttail,false,-leftkern)
+ end
+ end
+ end
+ if replace then
+ local i = p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(replacetail,false,-leftkern)
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ if usezwjkerns then
+ replace = copynode(current)
+ setchar(replace,0x200D) -- zwj
+ addxymargins(replace,-leftkern)
+ else
+ replace = fontkern(xscaled(current,leftkern))
+ end
+ setdisc(prevdisc,pre,post,replace) -- setreplace
+ end
+ end
+ end
+ end
+ -- moved down so we can copy zwj
+ local i = p.injections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(current,-leftkern)
+ end
+ end
+ end
+ prevdisc = nil
+ elseif char == false then
+ prevdisc = nil
+ elseif id == disc_code then
+ pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+ if pre then
+ for n in nextchar, pre do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.preinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(n,-leftkern)
+ end
+ end
+ end
+ end
+ end
+ if post then
+ for n in nextchar, post do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(n,-leftkern)
+ end
+ end
+ end
+ end
+ end
+ if replace then
+ for n in nextchar, replace do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(n,-leftkern)
+ end
+ end
+ end
+ end
+ end
+ prevdisc = current
+ else
+ prevdisc = nil
+ end
+ current = next
+ end
+ if keepregisteredcounts then
+ keepregisteredcounts = false
+ else
+ nofregisteredkerns = 0
+ end
+ if trace_injections then
+ show_result(head)
+ end
+ return head
+end
+
+-- local function inject_positions_only(head,where)
+-- if trace_injections then
+-- trace(head,"positions")
+-- end
+-- local current = head
+-- local prev = nil
+-- local next = nil
+-- local prevdisc = nil
+-- local prevglyph = nil
+-- local pre = nil -- saves a lookup
+-- local post = nil -- saves a lookup
+-- local replace = nil -- saves a lookup
+-- local pretail = nil -- saves a lookup
+-- local posttail = nil -- saves a lookup
+-- local replacetail = nil -- saves a lookup
+-- while current do
+-- local next, char, id = isnextchar(current)
+-- if char then
+-- local p = rawget(properties,current)
+-- if p then
+-- local i = p.injections
+-- if i then
+-- -- left|glyph|right
+-- local yoffset = i.yoffset
+-- if yoffset and yoffset ~= 0 then
+-- -- use raise when advance
+-- addyoffset(current,yscaled(current,yoffset))
+-- end
+-- local leftkern = i.leftkern or 0
+-- local rightkern = i.rightkern or 0
+-- if leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- end
+-- if rightkern ~= 0 then
+-- rightkern = xscaled(current,rightkern)
+-- end
+-- if useadvance then
+-- if leftkern ~= 0 or rightkern ~= 0 then
+-- addmargins(current,-leftkern,-rightkern)
+-- end
+-- else
+-- if leftkern ~= 0 then
+-- if leftkern == -rightkern then
+-- addxoffset(current,leftkern)
+-- rightkern = 0
+-- elseif prev and getid(prev) == glue_code then
+-- if useitalickerns then
+-- head = insert_node_before(head,current,italickern(leftkern))
+-- else
+-- setwidth(prev,getwidth(prev)+leftkern)
+-- end
+-- else
+-- head = insert_node_before(head,current,fontkern(leftkern))
+-- end
+-- end
+-- if rightkern ~= 0 then
+-- if next and getid(next) == glue_code then
+-- if useitalickerns then
+-- insert_node_after(head,current,italickern(rightkern))
+-- else
+-- setwidth(next, getwidth(next)+rightkern)
+-- end
+-- else
+-- insert_node_after(head,current,fontkern(rightkern))
+-- end
+-- end
+-- end
+-- else
+-- local i = p.emptyinjections
+-- if i then
+-- -- glyph|disc|glyph (special case)
+-- local rightkern = i.rightkern
+-- if rightkern and rightkern ~= 0 then
+-- if next and getid(next) == disc_code then
+-- if replace then
+-- -- error, we expect an empty one
+-- else
+-- rightkern = xscaled(current,rightkern)
+-- replace = fontkern(rightkern) -- maybe also leftkern
+-- done = true --KE
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if prevdisc then
+-- local done = false
+-- if post then
+-- local i = p.postinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- if useadvance then
+-- addmargins(posttail,-leftkern)
+-- else
+-- setlink(posttail,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- if replace then
+-- local i = p.replaceinjections
+-- if i then
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- leftkern = xscaled(current,leftkern)
+-- if useadvance then
+-- addmargins(replacetail,-leftkern)
+-- else
+-- setlink(replacetail,fontkern(leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- else
+-- local i = p.emptyinjections
+-- if i then
+-- -- new .. okay?
+-- local leftkern = i.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- replace = fontkern(xscaled(current,leftkern))
+-- done = true
+-- end
+-- end
+-- end
+-- if done then
+-- setdisc(prevdisc,pre,post,replace)
+-- end
+-- end
+-- end
+-- prevdisc = nil
+-- prevglyph = current
+-- elseif char == false then
+-- prevdisc = nil
+-- prevglyph = current
+-- elseif id == disc_code then
+-- pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+-- local done = false
+-- if pre then
+-- -- left|pre glyphs|right
+-- for n in nextchar, pre do
+-- local p = rawget(properties,n)
+-- if p then
+-- local i = p.injections or p.preinjections
+-- if i then
+-- local yoffset = i.yoffset
+-- local leftkern = i.leftkern or 0
+-- local rightkern = i.rightkern or 0
+-- if yoffset and yoffset ~= 0 then
+-- -- use raise when advance
+-- addyoffset(n,yscaled(n,yoffset))
+-- end
+-- if leftkern ~= 0 then
+-- leftkern = xscaled(n,leftkern)
+-- end
+-- if rightkern ~= 0 then
+-- rightkern = xscaled(n,rightkern)
+-- end
+-- if useadvance then
+-- if leftkern ~= 0 or rightkern ~= 0 then
+-- addmargins(pre,-leftkern,-rightkern)
+-- end
+-- else
+-- if leftkern ~= 0 then
+-- pre = insert_node_before(pre,n,fontkern(leftkern))
+-- done = true
+-- end
+-- if rightkern ~= 0 then
+-- insert_node_after(pre,n,fontkern(rightkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if post then
+-- -- left|post glyphs|right
+-- for n in nextchar, post do
+-- local p = rawget(properties,n)
+-- if p then
+-- local i = p.injections or p.postinjections
+-- if i then
+-- local yoffset = i.yoffset
+-- local leftkern = i.leftkern or 0
+-- local rightkern = i.rightkern or 0
+-- if yoffset and yoffset ~= 0 then
+-- -- use raise when advance
+-- addyoffset(n,yscaled(n,yoffset))
+-- end
+-- if leftkern ~= 0 then
+-- leftkern = xscaled(n,leftkern)
+-- end
+-- if rightkern ~= 0 then
+-- rightkern = xscaled(n,rightkern)
+-- end
+-- if useadvance then
+-- if leftkern ~= 0 or rightkern ~= 0 then
+-- addmargins(post,-leftkern,-rightkern)
+-- end
+-- else
+-- if leftkern ~= 0 then
+-- post = insert_node_before(post,n,fontkern(leftkern))
+-- done = true
+-- end
+-- if rightkern ~= 0 then
+-- insert_node_after(post,n,fontkern(rightkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if replace then
+-- -- left|replace glyphs|right
+-- for n in nextchar, replace do
+-- local p = rawget(properties,n)
+-- if p then
+-- local i = p.injections or p.replaceinjections
+-- if i then
+-- local yoffset = i.yoffset
+-- local leftkern = i.leftkern or 0
+-- local rightkern = i.rightkern or 0
+-- if yoffset and yoffset ~= 0 then
+-- -- use raise when advance
+-- addyoffset(n,yscaled(n,yoffset))
+-- end
+-- if leftkern ~= 0 then
+-- leftkern = xscaled(n,leftkern)
+-- end
+-- if rightkern ~= 0 then
+-- rightkern = xscaled(n,rightkern)
+-- end
+-- if useadvance then
+-- if leftkern ~= 0 or rightkern ~= 0 then
+-- addmargins(replace,-leftkern,-rightkern)
+-- end
+-- else
+-- if leftkern ~= 0 then
+-- replace = insert_node_before(replace,n,fontkern(leftkern))
+-- done = true
+-- end
+-- if rightkern ~= 0 then
+-- insert_node_after(replace,n,fontkern(rightkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if prevglyph then
+-- if pre then
+-- local p = rawget(properties,prevglyph)
+-- if p then
+-- local i = p.preinjections
+-- if i then
+-- -- glyph|pre glyphs
+-- local rightkern = i.rightkern
+-- if rightkern and rightkern ~= 0 then
+-- rightkern = xscaled(prevglyph,rightkern)
+-- if useadvance then
+-- addmargins(pre,-rightkern)
+-- else
+-- pre = insert_node_before(pre,pre,fontkern(rightkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if replace then
+-- local p = rawget(properties,prevglyph)
+-- if p then
+-- local i = p.replaceinjections
+-- if i then
+-- -- glyph|replace glyphs
+-- local rightkern = i.rightkern
+-- if rightkern and rightkern ~= 0 then
+-- rightkern = xscaled(prevglyph,rightkern)
+-- if useadvance then
+-- addmargins(replace,-rightkern)
+-- else
+-- replace = insert_node_before(replace,replace,fontkern(rightkern))
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if done then
+-- setdisc(current,pre,post,replace)
+-- end
+-- prevglyph = nil
+-- prevdisc = current
+-- else
+-- prevglyph = nil
+-- prevdisc = nil
+-- end
+-- prev = current
+-- current = next
+-- end
+-- --
+-- if keepregisteredcounts then
+-- keepregisteredcounts = false
+-- else
+-- nofregisteredpositions = 0
+-- end
+-- if trace_injections then
+-- show_result(head)
+-- end
+-- return head
+-- end
+
+local function inject_positions_only_kerns(head,where)
+ if trace_injections then
+ trace(head,"positions")
+ end
+ local current = head
+ local prev = nil
+ local next = nil
+ local prevdisc = nil
+ local prevglyph = nil
+ local pre = nil -- saves a lookup
+ local post = nil -- saves a lookup
+ local replace = nil -- saves a lookup
+ local pretail = nil -- saves a lookup
+ local posttail = nil -- saves a lookup
+ local replacetail = nil -- saves a lookup
+ while current do
+ local next, char, id = isnextchar(current)
+ if char then
+ local p = rawget(properties,current)
+ if p then
+ local i = p.injections
+ if i then
+ -- left|glyph|right
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ -- use raise when advance
+ addyoffset(current,yscaled(current,yoffset))
+ end
+ local leftkern = i.leftkern or 0
+ local rightkern = i.rightkern or 0
+ if leftkern ~= 0 then
+ leftkern = xscaled(current,leftkern)
+ end
+ if rightkern ~= 0 then
+ rightkern = xscaled(current,rightkern)
+ end
+ if leftkern ~= 0 then
+ if leftkern == -rightkern then
+ addxoffset(current,leftkern)
+ rightkern = 0
+ elseif prev and getid(prev) == glue_code then
+ if useitalickerns then
+ head = insert_node_before(head,current,italickern(leftkern))
+ else
+ setwidth(prev,getwidth(prev)+leftkern)
+ end
+ else
+ head = insert_node_before(head,current,fontkern(leftkern))
+ end
+ end
+ if rightkern ~= 0 then
+ if next and getid(next) == glue_code then
+ if useitalickerns then
+ insert_node_after(head,current,italickern(rightkern))
+ else
+ setwidth(next, getwidth(next)+rightkern)
+ end
+ else
+ insert_node_after(head,current,fontkern(rightkern))
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ -- glyph|disc|glyph (special case)
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ if next and getid(next) == disc_code then
+ if replace then
+ -- error, we expect an empty one
+ else
+ replace = fontkern(xscaled(current,rightkern)) -- maybe also leftkern
+ done = true --KE
+ end
+ end
+ end
+ end
+ end
+ if prevdisc then
+ local done = false
+ if post then
+ local i = p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ setlink(posttail,fontkern(xscaled(current,leftkern)))
+ done = true
+ end
+ end
+ end
+ if replace then
+ local i = p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ setlink(replacetail,fontkern(xscaled(current,leftkern)))
+ done = true
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ -- new .. okay?
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ replace = fontkern(xscaled(current,leftkern))
+ done = true
+ end
+ end
+ end
+ if done then
+ setdisc(prevdisc,pre,post,replace)
+ end
+ end
+ end
+ prevdisc = nil
+ prevglyph = current
+ elseif char == false then
+ prevdisc = nil
+ prevglyph = current
+ elseif id == disc_code then
+ pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+ local done = false
+ if pre then
+ -- left|pre glyphs|right
+ for n in nextchar, pre do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.preinjections
+ if i then
+ local yoffset = i.yoffset
+ local leftkern = i.leftkern
+ local rightkern = i.rightkern
+ if yoffset and yoffset ~= 0 then
+ addyoffset(n,yscaled(n,yoffset))
+ end
+ if leftkern and leftkern ~= 0 then
+ pre = insert_node_before(pre,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(pre,n,fontkern(xscaled(n,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if post then
+ -- left|post glyphs|right
+ for n in nextchar, post do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.postinjections
+ if i then
+ local yoffset = i.yoffset
+ local leftkern = i.leftkern
+ local rightkern = i.rightkern
+ if yoffset and yoffset ~= 0 then
+ addyoffset(n,yscaled(n,yoffset))
+ end
+ if leftkern and leftkern ~= 0 then
+ post = insert_node_before(post,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(post,n,fontkern(xscaled(n,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if replace then
+ -- left|replace glyphs|right
+ for n in nextchar, replace do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.replaceinjections
+ if i then
+ local yoffset = i.yoffset
+ local leftkern = i.leftkern
+ local rightkern = i.rightkern
+ if yoffset and yoffset ~= 0 then
+ addyoffset(n,yscaled(n,yoffset))
+ end
+ if leftkern and leftkern ~= 0 then
+ replace = insert_node_before(replace,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(replace,n,fontkern(xscaled(n,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if prevglyph then
+ -- there can only be useful properties when pre/replace start with a glyph
+ if pre then
+ local p = rawget(properties,prevglyph)
+ if p then
+ local i = p.preinjections
+ if i then
+ -- glyph|pre glyphs
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ pre = insert_node_before(pre,pre,fontkern(xscaled(prevglyph,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ if replace then
+ local p = rawget(properties,prevglyph)
+ if p then
+ local i = p.replaceinjections
+ if i then
+ -- glyph|replace glyphs
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ replace = insert_node_before(replace,replace,fontkern(xscaled(prevglyph,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if done then
+ setdisc(current,pre,post,replace)
+ end
+ prevglyph = nil
+ prevdisc = current
+ else
+ prevglyph = nil
+ prevdisc = nil
+ end
+ prev = current
+ current = next
+ end
+ --
+ if keepregisteredcounts then
+ keepregisteredcounts = false
+ else
+ nofregisteredpositions = 0
+ end
+ if trace_injections then
+ show_result(head)
+ end
+ return head
+end
+
+local function inject_positions_only_margins(head,where)
+ if trace_injections then
+ trace(head,"positions")
+ end
+ local current = head
+ local prev = nil
+ local next = nil
+ local prevdisc = nil
+ local prevglyph = nil
+ local pre = nil -- saves a lookup
+ local post = nil -- saves a lookup
+ local replace = nil -- saves a lookup
+ local pretail = nil -- saves a lookup
+ local posttail = nil -- saves a lookup
+ local replacetail = nil -- saves a lookup
+ while current do
+ local next, char, id = isnextchar(current)
+ if char then
+ local p = rawget(properties,current)
+ if p then
+ local i = p.injections
+ if i then
+ -- left|glyph|right
+ local yoffset = i.yoffset or 0
+ local leftkern = i.leftkern or 0
+ local rightkern = i.rightkern or 0
+ if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
+ addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ -- glyph|disc|glyph (special case)
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ if next and getid(next) == disc_code then
+ if replace then
+ -- error, we expect an empty one
+ else
+ if usezwjkerns then
+ replace = copynode(current)
+ setchar(replace,0x200D) -- zwj
+ addxymargins(replace,false,-rightkern)
+ else
+ replace = fontkern(xscaled(current,rightkern)) -- maybe also leftkern
+ end
+ done = true --KE
+ end
+ end
+ end
+ end
+ end
+ if prevdisc then
+ if post then
+ local i = p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(posttail,-leftkern)
+ end
+ end
+ end
+ if replace then
+ local i = p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ addxymargins(replacetail,-leftkern)
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ -- new .. okay?
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ if usezwjkerns then
+ replace = copynode(current)
+ setchar(replace,0x200D) -- zwj
+ addxymargins(replace,-leftkern)
+ else
+ replace = fontkern(xscaled(current,leftkern))
+ end
+ setdisc(prevdisc,pre,post,replace)
+ end
+ end
+ end
+ end
+ end
+ prevdisc = nil
+ prevglyph = current
+ elseif char == false then
+ prevdisc = nil
+ prevglyph = current
+ elseif id == disc_code then
+ pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+ if pre then
+ -- left|pre glyphs|right
+ for n in nextchar, pre do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.preinjections
+ if i then
+ local yoffset = i.yoffset or 0
+ local leftkern = i.leftkern or 0
+ local rightkern = i.rightkern or 0
+ if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
+ addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
+ end
+ end
+ end
+ end
+ end
+ if post then
+ -- left|post glyphs|right
+ for n in nextchar, post do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.postinjections
+ if i then
+ local yoffset = i.yoffset or 0
+ local leftkern = i.leftkern or 0
+ local rightkern = i.rightkern or 0
+ if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
+ addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
+ end
+ end
+ end
+ end
+ end
+ if replace then
+ -- left|replace glyphs|right
+ for n in nextchar, replace do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.replaceinjections
+ if i then
+ local yoffset = i.yoffset or 0
+ local leftkern = i.leftkern or 0
+ local rightkern = i.rightkern or 0
+ if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
+ addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
+ end
+ end
+ end
+ end
+ end
+ if prevglyph then
+ if pre then
+ local p = rawget(properties,prevglyph)
+ if p then
+ local i = p.preinjections
+ if i then
+ -- glyph|pre glyphs
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ addxymargins(pre,-rightkern,0,0)
+ end
+ end
+ end
+ end
+ if replace then
+ local p = rawget(properties,prevglyph)
+ if p then
+ local i = p.replaceinjections
+ if i then
+ -- glyph|replace glyphs
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ addxymargins(replace,-rightkern,0,0)
+ end
+ end
+ end
+ end
+ end
+ prevglyph = nil
+ prevdisc = current
+ else
+ prevglyph = nil
+ prevdisc = nil
+ end
+ prev = current
+ current = next
+ end
+ --
+ if keepregisteredcounts then
+ keepregisteredcounts = false
+ else
+ nofregisteredpositions = 0
+ end
+ if trace_injections then
+ show_result(head)
+ end
+ return head
+end
+
+local function showoffset(n,flag)
+ local x, y = getoffsets(n)
+ if x ~= 0 or y ~= 0 then
+ setcolor(n,"darkgray")
+ end
+end
+
+local function processmark(p,n,pn) -- p = basenode
+ local px, py = getoffsets(p)
+ local nx, ny = getoffsets(n)
+ local sx, sy = getxyscales(n) -- we assume n and p to have equal scales
+ local ox = 0
+ local rightkern = nil
+ local pp = rawget(properties,p)
+ if pp then
+ pp = pp.injections
+ if pp then
+ rightkern = pp.rightkern
+ end
+ end
+ local markdir = pn.markdir
+ if rightkern then -- x and w ~= 0
+ ox = px - sx * (pn.markx or 0) - rightkern
+ if markdir and markdir < 0 then
+ -- kern(w-x) glyph(p) kern(x) mark(n)
+ if not pn.markmark then
+ ox = ox + sx * (pn.leftkern or 0)
+ end
+ else
+ -- kern(x) glyph(p) kern(w-x) mark(n)
+ --
+ -- According to Kai we don't need to handle leftkern here but I'm
+ -- pretty sure I've run into a case where it was needed so maybe
+ -- some day we need something more clever here.
+ --
+ -- maybe we need to apply both then
+ --
+ if false then
+ -- a mark with kerning (maybe husayni needs it )
+ local leftkern = pp.leftkern
+ if leftkern then
+ ox = ox - sx * leftkern
+ end
+ end
+ end
+ else
+ ox = px - sx * (pn.markx or 0)
+ if markdir and markdir < 0 then
+ if not pn.markmark then
+ local leftkern = pn.leftkern
+ if leftkern then
+ ox = ox + sx * leftkern -- husayni needs it
+ end
+ end
+ end
+ if pn.checkmark then
+ local wn = getwidth(n) -- in arial marks have widths
+ if wn and wn ~= 0 then
+ wn = wn/2
+ if trace_injections then
+ report_injections("correcting non zero width mark %C",getchar(n))
+ end
+ -- -- bad: we should center
+ --
+ -- pn.leftkern = -wn
+ -- pn.rightkern = -wn
+ --
+ -- -- we're too late anyway as kerns are already injected so we do it the
+ -- -- ugly way (no checking if the previous is already a kern) .. maybe we
+ -- -- should fix the font instead
+ --
+ -- todo: head and check for prev / next kern
+ --
+ insert_node_before(n,n,fontkern(-wn))
+ insert_node_after(n,n,fontkern(-wn))
+ end
+ end
+ end
+ local oy = ny + py + sy * (pn.marky or 0)
+ if not pn.markmark then
+ local yoffset = pn.yoffset
+ if yoffset then
+ oy = oy + sy * yoffset -- husayni needs it
+ end
+ end
+ setoffsets(n,ox,oy)
+ if trace_marks then
+ showoffset(n,true)
+ end
+end
+
+local function inject_everything(head,where)
+ if trace_injections then
+ trace(head,"everything")
+ end
+ local hascursives = nofregisteredcursives > 0
+ local hasmarks = nofregisteredmarks > 0
+ --
+ local current = head
+ local last = nil
+ local prev = nil
+ local next = nil
+ local prevdisc = nil
+ local prevglyph = nil
+ local pre = nil -- saves a lookup
+ local post = nil -- saves a lookup
+ local replace = nil -- saves a lookup
+ local pretail = nil -- saves a lookup
+ local posttail = nil -- saves a lookup
+ local replacetail = nil -- saves a lookup
+ --
+ local cursiveanchor = nil
+ local minc = 0
+ local maxc = 0
+ local glyphs = { }
+ local marks = { }
+ local nofmarks = 0
+ --
+ while current do
+ local next, char, id = isnextchar(current)
+ if char then
+ local p = rawget(properties,current)
+ if p then
+ local i = p.injections
+ if i then
+ local pm = i.markbasenode
+ if pm then
+ nofmarks = nofmarks + 1
+ marks[nofmarks] = current
+ else
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ addyoffset(current,yscaled(current,yoffset)) -- or just * sy
+ end
+ if hascursives then
+ local cursivex = i.cursivex
+ if cursivex then
+ if cursiveanchor then
+ if cursivex ~= 0 then
+ i.leftkern = (i.leftkern or 0) + cursivex
+ end
+ if maxc == 0 then
+ minc = 1
+ maxc = 1
+ glyphs[1] = cursiveanchor
+ else
+ maxc = maxc + 1
+ glyphs[maxc] = cursiveanchor
+ end
+ properties[cursiveanchor].cursivedy = i.cursivey -- cursiveprops
+ last = current
+ else
+ maxc = 0
+ end
+ elseif maxc > 0 then
+ local nx, ny = getoffsets(current)
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + yscaled(current,properties[ti].cursivedy) -- stepwise increment
+ setoffsets(ti,false,ny)
+ if trace_cursive then
+ showoffset(ti)
+ end
+ end
+ maxc = 0
+ cursiveanchor = nil
+ end
+ if i.cursiveanchor then
+ cursiveanchor = current -- no need for both now
+ else
+ if maxc > 0 then
+ local nx, ny = getoffsets(current)
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + yscaled(current,properties[ti].cursivedy) -- stepwise increment
+ setoffsets(ti,false,ny)
+ if trace_cursive then
+ showoffset(ti)
+ end
+ end
+ maxc = 0
+ end
+ cursiveanchor = nil
+ end
+ end
+ -- left|glyph|right
+ local leftkern = i.leftkern
+ local rightkern = i.rightkern
+ if leftkern and leftkern ~= 0 then
+ local opposite = rightkern and leftkern == -rightkern
+ leftkern = xscaled(current,leftkern)
+ if opposite then
+ addxoffset(current,leftkern)
+ rightkern = 0
+ elseif prev and getid(prev) == glue_code then
+ if useitalickerns then
+ head = insert_node_before(head,current,italickern(leftkern))
+ else
+ setwidth(prev, getwidth(prev) + leftkern)
+ end
+ else
+ head = insert_node_before(head,current,fontkern(leftkern))
+ end
+ end
+ if rightkern and rightkern ~= 0 then
+ rightkern = xscaled(current,rightkern)
+ if next and getid(next) == glue_code then
+ if useitalickerns then
+ insert_node_after(head,current,italickern(rightkern))
+ else
+ setwidth(next, getwidth(next) + rightkern)
+ end
+ else
+ insert_node_after(head,current,fontkern(rightkern))
+ end
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ -- glyph|disc|glyph (special case)
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ if next and getid(next) == disc_code then
+ if replace then
+ -- error, we expect an empty one
+ else
+ replace = fontkern(xscaled(current,rightkern))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if prevdisc then
+ if p then
+ local done = false
+ if post then
+ local i = p.postinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ setlink(posttail,fontkern(xscaled(current,leftkern)))
+ done = true
+ end
+ end
+ end
+ if replace then
+ local i = p.replaceinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ setlink(replacetail,fontkern(xscaled(current,leftkern)))
+ done = true
+ end
+ end
+ else
+ local i = p.emptyinjections
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ replace = fontkern(xscaled(current,leftkern))
+ done = true
+ end
+ end
+ end
+ if done then
+ setdisc(prevdisc,pre,post,replace)
+ end
+ end
+ end
+ else
+ -- cursive
+ if hascursives and maxc > 0 then
+ local nx, ny = getoffsets(current)
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + yscaled(current,properties[ti].cursivedy)
+ setoffsets(ti,false,ny) -- can be mark, we could use properties
+ end
+ maxc = 0
+ cursiveanchor = nil
+ end
+ end
+ prevdisc = nil
+ prevglyph = current
+ elseif char == false then
+ -- base = nil
+ prevdisc = nil
+ prevglyph = current
+ elseif id == disc_code then
+ -- base = nil
+ pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+ local done = false
+ if pre then
+ -- left|pre glyphs|right
+ for n in nextchar, pre do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.preinjections
+ if i then
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ addyoffset(n,yscaled(n,yoffset))
+ end
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ pre = insert_node_before(pre,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(pre,n,fontkern(xscaled(n,rightkern)))
+ done = true
+ end
+ if hasmarks then
+ local pm = i.markbasenode
+ if pm then
+ processmark(pm,n,i)
+ end
+ end
+ end
+ end
+ end
+ end
+ if post then
+ -- left|post glyphs|right
+ for n in nextchar, post do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.postinjections
+ if i then
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ addyoffset(n,yscaled(n,yoffset))
+ end
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ post = insert_node_before(post,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ done = true
+ insert_node_after(post,n,fontkern(xscaled(n,rightkern)))
+ end
+ if hasmarks then
+ local pm = i.markbasenode
+ if pm then
+ processmark(pm,n,i)
+ end
+ end
+ end
+ end
+ end
+ end
+ if replace then
+ -- left|replace glyphs|right
+ for n in nextchar, replace do
+ local p = rawget(properties,n)
+ if p then
+ local i = p.injections or p.replaceinjections
+ if i then
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ addyoffset(n,yscaled(n,yoffset))
+ end
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ replace = insert_node_before(replace,n,fontkern(xscaled(n,leftkern)))
+ done = true
+ end
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(replace,n,fontkern(xscaled(n,rightkern)))
+ done = true
+ end
+ if hasmarks then
+ local pm = i.markbasenode
+ if pm then
+ processmark(pm,n,i)
+ end
+ end
+ end
+ end
+ end
+ end
+ if prevglyph then
+ if pre then
+ local p = rawget(properties,prevglyph)
+ if p then
+ local i = p.preinjections
+ if i then
+ -- glyph|pre glyphs
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ pre = insert_node_before(pre,pre,fontkern(xscaled(prevglyph,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ if replace then
+ local p = rawget(properties,prevglyph)
+ if p then
+ local i = p.replaceinjections
+ if i then
+ -- glyph|replace glyphs
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ replace = insert_node_before(replace,replace,fontkern(xscaled(prevglyph,rightkern)))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ if done then
+ setdisc(current,pre,post,replace)
+ end
+ prevglyph = nil
+ prevdisc = current
+ else
+ -- base = nil
+ prevglyph = nil
+ prevdisc = nil
+ end
+ prev = current
+ current = next
+ end
+ -- cursive
+ if hascursives and maxc > 0 then
+ local nx, ny = getoffsets(last)
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + yscaled(properties[ti].cursivedy)
+ setoffsets(ti,false,ny)
+ if trace_cursive then
+ showoffset(ti)
+ end
+ end
+ end
+ --
+ if nofmarks > 0 then
+ for i=1,nofmarks do
+ local m = marks[i]
+ local p = rawget(properties,m)
+ local i = p.injections
+ local b = i.markbasenode
+ processmark(b,m,i)
+ end
+ elseif hasmarks then
+ -- sometyhing bad happened
+ end
+ --
+ if keepregisteredcounts then
+ keepregisteredcounts = false
+ else
+ nofregisteredkerns = 0
+ nofregisteredpositions = 0
+ nofregisteredmarks = 0
+ nofregisteredcursives = 0
+ end
+ if trace_injections then
+ show_result(head)
+ end
+ return head
+end
+
+-- space triggers
+
+local triggers = false
+
+function nodes.injections.setspacekerns(font,sequence)
+ if triggers then
+ triggers[font] = sequence
+ else
+ triggers = { [font] = sequence }
+ end
+end
+
+local threshold = 1 -- todo: add a few methods for context
+
+directives.register("otf.threshold", function(v) threshold = tonumber(v) or 1 end)
+
+local getthreshold = function(font)
+ local p = parameters[font]
+ local f = p.factor
+ local s = p.spacing
+ local t = threshold * (s and s.width or p.space or 0) - 2
+ return t > 0 and t or 0, f
+end
+
+injections.getthreshold = getthreshold
+
+function injections.isspace(n,threshold,id)
+ if (id or getid(n)) == glue_code then
+ local w = getwidth(n)
+ if threshold and w > threshold then -- was >=
+ return 32
+ end
+ end
+end
+
+-- We have a plugin so that Kai can use the next in plain. Such a plugin is rather application
+-- specific.
+--
+-- local getboth = nodes.direct.getboth
+-- local getid = nodes.direct.getid
+-- local getprev = nodes.direct.getprev
+-- local getnext = nodes.direct.getnext
+--
+-- local whatsit_code = nodes.nodecodes.whatsit
+-- local glyph_code = nodes.nodecodes.glyph
+--
+-- local function getspaceboth(n) -- fragile: what it prev/next has no width field
+-- local prev, next = getboth(n)
+-- while prev and (getid(prev) == whatsit_code or (getwidth(prev) == 0 and getid(prev) ~= glyph_code)) do
+-- prev = getprev(prev)
+-- end
+-- while next and (getid(next) == whatsit_code or (getwidth(next) == 0 and getid(next) ~= glyph_code)) do
+-- next = getnext(next)
+-- end
+-- end
+--
+-- injections.installgetspaceboth(getspaceboth)
+
+local getspaceboth = getboth
+
+function injections.installgetspaceboth(gb)
+ getspaceboth = gb or getboth
+end
+
+local function injectspaces(head)
+
+ if not triggers then
+ return head
+ end
+ local lastfont = nil
+ local spacekerns = nil
+ local leftkerns = nil
+ local rightkerns = nil
+ local factor = 0
+ local threshold = 0
+ local leftkern = false
+ local rightkern = false
+ local xscale = 1
+
+ local function updatefont(font,trig)
+ leftkerns = trig.left
+ rightkerns = trig.right
+ lastfont = font
+ threshold,
+ factor = getthreshold(font)
+ end
+
+ for n in nextglue, head do
+ local prev, next = getspaceboth(n)
+ local prevchar = prev and ischar(prev)
+ local nextchar = next and ischar(next)
+ if nextchar then
+ local font = getfont(next)
+ local trig = triggers[font]
+ if trig then
+ if lastfont ~= font then
+ updatefont(font,trig)
+ end
+ if rightkerns then
+ rightkern = rightkerns[nextchar]
+ end
+ xscale = getxscale(next)
+ end
+ end
+ if prevchar then
+ local font = getfont(prev)
+ local trig = triggers[font]
+ if trig then
+ if lastfont ~= font then
+ updatefont(font,trig)
+ end
+ if leftkerns then
+ leftkern = leftkerns[prevchar]
+ end
+ xscale = getxscale(prev)
+ end
+ end
+ if leftkern then
+ local old = getwidth(n)
+ if old > threshold then
+ if rightkern then
+ if useitalickerns then
+ local lnew = leftkern * factor
+ local rnew = rightkern * factor
+ if trace_spaces then
+ report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
+ end
+ lnew = lnew * xscale
+ rnew = rnew * xscale
+ head = insert_node_before(head,n,italickern(lnew))
+ insert_node_after(head,n,italickern(rnew))
+ else
+ local new = old + (leftkern + rightkern) * factor
+ if trace_spaces then
+ report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
+ end
+ setwidth(n,new)
+ end
+ rightkern = false
+ else
+ if useitalickerns then
+ local new = leftkern * factor
+ if trace_spaces then
+ report_spaces("%C [%p + %p]",prevchar,old,new)
+ end
+ new = new * xscale
+ insert_node_after(head,n,italickern(new)) -- tricky with traverse but ok
+ else
+ local new = old + leftkern * factor * xscale
+ if trace_spaces then
+ report_spaces("%C [%p -> %p]",prevchar,old,new)
+ end
+ setwidth(n,new)
+ end
+ end
+ end
+ leftkern = false
+ elseif rightkern then
+ local old = getwidth(n)
+ if old > threshold then
+ local new = rightkern * factor * xscale
+ if useitalickerns then
+ if trace_spaces then
+ report_spaces("[%p + %p] %C",old,new,nextchar)
+ end
+ insert_node_after(head,n,italickern(new))
+ else
+ new = old + new
+ if trace_spaces then
+ report_spaces("[%p -> %p] %C",old,new,nextchar)
+ end
+ setwidth(n,new)
+ end
+ else
+ -- message
+ end
+ rightkern = false
+ end
+ end
+
+ triggers = false
+
+ return head
+end
+
+-- When advance is found to be okay there will be split functions (which is abit
+-- more efficient) but first we need to have scales done (we can also have addoffset
+-- and alike then)
+
+function injections.handler(head,where)
+ if triggers then
+ head = injectspaces(head)
+ end
+ if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
+ if trace_injections then
+ report_injections("injection variant %a (%s)","everything","kerns")
+ end
+ -- I will do useadvance here when I am playing with fonts that follow this
+ -- injection routem, which is seldom.
+ return inject_everything(head,where)
+ elseif nofregisteredpositions > 0 then
+ if trace_injections then
+ report_injections("injection variant %a (%s)","positions",useadvance and "margins" or "kerns")
+ end
+ if useadvance then
+ return inject_positions_only_margins(head,where)
+ else
+ return inject_positions_only_kerns(head,where)
+ end
+ elseif nofregisteredkerns > 0 then
+ if trace_injections then
+ report_injections("injection variant %a (%s)","kerns",useadvance and "margins" or "kerns")
+ end
+ if useadvance then
+ return inject_kerns_only_margins(head,where)
+-- return inject_positions_only_margins(head,where)
+ else
+ return inject_kerns_only_kerns(head,where)
+ end
+ else
+ return head
+ end
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-ots.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-ots.lmt
new file mode 100644
index 00000000000..37f76b258a7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-ots.lmt
@@ -0,0 +1,4475 @@
+if not modules then modules = { } end modules ['font-ots'] = { -- sequences
+ version = 1.001,
+ optimize = true,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+--[[ldx--
+<p>This module is a bit more split up that I'd like but since we also want to test
+with plain <l n='tex'/> it has to be so. This module is part of <l n='context'/>
+and discussion about improvements and functionality mostly happens on the
+<l n='context'/> mailing list.</p>
+
+<p>The specification of OpenType is (or at least decades ago was) kind of vague.
+Apart from a lack of a proper free specifications there's also the problem that
+Microsoft and Adobe may have their own interpretation of how and in what order to
+apply features. In general the Microsoft website has more detailed specifications
+and is a better reference. There is also some information in the FontForge help
+files. In the end we rely most on the Microsoft specification.</p>
+
+<p>Because there is so much possible, fonts might contain bugs and/or be made to
+work with certain rederers. These may evolve over time which may have the side
+effect that suddenly fonts behave differently. We don't want to catch all font
+issues.</p>
+
+<p>After a lot of experiments (mostly by Taco, me and Idris) the first implementation
+was already quite useful. When it did most of what we wanted, a more optimized version
+evolved. Of course all errors are mine and of course the code can be improved. There
+are quite some optimizations going on here and processing speed is currently quite
+acceptable and has been improved over time. Many complex scripts are not yet supported
+yet, but I will look into them as soon as <l n='context'/> users ask for it.</p>
+
+<p>The specification leaves room for interpretation. In case of doubt the Microsoft
+implementation is the reference as it is the most complete one. As they deal with
+lots of scripts and fonts, Kai and Ivo did a lot of testing of the generic code and
+their suggestions help improve the code. I'm aware that not all border cases can be
+taken care of, unless we accept excessive runtime, and even then the interference
+with other mechanisms (like hyphenation) are not trivial.</p>
+
+<p>Especially discretionary handling has been improved much by Kai Eigner who uses complex
+(latin) fonts. The current implementation is a compromis between his patches and my code
+and in the meantime performance is quite ok. We cannot check all border cases without
+compromising speed but so far we're okay. Given good test cases we can probably improve
+it here and there. Especially chain lookups are non trivial with discretionaries but
+things got much better over time thanks to Kai.</p>
+
+<p>Glyphs are indexed not by unicode but in their own way. This is because there is no
+relationship with unicode at all, apart from the fact that a font might cover certain
+ranges of characters. One character can have multiple shapes. However, at the
+<l n='tex'/> end we use unicode so and all extra glyphs are mapped into a private
+space. This is needed because we need to access them and <l n='tex'/> has to include
+then in the output eventually.</p>
+
+<p>The initial data table is rather close to the open type specification and also not
+that different from the one produced by <l n='fontforge'/> but we uses hashes instead.
+In <l n='context'/> that table is packed (similar tables are shared) and cached on disk
+so that successive runs can use the optimized table (after loading the table is
+unpacked).</p>
+
+<p>This module is sparsely documented because it is has been a moving target. The
+table format of the reader changed a bit over time and we experiment a lot with
+different methods for supporting features. By now the structures are quite stable</p>
+
+<p>Incrementing the version number will force a re-cache. We jump the number by one
+when there's a fix in the reader or processing code that can result in different
+results.</p>
+
+<p>This code is also used outside context but in context it has to work with other
+mechanisms. Both put some constraints on the code here.</p>
+
+--ldx]]--
+
+-- Remark: We assume that cursives don't cross discretionaries which is okay because it
+-- is only used in semitic scripts.
+--
+-- Remark: We assume that marks precede base characters.
+--
+-- Remark: When complex ligatures extend into discs nodes we can get side effects. Normally
+-- this doesn't happen; ff\d{l}{l}{l} in lm works but ff\d{f}{f}{f}.
+--
+-- Todo: check if we copy attributes to disc nodes if needed.
+--
+-- Todo: it would be nice if we could get rid of components. In other places we can use
+-- the unicode properties. We can just keep a lua table.
+--
+-- Remark: We do some disc juggling where we need to keep in mind that the pre, post and
+-- replace fields can have prev pointers to a nesting node ... I wonder if that is still
+-- needed.
+--
+-- Remark: This is not possible:
+--
+-- \discretionary {alpha-} {betagammadelta}
+-- {\discretionary {alphabeta-} {gammadelta}
+-- {\discretionary {alphabetagamma-} {delta}
+-- {alphabetagammadelta}}}
+--
+-- Remark: Something is messed up: we have two mark / ligature indices, one at the
+-- injection end and one here ... this is based on KE's patches but there is something
+-- fishy there as I'm pretty sure that for husayni we need some connection (as it's much
+-- more complex than an average font) but I need proper examples of all cases, not of
+-- only some.
+--
+-- Remark: I wonder if indexed would be faster than unicoded. It would be a major
+-- rewrite to have char being unicode + an index field in glyph nodes. Also more
+-- assignments have to be made in order to keep things in sync. So, it's a no-go.
+--
+-- Remark: We can provide a fast loop when there are no disc nodes (tests show a 1%
+-- gain). Smaller functions might perform better cache-wise. But ... memory becomes
+-- faster anyway, so ...
+--
+-- Remark: Some optimizations made sense for 5.2 but seem less important for 5.3 but
+-- anyway served their purpose.
+--
+-- Todo: just (0=l2r and 1=r2l) or maybe (r2l = true)
+
+-- Experiments with returning the data with the ischar are positive for lmtx but
+-- have a performance hit on mkiv because there we need to wrap ischardata (pending
+-- extensions to luatex which is unlikely to happen for such an experiment because
+-- we then can't remove it). Actually it might make generic slightly faster. Also,
+-- there are some corner cases where a data check comes before a char fetch and
+-- we're talking of millions of calls there. At some point I might make a version
+-- for lmtx that does it slightly different anyway.
+
+local type, next, tonumber = type, next, tonumber
+local random = math.random
+local formatters = string.formatters
+local insert = table.insert
+
+local registertracker = trackers.register
+
+local logs = logs
+local trackers = trackers
+local nodes = nodes
+local fonts = fonts
+
+local otf = fonts.handlers.otf
+local tracers = nodes.tracers
+
+local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end)
+local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end)
+local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end)
+local trace_ligatures = false registertracker("otf.ligatures", function(v) trace_ligatures = v end)
+local trace_contexts = false registertracker("otf.contexts", function(v) trace_contexts = v end)
+local trace_marks = false registertracker("otf.marks", function(v) trace_marks = v end)
+local trace_kerns = false registertracker("otf.kerns", function(v) trace_kerns = v end)
+local trace_cursive = false registertracker("otf.cursive", function(v) trace_cursive = v end)
+local trace_preparing = false registertracker("otf.preparing", function(v) trace_preparing = v end)
+local trace_bugs = false registertracker("otf.bugs", function(v) trace_bugs = v end)
+local trace_details = false registertracker("otf.details", function(v) trace_details = v end)
+local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end)
+local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end)
+local trace_plugins = false registertracker("otf.plugins", function(v) trace_plugins = v end)
+local trace_chains = false registertracker("otf.chains", function(v) trace_chains = v end)
+
+local trace_kernruns = false registertracker("otf.kernruns", function(v) trace_kernruns = v end)
+----- trace_discruns = false registertracker("otf.discruns", function(v) trace_discruns = v end)
+local trace_compruns = false registertracker("otf.compruns", function(v) trace_compruns = v end)
+local trace_testruns = false registertracker("otf.testruns", function(v) trace_testruns = v end)
+
+local forcediscretionaries = false
+local forcepairadvance = false -- for testing
+
+directives.register("otf.forcediscretionaries",function(v)
+ forcediscretionaries = v
+end)
+
+directives.register("otf.forcepairadvance",function(v)
+ forcepairadvance = v
+end)
+
+local report_direct = logs.reporter("fonts","otf direct")
+local report_subchain = logs.reporter("fonts","otf subchain")
+local report_chain = logs.reporter("fonts","otf chain")
+local report_process = logs.reporter("fonts","otf process")
+local report_warning = logs.reporter("fonts","otf warning")
+local report_run = logs.reporter("fonts","otf run")
+
+registertracker("otf.substitutions", "otf.singles","otf.multiples","otf.alternatives","otf.ligatures")
+registertracker("otf.positions", "otf.marks","otf.kerns","otf.cursive")
+registertracker("otf.actions", "otf.substitutions","otf.positions")
+registertracker("otf.sample", "otf.steps","otf.substitutions","otf.positions","otf.analyzing")
+registertracker("otf.sample.silent", "otf.steps=silent","otf.substitutions","otf.positions","otf.analyzing")
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local setnext = nuts.setnext
+local getprev = nuts.getprev
+local setprev = nuts.setprev
+local getboth = nuts.getboth
+local setboth = nuts.setboth
+local getid = nuts.getid
+local getstate = nuts.getstate
+local setsubtype = nuts.setsubtype
+local getchar = nuts.getchar
+local setchar = nuts.setchar
+local getdisc = nuts.getdisc
+local setdisc = nuts.setdisc
+local getreplace = nuts.getreplace
+local setlink = nuts.setlink
+local getwidth = nuts.getwidth
+local getoptions = nuts.getoptions
+local setoptions = nuts.setoptions
+
+local getglyphdata = nuts.getglyphdata
+local getscales = nuts.getscales
+
+---------------------------------------------------------------------------------------
+
+-- Beware: In ConTeXt components no longer are real components. We only keep track of
+-- their positions because some complex ligatures might need that. For the moment we
+-- use an x_ prefix because for now generic follows the other approach.
+
+local copy_no_components = nuts.copy_no_components
+local copy_only_glyphs = nuts.copy_only_glyphs
+local count_components = nuts.count_components
+local set_components = nuts.set_components
+local get_components = nuts.get_components
+local flush_components = nuts.flush_components
+
+---------------------------------------------------------------------------------------
+
+local ischar = nuts.ischar
+local isnextchar = nuts.isnextchar -- only makes sense when we know we need next
+local isprevchar = nuts.isprevchar -- only makes sense when we know we need prev
+local usesfont = nuts.uses_font
+
+local insert_node_after = nuts.insert_after
+local copy_node = nuts.copy
+local copy_node_list = nuts.copy_list
+local remove_node = nuts.remove
+local find_node_tail = nuts.tail
+local flush_node_list = nuts.flush_list
+local flush_node = nuts.flush_node
+local end_of_math = nuts.end_of_math
+local start_of_par = nuts.start_of_par
+
+local setmetatable = setmetatable
+local setmetatableindex = table.setmetatableindex
+
+local nextnode = nuts.traversers.node
+
+----- zwnj = 0x200C
+----- zwj = 0x200D
+
+local nodecodes = nodes.nodecodes
+local glyphcodes = nodes.glyphcodes
+
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+local disc_code = nodecodes.disc
+local math_code = nodecodes.math
+local dir_code = nodecodes.dir
+local par_code = nodecodes.par
+
+local lefttoright_code = nodes.dirvalues.lefttoright
+local righttoleft_code = nodes.dirvalues.righttoleft
+
+local discretionarydisc_code = nodes.disccodes.discretionary
+local ligatureglyph_code = glyphcodes.ligature
+
+local injections = nodes.injections
+local setmark = injections.setmark
+local setcursive = injections.setcursive
+local setkern = injections.setkern
+local setmove = injections.setmove
+local setposition = injections.setposition
+local resetinjection = injections.reset
+local copyinjection = injections.copy
+local setligaindex = injections.setligaindex
+local getligaindex = injections.getligaindex
+
+local fontdata = fonts.hashes.identifiers
+local fontfeatures = fonts.hashes.features
+
+local otffeatures = fonts.constructors.features.otf
+local registerotffeature = otffeatures.register
+
+local onetimemessage = fonts.loggers.onetimemessage or function() end
+
+local getrandom = utilities and utilities.randomizer and utilities.randomizer.get
+
+otf.defaultnodealternate = "none" -- first last
+
+-- We use a few semi-global variables. The handler can be called nested but this assumes
+-- that the same font is used.
+
+local tfmdata = false
+local characters = false
+local descriptions = false
+local marks = false
+local classes = false
+local currentfont = false
+local currentdynamic = false
+local currentscale = false
+local currentxscale = false
+local currentyscale = false
+local factor = 0
+local threshold = 0
+local checkmarks = false
+
+local discs = false
+local spaces = false
+
+local sweepnode = nil
+local sweephead = { } -- we don't nil entries but false them (no collection and such)
+
+local notmatchpre = { } -- to be checked: can we use false instead of nil / what if a == b tests
+local notmatchpost = { } -- to be checked: can we use false instead of nil / what if a == b tests
+local notmatchreplace = { } -- to be checked: can we use false instead of nil / what if a == b tests
+
+local handlers = { }
+
+local isspace = injections.isspace
+local getthreshold = injections.getthreshold
+
+local checkstep = (tracers and tracers.steppers.check) or function() end
+local registerstep = (tracers and tracers.steppers.register) or function() end
+local registermessage = (tracers and tracers.steppers.message) or function() end
+
+-- local function checkdisccontent(d)
+-- local pre, post, replace = getdisc(d)
+-- if pre then for n in traverse_id(glue_code,pre) do report("pre: %s",nodes.idstostring(pre)) break end end
+-- if post then for n in traverse_id(glue_code,post) do report("pos: %s",nodes.idstostring(post)) break end end
+-- if replace then for n in traverse_id(glue_code,replace) do report("rep: %s",nodes.idstostring(replace)) break end end
+-- end
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ if trace_steps == "silent" then
+ return
+ end
+ end
+ report_direct(...)
+end
+
+local function logwarning(...)
+ report_direct(...)
+end
+
+local gref do
+
+ local f_unicode = formatters["U+%X"] -- was ["%U"]
+ local f_uniname = formatters["U+%X (%s)"] -- was ["%U (%s)"]
+ local f_unilist = formatters["% t"]
+
+ gref = function(n) -- currently the same as in font-otb
+ if type(n) == "number" then
+ local description = descriptions[n]
+ local name = description and description.name
+ if name then
+ return f_uniname(n,name)
+ else
+ return f_unicode(n)
+ end
+ elseif n then
+ local t = { }
+ for i=1,#n do
+ local ni = n[i]
+ if tonumber(ni) then -- later we will start at 2
+ local di = descriptions[ni]
+ local nn = di and di.name
+ if nn then
+ t[#t+1] = f_uniname(ni,nn)
+ else
+ t[#t+1] = f_unicode(ni)
+ end
+ end
+ end
+ return f_unilist(t)
+ else
+ return "<error in node mode tracing>"
+ end
+ end
+
+end
+
+local function cref(dataset,sequence,index)
+ if not dataset then
+ return "no valid dataset"
+ end
+ local merged = sequence.merged and "merged " or ""
+ if index then
+ return formatters["feature %a, type %a, %schain lookup %a, index %a"](
+ dataset[4],sequence.type,merged,sequence.name,index)
+ else
+ return formatters["feature %a, type %a, %schain lookup %a"](
+ dataset[4],sequence.type,merged,sequence.name)
+ end
+end
+
+local function pref(dataset,sequence)
+ return formatters["feature %a, type %a, %slookup %a"](
+ dataset[4],sequence.type,sequence.merged and "merged " or "",sequence.name)
+end
+
+local function mref(rlmode)
+ if not rlmode or rlmode >= 0 then
+ return "l2r"
+ else
+ return "r2l"
+ end
+end
+
+-- The next code is somewhat complicated by the fact that some fonts can have ligatures made
+-- from ligatures that themselves have marks. This was identified by Kai in for instance
+-- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes
+-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next
+-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the
+-- third component.
+
+-- We can assume that languages that use marks are not hyphenated. We can also assume
+-- that at most one discretionary is present.
+
+-- We do need components in funny kerning mode but maybe I can better reconstruct then
+-- as we do have the font components info available; removing components makes the
+-- previous code much simpler. Also, later on copying and freeing becomes easier.
+-- However, for arabic we need to keep them around for the sake of mark placement
+-- and indices.
+
+local function flattendisk(head,disc)
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+ local prev, next = getboth(disc)
+ local ishead = head == disc
+ setdisc(disc)
+ flush_node(disc)
+ if pre then
+ flush_node_list(pre)
+ end
+ if post then
+ flush_node_list(post)
+ end
+ if ishead then
+ if replace then
+ if next then
+ setlink(replacetail,next)
+ end
+ return replace, replace
+ elseif next then
+ return next, next
+ else
+ -- return -- maybe warning
+ end
+ else
+ if replace then
+ if next then
+ setlink(replacetail,next)
+ end
+ setlink(prev,replace)
+ return head, replace
+ else
+ setlink(prev,next) -- checks for next anyway
+ return head, next
+ end
+ end
+end
+
+local function appenddisc(disc,list)
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+ local posthead = list
+ local replacehead = copy_node_list(list)
+ if post then
+ setlink(posttail,posthead)
+ else
+ post = posthead
+ end
+ if replace then
+ setlink(replacetail,replacehead)
+ else
+ replace = replacehead
+ end
+ setdisc(disc,pre,post,replace)
+end
+
+local function markstoligature(head,start,stop,char)
+ if start == stop and getchar(start) == char then
+ return head, start
+ else
+ local prev = getprev(start)
+ local next = getnext(stop)
+ setprev(start)
+ setnext(stop)
+ local base = copy_no_components(start,copyinjection)
+ if head == start then
+ head = base
+ end
+ resetinjection(base)
+ setchar(base,char)
+ setsubtype(base,ligatureglyph_code)
+ set_components(base,start)
+ setlink(prev,base,next)
+ flush_components(start)
+ return head, base
+ end
+end
+
+-- Remark for Kai: (some arabic fonts do mark + mark = other mark and such)
+--
+-- The hasmarks is needed for ligatures of marks that are part of a ligature in
+-- which case we assume that we can delete the marks anyway (we can always become
+-- more clever if needed) .. in fact the whole logic here should be redone. We're
+-- in the not discfound branch then. We now have skiphash too so we can be more
+-- selective if needed (todo).
+
+-- we can have more granularity here but for now we only do a simple check
+
+local no_left_ligature_code = 1
+local no_right_ligature_code = 2
+local no_left_kern_code = 4
+local no_right_kern_code = 8
+
+local has_glyph_option = nuts.has_glyph_option
+
+-- in lmtx we need to check the components and can be slightly more clever
+
+local function inhibited(start,stop)
+ for n in nextnode, start do
+ -- we asume glyph nodes
+ if n == start then
+ if has_glyph_option(n,no_right_ligature_code) then
+ return true
+ end
+ elseif n == stop then
+ if has_glyph_option(n,no_left_ligature_code) then
+ return true
+ else
+ return false
+ end
+ elseif has_glyph_option(n,no_left_ligature_code) then
+ return true
+ elseif has_glyph_option(n,no_right_ligature_code) then
+ return true
+ end
+ end
+ return false
+end
+
+local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) -- brr head
+ if start == stop and getchar(start) == char then
+ resetinjection(start)
+ setchar(start,char)
+ return head, start
+ end
+ if inhibited(start,stop) then
+ return head, start
+ end
+ local prev = getprev(start)
+ local next = getnext(stop)
+ local comp = start
+ setprev(start)
+ setnext(stop)
+ local base = copy_no_components(start,copyinjection)
+ if start == head then
+ head = base
+ end
+ resetinjection(base)
+ setchar(base,char)
+ setoptions(base,getoptions(start) | getoptions(stop)) -- maybe only lig options
+ setsubtype(base,ligatureglyph_code)
+ set_components(base,comp)
+ setlink(prev,base,next)
+ if not discfound then
+ local deletemarks = not skiphash or hasmarks
+ local components = start -- not used
+ local baseindex = 0
+ local componentindex = 0
+ local head = base
+ local current = base
+ -- first we loop over the glyphs in start ... stop
+ while start do
+ local char = getchar(start)
+ if not marks[char] then
+ baseindex = baseindex + componentindex
+ componentindex = count_components(start,marks)
+ -- we can be more clever here: "not deletemarks or (skiphash and not skiphash[char])"
+ -- and such:
+ elseif not deletemarks then
+ -- we can get a loop when the font expects otherwise (i.e. unexpected deletemarks)
+ setligaindex(start,baseindex + getligaindex(start,componentindex))
+ if trace_marks then
+ logwarning("%s: keep ligature mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
+ end
+ local n = copy_node(start)
+ copyinjection(n,start) -- is this ok ? we position later anyway
+ head, current = insert_node_after(head,current,n) -- unlikely that mark has components
+ elseif trace_marks then
+ logwarning("%s: delete ligature mark %s",pref(dataset,sequence),gref(char))
+ end
+ start = getnext(start)
+ end
+ -- we can have one accent as part of a lookup and another following
+ local start = getnext(current)
+ while start do
+ local nxt, char = isnextchar(start,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ -- also something skiphash here?
+ if marks[char] then
+ setligaindex(start,baseindex + getligaindex(start,componentindex))
+ if trace_marks then
+ logwarning("%s: set ligature mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
+ end
+ start = nxt
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ flush_components(components)
+ else
+ -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks
+ local discprev, discnext = getboth(discfound)
+ if discprev and discnext then
+ -- we assume normalization in context, and don't care about generic ... especially
+ -- \- can give problems as there we can have a negative char but that won't match
+ -- anyway
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(discfound,true)
+ if not replace then
+ -- looks like we never come here as it's not okay
+ local prev = getprev(base)
+ -- local comp = get_components(base) -- already set
+ local copied = copy_only_glyphs(comp)
+ if pre then
+ setlink(discprev,pre)
+ else
+ setnext(discprev) -- also blocks funny assignments
+ end
+ pre = comp -- is start
+ if post then
+ setlink(posttail,discnext)
+ setprev(post) -- nil anyway
+ else
+ post = discnext
+ setprev(discnext) -- also blocks funny assignments
+ end
+ setlink(prev,discfound,next)
+ setboth(base)
+ -- here components have a pointer so we can't free it!
+ set_components(base,copied)
+ replace = base
+ if forcediscretionaries then
+ setdisc(discfound,pre,post,replace,discretionarydisc_code)
+ else
+ setdisc(discfound,pre,post,replace)
+ end
+ base = prev
+ end
+ end
+ end
+ return head, base
+end
+
+local function multiple_glyphs(head,start,multiple,skiphash,what,stop) -- what to do with skiphash matches here
+ local nofmultiples = #multiple
+ if nofmultiples > 0 then
+ resetinjection(start)
+ setchar(start,multiple[1])
+ if nofmultiples > 1 then
+ local sn = getnext(start)
+ for k=2,nofmultiples do
+ -- untested:
+ --
+ -- while ignoremarks and marks[getchar(sn)] then
+ -- local sn = getnext(sn)
+ -- end
+ local n = copy_node(start) -- ignore components
+ resetinjection(n)
+ setchar(n,multiple[k])
+ insert_node_after(head,start,n)
+ start = n
+ end
+ if what == true then
+ -- we're ok
+ elseif what > 1 then
+ local m = multiple[nofmultiples]
+ for i=2,what do
+ local n = copy_node(start) -- ignore components
+ resetinjection(n)
+ setchar(n,m)
+ insert_node_after(head,start,n)
+ start = n
+ end
+ end
+ end
+ return head, start, true
+ else
+ if trace_multiples then
+ logprocess("no multiple for %s",gref(getchar(start)))
+ end
+ return head, start, false
+ end
+end
+
+local function get_alternative_glyph(start,alternatives,value)
+ local n = #alternatives
+ if n == 1 then
+ -- we could actually change that into a gsub and save some memory in the
+ -- font loader but it makes tracing more messy
+ return alternatives[1], trace_alternatives and "1 (only one present)"
+ elseif value == "random" then
+ local r = getrandom and getrandom("glyph",1,n) or random(1,n)
+ return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r)
+ elseif value == "first" then
+ return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1)
+ elseif value == "last" then
+ return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n)
+ end
+ value = value == true and 1 or tonumber(value)
+ if type(value) ~= "number" then
+ return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
+ end
+ -- local a = alternatives[value]
+ -- if a then
+ -- -- some kind of hash
+ -- return a, trace_alternatives and formatters["value %a, taking %a"](value,a)
+ -- end
+ if value > n then
+ local defaultalt = otf.defaultnodealternate
+ if defaultalt == "first" then
+ return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
+ elseif defaultalt == "last" then
+ return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
+ else
+ return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
+ end
+ elseif value == 0 then
+ return getchar(start), trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
+ elseif value < 1 then
+ return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
+ else
+ return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value)
+ end
+end
+
+-- handlers
+
+function handlers.gsub_single(head,start,dataset,sequence,replacement)
+ if trace_singles then
+ logprocess("%s: replacing %s by single %s",pref(dataset,sequence),gref(getchar(start)),gref(replacement))
+ end
+ resetinjection(start)
+ setchar(start,replacement)
+ return head, start, true
+end
+
+function handlers.gsub_alternate(head,start,dataset,sequence,alternative)
+ local kind = dataset[4]
+ local what = dataset[1]
+ local value = what == true and tfmdata.shared.features[kind] or what
+ local choice, comment = get_alternative_glyph(start,alternative,value)
+ if choice then
+ if trace_alternatives then
+ logprocess("%s: replacing %s by alternative %a to %s, %s",pref(dataset,sequence),gref(getchar(start)),gref(choice),comment)
+ end
+ resetinjection(start)
+ setchar(start,choice)
+ else
+ if trace_alternatives then
+ logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment)
+ end
+ end
+ return head, start, true
+end
+
+function handlers.gsub_multiple(head,start,dataset,sequence,multiple,rlmode,skiphash)
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple %s",pref(dataset,sequence),gref(getchar(start)),gref(multiple))
+ end
+ return multiple_glyphs(head,start,multiple,skiphash,dataset[1])
+end
+
+-- Don't we deal with disc otherwise now? I need to check if the next one can be
+-- simplified. Anyway, it can be way messier: marks that get removed as well as
+-- marks that are kept.
+
+function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skiphash)
+ local current = getnext(start)
+ if not current then
+ return head, start, false, nil
+ end
+ local stop = nil
+ local startchar = getchar(start)
+ if skiphash and skiphash[startchar] then
+ while current do
+ local nxt, char = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ local lg = ligature[char]
+ if lg then
+ stop = current
+ ligature = lg
+ current = nxt
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if stop then
+ local lig = ligature.ligature
+ if lig then
+ if trace_ligatures then
+ local stopchar = getchar(stop)
+ head, start = markstoligature(head,start,stop,lig)
+ logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))
+ else
+ head, start = markstoligature(head,start,stop,lig)
+ end
+ return head, start, true, false
+ else
+ -- ok, goto next lookup
+ end
+ end
+ else
+ local discfound = false
+ local hasmarks = marks[startchar]
+ while current do
+ local nxt, char, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ if skiphash and skiphash[char] then
+ current = nxt
+ else
+ local lg = ligature[char]
+ if lg then
+ if marks[char] then
+ hasmarks = true
+ end
+ stop = current -- needed for fake so outside then
+ ligature = lg
+ current = nxt
+ else
+ break
+ end
+ end
+ elseif char == false then
+ -- kind of weird
+ break
+ elseif id == disc_code then
+ discfound = current
+ break
+ else
+ break
+ end
+ end
+ -- of{f-}{}{f}e o{f-}{}{f}fe o{-}{}{ff}e (oe and ff ligature)
+ -- we can end up here when we have a start run .. testruns start at a disc but
+ -- so here we have the other case: char + disc
+ --
+ -- Challenge for Kai (latinmodern): \hyphenation{fii-f-f-iif} fiiffiif
+ --
+ if discfound then
+ -- don't assume marks in a disc and we don't run over a disc (for now)
+ local pre, post, replace = getdisc(discfound)
+ local match
+ if replace then
+ local nxt, char = isnextchar(replace,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char and ligature[char] then
+ match = true
+ end
+ end
+ if not match and pre then
+ local nxt, char = isnextchar(pre,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char and ligature[char] then
+ match = true
+ end
+ end
+ -- is this test ok:
+ if not match and not pre or not replace then
+ local nxt, char = isnextchar(getnext(discfound),currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char and ligature[char] then
+ match = true
+ end
+ end
+ if match then
+ -- we force a restart
+ local ishead = head == start
+ local prev = getprev(start)
+ if stop then
+ setnext(stop)
+ local copy = copy_node_list(start)
+ local tail = stop -- was: getprev(stop) -- Kai: needs checking on your samples
+ local liat = find_node_tail(copy)
+ if pre then
+ setlink(liat,pre)
+ end
+ if replace then
+ setlink(tail,replace)
+ end
+ pre = copy
+ replace = start
+ else
+ setnext(start)
+ local copy = copy_node(start)
+ if pre then
+ setlink(copy,pre)
+ end
+ if replace then
+ setlink(start,replace)
+ end
+ pre = copy
+ replace = start
+ end
+ setdisc(discfound,pre,post,replace)
+ if prev then
+ setlink(prev,discfound)
+ else
+ setprev(discfound)
+ head = discfound
+ end
+ start = discfound
+ return head, start, true, true
+ end
+ end
+ local lig = ligature.ligature
+ if lig then
+ if stop then
+ if trace_ligatures then
+ local stopchar = getchar(stop)
+ -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks)
+ head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks)
+ logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig))
+ -- we can have a rare case of multiple disc in a lig but that makes no sense language wise but if really
+ -- needed we could backtrack if we're in a disc node
+ else
+ -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks)
+ head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks)
+ end
+ else
+ -- weird but happens (in some arabic font)
+ resetinjection(start)
+ setchar(start,lig)
+ if trace_ligatures then
+ logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig))
+ end
+ end
+ return head, start, true, false
+ else
+ -- weird but happens, pseudo ligatures ... just the components
+ end
+ end
+ return head, start, false, false
+end
+
+function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection)
+ if has_glyph_option(start,no_right_kern_code) then
+ return head, start, false
+ else
+ local startchar = getchar(start)
+ local format = step.format
+ if format == "single" or type(kerns) == "table" then -- the table check can go
+ local dx, dy, w, h = setposition(0,start,factor,rlmode,kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by %s xy (%p,%p) and wh (%p,%p)",pref(dataset,sequence),gref(startchar),format,dx,dy,w,h)
+ end
+ else
+ local k = (format == "move" and setmove or setkern)(start,factor,rlmode,kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by %s %p",pref(dataset,sequence),gref(startchar),format,k)
+ end
+ end
+ return head, start, true
+ end
+end
+
+function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection)
+ if has_glyph_option(start,no_right_kern_code) then
+ return head, start, false
+ else
+ local snext = getnext(start)
+ if not snext then
+ return head, start, false
+ else
+ local prev = start
+ while snext do
+ local nxt, nextchar = isnextchar(snext,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if nextchar then
+ if skiphash and skiphash[nextchar] then -- includes marks too when flag
+ prev = snext
+ snext = nxt
+ else
+ local krn = kerns[nextchar]
+ if not krn then
+ break
+ end
+ local format = step.format
+ if format == "pair" then
+ local a = krn[1]
+ local b = krn[2]
+ if a == true then
+ -- zero
+ elseif a then -- #a > 0
+ local x, y, w, h = setposition(1,start,factor,rlmode,a,injection)
+ if trace_kerns then
+ local startchar = getchar(start)
+ logprocess("%s: shifting first of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
+ end
+ end
+ if b == true then
+ -- zero
+ start = snext -- cf spec
+ elseif b then -- #b > 0
+ local x, y, w, h = setposition(2,snext,factor,rlmode,b,injection)
+ if trace_kerns then
+ local startchar = getchar(start)
+ logprocess("%s: shifting second of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
+ end
+ start = snext -- cf spec
+ elseif forcepairadvance then
+ start = snext -- for testing, not cf spec
+ end
+ return head, start, true
+ elseif krn ~= 0 then
+ local k = (format == "move" and setmove or setkern)(snext,factor,rlmode,krn,injection)
+ if trace_kerns then
+ logprocess("%s: inserting %s %p between %s and %s as %s",pref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar),injection or "injections")
+ end
+ return head, start, true
+ else -- can't happen
+ break
+ end
+ end
+ else
+ break
+ end
+ end
+ return head, start, false
+ end
+ end
+end
+
+--[[ldx--
+<p>We get hits on a mark, but we're not sure if the it has to be applied so
+we need to explicitly test for basechar, baselig and basemark entries.</p>
+--ldx]]--
+
+function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode,skiphash)
+ local markchar = getchar(start)
+ if marks[markchar] then
+ local base = getprev(start) -- [glyph] [start=mark]
+ if base then
+ local prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if marks[basechar] then
+ -- todo: use prv
+ while base do
+ base = getprev(base)
+ if base then
+ prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
+ end
+ return head, start, false
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
+ end
+ return head, start, false
+ end
+ end
+ end
+ local ba = markanchors[1][basechar]
+ if ba then
+ local ma = markanchors[2]
+ local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
+ if trace_marks then
+ logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)",
+ pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ elseif trace_bugs then
+ -- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1)
+ end
+ elseif trace_bugs then
+ logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),2)
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
+ end
+ return head, start, false
+end
+
+function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode,skiphash)
+ local markchar = getchar(start)
+ if marks[markchar] then
+ local base = getprev(start) -- [glyph] [optional marks] [start=mark]
+ if base then
+ local prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if marks[basechar] then
+ -- todo: use prv
+ while base do
+ base = getprev(base)
+ if base then
+ prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
+ end
+ return head, start, false
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
+ end
+ return head, start, false
+ end
+ end
+ end
+ local ba = markanchors[1][basechar]
+ if ba then
+ local ma = markanchors[2]
+ if ma then
+ local index = getligaindex(start)
+ ba = ba[index]
+ if ba then
+ local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
+ if trace_marks then
+ logprocess("%s, index %s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)",
+ pref(dataset,sequence),index,bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return head, start, true
+ else
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(dataset,sequence),gref(markchar),gref(basechar),index)
+ end
+ end
+ end
+ elseif trace_bugs then
+ -- logwarning("%s: char %s is missing in font",pref(dataset,sequence),gref(basechar))
+ onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),2)
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
+ end
+ return head, start, false
+end
+
+function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode,skiphash)
+ local markchar = getchar(start)
+ if marks[markchar] then
+ local base = getprev(start) -- [glyph] [basemark] [start=mark]
+ local slc = getligaindex(start)
+ if slc then -- a rather messy loop ... needs checking with husayni
+ while base do
+ local blc = getligaindex(base)
+ if blc and blc ~= slc then
+ base = getprev(base)
+ else
+ break
+ end
+ end
+ end
+ if base then
+ local nxt, basechar = isnextchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then -- subtype test can go
+ local ba = markanchors[1][basechar] -- slot 1 has been made copy of the class hash
+ if ba then
+ local ma = markanchors[2]
+ local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
+ if trace_marks then
+ logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)",
+ pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
+ end
+ return head, start, false
+end
+
+function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,skiphash,step) -- to be checked
+ local startchar = getchar(start)
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
+ end
+ else
+ local nxt = getnext(start)
+ while nxt do
+ local n, nextchar = isnextchar(nxt,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if not nextchar then
+ break
+ elseif marks[nextchar] then -- always sequence.flags[1]
+ nxt = n
+ else
+ local exit = exitanchors[3]
+ if exit then
+ local entry = exitanchors[1][nextchar]
+ if entry then
+ entry = entry[2]
+ if entry then
+ local r2lflag = sequence.flags[4] -- mentioned in the standard
+ local dx, dy, bound = setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag)
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode))
+ end
+ return head, start, true
+ end
+ end
+ end
+ break
+ end
+ end
+ end
+ return head, start, false
+end
+
+--[[ldx--
+<p>I will implement multiple chain replacements once I run into a font that uses
+it. It's not that complex to handle.</p>
+--ldx]]--
+
+local chainprocs = { }
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ if trace_steps == "silent" then
+ return
+ end
+ end
+ report_subchain(...)
+end
+
+local logwarning = report_subchain
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ if trace_steps == "silent" then
+ return
+ end
+ end
+ report_chain(...)
+end
+
+local logwarning = report_chain
+
+-- We could share functions but that would lead to extra function calls with many
+-- arguments, redundant tests and confusing messages.
+
+-- The reversesub is a special case, which is why we need to store the replacements
+-- in a bit weird way. There is no lookup and the replacement comes from the lookup
+-- itself. It is meant mostly for dealing with Urdu.
+
+local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode,skiphash)
+ local char = getchar(start)
+ local replacement = replacements[char]
+ if replacement then
+ if trace_singles then
+ logprocess("%s: single reverse replacement of %s by %s",cref(dataset,sequence),gref(char),gref(replacement))
+ end
+ resetinjection(start)
+ setchar(start,replacement)
+ return head, start, true
+ else
+ return head, start, false
+ end
+end
+
+
+chainprocs.reversesub = reversesub
+
+--[[ldx--
+<p>This chain stuff is somewhat tricky since we can have a sequence of actions to be
+applied: single, alternate, multiple or ligature where ligature can be an invalid
+one in the sense that it will replace multiple by one but not neccessary one that
+looks like the combination (i.e. it is the counterpart of multiple then). For
+example, the following is valid:</p>
+
+<typing>
+<line>xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx</line>
+</typing>
+
+<p>Therefore we we don't really do the replacement here already unless we have the
+single lookup case. The efficiency of the replacements can be improved by deleting
+as less as needed but that would also make the code even more messy.</p>
+--ldx]]--
+
+--[[ldx--
+<p>Here we replace start by a single variant.</p>
+--ldx]]--
+
+-- To be done (example needed): what if > 1 steps
+
+-- this is messy: do we need this disc checking also in alternaties?
+
+local function reportzerosteps(dataset,sequence)
+ logwarning("%s: no steps",cref(dataset,sequence))
+end
+
+local function reportmoresteps(dataset,sequence)
+ logwarning("%s: more than 1 step",cref(dataset,sequence))
+end
+
+-- local function reportbadsteps(dataset,sequence)
+-- logwarning("%s: bad step, no proper return values",cref(dataset,sequence))
+-- end
+
+local function getmapping(dataset,sequence,currentlookup)
+ local steps = currentlookup.steps
+ local nofsteps = currentlookup.nofsteps
+ if nofsteps == 0 then
+ reportzerosteps(dataset,sequence)
+ currentlookup.mapping = false
+ return false
+ else
+ if nofsteps > 1 then
+ reportmoresteps(dataset,sequence)
+ end
+ local mapping = steps[1].coverage
+ currentlookup.mapping = mapping
+ currentlookup.format = steps[1].format
+ return mapping
+ end
+end
+
+function chainprocs.gsub_remove(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ if trace_chains then
+ logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start)))
+ end
+ head, start = remove_node(head,start,true)
+ return head, getprev(start), true
+end
+
+function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local current = start
+ while current do
+ local nxt, currentchar = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if currentchar then
+ local replacement = mapping[currentchar]
+ if not replacement or replacement == "" then
+ if trace_bugs then
+ logwarning("%s: no single for %s",cref(dataset,sequence,chainindex),gref(currentchar))
+ end
+ else
+ if trace_singles then
+ logprocess("%s: replacing single %s by %s",cref(dataset,sequence,chainindex),gref(currentchar),gref(replacement))
+ end
+ resetinjection(current)
+ setchar(current,replacement)
+ end
+ return head, start, true
+ elseif currentchar == false then
+ -- can't happen
+ break
+ elseif current == stop then
+ break
+ else
+ current = nxt
+ end
+ end
+ end
+ return head, start, false
+end
+
+--[[ldx--
+<p>Here we replace start by new glyph. First we delete the rest of the match.</p>
+--ldx]]--
+
+-- char_1 mark_1 -> char_x mark_1 (ignore marks)
+-- char_1 mark_1 -> char_x
+
+-- to be checked: do we always have just one glyph?
+-- we can also have alternates for marks
+-- marks come last anyway
+-- are there cases where we need to delete the mark
+
+function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local kind = dataset[4]
+ local what = dataset[1]
+ local value = what == true and tfmdata.shared.features[kind] or what -- todo: optimize in ctx
+ local current = start
+ while current do
+ local nxt, currentchar = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if currentchar then
+ local alternatives = mapping[currentchar]
+ if alternatives then
+ local choice, comment = get_alternative_glyph(current,alternatives,value)
+ if choice then
+ if trace_alternatives then
+ logprocess("%s: replacing %s by alternative %a to %s, %s",cref(dataset,sequence),gref(currentchar),choice,gref(choice),comment)
+ end
+ resetinjection(start)
+ setchar(start,choice)
+ else
+ if trace_alternatives then
+ logwarning("%s: no variant %a for %s, %s",cref(dataset,sequence),value,gref(currentchar),comment)
+ end
+ end
+ end
+ return head, start, true
+ elseif currentchar == false then
+ -- can't happen
+ break
+ elseif current == stop then
+ break
+ else
+ current = nxt
+ end
+ end
+ end
+ return head, start, false
+end
+
+--[[ldx--
+<p>Here we replace start by a sequence of new glyphs.</p>
+--ldx]]--
+
+function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local startchar = getchar(start)
+ local replacement = mapping[startchar]
+ if not replacement or replacement == "" then
+ if trace_bugs then
+ logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar))
+ end
+ else
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple characters %s",cref(dataset,sequence),gref(startchar),gref(replacement))
+ end
+ return multiple_glyphs(head,start,replacement,skiphash,dataset[1],stop)
+ end
+ end
+ return head, start, false
+end
+
+--[[ldx--
+<p>When we replace ligatures we use a helper that handles the marks. I might change
+this function (move code inline and handle the marks by a separate function). We
+assume rather stupid ligatures (no complex disc nodes).</p>
+--ldx]]--
+
+-- compare to handlers.gsub_ligature which is more complex ... why
+
+function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local startchar = getchar(start)
+ local ligatures = mapping[startchar]
+ if not ligatures then
+ if trace_bugs then
+ logwarning("%s: no ligatures starting with %s",cref(dataset,sequence,chainindex),gref(startchar))
+ end
+ else
+ local hasmarks = marks[startchar]
+ local current = getnext(start)
+ local discfound = false
+ local last = stop
+ local nofreplacements = 1
+ while current do
+ -- todo: ischar ... can there really be disc nodes here?
+-- local id = getid(current)
+-- if id == disc_code then
+-- if not discfound then
+-- discfound = current
+-- end
+-- if current == stop then
+-- break -- okay? or before the disc
+-- else
+-- current = getnext(current)
+-- end
+-- else
+-- local schar = getchar(current)
+-- if skiphash and skiphash[schar] then -- marks
+-- -- if current == stop then -- maybe add this
+-- -- break
+-- -- else
+-- current = getnext(current)
+-- -- end
+-- else
+-- local lg = ligatures[schar]
+-- if lg then
+-- ligatures = lg
+-- last = current
+-- nofreplacements = nofreplacements + 1
+-- if marks[char] then
+-- hasmarks = true
+-- end
+-- if current == stop then
+-- break
+-- else
+-- current = getnext(current)
+-- end
+-- else
+-- break
+-- end
+-- end
+-- end
+ --
+ local nxt, schar, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if schar then
+ if skiphash and skiphash[schar] then -- marks
+ -- if current == stop then -- maybe add this
+ -- break
+ -- else
+ current = nxt
+ -- end
+ else
+ local lg = ligatures[schar]
+ if lg then
+ ligatures = lg
+ last = current
+ nofreplacements = nofreplacements + 1
+ if marks[char] then
+ hasmarks = true
+ end
+ if current == stop then
+ break
+ else
+ current = nxt
+ end
+ else
+ break
+ end
+ end
+ elseif id == disc_code then
+ if not discfound then
+ discfound = current
+ end
+ if current == stop then
+ break -- okay? or before the disc
+ else
+ current = nxt
+ end
+ else
+ break
+ end
+ --
+ end
+ local ligature = ligatures.ligature
+ if ligature then
+ if chainindex then
+ stop = last
+ end
+ if trace_ligatures then
+ if start == stop then
+ logprocess("%s: replacing character %s by ligature %s case 3",cref(dataset,sequence,chainindex),gref(startchar),gref(ligature))
+ else
+ logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)),gref(ligature))
+ end
+ end
+ head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks)
+ return head, start, true, nofreplacements, discfound
+ elseif trace_bugs then
+ if start == stop then
+ logwarning("%s: replacing character %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar))
+ else
+ logwarning("%s: replacing character %s upto %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)))
+ end
+ end
+ end
+ end
+ return head, start, false, 0, false
+end
+
+function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ -- we actually should check no_left_kern_code with next
+ if not has_glyph_option(start,no_right_kern_code) then
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local startchar = getchar(start)
+ local kerns = mapping[startchar]
+ if kerns then
+ local format = currentlookup.format
+ if format == "single" then
+ local dx, dy, w, h = setposition(0,start,factor,rlmode,kerns) -- currentlookup.flags ?
+ if trace_kerns then
+ logprocess("%s: shifting single %s by %s (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),format,dx,dy,w,h)
+ end
+ else -- needs checking .. maybe no kerns format for single
+ local k = (format == "move" and setmove or setkern)(start,factor,rlmode,kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by %s %p",cref(dataset,sequence),gref(startchar),format,k)
+ end
+ end
+ return head, start, true
+ end
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) -- todo: injections ?
+ -- we actually should check no_left_kern_code with next
+ if not has_glyph_option(start,no_right_kern_code) then
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local snext = getnext(start)
+ if snext then
+ local startchar = getchar(start)
+ local kerns = mapping[startchar] -- always 1 step
+ if kerns then
+ local prev = start
+ while snext do
+ local nxt, nextchar = isnextchar(snext,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if not nextchar then
+ break
+ elseif skiphash and skiphash[nextchar] then
+ prev = snext
+ snext = nxt
+ else
+ local krn = kerns[nextchar]
+ if not krn then
+ break
+ end
+ local format = currentlookup.format
+ if format == "pair" then
+ local a = krn[1]
+ local b = krn[2]
+ if a == true then
+ -- zero
+ elseif a then
+ local x, y, w, h = setposition(1,start,factor,rlmode,a,"injections") -- currentlookups flags?
+ if trace_kerns then
+ local startchar = getchar(start)
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b == true then
+ -- zero
+ start = snext -- cf spec
+ elseif b then -- #b > 0
+ local x, y, w, h = setposition(2,snext,factor,rlmode,b,"injections")
+ if trace_kerns then
+ local startchar = getchar(start)
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ start = snext -- cf spec
+ elseif forcepairadvance then
+ start = snext -- for testing, not cf spec
+ end
+ return head, start, true
+ elseif krn ~= 0 then
+ local k = (format == "move" and setmove or setkern)(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting %s %p between %s and %s",cref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar))
+ end
+ return head, start, true
+ else
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local markchar = getchar(start)
+ if marks[markchar] then
+ local markanchors = mapping[markchar] -- always 1 step
+ if markanchors then
+ local base = getprev(start) -- [glyph] [start=mark]
+ if base then
+ local prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if marks[basechar] then
+ -- todo: use prv
+ while base do
+ base = getprev(base)
+ if base then
+ local prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
+ end
+ return head, start, false
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
+ end
+ return head, start, false
+ end
+ end
+ end
+ local ba = markanchors[1][basechar]
+ if ba then
+ local ma = markanchors[2]
+ if ma then
+ local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
+ if trace_marks then
+ logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)",
+ cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),1)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2)
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local markchar = getchar(start)
+ if marks[markchar] then
+ local markanchors = mapping[markchar] -- always 1 step
+ if markanchors then
+ local base = getprev(start) -- [glyph] [optional marks] [start=mark]
+ if base then
+ local prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if marks[basechar] then
+ -- todo: use prv
+ while base do
+ base = getprev(base)
+ if base then
+ local prv, basechar = isprevchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,1)
+ end
+ return head, start, false
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,2)
+ end
+ return head, start, false
+ end
+ end
+ end
+ local ba = markanchors[1][basechar]
+ if ba then
+ local ma = markanchors[2]
+ if ma then
+ local index = getligaindex(start)
+ ba = ba[index]
+ if ba then
+ local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
+ if trace_marks then
+ logprocess("%s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)",
+ cref(dataset,sequence),a or bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),1)
+ end
+ elseif trace_bugs then
+ logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2)
+ end
+ elseif trace_bugs then
+ logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s, mark %s is no mark",cref(dataset,sequence),gref(markchar))
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local markchar = getchar(start)
+ if marks[markchar] then
+ local markanchors = mapping[markchar] -- always 1 step
+ if markanchors then
+ local base = getprev(start) -- [glyph] [basemark] [start=mark]
+ local slc = getligaindex(start)
+ if slc then -- a rather messy loop ... needs checking with husayni
+ while base do
+ local blc = getligaindex(base)
+ if blc and blc ~= slc then
+ base = getprev(base)
+ else
+ break
+ end
+ end
+ end
+ if base then -- subtype test can go
+ local nxt, basechar = isnextchar(base,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if basechar then
+ local ba = markanchors[1][basechar]
+ if ba then
+ local ma = markanchors[2]
+ if ma then
+ local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
+ if trace_marks then
+ logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)",
+ cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),1)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2)
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
+ local mapping = currentlookup.mapping
+ if mapping == nil then
+ mapping = getmapping(dataset,sequence,currentlookup)
+ end
+ if mapping then
+ local startchar = getchar(start)
+ local exitanchors = mapping[startchar] -- always 1 step
+ if exitanchors then
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
+ end
+ else
+ local nxt = getnext(start)
+ while nxt do
+ local n, nextchar = isnextchar(nxt,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if not nextchar then
+ break
+ elseif marks[nextchar] then
+ -- should not happen (maybe warning)
+ nxt = n
+ else
+ local exit = exitanchors[3]
+ if exit then
+ local entry = exitanchors[1][nextchar]
+ if entry then
+ entry = entry[2]
+ if entry then
+ local r2lflag = sequence.flags[4] -- mentioned in the standard
+ local dx, dy, bound = setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag)
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode))
+ end
+ return head, start, true
+ end
+ end
+ elseif trace_bugs then
+ onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
+ end
+ break
+ end
+ end
+ end
+ elseif trace_cursive and trace_details then
+ logprocess("%s, cursive %s is already done",pref(dataset,sequence),gref(getchar(start)),alreadydone)
+ end
+ end
+ return head, start, false
+end
+
+-- what pointer to return, spec says stop
+-- to be discussed ... is bidi changer a space?
+-- elseif char == zwnj and sequence[n][32] then -- brrr
+
+local function show_skip(dataset,sequence,char,ck,class)
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8] or ck[2])
+end
+
+-- A previous version had disc collapsing code in the (single sub) handler plus some
+-- checking in the main loop, but that left the pre/post sequences undone. The best
+-- solution is to add some checking there and backtrack when a replace/post matches
+-- but it takes a bit of work to figure out an efficient way (this is what the
+-- sweep* names refer to). I might look into that variant one day again as it can
+-- replace some other code too. In that approach we can have a special version for
+-- gub and pos which gains some speed. This method does the test and passes info to
+-- the handlers. Here collapsing is handled in the main loop which also makes code
+-- elsewhere simpler (i.e. no need for the other special runners and disc code in
+-- ligature building). I also experimented with pushing preceding glyphs sequences
+-- in the replace/pre fields beforehand which saves checking afterwards but at the
+-- cost of duplicate glyphs (memory) but it's too much overhead (runtime).
+--
+-- In the meantime Kai had moved the code from the single chain into a more general
+-- handler and this one (renamed to chaindisk) is used now. I optimized the code a
+-- bit and brought it in sycn with the other code. Hopefully I didn't introduce
+-- errors. Note: this somewhat complex approach is meant for fonts that implement
+-- (for instance) ligatures by character replacement which to some extend is not
+-- that suitable for hyphenation. I also use some helpers. This method passes some
+-- states but reparses the list. There is room for a bit of speed up but that will
+-- be done in the context version. (In fact a partial rewrite of all code can bring
+-- some more efficiency.)
+--
+-- I didn't test it with extremes but successive disc nodes still can give issues
+-- but in order to handle that we need more complex code which also slows down even
+-- more. The main loop variant could deal with that: test, collapse, backtrack.
+
+local userkern = nuts.pool and nuts.pool.newkern -- context
+
+do if not userkern then -- generic
+
+ local thekern = nuts.new("kern",1) -- userkern
+ local setkern = nuts.setkern -- not injections.setkern
+
+ userkern = function(k)
+ local n = copy_node(thekern)
+ setkern(n,k)
+ return n
+ end
+
+end end
+
+local function checked(head)
+ local current = head
+ while current do
+ if getid(current) == glue_code then
+ local kern = userkern(getwidth(current))
+ if head == current then
+ local next = getnext(current)
+ if next then
+ setlink(kern,next)
+ end
+ flush_node(current)
+ head = kern
+ current = next
+ else
+ local prev, next = getboth(current)
+ setlink(prev,kern,next)
+ flush_node(current)
+ current = next
+ end
+ else
+ current = getnext(current)
+ end
+ end
+ return head
+end
+
+local function setdiscchecked(d,pre,post,replace)
+ if pre then pre = checked(pre) end
+ if post then post = checked(post) end
+ if replace then replace = checked(replace) end
+ setdisc(d,pre,post,replace)
+end
+
+local noflags = { false, false, false, false }
+
+local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)
+
+ local size = ck[5] - ck[4] + 1
+ local chainlookups = ck[6]
+ local done = false
+
+ -- current match
+ if chainlookups then
+ -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and
+ -- #lookups can be less than #current
+
+ if size == 1 then
+
+ -- if nofchainlookups > size then
+ -- -- bad rules
+ -- end
+
+ local chainlookup = chainlookups[1]
+ for j=1,#chainlookup do
+ local chainstep = chainlookup[j]
+ local chainkind = chainstep.type
+ local chainproc = chainprocs[chainkind]
+ if chainproc then
+ local ok
+ head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash)
+ if ok then
+ done = true
+ end
+ else
+ logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)
+ end
+ end
+
+ else
+
+ -- See LookupType 5: Contextual Substitution Subtable. Now it becomes messy. The
+ -- easiest case is where #current maps on #lookups i.e. one-to-one. But what if
+ -- we have a ligature. Cf the spec we then need to advance one character but we
+ -- really need to test it as there are fonts out there that are fuzzy and have
+ -- too many lookups:
+ --
+ -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes
+ --
+ -- Even worse are these family emoji shapes as they can have multiple lookups
+ -- per slot (probably only for gpos).
+
+ -- It's very unlikely that we will have skip classes here but still ... we seldom
+ -- enter this branch anyway.
+
+ local i = 1
+ local laststart = start
+ local nofchainlookups = #chainlookups -- useful?
+ while start do
+ if skiphash then -- hm, so we know we skip some
+ while start do
+ local nxt, char = isnextchar(start,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ if skiphash and skiphash[char] then
+ start = nxt
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ end
+ local chainlookup = chainlookups[i]
+ if chainlookup then
+ for j=1,#chainlookup do
+ local chainstep = chainlookup[j]
+ local chainkind = chainstep.type
+ local chainproc = chainprocs[chainkind]
+ if chainproc then
+ local ok, n
+ head, start, ok, n = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i)
+ -- messy since last can be changed !
+ if ok then
+ done = true
+ if n and n > 1 and i + n > nofchainlookups then
+ -- this is a safeguard, we just ignore the rest of the lookups
+ i = size -- prevents an advance
+ break
+ end
+ end
+ else
+ -- actually an error
+ logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind)
+ end
+ end
+ else
+ -- we skip but we could also delete as option .. what does an empty lookup actually mean
+ -- in opentype ... anyway, we could map it onto gsub_remove if needed
+ end
+ i = i + 1
+ if i > size or not start then
+ break
+ elseif start then
+ laststart = start
+ start = getnext(start)
+ end
+ end
+ if not start then
+ start = laststart
+ end
+
+ end
+ else
+ -- todo: needs checking for holes in the replacements
+ local replacements = ck[7]
+ if replacements then
+ head, start, done = reversesub(head,start,last,dataset,sequence,replacements,rlmode,skiphash)
+ else
+ done = true
+ if trace_contexts then
+ logprocess("%s: skipping match",cref(dataset,sequence))
+ end
+ end
+ end
+ return head, start, done
+end
+
+local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck)
+
+ if not start then
+ return head, start, false
+ end
+
+ local startishead = start == head
+ local seq = ck[3]
+ local f = ck[4]
+ local l = ck[5]
+ local s = #seq
+ local done = false
+ local sweepnode = sweepnode
+ local sweeptype = sweeptype
+ local sweepoverflow = false
+ local checkdisc = getprev(head)
+ local keepdisc = not sweepnode
+ local lookaheaddisc = nil
+ local backtrackdisc = nil
+ local current = start
+ local last = start
+ local prev = getprev(start)
+ local hasglue = false
+
+ -- fishy: so we can overflow and then go on in the sweep?
+ -- todo : id can also be glue_code as we checked spaces
+
+ local i = f
+ while i <= l do
+ local id = getid(current)
+ if id == glyph_code then
+ i = i + 1
+ last = current
+ current = getnext(current)
+ elseif id == glue_code then
+ i = i + 1
+ last = current
+ current = getnext(current)
+ hasglue = true
+ elseif id == disc_code then
+ if keepdisc then
+ keepdisc = false
+ lookaheaddisc = current
+ local replace = getreplace(current)
+ if not replace then
+ sweepoverflow = true
+ sweepnode = current
+ current = getnext(current)
+ else
+ -- we can use an iterator
+ while replace and i <= l do
+ if getid(replace) == glyph_code then
+ i = i + 1
+ end
+ replace = getnext(replace)
+ end
+ current = getnext(replace)
+ end
+ last = current
+ else
+ head, current = flattendisk(head,current)
+ end
+ else
+ last = current
+ current = getnext(current)
+ end
+ if current then
+ -- go on
+ elseif sweepoverflow then
+ -- we already are following up on sweepnode
+ break
+ elseif sweeptype == "post" or sweeptype == "replace" then
+ current = getnext(sweepnode)
+ if current then
+ sweeptype = nil
+ sweepoverflow = true
+ else
+ break
+ end
+ else
+ break -- added
+ end
+ end
+
+ if sweepoverflow then
+ local prev = current and getprev(current)
+ if not current or prev ~= sweepnode then
+ local head = getnext(sweepnode)
+ local tail = nil
+ if prev then
+ tail = prev
+ setprev(current,sweepnode)
+ else
+ tail = find_node_tail(head)
+ end
+ setnext(sweepnode,current)
+ setprev(head)
+ setnext(tail)
+ appenddisc(sweepnode,head)
+ end
+ end
+
+ if l < s then
+ local i = l
+ local t = sweeptype == "post" or sweeptype == "replace"
+ while current and i < s do
+ local id = getid(current)
+ if id == glyph_code then
+ i = i + 1
+ current = getnext(current)
+ elseif id == glue_code then
+ i = i + 1
+ current = getnext(current)
+ hasglue = true
+ elseif id == disc_code then
+ if keepdisc then
+ keepdisc = false
+ if notmatchpre[current] ~= notmatchreplace[current] then
+ lookaheaddisc = current
+ end
+ -- we assume a simple text only replace (we could use nuts.count)
+ local replace = getreplace(current)
+ while replace and i < s do
+ if getid(replace) == glyph_code then
+ i = i + 1
+ end
+ replace = getnext(replace)
+ end
+ current = getnext(current)
+ elseif notmatchpre[current] ~= notmatchreplace[current] then
+ head, current = flattendisk(head,current)
+ else
+ current = getnext(current) -- HH
+ end
+ else
+ current = getnext(current)
+ end
+ if not current and t then
+ current = getnext(sweepnode)
+ if current then
+ sweeptype = nil
+ end
+ end
+ end
+ end
+
+ if f > 1 then
+ local current = prev
+ local i = f
+ local t = sweeptype == "pre" or sweeptype == "replace"
+ if not current and t and current == checkdisc then
+ current = getprev(sweepnode)
+ end
+ while current and i > 1 do -- missing getprev added / moved outside
+ local id = getid(current)
+ if id == glyph_code then
+ i = i - 1
+ elseif id == glue_code then
+ i = i - 1
+ hasglue = true
+ elseif id == disc_code then
+ if keepdisc then
+ keepdisc = false
+ if notmatchpost[current] ~= notmatchreplace[current] then
+ backtrackdisc = current
+ end
+ -- we assume a simple text only replace (we could use nuts.count)
+ local replace = getreplace(current)
+ while replace and i > 1 do
+ if getid(replace) == glyph_code then
+ i = i - 1
+ end
+ replace = getnext(replace)
+ end
+ elseif notmatchpost[current] ~= notmatchreplace[current] then
+ head, current = flattendisk(head,current)
+ end
+ end
+ current = getprev(current)
+ if t and current == checkdisc then
+ current = getprev(sweepnode)
+ end
+ end
+ end
+ local done = false
+
+ if lookaheaddisc then
+
+ local cf = start
+ local cl = getprev(lookaheaddisc)
+ local cprev = getprev(start)
+ local insertedmarks = 0
+
+ while cprev do
+ local nxt, char = isnextchar(cf,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char and marks[char] then
+ insertedmarks = insertedmarks + 1
+ cf = cprev
+ startishead = cf == head
+ cprev = getprev(cprev)
+ else
+ break
+ end
+ end
+ setlink(cprev,lookaheaddisc)
+ setprev(cf)
+ setnext(cl)
+ if startishead then
+ head = lookaheaddisc
+ end
+ local pre, post, replace = getdisc(lookaheaddisc)
+ local new = copy_node_list(cf) -- br, how often does that happen
+ local cnew = new
+ if pre then
+ setlink(find_node_tail(cf),pre)
+ end
+ if replace then
+ local tail = find_node_tail(new)
+ setlink(tail,replace)
+ end
+ for i=1,insertedmarks do
+ cnew = getnext(cnew)
+ end
+ cl = start
+ local clast = cnew
+ for i=f,l do
+ cl = getnext(cl)
+ clast = getnext(clast)
+ end
+ if not notmatchpre[lookaheaddisc] then
+ local ok = false
+ cf, start, ok = chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck)
+ if ok then
+ done = true
+ end
+ end
+ if not notmatchreplace[lookaheaddisc] then
+ local ok = false
+ new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck)
+ if ok then
+ done = true
+ end
+ end
+ if hasglue then
+ setdiscchecked(lookaheaddisc,cf,post,new)
+ else
+ setdisc(lookaheaddisc,cf,post,new)
+ end
+ start = getprev(lookaheaddisc)
+ sweephead[cf] = getnext(clast) or false
+ sweephead[new] = getnext(cl) or false
+
+ elseif backtrackdisc then
+
+ local cf = getnext(backtrackdisc)
+ local cl = start
+ local cnext = getnext(start)
+ local insertedmarks = 0
+
+ while cnext do
+ local nxt, char = isnextchar(cnext,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char and marks[char] then
+ insertedmarks = insertedmarks + 1
+ cl = cnext
+ cnext = nxt
+ else
+ break
+ end
+ end
+ setlink(backtrackdisc,cnext)
+ setprev(cf)
+ setnext(cl)
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(backtrackdisc,true)
+ local new = copy_node_list(cf)
+ local cnew = find_node_tail(new)
+ for i=1,insertedmarks do
+ cnew = getprev(cnew)
+ end
+ local clast = cnew
+ for i=f,l do
+ clast = getnext(clast)
+ end
+ if not notmatchpost[backtrackdisc] then
+ local ok = false
+ cf, start, ok = chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck)
+ if ok then
+ done = true
+ end
+ end
+ if not notmatchreplace[backtrackdisc] then
+ local ok = false
+ new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck)
+ if ok then
+ done = true
+ end
+ end
+ if post then
+ setlink(posttail,cf)
+ else
+ post = cf
+ end
+ if replace then
+ setlink(replacetail,new)
+ else
+ replace = new
+ end
+ if hasglue then
+ setdiscchecked(backtrackdisc,pre,post,replace)
+ else
+ setdisc(backtrackdisc,pre,post,replace)
+ end
+ start = getprev(backtrackdisc)
+ sweephead[post] = getnext(clast) or false
+ sweephead[replace] = getnext(last) or false
+
+ else
+
+ local ok = false
+ head, start, ok = chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)
+ if ok then
+ done = true
+ end
+
+ end
+
+ return head, start, done
+end
+
+local function chaintrac(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode)
+ local rule = ck[1]
+ local lookuptype = ck[8] or ck[2]
+ local nofseq = #ck[3]
+ local first = ck[4]
+ local last = ck[5]
+ local char = getchar(start)
+ logwarning("%s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping",
+ cref(dataset,sequence),rule,match and "matches" or "nomatch",
+ gref(char),first-1,last-first+1,nofseq-last,lookuptype,
+ discseen and "" or "no ", sweepnode and "" or "not ")
+end
+
+-- The next one is quite optimized but still somewhat slow, fonts like ebgaramond
+-- are real torture tests because they have many steps with one context (having
+-- multiple contexts makes more sense) also because we (can) reduce them. Instead of
+-- a match boolean variable and check for that I decided to use a goto with labels
+-- instead. This is one of the cases where it makes the code more readable and we
+-- might even gain a bit performance.
+
+-- when we have less replacements (lookups) then current matches we can push too much into
+-- the previous disc .. such be it (<before><disc><current=fl><after> with only f done)
+
+local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash)
+ -- optimizing for rlmode gains nothing
+ local sweepnode = sweepnode
+ local sweeptype = sweeptype
+ local postreplace
+ local prereplace
+ local checkdisc
+ local discseen -- = false
+ if sweeptype then
+ if sweeptype == "replace" then
+ postreplace = true
+ prereplace = true
+ else
+ postreplace = sweeptype == "post"
+ prereplace = sweeptype == "pre"
+ end
+ checkdisc = getprev(head)
+ end
+ -- local currentfont = currentfont -- really ?
+ -- local currentscale = currentscale -- really ?
+ -- local currentxscale = currentxscale -- really ?
+ -- local currentyscale = currentyscale -- really ?
+
+ local skipped -- = false
+
+ local startprev,
+ startnext = getboth(start)
+ local done -- = false
+
+ -- we can have multiple hits and as we scan (currently) all we need to check
+ -- if we have a match ... contextchains have no real coverage table (with
+ -- unique entries)
+
+ -- fonts can have many steps (each doing one check) or many contexts
+
+ -- todo: make a per-char cache so that we have small contexts (when we have a context
+ -- n == 1 and otherwise it can be more so we can even distingish n == 1 or more)
+
+ local nofcontexts = contexts.n -- #contexts
+
+ local startchar = nofcontext == 1 or ischar(start,currentfont) -- already checked
+ for k=1,nofcontexts do -- does this disc mess work well with n > 1
+
+ local ck = contexts[k]
+ local seq = ck[3]
+ local f = ck[4] -- first current
+ if not startchar or not seq[f][startchar] then
+ -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts)
+ goto next
+ end
+ local s = seq.n -- or #seq
+ local l = ck[5] -- last current
+ local current = start
+ local last = start
+
+ if s == 1 then
+ goto next
+ end
+
+ -- current match
+
+ if l > f then
+ -- before/current/after | before/current | current/after
+ local discfound -- = nil
+ local n = f + 1
+ last = startnext -- the second in current (first already matched)
+ while n <= l do
+ if postreplace and not last then
+ last = getnext(sweepnode)
+ sweeptype = nil
+ end
+ if last then
+ local nxt, char, id = isnextchar(last,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ if skiphash and skiphash[char] then
+ skipped = true
+ if trace_skips then
+ show_skip(dataset,sequence,char,ck,classes[char])
+ end
+ last = nxt
+ elseif seq[n][char] then
+ if n < l then
+ last = nxt
+ end
+ n = n + 1
+ elseif discfound then
+ notmatchreplace[discfound] = true
+ if notmatchpre[discfound] then
+ goto next
+ else
+ break
+ end
+ else
+ goto next
+ end
+ elseif char == false then
+ if discfound then
+ notmatchreplace[discfound] = true
+ if notmatchpre[discfound] then
+ goto next
+ else
+ break
+ end
+ else
+ goto next
+ end
+ elseif id == disc_code then
+ -- elseif id == disc_code and (not discs or discs[last]) then
+ discseen = true
+ discfound = last
+ notmatchpre[last] = nil
+ notmatchpost[last] = true
+ notmatchreplace[last] = nil
+ local pre, post, replace = getdisc(last)
+ if pre then
+ local n = n
+ while pre do
+ if seq[n][getchar(pre)] then
+ n = n + 1
+ if n > l then
+ break
+ end
+ pre = getnext(pre)
+ else
+ notmatchpre[last] = true
+ break
+ end
+ end
+ if n <= l then
+ notmatchpre[last] = true
+ end
+ else
+ notmatchpre[last] = true
+ end
+ if replace then
+ -- so far we never entered this branch
+ while replace do
+ if seq[n][getchar(replace)] then
+ n = n + 1
+ if n > l then
+ break
+ end
+ replace = getnext(replace)
+ else
+ notmatchreplace[last] = true
+ if notmatchpre[last] then
+ goto next
+ else
+ break
+ end
+ end
+ end
+ -- why here again
+ if notmatchpre[last] then
+ goto next
+ end
+ end
+ -- maybe only if match
+ last = nxt
+ else
+ goto next
+ end
+ else
+ goto next
+ end
+ end
+ end
+
+ -- before
+
+ if f > 1 then
+ if startprev then
+ local prev = startprev
+ if prereplace and prev == checkdisc then
+ prev = getprev(sweepnode)
+ end
+ if prev then
+ local discfound -- = nil
+ local n = f - 1
+ while n >= 1 do
+ if prev then
+ local prv, char, id = isprevchar(prev,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ if skiphash and skiphash[char] then
+ skipped = true
+ if trace_skips then
+ show_skip(dataset,sequence,char,ck,classes[char])
+ end
+ prev = prv
+ elseif seq[n][char] then
+ if n > 1 then
+ prev = prv
+ end
+ n = n - 1
+ elseif discfound then
+ notmatchreplace[discfound] = true
+ if notmatchpost[discfound] then
+ goto next
+ else
+ break
+ end
+ else
+ goto next
+ end
+ elseif char == false then
+ if discfound then
+ notmatchreplace[discfound] = true
+ if notmatchpost[discfound] then
+ goto next
+ end
+ else
+ goto next
+ end
+ break
+ elseif id == disc_code then
+ -- elseif id == disc_code and (not discs or discs[prev]) then
+ -- the special case: f i where i becomes dottless i ..
+ discseen = true
+ discfound = prev
+ notmatchpre[prev] = true
+ notmatchpost[prev] = nil
+ notmatchreplace[prev] = nil
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true)
+ -- weird test: needs checking
+ if pre ~= start and post ~= start and replace ~= start then
+ if post then
+ local n = n
+ while posttail do
+ if seq[n][getchar(posttail)] then
+ n = n - 1
+ if posttail == post or n < 1 then
+ break
+ else
+ posttail = getprev(posttail)
+ end
+ else
+ notmatchpost[prev] = true
+ break
+ end
+ end
+ if n >= 1 then
+ notmatchpost[prev] = true
+ end
+ else
+ notmatchpost[prev] = true
+ end
+ if replace then
+ -- we seldom enter this branch (e.g. on brill efficient)
+ while replacetail do
+ if seq[n][getchar(replacetail)] then
+ n = n - 1
+ if replacetail == replace or n < 1 then
+ break
+ else
+ replacetail = getprev(replacetail)
+ end
+ else
+ notmatchreplace[prev] = true
+ if notmatchpost[prev] then
+ goto next
+ else
+ break
+ end
+ end
+ end
+ else
+ -- notmatchreplace[prev] = true -- not according to Kai
+ end
+ end
+ -- prev = getprev(prev)
+ prev = prv
+ -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then
+ -- elseif seq[n][32] and spaces[prev] then
+ -- n = n - 1
+ -- prev = prv
+ elseif id == glue_code then
+ local sn = seq[n]
+ if (sn[32] and spaces[prev]) or sn[0xFFFC] then
+ n = n - 1
+ prev = prv
+ else
+ goto next
+ end
+ elseif seq[n][0xFFFC] then
+ n = n - 1
+ prev = prv
+ else
+ goto next
+ end
+ else
+ goto next
+ end
+ end
+ else
+ goto next
+ end
+ else
+ goto next
+ end
+ end
+
+ -- after
+
+ if s > l then
+ local current = last and getnext(last)
+ if not current and postreplace then
+ current = getnext(sweepnode)
+ end
+ if current then
+ local discfound -- = nil
+ local n = l + 1
+ while n <= s do
+ if current then
+ local nxt, char, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ if skiphash and skiphash[char] then
+ skipped = true
+ if trace_skips then
+ show_skip(dataset,sequence,char,ck,classes[char])
+ end
+ current = nxt -- was absent
+ elseif seq[n][char] then
+ if n < s then -- new test
+ current = nxt -- was absent
+ end
+ n = n + 1
+ elseif discfound then
+ notmatchreplace[discfound] = true
+ if notmatchpre[discfound] then
+ goto next
+ else
+ break
+ end
+ else
+ goto next
+ end
+ elseif char == false then
+ if discfound then
+ notmatchreplace[discfound] = true
+ if notmatchpre[discfound] then
+ goto next
+ else
+ break
+ end
+ else
+ goto next
+ end
+ elseif id == disc_code then
+ -- elseif id == disc_code and (not discs or discs[current]) then
+ discseen = true
+ discfound = current
+ notmatchpre[current] = nil
+ notmatchpost[current] = true
+ notmatchreplace[current] = nil
+ local pre, post, replace = getdisc(current)
+ -- no detailed checking here, todo ?
+ if pre then
+ local n = n
+ while pre do
+ if seq[n][getchar(pre)] then
+ n = n + 1
+ if n > s then
+ break
+ else
+ pre = getnext(pre)
+ end
+ else
+ notmatchpre[current] = true
+ break
+ end
+ end
+ if n <= s then
+ notmatchpre[current] = true
+ end
+ else
+ notmatchpre[current] = true
+ end
+ if replace then
+ -- so far we never entered this branch
+ while replace do
+ if seq[n][getchar(replace)] then
+ n = n + 1
+ if n > s then
+ break
+ else
+ replace = getnext(replace)
+ end
+ else
+ notmatchreplace[current] = true
+ if notmatchpre[current] then
+ goto next
+ else
+ break
+ end
+ end
+ end
+ else
+ -- notmatchreplace[current] = true -- not according to Kai
+ end
+ current = getnext(current)
+ elseif id == glue_code then
+ local sn = seq[n]
+ if (sn[32] and spaces[current]) or sn[0xFFFC] then
+ n = n + 1
+ current = nxt
+ else
+ goto next
+ end
+ elseif seq[n][0xFFFC] then
+ n = n + 1
+ current = nxt
+ else
+ goto next
+ end
+ else
+ goto next
+ end
+ end
+ else
+ goto next
+ end
+ end
+
+ if trace_contexts then
+ chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode)
+ end
+ if discseen or sweepnode then
+ head, start, done = chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck)
+ else
+ head, start, done = chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck)
+ end
+ if done then
+ break
+ -- else
+ -- next context
+ end
+ ::next::
+ -- if trace_chains then
+ -- chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,false,discseen,sweepnode)
+ -- end
+ end
+ if discseen then
+ notmatchpre = { }
+ notmatchpost = { }
+ notmatchreplace = { }
+ -- notmatchpre = { a = 1, b = 1 } notmatchpre .a = nil notmatchpre .b = nil
+ -- notmatchpost = { a = 1, b = 1 } notmatchpost .a = nil notmatchpost .b = nil
+ -- notmatchreplace = { a = 1, b = 1 } notmatchreplace.a = nil notmatchreplace.b = nil
+ end
+ return head, start, done
+end
+
+handlers.gsub_context = handle_contextchain
+handlers.gsub_contextchain = handle_contextchain
+handlers.gsub_reversecontextchain = handle_contextchain
+handlers.gpos_contextchain = handle_contextchain
+handlers.gpos_context = handle_contextchain
+
+-- this needs testing
+
+local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash)
+ local steps = currentlookup.steps
+ local nofsteps = currentlookup.nofsteps
+ if nofsteps > 1 then
+ reportmoresteps(dataset,sequence)
+ end
+ -- probably wrong
+ local l = steps[1].coverage[getchar(start)]
+ if l then
+ return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash)
+ else
+ return head, start, false
+ end
+end
+
+chainprocs.gsub_context = chained_contextchain
+chainprocs.gsub_contextchain = chained_contextchain
+chainprocs.gsub_reversecontextchain = chained_contextchain
+chainprocs.gpos_contextchain = chained_contextchain
+chainprocs.gpos_context = chained_contextchain
+
+------------------------------
+
+-- experiment (needs no handler in font-otc so not now):
+--
+-- function otf.registerchainproc(name,f)
+-- -- chainprocs[name] = f
+-- chainprocs[name] = function(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash)
+-- local done = currentlookup.nofsteps > 0
+-- if not done then
+-- reportzerosteps(dataset,sequence)
+-- else
+-- head, start, done = f(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash)
+-- if not head or not start then
+-- reportbadsteps(dataset,sequence)
+-- end
+-- end
+-- return head, start, done
+-- end
+-- end
+
+local missing = setmetatableindex("table")
+local logwarning = report_process
+local resolved = { } -- we only resolve a font,script,language pair once
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ if trace_steps == "silent" then
+ return
+ end
+ end
+ report_process(...)
+end
+
+-- todo: pass all these 'locals' in a table
+
+local sequencelists = setmetatableindex(function(t,font)
+ local sequences = fontdata[font].resources.sequences
+ if not sequences or not next(sequences) then
+ sequences = false
+ end
+ t[font] = sequences
+ return sequences
+end)
+
+-- fonts.hashes.sequences = sequencelists
+
+do -- overcome local limit
+
+ local autofeatures = fonts.analyzers.features
+ local featuretypes = otf.tables.featuretypes
+ local defaultscript = otf.features.checkeddefaultscript
+ local defaultlanguage = otf.features.checkeddefaultlanguage
+
+ local wildcard = "*"
+ local default = "dflt"
+
+ local function initialize(sequence,script,language,enabled,autoscript,autolanguage)
+ local features = sequence.features
+ if features then
+ local order = sequence.order
+ if order then
+ local featuretype = featuretypes[sequence.type or "unknown"]
+ for i=1,#order do
+ local kind = order[i]
+ local valid = enabled[kind]
+ if valid then
+ local scripts = features[kind]
+ local languages = scripts and (
+ scripts[script] or
+ scripts[wildcard] or
+ (autoscript and defaultscript(featuretype,autoscript,scripts))
+ )
+ local enabled = languages and (
+ languages[language] or
+ languages[wildcard] or
+ (autolanguage and defaultlanguage(featuretype,autolanguage,languages))
+ )
+ if enabled then
+ return { valid, autofeatures[kind] or false, sequence, kind }
+ end
+ end
+ end
+ else
+ -- can't happen
+ end
+ end
+ return false
+ end
+
+ function otf.dataset(tfmdata,font) -- generic variant, overloaded in context
+ local shared = tfmdata.shared
+ local properties = tfmdata.properties
+ local language = properties.language or "dflt"
+ local script = properties.script or "dflt"
+ local enabled = shared.features
+ local autoscript = enabled and enabled.autoscript
+ local autolanguage = enabled and enabled.autolanguage
+ local res = resolved[font]
+ if not res then
+ res = { }
+ resolved[font] = res
+ end
+ local rs = res[script]
+ if not rs then
+ rs = { }
+ res[script] = rs
+ end
+ local rl = rs[language]
+ if not rl then
+ rl = {
+ -- indexed but we can also add specific data by key
+ }
+ rs[language] = rl
+ local sequences = tfmdata.resources.sequences
+ if sequences then
+ for s=1,#sequences do
+ local v = enabled and initialize(sequences[s],script,language,enabled,autoscript,autolanguage)
+ if v then
+ rl[#rl+1] = v
+ end
+ end
+ end
+ end
+ return rl
+ end
+
+end
+
+-- Functions like kernrun, comprun etc evolved over time and in the end look rather
+-- complex. It's a bit of a compromis between extensive copying and creating subruns.
+-- The logic has been improved a lot by Kai and Ivo who use complex fonts which
+-- really helped to identify border cases on the one hand and get insight in the diverse
+-- ways fonts implement features (not always that consistent and efficient). At the same
+-- time I tried to keep the code relatively efficient so that the overhead in runtime
+-- stays acceptable.
+
+local function report_disc(what,n)
+ report_run("%s: %s > %s",what,n,languages.serializediscretionary(n))
+end
+
+local function kernrun(disc,k_run,...)
+ --
+ -- we catch <font 1><disc font 2>
+ --
+ if trace_kernruns then
+ report_disc("kern",disc)
+ end
+ --
+ local prev, next = getboth(disc)
+ --
+ local nextstart = next
+ local done = false
+ --
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+ --
+ local prevmarks = prev
+ --
+ -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp
+ -- has happened but then it should be in the disc so basically this test indicates an error)
+ --
+ while prevmarks do
+ local prv, char = isprevchar(prevmarks,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char and marks[char] then
+ prevmarks = prv
+ else
+ break
+ end
+ end
+ --
+ if prev and not ischar(prev,currentfont) then -- and (pre or replace) -- weak test
+ prev = false
+ end
+ if next and not ischar(next,currentfont) then -- and (post or replace) -- weak test
+ next = false
+ end
+ --
+ -- we need to get rid of this nest mess some day .. has to be done otherwise
+ --
+ if pre then
+ if k_run(pre,"injections",nil,...) then
+ done = true
+ end
+ if prev then
+ setlink(prev,pre)
+ if k_run(prevmarks,"preinjections",pre,...) then -- or prev?
+ done = true
+ end
+ setprev(pre)
+ setlink(prev,disc)
+ end
+ end
+ --
+ if post then
+ if k_run(post,"injections",nil,...) then
+ done = true
+ end
+ if next then
+ setlink(posttail,next)
+ if k_run(posttail,"postinjections",next,...) then
+ done = true
+ end
+ setnext(posttail)
+ setlink(disc,next)
+ end
+ end
+ --
+ if replace then
+ if k_run(replace,"injections",nil,...) then
+ done = true
+ end
+ if prev then
+ setlink(prev,replace)
+ if k_run(prevmarks,"replaceinjections",replace,...) then -- getnext(replace))
+ done = true
+ end
+ setprev(replace)
+ setlink(prev,disc)
+ end
+ if next then
+ setlink(replacetail,next)
+ if k_run(replacetail,"replaceinjections",next,...) then
+ done = true
+ end
+ setnext(replacetail)
+ setlink(disc,next)
+ end
+ elseif prev and next then
+ setlink(prev,next)
+ if k_run(prevmarks,"emptyinjections",next,...) then
+ done = true
+ end
+ setlink(prev,disc,next)
+ end
+ if done and trace_testruns then
+ report_disc("done",disc)
+ end
+ return nextstart, done
+end
+
+-- fonts like ebgaramond do ligatures this way (less efficient than e.g. dejavu which
+-- will do the testrun variant)
+
+local function comprun(disc,c_run,...) -- vararg faster than the whole list
+ if trace_compruns then
+ report_disc("comp",disc)
+ end
+ --
+ local pre, post, replace = getdisc(disc)
+ local renewed = false
+ --
+ if pre then
+ sweepnode = disc
+ sweeptype = "pre" -- in alternative code preinjections is used (also used then for properties, saves a variable)
+ local new, done = c_run(pre,...)
+ if done then
+ pre = new
+ renewed = true
+ end
+ end
+ --
+ if post then
+ sweepnode = disc
+ sweeptype = "post"
+ local new, done = c_run(post,...)
+ if done then
+ post = new
+ renewed = true
+ end
+ end
+ --
+ if replace then
+ sweepnode = disc
+ sweeptype = "replace"
+ local new, done = c_run(replace,...)
+ if done then
+ replace = new
+ renewed = true
+ end
+ end
+ --
+ sweepnode = nil
+ sweeptype = nil
+ if renewed then
+ if trace_testruns then
+ report_disc("done",disc)
+ end
+ setdisc(disc,pre,post,replace)
+ end
+ --
+ return getnext(disc), renewed
+end
+
+-- if we can hyphenate in a lig then unlikely a lig so we
+-- could have a option here to ignore lig
+
+local test_flatten_start = 2 -- must start at 2 according to Kai
+
+directives.register("otf.testrun.forceflatten", function(v)
+ test_flatten_start = v and 1 or 2
+end)
+
+local function testrun(disc,t_run,c_run,...)
+ if trace_testruns then
+ report_disc("test",disc)
+ end
+ local prev, next = getboth(disc)
+ if not next then
+ -- weird discretionary
+ return
+ end
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+ local renewed = false
+ if post or replace then -- and prev then -- hm, we can start with a disc
+ if post then
+ setlink(posttail,next)
+ else
+ post = next
+ end
+ if replace then
+ setlink(replacetail,next)
+ else
+ replace = next
+ end
+ local d_post = t_run(post,next,...)
+ local d_replace = t_run(replace,next,...)
+ if d_post > 0 or d_replace > 0 then
+ local d = d_replace > d_post and d_replace or d_post
+ local head = getnext(disc) -- is: next
+ local tail = head
+ for i=test_flatten_start,d do
+ local nx = getnext(tail)
+ local id = getid(nx)
+ if id == disc_code then
+ head, tail = flattendisk(head,nx)
+ elseif id == glyph_code then
+ tail = nx
+ else
+ -- we can have overrun into a glue
+ break
+ end
+ end
+ next = getnext(tail)
+ setnext(tail)
+ setprev(head)
+ local new = copy_node_list(head)
+ if posttail then
+ setlink(posttail,head)
+ else
+ post = head
+ end
+ if replacetail then
+ setlink(replacetail,new)
+ else
+ replace = new
+ end
+ else
+ -- we stay inside the disc
+ if posttail then
+ setnext(posttail)
+ else
+ post = nil
+ end
+ if replacetail then
+ setnext(replacetail)
+ else
+ replace = nil
+ end
+ end
+ setlink(disc,next)
+ -- pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+ end
+ --
+ -- like comprun
+ --
+ if trace_testruns then
+ report_disc("more",disc)
+ end
+ --
+ if pre then
+ sweepnode = disc
+ sweeptype = "pre"
+ local new, ok = c_run(pre,...)
+ if ok then
+ pre = new
+ renewed = true
+ end
+ end
+ --
+ if post then
+ sweepnode = disc
+ sweeptype = "post"
+ local new, ok = c_run(post,...)
+ if ok then
+ post = new
+ renewed = true
+ end
+ end
+ --
+ if replace then
+ sweepnode = disc
+ sweeptype = "replace"
+ local new, ok = c_run(replace,...)
+ if ok then
+ replace = new
+ renewed = true
+ end
+ end
+ --
+ sweepnode = nil
+ sweeptype = nil
+ if renewed then
+ setdisc(disc,pre,post,replace)
+ if trace_testruns then
+ report_disc("done",disc)
+ end
+ end
+ -- next can have changed (copied list)
+ return getnext(disc), renewed
+end
+
+-- 1{2{\oldstyle\discretionary{3}{4}{5}}6}7\par
+-- 1{2\discretionary{3{\oldstyle3}}{{\oldstyle4}4}{5{\oldstyle5}5}6}7\par
+
+local nesting = 0
+
+local function c_run_single(head,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+ local done = false
+ local sweep = sweephead[head]
+ local start
+ if sweep then
+ start = sweep
+ -- sweephead[head] = nil
+ sweephead[head] = false
+ else
+ start = head
+ end
+ while start do
+ local nxt, char, id = isnextchar(start,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
+ if ok then
+ done = true
+ end
+ if start then
+ start = getnext(start)
+ end
+ else
+ start = nxt
+ end
+ elseif char == false then
+ return head, done
+ elseif sweep then
+ -- else we loose the rest
+ return head, done
+ else
+ -- in disc component ... really?
+ start = nxt
+ end
+ end
+ return head, done
+end
+
+local function t_run_single(start,stop,lookupcache)
+ local lastd = nil
+ while start ~= stop do
+ local nxt, char = isnextchar(start,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check
+ -- if we need more than ligatures we can outline the code and use functions
+ local s = nxt
+ local ss = nil
+ local sstop = s == stop
+ if not s then
+ s = ss
+ ss = nil
+ end
+ -- a bit weird: why multiple ... anyway we can't have a disc in a disc
+ -- how about post ... we can probably merge this into the while
+ while getid(s) == disc_code do
+ ss = getnext(s)
+ s = getreplace(s)
+ if not s then
+ s = ss
+ ss = nil
+ end
+ end
+ local l = nil
+ local d = 0
+ while s do
+ local nxt, char = isnextchar(s,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ local lg = lookupmatch[char]
+ if lg then
+ if sstop then
+ d = 1
+ elseif d > 0 then
+ d = d + 1
+ end
+ l = lg
+ s = nxt
+ sstop = s == stop
+ if not s then
+ s = ss
+ ss = nil
+ end
+ while getid(s) == disc_code do
+ ss = getnext(s)
+ s = getreplace(s)
+ if not s then
+ s = ss
+ ss = nil
+ end
+ end
+ lookupmatch = lg
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if l and l.ligature then -- so we test for ligature
+ lastd = d
+ end
+ end
+ if lastd then
+ return lastd
+ else
+ start = nxt
+ end
+ else
+ break
+ end
+ end
+ return 0
+end
+
+local function k_run_single(sub,injection,last,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+ for n in nextnode, sub do -- only gpos
+ if n == last then
+ break
+ else
+ local nxt, char = isnextchar(n,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection)
+ if ok then
+ return true
+ end
+ end
+ end
+ end
+ end
+end
+
+local function c_run_multiple(head,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+ local done = false
+ local sweep = sweephead[head]
+ local start
+ if sweep then
+ start = sweep
+ -- sweephead[head] = nil
+ sweephead[head] = false
+ else
+ start = head
+ end
+ while start do
+ local nxt, char = isnextchar(start,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ for i=1,nofsteps do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
+ if ok then
+ done = true
+ break
+ elseif not start then
+ -- don't ask why ... shouldn't happen
+ break
+ end
+ end
+ end
+ if start then
+ start = getnext(start)
+ end
+ elseif char == false then
+ -- whatever glyph
+ return head, done
+ elseif sweep then
+ -- else we loose the rest
+ return head, done
+ else
+ -- in disc component
+ start = nxt
+ end
+ end
+ return head, done
+end
+
+local function t_run_multiple(start,stop,steps,nofsteps)
+ local lastd = nil
+ while start ~= stop do
+ local startnext, char = isnextchar(start,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ for i=1,nofsteps do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- if we need more than ligatures we can outline the code and use functions
+ local s = startnext
+ local ss = nil
+ local sstop = s == stop
+ if not s then
+ s = ss
+ ss = nil
+ end
+ while getid(s) == disc_code do
+ ss = getnext(s)
+ s = getreplace(s)
+ if not s then
+ s = ss
+ ss = nil
+ end
+ end
+ local l = nil
+ local d = 0
+ while s do
+ local nxt, char = isnextchar(s,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ local lg = lookupmatch[char]
+ if lg then
+ if sstop then
+ d = 1
+ elseif d > 0 then
+ d = d + 1
+ end
+ l = lg
+ s = nxt
+ sstop = s == stop
+ if not s then
+ s = ss
+ ss = nil
+ end
+ while getid(s) == disc_code do
+ ss = getnext(s)
+ s = getreplace(s)
+ if not s then
+ s = ss
+ ss = nil
+ end
+ end
+ lookupmatch = lg
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ lastd = d
+ end
+ end
+ end
+ if lastd then
+ return lastd
+ else
+ start = startnext
+ end
+ else
+ break
+ end
+ end
+ return 0
+end
+
+local function k_run_multiple(sub,injection,last,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+ for n in nextnode, sub do -- only gpos
+ if n == last then
+ break
+ else
+ local nxt, char = isnextchar(n,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)
+ if char then
+ for i=1,nofsteps do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) -- sub was head
+ if ok then
+ return true
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local txtdirstate, pardirstate do -- this might change (no need for nxt in pardirstate)
+
+ local getdirection = nuts.getdirection
+
+ txtdirstate = function(start,stack,top,rlparmode)
+ local dir, pop = getdirection(start)
+ if pop then
+ if top == 1 then
+ return 0, rlparmode
+ else
+ top = top - 1
+ if stack[top] == righttoleft_code then
+ return top, -1
+ else
+ return top, 1
+ end
+ end
+ elseif dir == lefttoright_code then
+ top = top + 1
+ stack[top] = lefttoright_code
+ return top, 1
+ elseif dir == righttoleft_code then
+ top = top + 1
+ stack[top] = righttoleft_code
+ return top, -1
+ else
+ return top, rlparmode
+ end
+ end
+
+ pardirstate = function(start)
+ local dir = getdirection(start)
+ if dir == lefttoright_code then
+ return 1, 1
+ elseif dir == righttoleft_code then
+ return -1, -1
+ else
+ return 0, 0
+ end
+ end
+
+end
+
+-- These are non public helpers that can change without notice!
+
+otf.helpers = otf.helpers or { }
+otf.helpers.txtdirstate = txtdirstate
+otf.helpers.pardirstate = pardirstate
+
+-- This is the main loop. We run over the node list dealing with a specific font. The
+-- dynamic is a context specific thing. We could work on sub start-stop ranges instead
+-- but I wonder if there is that much speed gain (experiments showed that it made not
+-- much sense) and we need to keep track of directions anyway. Also at some point I
+-- want to play with font interactions and then we do need the full sweeps. Apart from
+-- optimizations the principles of processing the features hasn't changed much since
+-- the beginning.
+
+do
+
+ -- This is a measurable experimental speedup (only with hyphenated text and multiple
+ -- fonts per processor call), especially for fonts with lots of contextual lookups.
+
+ local fastdisc = true
+ local testdics = false
+
+ directives.register("otf.fastdisc",function(v) fastdisc = v end) -- normally enabled
+
+ -- using a merged combined hash as first test saves some 30% on ebgaramond and
+ -- about 15% on arabtype .. then moving the a test also saves a bit (even when
+ -- often a is not set at all so that one is a bit debatable
+
+ local otfdataset = nil -- todo: make an installer
+
+ local getfastdisc = { __index = function(t,k)
+ local v = usesfont(k,currentfont)
+ t[k] = v
+ return v
+ end }
+
+ local getfastspace = { __index = function(t,k)
+ -- we don't pass the id so that one can overload isspace
+ local v = isspace(k,threshold) or false
+ t[k] = v
+ return v
+ end }
+
+ function otf.featuresprocessor(head,font,dynamic,direction,n)
+
+ local sequences = sequencelists[font] -- temp hack
+
+ nesting = nesting + 1
+
+ if nesting == 1 then
+ currentfont = font
+ currentdynamic = dynamic
+ currentscale = false
+ currentxscale = false
+ currentyscale = false
+ tfmdata = fontdata[font]
+ descriptions = tfmdata.descriptions -- only needed in gref so we could pass node there instead
+ characters = tfmdata.characters -- but this branch is not entered that often anyway
+ local resources = tfmdata.resources
+ marks = resources.marks
+ classes = resources.classes
+ threshold,
+ factor = getthreshold(font)
+ checkmarks = tfmdata.properties.checkmarks
+
+ if not otfdataset then
+ otfdataset = otf.dataset
+ end
+
+ discs = fastdisc and n and n > 1 and setmetatable({},getfastdisc) -- maybe inline
+ spaces = setmetatable({},getfastspace)
+
+ elseif currentfont ~= font then
+
+ report_warning("nested call with a different font, level %s, quitting",nesting)
+ nesting = nesting - 1
+ return head, false
+
+ end
+
+ if trace_steps then
+ checkstep(head)
+ end
+
+ local initialrl = 0
+
+ if getid(head) == par_code and start_of_par(head) then
+ initialrl = pardirstate(head)
+ elseif direction == righttoleft_code then
+ initialrl = -1
+ end
+
+ local datasets = otfdataset(tfmdata,font,dynamic)
+ local dirstack = { nil } -- could move outside function but we can have local runs
+ sweephead = { }
+ -- sweephead = { a = 1, b = 1 } sweephead.a = nil sweephead.b = nil
+
+ -- Keeping track of the headnode is needed for devanagari. (I generalized it a bit
+ -- so that multiple cases are also covered.) We could prepend a temp node.
+
+ -- We don't goto the next node when a disc node is created so that we can then treat
+ -- the pre, post and replace. It's a bit of a hack but works out ok for most cases.
+
+ for s=1,#datasets do
+ local dataset = datasets[s]
+ local state = dataset[2]
+ local sequence = dataset[3] -- sequences[s] -- also dataset[5]
+ local rlparmode = initialrl
+ local topstack = 0
+ local typ = sequence.type
+ local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset
+ local forcetestrun = typ == "gsub_ligature" -- testrun is only for ligatures
+ local handler = handlers[typ] -- store in dataset
+ local steps = sequence.steps
+ local nofsteps = sequence.nofsteps
+ local skiphash = sequence.skiphash
+
+ if not steps then
+ -- This permits injection, watch the different arguments. Watch out, the arguments passed
+ -- are not frozen as we might extend or change this. Is this used at all apart from some
+ -- experiments?
+ local h, ok = handler(head,dataset,sequence,initialrl,font,dynamic) -- less arguments now
+ if h and h ~= head then
+ head = h
+ end
+ elseif typ == "gsub_reversecontextchain" then
+ --
+ -- This might need a check: if we have #before or #after > 0 then we might need to reverse
+ -- the before and after lists in the loader. But first I need to see a font that uses multiple
+ -- matches.
+ --
+ local start = find_node_tail(head)
+ local rlmode = 0 -- how important is this .. do we need to check for dir?
+ local merged = steps.merged
+ while start do
+ local prv, char, id = isprevchar(start,font,dynamic,state)
+ if char then
+ local m = merged[char]
+ if m then
+ currentscale, currentxscale, currentyscale = getscales(start)
+ for i=m[1],m[2] do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
+ if ok then
+ -- done = true
+ break
+ end
+ end
+ end
+ if start then
+ start = getprev(start)
+ end
+ else
+ start = prv
+ end
+ else
+ start = prv
+ end
+ end
+ else
+ local start = head
+ local rlmode = initialrl
+ if nofsteps == 1 then -- happens often
+ local step = steps[1]
+ local lookupcache = step.coverage
+ while start do
+ local nxt, char, id = isnextchar(start,font,dynamic,state) -- we can move the data/state checks here
+ if char then
+ if skiphash and skiphash[char] then -- we never needed it here but let's try
+ start = nxt
+ else
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ currentscale, currentxscale, currentyscale = getscales(start)
+ local ok, df
+ head, start, ok, df = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
+ if df then
+ -- print("restart 1",typ)
+ elseif start then
+ start = getnext(start) -- can be a new start
+ end
+ else
+ start = nxt
+ end
+ end
+ elseif char == false or id == glue_code then
+ -- a different font|state or glue (happens often)
+ start = nxt
+ elseif id == disc_code then
+ if not discs or discs[start] == true then
+ local ok
+ if gpossing then
+ start, ok = kernrun(start,k_run_single, lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+ elseif forcetestrun then
+ start, ok = testrun(start,t_run_single,c_run_single,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+ else
+ start, ok = comprun(start,c_run_single, lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+ end
+ -- if ok then
+ -- done = true
+ -- end
+ else
+ start = nxt
+ end
+ elseif id == math_code then
+ start = getnext(end_of_math(start))
+ elseif id == dir_code then
+ topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode)
+ start = nxt
+ -- elseif id == par_code and start_of_par(start) then
+ -- rlparmode, rlmode = pardirstate(start)
+ -- start = nxt
+ else
+ start = nxt
+ end
+ end
+ else
+ local merged = steps.merged
+ while start do
+ local nxt, char, id = isnextchar(start,font,dynamic,state)
+ if char then
+ if skiphash and skiphash[char] then -- we never needed it here but let's try
+ start = nxt
+ else
+ local m = merged[char]
+ if m then
+ currentscale, currentxscale, currentyscale = getscales(start)
+ local ok, df
+ for i=m[1],m[2] do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+-- local ok, df
+ head, start, ok, df = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
+ if df then
+ break
+ elseif ok then
+ break
+ elseif not start then
+ -- don't ask why ... shouldn't happen
+ break
+ end
+ end
+ end
+ if df then
+ -- print("restart 2",typ)
+ elseif start then
+ start = getnext(start) -- can be a new next
+ end
+ else
+ start = nxt
+ end
+ end
+ elseif char == false or id == glue_code then
+ -- a different font|state or glue (happens often)
+ start = nxt
+ elseif id == disc_code then
+ if not discs or discs[start] == true then
+ local ok
+ if gpossing then
+ start, ok = kernrun(start,k_run_multiple, steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+ elseif forcetestrun then
+ start, ok = testrun(start,t_run_multiple,c_run_multiple,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+ else
+ start, ok = comprun(start,c_run_multiple, steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+ end
+ else
+ start = nxt
+ end
+ elseif id == math_code then
+ start = getnext(end_of_math(start))
+ elseif id == dir_code then
+ topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode)
+ start = nxt
+ -- elseif id == par_code and start_of_par(start) then
+ -- rlparmode, rlmode = pardirstate(start)
+ -- start = nxt
+ else
+ start = nxt
+ end
+ end
+ end
+ end
+
+ if trace_steps then -- ?
+ registerstep(head)
+ end
+
+ end
+
+ nesting = nesting - 1
+
+ return head
+ end
+
+ -- This is not an official helper and used for tracing experiments. It can be changed as I like
+ -- at any moment. At some point it might be used in a module that can help font development.
+
+ function otf.datasetpositionprocessor(head,font,direction,dataset)
+
+ currentfont = font
+ currentdynamic = false
+ currentscale = false
+ currentxscale = false
+ currentyscale = false
+ tfmdata = fontdata[font]
+ descriptions = tfmdata.descriptions -- only needed in gref so we could pass node there instead
+ characters = tfmdata.characters -- but this branch is not entered that often anyway
+ local resources = tfmdata.resources
+ marks = resources.marks
+ classes = resources.classes
+ threshold,
+ factor = getthreshold(font)
+ checkmarks = tfmdata.properties.checkmarks
+
+ if type(dataset) == "number" then
+ dataset = otfdataset(tfmdata,font,0)[dataset]
+ end
+
+ local sequence = dataset[3] -- sequences[s] -- also dataset[5]
+ local typ = sequence.type
+ -- local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset
+
+ -- gpos_contextchain gpos_context
+
+ -- if not gpossing then
+ -- return head, false
+ -- end
+
+ local handler = handlers[typ] -- store in dataset
+ local steps = sequence.steps
+ local nofsteps = sequence.nofsteps
+
+ local done = false
+ local dirstack = { nil } -- could move outside function but we can have local runs (maybe a few more nils)
+ local start = head
+ local initialrl = (direction == righttoleft_code) and -1 or 0
+ local rlmode = initialrl
+ local rlparmode = initialrl
+ local topstack = 0
+ local merged = steps.merged
+
+ -- local matches = false
+ local position = 0
+
+ while start do
+ local nxt, char, id = isnextchar(start,currentfont,currentdynamic)
+ if char then
+ position = position + 1
+ local m = merged[char]
+ if m then
+ if skiphash and skiphash[char] then -- we never needed it here but let's try
+ start = nxt
+ else
+ for i=m[1],m[2] do
+ local step = steps[i]
+ local lookupcache = step.coverage
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ currentscale, currentxscale, currentyscale = getscales(start)
+ local ok
+ head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
+ if ok then
+ -- if matches then
+ -- matches[position] = i
+ -- else
+ -- matches = { [position] = i }
+ -- end
+ break
+ elseif not start then
+ break
+ end
+ end
+ end
+ if start then
+ start = getnext(start) -- start can be new
+ end
+ end
+ else
+ -- start = getnext(start)
+ start = nxt
+ end
+ elseif char == false or id == glue_code then
+ -- a different font|state or glue (happens often)
+ start = nxt
+ elseif id == math_code then
+ start = getnext(end_of_math(start))
+ elseif id == dir_code then
+ topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode)
+ start = nxt
+ -- elseif id == par_code and start_of_par(start) then
+ -- rlparmode, rlmode = pardirstate(start)
+ -- start = nxt
+ else
+ start = nxt
+ end
+ end
+
+ return head
+ end
+
+ -- end of experiment
+
+end
+
+-- so far
+
+local plugins = { }
+otf.plugins = plugins
+
+local report = logs.reporter("fonts")
+
+function otf.registerplugin(name,f)
+ if type(name) == "string" and type(f) == "function" then
+ plugins[name] = { name, f }
+ report()
+ report("plugin %a has been loaded, please be aware of possible side effects",name)
+ report()
+ if logs.pushtarget then
+ logs.pushtarget("log")
+ end
+ report("Plugins are not officially supported unless stated otherwise. This is because")
+ report("they bypass the regular font handling and therefore some features in ConTeXt")
+ report("(especially those related to fonts) might not work as expected or might not work")
+ report("at all. Some plugins are for testing and development only and might change")
+ report("whenever we feel the need for it.")
+ report()
+ if logs.poptarget then
+ logs.poptarget()
+ end
+ end
+end
+
+function otf.plugininitializer(tfmdata,value)
+ if type(value) == "string" then
+ tfmdata.shared.plugin = plugins[value]
+ end
+end
+
+function otf.pluginprocessor(head,font,dynamic,direction) -- n
+ local s = fontdata[font].shared
+ local p = s and s.plugin
+ if p then
+ if trace_plugins then
+ report_process("applying plugin %a",p[1])
+ end
+ return p[2](head,font,dynamic,direction)
+ else
+ return head, false
+ end
+end
+
+function otf.featuresinitializer(tfmdata,value)
+ -- nothing done here any more
+end
+
+registerotffeature {
+ name = "features",
+ description = "features",
+ default = true,
+ initializers = {
+ position = 1,
+ node = otf.featuresinitializer,
+ plug = otf.plugininitializer,
+ },
+ processors = {
+ node = otf.featuresprocessor,
+ plug = otf.pluginprocessor,
+ }
+}
+
+-- Moved here (up) a bit. This doesn't really belong in generic so it will
+-- move to a context module some day.
+
+local function markinitializer(tfmdata,value)
+ local properties = tfmdata.properties
+ properties.checkmarks = value
+end
+
+registerotffeature {
+ name = "checkmarks",
+ description = "check mark widths",
+ default = true,
+ initializers = {
+ node = markinitializer,
+ },
+}
+
+-- This can be used for extra handlers, but should be used with care! We implement one
+-- here but some more can be found in the osd (script devanagary) file. Now watch out:
+-- when a handler has steps, it is called as the other ones, but when we have no steps,
+-- we use a different call:
+--
+-- function(head,dataset,sequence,initialrl,font,dynamic)
+-- return head, done
+-- end
+--
+-- Also see (!!).
+
+otf.handlers = handlers
+
+if context then
+ return
+else
+ -- todo: move the following code someplace else
+end
+
+local setspacekerns = nodes.injections.setspacekerns if not setspacekerns then os.exit() end
+
+local tag = "kern"
+
+-- if fontfeatures then
+
+-- function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,dynamic)
+-- local features = fontfeatures[font]
+-- local enabled = features and features.spacekern and features[tag]
+-- if enabled then
+-- setspacekerns(font,sequence)
+-- end
+-- return head, enabled
+-- end
+
+-- else -- generic (no hashes)
+
+ function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,dynamic)
+ local shared = fontdata[font].shared
+ local features = shared and shared.features
+ local enabled = features and features.spacekern and features[tag]
+ if enabled then
+ setspacekerns(font,sequence)
+ end
+ return head, enabled
+ end
+
+-- end
+
+-- There are fonts out there that change the space but we don't do that kind of
+-- things in TeX.
+
+local function hasspacekerns(data)
+ local resources = data.resources
+ local sequences = resources.sequences
+ local validgpos = resources.features.gpos
+ if validgpos and sequences then
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ local steps = sequence.steps
+ if steps and sequence.features[tag] then
+ local kind = sequence.type
+ if kind == "gpos_pair" or kind == "gpos_single" then
+ for i=1,#steps do
+ local step = steps[i]
+ local coverage = step.coverage
+ local rules = step.rules
+ if rules then
+ -- not now: analyze (simple) rules
+ elseif not coverage then
+ -- nothing to do
+ elseif kind == "gpos_single" then
+ -- maybe a message that we ignore
+ elseif kind == "gpos_pair" then
+ local format = step.format
+ if format == "move" or format == "kern" then
+ local kerns = coverage[32]
+ if kerns then
+ return true
+ end
+ for k, v in next, coverage do
+ if v[32] then
+ return true
+ end
+ end
+ elseif format == "pair" then
+ local kerns = coverage[32]
+ if kerns then
+ for k, v in next, kerns do
+ local one = v[1]
+ if one and one ~= true then
+ return true
+ end
+ end
+ end
+ for k, v in next, coverage do
+ local kern = v[32]
+ if kern then
+ local one = kern[1]
+ if one and one ~= true then
+ return true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return false
+end
+
+otf.readers.registerextender {
+ name = "spacekerns",
+ action = function(data)
+ data.properties.hasspacekerns = hasspacekerns(data)
+ end
+}
+
+local function spaceinitializer(tfmdata,value) -- dynamic
+ local resources = tfmdata.resources
+ local spacekerns = resources and resources.spacekerns
+ if value and spacekerns == nil then
+ local rawdata = tfmdata.shared and tfmdata.shared.rawdata
+ local properties = rawdata.properties
+ if properties and properties.hasspacekerns then
+ local sequences = resources.sequences
+ local validgpos = resources.features.gpos
+ if validgpos and sequences then
+ local left = { }
+ local right = { }
+ local last = 0
+ local feat = nil
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ local steps = sequence.steps
+ if steps then
+ -- we don't support space kerns in other features
+ local kern = sequence.features[tag]
+ if kern then
+ local kind = sequence.type
+ if kind == "gpos_pair" or kind == "gpos_single" then
+ if feat then
+ for script, languages in next, kern do
+ local f = feat[script]
+ if f then
+ for l in next, languages do
+ f[l] = true
+ end
+ else
+ feat[script] = languages
+ end
+ end
+ else
+ feat = kern
+ end
+ for i=1,#steps do
+ local step = steps[i]
+ local coverage = step.coverage
+ local rules = step.rules
+ if rules then
+ -- not now: analyze (simple) rules
+ elseif not coverage then
+ -- nothing to do
+ elseif kind == "gpos_single" then
+ -- makes no sense in TeX
+ elseif kind == "gpos_pair" then
+ local format = step.format
+ if format == "move" or format == "kern" then
+ local kerns = coverage[32]
+ if kerns then
+ for k, v in next, kerns do
+ right[k] = v
+ end
+ end
+ for k, v in next, coverage do
+ local kern = v[32]
+ if kern then
+ left[k] = kern
+ end
+ end
+ elseif format == "pair" then
+ local kerns = coverage[32]
+ if kerns then
+ for k, v in next, kerns do
+ local one = v[1]
+ if one and one ~= true then
+ right[k] = one[3]
+ end
+ end
+ end
+ for k, v in next, coverage do
+ local kern = v[32]
+ if kern then
+ local one = kern[1]
+ if one and one ~= true then
+ left[k] = one[3]
+ end
+ end
+ end
+ end
+ end
+ end
+ last = i
+ end
+ else
+ -- no steps ... needed for old one ... we could use the basekerns
+ -- instead
+ end
+ end
+ end
+ left = next(left) and left or false
+ right = next(right) and right or false
+ if left or right then
+ spacekerns = {
+ left = left,
+ right = right,
+ }
+ if last > 0 then
+ local triggersequence = {
+ -- no steps, see (!!)
+ features = { [tag] = feat or { dflt = { dflt = true, } } },
+ flags = noflags,
+ name = "trigger_space_kerns",
+ order = { tag },
+ type = "trigger_space_kerns",
+ left = left,
+ right = right,
+ }
+ insert(sequences,last,triggersequence)
+ end
+ end
+ end
+ end
+ resources.spacekerns = spacekerns
+ end
+ return spacekerns
+end
+
+registerotffeature {
+ name = "spacekern",
+ description = "space kern injection",
+ default = true,
+ initializers = {
+ node = spaceinitializer,
+ },
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-pre.mkxl b/Master/texmf-dist/tex/context/base/mkxl/font-pre.mkxl
new file mode 100644
index 00000000000..e6e6930e1a8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-pre.mkxl
@@ -0,0 +1,1025 @@
+%D \module
+%D [ file=font-pre,
+%D version=2012.01.04, % moved from font-ini
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Predefined,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Predefined}
+
+\unprotect
+
+%D A basic set of features is defined here.
+
+% beware, base mode + dynamics can give weird effects
+
+% frac : with numr dnom
+%
+% vkrn valt vert vrt2 vpal : when vertical
+%
+% rtlm rtla : in r2l runs
+% ltrm ltra : in l2r runs
+%
+% rvrn : variable fonts
+%
+% rtbd lfbd : opbd
+%
+% rkrf rphf vatu vjmo tjmo rclt psts pstf ljmo haln
+% pres pref nukt
+% abvs abvm blwm blws cjct blwf akhn (indic)
+% half
+% abvf cfar (khmer)
+%
+% ccmp locl calt clig liga rlig
+%
+% mkmk mark kern (palt pwid) curs (by choice but some fonts need it)
+%
+% init medi isol fina (unicode)
+% fin3 fin2 med2 : syriac
+%
+% cpsp : percentage spacing (todo)
+%
+% dtls flac :math
+
+\definefontfeature
+ [always]
+ [mode=node, % we had 'auto', but let's try 'node' for a while and see what the impact is
+ script=auto, % on speed; 'base' just doesn't play well with dynamics; some day we can even
+ autoscript=position,
+ autolanguage=position,
+ kern=yes, % consider skipping the base passes when no base mode is used
+ mark=yes,
+ mkmk=yes,
+ curs=yes]
+
+\definefontfeature
+ [default]
+ [always]
+ [liga=yes,
+% ccmp=yes, % maybe too
+% locl=yes, % maybe too
+% calt=yes, % maybe too
+% clig=yes, % maybe too
+% rlig=yes, % maybe too
+ tlig=yes,
+ trep=yes] % texligatures=yes,texquotes=yes
+
+\definefontfeature
+ [original] % a clone of default so we can revert
+ [default]
+
+\definefontfeature
+ [smallcaps]
+ [always]
+ [smcp=yes,
+ tlig=yes,
+ trep=yes] % texligatures=yes,texquotes=yes
+
+\definefontfeature
+ [oldstyle]
+ [always]
+ [onum=yes,
+ liga=yes,
+ tlig=yes,
+ trep=yes] % texligatures=yes,texquotes=yes
+
+\definefontfeature
+ [inlinenumbers]
+ [pnum=yes,
+ tnum=no]
+
+\definefontfeature
+ [tabularnumbers]
+ [tnum=yes,
+ pnum=no]
+
+\definefontfeature
+ [oldstylenumbers]
+ [onum=yes]
+
+\definefontfeature
+ [zero]
+ [zero=yes]
+
+% \definefontfeature
+% [newstyle]
+% [onum=no]
+
+\definefontfeature % == default unless redefined
+ [ligatures]
+ [always]
+ [liga=yes,
+ tlig=yes,
+ trep=yes]
+
+\definefontfeature
+ [letterspacing]
+ [liga=no,
+ rlig=no,
+ clig=no,
+ dlig=no,
+ ccmp=yes,
+ keepligatures=auto]
+
+\definefontfeature % can be used for type1 fonts
+ [complete]
+ [always]
+ [compose=yes,
+ liga=yes,
+% ccmp=yes,
+% locl=yes,
+% calt=yes,
+% clig=yes,
+% rlig=yes,
+ tlig=yes,
+ trep=yes]
+
+\definefontfeature
+ [none]
+ [mode=none,
+ features=no]
+
+\definefontfeature
+ [semitic-complete]
+ [mode=node,analyze=yes,language=dflt,ccmp=yes,
+ autoscript=position,autolanguage=position,
+ init=yes,medi=yes,fina=yes,isol=yes,
+ % fin2=yes,fin3=yes,med2=yes,
+ mark=yes,mkmk=yes,kern=yes,curs=yes,
+ liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes]
+
+\definefontfeature
+ [semitic-simple]
+ [mode=node,analyze=yes,language=dflt,ccmp=yes,
+ autoscript=position,autolanguage=position,
+ init=yes,medi=yes,fina=yes,isol=yes,
+ % fin2=yes,fin3=yes,med2=yes,
+ mark=yes,mkmk=yes,kern=yes,curs=yes,
+ rlig=yes,calt=yes]
+
+\definefontfeature
+ [arabic]
+ [semitic-complete]
+ [script=arab]
+
+\definefontfeature
+ [syriac]
+ [arabic]
+ [fin2=yes,fin3=yes,med2=yes]
+
+\definefontfeature
+ [reordercombining]
+ [reordercombining=yes]
+
+\definefontfeature
+ [hebrew]
+ [semitic-complete]
+ [script=hebr,
+ reordercombining=yes] % seems to work best with reasonable fonts
+
+\definefontfeature
+ [simplearabic]
+ [semitic-simple]
+ [script=arab]
+
+\definefontfeature
+ [simplehebrew]
+ [semitic-simple]
+ [script=hebr]
+
+% indic
+
+\definefontfeature
+ [indic-common]
+ [mode=node,
+ language=dflt,
+ % localized
+ locl=yes,
+ % positioning
+ kern=yes,
+ dist=yes,
+ % above/below base marks
+ abvm=yes,
+ blwm=yes,
+ % basic shaping
+ nukt=yes,
+ akhn=yes,
+ rphf=yes,
+ % presentation
+ pres=yes,
+ abvs=yes,
+ blws=yes,
+ psts=yes,
+ haln=yes,
+ calt=yes]
+
+\definefontfeature
+ [devanagari-one]
+ [indic-common]
+ [script=deva,
+ % basic shaping
+ rkrf=yes,
+ blwf=yes,
+ half=yes,
+ vatu=yes,
+ cjct=yes]
+
+\definefontfeature
+ [bengali-one]
+ [indic-common]
+ [script=beng,
+ % basic shaping
+ blwf=yes,
+ half=yes,
+ pstf=yes,
+ vatu=yes,
+ cjct=yes,
+ % presentation
+ init=yes]
+
+\definefontfeature
+ [gujarati-one]
+ [indic-common]
+ [script=gujr,
+ % basic shaping
+ rkrf=yes,
+ blwf=yes,
+ half=yes,
+ vatu=yes,
+ cjct=yes]
+
+\definefontfeature
+ [gurmukhi-one]
+ [indic-common]
+ [script=guru,
+ % basic shaping
+ blwf=yes,
+ half=yes,
+ pstf=yes,
+ vatu=yes,
+ cjct=yes]
+
+\definefontfeature
+ [kannada-one]
+ [indic-common]
+ [script=knda,
+ % basic shaping
+ pref=yes,
+ blwf=yes,
+ half=yes,
+ pstf=yes,
+ cjct=yes]
+
+\definefontfeature
+ [malayalam-one]
+ [indic-common]
+ [script=mlym,
+ % basic shaping
+ pref=yes,
+ blwf=yes,
+ half=yes,
+ pstf=yes,
+ cjct=yes]
+
+\definefontfeature
+ [oriya-one]
+ [indic-common]
+ [script=orya,
+ % basic shaping
+ blwf=yes,
+ pstf=yes,
+ cjct=yes]
+
+\definefontfeature
+ [tamil-one]
+ [indic-common]
+ [script=taml,
+ % basic shaping
+ pref=yes,
+ half=yes]
+
+\definefontfeature
+ [telugu-one]
+ [indic-common]
+ [script=telu,
+ % basic shaping
+ pref=yes,
+ blwf=yes,
+ half=yes,
+ pstf=yes,
+ cjct=yes]
+
+\definefontfeature [devanagari-two] [devanagari-one] [script=dev2]
+\definefontfeature [bengali-two] [bengali-one] [script=bng2]
+\definefontfeature [gujarati-two] [gujarati-one] [script=gjr2]
+\definefontfeature [gurmukhi-two] [gurmukhi-one] [script=gur2]
+\definefontfeature [kannada-two] [kannada-one] [script=knd2]
+\definefontfeature [malayalam-two] [malayalam-one] [script=mlm2]
+\definefontfeature [oriya-two] [oriya-one] [script=ory2]
+\definefontfeature [tamil-two] [tamil-one] [script=tml2]
+\definefontfeature [telugu-two] [telugu-one] [script=tel2]
+
+% mongolian
+
+\definefontfeature
+ [mongolian]
+ [default]
+ [script=mong,
+ init=yes,medi=yes,fina=yes,isol=yes,
+ rlig=yes,rclt=yes,calt=yes,
+ %dlig=yes,cswh=yes, % optional
+ mset]
+
+% tibetan
+
+\definefontfeature
+ [tibetan]
+ [always]
+ [script=tibt,
+ language=dflt,
+ locl=yes,
+ ccmp=yes,
+ abvs=yes,
+ blws=yes,
+ calt=yes,
+ liga=yes,
+ abvm=yes,
+ blwm=yes]
+
+% cjk
+
+\definefontfeature
+ [jamoforms]
+ [ljmo=yes,
+ tjmo=yes,
+ vjmo=yes]
+
+% \definefontfeature
+% [japanese]
+% [default]
+% [language=jan]
+
+% \definefontfeature
+% [simplified-chinese]
+% [default]
+% [language=zhs]
+
+% \definefontfeature
+% [traditional-chinese]
+% [default]
+% [language=zht]
+
+% \definefontfeature
+% [chinese]
+% [simplified-chinese]
+
+% \definefontfeature
+% [korean]
+% [default]
+% [language=kor]
+
+% symbols:
+
+\definefontfeature
+ [dingbats]
+ [mode=base,
+ goodies=dingbats,
+ unicoding=yes]
+
+% math:
+
+\definefontfeature
+ [mathematics]
+ [mode=base,
+ % mode=none, % better, maybe do this last
+ kern=yes,
+ % liga=yes, % makes no sense
+ % tlig=yes, % makes no sense
+ % trep=yes, % makes no sense
+ mathnolimitsmode={0,800}, % this looks okay on the average font
+ mathalternates=yes,
+ mathitalics=yes, % we pass them
+ mathdimensions=all,
+ % mathkerns=yes,
+ % staircase=yes,
+ compactmath=yes,
+ % mathgaps=yes,
+ language=dflt,
+ script=math]
+
+\definefontfeature
+ [oldmath]
+ [oldmath=yes]
+
+\ifdefined\mathnolimitsmode
+ \mathnolimitsmode\plusone % font driven (only opentype)
+\fi
+
+\mathitalicsmode\plusone % 1 : add to simple noad
+%mathitalicsmode\plusthree % 2 : add italic kerns for diagnostics
+
+% \adaptfontfeature[*math*][mathnolimitsmode=1000] % only subscript
+
+\definefontfeature
+ [mathematics-l2r]
+ [mathematics]
+ []
+
+\definefontfeature
+ [mathematics-r2l]
+ [mathematics]
+ [rtlm=yes,
+ locl=yes]
+
+\definefontfeature[virtualmath] [mathematics]
+\definefontfeature[virtualmath-l2r] [mathematics-l2r]
+\definefontfeature[virtualmath-r2l] [mathematics-r2l]
+
+\definefontfeature[math-text] [mathematics] [ssty=no]
+\definefontfeature[math-script] [mathematics] [ssty=1,mathsize=yes]
+\definefontfeature[math-scriptscript] [mathematics] [ssty=2,mathsize=yes]
+
+\definefontfeature[math-text-l2r] [mathematics-l2r] [ssty=no]
+\definefontfeature[math-script-l2r] [mathematics-l2r] [ssty=1,mathsize=yes]
+\definefontfeature[math-scriptscript-l2r] [mathematics-l2r] [ssty=2,mathsize=yes]
+
+\definefontfeature[math-text-r2l] [mathematics-r2l] [ssty=no]
+\definefontfeature[math-script-r2l] [mathematics-r2l] [ssty=1,mathsize=yes]
+\definefontfeature[math-scriptscript-r2l] [mathematics-r2l] [ssty=2,mathsize=yes]
+
+% this will go away: could be a mode in the engine
+
+\definefontfeature[math-nostack-text] [math-text] [nostackmath=yes]
+\definefontfeature[math-nostack-script] [math-script] [nostackmath=yes]
+\definefontfeature[math-nostack-scriptscript][math-scriptscript][nostackmath=yes]
+
+% \definefontfeature[mathtext] [math-text]
+% \definefontfeature[mathscript] [math-script]
+% \definefontfeature[mathscriptscript] [math-scriptscript]
+
+% \definefontfeature
+% [missing]
+% [missing=yes]
+
+%D Nice to have too:
+
+\definefontfeature
+ [quality]
+ [expansion=quality,
+ protrusion=quality]
+
+\definefontfeature
+ [fullprotrusion]
+ [protrusion=pure]
+
+\definefontfeature
+ [slanted]
+ [slant=.2]
+
+% \definefontfeature
+% [boldened]
+% [extend=1.2]
+
+%D Neat:
+
+% By eye:
+%
+% \definefontfeature[boldened-10][effect={width=0.10,delta=1.0,hdelta=0.500,ddelta=0.150,vshift=0.125,extend=1.025,squeeze=0.99250}]
+% \definefontfeature[boldened-15][effect={width=0.15,delta=1.0,hdelta=0.500,ddelta=0.150,vshift=0.250,extend=1.050,squeeze=0.98750}]
+% \definefontfeature[boldened-20][effect={width=0.20,delta=1.0,hdelta=0.500,ddelta=0.150,vshift=0.375,extend=1.075,squeeze=0.98125}]
+% \definefontfeature[boldened-30][effect={width=0.30,delta=1.0,hdelta=0.500,ddelta=0.150,vshift=0.500,extend=1.100,squeeze=0.97500}]
+%
+% By calculation:
+%
+% \definefontfeature[boldened-10][effect={width=0.10,delta=1.0,hdelta=0.02500,ddelta=0.02500,vshift=0.02500,extend=1.050,squeeze=0.99500}]
+% \definefontfeature[boldened-15][effect={width=0.15,delta=1.0,hdelta=0.05625,ddelta=0.05625,vshift=0.05625,extend=1.075,squeeze=0.99250}]
+% \definefontfeature[boldened-20][effect={width=0.20,delta=1.0,hdelta=0.10000,ddelta=0.10000,vshift=0.10000,extend=1.100,squeeze=0.99000}]
+% \definefontfeature[boldened-30][effect={width=0.30,delta=1.0,hdelta=0.22500,ddelta=0.22500,vshift=0.22500,extend=1.150,squeeze=0.98500}]
+%
+% So we can do this:
+
+\definefontfeature[boldened-10][effect={width=0.10,auto=yes}]
+\definefontfeature[boldened-15][effect={width=0.15,auto=yes}]
+\definefontfeature[boldened-20][effect={width=0.20,auto=yes}]
+\definefontfeature[boldened-25][effect={width=0.25,auto=yes}]
+\definefontfeature[boldened-30][effect={width=0.30,auto=yes}]
+
+\definefontfeature
+ [boldened]
+ [boldened-30]
+
+%D Emoji etc:
+
+\definefontfeature[bandw:overlay][ccmp=yes,dist=yes]
+\definefontfeature[color:overlay][ccmp=yes,dist=yes,colr=yes]
+%definefontfeature[bandw:svg] [ccmp=yes,dist=yes]
+\definefontfeature[color:svg] [ccmp=yes,dist=yes,svg=yes]
+%definefontfeature[bandw:bitmap] [ccmp=yes,dist=yes,sbix=yes]
+\definefontfeature[color:bitmap] [ccmp=yes,dist=yes,sbix=yes] % also cblc
+
+% Plus an automatic one:
+
+\definefontfeature[color] [ccmp=yes,dist=yes,color=auto]
+
+%D We define some colors that are used in tracing (for instance \OPENTYPE\
+%D features). We cannot yet inherit because no colors are predefined.
+
+\definecolor[font:init][r=.75]
+\definecolor[font:medi][g=.75]
+\definecolor[font:fina][b=.75]
+\definecolor[font:isol][r=.75,g=.75] % [y=.75]
+\definecolor[font:mark][r=.75,b=.75] % [m=.75]
+\definecolor[font:rest][b=.75,g=.75] % [c=.75]
+
+\definecolor[font:0] [s=1]
+\definecolor[font:1] [r=.75]
+\definecolor[font:2] [g=.75]
+\definecolor[font:3] [b=.75]
+\definecolor[font:4] [r=.75,g=.75]
+\definecolor[font:5] [r=.75,b=.75]
+\definecolor[font:6] [b=.75,g=.75]
+\definecolor[font:7] [r=.75]
+\definecolor[font:8] [g=.75]
+\definecolor[font:9] [b=.75]
+
+\definecolor[f:r:t][a=1,t=.25,r=1]
+\definecolor[f:g:t][a=1,t=.25,g=1]
+\definecolor[f:b:t][a=1,t=.25,b=1]
+\definecolor[f:c:t][a=1,t=.25,c=1]
+\definecolor[f:m:t][a=1,t=.25,m=1]
+\definecolor[f:y:t][a=1,t=.25,y=1]
+\definecolor[f:k:t][a=1,t=.25,s=0]
+\definecolor[f:s:t][a=1,t=.25,s=0]
+
+\definepalet % weird place
+ [layout]
+ [grid=trace:dr,
+ page=trace:dg,
+ profile=f:s:t,
+ one=f:y:t,
+ mix=f:b:t]
+
+%D Now we're up to some definitions.
+
+\definebodyfontenvironment
+ [\s!default]
+ [ \s!text=1.0,
+ \s!script=0.7,
+ \s!scriptscript=0.5,
+ \s!a=1.200,
+ \s!b=1.440,
+ \s!c=1.728,
+ \s!d=2.074,
+ *=\font_currentfontscale, % wildcard
+ \s!x=0.8,
+ \s!xx=0.6,
+ \v!big=1.2,
+ \v!small=0.8,
+ \c!interlinespace=,
+ \s!em=\v!slanted]
+
+\definebodyfontenvironment
+ [20.7pt]
+ [ \s!text=20.7pt,
+ \s!script=\!!fourteenpointfour,
+ \s!scriptscript=\!!twelvepoint,
+ \s!x=17.3pt,
+ \s!xx=\!!fourteenpointfour,
+ \v!big=20.7pt, % !!!!
+ \v!small=17.3pt]
+
+\definebodyfontenvironment
+ [17.3pt]
+ [ \s!text=17.3pt,
+ \s!script=\!!twelvepoint,
+ \s!scriptscript=\!!tenpoint,
+ \s!x=\!!fourteenpointfour,
+ \s!xx=\!!twelvepoint,
+ \v!big=20.7pt,
+ \v!small=\!!fourteenpointfour]
+
+\definebodyfontenvironment
+ [\!!fourteenpointfour]
+ [ \s!text=\!!fourteenpointfour,
+ \s!script=\!!elevenpoint,
+ \s!scriptscript=\!!ninepoint,
+ \s!x=\!!twelvepoint,
+ \s!xx=\!!tenpoint,
+ \v!big=17.3pt,
+ \v!small=\!!twelvepoint]
+
+\definebodyfontenvironment
+ [\!!twelvepoint]
+ [ \s!text=\!!twelvepoint,
+ \s!script=\!!ninepoint,
+ \s!scriptscript=\!!sevenpoint,
+ \s!x=\!!tenpoint,
+ \s!xx=\!!eightpoint,
+ \v!big=\!!fourteenpointfour,
+ \v!small=\!!tenpoint]
+
+\definebodyfontenvironment
+ [\!!elevenpoint]
+ [ \s!text=\!!elevenpoint,
+ \s!script=\!!eightpoint,
+ \s!scriptscript=\!!sixpoint,
+ \s!x=\!!ninepoint,
+ \s!xx=\!!sevenpoint,
+ \v!big=\!!twelvepoint,
+ \v!small=\!!ninepoint]
+
+\definebodyfontenvironment
+ [\!!tenpoint]
+ [ \s!text=\!!tenpoint,
+ \s!script=\!!sevenpoint,
+ \s!scriptscript=\!!fivepoint,
+ \s!x=\!!eightpoint,
+ \s!xx=\!!sixpoint,
+ \v!big=\!!twelvepoint,
+ \v!small=\!!eightpoint]
+
+\definebodyfontenvironment
+ [\!!ninepoint]
+ [ \s!text=\!!ninepoint,
+ \s!script=\!!sevenpoint,
+ \s!scriptscript=\!!fivepoint,
+ \s!x=\!!sevenpoint,
+ \s!xx=\!!fivepoint,
+ \v!big=\!!elevenpoint,
+ \v!small=\!!sevenpoint]
+
+\definebodyfontenvironment
+ [\!!eightpoint]
+ [ \s!text=\!!eightpoint,
+ \s!script=\!!sixpoint,
+ \s!scriptscript=\!!fivepoint,
+ \s!x=\!!sixpoint,
+ \s!xx=\!!fivepoint,
+ \v!big=\!!tenpoint,
+ \v!small=\!!sixpoint]
+
+\definebodyfontenvironment
+ [\!!sevenpoint]
+ [ \s!text=\!!sevenpoint,
+ \s!script=\!!sixpoint,
+ \s!scriptscript=\!!fivepoint,
+ \s!x=\!!sixpoint,
+ \s!xx=\!!fivepoint,
+ \v!big=\!!ninepoint,
+ \v!small=\!!fivepoint]
+
+\definebodyfontenvironment
+ [\!!sixpoint]
+ [ \s!text=\!!sixpoint,
+ \s!script=\!!fivepoint,
+ \s!scriptscript=\!!fivepoint,
+ \s!x=\!!fivepoint,
+ \s!xx=\!!fivepoint,
+ \v!big=\!!eightpoint,
+ \v!small=\!!fivepoint]
+
+\definebodyfontenvironment
+ [\!!fivepoint]
+ [ \s!text=\!!fivepoint,
+ \s!script=\!!fivepoint,
+ \s!scriptscript=\!!fivepoint,
+ \s!x=\!!fivepoint,
+ \s!xx=\!!fivepoint,
+ \v!big=\!!sevenpoint,
+ \v!small=\!!fivepoint]
+
+\definebodyfontenvironment
+ [\!!fourpoint]
+ [ \s!text=\!!fourpoint,
+ \s!script=\!!fourpoint,
+ \s!scriptscript=\!!fourpoint,
+ \s!x=\!!fourpoint,
+ \s!xx=\!!fourpoint,
+ \v!big=\!!sixpoint,
+ \v!small=\!!fourpoint]
+
+\definebodyfontswitch [fourteenpointfour] [\!!fourteenpointfour]
+\definebodyfontswitch [twelvepoint] [\!!twelvepoint]
+\definebodyfontswitch [elevenpoint] [\!!elevenpoint]
+\definebodyfontswitch [tenpoint] [\!!tenpoint]
+\definebodyfontswitch [ninepoint] [\!!ninepoint]
+\definebodyfontswitch [eightpoint] [\!!eightpoint]
+\definebodyfontswitch [sevenpoint] [\!!sevenpoint]
+\definebodyfontswitch [sixpoint] [\!!sixpoint]
+\definebodyfontswitch [fivepoint] [\!!fivepoint]
+\definebodyfontswitch [fourpoint] [\!!fourpoint]
+
+%D So far.
+
+\definefontstyle [\s!hw] [\s!hw]
+\definefontstyle [\s!cg] [\s!cg]
+
+\definefontstyle [\v!roman,\v!serif,\v!regular] [\s!rm]
+\definefontstyle [\v!sansserif,\v!sans,\v!support] [\s!ss]
+\definefontstyle [\v!teletype,\v!type,\v!mono] [\s!tt]
+\definefontstyle [\v!handwritten] [\s!hw]
+\definefontstyle [\v!calligraphic] [\s!cg]
+\definefontstyle [\v!math,\v!mathematics] [\s!mm]
+
+\definefontalternative[\s!tf]
+\definefontalternative[\s!bf]
+\definefontalternative[\s!it]
+\definefontalternative[\s!sl]
+\definefontalternative[\s!bs]
+\definefontalternative[\s!bi]
+\definefontalternative[\s!sc]
+
+\definefontsize[\s!a] \definefontsize[\s!b]
+\definefontsize[\s!c] \definefontsize[\s!d]
+
+\definealternativestyle [\v!mediaeval] [\os] []
+\definealternativestyle [\v!normal] [\tf] []
+\definealternativestyle [\v!bold] [\bf] []
+\definealternativestyle [\v!type] [\tt] []
+\definealternativestyle [\v!mono] [\tt] []
+\definealternativestyle [\v!slanted] [\sl] []
+\definealternativestyle [\v!italic] [\it] []
+\definealternativestyle [\v!boldslanted,\v!slantedbold] [\bs] []
+\definealternativestyle [\v!bolditalic,\v!italicbold] [\bi] []
+
+\definealternativestyle [\v!small,\v!smallnormal] [\setsmallbodyfont\tf] []
+\definealternativestyle [\v!smallbold] [\setsmallbodyfont\bf] []
+\definealternativestyle [\v!smalltype] [\setsmallbodyfont\tt] []
+\definealternativestyle [\v!smallitalic,\v!smallslanted] [\setsmallbodyfont\sl] []
+\definealternativestyle [\v!smallboldslanted,\v!smallslantedbold] [\setsmallbodyfont\bs] []
+\definealternativestyle [\v!smallbolditalic,\v!smallitalicbold] [\setsmallbodyfont\bi] []
+
+\definealternativestyle [\v!bigger] [\setbigbodyfont \tf] []
+\definealternativestyle [\v!smaller] [\setsmallbodyfont\tf] []
+
+\definealternativestyle [\v!sans,\v!sansserif] [\ss]
+\definealternativestyle [\v!roman,\v!serif,\v!regular] [\rm]
+\definealternativestyle [\v!handwritten] [\hw]
+\definealternativestyle [\v!calligraphic] [\cg]
+
+\definealternativestyle [\v!sansnormal] [\ss\tf] []
+\definealternativestyle [\v!sansbold] [\ss\bf] []
+\definealternativestyle [\v!serifnormal] [\rm\tf] []
+\definealternativestyle [\v!serifbold] [\rm\bf] []
+\definealternativestyle [\v!mononormal] [\tt\tf] []
+\definealternativestyle [\v!monobold] [\tt\bf] []
+
+\definealternativestyle [typeface] [\typeface] [] % no translation here (quite basic)
+\definealternativestyle [boldface] [\boldface] []
+\definealternativestyle [slantedface] [\slantedface] []
+\definealternativestyle [italicface] [\italicface] []
+\definealternativestyle [swapface] [\swapface] []
+
+\definealternativestyle [emphasize] [\em] [\em] % new
+
+% For Alan:
+
+\definealternativestyle
+ [\v!camel]
+ [{\setcharactercasing[\v!camel]}]
+ [{\setcharactercasing[\v!camel]}]
+
+% % maybe we need interface neutral as well (for use in cld):
+%
+% \letcscsname\mediaeval \csname\v!mediaeval \endcsname
+% \letcscsname\normal \csname\v!normal \endcsname
+% \letcscsname\bold \csname\v!bold \endcsname
+% \letcscsname\mono \csname\v!mono \endcsname
+% \letcscsname\slanted \csname\v!slanted \endcsname
+% \letcscsname\italic \csname\v!italic \endcsname
+% \letcscsname\boldslanted \csname\v!boldslanted \endcsname
+% \letcscsname\slantedbold \csname\v!slantedbold \endcsname
+% \letcscsname\bolditalic \csname\v!bolditalic \endcsname
+% \letcscsname\italicbold \csname\v!italicbold \endcsname
+%
+% \letcscsname\small \csname\v!small \endcsname
+% \letcscsname\smallnormal \csname\v!smallnormal \endcsname
+% \letcscsname\smallbold \csname\v!smallbold \endcsname
+% \letcscsname\smalltype \csname\v!smalltype \endcsname
+% \letcscsname\smallslanted \csname\v!smallslanted \endcsname
+% \letcscsname\smallboldslanted\csname\v!smallboldslanted\endcsname
+% \letcscsname\smallslantedbold\csname\v!smallslantedbold\endcsname
+% \letcscsname\smallbolditalic \csname\v!smallbolditalic \endcsname
+% \letcscsname\smallitalicbold \csname\v!smallitalicbold \endcsname
+%
+% \letcscsname\bigger \csname\v!bigger \endcsname
+% \letcscsname\smaller \csname\v!smaller \endcsname
+%
+% \letcscsname\sans \csname\v!sans \endcsname
+% \letcscsname\sansserif \csname\v!sansserif \endcsname
+% \letcscsname\sansbold \csname\v!sansbold \endcsname
+
+%D We can go on and on and on:
+%D
+%D \starttyping
+%D \setupbodyfontenvironment[default][p=0.8,q=0.6]
+%D \definefontsize[p]
+%D \definefontsize[q]
+%D \stoptyping
+
+%D Slow but handy:
+
+\definealternativestyle [\v!smallbodyfont] [\setsmallbodyfont] []
+\definealternativestyle [\v!bigbodyfont] [\setbigbodyfont] []
+
+%D We treat {\sc Small Caps} and \cap {Pseudo Caps} a bit different. We also provide
+%D an \WORD {uppercase} style.
+
+\definealternativestyle [\v!WORD] [{\setcharactercasing[\v!WORD ]}] [{\setcharactercasing[\v!WORD ]}]
+\definealternativestyle [\v!word] [{\setcharactercasing[\v!word ]}] [{\setcharactercasing[\v!word ]}]
+\definealternativestyle [\v!Word] [{\setcharactercasing[\v!Word ]}] [{\setcharactercasing[\v!Word ]}]
+\definealternativestyle [\v!Words] [{\setcharactercasing[\v!Words ]}] [{\setcharactercasing[\v!Words ]}]
+\definealternativestyle [\v!capital] [{\setcharactercasing[\v!capital]}] [{\setcharactercasing[\v!capital]}]
+\definealternativestyle [\v!Capital] [{\setcharactercasing[\v!Capital]}] [{\setcharactercasing[\v!Capital]}]
+\definealternativestyle [\v!mixed] [{\setcharactercasing[\v!mixed ]}] [{\setcharactercasing[\v!mixed ]}]
+
+\definealternativestyle [\v!cap] [{\setcharactercasing[\v!cap ]}] [{\setcharactercasing[\v!cap ]}]
+\definealternativestyle [\v!Cap] [{\setcharactercasing[\v!Cap ]}] [{\setcharactercasing[\v!Cap ]}]
+
+%D From now (2013-03-17) on we have:
+
+\definefontfeature[f:smallcaps][smcp=yes]
+\definefontfeature[f:oldstyle] [onum=yes]
+\definefontfeature[f:tabular] [tnum=yes]
+\definefontfeature[f:superiors][sups=yes]
+\definefontfeature[f:inferiors][subs=yes]
+\definefontfeature[f:fractions][frac=yes]
+\definefontfeature[f:kern] [kern=yes]
+\definefontfeature[f:kerns] [kern=yes]
+
+\definealternativestyle [\v!smallcaps] [\setsmallcaps] [\setsmallcaps]
+\definealternativestyle [\v!oldstyle] [\setoldstyle ] [\setoldstyle ]
+\definealternativestyle [\v!fractions] [\setfractions\resetbreakpoints] [\setfractions\resetbreakpoints]
+
+\permanent\protected\def\setsmallcaps{\doaddfeature{f:smallcaps}}
+\permanent\protected\def\setoldstyle {\doaddfeature{f:oldstyle}}
+\permanent\protected\def\settabular {\doaddfeature{f:tabular}}
+\permanent\protected\def\setsuperiors{\doaddfeature{f:superiors}}
+\permanent\protected\def\setfractions{\doaddfeature{f:fractions}}
+
+% \permanent\protected\def\frc#1#2%
+% {\dontleavehmode
+% \begingroup
+% \addff{frac}%
+% \resetbreakpoints
+% #1/#2%
+% \endgroup}
+
+%D \macros
+%D {uppercasing, lowercasing}
+
+\definefontfeature[lowercasing][lowercasing=yes]
+\definefontfeature[uppercasing][uppercasing=yes]
+
+\permanent\protected\def\uppercasing{\addff{uppercasing}}
+\permanent\protected\def\lowercasing{\addff{lowercasing}}
+
+%D \macros
+%D {tinyfont}
+%D
+%D By default we load the Computer Modern Roman fonts (but not yet at this moment)
+%D and activate the 12pt roman bodyfont. Sans serif and teletype are also available
+%D and can be called for by \type {\ss} and \type {\tt}. Loading takes place
+%D elsewhere. For tracing purposes we define:
+
+\definefont[tinyfont][file:dejavusansmono at 1ex]
+
+%D \macros
+%D {infofont}
+%D
+%D The second situation occurs when we enable the info mode, and put all kind of
+%D status information in the margin. We don't want huge switches to the main
+%D bodyfont and style, so here too we use a direct method.
+
+\pushoverloadmode
+
+\let\infofont \relax
+\let\infofontbold \relax
+\let\smallinfofont \relax
+\let\smallinfofontbold\relax
+
+\definefont[infofont] [file:dejavusansmono*none at 6pt]
+\definefont[infofontbold] [file:dejavusansmono-bold*none at 6pt]
+\definefont[smallinfofont] [file:dejavusansmono*none at 3pt]
+\definefont[smallinfofontbold][file:dejavusansmono-bold*none at 3pt]
+
+%D Optimization (later we overload in math). Also needed in order to get \type {\ss}
+%D properly defined.
+
+\permanent\protected\def\normaltf{\let\fontalternative\s!tf\font_helpers_synchronize_font}
+\permanent\protected\def\normalbf{\let\fontalternative\s!bf\font_helpers_synchronize_font}
+\permanent\protected\def\normalit{\let\fontalternative\s!it\font_helpers_synchronize_font}
+\permanent\protected\def\normalsl{\let\fontalternative\s!sl\font_helpers_synchronize_font}
+\permanent\protected\def\normalbi{\let\fontalternative\s!bi\font_helpers_synchronize_font}
+\permanent\protected\def\normalbs{\let\fontalternative\s!bs\font_helpers_synchronize_font}
+
+\let\tf\normaltf
+\let\bf\normalbf
+\let\it\normalit
+\let\sl\normalsl
+\let\bi\normalbi
+\let\bs\normalbs
+
+\permanent\protected\def\normalrm{\font_helpers_set_current_font_style{\s!rm}}
+\permanent\protected\def\normalss{\font_helpers_set_current_font_style{\s!ss}}
+\permanent\protected\def\normaltt{\font_helpers_set_current_font_style{\s!tt}}
+
+\let\rm\normalrm
+\let\ss\normalss
+\let\tt\normaltt
+
+\popoverloadmode
+
+% experiment:
+
+\startsetups experiment:fonts:compact
+ \settrue\c_font_compact
+
+% \definefontfeature[virtualmath] [mathematics] [compactmath=yes]
+% \definefontfeature[virtualmath-l2r] [mathematics-l2r][compactmath=yes]
+% \definefontfeature[virtualmath-r2l] [mathematics-r2l][compactmath=yes]
+
+% \definefontfeature[math-text] [mathematics] [compactmath=yes]
+% \definefontfeature[math-script] [mathematics] [compactmath=yes]
+% \definefontfeature[math-scriptscript] [mathematics] [compactmath=yes]
+% \definefontfeature[math-text-l2r] [mathematics-l2r][compactmath=yes]
+% \definefontfeature[math-script-l2r] [mathematics-l2r][compactmath=yes]
+% \definefontfeature[math-scriptscript-l2r][mathematics-l2r][compactmath=yes]
+% \definefontfeature[math-text-r2l] [mathematics-r2l][compactmath=yes]
+% \definefontfeature[math-script-r2l] [mathematics-r2l][compactmath=yes]
+% \definefontfeature[math-scriptscript-r2l][mathematics-r2l][compactmath=yes]
+\stopsetups
+
+\installtexexperiment
+ {fonts.compact}
+ {\directsetup{experiment:fonts:compact}}
+ {}
+
+\protect \endinput
+
+% LM math vs CM math (analysis by Taco):
+%
+% Computer Modern Roman : Donald Knuth
+% Latin Modern : LM Font Revision Team
+%
+% lmex10.tfm % identical
+% lmmi5.tfm % identical
+% lmmi6.tfm % identical
+% lmmi7.tfm % identical
+% lmmi8.tfm % identical
+% lmmi9.tfm % identical
+% lmmi10.tfm % identical
+% lmmi12.tfm % identical
+% lmmib10.tfm % identical
+% lmsy5.tfm % extra chars: 254,255 (octal)
+% lmsy6.tfm % extra chars: 254,255 (octal)
+% lmsy7.tfm % extra chars: 254,255 (octal)
+% lmsy8.tfm % extra chars: 254,255 (octal)
+% lmsy9.tfm % extra chars: 254,255 (octal)
+% lmsy10.tfm % extra chars: 254,255 (octal)
+% lmbsy10.tfm % extra chars: 254,255 (octal)
+%
+% From the 'AMS' set:
+%
+% lmmib5.tfm % identical
+% lmmib7.tfm % identical
+% lmbsy5.tfm % extra chars: 254,255 (octal)
+% lmbsy7.tfm % extra chars: 254,255 (octal)
+%
+% The two extra characters are:
+%
+% /lessorequalslant
+% /greaterorequalslant
+
+% \unprotect
+%
+% \definehighlight[\v!italic ][\c!command=\v!no,\c!style=\v!italic]
+% \definehighlight[\v!bold ][\c!command=\v!no,\c!style=\v!bold]
+% \definehighlight[\v!bolditalic][\c!command=\v!no,\c!style=\v!bolditalic]
+% \definehighlight[\v!mono] [\c!command=\v!no,\c!style=\v!mono]
+% \definehighlight[\v!monobold] [\c!command=\v!no,\c!style=\v!monobold]
+%
+% \definehighlight[important] [\c!command=\v!no,\c!style=\v!bold]
+% \definehighlight[unimportant] [\c!command=\v!no,\c!color=darkgray]
+% \definehighlight[warning] [\c!command=\v!no,\c!style=\v!bold,\c!color=darkblue]
+% \definehighlight[error] [\c!command=\v!no,\c!style=\v!bold,\c!color=darkred]
+%
+% \protect
+%
+% \starttext
+% \highlight[italic] {italic}
+% \highlight[bolditalic] {bolditalic}
+% \highlight[bold] {bold}
+% \highlight[mono] {mono}
+% \highlight[monobold] {monobold}
+% \highlight[important] {important}
+% \highlight[unimportant]{unimportant}
+% \highlight[warning] {warning}
+% \highlight[error] {error}
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-sel.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-sel.mklx
new file mode 100644
index 00000000000..a25b7fab7c5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-sel.mklx
@@ -0,0 +1,282 @@
+%D \module
+%D [ file=font-sel,
+%D version=2016.08.28,
+%D title=\CONTEXT\ User Module,
+%D subtitle=Selectfont,
+%D author=Wolfgang Schuster,
+%D date=\currentdate,
+%D copyright=Wolfgang Schuster,
+%D license=GNU General Public License]
+
+\writestatus{loading}{ConTeXt User Module / Selectfont}
+
+\registerctxluafile{font-sel}{}
+
+\unprotect
+
+\installcorenamespace {selectfont}
+\installsimplecommandhandler \??selectfont {selectfont}
+
+\let\selectfont_index\!!zerocount
+
+\protected\def\selectfont_register[#style][#settings]%
+ {\begingroup
+ \edef\currentselectfont{\expandnamespacevalue\??fontshortstyle{#style}\s!rm}%
+ \checkselectfontparent
+ \setupcurrentselectfont[#settings]%
+ \edef\p_selectfont_preset{\selectfontparameter\c!preset}%
+ \ifempty\p_selectfont_preset \else
+ \processcommacommand[\p_selectfont_preset]\selectfont_preset_process
+ \setupcurrentselectfont[#settings]%
+ \fi
+ \font_helpers_process_alternative_list\clf_registerfontalternative
+ \setexpandedselectfontparameter\c!style{\expandnamespaceparameter\??fontshortstyle\selectfontparameter\c!style\s!rm}%
+ \xdef\selectfont_index{\clf_registerfontfamily
+ metadata {%
+ typeface {\selectfontparameter\c!label}%
+ style {\selectfontparameter\c!style}%
+ family {\selectfontparameter\c!name}%
+ }
+ options {%
+ designsize {\selectfontparameter\s!designsize}%
+ rscale {\selectfontparameter\s!rscale}%
+ goodies {\selectfontparameter\c!goodies}%
+ extras {\selectfontparameter\c!extras}%
+ features {\selectfontparameter\c!features}%
+ preset {\selectfontparameter\c!preset}%
+ range {\selectfontparameter\c!range}% fallback only
+ offset {\selectfontparameter\c!offset}% fallback only
+ check {\selectfontparameter\c!check}% fallback only
+ force {\selectfontparameter\c!force}% fallback only
+ }
+ userdata {%
+ \luaexpanded{#settings}%
+ }}%
+ \endgroup}
+
+%D \macros
+%D {definefontfamilypreset}
+
+\installcorenamespace {selectfontpreset}
+
+\permanent\tolerant\protected\def\definefontfamilypreset[#name]#spacer[#settings]%
+ {\defcsname\??selectfontpreset#name\endcsname{\setupcurrentselectfont[#settings]}%
+ \clf_definefontfamilypreset{#name}{\luaexpanded{#settings}}}
+
+\def\selectfont_preset_process#name%
+ {\ifcsname\??selectfontpreset#name\endcsname
+ \lastnamedcs
+ \else
+ % unknown preset
+ \fi}
+
+\definefontfamilypreset
+ [range:chinese]
+ [\c!range={cjkcompatibilityforms,
+ cjkcompatibilityideographs,
+ cjkcompatibilityideographssupplement,
+ cjkradicalssupplement,
+ cjkstrokes,
+ cjksymbolsandpunctuation,
+ cjkunifiedideographs,
+ cjkunifiedideographsextensiona,
+ cjkunifiedideographsextensionb,
+ halfwidthandfullwidthforms,
+ verticalforms,
+ bopomofo,
+ bopomofoextended}]
+
+\definefontfamilypreset
+ [range:japanese]
+ [\c!range={cjkcompatibilityforms,
+ cjkcompatibilityideographs,
+ cjkcompatibilityideographssupplement,
+ cjkradicalssupplement,
+ cjkstrokes,
+ cjksymbolsandpunctuation,
+ cjkunifiedideographs,
+ cjkunifiedideographsextensiona,
+ cjkunifiedideographsextensionb,
+ halfwidthandfullwidthforms,
+ verticalforms,
+ hiragana,
+ katakana}]
+
+\definefontfamilypreset
+ [range:korean]
+ [\c!range={cjkcompatibilityforms,
+ cjkcompatibilityideographs,
+ cjkcompatibilityideographssupplement,
+ cjkradicalssupplement,
+ cjkstrokes,
+ cjksymbolsandpunctuation,
+ cjkunifiedideographs,
+ cjkunifiedideographsextensiona,
+ cjkunifiedideographsextensionb,
+ halfwidthandfullwidthforms,
+ verticalforms,
+ hangulcompatibilityjamo,
+ hanguljamo,
+ hanguljamoextendeda,
+ hanguljamoextendedb,
+ hangulsyllables}]
+
+\definefontfamilypreset
+ [range:cyrillic]
+ [\c!range={cyrillic,
+ cyrillicextendeda,
+ cyrillicextendedb,
+ cyrillicsupplement}]
+
+\definefontfamilypreset
+ [range:greek]
+ [\c!range={greekandcoptic,
+ greekextended,
+ ancientgreeknumbers}]
+
+\definefontfamilypreset
+ [range:hebrew]
+ [\c!range={hebrew,
+ alphabeticpresentationforms}]
+
+\definefontfamilypreset [math:digitsnormal] [\c!range=digitsnormal]
+\definefontfamilypreset [math:digitsbold] [\c!range=digitsnormal,\c!offset=digitsbold,\s!tf=style:bold]
+
+\definefontfamilypreset [math:uppercasenormal] [\c!range=uppercasenormal]
+\definefontfamilypreset [math:uppercaseitalic] [\c!range=uppercasenormal,\c!offset=uppercaseitalic, \s!tf=style:italic]
+\definefontfamilypreset [math:uppercasebold] [\c!range=uppercasenormal,\c!offset=uppercasebold, \s!tf=style:bold]
+\definefontfamilypreset [math:uppercasebolditalic] [\c!range=uppercasenormal,\c!offset=uppercasebolditalic,\s!tf=style:bolditalic]
+
+\definefontfamilypreset [math:lowercasenormal] [\c!range=lowercasenormal]
+\definefontfamilypreset [math:lowercaseitalic] [\c!range=lowercasenormal,\c!offset=lowercaseitalic, \s!tf=style:italic]
+\definefontfamilypreset [math:lowercasebold] [\c!range=lowercasenormal,\c!offset=lowercasebold, \s!tf=style:bold]
+\definefontfamilypreset [math:lowercasebolditalic] [\c!range=lowercasenormal,\c!offset=lowercasebolditalic,\s!tf=style:bolditalic]
+
+\definefontfamilypreset [math:mathematicaloperators] [\c!range=mathematicaloperators]
+
+\definefontfamilypreset [math:lowercasegreeknormal] [\c!range=lowercasegreeknormal]
+\definefontfamilypreset [math:lowercasegreekitalic] [\c!range=lowercasegreeknormal,\c!offset=lowercasegreekitalic, \s!tf=style:italic]
+\definefontfamilypreset [math:lowercasegreekbold] [\c!range=lowercasegreeknormal,\c!offset=lowercasegreekbold, \s!tf=style:bold]
+\definefontfamilypreset [math:lowercasegreekbolditalic] [\c!range=lowercasegreeknormal,\c!offset=lowercasegreekbolditalic,\s!tf=style:bolditalic]
+
+\definefontfamilypreset [math:uppercasegreeknormal] [\c!range=uppercasegreeknormal]
+\definefontfamilypreset [math:uppercasegreekitalic] [\c!range=uppercasegreeknormal,\c!offset=uppercasegreekitalic, \s!tf=style:italic]
+\definefontfamilypreset [math:uppercasegreekbold] [\c!range=uppercasegreeknormal,\c!offset=uppercasegreekbold, \s!tf=style:bold]
+\definefontfamilypreset [math:uppercasegreekbolditalic] [\c!range=uppercasegreeknormal,\c!offset=uppercasegreekbolditalic,\s!tf=style:bolditalic]
+
+%D \macros
+%D {definefontfamily,definefallbackfamily}
+%D
+%D The \tex{definefontfamily} creates like \tex{definetypeface} a collection of font
+%D with different styles which can be later called with the \tex{setupbodyfont} command.
+%D
+%D The command takes three mandatory commands which are (a) the name of the fontclass,
+%D (b) the styles of the font and (c) the name of the font.
+%D
+%D \starttyping
+%D \definefontfamily [dejavu] [serif] [DejaVu Serif]
+%D \definefontfamily [dejavu] [sans] [DejaVu Sans]
+%D \definefontfamily [dejavu] [mono] [DejaVu Sans Mono]
+%D \definefontfamily [dejavu] [math] [XITS Math] [rscale=1.1]
+%D
+%D \definefontfamily [office] [serif] [Times New Roman]
+%D \definefontfamily [office] [sans] [Arial] [rscale=0.9]
+%D \definefontfamily [office] [mono] [Courier]
+%D \definefontfamily [office] [math] [TeX Gyre Termes Math]
+%D
+%D \definefontfamily [linux] [serif] [Linux Libertine O]
+%D \definefontfamily [linux] [sans] [Linux Biolinum O]
+%D \definefontfamily [linux] [mono] [Latin Modern Mono]
+%D \definefontfamily [linux] [math] [TeX Gyre Pagella Math] [rscale=0.9]
+%D
+%D \setupbodyfont[dejavu]
+%D
+%D \starttext
+%D
+%D \rm Serif \ss Sans \tt Mono \m{1+2=3}
+%D
+%D \switchtobodyfont[office]
+%D
+%D \rm Serif 123 \ss Sans \tt Mono \m{1+2=3}
+%D
+%D \switchtobodyfont[linux]
+%D
+%D \rm Serif 123 \ss Sans \tt Mono \m{1+2=3}
+%D
+%D \stoptext
+%D \stoptyping
+%D
+%D When a document contains different languages and the global font lacks some characters
+%D for one language, one could set a different font where these charcters are taken from.
+%D This fallback font (there can be more than one for a certain style) could be set with
+%D the \tex{definefallbackfamily} command which takes the same argument as
+%D the \tex{definefontfamily} command.
+%D
+%D \starttyping
+%D \definefallbackfamily [mainface] [serif] [DejaVu Serif] [range=cyrillic]
+%D \definefontfamily [mainface] [serif] [TeX Gyre Pagella]
+%D
+%D \setupbodyfont[mainface]
+%D
+%D \setuplanguage[en][patterns={us,ru}]
+%D
+%D \starttext
+%D
+%D \input knuth
+%D
+%D Традиционная систематика лишайников оказывается во многом условна и
+%D
+%D \stoptext
+%D \stoptyping
+%D
+%D Another feature of the module is the \type{designsize} key which allows one to enable
+%D optical sizes when they are a feature of the requested font.
+%D
+%D \starttyping
+%D \definefontfamily[mainface][serif][Latin Modern Roman][designsize=auto]
+%D
+%D \setupbodyfont[mainface]
+%D
+%D \starttext
+%D \scale[width=\textwidth]{\switchtobodyfont [6pt]\tf Regular, \it Italic \bf Bold and \bi BoldItalic}
+%D \scale[width=\textwidth]{\switchtobodyfont [8pt]\tf Regular, \it Italic \bf Bold and \bi BoldItalic}
+%D \scale[width=\textwidth]{\switchtobodyfont [10pt]\tf Regular, \it Italic \bf Bold and \bi BoldItalic}
+%D \scale[width=\textwidth]{\switchtobodyfont [12pt]\tf Regular, \it Italic \bf Bold and \bi BoldItalic}
+%D \scale[width=\textwidth]{\switchtobodyfont[17.3pt]\tf Regular, \it Italic \bf Bold and \bi BoldItalic}
+%D \stoptext
+%D \stoptyping
+
+% tf = … | * … | name:… | name:* … | file:… | file:* … | style:medium
+
+\permanent\tolerant\protected\def\definefontfamily[#typeface]#spacer[#style]#spacer[#family]#spacer[#settings]%
+ {\ifhastok={#settings}%
+ \selectfont_register[#style][\c!label={#typeface},\c!style={#style},\c!name={#family},#settings]%
+ \else
+ \selectfont_register[#style][\c!label={#typeface},\c!style={#style},\c!name={#family},\c!preset={#settings}]%
+ \fi
+ \clf_definefontfamily\selectfont_index\relax}
+
+\permanent\tolerant\def\definefallbackfamily[#typeface]#spacer[#style]#spacer[#family]#spacer[#settings]%
+ {\ifhastok={#settings}%
+ \selectfont_register[#style][\c!label={#typeface},\c!style={#style},\c!name={#family},#settings]%
+ \else
+ \selectfont_register[#style][\c!label={#typeface},\c!style={#style},\c!name={#family},\c!preset={#settings}]%
+ \fi
+ \clf_definefallbackfamily\selectfont_index\relax}
+
+\permanent\tolerant\protected\def\setupfontfamily[#style]#spacer[#settings]%
+ {\ifparameter#settings\or
+ \edef\currentselectfont{\expandnamespacevalue\??fontshortstyle{#style}\s!rm}%
+ \setupcurrentselectfont[#settings]%
+ \else
+ \let\currentselectfont\empty
+ \setupcurrentselectfont[#style]%
+ \fi}
+
+\setupselectfont
+ [ \c!features=\s!default,
+ \s!designsize=\s!default,
+ \s!rscale=\selectfontparameter\c!scale,
+ \c!scale=1]
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-set.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-set.mklx
new file mode 100644
index 00000000000..fd6a2f1ff51
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-set.mklx
@@ -0,0 +1,158 @@
+%D \module
+%D [ file=font-set,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Initial Loading,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Initial Loading}
+
+\unprotect
+
+%D Defining fonts using \type {\definebodyfont} takes time, so we prefer to
+%D predefine at least the Computer Modern Roman fonts. However, loading all those
+%D fonts at definition time would take both time and space. But even worse, once
+%D fonts are loaded into memory, their encoding vector is fixed, which is a handicap
+%D when we want to distribute the compact \type {fmt} files. So what we want to do
+%D is defining fonts in a way that postpones the loading. We accomplish this by only
+%D loading the fonts when we switch to another bodyfont size. Among the other
+%D alternatives, such as loading the font at the moment of activation and redefining
+%D the activation macro afterwards, this proved to be the most efficient
+%D alternative.
+%D
+%D The next few macros take care of the one exeption on this scheme. When at format
+%D generation time we load the default font file, the one that defines the Computer
+%D Modern Fonts, we don't want the fonts metrics to end up in the format file, so we
+%D temporary prohibit loading. This means that at runtime we have to load the
+%D default bodyfont size just before we start typesetting.
+
+% \enablemode[lmmath]
+
+\let\m_font_fallback_name\empty
+
+% keep as reference:
+%
+% \def\font_preloads_reset_nullfont % this is needed because some macro packages (tikz) misuse \nullfont
+% {\dorecurse\plusseven{\fontdimen\recurselevel\nullfont\zeropoint}% keep en eye on this as:
+% \clf_resetnullfont % in luatex 0.70 this will also do the previous
+% \glet\font_preloads_reset_nullfont\relax}
+
+\def\font_preload_check_mode
+ {\doifelsemode{lmmath}
+ {\def\m_font_fallback_name{modern-designsize-virtual}}% this will stay
+ {\def\m_font_fallback_name{modern-designsize}}% % this might become 'modern'
+ \glet\font_preload_check_mode\relax}
+
+\def\font_preload_default_fonts
+ {\font_preloads_reset
+ \font_preload_check_mode
+ \setupbodyfont[\m_font_fallback_name,\fontstyle,\fontbody]%
+ \showmessage\m!fonts6{fallback \m_font_fallback_name\space \fontstyle\normalspace\normalizedbodyfontsize}}
+
+\def\font_preload_default_fonts_mm
+ {\font_preload_check_mode
+ \writestatus\m!fonts{preloading \m_font_fallback_name\space (math)}%
+ \definetypeface[\fontclass][\s!mm][\s!math][\m_font_fallback_name][\s!default]%
+ \showmessage\m!fonts6{fallback \m_font_fallback_name\space mm \normalizedbodyfontsize}}
+
+\def\font_preload_default_fonts_tt
+ {\font_preload_check_mode
+ \writestatus\m!fonts{preloading \m_font_fallback_name\space (mono)}%
+ \definetypeface[\fontclass][\s!tt][\s!mono][\m_font_fallback_name][\s!default]%
+ \showmessage\m!fonts6{fallback \m_font_fallback_name\space tt \normalizedbodyfontsize}}
+
+\def\font_preloads_reset
+ {\glet\font_preload_default_fonts \relax
+ %\glet\font_preload_default_fonts_tt\relax
+ %\glet\font_preload_default_fonts_mm\relax
+ \glet\font_preloads_zero_stage \relax
+ \glet\font_preloads_first_stage \relax
+ \glet\font_preloads_second_stage \relax
+ %\glet\font_preloads_third_stage \relax
+ \global\everyhbox\emptytoks
+ \global\everyvbox\emptytoks
+ % old: \font_preloads_reset_nullfont, new:
+ \clf_resetnullfont}
+
+\appendtoks
+ \font_preloads_reset
+\to \everystartnotext
+
+\def\font_preloads_reset_checked
+ {\glet\font_preload_default_fonts_tt\relax
+ \glet\font_preload_default_fonts_mm\relax
+ \glet\font_preloads_third_stage \relax
+% \glet\font_preloads_fourth_stage \relax
+ }
+
+\def\font_preloads_zero_stage_indeed
+ {\definedfont[\s!file:lmmono10-regular sa 1]}
+
+\protected\def\font_preloads_zero_stage
+ {\writestatus\m!fonts{beware: no fonts are loaded yet, using 'lm mono' in box}%
+ \glet\font_preloads_zero_stage\font_preloads_zero_stage_indeed
+ \font_preloads_zero_stage}
+
+\protected\def\font_preloads_first_stage % % *nofonts -> *preloadfonts
+ {\doifelsemode{*preloadfonts}
+ {\font_preload_default_fonts
+ \writestatus\m!fonts{preloading latin modern fonts (first stage)}}
+ {\writestatus\m!fonts{latin modern fonts are not preloaded}}}
+
+\protected\def\font_preloads_second_stage
+ {\writestatus\m!fonts{preloading latin modern fonts (second stage)}%
+ \font_preload_default_fonts}
+
+\protected\def\font_preloads_third_stage % always
+ {\ifempty\fontclass
+ \writestatus\m!fonts{preloading latin modern fonts (third stage)}%
+ \font_preload_default_fonts
+ \font_preloads_reset
+ \else
+ \font_preloads_reset
+ \push_macro_fontstyle
+ \ifcsname\??fontclass\fontclass\s!mm\s!features\endcsname \else
+ \font_preload_default_fonts_mm
+ \fi
+ \ifcsname\??fontclass\fontclass\s!tt\s!features\endcsname \else
+ \font_preload_default_fonts_tt
+ \fi
+ \pop_macro_fontstyle
+ \font_preloads_reset_checked % reset third, mm and tt
+ \setupbodyfont[\fontstyle]%
+ \fi}
+
+\protected\def\font_preloads_fourth_stage
+ {\begingroup
+ %ifzeropt\fontcharwd\font\number`!\relax
+ \setbox\scratchbox\hpack{\tf c o n t e x t}%
+ \ifzeropt\wd\scratchbox
+ \writeline
+ \writestatus\m!fonts{!! No bodyfont has been defined and no defaults have been}%
+ \writestatus\m!fonts{!! loaded (maybe due to a missing \string\starttext).}%
+ \writeline
+ \fi
+ \font_preloads_reset
+ \endgroup}
+
+\appendtoks
+ \everyhbox{\font_preloads_zero_stage}%
+ \everyvbox{\font_preloads_zero_stage}%
+\to \everydump
+
+\protected\def\font_preloads_at_every_job {\font_preloads_first_stage }
+\protected\def\font_preloads_at_start_text{\font_preloads_second_stage}
+\protected\def\font_preloads_at_stop_text {\font_preloads_fourth_stage}
+\protected\def\font_preloads_at_definition{\font_preloads_third_stage
+ \glet\font_preloads_at_definition\relax}
+
+% \protected\def\checkpreloadedbodyfont % no \let here
+% {\font_preloads_at_start_text}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-sol.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-sol.mklx
new file mode 100644
index 00000000000..8f3d3d712cb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-sol.mklx
@@ -0,0 +1,131 @@
+%D \module
+%D [ file=font-sol,
+%D version=2009.05.19,
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Solutions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Support / Splitters}
+
+% todo: pass color for tracing
+
+%D This module is specially made for the oriental \TEX\ project. The working is as
+%D follows (and tuned for fonts like Idris' Husayni. The following method came to
+%D my mind after a couple of Skype sessions with Idris while working on the rough
+%D edges of the Husayni font and playing with font dynamics.
+%D
+%D \startitemize[packed]
+%D
+%D \item We define a couple of features sets, some can have stylistics variants
+%D that result in the same words getting a different width. Normally this
+%D happens in a goodies file.
+%D
+%D \item We group such features in a solution set. A solutionset can be enabled
+%D by setting an attribute.
+%D
+%D \item For each paragraph we identify words that get this set applied. We replace
+%D these words by a user node that refers to the original.
+%D
+%D \item For each word we apply the features to a copy that we associate with this
+%D original word.
+%D
+%D \item At the end we have a paragraph (node list) with user nodes that point to a
+%D cache that has originals and processed variants.
+%D
+%D \item When the paragraph is broken into lines we optimize the spacing by
+%D substituting variants.
+%D
+%D \stopitemize
+%D
+%D This approach permits us to use a dedicated paragraph builder, one that treats
+%D the user nodes special and takes the alternatives into account.
+%D
+%D Currently we assume only one solution being active. Maybe some day I'll support
+%D a mixture. This is only one way of optimizing and after several experiments this
+%D one was chosen as testcase. It took quite some experiments (and time) to get thus
+%D far.
+%D
+%D The is experimental code for the Oriental \TEX\ project and aspects of it might
+%D change.
+%D
+%D \starttyping
+%D \setupfontsolutions[method={random,preroll},criterium=1,randomseed=101]
+%D
+%D \definefontsolution % actually only the last run needs to be done this way
+%D [FancyHusayni]
+%D [goodies=husayni,
+%D solution=experimental]
+%D
+%D \definedfont[husayni*husayni-default at 24pt]
+%D \setupinterlinespace[line=36pt]
+%D \righttoleft
+%D \enabletrackers[parbuilders.solutions.splitters.colors]
+%D \setfontsolution[FancyHusayni]
+%D alb alb alb \par
+%D \resetfontsolution
+%D \disabletrackers[parbuilders.solutions.splitters.colors]
+%D \stoptyping
+
+\registerctxluafile{font-sol}{}
+
+\unprotect
+
+\definesystemattribute[splitter][public]
+
+\installcorenamespace{fontsolution}
+
+\installcommandhandler \??fontsolution {fontsolution} \??fontsolution
+
+\aliased\let\setupfontsolutions\setupfontsolution
+
+\appendtoks
+ \clf_definefontsolution
+ {\currentfontsolution}%
+ {% these are frozen
+ goodies {\fontsolutionparameter\s!goodies}%
+ solution {\fontsolutionparameter\c!solution}%
+ less {\fontsolutionparameter\c!less}%
+ more {\fontsolutionparameter\c!more}%
+ }%
+ \relax
+\to \everydefinefontsolution
+
+\permanent\protected\def\setfontsolution[#solution]% just one
+ {\edef\currentfontsolution{#solution}%
+ \clf_setfontsolution
+ {\currentfontsolution}%
+ {%
+ method {\fontsolutionparameter\c!method}%
+ criterium {\fontsolutionparameter\c!criterium}%
+ % randomseed {\fontsolutionparameter\c!random}%
+ }%
+ \relax}
+
+\let\currentfontsolution\empty
+
+\permanent\protected\def\resetfontsolution % resets all
+ {\clf_resetfontsolution
+ \let\currentfontsolution\empty}
+
+\permanent\protected\def\startfontsolution % [#1]
+ {\pushmacro\currentfontsolution
+ \setfontsolution}
+
+\permanent\protected\def\stopfontsolution
+ {\ifhmode\par\fi
+ \clf_stopfontsolution
+ \popmacro\currentfontsolution}
+
+% We initialize this module at the \LUA\ end.
+%
+% \setupfontsolutions
+% [\c!method={\v!normal,preroll},
+% \c!criterium=0]
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-sty.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-sty.mklx
new file mode 100644
index 00000000000..503356a4a88
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-sty.mklx
@@ -0,0 +1,439 @@
+%D \module
+%D [ file=font-sty,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Styles,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Styles}
+
+\unprotect
+
+%D \macros
+%D {definealternativestyle}
+%D
+%D In the main modules we are going to implement lots of parameterized commands and
+%D one of these parameters will concern the font to use. To suit consistent use of
+%D fonts we here implement a mechanism for defining the keywords that present a
+%D particular style or alternative.
+%D
+%D \starttyping
+%D \definealternativestyle [keywords] [\style] [\nostyle]
+%D \stoptyping
+%D
+%D The first command is used in the normal textflow, while the second command takes
+%D care of headings and alike. Consider the next two definitions:
+%D
+%D \starttyping
+%D \definealternativestyle [bold] [\bf] []
+%D \definealternativestyle [cap] [\cap] [\cap]
+%D \stoptyping
+%D
+%D A change \type {\bf} in a heading which is to be set in \type {\tfd} does not look
+%D that well, so therefore we leave the second argument of \type
+%D {\definealternativestyle} empty. When we capatalize characters using the pseudo
+%D small cap command \type {\cap}, we want this to take effect in both text and
+%D headings, which is accomplished by assigning both arguments.
+
+\installcorenamespace{alternativestyles} % settings
+\installcorenamespace{alternativestyle} % instances
+
+\installsetuponlycommandhandler \??alternativestyles {alternativestyles}
+
+\setnewconstant \c_font_current_alternative_style_index \plusone
+
+\permanent\tolerant\protected\def\definealternativestyle[#commands]#spacer[#variantone]#spacer[#varianttwo]%
+ {\processcommalist[#commands]{\font_basics_define_alternative_style_indeed{#variantone}{#varianttwo}}}
+
+% \let\definestyle\definealternativestyle % later redefined
+
+\newconstant\c_fonts_basics_alternative_style_method
+
+% maybe permanent
+
+\def\font_basics_define_alternative_style_indeed#variantone#varianttwo#command%
+ {\protected\defcsname\??alternativestyle#command\endcsname{\font_helpers_apply_alternative_style{#variantone}{#varianttwo}}%
+ \ifcsname#command\endcsname
+ % no redefinition
+ \orelse\ifnum\c_fonts_basics_alternative_style_method=\plusone
+ \ifparameter#command\or
+ \permanent\protected\edefcsname#command\endcsname{\triggergroupedcommandcs\begincsname\??alternativestyle#command\endcsname}%
+ \else
+ \permanent\protected\defcsname#command\endcsname{\triggergroupedcommand{#variantone}}%
+ \fi
+ \else
+ \permanent\protected\defcsname#command\endcsname{\triggergroupedcommand{#variantone}}%
+ \fi}
+
+\def\font_helpers_apply_alternative_style
+ {\ifcase\c_font_current_alternative_style_index
+ \expandafter\gobbletwoarguments
+ \or
+ \expandafter\firstoftwoarguments
+ \or
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\permanent\def\applyalternativestyle#name% public .. expandable? frozen?
+ {\begincsname\??alternativestyle#name\endcsname}
+
+\appendtoks
+ \doifelse{\alternativestylesparameter\c!method}\v!auto
+ {\c_fonts_basics_alternative_style_method\plusone}%
+ {\c_fonts_basics_alternative_style_method\zerocount}%
+\to \everysetupalternativestyles
+
+%D Maybe too generic, but probably ok is the following. (Maybe one day we will use a
+%D dedicated grouped command for styles.)
+
+% \appendtoks
+% \let\groupedcommand\thirdofthreearguments
+% \to \everysimplifycommands
+
+%D This command also defines the keyword as command. This means that the example
+%D definition of \type {bold} we gave before, results in a command \type {\bold}
+%D which can be used as:
+%D
+%D \startbuffer
+%D He's a \bold{bold} man with a {\bold head}.
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D or
+%D
+%D \startexample
+%D \definealternativestyle[bold][\bf][]\getbuffer
+%D \stopexample
+%D
+%D Such definitions are of course unwanted for \type {\cap} because this would
+%D result in an endless recursive call. Therefore we check on the existance of both
+%D the command and the substitution. The latter is needed because for instance \type
+%D {\type} is an entirely diferent command. That command handles verbatim, while the
+%D style command would just switch to teletype font. This is just an example of a
+%D tricky naming coincidence.
+%D
+%D \macros
+%D {doconvertfont,noconvertfont,
+%D dontconvertfont,redoconvertfont}
+%D
+%D After having defined such keywords, we can call for them by using
+%D
+%D \starttyping
+%D \doconvertfont{keyword}{text}
+%D \stoptyping
+%D
+%D We deliberately pass an argument. This enables us to assign converters that
+%D handle one argument, like \type {\cap}.
+%D
+%D By default the first specification is used to set the style, exept when we say
+%D \type {\dontconvertfont}, after which the second specification is used. We can
+%D also directly call for \type {\noconvertfont}. In nested calls, we can restore
+%D the conversion by saying \type {\redoconvertfont}.
+%D
+%D These commands are not grouped! Grouping is most probably done by the calling
+%D macro's and would lead to unnecessary overhead.
+
+\let\m_current_convert_font \empty
+\let\m_current_convert_font_dt\empty
+
+\permanent\protected\def\doconvertfont#specification% takes second argument / this command is obsolete
+ {\edef\m_current_convert_font{#specification}%
+ \ifempty\m_current_convert_font
+ %\expandafter\firstofoneargument
+ \else
+ \expandafter\font_helpers_do_convert_font
+ \fi}
+
+\def\font_helpers_do_convert_font
+ {\edef\m_current_convert_font_dt{\detokenize\expandafter{\m_current_convert_font}}%
+ \ifcsname\??alternativestyle\m_current_convert_font_dt\endcsname
+ \expandafter\lastnamedcs
+ \orelse\ifcsname\m_current_convert_font_dt\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\m_current_convert_font
+ \fi}
+
+%D Low level switches (downward compatible, but we keep them as one can use them in
+%D styles):
+%D
+%D \starttyping
+%D \usemodule[abr-02]
+%D \setuphead[chapter][style=\bfb]
+%D \setupfooter[style=\dontconvertfont\bf]
+%D \chapter{This is \TEX}
+%D \stoptyping
+
+\permanent\protected\def\dontconvertfont{\c_font_current_alternative_style_index\plustwo} % needs checking in usage
+\permanent\protected\def\redoconvertfont{\c_font_current_alternative_style_index\plusone} % needs checking in usage
+
+%D The new one:
+
+\setfalse\fontattributeisset
+
+\mutable\let\currentdetokenizedstyleparameter\empty
+
+\permanent\protected\def\dousestyleparameter#value%
+ {\edef\currentstyleparameter{#value}%
+ \ifempty\currentstyleparameter\else
+ \expandafter\dousecurrentstyleparameter
+ \fi}
+
+\permanent\protected\def\dousestylehashparameter#hash#parameter%
+ {\ifcsname#hash#parameter\endcsname
+ \expandafter\dousestyleparameter\lastnamedcs
+ \fi}
+
+\permanent\protected\def\dousecurrentstyleparameter % empty check outside here
+ {\edef\currentdetokenizedstyleparameter{\detokenize\expandafter{\currentstyleparameter}}%
+ \settrue\fontattributeisset % reset is done elsewhere
+ \ifcsname\??alternativestyle\currentdetokenizedstyleparameter\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\currentdetokenizedstyleparameter\endcsname
+ \lastnamedcs
+ \else
+ \currentstyleparameter
+ \fi}
+
+%D New commands (not yet interfaced):
+%D
+%D \startbuffer
+%D \definestyle[one][style=bold,color=darkblue]
+%D
+%D test \one{test} test
+%D test \style[one]{test} test
+%D test \style[color=red]{test} test
+%D test \style[Serif at 20pt]{test} test
+%D \stopbuffer
+%D
+%D \typebuffer \startlines \getbuffer \stoplines
+
+% definitions .. no tagging here
+
+\installcorenamespace{style}
+\installcorenamespace{stylecheck}
+\installcorenamespace{stylehack}
+
+\installcommandhandler \??style {style} \??style
+
+\setupstyle
+ [%\c!style=,
+ %\c!color=,
+ \c!method=\v!command]
+
+\appendtoks
+ \letvalue{\??stylecheck\currentstyle}\relax
+ \edef\p_method{\styleparameter\c!method}%
+ \ifx\p_method\v!command
+ \frozen\instance\setuevalue{\e!start\currentstyle}{\font_styles_apply_start{\currentstyle}}%
+ \frozen\instance\setuevalue{\e!stop \currentstyle}{\font_styles_apply_stop}%
+ \frozen\instance\setuevalue {\currentstyle}{\font_styles_apply_grouped{\currentstyle}}% no longer groupedcommand here
+ \fi
+\to \everydefinestyle
+
+\protected\def\font_styles_apply_start#name%
+ {\begingroup
+ \font_styles_use_defined{#name}}
+
+\protected\def\font_styles_apply_stop
+ {\endgroup}
+
+\protected\def\font_styles_apply_grouped#name% assumes that the next is { or \bgroup
+ {\bgroup
+ \def\currentstyle{\font_styles_use_defined{#name}}%
+ \afterassignment\currentstyle
+ \let\nexttoken}
+
+\protected\def\font_styles_use_defined#name%
+ {\edef\currentstyle{#name}%
+ \usestylestyleandcolor\c!style\c!color}
+
+\protected\def\font_styles_use_generic#specification%
+ {\let\currentstyle\s!unknown % reasonable generic tag
+ \letstyleparameter\c!style\empty
+ \letstyleparameter\c!color\empty
+ \setupcurrentstyle[#specification]%
+ \usestylestyleandcolor\c!style\c!color}
+
+% commands
+
+\installcorenamespace{styleargument}
+
+\permanent\protected\def\style[#name]% as this is can be a switch we use groupedcommand % frozen?
+ {\csname\??styleargument
+ \ifcsname\??stylecheck#name\endcsname
+ 2% defined as style
+ \orelse\ifcsname\??stylehack#name\endcsname
+ 4% defined as command
+ \orelse\ifcsname#name\endcsname
+ 1% defined as command
+ \else
+ 3% specification
+ \fi
+ \endcsname{#name}}
+
+\setvalue{\??styleargument1}#name%
+ {\expandafter\triggergroupedcommandcs\begincsname#name\endcsname}
+
+\setvalue{\??styleargument2}#name%
+ {\triggergroupedcommand{\font_styles_use_defined{#name}}} % or {\font_styles_apply_grouped{#name}}
+
+\setvalue{\??styleargument3}#specification%
+ {\doifelseassignment{#specification}\font_styles_assignment\font_styles_direct{#specification}}
+
+\setvalue{\??styleargument4}#name%
+ {\expandafter\triggergroupedcommandcs\begincsname\??stylehack#name\endcsname}
+
+\setvalue{\??stylehack\s!math}% dirty trick
+ {\groupedcommand\normalstartimath\normalstopimath}
+
+% \def\font_styles_assignment#specification{\groupedcommand{\font_styles_use_generic{#specification}}{}}
+% \def\font_styles_direct #specification{\groupedcommand{\definedfont[#specification]}{}}
+
+\def\font_styles_assignment#specification{\triggergroupedcommand{\font_styles_use_generic{#specification}}}
+\def\font_styles_direct #specification{\triggergroupedcommand{\definedfont[#specification]}}
+
+% environments
+
+\installcorenamespace{styleenvironment}
+
+\permanent\protected\def\startstyle[#name]%
+ {\begingroup
+ \csname\??styleenvironment
+ \ifcsname\??stylecheck#name\endcsname
+ 2% defined as style
+ \orelse\ifcsname#name\endcsname
+ 1% defined as command
+ \else
+ 3% specification
+ \fi
+ \endcsname{#name}}
+
+\permanent\protected\def\stopstyle
+ {\endgroup
+ \autoinsertnextspace} % will be configurable, maybe also in \definestartstop
+
+\setvalue{\??styleenvironment1}#name%
+ {\csname#name\endcsname}
+
+\setvalue{\??styleenvironment2}#name%
+ {\font_styles_use_defined{#name}}
+
+\setvalue{\??styleenvironment3}#specification%
+ {\doifelseassignment{#specification}\font_styles_start_assignment\font_styles_start_direct{#specification}}
+
+\def\font_styles_start_assignment#specification{\font_styles_use_generic{#specification}}
+\def\font_styles_start_direct #specification{\definedfont[#specification]\relax}
+
+%D Still experimental (might even go away).
+
+% \definestylecollection[mine]
+
+% \definestyleinstance[mine][default][sorry]
+% \definestyleinstance[mine][tt][bs][ttbs:\rm\sl]
+% \definestyleinstance[mine][tt][bf][ttbf:\rm\sl]
+% \definestyleinstance[mine][bf][\sl]
+% \definestyleinstance[mine][sl][\tt]
+
+% {\bf test \mine test \sl test \mine test \bs oeps \mine oeps {\tt test \mine \bf test}}
+
+\installcorenamespace{stylecollection}
+
+\permanent\tolerant\protected\def\definestylecollection[#name]%
+ {\ifarguments\or
+ \setuvalue{#name}{\styleinstance[#name]}%
+ \def\font_styles_define_style_collection_a#style%
+ {\def\font_styles_define_style_collection_b#alternative{\undefinevalue{\??stylecollection#name:#style:#alternative}}%
+ \font_helpers_process_alternative_list\font_styles_define_style_collection_b
+ \font_styles_define_style_collection_b\s!default}%
+ \font_helpers_process_style_list\font_styles_define_style_collection_a
+ \font_styles_define_style_collection_a\s!default
+ \fi}
+
+\let\font_styles_define_style_collection \relax
+\let\font_styles_define_style_collection_a\relax
+\let\font_styles_define_style_collection_b\relax
+
+\permanent\tolerant\protected\def\definestyleinstance[#instance]#*[#2]#*[#3]#*[#4]% [name] [rm|ss|tt|..] [sl|bf|...] [whatever]
+ {\ifparameter#instance\or
+ \ifarguments\or\or
+ \letcsname\??stylecollection#instance::#2\endcsname\empty
+ \or
+ \defcsname\??stylecollection#instance::#2\endcsname{#3}%
+ \or
+ \defcsname\??stylecollection#instance:#2:#3\endcsname{#4}%
+ \fi
+ \ifcsname#instance\endcsname\else\font_styles_define_style_collection[#instance]\fi
+ \fi}
+
+\permanent\protected\def\styleinstance[#instance]% maybe \lastnamedcs here too
+ {\csname\??stylecollection#instance:%
+ \ifcsname\??stylecollection#instance:\fontstyle:\fontalternative\endcsname
+ \fontstyle:\fontalternative
+ \orelse\ifcsname\??stylecollection#instance:\fontstyle:\s!default\endcsname
+ \fontstyle:\s!default
+ \orelse\ifcsname\??stylecollection#instance::\fontalternative\endcsname
+ :\fontalternative
+ \else
+ :\s!default
+ \fi
+ \endcsname}
+
+%D Variant selectors
+%D
+%D \starttyping
+%D \mathematics {\vsone{\utfchar{0x2229}}}
+%D \mathematics {\utfchar{0x2229}\vsone{}}
+%D \stoptyping
+
+\permanent\protected\edef\vsone#character{#character\normalUchar"FE00 } % used
+\permanent\protected\edef\vstwo#character{#character\normalUchar"FE01 } % not used but handy for testing
+
+%D For historic reasons we keep the following around but they are no longer that
+%D relevant for \MKIV. But \unknown\ they will be dropped in \LMTX !
+
+\permanent\protected\def\doattributes#1#2#3#4%
+ {\begingroup % geen \bgroup, anders in mathmode lege \hbox
+ \dousestylehashparameter{#1}{#2}%
+ \dousecolorhashparameter{#1}{#3}%
+ #4%
+ \endgroup}
+
+\permanent\protected\def\dostartattributes#1#2#3%
+ {\begingroup % geen \bgroup, anders in mathmode lege \hbox
+ \dousestylehashparameter{#1}{#2}%
+ \dousecolorhashparameter{#1}{#3}}
+
+\aliased\let\dostopattributes\endgroup
+
+%D New but it needs to be supported explicitly (as in natural tables).
+
+\newconditional\c_font_styles_math
+
+\protected\def\font_styles_math_reset
+ {\setfalse\c_font_styles_math}
+
+\protected\def\font_styles_math_start
+ {\ifconditional\c_font_styles_math
+ \startimath
+ \fi
+ \relax}
+
+\protected\def\font_styles_math_stop
+ {\relax
+ \ifconditional\c_font_styles_math
+ \stopimath
+ \fi}
+
+\definealternativestyle[\v!math][\settrue\c_font_styles_math]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-sym.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-sym.mklx
new file mode 100644
index 00000000000..c908ad849d3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-sym.mklx
@@ -0,0 +1,250 @@
+%D \module
+%D [ file=font-mat,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Symbolic Access,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Symbolic Access}
+
+\unprotect
+
+%D \macros
+%D {getglyph, symbolicfont}
+%D
+%D Individual glyphs can be accessed by using
+%D
+%D \starttyping
+%D \getglyph{fontname}{character}
+%D \stoptyping
+%D
+%D This macro is used in for instance the symbol modules and as one can see, it does
+%D obey the small and even smaller sizes. The \type {\symbolicfont} macro can be
+%D used to switch to a font named \type {fontname} (see \type {cont-log} and \type
+%D {symb-eur} for examples of symbolic definitions.
+
+\def\v_font_string_a
+ {\ifx\fontstyle\s!rm \s!Serif \orelse
+ \ifx\fontstyle\s!ss \s!Sans \orelse
+ \ifx\fontstyle\s!tt \s!Mono \else
+ \s!Serif \fi}
+
+\def\v_font_string_b
+ {\ifx\fontstyle\s!rm \s!Regular \orelse
+ \ifx\fontstyle\s!ss \s!Support \orelse
+ \ifx\fontstyle\s!tt \s!Type \else
+ \s!Serif \fi}
+
+\def\v_font_string_c
+ {\ifx\fontalternative\s!bf \s!Bold \orelse
+ \ifx\fontalternative\s!sl \s!Slanted \orelse
+ \ifx\fontalternative\s!it \s!Italic \orelse
+ \ifx\fontalternative\s!bs \s!BoldSlanted \orelse
+ \ifx\fontalternative\s!bi \s!BoldItalic \fi}
+
+\let\v_font_string_d\s!Serif % default fontstyle (will be redefined in type-ini)
+
+\definefontsynonym
+ [CurrentFont]
+ [\noexpand\v_font_string_a\noexpand\v_font_string_c]
+
+% potential generalization:
+%
+% \letvalue{\??fontfile:t:\s!rm}\s!Serif
+% \letvalue{\??fontfile:t:\s!ss}\s!Sans
+% \letvalue{\??fontfile:t:\s!tt}\s!Mono
+%
+% \letvalue{\??fontfile:a:\s!rm}\s!Regular
+% \letvalue{\??fontfile:a:\s!ss}\s!Support
+% \letvalue{\??fontfile:a:\s!tt}\s!Type
+%
+% \letvalue{\??fontfile:s:\s!bf}\s!Bold
+% \letvalue{\??fontfile:s:\s!sl}\s!Slanted
+% \letvalue{\??fontfile:s:\s!it}\s!Italic
+% \letvalue{\??fontfile:s:\s!bs}\s!BoldSlanted
+% \letvalue{\??fontfile:s:\s!bi}\s!BoldItalic
+%
+% \def\v_font_string_a{\executeifdefined{\??fontfile:t:\fontstyle}\s!Serif}
+% \def\v_font_string_a{\executeifdefined{\??fontfile:t:\fontstyle}\s!Serif}
+% \def\v_font_string_b{\executeifdefined{\??fontfile:a:\fontstyle}\s!Serif}
+% \def\v_font_string_c{\executeifdefined{\??fontfile:s:\fontstyle}\empty}
+% \def\v_font_string_d{\executeifdefined{\??fontfile:t:\csname\??typescriptdefaultstyles\fontclass\endcsname}\s!Serif}
+
+%D \macros
+%D {fontstylesuffix}
+%D
+%D The next macro is used to map non latin fontnames on fonts. See \type
+%D {font-uni} for an example of its use.
+
+% \frozen ...
+
+\permanent\def\fontstylesuffix% why the \s!Regular ? see \getglyph
+ {\ifx\fontalternative\s!tf \s!Regular \orelse
+ \ifx\fontalternative\s!bf \s!Bold \orelse
+ \ifx\fontalternative\s!sl \s!Slanted \orelse
+ \ifx\fontalternative\s!it \s!Italic \orelse
+ \ifx\fontalternative\s!bs \s!BoldSlanted \orelse
+ \ifx\fontalternative\s!bi \s!BoldItalic \orelse
+ \ifx\fontalternative\s!sc \s!Caps \else
+ \s!Regular \fi}
+
+\permanent\def\glyphfontfile#base% appends
+ {#base%
+ \ifcsname\??fontfile#base\v_font_string_a\v_font_string_c\endcsname
+ \v_font_string_a\v_font_string_c
+ \orelse\ifcsname\??fontfile#base\v_font_string_b\v_font_string_c\endcsname
+ \v_font_string_b\v_font_string_c
+ \orelse\ifcsname\??fontfile#base\v_font_string_a\endcsname
+ \v_font_string_a
+ \orelse\ifcsname\??fontfile#base\v_font_string_b\endcsname
+ \v_font_string_b
+ \orelse\ifcsname\??fontfile#base\v_font_string_c\endcsname
+ \v_font_string_c
+ \fi}
+
+%D The next macro can be used to make decisions based on the shape:
+
+\permanent\def\doifelseitalic#yes#nop%
+ {\ifx\fontalternative\s!sl#yes\orelse
+ \ifx\fontalternative\s!it#yes\orelse
+ \ifx\fontalternative\s!bs#yes\orelse
+ \ifx\fontalternative\s!bi#yes\else#nop\fi}
+
+\aliased\let\doifitalicelse\doifelseitalic
+
+%D For an example of usage of the following command, see \type {cont-log.tex}.
+%D
+%D \starttyping
+%D \def\symbolicfont#specification{\definedfont[\glyphfontfile{#specification} sa *]}
+%D \stoptyping
+%D
+%D Since we know what scaling it to be applied, we can implement a much faster
+%D alternative:
+
+\installcorenamespace{symbolfont}
+
+\mutable\let\skedsymbolfont \empty % for diagnostics
+\mutable\let\currentsymbolfont\empty
+\mutable\let\askedsymbolfont \empty
+
+\permanent\protected\def\setscaledstyledsymbolicfont#1#2#3% quite a slowdown, glyphfontfile
+ {\edef\askedsymbolfont{\truefontname{\glyphfontfile{#3}} at \the\dimexpr#2\dimexpr\currentfontbodyscale\dimexpr#1}%
+ \ifcsname\??symbolfont\askedsymbolfont\endcsname
+ \lastnamedcs
+ \else
+ \font_basics_define_symbolic_font
+ \fi}
+
+\permanent\protected\def\setscaleddirectsymbolicfont#1#2#3% quite a slowdown, glyphfontfile
+ {\edef\askedsymbolfont{\truefontname{#3} at \the\dimexpr#2\dimexpr\currentfontbodyscale\dimexpr#1}%
+ \ifcsname\??symbolfont\askedsymbolfont\endcsname
+ \lastnamedcs
+ \else
+ \font_basics_define_symbolic_font
+ \fi}
+
+\permanent\protected\def\setstyledsymbolicfont#fontname% quite a slowdown, glyphfontfile
+ {\edef\askedsymbolfont{\truefontname{\glyphfontfile{#fontname}} at \the\dimexpr\currentfontbodyscale\dimexpr\fontbody}%
+ \ifcsname\??symbolfont\askedsymbolfont\endcsname
+ \lastnamedcs
+ \else
+ \font_basics_define_symbolic_font
+ \fi}
+
+\permanent\protected\def\setdirectsymbolicfont#fontname%
+ {\edef\askedsymbolfont{\truefontname{#fontname} at \the\dimexpr\currentfontbodyscale\dimexpr\fontbody}%
+ \ifcsname\??symbolfont\askedsymbolfont\endcsname
+ \lastnamedcs
+ \else
+ \font_basics_define_symbolic_font
+ \fi}
+
+\def\font_basics_define_symbolic_font
+ {\definefont[currentsymbolfont][\askedsymbolfont]%
+ \currentsymbolfont
+ \gletcsname\??symbolfont\askedsymbolfont\endcsname\lastrawfontcall}
+
+\permanent\protected\def\getnamedglyphstyled#fontname#character{{\setstyledsymbolicfont{#fontname}\clf_fontchar{#character}}}
+\permanent\protected\def\getnamedglyphdirect#fontname#character{{\setdirectsymbolicfont{#fontname}\clf_fontchar{#character}}}
+\permanent\protected\def\getglyphstyled #fontname#character{{\setstyledsymbolicfont{#fontname}\doifelsenumber{#character}\char\donothing#character}}
+\permanent\protected\def\getglyphdirect #fontname#character{{\setdirectsymbolicfont{#fontname}\doifelsenumber{#character}\char\donothing#character}}
+\permanent\protected\def\resolvedglyphstyled#fontname#character{{\setstyledsymbolicfont{#fontname}\clf_tochar{#character}}}
+\permanent\protected\def\resolvedglyphdirect#fontname#character{{\setdirectsymbolicfont{#fontname}\clf_tochar{#character}}}
+
+% this one is wrong:
+
+\permanent\protected\def\getscaledglyph#scale#name#content%
+ {{\setscaledstyledsymbolicfont\fontbody{#scale}{#name}\doifelsenumber{#content}\char\donothing#content}}
+
+\aliased\let\getglyph \getglyphstyled % old, soon obsolete
+\aliased\let\getrawglyph \getglyphdirect % old, soon obsolete
+\aliased\let\symbolicsizedfont\setscaledstyledsymbolicfont % old, soon obsolete
+\aliased\let\symbolicfont \setstyledsymbolicfont % old, soon obsolete
+
+\permanent\protected\def\symbolicscaledfont{\setscaledstyledsymbolicfont\fontbody}
+
+%D The last implementation of \type {\getglyph} permits definitions like:
+%D
+%D \starttyping
+%D \definefontsynonym [EuroSans] [eurose]
+%D \definefontsynonym [EuroSansBold] [euroseb]
+%D \definefontsynonym [EuroSansItalic] [eurosei]
+%D \definefontsynonym [EuroSansSlanted] [eurosei]
+%D \definefontsynonym [EuroSansBoldItalic] [eurosebi]
+%D \definefontsynonym [EuroSansBoldSlanted] [eurosebi]
+%D
+%D \definesymbol [euro] [\getglyph{Euro}{\char160}]
+%D
+%D \def\euro{\symbol[euro]}
+%D \stoptyping
+%D
+%D These definitions guarantee that the next calls work okay:
+%D
+%D \starttyping
+%D \ss \tf\euro \bf\euro \sla\euro \itd\euro \bs\euro \bic\euro
+%D \stoptyping
+%D
+%D The shape as well as the size is adapted to the current environment.
+
+%D \macros
+%D {setfont}
+%D
+%D Every now and then we want to define a font directly, for instance when we
+%D typeset title pages. The next macro saves some typing:
+
+\mutable\let\nextfont\relax % this one could be more private but this is okay
+
+\permanent\protected\def\setfont% geen \font_helpers_set_font mogelijk
+ {\afterassignment\font_basics_set_font\font\nextfont=}
+
+\def\font_basics_set_font
+ {\nextfont\setupinterlinespace}% hm, we need to use \setuplocalinterlinespace
+
+%D One can call this macro as:
+%D
+%D \starttyping
+%D \setfont cmr10 at 60pt
+%D \stoptyping
+%D
+%D After which the font is active and the baselines and struts are set.
+
+%D \macros{doiffontcharelse}
+
+\permanent\protected\def\doifelsefontchar#specification#unicode% this could be a direct lua call
+ {\begingroup
+ \font_basics_define_font_without_parameters{thedefinedfont}{#specification}%
+ \iffontchar\font#unicode\relax
+ \endgroup\expandafter\firstoftwoarguments
+ \else
+ \endgroup\expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiffontcharelse\doifelsefontchar
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-tex.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-tex.lmt
new file mode 100644
index 00000000000..c960953a700
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-tex.lmt
@@ -0,0 +1,160 @@
+if not modules then modules = { } end modules ['font-mpf'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local formatters = string.formatters
+local sortedhash = table.sortedhash
+
+local addcharacters = fonts.constructors.addcharacters
+local fontdata = fonts.hashes.identifiers
+
+local otf = fonts.handlers.otf
+local otfregister = otf.features.register
+
+----- getshapes = fonts.dropins.getshapes
+
+local register = fonts.collections.register
+local checkenabled = fonts.collections.checkenabled
+local newprivateslot = fonts.helpers.newprivateslot
+
+local currentfont = font.current
+
+-- hm, seems to gobble the first \char
+
+local makesetups = formatters["box:%s:%S"]
+
+local function makeglyphbox(char,spec)
+ token.expand_macro("makeglyphbox",true,spec.setups or tostring(char))
+ local b = tex.takebox("glyphbox")
+ spec.code = {
+ width = b.width,
+ height = b.height,
+ depth = b.depth,
+ objnum = tex.saveboxresource(b,nil,nil,true),
+ }
+end
+
+local function setboxglyphs(category,fontid,unicode,specification)
+ local box = specification.code
+ if unicode and box then
+ local tfmdata = fontdata[fontid]
+ local characters = tfmdata.characters
+ local newdata = {
+ width = box.width or 0,
+ height = box.height or 0,
+ depth = box.depth or 0,
+ unicode = unicode,
+ }
+ -- pass dimensions to lua
+ characters[unicode] = newdata
+ -- this also adapts newdata (add commands)
+ fonts.dropins.swapone("box",tfmdata,specification,unicode)
+ -- pass dimensions to tex
+ addcharacters(fontid, { characters = { [unicode] = newdata } })
+ return fontid, unicode
+ else
+ logs.report("box glyph", "invalid glyph box for %C",unicode)
+ end
+end
+
+local function setboxglyph(specification)
+ if specification then
+ local name = specification.name
+ local unicode = specification.unicode
+ local font = currentfont()
+ if not unicode and name then
+ unicode = newprivateslot(name)
+ specification.unicode = unicode
+ end
+ if unicode then
+ if not specification.setups then
+ specification.setups = name
+ end
+ -- we can actually delay font if needed
+ register(font,unicode,function(font,private)
+ makeglyphbox(unicode,specification)
+ return setboxglyphs(category,font,unicode,specification)
+ end)
+ checkenabled()
+ end
+ end
+end
+
+local boxes = table.setmetatableindex("table")
+
+function fonts.helpers.registerglyphbox(specification)
+ local category = specification.category
+ local whatever = specification.unicode or specification.name
+ if category and whatever then
+ boxes[category][whatever] = { action = makeglyphbox } -- always
+ end
+end
+
+local function initializebox(tfmdata,kind,value)
+ local boxes = boxes[value]
+ if value then
+ local font = tfmdata.properties.id
+ -- just preregister the list
+ for char, spec in sortedhash(boxes) do
+ spec.setups = makesetups(value,char)
+ if type(char) == "string" then
+ char = newprivateslot(name)
+ end
+ -- check if spec.code
+ register(font,char,function(font,char)
+ local action = spec.action
+ if type(action) == "function" then
+ action(char,spec)
+ end
+ return setboxglyphs(value,font,char,spec)
+ end)
+ end
+ checkenabled()
+ end
+end
+
+fonts.helpers.setboxglyphs = setboxglyphs
+fonts.helpers.setboxglyph = setboxglyph
+
+interfaces.implement {
+ name = "registerboxglyph", -- combine with next one
+ public = true,
+ protected = true,
+ actions = fonts.helpers.registerglyphbox,
+ arguments = { {
+ { "category" },
+ { "unicode", "integer" },
+ { "name" },
+ } },
+}
+
+interfaces.implement {
+ name = "setboxglyph",
+ public = true,
+ protected = true,
+ actions = setboxglyph,
+ arguments = { {
+ { "category" },
+ { "unicode", "integer" },
+ { "name" },
+ { "*" }
+ } },
+}
+
+fonts.handlers.otf.features.register {
+ name = "box",
+ description = "box glyphs",
+ manipulators = {
+ base = initializebox,
+ node = initializebox,
+ }
+}
+
+-- fonts.helpers.registerboxglyph { category = "demo", unicode = 103 }
+-- fonts.helpers.registerboxglyph { category = "demo", unicode = 104 }
+-- fonts.helpers.registerboxglyph { category = "demo", unicode = 105 }
+-- fonts.helpers.registerboxglyph { category = "demo", unicode = 106 }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-tex.mkxl b/Master/texmf-dist/tex/context/base/mkxl/font-tex.mkxl
new file mode 100644
index 00000000000..ae936be3190
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-tex.mkxl
@@ -0,0 +1,121 @@
+%D \module
+%D [ file=font-tex,
+%D version=2021.23.01,
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=\TEX\ Glyphs,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / TeX Glyphs}
+
+\registerctxluafile{font-tex}{autosuffix}
+
+%D This mechanism turns boxes into glyphs. Of course this assumes that the
+%D backend code supports it. It's an experiment and we can do similar things
+%D with \METAPOST. The interface is not frozen and might change and evolve.
+
+%D \starttyping
+%D \starttext
+%D
+%D \startsetups box:demo:103
+%D \externalfigure[t:/sources/cow-brown.pdf][height=12pt]
+%D \stopsetups
+%D
+%D \startsetups box:demo:104
+%D \externalfigure[t:/sources/cow-black.pdf][height=10pt]
+%D \stopsetups
+%D
+%D \startsetups box:demo:105
+%D \externalfigure[t:/sources/mill.png][height=12pt]
+%D \stopsetups
+%D
+%D \startsetups box:demo:106
+%D \externalfigure[t:/sources/hacker.jpg][height=12pt]
+%D \stopsetups
+%D
+%D \registerboxglyph category {demo} unicode 103 \relax % rather low level for now
+%D \registerboxglyph category {demo} unicode 104 \relax % rather low level for now
+%D \registerboxglyph category {demo} unicode 105 \relax % rather low level for now
+%D \registerboxglyph category {demo} unicode 106 \relax % rather low level for now
+%D
+%D \definefontfeature[whatever][box=demo]
+%D
+%D \definedfont[Serif*whatever]
+%D
+%D % \enabletrackers[fonts.collecting]
+%D
+%D \startTEXpage%[offset=0pt]
+%D %\hbox{g}%
+%D \hbox{\char103}%
+%D \hbox{\char104}%
+%D \stopTEXpage
+%D
+%D \startsetups box:demo:whatever
+%D \startMPcode
+%D picture p ; p := image (draw figure "t:/sources/hacker.jpg" ysized 10pt);
+%D fill boundingbox p enlarged .5pt randomized 2pt withcolor "darkgray" ;
+%D clip p to boundingbox p enlarged -1pt randomized 2pt ;
+%D draw p ;
+%D \stopMPcode
+%D \stopsetups
+%D
+%D % in current font!
+%D
+%D \iffontchar\font\privatecharactercode{demo:whatever}\else
+%D \setboxglyph {
+%D category {demo}
+%D name {demo:whatever}
+%D setups {box:demo:whatever}
+%D }%
+%D \fi
+%D
+%D \startTEXpage[offset=10pt]
+%D \char\privatecharactercode{demo:whatever}%
+%D \stopTEXpage
+%D
+%D \startTEXpage%[offset=0pt]
+%D \ruledhbox{\char103}
+%D \ruledhbox{\char104}
+%D \ruledhbox{\char105}
+%D \ruledhbox{\char106}
+%D \stopTEXpage
+%D
+%D \dorecurse{1000}{
+%D \char 103\relax \space
+%D \char 104\relax \space
+%D \char 105\relax \space
+%D \char 106\relax \space
+%D }
+%D
+%D \page
+%D
+%D \setupalign[verytolerant,flushleft]
+%D
+%D \dorecurse{100}{
+%D #1:
+%D \glyph yoffset -2pt `i\relax
+%D \glyph yoffset -3pt `j\relax\space
+%D }
+%D \stoptext
+%D \stoptyping
+
+\newbox\glyphbox
+
+% somehow makes the first \char disappear
+%
+% \permanent\protected\def\makeglyphbox#1%
+% {\setbox\glyphbox\hbox{\directsetup{#1}}}
+%
+% so we group:
+
+\permanent\protected\def\makeglyphbox#1%
+ {\begingroup
+ \global\setbox\glyphbox\hbox{\directsetup{#1}}%
+ \finalizeobjectbox\glyphbox
+ % \writestatus{glyphbox}{#1 => (\the\wd\glyphbox,\the\ht\glyphbox,\the\dp\glyphbox)}%
+ \endgroup}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-tra.mkxl b/Master/texmf-dist/tex/context/base/mkxl/font-tra.mkxl
new file mode 100644
index 00000000000..625e2fe0b87
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-tra.mkxl
@@ -0,0 +1,118 @@
+%D \module
+%D [ file=font-tra,
+%D version=2009.01.02, % or so
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Tracing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\writestatus{loading}{ConTeXt Font Macros / Tracing}
+
+%D just use fontid
+
+%D \macros
+%D {showbodyfont}
+%D
+%D One can call for a rather simple overview of a bodyfont and the relations between
+%D its alternative fonts.
+%D
+%D \showsetup{showbodyfont}
+%D
+%D The current bodyfont (here we omitted the argument) looks like:
+%D
+%D \showbodyfont
+%D
+%D The implementation is rather straightforward in using \type {\halign}.
+
+\fetchmodulecommand \showbodyfont \f!font_run
+
+%D \macros
+%D {showfontstrip, showminimalbaseline}
+%D
+%D The next command can come in handy when combining different fonts into a
+%D collection (typeface) and determining optimal baseline distances.
+%D
+%D \showfontstrip \blank \showminimalbaseline
+
+\fetchmodulecommand \showfontstrip \f!font_run
+\fetchmodulecommand \showminimalbaseline \f!font_run
+
+%D \macros
+%D {showkerning}
+%D
+%D A goody is:
+%D
+%D \showkerning{Can you guess what kerning is?}
+
+\fetchmodulecommand \showkerning \f!font_run
+
+%D \macros
+%D {showbodyfontenvironment,showfont,showfontstyle,showligatures}
+%D
+%D The current bodyfontenvironment is:
+%D
+%D \showbodyfontenvironment
+%D
+%D This overview is generated using:
+%D
+%D \showsetup{showbodyfontenvironment}
+
+\fetchmodulecommand \showbodyfontenvironment \f!font_run
+
+%D The following command generates a fontmap:
+%D
+%D \startbuffer
+%D \showfont[SansBold at 12pt]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \getbuffer
+
+\fetchmodulecommand \showfont \f!font_run
+\fetchmodulecommand \showfontstyle \f!font_run
+\fetchmodulecommand \showligature \f!font_run
+\fetchmodulecommand \showligatures \f!font_run
+\fetchmodulecommand \showcharratio \f!font_run
+\fetchmodulecommand \showfontparameters \f!font_run
+
+\permanent\protected\def\showchardata#1{\ctxcommand{showchardata("#1")}} % todo: direct implementor
+\permanent\protected\def\showfontdata {\ctxcommand{showfontparameters()}} % todo: direct implementor
+
+%D \macros
+%D {doiffontpresentelse}
+%D
+%D \starttyping
+%D \doiffontpresentelse{texnansi-lmr10}{YES}{NO}
+%D \doiffontpresentelse{adam-lindsay-modern-serif}{YES}{NO}
+%D \stoptyping
+
+\permanent\protected\def\doifelsefontpresent#1{\clf_doifelsefontpresent{#1}}
+
+\aliased\let\doiffontpresentelse\doifelsefontpresent
+
+%D The otf tracer code is now moved. If the individual commands are needed
+%D one can just load the module.
+
+\fetchmodulecommand \showotfcomposition \f!font_run
+\fetchmodulecommand \showotfcompositionlist \f!font_run
+\fetchmodulecommand \startotfsample \f!font_run
+\fetchmodulecommand \startotfcompositionlist \f!font_run
+
+% \permanent\protected\def\savefontdata[#1]% not yet in i-*.xml
+% {\begingroup
+% \getdummyparameters[#1]%
+% \clf_savefont {
+% filename {\dummyparameter\c!file}
+% fontname {\dummyparameter\c!name}
+% method {\dummyparameter\c!method}
+% }%
+% \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-uni.mkxl b/Master/texmf-dist/tex/context/base/mkxl/font-uni.mkxl
new file mode 100644
index 00000000000..390f17468ff
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-uni.mkxl
@@ -0,0 +1,27 @@
+%D \module
+%D [ file=font-uni,
+%D version=2008.11.03, % 1999.10.10,
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=\UNICODE,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Unicode}
+
+%D In \MKIV\ we only provide the \type {\uchar} macro and implement it as just an
+%D \UTF\ converter. We expand it so best not use not use it for active characters.
+%D In practice this command is probably never used anyway but we keep if for old
+%D styles.
+
+\unprotect
+
+\permanent\def\uchar#1#2{\clf_uchar\numexpr#1\relax\numexpr#2\relax}
+
+\aliased\let\uc\uchar
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-unk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/font-unk.mkxl
new file mode 100644
index 00000000000..986a5c9bfa2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-unk.mkxl
@@ -0,0 +1,150 @@
+%D \module
+%D [ file=font-unk,
+%D version=1998.09.10,
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Unknown Defaults,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module is rather important, because it enables us to define and call for not
+%D yet defined fonts in a way completely independant of real font names. First we
+%D map some meaningful names onto unknown filenames.
+
+\unprotect
+
+\definefontsynonym [Serif] [unknown]
+\definefontsynonym [SerifBold] [unknown]
+\definefontsynonym [SerifItalic] [unknown]
+\definefontsynonym [SerifSlanted] [unknown]
+\definefontsynonym [SerifBoldItalic] [unknown]
+\definefontsynonym [SerifBoldSlanted] [unknown]
+\definefontsynonym [SerifCaps] [unknown]
+
+\definefontsynonym [Sans] [unknown]
+\definefontsynonym [SansBold] [unknown]
+\definefontsynonym [SansItalic] [unknown]
+\definefontsynonym [SansSlanted] [unknown]
+\definefontsynonym [SansBoldItalic] [unknown]
+\definefontsynonym [SansBoldSlanted] [unknown]
+\definefontsynonym [SansCaps] [unknown]
+
+\definefontsynonym [Mono] [unknown]
+\definefontsynonym [MonoBold] [unknown]
+\definefontsynonym [MonoItalic] [unknown]
+\definefontsynonym [MonoSlanted] [unknown]
+\definefontsynonym [MonoBoldItalic] [unknown]
+\definefontsynonym [MonoBoldSlanted] [unknown]
+\definefontsynonym [MonoCaps] [unknown]
+
+\definefontsynonym [MathRoman] [unknown]
+\definefontsynonym [MathRomanL2R] [MathRoman]
+\definefontsynonym [MathRomanR2L] [MathRoman]
+\definefontsynonym [MathRomanBold] [MathRoman]
+\definefontsynonym [MathRomanBoldL2R] [MathRomanBold]
+\definefontsynonym [MathRomanBoldR2L] [MathRomanBold]
+
+\definefontsynonym [Handwriting] [unknown]
+\definefontsynonym [Calligraphic] [unknown]
+
+%D This permit us to define (use) fonts that refer to the default style (so, Bold
+%D may expand to SansBold or SerifBold, depending on the default style in the
+%D typeface). The \LUA\ call is used to set the current alternative in a
+%D non|-|interfering way. This make sure that the CurrentFont synonym (in font-sym)
+%D is working as expected (e.g.\ in MixedCaps). Yes, this is complicates stuff.
+
+\definefontsynonym[\s!Normal] [\noexpand\clf_tf\noexpand\v_font_string_d]
+\definefontsynonym[\s!Bold] [\noexpand\clf_bf\noexpand\v_font_string_d\noexpand\s!Bold]
+\definefontsynonym[\s!Italic] [\noexpand\clf_it\noexpand\v_font_string_d\noexpand\s!Italic]
+\definefontsynonym[\s!Slanted] [\noexpand\clf_sl\noexpand\v_font_string_d\noexpand\s!Slanted]
+\definefontsynonym[\s!BoldItalic] [\noexpand\clf_bi\noexpand\v_font_string_d\noexpand\s!BoldItalic]
+\definefontsynonym[\s!BoldSlanted][\noexpand\clf_bs\noexpand\v_font_string_d\noexpand\s!BoldSlanted]
+\definefontsynonym[\s!Caps] [\noexpand\clf_tf\noexpand\v_font_string_d\noexpand\s!Caps]
+
+%D Also handy:
+
+\definefontsynonym [Regular] [Serif]
+\definefontsynonym [RegularBold] [SerifBold]
+\definefontsynonym [RegularItalic] [SerifItalic]
+\definefontsynonym [RegularSlanted] [SerifSlanted]
+\definefontsynonym [RegularBoldItalic] [SerifBoldItalic]
+\definefontsynonym [RegularBoldSlanted] [SerifBoldSlanted]
+\definefontsynonym [RegularCaps] [SerifCaps]
+
+\definefontsynonym [Support] [Sans]
+\definefontsynonym [SupportBold] [SansBold]
+\definefontsynonym [SupportItalic] [SansItalic]
+\definefontsynonym [SupportSlanted] [SansSlanted]
+\definefontsynonym [SupportBoldItalic] [SansBoldItalic]
+\definefontsynonym [SupportBoldSlanted] [SansBoldSlanted]
+\definefontsynonym [SupportCaps] [SansCaps]
+
+%D Well, not that good an idea:
+
+\definefontsynonym [Roman] [Serif]
+\definefontsynonym [RomanBold] [SerifBold]
+\definefontsynonym [RomanItalic] [SerifItalic]
+\definefontsynonym [RomanSlanted] [SerifSlanted]
+\definefontsynonym [RomanBoldItalic] [SerifBoldItalic]
+\definefontsynonym [RomanBoldSlanted] [SerifBoldSlanted]
+\definefontsynonym [RomanCaps] [SerifCaps]
+
+\definefontsynonym [Type] [Mono]
+\definefontsynonym [TypeBold] [MonoBold]
+\definefontsynonym [TypeItalic] [MonoItalic]
+\definefontsynonym [TypeSlanted] [MonoSlanted]
+\definefontsynonym [TypeBoldItalic] [MonoBoldItalic]
+\definefontsynonym [TypeBoldSlanted] [MonoBoldSlanted]
+\definefontsynonym [TypeCaps] [MonoCaps]
+
+%D Next we define roman, sans and monospaced font sets.
+
+\definebodyfont [default] [rm]
+ [\s!tf=Serif sa 1,
+ \s!bf=SerifBold sa 1,
+ \s!it=SerifItalic sa 1,
+ \s!sl=SerifSlanted sa 1,
+ \s!bi=SerifBoldItalic sa 1,
+ \s!bs=SerifBoldSlanted sa 1,
+ \s!sc=SerifCaps sa 1]
+
+\definebodyfont [default] [ss]
+ [\s!tf=Sans sa 1,
+ \s!bf=SansBold sa 1,
+ \s!it=SansItalic sa 1,
+ \s!sl=SansSlanted sa 1,
+ \s!bi=SansBoldItalic sa 1,
+ \s!bs=SansBoldSlanted sa 1,
+ \s!sc=SansCaps sa 1]
+
+\definebodyfont [default] [tt]
+ [\s!tf=Mono sa 1,
+ \s!bf=MonoBold sa 1,
+ \s!it=MonoItalic sa 1,
+ \s!sl=MonoSlanted sa 1,
+ \s!bi=MonoBoldItalic sa 1,
+ \s!bs=MonoBoldSlanted sa 1,
+ \s!sc=MonoCaps sa 1]
+
+\definebodyfont [default] [mm]
+ [\s!mr =MathRoman mo 1,
+ \s!mrlr=MathRomanL2R mo 1,
+ \s!mrrl=MathRomanR2L mo 1,
+ \s!mb =MathRomanBold mo 1,
+ \s!mblr=MathRomanBoldL2R mo 1,
+ \s!mbrl=MathRomanBoldR2L mo 1]
+
+\definebodyfont [default] [hw]
+ [\s!tf=Handwriting sa 1]
+
+\definebodyfont [default] [cg]
+ [\s!tf=Calligraphy sa 1]
+
+%D These definitions come into action as soon as names are mapped onto real file
+%D names (or names that themselves are mapped).
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-var.mklx b/Master/texmf-dist/tex/context/base/mkxl/font-var.mklx
new file mode 100644
index 00000000000..421521f50f5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-var.mklx
@@ -0,0 +1,57 @@
+%D \module
+%D [ file=font-var,
+%D version=2011.01.13, % (copied fron font-ini)
+%D title=\CONTEXT\ Font Macros,
+%D subtitle=Common Variables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Font Macros / Common Variables}
+
+\unprotect
+
+% some more will move here
+
+%D \macros
+%D {fontstyle,fontalternative,fontsize,fontbody}
+%D
+%D Within a bodyfont, fonts can come in different sizes. For instance \type {\tf} is
+%D accompanied by \type {\tfa}, \type {\tfb} etc. The first two characters denote
+%D the alternative, while the third character in these sequences represents the
+%D size. The actual size is saved in a macro. The styles, being roman (\type {\rm}),
+%D sans serif (\type {\ss}) etc. are also available.
+
+% fontclass % modern dejavu
+% fontbody % ... 10pt 11pt 12pt ...
+% fontstyle % rm ss tt mm hw cg ...
+% fontalternative % tf bf sl it bs bi sc ...
+% fontsize % a b c d ...
+% fontface % 0=normal 1=text 2=script 3=scriptscript 4=x 5=xx (math are temporary)
+
+\mutable\def\normalizedbodyfontsize{12pt}
+
+\immutable\def\defaultfontbody {\normalizedbodyfontsize}
+\immutable\let\defaultfontstyle \s!rm
+\immutable\let\defaultfontalternative\s!tf
+\immutable\let\defaultfontsize \empty
+\immutable\let\defaultfontface \!!zerocount
+
+\mutable\let\globalfontclass\empty
+\mutable\let\fontclass \empty
+\mutable\let\fontbody \defaultfontbody
+\mutable\let\fontstyle \defaultfontstyle
+\mutable\let\fontalternative\defaultfontalternative
+\mutable\let\fontsize \defaultfontsize
+\mutable\let\fontface \!!zerocount
+
+\installmacrostack\fontstyle
+
+% we can use an indirect mapping for fontclasses (map string onto numbers) and indeed this
+% is somewhat more efficient but also makes the code messy ... maybe some day ...
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-vfc.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-vfc.lmt
new file mode 100644
index 00000000000..9330bdea0b2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-vfc.lmt
@@ -0,0 +1,181 @@
+if not modules then modules = { } end modules ['font-vfc'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv and hand-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local select, type = select, type
+local insert = table.insert
+
+local fonts = fonts
+local helpers = fonts.helpers
+
+local setmetatableindex = table.setmetatableindex
+----- makeweak = table.makeweak
+
+-- Helpers dealing with virtual fonts: beware, these are final values so
+-- don't change the content of tables fetched from here!
+
+local push = { "push" }
+local pop = { "pop" }
+local dummy = { "comment" }
+
+function helpers.prependcommands(commands,...)
+ insert(commands,1,push)
+ for i=select("#",...),1,-1 do
+ local s = (select(i,...))
+ if s then
+ insert(commands,1,s)
+ end
+ end
+ insert(commands,pop)
+ return commands
+end
+
+function helpers.appendcommands(commands,...)
+ insert(commands,1,push)
+ insert(commands,pop)
+ for i=1,select("#",...) do
+ local s = (select(i,...))
+ if s then
+ insert(commands,s)
+ end
+ end
+ return commands
+end
+
+function helpers.prependcommandtable(commands,t)
+ insert(commands,1,push)
+ for i=#t,1,-1 do
+ local s = t[i]
+ if s then
+ insert(commands,1,s)
+ end
+ end
+ insert(commands,pop)
+ return commands
+end
+
+function helpers.appendcommandtable(commands,t)
+ insert(commands,1,push)
+ insert(commands,pop)
+ for i=1,#t do
+ local s = t[i]
+ if s then
+ insert(commands,s)
+ end
+ end
+ return commands
+end
+
+helpers.commands = utilities.storage.allocate {
+ char = setmetatableindex(function(t,k) local v = { "slot", 0, k } t[k] = v return v end),
+ right = setmetatableindex(function(t,k) local v = { "right", k } t[k] = v return v end),
+ left = setmetatableindex(function(t,k) local v = { "left", k } t[k] = v return v end),
+ down = setmetatableindex(function(t,k) local v = { "down", k } t[k] = v return v end),
+ up = setmetatableindex(function(t,k) local v = { "up", k } t[k] = v return v end),
+ push = push,
+ pop = pop,
+ dummy = dummy,
+}
+
+helpers.vfinjectors = {
+ -- backend specific: startcolor, stopcolor etc
+}
+
+-- maybe round()
+
+local defaultline = 16384
+
+helpers.vfspecials = {
+
+ backgrounds = setmetatableindex(function(t,h)
+ local v = setmetatableindex(function(t,d)
+ local v = setmetatableindex(function(t,w)
+ local v = { "frame", w, h, d, defaultline, true, true }
+ t[w] = v
+ return v
+ end)
+ t[d] = v
+ return v
+ end)
+ t[h] = v
+ return v
+ end),
+
+ outlines = setmetatableindex(function(t,h)
+ local v = setmetatableindex(function(t,d)
+ local v = setmetatableindex(function(t,w)
+ local v = { "frame", w, h, d, defaultline, false, true }
+ t[w] = v
+ return v
+ end)
+ t[d] = v
+ return v
+ end)
+ t[h] = v
+ return v
+ end),
+
+}
+
+-- In the past we only copied when we had something that got scaled but the problem
+-- is that we then run into issues when we extend a commands in the parent. This
+-- needs checking. When we go to glyph scaling, this can go away.
+--
+-- The alternative is to work with unscaled values and always do scaling at the
+-- TeX end. So, then we need accessors instead of tables and we also need to check
+-- all the context code: where do we access values?
+--
+-- On the other hand, in glyph scale mode we hardly do any scaling so we seldom
+-- call the next one.
+--
+-- What if we use named fields: then we can scale by field name.
+
+local scaled = {
+ right = true,
+ down = true,
+ left = true,
+ right = true,
+ offset = true,
+ rule = true,
+ char = false,
+ font = false,
+ slot = false,
+ use = false,
+ push = false,
+ pop = false,
+ lua = false,
+ -- obsolete
+ node = false,
+ -- additional ones are never scaled (color etc)
+}
+
+function helpers.scalecommands(list,hdelta,vdelta)
+ local n = #list
+ for i=1,n do
+ local key = list[i][1]
+ if scaled[list[i][1]] then
+ local result = { }
+ for i=1,n do
+ local cmd = list[i]
+ local key = ivc[1]
+ if key == "right" or key == "left" then
+ result[i] = { key, cmd[2]*hdelta }
+ elseif key == "down" or key == "up" then
+ result[i] = { key, cmd[2]*vdelta }
+ elseif key == "offset" or key == "rule" then
+ result[i] = { key, cmd[2]*hdelta, cmd[3]*vdelta }
+ -- elseif key == "frame" then
+ -- result[i] = cmd -- already scaled, for now
+ else
+ result[i] = cmd -- shared since in cache and untouched
+ end
+ end
+ return result
+ end
+ end
+ return list
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-vir.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-vir.lmt
new file mode 100644
index 00000000000..f60639d6ad8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/font-vir.lmt
@@ -0,0 +1,159 @@
+if not modules then modules = { } end modules ['font-vir'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is actually very experimental code but it has been so since we began with
+-- \LUATEX\ so \unknown. We keep it around because it's not that much code and
+-- because we have some demo's (in the test suite) that use it.
+
+local next, setmetatable, getmetatable = next, setmetatable, getmetatable
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local fastcopy = table.fastcopy
+
+local fonts = fonts
+local constructors = fonts.constructors
+local vf = constructors.handlers.vf
+vf.version = 1.000 -- same as tfm
+
+local definers = fonts.definers
+local methods = definers.methods
+
+local variants = allocate()
+local combinations = { }
+local combiner = { }
+local whatever = allocate()
+local helpers = allocate()
+local predefined = fonts.helpers.commands
+
+methods.variants = variants -- todo .. wrong namespace
+vf.combinations = combinations
+vf.combiner = combiner
+vf.whatever = whatever
+vf.helpers = helpers
+vf.predefined = predefined
+
+setmetatableindex(whatever, function(t,k) local v = { } t[k] = v return v end)
+
+local function checkparameters(g,f)
+ if f and g and not g.parameters and #g.fonts > 0 then
+ local p = { }
+ for k,v in next, f.parameters do
+ p[k] = v
+ end
+ g.parameters = p
+ setmetatable(p, getmetatable(f.parameters))
+ end
+end
+
+function methods.install(tag, rules)
+ vf.combinations[tag] = rules
+ variants[tag] = function(specification)
+ return vf.combine(specification,tag)
+ end
+end
+
+local function combine_load(g,name)
+ return constructors.readanddefine(name or g.specification.name,g.specification.size)
+end
+
+local function combine_assign(g, name, from, to, start, force)
+ local f, id = combine_load(g,name)
+ if f and id then
+ -- optimize for whole range, then just g = f
+ if not from then from, to = 0, 0xFF00 end
+ if not to then to = from end
+ if not start then start = from end
+ local fc = f.characters
+ local gc = g.characters
+ local fd = f.descriptions
+ local gd = g.descriptions
+ local hn = #g.fonts+1
+ g.fonts[hn] = { id = id } -- no need to be sparse
+ for i=from,to do
+ if fc[i] and (force or not gc[i]) then
+ gc[i] = fastcopy(fc[i],true) -- can be optimized
+ gc[i].commands = { { "slot", hn, start } }
+ gd[i] = fd[i]
+ end
+ start = start + 1
+ end
+ checkparameters(g,f)
+ end
+end
+
+local function combine_process(g,list)
+ if list then
+ for _,v in next, list do
+ (combiner.commands[v[1]] or nop)(g,v)
+ end
+ end
+end
+
+local function combine_names(g,name,force)
+ local f, id = constructors.readanddefine(name,g.specification.size)
+ if f and id then
+ local fc = f.characters
+ local gc = g.characters
+ local fd = f.descriptions
+ local gd = g.descriptions
+ g.fonts[#g.fonts+1] = { id = id } -- no need to be sparse
+ local hn = #g.fonts
+ for k, v in next, fc do
+ if force or not gc[k] then
+ gc[k] = fastcopy(v,true)
+ gc[k].commands = { { "slot", hn, k } }
+ gd[i] = fd[i]
+ end
+ end
+ checkparameters(g,f)
+ end
+end
+
+local combine_feature = function(g,v)
+ local key = v[2]
+ local value = v[3]
+ if key then
+ if value == nil then
+ value = true
+ end
+ local specification = g.specification
+ if specification then
+ local normalfeatures = specification.features.normal
+ if normalfeatures then
+ normalfeatures[key] = value -- otf?
+ end
+ end
+ end
+end
+
+combiner.commands = allocate {
+ ["initialize"] = function(g,v) combine_assign (g,g.properties.name) end,
+ ["include-method"] = function(g,v) combine_process (g,combinations[v[2]]) end, -- name
+ -- ["copy-parameters"] = function(g,v) combine_parameters(g,v[2]) end, -- name
+ ["copy-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],true) end, -- name, from-start, from-end, to-start
+ ["copy-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],true) end, -- name, from, to
+ ["fallback-range"] = function(g,v) combine_assign (g,v[2],v[3],v[4],v[5],false) end, -- name, from-start, from-end, to-start
+ ["fallback-char"] = function(g,v) combine_assign (g,v[2],v[3],v[3],v[4],false) end, -- name, from, to
+ ["copy-names"] = function(g,v) combine_names (g,v[2],true) end,
+ ["fallback-names"] = function(g,v) combine_names (g,v[2],false) end,
+ ["feature"] = combine_feature,
+}
+
+function vf.combine(specification,tag)
+ local g = {
+ name = specification.name,
+ properties = { },
+ fonts = { },
+ characters = { },
+ descriptions = { },
+ specification = fastcopy(specification),
+ }
+ combine_process(g,combinations[tag])
+ return g
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/good-mth.lmt b/Master/texmf-dist/tex/context/base/mkxl/good-mth.lmt
new file mode 100644
index 00000000000..87b6787df6a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/good-mth.lmt
@@ -0,0 +1,464 @@
+if not modules then modules = { } end modules ['good-mth'] = {
+ version = 1.000,
+ comment = "companion to font-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next, tonumber, unpack = type, next, tonumber, unpack
+local ceil = math.ceil
+local match = string.match
+
+local fonts = fonts
+
+local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
+local report_goodies = logs.reporter("fonts","goodies")
+
+local registerotffeature = fonts.handlers.otf.features.register
+
+local fontgoodies = fonts.goodies or { }
+
+local fontcharacters = fonts.hashes.characters
+
+local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
+
+local report_math = logs.reporter("mathematics","initializing")
+
+local nuts = nodes.nuts
+
+local setlink = nuts.setlink
+
+local nodepool = nuts.pool
+
+local new_kern = nodepool.kern
+local new_glyph = nodepool.glyph
+local new_hlist = nodepool.hlist
+local new_vlist = nodepool.vlist
+
+local insert_node_after = nuts.insert_after
+
+local helpers = fonts.helpers
+local upcommand = helpers.commands.up
+local rightcommand = helpers.commands.right
+local charcommand = helpers.commands.char
+local prependcommands = helpers.prependcommands
+
+-- experiment, we have to load the definitions immediately as they precede
+-- the definition so they need to be initialized in the typescript
+
+local function withscriptcode(tfmdata,unicode,data,action)
+ if type(unicode) == "string" then
+ local p, u = match(unicode,"^(.-):(.-)$")
+ if u then
+ u = tonumber(u)
+ if u then
+ local slots = fonts.helpers.mathscriptslots(tfmdata,u)
+ if slots then
+ if p == "*" then
+ action(u,data)
+ for i=1,#slots do
+ action(slots[i],data)
+ end
+ else
+ p = tonumber(p)
+ if p then
+ action(slots[p],data)
+ end
+ end
+ end
+ end
+ end
+ else
+ action(unicode,data)
+ end
+end
+
+local function finalize(tfmdata,feature,value)
+-- if tfmdata.mathparameters then -- funny, cambria text has this
+ local goodies = tfmdata.goodies
+ if goodies then
+ local virtualized = mathematics.virtualized
+ for i=1,#goodies do
+ local goodie = goodies[i]
+ local mathematics = goodie.mathematics
+ local dimensions = mathematics and mathematics.dimensions
+ if dimensions then
+ if trace_defining then
+ report_math("overloading dimensions in %a @ %p",tfmdata.properties.fullname,tfmdata.parameters.size)
+ end
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local parameters = tfmdata.parameters
+ local factor = parameters.factor
+ local hfactor = parameters.hfactor
+ local vfactor = parameters.vfactor
+ --
+ tfmdata.type = "virtual"
+ tfmdata.properties.virtualized = true
+ --
+ local function overloadone(unicode,data)
+ local character = characters[unicode]
+ if not character then
+ local c = virtualized[unicode]
+ if c then
+ character = characters[c]
+ end
+ end
+ if character then
+ local width = data.width
+ local height = data.height
+ local depth = data.depth
+ if trace_defining and (width or height or depth) then
+ report_math("overloading dimensions of %C, width %p, height %p, depth %p",
+ unicode,width or 0,height or 0,depth or 0)
+ end
+ if width then character.width = width * hfactor end
+ if height then character.height = height * vfactor end
+ if depth then character.depth = depth * vfactor end
+ --
+ local xoffset = data.xoffset
+ local yoffset = data.yoffset
+ if xoffset == "llx" then
+ local d = descriptions[unicode]
+ if d then
+ xoffset = - d.boundingbox[1] * hfactor
+ character.width = character.width + xoffset
+ xoffset = rightcommand[xoffset]
+ else
+ xoffset = nil
+ end
+ elseif xoffset and xoffset ~= 0 then
+ xoffset = rightcommand[xoffset * hfactor]
+ else
+ xoffset = nil
+ end
+ if yoffset and yoffset ~= 0 then
+ yoffset = upcommand[yoffset * vfactor]
+ else
+ yoffset = nil
+ end
+ if xoffset or yoffset then
+ local commands = characters.commands
+ if commands then
+ prependcommands(commands,yoffset,xoffset)
+ else
+ local slot = charcommand[unicode]
+ if xoffset and yoffset then
+ character.commands = { xoffset, yoffset, slot }
+ elseif xoffset then
+ character.commands = { xoffset, slot }
+ else
+ character.commands = { yoffset, slot }
+ end
+ end
+ end
+ elseif trace_defining then
+ report_math("no overloading dimensions of %C, not in font",unicode)
+ end
+ end
+ local function overload(dimensions)
+ for unicode, data in next, dimensions do
+ withscriptcode(tfmdata,unicode,data,overloadone)
+ end
+ end
+ if value == nil then
+ value = { "default" }
+ end
+ if value == "all" or value == true then
+ for name, value in next, dimensions do
+ overload(value)
+ end
+ else
+ if type(value) == "string" then
+ value = utilities.parsers.settings_to_array(value)
+ end
+ if type(value) == "table" then
+ for i=1,#value do
+ local d = dimensions[value[i]]
+ if d then
+ overload(d)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathdimensions",
+ description = "manipulate math dimensions",
+ -- default = true,
+ manipulators = {
+ base = finalize,
+ node = finalize,
+ }
+}
+
+local function initialize(goodies)
+ local mathgoodies = goodies.mathematics
+ if mathgoodies then
+ local virtuals = mathgoodies.virtuals
+ local mapfiles = mathgoodies.mapfiles
+ local maplines = mathgoodies.maplines
+ if virtuals then
+ for name, specification in next, virtuals do
+ -- beware, they are all constructed ... we should be more selective
+ mathematics.makefont(name,specification,goodies)
+ end
+ end
+ if mapfiles then
+ for i=1,#mapfiles do
+ fonts.mappings.loadfile(mapfiles[i]) -- todo: backend function
+ end
+ end
+ if maplines then
+ for i=1,#maplines do
+ fonts.mappings.loadline(maplines[i]) -- todo: backend function
+ end
+ end
+ end
+end
+
+fontgoodies.register("mathematics", initialize)
+
+-- local enabled = false directives.register("fontgoodies.mathkerning",function(v) enabled = v end)
+
+local function initialize(tfmdata)
+-- if enabled and tfmdata.mathparameters then -- funny, cambria text has this
+ if tfmdata.mathparameters then -- funny, cambria text has this
+ local goodies = tfmdata.goodies
+ if goodies then
+ local characters = tfmdata.characters
+ if characters[0x1D44E] then -- 119886
+ -- we have at least an italic a
+ for i=1,#goodies do
+ local mathgoodies = goodies[i].mathematics
+ if mathgoodies then
+ local kerns = mathgoodies.kerns
+ if kerns then
+ local function kernone(unicode,data)
+ local chardata = characters[unicode]
+ if chardata and (not chardata.mathkerns or data.force) then
+ chardata.mathkerns = data
+ end
+ end
+ for unicode, data in next, kerns do
+ withscriptcode(tfmdata,unicode,data,kernone)
+ end
+ return
+ end
+ end
+ end
+ else
+ return -- no proper math font anyway
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathkerns",
+ description = "math kerns",
+ -- default = true,
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+-- math italics (not really needed)
+--
+-- it would be nice to have a \noitalics\font option
+
+local function initialize(tfmdata)
+ local goodies = tfmdata.goodies
+ if goodies then
+ local shared = tfmdata.shared
+ for i=1,#goodies do
+ local mathgoodies = goodies[i].mathematics
+ if mathgoodies then
+ local mathitalics = mathgoodies.italics
+ if mathitalics then
+ local properties = tfmdata.properties
+ if properties.setitalics then
+ mathitalics = mathitalics[file.nameonly(properties.name)] or mathitalics
+ if mathitalics then
+ if trace_goodies then
+ report_goodies("loading mathitalics for font %a",properties.name)
+ end
+ local corrections = mathitalics.corrections
+ local defaultfactor = mathitalics.defaultfactor
+ -- properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change)
+ if corrections then
+ fontgoodies.registerpostprocessor(tfmdata, function(tfmdata) -- this is another tfmdata (a copy)
+ -- better make a helper so that we have less code being defined
+ local properties = tfmdata.properties
+ local parameters = tfmdata.parameters
+ local characters = tfmdata.characters
+ properties.mathitalic_defaultfactor = defaultfactor
+ properties.mathitalic_defaultvalue = defaultfactor * parameters.quad
+ if trace_goodies then
+ report_goodies("assigning mathitalics for font %a",properties.name)
+ end
+ local quad = parameters.quad
+ local hfactor = parameters.hfactor
+ for k, v in next, corrections do
+ local c = characters[k]
+ if c then
+ if v > -1 and v < 1 then
+ c.italic = v * quad
+ else
+ c.italic = v * hfactor
+ end
+ else
+ report_goodies("invalid mathitalics entry %U for font %a",k,properties.name)
+ end
+ end
+ end)
+ end
+ return -- maybe not as these can accumulate
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathitalics",
+ description = "additional math italic corrections",
+ -- default = true,
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+-- fontgoodies.register("mathitalics", initialize)
+
+local function mathradicalaction(n,h,v,font,mchar,echar)
+ local characters = fontcharacters[font]
+ local mchardata = characters[mchar]
+ local echardata = characters[echar]
+ local ewidth = echardata.width
+ local mwidth = mchardata.width
+ local delta = h - ewidth
+ local glyph = new_glyph(font,echar)
+ local head = glyph
+ if delta > 0 then
+ local count = ceil(delta/mwidth)
+ local kern = (delta - count * mwidth) / count
+ for i=1,count do
+ local k = new_kern(kern)
+ local g = new_glyph(font,mchar)
+ setlink(k,head)
+ setlink(g,k)
+ head = g
+ end
+ end
+ local height = mchardata.height
+ local list = new_hlist(head)
+ local kern = new_kern(height-v)
+ list = setlink(kern,list)
+ local list = new_vlist(kern)
+ insert_node_after(n,n,list)
+end
+
+local function mathhruleaction(n,h,v,font,bchar,mchar,echar)
+ local characters = fontcharacters[font]
+ local bchardata = characters[bchar]
+ local mchardata = characters[mchar]
+ local echardata = characters[echar]
+ local bwidth = bchardata.width
+ local mwidth = mchardata.width
+ local ewidth = echardata.width
+ local delta = h - ewidth - bwidth
+ local glyph = new_glyph(font,echar)
+ local head = glyph
+ if delta > 0 then
+ local count = ceil(delta/mwidth)
+ local kern = (delta - count * mwidth) / (count+1)
+ for i=1,count do
+ local k = new_kern(kern)
+ local g = new_glyph(font,mchar)
+ setlink(k,head)
+ setlink(g,k)
+ head = g
+ end
+ local k = new_kern(kern)
+ setlink(k,head)
+ head = k
+ end
+ local g = new_glyph(font,bchar)
+ setlink(g,head)
+ head = g
+ local height = mchardata.height
+ local list = new_hlist(head)
+ local kern = new_kern(height-v)
+ list = setlink(kern,list)
+ local list = new_vlist(kern)
+ insert_node_after(n,n,list)
+end
+
+local function initialize(tfmdata)
+ local goodies = tfmdata.goodies
+ if goodies then
+ local resources = tfmdata.resources
+ local ruledata = { }
+ for i=1,#goodies do
+ local mathematics = goodies[i].mathematics
+ if mathematics then
+ local rules = mathematics.rules
+ if rules then
+ for tag, name in next, rules do
+ ruledata[tag] = name
+ end
+ end
+ end
+ end
+ if next(ruledata) then
+ local characters = tfmdata.characters
+ local unicodes = resources.unicodes
+ if characters and unicodes then
+ local mathruleactions = resources.mathruleactions
+ if not mathruleactions then
+ mathruleactions = { }
+ resources.mathruleactions = mathruleactions
+ end
+ --
+ local mchar = unicodes[ruledata["radical.extender"] or false]
+ local echar = unicodes[ruledata["radical.end"] or false]
+ if mchar and echar then
+ mathruleactions.radicalaction = function(n,h,v,font)
+ mathradicalaction(n,h,v,font,mchar,echar)
+ end
+ end
+ --
+ local bchar = unicodes[ruledata["hrule.begin"] or false]
+ local mchar = unicodes[ruledata["hrule.extender"] or false]
+ local echar = unicodes[ruledata["hrule.end"] or false]
+ if bchar and mchar and echar then
+ mathruleactions.hruleaction = function(n,h,v,font)
+ mathhruleaction(n,h,v,font,bchar,mchar,echar)
+ end
+ end
+ -- not that nice but we need to register it at the tex end
+ -- context.enablemathrules("\\fontclass")
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathrules",
+ description = "check math rules",
+ default = true,
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-epd.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-epd.mkxl
new file mode 100644
index 00000000000..7ff9b86756e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-epd.mkxl
@@ -0,0 +1,93 @@
+%D \module
+%D [ file=grph-epd,
+%D version=2010.07.29,
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Merging Goodies,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Graphic Macros / Merging Goodies}
+
+\unprotect
+
+\registerctxluafile{grph-epd}{}
+
+% hm, this one is globally overloaded
+
+\mutable\let\mergedfigurereference\empty
+
+\defineoverlay[system:graphics:epdf][\directsetup{system:graphics:epdf}]
+
+\startsetups system:graphics:epdf
+ \clf_figure_mergegoodies{\externalfigureparameter\c!interaction}%
+ \reference[\mergedfigurereference]{}% todo: dest area
+\stopsetups
+
+\defineframed
+ [system_graphics_epdf]
+ [\c!frame=\v!off,
+ \c!offset=\v!overlay,
+ \c!background={\v!foreground,system:graphics:epdf}]
+
+\protected\def\grph_epdf_add_overlay
+ {\global\setbox\foundexternalfigure\vbox\bgroup % vpack ?
+ \system_graphics_epdf{\box\foundexternalfigure}%
+ \egroup}
+
+\appendtoks
+ \iflocation
+ \doif\figurefiletype{pdf}{\doifnot{\externalfigureparameter\c!interaction}\v!none\grph_epdf_add_overlay}%
+ \fi
+\to \externalfigurepostprocessors
+
+\defineframed
+ [epdfstampsymbol]
+ [\c!foregroundstyle=\v!mono,
+ \c!background=\c!color,
+ \c!rulethickness=.125\exheight,
+ \c!offset=.250\exheight,
+ \c!backgroundcolor=lightgray, % can be adapted before first usage
+ \c!framecolor=darkgray, % can be adapted before first usage
+ \c!corner=\v!round]
+
+\definesymbol[Stamped] [\epdfstampsymbol{Stamped}]
+
+\definesymbol[Approved] [\epdfstampsymbol{Approved}]
+\definesymbol[Experimental] [\epdfstampsymbol{Experimental}]
+\definesymbol[NotApproved] [\epdfstampsymbol{NotApproved}]
+\definesymbol[AsIs] [\epdfstampsymbol{AsIs}]
+\definesymbol[Expired] [\epdfstampsymbol{Expired}]
+\definesymbol[NotForPublicRelease] [\epdfstampsymbol{NotForPublicRelease}]
+\definesymbol[Confidential] [\epdfstampsymbol{Confidential}]
+\definesymbol[Final] [\epdfstampsymbol{Final}]
+\definesymbol[Sold] [\epdfstampsymbol{Sold}]
+\definesymbol[Departmental] [\epdfstampsymbol{Departmental}]
+\definesymbol[ForComment] [\epdfstampsymbol{ForComment}]
+\definesymbol[TopSecret] [\epdfstampsymbol{TopSecret}]
+\definesymbol[Draft] [\epdfstampsymbol{Draft}]
+\definesymbol[ForPublicRelease] [\epdfstampsymbol{ForPublicRelease}]
+
+\protect \endinput
+
+% /Properties << /xxxx 22 0 R >>
+% 21 0 obj << /Type /OCG /Name (xxxx) >> endobj
+% 22 0 obj << /OCGs [ 21 0 R ] /Type /OCMD >> endobj
+
+% \def\setepdflayer#1#2#3#4#5#6% x y w h (in bp) 0/1 destination
+% {\setlayer
+% [epdflinks]
+% [\c!x=#1bp,\c!y=#1\s!bp,\c!preset=\v!leftbottom]
+% {\button
+% [\c!width=#3\s!bp,\c!height=#4\s!bp,\c!offset=\v!overlay,\c!frame=\ifnum#5=1 on\else\v!off]%
+% {}[#6]}}
+
+% \def\setepdflayer#1#2#3#4#5#6% x y w h (in bp) 0/1 destination
+% {\setlayer
+% [epdflinks]
+% [\c!x=#1bp,\c!y=#1\s!bp,\c!preset=\v!leftbottom]
+% {\gotowdhtbox{#3\s!bp}{#4\s!bp}[#6]}}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-fig.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-fig.mkxl
new file mode 100644
index 00000000000..92d2c7dc69a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-fig.mkxl
@@ -0,0 +1,367 @@
+%D \module
+%D [ file=grph-fig,
+%D version=2006.08.26, % overhaul of 1997.03.31
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Figure Inclusion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% This is (yet) untested in LMTX!
+
+\unprotect
+
+%D Used in the styledesign manual:
+%
+% beware in mkiv we don't have the typeset- prefix
+%
+% \setbuffer[typeset-b]\endbuffer
+% \setbuffer[typeset-a]\endbuffer
+%
+% todo:
+%
+% \appendtoks \setbuffer[typeset-b]\endbuffer\to \everystarttext
+% \appendtoks \setbuffer[typeset-a]\endbuffer\to \everystarttext
+
+% we could use \typesetbuffer[*] to access the last one
+
+\newconstant\c_grph_buffers_mode
+
+\mutable\let\lasttypesetbuffer\empty
+
+\permanent\protected\def\typesetbuffer {\bgroup\setconstant\c_grph_buffers_mode\plusone \grph_buffers_typeset}
+\permanent\protected\def\typesetbufferonly{\bgroup\setconstant\c_grph_buffers_mode\zerocount\grph_buffers_typeset}
+
+\tolerant\permanent\protected\def\grph_buffers_typeset[#1]#*[#2]%
+ {\ifparameters
+ \grph_buffers_typeset_indeed\jobname\empty
+ \or
+ \ifhastok={#1}%
+ \grph_buffers_typeset_indeed\jobname{#1}%
+ \else
+ \grph_buffers_typeset_indeed{#1}\empty
+ \fi
+ \or
+ \grph_buffers_typeset_indeed{#1}{#2}%
+ \fi}
+
+\def\grph_buffers_typeset_indeed#1#2% we could use the via files
+ {\doifnot{#1}{*}{\xdef\lasttypesetbuffer{\clf_typesetbuffer{#1}}}%
+ \ifcase\c_grph_buffers_mode
+ % typesetonly
+ \or
+ \externalfigure[\lasttypesetbuffer][#2]%
+ \fi
+ \egroup}
+
+\tolerant\permanent\protected\def\runbuffer[#1]#*[#2]%
+ {\xdef\lasttypesetbuffer{\clf_runbuffer{#1}{#2}}}
+
+% For manuals and such:
+%
+% \definetypesetting [name] [options] [settings-a]
+%
+% \typesetfile [name] [file] [settings-b]
+% \typesetfile [file] [options] [settings-b]
+% \typesetfile [file] [settings-b]
+% \typesetfile [file]
+%
+% \enabletrackers[files.run]
+% \starttext
+% \typesetfile[oepsoeps.tex][width=10cm,frame=on]
+% \stoptext
+
+\installcorenamespace{typesettingfile}
+
+\tolerant\permanent\protected\def\definetypesetting[#1]#*[#2]#*[#3]% <name> options settings-a
+ {\ifparameter#1\or\setuvalue{\??typesettingfile#1}{\grph_typesetting_process_indeed{#2}{#3}}\fi}
+
+\tolerant\permanent\protected\def\typesetfile[#1]#*[#2]#*[#3]% <name> filename settings-b | filename options settings
+ {\ifcsname\??typesettingfile#1\endcsname
+ \lastnamedcs{#2}{#3}%
+ \orelse\ifparameter#3\or
+ \grph_typesetting_process_indeed{#2}{#3}{#1}{}%
+ \orelse\ifparameter#2\or % filename settings
+ \grph_typesetting_process_indeed{}{#2}{#1}{}%
+ \fi}
+
+\defineexternalfigure[typesetting] % so one can set a frame and such
+
+\def\grph_typesetting_process_indeed#1#2#3#4% options settings-a filename settings-b
+ {\begingroup
+ \edef\m_typesetting_name{\clf_runcontextjob{#3}{#1}}%
+ \ifempty\m_typesetting_name \else
+ \normalexpanded{\externalfigure[\m_typesetting_name][typesetting]}[#2,#4]%
+ \fi
+ \endgroup}
+
+%D Whatever ... hardly used ... but historic ... needs checking ... will probably
+%D become m-fig-nn.mkiv .. or I will extend it cq. clean it up when I needed it.
+%D After all, it's documented in old manuals.
+
+\newcount\c_grph_steps_reference
+\newdimen\d_grph_steps_x
+\newdimen\d_grph_steps_y
+\newbox \b_grph_steps_colorbar
+
+\ifdefined\referring\else \mutable\let\referring\relax \fi
+\ifdefined\marking \else \mutable\let\marking \relax \fi
+\ifdefined\remark \else \mutable\let\remark \relax \fi
+\ifdefined\colorbar \else \mutable\let\colorbar \relax \fi
+
+\tolerant\protected\def\grph_steps_place_figure#1#2#3#4[#5]%
+ {\hpack
+ {\setbox\scratchbox\hpack
+ {\useexternalfigure[\s!dummy][#2][#3,#5]%
+ \externalfigure[\s!dummy]}%
+ \grph_steps_calculate
+ \startpositioning
+ \enforced\let\referring\grph_steps_one_referring
+ \enforced\let\marking \grph_steps_one_marking
+ \enforced\let\remark \grph_steps_one_remark
+ \enforced\let\colorbar \grph_steps_one_colorbar
+ \position(0,0){\box\scratchbox}%
+ \linewidth\onepoint
+ \setuppositioning
+ [\c!unit=pt,%
+ \c!xscale=\withoutpt\the\d_grph_steps_x,%
+ \c!yscale=\withoutpt\the\d_grph_steps_y,%
+ \c!factor=1]%
+ \ignorespaces#4%
+ \enforced\let\referring\grph_steps_two_referring
+ \enforced\let\marking \grph_steps_two_marking
+ \enforced\let\remark \grph_steps_two_remark
+ \enforced\let\colorbar \grph_steps_two_colorbar
+ \ignorespaces#4\removeunwantedspaces % or just grab #4 unspaced
+ \stoppositioning
+ \ifvoid\b_grph_steps_colorbar\else\box\b_grph_steps_colorbar\fi}} % not really needed
+
+\protected\def\grph_steps_one_referring(#1,#2)#*(#3,#4)#*[#5]%
+ {\position(#1,#2){\grph_steps_goto(#3,#4){\externalfigureparameter\c!frames}[#5]}}
+
+\protected\def\grph_steps_one_marking(#1,#2)#*(#3,#4)#*[#5]%
+ {\position(#1,#2){\grph_steps_this_is(#3,#4){\externalfigureparameter\c!frames}[#5]}}
+
+\protected\def\grph_steps_one_remark (#-,#-)#*(#-,#-)#*[#-]#*#:#-{}% (x,y)(h,b)[...]{tekst}
+\protected\def\grph_steps_one_colorbar #-[#-]{}
+\protected\def\grph_steps_two_referring(#-,#-)#*(#-,#-)#*[#-]{}
+
+\let\grph_steps_two_marking\grph_steps_two_referring
+
+\protected\def\grph_steps_two_remark
+ {\grph_steps_comment\v!no}
+
+\protected\def\grph_steps_two_colorbar#1[#2]
+ {\begingroup
+ \global\setbox\b_grph_steps_colorbar\vpack % \vbox ?
+ {\forgetall
+ \processcommalist[#2]\grph_colorbar_make_step}%
+ \global\setbox\b_grph_steps_colorbar\vpack
+ {\hskip2\emwidth\box\b_grph_steps_colorbar}%
+ \global\wd\b_grph_steps_colorbar\zeropoint
+ \endgroup}
+
+\protected\def\grph_colorbar_make_step#1%
+ {\blackrule[\c!color=#1,\c!width=2\emwidth,\c!height=\exheight,\c!depth=\zeropoint]%
+ \endgraf}
+
+\permanent\protected\def\startfigure[#1]#*[#2]#*[#3]#:#4\stopfigure
+ {\doifelse{\externalfigureparameter\c!option}\v!test
+ {\grph_steps_test_figure{#1}{#2}{#3}{#4}%
+ \letexternalfigureparameter\c!frames\v!on}%
+ {\letexternalfigureparameter\c!frames\v!off}%
+ \setvalue{\??externalfigureinstance#1}%
+ {\grph_steps_place_figure{#1}{#2}{#3}{#4}}}
+
+\aliased\let\stopfigure\relax
+
+\ifdefined\referring \else \let\referring\relax \fi
+\ifdefined\marking \else \let\marking \relax \fi
+\ifdefined\remark \else \let\remark \relax \fi
+\ifdefined\colorbar \else \let\colorbar \relax \fi
+
+\protected\def\grph_steps_test_figure#1#2#3#4%
+ {\begingroup
+ \setbox\scratchbox\hpack
+ {\useexternalfigure[\s!dummy][#2][\c!wfactor=\v!max]%
+ \externalfigure[\s!dummy]}%
+ \let\referring\grph_steps_three_referring
+ \let\marking \grph_steps_three_marking
+ \let\remark \grph_steps_three_remark
+ \let\colorbar \grph_steps_three_colorbar
+ \c_grph_steps_reference\zerocount
+ \setbox\scratchboxone\vpack
+ {\hsize240\points
+ \startpositioning
+ \grph_steps_calculate
+ \position(0,0)
+ {\box\scratchbox}%
+ \position(0,0)
+ {\basegrid
+ [\c!nx=\externalfigureparameter\c!xmax,%
+ \c!dx=\withoutpt\the\d_grph_steps_x,%
+ \c!ny=\externalfigureparameter\c!ymax,%
+ \c!dy=\withoutpt\the\d_grph_steps_y,%
+ \c!xstep=1,%
+ \c!ystep=1,%
+ \c!scale=1,%
+ \c!offset=\v!no,%
+ \c!unit=pt]}%
+ \setuppositioning
+ [\c!unit=pt,%
+ \c!xscale=\withoutpt\the\d_grph_steps_x,%
+ \c!yscale=\withoutpt\the\d_grph_steps_y,%
+ \c!factor=1]%
+ \linewidth\onepoint
+ \ignorespaces#4\removeunwantedspaces % or just grab #4 unspaced
+ \stoppositioning
+ \vfill}%
+ \c_grph_steps_reference\zerocount
+ \enforced\let\referring\grph_steps_four_referring
+ \enforced\let\marking \grph_steps_four_marking
+ \enforced\let\remark \grph_steps_four_remark
+ \enforced\let\colorbar \grph_steps_four_colorbar
+ \setbox\scratchboxtwo\vbox % \vpack ?
+ {\forgetall
+ \begingroup
+ \tfa\doifelsenothing{#1}{#2}{#1}%
+ \endgroup
+ \blank
+ \tfxx#4%
+ \vfilll}%
+ \ifdim\ht\scratchboxone>\ht\scratchboxtwo
+ \ht\scratchboxtwo\ht\scratchboxone
+ \else
+ \ht\scratchboxone\ht\scratchboxtwo
+ \fi
+ \hpack
+ {\hskip3\emwidth
+ \tpack{\vskip12\points\box\scratchboxone\vskip6\points}%
+ \tpack{\vskip12\points\box\scratchboxtwo\vskip6\points}}%
+ \endgroup}
+
+\definesystemconstant{vwa}
+\definesystemconstant{vwb}
+
+\tolerant\protected\def\grph_steps_three_referring(#1,#2)#*(#3,#4)#*[#5]%
+ {\advance\c_grph_steps_reference\plusone
+ \position(#1,#2)
+ {\hbox{\the\c_grph_steps_reference}}%
+ \position(#1,#2)
+ {\gotosomeinternal\s!vwb{#5}\realfolio
+ {\grph_steps_marker(#3,#4)\v!on{\thisissomeinternal\s!vwa{#5}}}}}
+
+\protected\def\grph_steps_three_remark
+ {\grph_steps_comment\v!yes}
+
+\let\grph_steps_three_marking \grph_steps_three_referring
+\let\grph_steps_three_colorbar\grph_steps_one_colorbar
+
+\protected\def\grph_steps_four_referring{\grph_steps_text{\normalstartimath\rightarrow\normalstopimath}}
+\protected\def\grph_steps_four_marking {\grph_steps_text{\normalstartimath\leftarrow \normalstopimath}}
+
+\let\grph_steps_four_remark \grph_steps_one_remark
+\let\grph_steps_four_colorbar\grph_steps_one_colorbar
+
+% Helpers:
+
+\def\grph_steps_calculate
+ {\ifnum0\externalfigureparameter\c!xmax=\zerocount
+ \ifnum0\externalfigureparameter\c!ymax=\zerocount
+ \setexternalfigureparameter\c!ymax{24}%
+ \fi
+ \d_grph_steps_y\figureheight
+ \divide\d_grph_steps_y \externalfigureparameter\c!ymax
+ \d_grph_steps_x\d_grph_steps_y
+ \scratchdimen\figurewidth
+ \advance\scratchdimen\d_grph_steps_y
+ \divide \scratchdimen\d_grph_steps_y
+ \setexternalfigureparameter\c!xmax{\number\scratchdimen}%
+ \else
+ \d_grph_steps_x\figurewidth \divide\d_grph_steps_x \externalfigureparameter\c!xmax\relax
+ \d_grph_steps_y\figureheight \divide\d_grph_steps_y \externalfigureparameter\c!ymax\relax
+ \fi}
+
+\tolerant\def\grph_steps_comment#1(#2,#3)#*(#4,#5)#*[#6]#*#:#7% {kader}(x,y)(h,b)[...]{tekst}
+ {\position(#2,#3)%
+ {\setnostrut
+ \framed[\c!width=#4\d_grph_steps_x,\c!height=#5\d_grph_steps_y,\c!offset=\v!none,\c!frame=#1,#6]{#7}}}
+
+% \def\grph_steps_figure#1%
+% {\position(0,0){\getvalue{#1}}}
+
+\def\grph_steps_goto(#1,#2)#3[#4]% (h,b)kader[ref]
+ {\gotobox{\vpack{\grph_steps_area(#1,#2)#3{}}}[#4]}
+
+\tolerant\def\grph_steps_text#1(#2,#3)#*(#4,#5)#*[#6]%
+ {\advance\c_grph_steps_reference\plusone
+ \hbox % \hpack ?
+ {\quad
+ \thisissomeinternal\s!vwb{#6}%
+ \gotosomeinternal\s!vwa{#6}\realfolio{\hbox to 1.5\emwidth{\the\c_grph_steps_reference\hfill}}%
+ \quad#1 (#2,#3) (#4,#5) [#6]\hfill}%
+ \endgraf}
+
+\def\grph_steps_this_is(#1,#2)#3[#4]%
+ {\grph_steps_area(#1,#2){#3}{\dosetdirectpagereference{#4}}}
+
+\def\grph_steps_area(#1,#2)#3#4% (h,b){kader}{tekst}
+ {\bgroup
+ \setnostrut
+ \framed[\c!width=#1\d_grph_steps_x,\c!height=#2\d_grph_steps_y,\c!offset=\zeropoint,\c!frame=#3]{#4}%
+ \egroup}
+
+\def\grph_steps_marker(#1,#2)#3#4% (h,b){kader}{tekst}
+ {\framed[\c!width=#1\d_grph_steps_x,\c!height=#2\d_grph_steps_y,\c!offset=\v!none,\c!frame=#3]{#4}}
+
+\protect \endinput
+
+% \startbuffer
+% \definecolor [blue] [c=1,m=.38,y=0,k=.64]
+% \definecolor [yellow] [c=0,m=.28,y=1,k=.06]
+%
+% \definespotcolor [blue-100] [blue] [p=1]
+% \definespotcolor [yellow-100] [yellow] [p=1]
+%
+% \definemultitonecolor [combicolor] [blue=.12,yellow=.28] [c=.1,m=.1,y=.3,k=.1]
+%
+% \definemultitonecolor [combicolor-b] [blue=1] [c=1,m=.38,y=0,k=.64] % force multitone
+% \definemultitonecolor [combicolor-y] [yellow=1] [c=0,m=.28,y=1,k=.06] % force multitone
+%
+% \useexternalfigure[demo-a][mill.png] [object=no,width=.2\textwidth]
+% \useexternalfigure[demo-b][hacker-bw.jpg][object=no,width=.2\textwidth]
+%
+% \startbaselinecorrection \startcombination[4*1]
+% {\externalfigure[demo-a]} {no color}
+% {\externalfigure[demo-a][color=combicolor]} {indexed duotone}
+% {\externalfigure[demo-a][color=combicolor-b]} {spot color}
+% {\externalfigure[demo-a][color=combicolor-y]} {spot color}
+% \stopcombination \stopbaselinecorrection
+%
+% \startbaselinecorrection \startcombination[4*1]
+% {\externalfigure[demo-b]} {no color}
+% {\externalfigure[demo-b][color=combicolor]} {indexed duotone}
+% {\externalfigure[demo-b][color=combicolor-b]} {spot color}
+% {\externalfigure[demo-b][color=combicolor-y]} {spot color}
+% \stopcombination \stopbaselinecorrection
+%
+% \startbaselinecorrection \startcombination[4*1]
+% {\externalfigure[demo-a]} {no color}
+% {\externalfigure[demo-a][color=combicolor]} {indexed duotone}
+% {\externalfigure[demo-a][color=blue-100]} {spot color}
+% {\externalfigure[demo-a][color=yellow-100]} {spot color}
+% \stopcombination \stopbaselinecorrection
+%
+% \startbaselinecorrection \startcombination[4*1]
+% {\externalfigure[demo-b]} {no color}
+% {\externalfigure[demo-b][color=combicolor]} {indexed duotone}
+% {\externalfigure[demo-b][color=blue-100]} {spot color}
+% {\externalfigure[demo-b][color=yellow-100]} {spot color}
+% \stopcombination \stopbaselinecorrection
+% \stopbuffer
+%
+% \getbuffer \typebuffer
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-inc.lmt b/Master/texmf-dist/tex/context/base/mkxl/grph-inc.lmt
new file mode 100644
index 00000000000..4fa562cb8cd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-inc.lmt
@@ -0,0 +1,2160 @@
+if not modules then modules = { } end modules ['grph-inc'] = {
+ version = 1.001,
+ comment = "companion to grph-inc.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: in pdfe: pdfe.copyappearance(document,objnum)
+--
+-- local im = createimage { filename = fullname }
+-- local on = images.flushobject(im,document.__xrefs__[AP])
+
+-- todo: files are sometimes located twice
+-- todo: empty filename or only suffix always false (not found)
+-- lowercase types
+-- mps tex tmp svg
+-- partly qualified
+-- dimensions
+-- use metatables
+-- figures.boxnumber can go as we now can use names
+-- avoid push
+-- move some to command namespace
+
+--[[
+The ConTeXt figure inclusion mechanisms are among the oldest code
+in ConTeXt and evolved into a complex whole. One reason is that we
+deal with backend in an abstract way. What complicates matters is
+that we deal with internal graphics as well: TeX code, MetaPost code,
+etc. Later on figure databases were introduced, which resulted in
+a plug in model for locating images. On top of that runs a conversion
+mechanism (with caching) and resource logging.
+
+Porting that to Lua is not that trivial because quite some
+status information is kept between al these stages. Of course, image
+reuse also has some price, and so I decided to implement the graphics
+inclusion in several layers: detection, loading, inclusion, etc.
+
+Object sharing and scaling can happen at each stage, depending on the
+way the resource is dealt with.
+
+The TeX-Lua mix is suboptimal. This has to do with the fact that we cannot
+run TeX code from within Lua. Some more functionality will move to Lua.
+]]--
+
+-- todo: store loaded pages per pdf file someplace
+
+local tonumber, tostring, next, unpack = tonumber, tostring, next, unpack
+local format, lower, find, match, gsub = string.format, string.lower, string.find, string.match, string.gsub
+local longtostring = string.longtostring
+local contains = table.contains
+local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys
+local concat, insert, remove = table.concat, table.insert, table.remove
+local todimen = string.todimen
+local collapsepath = file.collapsepath
+local formatters = string.formatters
+local odd = math.odd
+local isfile, isdir, modificationtime = lfs.isfile, lfs.isdir, lfs.modification
+
+local P, R, S, Cc, C, Cs, Ct, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.Cc, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.match
+
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local replacetemplate = utilities.templates.replace
+
+local bpfactor = number.dimenfactors.bp
+
+images = images or { }
+local images = images
+
+local hasscheme = url.hasscheme
+local urlhashed = url.hashed
+
+local resolveprefix = resolvers.resolve
+
+local texgetbox = tex.getbox
+local texsetbox = tex.setbox
+
+local hpack = node.hpack
+
+local new_latelua = nodes.pool.latelua
+
+local context = context
+
+local implement = interfaces.implement
+local variables = interfaces.variables
+
+local codeinjections = backends.codeinjections
+local nodeinjections = backends.nodeinjections
+
+local trace_figures = false trackers.register ("graphics.locating", function(v) trace_figures = v end)
+local trace_bases = false trackers.register ("graphics.bases", function(v) trace_bases = v end)
+local trace_programs = false trackers.register ("graphics.programs", function(v) trace_programs = v end)
+local trace_conversion = false trackers.register ("graphics.conversion", function(v) trace_conversion = v end)
+local trace_inclusion = false trackers.register ("graphics.inclusion", function(v) trace_inclusion = v end)
+local trace_usage = false trackers.register ("graphics.usage", function(v) trace_usage = v end)
+
+local extra_check = false directives.register("graphics.extracheck", function(v) extra_check = v end)
+local auto_transform = true directives.register("graphics.autotransform", function(v) auto_transform = v end)
+
+local report = logs.reporter("graphics")
+local report_inclusion = logs.reporter("graphics","inclusion")
+
+local f_hash_part = formatters["%s->%s->%s->%s"]
+local f_hash_full = formatters["%s->%s->%s->%s->%s->%s->%s->%s"]
+
+local v_yes = variables.yes
+local v_global = variables["global"]
+local v_local = variables["local"]
+local v_default = variables.default
+local v_auto = variables.auto
+
+local maxdimen = tex.magicconstants.maxdimen -- 0x3FFFFFFF -- 2^30-1
+
+local ctx_doscalefigure = context.doscalefigure
+local ctx_relocateexternalfigure = context.relocateexternalfigure
+local ctx_startfoundexternalfigure = context.startfoundexternalfigure
+local ctx_stopfoundexternalfigure = context.stopfoundexternalfigure
+local ctx_dosetfigureobject = context.dosetfigureobject
+local ctx_doboxfigureobject = context.doboxfigureobject
+
+-- extensions
+
+function checkimage(figure)
+ if figure then
+ local width = figure.width or 0
+ local height = figure.height or 0
+ if width <= 0 or height <= 0 then
+ report_inclusion("image %a has bad dimensions (%p,%p), discarding",figure.filename or "?",width,height)
+ return false, "bad dimensions"
+ end
+ -- local xres = figure.xres
+ -- local yres = figure.yres
+ local changes = false
+ if height > width then
+ if height > maxdimen then
+ figure.height = maxdimen
+ figure.width = width * maxdimen/height
+ changed = true
+ end
+ elseif width > maxdimen then
+ figure.width = maxdimen
+ figure.height = height * maxdimen/width
+ changed = true
+ end
+ if changed then
+ report_inclusion("limiting natural dimensions of %a, old %p * %p, new %p * %p",
+ figure.filename,width,height,figure.width,figure.height)
+ end
+ if width >= maxdimen or height >= maxdimen then
+ report_inclusion("image %a is too large (%p,%p), discarding",
+ figure.filename,width,height)
+ return false, "dimensions too large"
+ end
+ return figure
+ end
+end
+
+-- This is a bit of abstraction. Fro a while we will follow the luatex image
+-- model.
+
+local imagekeys = {
+ -- only relevant ones (permitted in luatex)
+ "width", "height", "depth", "bbox",
+ "colordepth", "colorspace",
+ "filename", "filepath", "visiblefilename",
+ "imagetype", "stream",
+ "index", "objnum",
+ "pagebox", "page", "pages",
+ "rotation", "transform",
+ "xsize", "ysize", "xres", "yres",
+}
+
+local imagesizes = {
+ art = true, bleed = true, crop = true,
+ media = true, none = true, trim = true,
+}
+
+local imagetypes = { [0] =
+ "none",
+ "pdf", "png", "jpg", "jp2", "jbig2",
+ "stream",
+}
+
+imagetypes = table.swapped(imagetypes,imagetypes)
+
+images.keys = imagekeys
+images.types = imagetypes
+images.sizes = imagesizes
+
+local function createimage(specification)
+ return backends.codeinjections.newimage(specification)
+end
+
+local function copyimage(specification)
+ return backends.codeinjections.copyimage(specification)
+end
+
+local function scanimage(specification)
+ return backends.codeinjections.scanimage(specification)
+end
+
+local function embedimage(specification)
+ -- write the image to file
+ return backends.codeinjections.embedimage(specification)
+end
+
+local function wrapimage(specification)
+ -- create an image rule
+ return backends.codeinjections.wrapimage(specification)
+end
+
+images.create = createimage
+images.scan = scanimage
+images.copy = copyimage
+images.wrap = wrapimage
+images.embed = embedimage
+
+-- If really needed we can provide:
+--
+-- img = {
+-- new = createimage,
+-- scan = scanimage,
+-- copy = copyimage,
+-- node = wrapimage,
+-- write = function(specification) context(wrapimage(specification)) end,
+-- immediatewrite = embedimage,
+-- immediatewriteobject = function() end, -- not upported, experimental anyway
+-- boxes = function() return sortedkeys(imagesizes) end,
+-- fields = function() return imagekeys end,
+-- types = function() return { unpack(imagetypes,0,#imagetypes) } end,
+-- }
+
+-- end of copies / mapping
+
+local function imagetotable(imgtable)
+ if type(imgtable) == "table" then
+ return copy(imgtable)
+ end
+ local result = { }
+ for k=1,#imagekeys do
+ local key = imagekeys[k]
+ result[key] = imgtable[key]
+ end
+ return result
+end
+
+function images.serialize(i,...)
+ return table.serialize(imagetotable(i),...)
+end
+
+function images.print(i,...)
+ return table.print(imagetotable(i),...)
+end
+
+local function checkimagesize(size)
+ if size then
+ size = gsub(size,"box","")
+ return imagesizes[size] and size or "crop"
+ else
+ return "crop"
+ end
+end
+
+images.check = checkimage
+images.checksize = checkimagesize
+images.totable = imagetotable
+
+-- local indexed = { }
+--
+-- function images.ofindex(n)
+-- return indexed[n]
+-- end
+
+--- we can consider an grph-ini file
+
+figures = figures or { }
+local figures = figures
+
+figures.images = images
+figures.boxnumber = figures.boxnumber or 0
+figures.defaultsearch = true
+figures.defaultwidth = 0
+figures.defaultheight = 0
+figures.defaultdepth = 0
+figures.nofprocessed = 0
+figures.nofmissing = 0
+figures.preferquality = true -- quality over location
+
+local figures_loaded = allocate() figures.loaded = figures_loaded
+local figures_used = allocate() figures.used = figures_used
+local figures_found = allocate() figures.found = figures_found
+local figures_suffixes = allocate() figures.suffixes = figures_suffixes
+local figures_patterns = allocate() figures.patterns = figures_patterns
+local figures_resources = allocate() figures.resources = figures_resources
+
+local existers = allocate() figures.existers = existers
+local checkers = allocate() figures.checkers = checkers
+local includers = allocate() figures.includers = includers
+local remappers = allocate() figures.remappers = remappers
+local converters = allocate() figures.converters = converters
+local identifiers = allocate() figures.identifiers = identifiers
+local programs = allocate() figures.programs = programs
+
+local defaultformat = "pdf"
+local defaultprefix = "m_k_i_v_"
+
+figures.localpaths = allocate {
+ ".", "..", "../.."
+}
+
+figures.cachepaths = allocate {
+ prefix = "",
+ path = ".",
+ subpath = ".",
+}
+
+local figure_paths = allocate(table.copy(figures.localpaths))
+figures.paths = figure_paths
+
+local figures_order = allocate {
+ "pdf", "mps", "jpg", "png", "jp2", "jbig", "svg", "eps", "tif", "gif", "mov", "buffer", "tex", "cld", "auto",
+}
+
+local figures_formats = allocate { -- magic and order will move here
+ ["pdf"] = { list = { "pdf" } },
+ ["mps"] = { patterns = { "^mps$", "^%d+$" } }, -- we need to anchor
+ ["jpg"] = { list = { "jpg", "jpeg" } },
+ ["png"] = { list = { "png" } },
+ ["jp2"] = { list = { "jp2" } },
+ ["jbig"] = { list = { "jbig", "jbig2", "jb2" } },
+ ["svg"] = { list = { "svg", "svgz" } },
+ ["eps"] = { list = { "eps", "ai" } },
+ ["gif"] = { list = { "gif" } },
+ ["tif"] = { list = { "tif", "tiff" } },
+ ["mov"] = { list = { "mov", "flv", "mp4" } }, -- "avi" is not supported
+ ["buffer"] = { list = { "tmp", "buffer", "buf" } },
+ ["tex"] = { list = { "tex" } },
+ ["cld"] = { list = { "cld" } },
+ ["auto"] = { list = { "auto" } },
+}
+
+local figures_magics = allocate {
+ { format = "png", pattern = P("\137PNG\013\010\026\010") }, -- 89 50 4E 47 0D 0A 1A 0A,
+ { format = "jpg", pattern = P("\255\216\255") }, -- FF D8 FF
+ { format = "jp2", pattern = P("\000\000\000\012\106\080\032\032\013\010"), }, -- 00 00 00 0C 6A 50 20 20 0D 0A },
+ { format = "gif", pattern = P("GIF") },
+ { format = "pdf", pattern = (1 - P("%PDF"))^0 * P("%PDF") },
+}
+
+local figures_native = allocate {
+ pdf = true,
+ jpg = true,
+ jp2 = true,
+ png = true,
+}
+
+figures.formats = figures_formats -- frozen
+figures.magics = figures_magics -- frozen
+figures.order = figures_order -- frozen
+
+-- name checker
+
+local okay = P("m_k_i_v_")
+
+local pattern = (R("az","AZ") * P(":"))^-1 * ( -- a-z : | A-Z :
+ (okay + R("az","09") + S("_/") - P("_")^2)^1 * (P(".") * R("az")^1)^0 * P(-1) + -- a-z | single _ | /
+ (okay + R("az","09") + S("-/") - P("-")^2)^1 * (P(".") * R("az")^1)^0 * P(-1) + -- a-z | single - | /
+ (okay + R("AZ","09") + S("_/") - P("_")^2)^1 * (P(".") * R("AZ")^1)^0 * P(-1) + -- A-Z | single _ | /
+ (okay + R("AZ","09") + S("-/") - P("-")^2)^1 * (P(".") * R("AZ")^1)^0 * P(-1) -- A-Z | single - | /
+) * Cc(false) + Cc(true)
+
+function figures.badname(name)
+ if not name then
+ -- bad anyway
+ elseif not hasscheme(name) then
+ return lpegmatch(pattern,name)
+ else
+ return lpegmatch(pattern,file.basename(name))
+ end
+end
+
+logs.registerfinalactions(function()
+ local done = false
+ if trace_usage and figures.nofprocessed > 0 then
+ logs.startfilelogging(report,"names")
+ for _, data in sortedhash(figures_found) do
+ if done then
+ report()
+ else
+ done = true
+ end
+ report("asked : %s",data.askedname)
+ if data.found then
+ report("format : %s",data.format)
+ report("found : %s",data.foundname)
+ report("used : %s",data.fullname)
+ if data.badname then
+ report("comment : %s","bad name")
+ elseif data.comment then
+ report("comment : %s",data.comment)
+ end
+ else
+ report("comment : %s","not found")
+ end
+ end
+ logs.stopfilelogging()
+ end
+ if figures.nofmissing > 0 and logs.loggingerrors() then
+ logs.starterrorlogging(report,"missing figures")
+ for _, data in sortedhash(figures_found) do
+ report("%w%s",6,data.askedname)
+ end
+ logs.stoperrorlogging()
+ end
+end)
+
+-- We can set the order but only indirectly so that we can check for support.
+
+function figures.setorder(list) -- can be table or string
+ if type(list) == "string" then
+ list = settings_to_array(list)
+ end
+ if list and #list > 0 then
+ figures_order = allocate()
+ figures.order = figures_order
+ local done = { } -- just to be sure in case the list is generated
+ for i=1,#list do
+ local l = lower(list[i])
+ if figures_formats[l] and not done[l] then
+ figures_order[#figures_order+1] = l
+ done[l] = true
+ end
+ end
+ report_inclusion("lookup order % a",figures_order)
+ else
+ -- invalid list
+ end
+end
+
+local function guessfromstring(str)
+ if str then
+ for i=1,#figures_magics do
+ local pattern = figures_magics[i]
+ if lpegmatch(pattern.pattern,str) then
+ local format = pattern.format
+ if trace_figures then
+ report_inclusion("file %a has format %a",filename,format)
+ end
+ return format
+ end
+ end
+ end
+end
+
+figures.guessfromstring = guessfromstring
+
+function figures.guess(filename)
+ local f = io.open(filename,'rb')
+ if f then
+ local str = f:read(100)
+ f:close()
+ if str then
+ return guessfromstring(str)
+ end
+ end
+end
+
+local function setlookups() -- tobe redone .. just set locals
+ figures_suffixes = allocate()
+ figures_patterns = allocate()
+ for _, format in next, figures_order do
+ local data = figures_formats[format]
+ local list = data.list
+ if list then
+ for i=1,#list do
+ figures_suffixes[list[i]] = format -- hash
+ end
+ else
+ figures_suffixes[format] = format
+ end
+ local patterns = data.patterns
+ if patterns then
+ for i=1,#patterns do
+ figures_patterns[#figures_patterns+1] = { patterns[i], format } -- array
+ end
+ end
+ end
+ figures.suffixes = figures_suffixes
+ figures.patterns = figures_patterns
+end
+
+setlookups()
+
+figures.setlookups = setlookups
+
+function figures.registerresource(t)
+ local n = #figures_resources + 1
+ figures_resources[n] = t
+ return n
+end
+
+local function register(tag,what,target)
+ local data = figures_formats[target] -- resolver etc
+ if not data then
+ data = { }
+ figures_formats[target] = data
+ end
+ local d = data[tag] -- list or pattern
+ if d and not contains(d,what) then
+ d[#d+1] = what -- suffix or patternspec
+ else
+ data[tag] = { what }
+ end
+ if not contains(figures_order,target) then
+ figures_order[#figures_order+1] = target
+ end
+ setlookups()
+end
+
+function figures.registersuffix (suffix, target) register('list',suffix,target) end
+function figures.registerpattern(pattern,target) register('pattern',pattern,target) end
+
+implement { name = "registerfiguresuffix", actions = register, arguments = { "'list'", "string", "string" } }
+implement { name = "registerfigurepattern", actions = register, arguments = { "'pattern'", "string", "string" } }
+
+local last_locationset = last_locationset or nil
+local last_pathlist = last_pathlist or nil
+
+function figures.setpaths(locationset,pathlist)
+ if last_locationset == locationset and last_pathlist == pathlist then
+ -- this function can be called each graphic so we provide this optimization
+ return
+ end
+ local t, h = figure_paths, settings_to_hash(locationset)
+ if last_locationset ~= locationset then
+ -- change == reset (actually, a 'reset' would indeed reset
+ if h[v_local] then
+ t = table.fastcopy(figures.localpaths or { })
+ else
+ t = { }
+ end
+ figures.defaultsearch = h[v_default]
+ last_locationset = locationset
+ end
+ if h[v_global] then
+ local list = settings_to_array(pathlist)
+ for i=1,#list do
+ local s = list[i]
+ if not contains(t,s) then
+ t[#t+1] = s
+ end
+ end
+ end
+ figure_paths = t
+ last_pathlist = pathlist
+ figures.paths = figure_paths
+ if trace_figures then
+ report_inclusion("using locations %a",last_locationset)
+ report_inclusion("using paths % a",figure_paths)
+ end
+end
+
+implement { name = "setfigurepaths", actions = figures.setpaths, arguments = "2 strings" }
+
+-- check conversions and handle it here
+
+function figures.hash(data)
+ local status = data and data.status
+ return (status and status.hash or tostring(status.private)) or "nohash" -- the <img object>
+end
+
+-- interfacing to tex
+
+local function new() -- we could use metatables status -> used -> request but it needs testing
+ local request = {
+ name = false,
+ label = false,
+ format = false,
+ page = false,
+ width = false,
+ height = false,
+ preview = false,
+ ["repeat"] = false,
+ controls = false,
+ display = false,
+ mask = false,
+ conversion = false,
+ resolution = false,
+ color = false,
+ arguments = false,
+ cache = false,
+ prefix = false,
+ size = false,
+ }
+ local used = {
+ fullname = false,
+ format = false,
+ name = false,
+ path = false,
+ suffix = false,
+ width = false,
+ height = false,
+ }
+ local status = {
+ status = 0,
+ converted = false,
+ cached = false,
+ fullname = false,
+ format = false,
+ }
+ -- this needs checking because we might check for nil, the test case
+ -- is getfiguredimensions which then should return ~= 0
+ -- setmetatableindex(status, used)
+ -- setmetatableindex(used, request)
+ return {
+ request = request,
+ used = used,
+ status = status,
+ }
+end
+
+-- use table.insert|remove
+
+local lastfiguredata = nil -- will be topofstack or last so no { } (else problems with getfiguredimensions)
+local callstack = { }
+
+function figures.initialize(request)
+ local figuredata = new()
+ if request then
+ -- request.width/height are strings and are only used when no natural dimensions
+ -- can be determined; at some point the handlers might set them to numbers instead
+ local w = tonumber(request.width) or 0
+ local h = tonumber(request.height) or 0
+ local p = tonumber(request.page) or 0
+ request.width = w > 0 and w or nil
+ request.height = h > 0 and h or nil
+ --
+ request.page = p > 0 and p or 1
+ request.keepopen = p > 0
+ request.size = checkimagesize(request.size)
+ request.object = request.object == v_yes
+ request["repeat"] = request["repeat"] == v_yes
+ request.preview = request.preview == v_yes
+ request.cache = request.cache ~= "" and request.cache
+ request.prefix = request.prefix ~= "" and request.prefix
+ request.format = request.format ~= "" and request.format
+ request.compact = request.compact == v_yes
+ table.merge(figuredata.request,request)
+ end
+ return figuredata
+end
+
+function figures.push(request)
+ statistics.starttiming(figures)
+ local figuredata = figures.initialize(request) -- we could use table.sparse but we set them later anyway
+ insert(callstack,figuredata)
+ lastfiguredata = figuredata
+ return figuredata
+end
+
+function figures.pop()
+ remove(callstack)
+ lastfiguredata = callstack[#callstack] or lastfiguredata
+ statistics.stoptiming(figures)
+end
+
+function figures.current()
+ return callstack[#callstack] or lastfiguredata
+end
+
+local function get(category,tag,default)
+ local value = lastfiguredata and lastfiguredata[category]
+ value = value and value[tag]
+ if not value or value == "" or value == true then
+ return default or ""
+ else
+ return value
+ end
+end
+
+local function setdimensions(box)
+ local status = lastfiguredata and lastfiguredata.status
+ local used = lastfiguredata and lastfiguredata.used
+ if status and used then
+ local b = texgetbox(box)
+ local w = b.width
+ local h = b.height + b.depth
+ status.width = w
+ status.height = h
+ used.width = w
+ used.height = h
+ status.status = 10
+ end
+end
+
+figures.get = get
+figures.set = setdimensions
+
+implement { name = "figurestatus", actions = { get, context }, arguments = { "'status'", "string", "string" } }
+implement { name = "figurerequest", actions = { get, context }, arguments = { "'request'", "string", "string" } }
+implement { name = "figureused", actions = { get, context }, arguments = { "'used'", "string", "string" } }
+
+implement { name = "figurefilepath", public = true, actions = { get, file.dirname, context }, arguments = { "'used'", "'fullname'" } }
+implement { name = "figurefilename", public = true, actions = { get, file.nameonly, context }, arguments = { "'used'", "'fullname'" } }
+implement { name = "figurefiletype", public = true, actions = { get, file.extname, context }, arguments = { "'used'", "'fullname'" } }
+
+implement { name = "figuresetdimensions", actions = setdimensions, arguments = "integer" }
+
+-- todo: local path or cache path
+
+local function forbiddenname(filename)
+ if not filename or filename == "" then
+ return false
+ end
+ local expandedfullname = collapsepath(filename,true)
+ local expandedinputname = collapsepath(file.addsuffix(environment.jobfilename,environment.jobfilesuffix),true)
+ if expandedfullname == expandedinputname then
+ report_inclusion("skipping graphic with same name as input filename %a, enforce suffix",expandedinputname)
+ return true
+ end
+ local expandedoutputname = collapsepath(codeinjections.getoutputfilename(),true)
+ if expandedfullname == expandedoutputname then
+ report_inclusion("skipping graphic with same name as output filename %a, enforce suffix",expandedoutputname)
+ return true
+ end
+end
+
+local function rejected(specification)
+ if extra_check then
+ local fullname = specification.fullname
+ if fullname and figures_native[file.suffix(fullname)] and not figures.guess(fullname) then
+ specification.comment = "probably a bad file"
+ specification.found = false
+ specification.error = true
+ report_inclusion("file %a looks bad",fullname)
+ return true
+ end
+ end
+end
+
+local function wipe(str)
+ if str == "" or str == "default" or str == "unknown" then
+ return nil
+ else
+ return str
+ end
+end
+
+local function register(askedname,specification)
+ if not specification then
+ specification = { askedname = askedname, comment = "invalid specification" }
+ elseif forbiddenname(specification.fullname) then
+ specification = { askedname = askedname, comment = "forbidden name" }
+ elseif specification.internal then
+ -- no filecheck needed
+ specification.found = true
+ if trace_figures then
+ report_inclusion("format %a internally supported by engine",specification.format)
+ end
+ elseif not rejected(specification) then
+ local format = specification.format
+ if format then
+ local conversion = wipe(specification.conversion)
+ local resolution = wipe(specification.resolution)
+ local arguments = wipe(specification.arguments)
+ local newformat = conversion
+ if not newformat or newformat == "" then
+ newformat = defaultformat
+ end
+ if trace_conversion then
+ report_inclusion("checking conversion of %a, fullname %a, old format %a, new format %a, conversion %a, resolution %a, arguments %a",
+ askedname,
+ specification.fullname,
+ format,
+ newformat,
+ conversion or "default",
+ resolution or "default",
+ arguments or ""
+ )
+ end
+ -- begin of quick hack
+ local remapper = remappers[format]
+ if remapper then
+ remapper = remapper[conversion]
+ if remapper then
+ specification = remapper(specification) or specification
+ format = specification.format
+ newformat = format
+ conversion = nil
+ end
+ end
+ -- end of quick hack
+ local converter = (not remapper) and (newformat ~= format or resolution or arguments) and converters[format]
+ if converter then
+ local okay = converter[newformat]
+ if okay then
+ converter = okay
+ else
+ newformat = defaultformat
+ converter = converter[newformat]
+ end
+ elseif trace_conversion then
+ report_inclusion("no converter for %a to %a",format,newformat)
+ end
+ if converter then
+ -- todo: make this a function
+ --
+ -- todo: outline as helper function
+ --
+ local oldname = specification.fullname
+ local newpath = file.dirname(oldname)
+ local oldbase = file.basename(oldname)
+ --
+ -- problem: we can have weird filenames, like a.b.c (no suffix) and a.b.c.gif
+ -- so we cannot safely remove a suffix (unless we do that for known suffixes)
+ --
+ -- local newbase = file.removesuffix(oldbase) -- assumes a known suffix
+ --
+ -- so we now have (also see *):
+ --
+ local newbase = oldbase
+ --
+ local fc = specification.cache or figures.cachepaths.path
+ if fc and fc ~= "" and fc ~= "." then
+ newpath = gsub(fc,"%*",newpath) -- so cachedir can be "/data/cache/*"
+ else
+ newbase = defaultprefix .. newbase
+ end
+ local subpath = specification.subpath or figures.cachepaths.subpath
+ if subpath and subpath ~= "" and subpath ~= "." then
+ newpath = newpath .. "/" .. subpath
+ end
+ if not isdir(newpath) then
+ dir.makedirs(newpath)
+ if not file.is_writable(newpath) then
+ if trace_conversion then
+ report_inclusion("path %a is not writable, forcing conversion path %a",newpath,".")
+ end
+ newpath = "."
+ end
+ end
+ local prefix = specification.prefix or figures.cachepaths.prefix
+ if prefix and prefix ~= "" then
+ newbase = prefix .. newbase
+ end
+ local hash = ""
+ if resolution then
+ hash = hash .. "[r:" .. resolution .. "]"
+ end
+ if arguments then
+ hash = hash .. "[a:" .. arguments .. "]"
+ end
+ if hash ~= "" then
+ newbase = newbase .. "_" .. md5.hex(hash)
+ end
+ --
+ -- see *, we had:
+ --
+ -- local newbase = file.addsuffix(newbase,newformat)
+ --
+ -- but now have (result of Aditya's web image testing):
+ --
+ -- as a side effect we can now have multiple fetches with different
+ -- original figures_formats, not that it matters much (apart from older conversions
+ -- sticking around)
+ --
+ local newbase = newbase .. "." .. newformat
+ local newname = file.join(newpath,newbase)
+ oldname = collapsepath(oldname)
+ newname = collapsepath(newname)
+ local oldtime = modificationtime(oldname) or 0
+ local newtime = modificationtime(newname) or 0
+ if newtime == 0 or oldtime > newtime then
+ if trace_conversion then
+ report_inclusion("converting %a (%a) from %a to %a",askedname,oldname,format,newformat)
+ end
+ converter(oldname,newname,resolution or "", arguments or "")
+ else
+ if trace_conversion then
+ report_inclusion("no need to convert %a (%a) from %a to %a",askedname,oldname,format,newformat)
+ end
+ end
+ if io.exists(newname) and io.size(newname) > 0 then
+ specification.foundname = oldname
+ specification.fullname = newname
+ specification.prefix = prefix
+ specification.subpath = subpath
+ specification.converted = true
+ format = newformat
+ if not figures_suffixes[format] then
+ -- maybe the new format is lowres.png (saves entry in suffixes)
+ -- so let's do this extra check
+ local suffix = file.suffix(newformat)
+ if figures_suffixes[suffix] then
+ if trace_figures then
+ report_inclusion("using suffix %a as format for %a",suffix,format)
+ end
+ format = suffix
+ end
+ end
+ specification.format = format
+ elseif io.exists(oldname) then
+ report_inclusion("file %a is bugged",oldname)
+ if format and imagetypes[format] then
+ specification.fullname = oldname
+ end
+ specification.converted = false
+ specification.bugged = true
+ end
+ end
+ end
+ if format then
+ local found = figures_suffixes[format]
+ if not found then
+ specification.found = false
+ if trace_figures then
+ report_inclusion("format %a is not supported",format)
+ end
+ elseif imagetypes[format] then
+ specification.found = true
+ if trace_figures then
+ report_inclusion("format %a natively supported by backend",format)
+ end
+ else
+ specification.found = true -- else no foo.1 mps conversion
+ if trace_figures then
+ report_inclusion("format %a supported by output file format",format)
+ end
+ end
+ else
+ specification.askedname = askedname
+ specification.found = false
+ end
+ end
+ if specification.found then
+ specification.foundname = specification.foundname or specification.fullname
+ else
+ specification.foundname = nil
+ end
+ specification.badname = figures.badname(askedname)
+ local askedhash = f_hash_part(
+ askedname,
+ specification.conversion or "default",
+ specification.resolution or "default",
+ specification.arguments or ""
+ )
+ figures_found[askedhash] = specification
+ if not specification.found then
+ figures.nofmissing = figures.nofmissing + 1
+ end
+ return specification
+end
+
+local resolve_too = false -- true
+
+local internalschemes = {
+ file = true,
+ tree = true,
+ dirfile = true,
+ dirtree = true,
+}
+
+local function locate(request) -- name, format, cache
+ -- not resolvers.cleanpath(request.name) as it fails on a!b.pdf and b~c.pdf
+ -- todo: more restricted cleanpath
+ local askedname = request.name or ""
+ local askedcache = request.cache
+ local askedconversion = request.conversion
+ local askedresolution = request.resolution
+ local askedarguments = request.arguments
+ local askedhash = f_hash_part(
+ askedname,
+ askedconversion or "default",
+ askedresolution or "default",
+ askedarguments or ""
+ )
+ local foundname = figures_found[askedhash]
+ if foundname then
+ return foundname
+ end
+ --
+ --
+ local askedformat = request.format
+ if not askedformat or askedformat == "" or askedformat == "unknown" then
+ askedformat = file.suffix(askedname) or ""
+ elseif askedformat == v_auto then
+ if trace_figures then
+ report_inclusion("ignoring suffix of %a",askedname)
+ end
+ askedformat = ""
+ askedname = file.removesuffix(askedname)
+ end
+ -- protocol check
+ local hashed = urlhashed(askedname)
+ if not hashed then
+ -- go on
+ elseif internalschemes[hashed.scheme] then
+ local path = hashed.path
+ if path and path ~= "" then
+ askedname = path
+ end
+ else
+ local foundname = resolvers.findbinfile(askedname)
+ if not foundname or not isfile(foundname) then -- foundname can be dummy
+ if trace_figures then
+ report_inclusion("unknown url %a",askedname)
+ end
+ -- url not found
+ return register(askedname)
+ end
+ local guessedformat = figures.guess(foundname)
+ if askedformat ~= guessedformat then
+ if trace_figures then
+ report_inclusion("url %a has unknown format",askedname)
+ end
+ -- url found, but wrong format
+ return register(askedname)
+ else
+ if trace_figures then
+ report_inclusion("url %a is resolved to %a",askedname,foundname)
+ end
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ -- we could use the hashed data instead
+ local askedpath = file.is_rootbased_path(askedname)
+ local askedbase = file.basename(askedname)
+ if askedformat ~= "" then
+ askedformat = lower(askedformat)
+ if trace_figures then
+ report_inclusion("forcing format %a",askedformat)
+ end
+ local format = figures_suffixes[askedformat]
+ if not format then
+ for i=1,#figures_patterns do
+ local pattern = figures_patterns[i]
+ if find(askedformat,pattern[1]) then
+ format = pattern[2]
+ if trace_figures then
+ report_inclusion("asked format %a matches %a",askedformat,pattern[1])
+ end
+ break
+ end
+ end
+ end
+ if format then
+ local foundname, quitscanning, forcedformat, internal = figures.exists(askedname,format,resolve_too) -- not askedformat
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- askedname,
+ format = forcedformat or format,
+ cache = askedcache,
+ -- foundname = foundname, -- no
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ internal = internal,
+ })
+ elseif quitscanning then
+ return register(askedname)
+ end
+ askedformat = format -- new per 2013-08-05
+ elseif trace_figures then
+ report_inclusion("unknown format %a",askedformat)
+ end
+ if askedpath then
+ -- path and type given, todo: strip pieces of path
+ local foundname, quitscanning, forcedformat = figures.exists(askedname,askedformat,resolve_too)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- askedname,
+ format = forcedformat or askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ else
+ -- type given
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ local check = path .. "/" .. askedname
+ -- we pass 'true' as it can be an url as well, as the type
+ -- is given we don't waste much time
+ local foundname, quitscanning, forcedformat = figures.exists(check,askedformat,resolve_too)
+ if foundname then
+ return register(check, {
+ askedname = askedname,
+ fullname = foundname, -- check,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ if figures.defaultsearch then
+ local check = resolvers.findfile(askedname)
+ if check and check ~= "" then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = check,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ elseif askedpath then
+ if trace_figures then
+ report_inclusion("using rootbased path")
+ end
+ for i=1,#figures_order do
+ local format = figures_order[i]
+ local list = figures_formats[format].list or { format }
+ for j=1,#list do
+ local suffix = list[j]
+ local check = file.addsuffix(askedname,suffix)
+ local foundname, quitscanning, forcedformat = figures.exists(check,format,resolve_too)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- check,
+ format = forcedformat or format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ else
+ if figures.preferquality then
+ if trace_figures then
+ report_inclusion("unknown format, quality preferred")
+ end
+ for j=1,#figures_order do
+ local format = figures_order[j]
+ local list = figures_formats[format].list or { format }
+ for k=1,#list do
+ local suffix = list[k]
+ -- local name = file.replacesuffix(askedbase,suffix)
+ local name = file.replacesuffix(askedname,suffix)
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ local check = path .. "/" .. name
+ local isfile = internalschemes[urlhashed(check).scheme]
+ if not isfile then
+ if trace_figures then
+ report_inclusion("warning: skipping path %a",path)
+ end
+ else
+ local foundname, quitscanning, forcedformat = figures.exists(check,format,resolve_too) -- true)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foundname, -- check
+ format = forcedformat or format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments
+ })
+ end
+ end
+ end
+ end
+ end
+ else -- 'location'
+ if trace_figures then
+ report_inclusion("unknown format, using path strategy")
+ end
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ for j=1,#figures_order do
+ local format = figures_order[j]
+ local list = figures_formats[format].list or { format }
+ for k=1,#list do
+ local suffix = list[k]
+ local check = path .. "/" .. file.replacesuffix(askedbase,suffix)
+ local foundname, quitscanning, forcedformat = figures.exists(check,format,resolve_too)
+ if foundname then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = foudname, -- check,
+ format = forcedformat or format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ end
+ end
+ if figures.defaultsearch then
+ if trace_figures then
+ report_inclusion("using default tex path")
+ end
+ for j=1,#figures_order do
+ local format = figures_order[j]
+ local list = figures_formats[format].list or { format }
+ for k=1,#list do
+ local suffix = list[k]
+ local check = resolvers.findfile(file.replacesuffix(askedname,suffix))
+ if check and check ~= "" then
+ return register(askedname, {
+ askedname = askedname,
+ fullname = check,
+ format = format,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+ end
+ end
+ end
+ end
+ end
+ return register(askedname, { -- these two are needed for hashing 'found'
+ conversion = askedconversion,
+ resolution = askedresolution,
+ arguments = askedarguments,
+ })
+end
+
+-- -- -- plugins -- -- --
+
+function identifiers.default(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local l = locate(dr)
+ local foundname = l.foundname
+ local fullname = l.fullname or foundname
+ if fullname then
+ du.format = l.format or false
+ du.fullname = fullname -- can be cached
+ ds.fullname = foundname -- original
+ ds.format = l.format
+ ds.status = (l.bugged and 0) or (l.found and 10) or 0
+ end
+ return data
+end
+
+function figures.identify(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ if data then
+ local list = identifiers.list -- defined at the end
+ for i=1,#list do
+ local identifier = list[i]
+ local data = identifier(data)
+-- if data and (not data.status and data.status.status > 0) then
+ if data and (not data.status and data.status.status > 0) then
+ break
+ end
+ end
+ end
+ return data
+end
+
+function figures.exists(askedname,format,resolve)
+ return (existers[format] or existers.generic)(askedname,resolve)
+end
+
+function figures.check(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ return (checkers[data.status.format] or checkers.generic)(data)
+end
+
+local used_images = { }
+
+statistics.register("used graphics",function()
+ if trace_usage then
+ local filename = file.nameonly(environment.jobname) .. "-figures-usage.lua"
+ if next(figures_found) then
+ local found = { }
+ for _, data in sortedhash(figures_found) do
+ found[#found+1] = data
+ for k, v in next, data do
+ if v == false or v == "" then
+ data[k] = nil
+ end
+ end
+ end
+ for i=1,#used_images do
+ local u = used_images[i]
+ local s = u.status
+ if s then
+ s.status = nil -- doesn't say much here
+ if s.error then
+ u.used = { } -- better show that it's not used
+ end
+ end
+ for _, t in next, u do
+ for k, v in next, t do
+ if v == false or v == "" or k == "private" then
+ t[k] = nil
+ end
+ end
+ end
+ end
+ table.save(filename,{
+ found = found,
+ used = used_images,
+ } )
+ return format("log saved in '%s'",filename)
+ else
+ os.remove(filename)
+ end
+ end
+end)
+
+function figures.include(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ if trace_usage then
+ used_images[#used_images+1] = data
+ end
+ return (includers[data.status.format] or includers.generic)(data)
+end
+
+function figures.scale(data) -- will become lua code
+ data = data or callstack[#callstack] or lastfiguredata
+ ctx_doscalefigure()
+ return data
+end
+
+function figures.done(data)
+ figures.nofprocessed = figures.nofprocessed + 1
+ data = data or callstack[#callstack] or lastfiguredata
+ local dr, du, ds, nr = data.request, data.used, data.status, figures.boxnumber
+ local box = texgetbox(nr)
+ ds.width = box.width
+ ds.height = box.height
+ -- somehow this fails on some of tacos files
+ -- ds.xscale = ds.width /(du.width or 1)
+ -- ds.yscale = ds.height/(du.height or 1)
+ -- du.width and du.height can be false
+ if du.width and du.height and du.width > 0 and du.height > 0 then
+ ds.xscale = ds.width /du.width
+ ds.yscale = ds.height/du.height
+ elseif du.xsize and du.ysize and du.xsize > 0 and du.ysize > 0 then
+ ds.xscale = ds.width /du.xsize
+ ds.yscale = ds.height/du.ysize
+ else
+ ds.xscale = 1
+ ds.yscale = 1
+ end
+ -- sort of redundant but can be limited
+ ds.page = ds.page or du.page or dr.page
+ return data
+end
+
+function figures.dummy(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ local dr, du, nr = data.request, data.used, figures.boxnumber
+ local box = hpack(node.new("hlist")) -- we need to set the dir (luatex 0.60 buglet)
+ du.width = du.width or figures.defaultwidth
+ du.height = du.height or figures.defaultheight
+ du.depth = du.depth or figures.defaultdepth
+ box.width = du.width
+ box.height = du.height
+ box.depth = du.depth
+ texsetbox(nr,box) -- hm, should be global (to be checked for consistency)
+end
+
+-- -- -- generic -- -- --
+
+function existers.generic(askedname,resolve)
+ -- not findbinfile
+ local result
+ if hasscheme(askedname) then
+ result = resolvers.findbinfile(askedname)
+ elseif isfile(askedname) then
+ result = askedname
+ elseif resolve then
+ result = resolvers.findbinfile(askedname)
+ end
+ if not result or result == "" then
+ result = false
+ end
+ if trace_figures then
+ if result then
+ report_inclusion("%a resolved to %a",askedname,result)
+ else
+ report_inclusion("%a cannot be resolved",askedname)
+ end
+ end
+ return result
+end
+
+-- pdf : 0-3: 0 90 180 270
+-- jpeg: 0 unset 1-4: 0 90 180 270 5-8: flipped r/c
+
+local transforms = setmetatableindex (
+ {
+ ["orientation-1"] = 0, ["R0"] = 0,
+ ["orientation-2"] = 4, ["R0MH"] = 4,
+ ["orientation-3"] = 2, ["R180"] = 2,
+ ["orientation-4"] = 6, ["R0MV"] = 6,
+ ["orientation-5"] = 5, ["R270MH"] = 5,
+ ["orientation-6"] = 3, ["R90"] = 3,
+ ["orientation-7"] = 7, ["R90MH"] = 7,
+ ["orientation-8"] = 1, ["R270"] = 1,
+ },
+ function(t,k) -- transforms are 0 .. 7
+ local v = tonumber(k) or 0
+ if v < 0 or v > 7 then
+ v = 0
+ end
+ t[k] = v
+ return v
+ end
+)
+
+local function checktransform(figure,forced)
+ if auto_transform then
+
+ local orientation = (forced ~= "" and forced ~= v_auto and forced) or figure.orientation or 0
+ local transform = transforms["orientation-"..orientation]
+ figure.transform = transform
+ if odd(transform) then
+ return figure.height, figure.width
+ else
+ return figure.width, figure.height
+ end
+ end
+end
+
+local pagecount = { }
+
+function checkers.generic(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local name = du.fullname or "unknown generic"
+ local page = du.page or dr.page
+ local size = dr.size or "crop"
+ local color = dr.color or "natural"
+ local mask = dr.mask or "none"
+ local conversion = dr.conversion
+ local resolution = dr.resolution
+ local arguments = dr.arguments
+ local scanimage = dr.scanimage or scanimage
+ local userpassword = dr.userpassword
+ local ownerpassword = dr.ownerpassword
+ if not conversion or conversion == "" then
+ conversion = "default"
+ end
+ if not resolution or resolution == "" then
+ resolution = "default"
+ end
+ if not arguments or arguments == "" then
+ arguments = "default"
+ end
+ local hash = f_hash_full(
+ name,
+ page,
+ size,
+ color,
+ mask,
+ conversion,
+ resolution,
+ arguments
+ )
+ --
+ local figure = figures_loaded[hash]
+ if figure == nil then
+ figure = createimage {
+ filename = name,
+ page = page,
+ pagebox = dr.size,
+ keepopen = dr.keepopen or false,
+ userpassword = userpassword,
+ ownerpassword = ownerpassword,
+ -- visiblefilename = "", -- this prohibits the full filename ending up in the file
+ }
+ codeinjections.setfigurecolorspace(data,figure)
+ codeinjections.setfiguremask(data,figure)
+ if figure then
+ -- new, bonus check (a bogus check in lmtx)
+ if page and page > 1 then
+ local f = scanimage {
+ filename = name,
+ userpassword = userpassword,
+ ownerpassword = ownerpassword,
+ }
+ if f and f.page and f.pages < page then
+ report_inclusion("no page %i in %a, using page 1",page,name)
+ page = 1
+ figure.page = page
+ end
+ end
+ -- till here
+ local f, comment = checkimage(scanimage(figure))
+ if not f then
+ ds.comment = comment
+ ds.found = false
+ ds.error = true
+ end
+ if figure.attr and not f.attr then
+ -- tricky as img doesn't allow it
+ f.attr = figure.attr
+ end
+ if dr.cmyk == v_yes then
+ f.enforcecmyk = true
+ elseif dr.cmyk == v_auto and attributes.colors.model == "cmyk" then
+ f.enforcecmyk = true
+ end
+ figure = f
+ end
+ local f, d = codeinjections.setfigurealternative(data,figure)
+ figure = f or figure
+ data = d or data
+ figures_loaded[hash] = figure
+ if trace_conversion then
+ report_inclusion("new graphic, using hash %a",hash)
+ end
+ else
+ if trace_conversion then
+ report_inclusion("existing graphic, using hash %a",hash)
+ end
+ end
+ if figure then
+ local width, height = checktransform(figure,dr.transform)
+ --
+ du.width = width
+ du.height = height
+ du.pages = figure.pages
+ du.depth = figure.depth or 0
+ du.colordepth = figure.colordepth or 0
+ du.xresolution = figure.xres or 0
+ du.yresolution = figure.yres or 0
+ du.xsize = figure.xsize or 0
+ du.ysize = figure.ysize or 0
+ du.rotation = figure.rotation or 0 -- in pdf multiples or 90% in jpeg 1
+ du.orientation = figure.orientation or 0 -- jpeg 1 2 3 4 (0=unset)
+ ds.private = figure
+ ds.hash = hash
+ end
+ return data
+end
+
+local nofimages = 0
+local pofimages = { }
+
+function figures.getrealpage(index)
+ return pofimages[index] or 0
+end
+
+local function updatepage(specification)
+ local n = specification.n
+ pofimages[n] = pofimages[n] or tex.count.realpageno -- so when reused we register the first one only
+end
+
+function includers.generic(data)
+ local dr, du, ds = data.request, data.used, data.status
+ -- here we set the 'natural dimensions'
+ dr.width = du.width
+ dr.height = du.height
+ local hash = figures.hash(data)
+ local figure = figures_used[hash]
+ -- figures.registerresource {
+ -- filename = du.fullname,
+ -- width = dr.width,
+ -- height = dr.height,
+ -- }
+ if figure == nil then
+ figure = ds.private -- the img object
+ if figure then
+ figure = (dr.copyimage or copyimage)(figure)
+ if figure then
+ figure.width = dr.width or figure.width
+ figure.height = dr.height or figure.height
+ end
+ end
+ figures_used[hash] = figure
+ end
+ if figure then
+ local nr = figures.boxnumber
+ nofimages = nofimages + 1
+ ds.pageindex = nofimages
+ local image = wrapimage(figure)
+ local pager = new_latelua { action = updatepage, n = nofimages }
+ image.next = pager
+ pager.prev = image
+ local box = hpack(image)
+ box.width = figure.width
+ box.height = figure.height
+ box.depth = 0
+ texsetbox(nr,box)
+ ds.objectnumber = figure.objnum
+ -- indexed[figure.index] = figure
+ ctx_relocateexternalfigure()
+ end
+ return data
+end
+
+-- -- -- nongeneric -- -- --
+
+local function checkers_nongeneric(data,command) -- todo: macros and context.*
+ local dr, du, ds = data.request, data.used, data.status
+ local name = du.fullname or "unknown nongeneric"
+ local hash = name
+ if dr.object then
+ -- hm, bugged ... waiting for an xform interface
+ if not objects.data["FIG"][hash] then
+ if type(command) == "function" then
+ command()
+ end
+ ctx_dosetfigureobject("FIG",hash)
+ end
+ ctx_doboxfigureobject("FIG",hash)
+ elseif type(command) == "function" then
+ command()
+ end
+ return data
+end
+
+local function includers_nongeneric(data)
+ return data
+end
+
+checkers.nongeneric = checkers_nongeneric
+includers.nongeneric = includers_nongeneric
+
+-- -- -- mov -- -- --
+
+function checkers.mov(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local width = todimen(dr.width or figures.defaultwidth)
+ local height = todimen(dr.height or figures.defaultheight)
+ local foundname = du.fullname
+ dr.width, dr.height = width, height
+ du.width, du.height, du.foundname = width, height, foundname
+ if trace_inclusion then
+ report_inclusion("including movie %a, width %p, height %p",foundname,width,height)
+ end
+ -- we need to push the node.write in between ... we could make a shared helper for this
+ ctx_startfoundexternalfigure(width .. "sp",height .. "sp")
+ context(function()
+ nodeinjections.insertmovie {
+ width = width,
+ height = height,
+ factor = bpfactor,
+ ["repeat"] = dr["repeat"],
+ controls = dr.controls,
+ preview = dr.preview,
+ label = dr.label,
+ foundname = foundname,
+ }
+ end)
+ ctx_stopfoundexternalfigure()
+ return data
+end
+
+includers.mov = includers.nongeneric
+
+-- -- -- mps -- -- --
+
+internalschemes.mprun = true
+
+-- mprun.foo.1 mprun.6 mprun:foo.2
+
+local ctx_docheckfiguremprun = context.docheckfiguremprun
+local ctx_docheckfiguremps = context.docheckfiguremps
+
+local function internal(askedname)
+ local spec, mprun, mpnum = match(lower(askedname),"mprun([:%.]?)(.-)%.(%d+)")
+ -- mpnum = tonumber(mpnum) or 0 -- can be string or number, fed to context anyway
+ if spec ~= "" then
+ return mprun, mpnum
+ else
+ return "", mpnum
+ end
+end
+
+function existers.mps(askedname)
+ local mprun, mpnum = internal(askedname)
+ if mpnum then
+ return askedname, true, "mps", true
+ else
+ return existers.generic(askedname)
+ end
+end
+
+function checkers.mps(data)
+ local mprun, mpnum = internal(data.used.fullname)
+ if mpnum then
+ return checkers_nongeneric(data,function() ctx_docheckfiguremprun(mprun,mpnum) end)
+ else
+ return checkers_nongeneric(data,function() ctx_docheckfiguremps(data.used.fullname) end)
+ end
+end
+
+includers.mps = includers.nongeneric
+
+-- -- -- tex -- -- --
+
+local ctx_docheckfiguretex = context.docheckfiguretex
+
+function existers.tex(askedname)
+ askedname = resolvers.findfile(askedname)
+ return askedname ~= "" and askedname or false, true, "tex", true
+end
+
+function checkers.tex(data)
+ return checkers_nongeneric(data,function() ctx_docheckfiguretex(data.used.fullname) end)
+end
+
+includers.tex = includers.nongeneric
+
+-- -- -- buffer -- -- --
+
+local ctx_docheckfigurebuffer = context.docheckfigurebuffer
+
+function existers.buffer(askedname)
+ local name = file.nameonly(askedname)
+ local okay = buffers.exists(name)
+ return okay and name, true, "buffer", true -- always quit scanning
+end
+
+function checkers.buffer(data)
+ return checkers_nongeneric(data,function() ctx_docheckfigurebuffer(file.nameonly(data.used.fullname)) end)
+end
+
+includers.buffers = includers.nongeneric
+
+-- -- -- auto -- -- --
+
+function existers.auto(askedname)
+ local name = gsub(askedname, ".auto$", "")
+ local format = figures.guess(name)
+ -- if format then
+ -- report_inclusion("format guess %a for %a",format,name)
+ -- else
+ -- report_inclusion("format guess for %a is not possible",name)
+ -- end
+ return format and name, true, format
+end
+
+checkers.auto = checkers.generic
+includers.auto = includers.generic
+
+-- -- -- cld -- -- --
+
+local ctx_docheckfigurecld = context.docheckfigurecld
+
+function existers.cld(askedname)
+ askedname = resolvers.findfile(askedname)
+ return askedname ~= "" and askedname or false, true, "cld", true
+end
+
+function checkers.cld(data)
+ return checkers_nongeneric(data,function() ctx_docheckfigurecld(data.used.fullname) end)
+end
+
+includers.cld = includers.nongeneric
+
+-- -- -- converters -- -- --
+
+setmetatableindex(converters,"table")
+
+-- We keep this helper because it has been around for a while and therefore it can
+-- be a depedency in an existing workflow.
+
+function programs.makeoptions(options)
+ local to = type(options)
+ return (to == "table" and concat(options," ")) or (to == "string" and options) or ""
+end
+
+function programs.run(binary,argument,variables)
+ local found = nil
+ if type(binary) == "table" then
+ for i=1,#binary do
+ local b = binary[i]
+ found = os.which(b)
+ if found then
+ binary = b
+ break
+ end
+ end
+ if not found then
+ binary = concat(binary, " | ")
+ end
+ elseif binary then
+ found = os.which(match(binary,"[%S]+"))
+ end
+ if type(argument) == "table" then
+ argument = concat(argument," ") -- for old times sake
+ end
+ if not found then
+ report_inclusion("program %a is not installed",binary or "?")
+ elseif not argument or argument == "" then
+ report_inclusion("nothing to run, no arguments for program %a",binary)
+ else
+ -- no need to use the full found filename (found) .. we also don't quote the program
+ -- name any longer as in luatex there is too much messing with these names
+ local command = format([[%s %s]],binary,replacetemplate(longtostring(argument),variables))
+ if trace_conversion or trace_programs then
+ report_inclusion("running command: %s",command)
+ end
+ os.execute(command)
+ end
+end
+
+-- the rest of the code has been moved to grph-con.lua
+
+-- -- -- bases -- -- --
+
+local bases = allocate()
+figures.bases = bases
+
+local bases_list = nil -- index => { basename, fullname, xmlroot }
+local bases_used = nil -- [basename] => { basename, fullname, xmlroot } -- pointer to list
+local bases_found = nil
+local bases_enabled = false
+
+local function reset()
+ bases_list = allocate()
+ bases_used = allocate()
+ bases_found = allocate()
+ bases_enabled = false
+ bases.list = bases_list
+ bases.used = bases_used
+ bases.found = bases_found
+end
+
+reset()
+
+function bases.use(basename)
+ if basename == "reset" then
+ reset()
+ else
+ basename = file.addsuffix(basename,"xml")
+ if not bases_used[basename] then
+ local t = { basename, nil, nil }
+ bases_used[basename] = t
+ bases_list[#bases_list+1] = t
+ if not bases_enabled then
+ bases_enabled = true
+ xml.registerns("rlx","http://www.pragma-ade.com/schemas/rlx") -- we should be able to do this per xml file
+ end
+ if trace_bases then
+ report_inclusion("registering base %a",basename)
+ end
+ end
+ end
+end
+
+implement { name = "usefigurebase", actions = bases.use, arguments = "string" }
+
+local function bases_find(basename,askedlabel)
+ if trace_bases then
+ report_inclusion("checking for %a in base %a",askedlabel,basename)
+ end
+ basename = file.addsuffix(basename,"xml")
+ local t = bases_found[askedlabel]
+ if t == nil then
+ local base = bases_used[basename]
+ local page = 0
+ if base[2] == nil then
+ -- no yet located
+ for i=1,#figure_paths do
+ local path = resolveprefix(figure_paths[i]) -- we resolve (e.g. jobfile:)
+ local xmlfile = path .. "/" .. basename
+ if io.exists(xmlfile) then
+ base[2] = xmlfile
+ base[3] = xml.load(xmlfile)
+ if trace_bases then
+ report_inclusion("base %a loaded",xmlfile)
+ end
+ break
+ end
+ end
+ end
+ t = false
+ if base[2] and base[3] then -- rlx:library
+ for e in xml.collected(base[3],"/(*:library|figurelibrary)/*:figure/*:label") do
+ page = page + 1
+ if xml.text(e) == askedlabel then
+ t = {
+ base = file.replacesuffix(base[2],"pdf"),
+ format = "pdf",
+ name = xml.text(e,"../*:file"), -- to be checked
+ page = page,
+ }
+ bases_found[askedlabel] = t
+ if trace_bases then
+ report_inclusion("figure %a found in base %a",askedlabel,base[2])
+ end
+ return t
+ end
+ end
+ if trace_bases and not t then
+ report_inclusion("figure %a not found in base %a",askedlabel,base[2])
+ end
+ end
+ end
+ return t
+end
+
+-- we can access sequential or by name
+
+local function bases_locate(askedlabel)
+ for i=1,#bases_list do
+ local entry = bases_list[i]
+ local t = bases_find(entry[1],askedlabel,1,true)
+ if t then
+ return t
+ end
+ end
+ return false
+end
+
+function identifiers.base(data)
+ if bases_enabled then
+ local dr, du, ds = data.request, data.used, data.status
+ local fbl = bases_locate(dr.name or dr.label)
+ if fbl then
+ du.page = fbl.page
+ du.format = fbl.format
+ du.fullname = fbl.base
+ ds.fullname = fbl.name
+ ds.format = fbl.format
+ ds.page = fbl.page
+ ds.status = 10
+ end
+ end
+ return data
+end
+
+bases.locate = bases_locate
+bases.find = bases_find
+
+identifiers.list = {
+ identifiers.base,
+ identifiers.default
+}
+
+-- tracing
+
+statistics.register("graphics processing time", function()
+ local nofprocessed = figures.nofprocessed
+ if nofprocessed > 0 then
+ local nofnames, nofbadnames = 0, 0
+ for hash, data in next, figures_found do
+ nofnames = nofnames + 1
+ if data.badname then
+ nofbadnames = nofbadnames + 1
+ end
+ end
+ return format("%s seconds including tex, %s processed images, %s unique asked, %s bad names",
+ statistics.elapsedtime(figures),nofprocessed,nofnames,nofbadnames)
+ else
+ return nil
+ end
+end)
+
+-- helper
+
+function figures.applyratio(width,height,w,h) -- width and height are strings and w and h are numbers
+ if not width or width == "" then
+ if not height or height == "" then
+ return figures.defaultwidth, figures.defaultheight
+ else
+ height = todimen(height)
+ if w and h then
+ return height * w/h, height
+ else
+ return figures.defaultwidth, height
+ end
+ end
+ else
+ width = todimen(width)
+ if not height or height == "" then
+ if w and h then
+ return width, width * h/w
+ else
+ return width, figures.defaultheight
+ end
+ else
+ return width, todimen(height)
+ end
+ end
+end
+
+-- example of simple plugins:
+--
+-- figures.converters.png = {
+-- png = function(oldname,newname,resolution)
+-- local command = string.format('gm convert -depth 1 "%s" "%s"',oldname,newname)
+-- logs.report(string.format("running command %s",command))
+-- os.execute(command)
+-- end,
+-- }
+
+-- local n = "foo.pdf"
+-- local d = figures.getinfo(n)
+-- if d then
+-- for i=1,d.used.pages do
+-- local p = figures.getinfo(n,i)
+-- if p then
+-- local u = p.used
+-- print(u.width,u.height,u.orientation)
+-- end
+-- end
+-- end
+
+function figures.getinfo(name,page)
+ if type(name) == "string" then
+ name = { name = name, page = page }
+ end
+ if name.name then
+ local data = figures.push(name)
+ data = figures.identify(data)
+ if data.status and data.status.status > 0 then
+ data = figures.check(data)
+ end
+ figures.pop()
+ return data
+ end
+end
+
+function figures.getpdfinfo(name,page,metadata)
+ -- not that useful but as we have it for detailed inclusion we can as
+ -- we expose it
+ if type(name) ~= "table" then
+ name = { name = name, page = page, metadata = metadata }
+ end
+ return codeinjections.getinfo(name)
+end
+
+-- interfacing
+
+implement {
+ name = "figure_push",
+ scope = "private",
+ actions = figures.push,
+ arguments = {
+ {
+ { "name" },
+ { "label" },
+ { "page" },
+ { "file" },
+ { "size" },
+ { "object" },
+ { "prefix" },
+ { "cache" },
+ { "format" },
+ { "preset" },
+ { "controls" },
+ { "resources" },
+ { "preview" },
+ { "display" },
+ { "mask" },
+ { "conversion" },
+ { "resolution" },
+ { "color" },
+ { "cmyk" },
+ { "arguments" },
+ { "repeat" },
+ { "transform" },
+ { "compact" },
+ { "width", "dimen" },
+ { "height", "dimen" },
+ { "userpassword" },
+ { "ownerpassword" },
+ }
+ }
+}
+
+-- beware, we get a number passed by default
+
+implement { name = "figure_pop", scope = "private", actions = figures.pop }
+implement { name = "figure_done", scope = "private", actions = figures.done }
+implement { name = "figure_dummy", scope = "private", actions = figures.dummy }
+implement { name = "figure_identify", scope = "private", actions = figures.identify }
+implement { name = "figure_scale", scope = "private", actions = figures.scale }
+implement { name = "figure_check", scope = "private", actions = figures.check }
+implement { name = "figure_include", scope = "private", actions = figures.include }
+
+implement {
+ name = "setfigurelookuporder",
+ actions = figures.setorder,
+ arguments = "string"
+}
+
+implement {
+ name = "figure_reset",
+ scope = "private",
+ arguments = { "integer", "dimen", "dimen" },
+ actions = function(box,width,height)
+ figures.boxnumber = box
+ figures.defaultwidth = width
+ figures.defaultheight = height
+ end
+}
+
+-- require("util-lib-imp-gm")
+--
+-- figures.converters.tif.pdf = function(oldname,newname,resolution)
+-- logs.report("graphics","using gm library to convert %a",oldname)
+-- utilities.graphicmagick.convert {
+-- inputname = oldname,
+-- outputname = newname,
+-- }
+-- end
+--
+-- \externalfigure[t:/sources/hakker1b.tiff]
+
+-- something relatively new:
+
+local registered = { }
+
+local ctx_doexternalfigurerepeat = context.doexternalfigurerepeat
+
+implement {
+ name = "figure_register_page",
+ arguments = "3 strings",
+ actions = function(a,b,c)
+ registered[#registered+1] = { a, b, c }
+ context(#registered)
+ end
+}
+
+implement {
+ name = "figure_nof_registered_pages",
+ actions = function()
+ context(#registered)
+ end
+}
+
+implement {
+ name = "figure_flush_registered_pages",
+ arguments = "string",
+ actions = function(n)
+ local f = registered[tonumber(n)]
+ if f then
+ ctx_doexternalfigurerepeat(f[1],f[2],f[3],n)
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-inc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-inc.mkxl
new file mode 100644
index 00000000000..3c2e8e5c1d9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-inc.mkxl
@@ -0,0 +1,1073 @@
+%D \module
+%D [ file=grph-inc, % moved from core-fig
+%D version=2006.08.26, % overhaul of 1997.03.31
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Figure Inclusion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% \enabledirectives[graphics.conversion.eps.cleanup.ai]
+
+% \setupexternalfigures[directory=dirfile://./test/**]
+% \externalfigure[crappname(2).eps][frame=on]
+
+% todo: messages
+
+\writestatus{loading}{ConTeXt Graphic Macros / Figure Inclusion}
+
+\registerctxluafile{grph-img}{}
+\registerctxluafile{grph-inc}{autosuffix}
+\registerctxluafile{grph-bmp}{}
+\registerctxluafile{grph-chk}{}
+\registerctxluafile{grph-con}{}
+\registerctxluafile{grph-fil}{}
+%registerctxluafile{grph-mem}{} % obsolete
+\registerctxluafile{grph-u3d}{} % this will become a module
+%registerctxluafile{grph-swf}{} % this will become a module
+
+\unprotect
+
+%D Including graphics is complicated by the fact that we need to locate them first,
+%D optionally manipulate them and scale then next. Lookups are to be done as efficient
+%D as possible and inclusion of the data might happens only once. In \MKIV\ much of this
+%D is delegated to the \LUA\ end. There is not so much less code as in \MKII\ but it's
+%D more powerful, flexible, pluggable and some of the extended functionality has been
+%D moved from modules to the core. The overall functionality is rather stable and has
+%D not changed much over the years.
+
+\ifdefined\dotagfigure \else \aliased\let\dotagfigure\relax \fi
+
+\installcorenamespace{externalfigure}
+\installcorenamespace{externalfigureinstance}
+\installcorenamespace{externalfigurecollection}
+
+\installframedcommandhandler \??externalfigure {externalfigure} \??externalfigure
+
+\aliased\let\setupexternalfigures\setupexternalfigure
+
+\setupexternalfigures[% we really need the defaults
+ \c!method =,
+ \c!label =,
+ \c!size =,
+ \c!conversion =,
+ \c!resolution =,
+ \c!prefix =,
+ \c!cache =,
+ \c!page =\zerocount,
+ \c!file =,
+ \c!display =,
+ \c!mask =,
+ \c!preset =\v!yes,
+ \c!split =,
+ \c!color =,
+ \c!arguments =,
+ \c!symbol =\v!no,
+ \c!controls =\v!no,
+ \c!resources =,
+ \c!preview =\v!no,
+ \c!repeat =\v!no,
+ \c!foregroundcolor=,
+ \c!interaction =\v!none,
+ \c!hfactor =,
+ \c!wfactor =,
+ \c!factor =,
+ \c!defaultwidth =8\lineheight,
+ \c!defaultheight =6\lineheight,
+ \c!maxwidth =\externalfigureparameter\c!width,
+ \c!maxheight =\externalfigureparameter\c!height,
+ \c!xscale =,
+ \c!yscale =,
+ \c!scale =,
+ \c!sx =\externalfigureparameter\c!s,
+ \c!sy =\externalfigureparameter\c!s,
+ \c!s =1,
+ \c!width =,
+ \c!height =,
+ \c!lines =,
+ \c!grid =,
+ \c!bodyfont =\bodyfontsize,
+ \c!object =\v!yes,
+ \c!corner =\v!rectangular,
+ \c!frame =\v!off,
+ \c!option =,
+ \c!reset =\v!no,
+ \c!directory =,
+ \c!radius =.5\bodyfontsize,
+ \c!background =,
+ \c!splitcolor =\s!white,
+ \c!order =,
+ \c!equalwidth =,
+ \c!equalheight =,
+ \c!location ={\v!local,\v!global},
+ \c!frames =\v!off,
+ \c!ymax =24,
+ \c!xmax =,
+ \c!align =\v!none, % New, for Tacos extremely large graphics.
+ \c!crossreference =\v!no,
+ \c!transform =\v!auto,
+ \c!userpassword =,
+ \c!ownerpassword =,
+ \c!compact =,
+ \c!cmyk =,
+ ]
+
+%D Defining figures.
+
+\newcount\c_grph_include_nesting
+
+\newtoks \everyexternalfigureresets % for the moment still public
+\newtoks \everyexternalfigurechecks % for the moment still public
+
+% \useexternalfigure[alpha][cow]
+% \useexternalfigure[beta] [cow] [width=1cm]
+% \useexternalfigure[gamma][cow][alpha]
+% \useexternalfigure[delta][cow][alpha][width=2cm]
+%
+% full width : \externalfigure[cow] \par
+% 3cm width : \externalfigure[cow] [width=3cm] \par
+% full width : \externalfigure[alpha] \par
+% 1cm width : \externalfigure[beta] \par
+% full width : \externalfigure[gamma] \par
+% 2cm width : \externalfigure[delta] \par
+% 4cm width : \externalfigure[beta] [width=4cm] \par
+% 5cm width : \externalfigure[gamma][width=5cm] \par
+%
+% \defineexternalfigure[a][width=10cm]
+% \defineexternalfigure[b][width=5cm]
+% \externalfigure[cow][a]
+% \externalfigure[cow][b][height=8cm]
+%
+% \useexternalfigure[x][cow][width=10cm,height=1cm]
+% \externalfigure[x]
+% \externalfigure[x][width=3cm]
+%
+% [label] [filename]
+% [label] [filename] [parent]
+% [label] [filename] [parent] [settings]
+% [label] [filename] [settings]
+%
+% new: more convenient/efficient than
+%
+% \use..[a][a][setting] \externalfigure[b][a]
+%
+% is equivalent to:
+%
+% \def..[a][setting] \externalfigure[b][a]
+%
+% see x-res modules for usage:
+%
+% \defineexternalfigure[name][settings]
+
+%D Defining is persistent, i.e.\ when you redefine an instance, the already set
+%D parameters need to be set again or otherwise the old values will be used.
+%D
+%D New: \type {method=auto}: strips suffix and uses \quote {order} which is handy in
+%D some of four workflows where sources are used for web and print and where the web
+%D tools need a suffix (like gif) which we don't want as we want a high quality
+%D format.
+
+\newconditional\c_grph_include_trace_inheritance
+
+\installtextracker
+ {graphics.inheritance}
+ {\settrue \c_grph_include_trace_inheritance}
+ {\setfalse\c_grph_include_trace_inheritance}
+
+\installcorenamespace{externalfiguredefinition}
+
+\aliased\let\defineexternalfigures\defineexternalfigure
+
+% label file parent settings
+% label file settings
+% label file parent
+
+\permanent\tolerant\protected\def\useexternalfigure[#1]#*[#2]#*[#3]#*[#4]%
+ {\iftok{#1}\emptytoks
+ \iftok{#2}\emptytoks\else
+ \ifhastok={#3}%
+ \grph_include_use_indeed{#2}{#2}{#3}{#4}%
+ \else
+ \grph_include_use_indeed{#2}{#2}\empty{#4}%
+ \fi
+ \fi
+ \orelse\iftok{#2}\emptytoks
+ \ifhastok={#3}%
+ \grph_include_use_indeed{#1}{#1}\empty{#3}%
+ \else
+ \grph_include_use_indeed{#1}{#1}{#3}{#4}%
+ \fi
+ \else
+ \ifhastok={#3}%
+ \grph_include_use_indeed{#1}{#2}\empty{#3}
+ \else
+ \grph_include_use_indeed{#1}{#2}{#3}{#4}%
+ \fi
+ \fi}
+
+\def\grph_include_use_indeed#1#2#3#4%
+ {\dodoglobal\setvalue{\??externalfigureinstance#1}{\grph_include_setup{#2}{#3}{#4}}%
+ \grph_include_analyze_collection[#2][#4]}
+
+% inclusion
+
+\permanent\tolerant\protected\def\externalfigure[#1]#*[#2]#*[#3]%
+ {\ifhastok={#2}%
+ \grph_include_place[#1][][#2]%
+ \else
+ \grph_include_place[#1][#2][#3]%
+ \fi}
+
+% todo: chain them
+
+\def\grph_include_setup#1#2#3% name parent settings
+ {\edef\m_grph_include_name {#1}%
+ \edef\m_grph_include_parent{#2}%
+ \ifempty\m_grph_include_name \else
+ \let\p_grph_include_name\m_grph_include_name
+ \fi
+ \ifempty\m_grph_include_parent \else
+ \grph_include_inherit_from_parent\m_grph_include_parent
+ \fi
+ \setupcurrentexternalfigure[#3]}
+
+\def\grph_include_inherit_from_parent#1%
+ {\ifcsname\??externalfigure#1:\s!parent\endcsname
+ \let\currentexternalfigure#1%
+ \fi
+ \ifcsname\??externalfigureinstance#1\endcsname
+ \ifconditional\c_grph_include_trace_inheritance\writestatus\m!figures{inheriting from instance: #1}\fi
+ \csname\??externalfigureinstance#1\endcsname
+ \fi}
+
+\newtoks\t_grph_include_local_settings
+
+\appendtoks
+ \enforced\let\textunderscore\letterunderscore % {\string _} % space needed as _ is now letter in unprotected mode (probably no longer needed)
+ %
+ \dontcomplain
+ \restorecatcodes
+ \forgetall
+\to \t_grph_include_local_settings
+
+\def\grph_include_place_inherit
+ {\ifconditional\c_grph_include_trace_inheritance
+ \writestatus\m!figures{label: \p_grph_include_label, name: \p_grph_include_name, parent: \p_grph_include_parent}%
+ \fi
+ \ifempty\p_grph_include_parent
+ % nothing to be done
+ \orelse\ifx\p_grph_include_parent\p_grph_include_label
+ % redundant
+ \else
+ \grph_include_inherit_from_parent\p_grph_include_parent
+ \fi
+ \ifempty\p_grph_include_label
+ % nothing to be done
+ \else
+ \grph_include_inherit_from_parent\p_grph_include_label
+ \fi}
+
+\def\grph_include_place[#1][#2][#3]% [label][file][settings] | [file][settings] | [file][parent][settings]
+ {\bgroup
+ \advance\c_grph_include_nesting\plusone
+ \edef\currentexternalfigure{\the\c_grph_include_nesting}%
+ \checkexternalfigureparent % each inherits from the root
+ %
+ \the\everyexternalfigureresets
+ %
+ \edef\p_grph_include_label{#1}%
+ \let\p_grph_include_name\p_grph_include_label
+ \ifhastok={#2}%
+ % [label] [settings]
+ \let\p_grph_include_parent\p_grph_include_label
+ \grph_include_place_inherit
+ \setupcurrentexternalfigure[#2]%
+ \else
+ % [label] [parent] [settings]
+ \edef\p_grph_include_parent{#2}%
+ \ifempty\p_grph_include_parent
+ \let\p_grph_include_parent\p_grph_include_label
+ \fi
+ \grph_include_place_inherit
+ \setupcurrentexternalfigure[#3]%
+ \fi
+ %
+ \the\everyexternalfigurechecks
+ %
+ \the\t_grph_include_local_settings
+ %
+ \edef\p_width {\externalfigureparameter\c!width}%
+ \edef\p_height {\externalfigureparameter\c!height}%
+ \edef\p_label {\externalfigureparameter\c!label}%
+ \edef\p_reference{\externalfigureparameter\c!reference}%
+ %
+ \dostarttagged\t!image\empty
+ \clf_figure_push {
+ name {\p_grph_include_name}%
+ label {\ifempty\p_label\p_grph_include_label\else\p_label\fi}%
+ page {\externalfigureparameter\c!page}%
+ file {\externalfigureparameter\c!file}%
+ size {\externalfigureparameter\c!size}%
+ object {\externalfigureparameter\c!object}%
+ prefix {\externalfigureparameter\c!prefix}%
+ cache {\externalfigureparameter\c!cache}%
+ format {\externalfigureparameter\c!method}%
+ preset {\externalfigureparameter\c!prefix}%
+ controls {\externalfigureparameter\c!controls}%
+ resources {\externalfigureparameter\c!resources}%
+ preview {\externalfigureparameter\c!preview}%
+ display {\externalfigureparameter\c!display}%
+ mask {\externalfigureparameter\c!mask}%
+ conversion {\externalfigureparameter\c!conversion}%
+ resolution {\externalfigureparameter\c!resolution}%
+ color {\externalfigureparameter\c!color}% unprocessed raw key
+ cmyk {\externalfigureparameter\c!cmyk}% kind of special
+ arguments {\externalfigureparameter\c!arguments}% used for converters
+ repeat {\externalfigureparameter\c!repeat}%
+ transform {\externalfigureparameter\c!transform}%
+ compact {\externalfigureparameter\c!compact}% experiment, share fonts
+ userpassword {\externalfigureparameter\c!userpassword}%
+ ownerpassword{\externalfigureparameter\c!ownerpassword}%
+ \ifempty\p_width \else
+ width \dimexpr\p_width\relax
+ \fi
+ \ifempty\p_height \else
+ height \dimexpr\p_height\relax
+ \fi
+ }%\relax
+ \clf_figure_identify
+ \relax
+ \ifconditional\c_grph_include_test_only
+ \ifcase\figurestatus \else
+ \clf_figure_check
+ \clf_figure_dummy
+ \clf_figure_scale
+ \clf_figure_done
+ \fi
+ \grph_include_set_mode
+ \else
+ \ifcase\figurestatus
+ \clf_figure_dummy
+ \clf_figure_scale
+ \else
+ \clf_figure_check
+ \clf_figure_include
+ \clf_figure_scale
+ \fi
+ \clf_figure_done
+ \grph_include_set_mode
+ \grph_include_finalize
+ \fi
+ \clf_figure_pop
+ \dotagfigure
+ %
+ \scratchcounter\zerocount
+ \edef\p_crossreference{\externalfigureparameter\c!crossreference}%
+ \iflocation\iftrialtypesetting\else
+ \ifempty\p_crossreference
+ % nothing
+ \orelse\ifx\p_crossreference\v!no
+ % nothing
+ \orelse\ifx\p_crossreference\v!yes
+ \ifhastok={#2}%
+ \scratchcounter\clf_figure_register_page{#1}{}{#2}\relax
+ \else
+ \scratchcounter\clf_figure_register_page{#1}{#2}{#3}\relax
+ \fi
+ \else
+ \scratchcounter-\p_crossreference % passed by repeater
+ \fi
+ \fi\fi
+ \naturalvpack
+ attr \imageattribute \plustwo
+ \ifnum\scratchcounter>\zerocount
+ {\strc_references_cross_forward {ex:\number \scratchcounter}{\box\foundexternalfigure}}%
+ \orelse\ifnum\scratchcounter<\zerocount
+ {\strc_references_cross_backward{ex:\number-\scratchcounter}{\box\foundexternalfigure}}%
+ \else
+ {\box\foundexternalfigure}%
+ \fi
+ %
+ % \naturalvpack attr \imageattribute \plustwo \bgroup
+ % \ifcmpnum\scratchcounter\zerocount
+ % \strc_references_cross_backward{ex:\number-\scratchcounter}{\box\foundexternalfigure}%
+ % \or % equal
+ % \box\foundexternalfigure%
+ % \or % more
+ % \strc_references_cross_forward {ex:\number \scratchcounter}{\box\foundexternalfigure}%
+ % \fi
+ % \egroup
+ %
+ \dostoptagged
+ \egroup}
+
+%D Next we provide a cross referenced scale-up mechanism:
+
+\def\strc_references_cross_forward#1#2%
+ {\begingroup
+ \strc_references_set_simple_internal_reference{cross:b:#1}%
+ \naturalhpack
+ attr \destinationattribute\number\lastdestinationattribute
+ {\naturalhpack{\strc_references_flush_destination_nodes\directgotodumbbox{#2}[cross:f:#1]}}%
+ \endgroup}
+
+\def\strc_references_cross_backward#1#2%
+ {\begingroup
+ \strc_references_set_simple_internal_reference{cross:f:#1}%
+ \naturalhpack
+ attr \destinationattribute\number\lastdestinationattribute
+ {\naturalhpack{\strc_references_flush_destination_nodes\directgotodumbbox{#2}[cross:b:#1]}}%
+ \endgroup}
+
+\aliased\let\doexternalfigurerepeat\gobblefourarguments % called from lua end
+
+\permanent\tolerant\protected\def\placeregisteredexternalfigures[#1]%
+ {\page
+ \begingroup
+ \enforced\protected\def\doexternalfigurerepeat{\grph_include_place_registered_indeed{#1}}%
+ \dorecurse\clf_figure_nof_registered_pages
+ {\page
+ \clf_figure_flush_registered_pages\recurselevel
+ \page}%
+ \endgroup
+ \page}
+
+\protected\def\grph_include_place_registered_indeed#1#2#3#4#5% called from lua end
+ {\grph_include_place[#2][#3][#4,%
+ \c!hfactor=,\c!wfactor=,\c!factor=,%
+ \c!maxwidth=,\c!maxheight=,%
+ \c!xscale=,\c!yscale=,\c!scale=,%
+ \c!sx=,\c!sy=,\c!s=,%
+ \c!width=,\c!height=,%
+ \c!crossreference=#5,%
+ #1]}
+
+%D Scaling:
+
+\mutable\let\dowithfigure\relax % name might change (into a proper hook)
+
+\permanent\protected\def\doscalefigure % used at lua end
+ {\global\setbox\foundexternalfigure\vpack{\scale[\v!figure]{\dowithfigure{\box\foundexternalfigure}}}}
+
+\definescale % some day we will inherit
+ [\v!figure]
+ [\c!hfactor =\externalfigureparameter\c!hfactor,
+ \c!wfactor =\externalfigureparameter\c!wfactor,
+ \c!factor =\externalfigureparameter\c!factor,
+ \c!maxwidth =\externalfigureparameter\c!maxwidth,
+ \c!maxheight =\externalfigureparameter\c!maxheight,
+ \c!equalwidth =\externalfigureparameter\c!equalwidth,
+ \c!equalheight=\externalfigureparameter\c!equalheight,
+ \c!xscale =\externalfigureparameter\c!xscale,
+ \c!yscale =\externalfigureparameter\c!yscale,
+ \c!scale =\externalfigureparameter\c!scale,
+ \c!sx =\externalfigureparameter\c!sx,
+ \c!sy =\externalfigureparameter\c!sy,
+ \c!s =\externalfigureparameter\c!s,
+ \c!width =\externalfigureparameter\c!width,
+ \c!height =\externalfigureparameter\c!height,
+ \c!lines =\externalfigureparameter\c!lines]
+
+%D You can register additional suffixes with the following command:
+%D
+%D \starttyping
+%D \definegraphictypesynonym[jbig] [jb2]
+%D \definegraphictypesynonym[jbig2][jb2]
+%D \definegraphictypesynonym[jbg] [jb2]
+%D \stoptyping
+
+\permanent\tolerant\protected\def\definegraphictypesynonym[#1]#*[#2]%
+ {\clf_registerfiguresuffix{#1}{#2}}
+
+%D Additional paths can be installed with the regular setup command. The next
+%D macro picks up the list.
+
+\permanent\protected\def\setfigurepathlist
+ {\clf_setfigurepaths{\externalfigureparameter\c!location}{\externalfigureparameter\c!directory}}
+
+%D Variables:
+
+\newbox \foundexternalfigure
+\newtoks\externalfigurepostprocessors
+
+\permanent\def\figurestatus {\numexpr\clf_figurestatus{status}{0}\relax} % number: 0 = not found
+\permanent\def\figurewidth {\clf_figurestatus{width}{0}sp}
+\permanent\def\figureheight {\clf_figurestatus{height}{0}sp}
+\permanent\def\figurexscale {\clf_figurestatus{xscale}{1}}
+\permanent\def\figureyscale {\clf_figurestatus{yscale}{1}}
+\permanent\def\figurereference {\clf_figurestatus{reference}{}}
+
+\permanent\def\figuresize {\clf_figurerequest{size}{}}
+\permanent\def\figurelabel {\clf_figurerequest{label}{}}
+\permanent\def\figurefileoriginal {\clf_figurerequest{name}{}}
+\permanent\def\figurefilepage {\clf_figurerequest{page}{1}}
+\permanent\def\figurefileoptions {\clf_figurerequest{options}{}}
+\permanent\def\figurefileconversion{\clf_figurerequest{conversion}{}}
+\permanent\def\figurefileresolution{\clf_figurerequest{resolution}{}}
+\permanent\def\figurefilecolor {\clf_figurerequest{color}{}}
+\permanent\def\figurefilearguments {\clf_figurerequest{arguments}{}}
+\permanent\def\figurefilecache {\clf_figurerequest{cache}{}}
+\permanent\def\figurefileprefix {\clf_figurerequest{prefix}{}}
+
+\permanent\def\figurenaturalwidth {\clf_figureused{width}{\number\dimexpr\externalfigureparameter\c!defaultwidth\relax}sp}
+\permanent\def\figurenaturalheight {\clf_figureused{height}{\number\dimexpr\externalfigureparameter\c!defaultheight\relax}sp}
+\permanent\def\figurexresolution {\clf_figureused{xresolution}{0}}
+\permanent\def\figureyresolution {\clf_figureused{yresolution}{0}}
+\permanent\def\figureorientation {\clf_figureused{orientation}{1}}
+\permanent\def\figurerotation {\clf_figureused{rotation}{0}}
+\permanent\def\figurexsize {\clf_figureused{xsize}{0}}
+\permanent\def\figureysize {\clf_figureused{ysize}{0}}
+\permanent\def\figurecolordepth {\clf_figureused{colordepth}{0}}
+\permanent\def\figuredepth {\clf_figureused{depth}{0}}
+
+\permanent\def\figurefullname {\clf_figureused{fullname}{}}
+\permanent\def\noffigurepages {\clf_figureused{pages}{0}}
+
+
+% \permanent\def\figurefilepath {\clf_figurefilepath} % public in lua
+% \permanent\def\figurefilename {\clf_figurefilename} % public in lua
+% \permanent\def\figurefiletype {\clf_figurefiletype} % public in lua
+
+\permanent\let\naturalfigurewidth \figurenaturalwidth
+\permanent\let\naturalfigureheight \figurenaturalheight
+
+\aliased\let\figurescalewidth \figurewidth
+\aliased\let\figurescaleheight \figureheight
+\aliased\let\figurescalexscale \figurexscale
+\aliased\let\figurescaleyscale \figureyscale
+
+%D Abuse:
+%D
+%D \starttyping
+%D \externalfigure[rubish.pdf] \ifcase\figurestatus\relax \ctxlua{os.exit(999)} \fi
+%D \stoptyping
+
+%D Calculating:
+
+% \enabletrackers[figures.conversion]
+% \externalfigure[demo.svg]
+% \externalfigure[demo.svg][conversion=png]
+
+%D The following registers are used (if only to be downward compatible).
+
+\newconditional\c_grph_include_skip
+\newconditional\c_grph_include_test_only
+\newconditional\c_grph_include_level \setfalse\c_grph_include_level % true=background false=normal
+\newconditional\c_grph_include_flush \settrue \c_grph_include_flush % true=place false=ignore
+
+\newsystemmode\v!figure
+
+\def\grph_include_set_mode
+ {\ifcase\figurestatus
+ \globalresetsystemmode\v!figure % todo, also: \v!resource
+ \else
+ \globalsetsystemmode \v!figure % todo, also: \v!resource
+ \fi}
+
+\appendtoks
+ \clf_setfigurepaths
+ {\externalfigureparameter\c!location}%
+ {\externalfigureparameter\c!directory}%
+ \clf_figure_reset
+ \foundexternalfigure
+ \externalfigureparameter\c!defaultwidth
+ \externalfigureparameter\c!defaultheight
+ \relax
+\to \everyexternalfigureresets
+
+\appendtoks
+ \edef\p_option{\externalfigureparameter\c!option}%
+ \ifx\p_option\v!frame
+ \setfalse\c_grph_include_skip
+ \letexternalfigureparameter\c!frame\v!on
+ \orelse\ifx\p_option\v!empty
+ \settrue\c_grph_include_skip
+ \letexternalfigureparameter\c!frame\v!off
+ \else
+ \setfalse\c_grph_include_skip
+ \fi
+ % fake color in gray bitmaps, assumes that
+ % a transparent color is used
+ \edef\p_foregroundcolor{\externalfigureparameter\c!foregroundcolor}%
+ \ifempty\p_foregroundcolor \else
+ \setexternalfigureparameter\c!background{\v!foreground,\v!color}%
+ \letexternalfigureparameter\c!backgroundcolor\p_foregroundcolor
+ \fi
+\to \everyexternalfigurechecks
+
+%D Internal graphics are handled at the \TEX\ end:
+
+\def\grph_include_process_tex#1%
+ {\framed
+ [\c!strut=\v!no,\c!align=\v!normal,\c!frame=\v!off,
+ \c!offset=\v!overlay,\c!width=\v!fit,\c!height=\v!fit]
+ {\blank[\v!disable]#1\endgraf\removelastskip}} % disable should stay here!
+
+% used al lua end:
+
+\permanent\protected\def\docheckfigurebuffer #1{\global\setbox\foundexternalfigure\vpack{\grph_include_process_tex{\getbuffer[#1]}}}
+\permanent\protected\def\docheckfiguretex #1{\global\setbox\foundexternalfigure\vpack{\grph_include_process_tex{\input{#1}}}}
+\permanent\protected\def\docheckfigurecld #1{\global\setbox\foundexternalfigure\vbox {\cldprocessfile{#1}}}
+\permanent\protected\def\docheckfiguremps #1{\global\setbox\foundexternalfigure\vpack{\convertMPtoPDF{#1}11}}
+\permanent\protected\def\docheckfiguremprun #1#2{\global\setbox\foundexternalfigure\vpack{\useMPrun{#1}{#2}}}
+
+\permanent\protected\def\relocateexternalfigure
+ {\global\setbox\foundexternalfigure\vpack to \ht\foundexternalfigure\bgroup
+ %
+ % The \vss can (!) introduce 1 sp excess visible in xform which in itself
+ % is not that important but some don't like these cosmetic side effects, for
+ % instance we can get:
+ %
+ % vss : \vbox(845.1575+0.0)x597.23125, glue set 845.15747fil, direction TLT
+ % vskip : \vbox(845.1575+0.0)x597.23125, direction TLT
+ %
+ % or
+ %
+ % 1 0 0 1 0 0.00003 cm
+ % 1 0 0 1 0 0 cm
+ %
+ % This is a known property of using glue and can even depend on the architecture
+ % (float implementation). Anyway, let's for now use a skip. Of course this can
+ % shift the issue elsewhere, as vss is used a lot elsewhere.
+ %
+ % \vss
+ \vkern\ht\foundexternalfigure
+ %
+ \ht\foundexternalfigure\zeropoint
+ \dp\foundexternalfigure\zeropoint
+ \hpack to \wd\foundexternalfigure\bgroup
+ \box\foundexternalfigure
+ \hss
+ \egroup
+ \egroup}
+
+\permanent\protected\def\startfoundexternalfigure#1#2% ht wd
+ {\global\setbox\foundexternalfigure\vbox to #2\bgroup\vss\hbox to #1\bgroup}
+
+\permanent\protected\def\stopfoundexternalfigure
+ {\hss\egroup\egroup}
+
+\permanent\protected\def\emptyfoundexternalfigure % sort of obsolete
+ {\startfoundexternalfigure{\externalfigureparameter\c!defaultwidth}{\externalfigureparameter\c!defaultheight}%
+ \stopfoundexternalfigure}
+
+% \doifmodeelse{*\v!last}
+% {\settrue \c_grph_include_flush}
+% {\setfalse\c_grph_include_flush}%
+
+\def\grph_include_finalize
+ {\global\setbox\foundexternalfigure\vbox
+ {\ifcase\figurestatus
+ \letexternalfigureparameter\c!frame\v!on
+ \fi
+ \ifconditional\c_grph_include_flush
+ \ifconditional\c_grph_include_level % probably background
+ \ifconditional\c_grph_include_skip
+ % nothing
+ \fakebox\foundexternalfigure
+ \orelse\ifcase\figurestatus
+ % nothing
+ \else
+ \the\externalfigurepostprocessors
+ \box\foundexternalfigure
+ \fi
+ \else
+ \iftrialtypesetting \else \feedbackexternalfigure \fi
+ \settrue\c_grph_include_level
+ \ifconditional\c_grph_include_skip
+ \ifcase\figurestatus
+ \grph_include_replacement\figurelabel\figurefileoriginal{unknown}%
+ \else
+ \grph_include_replacement\figurelabel\figurefullname{skipped}%
+ \fi
+ \orelse\ifcase\figurestatus
+ \grph_include_replacement\figurelabel\figurefileoriginal{unknown}%
+ \else
+ \the\externalfigurepostprocessors
+ \edef\p_reset{\externalfigureparameter\c!reset}%
+ \ifx\p_reset\v!yes
+ \wd\foundexternalfigure\figurewidth
+ \ht\foundexternalfigure\figureheight
+ \dp\foundexternalfigure\zeropoint
+ \box\foundexternalfigure
+ \else
+ \letexternalfigureparameter\c!offset\v!overlay
+ \letexternalfigureparameter\c!width \figurewidth
+ \letexternalfigureparameter\c!height\figureheight
+ \inheritedexternalfigureframed{\box\foundexternalfigure}%
+ \fi
+ \fi
+ \fi
+ \else
+ % maybe also \the\externalfigurepostprocessors
+ \iftrialtypesetting \else \feedbackexternalfigure \fi
+ \fi}}
+
+\mutable\let\feedbackexternalfigure\relax % hook
+
+\permanent\protected\def\getfiguredimensions
+ {\dodoubleempty\grph_include_get_dimensions}
+
+\def\grph_include_get_dimensions[#1][#2]%
+ {\startnointerference
+ \settrue\c_grph_include_test_only
+ \externalfigure[#1][#2,\c!display=,\c!mask=,\c!object=\v!no]%
+ \stopnointerference}
+
+\permanent\protected\def\doifelsefigure#1%
+ {\getfiguredimensions[#1]% so data is available !
+ \ifcase\figurestatus
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doiffigureelse\doifelsefigure
+
+% No placement, handy for preprocessing:
+
+\permanent\tolerant\protected\def\registerexternalfigure[#1]#*[#2]#*[#3]%
+ {\startnointerference
+ \c_grph_include_test_only
+ \setfalse\c_grph_include_flush % == test ?
+ \externalfigure[#1][#2][#3]% or
+ \externalfigure[#1][#2,\c!display=,\c!mask=,\c!object=\v!no]%
+ \stopnointerference}
+
+% Helpers will be replaced when xforms are accessible at the lua end but then
+% we need to do the object offset there too.
+
+\permanent\protected\def\dosetfigureobject#1#2%
+ {\setobject{#1}{#2}\vpack{\box\foundexternalfigure}}
+
+\permanent\protected\def\doboxfigureobject#1#2%
+ {\global\setbox\foundexternalfigure\vpack{\getobject{#1}{#2}}} % probably one vbox too many
+
+% Figure bases
+
+\permanent\protected\def\usefigurebase[#1]%
+ {\clf_usefigurebase{#1}}
+
+\appendtoks
+ \setfigurepathlist % the path may be used elsewhere too (as in x-res-04)
+\to \everysetupexternalfigure
+
+\appendtoks
+ \clf_setfigurelookuporder{\externalfigureparameter\c!order}%
+\to \everysetupexternalfigure
+
+\definecolor[missingfigurecolor][s=.8]
+
+\def\grph_include_replacement#1#2#3%
+ {\bgroup
+ \letexternalfigureparameter\c!width\figurewidth
+ \letexternalfigureparameter\c!height\figureheight
+ \letexternalfigureparameter\c!background\v!color
+ \setexternalfigureparameter\c!backgroundcolor{missingfigurecolor}%
+ \setexternalfigureparameter\c!align{\v!middle,\v!lohi}% we default to \v!none
+ \inheritedexternalfigureframed
+ {\tt\tfxx \nohyphens
+ name: \expanded{\verbatimstring{#1}}\\%
+ file: \expanded{\verbatimstring{#2}}\\%
+ state: \expanded{\verbatimstring{#3}}}%
+ \egroup}
+
+% maybe setuphandler
+
+\newconditional\c_grph_include_in_collection
+
+\newdimen\d_grph_include_collection_minwidth
+\newdimen\d_grph_include_collection_maxwidth
+\newdimen\d_grph_include_collection_minheight
+\newdimen\d_grph_include_collection_maxheight
+
+\def\grph_include_analyze_collection[#1][#2]%
+ {\ifconditional\c_grph_include_in_collection
+ \setfalse\c_grph_include_in_collection
+ \getfiguredimensions[#1][#2]%
+ \settrue\c_grph_include_in_collection
+ \scratchdimen\naturalfigurewidth
+ \ifdim\scratchdimen>\d_grph_include_collection_maxwidth \d_grph_include_collection_maxwidth \scratchdimen \fi
+ \ifdim\scratchdimen<\d_grph_include_collection_minwidth \d_grph_include_collection_minwidth \scratchdimen \fi
+ \scratchdimen\naturalfigureheight
+ \ifdim\scratchdimen>\d_grph_include_collection_maxheight \d_grph_include_collection_maxheight\scratchdimen \fi
+ \ifdim\scratchdimen<\d_grph_include_collection_minheight \d_grph_include_collection_minheight\scratchdimen \fi
+ \fi}
+
+\mutable\let\currentexternalfigurecollection\empty
+
+\permanent\protected\def\startexternalfigurecollection[#1]%
+ {\begingroup
+ \def\currentexternalfigurecollection{#1}%
+ \settrue\c_grph_include_in_collection
+ \d_grph_include_collection_minwidth \maxdimen
+ \d_grph_include_collection_maxwidth \zeropoint
+ \d_grph_include_collection_minheight\maxdimen
+ \d_grph_include_collection_maxheight\zeropoint}
+
+\permanent\protected\def\stopexternalfigurecollection
+ {\xdefcsname\??externalfigurecollection\currentexternalfigurecollection:\c!minwidth \endcsname{\the\d_grph_include_collection_minwidth }%
+ \xdefcsname\??externalfigurecollection\currentexternalfigurecollection:\c!maxwidth \endcsname{\the\d_grph_include_collection_maxwidth }%
+ \xdefcsname\??externalfigurecollection\currentexternalfigurecollection:\c!minheight\endcsname{\the\d_grph_include_collection_minheight}%
+ \xdefcsname\??externalfigurecollection\currentexternalfigurecollection:\c!maxheight\endcsname{\the\d_grph_include_collection_maxheight}%
+ \endgroup}
+
+\permanent\def\externalfigurecollectionparameter#1#2%
+ {\csname
+ \ifcsname\??externalfigurecollection#1:#2\endcsname
+ \??externalfigurecollection#1:#2%
+ \else
+ \s!empty
+ \fi
+ \endcsname}
+
+\permanent\def\externalfigurecollectionminwidth #1{\externalfigurecollectionparameter{#1}\c!minwidth }
+\permanent\def\externalfigurecollectionmaxwidth #1{\externalfigurecollectionparameter{#1}\c!maxwidth }
+\permanent\def\externalfigurecollectionminheight#1{\externalfigurecollectionparameter{#1}\c!minheight}
+\permanent\def\externalfigurecollectionmaxheight#1{\externalfigurecollectionparameter{#1}\c!maxheight}
+
+\aliased\let\efcparameter\externalfigurecollectionparameter % still needed ?
+\aliased\let\efcminwidth \externalfigurecollectionminwidth % still needed ?
+\aliased\let\efcmaxwidth \externalfigurecollectionmaxwidth % still needed ?
+\aliased\let\efcminheight\externalfigurecollectionminheight % still needed ?
+\aliased\let\efcmaxheight\externalfigurecollectionmaxheight % still needed ?
+
+% \startexternalfigurecollection[name]
+% \useexternalfigure[cow] [cow.pdf]
+% \useexternalfigure[mill][mill.png]
+% \stopexternalfigurecollection
+% \starttext
+% \bTABLE
+% \bTR
+% \bTD \externalfigure[cow] [height=\externalfigurecollectionmaxheight{name}] \eTD
+% \bTD \externalfigure[mill][height=\externalfigurecollectionmaxheight{name}] \eTD
+% \eTR
+% \eTABLE
+% \stoptext
+
+\permanent\protected\def\overlayfigure#1%
+ {\externalfigure[#1][\c!width=\d_overlay_width,\c!height=\d_overlay_height]}
+
+% Experimental (will become cleaner):
+
+\permanent\tolerant\protected\def\includesvgfile[#1]#*[#2]%
+ {\dontleavehmode\begingroup
+ \getdummyparameters[\c!offset=\zeropoint,#2]%
+ \clf_includesvgfile{#1}\dimexpr\dummyparameter\c!offset\relax
+ \endgroup}
+
+\permanent\tolerant\protected\def\includesvgbuffer[#1]#*[#2]%
+ {\dontleavehmode\begingroup
+ \getdummyparameters[\c!offset=\zeropoint,#2]%
+ \clf_includesvgbuffer{#1}\dimexpr\dummyparameter\c!offset\relax
+ \endgroup}
+
+% Bonus:
+
+\useexternalfigure
+ [\v!buffer]
+ [\jobname.buffer]
+ [\c!object=\v!no]
+
+% Another two:
+
+\defineexternalfigure
+ [\v!inline]
+ [\c!height=\lineheight]
+
+\definemeasure
+ [\v!combination]
+ [(\textwidth
+ -\effectiveleftskip
+ -\effectiverightskip
+ -\numexpr\combinationparameter\c!nx-\plusone\relax\dimexpr\combinationparameter\c!distance\relax
+ )/\combinationparameter\c!nx]
+
+\defineexternalfigure
+ [\v!combination]
+ [\c!width=\measure{\v!combination}]
+
+% \startcombination[nx=2,ny=1]
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% \stopcombination
+
+% \startcombination[nx=2,ny=1]
+% {\externalfigure[dummy][width=\measure{combination}]} {}
+% {\externalfigure[dummy][width=\measure{combination}]} {}
+% \stopcombination
+
+% \startcombination[nx=2,ny=2]
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% \stopcombination
+
+% \startcombination[nx=3,ny=1]
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% \stopcombination
+
+% \startcombination[nx=4,ny=1]
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% {\externalfigure[dummy][combination]} {}
+% \stopcombination
+
+\permanent\protected\def\inlinefigure[#1]%
+ {\dontleavehmode
+ \sbox{\externalfigure[#1][\v!inline]}}
+
+%D Needs to be done global:
+
+\definelayer[epdfcontent]
+
+\protect \endinput
+
+%D Moved here because this already old code is nowhere documents (so I need to check
+%D it:
+%
+% \starttyping
+% \starttext
+%
+% \startluaparameterset [u3d:myset:controls:1]
+% view = {
+% name = 'default',
+% bg = {1,1,1},
+% mag = 100,
+% coo = {0,0,0},
+% c2c = {0,0,1},
+% rot = {40,0,60},
+% roo = 6,
+% lights = 'CAD'
+% },
+% js = 'cloudq.js'
+% \stopluaparameterset
+%
+% \startluaparameterset [u3d:myset:controls:2]
+% views = {
+% {
+% name = 'AnglePositioning',
+% bg = {1,1,1},
+% azimuth = 45,
+% altitude = 45,
+% roo = 50,
+% aac = 2.5,
+% lights = 'Artwork'
+% },
+% {
+% name = 'RotationPositioning',
+% bg = {1,1,1},
+% rot = {0,45,45},
+% roo = 50,
+% aac = 2.5,
+% lights = 'Artwork'
+% },
+% {
+% name = 'VectorPositioning',
+% bg = {1,0,0},
+% c2c = {1,1,math.sqrt(2)},
+% roo = 50,
+% aac = 2.5,
+% lights = 'CAD'
+% },
+% {
+% name = 'PositionPositioning',
+% bg = {1,0,0},
+% pos = {1+25,1+25,1+50/math.sqrt(2)},
+% aac = 2.5,
+% lights = 'CAD'
+% },
+% {
+% name = 'ortho',
+% bg = {1,1,1},
+% mag = 300,
+% lights = 'CAD',
+% crossection = {}
+% }
+% },
+% view = {
+% name = 'default',
+% bg = {1,1,1},
+% c2c = {-1,-1,0},
+% roo = 50,
+% aac = 2.5,
+% roll = 45,
+% lights = 'CAD',
+% crossection = {
+% normal = {-1,-1,-1},
+% transparent = true
+% },
+% nodes = {
+% {
+% name = 'xlabel',
+% visible = false
+% },
+% {
+% name = 'ylabel',
+% opacity = 0.5
+% },
+% {
+% name = 'zlabel',
+% rendermode = 'Wireframe'
+% }
+% }
+% }
+% \stopluaparameterset
+%
+% \useexternalfigure
+% [cloudq]
+% [cloudq.u3d]
+% [width=0.7\textwidth,
+% height=.7\textwidth,
+% display=u3d:myset:display:1,
+% controls=u3d:myset:controls:1]
+%
+% \useexternalfigure
+% [axes]
+% [axes.u3d]
+% [width=0.7\textwidth,
+% height=.7\textwidth,
+% controls=u3d:myset:controls:1]
+%
+% \startluaparameterset[u3d:myset:display:2]
+% toolbar = true,
+% preview = 'cloudq.png'
+% \stopluaparameterset
+% \startluaparameterset[u3d:myset:display:3]
+% toolbar = true,
+% tree = false,
+% preview = 'axes.png'
+% \stopluaparameterset
+% \startluaparameterset[u3d:myset:display:4]
+% toolbar = true,
+% tree = false,
+% view = {
+% name = 'view',
+% bg = {0.1,0.1,0.1},
+% c2c = {-1,-1,0},
+% roo = 50,
+% aac = 2.5,
+% roll = 45,
+% lights = 'Red'
+% }
+% \stopluaparameterset
+% \startluaparameterset[u3d:myset:display:5]
+% toolbar = true,
+% tree = false,
+% view = 'ortho'
+% \stopluaparameterset
+%
+% \placefigure[here]{none}{\externalfigure[cloudq][frame=on,display=u3d:myset:display:2]}
+% \placefigure[here]{none}{\externalfigure[axes] [frame=on,display=u3d:myset:display:3]}
+% \placefigure[here]{none}{\externalfigure[axes] [frame=on,display=u3d:myset:display:4]}
+% \placefigure[here]{none}{\externalfigure[axes] [frame=on,display=u3d:myset:display:5,width=0.5\textwidth,height=.5\textwidth]}
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-pat.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-pat.mkxl
new file mode 100644
index 00000000000..92fd5ad749a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-pat.mkxl
@@ -0,0 +1,119 @@
+%D \module
+%D [ file=grph-par,
+%D version=2016.07.08,
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Patterns,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This works ok in Okular and MuPDF but somehow xforms don't work in Acrobat
+%D (full nor reader). Also the basic offset is kind of unspecified. So \unknown\
+%D we're dealing with a fragile feature. So, don't rely on where the first (ulr)
+%D tile occurs.
+%D
+%D The two commands introduced here are not documented (yet).
+
+\writestatus{loading}{ConTeXt Graphic Macros / Patterns}
+
+\unprotect
+
+\registerctxluafile{grph-pat}{}
+
+\permanent\tolerant\protected\def\registerpattern[#1]#*[#2]%
+ {\begingroup
+ \letdummyparameter\c!name \s!dummy
+ \letdummyparameter\c!width \v!auto
+ \letdummyparameter\c!height \v!auto
+ \letdummyparameter\c!hoffset\zeropoint
+ \letdummyparameter\c!voffset\zeropoint
+ \unless\iftok{#2}\emptytoks
+ \setdummyparameter\c!name{#1}%
+ \getdummyparameters[#2]%
+ \orelse\ifhastok={#1}%
+ \getdummyparameters[#1]%
+ \else
+ \setdummyparameter\c!name{#1}%
+ \fi
+ \dowithnextboxcs\syst_boxes_registerpattern_indeed\hbox}
+
+\immutable\edef\v!auto_m{-\v!auto}
+
+\def\syst_boxes_registerpattern_indeed
+ {%\finalizeobjectbox\nextbox
+ \edef\p_width {\dummyparameter\c!width}%
+ \edef\p_height {\dummyparameter\c!height}%
+ \edef\p_hoffset{\dummyparameter\c!hoffset}%
+ \edef\p_voffset{\dummyparameter\c!voffset}%
+ \scratchwidth \dimexpr\ifx\p_width \v!auto\wd \nextbox\else\p_width \fi\relax
+ \scratchheight \dimexpr\ifx\p_height \v!auto\htdp\nextbox\else\p_height \fi\relax
+ \scratchhoffset\dimexpr\ifx\p_hoffset\v!auto\scratchwidth /2\orelse\ifx\p_hoffset\v!auto_m-\scratchwidth /2\else\p_hoffset\fi\relax
+ \scratchvoffset\dimexpr\ifx\p_voffset\v!auto\scratchheight/2\orelse\ifx\p_voffset\v!auto_m-\scratchheight/2\else\p_voffset\fi\relax
+ \clf_registerpattern
+ name {\dummyparameter\c!name}
+ number \nextbox
+ width \scratchwidth
+ height \scratchheight
+ hoffset \scratchhoffset
+ voffset \scratchvoffset
+ \relax
+ \endgroup}
+
+\permanent\tolerant\protected\def\applypattern[#1]#*[#2]%
+ {\hbox\bgroup
+ \letdummyparameter\c!name \s!dummy
+ \letdummyparameter\c!width \zeropoint
+ \letdummyparameter\c!height\zeropoint
+ \unless\iftok{#2}\emptytoks
+ \setdummyparameter\c!name{#1}%
+ \getdummyparameters[#2]%
+ \orelse\ifhastok={#1}%
+ \getdummyparameters[#1]%
+ \else
+ \setdummyparameter\c!name{#1}%
+ \fi
+ \clf_applypattern
+ name {\dummyparameter\c!name}
+ number \nextbox
+ width \dimexpr\dummyparameter\c!width\relax
+ height \dimexpr\dummyparameter\c!height\relax
+ \relax
+ \box\nextbox
+ \egroup}
+
+\protect
+
+\continueifinputfile{grph-pat.mkxl}
+
+\nopdfcompression
+
+\starttext
+
+ \registerpattern[demo]{It \darkred Works!}
+
+ \framed[offset=overlay]{\applypattern[demo][width=7cm,height=4cm]}
+
+ \blank
+
+ \registerpattern[name=more,hoffset=0bp,voffset=0pt]{\externalfigure[cow.pdf][width=1cm]}
+
+ \framed[offset=overlay]{\applypattern[name=more,width=7cm,height=4cm]}
+
+ \blank
+
+ \registerpattern[name=more,hoffset=auto,voffset=auto]{\externalfigure[cow.pdf][width=1cm]}
+
+ \framed[offset=overlay]{\applypattern[name=more,width=7cm,height=4cm]}
+
+ \blank
+
+ \registerpattern[name=more,hoffset=-auto,voffset=-auto]{\externalfigure[cow.pdf][width=1cm]}
+
+ \framed[offset=overlay]{\applypattern[name=more,width=7cm,height=4cm]}
+
+\stoptext
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-raw.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-raw.mkxl
new file mode 100644
index 00000000000..66194551bfc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-raw.mkxl
@@ -0,0 +1,64 @@
+%D \module
+%D [ file=grph-raw,
+%D version=2006.08.26, % overhaul of 1997.03.31
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Raw Bitmaps,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Graphic Macros / Raw Bitmaps}
+
+%D \startluacode
+%D function document.TestBitmap(nx,ny)
+%D local random = math.random
+%D local maxbit = 2^24
+%D for i=1,nx do
+%D for i=1,ny do
+%D context("%06x",random(0,maxbit))
+%D end
+%D end
+%D end
+%D \stopluacode
+%D
+%D \def\TestBitmap#1#2{\ctxlua{document.TestBitmap(#1,#2)}}
+%D
+%D \blank
+%D
+%D \startMPcode
+%D draw textext("\bitmapimage[x=100,y=100]{\TestBitmap{100}{100}}") xsized 10cm ;
+%D \stopMPcode
+%D
+%D \blank
+%D
+%D \startMPcode
+%D draw textext("\bitmapimage[x=200,y=50]{\TestBitmap{50}{200}}") xsized 10cm ;
+%D \stopMPcode
+
+\registerctxluafile{grph-raw}{}
+
+\unprotect
+
+\permanent\protected\def\bitmapimage[#1]#2%
+ {\hbox\bgroup
+ \getdummyparameters[\c!color=rgb,\c!width=,\c!height=,\c!x=,\c!y=,#1]%
+ \clf_bitmapimage
+ data {#2}%
+ colorspace {\directdummyparameter\c!color}%
+ width {\directdummyparameter\c!width}%
+ height {\directdummyparameter\c!height}%
+ xresolution {\directdummyparameter\c!x}%
+ yresolution {\directdummyparameter\c!y}%
+ \relax
+ \egroup}
+
+\permanent\protected\def\startbitmapimage[#1]#2\stopbitmapimage
+ {\bitmapimage[#1]{#2}}
+
+\aliased\let\stopbitmapimage\relax
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-rul.lmt b/Master/texmf-dist/tex/context/base/mkxl/grph-rul.lmt
new file mode 100644
index 00000000000..d8da5f76054
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-rul.lmt
@@ -0,0 +1,124 @@
+if not modules then modules = { } end modules ['grph-rul'] = {
+ version = 1.001,
+ comment = "companion to grph-rul.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local attributes = attributes
+local nodes = nodes
+local context = context
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+local userrule = nuts.rules.userrule
+local outlinerule = nuts.pool.outlinerule
+local ruleactions = nuts.rules.ruleactions
+
+local setattrlist = nuts.setattrlist
+local setattr = nuts.setattr
+local tonode = nuts.tonode
+
+local getattribute = tex.getattribute
+
+local a_color = attributes.private('color')
+local a_transparency = attributes.private('transparency')
+local a_colormodel = attributes.private('colormodel')
+
+local floor = math.floor
+local getrandom = utilities.randomizer.get
+
+do
+
+ local function unsupported() end
+
+ ruleactions.mp = unsupported
+ ruleactions.fill = unsupported
+ ruleactions.draw = unsupported
+ ruleactions.stroke = unsupported
+ ruleactions.box = unsupported
+
+end
+
+interfaces.implement {
+ name = "frule",
+ arguments = { {
+ { "width", "dimension" },
+ { "height", "dimension" },
+ { "depth", "dimension" },
+ { "radius", "dimension" },
+ { "line", "dimension" },
+ { "type", "string" },
+ { "data", "string" },
+ { "name", "string" },
+ { "radius", "dimension" },
+ { "corner", "string" },
+ } } ,
+ actions = function(t)
+ local rule = userrule(t)
+ if t.type == "mp" then
+ t.ma = getattribute(a_colormodel) or 1
+ t.ca = getattribute(a_color)
+ t.ta = getattribute(a_transparency)
+ else
+ setattrlist(rule,true)
+ end
+ context(tonode(rule)) -- will become context.nodes.flush
+ end
+}
+
+interfaces.implement {
+ name = "outlinerule",
+ public = true,
+ protected = true,
+ arguments = { {
+ { "width", "dimension" },
+ { "height", "dimension" },
+ { "depth", "dimension" },
+ { "line", "dimension" },
+ } } ,
+ actions = function(t)
+ local rule = outlinerule(t.width,t.height,t.depth,t.line)
+ setattrlist(rule,true)
+ context(tonode(rule)) -- will become context.nodes.flush
+ end
+}
+
+interfaces.implement {
+ name = "framedoutline",
+ arguments = { "dimension", "dimension", "dimension", "dimension" },
+ actions = function(w,h,d,l)
+ local rule = outlinerule(w,h,d,l)
+ setattrlist(rule,true)
+ context(tonode(rule)) -- will become context.nodes.flush
+ end
+}
+
+interfaces.implement {
+ name = "fakeword",
+ arguments = { {
+ { "factor", "dimension" },
+ { "name", "string" }, -- can be type
+ { "min", "dimension" },
+ { "max", "dimension" },
+ { "n", "integer" },
+ } } ,
+ actions = function(t)
+ local factor = t.factor or 0
+ local amount = getrandom("fakeword",t.min,t.max)
+ local rule = userrule {
+ height = 1.25*factor,
+ depth = 0.25*factor,
+ width = floor(amount/10000) * 10000,
+ line = 0.10*factor,
+ ma = getattribute(a_colormodel) or 1,
+ ca = getattribute(a_color),
+ ta = getattribute(a_transparency),
+ type = "mp",
+ name = t.name,
+ }
+ setattrlist(rule,true)
+ context(tonode(rule))
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-rul.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-rul.mkxl
new file mode 100644
index 00000000000..034e91734e3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-rul.mkxl
@@ -0,0 +1,89 @@
+%D \module
+%D [ file=grph-rul,
+%D version=2016.02.05, % from experiments
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Rule Trickery,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Graphic Macros / Rule Trickery}
+
+\registerctxluafile{grph-rul}{autosuffix}
+
+\unprotect
+
+\permanent\protected\def\frule{\relax\clf_frule} % will become a public implementer
+
+% just for fun:
+
+% \defineoverlay[normalframe]
+% [\frule
+% width \overlaywidth
+% height\overlayheight
+% line \overlaylinewidth
+% \relax]
+
+% \defineoverlay[ovalframe]
+% [\frule
+% width \overlaywidth
+% height \overlayheight
+% line \overlaylinewidth
+% radius \overlayradius
+% \relax]
+
+\protect \endinput
+
+% \starttext
+%
+% \testfeatureonce{25}{\dontleavehmode
+% \ruledhbox\bgroup
+% \red \frule width 2cm height 2cm depth 1cm radius 2mm line 2pt type fill\relax
+% \hskip-2cm
+% \green \frule width 2cm height 2cm depth 1cm radius 2mm line 2pt\relax
+% \egroup
+% \space}
+%
+% \blank
+%
+% \startuseMPgraphic{demoshape:back}
+% fill
+% unitcircle xysized (RuleWidth,RuleHeight+RuleDepth)
+% withcolor RuleColor ;
+% \stopuseMPgraphic
+%
+% \startuseMPgraphic{demoshape:fore}
+% draw
+% unitcircle xysized (RuleWidth,RuleHeight+RuleDepth)
+% withcolor RuleColor
+% withpen pencircle scaled RuleThickness ;
+% \stopuseMPgraphic
+%
+% \testfeatureonce{100}{\dontleavehmode
+% \ruledhbox\bgroup
+% \red \frule width 1cm height 3mm depth 1mm type mp line 2pt data {\includeMPgraphic{demoshape:back}}\relax
+% \hskip-1cm
+% \green \frule width 1cm height 3mm depth 1mm type mp line 2pt data {\includeMPgraphic{demoshape:fore}}\relax
+% \egroup
+% \space}
+%
+% \blank
+%
+% \dontleavehmode
+% \testfeatureonce{1}{\setbox\scratchbox\hbox{\framed {test}}}
+% \framed {test}
+% \testfeatureonce{1}{\setbox\scratchbox\hbox{\framed[background=normalframe,frame=off]{test}}}
+% \framed[background=normalframe,frame=off]{test}
+% \testfeatureonce{1}{\setbox\scratchbox\hbox{\framed[corner=round] {test}}}
+% \framed[corner=round] {test}
+% \testfeatureonce{1}{\setbox\scratchbox\hbox{\framed[background=ovalframe,frame=off] {test}}}
+% \framed[background=ovalframe,frame=off] {test}
+% \testfeatureonce{1}{\setbox\scratchbox\hbox{\framed[background=ovalframe,frame=on] {test}}}
+% \framed[background=ovalframe,frame=on] {test}
+%
+% \stoptext
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-trf.lmt b/Master/texmf-dist/tex/context/base/mkxl/grph-trf.lmt
new file mode 100644
index 00000000000..f68d6a6a807
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-trf.lmt
@@ -0,0 +1,126 @@
+if not modules then modules = { } end modules ['grph-trf'] = {
+ version = 1.001,
+ comment = "companion to grph-trf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- see grph-trf-todo.lua for older preliminary code for the rest
+
+local sind, cosd, tand, abs = math.sind, math.cosd, math.tand, math.abs
+
+local setdimension = tex.setdimensionvalue
+
+local function analyzerotate(rotation,width,height,depth,total,notfit,obeydepth)
+ --
+ -- print(rotation,width,height,depth,notfit,obeydepth)
+ --
+ local sin = sind(rotation)
+ local cos = cosd(rotation)
+ local abssin = abs(sin)
+ local abscos = abs(cos)
+ local xsize = 0
+ local ysize = 0
+ local xposition = 0
+ local yposition = 0
+ local xoffset = 0
+ local yoffset = 0
+ local newwidth = width
+ local newheight = height
+ local newdepth = depth
+ -- print(sin,cos)
+ if sin > 0 then
+ if cos > 0 then
+ xsize = cos * width + sin * total
+ ysize = sin * width + cos * total
+ yposition = cos * total
+ if notfit then
+ xoffset = - sin * height
+ newwidth = sin * depth + cos * width
+ else
+ newwidth = xsize + xposition - xoffset
+ end
+ if obeydepth then
+ yoffset = cos * depth
+ end
+ newheight = ysize - yoffset
+ newdepth = yoffset
+ -- print("case 1, done")
+ else
+ xsize = abscos * width + sin * total
+ ysize = sin * width + abscos * total
+ xposition = abscos * width
+ if notfit then
+ xoffset = - xsize + sin * depth
+ end
+ if obeydepth then
+ yoffset = abscos * height
+ newwidth = abssin * total + abscos * width + xoffset
+ else
+ newwidth = xsize
+ end
+ newheight = ysize - yoffset
+ newdepth = yoffset
+ -- print("case 2, done")
+ end
+ else
+ if cos < 0 then
+ xsize = abscos * width + abssin * total
+ ysize = abssin * width + abscos * total
+ xposition = xsize
+ yposition = abssin * width
+ if notfit then
+ xoffset = - xsize + abssin * height
+ end
+ if obeydepth then
+ yoffset = ysize + cos * depth
+ end
+ newwidth = notfit and (abssin * height) or xsize
+ newheight = ysize - yoffset
+ newdepth = yoffset
+ -- print("case 3, done")
+ else
+ xsize = cos * width + abssin * total
+ ysize = abssin * width + cos * total
+ xposition = abssin * total
+ yposition = cos * total
+ if notfit then
+ xoffset = - abssin * depth
+ newwidth = xsize + xoffset
+ else
+ newwidth = xsize
+ end
+ if obeydepth then
+ yoffset = cos * depth
+ end
+ newheight = ysize - yoffset + sin * width
+ newdepth = yoffset - sin * width
+ -- print("case 4, done")
+ end
+ end
+ setdimension("d_grph_rotate_x_size", xsize)
+ setdimension("d_grph_rotate_y_size", ysize)
+ setdimension("d_grph_rotate_x_position", xposition)
+ setdimension("d_grph_rotate_y_position", yposition)
+ setdimension("d_grph_rotate_x_offset", xoffset)
+ setdimension("d_grph_rotate_y_offset", yoffset)
+ setdimension("d_grph_rotate_new_width", newwidth)
+ setdimension("d_grph_rotate_new_height", newheight)
+ setdimension("d_grph_rotate_new_depth", newdepth)
+end
+
+interfaces.implement {
+ name = "analyzerotate",
+ actions = analyzerotate,
+ arguments = {
+-- "integer",
+ "number",
+ "dimension",
+ "dimension",
+ "dimension",
+ "dimension",
+ "conditional",
+ "conditional",
+ },
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/grph-trf.mkxl b/Master/texmf-dist/tex/context/base/mkxl/grph-trf.mkxl
new file mode 100644
index 00000000000..c7e0f5e2f21
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/grph-trf.mkxl
@@ -0,0 +1,1003 @@
+%D \module
+%D [ file=grph-trf,
+%D version=2006.08.26, % overhaul/split of 1997.03.31 core-fig
+%D title=\CONTEXT\ Graphic Macros,
+%D subtitle=Transformations,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Graphic Macros / Transformations}
+
+\unprotect
+
+%D We probably use too many dimens as the width calculations can go away. Some of
+%D this is an inheritance of limited backends (some supported fractions, some
+%D 1000's, some dimentions) so we calculate all of them. Nowadays scaling is always
+%D available so we could simplify the code. On the other hand, we now get some extra
+%D values for free.
+%D
+%D We could move the calculations to \LUA\ and clean up this lot anyway. On the
+%D other hand, there is some danger of messing up so it has a real low priority.
+
+\registerctxluafile{grph-trf}{autosuffix}
+
+% local:
+
+\newdimen\d_grph_scale_x_size
+\newdimen\d_grph_scale_y_size
+\newdimen\d_grph_scale_x_offset
+\newdimen\d_grph_scale_y_offset
+
+\newdimen\d_grph_scale_h_size
+\newdimen\d_grph_scale_v_size
+
+\newconditional\c_grph_scale_done
+\newconditional\c_grph_scale_scaling_done
+\newconditional\c_grph_scale_limit_factors \settrue\c_grph_scale_limit_factors
+
+\newconditional\c_grph_scale_swap_factor
+
+\newdimen\d_grph_scale_wd
+\newdimen\d_grph_scale_ht
+\newdimen\d_grph_scale_dp
+
+% global
+
+\newdimen\d_grph_scale_used_x_size
+\newdimen\d_grph_scale_used_y_size
+
+\newcount\c_grph_scale_used_x_scale
+\newcount\c_grph_scale_used_y_scale
+
+\let \m_grph_scale_used_x_scale\!!plusone
+\let \m_grph_scale_used_y_scale\!!plusone
+
+\newdimen\d_grph_scale_outer_v_size % we cannot manipulate any global vsize !
+
+% scratch:
+
+\let\m_grph_scale_temp \empty
+\let\m_grph_scale_temp_x\empty
+\let\m_grph_scale_temp_y\empty
+
+% public:
+
+\mutable\let\finalscaleboxxscale \!!plusone
+\mutable\let\finalscaleboxyscale \!!plusone
+\mutable\let\finalscaleboxwidth \!!zeropoint
+\mutable\let\finalscaleboxheight \!!zeropoint
+
+% we can let sx/sy win (first check)
+
+\installcorenamespace{scale}
+\installcorenamespace{scalegrid}
+\installcorenamespace{scalenorm}
+\installcorenamespace{scalefact}
+
+\installcommandhandler \??scale {scale} \??scale % we can have instances
+
+\setupscale
+ [\c!sx=\scaleparameter\c!s,
+ \c!sy=\scaleparameter\c!s,
+ \c!s=1,
+ %\c!scale=,
+ %\c!xscale=,
+ %\c!yscale=,
+ %\c!width=,
+ %\c!height=,
+ %\c!lines=,
+ %\c!factor=,
+ %\c!hfactor=,
+ %\c!wfactor=,
+ %\c!grid=,
+ %\c!equalwidth=,
+ %\c!equalheight=,
+ \c!maxwidth=\scaleparameter\c!width,
+ \c!maxheight=\scaleparameter\c!height]
+
+\permanent\tolerant\protected\def\scale[#1]#*[#2]%
+ {\bgroup
+ % this is quite common so we might make this a helper
+ \ifarguments
+ \let\currentscale\empty
+ \or
+ \ifhastok={#1}%
+ \let\currentscale\empty
+ \setupcurrentscale[#1]%
+ \else
+ \edef\currentscale{#1}%
+ \fi
+ \or
+ \edef\currentscale{#1}%
+ \setupcurrentscale[#2]%
+ \fi
+ %
+ \dowithnextboxcs\grph_scale_finish\hbox}
+
+\def\grph_scale_finish
+ {% todo: p_scale_
+ \edef\p_scale {\scaleparameter\c!scale }%
+ \edef\p_xscale {\scaleparameter\c!xscale }%
+ \edef\p_yscale {\scaleparameter\c!yscale }%
+ \edef\p_width {\scaleparameter\c!width }%
+ \edef\p_height {\scaleparameter\c!height }%
+ \edef\p_depth {\scaleparameter\c!depth }%
+ \edef\p_lines {\scaleparameter\c!lines }%
+ \edef\p_factor {\scaleparameter\c!factor }%
+ \edef\p_hfactor {\scaleparameter\c!hfactor }%
+ \edef\p_wfactor {\scaleparameter\c!wfactor }%
+ % \edef\p_grid {\scaleparameter\c!grid }% used once
+ \edef\p_maxwidth {\scaleparameter\c!maxwidth }%
+ \edef\p_maxheight {\scaleparameter\c!maxheight }%
+ \edef\p_sx {\scaleparameter\c!sx }%
+ \edef\p_sy {\scaleparameter\c!sy }%
+ \edef\p_equalwidth {\scaleparameter\c!equalwidth }%
+ \edef\p_equalheight{\scaleparameter\c!equalheight}%
+ %
+ \d_grph_scale_dp\dp\nextbox
+ \ifx\p_depth\v!no \ifzeropt\d_grph_scale_dp \else
+ \setbox\nextbox\naturalhpack{\raise\d_grph_scale_dp\box\nextbox}% new
+ \d_grph_scale_dp\dp\nextbox
+ \fi \fi
+ \d_grph_scale_wd\wd\nextbox
+ \d_grph_scale_ht\ht\nextbox
+ \d_grph_scale_dp\dp\nextbox
+ %
+ \glet\finalscaleboxxscale \!!plusone
+ \glet\finalscaleboxyscale \!!plusone
+ \xdef\finalscaleboxwidth {\the\d_grph_scale_wd}%
+ \xdef\finalscaleboxheight{\the\d_grph_scale_ht}%
+ %
+ \forgetall
+ \dontcomplain
+ %
+ \setfalse\c_grph_scale_done
+ \grph_scale_calculate
+ \ifconditional\c_grph_scale_done
+ \grph_scale_apply
+ \fi
+ \grph_scale_position
+ %
+ \box\nextbox
+ \egroup}
+
+\def\grph_scale_apply
+ {\d_grph_scale_wd\finalscaleboxxscale\d_grph_scale_wd
+ \d_grph_scale_ht\finalscaleboxyscale\d_grph_scale_ht
+ \d_grph_scale_dp\finalscaleboxyscale\d_grph_scale_dp
+ \ifdim\d_grph_scale_wd=\wd\nextbox
+ \ifdim\d_grph_scale_ht=\ht\nextbox
+ \ifdim\d_grph_scale_dp=\dp\nextbox
+ % \grph_scale_apply_nop
+ \else
+ \grph_scale_apply_yes
+ \fi
+ \else
+ \grph_scale_apply_yes
+ \fi
+ \else
+ \grph_scale_apply_yes
+ \fi}
+
+\def\grph_scale_apply_yes
+ {\setbox\nextbox\hcontainer
+ {\dostartscaling \finalscaleboxxscale \finalscaleboxyscale
+ \smashedbox\nextbox
+ \dostopscaling}%
+ \wd\nextbox\d_grph_scale_wd
+ \ht\nextbox\d_grph_scale_ht
+ \dp\nextbox\d_grph_scale_dp}
+
+\def\m_grph_scale_stamp_c{11}
+
+\def\grph_scale_calculate
+ {\ifdim\d_grph_scale_ht>\zeropoint \ifdim\d_grph_scale_wd>\zeropoint
+ \edef\m_grph_scale_stamp_a{\p_scale\p_xscale\p_yscale\p_factor\p_wfactor\p_hfactor\p_lines\p_width\p_height}%
+ \edef\m_grph_scale_stamp_b{\p_sx\p_sy}%
+ \ifempty\m_grph_scale_stamp_a
+ \ifx\m_grph_scale_stamp_b\m_grph_scale_stamp_c
+ % no scaling, don't change this (previous attempts failed anyway)
+ \insidefloattrue % trick
+ \grph_scale_calculations_yes
+ \else
+ \grph_scale_check_sx_sy
+ \grph_scale_calculations_nop
+ \fi
+ \else
+ \ifempty\m_grph_scale_stamp_b
+ % no need to check further
+ \else
+ \grph_scale_check_sx_sy
+ \fi
+ \grph_scale_calculations_yes
+ \fi
+ \fi \fi}
+
+\def\grph_scale_check_sx_sy
+ {\ifdim\p_sx\onepoint=\onepoint\else\edef\p_width {\the\dimexpr\p_sx\d_grph_scale_wd}\fi
+ \ifdim\p_sy\onepoint=\onepoint\else\edef\p_height{\the\dimexpr\p_sy\d_grph_scale_ht}\fi}
+
+\def\grph_scale_rounded#1%
+ {\thewithoutunit\dimexpr#1\points*100+32768\scaledpoint\relax}
+
+\def\grph_scale_calculations_nop
+ {\settrue\c_grph_scale_done
+ \xdef\finalscaleboxwidth {\the\dimexpr\p_sx\d_grph_scale_wd\relax}%
+ \xdef\finalscaleboxheight{\the\dimexpr\p_sy\d_grph_scale_ht\relax}%
+ \glet\finalscaleboxxscale\p_sx
+ \glet\finalscaleboxyscale\p_sy
+ \ifempty\finalscaleboxxscale\let\finalscaleboxxscale\!!plusone\fi
+ \ifempty\finalscaleboxyscale\let\finalscaleboxyscale\!!plusone\fi}
+
+\let\grph_scale_calculations_report\relax
+
+\def\grph_scale_calculations_yes
+ {\settrue\c_grph_scale_done
+ % initial values
+ \d_grph_scale_x_offset\zeropoint
+ \d_grph_scale_y_offset\zeropoint
+ \d_grph_scale_x_size \d_grph_scale_wd
+ \d_grph_scale_y_size \d_grph_scale_ht % alleen ht wordt geschaald!
+ % final values
+ \global\d_grph_scale_used_x_size \zeropoint % see note * (core-fig)
+ \global\d_grph_scale_used_y_size \zeropoint % see note * (core-fig)
+ \c_grph_scale_used_x_scale \plusone % see note * (core-fig)
+ \c_grph_scale_used_y_scale \plusone % see note * (core-fig)
+ \let\m_grph_scale_used_x_scale \!!plusone
+ \let\m_grph_scale_used_y_scale \!!plusone
+ % preparations
+ \setfalse\c_grph_scale_scaling_done
+ \grph_scale_check_parameters
+ % calculators
+ % beware, they operate in sequence, and calculate missing dimensions / messy
+ % grph_scale_by_nature % when? needed?
+ \ifconditional\c_grph_scale_scaling_done\else\grph_scale_by_factor \fi
+ \ifconditional\c_grph_scale_scaling_done\else\grph_scale_by_scale \fi
+ \ifconditional\c_grph_scale_scaling_done\else\grph_scale_by_dimension\fi
+ % used in actual scaling
+ \xdef\finalscaleboxwidth {\the\d_grph_scale_used_x_size}%
+ \xdef\finalscaleboxheight {\the\d_grph_scale_used_y_size}%
+ \glet\finalscaleboxxscale \m_grph_scale_used_x_scale
+ \glet\finalscaleboxyscale \m_grph_scale_used_y_scale
+ \grph_scale_calculations_report}
+
+\defcsname\??scalegrid\v!yes \endcsname{\getnoflines \d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
+\defcsname\??scalegrid\v!height \endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+\strutdepth}}
+\defcsname\??scalegrid\v!depth \endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight-\strutdepth}}
+\defcsname\??scalegrid\v!halfline\endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+.5\lineheight}}
+\defcsname\??scalegrid\v!fit \endcsname{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
+\letcsname\??scalegrid\empty \endcsname\donothing
+
+\def\grph_scale_check_parameters % resolve self referencing loops
+ {\ifempty\p_maxwidth \else \edef\p_maxwidth {\the\dimexpr\p_maxwidth }\fi
+ \ifempty\p_maxheight\else \edef\p_maxheight{\the\dimexpr\p_maxheight }\fi
+ \ifempty\p_lines \else \edef\p_height {\the\dimexpr\p_lines\lineheight}\fi
+ \begincsname\??scalegrid\scaleparameter\c!grid\endcsname}
+
+\def\grph_scale_by_nature % where ! ! ! ! !
+ {\ifempty\p_width \else \global\d_grph_scale_used_x_size\p_width \fi
+ \ifempty\p_height\else \global\d_grph_scale_used_y_size\p_height\fi
+ \ifempty\p_scale \else \c_grph_scale_used_x_scale\p_scale
+ \c_grph_scale_used_y_scale\p_scale \fi
+ \ifempty\p_xscale\else \c_grph_scale_used_x_scale\p_xscale\fi
+ \ifempty\p_yscale\else \c_grph_scale_used_y_scale\p_yscale\fi}
+
+% \defineexternalfigure[width-6][factor=auto,maxwidth=\textheight,maxheight=\textwidth]
+% \defineexternalfigure[width-7][factor=auto,maxwidth=\textwidth,maxheight=\textheight]
+% \placefigure{none}{\rotate[frame=on,offset=overlay]{\externalfigure[t:/sources/cow.pdf][width-6]}} \page
+% \placefigure{none}{\framed[frame=on,offset=overlay]{\externalfigure[t:/sources/cow.pdf][width-7]}}
+
+%D The \typpe {min} option makes sure that the smallest available space determines
+%D the max size (so we can get a bleed on the other axis):
+%D
+%D \startlinecorrection
+%D \startcombination[nx=2,ny=2,distance=4cm]
+%D {\externalfigure[hacker][factor=max,maxwidth=6cm,maxheight=6cm]} {}
+%D {\externalfigure[mill] [factor=max,maxwidth=6cm,maxheight=6cm]} {}
+%D {\externalfigure[hacker][factor=min,maxwidth=6cm,maxheight=6cm]} {}
+%D {\externalfigure[mill] [factor=min,maxwidth=6cm,maxheight=6cm]} {}
+%D \stopcombination
+%D \stoplinecorrection
+
+\def\m_grph_scale_factor_set{\v!min,\v!max,\v!fit,\v!broad,\v!auto} % can be an \edef
+
+\def\grph_scale_by_factor
+ {\doifelseinset\p_factor\m_grph_scale_factor_set
+ \grph_scale_by_factor_a
+ {\doifelseinset\p_hfactor\m_grph_scale_factor_set
+ \grph_scale_by_factor_b
+ {\doifelseinset\p_wfactor\m_grph_scale_factor_set
+ \grph_scale_by_factor_c
+ \grph_scale_by_factor_d}}}
+
+\def\grph_scale_by_factor_a
+ {\grph_scale_apply_size
+ \ifdim\d_grph_scale_x_size >\d_grph_scale_y_size
+ \grph_scale_calculate_norm \d_grph_scale_used_x_size\p_factor\p_maxwidth\hsize\d_grph_scale_h_size
+ \grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
+ \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size
+ \else
+ \grph_scale_calculate_norm \d_grph_scale_used_y_size\p_factor\p_maxheight\d_grph_scale_outer_v_size\d_grph_scale_v_size
+ \grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size
+ \fi
+ \grph_scale_by_factor_indeed}
+
+\def\grph_scale_by_factor_b
+ {\grph_scale_apply_size
+ \grph_scale_calculate_norm \d_grph_scale_used_y_size\p_hfactor\p_maxheight\d_grph_scale_outer_v_size\d_grph_scale_v_size
+ \grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size
+ \grph_scale_by_factor_indeed}
+
+\def\grph_scale_by_factor_c
+ {\grph_scale_apply_size
+ \grph_scale_calculate_norm \d_grph_scale_used_x_size\p_wfactor\p_maxwidth\hsize\d_grph_scale_h_size
+ \grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
+ \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size
+ \grph_scale_by_factor_indeed}
+
+\def\grph_scale_by_factor_d
+ {\grph_scale_calculate_norm\d_grph_scale_used_y_size\p_factor \p_height \textheight\d_grph_scale_v_size
+ \grph_scale_calculate_norm\d_grph_scale_used_y_size\p_hfactor\p_height \textheight\d_grph_scale_v_size
+ \grph_scale_calculate_norm\d_grph_scale_used_x_size\p_wfactor\p_width \hsize \hsize}
+
+\def\grph_scale_by_factor_indeed
+ {\grph_scale_calculate_fact\p_factor
+ \settrue\c_grph_scale_scaling_done
+ \ifconditional\c_grph_scale_limit_factors
+ \ifdim\d_grph_scale_used_x_size\ifconditional\c_grph_scale_swap_factor<\else>\fi\d_grph_scale_h_size
+ \global\d_grph_scale_used_y_size\zeropoint
+ \global\d_grph_scale_used_x_size\d_grph_scale_h_size
+ \orelse\ifdim\d_grph_scale_used_y_size\ifconditional\c_grph_scale_swap_factor<\else>\fi\d_grph_scale_v_size
+ \global\d_grph_scale_used_x_size\zeropoint
+ \global\d_grph_scale_used_y_size\d_grph_scale_v_size
+ \fi
+ \fi
+ \grph_scale_by_dimension}
+
+\def\grph_scale_by_scale
+ {\edef\m_grph_scale_temp{\p_scale\p_xscale\p_yscale}%
+ \ifempty\m_grph_scale_temp \else
+ \grph_scale_apply_scale\m_grph_scale_used_x_scale\p_xscale
+ \grph_scale_apply_scale\m_grph_scale_used_y_scale\p_yscale
+ % \global\d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size\relax % no global needed here
+ % \global\d_grph_scale_used_y_size\m_grph_scale_used_y_scale\d_grph_scale_y_size\relax % no global needed here
+ % wrong: we need to recalculate the scale
+ \global\d_grph_scale_used_x_size\zeropoint
+ \global\d_grph_scale_used_y_size\zeropoint
+ %
+ \ifempty\p_maxwidth
+ \ifempty\p_maxheight\orelse\ifdim\d_grph_scale_y_size>\p_maxheight\relax
+ \global\d_grph_scale_used_y_size\p_maxheight
+ \fi
+ \orelse\ifdim\d_grph_scale_x_size>\p_maxwidth\relax
+ \global\d_grph_scale_used_x_size\p_maxwidth
+ \fi
+ \fi}
+
+\def\grph_scale_by_dimension
+ {\ifdim\d_grph_scale_used_x_size>\zeropoint
+ \ifdim\d_grph_scale_used_y_size>\zeropoint
+ \grph_scale_by_dimension_a
+ \else
+ \grph_scale_by_dimension_b
+ \fi
+ \else
+ \ifdim\d_grph_scale_used_y_size>\zeropoint
+ \grph_scale_by_dimension_c
+ \else
+ \grph_scale_by_dimension_d
+ \fi
+ \fi}
+
+\def\grph_scale_by_dimension_a
+ {\grph_scale_by_dimension_indeed
+ {\grph_scale_calculate_scale\m_grph_scale_used_y_scale\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \grph_scale_calculate_scale\m_grph_scale_used_x_scale\d_grph_scale_used_x_size\d_grph_scale_x_size}%
+ {\grph_scale_calculate_scale\m_grph_scale_used_y_scale\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \grph_scale_calculate_scale\m_grph_scale_used_x_scale\d_grph_scale_used_x_size\d_grph_scale_x_size}%
+ {\grph_scale_calculate_scale\m_grph_scale_used_y_scale\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \grph_scale_calculate_scale\m_grph_scale_used_x_scale\d_grph_scale_used_x_size\d_grph_scale_x_size}}
+
+\def\grph_scale_by_dimension_b
+ {\grph_scale_by_dimension_indeed
+ {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
+ \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}%
+ {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
+ \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}%
+ {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
+ \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}}
+
+\def\grph_scale_by_dimension_c
+ {\grph_scale_by_dimension_indeed % weird .. three same cases
+ {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}%
+ {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}%
+ {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}}
+
+\def\grph_scale_by_dimension_d
+ {\grph_scale_by_dimension_indeed
+ {\grph_scale_apply_scale\m_grph_scale_used_x_scale\p_xscale
+ \grph_scale_apply_scale\m_grph_scale_used_y_scale\p_yscale
+ \global\d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size
+ \global\d_grph_scale_used_y_size\m_grph_scale_used_y_scale\d_grph_scale_y_size}%
+ {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
+ \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}%
+ {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
+ \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}}
+
+\def\grph_scale_by_dimension_indeed#1#2#3%
+ {#1\relax
+ \ifempty\p_maxwidth\orelse\ifdim\d_grph_scale_used_x_size>\p_maxwidth\relax
+ \global\d_grph_scale_used_x_size\p_maxwidth
+ #2\relax
+ \fi
+ \ifempty\p_maxheight\orelse\ifdim\d_grph_scale_used_y_size>\p_maxheight\relax
+ \global\d_grph_scale_used_y_size\p_maxheight
+ #3\relax
+ \fi}
+
+% we can use \lastnamedcs if we want to squeeze out some more
+
+\def\grph_scale_calculate_norm#1#2% todo: swap 1 and 2 and pass one less
+ {\csname\??scalenorm\ifcsname\??scalenorm#2\endcsname#2\else\s!unknown\fi\endcsname#1#2}
+
+\def\grph_scale_calculate_fact#1%
+ {\csname\??scalefact\ifcsname\??scalefact#1\endcsname#1\else\s!unknown\fi\endcsname}
+
+\defcsname\??scalenorm\v!max \endcsname#1#-#-#2#-{\global#1#2}
+\defcsname\??scalenorm\v!fit \endcsname#1#-#-#-#2{\global#1#2}
+\defcsname\??scalenorm\v!broad \endcsname#1#-#-#-#2{\global#1\dimexpr#2-4\externalfigureparameter\c!bodyfont\relax}
+\defcsname\??scalenorm\s!unknown\endcsname#1#2#-#-#-{\global#1\dimexpr#2\dimexpr\externalfigureparameter\c!bodyfont/10\relax\relax} % brr ex
+\defcsname\??scalenorm\v!auto \endcsname#1#-#2#-#-{\ifempty#2\else\global#1#2\fi}
+\defcsname\??scalenorm\empty \endcsname#1#-#2#-#-{\ifempty#2\else\global#1#2\fi}
+\defcsname\??scalenorm\s!default\endcsname#1#-#2#-#-{\ifempty#2\else\global#1#2\fi}
+
+\defcsname\??scalefact\v!min \endcsname{\global\settrue \c_grph_scale_swap_factor}
+\defcsname\??scalefact\s!unknown\endcsname{\global\setfalse\c_grph_scale_swap_factor}
+
+\defcsname\??scalenorm\v!min\endcsname#-#-#-#-#-% an ugly hack
+ {\d_grph_scale_used_x_size\hsize
+ \d_grph_scale_used_y_size\vsize}
+
+\def\grph_scale_calculate_scales#1#2%
+ {\edef\m_grph_scale_used_x_scale{\luaexpr{\number#1/\number#2}}%
+ \let\m_grph_scale_used_y_scale\m_grph_scale_used_x_scale}
+
+\def\grph_scale_calculate_scale#1#2#3%
+ {\edef#1{\luaexpr{\number#2/\number#3}}}
+
+\def\grph_scale_apply_scale#1#2% #1 = parameter / scale can be empty
+ {% no overflow
+ \edef#1{\luaexpr
+ {\number
+ \ifempty#2%
+ \ifempty\p_scale \plusthousand \else
+ \ifnum \p_scale=\zerocount \plusthousand \else
+ \p_scale \fi\fi
+ \orelse\ifnum#2=\zerocount
+ \ifempty\p_scale \plusthousand \else
+ \ifnum \p_scale=\zerocount \plusthousand \else
+ \p_scale \fi\fi
+ \else
+ #2%
+ \fi
+ /1000}}}
+
+\def\grph_scale_apply_size
+ {\ifempty\p_maxheight
+ \d_grph_scale_outer_v_size\textheight
+ \ifinner
+ \d_grph_scale_outer_v_size \vsize % \textheight =\vsize
+ \scratchdimen\vsize % \scratchdimen=\textheight
+ \orelse\ifinsidefloat
+ \d_grph_scale_outer_v_size \vsize % \textheight =\vsize
+ \scratchdimen\vsize % \scratchdimen=\textheight
+ \orelse\ifinpagebody
+ \d_grph_scale_outer_v_size \vsize % \textheight =\vsize
+ \scratchdimen\vsize % \scratchdimen=\textheight
+ \orelse\ifdim\pagegoal<\maxdimen
+ \ifdim\pagetotal<\pagegoal
+ \scratchdimen\dimexpr\pagegoal-\pagetotal\relax
+ \else
+ \scratchdimen\d_grph_scale_outer_v_size % \textheight
+ \fi
+ \else
+ \scratchdimen\d_grph_scale_outer_v_size % \textheight
+ \fi
+ \else
+ \scratchdimen\p_maxheight
+ \d_grph_scale_outer_v_size\scratchdimen
+ \fi
+ \ifempty\p_height
+ \d_grph_scale_v_size\scratchdimen
+ \else
+ \d_grph_scale_v_size\p_height
+ \fi
+ \ifempty\p_width
+ \d_grph_scale_h_size\hsize
+ \else
+ \d_grph_scale_h_size\p_width
+ \fi}
+
+% \startcombination
+% {\externalfigure[cow.pdf] [frame=on,height=3cm,equalwidth=6cm]} {a cow}
+% {\externalfigure[mill.png][frame=on,height=3cm,equalwidth=6cm]} {a mill}
+% \stopcombination
+
+\def\grph_scale_position
+ {\ifempty\p_equalwidth \else
+ \scratchdimen\p_equalwidth\relax
+ \ifdim\d_grph_scale_wd<\scratchdimen
+ \setbox\nextbox\naturalhpack to \scratchdimen{\hss\box\nextbox\hss}%
+ \fi
+ \fi
+ \ifempty\p_equalheight \else
+ \scratchdimen\p_equalheight\relax
+ \ifdim\d_grph_scale_ht<\scratchdimen
+ \setbox\nextbox\naturalvpack to \scratchdimen{\vss\box\nextbox\vss}%
+ \fi
+ \fi}
+
+\permanent\protected\def\fastscale#1%
+ {\ifnum#1=1000\relax
+ \expandafter\grph_scale_fast_nop
+ \else
+ \expandafter\grph_scale_fast_yes
+ \fi{#1}}
+
+\def\grph_scale_fast_nop#1%
+ {\hbox}
+
+\def\grph_scale_fast_yes#1%
+ {\edef\finalscaleboxxscale{\withoutpt\the\dimexpr#1\onepoint/1000\relax}% brrr
+ \let\finalscaleboxyscale\finalscaleboxxscale
+ \dowithnextboxcs\grph_scale_fast_finish\hbox} % container ?
+
+\def\grph_scale_fast_finish
+ {\grph_scale_apply
+ \box\nextbox
+ \endgroup}
+
+\permanent\protected\def\fastsxsy#1#2%
+ {\bgroup
+ \edef\p_sx{#1}%
+ \edef\p_sy{#2}%
+ \dowithnextboxcs\grph_scale_fast_sx_xy_finish\hbox} % container?
+
+\def\grph_scale_fast_sx_xy_finish
+ {\grph_scale_check_sx_sy
+ \d_grph_scale_wd\wd\nextbox
+ \d_grph_scale_ht\ht\nextbox
+ \d_grph_scale_dp\dp\nextbox
+ \grph_scale_calculations_nop
+ \grph_scale_apply
+ \box\nextbox
+ \egroup}
+
+%D \macros
+%D {clip, setupclipping}
+%D
+%D Although related to figures, clipping can be applied to arbitrary content. We can
+%D use \METAPOST\ to provide a non rectangular clipping path.
+%D
+%D \starttyping
+%D \startMPclip{fun}
+%D clip currentpicture to fullcircle
+%D shifted (.5,.5) xscaled \width yscaled \height ;
+%D \stopMPclip
+%D \stoptyping
+%D
+%D We get a rectangular piece of the figure when we say:
+%D
+%D \starttyping
+%D \clip[x=2,y=1]{\externalfigure[photo]}
+%D \stoptyping
+%D
+%D When we want to clip to the oval we defined a few lines ago, we say:
+%D
+%D \starttyping
+%D \clip[nx=1,ny=1,x=1,y=1,mp=fun]{\externalfigure[photo]}
+%D \stoptyping
+%D
+%D The general characteristics of clipping can be set up with
+%D
+%D \showsetup{setupclipping}
+
+\installcorenamespace{clipping}
+
+\installdirectcommandhandler \??clipping {clipping}
+
+\permanent\tolerant\protected\def\clip[#1]% nb top->bottom left->right
+ {\bgroup
+ \ifparameter#1\or
+ \setupcurrentclipping[#1]%
+ \fi
+ \dowithnextboxcs\grph_clip_finish\hbox}
+
+\def\grph_clip_finish
+ {\doifelse{\clippingparameter\c!state}\v!start
+ \grph_clip_yes_finish
+ \grph_clip_nop_finish}
+
+\def\grph_clip_yes_finish
+ {\ifdim\clippingparameter\c!width>\zeropoint
+ \scratchwidth \clippingparameter\c!width
+ \scratchxoffset\clippingparameter\c!hoffset
+ \else
+ \scratchwidth\dimexpr\wd\nextbox/\clippingparameter\c!nx\relax
+ \scratchxoffset\dimexpr\clippingparameter\c!x\scratchwidth-\scratchwidth\relax
+ \scratchwidth\clippingparameter\c!sx\scratchwidth
+ \fi
+ \relax % sure
+ \ifdim\clippingparameter\c!height>\zeropoint
+ \scratchheight\clippingparameter\c!height
+ \scratchyoffset\dimexpr\ht\nextbox-\clippingparameter\c!voffset-\scratchheight\relax
+ \else
+ \scratchheight\dimexpr\ht\nextbox/\clippingparameter\c!ny\relax
+ \scratchyoffset\dimexpr-\clippingparameter\c!y\scratchheight-\clippingparameter\c!sy\scratchheight+\scratchheight\relax
+ \scratchheight\clippingparameter\c!sy\scratchheight
+ \advance\scratchyoffset \ht\nextbox
+ \fi
+ \setbox\nextbox\naturalhpack
+ {\advance\scratchxoffset -\clippingparameter\c!leftoffset \relax
+ \advance\scratchyoffset -\clippingparameter\c!bottomoffset\relax
+ \hskip-\scratchxoffset
+ \lower\scratchyoffset
+ \box\nextbox}%
+ \wd\nextbox\zeropoint
+ \ht\nextbox\zeropoint
+ \dp\nextbox\zeropoint
+ \setbox\nextbox\naturalhpack
+ {\advance\scratchwidth \dimexpr\clippingparameter\c!leftoffset +\clippingparameter\c!rightoffset\relax
+ \advance\scratchheight\dimexpr\clippingparameter\c!bottomoffset+\clippingparameter\c!topoffset \relax
+ \dostartclipping{\clippingparameter\c!mp}\scratchwidth\scratchheight
+ \box\nextbox
+ \dostopclipping}%
+ \setbox\nextbox\hcontainer
+ {\hskip-\clippingparameter\c!leftoffset
+ \lower \clippingparameter\c!bottomoffset
+ \box\nextbox}%
+ \wd\nextbox\scratchwidth
+ \ht\nextbox\scratchheight
+ \dp\nextbox\zeropoint
+ \box\nextbox
+ \egroup}
+
+\def\grph_clip_nop_finish
+ {\box\nextbox
+ \egroup}
+
+\setupclipping
+ [\c!state=\v!start,
+ \c!n=\plusone, % was \plustwo
+ \c!nx=\clippingparameter\c!n,\c!x=\plusone,\c!sx=\plusone,
+ \c!ny=\clippingparameter\c!n,\c!y=\plusone,\c!sy=\plusone,
+ \c!width=\zeropoint,
+ \c!height=\zeropoint,
+ \c!hoffset=\zeropoint,
+ \c!voffset=\zeropoint,
+ \c!offset=\zeropoint,
+ \c!leftoffset=\clippingparameter\c!offset,
+ \c!rightoffset=\clippingparameter\c!offset,
+ \c!topoffset=\clippingparameter\c!offset,
+ \c!bottomoffset=\clippingparameter\c!offset,
+ \c!mp=]
+
+%D \startbuffer
+%D \startuseMPgraphic{test}
+%D path p ; p := fullcircle scaled 4cm ;
+%D draw p withpen pencircle scaled 1cm ;
+%D setbounds currentpicture to boundingbox p ;
+%D \stopuseMPgraphic
+%D
+%D \hbox to \hsize \bgroup
+%D \hss
+%D \ruledhbox{\useMPgraphic{test}}%
+%D \hss
+%D \ruledhbox{\clip{\useMPgraphic{test}}}%
+%D \hss
+%D \egroup
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D Mirroring.
+
+\permanent\protected\def\mirror
+ {\bgroup
+ \dowithnextboxcs\grph_mirror_finish\hbox}
+
+\def\grph_mirror_finish
+ {\scratchdimen\wd\nextbox
+ % better use an hbox (if no \forgetall, leftskip etc may creep in)
+ %\setbox\nextbox\vbox{\forgetall\dostartmirroring\hskip-\wd\nextbox\box\nextbox\dostopmirroring}%
+ \setbox\nextbox\naturalhpack
+ {\dostartmirroring
+ \hskip-\wd\nextbox
+ \box\nextbox
+ \dostopmirroring}%
+ \wd\nextbox\scratchdimen
+ \box\nextbox
+ \egroup}
+
+%D A couple of examples, demonstrating how the depth is taken care of:
+%D
+%D \startbuffer
+%D test\rotate[frame=on, rotation=0] {gans}%
+%D test\rotate[frame=on, rotation=90] {gans}%
+%D test\rotate[frame=on, rotation=180]{gans}%
+%D test\rotate[frame=on, rotation=270]{gans}%
+%D test
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D When we rotate over arbitrary angles, we need to relocate the resulting box
+%D because rotation brings that box onto the negative axis. The calculations (mostly
+%D sin and cosine) need to be tuned for the way a box is packages (i.e. the refence
+%D point). A typical example of drawing, scribbling, and going back to the days of
+%D school math.
+%D
+%D We do a bit more calculations than needed, simply because that way it's easier to
+%D debug the code.
+
+\installcorenamespace {rotate}
+\installcorenamespace {rotatelocation}
+\installcorenamespace {rotatepreset}
+
+%D These are set at the \LUA\ end (it's a cheaper operation than setting a dimen
+%D register and these are actually kind of constants not used in further
+%D calculations):
+
+\mutable\dimendef\d_grph_rotate_x_size \zeropoint
+\mutable\dimendef\d_grph_rotate_y_size \zeropoint
+\mutable\dimendef\d_grph_rotate_x_offset \zeropoint
+\mutable\dimendef\d_grph_rotate_y_offset \zeropoint
+\mutable\dimendef\d_grph_rotate_x_position\zeropoint
+\mutable\dimendef\d_grph_rotate_y_position\zeropoint
+
+\mutable\dimendef\d_grph_rotate_new_width \zeropoint
+\mutable\dimendef\d_grph_rotate_new_height\zeropoint
+\mutable\dimendef\d_grph_rotate_new_depth \zeropoint
+
+%D These aren't:
+
+\newdimen\d_grph_rotate_used_height
+
+\newdimen\d_grph_rotate_width
+\newdimen\d_grph_rotate_height
+\newdimen\d_grph_rotate_depth
+
+\newdimen\d_grph_rotate_saved_width
+\newdimen\d_grph_rotate_saved_height
+\newdimen\d_grph_rotate_saved_depth
+
+\newconditional\c_grph_rotate_obey_depth
+\newconditional\c_grph_rotate_not_fit
+\newconditional\c_grph_rotate_center
+
+\installframedcommandhandler \??rotate {rotate} \??rotate
+
+\setuprotate
+ [\c!rotation=90,
+ \c!location=\v!normal,
+ \c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!offset=\v!overlay,
+ \c!frame=\v!off]
+
+\let\p_rotation_location\empty
+\let\p_rotation_rotation\empty
+
+\permanent\tolerant\protected\def\rotate[#1]% \bgroup: \rotate kan argument zijn
+ {\bgroup
+ \ifparameter#1\or
+ \setupcurrentrotate[#1]%
+ \fi
+ \edef\p_rotation_location{\rotateparameter\c!location}%
+ \edef\p_rotation_rotation{\rotateparameter\c!rotation}%
+ \ifcsname\??rotatelocation\p_rotation_location\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\grph_rotate_default
+ \fi}
+
+\def\grph_rotate_framed
+ {\resetrotateparameter\c!location
+ \dowithnextboxcs\grph_rotate_finish\vbox
+ \inheritedrotateframed}
+
+\def\grph_rotate_normal
+ {\dowithnextboxcs\grph_rotate_finish\vbox}
+
+\def\grph_rotate_finish
+ {\grph_rotate_finish_indeed
+ \egroup}
+
+\def\grph_rotate_default
+ {\setfalse\c_grph_rotate_not_fit
+ \setfalse\c_grph_rotate_center
+ \settrue \c_grph_rotate_obey_depth
+ \grph_rotate_framed}
+
+\letcsname\??rotatelocation\v!default\endcsname\grph_rotate_default
+
+\defcsname\??rotatelocation\v!depth\endcsname
+ {\setfalse\c_grph_rotate_not_fit
+ \setfalse\c_grph_rotate_center
+ \settrue \c_grph_rotate_obey_depth
+ \grph_rotate_normal}
+
+\defcsname\??rotatelocation\v!fit\endcsname
+ {\settrue \c_grph_rotate_not_fit
+ \setfalse\c_grph_rotate_center
+ \settrue \c_grph_rotate_obey_depth
+ \grph_rotate_normal}
+
+\defcsname\??rotatelocation\v!broad\endcsname
+ {\setfalse\c_grph_rotate_not_fit
+ \setfalse\c_grph_rotate_center
+ \setfalse\c_grph_rotate_obey_depth
+ \grph_rotate_normal}
+
+\defcsname\??rotatelocation\v!high\endcsname
+ {\setfalse\c_grph_rotate_not_fit
+ \setfalse\c_grph_rotate_center
+ \setfalse\c_grph_rotate_obey_depth
+ \grph_rotate_framed}
+
+\defcsname\??rotatelocation\v!middle\endcsname
+ {\setfalse\c_grph_rotate_not_fit
+ \settrue \c_grph_rotate_center
+ \setfalse\c_grph_rotate_obey_depth % hm, depth ?
+ \grph_rotate_normal}
+
+\permanent\protected\def\dorotatebox#1% {angle} \hbox/\vbox/\vtop % a fast low level one
+ {\ifcase#1\relax
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\grph_rotate_box
+ \fi{#1}}
+
+\def\grph_rotate_box#1% {angle} \hbox/\vbox/\vtop
+ {\bgroup
+ \hbox\bgroup % compatibility hack
+ \edef\p_rotation_rotation{#1}%
+ \dowithnextboxcs\grph_rotate_finish_box}
+
+\def\grph_rotate_finish_box
+ {\setfalse\c_grph_rotate_not_fit % this is the same as broad but
+ \setfalse\c_grph_rotate_center % without the following grab as
+ \setfalse\c_grph_rotate_obey_depth % we call finish directly
+ \grph_rotate_finish_indeed
+ \egroup
+ \egroup}
+
+\def\grph_rotate_finish_indeed
+ {\naturalhpack\bgroup
+ \ifempty\p_rotation_rotation
+ \grph_rotate_finish_nop
+ \else
+ \grph_rotate_finish_yes
+ \fi
+ \egroup}
+
+\def\grph_rotate_finish_nop
+ {\boxcursor\box\nextbox}
+
+\defcsname\??rotatepreset\v!left\endcsname
+ {\edef\p_rotation_rotation{90}}
+
+\defcsname\??rotatepreset\v!right\endcsname
+ {\edef\p_rotation_rotation{270}}
+
+\defcsname\??rotatepreset\v!inner\endcsname
+ {\signalrightpage
+ \doifelserightpage{\def\p_rotation_rotation{270}}{\def\p_rotation_rotation{90}}}
+
+\defcsname\??rotatepreset\v!outer\endcsname
+ {\signalrightpage
+ \doifelserightpage{\def\p_rotation_rotation{90}}{\def\p_rotation_rotation{270}}}
+
+\letcsname\??rotatepreset\v!default\endcsname\empty
+
+\def\grph_rotate_finish_yes
+ {\begincsname\??rotatepreset\p_rotation_rotation\endcsname
+ \setbox\nextbox\naturalvpack{\box\nextbox}% not really needed
+ \dontcomplain
+ \ifconditional\c_grph_rotate_center
+ \d_grph_rotate_saved_width \wd\nextbox
+ \d_grph_rotate_saved_height\ht\nextbox
+ \d_grph_rotate_saved_depth \dp\nextbox
+ \setbox\nextbox\naturalhpack{\hskip-.5\wd\nextbox\lower.5\ht\nextbox\box\nextbox}%
+ \smashbox\nextbox
+ \fi
+ %
+ \d_grph_rotate_width \wd\nextbox
+ \d_grph_rotate_height\ht\nextbox
+ \d_grph_rotate_depth \dp\nextbox
+ %
+ \setbox\nextbox\naturalvpack{\naturalhpack{\raise\dp\nextbox\box\nextbox}}% can we do without
+ %
+ \d_grph_rotate_used_height\ht\nextbox
+ %
+ \clf_analyzerotate % rather accurate
+ \p_rotation_rotation\space
+ \d_grph_rotate_width
+ \d_grph_rotate_height
+ \d_grph_rotate_depth
+ \d_grph_rotate_used_height
+ \c_grph_rotate_not_fit
+ \c_grph_rotate_obey_depth
+ \relax
+ %
+ \setbox\nextbox\naturalvpack to \d_grph_rotate_y_size
+ {\vfilll
+ \hcontainer to \d_grph_rotate_x_size
+ {\dostartrotation\p_rotation_rotation
+ \wd\nextbox\zeropoint
+ \ht\nextbox\zeropoint
+ \box\nextbox
+ \dostoprotation
+ \hfill}%
+ \kern\d_grph_rotate_y_position}%
+ %
+ \setbox\nextbox\naturalhpack
+ {\kern\dimexpr\d_grph_rotate_x_position+\d_grph_rotate_x_offset\relax
+ \lower\d_grph_rotate_y_offset
+ \box\nextbox}%
+ %
+ \ifconditional\c_grph_rotate_center
+ \setbox\nextbox\naturalhpack{\hskip.5\d_grph_rotate_saved_width\lower-.5\d_grph_rotate_saved_height\box\nextbox}%
+ \wd\nextbox\d_grph_rotate_saved_width
+ \ht\nextbox\d_grph_rotate_saved_height
+ \dp\nextbox\d_grph_rotate_saved_depth
+ \else
+ \wd\nextbox\d_grph_rotate_new_width
+ \ht\nextbox\d_grph_rotate_new_height
+ \dp\nextbox\d_grph_rotate_new_depth
+ \fi
+ %
+ \boxcursor\box\nextbox}
+
+% \dostepwiserecurse{0}{360}{10}
+% {\startlinecorrection[blank]
+% \hbox
+% {\expanded{\setuprotate[rotation=\recurselevel]}%
+% \traceboxplacementtrue
+% \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=depth] {\ruledhbox{\bfb (depth)}}}}%
+% \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=fit] {\ruledhbox{\bfb (fit)}}}}%
+% \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=broad] {\ruledhbox{\bfb (broad)}}}}%
+% \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=normal]{\ruledhbox{\bfb (normal)}}}}%
+% \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=high] {\ruledhbox{\bfb (high)}}}}}
+% \stoplinecorrection}
+
+% \def\Test{\ruledhbox{%
+% \def\DemoX{\vl\kern.5\emwidth\vl}%
+% \kern\emwidth\ruledhpack{\green\rotate[rotation=20] {\ruledhpack{\DemoX}}}%
+% \kern\emwidth\ruledhpack{\blue \rotate[rotation=0] {\ruledhpack{\DemoX}}}%
+% \kern\emwidth\ruledhpack{\red \rotate[rotation=-20] {\ruledhpack{\DemoX}}}%
+% \kern\emwidth\ruledhpack{\green\rotate[rotation=200] {\ruledhpack{\DemoX}}}%
+% \kern\emwidth\ruledhpack{\blue \rotate[rotation=180] {\ruledhpack{\DemoX}}}%
+% \kern\emwidth\ruledhpack{\red \rotate[rotation=-200]{\ruledhpack{\DemoX}}}%
+% \kern\emwidth}}
+
+% \startTEXpage[offset=10pt,align=middle]
+% \setuprotate[location=fit] \Test \par {\infofont\setstrut\strut fit} \par
+% \setuprotate[location=depth] \Test \par {\infofont\setstrut\strut depth} \par
+% \setuprotate[location=broad] \Test \par {\infofont\setstrut\strut broad} \par
+% \setuprotate[location=high] \Test \par {\infofont\setstrut\strut high} \par
+% \setuprotate[location=middle] \Test \par {\infofont\setstrut\strut middle} \par
+% \setuprotate[location=default] \Test \par {\infofont\setstrut\strut default} \par
+% \stopTEXpage
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/hand-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/hand-ini.mkxl
new file mode 100644
index 00000000000..3fd82f239b4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/hand-ini.mkxl
@@ -0,0 +1,113 @@
+%D \module
+%D [ file=hand-ini,
+%D version=2008.02.12,
+%D title=\CONTEXT\ Handling Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Handling is implemented differently in \MKIV. Maybe I'll cook up
+%D some compatibility hack.
+
+% \definefontfeature[demo][default][mode=node,expansion=quality]
+% \definefontfeature[demo][default][mode=node,protrusion=pure]
+
+% experimental setup:
+%
+% \showframe \setupalign[hanging] \enabletrackers[fonts.protrusion]
+%
+% \definefontfeature[default] [default][mode=node,script=latn]
+% %definefontfeature[whatever][default][mode=node,lfbd=yes,rtbd=yes,script=latn]
+% \definefontfeature[whatever][default][mode=node,protrusion=quality]
+% \definefontfeature[whocares][default][mode=node,protrusion=yes,opbd=yes,script=latn] % left|right|yes
+%
+% \definedfont[fxlbi.otf*default] \input tufte 0123456789 \par \dorecurse{15}{1 2 3 4 5 6 7 8 9 0 } \par
+% \definedfont[fxlbi.otf*whatever] \input tufte 0123456789 \par \dorecurse{15}{1 2 3 4 5 6 7 8 9 0 } \par
+% \definedfont[fxlbi.otf*whocares] \input tufte 0123456789 \par \dorecurse{15}{1 2 3 4 5 6 7 8 9 0 } \par
+%
+% %definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn,featurefile=texgyrepagella-regularxx.fea]
+% \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn]
+%
+% \definedfont[texgyrepagella-regularxx.otf*whocares] \input tufte
+
+\unprotect
+
+%D Probbaly not yet used by many.
+
+\permanent\tolerant\protected\def\setupfontexpansion [#1]#*[#2]{\clf_setupfontexpansion {#1}{#2}}
+\permanent\tolerant\protected\def\setupfontprotrusion[#1]#*[#2]{\clf_setupfontprotrusion{#1}{#2}}
+
+% \setupfontprotrusion[quality-upright][vector=quality]
+% \setupfontprotrusion[quality-slanted][vector=quality,right=1.5]
+
+\pushoverloadmode
+
+ \let\adjustspacing\relax \newcount\adjustspacing % a little bit protection
+ \let\protrudechars\relax \newcount\protrudechars % a little bit protection
+
+\popoverloadmode
+
+%D At some point we can introduce extreme expansion, i.e. \type {\normaladjustspacing\plustwo}.
+
+% 0 = disabled
+% 1 = notused (thesis)
+% 2 = glyph + kern
+% 3 = glyph only (default)
+
+\def\font_expansion_enable {\normaladjustspacing\plusthree}
+\def\font_expansion_enable_k{\normaladjustspacing\plustwo}
+%def\font_expansion_enable_n{\normaladjustspacing\plusone}
+\def\font_expansion_disable {\normaladjustspacing\zerocount}
+
+\def\font_protruding_enable_b{\normalprotrudechars\plusthree} % also deal with r2l (experimental lmtx)
+\def\font_protruding_enable {\normalprotrudechars\plustwo }
+\def\font_protruding_disable {\normalprotrudechars\zerocount}
+
+\appendtoks \font_expansion_disable \to \everyforgetall % Here or not here?
+\appendtoks \font_protruding_disable \to \everyforgetall % Here or not here?
+
+% \def\fonthandlingerror{\writestatus\m!fonts{font handling is replaced by features in mkiv}}
+%
+% \let\fonthandling\empty
+%
+% \protected\def\startfonthandling #1{\fonthandlingerror\fonthandlingerror\gobbleuntil\stopfonthandling} % can't happen
+% \protected\def\definefonthandling {\dotripleempty\dodefinefonthandling}
+% \protected\def\setupfonthandling {\dodoubleempty\dosetupfonthandling }
+% \def\dodefinefonthandling[#1][#2][#3]{\fonthandlingerror}
+% \def\dosetupfonthandling [#1][#2]{\fonthandlingerror}
+% \def\usehandling [#1]{\fonthandlingerror}
+
+\installcorenamespace{expansion}
+
+\installcommandhandler \??expansion {expansion} \??expansion
+
+\appendtoks
+ \setuevalue{\??expansion:\currentexpansion}%
+ {\adjustspacingstep \expansionparameter\c!step\relax
+ \adjustspacingstretch\expansionparameter\c!stretch\relax
+ \adjustspacingshrink \expansionparameter\c!shrink\relax}%
+\to \everydefineexpansion
+
+\setupexpansion
+ [\c!step=1,
+ \c!stretch=40,
+ \c!shrink=20]
+
+% \defineexpansion
+% [myexpansion]
+% [\c!stretch=50,
+% \c!shrink=20]
+
+\permanent\tolerant\protected\def\setexpansion[#1]{\begincsname\??expansion:#1\endcsname}
+
+%D New in \LMTX: can be used grouped.
+
+\permanent\protected\def\noexpansion {\bitwiseflip\glyphoptions\noexpansioncode}
+\permanent\protected\def\noprotrusion{\bitwiseflip\glyphoptions\noprotrusioncode}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/java-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/java-ini.mkxl
new file mode 100644
index 00000000000..f0e260eeaa3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/java-ini.mkxl
@@ -0,0 +1,157 @@
+%D \module
+%D [ file=java-ini,
+%D version=1998.01.30,
+%D title=\CONTEXT\ JavaScript Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt JavaScript Macros / Initialization}
+
+\registerctxluafile{java-ini}{}
+
+\unprotect
+
+%D \macros
+%D {JS*}
+%D
+%D Because \JAVASCRIPT's are activated by the user, for instance by activating on a
+%D button, their support is closely related to the referencing mechanism.
+%D Integration takes place by
+%D
+%D \starttyping
+%D \goto{calculate total}[Sum()]
+%D \stoptyping
+%D
+%D The \type {()} classify this as a script. If they are absent, the keyword is
+%D treated as a normal reference. One can pass arguments to such a script by saying:
+%D
+%D \starttyping
+%D \goto{calculate total}[Sum(1.5,2.3)]
+%D \stoptyping
+
+%D \macros
+%D {startJScode}
+%D
+%D A piece of \JAVASCRIPT\ code is defined by saying:
+%D
+%D \starttyping
+%D \startJScode{name}
+%D name = 4 ;
+%D \stopJScode
+%D \stoptyping
+%D
+%D This assumes uses no preamble or presumes that the preamble is always loaded, the
+%D next definition also tells \CONTEXT\ to actually include the preamble needed.
+%D
+%D \starttyping
+%D \startJScode{uses} used {later}
+%D uses = 6 ;
+%D \stopJScode
+%D \stoptyping
+%D
+%D \macros
+%D {startJSpreamble}
+%D
+%D One can define insert \JAVASCRIPT\ code at the document level by using:
+%D
+%D \starttyping
+%D \startJSpreamble{oeps}
+%D oeps = 1 ;
+%D \stopJSpreamble
+%D \stoptyping
+%D
+%D which is the same as:
+%D
+%D \starttyping
+%D \startJSpreamble{now} used now
+%D now = 2 ;
+%D \stopJSpreamble
+%D \stoptyping
+%D
+%D while the next definition is only included when actually used.
+%D
+%D \starttyping
+%D \startJSpreamble{later} used later
+%D later = 3 ;
+%D \stopJSpreamble
+%D \stoptyping
+%D
+%D This command may be used more that once, but always before the first page is
+%D shipped out.
+%D
+%D \macros
+%D {setJSpreamble, addtoJSpreamble}
+%D
+%D In addition to the previous preamble definitions, we can set a preamble \quote
+%D {in||line} and add tokens to a preamble.
+%D
+%D \macros
+%D {useJSpreamblenow}
+%D
+%D This macro can be used to force inclusion of postponed \JAVASCRIPT\ preambles.
+
+\def\m_java_escape_u{\letterbackslash u}
+
+\permanent\protected\def\startJScode
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \enforced\let\u\m_java_escape_u
+ \java_start_code}
+
+\def\java_start_code#1\stopJScode
+ {\normalexpanded{\endgroup\clf_storejavascriptcode{#1}}}
+
+\aliased\let\stopJScode\relax
+
+\permanent\protected\def\startJSpreamble
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \enforced\let\u\m_java_escape_u
+ \java_start_preamble}
+
+\def\java_start_preamble#1\stopJSpreamble
+ {\normalexpanded{\endgroup\clf_storejavascriptpreamble{#1}}}
+
+\aliased\let\stopJSpreamble\relax
+
+\permanent\protected\def\setJSpreamble #1#2{\clf_setjavascriptpreamble {#1}{#2}}
+\permanent\protected\def\addtoJSpreamble#1#2{\clf_addtojavascriptpreamble{#1}{#2}}
+
+%D \macros
+%D {useJSscripts}
+%D
+%D In due time, users will build their collections of scripts, which can be used
+%D (loaded) when applicable. Although not all public, we will provide some general
+%D purpose scripts, collected in files with names like \type {java-...}. One can
+%D load these scripts with \type {\useJSscripts}, like:
+%D
+%D \starttyping
+%D \useJSscripts[fld]
+%D \stoptyping
+%D
+%D The not so complicated implementation of this macro is:
+
+\permanent\tolerant\protected\def\useJSscripts[#1]#*[#2]%
+ {\clf_usejavascriptscripts {#1}% two steps as this one calls tex code
+ \clf_usejavascriptpreamble{#2}}% so this one comes later
+
+\permanent\tolerant\protected\def\useJSpreamble[#1]%
+ {\clf_usejavascriptpreamble{#1}}% so this one comes later
+
+%D Here:
+
+\definefilesynonym[java-imp-fld.mkiv] [java-imp-fields.mkiv]
+\definefilesynonym[java-imp-stp.mkiv] [java-imp-steps.mkiv]
+\definefilesynonym[java-imp-fil.mkiv] [java-imp-print.mkiv]
+\definefilesynonym[java-imp-rhh.mkiv] [java-imp-highlight.mkiv]
+\definefilesynonym[java-imp-exa.mkiv] [java-imp-example.mkiv]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-def.mkxl
new file mode 100644
index 00000000000..85c1f3ac775
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-def.mkxl
@@ -0,0 +1,844 @@
+%D \module
+%D [ file=lang-def,
+%D version=2011.01.24, % merge of language files
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Languages Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Language Macros / Definitions}
+
+%D See \type {lang-*.tex} for some historic notes.
+
+\unprotect
+
+% Germanic Languages: Danish, Faeroese, Icelandic, Norwegian,
+% Swedish, German, Yiddish, Afrikaans, Dutch, English, Flemush,
+% Frisian, Plattdeutsch
+
+\installlanguage
+ [\s!en]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!month,\space,\v!day,{,\space},\v!year},
+ \s!patterns=\s!us,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=3]
+
+\installlanguage
+ [\s!nl]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!de]
+ [\c!spacing=\v!packed,
+ \s!lefthyphenmin=3,
+ \s!righthyphenmin=3,
+ \c!leftsentence=\hbox{\endash\space},
+ \c!rightsentence=\hbox{\space\endash},
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsinglesixquote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!date={\v!day,{.\space},\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!da]
+ [\c!spacing=\v!packed,
+ \c!leftsentence={\hbox{\endash\enskip}},
+ \c!rightsentence={\hbox{\enskip\endash}},
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsinglesixquote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!date={\v!day,{.\space},\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!sv]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\hbox{\endash\space},
+ \c!rightsentence=\hbox{\space\endash},
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\upperrightsingleninequote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperrightdoubleninequote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!af]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!year,\space,\v!month,\space,\v!day}]
+
+\installlanguage
+ [\s!nb]
+ [\c!spacing=\v!packed,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=2,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!date={\v!day,{.},\space,\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!nn]
+ [\c!spacing=\v!packed,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=2,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!date={\v!day,{.},\space,\v!month,\space,\v!year}]
+
+\installlanguage [\s!no] [\s!nb]
+\installlanguage [\s!norwegian] [\s!nb]
+\installlanguage [\s!bokmal] [\s!nb]
+\installlanguage [\s!nynorsk] [\s!nn]
+
+\installlanguage % old german
+ [deo]
+ [\c!spacing=\v!packed,
+ \c!default=\s!de]
+
+\installlanguage
+ [de-de]
+ [\c!spacing=\v!packed,
+ \c!default=\s!de]
+
+\installlanguage
+ [de-at]
+ [\c!spacing=\v!packed,
+ \c!default=\s!de,
+ \c!leftquote=\leftguillemot,
+ \c!rightquote=\rightguillemot,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot]
+
+\installlanguage
+ [de-ch]
+ [\c!spacing=\v!packed,
+ \c!default=\s!de]
+
+%D And some alternative (but very real) english patterns:
+
+\installlanguage
+ [en-gb]
+ [\c!default=\s!en,
+ \s!patterns=\s!gb,
+ \s!lefthyphenmin=3,
+ \s!righthyphenmin=3,
+ % the reverse of american (or maybe the american did it the reverse)
+ \c!leftquote=\upperleftdoublesixquote,
+ \c!rightquote=\upperrightdoubleninequote,
+ \c!leftquotation=\upperleftsinglesixquote,
+ \c!rightquotation=\upperrightsingleninequote]
+
+\installlanguage
+ [en-us]
+ [\c!default=\s!en,
+ \s!patterns=\s!us]
+
+\installlanguage [\s!uk] [\s!en-\s!gb]
+\installlanguage [\s!us] [\s!en-\s!us]
+
+\installlanguage [usenglish] [\s!en-\s!us]
+\installlanguage [ukenglish] [\s!en-\s!gb]
+\installlanguage [english] [\s!en]
+\installlanguage [dutch] [\s!nl]
+\installlanguage [german] [\s!de]
+\installlanguage [danish] [\s!da]
+\installlanguage [swedish] [\s!sv]
+\installlanguage [afrikaans] [\s!af]
+
+% Slavic Languages: Belarussian, Russian, Ukrainian, Bulgarian,
+% Macedonian, Serbian, Croatian, Slovenian, Czech, Kushubian,
+% Lusatian/Sorbian/Wendish, Polish, Slovak, Albanian, Illyrian,
+% Armenian
+
+\installlanguage
+ [\s!pl]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,{.\space},\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!cs]
+ [\c!spacing=\v!packed,
+ \c!leftsentence={\nobreakspace\endash\thickglue\penalty\zerocount\relax},
+ \c!rightsentence=\c!leftsentence,
+ \c!leftsubsentence=\c!leftsentence,
+ \c!rightsubsentence=\c!rightsentence,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsinglesixquote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!date={\v!day,{.\fourperemspace},\v!month,\space,\v!year},
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=3]
+
+\installlanguage
+ [\s!sk]
+ [\c!spacing=\v!packed,
+ \c!leftsentence={\nobreakspace\endash\thickglue\penalty\zerocount\relax},
+ \c!rightsentence=\c!leftsentence,
+ \c!leftsubsentence=\c!leftsentence,
+ \c!rightsubsentence=\c!rightsentence,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsinglesixquote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!date={\v!day,{.\fourperemspace},\v!month,\space,\v!year},
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=3]
+
+\installlanguage
+ [\s!hr]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\hbox{\endash\space},
+ \c!rightsentence=\hbox{\space\endash},
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,{.},\space,\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!sl]
+ [\c!command=\v!no,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\hbox{\endash\space},
+ \c!rightsentence=\hbox{\space\endash},
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\guilsingleright,
+ \c!rightquote=\guilsingleleft,
+ \c!leftquotation=\rightguillemot,
+ \c!rightquotation=\leftguillemot,
+ \c!date={\v!day,{.},\space,\v!month,\space,\v!year}]
+
+\installlanguage [\s!polish] [\s!pl]
+\installlanguage [\s!czech] [\s!cs]
+\installlanguage [\s!slovak] [\s!sk]
+\installlanguage [\s!croatian] [\s!hr]
+\installlanguage [\s!slovenian] [\s!sl]
+\installlanguage [slovene] [\s!sl] % both possible (mojca: still needed?)
+
+% Cyrillic Languages
+
+\installlanguage
+ [\s!ru]
+ [\s!lefthyphenmin=2,
+ \s!righthyphenmin=2,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\leftguillemot,
+ \c!rightsentence=\rightguillemot,
+ \c!leftsubsentence=\lowerleftdoubleninequote,
+ \c!rightsubsentence=\upperrightdoubleninequote,
+ \c!leftquote=\lowerleftdoubleninequote,
+ \c!rightquote=\upperrightdoubleninequote,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!ua]
+ [\s!lefthyphenmin=2,
+ \s!righthyphenmin=2,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\leftguillemot,
+ \c!rightsentence=\rightguillemot,
+ \c!leftsubsentence=\lowerleftdoubleninequote,
+ \c!rightsubsentence=\upperrightdoubleninequote,
+ \c!leftquote=\lowerleftdoubleninequote,
+ \c!rightquote=\upperrightdoubleninequote,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!date={\v!day,\space,\v!month,\space,\v!year},
+ \s!patterns=\s!uk]
+
+\installlanguage
+ [\s!be]
+ [\s!lefthyphenmin=2,
+ \s!righthyphenmin=2,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\leftguillemot,
+ \c!rightsentence=\rightguillemot,
+ \c!leftsubsentence=\lowerleftdoubleninequote,
+ \c!rightsubsentence=\upperrightdoublesixquote,
+ \c!leftquote=\lowerleftdoubleninequote,
+ \c!rightquote=\upperrightdoublesixquote,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+ \installlanguage
+ [\s!sr]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\hbox{\endash\space},
+ \c!rightsentence=\hbox{\space\endash},
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\upperrightsingleninequote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperleftdoubleninequote,
+ \c!date={\v!day,{.},\space,\v!month,\space,\v!year}]
+
+\installlanguage
+ [\s!mk]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\endash,
+ \c!rightsentence=\endash,
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\upperleftsingleninequote,
+ \c!rightquote=\upperrightsinglesixquote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage [\s!russian] [\s!ru]
+\installlanguage [\s!ukrainian] [\s!ua]
+\installlanguage [\s!belarussian][\s!be]
+\installlanguage [\s!serbian] [\s!sr]
+\installlanguage [\s!sr-latn] [\s!sr]
+\installlanguage [\s!sr-cyrl] [\s!sr]
+\installlanguage [\s!macedonian] [\s!mk]
+
+
+% Uralic Languages: Cheremiss, Estonian, Finnish, Karelian, Laap,
+% Mordvinian, Permian tongues, Hungarian, Ostyak, Vogul, Samoyed
+
+\installlanguage
+ [\s!fi]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!year,\space,\v!month,\space,\v!day}]
+
+\installlanguage
+ [\s!et]
+ [\c!default=\s!en,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\leftguillemot,
+ \c!rightquote=\rightguillemot,
+ \c!leftquotation=\lowerrightdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!time={h,{.},m},
+ \c!date={\v!day,.,\space,\v!month,\space,\v!year},
+ \s!patterns=\s!et]
+
+\installlanguage
+ [\s!hu]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\lowerleftsingleninequote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!year,.,\space,\v!month,\space,\v!day,.}]
+
+\installlanguage [\s!finnish] [\s!fi]
+\installlanguage [\s!estonian] [\s!et]
+\installlanguage [\s!hungarian] [\s!hu]
+
+% Altaic Languages: Uigur, Uzbek, Azeri/Azerbaijani, Chuvash,
+% Turkish, Turkmen, Kazakh, Kazar, Kireghiz, Noghay, Talar,
+% Buryat, Kalmuck, Khalkha
+
+\installlanguage
+ [\s!tr]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!year,\space,\v!month,\space,\v!day}]
+
+\installlanguage
+ [\s!tk]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!year,\space,\v!month,\space,\v!day},
+ \s!patterns=\s!tk,
+ \s!lefthyphenmin=1,
+ \s!righthyphenmin=2]
+
+\installlanguage [\s!turkish] [\s!tr]
+\installlanguage [\s!turkmen] [\s!tk]
+
+% Anatolian Languages
+
+% Hebrew
+
+\installlanguage % same as arabic now .. someone needs to fix this
+ [\s!he]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,\space,\v!month,{،\space},\v!year}]
+
+\installlanguage % same as arabic now .. someone needs to fix this
+ [\s!yi]
+ [\s!he]
+
+\installlanguage [\s!hebrew] [\s!he]
+\installlanguage [\s!yiddish] [\s!yi]
+
+% Arabic Languages
+
+\installlanguage
+ [\s!ar]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,\space,\v!month,{،\space},\v!year}]
+
+\installlanguage [\s!arabic] [\s!ar]
+
+\installlanguage
+ [\s!pe]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\leftguillemot,
+ \c!rightquote=\rightguillemot,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage [\s!persian] [\s!pe]
+\installlanguage [\s!fa] [\s!pe] % these two are redundant but sometimes might
+\installlanguage [\s!farsi] [\s!fa] % sound more natural .. best set labels to 'pe'
+
+% Just aliases to "ar" for now
+
+\installlanguage[\s!ar-ae][\c!default=\s!ar] % U.A.E.
+\installlanguage[\s!ar-bh][\c!default=\s!ar] % Bahrain
+\installlanguage[\s!ar-eg][\c!default=\s!ar] % Egypt
+\installlanguage[\s!ar-in][\c!default=\s!ar] % India?
+\installlanguage[\s!ar-kw][\c!default=\s!ar] % Kuwait
+\installlanguage[\s!ar-ly][\c!default=\s!ar] % Libya
+\installlanguage[\s!ar-om][\c!default=\s!ar] % Oman
+\installlanguage[\s!ar-qa][\c!default=\s!ar] % Qatar
+\installlanguage[\s!ar-sa][\c!default=\s!ar] % Saudi Arabia
+\installlanguage[\s!ar-sd][\c!default=\s!ar] % Sudan
+\installlanguage[\s!ar-tn][\c!default=\s!ar] % Tunisia
+\installlanguage[\s!ar-ye][\c!default=\s!ar] % Yemen
+
+% Farsi (Persian)
+
+\installlanguage[\s!ar-ir][\c!default=\s!pe]
+%installlanguage[\s!pe-ir][\c!default=\s!pe]
+%installlanguage[\s!fa-ir][\c!default=\s!fa]
+
+% Syriac months
+
+\installlanguage[\s!ar-sy][\c!default=\s!ar] % Syria
+\installlanguage[\s!ar-iq][\c!default=\s!ar-sy] % Iraq
+\installlanguage[\s!ar-jo][\c!default=\s!ar-sy] % Jordan
+\installlanguage[\s!ar-lb][\c!default=\s!ar-sy] % Lebanon
+
+% Maghribi months
+
+\installlanguage[\s!ar-dz][\c!default=\s!ar] % Algeria
+\installlanguage[\s!ar-ma][\c!default=\s!ar] % Morocco
+
+% Artificial Languages: Esperanto
+
+\installlanguage
+ [\s!esperanto]
+ [%\c!spacing=\v!packed,
+ %\c!leftsentence=\emdash,
+ %\c!rightsentence=\emdash,
+ %\c!leftsubsentence=\emdash,
+ %\c!rightsubsentence=\emdash,
+ %\c!leftquote=\lowerleftdoubleninequote,
+ %\c!rightquote=\upperrightdoublesixquote,
+ %\c!leftquotation=\lowerleftdoubleninequote,
+ %\c!rightquotation=\upperrightdoublesixquote,
+ %\c!date={\v!year,~m.,\space,\v!month,\space,\v!day,~d.},
+ \s!patterns=eo,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=2]
+
+% Baltic Languages: Lettish/Latvian, Lietuvių/Lithuanian
+
+\installlanguage
+ [\s!lt]
+ [\c!command=\v!no,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\lowerleftdoubleninequote,
+ \c!rightquote=\upperrightdoublesixquote,
+ \c!leftquotation=\lowerleftdoubleninequote,
+ \c!rightquotation=\upperrightdoublesixquote,
+ \c!date={\v!year,~m.,\space,\v!month,\space,\v!day,~d.},
+ \s!patterns=\s!lt,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=2]
+
+\installlanguage [\s!lithuanian] [\s!lt]
+
+% Celtic: Breton, Welsh, Irish, Manx, Scottish Gaelic
+
+% CJK: Chinese, Japanese, Korean
+
+\installlanguage
+ [\s!cn]
+ [\c!leftsentence=——,
+ \c!rightsentence=——,
+ \c!leftsubsentence=——,
+ \c!rightsubsentence=——,
+ \c!leftquote=‘,
+ \c!rightquote=’,
+ \c!leftquotation=“,
+ \c!rightquotation=”,
+ \c!date={\v!year,年,\space,\v!month,\v!day,日}]
+
+\installlanguage
+ [\s!ja]
+ [\c!leftsentence=——,
+ \c!rightsentence=——,
+ \c!leftsubsentence=——,
+ \c!rightsubsentence=——,
+ \c!leftquote=‘,
+ \c!rightquote=’,
+ \c!leftquotation=「,
+ \c!rightquotation=」,
+ \c!date={西暦,\v!year,年,\v!month,月,\v!day,日}]
+
+\installlanguage
+ [\s!kr]
+ [\c!leftsentence=——,
+ \c!rightsentence=——,
+ \c!leftsubsentence=——,
+ \c!rightsubsentence=——,
+ \c!leftquote=‘,
+ \c!rightquote=’,
+ \c!leftquotation=“,
+ \c!rightquotation=”,
+ % \c!date={서기,\space,\v!year,\labeltext{\v!year},\space,\v!month,\labeltext{\v!month},\space,\v!day,\labeltext{\v!day}}]
+ \c!date={\v!year,\labeltext{\v!year},\space,\v!month,\labeltext{\v!month},\space,\v!day,\labeltext{\v!day}}]
+
+\installlanguage [\s!chinese] [\s!cn]
+\installlanguage [\s!japanese] [\s!ja]
+\installlanguage [\s!korean] [\s!kr]
+
+% Greek
+
+\installlanguage
+ [\s!gr]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=“,
+ \c!rightquote=”,
+ \c!leftquotation=«,
+ \c!rightquotation=»,
+ \c!date={\v!day\space\v!month\space\v!year},
+ \s!patterns=\s!agr] % ok?
+
+\installlanguage [\s!greek] [\s!gr]
+
+\installlanguage
+ [\s!agr]
+ [\s!default=\s!gr,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote]
+
+\installlanguage [\s!ancientgreek] [\s!agr]
+
+% Indo-Iranian Languages: 34.
+
+% Italic Languages: Latin, Italian, Rhaeto-Romanic, Rumanian,
+% Sardian, Catalan, French, Ladino, Portuguese, Proven\c{c}al,
+% Spanish
+
+\installlanguage
+ [\s!fr]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\leftguillemot,
+ \c!rightquote=\rightguillemot,
+ \c!leftquotation=\leftguillemot,
+ \c!rightquotation=\rightguillemot,
+ \c!time={h,{\Uchar104 },m},% we need to cheat in order to get the h
+ \c!date={\v!day:mnem,\v!space,\v!month,\v!space,\v!year}]
+
+\installlanguage
+ [\s!es]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,{ de },\v!month,{ de },\v!year}]
+
+\installlanguage [sp] [\s!es] % old times context
+
+\installlanguage
+ [\s!ca]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+% Note GB left|/|right (sub)sentences are for \quote {incisi}.
+
+\installlanguage
+ [\s!it]
+ [\c!command=\v!no,
+ \c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\endash,
+ \c!rightsubsentence=\endash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!leftspeech=\leftguillemot,
+ \c!middlespeech=\leftguillemot,
+ \c!rightspeech=\rightguillemot,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage % the same as italian
+ [\s!la]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+\installlanguage % ancient latin
+ [\s!ala]
+ [\c!default=\s!la]
+
+\installlanguage
+ [\s!pt]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!year,\space,\v!month,\space,\v!day}]
+
+\installlanguage[\s!pt-br][\c!default=\s!pt] % Brazil
+\installlanguage[\s!es-es][\c!default=\s!es] % Spain
+\installlanguage[\s!es-la][\c!default=\s!es] % Latin America
+
+\installlanguage
+ [\s!ro]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\leftguillemot,
+ \c!rightquote=\rightguillemot,
+ \c!leftquotation=\lowerrightdoubleninequote,
+ \c!rightquotation=\upperleftdoublesixquote,
+ \c!date={\v!day,\space,\v!month,\space,\v!year}]
+
+%D For compatibility reasons we also define:
+
+\installlanguage [\s!french] [\s!fr]
+\installlanguage [\s!spanish] [\s!es]
+\installlanguage [\s!catalan] [\s!ca]
+\installlanguage [\s!italian] [\s!it]
+\installlanguage [\s!latin] [\s!la]
+\installlanguage [\s!portuguese] [\s!pt]
+\installlanguage [\s!romanian] [\s!ro]
+
+% Vietnamese Language
+
+\installlanguage
+ [\s!vi]
+ [\c!spacing=\v!packed,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\quoteleft,
+ \c!rightquote=\quoteright,
+ \c!leftquotation=\quotedblleft,
+ \c!rightquotation=\quotedblright,
+ \c!date={{ },dd,{/},mm,{/},yy}]
+
+\installlanguage [\s!vietnamese] [\s!vi]
+
+% Thai (teaser for Mojca)
+
+\installlanguage
+ [\s!th]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!month,\space,\v!day,{,\space},\v!year},
+ \s!patterns=\s!th,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=3]
+
+\installlanguage [\s!thai] [\s!th]
+
+%D Malayalam (needs to be checked)
+
+\installlanguage
+ [\s!ml]
+ [\c!spacing=\v!broad,
+ \c!leftsentence=\emdash,
+ \c!rightsentence=\emdash,
+ \c!leftsubsentence=\emdash,
+ \c!rightsubsentence=\emdash,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!date={\v!month,\space,\v!day,{,\space},\v!year},
+ \s!patterns=\s!ml,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=3]
+
+\installlanguage [\s!malayalam] [\s!ml]
+
+%D Todo: generate this one from languages.data
+
+\installlanguage[nld][\s!nl]
+\installlanguage[deu][\s!de]
+\installlanguage[eng][\s!en]
+\installlanguage[fra][\s!fr]
+%installlanguage[grk][\s!agr]
+
+%D This might go:
+
+\installlanguage[cz][\s!cs]
+%installlanguage[fa][\s!fr] % for dutchies only
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-dis.lmt b/Master/texmf-dist/tex/context/base/mkxl/lang-dis.lmt
new file mode 100644
index 00000000000..263225691f0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-dis.lmt
@@ -0,0 +1,222 @@
+if not modules then modules = { } end modules ['lang-dis'] = {
+ version = 1.001,
+ comment = "companion to lang-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local concat = table.concat
+
+local tex = tex
+local nodes = nodes
+
+local tasks = nodes.tasks
+local nuts = nodes.nuts
+
+local enableaction = tasks.enableaction
+local setaction = tasks.setaction
+
+local setfield = nuts.setfield
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getattr = nuts.getattr
+local getsubtype = nuts.getsubtype
+local setsubtype = nuts.setsubtype
+local getchar = nuts.getchar
+local setchar = nuts.setchar
+local getdisc = nuts.getdisc
+local setdisc = nuts.setdisc
+local getlanguage = nuts.getlanguage
+local getboth = nuts.getboth
+local setlist = nuts.setlist
+local setlink = nuts.setlink
+local isglyph = nuts.isglyph
+
+local copy_node = nuts.copy
+local remove_node = nuts.remove
+local flush_list = nuts.flush_list
+local flush_node = nuts.flush_node
+
+local nextdisc = nuts.traversers.disc
+
+local new_disc = nuts.pool.disc
+
+local nodecodes = nodes.nodecodes
+local disccodes = nodes.disccodes
+
+local disc_code = nodecodes.disc
+local glyph_code = nodecodes.glyph
+
+local explicitdisc_code = disccodes.explicit
+
+local a_visualize = attributes.private("visualizediscretionary")
+local setattribute = tex.setattribute
+
+local getlanguagedata = languages.getdata
+local prehyphenchar = language.prehyphenchar
+local posthyphenchar = language.posthyphenchar
+
+local check_regular = true
+
+local setlistcolor = nodes.tracers.colors.setlist
+
+function languages.visualizediscretionaries(head)
+ for d in nextdisc, head do
+ if getattr(d,a_visualize) then
+ local pre, post, replace = getdisc(d)
+ if pre then
+ setlistcolor(pre,"darkred")
+ end
+ if post then
+ setlistcolor(post,"darkgreen")
+ end
+ if replace then
+ setlistcolor(replace,"darkblue")
+ end
+ end
+ end
+ return head
+end
+
+local enabled = false
+
+function languages.showdiscretionaries(v)
+ if v == false then
+ setattribute(a_visualize,unsetvalue)
+ else -- also nil
+ if not enabled then
+ enableaction("processors","languages.visualizediscretionaries")
+ enabled = true
+ end
+ setattribute(a_visualize,1)
+ end
+end
+
+interfaces.implement {
+ name = "showdiscretionaries",
+ actions = languages.showdiscretionaries
+}
+
+do
+
+ local toutf = nodes.listtoutf
+ local utfchar = utf.char
+ local f_disc = string.formatters["{%s}{%s}{%s}"]
+ local replace = lpeg.replacer( {
+ [utfchar(0x200C)] = "|",
+ [utfchar(0x200D)] = "|",
+ }, nil, true)
+
+ local function convert(list)
+ return list and replace(toutf(list)) or ""
+ end
+
+ function languages.serializediscretionary(d) -- will move to tracer
+ local pre, post, replace = getdisc(d)
+ return f_disc(convert(pre),convert(post),convert(replace))
+ end
+
+end
+
+-- --
+
+local wiped = 0
+
+local flatten_discretionaries = nuts.flatten_discretionaries -- todo in nodes
+
+-- if flatten_discretionaries then
+
+ function languages.flatten(head)
+ local h, n = flatten_discretionaries(head)
+ wiped = wiped + n
+ return h, n > 0
+ end
+
+-- else
+--
+-- local function wipe(head,delayed)
+-- local p, n = getboth(delayed)
+-- local _, _, h, _, _, t = getdisc(delayed,true)
+-- if p or n then
+-- if h then
+-- setlink(p,h)
+-- setlink(t,n)
+-- setfield(delayed,"replace")
+-- else
+-- setlink(p,n)
+-- end
+-- end
+-- if head == delayed then
+-- head = h
+-- end
+-- wiped = wiped + 1
+-- flush_node(delayed)
+-- return head
+-- end
+--
+-- function languages.flatten(head)
+-- local delayed = nil
+-- for d in nextdisc, head do
+-- if delayed then
+-- head = wipe(head,delayed)
+-- end
+-- delayed = d
+-- end
+-- if delayed then
+-- return wipe(head,delayed), true
+-- else
+-- return head, false
+-- end
+-- end
+--
+-- end
+
+function languages.nofflattened()
+ return wiped -- handy for testing
+end
+
+-- experiment: for now not in not in export mode!
+
+local flatten = languages.flatten
+
+nodes.handlers.flattenline = flatten
+
+function nodes.handlers.flatten(head,where)
+ if head and (where == "box" or where == "adjusted_hbox") then
+ return flatten(head)
+ end
+ return head
+end
+
+directives.register("hyphenator.flatten",function(v)
+ -- use with care
+ setaction("processors","nodes.handlers.flatten",v)
+ setaction("contributers","nodes.handlers.flattenline",v)
+end)
+
+-- moved here:
+
+function languages.explicithyphen(template)
+ local pre, post
+ local disc = new_disc()
+ if template then
+ local langdata = getlanguagedata(getlanguage(template))
+ local instance = langdata and langdata.instance
+ if instance then
+ local prechr = prehyphenchar(instance)
+ local postchr = posthyphenchar(instance)
+ if prechr >= 0 then
+ pre = copy_node(template)
+ setchar(pre,prechr)
+ end
+ if postchr >= 0 then
+ post = copy_node(template)
+ setchar(post,postchr)
+ end
+ end
+ end
+ setdisc(disc,pre,post,nil,explicitdisc_code,tex.exhyphenpenalty)
+ return disc
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-frd.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-frd.mkxl
new file mode 100644
index 00000000000..352e5016dec
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-frd.mkxl
@@ -0,0 +1,143 @@
+%D \module
+%D [ file=lang-frd,
+%D version=2004.01.15,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Language Frequency Table Data,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\endinput
+
+%D This is experimental work! Old stuff, whenever I need it I might
+%D do it in \LUA.
+
+% http://www.onzetaal.nl/advies/letterfreq.html
+
+\startcharactertable[nl]
+ \charfreq a 7.47
+ \charfreq b 1.58
+ \charfreq c 1.24
+ \charfreq d 5.93
+ \charfreq e 18.91
+ \charfreq f .81
+ \charfreq g 3.4
+ \charfreq h 2.38
+ \charfreq i 6.5
+ \charfreq j 1.46
+ \charfreq k 2.25
+ \charfreq l 3.57
+ \charfreq m 2.21
+ \charfreq n 10.03
+ \charfreq o 6.06
+ \charfreq p 1.57
+ \charfreq q .009
+ \charfreq r 6.41
+ \charfreq s 3.73
+ \charfreq t 6.79
+ \charfreq u 1.99
+ \charfreq v 2.85
+ \charfreq w 1.52
+ \charfreq x .04
+ \charfreq y .035
+ \charfreq z 1.39
+\stopcharactertable
+
+% http://caislab.icu.ac.kr/course/2001/spring/ice605/down/010306.pdf
+
+% \startcharactertable[en]
+% \charfreq a 8.2
+% \charfreq b 1.5
+% \charfreq c 2.8
+% \charfreq d 4.3
+% \charfreq e 12.7
+% \charfreq f 2.2
+% \charfreq g 2
+% \charfreq h 6.1
+% \charfreq i 7
+% \charfreq j .2
+% \charfreq k .8
+% \charfreq l 4
+% \charfreq m 2.4
+% \charfreq n 6.7
+% \charfreq o 7.5
+% \charfreq p 1.9
+% \charfreq q .1
+% \charfreq r 6
+% \charfreq s 6.3
+% \charfreq t 9.1
+% \charfreq u 2.8
+% \charfreq v 1
+% \charfreq w 2.3
+% \charfreq x .1
+% \charfreq y 2
+% \charfreq z .1
+% \stopcharactertable
+
+% http://www.blankenburg.de/gat/pages/fach/info/analyse2.htm
+
+\startcharactertable[en]
+ \charfreq a 8.04
+ \charfreq b 1.54
+ \charfreq c 3.06
+ \charfreq d 3.99
+ \charfreq e 12.51
+ \charfreq f 2.3
+ \charfreq g 1.96
+ \charfreq h 5.49
+ \charfreq i 7.26
+ \charfreq j .16
+ \charfreq k .67
+ \charfreq l 4.14
+ \charfreq m 2.53
+ \charfreq n 7.09
+ \charfreq o 7.6
+ \charfreq p 2
+ \charfreq q .11
+ \charfreq r 6.12
+ \charfreq s 6.54
+ \charfreq t 9.25
+ \charfreq u 2.71
+ \charfreq v .99
+ \charfreq w 1.92
+ \charfreq x .19
+ \charfreq y 1.73
+ \charfreq z .09
+\stopcharactertable
+
+% http://www.blankenburg.de/gat/pages/fach/info/analyse2.htm
+
+\startcharactertable[de]
+ \charfreq a 6.47
+ \charfreq b 1.93
+ \charfreq c 2.68
+ \charfreq d 4.83
+ \charfreq e 17.48
+ \charfreq f 1.65
+ \charfreq g 3.06
+ \charfreq h 4.23
+ \charfreq i 7.73
+ \charfreq j .27
+ \charfreq k 1.46
+ \charfreq l 3.49
+ \charfreq m 2.58
+ \charfreq n 9.84
+ \charfreq o 2.98
+ \charfreq p .96
+ \charfreq q .02
+ \charfreq r 7.54
+ \charfreq s 6.83
+ \charfreq t 6.13
+ \charfreq u 4.17
+ \charfreq v .94
+ \charfreq w 1.48
+ \charfreq x .04
+ \charfreq y .08
+ \charfreq z 1.14
+\stopcharactertable
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-frq.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-frq.mkxl
new file mode 100644
index 00000000000..6b9853af574
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-frq.mkxl
@@ -0,0 +1,234 @@
+%D \module
+%D [ file=lang-frq,
+%D version=2004.01.15,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Frequency Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\endinput
+
+\writestatus{loading}{ConTeXt Language Macros / Frequency Tables}
+
+%D Some day I might redo this \LUA. But anyway, who uses it. It's rather old code.
+
+\unprotect
+
+%M \usemodule[layout]
+
+%D \macros
+%D {charwidthmethod}
+%D
+%D This module implements a method for determining the width of an
+%D average character in a language. It uses the dimensions of the
+%D current fonts.
+%D
+%D \def\ShwChrWd#1#2#3%
+%D {\chardef\charwidthmethod#1\relax
+%D \mainlanguage[#2#3]\the\dimexpr(\averagecharwidth)}
+%D
+%D \starttabulate[|c|c|c|c|c|c|]
+%D \HL
+%D \NC \NC\bf0=amount\NC\bf1=.5em\NC2=ex\NC\bf3=frequency\NC\bf4=list\NC\NR
+%D \HL
+%D \NC\bf en\NC\ShwChrWd0en\NC\ShwChrWd1en\NC\ShwChrWd2en\NC\ShwChrWd3en\NC\ShwChrWd4en\NC\NR
+%D \NC\bf nl\NC\ShwChrWd0nl\NC\ShwChrWd1nl\NC\ShwChrWd2nl\NC\ShwChrWd3nl\NC\ShwChrWd4nl\NC\NR
+%D \NC\bf de\NC\ShwChrWd0de\NC\ShwChrWd1de\NC\ShwChrWd2de\NC\ShwChrWd3de\NC\ShwChrWd4de\NC\NR
+%D \HL
+%D \stoptabulate
+%D
+%D Method~1 ignores the widths and assumes that each character has a
+%D width of .5em, which is true for most monospaced fonts. Method~2
+%D takes the x as starting point, and assumes that it's height kind of
+%D matches its width. Method~3 is the best one, and determines the
+%D average width based on the language specific character table.
+%D Method~4 is a mixture between the first two methods: character
+%D specific widths applied to an equal distribution. Method~0 reports
+%D the total count, which normally is~100.
+
+\setnewconstant\charwidthmethod=3 % 0=amount 1=em 2=ex 3=frequency 4=flattened >4=ex
+
+%D \macros
+%D {charwidthlanguage}
+%D
+%D The language used for the calculations is defined as:
+
+\def\charwidthlanguage{\currentmainlanguage}
+
+%D \macros
+%D {charfreq}
+%D
+%D This method comes into action in the following macro:
+
+\def\charfreq#1 #2 % character fraction
+ {+(\ifcase\charwidthmethod
+ #2\dimexpr100\onepoint\relax
+ \or
+ #2\dimexpr\emwidth/2\relax
+ \or
+ #2\dimexpr\exheight\relax
+ \or
+ #2\fontcharwd\font`#1%
+ \or
+ \dimexpr100\fontcharwd\font`#1/\charactertsize\charwidthlanguage\relax % ugly hack
+ \else
+ #2\dimexpr\exheight\relax
+ \fi)}
+
+%D \macros
+%D {startcharactertable}
+%D
+%D A frequency table is defined with the following macro. The \type
+%D {charfreq} macro is used in this table.
+
+\installcorenamespace{frequencywidths}
+\installcorenamespace{frequencycounts}
+
+\aliased\let\stopcharactertable\relax
+
+\permanent\protected\def\startcharactertable[#1]#2\stopcharactertable % \dimexpr has fuzzy lookahead
+ {\startnointerference
+ \gdefcsname\??frequencywidths#1\endcsname{#2}% the width vector
+ \scratchcounter\zerocount \def\charfreq##1 ##2 {\advance\scratchcounter\plusone} #2%
+ \xdefcsname\??frequencycounts#1\endcsname{\the\scratchcounter}% the character count
+ \stopnointerference}
+
+%D \macros
+%D {charactertable,charactertsize}
+%D
+%D The table content as well as the number of entries can be fetched with
+%D the following two macros. The architecture of the table and calling
+%D macro permits a fully expandable application.
+
+\permanent\def\charactertable#1%
+ {\csname\??frequencywidths\ifcsname\??frequencywidths#1\endcsname#1\else\s!en\fi\endcsname}
+
+\permanent\def\charactertsize#1%
+ {\csname\??frequencycounts\ifcsname\??frequencycounts#1\endcsname#1\else\s!en\fi\endcsname}
+
+%D Although it is of hardly any use, you can inherit a character table:
+%D
+%D \starttyping
+%D \startcharactertable[cz] \charactertable{en} \stopcharactertable
+%D \stoptyping
+
+\startcharactertable[en]
+ % empty
+\stopcharactertable % kind of default
+
+%D \macros
+%D {averagecharwidth}
+%D
+%D This macro reports the average width for the current main
+%D language (\the \dimexpr (\averagecharwidth)).
+
+\permanent\def\averagecharwidth{\dimexpr(\zeropoint\charactertable\charwidthlanguage)/100\relax}
+
+\permanent\protected\def\showcharfreq
+ {\hbox\bgroup
+ \charwidthlanguage:%
+ \dostepwiserecurse\zerocount\plusfour\plusone
+ {\setconstant\charwidthmethod\recurselevel\relax
+ \enspace\recurselevel/\the\dimexpr(\averagecharwidth)}%
+ \egroup}
+
+%D Just for fun, we show a few frequency tables as graphic (\in {figure}
+%D [fig:charfreq]).
+%D
+%D \startbuffer
+%D \definepalet [charfreq] [en=darkred, nl=darkgreen, de=darkblue]
+%D
+%D \def\charfreq#1 #2 %
+%D {\startMPdrawing
+%D interim linejoin := butt ;
+%D a := ASCII "#1" ;
+%D if (a >= (ASCII "a")) and (a <= (ASCII "z")) :
+%D draw ((0,#2*.25cm)--origin--(0,#2*.5cm))
+%D shifted (a*4mm+o,0)
+%D withpen pencircle scaled .5mm
+%D withcolor c;
+%D fi ;
+%D \stopMPdrawing}
+%D
+%D \resetMPdrawing
+%D \startMPdrawing
+%D numeric a, o ; a := o := 0 ;
+%D color c ; c := .5white ;
+%D string s ; s := "" ;
+%D \stopMPdrawing
+%D
+%D \startMPdrawing o := 0mm ; c := \MPcolor{charfreq:en} ; \stopMPdrawing
+%D \charactertable{en}
+%D
+%D \startMPdrawing o := 1mm ; c := \MPcolor{charfreq:nl} ; \stopMPdrawing
+%D \charactertable{nl}
+%D
+%D \startMPdrawing o := 2mm ; c := \MPcolor{charfreq:de} ; \stopMPdrawing
+%D \charactertable{de}
+%D
+%D \startMPdrawing
+%D for a := ASCII "a" upto ASCII "z" :
+%D draw textext.bot("\strut\tttf " & char a) shifted (a*4mm+1mm,-1mm) ;
+%D endfor ;
+%D \stopMPdrawing
+%D
+%D \MPdrawingdonetrue \getMPdrawing \resetMPdrawing
+%D \stopbuffer
+%D
+%D \placefigure
+%D [here]
+%D [fig:charfreq]
+%D {The character distributions for English, Dutch and German.}
+%D {\getbuffer}
+%D
+%D A few samples of usage of this mechanism are shown below:
+%D
+%D \startbuffer
+%D {\mainlanguage[en]\hsize65\averagecharwidth\mainlanguage[en]\input ward \blank}
+%D {\mainlanguage[nl]\hsize65\averagecharwidth\mainlanguage[en]\input ward \blank}
+%D {\mainlanguage[de]\hsize65\averagecharwidth\mainlanguage[en]\input ward \blank}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Although the widthts differ, the consequenes for breaking the paragraph
+%D into lines are minimal.
+
+%D \macros
+%D {freezeaveragecharacterwidth}
+%D
+%D This macro can be used to make sure that the width does not change during a
+%D page break when another font is used.
+
+\aliased\let\normalaveragecharacterwidth\averagecharacterwidth
+
+\permanent\def\freezeaveragecharacterwidth % global
+ {\enforced\xdef\averagecharacterwidth{\dimexpr\the\normalaveragecharacterwidth\relax}}
+
+%D Example:
+%D
+%D \starttyping
+%D \input lang-frq.mkiv
+%D \input lang-frd.mkiv
+%D
+%D \setupbodyfont
+%D [dejavu]
+%D
+%D \setemeasure{textwidth}{\the\dimexpr70\averagecharwidth}
+%D
+%D \setuplayout
+%D [width=\measure{textwidth}]
+%D
+%D \showframe
+%D
+%D \starttext
+%D \input ward
+%D \stoptext
+%D \stoptyping
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-hyp.lmt b/Master/texmf-dist/tex/context/base/mkxl/lang-hyp.lmt
new file mode 100644
index 00000000000..c356dbf3c47
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-hyp.lmt
@@ -0,0 +1,1806 @@
+if not modules then modules = { } end modules ['lang-hyp'] = {
+ version = 1.001,
+ comment = "companion to lang-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- In an automated workflow hypenation of long titles can be somewhat problematic
+-- especially when demands conflict. For that reason I played a bit with a Lua based
+-- variant of the traditional hyphenation machinery. This mechanism has been extended
+-- several times in projects, of which a good description can be found in TUGboat,
+-- Volume 27 (2006), No. 2 — Proceedings of EuroTEX2006: Automatic non-standard
+-- hyphenation in OpenOffice.org by László Németh.
+--
+-- Being the result of two days experimenting the following implementation is probably
+-- not completely okay yet. If there is demand I might add some more features and plugs.
+-- The performance is quite okay but can probably improved a bit, although this is not
+-- the most critital code. For instance, on a metafun manual run the overhead is about
+-- 0.3 seconds on 19 seconds which is not that bad.
+--
+-- In the procecess of wrapping up (for the ctx conference proceedings) I cleaned up
+-- and extended the code a bit. It can be used in production.
+--
+-- . a l g o r i t h m .
+-- 4l1g4
+-- l g o3
+-- 1g o
+-- 2i t h
+-- 4h1m
+-- ---------------------
+-- 4 1 4 3 2 0 4 1
+-- a l-g o-r i t h-m
+
+-- . a s s z o n n y a l .
+-- s1s z/sz=sz,1,3
+-- n1n y/ny=ny,1,3
+-- -----------------------
+-- 0 1 0 0 0 1 0 0 0/sz=sz,2,3,ny=ny,6,3
+-- a s-s z o n-n y a l/sz=sz,2,3,ny=ny,6,3
+--
+-- ab1cd/ef=gh,2,2 : acd - efd (pattern/replacement,start,length
+--
+-- todo : support hjcodes (<32 == length) like luatex does now (no need/demand so far)
+-- maybe : support hyphenation over range (can alsready be done using attributes/language)
+-- maybe : reset dictionary.hyphenated when a pattern is added and/or forced reset option
+-- todo : check subtypes (because they have subtle meanings in the line breaking)
+--
+-- word start (in tex engine):
+--
+-- boundary : yes when wordboundary
+-- hlist : when hyphenationbounds 1 or 3
+-- vlist : when hyphenationbounds 1 or 3
+-- rule : when hyphenationbounds 1 or 3
+-- dir : when hyphenationbounds 1 or 3
+-- whatsit : when hyphenationbounds 1 or 3
+-- glue : yes
+-- math : skipped
+-- glyph : exhyphenchar (one only) : yes (so no -- ---)
+-- otherwise : yes
+--
+-- word end (in tex engine):
+--
+-- boundary : yes
+-- glyph : yes when different language
+-- glue : yes
+-- penalty : yes
+-- kern : yes when not italic (for some historic reason)
+-- hlist : when hyphenationbounds 2 or 3
+-- vlist : when hyphenationbounds 2 or 3
+-- rule : when hyphenationbounds 2 or 3
+-- dir : when hyphenationbounds 2 or 3
+-- whatsit : when hyphenationbounds 2 or 3
+-- ins : when hyphenationbounds 2 or 3
+-- adjust : when hyphenationbounds 2 or 3
+
+local type, rawget, rawset, tonumber, next = type, rawget, rawset, tonumber, next
+
+local P, R, S, Cg, Cf, Ct, Cc, C, Carg, Cs = lpeg.P, lpeg.R, lpeg.S, lpeg.Cg, lpeg.Cf, lpeg.Ct, lpeg.Cc, lpeg.C, lpeg.Carg, lpeg.Cs
+local lpegmatch = lpeg.match
+
+local context = context
+
+local concat = table.concat
+local insert = table.insert
+local remove = table.remove
+local formatters = string.formatters
+local utfchar = utf.char
+local utfbyte = utf.byte
+
+if not characters then
+ require("char-ini")
+end
+
+local setmetatableindex = table.setmetatableindex
+
+-- \enabletrackers[hyphenator.steps=silent] will not write to the terminal
+
+local trace_steps = false trackers.register("hyphenator.steps", function(v) trace_steps = v end)
+local trace_visualize = false trackers.register("hyphenator.visualize",function(v) trace_visualize = v end)
+
+local report = logs.reporter("hyphenator")
+
+local implement = interfaces and interfaces.implement or function() end
+
+languages = languages or { }
+local hyphenators = languages.hyphenators or { }
+languages.hyphenators = hyphenators
+local traditional = hyphenators.traditional or { }
+hyphenators.traditional = traditional
+
+local dictionaries = setmetatableindex(function(t,k)
+ local v = {
+ patterns = { },
+ hyphenated = { },
+ specials = { },
+ exceptions = { },
+ loaded = false,
+ }
+ t[k] = v
+ return v
+end)
+
+hyphenators.dictionaries = dictionaries
+
+local character = lpeg.patterns.utf8character
+local digit = R("09")
+local weight = digit/tonumber + Cc(0)
+local fence = P(".")
+local hyphen = P("-")
+local space = P(" ")
+local char = character - space
+local validcharacter = (character - S("./"))
+local keycharacter = character - S("/")
+----- basepart = Ct( (Cc(0) * fence)^-1 * (weight * validcharacter)^1 * weight * (fence * Cc(0))^-1)
+local specpart = (P("/") * Cf ( Ct("") *
+ Cg ( Cc("before") * C((1-P("="))^1) * P("=") ) *
+ Cg ( Cc("after") * C((1-P(","))^1) ) *
+ ( P(",") *
+ Cg ( Cc("start") * ((1-P(","))^1/tonumber) * P(",") ) *
+ Cg ( Cc("length") * ((1-P(-1) )^1/tonumber) )
+ )^-1
+ , rawset))^-1
+
+local make_hashkey_p = Cs((digit/"" + keycharacter)^1)
+----- make_pattern_p = basepart * specpart
+local make_hashkey_e = Cs((hyphen/"" + keycharacter)^1)
+local make_pattern_e = Ct(P(char) * (hyphen * Cc(true) * P(char) + P(char) * Cc(false))^1) -- catch . and char after -
+
+-- local make_hashkey_c = Cs((digit + keycharacter/"")^1)
+-- local make_pattern_c = Ct((P(1)/tonumber)^1)
+
+-- local cache = setmetatableindex(function(t,k)
+-- local n = lpegmatch(make_hashkey_c,k)
+-- local v = lpegmatch(make_pattern_c,n)
+-- t[k] = v
+-- return v
+-- end)
+--
+-- local weight_n = digit + Cc("0")
+-- local basepart_n = Cs( (Cc("0") * fence)^-1 * (weight * validcharacter)^1 * weight * (fence * Cc("0"))^-1) / cache
+-- local make_pattern_n = basepart_n * specpart
+
+local make_pattern_c = Ct((P(1)/tonumber)^1)
+
+-- us + nl: 17664 entries -> 827 unique (saves some 3M)
+
+local cache = setmetatableindex(function(t,k)
+ local v = lpegmatch(make_pattern_c,k)
+ t[k] = v
+ return v
+end)
+
+local weight_n = digit + Cc("0")
+local fence_n = fence / "0"
+local char_n = validcharacter / ""
+local basepart_n = Cs(fence_n^-1 * (weight_n * char_n)^1 * weight_n * fence_n^-1) / cache
+local make_pattern_n = basepart_n * specpart
+
+local function register_pattern(patterns,specials,str,specification)
+ local k = lpegmatch(make_hashkey_p,str)
+ -- local v1, v2 = lpegmatch(make_pattern_p,str)
+ local v1, v2 = lpegmatch(make_pattern_n,str)
+ patterns[k] = v1 -- is this key still ok for complex patterns
+ if specification then
+ specials[k] = specification
+ elseif v2 then
+ specials[k] = v2
+ end
+end
+
+local function unregister_pattern(patterns,specials,str)
+ local k = lpegmatch(make_hashkey_p,str)
+ patterns[k] = nil
+ specials[k] = nil
+end
+
+local p_lower = lpeg.patterns.utf8lower
+
+local function register_exception(exceptions,str,specification)
+ local l = lpegmatch(p_lower,str)
+ local k = lpegmatch(make_hashkey_e,l)
+ local v = lpegmatch(make_pattern_e,l)
+ exceptions[k] = v
+end
+
+local p_pattern = ((Carg(1) * Carg(2) * C(char^1)) / register_pattern + 1)^1
+local p_exception = ((Carg(1) * C(char^1)) / register_exception + 1)^1
+local p_split = Ct(C(character)^1)
+
+function traditional.loadpatterns(language,filename)
+ local dictionary = dictionaries[language]
+ if not dictionary.loaded then
+ if not filename or filename == "" then
+ filename = "lang-" .. language
+ end
+ filename = file.addsuffix(filename,"lua")
+ local fullname = resolvers.findfile(filename)
+ if fullname and fullname ~= "" then
+ local specification = dofile(fullname)
+ if specification then
+ local patterns = specification.patterns
+ if patterns then
+ local data = patterns.data
+ if data and data ~= "" then
+ lpegmatch(p_pattern,data,1,dictionary.patterns,dictionary.specials)
+ end
+ end
+ local exceptions = specification.exceptions
+ if exceptions then
+ local data = exceptions.data
+ if data and data ~= "" then
+ lpegmatch(p_exception,data,1,dictionary.exceptions)
+ end
+ end
+ end
+ end
+ dictionary.loaded = true
+ end
+ return dictionary
+end
+
+local lcchars = characters.lcchars
+local uccodes = characters.uccodes
+local categories = characters.categories
+local nofwords = 0
+local nofhashed = 0
+
+local steps = nil
+local f_show = formatters["%w%s"]
+
+local function show_log()
+ if trace_steps == true then
+ report()
+ local w = #steps[1][1]
+ for i=1,#steps do
+ local s = steps[i]
+ report("%s%w%S %S",s[1],w - #s[1] + 3,s[2],s[3] or "")
+ end
+ report()
+ end
+end
+
+local function show_1(wsplit)
+ local u = concat(wsplit," ")
+ steps = { { f_show(0,u), f_show(0,u) } }
+end
+
+local function show_2(c,m,wsplit,done,i,spec)
+ local s = lpegmatch(p_split,c)
+ local t = { }
+ local n = #m
+ local w = #wsplit
+ for j=1,n do
+ t[#t+1] = m[j]
+ t[#t+1] = s[j]
+ end
+ local m = 2*i-2
+ local l = #t
+ local s = spec and table.sequenced(spec) or ""
+ if m == 0 then
+ steps[#steps+1] = { f_show(m, concat(t,"",2)), f_show(1,concat(done," ",2,#done),s) }
+ elseif i+1 == w then
+ steps[#steps+1] = { f_show(m-1,concat(t,"",1,#t-1)), f_show(1,concat(done," ",2,#done),s) }
+ else
+ steps[#steps+1] = { f_show(m-1,concat(t)), f_show(1,concat(done," ",2,#done),s) }
+ end
+end
+
+local function show_3(wsplit,done)
+ local t = { }
+ local h = { }
+ local n = #wsplit
+ for i=1,n do
+ local w = wsplit[i]
+ if i > 1 then
+ local d = done[i]
+ t[#t+1] = i > 2 and d % 2 == 1 and "-" or " "
+ h[#h+1] = d
+ end
+ t[#t+1] = w
+ h[#h+1] = w
+ end
+ steps[#steps+1] = { f_show(0,concat(h)), f_show(0,concat(t)) }
+ show_log()
+end
+
+local function show_4(wsplit,done)
+ steps = { { concat(wsplit," ") } }
+ show_log()
+end
+
+function traditional.lasttrace()
+ return steps
+end
+
+-- We could reuse the w table but as we cache the resolved words there is not much gain in
+-- that complication.
+--
+-- Beware: word can be a table and when n is passed to we can assume reuse so we need to
+-- honor that n then.
+--
+-- todo: a fast variant for tex ... less lookups (we could check is dictionary has changed)
+-- ... although due to caching the already done words, we don't do much here
+
+local function hyphenate(dictionary,word,n) -- odd is okay
+ nofwords = nofwords + 1
+ local hyphenated = dictionary.hyphenated
+ local isstring = type(word) == "string"
+ if isstring then
+ local done = hyphenated[word]
+ if done ~= nil then
+ return done
+ end
+ elseif n then
+ local done = hyphenated[concat(word,"",1,n)]
+ if done ~= nil then
+ return done
+ end
+ else
+ local done = hyphenated[concat(word)]
+ if done ~= nil then
+ return done
+ end
+ end
+ local key
+ if isstring then
+ key = word
+ word = lpegmatch(p_split,word)
+ if not n then
+ n = #word
+ end
+ else
+ if not n then
+ n = #word
+ end
+ key = concat(word,"",1,n)
+ end
+ local l = 1
+ local w = { "." }
+ -- local d = dictionary.codehash
+ for i=1,n do
+ local c = word[i]
+ -- l = l + (d[c] or 1)
+ l = l + 1
+ w[l] = lcchars[c] or c
+ end
+ l = l + 1
+ w[l] = "."
+ local c = concat(w,"",2,l-1)
+ --
+ local done = hyphenated[c]
+ if done ~= nil then
+ hyphenated[key] = done
+ nofhashed = nofhashed + 1
+ return done
+ end
+ --
+ local exceptions = dictionary.exceptions
+ local exception = exceptions[c]
+ if exception then
+ if trace_steps then
+ show_4(w,exception)
+ end
+ hyphenated[key] = exception
+ nofhashed = nofhashed + 1
+ return exception
+ end
+ --
+ if trace_steps then
+ show_1(w)
+ end
+ --
+ local specials = dictionary.specials
+ local patterns = dictionary.patterns
+ --
+ local spec
+ for i=1,l do
+ for j=i,l do
+ local c = concat(w,"",i,j)
+ local m = patterns[c]
+ if m then
+ local s = specials[c]
+ if not done then
+ done = { }
+ spec = nil
+ -- the string that we resolve has explicit fences (.) so done starts at
+ -- the first fence and runs upto the last one so we need one slot less
+ for i=1,l do
+ done[i] = 0
+ end
+ end
+ -- we run over the pattern that always has a (zero) value for each character
+ -- plus one more as we look at both sides
+ for k=1,#m do
+ local new = m[k]
+ if not new then
+ break
+ elseif new == true then
+ report("fatal error")
+ break
+ elseif new > 0 then
+ local pos = i + k - 1
+ local old = done[pos]
+ if not old then
+ -- break ?
+ elseif new > old then
+ done[pos] = new
+ if s then
+ local b = i + (s.start or 1) - 1
+ if b > 0 then
+ local e = b + (s.length or 2) - 1
+ if e > 0 then
+ if pos >= b and pos <= e then
+ if spec then
+ spec[pos] = { s, k - 1 }
+ else
+ spec = { [pos] = { s, k - 1 } }
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if trace_steps and done then
+ show_2(c,m,w,done,i,s)
+ end
+ end
+ end
+ end
+ if trace_steps and done then
+ show_3(w,done)
+ end
+ if done then
+ local okay = false
+ for i=3,#done do
+ if done[i] % 2 == 1 then
+ done[i-2] = spec and spec[i] or true
+ okay = true
+ else
+ done[i-2] = false
+ end
+ end
+ if okay then
+ done[#done] = nil
+ done[#done] = nil
+ else
+ done = false
+ end
+ else
+ done = false
+ end
+ hyphenated[key] = done
+ nofhashed = nofhashed + 1
+ return done
+end
+
+function traditional.gettrace(language,word)
+ if not word or word == "" then
+ return
+ end
+ local dictionary = dictionaries[language]
+ if dictionary then
+ local hyphenated = dictionary.hyphenated
+ hyphenated[word] = nil
+ hyphenate(dictionary,word)
+ return steps
+ end
+end
+
+local methods = setmetatableindex(function(t,k) local v = hyphenate t[k] = v return v end)
+
+function traditional.installmethod(name,f)
+ if rawget(methods,name) then
+ report("overloading %a is not permitted",name)
+ else
+ methods[name] = f
+ end
+end
+
+local s_detail_1 = "-"
+local f_detail_2 = formatters["%s-%s"]
+local f_detail_3 = formatters["{%s}{%s}{}"]
+local f_detail_4 = formatters["{%s%s}{%s%s}{%s}"]
+
+function traditional.injecthyphens(dictionary,word,specification)
+ if not word then
+ return false
+ end
+ if not specification then
+ return word
+ end
+ local hyphens = hyphenate(dictionary,word)
+ if not hyphens then
+ return word
+ end
+
+ -- the following code is similar to code later on but here we have strings while there
+ -- we have hyphen specs
+
+ local word = lpegmatch(p_split,word)
+ local size = #word
+
+ local leftmin = specification.leftcharmin or 2
+ local rightmin = size - (specification.rightcharmin or leftmin)
+ local leftchar = specification.leftchar
+ local rightchar = specification.rightchar
+
+ local result = { }
+ local rsize = 0
+ local position = 1
+
+ while position <= size do
+ if position >= leftmin and position <= rightmin then
+ local hyphen = hyphens[position]
+ if not hyphen then
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ elseif hyphen == true then
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ rsize = rsize + 1
+ if leftchar and rightchar then
+ result[rsize] = f_detail_3(rightchar,leftchar)
+ else
+ result[rsize] = s_detail_1
+ end
+ position = position + 1
+ else
+ local o, h = hyphen[2]
+ if o then
+ h = hyphen[1]
+ else
+ h = hyphen
+ o = 1
+ end
+ local b = position - o + (h.start or 1)
+ local e = b + (h.length or 2) - 1
+ if b > 0 and e >= b then
+ for i=1,b-position do
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ end
+ rsize = rsize + 1
+ if leftchar and rightchar then
+ result[rsize] = f_detail_4(h.before,rightchar,leftchar,h.after,concat(word,"",b,e))
+ else
+ result[rsize] = f_detail_2(h.before,h.after)
+ end
+ position = e + 1
+ else
+ -- error
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ end
+ end
+ else
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ end
+ end
+ return concat(result)
+end
+
+do
+
+ local word = C((1-space)^1)
+ local spaces = space^1
+
+ local u_pattern = (Carg(1) * Carg(2) * word / unregister_pattern + spaces)^1
+ local r_pattern = (Carg(1) * Carg(2) * word * Carg(3) / register_pattern + spaces)^1
+ local e_pattern = (Carg(1) * word / register_exception + spaces)^1
+
+ function traditional.registerpattern(language,str,specification)
+ local dictionary = dictionaries[language]
+ if specification == false then
+ lpegmatch(u_pattern,str,1,dictionary.patterns,dictionary.specials)
+ -- unregister_pattern(dictionary.patterns,dictionary.specials,str)
+ else
+ lpegmatch(r_pattern,str,1,dictionary.patterns,dictionary.specials,type(specification) == "table" and specification or false)
+ -- register_pattern(dictionary.patterns,dictionary.specials,str,specification)
+ end
+ end
+
+ function traditional.registerexception(language,str)
+ lpegmatch(e_pattern,str,1,dictionaries[language].exceptions)
+ end
+
+end
+
+-- todo: unicodes or utfhash ?
+
+if context then
+
+ local nodecodes = nodes.nodecodes
+ local disccodes = nodes.disccodes
+
+ local glyph_code = nodecodes.glyph
+ local disc_code = nodecodes.disc
+ local math_code = nodecodes.math
+ local hlist_code = nodecodes.hlist
+
+ local automaticdisc_code = disccodes.automatic
+ local regulardisc_code = disccodes.regular
+
+ local nuts = nodes.nuts
+ local tonode = nodes.tonode
+ local nodepool = nuts.pool
+
+ local new_disc = nodepool.disc
+ local new_penalty = nodepool.penalty
+
+ local getfield = nuts.getfield
+ local getfont = nuts.getfont
+ local getid = nuts.getid
+ local getattr = nuts.getattr
+ local getnext = nuts.getnext
+ local getprev = nuts.getprev
+ local getsubtype = nuts.getsubtype
+ local getlist = nuts.getlist
+ local getlanguage = nuts.getlanguage
+ local getattrlist = nuts.getattrlist
+ local setattrlist = nuts.setattrlist
+ local isglyph = nuts.isglyph
+ local ischar = nuts.ischar
+
+ local setchar = nuts.setchar
+ local setdisc = nuts.setdisc
+ local setlink = nuts.setlink
+ local setprev = nuts.setprev
+ local setnext = nuts.setnext
+
+ local insert_before = nuts.insert_before
+ local insert_after = nuts.insert_after
+ local copy_node = nuts.copy
+ local copy_list = nuts.copy_list
+ local remove_node = nuts.remove
+ local end_of_math = nuts.end_of_math
+ local node_tail = nuts.tail
+
+ local nexthlist = nuts.traversers.hlist
+ local nextdisc = nuts.traversers.disc
+
+ local setcolor = nodes.tracers.colors.set
+
+ local variables = interfaces.variables
+ local v_reset = variables.reset
+ local v_yes = variables.yes
+ local v_word = variables.word
+ local v_all = variables.all
+
+ local settings_to_array = utilities.parsers.settings_to_array
+
+ local unsetvalue = attributes.unsetvalue
+ local texsetattribute = tex.setattribute
+
+ local prehyphenchar = language.prehyphenchar
+ local posthyphenchar = language.posthyphenchar
+ local preexhyphenchar = language.preexhyphenchar
+ local postexhyphenchar = language.postexhyphenchar
+
+ local a_hyphenation = attributes.private("hyphenation")
+
+ local interwordpenalty = 5000
+
+ function traditional.loadpatterns(language)
+ return dictionaries[language]
+ end
+
+ -- for the moment we use an independent data structure
+
+ setmetatableindex(dictionaries,function(t,k)
+ if type(k) == "string" then
+ -- this will force a load if not yet loaded (we need a nicer way) for the moment
+ -- that will do (nneeded for examples that register a pattern specification
+ languages.getnumber(k)
+ end
+ local specification = languages.getdata(k)
+ local dictionary = {
+ patterns = { },
+ exceptions = { },
+ hyphenated = { },
+ specials = { },
+ instance = false,
+ characters = { },
+ unicodes = { },
+ }
+ if specification then
+ local resources = specification.resources
+ if resources then
+ local characters = dictionary.characters or { }
+ local unicodes = dictionary.unicodes or { }
+ for i=1,#resources do
+ local r = resources[i]
+ if not r.in_dictionary then
+ r.in_dictionary = true
+ local patterns = r.patterns
+ if patterns then
+ local data = patterns.data
+ if data then
+ -- regular patterns
+ lpegmatch(p_pattern,data,1,dictionary.patterns,dictionary.specials)
+ end
+ local extra = patterns.extra
+ if extra then
+ -- special patterns
+ lpegmatch(p_pattern,extra,1,dictionary.patterns,dictionary.specials)
+ end
+ end
+ local exceptions = r.exceptions
+ if exceptions then
+ local data = exceptions.data
+ if data and data ~= "" then
+ lpegmatch(p_exception,data,1,dictionary.exceptions)
+ end
+ end
+ local usedchars = lpegmatch(p_split,patterns.characters)
+ for i=1,#usedchars do
+ local char = usedchars[i]
+ local code = utfbyte(char)
+ local upper = uccodes[code]
+ characters[char] = code
+ unicodes [code] = char
+ if type(upper) == "table" then
+ for i=1,#upper do
+ local u = upper[i]
+ unicodes[u] = utfchar(u)
+ end
+ else
+ unicodes[upper] = utfchar(upper)
+ end
+ end
+ end
+ end
+ dictionary.characters = characters
+ dictionary.unicodes = unicodes
+ setmetatableindex(characters,function(t,k) local v = k and utfbyte(k) t[k] = v return v end)
+ end
+ t[specification.number] = dictionary
+ dictionary.instance = specification.instance -- needed for hyphenchars
+ end
+ t[k] = dictionary
+ return dictionary
+ end)
+
+ -- Beware: left and right min doesn't mean that in a 1 mmm hsize there can be snippets
+ -- with less characters than either of them! This could be an option but such a narrow
+ -- hsize doesn't make sense anyway.
+
+ -- We assume that featuresets are defined global ... local definitions (also mid paragraph)
+ -- make not much sense anyway. For the moment we assume no predefined sets so we don't need
+ -- to store them. Nor do we need to hash them in order to save space ... no sane user will
+ -- define many of them.
+
+ local featuresets = hyphenators.featuresets or { }
+ hyphenators.featuresets = featuresets
+
+ storage.shared.noflanguagesfeaturesets = storage.shared.noflanguagesfeaturesets or 0
+
+ local noffeaturesets = storage.shared.noflanguagesfeaturesets
+
+ storage.register("languages/hyphenators/featuresets",featuresets,"languages.hyphenators.featuresets")
+
+ ----- hash = table.sequenced(featureset,",") -- no need now
+
+ local function register(name,featureset)
+ noffeaturesets = noffeaturesets + 1
+ featureset.attribute = noffeaturesets
+ featuresets[noffeaturesets] = featureset -- access by attribute
+ featuresets[name] = featureset -- access by name
+ storage.shared.noflanguagesfeaturesets = noffeaturesets
+ return noffeaturesets
+ end
+
+ local function makeset(...)
+ -- a bit overkill, supporting variants but who cares
+ local set = { }
+ for i=1,select("#",...) do
+ local list = select(i,...)
+ local kind = type(list)
+ local used = nil
+ if kind == "string" then
+ if list == v_all then
+ -- not ok ... now all get ignored
+ return setmetatableindex(function(t,k) local v = utfchar(k) t[k] = v return v end)
+ elseif list ~= "" then
+ used = lpegmatch(p_split,list)
+ set = set or { }
+ for i=1,#used do
+ local char = used[i]
+ set[utfbyte(char)] = char
+ end
+ end
+ elseif kind == "table" then
+ if next(list) then
+ set = set or { }
+ for byte, char in next, list do
+ set[byte] = char == true and utfchar(byte) or char
+ end
+ elseif #list > 0 then
+ set = set or { }
+ for i=1,#list do
+ local l = list[i]
+ if type(l) == "number" then
+ set[l] = utfchar(l)
+ else
+ set[utfbyte(l)] = l
+ end
+ end
+ end
+ end
+ end
+ return set
+ end
+
+ -- category pd (tex also sees --- and -- as hyphens but do we really want that
+
+ local defaulthyphens = {
+ [0x002D] = true, -- HYPHEN-MINUS
+ [0x00AD] = 0x002D, -- SOFT HYPHEN (active in ConTeXt)
+ -- [0x058A] = true, -- ARMENIAN HYPHEN
+ -- [0x1400] = true, -- CANADIAN SYLLABICS HYPHEN
+ -- [0x1806] = true, -- MONGOLIAN TODO SOFT HYPHEN
+ [0x2010] = true, -- HYPHEN
+ -- [0x2011] = true, -- NON-BREAKING HYPHEN
+ -- [0x2012] = true, -- FIGURE DASH
+ [0x2013] = true, -- EN DASH
+ [0x2014] = true, -- EM DASH
+ -- [0x2015] = true, -- HORIZONTAL BAR
+ -- [0x2027] = true, -- HYPHENATION POINT
+ -- [0x2E17] = true, -- DOUBLE OBLIQUE HYPHEN
+ -- [0x2E1A] = true, -- HYPHEN WITH DIAERESIS
+ -- [0x2E3A] = true, -- TWO-EM DASH
+ -- [0x2E3B] = true, -- THREE-EM DASH
+ -- [0x2E40] = true, -- DOUBLE HYPHEN
+ -- [0x301C] = true, -- WAVE DASH
+ -- [0x3030] = true, -- WAVY DASH
+ -- [0x30A0] = true, -- KATAKANA-HIRAGANA DOUBLE HYPHEN
+ -- [0xFE31] = true, -- PRESENTATION FORM FOR VERTICAL EM DASH
+ -- [0xFE32] = true, -- PRESENTATION FORM FOR VERTICAL EN DASH
+ -- [0xFE58] = true, -- SMALL EM DASH
+ -- [0xFE63] = true, -- SMALL HYPHEN-MINUS
+ -- [0xFF0D] = true, -- FULLWIDTH HYPHEN-MINUS
+ }
+
+ local defaultjoiners = {
+ [0x200C] = true, -- nzwj
+ [0x200D] = true, -- zwj
+ }
+
+ local function somehyphenchar(c)
+ c = tonumber(c)
+ return c ~= 0 and c or nil
+ end
+
+ local function definefeatures(name,featureset)
+ local extrachars = featureset.characters -- "[]()"
+ local hyphenchars = featureset.hyphens
+ local joinerchars = featureset.joiners
+ local alternative = featureset.alternative
+ local rightwordmin = tonumber(featureset.rightwordmin)
+ local charmin = tonumber(featureset.charmin) -- luatex now also has hyphenationmin
+ local leftcharmin = tonumber(featureset.leftcharmin)
+ local rightcharmin = tonumber(featureset.rightcharmin)
+ local leftchar = somehyphenchar(featureset.leftchar)
+ local rightchar = somehyphenchar(featureset.rightchar)
+ local rightchars = featureset.rightchars
+local rightedge = featureset.rightedge
+local autohyphen = v_yes -- featureset.autohyphen -- insert disc
+local hyphenonly = v_yes -- featureset.hyphenonly -- don't hyphenate around
+ rightchars = rightchars == v_word and true or tonumber(rightchars)
+ joinerchars = joinerchars == v_yes and defaultjoiners or joinerchars -- table
+ hyphenchars = hyphenchars == v_yes and defaulthyphens or hyphenchars -- table
+ -- not yet ok: extrachars have to be ignored so it cannot be all)
+ featureset.extrachars = makeset(joinerchars or "",extrachars or "")
+ featureset.hyphenchars = makeset(hyphenchars or "")
+ featureset.alternative = alternative or "hyphenate"
+ featureset.rightwordmin = rightwordmin and rightwordmin > 0 and rightwordmin or nil
+ featureset.charmin = charmin and charmin > 0 and charmin or nil
+ featureset.leftcharmin = leftcharmin and leftcharmin > 0 and leftcharmin or nil
+ featureset.rightcharmin = rightcharmin and rightcharmin > 0 and rightcharmin or nil
+ featureset.rightchars = rightchars
+ featureset.leftchar = leftchar
+ featureset.rightchar = rightchar
+ -- featureset.strict = rightedge == "tex"
+featureset.autohyphen = autohyphen == v_yes
+featureset.hyphenonly = hyphenonly == v_yes
+ return register(name,featureset)
+ end
+
+ local function setfeatures(n)
+ if not n or n == v_reset then
+ n = false
+ else
+ local f = featuresets[n]
+ if not f and type(n) == "string" then
+ local t = settings_to_array(n)
+ local s = { }
+ for i=1,#t do
+ local ti = t[i]
+ local fs = featuresets[ti]
+ if fs then
+ for k, v in next, fs do
+ s[k] = v
+ end
+ end
+ end
+ n = register(n,s)
+ else
+ n = f and f.attribute
+ end
+ end
+ texsetattribute(a_hyphenation,n or unsetvalue)
+ end
+
+ traditional.definefeatures = definefeatures
+ traditional.setfeatures = setfeatures
+
+ implement {
+ name = "definehyphenationfeatures",
+ actions = definefeatures,
+ arguments = {
+ "string",
+ {
+ { "characters" },
+ { "hyphens" },
+ { "joiners" },
+ { "rightchars" },
+ { "rightwordmin", "integer" },
+ { "charmin", "integer" },
+ { "leftcharmin", "integer" },
+ { "rightcharmin", "integer" },
+ { "leftchar", "integer" },
+ { "rightchar", "integer" },
+ { "alternative" },
+ { "rightedge" },
+ }
+ }
+ }
+
+ implement {
+ name = "sethyphenationfeatures",
+ actions = setfeatures,
+ arguments = "string"
+ }
+
+ implement {
+ name = "registerhyphenationpattern",
+ actions = traditional.registerpattern,
+ arguments = { "string", "string", "boolean" }
+ }
+
+ implement {
+ name = "registerhyphenationexception",
+ actions = traditional.registerexception,
+ arguments = "2 strings",
+ }
+
+ -- This is a relative large function with local variables and local functions. A previous
+ -- implementation had the functions outside but this is cleaner and as efficient. The test
+ -- runs 100 times over tufte.tex, knuth.tex, zapf.tex, ward.tex and darwin.tex in lower
+ -- and uppercase with a 1mm hsize.
+ --
+ -- language=0 language>0 4 | 3 * slower
+ --
+ -- tex 2.34 | 1.30 2.55 | 1.45 0.21 | 0.15
+ -- lua 2.42 | 1.38 3.30 | 1.84 0.88 | 0.46
+ --
+ -- Of course we have extra overhead (virtual Lua machine) but also we check attributes and
+ -- support specific local options). The test puts the typeset text in boxes and discards
+ -- it. If we also flush the runtime is 4.31|2.56 and 4.99|2.94 seconds so the relative
+ -- difference is (somehow) smaller. The test has 536 pages. There is a little bit of extra
+ -- overhead because we store the patterns in a different way.
+ --
+ -- As usual I will look for speedups. Some 0.01 seconds could be gained by sharing patterns
+ -- which is not impressive but it does save some 3M memory on this test. (Some optimizations
+ -- already brought the 3.30 seconds down to 3.14 but it all depends on aggressive caching.)
+
+ -- As we kick in the hyphenator before fonts get handled, we don't look at implicit (font)
+ -- kerns or ligatures.
+
+ local starttiming = statistics.starttiming
+ local stoptiming = statistics.stoptiming
+
+ -- local strictids = {
+ -- [nodecodes.hlist] = true,
+ -- [nodecodes.vlist] = true,
+ -- [nodecodes.rule] = true,
+ -- [nodecodes.dir] = true,
+ -- [nodecodes.whatsit] = true,
+ -- [nodecodes.ins] = true,
+ -- [nodecodes.adjust] = true,
+ --
+ -- [nodecodes.math] = true,
+ -- [nodecodes.disc] = true,
+ --
+ -- [nodecodes.accent] = true, -- never used in context
+ -- }
+
+ -- a lot of overhead when only one char
+
+ function traditional.hyphenate(head)
+
+ local first = head
+ local tail = nil
+ local last = nil
+ local current = first
+ local dictionary = nil
+ local instance = nil
+ local characters = nil
+ local unicodes = nil
+ local exhyphenchar = tex.exhyphenchar
+ local extrachars = nil
+ local hyphenchars = nil
+ local language = nil
+ local lastfont = nil
+ local start = nil
+ local stop = nil
+ local word = { } -- we reuse this table
+ local size = 0
+ local leftchar = false
+ local rightchar = false -- utfbyte("-")
+ local leftexchar = false
+ local rightexchar = false -- utfbyte("-")
+ local leftmin = 0
+ local rightmin = 0
+ local charmin = 1
+ local leftcharmin = nil
+ local rightcharmin = nil
+ ----- leftwordmin = nil
+ local rightwordmin = nil
+ local rightchars = nil
+ local leftchar = nil
+ local rightchar = nil
+ local attr = nil
+ local lastwordlast = nil
+ local hyphenated = hyphenate
+ ----- strict = nil
+ local exhyphenpenalty = tex.exhyphenpenalty
+ local hyphenpenalty = tex.hyphenpenalty
+ local autohyphen = false
+ local hyphenonly = false
+
+ -- We cannot use an 'enabled' boolean (false when no characters or extras) because we
+ -- can have plugins that set a characters metatable and so) ... it doesn't save much
+ -- anyway. Using (unicodes and unicodes[code]) and a nil table when no characters also
+ -- doesn't save much. So there not that much to gain for languages that don't hyphenate.
+ --
+ -- enabled = (unicodes and (next(unicodes) or getmetatable(unicodes)))
+ -- or (extrachars and next(extrachars))
+ --
+ -- This can be used to not add characters i.e. keep size 0 but then we need to check for
+ -- attributes that change it, which costs time too. Not much to gain there.
+
+ starttiming(traditional)
+
+ local function insertpenalty()
+ local p = new_penalty(interwordpenalty)
+ setattrlist(p,last)
+ if trace_visualize then
+ nuts.setvisual(p,"penalty")
+ end
+ last = getprev(last)
+ first, last = insert_after(first,last,p)
+ end
+
+ local function synchronizefeatureset(a)
+ local f = a and featuresets[a]
+ if f then
+ hyphenated = methods[f.alternative or "hyphenate"]
+ extrachars = f.extrachars
+ hyphenchars = f.hyphenchars
+ rightwordmin = f.rightwordmin
+ charmin = f.charmin
+ leftcharmin = f.leftcharmin
+ rightcharmin = f.rightcharmin
+ leftchar = f.leftchar
+ rightchar = f.rightchar
+ -- strict = f.strict and strictids
+ rightchars = f.rightchars
+ autohyphen = f.autohyphen
+ hyphenonly = f.hyphenonly
+ if rightwordmin and rightwordmin > 0 and lastwordlast ~= rightwordmin then
+ -- so we can change mid paragraph but it's kind of unpredictable then
+ if not tail then
+ tail = node_tail(first)
+ end
+ last = tail
+ local inword = false
+ local count = 0
+ while last and rightwordmin > 0 do
+ local id = getid(last)
+ if id == glyph_code then
+ count = count + 1
+ inword = true
+ if trace_visualize then
+ setcolor(last,"darkgreen")
+ end
+ elseif inword then
+ inword = false
+ rightwordmin = rightwordmin - 1
+ if rightchars == true then
+ if rightwordmin > 0 then
+ insertpenalty()
+ end
+ elseif rightchars and count <= rightchars then
+ insertpenalty()
+ end
+ end
+ last = getprev(last)
+ end
+ lastwordlast = rightwordmin
+ end
+ if not charmin or charmin == 0 then
+ charmin = 1
+ end
+ else
+ hyphenated = methods.hyphenate
+ extrachars = false
+ hyphenchars = false
+ rightwordmin = false
+ charmin = 1
+ leftcharmin = false
+ rightcharmin = false
+ leftchar = false
+ rightchar = false
+ -- strict = false
+ autohyphen = false
+ hyphenonly = false
+ end
+
+ return a
+ end
+
+ local function flush(hyphens) -- todo: no need for result
+
+ local rightmin = size - rightmin
+ local result = { }
+ local rsize = 0
+ local position = 1
+
+ -- todo: remember last dics and don't go back to before that (plus message) ...
+ -- for simplicity we also assume that we don't start with a dics node
+ --
+ -- there can be a conflict: if we backtrack then we can end up in another disc
+ -- and get out of sync (dup chars and so)
+
+ while position <= size do
+ if position >= leftmin and position <= rightmin then
+ local hyphen = hyphens[position]
+ if not hyphen then
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ elseif hyphen == true then
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ rsize = rsize + 1
+ result[rsize] = true
+ position = position + 1
+ else
+ local o, h = hyphen[2]
+ if o then
+ -- { hyphen, offset)
+ h = hyphen[1]
+ else
+ -- hyphen
+ h = hyphen
+ o = 1
+ end
+ local b = position - o + (h.start or 1)
+ local e = b + (h.length or 2) - 1
+ if b > 0 and e >= b then
+ for i=1,b-position do
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ end
+ rsize = rsize + 1
+ result[rsize] = {
+ h.before or "", -- pre
+ h.after or "", -- post
+ concat(word,"",b,e), -- replace
+ h.right, -- optional after pre
+ h.left, -- optional before post
+ }
+ position = e + 1
+ else
+ -- error
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ end
+ end
+ else
+ rsize = rsize + 1
+ result[rsize] = word[position]
+ position = position + 1
+ end
+ end
+
+ local function serialize(replacement,leftchar,rightchar)
+ if not replacement then
+ return
+ elseif replacement == true then
+ local glyph = copy_node(stop)
+ setchar(glyph,leftchar or rightchar)
+ return glyph
+ end
+ local head = nil
+ local current = nil
+ if leftchar then
+ head = copy_node(stop)
+ current = head
+ setchar(head,leftchar)
+ end
+ local rsize = #replacement
+ if rsize == 1 then
+ local glyph = copy_node(stop)
+ setchar(glyph,characters[replacement])
+ if head then
+ insert_after(current,current,glyph)
+ else
+ head = glyph
+ end
+ current = glyph
+ elseif rsize > 0 then
+ local list = lpegmatch(p_split,replacement) -- this is an utf split (could be cached)
+ for i=1,#list do
+ local glyph = copy_node(stop)
+ setchar(glyph,characters[list[i]])
+ if head then
+ insert_after(current,current,glyph)
+ else
+ head = glyph
+ end
+ current = glyph
+ end
+ end
+ if rightchar then
+ local glyph = copy_node(stop)
+ insert_after(current,current,glyph)
+ setchar(glyph,rightchar)
+ end
+ return head
+ end
+
+ local current = start
+ local attrnode = start -- will be different, just the first char
+
+ for i=1,rsize do
+ local r = result[i]
+ if r == true then
+ local disc = new_disc()
+ local pre = nil
+ local post = nil
+ if rightchar then
+ pre = serialize(true,rightchar)
+ end
+ if leftchar then
+ post = serialize(true,leftchar)
+ end
+ setdisc(disc,pre,post,nil,regulardisc_code,hyphenpenalty)
+ if attrnode then
+ setattrlist(disc,attrnode)
+ end
+ -- could be a replace as well
+ insert_before(first,current,disc)
+ elseif type(r) == "table" then
+ local disc = new_disc()
+ local pre = r[1]
+ local post = r[2]
+ local replace = r[3]
+ local right = r[4] ~= false and rightchar
+ local left = r[5] ~= false and leftchar
+ if pre then
+ if pre ~= "" then
+ pre = serialize(pre,false,right)
+ else
+ pre = nil
+ end
+ end
+ if post then
+ if post ~= "" then
+ post = serialize(post,left,false)
+ else
+ post = nil
+ end
+ end
+ if replace then
+ if replace ~= "" then
+ replace = serialize(replace)
+ else
+ replace = nil
+ end
+ end
+ -- maybe regular code
+ setdisc(disc,pre,post,replace,regulardisc_code,hyphenpenalty)
+ if attrnode then
+ setattrlist(disc,attrnode)
+ end
+ insert_before(first,current,disc)
+ else
+ setchar(current,characters[r])
+ if i < rsize then
+ current = getnext(current)
+ end
+ end
+ end
+ if current and current ~= stop then
+ local current = getnext(current)
+ local last = getnext(stop)
+ while current ~= last do
+ first, current = remove_node(first,current,true)
+ end
+ end
+
+ end
+
+ local function inject(leftchar,rightchar,code,attrnode)
+ if first ~= current then
+ local disc = new_disc()
+ first, current, glyph = remove_node(first,current)
+ first, current = insert_before(first,current,disc)
+ if trace_visualize then
+ setcolor(glyph,"darkred") -- these get checked
+ setcolor(disc,"darkgreen") -- in the colorizer
+ end
+ local pre = nil
+ local post = nil
+ local replace = glyph
+ if leftchar and leftchar > 0 then
+ post = copy_node(glyph)
+ setchar(post,leftchar)
+ end
+ pre = copy_node(glyph)
+ setchar(pre,rightchar and rightchar > 0 and rightchar or code)
+ setdisc(disc,pre,post,replace,automaticdisc_code,hyphenpenalty) -- ex ?
+ if attrnode then
+ setattrlist(disc,attrnode)
+ end
+ end
+ return current
+ end
+
+ local function injectseries(current,last,next,attrnode)
+ local disc = new_disc()
+ local start = current
+ first, current = insert_before(first,current,disc)
+ setprev(start)
+ setnext(last)
+ if next then
+ setlink(current,next)
+ else
+ setnext(current)
+ end
+ local pre = copy_list(start)
+ local post = nil
+ local replace = start
+ setdisc(disc,pre,post,replace,automaticdisc_code,hyphenpenalty) -- ex ?
+ if attrnode then
+ setattrlist(disc,attrnode)
+ end
+ return current
+ end
+
+ local a = getattr(first,a_hyphenation)
+ if a ~= attr then
+ attr = synchronizefeatureset(a)
+ end
+
+ -- The first attribute in a word determines the way a word gets hyphenated and if
+ -- relevant, other properties are also set then. We could optimize for silly one-char
+ -- cases but it has no priority as the code is still not that much slower than the
+ -- native hyphenator and this variant also provides room for extensions.
+
+ local skipping = false
+
+ -- In "word word word." the sequences "word" and "." can be a different font!
+
+ while current and current ~= last do -- and current
+ local code, id = isglyph(current)
+ if code then
+ if skipping then
+ current = getnext(current)
+ else
+ local lang = getlanguage(current)
+ local font = getfont(current)
+ if lang ~= language or font ~= lastfont then
+ if dictionary and size > charmin and leftmin + rightmin <= size then
+ -- only german has many words starting with an uppercase character
+ if categories[word[1]] == "lu" and getfield(start,"uchyph") < 0 then
+ -- skip
+ else
+ local hyphens = hyphenated(dictionary,word,size)
+ if hyphens then
+ flush(hyphens)
+ end
+ end
+ end
+ lastfont = font
+ if language ~= lang and lang > 0 then
+ --
+ dictionary = dictionaries[lang]
+ instance = dictionary.instance
+ characters = dictionary.characters
+ unicodes = dictionary.unicodes
+ --
+ local a = getattr(current,a_hyphenation)
+ attr = synchronizefeatureset(a)
+ leftchar = leftchar or (instance and posthyphenchar (instance)) -- we can make this more
+ rightchar = rightchar or (instance and prehyphenchar (instance)) -- efficient if needed
+ leftexchar = (instance and preexhyphenchar (instance))
+ rightexchar = (instance and postexhyphenchar(instance))
+ leftmin = leftcharmin or getfield(current,"lhmin")
+ rightmin = rightcharmin or getfield(current,"rhmin")
+ if not leftchar or leftchar < 0 then
+ leftchar = false
+ end
+ if not rightchar or rightchar < 0 then
+ rightchar = false
+ end
+ --
+ local char = unicodes[code] or (extrachars and extrachars[code])
+ if char then
+ word[1] = char
+ size = 1
+ start = current
+ else
+ size = 0
+ end
+ else
+ size = 0
+ end
+ language = lang
+ elseif language <= 0 then
+ --
+ elseif size > 0 then
+ local char = unicodes[code] or (extrachars and extrachars[code])
+ if char then
+ size = size + 1
+ word[size] = char
+ elseif dictionary then
+ if not hyphenonly or code ~= exhyphenchar then
+ if size > charmin and leftmin + rightmin <= size then
+ if categories[word[1]] == "lu" and getfield(start,"uchyph") < 0 then
+ -- skip
+ else
+ local hyphens = hyphenated(dictionary,word,size)
+ if hyphens then
+ flush(hyphens)
+ end
+ end
+ end
+ end
+ size = 0
+ if code == exhyphenchar then -- normally the -
+ local next = getnext(current)
+ local last = current
+ local font = getfont(current)
+ while next and ischar(next,font) == code do
+ last = next
+ next = getnext(next)
+ end
+ if not autohyphen then
+ current = last
+ elseif current == last then
+ current = inject(leftexchar,rightexchar,code,current)
+ else
+ current = injectseries(current,last,next,current)
+ end
+ if hyphenonly then
+ skipping = true
+ end
+ elseif hyphenchars then
+ local char = hyphenchars[code]
+ if char == true then
+ char = code
+ end
+ if char then
+ current = inject(leftchar and char or nil,rightchar and char or nil,char,current)
+ end
+ end
+ end
+ else
+ local a = getattr(current,a_hyphenation)
+ if a ~= attr then
+ attr = synchronizefeatureset(a) -- influences extrachars
+ leftchar = leftchar or (instance and posthyphenchar (instance)) -- we can make this more
+ rightchar = rightchar or (instance and prehyphenchar (instance)) -- efficient if needed
+ leftexchar = (instance and preexhyphenchar (instance))
+ rightexchar = (instance and postexhyphenchar(instance))
+ leftmin = leftcharmin or getfield(current,"lhmin")
+ rightmin = rightcharmin or getfield(current,"rhmin")
+ if not leftchar or leftchar < 0 then
+ leftchar = false
+ end
+ if not rightchar or rightchar < 0 then
+ rightchar = false
+ end
+ end
+ --
+ local char = unicodes[code] or (extrachars and extrachars[code])
+ if char then
+ word[1] = char
+ size = 1
+ start = current
+ end
+ end
+ stop = current
+ current = getnext(current)
+ end
+ else
+ if skipping then
+ skipping = false
+ end
+ if id == disc_code then
+ size = 0
+ current = getnext(current)
+ if hyphenonly then
+ skipping = true
+ end
+ -- elseif strict and strict[id] then
+ -- current = id == math_code and getnext(end_of_math(current)) or getnext(current)
+ -- size = 0
+ else
+ current = id == math_code and getnext(end_of_math(current)) or getnext(current)
+ end
+ if size > 0 then
+ if dictionary and size > charmin and leftmin + rightmin <= size then
+ if categories[word[1]] == "lu" and getfield(start,"uchyph") < 0 then
+ -- skip
+ else
+ local hyphens = hyphenated(dictionary,word,size)
+ if hyphens then
+ flush(hyphens)
+ end
+ end
+ end
+ size = 0
+ end
+ end
+ end
+ -- we can have quit due to last so we need to flush the last seen word, we could move
+ -- this in the loop and test for current but ... messy
+ if dictionary and size > charmin and leftmin + rightmin <= size then
+ if categories[word[1]] == "lu" and getfield(start,"uchyph") < 0 then
+ -- skip
+ else
+ local hyphens = hyphenated(dictionary,word,size)
+ if hyphens then
+ flush(hyphens)
+ end
+ end
+ end
+
+ stoptiming(traditional)
+
+ return head
+ end
+
+ statistics.register("hyphenation",function()
+ if nofwords > 0 or statistics.elapsed(traditional) > 0 then
+ return string.format("%s words hyphenated, %s unique, used time %s",
+ nofwords,nofhashed,statistics.elapsedseconds(traditional) or 0)
+ end
+ end)
+
+ local texmethod = "builders.kernel.hyphenation"
+ local oldmethod = texmethod
+ local newmethod = texmethod
+
+ -- local newmethod = "languages.hyphenators.traditional.hyphenate"
+ --
+ -- nodes.tasks.prependaction("processors","words",newmethod)
+ -- nodes.tasks.disableaction("processors",oldmethod)
+ --
+ -- nodes.tasks.replaceaction("processors","words",oldmethod,newmethod)
+
+ -- \enabledirectives[hyphenators.method=traditional]
+ -- \enabledirectives[hyphenators.method=builtin]
+
+ -- push / pop ? check first attribute
+
+ -- local replaceaction = nodes.tasks.replaceaction -- no longer overload this way (too many local switches)
+
+ local hyphenate = language.hyphenate
+ local hyphenating = nuts.hyphenating
+ local methods = { }
+ local usedmethod = false
+ local stack = { }
+
+ local original = hyphenating and
+ function(head)
+ return (hyphenating(head))
+ end
+ or
+ function(head)
+ hyphenate(tonode(head))
+ return head -- a nut
+ end
+
+ -- local has_language = language.has_language
+ --
+ -- local function original(head) -- kernel.hyphenation(head)
+ -- local h = tonode(head)
+ -- if has_language(h) then
+ -- hyphenate(h)
+ -- end
+ -- return head
+ -- end
+
+ local getcount = tex.getcount
+
+ hyphenators.methods = methods
+ local optimize = false
+
+ directives.register("hyphenator.optimize", function(v) optimize = v end)
+
+ function hyphenators.handler(head,groupcode)
+ if usedmethod then
+ if optimize and (groupcode == "hbox" or groupcode == "adjusted_hbox") then
+ if getcount("hyphenstate") > 0 then
+ forced = false
+ return usedmethod(head)
+ else
+ return head
+ end
+ else
+ return usedmethod(head)
+ end
+ else
+ return head
+ end
+ end
+
+ methods.tex = original
+ methods.original = original
+ methods.expanded = original -- was expanded before 1.005
+ methods.traditional = languages.hyphenators.traditional.hyphenate
+ methods.none = false -- function(head) return head, false end
+
+ usedmethod = original
+
+ local function setmethod(method)
+ usedmethod = type(method) == "string" and methods[method]
+ if usedmethod == nil then
+ usedmethod = methods.tex
+ end
+ end
+ local function pushmethod(method)
+ insert(stack,usedmethod)
+ usedmethod = type(method) == "string" and methods[method]
+ if usedmethod == nil then
+ usedmethod = methods.tex
+ end
+ end
+ local function popmethod()
+ usedmethod = remove(stack) or methods.tex
+ end
+
+ hyphenators.setmethod = setmethod
+ hyphenators.pushmethod = pushmethod
+ hyphenators.popmethod = popmethod
+
+ directives.register("hyphenators.method",setmethod)
+
+ function hyphenators.setup(specification)
+ local method = specification.method
+ if method then
+ setmethod(method)
+ end
+ end
+
+ implement { name = "sethyphenationmethod", actions = setmethod, arguments = "string" }
+ implement { name = "pushhyphenation", actions = pushmethod, arguments = "string" }
+ implement { name = "pophyphenation", actions = popmethod }
+
+ -- can become a runtime loaded one:
+
+ local context = context
+ local ctx_NC = context.NC
+ local ctx_NR = context.NR
+ local ctx_verbatim = context.verbatim
+
+ function hyphenators.showhyphenationtrace(language,word)
+ if not word or word == "" then
+ return
+ end
+ local saved = trace_steps
+ trace_steps = "silent"
+ local steps = traditional.gettrace(language,word)
+ trace_steps = saved
+ if steps then
+ local n = #steps
+ if n > 0 then
+ context.starttabulate { "|r|l|l|l|" }
+ for i=1,n do
+ local s = steps[i]
+ ctx_NC() if i > 1 and i < n then context(i-1) end
+ ctx_NC() ctx_verbatim(s[1])
+ ctx_NC() ctx_verbatim(s[2])
+ ctx_NC() ctx_verbatim(s[3])
+ ctx_NC()
+ ctx_NR()
+ end
+ context.stoptabulate()
+ end
+ end
+ end
+
+ implement {
+ name = "showhyphenationtrace",
+ actions = hyphenators.showhyphenationtrace,
+ arguments = "2 strings",
+ }
+
+ function nodes.stripdiscretionaries(head)
+ for l in nexthlist, head do
+ for d in nextdisc, getlist(l) do
+ remove_node(h,false,true)
+ end
+ end
+ return head
+ end
+
+
+else
+
+ -- traditional.loadpatterns("nl","lang-nl")
+ -- traditional.loadpatterns("de","lang-de")
+ -- traditional.loadpatterns("us","lang-us")
+
+ -- traditional.registerpattern("nl","e1ë", { start = 1, length = 2, before = "e", after = "e" } )
+ -- traditional.registerpattern("nl","oo7ë", { start = 2, length = 3, before = "o", after = "e" } )
+ -- traditional.registerpattern("de","qqxc9xkqq",{ start = 3, length = 4, before = "ab", after = "cd" } )
+
+ -- local specification = {
+ -- leftcharmin = 2,
+ -- rightcharmin = 2,
+ -- leftchar = "<",
+ -- rightchar = ">",
+ -- }
+
+ -- print("reëel", traditional.injecthyphens(dictionaries.nl,"reëel", specification),"r{e>}{<e}{eë}el")
+ -- print("reeëel", traditional.injecthyphens(dictionaries.nl,"reeëel", specification),"re{e>}{<e}{eë}el")
+ -- print("rooëel", traditional.injecthyphens(dictionaries.nl,"rooëel", specification),"r{o>}{<e}{ooë}el")
+
+ -- print( "qxcxkq", traditional.injecthyphens(dictionaries.de, "qxcxkq", specification),"")
+ -- print( "qqxcxkqq", traditional.injecthyphens(dictionaries.de, "qqxcxkqq", specification),"")
+ -- print( "qqqxcxkqqq", traditional.injecthyphens(dictionaries.de, "qqqxcxkqqq", specification),"")
+ -- print("qqqqxcxkqqqq",traditional.injecthyphens(dictionaries.de,"qqqqxcxkqqqq",specification),"")
+
+ -- print("kunstmatig", traditional.injecthyphens(dictionaries.nl,"kunstmatig", specification),"")
+ -- print("kunststofmatig", traditional.injecthyphens(dictionaries.nl,"kunststofmatig", specification),"")
+ -- print("kunst[stof]matig", traditional.injecthyphens(dictionaries.nl,"kunst[stof]matig", specification),"")
+
+ -- traditional.loadpatterns("us","lang-us")
+
+ -- local specification = {
+ -- leftcharmin = 2,
+ -- rightcharmin = 2,
+ -- leftchar = false,
+ -- rightchar = false,
+ -- }
+
+ -- trace_steps = true
+
+ -- print("components", traditional.injecthyphens(dictionaries.us,"components", specification),"")
+ -- print("single", traditional.injecthyphens(dictionaries.us,"single", specification),"sin-gle")
+ -- print("everyday", traditional.injecthyphens(dictionaries.us,"everyday", specification),"every-day")
+ -- print("associate", traditional.injecthyphens(dictionaries.us,"associate", specification),"as-so-ciate")
+ -- print("philanthropic", traditional.injecthyphens(dictionaries.us,"philanthropic", specification),"phil-an-thropic")
+ -- print("projects", traditional.injecthyphens(dictionaries.us,"projects", specification),"projects")
+ -- print("Associate", traditional.injecthyphens(dictionaries.us,"Associate", specification),"As-so-ciate")
+ -- print("Philanthropic", traditional.injecthyphens(dictionaries.us,"Philanthropic", specification),"Phil-an-thropic")
+ -- print("Projects", traditional.injecthyphens(dictionaries.us,"Projects", specification),"Projects")
+
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-hyp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-hyp.mkxl
new file mode 100644
index 00000000000..87423d6d365
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-hyp.mkxl
@@ -0,0 +1,262 @@
+%D \module
+%D [ file=lang-ini,
+%D version=2014.08.10,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Experimental Patterns,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is an experimental module. We often have to deal with titles that have
+%D conflicting demands:
+%D
+%D \startitemize
+%D \startitem They go into a dedicated space (often a graphic). \stopitem
+%D \startitem The words cannot be hyphenated. \stopitem
+%D \startitem But as an escape they can get hyphenated. \stopitem
+%D \startitem In that case we want at least an extra word on the last line. \stopitem
+%D \stopitemize
+%D
+%D These and maybe more cases can be dealt with using dedicated hyphenation
+%D mechanisms. At he same time we want to experiment with more extensive patterns
+%D as discussed in {\em TUGboat, Volume 27 (2006), No. 2—Proceedings of EuroTEX2006}.
+
+% lua: 5.341 5.354
+% tex: 5.174 5.262
+
+\writestatus{loading}{ConTeXt Language Macros / Initialization}
+
+\registerctxluafile{lang-dis}{autosuffix}
+\registerctxluafile{lang-hyp}{autosuffix}
+
+\unprotect
+
+\definesystemattribute[hyphenation][public]
+
+%D After a decade of playing with these things in \LUATEX|/|\MKIV\ it's time to
+%D finish the way we deal with discretionaries. Apart from the fact that they play a
+%D role in hyphenation they also need to be dealt with in fonts. Flattening, cleanup
+%D and such are now more or less default in \CONTEXT\ so we can simplify some of the
+%D code. We also use the new penalty mechanism.
+
+\newcount\compoundhyphenpenalty
+
+% \ifcase\contextlmtxmode
+% \hyphenpenaltymode\plusfour
+% \automatichyphenmode\plusone
+% \fi
+
+\hyphenpenalty 50 % hyphenator
+\automatichyphenpenalty 50 % -
+\explicithyphenpenalty 50 % \-
+\compoundhyphenpenalty 50
+\exceptionpenalty 1000
+
+%D This command can change! At some point we will keep the setting with the
+%D paragraph and then the \type {\par} can go.
+
+\installcorenamespace{hyphenation}
+\installcorenamespace{hyphenationfeatures}
+
+\installparameterhandler \??hyphenation {hyphenation}
+\installsetuphandler \??hyphenation {hyphenation}
+
+\setuphyphenation
+ [\c!method=\s!default,
+ \s!righthyphenchar=0, % number tzt g: etc
+ \s!lefthyphenchar=0] % number
+
+\appendtoks
+ \clf_sethyphenationmethod{\hyphenationparameter\c!method}%
+\to \everysetuphyphenation
+
+%D These are mostly meant for manuals:
+
+\permanent\protected\def\starthyphenation[#1]%
+ {\begingroup
+ \clf_pushhyphenation{#1}}
+
+\permanent\protected\def\stophyphenation
+ {\ifhmode\par\fi
+ \clf_pophyphenation
+ \endgroup}
+
+% This is a global setting, so we need to disable it when needed. However, as
+% we are (hopefully) compatible and attribute driven one can also just keep it
+% enabled.
+%
+% \setuphyphenation
+% [\c!method=\s!traditional] % no translations
+
+\permanent\tolerant\protected\def\definehyphenationfeatures[#1]#*[#2]%
+ {\begingroup
+ \letdummyparameter\c!characters \empty % maybe \s!characters
+ \letdummyparameter\c!hyphens \empty % maybe \s!hyphens
+ \letdummyparameter\c!joiners \empty % maybe \s!joiners
+ \letdummyparameter\c!rightwords \zerocount % maybe \s!rightwords
+ \letdummyparameter\s!lefthyphenmin \zerocount
+ \letdummyparameter\s!righthyphenmin \zerocount
+ \letdummyparameter\s!hyphenmin \zerocount
+ \letdummyparameter\s!lefthyphenchar \zerocount
+ \letdummyparameter\s!righthyphenchar\zerocount
+ \letdummyparameter\c!alternative \empty
+ \letdummyparameter\c!rightedge \empty
+ \letdummyparameter\c!rightchars \empty
+ \getdummyparameters[#2]%
+ \clf_definehyphenationfeatures
+ {#1}%
+ {
+ characters {\dummyparameter\c!characters}%
+ hyphens {\dummyparameter\c!hyphens}%
+ joiners {\dummyparameter\c!joiners}%
+ rightwordmin \numexpr\dummyparameter\c!rightwords\relax
+ rightchars {\dummyparameter\c!rightchars}%
+ charmin \numexpr\dummyparameter\s!hyphenmin\relax
+ leftcharmin \numexpr\dummyparameter\s!lefthyphenmin\relax
+ rightcharmin \numexpr\dummyparameter\s!righthyphenmin\relax
+ leftchar \numexpr\dummyparameter\s!lefthyphenchar\relax
+ rightchar \numexpr\dummyparameter\s!righthyphenchar\relax
+ alternative {\dummyparameter\c!alternative}%
+ rightedge {\dummyparameter\c!rightedge}%
+ % autohyphen {\dummyparameter\c!autohyphen}
+ % hyphenonly {\dummyparameter\c!hyphenonly}
+ }%
+ \relax
+ \endgroup}
+
+\permanent\protected\def\sethyphenationfeatures[#1]%
+ {\clf_sethyphenationfeatures{#1}}
+
+\permanent\protected\def\resethyphenationfeatures
+ {\attribute\hyphenationattribute\attributeunsetvalue}
+
+\resethyphenationfeatures
+
+% todo: \start ... \stop too
+
+\permanent\tolerant\protected\def\registerhyphenationpattern[#1]#*[#2]%
+ {\clf_registerhyphenationpattern\ifparameter#2\or{#1}{#2}\else{\currentlanguage}{#1}\fi\s!true\relax}
+
+\permanent\tolerant\protected\def\unregisterhyphenationpattern[#1]#*[#2]%
+ {\clf_registerhyphenationpattern\ifparameter#2\or{#1}{#2}\else{\currentlanguage}{#1}\fi\s!false\relax}
+
+\permanent\tolerant\protected\def\registerhyphenationexception[#1]#*[#2]%
+ {\clf_registerhyphenationexception\ifparameter#2\or{#1}{#2}\else{\currentlanguage}{#1}\fi\relax}
+
+\permanent\tolerant\protected\def\showhyphenationtrace[#1]#*[#2]%
+ {\begingroup
+ \tt
+ \clf_showhyphenationtrace\ifparameter#2\or{#1}{#2}\else{\currentlanguage}{#1}\fi\relax
+ \endgroup}
+
+% For old times sake:
+
+\permanent\protected\def\atleastoneword#1%
+ {\begingroup
+ \starthyphenation[traditional]% this might become default or a faster switch
+ \sethyphenationfeatures[words]%
+ #1\par
+ \stophyphenation
+ \endgroup}
+
+%D For me:
+
+\permanent\protected\def\showdiscretionaries
+ {\clf_showdiscretionaries}
+
+%D These are (at least now) not cummulative:
+
+\definehyphenationfeatures % just an example
+ [fences]
+ [\c!characters={[]()}]
+
+\definehyphenationfeatures
+ [words]
+ [\c!rightwords=1,
+ \s!lefthyphenmin=4,
+ \s!righthyphenmin=4]
+
+\definehyphenationfeatures
+ [default]
+ [%c!rightedge=\v!tex,
+ \c!hyphens=\v!yes,
+ \c!joiners=\v!yes]
+
+\definehyphenationfeatures
+ [strict]
+ [\c!rightedge=\s!tex]
+
+% \sethyphenationfeatures
+% [fences]
+
+% \sethyphenationfeatures
+% [default,fences]
+
+% \setuphyphenation % will be default
+% [method=expanded]
+
+\protect \endinput
+
+% \starttext
+%
+% \enabledirectives[hyphenators.method=traditional]
+%
+% % \dorecurse{1000}{\input tufte \par}
+%
+% \setupalign[verytolerant,flushleft]
+% \setuplayout[width=140pt] \showframe
+%
+% longword longword long word longword longwordword \blank
+%
+% \enabledirectives[hyphenators.rightwordsmin=1]
+%
+% longword longword long word longword longwordword\blank
+%
+% \disabledirectives[hyphenators.rightwordsmin]
+%
+% longword longword long word longword longwordword\blank
+%
+% \atleastoneword{longword longword long word longword longwordword}
+%
+% \enabledirectives[hyphenators.method=traditional]
+%
+% \stoptext
+
+% \startluacode
+% -- e1ë/e=e reëel re-eel
+% -- a1atje./a=t,1,3 omaatje oma-tje
+% -- schif1f/ff=f,5,2 Schiffahrt Schiff-fahrt
+%
+% languages.hyphenators.traditional.registerpattern("en","a1b", { start = 1, length = 2, before = "CD", after = "EF" } )
+% languages.hyphenators.traditional.registerpattern("en","e1ë", { start = 1, length = 2, before = "e", after = "e" } )
+% languages.hyphenators.traditional.registerpattern("en","oo1ë", { start = 2, length = 2, before = "o", after = "e" } )
+% languages.hyphenators.traditional.registerpattern("en","qqxc9xkqq",{ start = 3, length = 4, before = "ab", after = "cd" } ) -- replacement start length
+%
+% -- print("reëel", injecthyphens(dictionaries.nl,"reëel", 2,2))
+% -- print("reeëel", injecthyphens(dictionaries.nl,"reeëel", 2,2))
+% -- print("rooëel", injecthyphens(dictionaries.nl,"rooëel", 2,2))
+% -- print( "QXcXkQ", injecthyphens(dictionaries.de, "QXcXkQ", 2,2))
+% -- print( "QQXcXkQQ", injecthyphens(dictionaries.de, "QQXcXkQQ", 2,2))
+% -- print( "QQQXcXkQQQ", injecthyphens(dictionaries.de, "QQQXcXkQQQ", 2,2))
+% -- print("QQQQXcXkQQQQ",injecthyphens(dictionaries.de,"QQQQXcXkQQQQ",2,2))
+% --
+% -- print( "QQXcXkQQ QQXcXkQQ", injecthyphens(dictionaries.de, "QQXcXkQQ QQXcXkQQ", 2,2))
+% \stopluacode
+%
+% \starttext
+%
+% \blank
+%
+% xreëel rooëel \par xxabxx xxxabxxx \par
+%
+% \hsize1mm \lefthyphenmin2 \righthyphenmin2
+%
+% \blank Capacity \blank capacity \blank xyabxy \blank xreëel \blank rooëel \blank
+%
+% xy\discretionary{CD}{EF}{ab}xy % xxacceedxxx
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/lang-ini.lmt
new file mode 100644
index 00000000000..5f2dd1a55d3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-ini.lmt
@@ -0,0 +1,670 @@
+if not modules then modules = { } end modules ['lang-ini'] = {
+ version = 1.001,
+ comment = "companion to lang-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- needs a cleanup (share locals)
+-- discard language when redefined
+
+-- 002D : hyphen-minus (ascii)
+-- 2010 : hyphen
+-- 2011 : nonbreakable hyphen
+-- 2013 : endash (compound hyphen)
+
+--~ language:hyphenation(string) string = language:hyphenation() language:clear_hyphenation()
+
+-- todo: no foo:bar but foo(bar,...)
+
+local type, tonumber, next = type, tonumber, next
+local utfbyte = utf.byte
+local format, gsub, gmatch, find = string.format, string.gsub, string.gmatch, string.find
+local concat, sortedkeys, sortedpairs, keys, insert = table.concat, table.sortedkeys, table.sortedpairs, table.keys, table.insert
+local utfvalues, strip, utfcharacters = string.utfvalues, string.strip, utf.characters
+
+local context = context
+local commands = commands
+local implement = interfaces.implement
+
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_set = utilities.parsers.settings_to_set
+
+local trace_patterns = false trackers.register("languages.patterns", function(v) trace_patterns = v end)
+
+local report_initialization = logs.reporter("languages","initialization")
+
+local prehyphenchar = language.prehyphenchar -- global per language
+local posthyphenchar = language.posthyphenchar -- global per language
+local preexhyphenchar = language.preexhyphenchar -- global per language
+local postexhyphenchar = language.postexhyphenchar -- global per language
+----- lefthyphenmin = language.lefthyphenmin
+----- righthyphenmin = language.righthyphenmin
+local sethjcode = language.sethjcode
+local currentlanguage = language.current -- or function() return tex.normallanguage or tex.language end
+
+local uccodes = characters.uccodes
+local lccodes = characters.lccodes
+
+local new_language = language.new
+
+languages = languages or {}
+local languages = languages
+
+languages.version = 1.010
+
+languages.registered = languages.registered or { }
+local registered = languages.registered
+
+languages.associated = languages.associated or { }
+local associated = languages.associated
+
+languages.numbers = languages.numbers or { }
+local numbers = languages.numbers
+
+languages.data = languages.data or { }
+local data = languages.data
+
+storage.register("languages/registered",registered,"languages.registered")
+storage.register("languages/associated",associated,"languages.associated")
+storage.register("languages/numbers", numbers, "languages.numbers")
+storage.register("languages/data", data, "languages.data")
+
+local variables = interfaces.variables
+
+local v_reset = variables.reset
+local v_yes = variables.yes
+
+local nofloaded = 0
+
+local function resolve(tag)
+ local data, instance = registered[tag], nil
+ if data then
+ instance = data.instance
+ if not instance then
+ instance = new_language(data.number)
+ data.instance = instance
+ end
+ end
+ return data, instance
+end
+
+local function tolang(what) -- returns lang object
+ if not what then
+ what = currentlanguage()
+ end
+ local tag = numbers[what]
+ local data = tag and registered[tag] or registered[what]
+ if data then
+ local instance = data.instance -- .lang -- was this ok ?
+ if not instance then
+ instance = new_language(data.number)
+ data.instance = instance
+ end
+ return instance
+ end
+end
+
+function languages.getdata(tag) -- or number
+ if tag then
+ return registered[tag] or registered[numbers[tag]]
+ else
+ return registered[numbers[currentlanguage()]]
+ end
+end
+
+-- languages.tolang = tolang
+
+-- patterns=en
+-- patterns=en,de
+
+local function validdata(loaded,what,tag)
+ local dataset = loaded[what]
+ if dataset then
+ local data = dataset.data
+ if not data or data == "" then
+ -- nothing
+ elseif dataset.compression == "zlib" then
+ data = zlib.decompress(data)
+ if dataset.length and dataset.length ~= #data then
+ report_initialization("compression error in %a for language %a","patterns",what,tag)
+ end
+ return data
+ else
+ return data
+ end
+ end
+end
+
+-- languages.hjcounts[unicode].count
+
+-- hjcode: 0 not to be hyphenated
+-- 1--31 length
+-- 32 zero length
+-- > 32 hyphenated with length 1
+
+local function sethjcodes(instance,loaded,what,factor)
+ local l = loaded[what]
+ local c = l and l.characters
+ if c then
+ local hjcounts = factor and languages.hjcounts or false
+ --
+ local h = loaded.codehash
+ if not h then
+ h = { }
+ loaded.codehash = h
+ end
+ --
+ local function setcode(l)
+ local u = uccodes[l]
+ local s = l
+ if hjcounts then
+ local c = hjcounts[l]
+ if c then
+ c = c.count
+ if not c then
+ -- error, keep as 1
+ elseif c <= 0 then
+ -- counts as 0 i.e. ignored
+ s = 32
+ elseif c >= 31 then
+ -- counts as 31
+ s = 31
+ else
+ -- count c times
+ s = c
+ end
+ end
+ end
+ sethjcode(instance,l,s)
+ h[l] = s
+ if u ~= l and type(u) == "number" then
+ sethjcode(instance,u,s)
+ h[u] = lccodes[l]
+ end
+ end
+ --
+ local s = tex.savinghyphcodes
+ tex.savinghyphcodes = 0
+ if type(c) == "table" then
+ for l in next, c do
+ setcode(utfbyte(l))
+ end
+ else
+ for l in utfvalues(c) do
+ setcode(l)
+ end
+ end
+ tex.savinghyphcodes = s
+ end
+end
+
+-- 2'2 conflicts with 4' ... and luatex barks on it
+
+local P, R, Cs, Ct, lpegmatch, lpegpatterns = lpeg.P, lpeg.R, lpeg.Cs, lpeg.Ct, lpeg.match, lpeg.patterns
+
+local utfsplit = utf.split
+
+local space = lpegpatterns.space
+local whitespace = lpegpatterns.whitespace^1
+local nospace = lpegpatterns.utf8char - whitespace
+local digit = lpegpatterns.digit
+----- endofstring = #whitespace + P(-1)
+local endofstring = #whitespace
+
+local word = (digit/"")^0 * (digit/"" * endofstring + digit/" " + nospace)^1
+local anyword = (1-whitespace)^1
+local analyze = Ct((whitespace + Cs(word))^1)
+
+local function unique(tag,requested,loaded)
+ local nofloaded = #loaded
+ if nofloaded == 0 then
+ return ""
+ elseif nofloaded == 1 then
+ return loaded[1]
+ else
+ insert(loaded,1," ") -- no need then for special first word
+ -- insert(loaded, " ")
+ loaded = concat(loaded," ")
+ local t = lpegmatch(analyze,loaded) or { }
+ local h = { }
+ local b = { }
+ for i=1,#t do
+ local ti = t[i]
+ local hi = h[ti]
+ if not hi then
+ h[ti] = 1
+ elseif hi == 1 then
+ h[ti] = 2
+ b[#b+1] = utfsplit(ti," ")
+ end
+ end
+ -- sort
+ local nofbad = #b
+ if nofbad > 0 then
+ local word
+ for i=1,nofbad do
+ local bi = b[i]
+ local p = P(bi[1])
+ for i=2,#bi do
+ p = p * digit * P(bi[i])
+ end
+ if word then
+ word = word + p
+ else
+ word = p
+ end
+ report_initialization("language %a, patterns %a, discarding conflict (0-9)%{[0-9]}t(0-9)",tag,requested,bi)
+ end
+ t, h, b = nil, nil, nil -- permit gc
+ local someword = digit^0 * word * digit^0 * endofstring / ""
+ -- local strip = Cs(someword^-1 * (someword + anyword + whitespace)^1)
+ local strip = Cs((someword + anyword + whitespace)^1)
+ return lpegmatch(strip,loaded) or loaded
+ else
+ return loaded
+ end
+ end
+end
+
+local shared = false
+
+local function loaddefinitions(tag,specification)
+ statistics.starttiming(languages)
+ local data, instance = resolve(tag)
+ local requested = specification.patterns or ""
+ local definitions = settings_to_array(requested)
+ if #definitions > 0 then
+ if trace_patterns then
+ report_initialization("pattern specification for language %a: %s",tag,specification.patterns)
+ end
+ local ploaded = instance:patterns()
+ local eloaded = instance:hyphenation()
+ if not ploaded or ploaded == "" then
+ ploaded = { }
+ else
+ ploaded = { ploaded }
+ end
+ if not eloaded or eloaded == "" then
+ eloaded = { }
+ else
+ eloaded = { eloaded }
+ end
+ local dataused = data.used
+ local ok = false
+ local resources = data.resources or { }
+ data.resources = resources
+ if not shared then
+ local found = resolvers.findfile("lang-exc.lua")
+ if found then
+ shared = dofile(found)
+ if type(shared) == "table" then
+ shared = concat(shared," ")
+ else
+ shared = true
+ end
+ else
+ shared = true
+ end
+ end
+ for i=1,#definitions do
+ local definition = definitions[i]
+ if definition == "" then
+ -- error
+ elseif definition == v_reset then
+ if trace_patterns then
+ report_initialization("clearing patterns for language %a",tag)
+ end
+ instance:clear_patterns()
+ instance:clear_hyphenation()
+ ploaded = { }
+ eloaded = { }
+ elseif not dataused[definition] then
+ dataused[definition] = definition
+ local filename = "lang-" .. definition .. ".lua"
+ local fullname = resolvers.findfile(filename) or ""
+ if fullname == "" then
+ fullname = resolvers.findfile(filename .. ".gz") or ""
+ end
+ if fullname ~= "" then
+ if trace_patterns then
+ report_initialization("loading definition %a for language %a from %a",definition,tag,fullname)
+ end
+ local suffix, gzipped = gzip.suffix(fullname)
+ local loaded = table.load(fullname,gzipped and gzip.load)
+ if loaded then -- todo: version test
+ ok, nofloaded = true, nofloaded + 1
+ sethjcodes(instance,loaded,"patterns",specification.factor)
+ sethjcodes(instance,loaded,"exceptions",specification.factor)
+ local p = validdata(loaded,"patterns",tag)
+ local e = validdata(loaded,"exceptions",tag)
+ if p and p ~= "" then
+ ploaded[#ploaded+1] = p
+ end
+ if e and e ~= "" then
+ eloaded[#eloaded+1] = e
+ end
+ resources[#resources+1] = loaded -- so we can use them otherwise
+ else
+ report_initialization("invalid definition %a for language %a in %a",definition,tag,filename)
+ end
+ elseif trace_patterns then
+ report_initialization("invalid definition %a for language %a in %a",definition,tag,filename)
+ end
+ elseif trace_patterns then
+ report_initialization("definition %a for language %a already loaded",definition,tag)
+ end
+ end
+ if #ploaded > 0 then
+ -- why not always clear
+ instance:clear_patterns()
+ instance:patterns(unique(tag,requested,ploaded))
+ end
+ if #eloaded > 0 then
+ -- why not always clear
+ instance:clear_hyphenation()
+ instance:hyphenation(concat(eloaded," "))
+ end
+ if type(shared) == "string" then
+ instance:hyphenation(shared)
+ end
+ return ok
+ elseif trace_patterns then
+ report_initialization("no definitions for language %a",tag)
+ end
+ statistics.stoptiming(languages)
+end
+
+storage.shared.noflanguages = storage.shared.noflanguages or 0
+
+local noflanguages = storage.shared.noflanguages
+
+function languages.define(tag,parent)
+ noflanguages = noflanguages + 1
+ if trace_patterns then
+ report_initialization("assigning number %a to %a",noflanguages,tag)
+ end
+ numbers[noflanguages] = tag
+ registered[tag] = {
+ tag = tag,
+ parent = parent or "",
+ patterns = "",
+ loaded = false,
+ used = { },
+ dirty = true,
+ number = noflanguages,
+ instance = nil, -- luatex data structure
+ synonyms = { },
+ }
+ storage.shared.noflanguages = noflanguages
+end
+
+function languages.setsynonym(synonym,tag) -- convenience function
+ local l = registered[tag]
+ if l then
+ l.synonyms[synonym] = true -- maybe some day more info
+ end
+end
+
+function languages.installed(separator)
+ return concat(sortedkeys(registered),separator or ",")
+end
+
+function languages.current(n)
+ return numbers[n and tonumber(n) or currentlanguage()]
+end
+
+function languages.associate(tag,script,language) -- not yet used
+ associated[tag] = { script, language }
+end
+
+function languages.association(tag) -- not yet used
+ if not tag then
+ tag = numbers[currentlanguage()]
+ elseif type(tag) == "number" then
+ tag = numbers[tag]
+ end
+ local lat = tag and associated[tag]
+ if lat then
+ return lat[1], lat[2]
+ end
+end
+
+function languages.loadable(tag,defaultlanguage) -- hack
+ local l = registered[tag] -- no synonyms
+ if l and resolvers.findfile("lang-"..l.patterns..".lua") then
+ return true
+ else
+ return false
+ end
+end
+
+-- a bit messy, we will do all language setting in lua as we can now assign
+-- and 'patterns' will go away here.
+
+function languages.unload(tag)
+ local l = registered[tag]
+ if l then
+ l.dirty = true
+ end
+end
+
+if environment.initex then
+
+ function languages.getnumber()
+ return 0
+ end
+
+else
+
+ function languages.getnumber(tag,default,patterns,factor)
+ local l = registered[tag]
+ if l then
+ if l.dirty then
+ l.factor = factor == v_yes and true or false
+ if trace_patterns then
+ report_initialization("checking patterns for %a with default %a",tag,default)
+ end
+ -- patterns is already resolved to parent patterns if applicable
+ if patterns and patterns ~= "" then
+ if l.patterns ~= patterns then
+ l.patterns = patterns
+ if trace_patterns then
+ report_initialization("loading patterns for %a using specification %a",tag,patterns)
+ end
+ loaddefinitions(tag,l)
+ else
+ -- unchanged
+ end
+ elseif l.patterns == "" then
+ l.patterns = tag
+ if trace_patterns then
+ report_initialization("loading patterns for %a using tag",tag)
+ end
+ local ok = loaddefinitions(tag,l)
+ if not ok and tag ~= default then
+ l.patterns = default
+ if trace_patterns then
+ report_initialization("loading patterns for %a using default",tag)
+ end
+ loaddefinitions(tag,l)
+ end
+ end
+ l.loaded = true
+ l.dirty = false
+ end
+ return l.number
+ else
+ return 0
+ end
+ end
+
+ numbers[0] = "null"
+
+ registered.null = {
+ number = 0,
+ instance = new_language(0),
+ }
+
+end
+
+-- not that usefull, global values
+
+function languages.prehyphenchar (what) return prehyphenchar (tolang(what)) end
+function languages.posthyphenchar (what) return posthyphenchar (tolang(what)) end
+function languages.preexhyphenchar (what) return preexhyphenchar (tolang(what)) end
+function languages.postexhyphenchar(what) return postexhyphenchar(tolang(what)) end
+-------- languages.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end
+-------- languages.righthyphenmin (what) return righthyphenmin (tolang(what)) end
+
+-- e['implementer']= 'imple{m}{-}{-}menter'
+-- e['manual'] = 'man{}{}{}'
+-- e['as'] = 'a-s'
+-- e['user-friendly'] = 'user=friend-ly'
+-- e['exceptionally-friendly'] = 'excep-tionally=friend-ly'
+
+local invalid = { "{", "}", "-" }
+
+local function collecthjcodes(data,str)
+ local found = data.extras and data.extras.characters or { }
+ for s in utfcharacters(str) do
+ if not found[s] then
+ found[s] = true
+ end
+ end
+ for i=1,#invalid do -- less checks this way
+ local c = invalid[i]
+ if found[c] then
+ found[c] = nil
+ end
+ end
+ data.extras = { characters = found }
+ sethjcodes(data.instance,data,"extras",data.factor)
+end
+
+function languages.loadwords(tag,filename)
+ local data, instance = resolve(tag)
+ if data then
+ statistics.starttiming(languages)
+ local str = io.loaddata(filename) or ""
+ collecthjcodes(data,str)
+ instance:hyphenation(str)
+ statistics.stoptiming(languages)
+ end
+end
+
+
+function languages.setexceptions(tag,str)
+ local data, instance = resolve(tag)
+ if data then
+ str = strip(str) -- we need to strip leading spaces
+ collecthjcodes(data,str)
+ instance:hyphenation(str)
+ end
+end
+
+function languages.hyphenate(tag,str)
+ -- todo: does this still work?
+ local data, instance = resolve(tag)
+ if data then
+ return instance:hyphenate(str)
+ else
+ return str
+ end
+end
+
+-- hyphenation.define ("zerolanguage")
+-- hyphenation.loadpatterns ("zerolanguage") -- else bug
+-- hyphenation.loadexceptions("zerolanguage") -- else bug
+
+languages.logger = languages.logger or { }
+
+function languages.logger.report()
+ local result, r = { }, 0
+ for tag, l in sortedpairs(registered) do
+ if l.loaded then
+ r = r + 1
+ result[r] = format("%s:%s:%s",tag,l.parent,l.number)
+ end
+ end
+ return r > 0 and concat(result," ") or "none"
+end
+
+-- must happen at the tex end .. will use lang-def.lua
+
+languages.associate('en','latn','eng')
+languages.associate('uk','latn','eng')
+languages.associate('nl','latn','nld')
+languages.associate('de','latn','deu')
+languages.associate('fr','latn','fra')
+
+statistics.register("loaded patterns", function()
+ local result = languages.logger.report()
+ if result ~= "none" then
+ -- return result
+ return format("%s, load time: %s",result,statistics.elapsedtime(languages))
+ end
+end)
+
+-- statistics.register("language load time", function()
+-- -- often zero so we can merge that in the above
+-- return statistics.elapsedseconds(languages, format(", nofpatterns: %s",nofloaded))
+-- end)
+
+-- interface
+
+implement {
+ name = "languagenumber",
+ actions = { languages.getnumber, context },
+ arguments = "4 strings"
+}
+
+implement {
+ name = "installedlanguages",
+ actions = { languages.installed, context },
+}
+
+implement {
+ name = "definelanguage",
+ actions = languages.define,
+ arguments = "2 strings"
+}
+
+implement {
+ name = "setlanguagesynonym",
+ actions = languages.setsynonym,
+ arguments = "2 strings"
+}
+
+implement {
+ name = "unloadlanguage",
+ actions = languages.unload,
+ arguments = "string"
+}
+
+implement {
+ name = "setlanguageexceptions",
+ actions = languages.setexceptions,
+ arguments = "2 strings"
+}
+
+implement {
+ name = "currentprehyphenchar",
+ actions = function()
+ local c = prehyphenchar(tolang())
+ if c and c > 0 then
+ context.char(c)
+ end
+ end
+}
+
+implement {
+ name = "currentposthyphenchar",
+ actions = function()
+ local c = posthyphenchar(tolang())
+ if c and c > 0 then
+ context.char(c)
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-ini.mkxl
new file mode 100644
index 00000000000..5df975c661f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-ini.mkxl
@@ -0,0 +1,787 @@
+%D \module
+%D [ file=lang-ini,
+%D version=1996.01.25,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% \cldcontext{languages.numbers[tex.count.mainlanguagenumber]}
+
+%D This module implements multi||language support of \CONTEXT, which should not be
+%D confused with the multi||lingual interface. This support will be extended when
+%D needed. Properties of languages are defined in \TEX\ files as well as \LUA\
+%D files.
+
+\writestatus{loading}{ConTeXt Language Macros / Initialization}
+
+\registerctxluafile{lang-ini}{autosuffix}
+\registerctxluafile{lang-def}{}
+\registerctxluafile{lang-cnt}{}
+
+\unprotect
+
+\ifdefined\nonfrenchspacing\else \let\nonfrenchspacing\relax \fi
+\ifdefined\frenchspacing \else \let\frenchspacing \relax \fi
+
+%D When loading hyphenation patterns, \TEX\ assign a number to each loaded table,
+%D starting with~0. Switching to a specific table is done by assigning the relevant
+%D number to the predefined \COUNTER\ \type {\language}. However, in \MKIV\ a lot
+%D of management is delegated to \LUA.
+
+%D We keep track of the last loaded patterns by means of a pseudo \COUNTER. This
+%D just one of those situations in which we don't want to spent a real one. Language
+%D zero has no patterns, first of all because I like to start numbering at one. It
+%D may come in handy for special purposes as well.
+
+\normallanguage\zerocount
+
+%D \macros
+%D {currentlanguage, setupcurrentlanguage}
+%D
+%D Instead of numbers,we are going to use symbolic names for the languages. The
+%D current langage is saved in the macro \type {\currentlanguage}. The setup macro
+%D is mainly used for cosmetic purposes.
+%D
+%D \starttyping
+%D \dorecurse{3}
+%D {\language[nl]
+%D \startmode[*en] english \stopmode
+%D \startmode[*nl] dutch \stopmode
+%D \language[en]
+%D \startmode[*en] english \stopmode
+%D \startmode[*nl] dutch \stopmode}
+%D \stoptyping
+
+\mutable\let\askedlanguage \empty
+\mutable\let\currentlanguage \empty
+\mutable\let\currentmainlanguage\empty
+
+%D \macros
+%D {defaultlanguage,languageparameter,specificlanguageparameter}
+%D
+%D We don't use the commandhandler here (yet) because we have a rather special
+%D fallback mechanism so quite some compatibility testing is needed.
+
+\installcorenamespace{language}
+\installcorenamespace{languagelinked}
+
+\mutable\def\currentusedlanguage{\currentlanguage}
+
+\permanent\def\defaultlanguage#1%
+ {\ifcsname\??language#1\s!default\endcsname
+ \expandafter\defaultlanguage\lastnamedcs
+ \else
+ #1%
+ \fi}
+
+\permanent\def\languageparameter#1%
+ {\ifcsname\??language\currentlanguage#1\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??language\currentlanguage\s!default\endcsname
+ \expandafter\specificlanguageparameter\lastnamedcs{#1}%
+ \orelse\ifcsname\??language\s!default#1\endcsname
+ \lastnamedcs
+ \fi}
+
+\permanent\def\specificlanguageparameter#1#2%
+ {\ifcsname\??language#1#2\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??language#1\s!default\endcsname
+ \expandafter\specificlanguageparameter\lastnamedcs{#2}%
+ \orelse\ifcsname\??language\s!default#2\endcsname
+ \lastnamedcs
+ \fi}
+
+\permanent\def\mainlanguageparameter#1%
+ {\ifcsname\??language\currentmainlanguage#1\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??language\currentmainlanguage\s!default\endcsname
+ \expandafter\specificlanguageparameter\lastnamedcs{#1}%
+ \orelse\ifcsname\??language\s!default#1\endcsname
+ \lastnamedcs
+ \fi}
+
+\aliased\let\usedlanguageparameter\languageparameter
+
+\permanent\def\askedlanguageparameter#1% assumes \currentusedlanguage being set
+ {\ifcsname\??language\currentusedlanguage#1\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??language\currentusedlanguage\s!default\endcsname
+ \expandafter\specificlanguageparameter\lastnamedcs{#1}%
+ \orelse\ifcsname\??language\s!default#1\endcsname
+ \lastnamedcs
+ \fi}
+
+\permanent\protected\def\setusedlanguage#1%
+ {\edef\currentusedlanguage{\reallanguagetag{#1}}%
+ \ifempty\currentusedlanguage
+ \let\currentusedlanguage \currentlanguage
+ \enforced\let\usedlanguageparameter\languageparameter
+ \orelse\ifx\currentusedlanguage\v!global
+ \let\currentusedlanguage \currentmainlanguage
+ \enforced\let\usedlanguageparameter\mainlanguageparameter
+ \orelse\ifx\currentusedlanguage\v!local
+ \let\currentusedlanguage \currentlanguage
+ \enforced\let\usedlanguageparameter\languageparameter
+ \else
+ \enforced\let\usedlanguageparameter\askedlanguageparameter
+ \fi}
+
+\permanent\protected\def\setupcurrentlanguage[#1]%
+ {\setcurrentlanguage\currentmainlanguage{#1}}
+
+\permanent\protected\def\setcurrentlanguage#1#2% sets modes: **id (currentmain) *id (current)
+ {\edef\p_askedlanguage{#1}% otherwise clash with \askedlanguage
+ \ifempty\p_askedlanguage \else
+ \ifempty\currentmainlanguage\else\resetsystemmode{\systemmodeprefix\currentmainlanguage}\fi
+ \let\currentmainlanguage\p_askedlanguage
+ \setsystemmode{\systemmodeprefix\currentmainlanguage}%
+ \fi
+ \edef\p_askedlanguage{#2}%
+ \ifempty\p_askedlanguage \else
+ \ifempty\currentlanguage\else\resetsystemmode\currentlanguage\fi
+ \let\currentlanguage\p_askedlanguage
+ \setsystemmode\currentlanguage
+ \fi}
+
+%D The internal macros will be defined later.
+
+%D \macros
+%D {installlanguage}
+%D
+%D Hyphenation patterns can only be loaded when the format file is prepared. The
+%D next macro takes care of this loading. A language is specified with
+%D
+%D \showsetup{installlanguage}
+%D
+%D When \type {state} equals \type {start}, both patterns and additional hyphenation
+%D specifications are loaded. These files are seached for in the patterns path
+%D have names like \type {lang-nl.lua}.
+%D
+%D The \type {spacing} variable specifies how the spaces after punctuation has to be
+%D handled. English is by tradition more tolerant to inter||sentence spacing than
+%D other languages.
+%D
+%D This macro also defines \type {\identifier} as a shortcut switch to the language.
+%D Furthermore the command defined as being language specific, are executed. With
+%D \type {default} we can default to another language (patterns) at format
+%D generation time. Patterns are loaded at runtime.
+
+\newtoks \everysetuplanguage
+
+\aliased\let\installedlanguages\clf_installedlanguages % no need for \clf_
+
+\permanent\protected\def\doifelselanguage#1%
+ {\ifcsname\??language#1\c!state\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiflanguageelse\doifelselanguage
+
+\permanent\def\reallanguagetag#1%
+ {\ifcsname\??languagelinked#1\endcsname\lastnamedcs\else#1\fi}
+
+% \language[#1] gave unwanted side effect of loading language specifics
+
+\mutable\let\currentsetuplanguage\empty
+
+\permanent\tolerant\protected\def\installlanguage[#1]#*[#2]%
+ {\ifcondition\validassignment{#2}%
+ \doifelselanguage{#1}
+ {\getparameters[\??language#1][#2]}
+ {\defcsname\??languagelinked#1\endcsname{#1}%
+ \getparameters[\??language#1][\c!state=\v!start,#2]%
+ \lang_basics_install_indeed{#1}{#1}}%
+ \edef\currentsetuplanguage{#1}%
+ \clf_definelanguage{#1}{\specificlanguageparameter{#1}\s!default}%
+ \the\everysetuplanguage
+ \else
+ \defcsname\??languagelinked#1\endcsname{#2}%
+ \clf_setlanguagesynonym{#1}{#2}%
+ \lang_basics_install_indeed{#1}{#2}%
+ \fi}
+
+\def\lang_basics_install_indeed#1#2%
+ {\ifcstok{\specificlanguageparameter{#1}\c!command}\v!no\orelse\ifcsname#1\endcsname\else
+ \frozen\instance\protected\defcsname#1\endcsname{\lang_basics_set_current[#2]}%
+ \fi}
+
+%D When the second argument is a language identifier, a synonym is created. This
+%D feature is present because we used dutch mnemonics in the dutch version, but
+%D nowadays conform a standard.
+
+\permanent\protected\def\doifelsepatterns#1%
+ {\begingroup % will change
+ \lang_basics_set_current[#1]%
+ \ifnum\normallanguage>\zerocount
+ \endgroup\expandafter\firstoftwoarguments
+ \else
+ \endgroup\expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifpatternselse\doifelsepatterns
+
+%D \macros
+%D {setuplanguage}
+%D
+%D Quick and dirty, but useful:
+%D
+%D \showsetup{setuplanguage}
+%D
+%D Beware, this command can only be used when a language is installed.
+
+\ifdefined\lang_basics_synchronize \else
+ \let\lang_basics_synchronize\relax % be nice for setups till we have one
+\fi
+
+\installmacrostack\currentlanguage
+
+\permanent\tolerant\protected\def\setuplanguage[#1]#*[#2]%
+ {\ifarguments
+ % only synchronize
+ \or
+ \let\currentsetuplanguage\currentlanguage
+ \getparameters[\??language\currentsetuplanguage][#1]%
+ \the\everysetuplanguage
+ \or
+ \push_macro_currentlanguage % can be default
+ \edef\currentsetuplanguage{\reallanguagetag{#1}}%
+ \getparameters[\??language\currentsetuplanguage][#2]%
+ \the\everysetuplanguage
+ \pop_macro_currentlanguage
+ %\doif\currentsetuplanguage\currentlanguage we can have influenced inheritance (default)
+ \fi
+ \lang_basics_synchronize}
+
+\appendtoks
+ \clf_unloadlanguage{\currentsetuplanguage}%
+\to \everysetuplanguage
+
+\setuplanguage
+ [\s!default]
+ [\s!patterns=,
+ \s!lefthyphenmin=2,
+ \s!righthyphenmin=2,
+ \s!lefthyphenchar=-1,
+ \s!righthyphenchar=45,
+ % used in compound i.e. interfaced with c! and can be anything so no numbers
+ \c!lefthyphen=,
+ \c!righthyphen=-,
+ \c!hyphen=-,
+ \c!spacing=\v!packed,
+ \c!compoundhyphen=\compoundhyphen,
+ \c!rightcompoundhyphen=\compoundhyphen,
+ \c!leftcompoundhyphen=,
+ \c!midsentence=---,
+ \c!leftsentence=---,
+ \c!rightsentence=---,
+ \c!leftsubsentence=---,
+ \c!rightsubsentence=---,
+ \c!leftquote=\upperleftsinglesixquote,
+ \c!rightquote=\upperrightsingleninequote,
+ \c!leftquotation=\upperleftdoublesixquote,
+ \c!rightquotation=\upperrightdoubleninequote,
+ \c!leftspeech=\languageparameter\c!leftquotation,
+ \c!middlespeech=,
+ \c!rightspeech=\languageparameter\c!rightquotation,
+ \c!limittext=\unknown,
+ \c!time={h,:,m},
+ \c!date={\v!year,\ ,\v!month,\ ,\v!day},
+ \c!text=Ag,
+ \c!font=] % \v!auto : experimental !
+
+% to be tested:
+%
+% \setuplanguage
+% [\s!default]
+% [\c!righthyphenchar="AD]
+
+%D The values \type {leftsentence} and \type {rightsentence} can be (and are) used
+%D to implement automatic subsentence boundary glyphs, like in {\fr |<|french
+%D guillemots|>|} or {\de |<|german guillemots|>|} or {\nl |<|dutch dashes|>|} like
+%D situations. Furthermore \type {leftquotation} and \type {leftquote} come into
+%D view \quotation {when we quote} or \quote {quote} something.
+
+%D \macros
+%D {currentdatespecification, currenttimespecification}
+%D
+%D Just to make things easy we can ask for the current date specification by saying:
+
+\permanent\def\currentdatespecification{\languageparameter\c!date}
+\permanent\def\currenttimespecification{\languageparameter\c!time}
+
+%D Carefull reading of these macros shows that it's legal to say
+%D
+%D \starttyping
+%D \installlanguage [du] [de]
+%D \stoptyping
+
+%D \macros
+%D {language,mainlanguage}
+%D
+%D Switching to another language (actually another hyphenation pattern) is done
+%D with:
+%D
+%D \starttyping
+%D \language[identifier]
+%D \stoptyping
+%D
+%D or with \type {\identifier}. Just to be compatible with \PLAIN\ \TEX, we still
+%D support the original meaning, so
+%D
+%D \starttyping
+%D \language=1
+%D \stoptyping
+%D
+%D is a valid operation, where the relation between number and language depends on
+%D the order in installing languages.
+%D
+%D \showsetup{language}
+%D \showsetup{mainlanguage}
+%D
+%D Both commands take a predefined language identifier as argument. We can use \type
+%D {\mainlanguage[identifier]} for setting the (indeed) main language. This is the
+%D language used for translating labels like {\em figure} and {\em table}. The main
+%D language defaults to the current language.
+
+\newtoks \everylanguage
+
+\installcorenamespace{languagenumbers}
+
+\appendtoks
+ % we need to reassign the number because new patterns can be defined later on
+ % so let's hope not that many \setups happen during a run
+ \gletcsname\??languagenumbers\currentlanguage\endcsname\undefined
+\to \everysetuplanguage
+
+\def\lang_basics_synchronize_yes
+ {\zerocount % see below
+ \global\expandafter\chardef\csname\??languagenumbers\currentlanguage\endcsname
+ \clf_languagenumber
+ {\currentlanguage}%
+ {\defaultlanguage\currentlanguage}%
+ {\languageparameter\s!patterns}%
+ {\languageparameter\c!factor}%
+ \relax
+ \normallanguage\csname\??languagenumbers\currentlanguage\endcsname}
+
+\let\lang_basics_synchronize_nop\zerocount % not loaded anyway
+
+\letvalue{\??languagenumbers}\lang_basics_synchronize_nop % initime
+
+\appendtoks
+ \letvalue{\??languagenumbers}\lang_basics_synchronize_yes % runtime
+\to \everydump
+
+\def\lang_basics_synchronize
+ {\normallanguage\csname\??languagenumbers
+ \ifcsname\??languagenumbers\currentlanguage\endcsname
+ \currentlanguage
+ \fi
+ \endcsname
+ \relax
+ \the\everylanguage
+ \relax}
+
+\newcount\hyphenstate
+\newcount\hyphenminoffset
+
+% This is the old implementation: a hack that sets the min values, because we want
+% to keep the language set:
+%
+% \protected\def\nohyphens % nicer for url's
+% {\ifrelax\dohyphens
+% \protected\edef\dohyphens
+% {\hyphenminoffset\the\hyphenminoffset\relax
+% \lang_basics_synchronize_min_max}%
+% \fi
+% \hyphenminoffset\plusthousand
+% \lang_basics_synchronize_min_max}
+%
+% \let\dohyphens\relax
+
+% But this one is nicer because we do keep the language set as well as don't mess
+% with the min values (it's more efficient too). We might get some more bits in
+% this mode (engine specific).
+
+\exhyphenchar 45 % to permit breaking at explicit hyphens
+
+% \uchyph\plusone :
+
+% \chardef \completehyphenationmodecode \numexpr
+\permanent \integerdef \completehyphenationmodecode \numexpr
+ \normalhyphenationmodecode % \discretionary
+ + \automatichyphenationmodecode % -
+ + \explicithyphenationmodecode % \-
+ + \syllablehyphenationmodecode % pattern driven
+ + \uppercasehyphenationmodecode % replaces \uchyph
+ + \compoundhyphenationmodecode % replaces \compoundhyphenmode
+ % \strictstarthyphenationmodecode % replaces \hyphenationbounds (strict = original tex)
+ % \strictendhyphenationmodecode % replaces \hyphenationbounds (strict = original tex)
+ + \automaticpenaltyhyphenationmodecode % replaces \hyphenpenaltymode (otherwise use \exhyphenpenalty)
+ + \explicitpenaltyhyphenationmodecode % replaces \hyphenpenaltymode (otherwise use \exhyphenpenalty)
+ + \permitgluehyphenationmodecode % turn glue into kern in \discretionary
+ + \permitallhyphenationmodecode % okay, let's be even more tolerant
+ + \permitmathreplacehyphenationmodecode % and again we're more permissive
+\relax
+
+\permanent\protected\def\dohyphens{\hyphenationmode\completehyphenationmodecode}
+\permanent\protected\def\nohyphens{\hyphenationmode\zerocount}
+
+\permanent\protected\def\usehyphensparameter#1%
+ {\edef\p_hyphens{#1\c!hyphens}%
+ \ifx\p_hyhens\v!no
+ \nohyphens
+ \fi}
+
+\dohyphens
+
+%D The rest stays the same as in mkiv:
+
+\permanent\protected\def\lesshyphens
+ {\advance\hyphenminoffset\plusone
+ \lang_basics_synchronize_min_max}
+
+\permanent\protected\def\morehyphens
+ {\ifcase\hyphenminoffset \else
+ \advance\hyphenminoffset\minusone
+ \fi
+ \lang_basics_synchronize_min_max}
+
+\protected\def\lang_basics_synchronize_min_max % maybe store this at the lua end
+ {% these values are stored along with glyph nodes
+ \lefthyphenmin \numexpr0\languageparameter\s!lefthyphenmin +\hyphenminoffset\relax
+ \righthyphenmin\numexpr0\languageparameter\s!righthyphenmin+\hyphenminoffset\relax
+ \hyphenationmin\numexpr0\languageparameter\s!hyphenmin\relax
+ % these values are stored with the language (global!)
+ \prehyphenchar \languageparameter\s!righthyphenchar\relax
+ \posthyphenchar\languageparameter\s!lefthyphenchar \relax}
+
+\appendtoks
+ \lang_basics_synchronize_min_max
+\to \everylanguage
+
+\permanent\protected\def\unhyphenated
+ {\groupedcommand{\lefthyphenmin\maxdimen}\donothing}
+
+% \appendtoks
+% \setups[\languageparameter\c!setups]%
+% \to \everylanguage
+
+%D You can setup the default language to reset settings.
+
+\mutable\let\currentlanguagesetups\empty
+
+\appendtoks
+ \edef\currentlanguagesetups{\languageparameter\c!setups}%
+ \ifempty\currentlanguagesetups \else
+ \setups[\currentlanguagesetups]%
+ \fi
+\to \everylanguage
+
+% new
+
+\appendtoks
+ \usebidiparameter\languageparameter
+\to \everylanguage
+
+% this will move to core-spa !
+
+\appendtoks
+ \edef\p_spacing{\languageparameter\c!spacing}%
+ \ifx\p_spacing\v!broad
+ \nonfrenchspacing
+ \else
+ \frenchspacing
+ \fi
+\to \everylanguage
+
+% \mainlanguage[nl] \setuplanguage[nl][lefthyphen=,righthyphen=?]
+%
+% \dorecurse{100}{dit is toch wel een heel\normalhyphendiscretionary lang\normalhyphendiscretionary woord \recurselevel\ }
+% \dorecurse{100}{dit is toch wel een heellangwoord \recurselevel\ }
+
+% new experimental feature
+
+\permanent\protected\def\setuplanguages
+ {\setuplanguage[\s!default]}
+
+% \setuplanguages[\c!font=\v!auto]
+% \setuplanguage[\s!default][\c!font=\v!auto]
+% \setuplanguage[nl][\c!font=\v!auto]
+
+\appendtoks
+ \edef\p_language_font{\languageparameter\c!font}%
+ \ifempty\p_language_font
+ \orelse\ifx\p_language_font\v!auto
+ \doaddfeature\currentlanguage
+ \else
+ \doaddfeature\p_language_font
+ \fi
+\to \everylanguage
+
+%D Fast switcher
+
+\def\lang_basics_switch_asked
+ {\ifcsname\??languagelinked\askedlanguage\endcsname
+ \edef\askedlanguage{\lastnamedcs}%
+ \ifx\currentlanguage\askedlanguage \else
+ \setcurrentlanguage\currentmainlanguage\askedlanguage
+ \lang_basics_synchronize
+ \fi
+ \fi}
+
+\permanent\protected\def\uselanguageparameter#1%
+ {\edef\askedlanguage{#1\c!language}%
+ \ifempty\askedlanguage\else\lang_basics_switch_asked\fi}
+
+\permanent\protected\def\douselanguageparameter#1% fast setter
+ {\edef\askedlanguage{#1}%
+ \ifempty\askedlanguage\else\lang_basics_switch_asked\fi}
+
+\protected\def\lang_basics_set_current[#1]%
+ {\edef\askedlanguage{#1}%
+ \ifempty\askedlanguage\else\lang_basics_switch_asked\fi}
+
+\pushoverloadmode
+
+ \permanent\protected\def\language
+ {\doifelsenextoptionalcs\lang_basics_set_current\normallanguage}
+
+ \aliased\let\setlanguage\language % we make these synonyms
+
+ \aliased\let\patterns\gobbleoneargument
+
+\popoverloadmode
+
+\newcount\mainlanguagenumber
+
+%D Beware: you might need to use \type {\dontleavehmode} outside and|/|or \type {\par}
+%D inside the group!
+
+\permanent\protected\def\startlanguage
+ {\begingroup\language}
+
+\permanent\let\stoplanguage\endgroup
+
+\permanent\protected\def\mainlanguage[#1]%
+ {\edef\askedlanguage{#1}%
+ \ifempty\askedlanguage
+ \orelse\ifcsname\??languagelinked\askedlanguage\endcsname
+ %\edef\askedlanguage{\csname\??languagelinked\askedlanguage\endcsname}%
+ \edef\askedlanguage{\lastnamedcs}%
+ \ifx\currentlanguage\askedlanguage
+ \ifx\currentmainlanguage\askedlanguage \else
+ \setcurrentlanguage\askedlanguage\askedlanguage
+ \lang_basics_synchronize
+ \fi
+ \else
+ \setcurrentlanguage\askedlanguage\askedlanguage
+ \lang_basics_synchronize
+ \fi
+ \fi
+ \mainlanguagenumber\normallanguage}
+
+\appendtoks
+ \normallanguage\mainlanguagenumber
+\to \everybeforepagebody
+
+%D Used at all?
+
+\permanent\def\splitsequence#1#2%
+ {\doifelse{#1}\v!no{#2}{\doifelse{#1}\v!yes{\languageparameter\c!limittext}{#1}}}
+
+\permanent\def\splitsymbol#1%
+ {\splitsequence{#1}{\languageparameter\c!limittext}}
+
+%D Just like with subsentence boundary symbols, quotes placement depends on the
+%D current language, therefore we show the defaults here.
+%D
+%D \def\ShowLanguageValues [#1] [#2] #3 #4
+%D {\blank
+%D \startlinecorrection
+%D \vbox\bgroup
+%D \language[#1]
+%D \midaligned{\bf#2 subsentence symbol and quotes}
+%D \framed[width=\hsize,frame=off,topframe=on,bottomframe=on,offset=.5ex]
+%D {\hfil\quotation{#3 #4}\hfil\quote{#2}\hfil
+%D \startsubsentence\startsubsentence#3\stopsubsentence#4\stopsubsentence\hfil}
+%D \egroup
+%D \stoplinecorrection
+%D \blank}
+%D
+%D \ShowLanguageValues [af] [afrikaans] afrikaanse ...
+%D \ShowLanguageValues [ca] [catalan] catalan ...
+%D \ShowLanguageValues [cs] [czech] tjechisch tex
+%D \ShowLanguageValues [cs] [slovak] slowaakse ...
+%D \ShowLanguageValues [da] [danish] deense ...
+%D \ShowLanguageValues [de] [german] duitse degelijkheid
+%D \ShowLanguageValues [en] [english] engelse humor
+%D \ShowLanguageValues [et] [estonian] ...
+%D \ShowLanguageValues [fi] [finnish] finse ...
+%D \ShowLanguageValues [fr] [french] franse slag
+%D \ShowLanguageValues [it] [italian] italiaanse ...
+%D \ShowLanguageValues [la] [latin] latijnse missen
+%D \ShowLanguageValues [nl] [dutch] nederlandse zuinigheid
+%D \ShowLanguageValues [nb] [bokmal] noorse zalm
+%D \ShowLanguageValues [nn] [nnynorsk] noorse zalm
+%D \ShowLanguageValues [pl] [polish] poolse vlag
+%D \ShowLanguageValues [pt] [portuguese] portugese ...
+%D \ShowLanguageValues [es] [spanish] spaans benauwd
+%D \ShowLanguageValues [sv] [swedish] zweedse ...
+%D \ShowLanguageValues [tr] [turkish] turks fruit
+
+%D We support a lot of languages. These are specified and loaded in separate files,
+%D according to their roots. Here we only take care of (postponed) setting of the
+%D current language.
+%D
+%D \unprotect
+%D \placetable{The germanic languages (\type{lang-ger})}
+%D \starttable[||||]
+%D \HL
+%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR
+%D \HL
+%D \NC \s!nl \NC dutch \NC germanic \NC\FR
+%D \NC \s!en \NC english \NC germanic \NC\MR
+%D \NC \s!de \NC german \NC germanic \NC\MR
+%D \NC \s!da \NC danish \NC germanic \NC\MR
+%D \NC \s!sv \NC swedish \NC germanic \NC\MR
+%D \NC \s!af \NC afrikaans \NC germanic \NC\MR
+%D \NC \s!nb \NC bokmal \NC germanic \NC\LR
+%D \NC \s!nn \NC nynorsk \NC germanic \NC\LR
+%D \HL
+%D \stoptable
+%D \protect
+%D
+%D \unprotect
+%D \placetable{The italic languages (\type{lang-ita})}
+%D \starttable[||||]
+%D \HL
+%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR
+%D \HL
+%D \NC \s!fr \NC french \NC italic \NC\FR
+%D \NC \s!ca \NC catalan \NC italic \NC\MR
+%D \NC \s!es \NC spanish \NC italic \NC\MR
+%D \NC \s!it \NC italian \NC italic \NC\MR
+%D \NC \s!la \NC latin \NC italic \NC\MR
+%D \NC \s!pt \NC portuguese \NC italic \NC\LR
+%D \HL
+%D \stoptable
+%D \protect
+%D
+%D \unprotect
+%D \placetable{The slavic languages (\type{lang-sla})}
+%D \starttable[||||]
+%D \HL
+%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR
+%D \HL
+%D \NC \s!pl \NC polish \NC slavic \NC\FR
+%D \NC \s!cs \NC czech \NC slavic \NC\MR
+%D \NC \s!sk \NC slavik \NC slavic \NC\LR
+%D \HL
+%D \stoptable
+%D \protect
+%D \unprotect
+%D
+%D \placetable{The altaic languages (\type{lang-alt})}
+%D \starttable[||||]
+%D \HL
+%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR
+%D \HL
+%D \NC \s!tr \NC turkish \NC altaic \NC\SR
+%D \HL
+%D \stoptable
+%D
+%D \placetable{The uralic languages (\type{lang-ura})}
+%D \starttable[||||]
+%D \HL
+%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR
+%D \HL
+%D \NC \s!fi \NC finnish \NC uralic \NC\SR
+%D \HL
+%D \stoptable
+%D \protect
+
+\permanent\protected\def\nopatterns{\normallanguage\minusone}
+
+%D We default to the language belonging to the interface. This is one of the few
+%D places outside the interface modules where \type {\startinterface} is used.
+
+\setupcurrentlanguage[\s!en]
+
+\permanent\protected\def\initializemainlanguage
+ {\mainlanguage[\currentlanguage]%
+ \showmessage\m!languages9\currentlanguage}
+
+%D New:
+
+\permanent\let\stopexceptions\relax
+
+\tolerant\permanent\protected\def\startexceptions[#1]#:#2\stopexceptions
+ {\begingroup
+ \edef\askedlanguage{\reallanguagetag{#1}}%
+ \ifempty\askedlanguage
+ \let\askedlanguage\currentlanguage
+ \fi
+ \clf_setlanguageexceptions{\askedlanguage}{#2}%
+ \endgroup}
+
+\pushoverloadmode
+
+\permanent\protected\def\hyphenation
+ {\clf_setlanguageexceptions{\currentlanguage}}
+
+\popoverloadmode
+
+%D New:
+
+\permanent\protected\def\traceddiscretionary#1#%
+ {\dontleavehmode\lang_basics_traced_discretionary{#1}}
+
+\protected\def\lang_basics_traced_discretionary#1#2#3#4%
+ {\normaldiscretionary#1{\darkred#2}{\darkgreen#3}{\darkblue#4}}
+
+\installtextracker
+ {discretionaries}
+ {\enforced\let\discretionary\traceddiscretionary}
+ {\enforced\let\discretionary\normaldiscretionary}
+
+\permanent\protected\def\samplediscretionary
+ {\traceddiscretionary
+ {pre\clf_currentprehyphenchar}%
+ {\clf_currentposthyphenchar post}%
+ {replace}}
+
+%D A typical \LMTX\ feature:
+%D
+%D \starttyping
+%D whatever\par
+%D {\nokerning whatever}\par
+%D efficient ff fi\par
+%D {\noligaturing efficient ff fi}\par
+%D {\nokerning\noligaturing efficient ff fi}\par
+%D {e{\noligaturing ffi}cient}\par
+%D {ef{\noleftligaturing f}icient ff fi}\par
+%D {ef{\norightligaturing f}icient ff fi}\par
+%D \stoptyping
+
+\immutable\chardef\nokerningcode \numexpr\noleftkerncode +\norightkerncode \relax
+\immutable\chardef\noligaturingcode\numexpr\noleftligaturecode+\norightligaturecode\relax
+
+\permanent\protected\def\nokerning {\bitwiseflip\glyphoptions\nokerningcode}
+\permanent\protected\def\noligaturing{\bitwiseflip\glyphoptions\noligaturingcode}
+
+\permanent\protected\def\noleftkerning {\bitwiseflip\glyphoptions\noleftkerncode}
+\permanent\protected\def\noleftligaturing {\bitwiseflip\glyphoptions\noleftligaturecode}
+\permanent\protected\def\norightkerning {\bitwiseflip\glyphoptions\norightkerncode}
+\permanent\protected\def\norightligaturing{\bitwiseflip\glyphoptions\norightligaturecode}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-lab.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-lab.mkxl
new file mode 100644
index 00000000000..369049ed162
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-lab.mkxl
@@ -0,0 +1,377 @@
+%D \module
+%D [ file=lang-lab,
+%D version=1997.08.27,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Labels,
+%D author=Hans Hagen / Tobias Burnus,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Language Macros / Labels}
+
+\registerctxluafile{lang-lab}{}
+\registerctxluafile{lang-txt}{}
+
+\unprotect
+
+%D Left-overs:
+
+\ifdefined\sixperemspace \else
+ \permanent\def\sixperemspace{\normalUchar"2006}
+\fi
+
+%D In this module we deal with language dependant labels and prefixes, like in {\em
+%D Figure~12} and {\em Chapter 1}. In this file we set the default values. Users can
+%D easily overrule these.
+%D
+%D This module is dedicated to the grandfather of Tobias Burnus, who's extensive
+%D languages oriented library helped us a lot in finding the right translations. All
+%D those labels are collected in files that reflect their common ancestor.
+%D
+%D Not all languages can be satisfied with the labeling mechanism as provided here.
+%D Chinese for instance put a label in front as well as after a part number. This is
+%D why the current implementation of labels supports two labels too.
+
+%D \macros
+%D {setupheadtext, setuplabeltext}
+%D
+%D First we present some macros that deal with what we will call head and label
+%D texts. Such texts are defines by:
+%D
+%D \showsetup{setupheadtext}
+%D \showsetup{setuplabeltext}
+%D
+%D A regular \CONTEXT\ stores some 1500 labels at most.
+%D
+%D These commands accept all kind of inputs:
+%D
+%D \starttyping
+%D \setuplabeltext [language] [labellabel=text]
+%D \setuplabeltext [language] [labellabel=text,labellabel=text,...]
+%D \setuplabeltext [labellabel=text]
+%D \setuplabeltext [labellabel=text,labellabel=text,...]
+%D \stoptyping
+%D
+%D The last two cases concern the current language.
+
+\installcorenamespace{label}
+
+% \let\currentlabelcategory\empty
+
+\permanent\tolerant\protected\def\definelabelclass[#1]#*[#2]%
+ {\normalexpanded
+ {\lang_labels_define_class_indeed
+ {#1}%
+ {\ifchknum#2\or#2\else\zerocount\fi}%
+ \expandafter\noexpand\csname lang_labels_#1_text_1\endcsname
+ \expandafter\noexpand\csname #1language\endcsname
+ \expandafter\noexpand\csname lang_labels_#1_text_2\endcsname
+ \expandafter\noexpand\csname left#1text\endcsname
+ \expandafter\noexpand\csname right#1text\endcsname
+ \expandafter\noexpand\csname #1texts\endcsname
+ \expandafter\noexpand\csname #1text\endcsname}}
+
+% hm, not interfaced
+
+\mutable\let\thetextprefix\empty
+
+\mutable\let\currenttextprefixclass\s!unknown
+\mutable\let\currenttextprefixname \empty
+\mutable\let\currenttextprefixtag \s!unknown
+
+\protected\def\lang_labels_define_class_indeed#1#2#3#4#5#6#7#8#9%
+ {\permanent\instance\protected\defcsname setup#1text\endcsname{\protecttextprefixes#2\def\currenttextprefixclass{#1}\lang_labels_text_prefix_setup}%
+ \permanent\instance\protected\defcsname preset#1text\endcsname{\protecttextprefixes 1\def\currenttextprefixclass{#1}\lang_labels_text_prefix_setup}%
+ \permanent\instance\protected\defcsname copy#1text\endcsname{\protecttextprefixes 1\def\currenttextprefixclass{#1}\lang_labels_text_prefix_copy }%
+ \permanent\instance\protected\defcsname start#1text\endcsname{\protecttextprefixes 1\def\currenttextprefixclass{#1}\lang_labels_text_prefix_start[#1]}%
+ \permanent\instance \letcsname stop#1text\endcsname \relax
+ \permanent\instance\def#4{\reallanguagetag{\defaultlanguage\currentmainlanguage}}%
+ \ifnum#2=\plustwo % used for math and tags
+ \instance\def#3{#5#4}%
+% \instance\def#5##1##2% ##1=language
+% {\ifcsname\??label\currentlabelcategory#1:##1:##2\endcsname
+% \lastnamedcs
+% \orelse\ifcsname\??label#1:##1:##2\endcsname
+% \lastnamedcs
+% \orelse\ifcsname\??language##1\s!default\endcsname
+% \expandafter#5\lastnamedcs{##2}%
+% \orelse\ifcsname\??label\currentlabelcategory#1:##2\endcsname
+% \lastnamedcs
+% \orelse\ifcsname\??label#1:##2\endcsname
+% \lastnamedcs
+% \orelse\ifcsname\??label\currentlabelcategory#1:\s!en:##2\endcsname
+% \lastnamedcs
+% \orelse\ifcsname\??label#1:\s!en:##2\endcsname
+% \lastnamedcs
+% \else
+% ##2%
+% \fi}%
+ \instance\def#5##1##2% ##1=language
+ {\ifcsname\??label#1:##1:##2\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??language##1\s!default\endcsname
+ \expandafter#5\lastnamedcs{##2}%
+ \orelse\ifcsname\??label#1:##2\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??label#1:\s!en:##2\endcsname
+ \lastnamedcs
+ \else
+ ##2%
+ \fi}%
+ \permanent\instance\let#6\gobbleoneargument
+ \permanent\instance\let#7\gobbleoneargument
+ \permanent\instance\let#8\gobbletwoarguments
+ \permanent\instance\let#9#3%
+ \else
+ \instance\protected\def#3{#5#4}%
+ \instance\protected\def#5##1##2%
+ {\ifcsname\??label#1:##1:##2\endcsname
+ \enforced\expandafter\let\expandafter\thetextprefix\lastnamedcs
+ \orelse\ifcsname\??language#4\s!default\endcsname
+ \expandafter#5\lastnamedcs{##2}%
+ \orelse\ifcsname\??label#1:##2\endcsname
+ \enforced\expandafter\let\expandafter\thetextprefix\lastnamedcs
+ \orelse\ifcsname\??label#1:\s!en:##2\endcsname
+ \enforced\expandafter\let\expandafter\thetextprefix\lastnamedcs
+ \else
+ \enforced\let\thetextprefix\dummytextprefix
+ \fi}%
+ \permanent \instance\protected\def#6##1{#3{##1}\expandafter\firstoftwoarguments \thetextprefix}% \flushleftlabelclass
+ \permanent\instance\protected\def#7##1{#3{##1}\expandafter\secondoftwoarguments \thetextprefix}% \flushrightlabelclass
+ \permanent\instance\protected\def#8##1{#3{##1}\expandafter\lang_labels_flush_both\thetextprefix}% \flushbothlabelclass #2
+ \permanent\instance\protected\def#9##1{#3{##1}\expandafter\firstoftwoarguments \thetextprefix}% \flushleftlabelclass
+ \fi
+ \appendtoks
+ \enforced\permanent\instance\let#6\firstofoneargument % to be checked
+ \enforced\permanent\instance\let#7\firstofoneargument % to be checked
+ \enforced\permanent\instance\let#8\firstofoneargument % to be checked
+ \enforced\permanent\instance\let#9\firstofoneargument % to be checked
+ \to \everysimplifycommands}
+
+% why not lang_* ?
+
+% \let\flushleftlabelclass \firstoftwoarguments
+% \let\flushrightlabelclass\secondoftwoarguments
+
+\permanent\def\lang_labels_flush_both#1#2#3{#1#3#2}
+
+\permanent\def\dummytextprefix{\empty\empty}
+
+%D \macros
+%D {headtext, labeltext, leftlabeltext, rightlabeltext, labeltexts}
+%D
+%D Once defined, head and label texts can be called upon using:
+%D
+%D \showsetup{headtext}
+%D \showsetup{labeltext}
+%D
+%D \macros
+%D {presetheadtext,presetlabeltext}
+%D
+%D These macros enable us to automatically define head and label texts without
+%D replacing predefined ones. They are internal macros.
+
+\appendtoks \enforced\let\labellanguage\currentlanguage \to \everycurrentdate
+
+\newconstant\protecttextprefixes
+
+\tolerant\def\lang_labels_text_prefix_start[#1]#*[#2]#*[#3]% class language name
+ {\ifarguments\or\or
+ \edef\currenttextprefixtag{\reallanguagetag\currentmainlanguage}%
+ \edef\currenttextprefixname{#2}%
+ \else
+ \edef\currenttextprefixtag{\reallanguagetag{#2}}%
+ \edef\currenttextprefixname{#3}%
+ \fi
+ \grabuntil{stop#1text}\lang_labels_text_prefix_start_indeed}
+
+\def\lang_labels_text_prefix_start_indeed#1% text (not special checking done here yet, only for long texts anyway)
+ {\edefcsname\??label\currenttextprefixclass:\currenttextprefixtag:\currenttextprefixname\endcsname{{\clf_strip{#1}}\empty}}
+
+\tolerant\def\lang_labels_text_prefix_setup[#1]#*[#2]%
+ {\ifarguments\or
+ \edef\currenttextprefixtag{\reallanguagetag\currentmainlanguage}%
+ \processcommalist[#1]\lang_labels_text_prefix_setup_indeed
+ \else
+ \edef\currenttextprefixtag{\reallanguagetag{#1}}%
+ \processcommalist[#2]\lang_labels_text_prefix_setup_indeed
+ \fi}
+
+\def\lang_labels_text_prefix_setup_indeed#1%
+ {\lang_labels_text_prefix_assign[#1]}
+
+\def\lang_labels_text_prefix_assign[#1=#2]%
+ {\lang_labels_text_prefix_assign_indeed{#1}[#2,,]}
+
+\def\lang_labels_text_prefix_assign_indeed#1%
+ {\ifcase\protecttextprefixes
+ % no checking
+ \expandafter\lang_labels_text_prefix_assign_yes
+ \or
+ % checking
+ \ifcsname\??label\currenttextprefixclass:\currenttextprefixtag:#1\endcsname
+ \expandafter\expandafter\expandafter\lang_labels_text_prefix_assign_nop
+ \else
+ \expandafter\expandafter\expandafter\lang_labels_text_prefix_assign_yes
+ \fi
+ \or
+ % simple assignment (a bit overkill but it fits in the whole)
+ \expandafter\lang_labels_text_prefix_assign_dumb
+ \fi{#1}}
+
+\let\m_lang_labels_left \empty
+\let\m_lang_labels_right\empty
+
+\def\lang_labels_text_prefix_assign_yes#1[#2,#3,#4]%
+ {\def\m_lang_labels_left {#2}% no longer an edef ... else \Word undefined expansion issues
+ \def\m_lang_labels_right{#3}% no longer an edef ... else \Word undefined expansion issues
+ \defcsname\??label\currenttextprefixclass:\currenttextprefixtag:#1%
+ \ifempty\m_lang_labels_right
+ \ifempty\m_lang_labels_left
+ \endcsname{\empty\empty}%
+ \else
+ \endcsname{{#2}\empty}%
+ \fi
+ \else
+ \endcsname{{#2}{#3}}%
+ \fi}
+
+\def\lang_labels_text_prefix_assign_nop#1[#2]%
+ {}
+
+\def\lang_labels_text_prefix_assign_dumb#1[#2,#3]%
+ {\defcsname\??label\currenttextprefixclass:\currenttextprefixtag:#1\endcsname{#2}}
+
+\permanent\protected\def\setlabeltextpair#1#2#3#4#5% a fast one for usage at the Lua end
+ {%\writestatus{!!!!}{#1:\reallanguagetag{#2}:#3}%
+ \defcsname\??label#1:\reallanguagetag{#2}:#3\endcsname{{#4}{#5}}} % class tag key left right
+
+\tolerant\def\lang_labels_text_prefix_copy[#1]#*[#2]%
+ {\ifarguments\or
+ \edef\currenttextprefixtag{\reallanguagetag\currentmainlanguage}%
+ \processcommalist[#1]\lang_labels_text_prefix_copy_indeed
+ \else
+ \edef\currenttextprefixtag{\reallanguagetag{#1}}%
+ \processcommalist[#2]\lang_labels_text_prefix_copy_indeed
+ \fi}
+
+\def\lang_labels_text_prefix_copy_indeed#1%
+ {\lang_labels_text_prefix_copy_pair[#1]}
+
+\def\lang_labels_text_prefix_copy_pair[#1=#2]%
+ {\lang_labels_text_prefix_copy_pair_indeed{#1}[#2,,]}
+
+% \def\lang_labels_text_prefix_copy_pair_indeed#1[#2,#3]%
+% {\expandafter\let
+% \csname\??label\currenttextprefixclass:\currenttextprefixtag:#1\expandafter\endcsname
+% \csname\??label\currenttextprefixclass:\currenttextprefixtag:#2\endcsname}
+%
+% this delays the aliasing so that we can switch mainlanguage in between
+
+\def\lang_labels_text_prefix_copy_pair_indeed#1[#2,#3]%
+ {\edefcsname\??label\currenttextprefixclass:#1\endcsname
+ {{\noexpand\csname\??label\currenttextprefixclass:\noexpand\reallanguagetag\noexpand\currentmainlanguage:#2\endcsname}{}}}
+
+\definelabelclass [head] [0] % titles
+\definelabelclass [label] [0] % texts
+\definelabelclass [mathlabel] [0] % functions
+\definelabelclass [taglabel] [2] % tags
+
+\clf_definelabels{head}{titles}\s!true\relax
+\clf_definelabels{label}{texts}\s!true\relax
+\clf_definelabels{mathlabel}{functions}\s!false\relax
+\clf_definelabels{taglabel}{tags}\s!false\relax
+
+%D \macros
+%D {translate}
+%D
+%D Sometimes macros contain language specific words that are to be typeset. Such
+%D macros can be made (more) language independant by using:
+%D
+%D \showsetup{translate}
+%D
+%D like for instance:
+%D
+%D \starttyping
+%D \translate[en=something,nl=iets]
+%D \stoptyping
+%D
+%D which expands to {\em something} or {\em iets}, depending on de current language.
+
+\installcorenamespace{translation}
+
+\permanent\tolerant\protected\def\translate[#1]%
+ {\getparameters[\??translation][#1]%
+ \ifcsname\??translation\currentlanguage\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??translation\s!en\endcsname
+ \lastnamedcs
+ \else
+ [translation #1]%
+ \fi}
+
+%D When used without argument, the last defined values are used. This enables
+%D repetitive use like
+%D
+%D \starttyping
+%D \en \translate\ means \nl \translate
+%D \stoptyping
+
+%D \macros
+%D {assigntranslation}
+%D
+%D This macro is a system macro, and can be used to assign a translation to a macro.
+%D Its form is:
+%D
+%D \starttyping
+%D \assigntranslation[en=something,nl=iets]\to\command
+%D \stoptyping
+
+% nicer is \edef\command{\translated[...]} ... todo
+
+\permanent\protected\def\assigntranslation[#1]\to#2% bad, this \to
+ {\getparameters[\??translation][#1]%
+ \edef#2{\begincsname\??translation\currentlanguage\endcsname}}
+
+%D \macros
+%D {commalistsentence}
+%D
+%D Redone in \LUA:
+%D
+%D \startbuffer
+%D \commalistsentence[aap,noot,mies]
+%D \commalistsentence[aap,noot]
+%D \commalistsentence[aap]
+%D \commalistsentence[a,b,c]
+%D \commalistsentence[a,b,c][{ \& },{ and }]
+%D \commalistsentence[a,b,c][+,-]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+\permanent\tolerant\protected\def\commalistsentence[#1]#*[#2]%
+ {\clf_concatcommalist
+ text {#1}%
+ separators {#2}%
+ separator {\detokenize\expandafter{\normalexpanded{\labeltext{and-1}}}}%
+ last {\detokenize\expandafter{\normalexpanded{\labeltext{and-2}}}}%
+ \relax}
+
+\setuplabeltext [\s!nl] [and-1={{, }}, and-2={{ en }}] % 1, 2 en 3
+\setuplabeltext [\s!en] [and-1={{, }}, and-2={{, }}] % 1, 2, 3
+\setuplabeltext [\s!de] [and-1={{, }}, and-2={{ und }}] % 1, 2 und 3
+\setuplabeltext [\s!hr] [and-1={{, }}, and-2={{ i }}] % 1, 2 i 3
+
+%D Goodie (handy at \LUA\ end):
+
+\permanent\protected\def\LABELTEXT#1{\WORD{\labeltext{#1}}} % only for simple cases!
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-mis.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-mis.mkxl
new file mode 100644
index 00000000000..7b6c9085736
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-mis.mkxl
@@ -0,0 +1,586 @@
+%D \module
+%D [ file=lang-mis,
+%D version=1997.03.20, % used to be supp-lan.tex
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Compounds,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This one will be updated stepwise to \LMTX. See lang-mis.mkiv for previous
+%D implementations and removed code.
+
+\writestatus{loading}{ConTeXt Language Macros / Compounds}
+
+%D More or less replaced.
+
+%D \gdef\starttest#1\stoptest{\starttabulate[|l|l|p|]#1\stoptabulate}
+%D \gdef\test #1{\NC\detokenize{#1}\NC\hyphenatedword{#1}\NC#1\NC\NR}
+
+\unprotect
+
+%D One of \TEX's strong points in building paragraphs is the way hyphenations are
+%D handled. Although for real good hyphenation of non||english languages some
+%D extensions to the program are needed, fairly good results can be reached with the
+%D standard mechanisms and an additional macro, at least in Dutch.
+%D
+%D \CONTEXT\ originates in the wish to typeset educational materials, especially in
+%D a technical environment. In production oriented environments, a lot of compound
+%D words are used. Because the Dutch language poses no limits on combining words, we
+%D often favor putting dashes between those words, because it facilitates reading,
+%D at least for those who are not that accustomed to it.
+%D
+%D In \TEX\ compound words, separated by a hyphen, are not hyphenated at all. In
+%D spite of the multiple pass paragraph typesetting this can lead to parts of words
+%D sticking into the margin. The solution lays in saying \type
+%D {spoelwater||terugwinunit} instead of \type {spoelwater-terugwinunit}. By using a
+%D one character command like \type {|}, delimited by the same character \type {|},
+%D we get ourselves both a decent visualization (in \TEXEDIT\ and colored verbatim
+%D we color these commands yellow) and an efficient way of combining words.
+%D
+%D The sequence \type{||} simply leads to two words connected by a hyphen. Because
+%D we want to distinguish such a hyphen from the one inserted when \TEX\ hyphenates
+%D a word, we use a bit longer one.
+%D
+%D \hyphenation {spoel-wa-ter te-rug-win-unit}
+%D
+%D \starttest
+%D \test {spoelwater||terugwinunit}
+%D \stoptest
+%D
+%D As we already said, the \type{|} is a command. This commands accepts an optional
+%D argument before it's delimiter, which is also a \type{|}.
+%D
+%D \hyphenation {po-ly-meer che-mie}
+%D
+%D \starttest
+%D \test {polymeer|*|chemie}
+%D \stoptest
+%D
+%D Arguments like \type{*} are not interpreted and inserted directly, in contrary to
+%D arguments like:
+%D
+%D \starttest
+%D \test {polymeer|~|chemie}
+%D \test {|(|polymeer|)|chemie}
+%D \test {polymeer|(|chemie|)| }
+%D \stoptest
+%D
+%D Although such situations seldom occur |<|we typeset thousands of pages before we
+%D encountered one that forced us to enhance this mechanism|>| we also have to take
+%D care of comma's.
+%D
+%D \hyphenation {uit-stel-len}
+%D
+%D \starttest
+%D \test {op||, in|| en uitstellen}
+%D \stoptest
+%D
+%D The next special case (concerning quotes) was brought to my attention by Piet
+%D Tutelaers, one of the driving forces behind rebuilding hyphenation patterns for
+%D the dutch language.\footnote{In 1996 the spelling of the dutch language has been
+%D slightly reformed which made this topic actual again.} We'll also take care of
+%D this case.
+%D
+%D \starttest
+%D \test {AOW|'|er}
+%D \test {cd|'|tje}
+%D \test {ex|-|PTT|'|er}
+%D \test {rock|-|'n|-|roller}
+%D \stoptest
+%D
+%D Tobias Burnus pointed out that I should also support something like
+%D
+%D \starttest
+%D \test {well|_|known}
+%D \stoptest
+%D
+%D to stress the compoundness of hyphenated words.
+%D
+%D Of course we also have to take care of the special case:
+%D
+%D \starttest
+%D \test {text||color and ||font}
+%D \stoptest
+
+%D \macros
+%D {installdiscretionaries}
+%D
+%D The mechanism described here is one of the older inner parts of \CONTEXT. The
+%D most recent extensions concerns some special cases as well as the possibility to
+%D install other characters as delimiters. The prefered way of specifying compound
+%D words is using \type{||}, which is installed by:
+%D
+%D \starttyping
+%D \installdiscretionary | -
+%D \stoptyping
+%D
+%D Some alternative definitions are:
+%D
+%D \startbuffer
+%D \installdiscretionary * -
+%D \installdiscretionary + -
+%D \installdiscretionary / -
+%D \installdiscretionary ~ -
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D after which we can say:
+%D
+%D \start \getbuffer
+%D \starttest
+%D \test {test**test**test}
+%D \test {test++test++test}
+%D \test {test//test//test}
+%D \test {test~~test~~test}
+%D \stoptest
+%D \stop
+
+%D \macros
+%D {compoundhyphen}
+%D
+%D Now let's go to the macros. First we define some variables. In the main \CONTEXT\
+%D modules these can be tuned by a setup command. Watch the (maybe) better looking
+%D compound hyphen.
+
+% hm why ex
+
+\ifdefined\compoundhyphen \else
+ \permanent\protected\def\compoundhyphen{\hbox{-\kern-.10775\emwidth-}} % .25\exheight
+\fi
+
+%D The last two variables are needed for subsentences |<|like this one|>| which we
+%D did not yet mention. We want to enable breaking but at the same time don't want
+%D compound characters like |-| or || to be separated from the words. \TEX\ hackers
+%D will recognise the next two macro's:
+
+\ifdefined\prewordbreak \else \permanent\protected\def\prewordbreak {\penalty\plustenthousand\hskip\zeropoint\relax} \fi
+\ifdefined\postwordbreak\else \permanent\protected\def\postwordbreak {\penalty\zerocount \hskip\zeropoint\relax} \fi
+\ifdefined\hspaceamount \else \def\hspaceamount#1#2{.16667\emwidth} \fi % will be overloaded
+
+%frozen\protected\def\permithyphenation{\ifhmode\prewordbreak\fi} % doesn't remove spaces
+\permanent\protected\def\permithyphenation{\ifhmode\wordboundary\fi} % doesn't remove spaces
+
+%D \macros
+%D {beginofsubsentence,endofsubsentence,
+%D beginofsubsentencespacing,endofsubsentencespacing}
+%D
+%D In the previous macros we provided two hooks which can be used to support nested
+%D sub||sentences. In \CONTEXT\ these hooks are used to insert a small space when
+%D needed.
+
+%D The following piece of code is a torture test compound handling. The \type
+%D {\relax} before the \type {\ifmmode} is needed because of the alignment scanner
+%D (in \ETEX\ this problem is not present because there a protected macro is not
+%D expanded. Thanks to Tobias Burnus for providing this example.
+%D
+%D \startformula
+%D \left|f(x_n)-{1\over2}\right| =
+%D {\cases{|{1\over2}-x_n| &for $0\le x_n < {1\over2}$\cr
+%D |x_n-{1\over2}| &for ${1\over2}<x_n\le1$ \cr}}
+%D \stopformula
+
+\installcorenamespace{discretionaryaction}
+\installcorenamespace{discretionarytext}
+\installcorenamespace{discretionarymath}
+\installcorenamespace{discretionaryboth}
+\installcorenamespace{discretionarymode}
+
+\permanent\protected\def\installdiscretionary#1#2%
+ {\edefcsname\??discretionarymath\detokenize{#1}\endcsname{\detokenize{#1}}% ?
+ \defcsname \??discretionarytext\detokenize{#1}\endcsname{#2}%
+ \defcsname \??discretionaryboth\detokenize{#1}\endcsname{\lang_discretionaries_command#1}%
+ \scratchcounter\expandafter`\detokenize{#1}%
+ \expandafter\uedcatcodecommand\expandafter\ctxcatcodes\expandafter\scratchcounter\csname\??discretionaryboth\detokenize{#1}\endcsname}
+
+\permanent\protected\def\handlemathmodediscretionary#1{\ifcsname\??discretionarymath\detokenize{#1}\endcsname\lastnamedcs}
+\permanent\protected\def\handletextmodediscretionary#1{\ifcsname\??discretionarytext\detokenize{#1}\endcsname\lastnamedcs}
+
+\permanent\protected\def\installdiscretionaries#1#2{\writestatus\m!system{use \string \installdiscretionary}} % obsolete
+
+\setnewconstant\discretionarymode\plusone
+
+\permanent\protected\def\ignorediscretionaries{\discretionarymode\zerocount}
+\permanent\protected\def\obeydiscretionaries {\discretionarymode\plusone}
+
+\def\lang_discretionaries_command
+ {% if direct if, we need \relax for lookahead in math mode
+ \csname\??discretionarymode
+ \ifcase\discretionarymode
+ n%
+ \orelse\ifmmode
+ m%
+ \else
+ t%
+ \fi
+ \endcsname}
+
+\setvalue{\??discretionarymode n}#1%
+ {\detokenize{#1}}
+
+%D The macro \type{\lang_discretionaries_check_before} takes care of loners like
+%D \type{||word}, while it counterpart \type {\lang_discretionaries_check_after} is
+%D responsible for handling the comma.
+
+\newsignal\d_lang_discretionaries_breakpoint % todo: never consulted so maybe obsolete
+
+\newconditional\punctafterdiscretionary
+\newconditional\spaceafterdiscretionary
+
+\def\lang_discretionaries_check_before %is used grouped
+ {\ifvmode
+ \dontleavehmode
+ \fi
+ \ifhmode
+ %\begingroup
+ %\setbox\scratchbox\lastbox
+ %\ifzeropt\wd\scratchbox
+ % \box\scratchbox\relax
+ % \endgroup
+ % \let\postwordbreak\prewordbreak
+ %\else
+ % \box\scratchbox\relax
+ % \endgroup
+ %\fi
+ \fi}
+
+\def\lang_discretionaries_check_after
+ {\setfalse\punctafterdiscretionary
+ \setfalse\spaceafterdiscretionary
+ \ifx\blankspace\nexttoken \settrue \spaceafterdiscretionary \orelse
+ \ifx\space \nexttoken \settrue \spaceafterdiscretionary \orelse
+ \ifx .\nexttoken \settrue \punctafterdiscretionary \orelse
+ \ifx ,\nexttoken \settrue \punctafterdiscretionary \orelse
+ \ifx :\nexttoken \settrue \punctafterdiscretionary \orelse
+ \ifx ;\nexttoken \settrue \punctafterdiscretionary \fi}
+
+\letvalue{\??discretionarymode m}\handlemathmodediscretionary
+
+\setvalue{\??discretionarymode t}#1%
+ {\bgroup
+ \let\nextnextnext\egroup
+ \def\next##1#1%
+ {\def\next{\activedododotextmodediscretionary#1{##1}}%
+ \futurelet\nexttoken\next}%
+ \next}
+
+\let\lang_discretionaries_token \relax
+\let\lang_discretionaries_action\relax
+
+\permanent\protected\def\activedododotextmodediscretionary#1#2%
+ {\edef\lang_discretionaries_token{\detokenize{#2}}%
+ \def\lang_discretionaries_action{\handletextmodediscretionary{#1}}%
+ \lang_discretionaries_check_after
+ \ifempty\lang_discretionaries_token
+ \ifx#1\nexttoken % takes care of ||| and +++ and ......
+ \ifcsname\??discretionaryaction\string#1\endcsname
+ \lastnamedcs
+ \orelse\ifconditional\spaceafterdiscretionary
+ %\prewordbreak\hbox{\string#1}\relax
+ \wordboundary\hbox{\string#1}\relax
+ \orelse\ifconditional\punctafterdiscretionary
+ %\prewordbreak\hbox{\string#1}\relax
+ \wordboundary\hbox{\string#1}\wordboundary
+ \else
+ %\prewordbreak\hbox{\string#1}\prewordbreak
+ \wordboundary\hbox{\string#1}\wordboundary
+ \fi
+ \def\nextnextnext{\afterassignment\egroup\let\next=}%
+ \else
+ \lang_discretionaries_check_before
+ % the next line has been changed (20050203)
+ % \prewordbreak\hbox{\lang_discretionaries_action\nexttoken}\postwordbreak
+ % but an hbox blocks a possible \discretionary
+ \ifcsname\??discretionaryaction\endcsname
+ \lastnamedcs
+ \orelse\ifconditional\spaceafterdiscretionary
+ %\prewordbreak\lang_discretionaries_action\relax
+ \wordboundary\lang_discretionaries_action\relax
+ \orelse\ifconditional\punctafterdiscretionary
+ %\prewordbreak\lang_discretionaries_action\relax
+ \wordboundary\lang_discretionaries_action\relax
+ \else
+ %\prewordbreak\lang_discretionaries_action\prewordbreak
+ \wordboundary\lang_discretionaries_action\wordboundary
+ \fi
+ \fi
+ \orelse\ifcsname\??discretionaryaction\lang_discretionaries_token\endcsname
+ \lastnamedcs
+ \else
+ \lang_discretionaries_check_before
+ \ifconditional\spaceafterdiscretionary
+ %\prewordbreak\hbox{#2}\relax
+ \wordboundary\hbox{#2}\relax
+ \orelse\ifconditional\punctafterdiscretionary
+ %\prewordbreak\hbox{#2}\relax
+ \wordboundary\hbox{#2}\relax
+ \else
+ %\prewordbreak\discretionary{\hbox{#2}}{}{\hbox{#2}}\postwordbreak
+ \wordboundary\discretionary{\hbox{#2}}{}{\hbox{#2}}\wordboundary
+ %\discretionary options \plusthree{\hbox{#2}}{}{\hbox{#2}}%
+ \fi
+ \fi
+ \nextnextnext}
+
+%D \macros
+%D {directdiscretionary}
+%D
+%D In those situations where the nature of characters is less predictable, we can
+%D use the more direct approach:
+
+\permanent\protected\def\directdiscretionary
+ {\csname\??discretionarymode
+ \ifcase\discretionarymode
+ n%
+ \else
+ d%
+ \fi
+ \endcsname}
+
+\permanent\protected\def\indirectdiscretionary
+ {\csname\??discretionarymode
+ \ifcase\discretionarymode
+ n%
+ \else
+ i%
+ \fi
+ \endcsname}
+
+\setuvalue{\??discretionarymode d}#1%
+ {\edef\lang_discretionaries_token{\detokenize{#1}}%
+ \let\lang_discretionaries_action\compoundhyphen
+ \ifcsname\??discretionaryaction\lang_discretionaries_token\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\indirectdiscretionary
+ \fi{#1}}
+
+\setuvalue{\??discretionarymode i}#1%
+ %{\prewordbreak\discretionary{\hbox{#1}}{}{\hbox{#1}}\postwordbreak}
+ {\wordboundary\discretionary{\hbox{#1}}{}{\hbox{#1}}\wordboundary}
+ %{\discretionary options \plusthree{\hbox{#1}}{}{\hbox{#1}}}
+
+\permanent\protected\def\definetextmodediscretionary #1
+ {\defcsname\??discretionaryaction\detokenize{#1}\endcsname}
+
+% \start \hsize 1mm
+% test |||test test|||, test\blank
+% test test|-|, test|-| and test|-|test\blank
+% test test|_|, test|_| and test|_|test\blank
+% test cd|'|tje\blank
+% test |(|test test|)|, test\blank
+% test test test|x|, test\blank
+% test|~|test
+% test|^|test
+% \stop
+
+% x\discretionary{1}{2}{3}xxxxxxx
+% xxxxxxx\discretionary{1}{2}{3}x
+%
+% xxx3xxx
+% xxx1<newline>2xxx
+
+\def\lang_discretionaries_hyphen_like#1#2%
+ {\ifconditional\spaceafterdiscretionary
+ %prewordbreak\hbox{#1}\relax
+ \wordboundary\hbox{#1}\relax
+ \orelse\ifconditional\punctafterdiscretionary
+ %prewordbreak\hbox{#1}\relax
+ \wordboundary\hbox{#1}\relax
+ \else
+ %\prewordbreak#2\postwordbreak % was prewordbreak
+ \wordboundary#2\wordboundary
+ \fi}
+
+\definetextmodediscretionary {}
+ {\lang_discretionaries_hyphen_like\textmodehyphen\textmodehyphendiscretionary}
+
+\definetextmodediscretionary -
+ {\lang_discretionaries_hyphen_like\normalhyphen\normalhyphendiscretionary}
+
+\definetextmodediscretionary _
+ {\lang_discretionaries_hyphen_like\composedhyphen\composedhyphendiscretionary}
+
+\definetextmodediscretionary )
+ {\lang_discretionaries_hyphen_like{)}{\discretionary{-)}{}{)}}}
+
+\definetextmodediscretionary (
+ {\ifdim\lastskip>\zeropoint
+ %(\prewordbreak
+ (\wordboundary
+ \else
+ %\prewordbreak\discretionary{}{(-}{(}\prewordbreak
+ \wordboundary\discretionary{}{(-}{(}\wordboundary
+ %\discretionary options \plusthree{}{(-}{(}%
+ \fi}
+
+\definetextmodediscretionary ~
+ %{\prewordbreak\discretionary{-}{}{\thinspace}\postwordbreak}
+ {\wordboundary\discretionary{-}{}{\thinspace}\wordboundary}
+ %{\discretionary options \plusthree{-}{}{\thinspace}}
+
+\definetextmodediscretionary '
+ %{\prewordbreak\discretionary{-}{}{'}\postwordbreak}
+ {\wordboundary\discretionary{-}{}{'}\wordboundary}
+ %{\discretionary options \plusthree{-}{}{'}}
+
+\definetextmodediscretionary ^
+ %{\prewordbreak\discretionary{\hbox{\normalstartimath|\normalstopimath}}{}{\hbox{\normalstartimath|\normalstopimath}}%
+ % \postwordbreak} % bugged
+ {\wordboundary
+ \discretionary{\hbox{\normalstartimath|\normalstopimath}}{}{\hbox{\normalstartimath|\normalstopimath}}%
+ \wordboundary} % bugged
+ %{\discretionary options \plusthree{\hbox{\normalstartimath|\normalstopimath}}{}{\hbox{\normalstartimath|\normalstopimath}}}
+
+\definetextmodediscretionary <
+ %{\beginofsubsentence\prewordbreak\beginofsubsentencespacing
+ {\beginofsubsentence\wordboundary\beginofsubsentencespacing
+ \aftergroup\ignorespaces} % tricky, we need to go over the \nextnextnext
+
+\definetextmodediscretionary >
+ {\removeunwantedspaces
+ %\endofsubsentencespacing\prewordbreak\endofsubsentence}
+ \endofsubsentencespacing\wordboundary\endofsubsentence}
+
+\definetextmodediscretionary =
+ {\removeunwantedspaces
+ %\prewordbreak\midsentence\prewordbreak
+ \wordboundary\midsentence\wordboundary
+ \aftergroup\ignorespaces}
+
+% french
+
+%definetextmodediscretionary : {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{:}:}
+%definetextmodediscretionary ; {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{;};}
+%definetextmodediscretionary ? {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{?}?}
+%definetextmodediscretionary ! {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{!}!}
+
+\definetextmodediscretionary : {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{:}:}
+\definetextmodediscretionary ; {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{;};}
+\definetextmodediscretionary ? {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{?}?}
+\definetextmodediscretionary ! {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{!}!}
+
+%definetextmodediscretionary * {\prewordbreak\discretionary{-}{}{\kern.05\emwidth}\prewordbreak}
+\definetextmodediscretionary * {\wordboundary\discretionary{-}{}{\kern.05\emwidth}\wordboundary}
+
+% spanish
+
+%definetextmodediscretionary ?? {\prewordbreak\questiondown}
+%definetextmodediscretionary !! {\prewordbreak\exclamdown}
+
+\definetextmodediscretionary ?? {\wordboundary\questiondown}
+\definetextmodediscretionary !! {\wordboundary\exclamdown}
+
+%D \installdiscretionary | +
+%D \installdiscretionary + =
+
+\permanent\protected\def\defaultdiscretionaryhyphen{\compoundhyphen}
+
+\installdiscretionary | \defaultdiscretionaryhyphen % installs in ctx and prt will fall back on it
+
+%D \macros
+%D {fakecompoundhyphen}
+%D
+%D In headers and footers as well as in active pieces of text we need a dirty hack.
+%D Try to imagine what is needed to savely break the next text across a line and at
+%D the same time make the words interactive.
+%D
+%D \starttyping
+%D \goto{Some||Long||Word}
+%D \stoptyping
+
+\permanent\protected\def\fakecompoundhyphen
+ {\enforced\permanent\protected\def\|{\mathortext\vert\lang_compounds_fake_hyphen}}
+
+\def\lang_compounds_fake_hyphen
+ {\enforced\permanent\protected\def##1|%
+ {\doifelsenothing{##1}\compoundhyphen{##1}%
+ \kern\d_lang_discretionaries_breakpoint
+ \allowbreak}}
+
+%D \macros
+%D {midworddiscretionary}
+%D
+%D If needed, one can add a discretionary hyphen using \type
+%D {\midworddiscretionary}. This macro does the same as \PLAIN\ \TEX's \type {\-},
+%D but, like the ones implemented earlier, this one also looks ahead for spaces and
+%D grouping tokens.
+
+\permanent\protected\def\midworddiscretionary
+ {\futurelet\nexttoken\lang_discretionaries_mid_word}
+
+\def\lang_discretionaries_mid_word
+ {\ifx\nexttoken\blankspace\orelse
+ \ifx\nexttoken\bgroup \orelse
+ \ifx\nexttoken\egroup \orelse
+ \discretionary{-}{}{}%
+ \fi}
+
+% \aliased\let\ignorecompoundcharacter\relax
+
+%D \macros
+%D {disablediscretionaries,disablecompoundcharacter}
+%D
+%D Occasionally we need to disable this mechanism. For the moment we assume that
+%D \type {|} is used.
+
+\aliased\let\disablediscretionaries \ignorediscretionaries
+%aliased\let\disablecompoundcharacters\ignorecompoundcharacter
+
+%D \macros
+%D {normalcompound}
+%D
+%D Handy in for instance XML. (Kind of obsolete)
+
+\ifdefined\normalcompound \else \aliased\let\normalcompound=| \fi
+
+%D \macros
+%D {compound}
+%D
+%D We will overload the already active \type {|} so we have to save its meaning in
+%D order to be able to use this handy macro.
+%D
+%D \starttyping
+%D so test\compound{}test can be used instead of test||test
+%D \stoptyping
+
+\bgroup
+
+ \catcode\barasciicode\activecatcode
+
+ \permanent\protected\gdef\compound#1{|#1|}
+
+ \doglobal \appendtoks
+ \enforced\permanent\protected\def|#1|{\ifx#1\empty\empty-\else#1\fi}%
+ \to \everysimplifycommands
+
+\egroup
+
+%D Here we hook some code into the clean up mechanism needed for verbatim data.
+
+\appendtoks
+ %disablecompoundcharacters
+ \disablediscretionaries
+\to \everycleanupfeatures
+
+%D Here:
+%D
+%D \startbuffer
+%D {\red somelongword}{\blue \compounddiscretionary}{\green somelongword}
+%D \stopbuffer
+%D
+%D \typebuffer \blank {\hsize3mm\getbuffer\par} \blank
+
+\permanent\protected\def\compounddiscretionary
+ {\discretionary
+ options \plusthree
+ {\ifnum\prehyphenchar >\zerocount\char\prehyphenchar \fi}%
+ {\ifnum\posthyphenchar>\zerocount\char\posthyphenchar\fi}%
+ {\ifnum\posthyphenchar>\zerocount\char\posthyphenchar\fi}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-rep.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-rep.mkxl
new file mode 100644
index 00000000000..36e48424249
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-rep.mkxl
@@ -0,0 +1,74 @@
+%D \module
+%D [ file=lang-rep,
+%D version=2013.04.28,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Substitution,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D As I needed an example of messing with nodes for the bacho\TEX\ tutorial
+%D I cooked up this. In the end I decided to stick to a simpler example and
+%D just finished this off in case someone really needs it.
+
+\writestatus{loading}{ConTeXt Language Macros / Replacements}
+
+\unprotect
+
+\registerctxluafile{lang-rep}{}
+
+\definesystemattribute[replacements][public,global]
+
+%D \startluacode
+%D
+%D -- todo: other nodes (prelude to more experiments with auto dropped caps)
+%D
+%D languages.replacements.add("basics", {
+%D ["aap"] = "monkey",
+%D ["noot"] = "nut",
+%D ["never"] = "forever",
+%D ["newer"] = "cooler",
+%D ["new"] = "cool",
+%D -- ["special"] = "veryspe{>>>}{<<<}{=}cial",
+%D })
+%D
+%D \stopluacode
+%D
+%D \replaceword[more][this][that]
+%D \replaceword[more][crap][support]
+%D \replaceword[more][---][—]
+%D \replaceword[basics][special][veryspe{>>>}{<<<}{=}cial]
+%D
+%D \starttyping
+%D \start \setreplacements[basics] What the heck, it's now or never, isn't it new? \par \stop
+%D \start \setreplacements[more] Do we --- {\it really} --- need this kind of crap? \par \stop
+%D \start \setreplacements[basics] All kinds of special thingies! \par \stop
+%D \start \setreplacements[basics] \hsize1mm special \par \stop
+%D \stoptyping
+
+\permanent\protected\def\setreplacements[#1]%
+ {\clf_setreplacements{#1}}
+
+\permanent\protected\def\resetreplacements
+ {\c_attr_replacements\attributeunsetvalue}
+
+\permanent\tolerant\protected\def\replaceword[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \clf_addreplacementslist{#1}{#2}%
+ \or
+ \clf_addreplacements{#1}{#2}{#3}%
+ \fi}
+
+\appendtoks
+ \resetreplacements
+\to \everyresettypesetting
+
+\appendtoks
+ \resetreplacements
+\to \everyinitializeverbatim
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-spa.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-spa.mkxl
new file mode 100644
index 00000000000..3e4972acbe8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-spa.mkxl
@@ -0,0 +1,74 @@
+%D \module
+%D [ file=lang-spa,
+%D version=2002.04.17,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Spacing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Language Macros / Spacing}
+
+%D This module was created in the process of enhancing support for French (with the
+%D help of Daniel Flipo).
+%D
+%D This will (or already has) become obsolete!
+
+\unprotect
+
+\definehspace [\s!fr] [quotation] [\flexiblespaceamount{.8}{.3}{.8}]
+\definehspace [\s!fr] [sentence] [\fixedspaceamount{1}]
+
+%definehspace [\s!fr] [quote] [\flexiblespaceamount{.8}{.3}{.8}]
+%definehspace [\s!fr] [speech] [\flexiblespaceamount{.8}{.3}{.8}]
+
+\definehspace [\s!fr] [interquotation] [\zeropoint]
+\definehspace [\s!fr] [intersentence] [\zeropoint]
+
+\definehspace [\string :] [\zeropoint]
+\definehspace [\string ;] [\zeropoint]
+\definehspace [\string !] [\zeropoint]
+\definehspace [\string ?] [\zeropoint]
+
+\definehspace [\s!fr] [\string :] [\spaceamount]
+\definehspace [\s!fr] [\string ;] [.16667em]
+\definehspace [\s!fr] [\string !] [.16667em]
+\definehspace [\s!fr] [\string ?] [.16667em]
+
+%D Alternative discretionary handlers:
+
+%definetextmodediscretionary : {\removeunwantedspaces\prewordbreak\kern\hspaceamount\currentlanguage{:}:}
+%definetextmodediscretionary ; {\removeunwantedspaces\prewordbreak\kern\hspaceamount\currentlanguage{;};}
+%definetextmodediscretionary ? {\removeunwantedspaces\prewordbreak\kern\hspaceamount\currentlanguage{?}?}
+%definetextmodediscretionary ! {\removeunwantedspaces\prewordbreak\kern\hspaceamount\currentlanguage{!}!}
+
+\definetextmodediscretionary : {\removeunwantedspaces\wordboundary\kern\hspaceamount\currentlanguage{:}:}
+\definetextmodediscretionary ; {\removeunwantedspaces\wordboundary\kern\hspaceamount\currentlanguage{;};}
+\definetextmodediscretionary ? {\removeunwantedspaces\wordboundary\kern\hspaceamount\currentlanguage{?}?}
+\definetextmodediscretionary ! {\removeunwantedspaces\wordboundary\kern\hspaceamount\currentlanguage{!}!}
+
+%D \startbuffer
+%D \mainlanguage[en] \quotation{test \quotation{test} test}\par
+%D \mainlanguage[nl] \quotation{test \quotation{test} test}\par
+%D \mainlanguage[fr] \quotation{test \quotation{test} test}\par
+%D
+%D \mainlanguage[en] \quotation{\quotation{test} test}\par
+%D \mainlanguage[nl] \quotation{\quotation{test} test}\par
+%D \mainlanguage[fr] \quotation{\quotation{test} test}\par
+%D
+%D \mainlanguage[en] |<|test |<|test|>| test|>| \par
+%D \mainlanguage[nl] |<|test |<|test|>| test|>| \par
+%D \mainlanguage[fr] |<|test |<|test|>| test|>| \par
+%D
+%D \mainlanguage[en] |<||<|test|>| test|>| \par
+%D \mainlanguage[nl] |<||<|test|>| test|>| \par
+%D \mainlanguage[fr] |<||<|test|>| test|>| \par
+%D \stopbuffer
+%D
+%D \typebuffer {\getbuffer}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-tra.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-tra.mkxl
new file mode 100644
index 00000000000..b087832e04b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-tra.mkxl
@@ -0,0 +1,95 @@
+%D \module
+%D [ file=lang-tra,
+%D version=2020.10.31,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Transliteration,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Language Macros / Transliteration}
+
+\registerctxluafile{lang-tra}{}
+
+\unprotect
+
+\definesystemattribute[transliteration][public,global]
+
+\installcorenamespace {transliteration}
+
+\permanent\tolerant\protected\def\definetransliteration[#1]#*[#2]%
+ {\clf_definedtransliteration{#1}{#2}}
+
+\permanent\protected\def\settransliteration[#1]%
+ {\clf_settransliteration{#1}}
+
+\permanent\protected\def\starttransliteration[#1]%
+ {\begingroup
+ \clf_settransliteration{#1}}
+
+\permanent\protected\def\stoptransliteration
+ {\endgroup}
+
+\permanent\protected\def\resettransliteration
+ {\c_attr_transliteration\attributeunsetvalue}
+
+\appendtoks
+ \resettransliteration
+\to \everyinitializeverbatim
+
+\protect \endinput
+
+% \setupbodyfont[dejavu]
+%
+% \input lang-tra.mkiv
+%
+% \definetransliteration[serbian][serbian to latin]
+%
+% \starttext
+%
+% \settransliteration[serbian to latin]
+%
+% \startchapter[title={АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ}]
+% АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ
+% \start
+% \settransliteration[serbian to latin]
+% АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ
+% \stop
+% АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ
+% \stopchapter
+%
+% \resettransliteration
+%
+% \startchapter[title={АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ}]
+% АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ
+% \start
+% \settransliteration[serbian to latin]
+% АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ
+% \stop
+% АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ
+% \stopchapter
+%
+% \definehead[ChapterA][chapter]
+% \definehead[ChapterB][chapter]
+%
+% \startsetups s-to-l
+% \settransliteration[serbian to latin]
+% \red
+% \stopsetups
+%
+% % maybe we need a setups key for this
+% % hm, \definesetups [xxx] [aa,bb,cc]
+%
+% \setuphead[ChapterB][commandbefore=\setups{s-to-l}]
+%
+% \startChapterA[title={АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ}]
+% \stopChapterA
+%
+% \startChapterB[title={АБВ ГДЕ ЖЗИЙ КЛМНОП РСТУ ФХЦЧ ШЩЪЬ ЮЯ}]
+% \stopChapterB
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-url.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-url.mkxl
new file mode 100644
index 00000000000..04a55fcf93f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-url.mkxl
@@ -0,0 +1,167 @@
+%D \module
+%D [ file=lang-url,
+%D version=2008.01.22, % used to be lang-mis
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Language Options,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{lang-url}{}
+
+\unprotect
+
+%D Traditionally this code is in the language namespace as
+%D hyphenation relates to languages.
+
+%D \macros
+%D {hyphenatedurl}
+%D
+%D For those who want to put full \URL's in a text, we offer
+%D
+%D \startbuffer
+%D \hyphenatedurl{http://optimist.optimist/optimist/optimist.optimist#optimist}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D which breaks at the appropriate places. Watch the \type{#}
+%D hack.
+%D
+%D When passed as argument, like in \type {\goto}, one needs
+%D to substitute a \type {\\} for each \type{#}.
+%D
+%D \startbuffer
+%D \hyphenatedurl{http://this.is.a.rather/strange/reference#indeed}
+%D \stopbuffer
+%D
+%D \typebuffer
+
+\newtoks\everyhyphenatedurl
+
+\appendtoks
+ \resetfontfeature
+ \resetcharacterspacing
+ %
+ \enforced\let\&\letterampersand
+ \enforced\let\#\letterhash
+ \enforced\let\~\lettertilde
+ \enforced\let\\\letterbackslash
+ \enforced\let\$\letterdollar
+ \enforced\let\^\letterhat
+ \enforced\let\_\letterunderscore
+ \enforced\let\{\letterleftbrace
+ \enforced\let\}\letterrightbrace
+ \enforced\let\|\letterbar
+ \enforced\let~=\lettertilde
+ \enforced\let|=\letterbar
+ \enforced\let\Ux\eUx
+\to \everyhyphenatedurl
+
+\mutable\let\hyphenatedurlseparator \empty % \periodcentered
+\mutable\let\hyphenatedurldiscretionary\empty
+
+\setnewconstant\hyphenatedurllefthyphenmin \plusthree
+\setnewconstant\hyphenatedurlrighthyphenmin\plusthree
+
+\def\lang_url_more{\penalty\plustenthousand\hskip\scratchskipone\penalty\plustenthousand}
+\def\lang_url_less{\penalty\plustenthousand\hskip\scratchskiptwo\penalty\plustenthousand}
+\def\lang_url_trac{\penalty\plustenthousand\hsmash{\darkred\vl}\penalty\plustenthousand}
+
+\def\lang_url_a#1{\lang_url_more\char#1\lang_url_show\discretionary{}{\hyphenatedurlseparator}{}}
+\def\lang_url_b#1{\lang_url_more\discretionary{\hyphenatedurlseparator}{}{}\lang_url_show\char#1\relax}
+\def\lang_url_d {\lang_url_more\discretionary{\lang_url_show}{\lang_url_show}{\lang_url_show}}
+\def\lang_url_l#1{\lang_url_less\char#1\relax}
+\def\lang_url_c#1{\lang_url_more\char#1\relax}
+\def\lang_url_L#1{\lang_url_less\char#1\relax}
+\def\lang_url_C#1{\lang_url_more\char#1\relax}
+
+\permanent\protected\def\showhyphenatedurlbreaks % for old times sake
+ {\let\lang_url_show\lang_url_trac}
+
+\installtextracker
+ {languages.url}
+ {\let\lang_url_show\lang_url_trac}
+ {\let\lang_url_show\relax}
+
+\let\lang_url_show\relax
+
+\permanent\protected\def\sethyphenatedurlnormal#1{\clf_sethyphenatedurlcharacters{#1}{\v!normal}}
+\permanent\protected\def\sethyphenatedurlbefore#1{\clf_sethyphenatedurlcharacters{#1}{\v!before}}
+\permanent\protected\def\sethyphenatedurlafter #1{\clf_sethyphenatedurlcharacters{#1}{\v!after }}
+
+% \sethyphenatedurlafter{ABCDEF}
+
+\permanent\protected\def\hyphenatedurl#1%
+ {\dontleavehmode
+ \begingroup
+ \the\everyhyphenatedurl
+ \normallanguage\zerocount % \nohyphens in mkiv
+ \scratchskipone\zeropoint\s!plus\dimexpr\emwidth/12\s!minus\dimexpr\emwidth/24\relax % 1.00pt @ 12pt
+ \scratchskiptwo\zeropoint\s!plus\dimexpr\emwidth/48\s!minus\dimexpr\emwidth/96\relax % 0.25pt @ 12pt
+ \unprotect
+ \normalexpanded{\noexpand\clf_hyphenatedurl
+ {\noexpand\detokenize{#1}}%
+ \hyphenatedurllefthyphenmin
+ \hyphenatedurlrighthyphenmin
+ {\hyphenatedurldiscretionary}%
+ }%
+ \protect
+ \endgroup}
+
+%D \macros
+%D {hyphenatedfilename}
+%D
+%D For the moment we treat filenames in a similar way,
+%D
+%D \starttyping
+%D \hyphenatedfilename{here/there/filename.suffix}
+%D \stoptyping
+
+\ifdefined\hyphenatedfilename \else \aliased\let\hyphenatedfilename\hyphenatedurl \fi
+
+% \def\test#1%
+% {\dontleavehmode
+% \begingroup
+% \tttf
+% \hyphenatedurl {%
+% \letterampersand #1\letterampersand #1\letterampersand #1\letterampersand #1\letterampersand
+% \letterhash #1\letterhash #1\letterpercent #1\letterslash #1\letterampersand
+% }%
+% \endgroup}
+
+% \dorecurse{100}{\test{a} \test{ab} \test{abc} \test{abcd} \test{abcde} \test{abcdef}}
+
+\protect \endinput
+
+% \setuppapersize[A7]
+%
+% \protected\def\WhateverA#1%
+% {\dontleavehmode
+% \begingroup
+% \prehyphenchar"B7\relax
+% \setbox\scratchbox\hbox{\tttf#1}%
+% \prehyphenchar`-\relax
+% \unhbox\scratchbox
+% \endgroup}
+%
+% \protected\def\WhateverB#1%
+% {\dontleavehmode
+% \begingroup
+% \tttf
+% \prehyphenchar\minusone
+% % \localrightbox{\llap{_}}%
+% \localrightbox{\llap{\smash{\lower1.5ex\hbox{\char"2192}}}}%
+% \setbox\scratchbox\hbox{#1}%
+% \prehyphenchar`-\relax
+% \unhbox\scratchbox
+% \endgroup}
+%
+% \begingroup \hsize1cm
+% \WhateverA{thisisaboringandverylongcommand}\par
+% \WhateverB{thisisaboringandverylongcommand}\par
+% \endgroup
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lang-wrd.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lang-wrd.mkxl
new file mode 100644
index 00000000000..ba1cfc5710d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lang-wrd.mkxl
@@ -0,0 +1,69 @@
+%D \module
+%D [ file=lang-wrd,
+%D version=2006.XX.XX,
+%D title=\CONTEXT\ Language Macros,
+%D subtitle=Checking,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Experimental.
+
+\writestatus{loading}{ConTeXt Language Macros / Checking}
+
+\registerctxluafile{lang-wrd}{}
+
+\unprotect
+
+% \loadspellchecklist[en][words-en.txt]
+% \loadspellchecklist[us][words-en.txt]
+% \loadspellchecklist[nl][words-nl.txt]
+% \setupspellchecking[state=start,method=1]
+%
+% \setupspellchecking[state=start,method=2]
+% ...
+% \typefile{\jobname.words}
+%
+% beware, maybe some day we will honour grouping
+%
+% 1: spell checking
+% 2: word counting
+% 3: language coloring
+
+\installcorenamespace{spell}
+
+\installdirectcommandhandler \??spell {spellchecking}
+
+\appendtoks
+ \ifcstok{\directspellcheckingparameter\c!state}\v!start
+ \clf_enablespellchecking
+ method {\directspellcheckingparameter\c!method}%
+ list {\directspellcheckingparameter\c!list}%
+ \relax
+ \else
+ \clf_disablespellchecking
+ \fi
+\to \everysetupspellchecking
+
+\permanent\tolerant\protected\def\loadspellchecklist[#1]#*[#2]% can be public implementor
+ {\clf_loadspellchecklist{#1}{#2}}
+
+\setupspellchecking
+ [\c!state=\v!stop,
+ \c!method=1,
+ \c!list=]
+
+\definecolor[word:yes] [g=.75]
+\definecolor[word:no] [r=.75]
+
+%definecolor[word:unset] [s=.75]
+\definecolor[word:en] [b=.75]
+\definecolor[word:de] [r=.75]
+\definecolor[word:nl] [g=.75]
+\definecolor[word:unknown][r=.75,g=.75]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/layo-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/layo-ini.lmt
new file mode 100644
index 00000000000..1570075d9a1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/layo-ini.lmt
@@ -0,0 +1,115 @@
+if not modules then modules = { } end modules ['layo-ini'] = {
+ version = 1.001,
+ comment = "companion to layo-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We need to share information between the TeX and Lua end about the typographical
+-- model. This happens here. This code might move.
+
+local texgetcount = tex.getcount
+local conditionals = tex.conditionals
+
+layouts = {
+ status = { },
+}
+
+local status = layouts.status
+
+function status.leftorrightpageaction(left,right)
+ if left == nil then
+ left, right = false, true
+ end
+ if not conditionals.layoutisdoublesided then
+ return left, right
+ elseif conditionals.layoutissinglesided then
+ return left, right
+ elseif texgetcount("pagenoshift") % 2 == 0 then
+ if texgetcount("realpageno") % 2 == 0 then
+ return right, left
+ else
+ return left, right
+ end
+ else
+ if texgetcount("realpageno") % 2 == 0 then
+ return left, right
+ else
+ return right, left
+ end
+ end
+end
+
+function status.isleftpage(r)
+ if not conditionals.layoutisdoublesided then
+ return false
+ elseif conditionals.layoutissinglesided then
+ return false
+ elseif texgetcount("pagenoshift") % 2 == 0 then
+ return (r or texgetcount("realpageno")) % 2 == 0
+ else
+ return not (r or texgetcount("realpageno")) % 2 == 0
+ end
+end
+
+-- Instead of making these these driver specific we make them generic. We can even consider
+-- to make these registers at the tex end.
+
+local canvas = {
+ pagespec = "default", -- v_default
+ paperwidth = 0,
+ paperheight = 0,
+ topoffset = 0,
+ leftoffset = 0,
+ height = 0,
+ width = 0,
+ cropoffset = 0,
+ bleedoffset = 0,
+ trimoffset = 0,
+ artoffset = 0,
+ doublesided = false,
+ marked = false,
+ copies = false,
+}
+
+function layouts.setupcanvas(specification)
+ local paperheight = specification.paperheight or canvas.paperheight
+ local paperwidth = specification.paperwidth or canvas.paperwidth
+ local cropoffset = specification.cropoffset or 0
+ local trimoffset = cropoffset - (specification.trimoffset or 0)
+ local bleedoffset = trimoffset - (specification.bleedoffset or 0)
+ local artoffset = bleedoffset - (specification.artoffset or 0)
+ --
+ canvas.paperheight = paperheight
+ canvas.paperwidth = paperwidth
+ canvas.cropoffset = cropoffset
+ canvas.trimoffset = trimoffset
+ canvas.bleedoffset = bleedoffset
+ canvas.artoffset = artoffset
+ --
+ canvas.pagespec = specification.mode or pagespec
+ canvas.topoffset = specification.topoffset or 0
+ canvas.leftoffset = specification.leftoffset or 0
+ canvas.height = specification.height or paperheight
+ canvas.width = specification.width or paperwidth
+ canvas.marked = specification.print
+ --
+ local copies = specification.copies
+ if type(copies) == "number" and copies < 2 then
+ canvas.copies = false
+ end
+ --
+ local doublesided = specification.doublesided
+ if doublesided ~= nil then
+ canvas.doublesided = doublesided
+ end
+end
+
+function layouts.getpagedimensions()
+ return canvas.paperwidth, canvas.paperheight
+end
+
+function layouts.getcanvas()
+ return canvas
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/layo-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/layo-ini.mkxl
new file mode 100644
index 00000000000..73b7e978e16
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/layo-ini.mkxl
@@ -0,0 +1,30 @@
+%D \module
+%D [ file=layo-ini,
+%D version=2011.02.18,
+%D title=\CONTEXT\ Layout Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Layout Macros / Initialization}
+
+%D This module deals with the basic variables etc. for layout and code from other
+%D modules will move here. This will make the dependencies somewhat cleaner.
+
+\unprotect
+
+%D We need to check this in case a smaller format is loaded.
+
+\ifdefined\layoutisdoublesided \else \newconditional\layoutisdoublesided \fi
+\ifdefined\layoutissinglesided \else \newconditional\layoutissinglesided \fi
+\ifdefined\pagenoshift \else \newcount \pagenoshift \fi
+\ifdefined\realpageno \else \newcount \realpageno \fi
+
+\registerctxluafile{layo-ini}{autosuffix}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-curl.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-curl.lmt
new file mode 100644
index 00000000000..b307397948c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-curl.lmt
@@ -0,0 +1,296 @@
+if not modules then modules = { } end modules ['libs-imp-curl'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- c:/data/develop/tex-context/tex/texmf-win64/bin/lib/luametatex/lua/copies/curl/libcurl.dll
+
+local libname = "curl"
+local libfile = "libcurl"
+
+local curllib = resolvers.libraries.validoptional(libname)
+
+if not curllib then return end
+
+-- We're good, so we continue.
+
+local next, type = next, type
+local lower, gsub = string.lower, string.gsub
+
+local mapping = {
+ ["acceptencoding"] = 102,
+ ["accepttimeoutms"] = 212,
+ ["addressscope"] = 171,
+ ["append"] = 50,
+ ["autoreferer"] = 58,
+ ["buffersize"] = 98,
+ ["cainfo"] = 65,
+ ["capath"] = 97,
+ ["certinfo"] = 172,
+ -- ["chunkbgnfunction"] = 198,
+ ["chunkdata"] = 201,
+ -- ["chunkendfunction"] = 199,
+ ["closepolicy"] = 72,
+ ["closesocketdata"] = 209,
+ -- ["closesocketfunction"] = 208,
+ ["connectonly"] = 141,
+ ["connecttimeout"] = 78,
+ ["connecttimeoutms"] = 156,
+ -- ["convfromnetworkfunction"] = 142,
+ -- ["convfromutf8function"] = 144,
+ -- ["convtonetworkfunction"] = 143,
+ ["cookie"] = 22,
+ ["cookiefile"] = 31,
+ ["cookiejar"] = 82,
+ ["cookielist"] = 135,
+ ["cookiesessionv"] = 96,
+ ["copypostfields"] = 165,
+ ["crlf"] = 27,
+ ["crlfile"] = 169,
+ ["customrequest"] = 36,
+ ["debugdata"] = 95,
+ -- ["debugfunction"] = 94,
+ ["dirlistonly"] = 48,
+ ["dnscachetimeout"] = 92,
+ ["dnsinterface"] = 221,
+ ["dnslocalip4"] = 222,
+ ["dnslocalip6"] = 223,
+ ["dnsservers"] = 211,
+ ["dnsuseglobalcache"] = 91,
+ ["egdsocket"] = 77,
+ ["errorbuffer"] = 10,
+ ["expect100timeoutms"] = 227,
+ ["failonerror"] = 45,
+ ["file"] = 1,
+ ["filetime"] = 69,
+ ["fnmatchdata"] = 202,
+ -- ["fnmatchfunction"] = 200,
+ ["followlocation"] = 52,
+ ["forbidreuse"] = 75,
+ ["freshconnect"] = 74,
+ ["ftpaccount"] = 134,
+ ["ftpalternativetouser"] = 147,
+ ["ftpcreatemissingdirs"] = 110,
+ ["ftpfilemethod"] = 138,
+ ["ftpresponsetimeout"] = 112,
+ ["ftpskippasvip"] = 137,
+ ["ftpsslccc"] = 154,
+ ["ftpuseeprt"] = 106,
+ ["ftpuseepsv"] = 85,
+ ["ftpusepret"] = 188,
+ ["ftpport"] = 17,
+ ["ftpsslauth"] = 129,
+ ["gssapidelegation"] = 210,
+ ["header"] = 42,
+ ["headerdata"] = 29,
+ -- ["headerfunction"] = 79,
+ ["http200aliases"] = 104,
+ ["httpcontentdecoding"] = 158,
+ ["httptransferdecoding"] = 157,
+ ["httpversion"] = 84,
+ ["httpauth"] = 107,
+ ["httpget"] = 80,
+ ["httpheader"] = 23,
+ ["httppost"] = 24,
+ ["httpproxytunnel"] = 61,
+ ["ignorecontentlength"] = 136,
+ ["infile"] = 9,
+ ["infilesize"] = 14,
+ ["infilesizelarge"] = 115,
+ ["interface"] = 62,
+ ["interleavedata"] = 195,
+ -- ["interleavefunction"] = 196,
+ ["ioctldata"] = 131,
+ -- ["ioctlfunction"] = 130,
+ ["ipresolve"] = 113,
+ ["issuercert"] = 170,
+ ["keypasswd"] = 26,
+ ["krblevel"] = 63,
+ ["localport"] = 139,
+ ["localportrange"] = 140,
+ ["loginoptions"] = 224,
+ ["lowspeedlimit"] = 19,
+ ["lowspeedtime"] = 20,
+ ["mailauth"] = 217,
+ ["mailfrom"] = 186,
+ ["mailrcpt"] = 187,
+ ["maxrecvspeedlarge"] = 146,
+ ["maxsendspeedlarge"] = 145,
+ ["maxconnects"] = 71,
+ ["maxfilesize"] = 114,
+ ["maxfilesizelarge"] = 117,
+ ["maxredirs"] = 68,
+ ["netrc"] = 51,
+ ["netrcfile"] = 118,
+ ["newdirectoryperms"] = 160,
+ ["newfileperms"] = 159,
+ ["nobody"] = 44,
+ ["noprogress"] = 43,
+ ["noproxy"] = 177,
+ ["nosignal"] = 99,
+ ["opensocketdata"] = 164,
+ -- ["opensocketfunction"] = 163,
+ ["password"] = 174,
+ ["port"] = 3,
+ ["post"] = 47,
+ -- ["postfields"] = 15,
+ -- ["postfieldsize"] = 60,
+ -- ["postfieldsizelarge"] = 120,
+ ["postquote"] = 39,
+ ["postredir"] = 161,
+ ["prequote"] = 93,
+ ["private"] = 103,
+ ["progressdata"] = 57,
+ -- ["progressfunction"] = 56,
+ ["protocols"] = 181,
+ ["proxy"] = 4,
+ ["proxytransfermode"] = 166,
+ ["proxyauth"] = 111,
+ ["proxypassword"] = 176,
+ ["proxyport"] = 59,
+ ["proxytype"] = 101,
+ ["proxyusername"] = 175,
+ ["proxyuserpwd"] = 6,
+ ["put"] = 54,
+ ["quote"] = 28,
+ ["randomfile"] = 76,
+ ["range"] = 7,
+ ["readdata"] = 9,
+ -- ["readfunction"] = 12,
+ ["redirprotocols"] = 182,
+ ["referer"] = 16,
+ ["resolve"] = 203,
+ ["resumefrom"] = 21,
+ ["resumefromlarge"] = 116,
+ ["rtspclientcseq"] = 193,
+ ["rtsprequest"] = 189,
+ ["rtspservercseq"] = 194,
+ ["rtspsessionid"] = 190,
+ ["rtspstreamuri"] = 191,
+ ["rtsptransport"] = 192,
+ ["rtspheader"] = 23,
+ ["saslir"] = 218,
+ ["seekdata"] = 168,
+ -- ["seekfunction"] = 167,
+ ["serverresponsetimeout"] = 112,
+ ["share"] = 100,
+ ["sockoptdata"] = 149,
+ -- ["sockoptfunction"] = 148,
+ ["socks5gssapinec"] = 180,
+ ["socks5gssapiservice"] = 179,
+ ["sshauthtypes"] = 151,
+ ["sshhostpublickeymd5"] = 162,
+ ["sshkeydata"] = 185,
+ -- ["sshkeyfunction"] = 184,
+ ["sshknownhosts"] = 183,
+ ["sshprivatekeyfile"] = 153,
+ ["sshpublickeyfile"] = 152,
+ ["sslcipherlist"] = 83,
+ ["sslctxdata"] = 109,
+ -- ["sslctxfunction"] = 108,
+ ["sslenablealpn"] = 226,
+ ["sslenablenpn"] = 225,
+ ["ssloptions"] = 216,
+ ["sslsessionidcache"] = 150,
+ ["sslverifyhost"] = 81,
+ ["sslverifypeer"] = 64,
+ ["sslcert"] = 25,
+ ["sslcerttype"] = 86,
+ ["sslengine"] = 89,
+ ["sslenginedefault"] = 90,
+ ["sslkey"] = 87,
+ ["sslkeytype"] = 88,
+ ["sslversion"] = 32,
+ ["stderr"] = 37,
+ ["tcpkeepalive"] = 213,
+ ["tcpkeepidle"] = 214,
+ ["tcpkeepintvl"] = 215,
+ ["tcpnodelay"] = 121,
+ ["telnetoptions"] = 70,
+ ["tftpblksize"] = 178,
+ ["timecondition"] = 33,
+ ["timeout"] = 13,
+ ["timeoutms"] = 155,
+ ["timevalue"] = 34,
+ ["tlsauthpassword"] = 205,
+ ["tlsauthtype"] = 206,
+ ["tlsauthusername"] = 204,
+ ["transferencoding"] = 207,
+ ["transfertext"] = 53,
+ ["unrestrictedauth"] = 105,
+ ["upload"] = 46,
+ ["url"] = 2,
+ ["usessl"] = 119,
+ ["useragent"] = 18,
+ ["username"] = 173,
+ ["userpwd"] = 5,
+ ["verbose"] = 41,
+ ["wildcardmatch"] = 197,
+ ["writedata"] = 1,
+ -- ["writefunction"] = 11,
+ ["writeheader"] = 29,
+ ["writeinfo"] = 40,
+ ["xferinfodata"] = 57,
+ -- ["xferinfofunction"] = 219,
+ ["xoauth2bearer"] = 220,
+}
+
+table.setmetatableindex(mapping,function(t,k)
+ local s = gsub(lower(k),"[^a-z0-9]","")
+ local v = rawget(t,s) or false
+ t[k] = v
+ return v
+end)
+
+local curl_fetch = curllib.fetch
+local curl_escape = curllib.escape
+local curl_unescape = curllib.unescape
+local curl_getversion = curllib.getversion
+
+local report = logs.reporter(libname)
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local function fetch(options)
+ if okay() then
+ local t = type(options)
+ if t == "table" then
+ local o = { }
+ for name, value in next, options do
+ local index = mapping[name]
+ if index then
+ o[index] = value
+ end
+ end
+ return curl_fetch(o)
+ elseif t == "string" then
+ return curl_fetch { [mapping.url] = options }
+ else
+ report("invalid argument")
+ end
+ end
+end
+
+local curl = {
+ getversion = function () return okay() and curl_getversion() end,
+ escape = function (s) return okay() and curl_escape (s) end,
+ unescape = function (s) return okay() and curl_unescape (s) end,
+ fetch = fetch,
+}
+
+-- inspect(curl.fetch("http://www.pragma-ade.com/index.html"))
+-- inspect(curl.fetch { url = "http://www.pragma-ade.com/index.html" })
+
+package.loaded[libname] = curl
+
+return curl
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-ghostscript.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-ghostscript.lmt
new file mode 100644
index 00000000000..b129a58c143
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-ghostscript.lmt
@@ -0,0 +1,111 @@
+if not modules then modules = { } end modules ['libs-imp-ghostscript'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local libname = "ghostscript"
+local libfile = "gsdll64" -- what on unix?
+
+local gslib = resolvers.libraries.validoptional(libname)
+
+if not gslib then return end
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local insert = table.insert
+local formatters = string.formatters
+
+local ghostscript = utilities.ghostscript or { }
+utilities.ghostscript = ghostscript
+
+local gs_execute = gslib.execute
+local nofruns = 0
+local report = logs.reporter(libname)
+
+function ghostscript.convert(specification)
+ if okay() then
+ --
+ nofruns = nofruns + 1
+ statistics.starttiming(ghostscript)
+ --
+ local inputname = specification.inputname
+ if not inputname or inputname == "" then
+ report("invalid run %s, no inputname specified",nofruns)
+ statistics.stoptiming(ghostscript)
+ return false
+ end
+ local outputname = specification.outputname
+ if not outputname or outputname == "" then
+ outputname = file.replacesuffix(inputname,"pdf")
+ end
+ --
+ if not lfs.isfile(inputname) then
+ report("invalid run %s, input file %a is not found",nofruns,inputname)
+ statistics.stoptiming(ghostscript)
+ return false
+ end
+ --
+ local device = specification.device
+ if not device or device == "" then
+ device = "pdfwrite"
+ end
+ --
+ local code = specification.code
+ if not code or code == "" then
+ code = ".setpdfwrite"
+ end
+ --
+ local options = specification.options or { }
+ --
+ insert(options,"-dNOPAUSE")
+ insert(options,"-dBATCH")
+ insert(options,"-dSAFER")
+ insert(options,formatters["-sDEVICE=%s"](device))
+ insert(options,formatters["-sOutputFile=%s"](outputname))
+ insert(options,"-c")
+ insert(options,code)
+ insert(options,"-f")
+ insert(options,inputname)
+ --
+ report("run %s, input file %a, outputfile %a",nofruns,inputname,outputname)
+ report("")
+ local done = gslib_execute(options)
+ report("")
+ --
+ statistics.stoptiming(ghostscript)
+ if done then
+ return outputname
+ else
+ report("run %s quit with errors",nofruns)
+ return false
+ end
+ end
+end
+
+function ghostscript.statistics(report)
+ local runtime = statistics.elapsedtime(ghostscript)
+ if report then
+ report("nofruns %s, runtime %s",nofruns,runtime)
+ else
+ return {
+ runtime = runtime,
+ nofruns = nofruns,
+ }
+ end
+end
+
+-- for i=1,100 do
+-- ghostscript.convert { inputname = "temp.eps" }
+-- ghostscript.convert { inputname = "t:/escrito/tiger.eps" }
+-- end
+-- ghostscript.statistics(true)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-graphicsmagick.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-graphicsmagick.lmt
new file mode 100644
index 00000000000..6f5d1d967fd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-graphicsmagick.lmt
@@ -0,0 +1,77 @@
+if not modules then modules = { } end modules ['libs-imp-graphicsmagick'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local libname = "graphicsmagick"
+local libfile = { "CORE_RL_magick_", "CORE_RL_wand_" }
+
+local gmlib = resolvers.libraries.validoptional(libname)
+
+if not gmlib then return end
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local graphicsmagick = utilities.graphicsmagick or { }
+utilities.graphicsmagick = graphicsmagick
+utilities.graphicmagick = graphicsmagick
+
+local gm_execute = gmlib.execute
+local nofruns = 0
+local report = logs.reporter(libname)
+
+function graphicsmagick.convert(specification)
+ if okay() then
+ --
+ nofruns = nofruns + 1
+ statistics.starttiming(graphicsmagick)
+ --
+ local inputname = specification.inputname
+ if not inputname or inputname == "" then
+ report("invalid run %s, no inputname specified",nofruns)
+ statistics.stoptiming(graphicsmagick)
+ return false
+ end
+ local outputname = specification.outputname
+ if not outputname or outputname == "" then
+ outputname = file.replacesuffix(inputname,"pdf")
+ end
+ --
+ if not lfs.isfile(inputname) then
+ report("invalid run %s, input file %a is not found",nofruns,inputname)
+ statistics.stoptiming(graphicsmagick)
+ return false
+ end
+ --
+ report("run %s, input file %a, outputfile %a",nofruns,inputname,outputname)
+ --
+ gm_execute { inputfile = inputname, outputfile = outputname }
+ --
+ statistics.stoptiming(graphicsmagick)
+ end
+end
+
+function graphicsmagick.statistics(report)
+ local runtime = statistics.elapsedtime(graphicsmagick)
+ if report then
+ report("nofruns %s, runtime %s",nofruns,runtime)
+ else
+ return {
+ runtime = runtime,
+ nofruns = nofruns,
+ }
+ end
+end
+
+-- graphicsmagick.convert { inputname = "t:/sources/hacker.jpg", outputname = "e:/tmp/hacker.png" }
+-- graphicsmagick.statistics(true)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.lmt
new file mode 100644
index 00000000000..f8e4a990715
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.lmt
@@ -0,0 +1,58 @@
+if not modules then modules = { } end modules ['libs-imp-kpse'] = {
+ version = 1.001,
+ comment = "companion to luat-imp-kpse.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is an experiment. It might make sense to have this available in case I want
+-- more runners to use LuaMetaTeX in which case (as with mtxrun using LuaTeX) we
+-- need to load kpse.
+
+local libname = "kpse"
+local libfile = (os.platform == "win64" and "kpathsea*w64")
+ or (os.platform == "win32" and "kpathsea*w32")
+ or "libkpathsea"
+local libkpse = resolvers.libraries.validoptional(libname)
+
+if package.loaded[libname] then
+ return package.loaded[libname]
+end
+
+-- This is a variant that loaded directly:
+
+-- kpse = libkpse -- the library will issue warnings anyway
+--
+-- resolvers.libraries.optionalloaded(libname,libfile) -- no need to chedk if true
+
+-- This variant delays loading and has a bit more protection:
+
+local function okay()
+ if libkpse and resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local kpse = { }
+
+for k, v in next, libkpse do
+ kpse[k] = function(...) if okay() then return v(...) end end
+end
+
+-- We properly register the module:
+
+package.loaded[libname] = kpse
+
+optional.loaded.kpse = kpse
+
+-- A simple test:
+
+-- kpse.set_program_name("pdftex")
+-- print("find file:",kpse.find_file("oeps.tex"))
+-- print("find file:",kpse.find_file("context.mkii"))
+
+return kpse
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.mkxl b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.mkxl
new file mode 100644
index 00000000000..f76c9902904
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-kpse.mkxl
@@ -0,0 +1,38 @@
+%D \module
+%D [ file=libs-imp-kpse,
+%D version=2020.05.19,
+%D title=\CONTEXT\ System Modules,
+%D subtitle=KPSE File Searching,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is not really a module. It's more some left over from an experiment. Who
+%D know when it comes in handy; probably never in a real run, but I might want to
+%D make a \LUAMETATEX\ plain format one day, just for the fun of it.
+
+% \registerctxluafile{libs-imp-kpse}{autosuffix}
+
+\continueifinputfile{libs-imp-kpse.mkxl}
+
+\starttext
+
+\startTEXpage
+ \startluacode
+ local kpse = require("libs-imp-kpse")
+
+ kpse.set_program_name("pdftex")
+
+ local name = "context.mkii"
+
+ context("%s: %s",name,kpse.find_file(name))
+ \stopluacode
+\stopTEXpage
+
+\stoptext
+
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.lmt
new file mode 100644
index 00000000000..264c8e53126
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.lmt
@@ -0,0 +1,126 @@
+if not modules then modules = { } end modules ['libs-imp-mujs'] = {
+ version = 1.001,
+ comment = "companion to luat-imp-mujs.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is an experiment. When a new user knows \JAVASCRIPT\ it can be a
+-- stepping stone to using \LUA.
+
+-- local ecmascript = optional.mujs.initialize("libmujs")
+-- local execute = optional.mujs.execute
+
+local libname = "mujs"
+local libfile = "libmujs"
+
+if package.loaded[libname] then
+ return package.loaded[libname]
+end
+
+local mujslib = resolvers.libraries.validoptional(libname)
+
+if not mujslib then
+ return
+end
+
+local files = { }
+local openfile = io.open
+local findfile = resolvers.findfile
+
+local mujs_execute = mujslib.execute
+local mujs_dofile = mujslib.dofile
+local mujs_reset = mujslib.reset
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ mujs_execute(
+ "var catcodes = { " ..
+ "'tex': " .. tex.texcatcodes .. "," ..
+ "'ctx': " .. tex.ctxcatcodes .. "," ..
+ "'prt': " .. tex.prtcatcodes .. "," ..
+ "'vrb': " .. tex.vrbcatcodes .. "," ..
+ "};"
+ )
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+mujslib.setfindfile(findfile)
+
+mujslib.setopenfile(function(name)
+ local full = findfile(name)
+ if full then
+ local f = openfile(full,"rb")
+ if f then
+ for i=1,100 do
+ if not files[i] then
+ files[i] = f
+ return i
+ end
+ end
+ end
+ end
+end)
+
+mujslib.setclosefile(function(id)
+ local f = files[id]
+ if f then
+ f:close()
+ files[id] = false
+ end
+end)
+
+mujslib.setreadfile(function(id,how)
+ local f = files[id]
+ if f then
+ return (f:read(how or "*l"))
+ end
+end)
+
+mujslib.setseekfile(function(id,whence,offset)
+ local f = files[id]
+ if f then
+ return (f:seek(whence,offset))
+ end
+end)
+
+local reporters = {
+ console = logs.reporter("mujs","console"),
+ report = logs.reporter("mujs","report"),
+}
+
+mujslib.setconsole(function(category,name)
+ reporters[category](name)
+end)
+
+local mujs = {
+ ["execute"] = function(c,s) if okay() then mujs_execute(c,s) end end,
+ ["dofile"] = function(n) if okay() then mujs_dofile(n) end end,
+ ["reset"] = function(n) if okay() then mujs_reset(n) end end,
+}
+
+package.loaded[libname] = mujs
+
+optional.loaded.mujs = mujs
+
+interfaces.implement {
+ name = "ecmacode",
+ actions = mujs.execute,
+ arguments = "string",
+ public = true,
+}
+
+interfaces.implement {
+ name = "ecmafile",
+ actions = mujs.dofile,
+ arguments = "string",
+ public = true,
+ protected = true,
+}
+
+return mujs
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.mkxl b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.mkxl
new file mode 100644
index 00000000000..a8fe5801f39
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mujs.mkxl
@@ -0,0 +1,121 @@
+%D \module
+%D [ file=libs-imp-mujs,
+%D version=2020.02.08,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=MuJS interpreter,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Just an experiment:
+%D
+%D \starttyping
+%D http://mujs.com
+%D \stoptyping
+
+\ifdefined\ecmacode
+ \expandafter \endinput
+\fi
+
+\registerctxluafile{libs-imp-mujs}{autosuffix}
+
+\unprotect
+
+% todo: environment
+
+\let\stopecmacode\relax
+
+\protected\def\startecmacode % \stopecmacode
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \luat_start_ecma_code_indeed}
+
+\def\luat_start_ecma_code_indeed#1\stopecmacode
+ {\normalexpanded{\endgroup\noexpand\ecmacode{#1}}}
+
+\protect
+
+\continueifinputfile{libs-imp-mujs.mkxl}
+
+\usemodule[article-basic,abbreviations-logos]
+
+\starttext
+
+\startbuffer
+\startluacode
+ local mujs = require("libs-imp-mujs")
+
+ mujs.execute [[
+ var MyMax = 1000;
+ ]]
+
+ mujs.execute [[
+ console("Example One!");
+ texsprint("\\startpacked");
+ for (var i = 1; i <= MyMax; i = i + 1) {
+ texprint(
+ "This is a buildbot compilation challenge for Mojca: "
+ .concat(Math.sqrt(i/MyMax))
+ .concat("!\\par")
+ );
+ }
+ texsprint("\\stoppacked");
+ ]]
+
+ -- mujs.execute [[
+ -- something very bad: an error
+ -- ]]
+\stopluacode
+
+\startecmacode
+ console("Example Two!");
+ for (var i = 1; i <= MyMax; i = i + 1) {
+ texsprint(
+ "And an opportunity for Alan to attract highschool students to \\TeX: "
+ .concat(i)
+ .concat("! ")
+ );
+ }
+\stopecmacode
+
+\ecmacode {
+ console("Example Three!");
+ texprint("And otherwise a way to prototype \\JAVASCRIPT\\ code in \\PDF.")
+}
+
+\startecmacode
+ var f = File("libs-imp-mujs.mkxl","r");
+ console(f);
+ // var l = f.read(400);
+ var l = f.read("*a");
+ console(l);
+ f.close();
+\stopecmacode
+
+\startecmacode
+ console("Example Four!");
+ texsprint("\\startMPcode "
+ .concat(' fill fullcircle scaled 6cm withcolor "darkred";')
+ .concat(' fill fullcircle scaled 4cm withcolor "darkgreen";')
+ .concat(' fill fullcircle scaled 2cm withcolor "darkblue";')
+ .concat("\\stopMPcode ")
+ );
+\stopecmacode
+
+\startluacode
+ context.startMPcode()
+ context('fill fullcircle scaled 6cm withcolor "middlecyan";')
+ context('fill fullcircle scaled 4cm withcolor "middlemagenta";')
+ context('fill fullcircle scaled 2cm withcolor "middleyellow";')
+ context.stopMPcode()
+\stopluacode
+\stopbuffer
+
+\typebuffer \page \getbuffer
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mysql.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mysql.lmt
new file mode 100644
index 00000000000..3e938a6de48
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-mysql.lmt
@@ -0,0 +1,220 @@
+if not modules then modules = { } end modules ['libs-imp-mysql'] = {
+ version = 1.001,
+ comment = "companion to util-sql.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- c:/data/develop/tex-context/tex/texmf-win64/bin/lib/luametatex/lua/copies/mysql/libmysql.dll
+
+local libname = "mysql"
+local libfile = "libmysql"
+
+local mysqllib = resolvers.libraries.validoptional(libname)
+
+if not mysqllib then return end
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local lpegmatch = lpeg.match
+local setmetatable = setmetatable
+
+local sql = utilities.sql or require("util-sql")
+local report = logs.reporter(libname)
+
+local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
+local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
+
+local mysql_open = mysqllib.open
+local mysql_close = mysqllib.close
+local mysql_execute = mysqllib.execute
+local mysql_getmessage = mysqllib.getmessage
+
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local preparetemplate = helpers.preparetemplate
+local querysplitter = helpers.querysplitter
+local cache = { }
+local timeout -- = 3600 -- to be tested
+
+local function connect(specification)
+ local db = mysql_open(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+ if db and timeout then
+ mysql_execute(db,formatters["SET SESSION connect_timeout=%s ;"](timeout))
+ end
+ return db
+end
+
+local function execute_once(specification,retry)
+ if okay() then
+ if trace_sql then
+ report("executing mysql")
+ end
+ if not validspecification(specification) then
+ report("error in specification")
+ end
+ local query = preparetemplate(specification)
+ if not query then
+ report("error in preparation")
+ return
+ else
+ query = lpegmatch(querysplitter,query)
+ end
+ local base = specification.database -- or specification.presets and specification.presets.database
+ if not base then
+ report("no database specified")
+ return
+ end
+ local result = { }
+ local keys = { }
+ local id = specification.id
+ local db = nil
+ if id then
+ local session = cache[id]
+ if session then
+ db = session.db
+ else
+ db = connect(specification)
+ if not db then
+ report("no session database specified")
+ else
+ cache[id] = {
+ specification = specification,
+ db = db,
+ }
+ end
+ end
+ else
+ db = connect(specification)
+ end
+ if not db then
+ report("no database opened")
+ else
+ local converter = specification.converter
+ local nofrows = 0
+ local callback = nil
+ if converter then
+ local convert = converter.mysql
+ callback = function(nofcolumns,values,fields)
+ nofrows = nofrows + 1
+ result[nofrows] = convert(values)
+ end
+ else
+ callback = function(nofcolumns,values,fields)
+ local column = { }
+ for i=1,nofcolumns do
+ local field
+ if fields then
+ field = fields[i]
+ keys[i] = field
+ else
+ field = keys[i]
+ end
+ if field then
+ column[field] = values[i]
+ end
+ end
+ nofrows = nofrows + 1
+ result[nofrows] = column
+ end
+ end
+ for i=1,#query do
+ local okay = mysql_execute(db,query[i],callback)
+ if not okay then
+ if id and retry and i == 1 then
+ report("error: %s, retrying to connect",mysql_getmessage(db))
+ mysql_close(db)
+ cache[id] = nil
+ return execute_once(specification,false)
+ else
+ report("error: %s",mysql_getmessage(db))
+ end
+ end
+ end
+ end
+ if db and not id then
+ mysql_close(db)
+ end
+ -- bonus
+ local one = result[1]
+ if one then
+ setmetatable(result,{ __index = one } )
+ end
+ --
+ return result, keys
+ else
+ report("error: ","no library loaded")
+ end
+end
+
+local function execute(specification)
+ return execute_once(specification,true)
+end
+
+-- Here we build the dataset stepwise so we don't use the data hack that
+-- is used in the client variant.
+
+local wraptemplate = [[
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(cells)
+ -- %s (not needed)
+ -- %s (not needed)
+ return {
+ %s
+ }
+end
+]]
+
+-- return function(result)
+-- if not result then
+-- return { }
+-- end
+-- local nofrows = #result
+-- if nofrows == 0 then
+-- return { }
+-- end
+-- local target = { } -- no %s needed here
+-- for i=1,nofrows do
+-- target[%s] = {
+-- %s
+-- }
+-- end
+-- return result
+-- end
+
+local celltemplate = "cells[%s]"
+
+methods.mysql = {
+ execute = execute,
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
+
+package.loaded["util-sql-imp-ffi"] = methods.mysql
+package.loaded["util-sql-imp-mysql"] = methods.mysql
+package.loaded["util-sql-imp-library"] = methods.mysql
+package.loaded[libname] = methods.mysql
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-postgress.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-postgress.lmt
new file mode 100644
index 00000000000..629079b01a5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-postgress.lmt
@@ -0,0 +1,200 @@
+if not modules then modules = { } end modules ['libs-imp-postgress'] = {
+ version = 1.001,
+ comment = "companion to util-sql.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Not yet untested !
+
+-- c:/data/develop/tex-context/tex/texmf-win64/bin/lib/luametatex/lua/copies/postgress/libpq.dll
+
+-- we can now share the code between mysql, postgress, sqlite ... todo
+
+local libname = "postgress"
+local libfile = "libpq"
+
+local postgresslib = resolvers.libraries.validoptional(libname)
+
+if not postgresslib then return end
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local lpegmatch = lpeg.match
+local setmetatable = setmetatable
+
+local sql = utilities.sql or require("util-sql")
+local report = logs.reporter(libname)
+
+local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
+local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
+
+local postgress_open = postgresslib.open
+local postgress_close = postgresslib.close
+local postgress_execute = postgresslib.execute
+local postgress_getmessage = postgresslib.getmessage
+
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local preparetemplate = helpers.preparetemplate
+local querysplitter = helpers.querysplitter -- not needed
+local cache = { }
+
+local function connect(specification)
+ return postgress_open(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+end
+
+local function execute_once(specification,retry)
+ if okay() then
+ if trace_sql then
+ report("executing postgress")
+ end
+ if not validspecification(specification) then
+ report("error in specification")
+ end
+ local query = preparetemplate(specification)
+ if not query then
+ report("error in preparation")
+ return
+ else
+ query = lpegmatch(querysplitter,query) -- not needed
+ end
+ local base = specification.database -- or specification.presets and specification.presets.database
+ if not base then
+ report("no database specified")
+ return
+ end
+ local result = { }
+ local keys = { }
+ local id = specification.id
+ local db = nil
+ if id then
+ local session = cache[id]
+ if session then
+ db = session.db
+ else
+ db = connect(specification)
+ if not db then
+ report("no session database specified")
+ else
+ cache[id] = {
+ specification = specification,
+ db = db,
+ }
+ end
+ end
+ else
+ db = connect(specification)
+ end
+ if not db then
+ report("no database opened")
+ else
+ local converter = specification.converter
+ local nofrows = 0
+ local callback = nil
+ if converter then
+ local convert = converter.postgress
+ callback = function(nofcolumns,values,fields)
+ nofrows = nofrows + 1
+ result[nofrows] = convert(values)
+ end
+ else
+ callback = function(nofcolumns,values,fields)
+ local column = { }
+ for i=1,nofcolumns do
+ local field
+ if fields then
+ field = fields[i]
+ keys[i] = field
+ else
+ field = keys[i]
+ end
+ if field then
+ column[field] = values[i]
+ end
+ end
+ nofrows = nofrows + 1
+ result[nofrows] = column
+ end
+ end
+ for i=1,#query do
+ local okay = postgress_execute(db,query[i],callback)
+ if not okay then
+ if id and retry and i == 1 then
+ report("error: %s, retrying to connect",postgress_getmessage(db))
+ postgress_close(db)
+ cache[id] = nil
+ return execute_once(specification,false)
+ else
+ report("error: %s",postgress_getmessage(db))
+ end
+ end
+ end
+ end
+ if db and not id then
+ postgress_close(db)
+ end
+ -- bonus
+ local one = result[1]
+ if one then
+ setmetatable(result,{ __index = one } )
+ end
+ --
+ return result, keys
+ else
+ report("error: ","no library loaded")
+ end
+end
+
+local function execute(specification)
+ return execute_once(specification,true)
+end
+
+-- Here we build the dataset stepwise so we don't use the data hack that
+-- is used in the client variant.
+
+local wraptemplate = [[
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(cells)
+ -- %s (not needed)
+ -- %s (not needed)
+ return {
+ %s
+ }
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.postgress = {
+ execute = execute,
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
+
+package.loaded["util-sql-imp-postgress"] = methods.postgress
+package.loaded[libname] = methods.postgress
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-sqlite.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-sqlite.lmt
new file mode 100644
index 00000000000..5d38986f347
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-sqlite.lmt
@@ -0,0 +1,187 @@
+if not modules then modules = { } end modules ['libs-imp-sqlite'] = {
+ version = 1.001,
+ comment = "companion to util-sql.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- c:/data/develop/tex-context/tex/texmf-win64/bin/lib/luametatex/lua/copies/sqlite/sqlite3.dll
+
+local libname = "sqlite"
+local libfile = "sqlite3"
+
+local sqlitelib = resolvers.libraries.validoptional(libname)
+
+if not sqlitelib then return end
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local next, tonumber = next, tonumber
+local setmetatable = setmetatable
+local formatters = string.formatters
+
+local sql = utilities.sql or require("util-sql")
+local report = logs.reporter(libname)
+
+local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
+local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
+
+local sqlite_open = sqlitelib.open
+local sqlite_close = sqlitelib.close
+local sqlite_execute = sqlitelib.execute
+local sqlite_getmessage = sqlitelib.getmessage
+
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local preparetemplate = helpers.preparetemplate
+local cache = { }
+
+-- synchronous journal_mode locking_mode 1000 logger inserts
+--
+-- normal normal normal 6.8
+-- off off normal 0.1
+-- normal off normal 2.1
+-- normal persist normal 5.8
+-- normal truncate normal 4.2
+-- normal truncate exclusive 4.1
+
+local f_preamble = formatters[ [[
+ATTACH `%s` AS `%s` ;
+PRAGMA `%s`.synchronous = normal ;
+]] ]
+
+local function execute(specification)
+ if okay() then
+ if trace_sql then
+ report("executing sqlite")
+ end
+ if not validspecification(specification) then
+ report("error in specification")
+ end
+ local query = preparetemplate(specification)
+ if not query then
+ report("error in preparation")
+ return
+ end
+ local base = specification.database -- or specification.presets and specification.presets.database
+ if not base then
+ report("no database specified")
+ return
+ end
+ local filename = file.addsuffix(base,"db")
+ local result = { }
+ local keys = { }
+ local id = specification.id
+ local db = nil
+ local preamble = nil
+ if id then
+ local session = cache[id]
+ if session then
+ db = session.db
+ else
+ db = sqlite_open(filename)
+ preamble = f_preamble(filename,base,base)
+ if not db then
+ report("no session database specified")
+ else
+ cache[id] = {
+ name = filename,
+ db = db,
+ }
+ end
+ end
+ else
+ db = open_db(filename)
+ preamble = f_preamble(filename,base,base)
+ end
+ if not db then
+ report("no database opened")
+ else
+ local converter = specification.converter
+ local nofrows = 0
+ local callback = nil
+ if preamble then
+ query = preamble .. query -- only needed in open
+ end
+ if converter then
+ local convert = converter.sqlite
+ callback = function(nofcolumns,values,fields)
+ nofrows = nofrows + 1
+ result[nofrows] = convert(values)
+ end
+ else
+ callback = function(nofcolumns,values,fields)
+ local column = { }
+ for i=1,nofcolumns do
+ local field
+ if fields then
+ field = fields[i]
+ keys[i] = field
+ else
+ field = keys[i]
+ end
+ if field then
+ column[field] = values[i]
+ end
+ end
+ nofrows = nofrows + 1
+ result[nofrows] = column
+ end
+ end
+ local okay = sqlite_execute(db,query,callback)
+ if not okay then
+ report("error: %s",sqlite_getmessage(db))
+ -- elseif converter then
+ -- result = converter.sqlite(result)
+ end
+ end
+ if db and not id then
+ sqlite_close(db)
+ end
+ return result, keys
+ else
+ report("error: ","no library loaded")
+ end
+end
+
+local wraptemplate = [[
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(cells)
+ -- %s (not needed)
+ -- %s (not needed)
+ return {
+ %s
+ }
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.sqlite = {
+ execute = execute,
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
+
+package.loaded["util-sql-imp-sqlite"] = methods.sqlite
+package.loaded[libname] = methods.sqlite
+
+return methods.sqlite
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.lmt b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.lmt
new file mode 100644
index 00000000000..2c346ae7d5f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.lmt
@@ -0,0 +1,161 @@
+if not modules then modules = { } end modules ['libs-imp-ghostscript'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local libname = "zint"
+local libfile = "libzint" -- what on unix?
+-- local libfile = "/usr/local/lib/libzint.so"
+
+local zintlib = resolvers.libraries.validoptional(libname)
+
+if not zintlib then return end
+
+local function okay()
+ if resolvers.libraries.optionalloaded(libname,libfile) then
+ okay = function() return true end
+ else
+ okay = function() return false end
+ end
+ return okay()
+end
+
+local zint = utilities.zint or { }
+utilities.zint = zint
+
+local zintlib_execute = zintlib.execute
+
+local next, type, unpack = next, type, unpack
+local lower, gsub = string.lower, string.gsub
+
+local mapping = {
+ ["code 11"] = 1, ["pharma one-track"] = 51, ["aztec code"] = 92,
+ ["standard 2of5"] = 2, ["pzn"] = 52, ["daft code"] = 93,
+ ["interleaved 2of5"] = 3, ["pharma two-track"] = 53, ["micro qr code"] = 97,
+ ["iata 2of5"] = 4, ["pdf417"] = 55, ["hibc code 128"] = 98,
+ ["data logic"] = 6, ["pdf417 trunc"] = 56, ["hibc code 39"] = 99,
+ ["industrial 2of5"] = 7, ["maxicode"] = 57, ["hibc data matrix"] = 102,
+ ["code 39"] = 8, ["qr code"] = 58, ["hibc qr code"] = 104,
+ ["extended code 39"] = 9, ["code 128-b"] = 60, ["hibc pdf417"] = 106,
+ ["ean"] = 13, ["ap standard customer"] = 63, ["hibc micropdf417"] = 108,
+ ["ean + check"] = 14, ["ap reply paid"] = 66, ["hibc codablock-f"] = 110,
+ ["gs1-128"] = 16, ["ap routing"] = 67, ["hibc aztec code"] = 112,
+ ["codabar"] = 18, ["ap redirection"] = 68, ["dotcode"] = 115,
+ ["code 128"] = 20, ["isbn"] = 69, ["han xin code"] = 116,
+ ["leitcode"] = 21, ["rm4scc"] = 70, ["rm mailmark"] = 121,
+ ["identcode"] = 22, ["data matrix"] = 71, ["aztec runes"] = 128,
+ ["code 16k"] = 23, ["ean-14"] = 72, ["code 32"] = 129,
+ ["code 49"] = 24, ["vin (north america)"] = 73, ["comp ean"] = 130,
+ ["code 93"] = 25, ["codablock-f"] = 74, ["comp gs1-128"] = 131,
+ ["flattermarken"] = 28, ["nve-18"] = 75, ["comp databar omni"] = 132,
+ ["gs1 databar omni"] = 29, ["japanese post"] = 76, ["comp databar ltd"] = 133,
+ ["gs1 databar ltd"] = 30, ["korea post"] = 77, ["comp databar expom"] = 134,
+ ["gs1 databar expom"] = 31, ["gs1 databar stack"] = 79, ["comp upc-a"] = 135,
+ ["telepen alpha"] = 32, ["gs1 databar stack omni"] = 80, ["comp upc-e"] = 136,
+ ["upc-a"] = 34, ["gs1 databar eso"] = 81, ["comp databar stack"] = 137,
+ ["upc-a + check"] = 35, ["planet"] = 82, ["comp databar stack omni"] = 138,
+ ["upc-e"] = 37, ["micropdf"] = 84, ["comp databar eso"] = 139,
+ ["upc-e + check"] = 38, ["usps onecode"] = 85, ["channel code"] = 140,
+ ["postnet"] = 40, ["uk plessey"] = 86, ["code one"] = 141,
+ ["msi plessey"] = 47, ["telepen numeric"] = 87, ["grid matrix"] = 142,
+ ["fim"] = 49, ["itf-14"] = 89, ["upnqr"] = 143,
+ ["logmars"] = 50, ["kix code"] = 90, ["rmqr"] = 145,
+}
+
+table.setmetatableindex(mapping,function(t,k)
+ local s = gsub(lower(k),"[^a-z0-9]","")
+ local v = rawget(t,s) or false
+ t[k] = v
+ return v
+end)
+
+local report = logs.reporter("zint")
+local context = context
+local shown = false
+
+----- f_rectangle = string.formatters["%sofill unitsquare xysized (%N,%N) shifted (%N,%N);"]
+
+local function execute(specification)
+ if okay() then
+ local code = specification.code
+ local text = specification.text
+ if code then
+ local id = mapping[code]
+ if id then
+ specification.code = id
+ local result = zintlib_execute(specification)
+ if result then
+ -- not that fast but if needed we can speed it up
+ context.startMPcode()
+ local rectangles = result.rectangles
+ local hexagons = result.hexagons
+ local circles = result.circles
+ local strings = result.strings
+ if rectangles then
+ local n = #rectangles
+ for i=1,n do
+ local r = rectangles[i]
+ context("%sofill unitsquare xysized (%N,%N) shifted (%N,%N);",
+ i == n and "d" or "n",r[3],r[4],r[1],r[2])
+ -- rectangles[i] = f_rectangle(i == n and "d" or "n",r[3],r[4],r[1],r[2])
+ end
+ -- context("% t",rectangles)
+ end
+ if hexagons then
+ local n = #hexagons
+ for i=1,#hexagons do
+ context("%sofill (%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--cycle;",
+ i == n and "d" or "n",unpack(hexagons[i]))
+ end
+ end
+ if circles then
+ local n = #circles
+ for i=1,#circles do
+ local c = circles[i]
+ context("%sofill unitcircle scaled %N shifted (%N,%N);",
+ i == n and "d" or "n",c[3],c[1],c[2])
+ end
+ end
+ if strings then
+ -- We set the font at the encapsulating level.
+ for i=1,#strings do
+ local s = strings[i]
+ context('draw textext("%s") scaled (%N/10) shifted (%N,%N);',
+ s[4],s[3],s[1],s[2])
+ end
+ end
+ context.stopMPcode()
+ end
+ else
+ report("unknown barcode alternative %a",code)
+ if not shown then
+ report("")
+ report("valid barcode alternatives:")
+ report("")
+ local list = table.sortedkeys(mapping)
+ for i=1,#list do
+ report(" %s", list[i])
+ end
+ report("")
+ shown = true
+ end
+ end
+ end
+ end
+end
+
+optional.loaded.zint = { execute = execute }
+
+interfaces.implement {
+ name = "zint",
+ actions = execute,
+ arguments = {
+ {
+ { "code" },
+ { "text" },
+ }
+ }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.mkxl b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.mkxl
new file mode 100644
index 00000000000..b2adf05ad82
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-imp-zint.mkxl
@@ -0,0 +1,64 @@
+%D \module
+%D [ file=m-zint,
+%D version=2010.12.07,
+%D title=\CONTEXT\ Extra Modules,
+%D subtitle=Zint Barcode Generator,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Using \type {zint} seems to be the easiest way to generate
+%D (PDF417) barcodes so therefore we now have this module. There
+%D are proper (also windows) binaries at:
+%D
+%D \starttyping
+%D http://www.zint.org.uk
+%D \stoptyping
+%D
+%D There is a bit more code than needed as we want to be able to
+%D feed names.
+
+\registerctxluafile{libs-imp-zint}{autosuffix}
+
+\unprotect
+
+\definefont[usedzintfont][dejavusansmono at 10bp]
+
+\permanent\protected\def\barcode[#1]% [alternative=,text=]
+ {\bgroup
+ \usedzintfont
+ \getdummyparameters
+ [\c!alternative=,\c!text=,#1]%
+ \scale
+ [#1]%
+ {\clf_zint
+ code {\dummyparameter\c!alternative}
+ text {\dummyparameter\c!text}
+ \relax}
+ \egroup}
+
+\protect
+
+\continueifinputfile{libs-imp-zint.mkxl}
+
+\starttext
+
+% \dorecurse{1000}{
+ \startTEXpage
+ \barcode[alternative=PDF417,text={Hans Hagen}]%
+ \blank
+ \barcode[alternative=PDF417,text={Ton Otten}]%
+ \blank
+ \barcode[alternative=ISBN,text=9789490688011]%
+ \blank
+ \barcode[alternative=isbn,text=9789490688011,width=3cm]%
+ \stopTEXpage
+% }
+
+\stoptext
+
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/libs-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/libs-ini.mkxl
new file mode 100644
index 00000000000..d77046fbffe
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/libs-ini.mkxl
@@ -0,0 +1,32 @@
+%D \module
+%D [ file=libs-ini,
+%D version=2020.01.02,
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Libraries,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{libs-ini}{} % shared with mkiv
+
+\registerctxluafile{libs-imp-curl}{autosuffix}
+\registerctxluafile{libs-imp-ghostscript}{autosuffix}
+\registerctxluafile{libs-imp-graphicsmagick}{autosuffix}
+
+% These are loaded on demand:
+
+% \registerctxluafile{libs-imp-mysql}{autosuffix}
+% \registerctxluafile{libs-imp-sqlite}{autosuffix}
+% \registerctxluafile{libs-imp-postgress}{autosuffix}
+% \registerctxluafile{libs-imp-kpse}{autosuffix}
+
+\unprotect
+
+% \permanent\protected\def\uselibrary[#1]%
+% {\clf_uselibrary{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-ano.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-ano.lmt
new file mode 100644
index 00000000000..86bcd4ad5dd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-ano.lmt
@@ -0,0 +1,1401 @@
+if not modules then modules = { } end modules ['lpdf-ano'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- when using rotation: \disabledirectives[refences.sharelinks] (maybe flag links)
+
+-- todo: /AA << WC << ... >> >> : WillClose actions etc
+
+-- internal references are indicated by a number (and turned into <autoprefix><number>)
+-- we only flush internal destinations that are referred
+
+local next, tostring, tonumber, rawget, type = next, tostring, tonumber, rawget, type
+local rep, format, find = string.rep, string.format, string.find
+local min = math.min
+local lpegmatch = lpeg.match
+local formatters = string.formatters
+local sortedkeys, concat = table.sortedkeys, table.concat
+
+local backends, lpdf = backends, lpdf
+
+local trace_references = false trackers.register("references.references", function(v) trace_references = v end)
+local trace_destinations = false trackers.register("references.destinations", function(v) trace_destinations = v end)
+local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end)
+
+local log_destinations = false directives.register("destinations.log", function(v) log_destinations = v end)
+local untex_urls = true directives.register("references.untexurls", function(v) untex_urls = v end)
+
+local report_references = logs.reporter("backend","references")
+local report_destinations = logs.reporter("backend","destinations")
+local report_bookmarks = logs.reporter("backend","bookmarks")
+
+local variables = interfaces.variables
+local v_auto = variables.auto
+local v_page = variables.page
+local v_name = variables.name
+
+local factor = number.dimenfactors.bp
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+local registrations = backends.pdf.registrations
+
+local javascriptcode = interactions.javascripts.code
+
+local references = structures.references
+local bookmarks = structures.bookmarks
+
+local flaginternals = references.flaginternals
+local usedinternals = references.usedinternals
+local usedviews = references.usedviews
+
+local runners = references.runners
+local specials = references.specials
+local handlers = references.handlers
+local executers = references.executers
+
+local nodepool = nodes.pool
+
+local new_latelua = nodepool.latelua
+
+local texgetcount = tex.getcount
+
+local jobpositions = job.positions
+local getpos = jobpositions.getpos
+local gethpos = jobpositions.gethpos
+local getvpos = jobpositions.getvpos
+
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfreference = lpdf.reference
+local pdfunicode = lpdf.unicode
+local pdfconstant = lpdf.constant
+local pdfnull = lpdf.null
+local pdfaddtocatalog = lpdf.addtocatalog
+local pdfaddtonames = lpdf.addtonames
+local pdfaddtopageattributes = lpdf.addtopageattributes
+local pdfrectangle = lpdf.rectangle
+
+local pdfflushobject
+local pdfshareobjectreference
+local pdfreserveobject
+local pdfpagereference
+local pdfdelayedobject
+local pdfregisterannotation
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfshareobjectreference = lpdf.shareobjectreference
+ pdfreserveobject = lpdf.reserveobject
+ pdfpagereference = lpdf.pagereference
+ pdfdelayedobject = lpdf.delayedobject
+ pdfregisterannotation = lpdf.registerannotation
+end)
+
+-- todo: 3dview
+
+----- pdf_annot = pdfconstant("Annot")
+local pdf_uri = pdfconstant("URI")
+local pdf_gotor = pdfconstant("GoToR")
+local pdf_goto = pdfconstant("GoTo")
+local pdf_launch = pdfconstant("Launch")
+local pdf_javascript = pdfconstant("JavaScript")
+local pdf_link = pdfconstant("Link")
+local pdf_n = pdfconstant("N")
+local pdf_t = pdfconstant("T")
+local pdf_fit = pdfconstant("Fit")
+local pdf_named = pdfconstant("Named")
+
+local autoprefix = "#"
+local usedautoprefixes = { }
+
+local function registerautoprefix(name)
+ local internal = autoprefix .. name
+ if usedautoprefixes[internal] == nil then
+ usedautoprefixes[internal] = false
+ end
+ return internal
+end
+
+local function useautoprefix(name)
+ local internal = autoprefix .. name
+ usedautoprefixes[internal] = true
+ return internal
+end
+
+local function checkautoprefixes(destinations)
+ for k, v in next, usedautoprefixes do
+ if not v then
+ if trace_destinations then
+ report_destinations("flushing unused autoprefix %a",k)
+ end
+ destinations[k] = nil
+ end
+ end
+end
+
+local maxslice = 32 -- could be made configureable ... 64 is also ok
+
+local function pdfmakenametree(list,apply)
+ if not next(list) then
+ return
+ end
+ local slices = { }
+ local sorted = sortedkeys(list)
+ local size = #sorted
+ local maxslice = maxslice
+ if size <= 1.5*maxslice then
+ maxslice = size
+ end
+ for i=1,size,maxslice do
+ local amount = min(i+maxslice-1,size)
+ local names = pdfarray { }
+ local n = 0
+ for j=i,amount do
+ local name = sorted[j]
+ local target = list[name]
+ n = n + 1 ; names[n] = tostring(name)
+ n = n + 1 ; names[n] = apply and apply(target) or target
+ end
+ local first = sorted[i]
+ local last = sorted[amount]
+ local limits = pdfarray {
+ first,
+ last,
+ }
+ local d = pdfdictionary {
+ Names = names,
+ Limits = limits,
+ }
+ slices[#slices+1] = {
+ reference = pdfreference(pdfflushobject(d)),
+ limits = limits,
+ }
+ end
+ local function collectkids(slices,first,last)
+ local f = slices[first]
+ local l = slices[last]
+ if f and l then
+ local k = pdfarray()
+ local n = 0
+ local d = pdfdictionary {
+ Kids = k,
+ Limits = pdfarray {
+ f.limits[1],
+ l.limits[2],
+ },
+ }
+ for i=first,last do
+ n = n + 1 ; k[n] = slices[i].reference
+ end
+ return d
+ end
+ end
+ if #slices == 1 then
+ return slices[1].reference
+ else
+ while true do
+ local size = #slices
+ if size > maxslice then
+ local temp = { }
+ local n = 0
+ for i=1,size,maxslice do
+ local kids = collectkids(slices,i,min(i+maxslice-1,size))
+ if kids then
+ n = n + 1
+ temp[n] = {
+ reference = pdfreference(pdfflushobject(kids)),
+ limits = kids.Limits,
+ }
+ else
+ -- error
+ end
+ end
+ slices = temp
+ else
+ local kids = collectkids(slices,1,size)
+ if kids then
+ return pdfreference(pdfflushobject(kids))
+ else
+ -- error
+ return
+ end
+ end
+ end
+ end
+end
+
+lpdf.makenametree = pdfmakenametree
+
+-- Bah, I hate this kind of features .. anyway, as we have delayed resolving we
+-- only support a document-wide setup and it has to be set before the first one
+-- is used. Also, we default to a non-intrusive gray and the outline is kept
+-- thin without dashing lines. This is as far as I'm prepared to go. This way
+-- it can also be used as a debug feature.
+
+local pdf_border_style = pdfarray { 0, 0, 0 } -- radius radius linewidth
+local pdf_border_color = nil
+local set_border = false
+
+local function pdfborder()
+ set_border = true
+ return pdf_border_style, pdf_border_color
+end
+
+lpdf.border = pdfborder
+
+directives.register("references.border",function(v)
+ if v and not set_border then
+ if type(v) == "string" then
+ local m = attributes.list[attributes.private('color')] or { }
+ local c = m and m[v]
+ local v = c and attributes.colors.value(c)
+ if v then
+ local r = v[3]
+ local g = v[4]
+ local b = v[5]
+ -- if r == g and g == b then
+ -- pdf_border_color = pdfarray { r } -- reduced, not not ... bugged viewers
+ -- else
+ pdf_border_color = pdfarray { r, g, b } -- always rgb
+ -- end
+ end
+ end
+ if not pdf_border_color then
+ pdf_border_color = pdfarray { .6, .6, .6 } -- no reduce to { 0.6 } as there are buggy viewers out there
+ end
+ pdf_border_style = pdfarray { 0, 0, .5 } -- < 0.5 is not show by acrobat (at least not in my version)
+ end
+end)
+
+-- the used and flag code here is somewhat messy in the sense
+-- that it belongs in strc-ref but at the same time depends on
+-- the backend so we keep it here
+
+-- the caching is somewhat memory intense on the one hand but
+-- it saves many small temporary tables so it might pay off
+
+local pagedestinations = setmetatableindex(function(t,k)
+ k = tonumber(k)
+ if not k or k <= 0 then
+ return pdfnull()
+ end
+ local v = rawget(t,k)
+ if v then
+ -- report_references("page number expected, got %s: %a",type(k),k)
+ return v
+ end
+ local v = k > 0 and pdfarray {
+ pdfreference(pdfpagereference(k)),
+ pdf_fit,
+ } or pdfnull()
+ t[k] = v
+ return v
+end)
+
+local pagereferences = setmetatableindex(function(t,k)
+ k = tonumber(k)
+ if not k or k <= 0 then
+ return nil
+ end
+ local v = rawget(t,k)
+ if v then
+ return v
+ end
+ local v = pdfdictionary { -- can be cached
+ S = pdf_goto,
+ D = pagedestinations[k],
+ }
+ t[k] = v
+ return v
+end)
+
+local defaultdestination = pdfarray { 0, pdf_fit }
+
+-- fit is default (see lpdf-nod)
+
+local destinations = { }
+local reported = setmetatableindex("table")
+
+local function pdfregisterdestination(name,reference)
+ local d = destinations[name]
+ if d then
+ if not reported[name][reference] then
+ report_destinations("ignoring duplicate destination %a with reference %a",name,reference)
+ reported[name][reference] = true
+ end
+ else
+ destinations[name] = reference
+ end
+end
+
+lpdf.registerdestination = pdfregisterdestination
+
+logs.registerfinalactions(function()
+ if log_destinations and next(destinations) then
+ local report = logs.startfilelogging("references","used destinations")
+ local n = 0
+ for destination, pagenumber in table.sortedhash(destinations) do
+ report("% 4i : %-5s : %s",pagenumber,usedviews[destination] or defaultview,destination)
+ n = n + 1
+ end
+ logs.stopfilelogging()
+ report_destinations("%s destinations saved in log file",n)
+ end
+end)
+
+local function pdfdestinationspecification()
+ if next(destinations) then -- safeguard
+ checkautoprefixes(destinations)
+ local r = pdfmakenametree(destinations,pdfreference)
+ if r then
+ pdfaddtonames("Dests",r)
+ end
+ if not log_destinations then
+ destinations = nil
+ end
+ end
+end
+
+lpdf.destinationspecification = pdfdestinationspecification
+
+lpdf.registerdocumentfinalizer(pdfdestinationspecification,"collect destinations")
+
+-- todo
+
+local destinations = { }
+
+local f_xyz = formatters["<< /D [ %i 0 R /XYZ %.6N %.6N null ] >>"]
+local f_fit = formatters["<< /D [ %i 0 R /Fit ] >>"]
+local f_fitb = formatters["<< /D [ %i 0 R /FitB ] >>"]
+local f_fith = formatters["<< /D [ %i 0 R /FitH %.6N ] >>"]
+local f_fitv = formatters["<< /D [ %i 0 R /FitV %.6N ] >>"]
+local f_fitbh = formatters["<< /D [ %i 0 R /FitBH %.6N ] >>"]
+local f_fitbv = formatters["<< /D [ %i 0 R /FitBV %.6N ] >>"]
+local f_fitr = formatters["<< /D [ %i 0 R /FitR %.6N %.6N %.6N %.6N ] >>"]
+
+local v_standard = variables.standard
+local v_frame = variables.frame
+local v_width = variables.width
+local v_minwidth = variables.minwidth
+local v_height = variables.height
+local v_minheight = variables.minheight
+local v_fit = variables.fit
+local v_tight = variables.tight
+
+-- nicer is to create dictionaries and set properties but it's a bit overkill
+
+-- The problem with the following settings is that they are guesses: we never know
+-- if a box is part of something larger that needs to be in view, or that we are
+-- dealing with a vbox or vtop so the used h/d values cannot be trusted in a tight
+-- view. Of course some decent additional offset would be nice so maybe i'll add
+-- that some day. I never use anything else than 'fit' anyway as I think that the
+-- document should fit the device (and vice versa). In fact, with todays swipe
+-- and finger zooming this whole view is rather useless and as with any zooming
+-- one looses the overview and keeps zooming.
+
+-- todo: scaling
+
+-- local destinationactions = {
+-- [v_standard] = function(r,w,h,d) return f_xyz (r,gethpos()*factor,(getvpos()+h)*factor) end, -- local left,top with no zoom
+-- [v_frame] = function(r,w,h,d) return f_fitr (r,pdfrectangle(w,h,d)) end, -- fit rectangle in window
+-- [v_width] = function(r,w,h,d) return f_fith (r,(getvpos()+h)*factor) end, -- top coordinate, fit width of page in window
+-- [v_minwidth] = function(r,w,h,d) return f_fitbh(r,(getvpos()+h)*factor) end, -- top coordinate, fit width of content in window
+-- [v_height] = function(r,w,h,d) return f_fitv (r,gethpos()*factor) end, -- left coordinate, fit height of page in window
+-- [v_minheight] = function(r,w,h,d) return f_fitbv(r,gethpos()*factor) end, -- left coordinate, fit height of content in window [v_fit] = f_fit, -- fit page in window
+-- [v_tight] = f_fitb, -- fit content in window
+-- [v_fit] = f_fit,
+-- }
+
+local destinationactions = {
+ [v_standard] = function(r,w,h,d,o) -- local left,top with no zoom
+ local tx, ty = getpos()
+ return f_xyz(r,tx*factor,(ty+h+2*o)*factor) -- we can assume margins
+ end,
+ [v_frame] = function(r,w,h,d,o) -- fit rectangle in window
+ return f_fitr(r,pdfrectangle(w,h,d,o))
+ end,
+ [v_width] = function(r,w,h,d,o) -- top coordinate, fit width of page in window
+ return f_fith(r,(getvpos()+h+o)*factor)
+ end,
+ [v_minwidth] = function(r,w,h,d,o) -- top coordinate, fit width of content in window
+ return f_fitbh(r,(getvpos()+h+o)*factor)
+ end,
+ [v_height] = function(r,w,h,d,o) -- left coordinate, fit height of page in window
+ return f_fitv(r,(gethpos())*factor)
+ end,
+ [v_minheight] = function(r,w,h,d,o) -- left coordinate, fit height of content in window
+ return f_fitbv(r,(gethpos())*factor)
+ end,
+ [v_tight] = f_fitb, -- fit content in window
+ [v_fit] = f_fit, -- fit content in window
+}
+
+local mapping = {
+ [v_standard] = v_standard, xyz = v_standard,
+ [v_frame] = v_frame, fitr = v_frame,
+ [v_width] = v_width, fith = v_width,
+ [v_minwidth] = v_minwidth, fitbh = v_minwidth,
+ [v_height] = v_height, fitv = v_height,
+ [v_minheight] = v_minheight, fitbv = v_minheight,
+ [v_fit] = v_fit, fit = v_fit,
+ [v_tight] = v_tight, fitb = v_tight,
+}
+
+local defaultview = v_fit
+local defaultaction = destinationactions[defaultview]
+local offset = 0 -- 65536*5
+
+directives.register("destinations.offset", function(v)
+ offset = string.todimen(v) or 0
+end)
+
+-- A complication is that we need to use named destinations when we have views so we
+-- end up with a mix. A previous versions just output multiple destinations but now
+-- that we moved all to here we can be more sparse.
+
+local pagedestinations = setmetatableindex(function(t,k) -- not the same as the one above!
+ local v = pdfdelayedobject(f_fit(k))
+ t[k] = v
+ return v
+end)
+
+local function flushdestination(specification)
+ local names = specification.names
+ local view = specification.view
+ local r = pdfpagereference(texgetcount("realpageno"))
+ if (references.innermethod ~= v_name) and (view == defaultview or not view or view == "") then
+ r = pagedestinations[r]
+ else
+ local action = view and destinationactions[view] or defaultaction
+ r = pdfdelayedobject(action(r,specification.width,specification.height,specification.depth,offset))
+ end
+ for n=1,#names do
+ local name = names[n]
+ if name then
+ pdfregisterdestination(name,r)
+ end
+ end
+end
+
+function nodeinjections.destination(width,height,depth,names,view)
+ -- todo check if begin end node / was comment
+ view = view and mapping[view] or defaultview
+ if trace_destinations then
+ report_destinations("width %p, height %p, depth %p, names %|t, view %a",width,height,depth,names,view)
+ end
+ local method = references.innermethod
+ local noview = view == defaultview
+ local doview = false
+ -- we could save some aut's by using a name when given but it doesn't pay off apart
+ -- from making the code messy and tracing hard .. we only save some destinations
+ -- which we already share anyway
+ if method == v_page then
+ for n=1,#names do
+ local name = names[n]
+ local used = usedviews[name]
+ if used and used ~= true then
+ -- already done, maybe a warning
+ elseif type(name) == "number" then
+ -- if noview then
+ -- usedviews[name] = view
+ -- names[n] = false
+ -- else
+ usedviews[name] = view
+ names[n] = false
+ -- end
+ else
+ usedviews[name] = view
+ end
+ end
+ elseif method == v_name then
+ for n=1,#names do
+ local name = names[n]
+ local used = usedviews[name]
+ if used and used ~= true then
+ -- already done, maybe a warning
+ elseif type(name) == "number" then
+ local used = usedinternals[name]
+ usedviews[name] = view
+ names[n] = registerautoprefix(name)
+ doview = true
+ else
+ usedviews[name] = view
+ doview = true
+ end
+ end
+ else
+ for n=1,#names do
+ local name = names[n]
+ if usedviews[name] then
+ -- already done, maybe a warning
+ elseif type(name) == "number" then
+ if noview then
+ usedviews[name] = view
+ names[n] = false
+ else
+ local used = usedinternals[name]
+ if used and used ~= defaultview then
+ usedviews[name] = view
+ names[n] = registerautoprefix(name)
+ doview = true
+ else
+ names[n] = false
+ end
+ end
+ else
+ usedviews[name] = view
+ doview = true
+ end
+ end
+ end
+ if doview then
+ return new_latelua {
+ action = flushdestination,
+ width = width,
+ height = height,
+ depth = depth,
+ names = names,
+ view = view,
+ }
+ end
+end
+
+-- we could share dictionaries ... todo
+
+local function pdflinkpage(page)
+ return pagereferences[page]
+end
+
+local function pdflinkinternal(internal,page)
+ -- local method = references.innermethod
+ if internal then
+ flaginternals[internal] = true -- for bookmarks and so
+ local used = usedinternals[internal]
+ if used == defaultview or used == true then
+ return pagereferences[page]
+ else
+ if type(internal) ~= "string" then
+ internal = useautoprefix(internal)
+ end
+ return pdfdictionary {
+ S = pdf_goto,
+ D = internal,
+ }
+ end
+ else
+ return pagereferences[page]
+ end
+end
+
+local function pdflinkname(destination,internal,page)
+ local method = references.innermethod
+ if method == v_auto then
+ local used = defaultview
+ if internal then
+ flaginternals[internal] = true -- for bookmarks and so
+ used = usedinternals[internal] or defaultview
+ end
+ if used == defaultview then -- or used == true then
+ return pagereferences[page]
+ else
+ return pdfdictionary {
+ S = pdf_goto,
+ D = destination,
+ }
+ end
+ elseif method == v_name then
+ -- flaginternals[internal] = true -- for bookmarks and so
+ return pdfdictionary {
+ S = pdf_goto,
+ D = destination,
+ }
+ else
+ return pagereferences[page]
+ end
+end
+
+-- annotations
+
+local function pdffilelink(filename,destination,page,actions)
+ if not filename or filename == "" or file.basename(filename) == tex.jobname then
+ return false
+ end
+ filename = file.addsuffix(filename,"pdf")
+ if (not destination or destination == "") or (references.outermethod == v_page) then
+ destination = pdfarray { (page or 1) - 1, pdf_fit }
+ end
+ return pdfdictionary {
+ S = pdf_gotor, -- can also be pdf_launch
+ F = filename,
+ D = destination or defaultdestination,
+ NewWindow = actions.newwindow and true or nil,
+ }
+end
+
+local untex = references.urls.untex
+
+local function pdfurllink(url,destination,page)
+ if not url or url == "" then
+ return false
+ end
+ if untex_urls then
+ url = untex(url) -- last minute cleanup of \* and spaces
+ end
+ if destination and destination ~= "" then
+ url = url .. "#" .. destination
+ end
+ return pdfdictionary {
+ S = pdf_uri,
+ URI = url,
+ }
+end
+
+local function pdflaunch(program,parameters)
+ if not program or program == "" then
+ return false
+ end
+ return pdfdictionary {
+ S = pdf_launch,
+ F = program,
+ D = ".",
+ P = parameters ~= "" and parameters or nil
+ }
+end
+
+local function pdfjavascript(name,arguments)
+ local script = javascriptcode(name,arguments) -- make into object (hash)
+ if script then
+ return pdfdictionary {
+ S = pdf_javascript,
+ JS = script,
+ }
+ end
+end
+
+local function pdfaction(actions)
+ local nofactions = #actions
+ if nofactions > 0 then
+ local a = actions[1]
+ local action = runners[a.kind]
+ if action then
+ action = action(a,actions)
+ end
+ if action then
+ local first = action
+ for i=2,nofactions do
+ local a = actions[i]
+ local what = runners[a.kind]
+ if what then
+ what = what(a,actions)
+ end
+ if action == what then
+ -- ignore this one, else we get a loop
+ elseif what then
+ action.Next = what
+ action = what
+ else
+ -- error
+ return nil
+ end
+ end
+ return first, actions.n or #actions
+ end
+ end
+end
+
+lpdf.action = pdfaction
+
+function codeinjections.prerollreference(actions) -- share can become option
+ if actions then
+ local main, n = pdfaction(actions)
+ if main then
+ local bs, bc = pdfborder()
+ main = pdfdictionary {
+ Subtype = pdf_link,
+ -- Border = bs,
+ Border = pdfshareobjectreference(bs),
+ C = bc,
+ H = (not actions.highlight and pdf_n) or nil,
+ A = pdfshareobjectreference(main),
+ F = 4, -- print (mandate in pdf/a)
+ }
+ return main("A"), n
+ end
+ end
+end
+
+-- local function use_normal_annotations()
+--
+-- local function reference(width,height,depth,prerolled) -- keep this one
+-- if prerolled then
+-- if trace_references then
+-- report_references("width %p, height %p, depth %p, prerolled %a",width,height,depth,prerolled)
+-- end
+-- return pdfannotation_node(width,height,depth,prerolled)
+-- end
+-- end
+--
+-- local function finishreference()
+-- end
+--
+-- return reference, finishreference
+--
+-- end
+
+-- eventually we can do this for special refs only
+
+local hashed = { }
+local nofunique = 0
+local nofused = 0
+local nofspecial = 0
+local share = true
+
+local f_annot = formatters["<< /Type /Annot %s /Rect [ %.6N %.6N %.6N %.6N ] >>"]
+local f_quadp = formatters["<< /Type /Annot %s /QuadPoints [ %s ] /Rect [ %.6N %.6N %.6N %.6N ] >>"]
+
+directives.register("references.sharelinks", function(v)
+ share = v
+end)
+
+setmetatableindex(hashed,function(t,k)
+ local v = pdfdelayedobject(k)
+ if share then
+ t[k] = v
+ end
+ nofunique = nofunique + 1
+ return v
+end)
+
+local function toquadpoints(paths)
+ local t, n = { }, 0
+ for i=1,#paths do
+ local path = paths[i]
+ local size = #path
+ for j=1,size do
+ local p = path[j]
+ n = n + 1 ; t[n] = p[1]
+ n = n + 1 ; t[n] = p[2]
+ end
+ local m = size % 4
+ if m > 0 then
+ local p = path[size]
+ for j=size+1,m do
+ n = n + 1 ; t[n] = p[1]
+ n = n + 1 ; t[n] = p[2]
+ end
+ end
+ end
+ return concat(t," ")
+end
+
+local function finishreference(specification)
+ local prerolled = specification.prerolled
+ local quadpoints = specification.mesh
+ local llx, lly,
+ urx, ury = pdfrectangle(specification.width,specification.height,specification.depth)
+ local specifier = nil
+ if quadpoints and #quadpoints > 0 then
+ specifier = f_quadp(prerolled,toquadpoints(quadpoints),llx,lly,urx,ury)
+ else
+ specifier = f_annot(prerolled,llx,lly,urx,ury)
+ end
+ nofused = nofused + 1
+ return pdfregisterannotation(hashed[specifier])
+end
+
+local function finishannotation(specification)
+ local prerolled = specification.prerolled
+ local objref = specification.objref
+ if type(prerolled) == "function" then
+ prerolled = prerolled()
+ end
+ local annot = f_annot(prerolled,pdfrectangle(specification.width,specification.height,specification.depth))
+ if objref then
+ pdfdelayedobject(annot,objref)
+ else
+ objref = pdfdelayedobject(annot)
+ end
+ nofspecial = nofspecial + 1
+ return pdfregisterannotation(objref)
+end
+
+function nodeinjections.reference(width,height,depth,prerolled,mesh)
+ if prerolled then
+ if trace_references then
+ report_references("link: width %p, height %p, depth %p, prerolled %a",width,height,depth,prerolled)
+ end
+ return new_latelua {
+ action = finishreference,
+ width = width,
+ height = height,
+ depth = depth,
+ prerolled = prerolled,
+ mesh = mesh,
+ }
+ end
+end
+
+function nodeinjections.annotation(width,height,depth,prerolled,objref)
+ if prerolled then
+ if trace_references then
+ report_references("special: width %p, height %p, depth %p, prerolled %a",width,height,depth,
+ type(prerolled) == "string" and prerolled or "-")
+ end
+ return new_latelua {
+ action = finishannotation,
+ width = width,
+ height = height,
+ depth = depth,
+ prerolled = prerolled,
+ objref = objref or false,
+ }
+ end
+end
+
+-- beware, we register during a latelua sweep so we have to make sure that
+-- we finalize after that (also in a latelua for the moment as we have no
+-- callback yet)
+
+local annotations = nil
+
+function lpdf.registerannotation(n)
+ if annotations then
+ annotations[#annotations+1] = pdfreference(n)
+ else
+ annotations = pdfarray { pdfreference(n) } -- no need to use lpdf.array cum suis
+ end
+end
+
+pdfregisterannotation = lpdf.registerannotation
+
+function lpdf.annotationspecification()
+ if annotations then
+ local r = pdfdelayedobject(tostring(annotations)) -- delayed so okay in latelua
+ if r then
+ pdfaddtopageattributes("Annots",pdfreference(r))
+ end
+ annotations = nil
+ end
+end
+
+lpdf.registerpagefinalizer(lpdf.annotationspecification,"finalize annotations")
+
+statistics.register("pdf annotations", function()
+ if nofused > 0 or nofspecial > 0 then
+ return format("%s links (%s unique), %s special",nofused,nofunique,nofspecial)
+ else
+ return nil
+ end
+end)
+
+-- runners and specials
+
+local splitter = lpeg.splitat(",",true)
+
+runners["inner"] = function(var,actions)
+ local internal = false
+ local name = nil
+ local method = references.innermethod
+ local vi = var.i
+ local page = var.r
+ if vi then
+ local vir = vi.references
+ if vir then
+ -- todo: no need for it when we have a real reference ... although we need
+ -- this mess for prefixes anyway
+ local reference = vir.reference
+ if reference and reference ~= "" then
+ reference = lpegmatch(splitter,reference) or reference
+ var.inner = reference
+ local prefix = var.p
+ if prefix and prefix ~= "" then
+ var.prefix = prefix
+ name = prefix .. ":" .. reference
+ else
+ name = reference
+ end
+ end
+ internal = vir.internal
+ if internal then
+ flaginternals[internal] = true
+ end
+ end
+ end
+ if name then
+ return pdflinkname(name,internal,page)
+ elseif internal then
+ return pdflinkinternal(internal,page)
+ elseif page then
+ return pdflinkpage(page)
+ else
+ -- real bad
+ end
+end
+
+runners["inner with arguments"] = function(var,actions)
+ report_references("todo: inner with arguments")
+ return false
+end
+
+runners["outer"] = function(var,actions)
+ local file, url = references.checkedfileorurl(var.outer,var.outer)
+ if file then
+ return pdffilelink(file,var.arguments,nil,actions)
+ elseif url then
+ return pdfurllink(url,var.arguments,nil,actions)
+ end
+end
+
+runners["outer with inner"] = function(var,actions)
+ return pdffilelink(references.checkedfile(var.outer),var.inner,var.r,actions)
+end
+
+runners["special outer with operation"] = function(var,actions)
+ local handler = specials[var.special]
+ return handler and handler(var,actions)
+end
+
+runners["special outer"] = function(var,actions)
+ report_references("todo: special outer")
+ return false
+end
+
+runners["special"] = function(var,actions)
+ local handler = specials[var.special]
+ return handler and handler(var,actions)
+end
+
+runners["outer with inner with arguments"] = function(var,actions)
+ report_references("todo: outer with inner with arguments")
+ return false
+end
+
+runners["outer with special and operation and arguments"] = function(var,actions)
+ report_references("todo: outer with special and operation and arguments")
+ return false
+end
+
+runners["outer with special"] = function(var,actions)
+ report_references("todo: outer with special")
+ return false
+end
+
+runners["outer with special and operation"] = function(var,actions)
+ report_references("todo: outer with special and operation")
+ return false
+end
+
+runners["special operation"] = runners["special"]
+runners["special operation with arguments"] = runners["special"]
+
+local reported = { }
+
+function specials.internal(var,actions) -- better resolve in strc-ref
+ local o = var.operation
+ local i = o and tonumber(o)
+ local v = i and references.internals[i]
+ if v then
+ flaginternals[i] = true -- also done in pdflinkinternal
+ return pdflinkinternal(i,v.references.realpage)
+ end
+ local v = i or o or "<unset>"
+ if not reported[v] then
+ report_references("no internal reference %a",v)
+ reported[v] = true
+ end
+end
+
+-- realpage already resolved
+
+specials.i = specials.internal
+
+local pages = references.pages
+
+function specials.page(var,actions)
+ local file = var.f
+ if file then
+ return pdffilelink(references.checkedfile(file),nil,var.operation,actions)
+ else
+ local p = var.r
+ if not p then -- todo: call special from reference code
+ p = pages[var.operation]
+ if type(p) == "function" then -- double
+ p = p()
+ else
+ p = references.realpageofpage(tonumber(p))
+ end
+ end
+ return pdflinkpage(p or var.operation)
+ end
+end
+
+function specials.realpage(var,actions)
+ local file = var.f
+ if file then
+ return pdffilelink(references.checkedfile(file),nil,var.operation,actions)
+ else
+ return pdflinkpage(var.operation)
+ end
+end
+
+function specials.userpage(var,actions)
+ local file = var.f
+ if file then
+ return pdffilelink(references.checkedfile(file),nil,var.operation,actions)
+ else
+ local p = var.r
+ if not p then -- todo: call special from reference code
+ p = var.operation
+ if p then -- no function and special check here. only numbers
+ p = references.realpageofpage(tonumber(p))
+ end
+ -- if p then
+ -- var.r = p
+ -- end
+ end
+ return pdflinkpage(p or var.operation)
+ end
+end
+
+function specials.deltapage(var,actions)
+ local p = tonumber(var.operation)
+ if p then
+ p = references.checkedrealpage(p + texgetcount("realpageno"))
+ return pdflinkpage(p)
+ end
+end
+
+-- sections
+
+function specials.section(var,actions)
+ -- a bit duplicate
+ local sectionname = var.arguments
+ local destination = var.operation
+ local internal = structures.sections.internalreference(sectionname,destination)
+ if internal then
+ var.special = "internal"
+ var.operation = internal
+ var.arguments = nil
+ return specials.internal(var,actions)
+ end
+end
+
+-- todo, do this in references namespace ordered instead (this is an experiment)
+
+local splitter = lpeg.splitat(":")
+
+function specials.order(var,actions) -- references.specials !
+ local operation = var.operation
+ if operation then
+ local kind, name, n = lpegmatch(splitter,operation)
+ local order = structures.lists.ordered[kind]
+ order = order and order[name]
+ local v = order[tonumber(n)]
+ local r = v and v.references.realpage
+ if r then
+ var.operation = r -- brrr, but test anyway
+ return specials.page(var,actions)
+ end
+ end
+end
+
+function specials.url(var,actions)
+ return pdfurllink(references.checkedurl(var.operation),var.arguments,nil,actions)
+end
+
+function specials.file(var,actions)
+ return pdffilelink(references.checkedfile(var.operation),var.arguments,nil,actions)
+end
+
+function specials.fileorurl(var,actions)
+ local file, url = references.checkedfileorurl(var.operation,var.operation)
+ if file then
+ return pdffilelink(file,var.arguments,nil,actions)
+ elseif url then
+ return pdfurllink(url,var.arguments,nil,actions)
+ end
+end
+
+function specials.program(var,content)
+ local program = references.checkedprogram(var.operation)
+ return pdflaunch(program,var.arguments)
+end
+
+function specials.javascript(var)
+ return pdfjavascript(var.operation,var.arguments)
+end
+
+specials.JS = specials.javascript
+
+executers.importform = pdfdictionary { S = pdf_named, N = pdfconstant("AcroForm:ImportFDF") }
+executers.exportform = pdfdictionary { S = pdf_named, N = pdfconstant("AcroForm:ExportFDF") }
+executers.first = pdfdictionary { S = pdf_named, N = pdfconstant("FirstPage") }
+executers.previous = pdfdictionary { S = pdf_named, N = pdfconstant("PrevPage") }
+executers.next = pdfdictionary { S = pdf_named, N = pdfconstant("NextPage") }
+executers.last = pdfdictionary { S = pdf_named, N = pdfconstant("LastPage") }
+executers.backward = pdfdictionary { S = pdf_named, N = pdfconstant("GoBack") }
+executers.forward = pdfdictionary { S = pdf_named, N = pdfconstant("GoForward") }
+executers.print = pdfdictionary { S = pdf_named, N = pdfconstant("Print") }
+executers.exit = pdfdictionary { S = pdf_named, N = pdfconstant("Quit") }
+executers.close = pdfdictionary { S = pdf_named, N = pdfconstant("Close") }
+executers.save = pdfdictionary { S = pdf_named, N = pdfconstant("Save") }
+executers.savenamed = pdfdictionary { S = pdf_named, N = pdfconstant("SaveAs") }
+executers.opennamed = pdfdictionary { S = pdf_named, N = pdfconstant("Open") }
+executers.help = pdfdictionary { S = pdf_named, N = pdfconstant("HelpUserGuide") }
+executers.toggle = pdfdictionary { S = pdf_named, N = pdfconstant("FullScreen") }
+executers.search = pdfdictionary { S = pdf_named, N = pdfconstant("Find") }
+executers.searchagain = pdfdictionary { S = pdf_named, N = pdfconstant("FindAgain") }
+executers.gotopage = pdfdictionary { S = pdf_named, N = pdfconstant("GoToPage") }
+executers.query = pdfdictionary { S = pdf_named, N = pdfconstant("AcroSrch:Query") }
+executers.queryagain = pdfdictionary { S = pdf_named, N = pdfconstant("AcroSrch:NextHit") }
+executers.fitwidth = pdfdictionary { S = pdf_named, N = pdfconstant("FitWidth") }
+executers.fitheight = pdfdictionary { S = pdf_named, N = pdfconstant("FitHeight") }
+
+local function fieldset(arguments)
+ -- [\dogetfieldset{#1}]
+ return nil
+end
+
+function executers.resetform(arguments)
+ arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments)
+ return pdfdictionary {
+ S = pdfconstant("ResetForm"),
+ Field = fieldset(arguments[1])
+ }
+end
+
+local formmethod = "post" -- "get" "post"
+local formformat = "xml" -- "xml" "html" "fdf"
+
+-- bit 3 = html bit 6 = xml bit 4 = get
+
+local flags = {
+ get = {
+ html = 12, fdf = 8, xml = 40,
+ },
+ post = {
+ html = 4, fdf = 0, xml = 32,
+ }
+}
+
+function executers.submitform(arguments)
+ arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments)
+ local flag = flags[formmethod] or flags.post
+ flag = (flag and (flag[formformat] or flag.xml)) or 32 -- default: post, xml
+ return pdfdictionary {
+ S = pdfconstant("SubmitForm"),
+ F = arguments[1],
+ Field = fieldset(arguments[2]),
+ Flags = flag,
+ -- \PDFsubmitfiller
+ }
+end
+
+local pdf_hide = pdfconstant("Hide")
+
+function executers.hide(arguments)
+ return pdfdictionary {
+ S = pdf_hide,
+ H = true,
+ T = arguments,
+ }
+end
+
+function executers.show(arguments)
+ return pdfdictionary {
+ S = pdf_hide,
+ H = false,
+ T = arguments,
+ }
+end
+
+local pdf_movie = pdfconstant("Movie")
+local pdf_start = pdfconstant("Start")
+local pdf_stop = pdfconstant("Stop")
+local pdf_resume = pdfconstant("Resume")
+local pdf_pause = pdfconstant("Pause")
+
+local function movie_or_sound(operation,arguments)
+ arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments)
+ return pdfdictionary {
+ S = pdf_movie,
+ T = format("movie %s",arguments[1] or "noname"),
+ Operation = operation,
+ }
+end
+
+function executers.startmovie (arguments) return movie_or_sound(pdf_start ,arguments) end
+function executers.stopmovie (arguments) return movie_or_sound(pdf_stop ,arguments) end
+function executers.resumemovie(arguments) return movie_or_sound(pdf_resume,arguments) end
+function executers.pausemovie (arguments) return movie_or_sound(pdf_pause ,arguments) end
+
+function executers.startsound (arguments) return movie_or_sound(pdf_start ,arguments) end
+function executers.stopsound (arguments) return movie_or_sound(pdf_stop ,arguments) end
+function executers.resumesound(arguments) return movie_or_sound(pdf_resume,arguments) end
+function executers.pausesound (arguments) return movie_or_sound(pdf_pause ,arguments) end
+
+function specials.action(var)
+ local operation = var.operation
+ if var.operation and operation ~= "" then
+ local e = executers[operation]
+ if type(e) == "table" then
+ return e
+ elseif type(e) == "function" then
+ return e(var.arguments)
+ end
+ end
+end
+
+local function build(levels,start,parent,method,nested)
+ local startlevel = levels[start].level
+ local noflevels = #levels
+ local i = start
+ local n = 0
+ local child, entry, m, prev, first, last, f, l
+ while i and i <= noflevels do
+ local current = levels[i]
+ if current.usedpage == false then
+ -- safeguard
+ i = i + 1
+ else
+ local level = current.level
+ local title = current.title
+ local reference = current.reference
+ local opened = current.opened
+ local reftype = type(reference)
+ local block = nil
+ local variant = "unknown"
+ if reftype == "table" then
+ -- we're okay
+ variant = "list"
+ block = reference.block
+ realpage = reference.realpage
+ elseif reftype == "string" then
+ local resolved = references.identify("",reference)
+ realpage = resolved and structures.references.setreferencerealpage(resolved) or 0
+ if realpage > 0 then
+ variant = "realpage"
+ realpage = realpage
+ reference = structures.pages.collected[realpage]
+ block = reference and reference.block
+ end
+ elseif reftype == "number" then
+ if reference > 0 then
+ variant = "realpage"
+ realpage = reference
+ reference = structures.pages.collected[realpage]
+ block = reference and reference.block
+ end
+ else
+ -- error
+ end
+ current.block = block
+ if variant == "unknown" then
+ -- error, ignore
+ i = i + 1
+ -- elseif (level < startlevel) or (i > 1 and block ~= levels[i-1].reference.block) then
+ elseif (level < startlevel) or (i > 1 and block ~= levels[i-1].block) then
+ if nested then -- could be an option but otherwise we quit too soon
+ if entry then
+ pdfflushobject(child,entry)
+ else
+ report_bookmarks("error 1")
+ end
+ return i, n, first, last
+ else
+ report_bookmarks("confusing level change at level %a around %a",level,title)
+ startlevel = level
+ end
+ end
+ if level == startlevel then
+ if trace_bookmarks then
+ report_bookmarks("%3i %w%s %s",realpage,(level-1)*2,(opened and "+") or "-",title)
+ end
+ local prev = child
+ child = pdfreserveobject()
+ if entry then
+ entry.Next = child and pdfreference(child)
+ pdfflushobject(prev,entry)
+ end
+ local action = nil
+ if variant == "list" then
+ action = pdflinkinternal(reference.internal,reference.realpage)
+ elseif variant == "realpage" then
+ action = pagereferences[realpage]
+ else
+ -- hm, what to do
+ end
+ entry = pdfdictionary {
+ Title = pdfunicode(title),
+ Parent = parent,
+ Prev = prev and pdfreference(prev),
+ A = action,
+ }
+ -- entry.Dest = pdflinkinternal(reference.internal,reference.realpage)
+ if not first then
+ first, last = child, child
+ end
+ prev = child
+ last = prev
+ n = n + 1
+ i = i + 1
+ elseif i < noflevels and level > startlevel then
+ i, m, f, l = build(levels,i,pdfreference(child),method,true)
+ if entry then
+ entry.Count = (opened and m) or -m
+ if m > 0 then
+ entry.First = pdfreference(f)
+ entry.Last = pdfreference(l)
+ end
+ else
+ report_bookmarks("error 2")
+ end
+ else
+ -- missing intermediate level but ok
+ i, m, f, l = build(levels,i,pdfreference(child),method,true)
+ if entry then
+ entry.Count = (opened and m) or -m
+ if m > 0 then
+ entry.First = pdfreference(f)
+ entry.Last = pdfreference(l)
+ end
+ pdfflushobject(child,entry)
+ else
+ report_bookmarks("error 3")
+ end
+ return i, n, first, last
+ end
+ end
+ end
+ pdfflushobject(child,entry)
+ return nil, n, first, last
+end
+
+function codeinjections.addbookmarks(levels,method)
+ if levels and #levels > 0 then
+ local parent = pdfreserveobject()
+ local _, m, first, last = build(levels,1,pdfreference(parent),method or "internal",false)
+ local dict = pdfdictionary {
+ Type = pdfconstant("Outlines"),
+ First = pdfreference(first),
+ Last = pdfreference(last),
+ Count = m,
+ }
+ pdfflushobject(parent,dict)
+ pdfaddtocatalog("Outlines",lpdf.reference(parent))
+ end
+end
+
+-- this could also be hooked into the frontend finalizer
+
+lpdf.registerdocumentfinalizer(function() bookmarks.place() end,1,"bookmarks") -- hm, why indirect call
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-aux.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-aux.lmt
new file mode 100644
index 00000000000..91636ad04e0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-aux.lmt
@@ -0,0 +1,153 @@
+if not modules then modules = { } end modules ['lpdf-aux'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tonumber = tonumber
+local format, concat = string.format, table.concat
+local utfchar, utfbyte, char = utf.char, utf.byte, string.char
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local P, C, R, S, Cc, Cs, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs, lpeg.V
+local rshift = bit32.rshift
+
+lpdf = lpdf or { }
+
+-- tosixteen --
+
+local cache = table.setmetatableindex(function(t,k) -- can be made weak
+ local v = utfbyte(k)
+ if v < 0x10000 then
+ v = format("%04x",v)
+ else
+ v = v - 0x10000
+ v = format("%04x%04x",rshift(v,10)+0xD800,v%1024+0xDC00)
+ end
+ t[k] = v
+ return v
+end)
+
+local unified = Cs(Cc("<feff") * (lpegpatterns.utf8character/cache)^1 * Cc(">"))
+
+function lpdf.tosixteen(str) -- an lpeg might be faster (no table)
+ if not str or str == "" then
+ return "<feff>" -- not () as we want an indication that it's unicode
+ else
+ return lpegmatch(unified,str)
+ end
+end
+
+-- fromsixteen --
+
+-- local zero = S(" \n\r\t") + P("\\ ")
+-- local one = C(4)
+-- local two = P("d") * R("89","af") * C(2) * C(4)
+--
+-- local pattern = P { "start",
+-- start = V("wrapped") + V("unwrapped") + V("original"),
+-- original = Cs(P(1)^0),
+-- wrapped = P("<") * V("unwrapped") * P(">") * P(-1),
+-- unwrapped = P("feff")
+-- * Cs( (
+-- zero / ""
+-- + two / function(a,b)
+-- a = (tonumber(a,16) - 0xD800) * 1024
+-- b = (tonumber(b,16) - 0xDC00)
+-- return utfchar(a+b)
+-- end
+-- + one / function(a)
+-- return utfchar(tonumber(a,16))
+-- end
+-- )^1 ) * P(-1)
+-- }
+--
+-- function lpdf.fromsixteen(s)
+-- return lpegmatch(pattern,s) or s
+-- end
+
+local more = 0
+
+local pattern = C(4) / function(s) -- needs checking !
+ local now = tonumber(s,16)
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000
+ more = 0
+ return utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ return "" -- else the c's end up in the stream
+ else
+ return utfchar(now)
+ end
+end
+
+local pattern = P(true) / function() more = 0 end * Cs(pattern^0)
+
+function lpdf.fromsixteen(str)
+ if not str or str == "" then
+ return ""
+ else
+ return lpegmatch(pattern,str)
+ end
+end
+
+-- frombytes --
+
+local b_pattern = Cs((P("\\")/"" * (
+ S("()")
+ + S("nrtbf")/ { n = "\n", r = "\r", t = "\t", b = "\b", f = "\f" }
+ + lpegpatterns.octdigit^-3 / function(s) return char(tonumber(s,8)) end)
++ P(1))^0)
+
+local u_pattern = lpegpatterns.utfbom_16_be * lpegpatterns.utf16_to_utf8_be -- official
+ + lpegpatterns.utfbom_16_le * lpegpatterns.utf16_to_utf8_le -- we've seen these
+
+local h_pattern = lpegpatterns.hextobytes
+
+local zero = S(" \n\r\t") + P("\\ ")
+local one = C(4)
+local two = P("d") * R("89","af") * C(2) * C(4)
+
+local x_pattern = P { "start",
+ start = V("wrapped") + V("unwrapped") + V("original"),
+ original = Cs(P(1)^0),
+ wrapped = P("<") * V("unwrapped") * P(">") * P(-1),
+ unwrapped = P("feff")
+ * Cs( (
+ zero / ""
+ + two / function(a,b)
+ a = (tonumber(a,16) - 0xD800) * 1024
+ b = (tonumber(b,16) - 0xDC00)
+ return utfchar(a+b)
+ end
+ + one / function(a)
+ return utfchar(tonumber(a,16))
+ end
+ )^1 ) * P(-1)
+}
+
+function lpdf.frombytes(s,hex)
+ if not s or s == "" then
+ return ""
+ end
+ if hex then
+ local x = lpegmatch(x_pattern,s)
+ if x then
+ return x
+ end
+ local h = lpegmatch(h_pattern,s)
+ if h then
+ return h
+ end
+ else
+ local u = lpegmatch(u_pattern,s)
+ if u then
+ return u
+ end
+ end
+ return lpegmatch(b_pattern,s)
+end
+
+-- done --
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-col.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-col.lmt
new file mode 100644
index 00000000000..ea60e0551d8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-col.lmt
@@ -0,0 +1,837 @@
+if not modules then modules = { } end modules ['lpdf-col'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next, tostring, tonumber = type, next, tostring, tonumber
+local char, byte, format, gsub, rep, gmatch = string.char, string.byte, string.format, string.gsub, string.rep, string.gmatch
+local settings_to_array, settings_to_numbers = utilities.parsers.settings_to_array, utilities.parsers.settings_to_numbers
+local concat = table.concat
+local round = math.round
+local formatters = string.formatters
+
+local backends, lpdf, nodes = backends, lpdf, nodes
+
+local allocate = utilities.storage.allocate
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+local registrations = backends.pdf.registrations
+
+local nodepool = nodes.nuts.pool
+local register = nodepool.register
+local setstate = nodepool.setstate
+
+local pdfconstant = lpdf.constant
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfreference = lpdf.reference
+local pdfverbose = lpdf.verbose
+
+local pdfflushobject
+local pdfdelayedobject
+local pdfflushstreamobject
+local pdfshareobjectreference
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfdelayedobject = lpdf.delayedobject
+ pdfflushstreamobject = lpdf.flushstreamobject
+ pdfshareobjectreference = lpdf.shareobjectreference
+end)
+
+local addtopageattributes = lpdf.addtopageattributes
+local adddocumentcolorspace = lpdf.adddocumentcolorspace
+local adddocumentextgstate = lpdf.adddocumentextgstate
+
+local colors = attributes.colors
+local registercolor = colors.register
+local colorsvalue = colors.value
+local forcedmodel = colors.forcedmodel
+local getpagecolormodel = colors.getpagecolormodel
+local colortoattributes = colors.toattributes
+
+local transparencies = attributes.transparencies
+local registertransparancy = transparencies.register
+local transparenciesvalue = transparencies.value
+local transparencytoattribute = transparencies.toattribute
+
+local unsetvalue = attributes.unsetvalue
+
+local setmetatableindex = table.setmetatableindex
+
+local c_transparency = pdfconstant("Transparency")
+
+local f_gray = formatters["%.3N g %.3N G"]
+local f_rgb = formatters["%.3N %.3N %.3N rg %.3N %.3N %.3N RG"]
+local f_cmyk = formatters["%.3N %.3N %.3N %.3N k %.3N %.3N %.3N %.3N K"]
+local f_spot = formatters["/%s cs /%s CS %s SCN %s scn"]
+local f_tr = formatters["Tr%s"]
+local f_cm = formatters["q %.6N %.6N %.6N %.6N %.6N %.6N cm"]
+local f_effect = formatters["%s Tc %s w %s Tr"] -- %.6N ?
+local f_tr_gs = formatters["/Tr%s gs"]
+local f_num_1 = formatters["%.3N %.3N"]
+local f_num_2 = formatters["%.3N %.3N"]
+local f_num_3 = formatters["%.3N %.3N %.3N"]
+local f_num_4 = formatters["%.3N %.3N %.3N %.3N"]
+
+local report_color = logs.reporter("colors","backend")
+
+-- page groups (might move to lpdf-ini.lua)
+
+local colorspaceconstants = allocate { -- v_none is ignored
+ gray = pdfconstant("DeviceGray"),
+ rgb = pdfconstant("DeviceRGB"),
+ cmyk = pdfconstant("DeviceCMYK"),
+ all = pdfconstant("DeviceRGB"), -- brr
+}
+
+local transparencygroups = { }
+
+lpdf.colorspaceconstants = colorspaceconstants
+lpdf.transparencygroups = transparencygroups
+
+setmetatableindex(transparencygroups, function(transparencygroups,colormodel)
+ local cs = colorspaceconstants[colormodel]
+ if cs then
+ local d = pdfdictionary {
+ S = c_transparency,
+ CS = cs,
+ I = true,
+ }
+ -- local g = pdfreference(pdfflushobject(tostring(d)))
+ local g = pdfreference(pdfdelayedobject(tostring(d)))
+ transparencygroups[colormodel] = g
+ return g
+ else
+ transparencygroups[colormodel] = false
+ return false
+ end
+end)
+
+local function addpagegroup()
+ local model = getpagecolormodel()
+ if model then
+ local g = transparencygroups[model]
+ if g then
+ addtopageattributes("Group",g)
+ end
+ end
+end
+
+lpdf.registerpagefinalizer(addpagegroup,3,"pagegroup")
+
+-- injection code (needs a bit reordering)
+
+-- color injection
+
+function nodeinjections.rgbcolor(r,g,b)
+ return register(setstate(f_rgb(r,g,b,r,g,b)))
+end
+
+function nodeinjections.cmykcolor(c,m,y,k)
+ return register(setstate(f_cmyk(c,m,y,k,c,m,y,k)))
+end
+
+function nodeinjections.graycolor(s) -- caching 0/1 does not pay off
+ return register(setstate(f_gray(s,s)))
+end
+
+function nodeinjections.spotcolor(n,f,d,p)
+ if type(p) == "string" then
+ p = gsub(p,","," ") -- brr misuse of spot
+ end
+ return register(setstate(f_spot(n,n,p,p)))
+end
+
+function nodeinjections.transparency(n)
+ return register(setstate(f_tr_gs(n)))
+end
+
+-- a bit weird but let's keep it here for a while
+
+local effects = {
+ normal = 0,
+ inner = 0,
+ outer = 1,
+ both = 2,
+ hidden = 3,
+}
+
+local bp = number.dimenfactors.bp
+
+function nodeinjections.effect(effect,stretch,rulethickness)
+ -- always, no zero test (removed)
+ rulethickness = bp * rulethickness
+ effect = effects[effect] or effects['normal']
+ return register(setstate(f_effect(stretch,rulethickness,effect))) -- watch order
+end
+
+-- spot- and indexcolors
+
+local pdf_separation = pdfconstant("Separation")
+local pdf_indexed = pdfconstant("Indexed")
+local pdf_device_n = pdfconstant("DeviceN")
+local pdf_device_rgb = pdfconstant("DeviceRGB")
+local pdf_device_cmyk = pdfconstant("DeviceCMYK")
+local pdf_device_gray = pdfconstant("DeviceGray")
+local pdf_extgstate = pdfconstant("ExtGState")
+
+local pdf_rgb_range = pdfarray { 0, 1, 0, 1, 0, 1 }
+local pdf_cmyk_range = pdfarray { 0, 1, 0, 1, 0, 1, 0, 1 }
+local pdf_gray_range = pdfarray { 0, 1 }
+
+local f_rgb_function = formatters["dup %s mul exch dup %s mul exch %s mul"]
+local f_cmyk_function = formatters["dup %s mul exch dup %s mul exch dup %s mul exch %s mul"]
+local f_gray_function = formatters["%s mul"]
+
+local documentcolorspaces = pdfdictionary()
+
+local spotcolorhash = { } -- not needed
+local spotcolornames = { }
+local indexcolorhash = { }
+local delayedindexcolors = { }
+
+function registrations.spotcolorname(name,e)
+ spotcolornames[name] = e or name
+end
+
+function registrations.getspotcolorreference(name)
+ return spotcolorhash[name]
+end
+
+-- beware: xpdf/okular/evince cannot handle the spot->process shade
+
+-- This should become delayed i.e. only flush when used; in that case we need
+-- need to store the specification and then flush them when accesssomespotcolor
+-- is called. At this moment we assume that splotcolors that get defined are
+-- also used which keeps the overhad small anyway. Tricky for mp ...
+
+local processcolors
+
+local function registersomespotcolor(name,noffractions,names,p,colorspace,range,funct)
+ noffractions = tonumber(noffractions) or 1 -- to be checked
+ if noffractions == 0 then
+ -- can't happen
+ elseif noffractions == 1 then
+ local dictionary = pdfdictionary {
+ FunctionType = 4,
+ Domain = { 0, 1 },
+ Range = range,
+ }
+ local calculations = pdfflushstreamobject(format("{ %s }",funct),dictionary)
+ -- local calculations = pdfobject {
+ -- type = "stream",
+ -- immediate = true,
+ -- string = format("{ %s }",funct),
+ -- attr = dictionary(),
+ -- }
+ local array = pdfarray {
+ pdf_separation,
+ pdfconstant(spotcolornames[name] or name),
+ colorspace,
+ pdfreference(calculations),
+ }
+ local m = pdfflushobject(array)
+ local mr = pdfreference(m)
+ spotcolorhash[name] = m
+ documentcolorspaces[name] = mr
+ adddocumentcolorspace(name,mr)
+ else
+ local cnames = pdfarray()
+ local domain = pdfarray()
+ local colorants = pdfdictionary()
+ for n in gmatch(names,"[^,]+") do
+ local name = spotcolornames[n] or n
+ -- the cmyk names assume that they are indeed these colors
+ if n == "cyan" then
+ name = "Cyan"
+ elseif n == "magenta" then
+ name = "Magenta"
+ elseif n == "yellow" then
+ name = "Yellow"
+ elseif n == "black" then
+ name = "Black"
+ else
+ local sn = spotcolorhash[name] or spotcolorhash[n]
+ if not sn then
+ report_color("defining %a as colorant",name)
+ colors.definespotcolor("",name,"p=1",true)
+ sn = spotcolorhash[name] or spotcolorhash[n]
+ end
+ if sn then
+ colorants[name] = pdfreference(sn)
+ else
+ -- maybe some day generate colorants (spot colors for multi) automatically
+ report_color("unknown colorant %a, using black instead",name or n)
+ name = "Black"
+ end
+ end
+ cnames[#cnames+1] = pdfconstant(name)
+ domain[#domain+1] = 0
+ domain[#domain+1] = 1
+ end
+ if not processcolors then
+ local specification = pdfdictionary {
+ ColorSpace = pdfconstant("DeviceCMYK"),
+ Components = pdfarray {
+ pdfconstant("Cyan"),
+ pdfconstant("Magenta"),
+ pdfconstant("Yellow"),
+ pdfconstant("Black")
+ }
+ }
+ processcolors = pdfreference(pdfflushobject(specification))
+ end
+ local dictionary = pdfdictionary {
+ FunctionType = 4,
+ Domain = domain,
+ Range = range,
+ }
+ local calculation = pdfflushstreamobject(format("{ %s %s }",rep("pop ",noffractions),funct),dictionary)
+ local channels = pdfdictionary {
+ Subtype = pdfconstant("NChannel"),
+ Colorants = colorants,
+ Process = processcolors,
+ }
+ local array = pdfarray {
+ pdf_device_n,
+ cnames,
+ colorspace,
+ pdfreference(calculation),
+ pdfshareobjectreference(tostring(channels)), -- optional but needed for shades
+ }
+ local m = pdfflushobject(array)
+ local mr = pdfreference(m)
+ spotcolorhash[name] = m
+ documentcolorspaces[name] = mr
+ adddocumentcolorspace(name,mr)
+ end
+end
+
+-- wrong name
+
+local function registersomeindexcolor(name,noffractions,names,p,colorspace,range,funct)
+ noffractions = tonumber(noffractions) or 1 -- to be checked
+ local cnames = pdfarray()
+ local domain = pdfarray()
+ local names = settings_to_array(#names == 0 and name or names)
+ local values = settings_to_numbers(p)
+ names [#names +1] = "None"
+ values[#values+1] = 1
+ -- check for #names == #values
+ for i=1,#names do
+ local name = names[i]
+ local spot = spotcolornames[name]
+ cnames[#cnames+1] = pdfconstant(spot ~= "" and spot or name)
+ domain[#domain+1] = 0
+ domain[#domain+1] = 1
+ end
+ local dictionary = pdfdictionary {
+ FunctionType = 4,
+ Domain = domain,
+ Range = range,
+ }
+ local n = pdfflushstreamobject(format("{ %s %s }",rep("exch pop ",noffractions),funct),dictionary) -- exch pop
+ local a = pdfarray {
+ pdf_device_n,
+ cnames,
+ colorspace,
+ pdfreference(n),
+ }
+ local vector = { }
+ local set = { }
+ local n = #values
+ for i=255,0,-1 do
+ for j=1,n do
+ set[j] = format("%02X",round(values[j]*i))
+ end
+ vector[#vector+1] = concat(set)
+ end
+ vector = pdfverbose { "<", concat(vector, " "), ">" }
+ local n = pdfflushobject(pdfarray{ pdf_indexed, a, 255, vector })
+ adddocumentcolorspace(format("%s_indexed",name),pdfreference(n))
+ return n
+end
+
+-- actually, names (parent) is the hash
+
+local function delayindexcolor(name,names,func)
+ local hash = (names ~= "" and names) or name
+ delayedindexcolors[hash] = func
+end
+
+local function indexcolorref(name) -- actually, names (parent) is the hash
+ local parent = colors.spotcolorparent(name)
+ local data = indexcolorhash[name]
+ if data == nil then
+ local delayedindexcolor = delayedindexcolors[parent]
+ if type(delayedindexcolor) == "function" then
+ data = delayedindexcolor()
+ delayedindexcolors[parent] = true
+ end
+ indexcolorhash[parent] = data or false
+ end
+ return data
+end
+
+function registrations.rgbspotcolor(name,noffractions,names,p,r,g,b)
+ if noffractions == 1 then
+ registersomespotcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_rgb_function(r,g,b))
+ else
+ registersomespotcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_num_3(r,g,b))
+ end
+ delayindexcolor(name,names,function()
+ return registersomeindexcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_rgb_function(r,g,b))
+ end)
+end
+
+function registrations.cmykspotcolor(name,noffractions,names,p,c,m,y,k)
+ if noffractions == 1 then
+ registersomespotcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_cmyk_function(c,m,y,k))
+ else
+ registersomespotcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_num_4(c,m,y,k))
+ end
+ delayindexcolor(name,names,function()
+ return registersomeindexcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_cmyk_function(c,m,y,k))
+ end)
+end
+
+function registrations.grayspotcolor(name,noffractions,names,p,s)
+ if noffractions == 1 then
+ registersomespotcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_gray_function(s))
+ else
+ registersomespotcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_num_1(s))
+ end
+ delayindexcolor(name,names,function()
+ return registersomeindexcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_gray_function(s))
+ end)
+end
+
+function registrations.rgbindexcolor(name,noffractions,names,p,r,g,b)
+ registersomeindexcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_rgb_function(r,g,b))
+end
+
+function registrations.cmykindexcolor(name,noffractions,names,p,c,m,y,k)
+ registersomeindexcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_cmyk_function(c,m,y,k))
+end
+
+function registrations.grayindexcolor(name,noffractions,names,p,s)
+ registersomeindexcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_gray_function(s))
+end
+
+function codeinjections.setfigurecolorspace(data,figure)
+ local color = data.request.color
+ if color then -- != v_default
+ local ref = indexcolorref(color)
+ if ref then
+ figure.colorspace = ref
+ data.used.color = color
+ data.used.colorref = ref
+ end
+ end
+end
+
+-- transparency
+
+local pdftransparencies = { [0] =
+ pdfconstant("Normal"),
+ pdfconstant("Normal"),
+ pdfconstant("Multiply"),
+ pdfconstant("Screen"),
+ pdfconstant("Overlay"),
+ pdfconstant("SoftLight"),
+ pdfconstant("HardLight"),
+ pdfconstant("ColorDodge"),
+ pdfconstant("ColorBurn"),
+ pdfconstant("Darken"),
+ pdfconstant("Lighten"),
+ pdfconstant("Difference"),
+ pdfconstant("Exclusion"),
+ pdfconstant("Hue"),
+ pdfconstant("Saturation"),
+ pdfconstant("Color"),
+ pdfconstant("Luminosity"),
+ pdfconstant("Compatible"), -- obsolete; 'Normal' is used in this case
+}
+
+local documenttransparencies = { }
+local transparencyhash = { } -- share objects
+
+local done, signaled = false, false
+
+function registrations.transparency(n,a,t)
+ if not done then
+ local d = pdfdictionary {
+ Type = pdf_extgstate,
+ ca = 1,
+ CA = 1,
+ BM = pdftransparencies[1],
+ AIS = false,
+ }
+ local m = pdfflushobject(d)
+ local mr = pdfreference(m)
+ transparencyhash[0] = m
+ documenttransparencies[0] = mr
+ adddocumentextgstate("Tr0",mr)
+ done = true
+ end
+ if n > 0 and not transparencyhash[n] then
+ local d = pdfdictionary {
+ Type = pdf_extgstate,
+ ca = tonumber(t),
+ CA = tonumber(t),
+ BM = pdftransparencies[tonumber(a)] or pdftransparencies[0],
+ AIS = false,
+ }
+ local m = pdfflushobject(d)
+ local mr = pdfreference(m)
+ transparencyhash[n] = m
+ documenttransparencies[n] = mr
+ adddocumentextgstate(f_tr(n),mr)
+ end
+end
+
+statistics.register("page group warning", function()
+ if done then
+ local model = getpagecolormodel()
+ if model and not transparencygroups[model] then
+ return "transparencies are used but no pagecolormodel is set"
+ end
+ end
+end)
+
+-- Literals needed to inject code in the mp stream, we cannot use attributes there
+-- since literals may have qQ's, much may go away once we have mplib code in place.
+--
+-- This module assumes that some functions are defined in the colors namespace
+-- which most likely will be loaded later.
+
+local function lpdfcolor(model,ca,default) -- todo: use gray when no color
+ if colors.supported then
+ local cv = colorsvalue(ca)
+ if cv then
+ if model == 1 then
+ model = cv[1]
+ end
+ model = forcedmodel(model)
+ if model == 2 then
+ local s = cv[2]
+ return f_gray(s,s)
+ elseif model == 3 then
+ local r = cv[3]
+ local g = cv[4]
+ local b = cv[5]
+ return f_rgb(r,g,b,r,g,b)
+ elseif model == 4 then
+ local c = cv[6]
+ local m = cv[7]
+ local y = cv[8]
+ local k = cv[9]
+ return f_cmyk(c,m,y,k,c,m,y,k)
+ else
+ local n = cv[10]
+ local f = cv[11]
+ local d = cv[12]
+ local p = cv[13]
+ if type(p) == "string" then
+ p = gsub(p,","," ") -- brr misuse of spot
+ end
+ return f_spot(n,n,p,p)
+ end
+ else
+ return f_gray(default or 0,default or 0)
+ end
+ else
+ return ""
+ end
+end
+
+lpdf.color = lpdfcolor
+
+interfaces.implement {
+ name = "lpdf_color",
+ actions = { lpdfcolor, context },
+ arguments = "integer"
+}
+
+function lpdf.colorspec(model,ca,default)
+ if ca and ca > 0 then
+ local cv = colors.value(ca)
+ if cv then
+ if model == 1 then
+ model = cv[1]
+ end
+ if model == 2 then
+ return pdfarray { cv[2] }
+ elseif model == 3 then
+ return pdfarray { cv[3],cv[4],cv[5] }
+ elseif model == 4 then
+ return pdfarray { cv[6],cv[7],cv[8],cv[9] }
+ elseif model == 5 then
+ return pdfarray { cv[13] }
+ end
+ end
+ end
+ if default then
+ return default
+ end
+end
+
+function lpdf.pdfcolor(attribute) -- bonus, for pgf and friends
+ return lpdfcolor(1,attribute)
+end
+
+function lpdf.transparency(ct,default) -- kind of overlaps with transparencycode
+ -- beware, we need this hack because normally transparencies are not
+ -- yet registered and therefore the number is not not known ... we
+ -- might use the attribute number itself in the future
+ if transparencies.supported then
+ local ct = transparenciesvalue(ct)
+ if ct then
+ return f_tr_gs(registertransparancy(nil,ct[1],ct[2],true))
+ else
+ return f_tr_gs(0)
+ end
+ else
+ return ""
+ end
+end
+
+function lpdf.colorvalue(model,ca,default)
+ local cv = colorsvalue(ca)
+ if cv then
+ if model == 1 then
+ model = cv[1]
+ end
+ model = forcedmodel(model)
+ if model == 2 then
+ return f_num_1(cv[2])
+ elseif model == 3 then
+ return f_num_3(cv[3],cv[4],cv[5])
+ elseif model == 4 then
+ return f_num_4(cv[6],cv[7],cv[8],cv[9])
+ else
+ return f_num_1(cv[13])
+ end
+ else
+ return f_num_1(default or 0)
+ end
+end
+
+function lpdf.colorvalues(model,ca,default)
+ local cv = colorsvalue(ca)
+ if cv then
+ if model == 1 then
+ model = cv[1]
+ end
+ model = forcedmodel(model)
+ if model == 2 then
+ return cv[2]
+ elseif model == 3 then
+ return cv[3], cv[4], cv[5]
+ elseif model == 4 then
+ return cv[6], cv[7], cv[8], cv[9]
+ elseif model == 5 then
+ return cv[13]
+ end
+ else
+ return default or 0
+ end
+end
+
+function lpdf.transparencyvalue(ta,default)
+ local tv = transparenciesvalue(ta)
+ if tv then
+ return tv[2]
+ else
+ return default or 1
+ end
+end
+
+function lpdf.colorspace(model,ca)
+ local cv = colorsvalue(ca)
+ if cv then
+ if model == 1 then
+ model = cv[1]
+ end
+ model = forcedmodel(model)
+ if model == 2 then
+ return "DeviceGray"
+ elseif model == 3 then
+ return "DeviceRGB"
+ elseif model == 4 then
+ return "DeviceCMYK"
+ end
+ end
+ return "DeviceGRAY"
+end
+
+-- by registering we getconversion for free (ok, at the cost of overhead)
+
+local intransparency = false
+local pdfcolor = lpdf.color
+
+function lpdf.rgbcode(model,r,g,b)
+ if colors.supported then
+ return pdfcolor(model,registercolor(nil,'rgb',r,g,b))
+ else
+ return ""
+ end
+end
+
+function lpdf.cmykcode(model,c,m,y,k)
+ if colors.supported then
+ return pdfcolor(model,registercolor(nil,'cmyk',c,m,y,k))
+ else
+ return ""
+ end
+end
+
+function lpdf.graycode(model,s)
+ if colors.supported then
+ return pdfcolor(model,registercolor(nil,'gray',s))
+ else
+ return ""
+ end
+end
+
+function lpdf.spotcode(model,n,f,d,p)
+ if colors.supported then
+ return pdfcolor(model,registercolor(nil,'spot',n,f,d,p)) -- incorrect
+ else
+ return ""
+ end
+end
+
+function lpdf.transparencycode(a,t)
+ if transparencies.supported then
+ intransparency = true
+ return f_tr_gs(registertransparancy(nil,a,t,true)) -- true forces resource
+ else
+ return ""
+ end
+end
+
+function lpdf.finishtransparencycode()
+ if transparencies.supported and intransparency then
+ intransparency = false
+ return f_tr_gs(0) -- we happen to know this -)
+ else
+ return ""
+ end
+end
+
+do
+
+ local pdfprint, pdfcolor, pdftransparency
+
+ updaters.register("backend.update.lpdf",function()
+ pdfprint = lpdf.print
+ pdfcolor = lpdf.color
+ pdftransparency = lpdf.transparency
+ end)
+
+ local c_cache = setmetatableindex(function(t,m)
+ -- We inherit the outer transparency.
+ local v = setmetatableindex(function(t,c)
+ local p = "q " .. pdfcolor(m,c)
+ t[c] = p
+ return p
+ end)
+ t[m] = v
+ return v
+ end)
+
+ local t_cache = setmetatableindex(function(t,transparency)
+ local p = pdftransparency(transparency)
+ local v = setmetatableindex(function(t,colormodel)
+ local v = setmetatableindex(function(t,color)
+ local v = "q " .. pdfcolor(colormodel,color) .. " " .. p
+ t[color] = v
+ return v
+ end)
+ t[colormodel] = v
+ return v
+ end)
+ t[transparency] = v
+ return v
+ end)
+
+ updaters.register("backend.update.lpdf",function()
+
+ local vfinjectors = fonts.helpers.vfinjectors
+
+ vfinjectors.pdf = function(pos_h,pos_v,packet)
+ pdfprint(packet[2],packet[3])
+ end
+
+ vfinjectors.startcolor = function(pos_h,pos_v,packet)
+ local color = type(packet) == "table" and packet[2] or packet
+ if color then
+ local m, c = colortoattributes(color)
+ local t = transparencytoattribute(color)
+ if t and t ~= unsetvalue then
+ pdfprint("page", t_cache[t][m][c]) -- "q " .. pdfcolor(m,c) .. " " .. pdftransparency(t)
+ else
+ pdfprint("page", c_cache[m][c]) -- "q " .. pdfcolor(m,c))
+ end
+ else
+ pdfprint("page", "q")
+ end
+ end
+
+ vfinjectors.stopcolor = function()
+ -- pdfprint("text", "Q")
+ pdfprint("page", "Q")
+ end
+
+ end)
+
+end
+
+-- These generate the in-stream color commands:
+
+do
+
+ local color = lpdf.color
+ local transparency = lpdf.transparency
+
+ local fonts = { }
+ lpdf.fonts = fonts
+
+ fonts.color_indirect = function(c,t)
+ if c and t then
+ return color(1,c) .. " " .. transparency(t)
+ elseif c then
+ return color(1,c)
+ elseif t then
+ return transparency(t)
+ else
+ return false
+ end
+ end
+
+ local colors = attributes.colors
+ local rgbtocmyk = colors.rgbtocmyk
+
+ local f_cmyk = formatters["%.3N %.3f %.3N %.3N k"]
+ local f_rgb = formatters["%.3N %.3f %.3N rg"]
+ local f_gray = formatters["%.3N g"]
+
+ fonts.color_direct = function(r,g,b)
+ local m = colors.model
+ if r == g and g == b then
+ return f_gray(r)
+ elseif m == "cmyk" then
+ return f_cmyk(rgbtocmyk(r,g,b))
+ else
+ return f_rgb(r,g,b)
+ end
+ end
+
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-emb.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-emb.lmt
new file mode 100644
index 00000000000..4337e5931dd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-emb.lmt
@@ -0,0 +1,2233 @@
+if not modules then modules = { } end modules ['lpdf-ini'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- vkgoeswild: Pink Floyd - Shine on You Crazy Diamond - piano cover (around that
+-- time I redid the code, a reminder so to say)
+
+-- At some point I wanted to have access to the shapes so that we could use them in
+-- metapost. So, after looking at the cff and ttf specifications, I decided to write
+-- parsers. At somepoint we needed a cff parser anyway in order to calculate the
+-- dimensions. Then variable fonts came around and a option was added to recreate
+-- streams of operators and a logical next step was to do all inclusion that way. It
+-- was only then that I found out that some of the juggling also happens in the the
+-- backend, but spread over places, so I could have saved myself some time
+-- deciphering the specifications. Anyway, here we go.
+--
+-- Color fonts are a bit messy. Catching issues with older fonts will break new ones
+-- so I don't think that it's wise to build in too many catches (like for fonts with
+-- zero boundingboxes, weird dimensions, transformations that in a next version are
+-- fixed, etc.). Better is then to wait till something gets fixed. If a spec doesn't
+-- tell me how to deal with it ... I'll happily wait till it does. After all we're
+-- not in a hurry as these fonts are mostly meant for the web or special purposes
+-- with manual tweaking in desk top publishing applications. Keep in mind that Emoji
+-- can have funny dimensions (e.g. to be consistent within a font, so no tight ones).
+
+-- When we have moved to lmtx I will document a bit more. Till then it's experimental
+-- and subjected to change.
+
+local next, type, unpack, rawget = next, type, unpack, rawget
+local char, byte, gsub, sub, match, rep, gmatch = string.char, string.byte, string.gsub, string.sub, string.match, string.rep, string.gmatch
+local formatters = string.formatters
+local format = string.format
+local concat, sortedhash, sort = table.concat, table.sortedhash, table.sort
+local utfchar = utf.char
+local random, round, max, abs, ceiling = math.random, math.round, math.max, math.abs, math.ceiling
+local extract, lshift, rshift, band, bor = bit32.extract, bit32.lshift, bit32.rshift, bit32.band, bit32.bor
+local idiv = number.idiv
+local setmetatableindex = table.setmetatableindex
+
+local pdfnull = lpdf.null
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfconstant = lpdf.constant
+local pdfstring = lpdf.string
+local pdfreference = lpdf.reference
+
+local pdfreserveobject
+local pdfflushobject
+local pdfflushstreamobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+ pdfflushobject = lpdf.flushobject
+ pdfflushstreamobject = lpdf.flushstreamobject
+end)
+
+local report_fonts = logs.reporter("backend","fonts")
+
+local trace_fonts = false
+local trace_details = false
+
+local dimenfactors = number.dimenfactors
+local bpfactor = dimenfactors.bp
+local ptfactor = dimenfactors.pt
+
+trackers.register("backend.pdf.fonts",function(v) trace_fonts = v end)
+
+local readers = fonts.handlers.otf.readers
+local getinfo = readers.getinfo
+
+local setposition = utilities.files.setposition
+local readstring = utilities.files.readstring
+local openfile = utilities.files.open
+local closefile = utilities.files.close
+
+local getmapentry = fonts.mappings.getentry
+
+-- needs checking: signed vs unsigned
+
+local tocardinal1 = char
+
+local function tocardinal2(n)
+ return char(extract(n,8,8),extract(n,0,8))
+end
+
+local function tocardinal3(n)
+ return char(extract(n,16,8),extract(n,8,8),extract(n,0,8))
+end
+
+local function tocardinal4(n)
+ return char(extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
+end
+
+local function tointeger2(n)
+ return char(extract(n,8,8),extract(n,0,8))
+end
+
+local function tointeger3(n)
+ return char(extract(n,16,8),extract(n,8,8),extract(n,0,8))
+end
+
+local function tointeger4(n)
+ return char(extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
+end
+
+local function tocardinal8(n)
+ local l = idiv(n,0x100000000)
+ local r = n % 0x100000000
+ return char(extract(l,24,8),extract(l,16,8),extract(l,8,8),extract(l,0,8),
+ extract(r,24,8),extract(r,16,8),extract(r,8,8),extract(r,0,8))
+end
+
+-- A couple of shared helpers.
+
+local tounicodedictionary, widtharray, collectindices, subsetname, includecidset, forcecidset, tocidsetdictionary
+
+do
+
+ -- Because we supply tounicodes ourselves we only use bfchar mappings (as in the
+ -- backend). In fact, we can now no longer pass the tounicodes to the frontend but
+ -- pick them up from the descriptions.
+
+ local f_mapping_2 = formatters["<%02X> <%s>"]
+ local f_mapping_4 = formatters["<%04X> <%s>"]
+
+ local tounicode = fonts.mappings.tounicode
+
+local tounicode_template = [[
+%%!PS-Adobe-3.0 Resource-CMap
+%%%%DocumentNeededResources: ProcSet (CIDInit)
+%%%%IncludeResource: ProcSet (CIDInit)
+%%%%BeginResource: CMap (TeX-%s-0)
+%%%%Title: (TeX-%s-0 TeX %s 0)|
+%%%%Version: 1.000
+%%%%EndComments
+/CIDInit /ProcSet findresource begin
+ 12 dict begin
+ begincmap
+ /CIDSystemInfo
+ << /Registry (TeX) /Ordering (%s) /Supplement 0 >>
+ def
+ /CMapName
+ /TeX-Identity-%s
+ def
+ /CMapType
+ 2
+ def
+ 1 begincodespacerange
+ <%s> <%s>
+ endcodespacerange
+ %i beginbfchar
+%s
+ endbfchar
+ endcmap
+ CMapName currentdict /CMap defineresource pop
+ end
+end
+%%%%EndResource
+%%%%EOF]]
+
+ tounicodedictionary = function(details,indices,maxindex,name,wide)
+ local mapping = { }
+ local length = 0
+ if maxindex > 0 then
+ local f_mapping = wide and f_mapping_4 or f_mapping_2
+ for index=1,maxindex do
+ local data = indices[index]
+ if data then
+ length = length + 1
+ local unicode = data.unicode
+ if unicode then
+ unicode = tounicode(unicode)
+ else
+ unicode = "FFFD"
+ end
+ mapping[length] = f_mapping(index,unicode)
+ end
+ end
+ end
+ local name = gsub(name,"%+","-") -- like luatex does
+ local first = wide and "0000" or "00"
+ local last = wide and "FFFF" or "FF"
+ local blob = format(tounicode_template,name,name,name,name,name,first,last,length,concat(mapping,"\n"))
+ return blob
+ end
+
+ widtharray = function(details,indices,maxindex,units)
+ local widths = pdfarray()
+ local length = 0
+ local factor = 10000 / units
+ if maxindex > 0 then
+ local lastindex = -1
+ local sublist = nil
+ for index=1,maxindex do
+ local data = indices[index]
+ if data then
+ local width = data.width -- hm, is inaccurate for cff, so take from elsewhere
+ if width then
+ -- width = round(width * 10000 / units) / 10
+ width = round(width * factor) / 10
+ else
+ width = 0
+ end
+ if index == lastindex + 1 then
+ sublist[#sublist+1] = width
+ else
+ if sublist then
+ length = length + 1
+ widths[length] = sublist
+ end
+ sublist = pdfarray { width }
+ length = length + 1
+ widths[length] = index
+ end
+ lastindex = index
+ end
+ end
+ length = length + 1
+ widths[length] = sublist
+ end
+ return widths
+ end
+
+ collectindices = function(descriptions,indices)
+ local minindex = 0xFFFF
+ local maxindex = 0
+ local reverse = { }
+ -- todo: already at definition time trigger copying streams
+ -- and add extra indices ... first i need a good example of
+ -- a clash
+ -- for unicode, data in next, descriptions do
+ -- local i = data.index or unicode
+ -- if reverse[i] then
+ -- print("CLASH")
+ -- else
+ -- reverse[i] = data
+ -- end
+ -- end
+ for unicode, data in next, descriptions do
+ reverse[data.index or unicode] = data
+ end
+ for index in next, indices do
+ if index > maxindex then
+ maxindex = index
+ end
+ if index < minindex then
+ minindex = index
+ end
+ indices[index] = reverse[index]
+ end
+ if minindex > maxindex then
+ minindex = maxindex
+ end
+ return indices, minindex, maxindex
+ end
+
+ includecidset = false
+ forcecidset = false -- for private testing only
+
+ directives.register("backend.pdf.forcecidset",function(v)
+ forcecidset = v
+ end)
+
+ tocidsetdictionary = function(indices,min,max)
+ if includecidset or forcecidset then
+ local b = { }
+ local m = idiv(max+7,8)
+ for i=0,max do
+ b[i] = 0
+ end
+ b[0] = bor(b[0],lshift(1,7)) -- force notdef into the file
+ for i=min,max do
+ if indices[i] then
+ local bi = idiv(i,8)
+ local ni = i % 8
+ b[bi] = bor(b[bi],lshift(1,7-ni))
+ end
+ end
+ b = char(unpack(b,0,#b))
+ return pdfreference(pdfflushstreamobject(b))
+ end
+ end
+
+ -- Actually we can use the same as we only embed once.
+
+ -- subsetname = function(name)
+ -- return "CONTEXT" .. name
+ -- end
+
+ local prefixes = { } -- todo: set fixed one
+
+ subsetname = function(name)
+ local prefix
+ while true do
+ prefix = utfchar(random(65,90),random(65,90),random(65,90),random(65,90),random(65,90),random(65,90))
+ if not prefixes[prefix] then
+ prefixes[prefix] = true
+ break
+ end
+ end
+ return prefix .. "+" .. name
+ end
+
+end
+
+-- The three writers: opentype, truetype and type1.
+
+local mainwriters = { }
+
+do
+
+ -- advh = os2.ascender - os2.descender
+ -- tsb = default_advh - os2.ascender
+
+ -- truetype has the following tables:
+
+ -- head : mandate
+ -- hhea : mandate
+ -- vhea : mandate
+ -- hmtx : mandate
+ -- maxp : mandate
+ -- glyf : mandate
+ -- loca : mandate
+ --
+ -- cvt : if needed (but we flatten)
+ -- fpgm : if needed (but we flatten)
+ -- prep : if needed (but we flatten)
+ -- PCLT : if needed (but we flatten)
+ --
+ -- name : not needed for T2: backend does that
+ -- post : not needed for T2: backend does that
+ -- OS/2 : not needed for T2: backend does that
+ -- cmap : not needed for T2: backend does that
+
+ local streams = utilities.streams
+ local openstring = streams.openstring
+ local readcardinal2 = streams.readcardinal2
+ ----- readcardinal4 = streams.readcardinal4
+
+ local otfreaders = fonts.handlers.otf.readers
+
+ local function readcardinal4(f) -- this needs to be sorted out
+ local a = readcardinal2(f)
+ local b = readcardinal2(f)
+ if a and b then
+ return a * 0x10000 + b
+ end
+ end
+
+ -- -- --
+
+ local tablereaders = { }
+ local tablewriters = { }
+ local tablecreators = { }
+ local tableloaders = { }
+
+ local openfontfile, closefontfile, makefontfile, makemetadata do
+
+ local details = {
+ details = true,
+ platformnames = true,
+ platformextras = true,
+ }
+
+ -- .022 sec on luatex manual, neglectable:
+
+ -- local function checksum(data)
+ -- local s = openstring(data)
+ -- local n = 0
+ -- local d = #data
+ -- while true do
+ -- local c = readcardinal4(s)
+ -- if c then
+ -- n = (n + c) % 0x100000000
+ -- else
+ -- break
+ -- end
+ -- end
+ -- return n
+ -- end
+
+ local function checksum(data)
+ local s = openstring(data)
+ local n = 0
+ local d = #data
+ while true do
+ local a = readcardinal2(s)
+ local b = readcardinal2(s)
+ if b then
+ n = (n + a * 0x10000 + b) % 0x100000000
+ else
+ break
+ end
+ end
+ return n
+ end
+
+ openfontfile = function(details)
+ return {
+ offset = 0,
+ order = { },
+ used = { },
+ details = details,
+ streams = details.streams,
+ }
+ end
+
+ closefontfile = function(fontfile)
+ for k, v in next, fontfile do
+ fontfile[k] = nil -- so it can be collected asap
+ end
+ end
+
+ local metakeys = {
+ "uniqueid", "version",
+ "copyright", "license", "licenseurl",
+ "manufacturer", "vendorurl",
+ "family", "subfamily",
+ "typographicfamily", "typographicsubfamily",
+ "fullname", "postscriptname",
+ }
+
+ local template = [[
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+ <x:xmpmeta xmlns:x="adobe:ns:meta/">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about="" xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/">
+
+%s
+
+ </rdf:Description>
+ </rdf:RDF>
+ </x:xmpmeta>
+<?xpacket end="w"?>]]
+
+ makemetadata = function(fontfile)
+ local names = fontfile.streams.names
+ local list = { }
+ local f_name = formatters["<pdfx:%s>%s</pdfx:%s>"]
+ for i=1,#metakeys do
+ local m = metakeys[i]
+ local n = names[m]
+ if n then
+ list[#list+1] = f_name(m,n,m)
+ end
+ end
+ return format(template,concat(list,"\n"))
+ end
+
+ makefontfile = function(fontfile)
+ local order = fontfile.order
+ local used = fontfile.used
+ local count = 0
+ for i=1,#order do
+ local tag = order[i]
+ local data = fontfile[tag]
+ if data and #data > 0 then
+ count = count + 1
+ else
+ fontfile[tag] = false
+ end
+ end
+ local offset = 12 + (count * 16)
+ local headof = 0
+ local list = {
+ "" -- placeholder
+ }
+ local i = 1
+ local k = 0
+ while i <= count do
+ i = lshift(i,1)
+ k = k + 1
+ end
+ local searchrange = lshift(i,3)
+ local entryselector = k - 1
+ local rangeshift = lshift(count,4) - lshift(i,3)
+ local index = {
+ tocardinal4(0x00010000), -- tables.version
+ tocardinal2(count),
+ tocardinal2(searchrange),
+ tocardinal2(entryselector),
+ tocardinal2(rangeshift),
+ }
+ --
+ local ni = #index
+ local nl = #list
+ for i=1,#order do
+ local tag = order[i]
+ local data = fontfile[tag]
+ if data then
+ local csum = checksum(data)
+ local dlength = #data
+ local length = idiv(dlength+3,4) * 4
+ local padding = length - dlength
+ nl = nl + 1 ; list[nl] = data
+ for i=1,padding do
+ nl = nl + 1 ; list[nl] = "\0"
+ end
+ if #tag == 3 then
+ tag = tag .. " "
+ end
+ ni = ni + 1 ; index[ni] = tag -- must be 4 chars
+ ni = ni + 1 ; index[ni] = tocardinal4(csum)
+ ni = ni + 1 ; index[ni] = tocardinal4(offset)
+ ni = ni + 1 ; index[ni] = tocardinal4(dlength)
+ used[i] = offset -- not used
+ if tag == "head" then
+ headof = offset
+ end
+ offset = offset + length
+ end
+ end
+ list[1] = concat(index)
+ local off = #list[1] + headof + 1 + 8
+ list = concat(list)
+ local csum = (0xB1B0AFBA - checksum(list)) % 0x100000000
+ list = sub(list,1,off-1) .. tocardinal4(csum) .. sub(list,off+4,#list)
+ return list
+ end
+
+ local function register(fontfile,name)
+ local u = fontfile.used
+ local o = fontfile.order
+ if not u[name] then
+ o[#o+1] = name
+ u[name] = true
+ end
+ end
+
+ local function create(fontfile,name)
+ local t = { }
+ fontfile[name] = t
+ return t
+ end
+
+ local function write(fontfile,name)
+ local t = fontfile[name]
+ if not t then
+ return
+ end
+ register(fontfile,name)
+ if type(t) == "table" then
+ if t[0] then
+ fontfile[name] = concat(t,"",0,#t)
+ elseif #t > 0 then
+ fontfile[name] = concat(t)
+ else
+ fontfile[name] = false
+ end
+ end
+ end
+
+ tablewriters.head = function(fontfile)
+ register(fontfile,"head")
+ local t = fontfile.streams.fontheader
+ fontfile.head = concat {
+ tocardinal4(t.version),
+ tocardinal4(t.fontversionnumber),
+ tocardinal4(t.checksum),
+ tocardinal4(t.magic),
+ tocardinal2(t.flags),
+ tocardinal2(t.units),
+ tocardinal8(t.created),
+ tocardinal8(t.modified),
+ tocardinal2(t.xmin),
+ tocardinal2(t.ymin),
+ tocardinal2(t.xmax),
+ tocardinal2(t.ymax),
+ tocardinal2(t.macstyle),
+ tocardinal2(t.smallpixels),
+ tocardinal2(t.directionhint),
+ tocardinal2(t.indextolocformat),
+ tocardinal2(t.glyphformat),
+ }
+ end
+
+ tablewriters.hhea = function(fontfile)
+ register(fontfile,"hhea")
+ local t = fontfile.streams.horizontalheader
+ local n = t and fontfile.nofglyphs or 0
+ fontfile.hhea = concat {
+ tocardinal4(t.version),
+ tocardinal2(t.ascender),
+ tocardinal2(t.descender),
+ tocardinal2(t.linegap),
+ tocardinal2(t.maxadvancewidth),
+ tocardinal2(t.minleftsidebearing),
+ tocardinal2(t.minrightsidebearing),
+ tocardinal2(t.maxextent),
+ tocardinal2(t.caretsloperise),
+ tocardinal2(t.caretsloperun),
+ tocardinal2(t.caretoffset),
+ tocardinal2(t.reserved_1),
+ tocardinal2(t.reserved_2),
+ tocardinal2(t.reserved_3),
+ tocardinal2(t.reserved_4),
+ tocardinal2(t.metricdataformat),
+ tocardinal2(n) -- t.nofmetrics
+ }
+ end
+
+ tablewriters.vhea = function(fontfile)
+ local t = fontfile.streams.verticalheader
+ local n = t and fontfile.nofglyphs or 0
+ register(fontfile,"vhea")
+ fontfile.vhea = concat {
+ tocardinal4(t.version),
+ tocardinal2(t.ascender),
+ tocardinal2(t.descender),
+ tocardinal2(t.linegap),
+ tocardinal2(t.maxadvanceheight),
+ tocardinal2(t.mintopsidebearing),
+ tocardinal2(t.minbottomsidebearing),
+ tocardinal2(t.maxextent),
+ tocardinal2(t.caretsloperise),
+ tocardinal2(t.caretsloperun),
+ tocardinal2(t.caretoffset),
+ tocardinal2(t.reserved_1),
+ tocardinal2(t.reserved_2),
+ tocardinal2(t.reserved_3),
+ tocardinal2(t.reserved_4),
+ tocardinal2(t.metricdataformat),
+ tocardinal2(n) -- t.nofmetrics
+ }
+ end
+
+ tablewriters.maxp = function(fontfile)
+ register(fontfile,"maxp")
+ local t = fontfile.streams.maximumprofile
+ local n = fontfile.nofglyphs
+ -- if fontfile.streams.cffinfo then
+ -- error
+ -- end
+ fontfile.maxp = concat {
+ tocardinal4(0x00010000),
+ tocardinal2(n),
+ tocardinal2(t.points),
+ tocardinal2(t.contours),
+ tocardinal2(t.compositepoints),
+ tocardinal2(t.compositecontours),
+ tocardinal2(t.zones),
+ tocardinal2(t.twilightpoints),
+ tocardinal2(t.storage),
+ tocardinal2(t.functiondefs),
+ tocardinal2(t.instructiondefs),
+ tocardinal2(t.stackelements),
+ tocardinal2(t.sizeofinstructions),
+ tocardinal2(t.componentelements),
+ tocardinal2(t.componentdepth),
+ }
+ end
+
+ tablecreators.loca = function(fontfile) return create(fontfile,"loca") end
+ tablewriters .loca = function(fontfile) return write (fontfile,"loca") end
+
+ tablecreators.glyf = function(fontfile) return create(fontfile,"glyf") end
+ tablewriters .glyf = function(fontfile) return write (fontfile,"glyf") end
+
+ tablecreators.hmtx = function(fontfile) return create(fontfile,"hmtx") end
+ tablewriters .hmtx = function(fontfile) return write (fontfile,"hmtx") end
+
+ tablecreators.vmtx = function(fontfile) return create(fontfile,"vmtx") end
+ tablewriters .vmtx = function(fontfile) return write (fontfile,"vmtx") end
+
+ tableloaders .cmap = function(fontfile) return read (fontfile,"cmap") end
+ tablewriters .cmap = function(fontfile) return write (fontfile,"cmap") end
+
+ tableloaders .name = function(fontfile) return read (fontfile,"name") end
+ tablewriters .name = function(fontfile) return write (fontfile,"name") end
+
+ tableloaders .post = function(fontfile) return read (fontfile,"post") end
+ tablewriters .post = function(fontfile) return write (fontfile,"post") end
+
+ end
+
+ mainwriters["truetype"] = function(details)
+ --
+ local fontfile = openfontfile(details)
+ local basefontname = details.basefontname
+ local streams = details.streams
+ local blobs = streams.streams
+ local fontheader = streams.fontheader
+ local horizontalheader = streams.horizontalheader
+ local verticalheader = streams.verticalheader
+ local maximumprofile = streams.maximumprofile
+ local names = streams.names
+ local descriptions = details.rawdata.descriptions
+ local metadata = details.rawdata.metadata
+ local indices = details.indices
+ local metabbox = { fontheader.xmin, fontheader.ymin, fontheader.xmax, fontheader.ymax }
+ local indices,
+ minindex,
+ maxindex = collectindices(descriptions,indices)
+ local glyphstreams = tablecreators.glyf(fontfile)
+ local locations = tablecreators.loca(fontfile)
+ local horizontals = tablecreators.hmtx(fontfile)
+ local verticals = tablecreators.vmtx(fontfile)
+ --
+ local zero2 = tocardinal2(0)
+ local zero4 = tocardinal4(0)
+ --
+ local horizontal = horizontalheader.nofmetrics > 0
+ local vertical = verticalheader.nofmetrics > 0
+ --
+ local streamoffset = 0
+ local lastoffset = zero4
+ local g, h, v = 0, 0, 0
+ --
+ -- todo: locate notdef
+ --
+ if minindex > 0 then
+ local blob = blobs[0]
+ if blob and #blob > 0 then
+ locations[0] = lastoffset
+ g = g + 1 ; glyphstreams[g] = blob
+ h = h + 1 ; horizontals [h] = zero4
+ if vertical then
+ v = v + 1 ; verticals[v] = zero4
+ end
+ streamoffset = streamoffset + #blob
+ lastoffset = tocardinal4(streamoffset)
+ else
+ report_fonts("missing .notdef in font %a",basefontname)
+ end
+ -- todo: use a rep for h/v
+ for index=1,minindex-1 do
+ locations[index] = lastoffset
+ h = h + 1 ; horizontals[h] = zero4
+ if vertical then
+ v = v + 1 ; verticals[v] = zero4
+ end
+ end
+ end
+ for index=minindex,maxindex do
+ locations[index] = lastoffset
+ local data = indices[index]
+ if data then
+ local blob = blobs[index] -- we assume padding
+ if blob and #blob > 0 then
+ g = g + 1 ; glyphstreams[g] = blob
+ h = h + 1 ; horizontals [h] = tocardinal2(data.width or 0)
+ h = h + 1 ; horizontals [h] = tocardinal2(data.boundingbox[1])
+ if vertical then
+ v = v + 1 ; verticals[v] = tocardinal2(data.height or 0)
+ v = v + 1 ; verticals[v] = tocardinal2(data.boundingbox[3])
+ end
+ streamoffset = streamoffset + #blob
+ lastoffset = tocardinal4(streamoffset)
+ else
+ h = h + 1 ; horizontals[h] = zero4
+ if vertical then
+ v = v + 1 ; verticals[v] = zero4
+ end
+ report_fonts("missing blob for index %i in font %a",index,basefontname)
+ end
+ else
+ h = h + 1 ; horizontals[h] = zero4
+ if vertical then
+ v = v + 1 ; verticals[v] = zero4
+ end
+ end
+ end
+ locations[maxindex+1] = lastoffset -- cf spec
+ --
+ local nofglyphs = maxindex + 1 -- include zero
+ --
+ fontheader.checksum = 0
+ fontheader.indextolocformat = 1
+ maximumprofile.nofglyphs = nofglyphs
+ --
+ fontfile.format = "tff"
+ fontfile.basefontname = basefontname
+ fontfile.nofglyphs = nofglyphs
+ --
+ tablewriters.head(fontfile)
+ tablewriters.hhea(fontfile)
+ if vertical then
+ tablewriters.vhea(fontfile)
+ end
+ tablewriters.maxp(fontfile)
+
+ tablewriters.loca(fontfile)
+ tablewriters.glyf(fontfile)
+
+ tablewriters.hmtx(fontfile)
+ if vertical then
+ tablewriters.vmtx(fontfile)
+ end
+ --
+ local fontdata = makefontfile(fontfile)
+ local fontmeta = makemetadata(fontfile)
+ --
+ fontfile = closefontfile(fontfile)
+ --
+ local units = metadata.units
+ local basefont = pdfconstant(basefontname)
+ local widths = widtharray(details,indices,maxindex,units)
+ local object = details.objectnumber
+ local tounicode = tounicodedictionary(details,indices,maxindex,basefontname,true)
+ local tocidset = tocidsetdictionary(indices,minindex,maxindex)
+ local metabbox = metadata.boundingbox or { 0, 0, 0, 0 }
+ local fontbbox = pdfarray { unpack(metabbox) }
+ local ascender = metadata.ascender
+ local descender = metadata.descender
+ local capheight = metadata.capheight or fontbbox[4]
+ local stemv = metadata.weightclass
+ local italicangle = metadata.italicangle
+ local xheight = metadata.xheight or fontbbox[4]
+ --
+ if stemv then
+ stemv = (stemv/65)^2 + 50
+ end
+ --
+ local function scale(n)
+ if n then
+ return round((n) * 10000 / units) / 10
+ else
+ return 0
+ end
+ end
+ --
+ local reserved = pdfreserveobject()
+ local child = pdfdictionary {
+ Type = pdfconstant("Font"),
+ Subtype = pdfconstant("CIDFontType2"),
+ BaseFont = basefont,
+ FontDescriptor = pdfreference(reserved),
+ W = pdfreference(pdfflushobject(widths)),
+ CIDToGIDMap = pdfconstant("Identity"),
+ CIDSystemInfo = pdfdictionary {
+ Registry = pdfstring("Adobe"),
+ Ordering = pdfstring("Identity"),
+ Supplement = 0,
+ }
+ }
+ local descendants = pdfarray {
+ pdfreference(pdfflushobject(child)),
+ }
+ local descriptor = pdfdictionary {
+ Type = pdfconstant("FontDescriptor"),
+ FontName = basefont,
+ Flags = 4,
+ FontBBox = fontbbox,
+ Ascent = scale(ascender),
+ Descent = scale(descender),
+ ItalicAngle = round(italicangle or 0),
+ CapHeight = scale(capheight),
+ StemV = scale(stemv),
+ XHeight = scale(xheight),
+ FontFile2 = pdfreference(pdfflushstreamobject(fontdata)),
+ CIDSet = tocidset,
+ Metadata = fontmeta and pdfreference(pdfflushstreamobject(fontmeta)) or nil,
+ }
+ local parent = pdfdictionary {
+ Type = pdfconstant("Font"),
+ Subtype = pdfconstant("Type0"),
+ Encoding = pdfconstant(details.properties.writingmode == "vertical" and "Identity-V" or "Identity-H"),
+ BaseFont = basefont,
+ DescendantFonts = descendants,
+ ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
+ }
+ pdfflushobject(reserved,descriptor)
+ pdfflushobject(object,parent)
+ --
+ -- if trace_details then
+ -- local name = "temp.ttf"
+ -- report_fonts("saving %a",name)
+ -- io.savedata(name,fontdata)
+ -- inspect(fonts.handlers.otf.readers.loadfont(name))
+ -- end
+ --
+ end
+
+ do
+ -- todo : cff2
+
+ local details = {
+ details = true,
+ platformnames = true,
+ platformextras = true,
+ }
+
+ tablecreators.cff = function(fontfile)
+ fontfile.charstrings = { }
+ fontfile.charmappings = { }
+ fontfile.cffstrings = { }
+ fontfile.cffhash = { }
+ return fontfile.charstrings , fontfile.charmappings
+ end
+
+ local todictnumber, todictreal, todictinteger, todictoffset do
+
+ local maxnum = 0x7FFFFFFF
+ local minnum = - 0x7FFFFFFF - 1
+ local epsilon = 1.0e-5
+
+ local int2tag = "\28"
+ local int4tag = "\29"
+ local realtag = "\30"
+
+ todictinteger = function(n)
+ if not n then
+ return char(band(139,0xFF))
+ elseif n >= -107 and n <= 107 then
+ return char(band(n + 139,0xFF))
+ elseif n >= 108 and n <= 1131 then
+ n = 0xF700 + n - 108
+ return char(band(rshift(n,8),0xFF),band(n,0xFF))
+ elseif n >= -1131 and n <= -108 then
+ n = 0xFB00 - n - 108
+ return char(band(rshift(n,8),0xFF),band(n,0xFF))
+ elseif n >= -32768 and n <= 32767 then
+ -- return int2tag .. tointeger2(n)
+ return char(28,extract(n,8,8),extract(n,0,8))
+ else
+ -- return int4tag .. tointeger4(n)
+ return char(29,extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
+ end
+ end
+
+ -- -- not called that often
+ --
+ -- local encoder = readers.cffencoder
+ --
+ -- todictinteger = function(n)
+ -- if not n then
+ -- return encoder[0]
+ -- elseif n >= -1131 and n <= 1131 then
+ -- return encoder[n]
+ -- elseif n >= -32768 and n <= 32767 then
+ -- -- return int2tag .. tointeger2(n)
+ -- return char(28,extract(n,8,8),extract(n,0,8))
+ -- else
+ -- -- return int4tag .. tointeger4(n)
+ -- return char(29,extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
+ -- end
+ -- end
+
+ todictoffset = function(n)
+ return int4tag .. tointeger4(n)
+ end
+
+ local e = false
+ local z = byte("0")
+ local dp = 10
+ local ep = 11
+ local em = 12
+ local mn = 14
+ local es = 15
+
+ local fg = formatters["%g"]
+
+ todictreal = function(v)
+ local s = fg(v)
+ local t = { [0] = realtag }
+ local n = 0
+ for s in gmatch(s,".") do
+ if s == "e" or s == "E" then
+ e = true
+ elseif s == "+" then
+ -- skip
+ elseif s == "-" then
+ n = n + 1
+ if e then
+ t[n] = em
+ e = false
+ else
+ t[n] = mn
+ end
+ else
+ if e then
+ n = n + 1
+ t[n] = ep
+ e = false
+ end
+ n = n + 1
+ if s == "." then
+ t[n] = dp
+ else
+ t[n] = byte(s) - z
+ end
+ end
+ end
+ n = n + 1
+ t[n] = es
+ if (n % 2) ~= 0 then
+ n = n + 1
+ t[n] = es
+ end
+ local j = 0
+ for i=1,n,2 do
+ j = j + 1
+ t[j] = char(t[i]*0x10+t[i+1])
+ end
+ t = concat(t,"",0,j)
+ return t
+ end
+
+ todictnumber = function(n)
+ if not n or n == 0 then
+ return todictinteger(0)
+ elseif (n > maxnum or n < minnum or (abs(n - round(n)) > epsilon)) then
+ return todictreal(n)
+ else
+ return todictinteger(n)
+ end
+ end
+
+ end
+
+ local todictkey = char
+
+ local function todictstring(fontfile,value)
+ if not value then
+ value = ""
+ end
+ local s = fontfile.cffstrings
+ local h = fontfile.cffhash
+ local n = h[value]
+ if not n then
+ n = #s + 1
+ s[n] = value
+ h[value] = n
+ end
+ return todictinteger(390+n)
+ end
+
+ local function todictboolean(b)
+ return todictinteger(b and 1 or 0)
+ end
+
+ local function todictdeltas(t)
+ local r = { }
+ for i=1,#t do
+ r[i] = todictnumber(t[i]-(t[i-1] or 0))
+ end
+ return concat(r)
+ end
+
+ local function todictarray(t)
+ local r = { }
+ for i=1,#t do
+ r[i] = todictnumber(t[i])
+ end
+ return concat(r)
+ end
+
+ local function writestring(target,source,offset,what)
+ target[#target+1] = source
+ -- report_fonts("string : %-11s %06i # %05i",what,offset,#source)
+ return offset + #source
+ end
+
+ local function writetable(target,source,offset,what)
+ source = concat(source)
+ target[#target+1] = source
+ -- report_fonts("table : %-11s %06i # %05i",what,offset,#source)
+ return offset + #source
+ end
+
+ local function writeindex(target,source,offset,what)
+ local n = #source
+ local t = #target
+ t = t + 1 ; target[t] = tocardinal2(n)
+ if n > 0 then
+ local data = concat(source)
+ local size = #data -- assume the worst
+ local offsetsize, tocardinal
+ if size < 0xFF then
+ offsetsize, tocardinal = 1, tocardinal1
+ elseif size < 0xFFFF then
+ offsetsize, tocardinal = 2, tocardinal2
+ elseif size < 0xFFFFFF then
+ offsetsize, tocardinal = 3, tocardinal3
+ elseif size < 0xFFFFFFFF then
+ offsetsize, tocardinal = 4, tocardinal4
+ end
+ -- report_fonts("index : %-11s %06i # %05i (%i entries with offset size %i)",what,offset,#data,n,offsetsize)
+ offset = offset + 2 + 1 + (n + 1) * offsetsize + size
+ -- bytes per offset
+ t = t + 1 ; target[t] = tocardinal1(offsetsize)
+ -- list of offsets (one larger for length calculation)
+ local offset = 1 -- mandate
+ t = t + 1 ; target[t] = tocardinal(offset)
+ for i=1,n do
+ offset = offset + #source[i]
+ t = t + 1 ; target[t] = tocardinal(offset)
+ end
+ t = t + 1 ; target[t] = data
+ else
+ -- report_fonts("index : %-11s %06i # %05i (no entries)",what,offset,0)
+ offset = offset + 2
+ end
+ -- print("offset",offset,#concat(target))
+ return offset
+ end
+
+ tablewriters.cff = function(fontfile)
+ --
+ local streams = fontfile.streams
+ local cffinfo = streams.cffinfo or { }
+ local names = streams.names or { }
+ local fontheader = streams.fontheader or { }
+ local basefontname = fontfile.basefontname
+ --
+ local offset = 0
+ local dictof = 0
+ local target = { }
+ --
+ local charstrings = fontfile.charstrings
+ local nofglyphs = #charstrings + 1
+ local fontmatrix = { 0.001, 0, 0, 0.001, 0, 0 } -- todo
+ local fontbbox = fontfile.fontbbox
+ local defaultwidth = cffinfo.defaultwidth or 0
+ local nominalwidth = cffinfo.nominalwidth or 0
+ local bluevalues = cffinfo.bluevalues
+ local otherblues = cffinfo.otherblues
+ local familyblues = cffinfo.familyblues
+ local familyotherblues = cffinfo.familyotherblues
+ local bluescale = cffinfo.bluescale
+ local blueshift = cffinfo.blueshift
+ local bluefuzz = cffinfo.bluefuzz
+ local stdhw = cffinfo.stdhw
+ local stdvw = cffinfo.stdvw
+ --
+ if defaultwidth == 0 then defaultwidth = nil end
+ if nomimalwidth == 0 then nominalwidth = nil end
+ if bluevalues then bluevalues = todictarray(bluevalues) end
+ if otherblues then otherblues = todictarray(otherblues) end
+ if familyblues then familyblues = todictarray(familyblues) end
+ if familyotherblues then familyotherblues = todictarray(familyotherblues) end
+ if bluescale then bluescale = todictnumber(bluescale) end
+ if blueshift then blueshift = todictnumber(blueshift) end
+ if bluefuzz then bluefuzz = todictnumber(bluefuzz) end
+ if stdhw then stdhw = todictdeltas(stdhw) end
+ if stdvw then stdvw = todictdeltas(stdvw) end
+ --
+ local fontversion = todictstring(fontfile,fontheader.fontversion or "uknown version")
+ local familyname = todictstring(fontfile,cffinfo.familyname or names.family or basefontname)
+ local fullname = todictstring(fontfile,cffinfo.fullname or basefontname)
+ local weight = todictstring(fontfile,cffinfo.weight or "Normal")
+ local fontbbox = todictarray(fontbbox)
+ local strokewidth = todictnumber(cffinfo.strokewidth)
+ local monospaced = todictboolean(cffinfo.monospaced)
+ local italicangle = todictnumber(cffinfo.italicangle)
+ local underlineposition = todictnumber(cffinfo.underlineposition)
+ local underlinethickness = todictnumber(cffinfo.underlinethickness)
+ local charstringtype = todictnumber(2)
+ local fontmatrix = todictarray(fontmatrix)
+ local ros = todictstring(fontfile,"Adobe") -- registry
+ .. todictstring(fontfile,"Identity") -- identity
+ .. todictnumber(0) -- supplement
+ local cidcount = todictnumber(fontfile.nofglyphs)
+ local fontname = todictstring(fontfile,basefontname)
+ local fdarrayoffset = todictoffset(0)
+ local fdselectoffset = todictoffset(0)
+ local charstringoffset = todictoffset(0)
+ local charsetoffset = todictoffset(0)
+ local privateoffset = todictoffset(0)
+ --
+ local defaultwidthx = todictnumber(defaultwidth)
+ local nominalwidthx = todictnumber(nominalwidth)
+ local private = ""
+ .. (defaultwidthx and (defaultwidthx .. todictkey(20)) or "")
+ .. (nominalwidthx and (nominalwidthx .. todictkey(21)) or "")
+ .. (bluevalues and (bluevalues .. todictkey(6)) or "")
+ .. (otherblues and (otherblues .. todictkey(7)) or "")
+ .. (familyblues and (familyblues .. todictkey(8)) or "")
+ .. (familyotherblues and (familyotherblues .. todictkey(9)) or "")
+ .. (bluescale and (bluescale .. todictkey(12,9)) or "")
+ .. (blueshift and (blueshift .. todictkey(12,10)) or "")
+ .. (bluefuzz and (bluefuzz .. todictkey(12,11)) or "")
+ .. (stdhw and (stdhw .. todictkey(12,12)) or "")
+ .. (stdvw and (stdvw .. todictkey(12,13)) or "")
+ local privatesize = todictnumber(#private)
+ local privatespec = privatesize .. privateoffset
+ --
+ -- header (fixed @ 1)
+ --
+ local header =
+ tocardinal1(1) -- major
+ .. tocardinal1(0) -- minor
+ .. tocardinal1(4) -- header size
+ .. tocardinal1(4) -- offset size
+ --
+ offset = writestring(target,header,offset,"header")
+ --
+ -- name index (fixed @ 2) (has to be sorted)
+ --
+ local names = {
+ basefontname,
+ }
+ --
+ offset = writeindex(target,names,offset,"names")
+ --
+ -- topdict index (fixed @ 3)
+ --
+ local topvars =
+ charstringoffset .. todictkey(17)
+ .. charsetoffset .. todictkey(15)
+ .. fdarrayoffset .. todictkey(12,36)
+ .. fdselectoffset .. todictkey(12,37)
+ .. privatespec .. todictkey(18)
+ --
+ local topdict = {
+ ros .. todictkey(12,30) -- first
+ .. cidcount .. todictkey(12,34)
+ .. familyname .. todictkey( 3)
+ .. fullname .. todictkey( 2)
+ .. weight .. todictkey( 4)
+ .. fontbbox .. todictkey( 5)
+ .. monospaced .. todictkey(12, 1)
+ .. italicangle .. todictkey(12, 2)
+ .. underlineposition .. todictkey(12, 3)
+ .. underlinethickness .. todictkey(12, 4)
+ .. charstringtype .. todictkey(12, 6)
+ .. fontmatrix .. todictkey(12, 7)
+ .. strokewidth .. todictkey(12, 8)
+ .. topvars
+ }
+ --
+ offset = writeindex(target,topdict,offset,"topdict")
+ dictof = #target
+ --
+ -- string index (fixed @ 4)
+ --
+ offset = writeindex(target,fontfile.cffstrings,offset,"strings")
+ --
+ -- global subroutine index (fixed @ 5)
+ --
+ offset = writeindex(target,{},offset,"globals")
+ --
+ -- Encoding (cff1)
+ --
+ -- offset = writeindex(target,{},offset,"encoding")
+ --
+ -- Charsets
+ --
+ charsetoffset = todictoffset(offset)
+ offset = writetable(target,fontfile.charmappings,offset,"charsets")
+ --
+ -- fdselect
+ --
+ local fdselect =
+ tocardinal1(3) -- format
+ .. tocardinal2(1) -- n of ranges
+ -- entry 1
+ .. tocardinal2(0) -- first gid
+ .. tocardinal1(0) -- fd index
+ -- entry 2
+-- .. tocardinal2(fontfile.sparsemax-1) -- sentinel
+ .. tocardinal2(fontfile.sparsemax) -- sentinel
+ --
+ fdselectoffset = todictoffset(offset)
+ offset = writestring(target,fdselect,offset,"fdselect")
+ --
+ -- charstrings
+ --
+ charstringoffset = todictoffset(offset)
+ offset = writeindex(target,charstrings,offset,"charstrings")
+ --
+ -- font dict
+ --
+ -- offset = writeindex(target,{},offset,"fontdict")
+ --
+ -- private
+ --
+ privateoffset = todictoffset(offset)
+ privatespec = privatesize .. privateoffset
+ offset = writestring(target,private,offset,"private")
+ --
+ local fdarray = {
+ fontname .. todictkey(12,38)
+ .. privatespec .. todictkey(18)
+ }
+ fdarrayoffset = todictoffset(offset)
+ offset = writeindex(target,fdarray,offset,"fdarray")
+ --
+ topdict = target[dictof]
+ topdict = sub(topdict,1,#topdict-#topvars)
+ topvars =
+ charstringoffset .. todictkey(17)
+ .. charsetoffset .. todictkey(15)
+ .. fdarrayoffset .. todictkey(12,36)
+ .. fdselectoffset .. todictkey(12,37)
+ .. privatespec .. todictkey(18)
+ target[dictof] = topdict .. topvars
+ --
+ target = concat(target)
+ -- if trace_details then
+ -- local name = "temp.cff"
+ -- report_fonts("saving %a",name)
+ -- io.savedata(name,target)
+ -- inspect(fonts.handlers.otf.readers.cffcheck(name))
+ -- end
+ return target
+ end
+
+ end
+
+ -- todo: check widths (missing a decimal)
+
+ mainwriters["opentype"] = function(details)
+ --
+ local fontfile = openfontfile(details)
+ local basefontname = details.basefontname
+ local streams = details.streams
+ local blobs = streams.streams
+ local fontheader = streams.fontheader
+ local maximumprofile = streams.maximumprofile
+ local names = streams.names
+ local descriptions = details.rawdata.descriptions
+ local metadata = details.rawdata.metadata
+ local indices = details.indices
+ local metabbox = { fontheader.xmin, fontheader.ymin, fontheader.xmax, fontheader.ymax }
+ local correction = 1
+
+ -- (*) We share code with type1 and when we have old school tfm with
+ -- pfb but without descriptions we're kind of toast.
+
+ if not descriptions or not next(descriptions) then
+ -- This is good enough, we only need indices and widths.
+ descriptions = details.fontdata.characters
+ -- This is a hack, we have no basepoints.
+ correction = details.fontdata.parameters.size / 1000
+ -- And this needs checking.
+ correction = correction * bpfactor / ptfactor
+ metadata = { }
+ end
+
+ --
+
+ local indices,
+ minindex,
+ maxindex = collectindices(descriptions,indices)
+ local streamoffset = 0
+ local glyphstreams,
+ charmappings = tablecreators.cff(fontfile)
+ --
+ local zero2 = tocardinal2(0)
+ local zero4 = tocardinal4(0)
+ --
+ -- we need to locate notdef (or store its unicode someplace)
+ --
+ local blob = blobs[0] or "\14"
+ local sparsemax = 1
+ local lastoffset = zero4
+ glyphstreams[sparsemax] = blob
+ charmappings[sparsemax] = tocardinal1(0) -- format 0
+ streamoffset = streamoffset + #blob
+ lastoffset = tocardinal4(streamoffset)
+ if minindex == 0 then
+ minindex = 1
+ end
+ for index=minindex,maxindex do
+ if indices[index] then
+ local blob = blobs[index] or "\14"
+ sparsemax = sparsemax + 1
+ glyphstreams[sparsemax] = blob
+ charmappings[sparsemax] = tocardinal2(index)
+ streamoffset = streamoffset + #blob
+ lastoffset = tocardinal4(streamoffset)
+ end
+ end
+ --
+ fontfile.nofglyphs = maxindex + 1
+ fontfile.sparsemax = sparsemax
+ fontfile.format = "cff"
+ fontfile.basefontname = basefontname
+ fontfile.fontbbox = metabbox
+ --
+ local fontdata = tablewriters.cff(fontfile)
+ local fontmeta = makemetadata(fontfile)
+ --
+ fontfile = closefontfile(fontfile)
+ --
+ local units = fontheader.units or metadata.units
+
+ local basefont = pdfconstant(basefontname)
+ local widths = widtharray(details,indices,maxindex,units * correction)
+ local object = details.objectnumber
+ local tounicode = tounicodedictionary(details,indices,maxindex,basefontname,true)
+ local tocidset = tocidsetdictionary(indices,minindex,maxindex)
+ local fontbbox = pdfarray { unpack(metabbox) }
+ local ascender = metadata.ascender or 0
+ local descender = metadata.descender or 0
+ local capheight = metadata.capheight or fontbbox[4]
+ local stemv = metadata.weightclass
+ local italicangle = metadata.italicangle
+ local xheight = metadata.xheight or fontbbox[4]
+ if stemv then
+ stemv = (stemv/65)^2 + 50
+ else
+-- stemv = 2
+ end
+ --
+ local function scale(n)
+ if n then
+ return round((n) * 10000 / units) / 10
+ else
+ return 0
+ end
+ end
+ --
+ local reserved = pdfreserveobject()
+ local child = pdfdictionary {
+ Type = pdfconstant("Font"),
+ Subtype = pdfconstant("CIDFontType0"),
+ BaseFont = basefont,
+ FontDescriptor = pdfreference(reserved),
+ W = pdfreference(pdfflushobject(widths)),
+ CIDSystemInfo = pdfdictionary {
+ Registry = pdfstring("Adobe"),
+ Ordering = pdfstring("Identity"),
+ Supplement = 0,
+ }
+ }
+ local descendants = pdfarray {
+ pdfreference(pdfflushobject(child)),
+ }
+ local fontstream = pdfdictionary {
+ Subtype = pdfconstant("CIDFontType0C"),
+ }
+ local descriptor = pdfdictionary {
+ Type = pdfconstant("FontDescriptor"),
+ FontName = basefont,
+ Flags = 4,
+ FontBBox = fontbbox,
+ Ascent = scale(ascender),
+ Descent = scale(descender),
+ ItalicAngle = round(italicangle or 0),
+ CapHeight = scale(capheight),
+ StemV = scale(stemv),
+ XHeight = scale(xheight),
+ CIDSet = tocidset,
+ FontFile3 = pdfreference(pdfflushstreamobject(fontdata,fontstream())),
+ Metadata = fontmeta and pdfreference(pdfflushstreamobject(fontmeta)) or nil,
+ }
+ local parent = pdfdictionary {
+ Type = pdfconstant("Font"),
+ Subtype = pdfconstant("Type0"),
+ Encoding = pdfconstant(details.properties.writingmode == "vertical" and "Identity-V" or "Identity-H"),
+ BaseFont = basefont,
+ DescendantFonts = descendants,
+ ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
+ }
+ pdfflushobject(reserved,descriptor)
+ pdfflushobject(object,parent)
+ end
+
+ mainwriters["type1"] = function(details)
+ -- We abuse the cff includer which is ok but for special cases like
+ -- tfm -> pfb we don't have the right descriptions and scale so this
+ -- is why we cheat elsewhere. Maybe I should just drop that kind of
+ -- support and assume afm files to be present. See (*) above.
+ local s = details.streams
+ local m = details.rawdata.metadata
+ if m then
+ local h = s.horizontalheader
+ local c = s.cffinfo
+ local n = s.names
+ h.ascender = m.ascender or h.ascender
+ h.descender = m.descender or h.descender
+ n.copyright = m.copyright or n.copyright
+ n.family = m.familyname or n.familyname
+ n.fullname = m.fullname or n.fullname
+ n.fontname = m.fontname or n.fontname
+ n.subfamily = m.subfamilyname or n.subfamilyname
+ n.version = m.version or n.version
+ setmetatableindex(h,m)
+ setmetatableindex(c,m)
+ setmetatableindex(n,m)
+ end
+ mainwriters["opentype"](details)
+ end
+
+ do
+
+ -- The methods might become plugins.
+
+ local methods = { }
+
+ local pdfimage = lpdf.epdf.image
+ local openpdf = pdfimage.open
+ local closepdf = pdfimage.close
+ local copypage = pdfimage.copy
+
+ local embedimage = images.embed
+
+ local f_glyph = formatters["G%d"]
+ local f_char = formatters["BT /V%d 1 Tf [<%04X>] TJ ET"]
+ local f_width = formatters["%.6N 0 d0"]
+ local f_index = formatters["I%d"]
+ local f_image_xy = formatters["%.6N 0 d0 1 0 0 1 %.6N %.6N cm /%s Do"]
+ local f_image_c = formatters["/%s Do"]
+ local f_image_c_xy = formatters["%.6N 0 0 %.6N %.6N %.6N cm /%s Do"]
+ local f_image_w = formatters["%.6N 0 d0 %s"]
+ local f_image_d = formatters["%.6N 0 d0 1 0 0 1 0 %.6N cm /%s Do"]
+ local f_stream = formatters["%.6N 0 d0 %s"]
+ local f_stream_c = formatters["%.6N 0 0 0 0 0 d1 %s"]
+ local f_stream_d = formatters["%.6N 0 d0 1 0 0 1 0 %.6N cm %s"]
+ -- local f_stream_s = formatters["%.6N 0 0 %.6N 0 0 cm /%s Do"]
+
+ -- A type 3 font has at most 256 characters and Acrobat also wants a zero slot
+ -- to be filled. We can share a mandate zero slot character. We also need to
+ -- make sure that we use bytes as index in the page stream as well as in the
+ -- tounicode vector.
+
+ -- All this type three magic is a tribute to Don Knuth who uses bitmap fonts in
+ -- his books.
+
+ local c_notdef = nil
+ local r_notdef = nil
+ local w_notdef = nil
+ local fontbbox = nil
+
+ -- pk inclusion (not really tested but not really used either)
+
+ function methods.pk(filename)
+ local pkfullname = resolvers.findpk(basedfontname,resolution)
+ if not pkfullname or pkfullname == "" then
+ return
+ end
+ local readers = fonts.handlers.tfm.readers
+ local result = readers.loadpk(pkfullname)
+ local convert = readers.pktopdf
+ if not result or result.error then
+ return
+ end
+ local resolution = 600
+ local widthfactor = resolution / 72
+ local scalefactor = 72 / resolution / 10
+ local factor = widthfactor / 65536
+ local function pktopdf(glyph,data)
+ return convert(glyph,data,factor) -- return pdfcode, width
+ end
+ return result.glyphs, scalefactor, pktopdf, false, false
+ end
+
+ -- pdf inclusion
+
+ local used = setmetatableindex("table")
+
+ -- function lpdf.registerfontmethod(name,f)
+ -- if f and not methods[name] then
+ -- methods[name] = f
+ -- end
+ -- end
+
+ function methods.pdf(filename,details)
+ local properties = details.properties
+ local pdfshapes = properties.indexdata[1]
+ local pdfdoc = openpdf(pdfshapes.filename)
+ local xforms = pdfdictionary()
+ local nofglyphs = 0
+ if pdfdoc then
+ local scale = 10 * details.parameters.size/details.parameters.designsize
+ local units = details.parameters.units
+ local factor = units * bpfactor / scale
+ local fixdepth = pdfshapes.fixdepth
+ local useddoc = used[pdfdoc]
+ local function pdftopdf(glyph,data)
+ local width = (data.width or 0) * factor
+ local image = useddoc[glyph]
+ local reference = nil
+ if not image then
+ image = embedimage(copypage(pdfdoc,glyph))
+ nofglyphs = nofglyphs + 1
+ local name = f_glyph(nofglyphs)
+ local stream = nil
+ if fixdepth then
+ local depth = data.depth or 0
+ if depth ~= 0 then
+ local d = data.dropin.descriptions[data.index]
+ local b = d.boundingbox
+ local l = b[1]
+ local r = b[3]
+ local w = r - l
+ local scale = w / d.width
+ local x = l
+ -- local y = - b[4] - b[2] - (d.depth or 0)
+ local y = - (d.depth or 0)
+ local scale = w / (image.width * bpfactor)
+ stream = f_image_c_xy(scale,scale,x,y,name)
+ end
+ end
+ if not stream then
+ stream = f_image_c(name)
+ end
+ useddoc[glyph] = image
+ image.embedded_name = name
+ image.embedded_stream = stream
+ image.embedded_reference = pdfreference(image.objnum)
+ end
+ xforms[image.embedded_name] = image.embedded_reference
+ return f_image_w(width,image.embedded_stream), width
+ end
+ local function closepdf()
+ -- closepdf(pdfdoc)
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return pdfshapes, 1/units, pdftopdf, closepdf, getresources
+ end
+ end
+
+ -- box inclusion (todo: we can share identical glyphs if needed but the gain
+ -- is minimal especially when we use compact font mode)
+
+ function methods.box(filename,details)
+ local properties = details.properties
+ local boxes = properties.indexdata[1]
+ local xforms = pdfdictionary()
+ local nofglyphs = 0
+ local scale = 10 * details.parameters.size/details.parameters.designsize
+ local units = details.parameters.units
+ local function boxtopdf(image,data) -- image == glyph
+ nofglyphs = nofglyphs + 1
+ local scale = units / scale -- e.g. 1000 / 12
+ local width = (data.width or 0) * bpfactor * scale
+ local depth = - (data.depth or 0) * bpfactor * scale
+ local name = f_glyph(nofglyphs)
+ local stream = f_image_c_xy(scale,scale,0,depth,name)
+ image.embedded_name = name
+ image.embedded_stream = stream
+ image.embedded_reference = pdfreference(image.objnum)
+ xforms[name] = image.embedded_reference
+ return f_image_w(width,stream), width
+ end
+ local function wrapup()
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return boxes, 1/units, boxtopdf, wrapup, getresources
+ end
+
+ -- mps inclusion
+
+ local decompress = gzip.decompress
+ local metapost = metapost
+ local simplemprun = metapost.simple
+ local setparameterset = metapost.setparameterset
+
+ function methods.mps(filename,details)
+ local properties = details.properties
+ local parameters = details.parameters
+ local mpshapes = properties.indexdata[1] -- indexdata will change
+ if mpshapes then
+ local scale = 10 * parameters.size/parameters.designsize
+ local units = mpshapes.units or parameters.units
+ local factor = units * bpfactor / scale
+ local fixdepth = mpshapes.fixdepth
+ local usecolor = mpshapes.usecolor
+ local specification = mpshapes.specification or { }
+ local shapedefinitions = mpshapes.shapes
+ local instance = mpshapes.instance
+ --
+ simplemprun(instance,"begingroup;",true,true)
+ setparameterset("mpsfont",specification)
+ specification.scale = specification.scale or scale
+ specification.parameters = parameters
+ specification.properties = properties
+ specification.parentdata = details.fontdata.parentdata
+ -------------.characters = details.fontdata.characters
+ -------------.descriptions = details.fontdata.descriptions
+ if shapedefinitions then
+ local preamble = shapedefinitions.parameters.preamble
+ if preamble then
+ simplemprun(instance,preamble,true,true)
+ end
+ end
+ --
+ local function mpstopdf(mp,data)
+ local width = data.width
+ if decompress then
+ mp = decompress(mp)
+ end
+ local pdf = simplemprun(instance,mp,true)
+ local width = width * factor
+ if usecolor then
+ return f_stream_c(width,pdf), width
+ elseif fixdepth then
+ local depth = data.depth or 0
+ local height = data.height or 0
+ if depth ~= 0 or height ~= 0 then
+ return f_stream_d(width,(-height-depth)*factor,pdf), width
+ end
+ end
+ return f_stream(width,pdf), width
+ end
+ --
+ local function resetmps()
+ setparameterset("mpsfont")
+ simplemprun(instance,"endgroup;",true,true)
+ specification.parameters = nil
+ specification.properties = nil
+ specification.parentdata = nil
+ -------------.characters = nil
+ -------------.descriptions = nil
+ end
+ --
+ local function getresources()
+ return lpdf.collectedresources {
+ serialize = false,
+ }
+ end
+ --
+ return mpshapes, 1/units, mpstopdf, resetmps, getresources
+ end
+ end
+
+ -- png inclusion
+
+ -- With d1 the image mask is used when given and obeys color. So it is needed for pure bw
+ -- bitmap fonts, so here we really need d0.
+ --
+ -- Acrobat X pro only seems to see the image mask but other viewers are doing it ok. Acrobat
+ -- reader crashes. We really need to add a notdef!
+
+ local files = utilities.files
+ local openfile = files.open
+ local closefile = files.close
+ local setposition = files.setposition
+ local readstring = files.readstring
+
+ function methods.png(filename,details)
+ local properties = details.properties
+ local pngshapes = properties.indexdata[1]
+ if pngshapes then
+ local parameters = details.parameters
+ local png = properties.png
+ local hash = png.hash
+ local xforms = pdfdictionary()
+ local nofglyphs = 0
+ local scale = 10 * parameters.size/parameters.designsize
+ local factor = bpfactor / scale
+ -- local units = parameters.units -- / 1000
+ local units = 1000
+ local filehandle = openfile(details.filename,true)
+ local function pngtopdf(glyph,data)
+ -- local info = graphics.identifiers.png(glyph.data,"string")
+ local offset = glyph.o
+ local size = glyph.s
+ local pdfdata = nil
+ if offset and size then
+ setposition(filehandle,offset)
+ local blob = readstring(filehandle,size)
+ local info = graphics.identifiers.png(blob,"string")
+ info.enforcecmyk = pngshapes.enforcecmyk
+ local image = lpdf.injectors.png(info,"string")
+ local width = (data.width or 0) * factor
+ if image then
+ embedimage(image)
+ nofglyphs = nofglyphs + 1
+ local xoffset = (glyph.x or 0) / units
+ local yoffset = (glyph.y or 0) / units
+ local name = f_glyph(nofglyphs)
+ xforms[name] = pdfreference(image.objnum)
+ pdfdata = f_image_xy(width,xoffset,yoffset,name)
+ end
+ end
+ return pdfdata or f_stream(width), width
+ end
+ local function closepng()
+ if filehandle then
+ closefile(filehandle)
+ end
+ pngshapes = nil
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return pngshapes, 1, pngtopdf, closepng, getresources
+ end
+ end
+
+ function methods.color(filename,details)
+ local colrshapes = details.properties.indexdata[1]
+ local colrvalues = details.properties.indexdata[2]
+ local usedfonts = { }
+ local function colrtopdf(description,data)
+ -- descriptions by index
+ local colorlist = description.colors
+ if colorlist then
+ local dropdata = data.dropin
+ local dropid = dropdata.properties.id
+ local dropunits = dropdata.parameters.units -- shared
+ local descriptions = dropdata.descriptions
+ usedfonts[dropid] = dropid
+ local w = description.width or 0
+ local s = #colorlist
+ local l = nil
+ local t = { f_width(w) }
+ local n = 1
+ local d = colrvalues[#colrvalues]
+ for i=1,s do
+ local entry = colorlist[i]
+ local v = colrvalues[entry.class] or d
+ if v and l ~= v then
+ n = n + 1 ; t[n] = v
+ l = v
+ end
+ local e = descriptions[entry.slot]
+ if e then
+ n = n + 1 ; t[n] = f_char(dropid,e.index)
+ end
+ end
+ return concat(t," "), w / dropunits
+ end
+ end
+ local function getresources()
+ return lpdf.collectedresources {
+ serialize = false,
+ fonts = usedfonts,
+ fontprefix = "V",
+ }
+ end
+ return colrshapes, 1, colrtopdf, false, getresources
+ end
+
+ mainwriters["type3"] = function(details)
+ local properties = details.properties
+ local basefontname = properties.basefontname or details.basefontname or properties.name -- messy
+ local askedmethod = properties.method or "pk"
+ local method = methods[askedmethod] or methods.pk
+ if not method then
+ return
+ end
+ local glyphs, scalefactor, glyphtopdf, reset, getresources = method(basedfontname,details)
+ if not glyphs then
+ return
+ end
+ local parameters = details.parameters
+ local object = details.objectnumber
+ local factor = parameters.factor -- normally 1
+ local fontmatrix = pdfarray { scalefactor, 0, 0, scalefactor, 0, 0 }
+ local indices,
+ minindex,
+ maxindex = collectindices(details.fontdata.characters,details.indices)
+ local widths = pdfarray()
+ local differences = pdfarray()
+ local charprocs = pdfdictionary()
+ local basefont = pdfconstant(basefontname)
+ local d = 0
+ local w = 0
+ local forcenotdef = minindex > 0
+ local lastindex = -0xFF
+ if forcenotdef then
+ widths[0] = 0
+ minindex = 0
+ lastindex = 0
+ d = 2
+ if not c_notdef then
+ w_notdef = 0
+ c_notdef = pdfconstant(".notdef")
+ r_notdef = pdfreference(pdfflushstreamobject("0 0 d0"))
+ end
+ differences[1] = w_notdef
+ differences[2] = c_notdef
+ charprocs[".notdef"] = r_notdef
+ end
+
+ for i=1,maxindex-minindex+1 do
+ widths[i] = 0
+ end
+
+ for index, data in sortedhash(indices) do
+ local name = f_index(index)
+ local glyph = glyphs[index]
+ if glyph then
+ local stream, width = glyphtopdf(glyph,data)
+ if stream then
+ if index - 1 ~= lastindex then
+ d = d + 1 differences[d] = index
+ end
+ lastindex = index
+ d = d + 1 differences[d] = pdfconstant(name)
+ charprocs[name] = pdfreference(pdfflushstreamobject(stream))
+ widths[index-minindex+1] = width
+ end
+ else
+ report_fonts("missing glyph %i in type3 font %a",index,basefontname)
+ end
+ end
+ if not fontbbox then
+ -- The specification permits zero values and these are actually also more
+ -- robust as then there are no assumptions and no accuracy is needed.
+ fontbbox = pdfarray { 0, 0, 0, 0 }
+ end
+ local encoding = pdfdictionary {
+ Type = pdfconstant("Encoding"),
+ Differences = differences,
+ }
+ local tounicode = tounicodedictionary(details,indices,maxindex,basefontname,false)
+ local resources = getresources and getresources()
+ if not resources or not next(resources) then
+ -- resources = lpdf.procset(true)
+ resources = nil
+ end
+ local descriptor = pdfdictionary {
+ -- most is optional in type3
+ Type = pdfconstant("FontDescriptor"),
+ FontName = basefont,
+ Flags = 4,
+ ItalicAngle = 0,
+ }
+ local parent = pdfdictionary {
+ Type = pdfconstant("Font"),
+ Subtype = pdfconstant("Type3"),
+ Name = basefont,
+ FontBBox = fontbbox,
+ FontMatrix = fontmatrix,
+ CharProcs = pdfreference(pdfflushobject(charprocs)),
+ Encoding = pdfreference(pdfflushobject(encoding)),
+ FirstChar = minindex,
+ LastChar = maxindex,
+ Widths = pdfreference(pdfflushobject(widths)),
+ FontDescriptor = pdfreference(pdfflushobject(descriptor)),
+ Resources = resources,
+ ToUnicode = tounicode and pdfreference(pdfflushstreamobject(tounicode)),
+ }
+ pdfflushobject(object,parent)
+ if reset then
+ reset()
+ end
+ end
+
+ end
+
+end
+
+-- writingmode
+
+local usedfonts = fonts.hashes.identifiers -- for now
+local noffonts = 0
+
+-- The main injector.
+
+-- here we need to test for sharing otherwise we reserve too many
+-- objects
+
+local getstreamhash = fonts.handlers.otf.getstreamhash
+local loadstreamdata = fonts.handlers.otf.loadstreamdata
+
+-- we can actually now number upwards (so not use fontid in /F)
+
+local objects = setmetatableindex(function(t,k)
+ local v
+ if type(k) == "number" then
+ local h = getstreamhash(k)
+ v = rawget(t,h)
+ if not v then
+ v = pdfreserveobject()
+ t[h] = v
+ end
+ if trace_fonts then
+ report_fonts("font id %i bound to hash %s and object %i",k,h,v)
+ end
+ else
+ -- no problem as it can be svg only
+ -- report_fonts("fatal error, hash %s asked but not used",k,h,v)
+ v = pdfreserveobject()
+ t[k] = v
+ end
+ return v
+end)
+
+local n = 0
+
+local names = setmetatableindex(function(t,k)
+ local v
+ if type(k) == "number" then
+ local h = getstreamhash(k)
+ v = rawget(t,h)
+ if not v then
+ n = n + 1
+ v = n
+ t[h] = v
+ end
+ if trace_fonts then
+ report_fonts("font id %i bound to hash %s and name %i",k,h,n)
+ end
+ end
+ t[k] = v
+ return v
+end)
+
+function lpdf.flushfonts()
+
+ local mainfonts = { }
+
+ statistics.starttiming(objects)
+
+ for fontid, used in sortedhash(lpdf.usedcharacters) do
+
+ -- for a bitmap we need a different hash unless we stick to a fixed high
+ -- resolution which makes much sense
+
+ local hash = getstreamhash(fontid)
+ if hash then
+ local parent = mainfonts[hash]
+ if not parent then
+ local fontdata = usedfonts[fontid]
+ local rawdata = fontdata.shared and fontdata.shared.rawdata
+ local resources = fontdata.resources
+ local properties = fontdata.properties -- writingmode and type3
+ local parameters = fontdata.parameters -- used in type3
+ if not rawdata then
+ -- we have a virtual font that loaded directly ... at some point i will
+ -- sort this out (in readanddefine we need to do a bit more) .. the problem
+ -- is that we have a hybrid font then
+ for xfontid, xfontdata in next, fonts.hashes.identifiers do
+ if fontid ~= xfontid then
+ local xhash = getstreamhash(xfontid)
+ if hash == xhash then
+ rawdata = xfontdata.shared and xfontdata.shared.rawdata
+ if rawdata then
+ resources = xfontdata.resources
+ properties = xfontdata.properties
+ parameters = xfontdata.parameters
+ break
+ end
+ end
+ end
+ end
+ end
+ if rawdata then
+ parent = {
+ hash = hash,
+ fontdata = fontdata,
+ filename = resources.filename or properties.filename or "unset",
+ indices = { },
+ rawdata = rawdata,
+ properties = properties, -- we assume consistency
+ parameters = parameters, -- we assume consistency
+ streams = { },
+ objectnumber = objects[hash],
+ basefontname = subsetname(properties.psname or properties.name or "unset"),
+ name = names[hash],
+ }
+ mainfonts[hash] = parent
+ noffonts = noffonts + 1
+ end
+ end
+ if parent then
+ local indices = parent.indices
+ for k in next, used do
+ indices[k] = true
+ end
+ end
+ end
+ end
+
+ -- this is no not yet ok for tfm / type 1 .. we need to move the nested blobs ourside the loop
+
+ for hash, details in sortedhash(mainfonts) do
+ local filename = details.filename
+ if next(details.indices) then
+ if trace_fonts then
+ report_fonts("embedding %a hashed as %a",filename,hash)
+ end
+ local properties = details.properties
+ local bitmap = properties.usedbitmap
+ local method = properties.method -- will be pk | pdf | svg | ...
+ if bitmap or method then
+ local format = "type3"
+ local writer = mainwriters[format]
+ if writer then
+ if trace_fonts then
+ report_fonts("using main writer %a",format)
+ end
+ writer(details)
+ end
+ else
+ local format = properties.format
+ local writer = mainwriters[format]
+ if not writer then
+ -- This will move to the tpk module where we will also deal
+ -- with bitmaps then.
+ local encoding, pfbfile, encfile = getmapentry(filename)
+ if trace_fonts then
+ report_fonts("file %a resolved to encoding %a and file %a",filename,encoding,pfbfile)
+ end
+ if encoding and pfbfile then
+ filename = pfbfile
+ format = "type1"
+ --
+ -- another (temp) hack
+ local size = details.fontdata.parameters.size
+ local factor = details.fontdata.parameters.factor
+ local descriptions = { }
+ local characters = details.fontdata.characters
+ --
+ local names, _, _, metadata = fonts.constructors.handlers.pfb.loadvector(pfbfile)
+ local reverse = table.swapped(names)
+ local vector = encoding.vector
+ local indices = details.indices
+ local remapped = { }
+ local factor = bpfactor * size / 65536
+ for k, v in next, indices do
+ local name = vector[k]
+ local index = reverse[name] or 0
+ local width = factor * (characters[k].width or 0)
+ descriptions[k] = {
+ width = width,
+ index = index,
+ name = name,
+ }
+ remapped[index] = true
+ end
+ details.indices = remapped
+ --
+ details.rawdata.descriptions = descriptions
+ details.filename = filename
+ details.rawdata.metadata = { }
+ --
+ properties.filename = filename
+ properties.format = format
+ writer = mainwriters[format]
+ end
+ end
+ if not writer then
+ local pfbfile = file.replacesuffix(filename,"pfb")
+ if encoding and pfbfile then
+ filename = pfbfile
+ format = "type1"
+ --
+ -- another (temp) hack
+ local size = details.fontdata.parameters.size
+ local factor = details.fontdata.parameters.factor
+ local descriptions = { }
+ local characters = details.fontdata.characters
+ --
+ local names, _, _, metadata = fonts.constructors.handlers.pfb.loadvector(pfbfile)
+ local reverse = table.swapped(names)
+ local vector = encoding.vector
+ local indices = details.indices
+ local remapped = { }
+ local factor = bpfactor * size / 65536
+ for k, v in next, indices do
+ local name = vector[k]
+ local index = reverse[name] or 0
+ local width = factor * (characters[k].width or 0)
+ descriptions[k] = {
+ width = width,
+ index = index,
+ name = name,
+ }
+ remapped[index] = true
+ end
+ details.indices = remapped
+ --
+ details.rawdata.descriptions = descriptions
+ details.filename = filename
+ details.rawdata.metadata = { }
+ --
+ properties.filename = filename
+ properties.format = format
+ writer = mainwriters[format]
+ end
+ end
+ if writer then
+ if trace_fonts then
+ report_fonts("using main writer %a",format)
+ end
+ -- better move this test to the writers .. cleaner
+ local streams = loadstreamdata(details.fontdata)
+ if streams and streams.fontheader and streams.names then
+ details.streams = streams
+ writer(details)
+ details.streams = { }
+ elseif trace_fonts then
+ -- can be ok for e.g. emoji
+ report_fonts("no streams in %a",filename)
+ end
+ -- free some memory
+ else -- if trace_fonts then
+ report_fonts("no %a writer for %a",format,filename)
+ end
+ end
+ else -- not problem for svg ...
+ -- report_fonts("no indices for %a",filename)
+ end
+ if trace_fonts then
+ report_fonts("embedded indices: % t",table.sortedkeys(details.indices))
+ end
+ mainfonts[details.hash] = false -- done
+ end
+
+ statistics.stoptiming(objects)
+
+end
+
+statistics.register("font embedding time",function()
+ if noffonts > 0 then
+ return format("%s seconds, %s fonts", statistics.elapsedtime(objects),noffonts)
+ end
+end)
+
+-- this is temporary
+
+function lpdf.setomitcidset(v)
+ -- dummy: no longer needed
+ includecidset = not toboolean(v)
+end
+
+function lpdf.setomitcharset(v)
+ -- dummy
+end
+
+function lpdf.getfontobjectnumber(k)
+ return objects[k]
+end
+
+function lpdf.getfontname(k)
+ return names[k]
+end
+
+-- local done = false -- todo:
+
+-- updaters.register("backend.update",function()
+-- if not done then
+ lpdf.registerdocumentfinalizer(lpdf.flushfonts,1,"wrapping up fonts")
+-- done = true
+-- end
+-- end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-enc.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-enc.lmt
new file mode 100644
index 00000000000..090fb15cd5d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-enc.lmt
@@ -0,0 +1,157 @@
+if not modules then modules = { } end modules ['lpdf-enc'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- delayed loading
+
+local pdfconstant = lpdf.constant
+
+return lpdf.dictionary {
+ Type = pdfconstant("Encoding"),
+ Differences = lpdf.array {
+ 24,
+ pdfconstant("breve"),
+ pdfconstant("caron"),
+ pdfconstant("circumflex"),
+ pdfconstant("dotaccent"),
+ pdfconstant("hungarumlaut"),
+ pdfconstant("ogonek"),
+ pdfconstant("ring"),
+ pdfconstant("tilde"),
+ 39,
+ pdfconstant("quotesingle"),
+ 96,
+ pdfconstant("grave"),
+ 128,
+ pdfconstant("bullet"),
+ pdfconstant("dagger"),
+ pdfconstant("daggerdbl"),
+ pdfconstant("ellipsis"),
+ pdfconstant("emdash"),
+ pdfconstant("endash"),
+ pdfconstant("florin"),
+ pdfconstant("fraction"),
+ pdfconstant("guilsinglleft"),
+ pdfconstant("guilsinglright"),
+ pdfconstant("minus"),
+ pdfconstant("perthousand"),
+ pdfconstant("quotedblbase"),
+ pdfconstant("quotedblleft"),
+ pdfconstant("quotedblright"),
+ pdfconstant("quoteleft"),
+ pdfconstant("quoteright"),
+ pdfconstant("quotesinglbase"),
+ pdfconstant("trademark"),
+ pdfconstant("fi"),
+ pdfconstant("fl"),
+ pdfconstant("Lslash"),
+ pdfconstant("OE"),
+ pdfconstant("Scaron"),
+ pdfconstant("Ydieresis"),
+ pdfconstant("Zcaron"),
+ pdfconstant("dotlessi"),
+ pdfconstant("lslash"),
+ pdfconstant("oe"),
+ pdfconstant("scaron"),
+ pdfconstant("zcaron"),
+ 160,
+ pdfconstant("Euro"),
+ 164,
+ pdfconstant("currency"),
+ 166,
+ pdfconstant("brokenbar"),
+ 168,
+ pdfconstant("dieresis"),
+ pdfconstant("copyright"),
+ pdfconstant("ordfeminine"),
+ 172,
+ pdfconstant("logicalnot"),
+ pdfconstant(".notdef"),
+ pdfconstant("registered"),
+ pdfconstant("macron"),
+ pdfconstant("degree"),
+ pdfconstant("plusminus"),
+ pdfconstant("twosuperior"),
+ pdfconstant("threesuperior"),
+ pdfconstant("acute"),
+ pdfconstant("mu"),
+ 183,
+ pdfconstant("periodcentered"),
+ pdfconstant("cedilla"),
+ pdfconstant("onesuperior"),
+ pdfconstant("ordmasculine"),
+ 188,
+ pdfconstant("onequarter"),
+ pdfconstant("onehalf"),
+ pdfconstant("threequarters"),
+ 192,
+ pdfconstant("Agrave"),
+ pdfconstant("Aacute"),
+ pdfconstant("Acircumflex"),
+ pdfconstant("Atilde"),
+ pdfconstant("Adieresis"),
+ pdfconstant("Aring"),
+ pdfconstant("AE"),
+ pdfconstant("Ccedilla"),
+ pdfconstant("Egrave"),
+ pdfconstant("Eacute"),
+ pdfconstant("Ecircumflex"),
+ pdfconstant("Edieresis"),
+ pdfconstant("Igrave"),
+ pdfconstant("Iacute"),
+ pdfconstant("Icircumflex"),
+ pdfconstant("Idieresis"),
+ pdfconstant("Eth"),
+ pdfconstant("Ntilde"),
+ pdfconstant("Ograve"),
+ pdfconstant("Oacute"),
+ pdfconstant("Ocircumflex"),
+ pdfconstant("Otilde"),
+ pdfconstant("Odieresis"),
+ pdfconstant("multiply"),
+ pdfconstant("Oslash"),
+ pdfconstant("Ugrave"),
+ pdfconstant("Uacute"),
+ pdfconstant("Ucircumflex"),
+ pdfconstant("Udieresis"),
+ pdfconstant("Yacute"),
+ pdfconstant("Thorn"),
+ pdfconstant("germandbls"),
+ pdfconstant("agrave"),
+ pdfconstant("aacute"),
+ pdfconstant("acircumflex"),
+ pdfconstant("atilde"),
+ pdfconstant("adieresis"),
+ pdfconstant("aring"),
+ pdfconstant("ae"),
+ pdfconstant("ccedilla"),
+ pdfconstant("egrave"),
+ pdfconstant("eacute"),
+ pdfconstant("ecircumflex"),
+ pdfconstant("edieresis"),
+ pdfconstant("igrave"),
+ pdfconstant("iacute"),
+ pdfconstant("icircumflex"),
+ pdfconstant("idieresis"),
+ pdfconstant("eth"),
+ pdfconstant("ntilde"),
+ pdfconstant("ograve"),
+ pdfconstant("oacute"),
+ pdfconstant("ocircumflex"),
+ pdfconstant("otilde"),
+ pdfconstant("odieresis"),
+ pdfconstant("divide"),
+ pdfconstant("oslash"),
+ pdfconstant("ugrave"),
+ pdfconstant("uacute"),
+ pdfconstant("ucircumflex"),
+ pdfconstant("udieresis"),
+ pdfconstant("yacute"),
+ pdfconstant("thorn"),
+ pdfconstant("ydieresis"),
+ },
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-epa.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-epa.lmt
new file mode 100644
index 00000000000..b37e9c0432a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-epa.lmt
@@ -0,0 +1,1107 @@
+if not modules then modules = { } end modules ['lpdf-epa'] = {
+ version = 1.001,
+ comment = "companion to lpdf-epa.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Links can also have quadpoint
+
+-- embedded files ... not bound to a page
+
+local type, tonumber, next = type, tonumber, next
+local format, gsub, lower, find = string.format, string.gsub, string.lower, string.find
+local formatters = string.formatters
+local concat, merged = table.concat, table.merged
+local abs = math.abs
+local expandname = file.expandname
+local allocate = utilities.storage.allocate
+local bor, band = bit32.bor, bit32.band
+local isfile = lfs.isfile
+
+local trace_links = false trackers.register("figures.links", function(v) trace_links = v end)
+local trace_comments = false trackers.register("figures.comments", function(v) trace_comments = v end)
+local trace_fields = false trackers.register("figures.fields", function(v) trace_fields = v end)
+local trace_outlines = false trackers.register("figures.outlines", function(v) trace_outlines = v end)
+
+local report_link = logs.reporter("backend","link")
+local report_comment = logs.reporter("backend","comment")
+local report_field = logs.reporter("backend","field")
+local report_outline = logs.reporter("backend","outline")
+
+local lpdf = lpdf
+local backends = backends
+local context = context
+
+local nodeinjections = backends.pdf.nodeinjections
+
+local setmacro = tokens.setters.macro
+
+local pdfarray = lpdf.array
+local pdfdictionary = lpdf.dictionary
+local pdfconstant = lpdf.constant
+local pdfreference = lpdf.reference
+
+local pdfreserveobject
+local pdfgetpos
+
+updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+ pdfgetpos = lpdf.getpos
+end)
+
+local pdfcopyboolean = lpdf.copyboolean
+local pdfcopyunicode = lpdf.copyunicode
+local pdfcopyarray = lpdf.copyarray
+local pdfcopydictionary = lpdf.copydictionary
+local pdfcopynumber = lpdf.copynumber
+local pdfcopyinteger = lpdf.copyinteger
+local pdfcopystring = lpdf.copystring
+local pdfcopyconstant = lpdf.copyconstant
+
+local createimage = images.create
+local embedimage = images.embed
+
+local hpack_node = nodes.hpack
+
+local loadpdffile = lpdf.epdf.load
+
+local nameonly = file.nameonly
+
+local variables = interfaces.variables
+local codeinjections = backends.pdf.codeinjections
+----- urlescaper = lpegpatterns.urlescaper
+----- utftohigh = lpegpatterns.utftohigh
+local escapetex = characters.filters.utf.private.escape
+
+local bookmarks = structures.bookmarks
+
+local maxdimen = 0x3FFFFFFF -- 2^30-1
+
+local bpfactor = number.dimenfactors.bp
+
+local layerspec = {
+ "epdfcontent"
+}
+
+local getpos = function() getpos = backends.codeinjections.getpos return getpos() end
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local jobembedded = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.embedded = jobembedded
+
+local function initializer()
+ tobesaved = jobembedded.tobesaved
+ collected = jobembedded.collected
+end
+
+job.register('job.embedded.collected',tobesaved,initializer)
+
+local function validdocument(specification)
+ if figures and not specification then
+ specification = figures and figures.current()
+ specification = specification and specification.status
+ end
+ if specification then
+ local fullname = specification.fullname
+ local expanded = lower(expandname(fullname))
+ -- we could add a check for duplicate page insertion
+ tobesaved[expanded] = true
+ --- but that is messy anyway so we forget about it
+ return specification, fullname, loadpdffile(fullname) -- costs time
+ end
+end
+
+local function getmediasize(specification,pagedata)
+ local xscale = specification.xscale or 1
+ local yscale = specification.yscale or 1
+ ----- size = specification.size or "crop" -- todo
+ local mediabox = pagedata.MediaBox
+ local llx = mediabox[1]
+ local lly = mediabox[2]
+ local urx = mediabox[3]
+ local ury = mediabox[4]
+ local width = xscale * (urx - llx) -- \\overlaywidth, \\overlayheight
+ local height = yscale * (ury - lly) -- \\overlaywidth, \\overlayheight
+ return llx, lly, urx, ury, width, height, xscale, yscale
+end
+
+local function getdimensions(annotation,llx,lly,xscale,yscale,width,height,report)
+ local rectangle = annotation.Rect
+ local a_llx = rectangle[1]
+ local a_lly = rectangle[2]
+ local a_urx = rectangle[3]
+ local a_ury = rectangle[4]
+ local x = xscale * (a_llx - llx)
+ local y = yscale * (a_lly - lly)
+ local w = xscale * (a_urx - a_llx)
+ local h = yscale * (a_ury - a_lly)
+ if w > width or h > height or w < 0 or h < 0 or abs(x) > (maxdimen/2) or abs(y) > (maxdimen/2) then
+ report("broken rectangle [%.6F %.6F %.6F %.6F] (max: %.6F)",a_llx,a_lly,a_urx,a_ury,maxdimen/2)
+ return
+ end
+ return x, y, w, h, a_llx, a_lly, a_urx, a_ury
+end
+
+local layerused = false
+
+-- local function initializelayer(height,width)
+-- if not layerused then
+-- context.definelayer(layerspec, { height = height .. "bp", width = width .. "bp" })
+-- layerused = true
+-- end
+-- end
+
+local function initializelayer(height,width)
+-- if not layerused then
+ context.setuplayer(layerspec, { height = height .. "bp", width = width .. "bp" })
+ layerused = true
+-- end
+end
+
+function codeinjections.flushmergelayer()
+ if layerused then
+ context.flushlayer(layerspec)
+ layerused = false
+ end
+end
+
+local f_namespace = formatters["lpdf-epa-%s-"]
+
+local function makenamespace(filename)
+ filename = gsub(lower(nameonly(filename)),"[^%a%d]+","-")
+ return f_namespace(filename)
+end
+
+local function add_link(x,y,w,h,destination,what)
+ x = x .. "bp"
+ y = y .. "bp"
+ w = w .. "bp"
+ h = h .. "bp"
+ if trace_links then
+ report_link("destination %a, type %a, dx %s, dy %s, wd %s, ht %s",destination,what,x,y,w,h)
+ end
+ local locationspec = { -- predefining saves time
+ x = x,
+ y = y,
+ preset = "leftbottom",
+ }
+ local buttonspec = {
+ width = w,
+ height = h,
+ offset = variables.overlay,
+ frame = trace_links and variables.on or variables.off,
+ }
+ context.setlayer (
+ layerspec,
+ locationspec,
+ function() context.button ( buttonspec, "", { destination } ) end
+ -- context.nested.button(buttonspec, "", { destination }) -- time this
+ )
+end
+
+local function link_goto(x,y,w,h,document,annotation,pagedata,namespace)
+ local a = annotation.A
+ if a then
+ local destination = a.D -- [ 18 0 R /Fit ]
+ local what = "page"
+ if type(destination) == "string" then
+ local destinations = document.destinations
+ local wanted = destinations[destination]
+ destination = wanted and wanted.D -- is this ok? isn't it destination already a string?
+ if destination then what = "named" end
+ end
+ local pagedata = destination and destination[1]
+ if pagedata then
+ local destinationpage = pagedata.number
+ if destinationpage then
+ add_link(x,y,w,h,namespace .. destinationpage,what)
+ end
+ end
+ end
+end
+
+local function link_uri(x,y,w,h,document,annotation)
+ local url = annotation.A.URI
+ if url then
+ -- url = lpegmatch(urlescaper,url)
+ -- url = lpegmatch(utftohigh,url)
+ url = escapetex(url)
+ add_link(x,y,w,h,formatters["url(%s)"](url),"url")
+ end
+end
+
+-- The rules in PDF on what a 'file specification' is, is in fact quite elaborate
+-- (see section 3.10 in the 1.7 reference) so we need to test for string as well
+-- as a table. TH/20140916
+
+-- When embedded is set then files need to have page references which is seldom the
+-- case but you can generate them with context:
+--
+-- \setupinteraction[state=start,page={page,page}]
+--
+-- see tests/mkiv/interaction/cross[1|2|3].tex for an example
+
+local embedded = false directives.register("figures.embedded", function(v) embedded = v end)
+local reported = { }
+
+local function link_file(x,y,w,h,document,annotation)
+ local a = annotation.A
+ if a then
+ local filename = a.F
+ if type(filename) == "table" then
+ filename = filename.F
+ end
+ if filename then
+ filename = escapetex(filename)
+ local destination = a.D
+ if not destination then
+ add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
+ elseif type(destination) == "string" then
+ add_link(x,y,w,h,formatters["%s::%s"](filename,destination),"file (named)")
+ else
+ -- hm, zero offset so maybe: destination + 1
+ destination = tonumber(destination[1]) -- array
+ if destination then
+ destination = destination + 1
+ local loaded = collected[lower(expandname(filename))]
+ if embedded and loaded then
+ add_link(x,y,w,h,makenamespace(filename) .. destination,what)
+ else
+ if loaded and not reported[filename] then
+ report_link("reference to an also loaded file %a, consider using directive: figures.embedded",filename)
+ reported[filename] = true
+ end
+ add_link(x,y,w,h,formatters["%s::page(%s)"](filename,destination),"file (page)")
+ end
+ else
+ add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
+ end
+ end
+ end
+ end
+end
+
+-- maybe handler per subtype and then one loop but then what about order ...
+
+function codeinjections.mergereferences(specification)
+ local specification, fullname, document = validdocument(specification)
+ if not document then
+ return ""
+ end
+ local pagenumber = specification.page or 1
+ local pagedata = document.pages[pagenumber]
+ local annotations = pagedata and pagedata.Annots
+ local namespace = makenamespace(fullname)
+ local reference = namespace .. pagenumber
+ if annotations and #annotations > 0 then
+ local llx, lly, urx, ury, width, height, xscale, yscale = getmediasize(specification,pagedata,xscale,yscale)
+ initializelayer(height,width)
+ for i=1,#annotations do
+ local annotation = annotations[i]
+ if annotation then
+ if annotation.Subtype == "Link" then
+ local a = annotation.A
+ if not a then
+ local d = annotation.Dest
+ if d then
+ annotation.A = { S = "GoTo", D = d } -- no need for a dict
+ end
+ end
+ if not a then
+ report_link("missing link annotation")
+ else
+ local x, y, w, h = getdimensions(annotation,llx,lly,xscale,yscale,width,height,report_link)
+ if x then
+ local linktype = a.S
+ if linktype == "GoTo" then
+ link_goto(x,y,w,h,document,annotation,pagedata,namespace)
+ elseif linktype == "GoToR" then
+ link_file(x,y,w,h,document,annotation)
+ elseif linktype == "URI" then
+ link_uri(x,y,w,h,document,annotation)
+ elseif trace_links then
+ report_link("unsupported link annotation %a",linktype)
+ end
+ end
+ end
+ end
+ elseif trace_links then
+ report_link("broken annotation, index %a",i)
+ end
+ end
+ end
+ -- moved outside previous test
+ setmacro("mergedfigurereference",reference,"global")
+ if trace_links then
+ report_link("setting figure reference to %a",reference)
+ end
+ specification.reference = reference
+ return namespace
+end
+
+function codeinjections.mergeviewerlayers(specification)
+ -- todo: parse included page for layers .. or only for whole document inclusion
+ if true then
+ return
+ end
+ local specification, fullname, document = validdocument(specification)
+ if not document then
+ return ""
+ end
+ local namespace = makenamespace(fullname)
+ local layers = document.layers
+ if layers then
+ for i=1,#layers do
+ local layer = layers[i]
+ if layer then
+ local tag = namespace .. gsub(layer," ",":")
+ local title = tag
+ if trace_links then
+ report_link("using layer %a",tag)
+ end
+ attributes.viewerlayers.define { -- also does some cleaning
+ tag = tag, -- todo: #3A or so
+ title = title,
+ visible = variables.start,
+ editable = variables.yes,
+ printable = variables.yes,
+ }
+ codeinjections.useviewerlayer(tag)
+ elseif trace_links then
+ report_link("broken layer, index %a",i)
+ end
+ end
+ end
+end
+
+-- It took a bit of puzzling and playing around to come to the following
+-- implementation. In the end it looks simple but as usual it takes a while
+-- to see what the specification (and implementation) boils down to. Lots of
+-- shared properties and such. The scaling took some trial and error as
+-- viewers differ. I had to extend some low level helpers to make it more
+-- comfortable. Hm, the specification is somewhat incomplete as some fields
+-- are permitted even if not mentioned so in the end we can share more code.
+--
+-- If all works ok, we can get rid of some copies which saves time and space.
+
+local commentlike = {
+ Text = "text",
+ FreeText = "freetext",
+ Line = "line",
+ Square = "shape",
+ Circle = "shape",
+ Polygon = "poly",
+ PolyLine = "poly",
+ Highlight = "markup",
+ Underline = "markup",
+ Squiggly = "markup",
+ StrikeOut = "markup",
+ Caret = "text",
+ Stamp = "stamp",
+ Ink = "ink",
+ Popup = "popup",
+}
+
+local function copyBS(v) -- dict can be shared
+ if v then
+ -- return pdfdictionary {
+ -- Type = copypdfconstant(V.Type),
+ -- W = copypdfnumber (V.W),
+ -- S = copypdfstring (V.S),
+ -- D = copypdfarray (V.D),
+ -- }
+ return copypdfdictionary(v)
+ end
+end
+
+local function copyBE(v) -- dict can be shared
+ if v then
+ -- return pdfdictionary {
+ -- S = copypdfstring(V.S),
+ -- I = copypdfnumber(V.I),
+ -- }
+ return copypdfdictionary(v)
+ end
+end
+
+local function copyBorder(v) -- dict can be shared
+ if v then
+ -- todo
+ return copypdfarray(v)
+ end
+end
+
+local function copyPopup(v,references)
+ if v then
+ local p = references[v]
+ if p then
+ return pdfreference(p)
+ end
+ end
+end
+
+local function copyParent(v,references)
+ if v then
+ local p = references[v]
+ if p then
+ return pdfreference(p)
+ end
+ end
+end
+
+local function copyIRT(v,references)
+ if v then
+ local p = references[v]
+ if p then
+ return pdfreference(p)
+ end
+ end
+end
+
+local function copyC(v)
+ if v then
+ -- todo: check color space
+ return pdfcopyarray(v)
+ end
+end
+
+local function finalizer(d,xscale,yscale,a_llx,a_ury)
+ local q = d.QuadPoints or d.Vertices or d.CL
+ if q then
+ return function()
+ local h, v = pdfgetpos() -- already scaled
+ for i=1,#q,2 do
+ q[i] = xscale * q[i] + (h*bpfactor - xscale * a_llx)
+ q[i+1] = yscale * q[i+1] + (v*bpfactor - yscale * a_ury)
+ end
+ return d()
+ end
+ end
+ q = d.InkList or d.Path
+ if q then
+ return function()
+ local h, v = pdfgetpos() -- already scaled
+ for i=1,#q do
+ local q = q[i]
+ for i=1,#q,2 do
+ q[i] = xscale * q[i] + (h*bpfactor - xscale * a_llx)
+ q[i+1] = yscale * q[i+1] + (v*bpfactor - yscale * a_ury)
+ end
+ end
+ return d()
+ end
+ end
+ return d()
+end
+
+local validstamps = {
+ Approved = true,
+ Experimental = true,
+ NotApproved = true,
+ AsIs = true,
+ Expired = true,
+ NotForPublicRelease = true,
+ Confidential = true,
+ Final = true,
+ Sold = true,
+ Departmental = true,
+ ForComment = true,
+ TopSecret = true,
+ Draft = true,
+ ForPublicRelease = true,
+}
+
+-- todo: we can use runlocal instead of steps
+
+local function validStamp(v)
+ local name = "Stamped" -- fallback
+ if v then
+ local ok = validstamps[v]
+ if ok then
+ name = ok
+ else
+ for k in next, validstamps do
+ if find(v,k.."$") then
+ name = k
+ validstamps[v] = k
+ break
+ end
+ end
+ end
+ end
+ -- we temporary return to \TEX:
+ context.predefinesymbol { name }
+ context.step()
+ -- beware, an error is not reported
+ return pdfconstant(name), codeinjections.analyzenormalsymbol(name)
+end
+
+local annotationflags = lpdf.flags.annotations
+
+local function copyF(v,lock) -- todo: bxor 24
+ if lock then
+ v = bor(v or 0,annotationflags.ReadOnly + annotationflags.Locked + annotationflags.LockedContents)
+ end
+ if v then
+ return pdfcopyinteger(v)
+ end
+end
+
+-- Speed is not really an issue so we don't optimize this code too much. In the end (after
+-- testing we end up with less code that we started with.
+
+function codeinjections.mergecomments(specification)
+ local specification, fullname, document = validdocument(specification)
+ if not document then
+ return ""
+ end
+ local pagenumber = specification.page or 1
+ local pagedata = document.pages[pagenumber]
+ local annotations = pagedata and pagedata.Annots
+ if annotations and #annotations > 0 then
+ local llx, lly, urx, ury, width, height, xscale, yscale = getmediasize(specification,pagedata,xscale,yscale)
+ initializelayer(height,width)
+ --
+ local lockflags = specification.lock -- todo: proper parameter
+ local references = { }
+ local usedpopups = { }
+ for i=1,#annotations do
+ local annotation = annotations[i]
+ if annotation then
+ local subtype = annotation.Subtype
+ if commentlike[subtype] then
+ references[annotation] = pdfreserveobject()
+ local p = annotation.Popup
+ if p then
+ usedpopups[p] = true
+ end
+ end
+ end
+ end
+ --
+ for i=1,#annotations do
+ -- we keep the order
+ local annotation = annotations[i]
+ if annotation then
+ local reference = references[annotation]
+ if reference then
+ local subtype = annotation.Subtype
+ local kind = commentlike[subtype]
+ if kind ~= "popup" or usedpopups[annotation] then
+ local x, y, w, h, a_llx, a_lly, a_urx, a_ury = getdimensions(annotation,llx,lly,xscale,yscale,width,height,report_comment)
+ if x then
+ local voffset = h
+ local dictionary = pdfdictionary {
+ Subtype = pdfconstant (subtype),
+ -- common (skipped: P AP AS OC AF BM StructParent)
+ Contents = pdfcopyunicode(annotation.Contents),
+ NM = pdfcopystring (annotation.NM),
+ M = pdfcopystring (annotation.M),
+ F = copyF (annotation.F,lockflags),
+ C = copyC (annotation.C),
+ ca = pdfcopynumber (annotation.ca),
+ CA = pdfcopynumber (annotation.CA),
+ Lang = pdfcopystring (annotation.Lang),
+ -- also common
+ CreationDate = pdfcopystring (annotation.CreationDate),
+ T = pdfcopyunicode(annotation.T),
+ Subj = pdfcopyunicode(annotation.Subj),
+ -- border
+ Border = pdfcopyarray (annotation.Border),
+ BS = copyBS (annotation.BS),
+ BE = copyBE (annotation.BE),
+ -- sort of common
+ Popup = copyPopup (annotation.Popup,references),
+ RC = pdfcopyunicode(annotation.RC) -- string or stream
+ }
+ if kind == "markup" then
+ dictionary.IRT = copyIRT (annotation.IRT,references)
+ dictionary.RT = pdfconstant (annotation.RT)
+ dictionary.IT = pdfcopyconstant (annotation.IT)
+ dictionary.QuadPoints = pdfcopyarray (annotation.QuadPoints)
+ -- dictionary.RD = pdfcopyarray (annotation.RD)
+ elseif kind == "text" then
+ -- somehow F fails to view : /F 24 : bit4=nozoom bit5=norotate
+ dictionary.F = nil
+ dictionary.Open = pdfcopyboolean (annotation.Open)
+ dictionary.Name = pdfcopyunicode (annotation.Name)
+ dictionary.State = pdfcopystring (annotation.State)
+ dictionary.StateModel = pdfcopystring (annotation.StateModel)
+ dictionary.IT = pdfcopyconstant (annotation.IT)
+ dictionary.QuadPoints = pdfcopyarray (annotation.QuadPoints)
+ dictionary.RD = pdfcopyarray (annotation.RD) -- caret
+ dictionary.Sy = pdfcopyconstant (annotation.Sy) -- caret
+ voffset = 0
+ elseif kind == "freetext" then
+ dictionary.DA = pdfcopystring (annotation.DA)
+ dictionary.Q = pdfcopyinteger (annotation.Q)
+ dictionary.DS = pdfcopystring (annotation.DS)
+ dictionary.CL = pdfcopyarray (annotation.CL)
+ dictionary.IT = pdfcopyconstant (annotation.IT)
+ dictionary.LE = pdfcopyconstant (annotation.LE)
+ -- dictionary.RC = pdfcopystring (annotation.RC)
+ elseif kind == "line" then
+ dictionary.LE = pdfcopyarray (annotation.LE)
+ dictionary.IC = pdfcopyarray (annotation.IC)
+ dictionary.LL = pdfcopynumber (annotation.LL)
+ dictionary.LLE = pdfcopynumber (annotation.LLE)
+ dictionary.Cap = pdfcopyboolean (annotation.Cap)
+ dictionary.IT = pdfcopyconstant (annotation.IT)
+ dictionary.LLO = pdfcopynumber (annotation.LLO)
+ dictionary.CP = pdfcopyconstant (annotation.CP)
+ dictionary.Measure = pdfcopydictionary(annotation.Measure) -- names
+ dictionary.CO = pdfcopyarray (annotation.CO)
+ voffset = 0
+ elseif kind == "shape" then
+ dictionary.IC = pdfcopyarray (annotation.IC)
+ -- dictionary.RD = pdfcopyarray (annotation.RD)
+ voffset = 0
+ elseif kind == "stamp" then
+ local name, appearance = validStamp(annotation.Name)
+ dictionary.Name = name
+ dictionary.AP = appearance
+ voffset = 0
+ elseif kind == "ink" then
+ dictionary.InkList = pdfcopyarray (annotation.InkList)
+ elseif kind == "poly" then
+ dictionary.Vertices = pdfcopyarray (annotation.Vertices)
+ -- dictionary.LE = pdfcopyarray (annotation.LE) -- todo: names in array
+ dictionary.IC = pdfcopyarray (annotation.IC)
+ dictionary.IT = pdfcopyconstant (annotation.IT)
+ dictionary.Measure = pdfcopydictionary(annotation.Measure)
+ dictionary.Path = pdfcopyarray (annotation.Path)
+ -- dictionary.RD = pdfcopyarray (annotation.RD)
+ elseif kind == "popup" then
+ dictionary.Open = pdfcopyboolean (annotation.Open)
+ dictionary.Parent = copyParent (annotation.Parent,references)
+ voffset = 0
+ end
+ if dictionary then
+ local locationspec = {
+ x = x .. "bp",
+ y = y .. "bp",
+ voffset = voffset .. "bp",
+ preset = "leftbottom",
+ }
+ local finalize = finalizer(dictionary,xscale,yscale,a_llx,a_ury)
+ context.setlayer(layerspec,locationspec,function()
+ context(hpack_node(nodeinjections.annotation(w/bpfactor,h/bpfactor,0,finalize,reference)))
+ end)
+ end
+ end
+ else
+ -- report_comment("skipping annotation, index %a",i)
+ end
+ end
+ elseif trace_comments then
+ report_comment("broken annotation, index %a",i)
+ end
+ end
+ end
+ return namespace
+end
+
+local widgetflags = lpdf.flags.widgets
+
+local function flagstoset(flag,flags)
+ local t = { }
+ if flags then
+ for k, v in next, flags do
+ if band(flag,v) ~= 0 then
+ t[k] = true
+ end
+ end
+ end
+ return t
+end
+
+-- BS : border style dict
+-- R : rotation 0 90 180 270
+-- BG : background array
+-- CA : caption string
+-- RC : roll over caption
+-- AC : down caption
+-- I/RI/IX : icon streams
+-- IF : fit dictionary
+-- TP : text position number
+
+-- Opt : array of texts
+-- TI : top index
+
+-- V : value
+-- DV : default value
+-- DS : default string
+-- RV : rich
+-- Q : quadding (0=left 1=middle 2=right)
+
+function codeinjections.mergefields(specification)
+ local specification, fullname, document = validdocument(specification)
+ if not document then
+ return ""
+ end
+ local pagenumber = specification.page or 1
+ local pagedata = document.pages[pagenumber]
+ local annotations = pagedata and pagedata.Annots
+ if annotations and #annotations > 0 then
+ local llx, lly, urx, ury, width, height, xscale, yscale = getmediasize(specification,pagedata,xscale,yscale)
+ initializelayer(height,width)
+ --
+ for i=1,#annotations do
+ -- we keep the order
+ local annotation = annotations[i]
+ if annotation then
+ local subtype = annotation.Subtype
+ if subtype == "Widget" then
+ local parent = annotation.Parent or { }
+ local name = annotation.T or parent.T
+ local what = annotation.FT or parent.FT
+ if name and what then
+ local x, y, w, h, a_llx, a_lly, a_urx, a_ury = getdimensions(annotation,llx,lly,xscale,yscale,width,height,report_field)
+ if x then
+ x = x .. "bp"
+ y = y .. "bp"
+ local W, H = w, h
+ w = w .. "bp"
+ h = h .. "bp"
+ if trace_fields then
+ report_field("field %a, type %a, dx %s, dy %s, wd %s, ht %s",name,what,x,y,w,h)
+ end
+ local locationspec = {
+ x = x,
+ y = y,
+ preset = "leftbottom",
+ }
+ --
+ local aflags = flagstoset(annotation.F or parent.F, annotationflags)
+ local wflags = flagstoset(annotation.Ff or parent.Ff, widgetflags)
+ if what == "Tx" then
+ -- DA DV F FT MaxLen MK Q T V | AA OC
+ if wflags.MultiLine then
+ wflags.MultiLine = nil
+ what = "text"
+ else
+ what = "line"
+ end
+ -- via context
+ local fieldspec = {
+ width = w,
+ height = h,
+ offset = variables.overlay,
+ frame = trace_links and variables.on or variables.off,
+ n = annotation.MaxLen or (parent and parent.MaxLen),
+ type = what,
+ option = concat(merged(aflags,wflags),","),
+ }
+ context.setlayer (layerspec,locationspec,function()
+ context.definefieldbody ( { name } , fieldspec )
+ context.fieldbody ( { name } )
+ end)
+ --
+ elseif what == "Btn" then
+ if wflags.Radio or wflags.RadiosInUnison then
+ -- AP AS DA F Ff FT H MK T V | AA OC
+ wflags.Radio = nil
+ wflags.RadiosInUnison = nil
+ what = "radio"
+ elseif wflags.PushButton then
+ -- AP DA F Ff FT H MK T | AA OC
+ --
+ -- Push buttons only have an appearance and some associated
+ -- actions so they are not worth copying.
+ --
+ wflags.PushButton = nil
+ what = "push"
+ else
+ -- AP AS DA F Ff FT H MK T V | OC AA
+ what = "check"
+ -- direct
+ local AP = annotation.AP or (parent and parent.AP)
+ if AP then
+ local a = document.__xrefs__[AP]
+ if a and pdfe.copyappearance then
+ local o = pdfe.copyappearance(document,a)
+ if o then
+ AP = pdfreference(o)
+ end
+ end
+ end
+ local dictionary = pdfdictionary {
+ Subtype = pdfconstant("Widget"),
+ FT = pdfconstant("Btn"),
+ T = pdfcopyunicode(annotation.T or parent.T),
+ F = pdfcopyinteger(annotation.F or parent.F),
+ Ff = pdfcopyinteger(annotation.Ff or parent.Ff),
+ AS = pdfcopyconstant(annotation.AS or (parent and parent.AS)),
+ AP = AP and pdfreference(AP),
+ }
+ local finalize = dictionary()
+ context.setlayer(layerspec,locationspec,function()
+ context(hpack_node(nodeinjections.annotation(W/bpfactor,H/bpfactor,0,finalize)))
+ end)
+ --
+ end
+ elseif what == "Ch" then
+ -- F Ff FT Opt T | AA OC (rest follows)
+ if wflags.PopUp then
+ wflags.PopUp = nil
+ if wflags.Edit then
+ wflags.Edit = nil
+ what = "combo"
+ else
+ what = "popup"
+ end
+ else
+ what = "choice"
+ end
+ elseif what == "Sig" then
+ what = "signature"
+ else
+ what = nil
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+-- Beware, bookmarks can be in pdfdoc encoding or in unicode. However, in mkiv we
+-- write out the strings in unicode (hex). When we read them in, we check for a bom
+-- and convert to utf.
+
+function codeinjections.getbookmarks(filename)
+
+ -- The first version built a nested tree and flattened that afterwards ... but I decided
+ -- to keep it simple and flat.
+
+ local list = bookmarks.extras.get(filename)
+
+ if list then
+ return list
+ else
+ list = { }
+ end
+
+ local document = nil
+
+ if isfile(filename) then
+ document = loadpdffile(filename)
+ else
+ report_outline("unknown file %a",filename)
+ bookmarks.extras.register(filename,list)
+ return list
+ end
+
+ local outlines = document.Catalog.Outlines
+ local pages = document.pages
+ local nofpages = document.nofpages
+ local destinations = document.destinations
+
+ -- I need to check this destination analyzer with the one in annotations .. best share
+ -- code (and not it's inconsistent). On the todo list ...
+
+ local function setdestination(current,entry)
+ local destination = nil
+ local action = current.A
+ if action then
+ local subtype = action.S
+ if subtype == "GoTo" then
+ destination = action.D
+ local kind = type(destination)
+ if kind == "string" then
+ entry.destination = destination
+ destination = destinations[destination]
+ local pagedata = destination and destination[1]
+ if pagedata then
+ entry.realpage = pagedata.number
+ end
+ elseif kind == "table" then
+ local pageref = #destination
+ if pageref then
+ local pagedata = pages[pageref]
+ if pagedata then
+ entry.realpage = pagedata.number
+ end
+ end
+ end
+ -- elseif subtype then
+ -- report("unsupported bookmark action %a",subtype)
+ end
+ else
+ local destination = current.Dest
+ if destination then
+ if type(destination) == "string" then
+ local wanted = destinations[destination]
+ destination = wanted and wanted.D
+ if destination then
+ entry.destination = destination
+ end
+ else
+ local pagedata = destination and destination[1]
+ if pagedata and pagedata.Type == "Page" then
+ entry.realpage = pagedata.number
+ -- else
+ -- report("unsupported bookmark destination (no page)")
+ end
+ end
+ end
+ end
+ end
+
+ local function traverse(current,depth)
+ while current do
+ -- local title = current.Title
+ local title = current("Title") -- can be pdfdoc or unicode
+ if title then
+ local entry = {
+ level = depth,
+ title = title,
+ }
+ list[#list+1] = entry
+ setdestination(current,entry)
+ if trace_outlines then
+ report_outline("%w%s",2*depth,title)
+ end
+ end
+ local first = current.First
+ if first then
+ local current = first
+ while current do
+ local title = current.Title
+ if title and trace_outlines then
+ report_outline("%w%s",2*depth,title)
+ end
+ local entry = {
+ level = depth,
+ title = title,
+ }
+ setdestination(current,entry)
+ list[#list+1] = entry
+ traverse(current.First,depth+1)
+ current = current.Next
+ end
+ end
+ current = current.Next
+ end
+ end
+
+ if outlines then
+ if trace_outlines then
+ report_outline("outline of %a:",document.filename)
+ report_outline()
+ end
+ traverse(outlines,0)
+ if trace_outlines then
+ report_outline()
+ end
+ elseif trace_outlines then
+ report_outline("no outline in %a",document.filename)
+ end
+
+ bookmarks.extras.register(filename,list)
+
+ return list
+
+end
+
+function codeinjections.mergebookmarks(specification)
+ -- codeinjections.getbookmarks(document)
+ if not specification then
+ specification = figures and figures.current()
+ specification = specification and specification.status
+ end
+ if specification then
+ local fullname = specification.fullname
+ local bookmarks = backends.codeinjections.getbookmarks(fullname)
+ local realpage = tonumber(specification.page) or 1
+ for i=1,#bookmarks do
+ local b = bookmarks[i]
+ if not b.usedpage then
+ if b.realpage == realpage then
+ if trace_options then
+ report_outline("using %a at page %a of file %a",b.title,realpage,fullname)
+ end
+ b.usedpage = true
+ b.section = structures.sections.currentsectionindex()
+ b.pageindex = specification.pageindex
+ end
+ end
+ end
+ end
+end
+
+-- A bit more than a placeholder but in the same perspective as
+-- inclusion of comments and fields:
+--
+-- getinfo{ filename = "tt.pdf", metadata = true }
+-- getinfo{ filename = "tt.pdf", page = 1, metadata = "xml" }
+-- getinfo("tt.pdf")
+
+function codeinjections.getinfo(specification)
+ if type(specification) == "string" then
+ specification = { filename = specification }
+ end
+ local filename = specification.filename
+ if type(filename) == "string" and isfile(filename) then
+ local pdffile = loadpdffile(filename)
+ if pdffile then
+ local pagenumber = specification.page or 1
+ local metadata = specification.metadata
+ local catalog = pdffile.Catalog
+ local info = pdffile.Info
+ local pages = pdffile.pages
+ local nofpages = pdffile.nofpages
+ if metadata then
+ local m = catalog.Metadata
+ if m then
+ m = m()
+ if metadata == "xml" then
+ metadata = xml.convert(m)
+ else
+ metadata = m
+ end
+ else
+ metadata = nil
+ end
+ else
+ metadata = nil
+ end
+ if pagenumber > nofpages then
+ pagenumber = nofpages
+ end
+ local nobox = { 0, 0, 0, 0 }
+ local crop = nobox
+ local media = nobox
+ local page = pages[pagenumber]
+ if page then
+ crop = page.CropBox or nobox
+ media = page.MediaBox or crop or nobox
+ end
+ local bbox = crop or media or nobox
+ return {
+ filename = filename,
+ pdfversion = tonumber(catalog.Version),
+ nofpages = nofpages,
+ title = info.Title,
+ creator = info.Creator,
+ producer = info.Producer,
+ creationdate = info.CreationDate,
+ modification = info.ModDate,
+ metadata = metadata,
+ width = bbox[4] - bbox[2],
+ height = bbox[3] - bbox[1],
+ cropbox = { crop[1], crop[2], crop[3], crop[4] }, -- we need access
+ mediabox = { media[1], media[2], media[3], media[4] } , -- we need access
+ }
+ end
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-fld.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-fld.lmt
new file mode 100644
index 00000000000..eacbb085d88
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-fld.lmt
@@ -0,0 +1,1501 @@
+if not modules then modules = { } end modules ['lpdf-fld'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- TURN OFF: preferences -> forms -> highlight -> etc
+
+-- The problem with widgets is that so far each version of acrobat has some
+-- rendering problem. I tried to keep up with this but it makes no sense to do so as
+-- one cannot rely on the viewer not changing. Especially Btn fields are tricky as
+-- their appearences need to be synchronized in the case of children but e.g.
+-- acrobat 10 does not retain the state and forces a check symbol. If you make a
+-- file in acrobat then it has MK entries that seem to overload the already present
+-- appearance streams (they're probably only meant for printing) as it looks like
+-- the viewer has some fallback on (auto generated) MK behaviour built in. So ...
+-- hard to test. Unfortunately not even the default appearance is generated. This
+-- will probably be solved at some point.
+--
+-- Also, for some reason the viewer does not always show custom appearances when
+-- fields are being rolled over or clicked upon, and circles or checks pop up when
+-- you don't expect them. I fear that this kind of instability eventually will kill
+-- pdf forms. After all, the manual says: "individual annotation handlers may ignore
+-- this entry and provide their own appearances" and one might wonder what
+-- 'individual' means here, but effectively this renders the whole concept of
+-- appearances useless.
+--
+-- Okay, here is one observation. A pdf file contains objects and one might consider
+-- each one to be a static entity when read in. However, acrobat starts rendering
+-- and seems to manipulate (appearance streams) of objects in place (this is visible
+-- when the file is saved again). And, combined with some other caching and hashing,
+-- this might give side effects for shared objects. So, it seems that for some cases
+-- one can best be not too clever and not share but duplicate information. Of course
+-- this defeats the whole purpose of these objects. Of course I can be wrong.
+--
+-- A rarther weird side effect of the viewer is that the highlighting of fields
+-- obscures values, unless you uses one of the BS variants, and this makes custum
+-- appearances rather useless as there is no way to control this apart from changing
+-- the viewer preferences. It could of course be a bug but it would be nice if the
+-- highlighting was at least transparent. I have no clue why the built in shapes
+-- work ok (some xform based appearances are generated) while equally valid other
+-- xforms fail. It looks like acrobat appearances come on top (being refered to in
+-- the MK) while custom ones are behind the highlight rectangle. One can disable the
+-- "Show border hover color for fields" option in the preferences. If you load
+-- java-imp-rhh this side effect gets disabled and you get what you expect (it took
+-- me a while to figure out this hack).
+--
+-- When highlighting is enabled, those default symbols flash up, so it looks like we
+-- have some inteference between this setting and custom appearances.
+--
+-- Anyhow, the NeedAppearances is really needed in order to get a rendering for
+-- printing especially when highlighting (those colorfull foregrounds) is on.
+
+local tostring, tonumber, next = tostring, tonumber, next
+local gmatch, lower, format, formatters = string.gmatch, string.lower, string.format, string.formatters
+local lpegmatch = lpeg.match
+local bpfactor, todimen = number.dimenfactors.bp, string.todimen
+local sortedhash = table.sortedhash
+local trace_fields = false trackers.register("backends.fields", function(v) trace_fields = v end)
+
+local report_fields = logs.reporter("backend","fields")
+
+local backends, lpdf = backends, lpdf
+
+local variables = interfaces.variables
+local context = context
+
+local references = structures.references
+local settings_to_array = utilities.parsers.settings_to_array
+
+local pdfbackend = backends.pdf
+
+local nodeinjections = pdfbackend.nodeinjections
+local codeinjections = pdfbackend.codeinjections
+local registrations = pdfbackend.registrations
+
+local registeredsymbol = codeinjections.registeredsymbol
+
+local pdfstream = lpdf.stream
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfreference = lpdf.reference
+local pdfunicode = lpdf.unicode
+local pdfstring = lpdf.string
+local pdfconstant = lpdf.constant
+local pdfaction = lpdf.action
+
+local pdfflushobject
+local pdfshareobjectreference
+local pdfshareobject
+local pdfreserveobject
+local pdfpagereference
+local pdfmajorversion
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfshareobjectreference = lpdf.shareobjectreference
+ pdfshareobject = lpdf.shareobject
+ pdfreserveobject = lpdf.reserveobject
+ pdfpagereference = lpdf.pagereference
+ pdfmajorversion = lpdf.majorversion
+end)
+
+local pdfcolor = lpdf.color
+local pdfcolorvalues = lpdf.colorvalues
+local pdflayerreference = lpdf.layerreference
+
+local hpack_node = node.hpack
+
+local submitoutputformat = 0 -- 0=unknown 1=HTML 2=FDF 3=XML => not yet used, needs to be checked
+
+local pdf_widget = pdfconstant("Widget")
+local pdf_tx = pdfconstant("Tx")
+local pdf_sig = pdfconstant("Sig")
+local pdf_ch = pdfconstant("Ch")
+local pdf_btn = pdfconstant("Btn")
+local pdf_yes = pdfconstant("Yes")
+local pdf_off = pdfconstant("Off")
+local pdf_p = pdfconstant("P") -- None Invert Outline Push
+local pdf_n = pdfconstant("N") -- None Invert Outline Push
+--
+local pdf_no_rect = pdfarray { 0, 0, 0, 0 }
+
+local splitter = lpeg.splitat("=>")
+
+local formats = {
+ html = 1, fdf = 2, xml = 3,
+}
+
+function codeinjections.setformsmethod(name)
+ submitoutputformat = formats[lower(name)] or formats.xml
+end
+
+local flag = { -- /Ff
+ ReadOnly = 0x00000001, -- 2^ 0
+ Required = 0x00000002, -- 2^ 1
+ NoExport = 0x00000004, -- 2^ 2
+ MultiLine = 0x00001000, -- 2^12
+ Password = 0x00002000, -- 2^13
+ NoToggleToOff = 0x00004000, -- 2^14
+ Radio = 0x00008000, -- 2^15
+ PushButton = 0x00010000, -- 2^16
+ PopUp = 0x00020000, -- 2^17
+ Edit = 0x00040000, -- 2^18
+ Sort = 0x00080000, -- 2^19
+ FileSelect = 0x00100000, -- 2^20
+ DoNotSpellCheck = 0x00400000, -- 2^22
+ DoNotScroll = 0x00800000, -- 2^23
+ Comb = 0x01000000, -- 2^24
+ RichText = 0x02000000, -- 2^25
+ RadiosInUnison = 0x02000000, -- 2^25
+ CommitOnSelChange = 0x04000000, -- 2^26
+}
+
+local plus = { -- /F
+ Invisible = 0x00000001, -- 2^0
+ Hidden = 0x00000002, -- 2^1
+ Printable = 0x00000004, -- 2^2
+ Print = 0x00000004, -- 2^2
+ NoZoom = 0x00000008, -- 2^3
+ NoRotate = 0x00000010, -- 2^4
+ NoView = 0x00000020, -- 2^5
+ ReadOnly = 0x00000040, -- 2^6
+ Locked = 0x00000080, -- 2^7
+ ToggleNoView = 0x00000100, -- 2^8
+ LockedContents = 0x00000200, -- 2^9
+ AutoView = 0x00000100, -- 2^8
+}
+
+-- todo: check what is interfaced
+
+flag.readonly = flag.ReadOnly
+flag.required = flag.Required
+flag.protected = flag.Password
+flag.sorted = flag.Sort
+flag.unavailable = flag.NoExport
+flag.nocheck = flag.DoNotSpellCheck
+flag.fixed = flag.DoNotScroll
+flag.file = flag.FileSelect
+
+plus.hidden = plus.Hidden
+plus.printable = plus.Printable
+plus.auto = plus.AutoView
+
+lpdf.flags.widgets = flag
+lpdf.flags.annotations = plus
+
+-- some day .. lpeg with function or table
+
+local function fieldflag(specification) -- /Ff
+ local o, n = specification.option, 0
+ if o and o ~= "" then
+ for f in gmatch(o,"[^, ]+") do
+ n = n + (flag[f] or 0)
+ end
+ end
+ return n
+end
+
+local function fieldplus(specification) -- /F
+ local o, n = specification.option, 0
+ if o and o ~= "" then
+ for p in gmatch(o,"[^, ]+") do
+ n = n + (plus[p] or 0)
+ end
+ end
+-- n = n + 4
+ return n
+end
+
+-- keep:
+--
+-- local function checked(what)
+-- local set, bug = references.identify("",what)
+-- if not bug and #set > 0 then
+-- local r, n = pdfaction(set)
+-- return pdfshareobjectreference(r)
+-- end
+-- end
+--
+-- local function fieldactions(specification) -- share actions
+-- local d, a = { }, nil
+-- a = specification.mousedown
+-- or specification.clickin if a and a ~= "" then d.D = checked(a) end
+-- a = specification.mouseup
+-- or specification.clickout if a and a ~= "" then d.U = checked(a) end
+-- a = specification.regionin if a and a ~= "" then d.E = checked(a) end -- Enter
+-- a = specification.regionout if a and a ~= "" then d.X = checked(a) end -- eXit
+-- a = specification.afterkey if a and a ~= "" then d.K = checked(a) end
+-- a = specification.format if a and a ~= "" then d.F = checked(a) end
+-- a = specification.validate if a and a ~= "" then d.V = checked(a) end
+-- a = specification.calculate if a and a ~= "" then d.C = checked(a) end
+-- a = specification.focusin if a and a ~= "" then d.Fo = checked(a) end
+-- a = specification.focusout if a and a ~= "" then d.Bl = checked(a) end
+-- a = specification.openpage if a and a ~= "" then d.PO = checked(a) end
+-- a = specification.closepage if a and a ~= "" then d.PC = checked(a) end
+-- -- a = specification.visiblepage if a and a ~= "" then d.PV = checked(a) end
+-- -- a = specification.invisiblepage if a and a ~= "" then d.PI = checked(a) end
+-- return next(d) and pdfdictionary(d)
+-- end
+
+local mapping = {
+ mousedown = "D", clickin = "D",
+ mouseup = "U", clickout = "U",
+ regionin = "E",
+ regionout = "X",
+ afterkey = "K",
+ format = "F",
+ validate = "V",
+ calculate = "C",
+ focusin = "Fo",
+ focusout = "Bl",
+ openpage = "PO",
+ closepage = "PC",
+ -- visiblepage = "PV",
+ -- invisiblepage = "PI",
+}
+
+local function fieldactions(specification) -- share actions
+ local d = nil
+ for key, target in sortedhash(mapping) do -- sort so that we can compare pdf
+ local code = specification[key]
+ if code and code ~= "" then
+ -- local a = checked(code)
+ local set, bug = references.identify("",code)
+ if not bug and #set > 0 then
+ local a = pdfaction(set) -- r, n
+ if a then
+ local r = pdfshareobjectreference(a)
+ if d then
+ d[target] = r
+ else
+ d = pdfdictionary { [target] = r }
+ end
+ else
+ report_fields("invalid field action %a, case %s",code,2)
+ end
+ else
+ report_fields("invalid field action %a, case %s",code,1)
+ end
+ end
+ end
+ -- if d then
+ -- d = pdfshareobjectreference(d) -- not much overlap or maybe only some patterns
+ -- end
+ return d
+end
+
+-- fonts and color
+
+local pdfdocencodingvector, pdfdocencodingcapsule
+
+-- The pdf doc encoding vector is needed in order to trigger propper unicode. Interesting is that when
+-- a glyph is not in the vector, it is still visible as it is taken from some other font. Messy.
+
+-- To be checked: only when text/line fields.
+
+local function checkpdfdocencoding()
+ report_fields("adding pdfdoc encoding vector")
+ local encoding = dofile(resolvers.findfile("lpdf-enc.lmt")) -- no checking, fatal if not present
+ pdfdocencodingvector = pdfreference(pdfflushobject(encoding))
+ local capsule = pdfdictionary {
+ PDFDocEncoding = pdfdocencodingvector
+ }
+ pdfdocencodingcapsule = pdfreference(pdfflushobject(capsule))
+ checkpdfdocencoding = function() end
+end
+
+local fontnames = {
+ rm = {
+ tf = "Times-Roman",
+ bf = "Times-Bold",
+ it = "Times-Italic",
+ sl = "Times-Italic",
+ bi = "Times-BoldItalic",
+ bs = "Times-BoldItalic",
+ },
+ ss = {
+ tf = "Helvetica",
+ bf = "Helvetica-Bold",
+ it = "Helvetica-Oblique",
+ sl = "Helvetica-Oblique",
+ bi = "Helvetica-BoldOblique",
+ bs = "Helvetica-BoldOblique",
+ },
+ tt = {
+ tf = "Courier",
+ bf = "Courier-Bold",
+ it = "Courier-Oblique",
+ sl = "Courier-Oblique",
+ bi = "Courier-BoldOblique",
+ bs = "Courier-BoldOblique",
+ },
+ symbol = {
+ dingbats = "ZapfDingbats",
+ }
+}
+
+local usedfonts = { }
+
+local function fieldsurrounding(specification)
+ local fontsize = specification.fontsize or "12pt"
+ local fontstyle = specification.fontstyle or "rm"
+ local fontalternative = specification.fontalternative or "tf"
+ local colorvalue = tonumber(specification.colorvalue)
+ local s = fontnames[fontstyle]
+ if not s then
+ fontstyle, s = "rm", fontnames.rm
+ end
+ local a = s[fontalternative]
+ if not a then
+ alternative, a = "tf", s.tf
+ end
+ local tag = fontstyle .. fontalternative
+ fontsize = todimen(fontsize)
+ fontsize = fontsize and (bpfactor * fontsize) or 12
+ fontraise = 0.1 * fontsize -- todo: figure out what the natural one is and compensate for strutdp
+ local fontcode = formatters["%0.4F Tf %0.4F Ts"](fontsize,fontraise)
+ -- we could test for colorvalue being 1 (black) and omit it then
+ local colorcode = pdfcolor(3,colorvalue) -- we force an rgb color space
+ if trace_fields then
+ report_fields("using font, style %a, alternative %a, size %p, tag %a, code %a",fontstyle,fontalternative,fontsize,tag,fontcode)
+ report_fields("using color, value %a, code %a",colorvalue,colorcode)
+ end
+ local stream = pdfstream {
+ pdfconstant(tag),
+ formatters["%s %s"](fontcode,colorcode)
+ }
+ usedfonts[tag] = a -- the name
+ -- move up with "x.y Ts"
+ return tostring(stream)
+end
+
+-- Can we use any font?
+
+codeinjections.fieldsurrounding = fieldsurrounding
+
+local function registerfonts()
+ if next(usedfonts) then
+ checkpdfdocencoding() -- already done
+ local pdffontlist = pdfdictionary()
+ local pdffonttype = pdfconstant("Font")
+ local pdffontsubtype = pdfconstant("Type1")
+ for tag, name in sortedhash(usedfonts) do
+ local f = pdfdictionary {
+ Type = pdffonttype,
+ Subtype = pdffontsubtype,
+ Name = pdfconstant(tag),
+ BaseFont = pdfconstant(name),
+ Encoding = pdfdocencodingvector,
+ }
+ pdffontlist[tag] = pdfreference(pdfflushobject(f))
+ end
+ return pdffontlist
+ end
+end
+
+-- symbols
+
+local function fieldappearances(specification)
+ -- todo: caching
+ local values = specification.values
+ local default = specification.default -- todo
+ if not values then
+ -- error
+ return
+ end
+ local v = settings_to_array(values)
+ local n, r, d
+ if #v == 1 then
+ n, r, d = v[1], v[1], v[1]
+ elseif #v == 2 then
+ n, r, d = v[1], v[1], v[2]
+ else
+ n, r, d = v[1], v[2], v[3]
+ end
+ local appearance = pdfdictionary {
+ N = registeredsymbol(n),
+ R = registeredsymbol(r),
+ D = registeredsymbol(d),
+ }
+ return pdfshareobjectreference(appearance)
+-- return pdfreference(pdfflushobject(appearance))
+end
+
+-- The rendering part of form support has always been crappy and didn't really
+-- improve over time. Did bugs become features? Who knows. Why provide for instance
+-- control over appearance and then ignore it when the mouse clicks someplace else.
+-- Strangely enough a lot of effort went into JavaScript support while basic
+-- appearance control of checkboxes stayed poor. I found this link when googling for
+-- conformation after the n^th time looking into this behaviour:
+--
+-- https://stackoverflow.com/questions/15479855/pdf-appearance-streams-checkbox-not-shown-correctly-after-focus-lost
+--
+-- ... "In particular check boxes, therefore, whenever not interacting with the user, shall
+-- be displayed using their normal captions, not their appearances."
+--
+-- So: don't use check boxes. In fact, even radio buttons can have these funny "flash some
+-- funny symbol" side effect when clocking on them. I tried all combinations if /H and /AP
+-- and /AS and ... Because (afaiks) the acrobat interface assumes that one uses dingbats no
+-- one really cared about getting custom appeances done well. This erratic behaviour might
+-- as well be the reason why no open source viewer ever bothered implementing forms. It's
+-- probably also why most forms out there look kind of bad.
+
+local function fieldstates_precheck(specification)
+ local values = specification.values
+ local default = specification.default
+ if not values or values == "" then
+ return
+ end
+ local yes = settings_to_array(values)[1]
+ local yesshown, yesvalue = lpegmatch(splitter,yes)
+ if not (yesshown and yesvalue) then
+ yesshown = yes
+ end
+ return default == settings_to_array(yesshown)[1] and pdf_yes or pdf_off
+end
+
+local function fieldstates_check(specification)
+ -- we don't use Opt here (too messy for radio buttons)
+ local values = specification.values
+ local default = specification.default
+ if not values or values == "" then
+ -- error
+ return
+ end
+ local v = settings_to_array(values)
+ local yes, off, yesn, yesr, yesd, offn, offr, offd
+ if #v == 1 then
+ yes, off = v[1], v[1]
+ else
+ yes, off = v[1], v[2]
+ end
+ local yesshown, yesvalue = lpegmatch(splitter,yes)
+ if not (yesshown and yesvalue) then
+ yesshown = yes, yes
+ end
+ yes = settings_to_array(yesshown)
+ local offshown, offvalue = lpegmatch(splitter,off)
+ if not (offshown and offvalue) then
+ offshown = off, off
+ end
+ off = settings_to_array(offshown)
+ if #yes == 1 then
+ yesn, yesr, yesd = yes[1], yes[1], yes[1]
+ elseif #yes == 2 then
+ yesn, yesr, yesd = yes[1], yes[1], yes[2]
+ else
+ yesn, yesr, yesd = yes[1], yes[2], yes[3]
+ end
+ if #off == 1 then
+ offn, offr, offd = off[1], off[1], off[1]
+ elseif #off == 2 then
+ offn, offr, offd = off[1], off[1], off[2]
+ else
+ offn, offr, offd = off[1], off[2], off[3]
+ end
+ if not yesvalue then
+ yesvalue = yesdefault or yesn
+ end
+ if not offvalue then
+ offvalue = offn
+ end
+ if default == yesn then
+ default = pdf_yes
+ yesvalue = yesvalue == yesn and "Yes" or "Off"
+ else
+ default = pdf_off
+ yesvalue = "Off"
+ end
+ local appearance
+ -- if false then
+ if true then
+ -- needs testing
+ appearance = pdfdictionary { -- maybe also cache components
+ N = pdfshareobjectreference(pdfdictionary { Yes = registeredsymbol(yesn), Off = registeredsymbol(offn) }),
+ R = pdfshareobjectreference(pdfdictionary { Yes = registeredsymbol(yesr), Off = registeredsymbol(offr) }),
+ D = pdfshareobjectreference(pdfdictionary { Yes = registeredsymbol(yesd), Off = registeredsymbol(offd) }),
+ }
+ else
+ appearance = pdfdictionary { -- maybe also cache components
+ N = pdfdictionary { Yes = registeredsymbol(yesn), Off = registeredsymbol(offn) },
+ R = pdfdictionary { Yes = registeredsymbol(yesr), Off = registeredsymbol(offr) },
+ D = pdfdictionary { Yes = registeredsymbol(yesd), Off = registeredsymbol(offd) }
+ }
+ end
+ local appearanceref = pdfshareobjectreference(appearance)
+ -- local appearanceref = pdfreference(pdfflushobject(appearance))
+ return appearanceref, default, yesvalue
+end
+
+-- It looks like there is always a (MK related) symbol used and that the appearances
+-- are only used as ornaments behind a symbol. So, contrary to what we did when
+-- widgets showed up, we now limit ourself to more dumb definitions. Especially when
+-- highlighting is enabled weird interferences happen. So, we play safe (some nice
+-- code has been removed that worked well till recently).
+
+local function fieldstates_radio(specification,name,parent)
+ local values = values or specification.values
+ local default = default or parent.default -- specification.default
+ if not values or values == "" then
+ -- error
+ return
+ end
+ local v = settings_to_array(values)
+ local yes, off, yesn, yesr, yesd, offn, offr, offd
+ if #v == 1 then
+ yes, off = v[1], v[1]
+ else
+ yes, off = v[1], v[2]
+ end
+ -- yes keys might be the same in the three appearances within a field
+ -- but can best be different among fields ... don't ask why
+ local yessymbols, yesvalue = lpegmatch(splitter,yes) -- n,r,d=>x
+ if not (yessymbols and yesvalue) then
+ yessymbols = yes
+ end
+ if not yesvalue then
+ yesvalue = name
+ end
+ yessymbols = settings_to_array(yessymbols)
+ if #yessymbols == 1 then
+ yesn = yessymbols[1]
+ yesr = yesn
+ yesd = yesr
+ elseif #yessymbols == 2 then
+ yesn = yessymbols[1]
+ yesr = yessymbols[2]
+ yesd = yesr
+ else
+ yesn = yessymbols[1]
+ yesr = yessymbols[2]
+ yesd = yessymbols[3]
+ end
+ -- we don't care about names, as all will be /Off
+ local offsymbols = lpegmatch(splitter,off) or off
+ offsymbols = settings_to_array(offsymbols)
+ if #offsymbols == 1 then
+ offn = offsymbols[1]
+ offr = offn
+ offd = offr
+ elseif #offsymbols == 2 then
+ offn = offsymbols[1]
+ offr = offsymbols[2]
+ offd = offr
+ else
+ offn = offsymbols[1]
+ offr = offsymbols[2]
+ offd = offsymbols[3]
+ end
+ if default == name then
+ default = pdfconstant(name)
+ else
+ default = pdf_off
+ end
+ --
+ local appearance
+ if false then -- needs testing
+ appearance = pdfdictionary { -- maybe also cache components
+ N = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesn), Off = registeredsymbol(offn) }),
+ R = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesr), Off = registeredsymbol(offr) }),
+ D = pdfshareobjectreference(pdfdictionary { [name] = registeredsymbol(yesd), Off = registeredsymbol(offd) }),
+ }
+ else
+ appearance = pdfdictionary { -- maybe also cache components
+ N = pdfdictionary { [name] = registeredsymbol(yesn), Off = registeredsymbol(offn) },
+ R = pdfdictionary { [name] = registeredsymbol(yesr), Off = registeredsymbol(offr) },
+ D = pdfdictionary { [name] = registeredsymbol(yesd), Off = registeredsymbol(offd) }
+ }
+ end
+ local appearanceref = pdfshareobjectreference(appearance) -- pdfreference(pdfflushobject(appearance))
+ return appearanceref, default, yesvalue
+end
+
+local function fielddefault(field,pdf_yes)
+ local default = field.default
+ if not default or default == "" then
+ local values = settings_to_array(field.values)
+ default = values[1]
+ end
+ if not default or default == "" then
+ return pdf_off
+ else
+ return pdf_yes or pdfconstant(default)
+ end
+end
+
+local function fieldoptions(specification)
+ local values = specification.values
+ local default = specification.default
+ if values then
+ local v = settings_to_array(values)
+ for i=1,#v do
+ local vi = v[i]
+ local shown, value = lpegmatch(splitter,vi)
+ if shown and value then
+ v[i] = pdfarray { pdfunicode(value), shown }
+ else
+ v[i] = pdfunicode(v[i])
+ end
+ end
+ return pdfarray(v)
+ end
+end
+
+local mapping = {
+ -- acrobat compliant (messy, probably some pdfdoc encoding interference here)
+ check = "4", -- 0x34
+ circle = "l", -- 0x6C
+ cross = "8", -- 0x38
+ diamond = "u", -- 0x75
+ square = "n", -- 0x6E
+ star = "H", -- 0x48
+}
+
+local function todingbat(n)
+ if n and n ~= "" then
+ return mapping[n] or ""
+ end
+end
+
+local function fieldrendering(specification)
+ local bvalue = tonumber(specification.backgroundcolorvalue)
+ local fvalue = tonumber(specification.framecolorvalue)
+ local svalue = specification.fontsymbol
+ if bvalue or fvalue or (svalue and svalue ~= "") then
+ return pdfdictionary {
+ BG = bvalue and pdfarray { pdfcolorvalues(3,bvalue) } or nil, -- or zero_bg,
+ BC = fvalue and pdfarray { pdfcolorvalues(3,fvalue) } or nil, -- or zero_bc,
+ CA = svalue and pdfstring (svalue) or nil,
+ }
+ end
+end
+
+-- layers
+
+local function fieldlayer(specification) -- we can move this in line
+ local layer = specification.layer
+ return (layer and pdflayerreference(layer)) or nil
+end
+
+-- defining
+
+local fields, radios, clones, fieldsets, calculationset = { }, { }, { }, { }, nil
+
+local xfdftemplate = [[
+<?xml version='1.0' encoding='UTF-8'?>
+
+<xfdf xmlns='http://ns.adobe.com/xfdf/'>
+ <f href='%s.pdf'/>
+ <fields>
+%s
+ </fields>
+</xfdf>
+]]
+
+function codeinjections.exportformdata(name)
+ local result = { }
+ for k, v in sortedhash(fields) do
+ result[#result+1] = formatters[" <field name='%s'><value>%s</value></field>"](v.name or k,v.default or "")
+ end
+ local base = file.basename(tex.jobname)
+ local xfdf = format(xfdftemplate,base,table.concat(result,"\n"))
+ if not name or name == "" then
+ name = base
+ end
+ io.savedata(file.addsuffix(name,"xfdf"),xfdf)
+end
+
+function codeinjections.definefieldset(tag,list)
+ fieldsets[tag] = list
+end
+
+function codeinjections.getfieldset(tag)
+ return fieldsets[tag]
+end
+
+local function fieldsetlist(tag)
+ if tag then
+ local ft = fieldsets[tag]
+ if ft then
+ local a = pdfarray()
+ for name in gmatch(list,"[^, ]+") do
+ local f = field[name]
+ if f and f.pobj then
+ a[#a+1] = pdfreference(f.pobj)
+ end
+ end
+ return a
+ end
+ end
+end
+
+function codeinjections.setfieldcalculationset(tag)
+ calculationset = tag
+end
+
+interfaces.implement {
+ name = "setfieldcalculationset",
+ actions = codeinjections.setfieldcalculationset,
+ arguments = "string",
+}
+
+local function predefinesymbols(specification)
+ local values = specification.values
+ if values then
+ local symbols = settings_to_array(values)
+ for i=1,#symbols do
+ local symbol = symbols[i]
+ local a, b = lpegmatch(splitter,symbol)
+ codeinjections.presetsymbol(a or symbol)
+ end
+ end
+end
+
+function codeinjections.getdefaultfieldvalue(name)
+ local f = fields[name]
+ if f then
+ local values = f.values
+ local default = f.default
+ if not default or default == "" then
+ local symbols = settings_to_array(values)
+ local symbol = symbols[1]
+ if symbol then
+ local a, b = lpegmatch(splitter,symbol) -- splits at =>
+ default = a or symbol
+ end
+ end
+ return default
+ end
+end
+
+function codeinjections.definefield(specification)
+ local n = specification.name
+ local f = fields[n]
+ if not f then
+ local fieldtype = specification.type
+ if not fieldtype then
+ if trace_fields then
+ report_fields("invalid definition for %a, unknown type",n)
+ end
+ elseif fieldtype == "radio" then
+ local values = specification.values
+ if values and values ~= "" then
+ values = settings_to_array(values)
+ for v=1,#values do
+ radios[values[v]] = { parent = n }
+ end
+ fields[n] = specification
+ if trace_fields then
+ report_fields("defining %a as type %a",n,"radio")
+ end
+ elseif trace_fields then
+ report_fields("invalid definition of radio %a, missing values",n)
+ end
+ elseif fieldtype == "sub" then
+ -- not in main field list !
+ local radio = radios[n]
+ if radio then
+ -- merge specification
+ for key, value in next, specification do
+ radio[key] = value
+ end
+ if trace_fields then
+ local p = radios[n] and radios[n].parent
+ report_fields("defining %a as type sub of radio %a",n,p)
+ end
+ elseif trace_fields then
+ report_fields("invalid definition of radio sub %a, no parent given",n)
+ end
+ predefinesymbols(specification)
+ elseif fieldtype == "text" or fieldtype == "line" then
+ fields[n] = specification
+ if trace_fields then
+ report_fields("defining %a as type %a",n,fieldtype)
+ end
+ if specification.values ~= "" and specification.default == "" then
+ specification.default, specification.values = specification.values, nil
+ end
+ else
+ fields[n] = specification
+ if trace_fields then
+ report_fields("defining %a as type %a",n,fieldtype)
+ end
+ predefinesymbols(specification)
+ end
+ elseif trace_fields then
+ report_fields("invalid definition for %a, already defined",n)
+ end
+end
+
+function codeinjections.clonefield(specification) -- obsolete
+ local p = specification.parent
+ local c = specification.children
+ local v = specification.alternative
+ if not p or not c then
+ if trace_fields then
+ report_fields("invalid clone, children %a, parent %a, alternative %a",c,p,v)
+ end
+ return
+ end
+ local x = fields[p] or radios[p]
+ if not x then
+ if trace_fields then
+ report_fields("invalid clone, unknown parent %a",p)
+ end
+ return
+ end
+ for n in gmatch(c,"[^, ]+") do
+ local f, r, c = fields[n], radios[n], clones[n]
+ if f or r or c then
+ if trace_fields then
+ report_fields("already cloned, child %a, parent %a, alternative %a",n,p,v)
+ end
+ else
+ if trace_fields then
+ report_fields("cloning, child %a, parent %a, alternative %a",n,p,v)
+ end
+ clones[n] = specification
+ predefinesymbols(specification)
+ end
+ end
+end
+
+function codeinjections.getfieldcategory(name)
+ local f = fields[name] or radios[name] or clones[name]
+ if f then
+ local g = f.category
+ if not g or g == "" then
+ local v, p, t = f.alternative, f.parent, f.type
+ if v == "clone" or v == "copy" then
+ f = fields[p] or radios[p]
+ g = f and f.category
+ elseif t == "sub" then
+ f = fields[p]
+ g = f and f.category
+ end
+ end
+ return g
+ end
+end
+
+--
+
+function codeinjections.validfieldcategory(name)
+ return fields[name] or radios[name] or clones[name]
+end
+
+function codeinjections.validfieldset(name)
+ return fieldsets[tag]
+end
+
+function codeinjections.validfield(name)
+ return fields[name]
+end
+
+--
+
+local alignments = {
+ flushleft = 0, right = 0,
+ center = 1, middle = 1,
+ flushright = 2, left = 2,
+}
+
+local function fieldalignment(specification)
+ return alignments[specification.align] or 0
+end
+
+local function enhance(specification,option)
+ local so = specification.option
+ if so and so ~= "" then
+ specification.option = so .. "," .. option
+ else
+ specification.option = option
+ end
+ return specification
+end
+
+-- finish (if we also collect parents we can inline the kids which is
+-- more efficient ... but hardly anyone used widgets so ...)
+
+local collected = pdfarray()
+local forceencoding = false
+
+-- todo : check #opt
+
+local function finishfields()
+ local sometext = forceencoding
+ local somefont = next(usedfonts)
+ for name, field in sortedhash(fields) do
+ local kids = field.kids
+ if kids then
+ pdfflushobject(field.kidsnum,kids)
+ end
+ local opt = field.opt
+ if opt then
+ pdfflushobject(field.optnum,opt)
+ end
+ local type = field.type
+ if not sometext and (type == "text" or type == "line") then
+ sometext = true
+ end
+ end
+ for name, field in sortedhash(radios) do
+ local kids = field.kids
+ if kids then
+ pdfflushobject(field.kidsnum,kids)
+ end
+ local opt = field.opt
+ if opt then
+ pdfflushobject(field.optnum,opt)
+ end
+ end
+ if #collected > 0 then
+ local acroform = pdfdictionary {
+ NeedAppearances = pdfmajorversion() == 1 or nil,
+ Fields = pdfreference(pdfflushobject(collected)),
+ CO = fieldsetlist(calculationset),
+ }
+ if sometext or somefont then
+ checkpdfdocencoding()
+ if sometext then
+ usedfonts.tttf = fontnames.tt.tf
+ acroform.DA = "/tttf 12 Tf 0 g"
+ end
+ acroform.DR = pdfdictionary {
+ Font = registerfonts(),
+ Encoding = pdfdocencodingcapsule,
+ }
+ end
+ -- maybe:
+ -- if sometext then
+ -- checkpdfdocencoding()
+ -- if sometext then
+ -- usedfonts.tttf = fontnames.tt.tf
+ -- acroform.DA = "/tttf 12 Tf 0 g"
+ -- end
+ -- acroform.DR = pdfdictionary {
+ -- Font = registerfonts(),
+ -- Encoding = pdfdocencodingcapsule,
+ -- }
+ -- elseif somefont then
+ -- acroform.DR = pdfdictionary {
+ -- Font = registerfonts(),
+ -- }
+ -- end
+ lpdf.addtocatalog("AcroForm",pdfreference(pdfflushobject(acroform)))
+ end
+end
+
+lpdf.registerdocumentfinalizer(finishfields,"form fields")
+
+local methods = { }
+
+function nodeinjections.typesetfield(name,specification)
+ local field = fields[name] or radios[name] or clones[name]
+ if not field then
+ report_fields( "unknown child %a",name)
+ -- unknown field
+ return
+ end
+ local alternative, parent = field.alternative, field.parent
+ if alternative == "copy" or alternative == "clone" then -- only in clones
+ field = fields[parent] or radios[parent]
+ end
+ local method = methods[field.type]
+ if method then
+ return method(name,specification,alternative)
+ else
+ report_fields( "unknown method %a for child %a",field.type,name)
+ end
+end
+
+local function save_parent(field,specification,d)
+ local kidsnum = pdfreserveobject()
+ d.Kids = pdfreference(kidsnum)
+ field.kidsnum = kidsnum
+ field.kids = pdfarray()
+-- if d.Opt then
+-- local optnum = pdfreserveobject()
+-- d.Opt = pdfreference(optnum)
+-- field.optnum = optnum
+-- field.opt = pdfarray()
+-- end
+ local pnum = pdfflushobject(d)
+ field.pobj = pnum
+ collected[#collected+1] = pdfreference(pnum)
+end
+
+local function save_kid(field,specification,d,optname)
+ local kn = pdfreserveobject()
+ field.kids[#field.kids+1] = pdfreference(kn)
+-- if optname then
+-- local opt = field.opt
+-- if opt then
+-- opt[#opt+1] = optname
+-- end
+-- end
+ local width = specification.width or 0
+ local height = specification.height or 0
+ local depth = specification.depth or 0
+ local box = hpack_node(nodeinjections.annotation(width,height,depth,d(),kn))
+ -- redundant
+ box.width = width
+ box.height = height
+ box.depth = depth
+ return box
+end
+
+local function makelineparent(field,specification)
+ local text = pdfunicode(field.default)
+ local length = tonumber(specification.length or 0) or 0
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ T = pdfunicode(specification.title),
+ F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ OC = fieldlayer(specification),
+ DA = fieldsurrounding(specification),
+ AA = fieldactions(specification),
+ FT = pdf_tx,
+ Q = fieldalignment(specification),
+ MaxLen = length == 0 and 1000 or length,
+ DV = text,
+ V = text,
+ }
+ save_parent(field,specification,d)
+end
+
+local function makelinechild(name,specification)
+ local field = clones[name]
+ local parent = nil
+ if field then
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("forcing parent text %a",parent.name)
+ end
+ makelineparent(parent,specification)
+ end
+ else
+ parent = fields[name]
+ field = parent
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("using parent text %a",name)
+ end
+ makelineparent(parent,specification)
+ end
+ end
+ if trace_fields then
+ report_fields("using child text %a",name)
+ end
+ -- we could save a little by not setting some key/value when it's the
+ -- same as parent but it would cost more memory to keep track of it
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ Parent = pdfreference(parent.pobj),
+ F = fieldplus(specification),
+ OC = fieldlayer(specification),
+ DA = fieldsurrounding(specification),
+ AA = fieldactions(specification),
+ MK = fieldrendering(specification),
+ Q = fieldalignment(specification),
+ }
+ return save_kid(parent,specification,d)
+end
+
+function methods.line(name,specification)
+ return makelinechild(name,specification)
+end
+
+function methods.text(name,specification)
+ return makelinechild(name,enhance(specification,"MultiLine"))
+end
+
+-- copy of line ... probably also needs a /Lock
+
+local function makesignatureparent(field,specification)
+ local text = pdfunicode(field.default)
+ local length = tonumber(specification.length or 0) or 0
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ T = pdfunicode(specification.title),
+ F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ OC = fieldlayer(specification),
+ DA = fieldsurrounding(specification),
+ AA = fieldactions(specification),
+ FT = pdf_sig,
+ Q = fieldalignment(specification),
+ MaxLen = length == 0 and 1000 or length,
+ DV = text,
+ V = text,
+ }
+ save_parent(field,specification,d)
+end
+
+local function makesignaturechild(name,specification)
+ local field = clones[name]
+ local parent = nil
+ if field then
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("forcing parent signature %a",parent.name)
+ end
+ makesignatureparent(parent,specification)
+ end
+ else
+ parent = fields[name]
+ field = parent
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("using parent text %a",name)
+ end
+ makesignatureparent(parent,specification)
+ end
+ end
+ if trace_fields then
+ report_fields("using child text %a",name)
+ end
+ -- we could save a little by not setting some key/value when it's the
+ -- same as parent but it would cost more memory to keep track of it
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ Parent = pdfreference(parent.pobj),
+ F = fieldplus(specification),
+ OC = fieldlayer(specification),
+ DA = fieldsurrounding(specification),
+ AA = fieldactions(specification),
+ MK = fieldrendering(specification),
+ Q = fieldalignment(specification),
+ }
+ return save_kid(parent,specification,d)
+end
+
+function methods.signature(name,specification)
+ return makesignaturechild(name,specification)
+end
+--
+
+local function makechoiceparent(field,specification)
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ T = pdfunicode(specification.title),
+ F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification),
+ FT = pdf_ch,
+ Opt = fieldoptions(field), -- todo
+ }
+ save_parent(field,specification,d)
+end
+
+local function makechoicechild(name,specification)
+ local field = clones[name]
+ local parent = nil
+ if field then
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("forcing parent choice %a",parent.name)
+ end
+ makechoiceparent(parent,specification,extras)
+ end
+ else
+ parent = fields[name]
+ field = parent
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("using parent choice %a",name)
+ end
+ makechoiceparent(parent,specification,extras)
+ end
+ end
+ if trace_fields then
+ report_fields("using child choice %a",name)
+ end
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ Parent = pdfreference(parent.pobj),
+ F = fieldplus(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification),
+ }
+ return save_kid(parent,specification,d) -- do opt here
+end
+
+function methods.choice(name,specification)
+ return makechoicechild(name,specification)
+end
+
+function methods.popup(name,specification)
+ return makechoicechild(name,enhance(specification,"PopUp"))
+end
+
+function methods.combo(name,specification)
+ return makechoicechild(name,enhance(specification,"PopUp,Edit"))
+end
+
+local function makecheckparent(field,specification)
+ local default = fieldstates_precheck(field)
+ local d = pdfdictionary {
+ T = pdfunicode(specification.title), -- todo: when tracing use a string
+ F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification), -- can be shared
+ FT = pdf_btn,
+ V = fielddefault(field,default),
+ }
+ save_parent(field,specification,d)
+end
+
+local function makecheckchild(name,specification)
+ local field = clones[name]
+ local parent = nil
+ if field then
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("forcing parent check %a",parent.name)
+ end
+ makecheckparent(parent,specification,extras)
+ end
+ else
+ parent = fields[name]
+ field = parent
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("using parent check %a",name)
+ end
+ makecheckparent(parent,specification,extras)
+ end
+ end
+ if trace_fields then
+ report_fields("using child check %a",name)
+ end
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ Parent = pdfreference(parent.pobj),
+ F = fieldplus(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification), -- can be shared
+ H = pdf_n,
+ }
+ local fontsymbol = specification.fontsymbol
+ if fontsymbol and fontsymbol ~= "" then
+ specification.fontsymbol = todingbat(fontsymbol)
+ specification.fontstyle = "symbol"
+ specification.fontalternative = "dingbats"
+ d.DA = fieldsurrounding(specification)
+ d.MK = fieldrendering(specification)
+ return save_kid(parent,specification,d)
+ else
+ local appearance, default, value = fieldstates_check(field)
+ d.AS = default
+ d.AP = appearance
+ return save_kid(parent,specification,d)
+ end
+end
+
+function methods.check(name,specification)
+ return makecheckchild(name,specification)
+end
+
+local function makepushparent(field,specification) -- check if we can share with the previous
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ T = pdfunicode(specification.title),
+ F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification), -- can be shared
+ FT = pdf_btn,
+ AP = fieldappearances(field),
+ H = pdf_p,
+ }
+ save_parent(field,specification,d)
+end
+
+local function makepushchild(name,specification)
+ local field, parent = clones[name], nil
+ if field then
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("forcing parent push %a",parent.name)
+ end
+ makepushparent(parent,specification)
+ end
+ else
+ parent = fields[name]
+ field = parent
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("using parent push %a",name)
+ end
+ makepushparent(parent,specification)
+ end
+ end
+ if trace_fields then
+ report_fields("using child push %a",name)
+ end
+ local fontsymbol = specification.fontsymbol
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ Parent = pdfreference(field.pobj),
+ F = fieldplus(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification), -- can be shared
+ H = pdf_p,
+ }
+ if fontsymbol and fontsymbol ~= "" then
+ specification.fontsymbol = todingbat(fontsymbol)
+ specification.fontstyle = "symbol"
+ specification.fontalternative = "dingbats"
+ d.DA = fieldsurrounding(specification)
+ d.MK = fieldrendering(specification)
+ else
+ d.AP = fieldappearances(field)
+ end
+ return save_kid(parent,specification,d)
+end
+
+function methods.push(name,specification)
+ return makepushchild(name,enhance(specification,"PushButton"))
+end
+
+local function makeradioparent(field,specification)
+ specification = enhance(specification,"Radio,RadiosInUnison,Print,NoToggleToOff")
+ local d = pdfdictionary {
+ T = field.name,
+ FT = pdf_btn,
+ -- F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ -- H = pdf_n,
+ V = fielddefault(field),
+ }
+ save_parent(field,specification,d)
+end
+
+-- local function makeradiochild(name,specification)
+-- local field = clones[name]
+-- local parent = nil
+-- local pname = nil
+-- if field then
+-- pname = field.parent
+-- field = radios[pname]
+-- parent = fields[pname]
+-- if not parent.pobj then
+-- if trace_fields then
+-- report_fields("forcing parent radio %a",parent.name)
+-- end
+-- makeradioparent(parent,parent)
+-- end
+-- else
+-- field = radios[name]
+-- if not field then
+-- report_fields("there is some problem with field %a",name)
+-- return nil
+-- end
+-- pname = field.parent
+-- parent = fields[pname]
+-- if not parent.pobj then
+-- if trace_fields then
+-- report_fields("using parent radio %a",name)
+-- end
+-- makeradioparent(parent,parent)
+-- end
+-- end
+-- if trace_fields then
+-- report_fields("using child radio %a with values %a and default %a",name,field.values,field.default)
+-- end
+-- local fontsymbol = specification.fontsymbol
+-- -- fontsymbol = "circle"
+-- local d = pdfdictionary {
+-- Subtype = pdf_widget,
+-- Parent = pdfreference(parent.pobj),
+-- F = fieldplus(specification),
+-- OC = fieldlayer(specification),
+-- AA = fieldactions(specification),
+-- H = pdf_n,
+-- -- H = pdf_p,
+-- -- P = pdfpagereference(true),
+-- }
+-- if fontsymbol and fontsymbol ~= "" then
+-- specification.fontsymbol = todingbat(fontsymbol)
+-- specification.fontstyle = "symbol"
+-- specification.fontalternative = "dingbats"
+-- d.DA = fieldsurrounding(specification)
+-- d.MK = fieldrendering(specification)
+-- return save_kid(parent,specification,d) -- todo: what if no value
+-- else
+-- local appearance, default, value = fieldstates_radio(field,name,fields[pname])
+-- d.AP = appearance
+-- d.AS = default -- /Whatever
+-- return save_kid(parent,specification,d,value)
+-- end
+-- end
+
+local function makeradiochild(name,specification)
+ local field, parent = clones[name], nil
+ if field then
+ field = radios[field.parent]
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("forcing parent radio %a",parent.name)
+ end
+ makeradioparent(parent,parent)
+ end
+ else
+ field = radios[name]
+ if not field then
+ report_fields("there is some problem with field %a",name)
+ return nil
+ end
+ parent = fields[field.parent]
+ if not parent.pobj then
+ if trace_fields then
+ report_fields("using parent radio %a",name)
+ end
+ makeradioparent(parent,parent)
+ end
+ end
+ if trace_fields then
+ report_fields("using child radio %a with values %a and default %a",name,field.values,field.default)
+ end
+ local fontsymbol = specification.fontsymbol
+ -- fontsymbol = "circle"
+ local d = pdfdictionary {
+ Subtype = pdf_widget,
+ Parent = pdfreference(parent.pobj),
+ F = fieldplus(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification),
+ H = pdf_n,
+ }
+ if fontsymbol and fontsymbol ~= "" then
+ specification.fontsymbol = todingbat(fontsymbol)
+ specification.fontstyle = "symbol"
+ specification.fontalternative = "dingbats"
+ d.DA = fieldsurrounding(specification)
+ d.MK = fieldrendering(specification)
+ end
+ local appearance, default, value = fieldstates_radio(field,name,fields[field.parent])
+ d.AP = appearance
+ d.AS = default -- /Whatever
+-- d.MK = pdfdictionary { BC = pdfarray {0}, BG = pdfarray { 1 } }
+d.BS = pdfdictionary { S = pdfconstant("I"), W = 1 }
+ return save_kid(parent,specification,d,value)
+end
+
+function methods.sub(name,specification)
+ return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison"))
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-fmt.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-fmt.lmt
new file mode 100644
index 00000000000..c6a3f25ff5e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-fmt.lmt
@@ -0,0 +1,1020 @@
+if not modules then modules = { } end modules ['lpdf-fmt'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Peter Rolf and Hans Hagen",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- Thanks to Luigi and Steffen for testing.
+
+-- context --directives="backend.format=PDF/X-1a:2001" --trackers=backend.format yourfile
+
+local tonumber = tonumber
+local lower, gmatch, format, find = string.lower, string.gmatch, string.format, string.find
+local concat, serialize, sortedhash = table.concat, table.serialize, table.sortedhash
+
+local trace_format = false trackers.register("backend.format", function(v) trace_format = v end)
+local trace_variables = false trackers.register("backend.variables", function(v) trace_variables = v end)
+
+local report_backend = logs.reporter("backend","profiles")
+
+local backends, lpdf = backends, lpdf
+
+local codeinjections = backends.pdf.codeinjections
+
+local variables = interfaces.variables
+local viewerlayers = attributes.viewerlayers
+local colors = attributes.colors
+local transparencies = attributes.transparencies
+
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfconstant = lpdf.constant
+local pdfreference = lpdf.reference
+local pdfflushobject = lpdf.flushobject
+local pdfstring = lpdf.string
+local pdfverbose = lpdf.verbose
+local pdfflushstreamfileobject = lpdf.flushstreamfileobject
+
+local addtoinfo = lpdf.addtoinfo
+local injectxmpinfo = lpdf.injectxmpinfo
+local insertxmpinfo = lpdf.insertxmpinfo
+
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+--[[
+ Comments by Peter:
+
+ output intent : only one profile per color space (and device class)
+ default color space : (theoretically) several profiles per color space possible
+
+ The default color space profiles define the current gamuts (part of/all the
+ colors we have in the document), while the output intent profile declares the
+ gamut of the output devices (the colors that we get normally a printer or
+ monitor).
+
+ Example:
+
+ I have two RGB pictures (both 'painted' in /DeviceRGB) and I declare sRGB as
+ default color space for one picture and AdobeRGB for the other. As output
+ intent I use ISO_coated_v2_eci.icc.
+
+ If I had more than one output intent profile for the combination CMYK/printer I
+ can't decide which one to use. But it is no problem to use several default color
+ space profiles for the same color space as it's just a different color
+ transformation. The relation between picture and profile is clear.
+]]--
+
+local channels = {
+ gray = 1,
+ grey = 1,
+ rgb = 3,
+ cmyk = 4,
+}
+
+local prefixes = {
+ gray = "DefaultGray",
+ grey = "DefaultGray",
+ rgb = "DefaultRGB",
+ cmyk = "DefaultCMYK",
+}
+
+local formatspecification = nil
+local formatname = nil
+
+-- * correspondent document wide flags (write once) needed for permission tests
+
+-- defaults as mt
+
+local formats = utilities.storage.allocate {
+ version = {
+ external_icc_profiles = 1.4, -- 'p' in name; URL reference of output intent
+ jbig2_compression = 1.4,
+ jpeg2000_compression = 1.5, -- not supported yet
+ nchannel_colorspace = 1.6, -- 'n' in name; n-channel colorspace support
+ open_prepress_interface = 1.3, -- 'g' in name; reference to external graphics
+ optional_content = 1.5,
+ transparency = 1.4,
+ object_compression = 1.5,
+ attachments = 1.7,
+ },
+ default = {
+ pdf_version = 1.7, -- todo: block tex primitive
+ format_name = "default",
+ xmp_file = "lpdf-pdx.xml",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ nchannel_colorspace = true, -- unknown
+ internal_icc_profiles = true, -- controls profile inclusion
+ external_icc_profiles = true, -- controls profile inclusion
+ include_intents = true,
+ open_prepress_interface = true, -- unknown
+ optional_content = true, -- todo: block at lua level
+ transparency = true, -- todo: block at lua level
+ jbig2_compression = true, -- todo: block at lua level (dropped anyway)
+ jpeg2000_compression = true, -- todo: block at lua level (dropped anyway)
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = true,
+ inject_metadata = function()
+ -- nothing
+ end
+ },
+ data = {
+ ["pdf/x-1a:2001"] = {
+ pdf_version = 1.3,
+ format_name = "PDF/X-1a:2001",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ spot_colors = true,
+ internal_icc_profiles = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ addtoinfo("GTS_PDFXVersion","PDF/X-1a:2001")
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'><pdfxid:GTS_PDFXVersion>PDF/X-1a:2001</pdfxid:GTS_PDFXVersion></rdf:Description>",false)
+ end
+ },
+ ["pdf/x-1a:2003"] = {
+ pdf_version = 1.4,
+ format_name = "PDF/X-1a:2003",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ spot_colors = true,
+ internal_icc_profiles = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ addtoinfo("GTS_PDFXVersion","PDF/X-1a:2003")
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'><pdfxid:GTS_PDFXVersion>PDF/X-1a:2003</pdfxid:GTS_PDFXVersion></rdf:Description>",false)
+ end
+ },
+ ["pdf/x-3:2002"] = {
+ pdf_version = 1.3,
+ format_name = "PDF/X-3:2002",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ include_intents = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ addtoinfo("GTS_PDFXVersion","PDF/X-3:2002")
+ end
+ },
+ ["pdf/x-3:2003"] = {
+ pdf_version = 1.4,
+ format_name = "PDF/X-3:2003",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ include_intents = true,
+ jbig2_compression = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ addtoinfo("GTS_PDFXVersion","PDF/X-3:2003")
+ end
+ },
+ ["pdf/x-4"] = {
+ pdf_version = 1.6,
+ format_name = "PDF/X-4",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ include_intents = true,
+ optional_content = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'><pdfxid:GTS_PDFXVersion>PDF/X-4</pdfxid:GTS_PDFXVersion></rdf:Description>",false)
+ insertxmpinfo("xml://rdf:Description/xmpMM:InstanceID","<xmpMM:VersionID>1</xmpMM:VersionID>",false)
+ insertxmpinfo("xml://rdf:Description/xmpMM:InstanceID","<xmpMM:RenditionClass>default</xmpMM:RenditionClass>",false)
+ end
+ },
+ ["pdf/x-4p"] = {
+ pdf_version = 1.6,
+ format_name = "PDF/X-4p",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ external_icc_profiles = true,
+ include_intents = true,
+ optional_content = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfxid='http://www.npes.org/pdfx/ns/id/'><pdfxid:GTS_PDFXVersion>PDF/X-4p</pdfxid:GTS_PDFXVersion></rdf:Description>",false)
+ insertxmpinfo("xml://rdf:Description/xmpMM:InstanceID","<xmpMM:VersionID>1</xmpMM:VersionID>",false)
+ insertxmpinfo("xml://rdf:Description/xmpMM:InstanceID","<xmpMM:RenditionClass>default</xmpMM:RenditionClass>",false)
+ end
+ },
+ ["pdf/x-5g"] = {
+ pdf_version = 1.6,
+ format_name = "PDF/X-5g",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ include_intents = true,
+ open_prepress_interface = true,
+ optional_content = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ -- todo
+ end
+ },
+ ["pdf/x-5pg"] = {
+ pdf_version = 1.6,
+ format_name = "PDF/X-5pg",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ external_icc_profiles = true,
+ include_intents = true,
+ open_prepress_interface = true,
+ optional_content = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ -- todo
+ end
+ },
+ ["pdf/x-5n"] = {
+ pdf_version = 1.6,
+ format_name = "PDF/X-5n",
+ xmp_file = "lpdf-pdx.xml",
+ gts_flag = "GTS_PDFX",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ calibrated_rgb_colors = true,
+ spot_colors = true,
+ cielab_colors = true,
+ internal_icc_profiles = true,
+ include_intents = true,
+ optional_content = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ nchannel_colorspace = true,
+ object_compression = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ -- todo
+ end
+ },
+ ["pdf/a-1a:2005"] = {
+ pdf_version = 1.4,
+ format_name = "pdf/a-1a:2005",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true, -- new: forms are allowed (with limitations); no JS, other restrictions are unknown (TODO)
+ tagging = true, -- new: the only difference to PDF/A-1b
+ internal_icc_profiles = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>1</pdfaid:part><pdfaid:conformance>A</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ ["pdf/a-1b:2005"] = {
+ pdf_version = 1.4,
+ format_name = "pdf/a-1b:2005",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ internal_icc_profiles = true,
+ include_cidsets = true,
+ include_charsets = true,
+ attachments = false,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>1</pdfaid:part><pdfaid:conformance>B</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ -- Only PDF/A Attachments are allowed but we don't check the attachments
+ -- for any quality: they are just blobs.
+ ["pdf/a-2a"] = {
+ pdf_version = 1.7,
+ format_name = "pdf/a-2a",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = true,
+ internal_icc_profiles = true,
+ transparency = true, -- new
+ jbig2_compression = true,
+ jpeg2000_compression = true, -- new
+ object_compression = true, -- new
+ include_cidsets = false,
+ include_charsets = false,
+ attachments = true, -- new
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>2</pdfaid:part><pdfaid:conformance>A</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ ["pdf/a-2b"] = {
+ pdf_version = 1.7,
+ format_name = "pdf/a-2b",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = false,
+ internal_icc_profiles = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = false,
+ include_charsets = false,
+ attachments = true,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>2</pdfaid:part><pdfaid:conformance>B</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ -- This is like the b variant, but it requires Unicode mapping of fonts
+ -- which we do anyway.
+ ["pdf/a-2u"] = {
+ pdf_version = 1.7,
+ format_name = "pdf/a-2u",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = false,
+ internal_icc_profiles = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = false,
+ include_charsets = false,
+ attachments = true,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>2</pdfaid:part><pdfaid:conformance>U</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ -- Any type of attachment is allowed but we don't check the quality
+ -- of them.
+ ["pdf/a-3a"] = {
+ pdf_version = 1.7,
+ format_name = "pdf/a-3a",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = true,
+ internal_icc_profiles = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = false,
+ include_charsets = false,
+ attachments = true,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>3</pdfaid:part><pdfaid:conformance>A</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ ["pdf/a-3b"] = {
+ pdf_version = 1.7,
+ format_name = "pdf/a-3b",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = false,
+ internal_icc_profiles = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = false,
+ include_charsets = false,
+ attachments = true,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>3</pdfaid:part><pdfaid:conformance>B</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ ["pdf/a-3u"] = {
+ pdf_version = 1.7,
+ format_name = "pdf/a-3u",
+ xmp_file = "lpdf-pda.xml",
+ gts_flag = "GTS_PDFA1",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = false,
+ internal_icc_profiles = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = false,
+ include_charsets = false,
+ attachments = true,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>3</pdfaid:part><pdfaid:conformance>U</pdfaid:conformance></rdf:Description>",false)
+ end
+ },
+ ["pdf/ua-1"] = { -- based on PDF/A-3a, but no 'gts_flag'
+ pdf_version = 1.7,
+ format_name = "pdf/ua-1",
+ xmp_file = "lpdf-pua.xml",
+ gray_scale = true,
+ cmyk_colors = true,
+ rgb_colors = true,
+ spot_colors = true,
+ calibrated_rgb_colors = true, -- unknown
+ cielab_colors = true, -- unknown
+ include_intents = true,
+ forms = true,
+ tagging = true,
+ internal_icc_profiles = true,
+ transparency = true,
+ jbig2_compression = true,
+ jpeg2000_compression = true,
+ object_compression = true,
+ include_cidsets = true,
+ include_charsets = true, --- really ?
+ attachments = true,
+ inject_metadata = function()
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/'><pdfaid:part>3</pdfaid:part><pdfaid:conformance>A</pdfaid:conformance></rdf:Description>",false)
+ injectxmpinfo("xml://rdf:RDF","<rdf:Description rdf:about='' xmlns:pdfuaid='http://www.aiim.org/pdfua/ns/id/'><pdfuaid:part>1</pdfuaid:part></rdf:Description>",false)
+ end
+ },
+ }
+}
+
+lpdf.formats = formats -- it does not hurt to have this one visible
+
+local filenames = {
+ "colorprofiles.xml",
+ "colorprofiles.lua",
+}
+
+local function locatefile(filename)
+ local fullname = resolvers.findfile(filename,"icc",1,true)
+ if not fullname or fullname == "" then
+ fullname = resolvers.finders.byscheme("loc",filename) -- could be specific to the project
+ end
+ return fullname or ""
+end
+
+local function loadprofile(name,filename)
+ local profile = false
+ local databases = filename and filename ~= "" and settings_to_array(filename) or filenames
+ for i=1,#databases do
+ local filename = locatefile(databases[i])
+ if filename and filename ~= "" then
+ local suffix = file.suffix(filename)
+ local lname = lower(name)
+ if suffix == "xml" then
+ local xmldata = xml.load(filename) -- no need for caching it
+ if xmldata then
+ profile = xml.filter(xmldata,format('xml://profiles/profile/(info|filename)[lower(text())=="%s"]/../table()',lname))
+ end
+ elseif suffix == "lua" then
+ local luadata = loadfile(filename)
+ luadata = ludata and luadata()
+ if luadata then
+ profile = luadata[name] or luadata[lname] -- hashed
+ if not profile then
+ for i=1,#luadata do
+ local li = luadata[i]
+ if lower(li.info) == lname then -- indexed
+ profile = li
+ break
+ end
+ end
+ end
+ end
+ end
+ if profile then
+ if next(profile) then
+ report_backend("profile specification %a loaded from %a",name,filename)
+ return profile
+ elseif trace_format then
+ report_backend("profile specification %a loaded from %a but empty",name,filename)
+ end
+ return false
+ end
+ end
+ end
+ report_backend("profile specification %a not found in %a",name,concat(filenames, ", "))
+end
+
+local function urls(url)
+ if not url or url == "" then
+ return nil
+ else
+ local u = pdfarray()
+ for url in gmatch(url,"([^, ]+)") do
+ if find(url,"^http") then
+ u[#u+1] = pdfdictionary {
+ FS = pdfconstant("URL"),
+ F = pdfstring(url),
+ }
+ end
+ end
+ return u
+ end
+end
+
+local function profilename(filename)
+ return lower(file.basename(filename))
+end
+
+local internalprofiles = { }
+
+local function handleinternalprofile(s,include)
+ local filename, colorspace = s.filename or "", s.colorspace or ""
+ if filename == "" or colorspace == "" then
+ report_backend("error in internal profile specification: %s",serialize(s,false))
+ else
+ local tag = profilename(filename)
+ local profile = internalprofiles[tag]
+ if not profile then
+ local colorspace = lower(colorspace)
+ if include then
+ -- local fullname = resolvers.findctxfile(filename) or ""
+ local fullname = locatefile(filename)
+ local channel = channels[colorspace] or nil
+ if fullname == "" then
+ report_backend("error, couldn't locate profile %a",filename)
+ elseif not channel then
+ report_backend("error, couldn't resolve channel entry for colorspace %a",colorspace)
+ else
+ profile = pdfflushstreamfileobject(fullname,pdfdictionary{ N = channel },false) -- uncompressed
+ internalprofiles[tag] = profile
+ if trace_format then
+ report_backend("including %a color profile from %a",colorspace,fullname)
+ end
+ end
+ else
+ internalprofiles[tag] = true
+ if trace_format then
+ report_backend("not including %a color profile %a",colorspace,filename)
+ end
+ end
+ end
+ return profile
+ end
+end
+
+local externalprofiles = { }
+
+local function handleexternalprofile(s,include) -- specification (include ignored here)
+ local name, url, filename, checksum, version, colorspace =
+ s.info or s.filename or "", s.url or "", s.filename or "", s.checksum or "", s.version or "", s.colorspace or ""
+ if false then -- somehow leads to invalid pdf
+ local iccprofile = colors.iccprofile(filename)
+ if iccprofile then
+ name = name ~= "" and name or iccprofile.tags.desc.cleaned or ""
+ url = url ~= "" and url or iccprofile.tags.dmnd.cleaned or ""
+ checksum = checksum ~= "" and checksum or file.checksum(iccprofile.fullname) or ""
+ version = version ~= "" and version or iccprofile.header.version or ""
+ colorspace = colorspace ~= "" and colorspace or iccprofile.header.colorspace or ""
+ end
+ -- table.print(iccprofile)
+ end
+ if name == "" or url == "" or checksum == "" or version == "" or colorspace == "" or filename == "" then
+ local profile = handleinternalprofile(s)
+ if profile then
+ report_backend("incomplete external profile specification, falling back to internal")
+ else
+ report_backend("error in external profile specification: %s",serialize(s,false))
+ end
+ else
+ local tag = profilename(filename)
+ local profile = externalprofiles[tag]
+ if not profile then
+ local d = pdfdictionary {
+ ProfileName = name, -- not file name!
+ ProfileCS = colorspace,
+ URLs = urls(url), -- array containing at least one URL
+ CheckSum = pdfverbose { "<", lower(checksum), ">" }, -- 16byte MD5 hash
+ ICCVersion = pdfverbose { "<", version, ">" }, -- bytes 8..11 from the header of the ICC profile, as a hex string
+ }
+ profile = pdfflushobject(d)
+ externalprofiles[tag] = profile
+ end
+ return profile
+ end
+end
+
+local loadeddefaults = { }
+
+local function handledefaultprofile(s,spec) -- specification
+ local filename, colorspace = s.filename or "", lower(s.colorspace or "")
+ if filename == "" or colorspace == "" then
+ report_backend("error in default profile specification: %s",serialize(s,false))
+ elseif not loadeddefaults[colorspace] then
+ local tag = profilename(filename)
+ local n = internalprofiles[tag] -- or externalprofiles[tag]
+ if n == true then -- not internalized
+ report_backend("no default profile %a for colorspace %a",filename,colorspace)
+ elseif n then
+ local a = pdfarray {
+ pdfconstant("ICCBased"),
+ pdfreference(n),
+ }
+ -- used in page /Resources, so this must be inserted at runtime
+ lpdf.adddocumentcolorspace(prefixes[colorspace],pdfreference(pdfflushobject(a)))
+ loadeddefaults[colorspace] = true
+ report_backend("setting %a as default %a color space",filename,colorspace)
+ else
+ report_backend("no default profile %a for colorspace %a",filename,colorspace)
+ end
+ elseif trace_format then
+ report_backend("a default %a colorspace is already in use",colorspace)
+ end
+end
+
+local loadedintents = { }
+local intents = pdfarray()
+
+local function handleoutputintent(s,spec)
+ local url = s.url or ""
+ local filename = s.filename or ""
+ local name = s.info or filename
+ local id = s.id or ""
+ local outputcondition = s.outputcondition or ""
+ local info = s.info or ""
+ if name == "" or id == "" then
+ report_backend("error in output intent specification: %s",serialize(s,false))
+ elseif not loadedintents[name] then
+ local tag = profilename(filename)
+ local internal, external = internalprofiles[tag], externalprofiles[tag]
+ if internal or external then
+ local d = {
+ Type = pdfconstant("OutputIntent"),
+ S = pdfconstant(spec.gts_flag or "GTS_PDFX"),
+ OutputConditionIdentifier = id,
+ RegistryName = url,
+ OutputCondition = outputcondition,
+ Info = info,
+ }
+ if internal and internal ~= true then
+ d.DestOutputProfile = pdfreference(internal)
+ elseif external and external ~= true then
+ d.DestOutputProfileRef = pdfreference(external)
+ else
+ report_backend("omitting reference to profile for intent %a",name)
+ end
+ intents[#intents+1] = pdfreference(pdfflushobject(pdfdictionary(d)))
+ if trace_format then
+ report_backend("setting output intent to %a with id %a for entry %a",name,id,#intents)
+ end
+ else
+ report_backend("invalid output intent %a",name)
+ end
+ loadedintents[name] = true
+ elseif trace_format then
+ report_backend("an output intent with name %a is already in use",name)
+ end
+end
+
+local function handleiccprofile(message,spec,name,filename,how,options,alwaysinclude,gts_flag)
+ if name and name ~= "" then
+ local list = settings_to_array(name)
+ for i=1,#list do
+ local name = list[i]
+ local profile = loadprofile(name,filename)
+ if trace_format then
+ report_backend("handling %s %a",message,name)
+ end
+ if profile then
+ if formatspecification.cmyk_colors then
+ profile.colorspace = profile.colorspace or "CMYK"
+ else
+ profile.colorspace = profile.colorspace or "RGB"
+ end
+ local external = formatspecification.external_icc_profiles
+ local internal = formatspecification.internal_icc_profiles
+ local include = formatspecification.include_intents
+ local always, never = options[variables.always], options[variables.never]
+ if always or alwaysinclude then
+ if trace_format then
+ report_backend("forcing internal profiles") -- can make preflight unhappy
+ end
+ -- internal, external = true, false
+ internal, external = not never, false
+ elseif never then
+ if trace_format then
+ report_backend("forcing external profiles") -- can make preflight unhappy
+ end
+ internal, external = false, true
+ end
+ if external then
+ if trace_format then
+ report_backend("handling external profiles cf. %a",name)
+ end
+ handleexternalprofile(profile,false)
+ else
+ if trace_format then
+ report_backend("handling internal profiles cf. %a",name)
+ end
+ if internal then
+ handleinternalprofile(profile,always or include)
+ else
+ report_backend("no profile inclusion for %a",formatname)
+ end
+ end
+ how(profile,spec)
+ elseif trace_format then
+ report_backend("unknown profile %a",name)
+ end
+ end
+ end
+end
+
+local function flushoutputintents()
+ if #intents > 0 then
+ lpdf.addtocatalog("OutputIntents",pdfreference(pdfflushobject(intents)))
+ end
+end
+
+lpdf.registerdocumentfinalizer(flushoutputintents,2,"output intents")
+
+function codeinjections.setformat(s)
+ local format = s.format or ""
+ local level = tonumber(s.level)
+ local intent = s.intent or ""
+ local profile = s.profile or ""
+ local option = s.option or ""
+ local filename = s.file or ""
+ if format ~= "" then
+ local spec = formats.data[lower(format)]
+ if spec then
+ formatspecification = spec
+ formatname = spec.format_name
+ report_backend("setting format to %a",formatname)
+ local xmp_file = formatspecification.xmp_file or ""
+ if xmp_file == "" then
+ -- weird error
+ else
+ codeinjections.setxmpfile(xmp_file)
+ end
+ if not level then
+ level = 3 -- good compromise, default anyway
+ end
+ local pdf_version = spec.pdf_version * 10
+ local inject_metadata = spec.inject_metadata
+ local majorversion = math.floor(math.div(pdf_version,10))
+ local minorversion = math.floor(math.mod(pdf_version,10))
+ local objectcompression = spec.object_compression and pdf_version >= 15
+ local compresslevel = level or lpdf.compresslevel() -- keep default
+ local objectcompresslevel = (objectcompression and (level or lpdf.objectcompresslevel())) or 0
+ lpdf.setcompression(compresslevel,objectcompresslevel)
+ lpdf.setversion(majorversion,minorversion)
+ if objectcompression then
+ report_backend("forcing pdf version %s.%s, compression level %s, object compression level %s",
+ majorversion,minorversion,compresslevel,objectcompresslevel)
+ elseif compresslevel > 0 then
+ report_backend("forcing pdf version %s.%s, compression level %s, object compression disabled",
+ majorversion,minorversion,compresslevel)
+ else
+ report_backend("forcing pdf version %s.%s, compression disabled",
+ majorversion,minorversion)
+ end
+ --
+ -- cid sets can always omitted now, but those validators still complain so let's
+ -- for a while keep it (for luigi):
+ --
+ lpdf.setomitcidset (formatspecification.include_cidsets == false and 1 or 0) -- why a number
+ lpdf.setomitcharset(formatspecification.include_charsets == false and 1 or 0) -- why a number
+ --
+ -- maybe block by pdf version
+ --
+ codeinjections.settaggingsupport(formatspecification.tagging)
+ codeinjections.setattachmentsupport(formatspecification.attachments)
+ --
+ -- context.setupcolors { -- not this way
+ -- cmyk = spec.cmyk_colors and variables.yes or variables.no,
+ -- rgb = spec.rgb_colors and variables.yes or variables.no,
+ -- }
+ --
+ colors.forcesupport(
+ spec.gray_scale or false,
+ spec.rgb_colors or false,
+ spec.cmyk_colors or false,
+ spec.spot_colors or false,
+ spec.nchannel_colorspace or false
+ )
+ transparencies.forcesupport(
+ spec.transparency or false
+ )
+ viewerlayers.forcesupport(
+ spec.optional_content or false
+ )
+ viewerlayers.setfeatures(
+ spec.has_order or false -- new
+ )
+ --
+ -- spec.jbig2_compression : todo, block in image inclusion
+ -- spec.jpeg2000_compression : todo, block in image inclusion
+ --
+ if type(inject_metadata) == "function" then
+ inject_metadata()
+ end
+ local options = settings_to_hash(option)
+ handleiccprofile("color profile",spec,profile,filename,handledefaultprofile,options,true)
+ handleiccprofile("output intent",spec,intent,filename,handleoutputintent,options,false)
+ if trace_variables then
+ for k, v in sortedhash(formats.default) do
+ local v = formatspecification[k]
+ if type(v) ~= "function" then
+ report_backend("%a = %a",k,v or false)
+ end
+ end
+ end
+ function codeinjections.setformat(noname)
+ if trace_format then
+ report_backend("error, format is already set to %a, ignoring %a",formatname,noname.format)
+ end
+ end
+ else
+ report_backend("error, format %a is not supported",format)
+ end
+ elseif level then
+ lpdf.setcompression(level,level)
+ else
+ -- we ignore this as we hook it in \everysetupbackend
+ end
+end
+
+directives.register("backend.format", function(v) -- table !
+ local tv = type(v)
+ if tv == "table" then
+ codeinjections.setformat(v)
+ elseif tv == "string" then
+ codeinjections.setformat { format = v }
+ end
+end)
+
+interfaces.implement {
+ name = "setformat",
+ actions = codeinjections.setformat,
+ arguments = { { "*" } }
+}
+
+function codeinjections.getformatoption(key)
+ return formatspecification and formatspecification[key]
+end
+
+-- function codeinjections.getformatspecification()
+-- return formatspecification
+-- end
+
+function codeinjections.supportedformats()
+ local t = { }
+ for k, v in sortedhash(formats.data) do
+ t[#t+1] = k
+ end
+ return t
+end
+
+-- The following is somewhat cleaner but then we need to flag that there are
+-- color spaces set so that the page flusher does not optimize the (at that
+-- moment) still empty array away. So, next(d_colorspaces) should then become
+-- a different test, i.e. also on flag. I'll add that when we need more forward
+-- referencing.
+--
+-- local function embedprofile = handledefaultprofile
+--
+-- local function flushembeddedprofiles()
+-- for colorspace, filename in next, defaults do
+-- embedprofile(colorspace,filename)
+-- end
+-- end
+--
+-- local function handledefaultprofile(s)
+-- defaults[lower(s.colorspace)] = s.filename
+-- end
+--
+-- lpdf.registerdocumentfinalizer(flushembeddedprofiles,1,"embedded color profiles")
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-fnt.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-fnt.lmt
new file mode 100644
index 00000000000..ee16303b060
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-fnt.lmt
@@ -0,0 +1,194 @@
+if not modules then modules = { } end modules ['lpdf-fnt'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code.
+
+local match, gmatch = string.match, string.gmatch
+local tonumber, rawget = tonumber, rawget
+
+local pdfe = lpdf.epdf
+local pdfreference = lpdf.reference
+
+local pdfreserveobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+end)
+
+local tobemerged = { }
+local trace_merge = false trackers.register("graphics.fonts",function(v) trace_merge = v end)
+local report_merge = logs.reporter("graphics","fonts")
+
+local function register(usedname,cleanname)
+ local cleanname = cleanname or fonts.names.cleanname(usedname)
+ local fontid = fonts.definers.internal { name = cleanname }
+ if fontid then
+ local objref = pdfreserveobject()
+ if trace_merge then
+ report_merge("registering %a with name %a, id %a and object %a",usedname,cleanname,fontid,objref)
+ end
+ return {
+ id = fontid,
+ reference = objref,
+ indices = { },
+ cleanname = cleanname,
+ }
+ end
+ return false
+end
+
+function lpdf.registerfont(usedname,cleanname)
+ local v = register(usedname,cleanname)
+ tobemerged[usedname] = v
+ return v
+end
+
+table.setmetatableindex(tobemerged,function(t,k)
+ return lpdf.registerfont(k)
+end)
+
+local function finalizefont(v)
+ local indextoslot = fonts.helpers.indextoslot
+ if v then
+ local id = v.id
+ local n = 0
+ for i in next, v.indices do
+ local u = indextoslot(id,i)
+ n = n + 1
+ end
+ v.n = n
+ end
+end
+
+statistics.register("merged fonts", function()
+ if next(tobemerged) then
+ local t = { }
+ for k, v in table.sortedhash(tobemerged) do
+ t[#t+1] = string.formatters["%s (+%i)"](k,v.n)
+ end
+ return table.concat(t," ")
+ end
+end)
+
+function lpdf.finalizefonts()
+ for k, v in next, tobemerged do
+ finalizefont(v)
+ end
+end
+
+callback.register("font_descriptor_objnum_provider",function(name)
+ local m = rawget(tobemerged,name)
+ if m then
+ -- finalizefont(m)
+ local r = m.reference or 0
+ if trace_merge then
+ report_merge("using object %a for font descriptor of %a",r,name)
+ end
+ return r
+ end
+ return 0
+end)
+
+local function getunicodes1(str,indices)
+ for s in gmatch(str,"beginbfrange%s*(.-)%s*endbfrange") do
+ for first, last, offset in gmatch(s,"<([^>]+)>%s+<([^>]+)>%s+<([^>]+)>") do
+ for i=tonumber(first,16),tonumber(last,16) do
+ indices[i] = true
+ end
+ end
+ end
+ for s in gmatch(str,"beginbfchar%s*(.-)%s*endbfchar") do
+ for old, new in gmatch(s,"<([^>]+)>%s+<([^>]+)>") do
+ indices[tonumber(old,16)] = true
+ end
+ end
+end
+
+local function getunicodes2(widths,indices)
+ for i=1,#widths,2 do
+ local start = widths[i]
+ local count = #widths[i+1]
+ if start and count then
+ for i=start,start+count-1 do
+ indices[i] = true
+ end
+ end
+ end
+end
+
+local function checkedfonts(pdfdoc,xref,copied,page)
+ local list = page.Resources.Font
+ local done = { }
+ for k, somefont in pdfe.expanded(list) do
+ if somefont.Subtype == "Type0" and somefont.Encoding == "Identity-H" then
+ local descendants = somefont.DescendantFonts
+ if descendants then
+ for i=1,#descendants do
+ local d = descendants[i]
+ if d then
+ local subtype = d.Subtype
+ if subtype == "CIDFontType0" or subtype == "CIDFontType2" then
+ local basefont = somefont.BaseFont
+ if basefont then
+ local fontname = match(basefont,"^[A-Z]+%+(.+)$")
+ local fontdata = tobemerged[fontname]
+ if fontdata then
+ local descriptor = d.FontDescriptor
+ if descriptor then
+ local okay = false
+ local widths = d.W
+ if widths then
+ getunicodes2(widths,fontdata.indices)
+ okay = true
+ else
+ local tounicode = somefont.ToUnicode
+ if tounicode then
+ getunicodes1(tounicode(),fontdata.indices)
+ okay = true
+ end
+ end
+ if okay then
+ local r = xref[descriptor]
+ done[r] = fontdata.reference
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return next(done) and done
+end
+
+function lpdf.epdf.plugin(pdfdoc,xref,copied,page) -- needs checking
+ local done = checkedfonts(pdfdoc,xref,copied,page)
+ if done then
+ return {
+ FontDescriptor = function(xref,copied,object,key,value,copyobject)
+ local r = value[3]
+ local d = done[r]
+ if d then
+ return pdfreference(d)
+ else
+ return copyobject(xref,copied,object,key,value)
+ end
+ end
+ }
+ end
+end
+
+lpdf.registerdocumentfinalizer(lpdf.finalizefonts)
+
+-- already defined in font-ocl but the context variatn will go here
+--
+-- function lpdf.vfimage(wd,ht,dp,data,name)
+-- return { "image", { filename = name, width = wd, height = ht, depth = dp } }
+-- end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-grp.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-grp.lmt
new file mode 100644
index 00000000000..6adbe8c3c91
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-grp.lmt
@@ -0,0 +1,302 @@
+if not modules then modules = { } end modules ['lpdf-grp'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, tonumber = type, tonumber
+local formatters, gsub = string.formatters, string.gsub
+local concat = table.concat
+local round = math.round
+
+local backends, lpdf = backends, lpdf
+
+local nodeinjections = backends.pdf.nodeinjections
+
+local colors = attributes.colors
+local basepoints = number.dimenfactors.bp
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+local registrations = backends.pdf.registrations
+
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfconstant = lpdf.constant
+local pdfboolean = lpdf.boolean
+local pdfreference = lpdf.reference
+
+local pdfflushobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+end)
+
+local createimage = images.create
+local wrapimage = images.wrap
+local embedimage = images.embed
+
+-- can also be done indirectly:
+--
+-- 12 : << /AntiAlias false /ColorSpace 8 0 R /Coords [ 0.0 0.0 1.0 0.0 ] /Domain [ 0.0 1.0 ] /Extend [ true true ] /Function 22 0 R /ShadingType 2 >>
+-- 22 : << /Bounds [ ] /Domain [ 0.0 1.0 ] /Encode [ 0.0 1.0 ] /FunctionType 3 /Functions [ 31 0 R ] >>
+-- 31 : << /C0 [ 1.0 0.0 ] /C1 [ 0.0 1.0 ] /Domain [ 0.0 1.0 ] /FunctionType 2 /N 1.0 >>
+
+local function shade(stype,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
+ local func = nil
+ --
+ -- domain has to be consistently added in all dictionaries here otherwise
+ -- acrobat fails with a drawing error
+ --
+ domain = pdfarray(domain)
+ n = tonumber(n)
+ --
+ if steps then
+ local list = pdfarray()
+ local bounds = pdfarray()
+ local encode = pdfarray()
+ for i=1,steps do
+ if i < steps then
+ bounds[i] = fractions[i] or 1
+ end
+ encode[2*i-1] = 0
+ encode[2*i] = 1
+ list [i] = pdfdictionary {
+ FunctionType = 2,
+ Domain = domain,
+ C0 = pdfarray(color_a[i]),
+ C1 = pdfarray(color_b[i]),
+ N = n,
+ }
+ end
+ func = pdfdictionary {
+ FunctionType = 3,
+ Bounds = bounds,
+ Encode = encode,
+ Functions = list,
+ Domain = domain,
+ }
+ else
+ func = pdfdictionary {
+ FunctionType = 2,
+ Domain = domain,
+ C0 = pdfarray(color_a),
+ C1 = pdfarray(color_b),
+ N = n,
+ }
+ end
+ separation = separation and registrations.getspotcolorreference(separation)
+ local s = pdfdictionary {
+ ShadingType = stype,
+ ColorSpace = separation and pdfreference(separation) or pdfconstant(colorspace),
+ Domain = domain,
+ Function = pdfreference(pdfflushobject(func)),
+ Coords = pdfarray(coordinates),
+ Extend = pdfarray { true, true },
+ AntiAlias = pdfboolean(true),
+ }
+ lpdf.adddocumentshade(name,pdfreference(pdfflushobject(s)))
+end
+
+function lpdf.circularshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
+ shade(3,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
+end
+
+function lpdf.linearshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
+ shade(2,name,domain,color_a,color_b,n,colorspace,coordinates,separation,steps,fractions)
+end
+
+-- inline bitmaps but xform'd
+--
+-- we could derive the colorspace if we strip the data
+-- and divide by x*y
+
+local template = "q BI %s ID %s > EI Q"
+local factor = 72/300
+
+function nodeinjections.injectbitmap(t)
+ -- encoding is ascii hex, no checking here
+ local xresolution, yresolution = t.xresolution or 0, t.yresolution or 0
+ if xresolution == 0 or yresolution == 0 then
+ return -- fatal error
+ end
+ local colorspace = t.colorspace
+ if colorspace ~= "rgb" and colorspace ~= "cmyk" and colorspace ~= "gray" then
+ -- not that efficient but ok
+ local d = gsub(t.data,"[^0-9a-f]","")
+ local b = math.round(#d / (xresolution * yresolution))
+ if b == 2 then
+ colorspace = "gray"
+ elseif b == 6 then
+ colorspace = "rgb"
+ elseif b == 8 then
+ colorspace = "cmyk"
+ end
+ end
+ colorspace = lpdf.colorspaceconstants[colorspace]
+ if not colorspace then
+ return -- fatal error
+ end
+ local d = pdfdictionary {
+ W = xresolution,
+ H = yresolution,
+ CS = colorspace,
+ BPC = 8,
+ F = pdfconstant("AHx"),
+ -- CS = nil,
+ -- BPC = 1,
+ -- IM = true,
+ }
+ -- for some reasons it only works well if we take a 1bp boundingbox
+ local urx, ury = 1/basepoints, 1/basepoints
+ -- urx = (xresolution/300)/basepoints
+ -- ury = (yresolution/300)/basepoints
+ local width, height = t.width or 0, t.height or 0
+ if width == 0 and height == 0 then
+ width = factor * xresolution / basepoints
+ height = factor * yresolution / basepoints
+ elseif width == 0 then
+ width = height * xresolution / yresolution
+ elseif height == 0 then
+ height = width * yresolution / xresolution
+ end
+ local a = pdfdictionary {
+ BBox = pdfarray { 0, 0, urx * basepoints, ury * basepoints }
+ }
+ local image = createimage {
+ stream = formatters[template](d(),t.data),
+ width = width,
+ height = height,
+ bbox = { 0, 0, urx, ury },
+ attr = a(),
+ nobbox = true,
+ }
+ return wrapimage(image)
+end
+
+-- general graphic helpers
+
+function codeinjections.setfigurealternative(data,figure)
+ local request = data.request
+ local display = request.display
+ if display and display ~= "" then
+ local nested = figures.push {
+ name = display,
+ page = request.page,
+ size = request.size,
+ prefix = request.prefix,
+ cache = request.cache,
+ width = request.width,
+ height = request.height,
+ }
+ figures.identify()
+ local displayfigure = figures.check()
+ if displayfigure then
+ -- figure.aform = true
+ embedimage(figure)
+ local a = pdfarray {
+ pdfdictionary {
+ Image = pdfreference(figure.objnum),
+ DefaultForPrinting = true,
+ }
+ }
+ local d = pdfdictionary {
+ Alternates = pdfreference(pdfflushobject(a)),
+ }
+ displayfigure.attr = d()
+ figures.pop()
+ return displayfigure, nested
+ else
+ figures.pop()
+ end
+ end
+end
+
+function codeinjections.getpreviewfigure(request)
+ local figure = figures.initialize(request)
+ if not figure then
+ return
+ end
+ figure = figures.identify(figure)
+ if not (figure and figure.status and figure.status.fullname) then
+ return
+ end
+ figure = figures.check(figure)
+ if not (figure and figure.status and figure.status.fullname) then
+ return
+ end
+ local image = figure.status.private
+ if image then
+ embedimage(image)
+ end
+ return figure
+end
+
+function codeinjections.setfiguremask(data,figure) -- mark
+ local request = data.request
+ local mask = request.mask
+ if mask and mask ~= "" then
+ figures.push {
+ name = mask,
+ page = request.page,
+ size = request.size,
+ prefix = request.prefix,
+ cache = request.cache,
+ width = request.width,
+ height = request.height,
+ }
+ mask = figures.identify()
+ mask = figures.check(mask)
+ if mask then
+ local image = mask.status.private
+ if image then
+ figures.include(mask)
+ embedimage(image)
+ local d = pdfdictionary {
+ Interpolate = false,
+ SMask = pdfreference(mask.status.objectnumber),
+ }
+ figure.attr = d()
+ end
+ end
+ figures.pop()
+ end
+end
+
+-- experimental (q Q is not really needed)
+
+local saveboxresource = tex.boxresources.save
+local nofpatterns = 0
+local f_pattern = formatters["q /Pattern cs /%s scn 0 0 %.6N %.6N re f Q"]
+
+function lpdf.registerpattern(specification)
+ nofpatterns = nofpatterns + 1
+ local d = pdfdictionary {
+ Type = pdfconstant("Pattern"),
+ PatternType = 1,
+ PaintType = 1,
+ TilingType = 2,
+ XStep = (specification.width or 10) * basepoints,
+ YStep = (specification.height or 10) * basepoints,
+ Matrix = {
+ 1, 0, 0, 1,
+ (specification.hoffset or 0) * basepoints,
+ (specification.voffset or 0) * basepoints,
+ },
+ }
+
+ local resources = lpdf.collectedresources{ patterns = false }
+ local attributes = d()
+ local onlybounds = 1
+ local patternobj = saveboxresource(specification.number,attributes,resources,true,onlybounds)
+ lpdf.adddocumentpattern("Pt" .. nofpatterns,lpdf.reference(patternobj ))
+ return nofpatterns
+end
+
+function lpdf.patternstream(n,width,height)
+ return f_pattern("Pt" .. n,width*basepoints,height*basepoints)
+end
+
+backends.pdf.codeinjections.registerpattern = lpdf.registerpattern
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-img.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-img.lmt
new file mode 100644
index 00000000000..50034c36025
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-img.lmt
@@ -0,0 +1,1351 @@
+if not modules then modules = { } end modules ['lpdf-img'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This started as an experiment but has potential for some (cached) optimizations.
+-- At some point we can also use it for fonts. For small images performance is ok
+-- with pure lua but for bigger images we can use some helpers. Normally in a
+-- typesetting workflow non-interlaced images are used. One should convert
+-- interlaced images to more efficient non-interlaced ones (ok, we can cache
+-- them if needed).
+--
+-- The \LUA\ code is slightly optimized so we could have done with less lines if
+-- we wanted but best gain a little. The idea is that we collect striped (in stages)
+-- so that we can play with substitutions.
+
+local type = type
+local concat, move = table.concat, table.move
+local ceil, min = math.ceil, math.min
+local char, byte, find, gmatch = string.char, string.byte, string.find, string.gmatch
+local idiv = number.idiv
+local band, rshift = bit32.band, bit32.rshift
+
+local loaddata = io.loaddata
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+local streams = utilities.streams
+local openstring = streams.openstring
+local readstring = streams.readstring
+local readbytetable = streams.readbytetable
+
+local newreader = io.newreader
+
+local tobytetable = string.bytetable
+
+local lpdf = lpdf or { }
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfconstant = lpdf.constant
+local pdfstring = lpdf.string
+local pdfreference = lpdf.reference
+local pdfverbose = lpdf.verbose
+
+local pdfflushstreamobject
+local pdfmajorversion
+local pdfminorversion
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushstreamobject = lpdf.flushstreamobject
+ pdfmajorversion = lpdf.majorversion
+ pdfminorversion = lpdf.minorversion
+end)
+
+local createimage = images.create
+
+local zlibcompress = (xzip or zlib).compress
+local zlibdecompress = (xzip or zlib).decompress
+
+local trace = false
+
+local report_jpg = logs.reporter("graphics","jpg")
+local report_jp2 = logs.reporter("graphics","jp2")
+local report_png = logs.reporter("graphics","png")
+
+trackers.register("graphics.backend", function(v) trace = v end)
+
+local injectors = { }
+lpdf.injectors = injectors
+
+-- todo: load from a virtual file
+
+local function loadcontent(filename,method)
+ return method == "string" and filename or loaddata(filename)
+end
+
+local function newcontent(filename,method)
+ return newreader(filename,method)
+end
+
+--
+
+local chars = setmetatableindex(function(t,k) -- share this one
+ local v = (k <= 0 and "\000") or (k >= 255 and "\255") or char(k)
+ t[k] = v
+ return v
+end)
+
+do
+
+ function injectors.jpg(specification,method)
+ if specification.error then
+ return
+ end
+ local filename = specification.filename
+ if not filename then
+ return
+ end
+ local colorspace = specification.colorspace or jpg_gray
+ local decodearray = nil
+ ----- procset = colorspace == 0 and "image b" or "image c"
+ if colorspace == 1 then
+ colorspace = "DeviceGray"
+ elseif colorspace == 2 then
+ colorspace = "DeviceRGB"
+ elseif colorspace == 3 then
+ colorspace = "DeviceCMYK"
+ decodearray = pdfarray { 1, 0, 1, 0, 1, 0, 1, 0 }
+ end
+ -- todo: set filename
+ local xsize = specification.xsize
+ local ysize = specification.ysize
+ local colordepth = specification.colordepth
+ local content = loadcontent(filename,method)
+ local xobject = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Image"),
+ -- BBox = pdfarray { 0, 0, xsize, ysize },
+ Width = xsize,
+ Height = ysize,
+ BitsPerComponent = colordepth,
+ Filter = pdfconstant("DCTDecode"),
+ ColorSpace = pdfconstant(colorspace),
+ Decode = decodearray,
+ Length = #content, -- specification.length
+ } + specification.attr
+ if trace then
+ report_jpg("%s: width %i, height %i, colordepth %i, size %i",filename,xsize,ysize,colordepth,#content)
+ end
+ return createimage {
+ bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate
+ transform = specification.transform,
+ nolength = true,
+ nobbox = true,
+ notype = true,
+ stream = content,
+ attr = xobject(),
+ }
+ end
+
+end
+
+do
+
+ function injectors.jp2(specification,method)
+ if specification.error then
+ return
+ end
+ local filename = specification.filename
+ if not filename then
+ return
+ end
+ -- todo: set filename
+ local xsize = specification.xsize
+ local ysize = specification.ysize
+ local content = loadcontent(filename,method)
+ local xobject = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Image"),
+ BBox = pdfarray { 0, 0, xsize, ysize },
+ Width = xsize,
+ Height = ysize,
+ Filter = pdfconstant("JPXDecode"),
+ Length = #content, -- specification.length
+ } + specification.attr
+ if trace then
+ report_jp2("%s: width %i, height %i, size %i",filename,xsize,ysize,#content)
+ end
+ return createimage {
+ bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate
+ transform = specification.transform,
+ nolength = true,
+ nobbox = true,
+ notype = true,
+ stream = content,
+ attr = xobject(),
+ }
+ end
+
+end
+
+do
+
+ -- We don't like interlaced files. You can deinterlace them beforehand because otherwise
+ -- each run you add runtime. Actually, even masked images can best be converted to PDF
+ -- beforehand.
+
+ -- The amount of code is larger that I like and looks somewhat redundant but we sort of
+ -- optimize a few combinations that happen often.
+
+ local pngapplyfilter = pngdecode.applyfilter
+ local pngsplitmask = pngdecode.splitmask
+ local pnginterlace = pngdecode.interlace
+ local pngexpand = pngdecode.expand
+ local pngtocmyk = pngdecode.tocmyk
+
+ local filtermask, decodemask, decodestrip, transpose, expand, tocmyk
+
+ local newindex = lua.newindex
+ local newtable = lua.newtable
+
+ local function newoutput(size)
+ if newindex then
+ return newindex(size,0)
+ end
+ local t = newtable and newtable(size,0) or { }
+ for i=1,size do
+ t[i] = 0
+ end
+ return t
+ end
+
+ local function convert(t)
+ if type(t) == "table" then
+ for i=1,#t do
+ local ti = t[i]
+ if ti ~= "" then -- soon gone
+ t[i] = chars[ti]
+ end
+ end
+ return concat(t)
+ else
+ return t
+ end
+ end
+
+ local function zero(t,k)
+ return 0
+ end
+
+ local function applyfilter(t,xsize,ysize,bpp)
+ local len = xsize * bpp + 1
+ local n = 1
+ local m = len - 1
+ for i=1,ysize do
+ local filter = t[n]
+ t[n] = ""
+ if filter == 0 then
+ elseif filter == 1 then
+ for j=n+bpp+1,n+m do
+ t[j] = (t[j] + t[j-bpp]) % 256
+ end
+ elseif filter == 2 then
+ for j=n+1,n+m do
+ t[j] = (t[j] + t[j-len]) % 256
+ end
+ elseif filter == 3 then
+ for j=n+1,n+bpp do
+ t[j] = (t[j] + idiv(t[j-len],2)) % 256
+ end
+ for j=n+bpp+1,n+m do
+ t[j] = (t[j] + idiv(t[j-bpp] + t[j-len],2)) % 256
+ end
+ elseif filter == 4 then
+ for j=n+1,n+bpp do
+ local p = j - len
+ local b = t[p]
+ if b > 0 then
+ t[j] = (t[j] + b) % 256
+ end
+ end
+ for j=n+bpp+1,n+m do
+ local p = j - len
+ local a = t[j-bpp]
+ local b = t[p]
+ local c = t[p-bpp]
+ local pa = b - c
+ local pb = a - c
+ local pc = pa + pb
+ if pa < 0 then pa = - pa end
+ if pb < 0 then pb = - pb end
+ if pc < 0 then pc = - pc end
+ t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256
+ end
+ end
+ n = n + len
+ end
+ return t
+ end
+
+ local filtermask_l = function (content,xsize,ysize,colordepth,colorspace,hasfilter)
+ local mask = { }
+ local bytes = colordepth == 16 and 2 or 1
+ local bpp = colorspace == "DeviceRGB" and 3 or 1
+ local length = #content
+ local size = ysize * xsize * ((bpp+1)*bytes + (hasfilter and 1 or 0))
+ local n = 1
+ local l = 1
+ if bytes == 2 then
+ if bpp == 1 then
+ for i=1,ysize do
+ if hasfilter then
+ content[n] = "" ; n = n + 1
+ end
+ for j=1,xsize do
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ mask[l] = chars[content[n]] ; l = l + 1
+ content[n] = "" ; n = n + 1
+ mask[l] = chars[content[n]] ; l = l + 1
+ content[n] = "" ; n = n + 1
+ end
+ end
+ elseif bpp == 3 then
+ for i=1,ysize do
+ if hasfilter then
+ content[n] = "" ; n = n + 1
+ end
+ for j=1,xsize do
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ mask[l] = chars[content[n]] ; l = l + 1
+ content[n] = "" ; n = n + 1
+ mask[l] = chars[content[n]] ; l = l + 1
+ content[n] = "" ; n = n + 1
+ end
+ end
+ else
+ return "", ""
+ end
+ else
+ if bpp == 1 then
+ for i=1,ysize do
+ if hasfilter then
+ content[n] = "" ; n = n + 1
+ end
+ for j=1,xsize do
+ content[n] = chars[content[n]] ; n = n + 1
+ mask[l] = chars[content[n]] ; l = l + 1
+ content[n] = "" ; n = n + 1
+ end
+ end
+ elseif bpp == 3 then
+ for i=1,ysize do
+ if hasfilter then
+ content[n] = "" ; n = n + 1
+ end
+ for j=1,xsize do
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ content[n] = chars[content[n]] ; n = n + 1
+ mask[l] = chars[content[n]] ; l = l + 1
+ content[n] = "" ; n = n + 1
+ end
+ end
+ else
+ return "", ""
+ end
+ end
+ return concat(content), concat(mask)
+ end
+
+ local decodemask_l = function(content,xsize,ysize,colordepth,colorspace)
+ local bytes = colordepth == 16 and 2 or 1
+ local bpp = colorspace == "DeviceRGB" and 3 or 1
+ local slice = bytes*(bpp+1)
+ local length = #content
+ local size = ysize * xsize * ((bpp+1)*bytes + 1) -- assume filter
+ content = openstring(content)
+ content = readbytetable(content,length)
+ setmetatableindex(content,zero)
+ applyfilter(content,xsize,ysize,slice)
+ content, mask = filtermask(content,xsize,ysize,colordepth,colorspace,true)
+ return content, mask
+ end
+
+ local filtermask_c = function(content,xsize,ysize,colordepth,colorspace)
+ local bytes = colordepth == 16 and 2 or 1
+ local bpp = colorspace == "DeviceRGB" and 3 or 1
+ return pngsplitmask(content,xsize,ysize,bpp,bytes)
+ end
+
+ local decodemask_c = function(content,xsize,ysize,colordepth,colorspace)
+ local mask = true
+ local filter = false
+ local bytes = colordepth == 16 and 2 or 1
+ local bpp = colorspace == "DeviceRGB" and 3 or 1
+ local slice = bytes * (bpp + 1) -- always a mask
+ content = pngapplyfilter(content,xsize,ysize,slice)
+ return pngsplitmask(content,xsize,ysize,bpp,bytes,mask,filter)
+ end
+
+ local function decodestrip_l(s,nx,ny,slice)
+ local input = readbytetable(s,ny*(nx*slice+1))
+ setmetatableindex(input,zero)
+ applyfilter(input,nx,ny,slice)
+ return input, true
+ end
+
+ local function decodestrip_c(s,nx,ny,slice)
+ local input = readstring(s,ny*(nx*slice+1))
+ input = pngapplyfilter(input,nx,ny,slice)
+ return input, false
+ end
+
+ local xstart = { 0, 4, 0, 2, 0, 1, 0 }
+ local ystart = { 0, 0, 4, 0, 2, 0, 1 }
+ local xstep = { 8, 8, 4, 4, 2, 2, 1 }
+ local ystep = { 8, 8, 8, 4, 4, 2, 2 }
+
+ local xblock = { 8, 4, 4, 2, 2, 1, 1 }
+ local yblock = { 8, 8, 4, 4, 2, 2, 1 }
+
+ local function transpose_l(xsize,ysize,slice,pass,input,output,filter)
+ local xstart = xstart[pass]
+ local xstep = xstep[pass]
+ local ystart = ystart[pass]
+ local ystep = ystep[pass]
+ local nx = idiv(xsize + xstep - xstart - 1,xstep)
+ local ny = idiv(ysize + ystep - ystart - 1,ystep)
+ local offset = filter and 1 or 0
+ local xstep = xstep * slice
+ local xstart = xstart * slice
+ local xsize = xsize * slice
+ local target = ystart * xsize + xstart + 1
+ local ystep = ystep * xsize
+ local start = 1
+ local plus = nx * xstep
+ local step = plus - xstep
+ if not output then
+ output = newoutput(xsize*(parts or slice)*ysize)
+ end
+ if slice == 1 then
+ for j=0,ny-1 do
+ start = start + offset
+ local target = target + j * ystep
+ for target=target,target+step,xstep do
+ output[target] = input[start]
+ start = start + slice
+ end
+ end
+ elseif slice == 2 then
+ for j=0,ny-1 do
+ start = start + offset
+ local target = target + j * ystep
+ for target=target,target+step,xstep do
+ output[target] = input[start]
+ output[target+1] = input[start+1]
+ start = start + slice
+ end
+ end
+ elseif slice == 3 then
+ for j=0,ny-1 do
+ start = start + offset
+ local target = target + j * ystep
+ for target=target,target+step,xstep do
+ output[target] = input[start]
+ output[target+1] = input[start+1]
+ output[target+2] = input[start+2]
+ start = start + slice
+ end
+ end
+ elseif slice == 4 then
+ for j=0,ny-1 do
+ start = start + offset
+ local target = target + j * ystep
+ for target=target,target+step,xstep do
+ output[target] = input[start]
+ output[target+1] = input[start+1]
+ output[target+2] = input[start+2]
+ output[target+3] = input[start+3]
+ start = start + slice
+ end
+ end
+ else
+ local delta = slice - 1
+ for j=0,ny-1 do
+ start = start + offset
+ local target = target + j * ystep
+ for target=target,target+step,xstep do
+ move(input,start,start+delta,target,output)
+ start = start + slice
+ end
+ end
+ end
+ return output;
+ end
+
+ local transpose_c = pnginterlace
+
+ -- print(band(rshift(v,4),0x03),extract(v,4,2))
+ -- print(band(rshift(v,6),0x03),extract(v,6,2))
+
+ local function expand_l(t,xsize,ysize,parts,run,factor,filter)
+ local size = ysize * xsize + 1 -- a bit of overshoot, needs testing, probably a few bytes us ok
+ local xline = filter and (run+1) or run
+ local f = filter and 1 or 0
+ local l = xline - 1
+ local n = 1
+ local o = newoutput(size)
+ local k = 0
+ if factor then
+ if parts == 4 then
+ for i=1,ysize do
+ for j=n+f,n+l do
+ local v = t[j]
+ if v == 0 then
+ k = k + 2
+ else
+ k = k + 1 ; o[k] = extract4(v,4) * 0x11
+ k = k + 1 ; o[k] = extract4(v,0) * 0x11
+ end
+ end
+ k = i * xsize
+ n = n + xline
+ end
+ elseif parts == 2 then
+ for i=1,ysize do
+ for j=n+f,n+l do
+ local v = t[j]
+ if v == 0 then
+ k = k + 4
+ else
+ k = k + 1 ; o[k] = extract2(v,6) * 0x55
+ k = k + 1 ; o[k] = extract2(v,4) * 0x55
+ k = k + 1 ; o[k] = extract2(v,2) * 0x55
+ k = k + 1 ; o[k] = extract2(v,0) * 0x55
+ end
+ end
+ k = i * xsize
+ n = n + xline
+ end
+ else
+ for i=1,ysize do
+ for j=n+f,n+l do
+ local v = t[j]
+ if v == 0 then
+ k = k + 8
+ else
+ k = k + 1 ; if band(v,0x80) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,7) * 0xFF
+ k = k + 1 ; if band(v,0x40) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,6) * 0xFF
+ k = k + 1 ; if band(v,0x20) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,5) * 0xFF
+ k = k + 1 ; if band(v,0x10) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,4) * 0xFF
+ k = k + 1 ; if band(v,0x08) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,3) * 0xFF
+ k = k + 1 ; if band(v,0x04) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,2) * 0xFF
+ k = k + 1 ; if band(v,0x02) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,1) * 0xFF
+ k = k + 1 ; if band(v,0x01) ~= 0 then o[k] = 0xFF end -- o[k] = extract1(v,0) * 0xFF
+ end
+ end
+ k = i * xsize
+ n = n + xline
+ end
+ end
+ else
+ if parts == 4 then
+ for i=1,ysize do
+ for j=n+f,n+l do
+ local v = t[j]
+ if v == 0 then
+ k = k + 2
+ else
+ k = k + 1 ; o[k] = extract4(v,4)
+ k = k + 1 ; o[k] = extract4(v,0)
+ end
+ end
+ k = i * xsize
+ n = n + xline
+ end
+ elseif parts == 2 then
+ for i=1,ysize do
+ for j=n+f,n+l do
+ local v = t[j]
+ if v == 0 then
+ k = k + 4
+ else
+ k = k + 1 ; o[k] = extract2(v,6)
+ k = k + 1 ; o[k] = extract2(v,4)
+ k = k + 1 ; o[k] = extract2(v,2)
+ k = k + 1 ; o[k] = extract2(v,0)
+ end
+ end
+ k = i * xsize
+ n = n + xline
+ end
+ else
+ for i=1,ysize do
+ for j=n+f,n+l do
+ local v = t[j]
+ if v == 0 then
+ k = k + 8
+ else
+ k = k + 1 ; if band(v,0x80) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,7)
+ k = k + 1 ; if band(v,0x40) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,6)
+ k = k + 1 ; if band(v,0x20) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,5)
+ k = k + 1 ; if band(v,0x10) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,4)
+ k = k + 1 ; if band(v,0x08) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,3)
+ k = k + 1 ; if band(v,0x04) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,2)
+ k = k + 1 ; if band(v,0x02) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,1)
+ k = k + 1 ; if band(v,0x01) ~= 0 then o[k] = 1 end -- o[k] = extract1(v,0)
+ end
+ end
+ k = i * xsize
+ n = n + xline
+ end
+ end
+ end
+ for i=size,xsize * ysize +1,-1 do
+ o[i] = nil
+ end
+ return o, false
+ end
+
+ local expand_c = pngexpand
+
+ local function analyze(colordepth,colorspace,palette,mask)
+ -- return bytes, parts, factor
+ if palette then
+ if colordepth == 16 then
+ return 2, false, false
+ elseif colordepth == 8 then
+ return 1, false, false
+ elseif colordepth == 4 then
+ return 1, 4, false
+ elseif colordepth == 2 then
+ return 1, 2, false
+ elseif colordepth == 1 then
+ return 1, 1, false
+ end
+ elseif colorspace == "DeviceGray" then
+ if colordepth == 16 then
+ return mask and 4 or 2, false, false
+ elseif colordepth == 8 then
+ return mask and 2 or 1, false, false
+ elseif colordepth == 4 then
+ return 1, 4, true
+ elseif colordepth == 2 then
+ return 1, 2, true
+ elseif colordepth == 1 then
+ return 1, 1, true
+ end
+ else
+ if colordepth == 16 then
+ return mask and 8 or 6, false, false
+ elseif colordepth == 8 then
+ return mask and 4 or 3, false, false
+ elseif colordepth == 4 then
+ return 3, 4, true
+ elseif colordepth == 2 then
+ return 3, 2, true
+ elseif colordepth == 1 then
+ return 3, 1, true
+ end
+ end
+ return false, false, false
+ end
+
+ -- 1 6 4 6 2 6 4 6
+ -- 7 7 7 7 7 7 7 7
+ -- 5 6 5 6 5 6 5 6
+ -- 7 7 7 7 7 7 7 7
+ -- 3 6 4 6 3 6 4 6
+ -- 7 7 7 7 7 7 7 7
+ -- 5 6 5 6 5 6 5 6
+ -- 7 7 7 7 7 7 7 7
+
+ local function deinterlace(content,xsize,ysize,colordepth,colorspace,palette,mask)
+ local slice, parts, factor = analyze(colordepth,colorspace,palette,mask)
+ if slice then
+ content = openstring(zlibdecompress(content))
+ local filter = false
+ local output = false
+ for pass=1,7 do
+ local xstart = xstart[pass]
+ local xstep = xstep[pass]
+ local ystart = ystart[pass]
+ local ystep = ystep[pass]
+ local nx = idiv(xsize + xstep - xstart - 1,xstep)
+ local ny = idiv(ysize + ystep - ystart - 1,ystep)
+ if nx > 0 and ny > 0 then
+ local input, filter
+ if parts then
+ local nxx = ceil(nx*parts/8)
+ input, filter = decodestrip(content,nxx,ny,slice)
+ input, filter = expand(input,nx,ny,parts,nxx,factor,filter)
+ else
+ input, filter = decodestrip(content,nx,ny,slice)
+ end
+ output = transpose(xsize,ysize,slice,pass,input,output,filter)
+ end
+ -- if pass == 3 then
+ -- break -- still looks ok, could be nice for a preroll
+ -- end
+ end
+ return output, parts and 8 or false
+ end
+ end
+
+ -- 1 (palette used), 2 (color used), and 4 (alpha channel used)
+
+ -- paeth:
+ --
+ -- p = a + b - c
+ -- pa = abs(p - a) => a + b - c - a => b - c
+ -- pb = abs(p - b) => a + b - c - b => a - c
+ -- pc = abs(p - c) => a + b - c - c => a + b - c - c => a - c + b - c => pa + pb
+
+ local function full(t,k) local v = "\xFF" t[k] = v return v end
+
+ local function expandvector(transparent)
+ local s = openstring(transparent)
+ local n = #transparent
+ local r = { }
+ for i=0,n-1 do
+ r[i] = readstring(s,1) -- readchar
+ end
+ setmetatableindex(r,full)
+ return r
+ end
+
+ local function createmask_l(content,palette,transparent,xsize,ysize,colordepth,colorspace)
+ if palette then
+ local r = expandvector(transparent)
+ local size = xsize*ysize
+ local len = ceil(xsize*colordepth/8) + 1
+ local o = newoutput(xsize*ysize)
+ local u = setmetatableindex(zero)
+ content = zlibdecompress(content)
+ content = openstring(content)
+ for i=0,ysize-1 do
+ local t = readbytetable(content,len)
+ local k = i * xsize
+ local filter = t[1]
+ if filter == 0 then
+ elseif filter == 1 then
+ for j=3,len do
+ t[j] = (t[j] + t[j-1]) % 256
+ end
+ elseif filter == 2 then
+ for j=2,len do
+ t[j] = (t[j] + u[j]) % 256
+ end
+ elseif filter == 3 then
+ local j = 2
+ t[j] = (t[j] + idiv(u[j],2)) % 256
+ for j=3,len do
+ t[j] = (t[j] + idiv(t[j-1] + u[j],2)) % 256
+ end
+ elseif filter == 4 then
+ local j = 2
+ local p = j - len
+ local b = t[p]
+ if b < 0 then
+ b = - b
+ end
+ if b > 0 then
+ t[j] = (t[j] + b) % 256
+ end
+ for j=3,len do
+ local p = j - len
+ local a = t[j-1]
+ local b = t[p]
+ local c = t[p-1]
+ local pa = b - c
+ local pb = a - c
+ local pc = pa + pb
+ if pa < 0 then pa = - pa end
+ if pb < 0 then pb = - pb end
+ if pc < 0 then pc = - pc end
+ t[j] = (t[j] + ((pa <= pb and pa <= pc and a) or (pb <= pc and b) or c)) % 256
+ end
+ end
+ if colordepth == 8 then
+ for j=2,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[v]
+ end
+ elseif colordepth == 4 then
+ for j=2,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[extract4(v,4)]
+ k = k + 1 ; o[k] = r[extract4(v,0)]
+ end
+ elseif colordepth == 2 then
+ for j=2,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[extract2(v,6)]
+ k = k + 1 ; o[k] = r[extract2(v,4)]
+ k = k + 1 ; o[k] = r[extract2(v,2)]
+ k = k + 1 ; o[k] = r[extract2(v,0)]
+ end
+ else
+ for j=2,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[extract1(v,7)]
+ k = k + 1 ; o[k] = r[extract1(v,6)]
+ k = k + 1 ; o[k] = r[extract1(v,5)]
+ k = k + 1 ; o[k] = r[extract1(v,4)]
+ k = k + 1 ; o[k] = r[extract1(v,3)]
+ k = k + 1 ; o[k] = r[extract1(v,2)]
+ k = k + 1 ; o[k] = r[extract1(v,1)]
+ k = k + 1 ; o[k] = r[extract1(v,0)]
+ end
+ end
+ u = t
+ end
+ return concat(o,"",1,size)
+ end
+ end
+
+ local function createmask_c(content,palette,transparent,xsize,ysize,colordepth,colorspace)
+ if palette then
+ local r = expandvector(transparent)
+ local size = xsize*ysize
+ local len = ceil(xsize*colordepth/8)
+ local o = newoutput(size)
+ content = zlibdecompress(content)
+ content = pngapplyfilter(content,len,ysize,1) -- nostrip (saves copy)
+ content = openstring(content)
+ for i=0,ysize-1 do
+ local t = readbytetable(content,len)
+ local k = i * xsize
+ if colordepth == 8 then
+ for j=1,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[v]
+ end
+ elseif colordepth == 4 then
+ for j=1,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[extract4(v,4)]
+ k = k + 1 ; o[k] = r[extract4(v,0)]
+ end
+ elseif colordepth == 2 then
+ for j=1,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[extract2(v,6)]
+ k = k + 1 ; o[k] = r[extract2(v,4)]
+ k = k + 1 ; o[k] = r[extract2(v,2)]
+ k = k + 1 ; o[k] = r[extract2(v,0)]
+ end
+ else
+ for j=1,len do
+ local v = t[j]
+ k = k + 1 ; o[k] = r[extract1(v,7)]
+ k = k + 1 ; o[k] = r[extract1(v,6)]
+ k = k + 1 ; o[k] = r[extract1(v,5)]
+ k = k + 1 ; o[k] = r[extract1(v,4)]
+ k = k + 1 ; o[k] = r[extract1(v,3)]
+ k = k + 1 ; o[k] = r[extract1(v,2)]
+ k = k + 1 ; o[k] = r[extract1(v,1)]
+ k = k + 1 ; o[k] = r[extract1(v,0)]
+ end
+ end
+ end
+ return concat(o,"",1,size)
+ end
+ end
+
+ local function tocmyk_l(content,colordepth)
+ local l = #content
+ local t = { }
+ local n = 0
+ if colordepth == 8 then
+ for i=1,l,3 do
+ local r, g, b = byte(content,i,i+2)
+ n = n + 1 ; t[n] = char(255-r,255-g,255-b,0) -- a tad faster than chars[...]
+ end
+ else
+ for i=1,l,6 do
+ local r1, r2, g1, g2, b1, b2 = byte(content,i,i+5)
+ n = n + 1 ; t[n] = char(255-r1,255-r2,255-g1,255-g2,255-b1,255-b2,0,0)
+ end
+ end
+ return concat(t)
+ end
+
+ local tocmyk_c = pngtocmyk or tocmyk_l
+
+ local function converttocmyk(content,colorspace,colordepth)
+ if colorspace == "DeviceRGB" and colordepth == 8 or colordepth == 16 then
+ local done = tocmyk(content,colordepth)
+ if done then
+ content = done
+ colorspace = "DeviceCMYK"
+ end
+ end
+ return content, colorspace
+ end
+
+ local function switch(v)
+ if v then
+ filtermask = filtermask_l
+ decodemask = decodemask_l
+ decodestrip = decodestrip_l
+ transpose = transpose_l
+ expand = expand_l
+ createmask = createmask_l
+ tocmyk = tocmyk_l
+ else
+ filtermask = filtermask_c
+ decodemask = decodemask_c
+ decodestrip = decodestrip_c
+ transpose = transpose_c
+ expand = expand_c
+ createmask = createmask_c
+ tocmyk = tocmyk_c
+ end
+ end
+
+ if pngapplyfilter then
+ switch(false)
+ directives.register("graphics.png.purelua",switch)
+ else
+ switch(true)
+ end
+
+ local alwaysdecode = false -- trucky with palettes
+ local compresslevel = 3
+
+ directives.register("graphics.png.recompress", function(v)
+ alwaysdecode = v
+ end)
+
+ directives.register("graphics.png.compresslevel", function(v)
+ v = tonumber(v)
+ if compresslevel >= 0 or compresslevel <= 9 then
+ compresslevel = v
+ end
+ end)
+
+ function injectors.png(specification,method) -- todo: method in specification
+ if specification.error then
+ return
+ end
+ local filename = specification.filename
+ if not filename then
+ return
+ end
+ local colorspace = specification.colorspace
+ if not colorspace then
+ return
+ end
+ local interlace = specification.interlace or 0
+ if interlace == 1 then
+ interlace = true
+ elseif interlace == 0 then
+ interlace = false
+ else
+ report_png("unknown interlacing %i",interlace)
+ return
+ end
+ local tables = specification.tables
+ if not tables then
+ return
+ end
+ local idat = tables.idat
+ if not idat then
+ return
+ end
+ local pngfile = newcontent(filename,method)
+ if not pngfile then
+ return
+ end
+ local content = idat(pngfile,true)
+ tables.idat = false
+ --
+ -- if tables.gama then
+ -- report_png("ignoring gamma correction")
+ -- end
+ --
+ local xsize = specification.xsize
+ local ysize = specification.ysize
+ local colordepth = specification.colordepth or 8
+ local mask = false
+ local transparent = false
+ local palette = false
+ local enforcecmyk = specification.enforcecmyk
+ local colors = 1
+ if colorspace == 0 then -- gray | image b
+ colorspace = "DeviceGray"
+ transparent = true
+ elseif colorspace == 2 then -- rgb | image c
+ colorspace = "DeviceRGB"
+ colors = 3
+ transparent = true
+ elseif colorspace == 3 then -- palette | image c+i
+ colorspace = "DeviceRGB"
+ palette = true
+ transparent = true
+ elseif colorspace == 4 then -- gray | alpha | image b
+ colorspace = "DeviceGray"
+ mask = true
+ elseif colorspace == 6 then -- rgb | alpha | image c
+ colorspace = "DeviceRGB"
+ colors = 3
+ mask = true
+ else
+ report_png("unknown colorspace %i",colorspace)
+ return
+ end
+ --
+ if transparent then
+ local trns = tables.trns
+ if trns then
+ transparent = trns(pngfile,true)
+ if transparent == "" then
+ transparent = false
+ end
+ tables.trns = false
+ else
+ transparent = false
+ end
+ end
+ --
+ local decode = alwaysdecode -- tricky, might go away
+ local filter = pdfconstant("FlateDecode")
+ local major = pdfmajorversion()
+ local minor = pdfminorversion()
+ if major > 1 then
+ -- we're okay
+ elseif minor < 5 and colordepth == 16 then
+ report_png("16 bit colordepth not supported in pdf < 1.5")
+ return
+ elseif minor < 4 and (mask or transparent) then
+ report_png("alpha channels not supported in pdf < 1.4")
+ return
+ elseif minor < 2 then
+ report_png("you'd better use a version > 1.2")
+ return
+ -- decode = true
+ end
+ --
+ -- todo: compresslevel (or delegate)
+ --
+ if palette then
+ local plte = tables.plte
+ if plte then
+ palette = plte(pngfile,true)
+ if palette == "" then
+ palette = false
+ end
+ tables.plte = false
+ else
+ palette = false
+ end
+ end
+ --
+ if interlace then
+ local r, p = deinterlace(content,xsize,ysize,colordepth,colorspace,palette,mask)
+ if not r then
+ return
+ end
+ if p then
+ colordepth = p
+ end
+ if mask then
+ if not (colordepth == 8 or colordepth == 16) then
+ report_png("mask can't be split from the image")
+ return
+ end -- get rid of bpp:
+ content, mask = filtermask(r,xsize,ysize,colordepth,colorspace,false)
+ else
+ content = convert(r) -- can be in deinterlace if needed
+ end
+ if enforcecmyk then
+ content, colorspace = converttocmyk(content,colorspace,colordepth)
+ end
+ if compresslevel > 0 then
+ content = zlibcompress(content,compresslevel)
+ else
+ filter = nil
+ end
+ decode = true
+ elseif mask then
+ if not (colordepth == 8 or colordepth == 16) then
+ report_png("mask can't be split from the image")
+ return
+ end
+ content = zlibdecompress(content)
+ content, mask = decodemask(content,xsize,ysize,colordepth,colorspace)
+ if enforcecmyk and not palette then
+ content, colorspace = converttocmyk(content,colorspace,colordepth)
+ end
+ if compresslevel > 0 then
+ content = zlibcompress(content,compresslevel)
+ else
+ filter = nil
+ end
+ decode = true -- we don't copy the filter byte
+ elseif transparent then
+ -- in test suite
+ -- how about decode/recompress here
+ if palette then
+ mask = createmask(content,palette,transparent,xsize,ysize,colordepth,colorspace)
+ else
+ pallette = false
+ end
+ elseif decode or (enforcecmyk and not palette) then
+ -- this one needs checking
+ local bytes = analyze(colordepth,colorspace)
+ if bytes then
+ content = zlibdecompress(content)
+ content = decodestrip(openstring(content),xsize,ysize,bytes)
+ if enforcecmyk and not palette then
+ content, colorspace = converttocmyk(content,colorspace,colordepth)
+ end
+ if compresslevel > 0 then
+ content = zlibcompress(content,compresslevel)
+ else
+ filter = nil
+ end
+ else
+ return
+ end
+ decode = true -- due to enforcecmyk
+ else
+ -- print("PASS ON")
+ end
+ if palette then
+ local colorspace = "DeviceRGB"
+ local nofbytes = 3
+ if enforcecmyk then
+ palette = converttocmyk(palette,colorspace,8)
+ colorspace = "DeviceCMYK"
+ nofbytes = 4
+ end
+ palette = pdfarray {
+ pdfconstant("Indexed"),
+ pdfconstant(colorspace),
+ idiv(#palette,nofbytes),
+ pdfreference(pdfflushstreamobject(palette)),
+ }
+ end
+ pngfile:close()
+ local xobject = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Image"),
+ -- BBox = pdfarray { 0, 0, xsize, ysize },
+ Width = xsize,
+ Height = ysize,
+ BitsPerComponent = colordepth,
+ Filter = filter,
+ ColorSpace = palette or pdfconstant(colorspace),
+ Length = #content,
+ } + specification.attr
+ if mask then
+ local d = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Image"),
+ Width = xsize,
+ Height = ysize,
+ BitsPerComponent = palette and 8 or colordepth,
+ ColorSpace = pdfconstant("DeviceGray"),
+ }
+ xobject.SMask = pdfreference(pdfflushstreamobject(mask,d()))
+ end
+ if not decode then
+ xobject.DecodeParms = pdfdictionary {
+ Colors = colors,
+ Columns = xsize,
+ BitsPerComponent = colordepth,
+ Predictor = 15,
+ }
+ end
+ if trace then
+ report_png("%s: width %i, height %i, colordepth %i, size %i, palette %l, mask %l, transparent %l, decode %l",filename,xsize,ysize,colordepth,#content,palette,mask,transparent,decode)
+ end
+ if specification.colorref then
+ xobject.ColorSpace = pdfreference(specification.colorref)
+ end
+ local width = specification.width or xsize * 65536
+ local height = specification.height or ysize * 65536
+ return createimage {
+ bbox = { 0, 0, width/xsize, height/ysize }, -- mandate
+ transform = specification.transform,
+ nolength = true,
+ nobbox = true,
+ notype = true,
+ stream = content,
+ attr = xobject(),
+ }
+ end
+
+end
+
+do
+
+ local function pack(specification,what)
+ local t = { }
+ local n = 0
+ local s = specification.colorspace
+ local d = specification.data
+ local x = specification.xsize
+ local y = specification.ysize
+ if what == "mask" then
+ d = specification.mask
+ s = 1
+ elseif what == "indexed" then
+ s = 1
+ elseif what == "index" then
+ d = specification.index
+ s = - s
+ end
+ if s > 0 then
+ if s == 1 then
+ for i=1,y do
+ local r = d[i]
+ for j=1,x do
+ n = n + 1 ; t[n] = chars[r[j]]
+ end
+ end
+ elseif s == 2 then
+ for i=1,y do
+ local r = d[i]
+ for j=1,x do
+ local c = r[j]
+ n = n + 1 ; t[n] = chars[c[1]]
+ n = n + 1 ; t[n] = chars[c[2]]
+ n = n + 1 ; t[n] = chars[c[3]]
+ -- n = n + 1 ; t[n] = char(c[1],c[2],c[3]) -- test this
+ end
+ end
+ elseif s == 3 then
+ for i=1,y do
+ local r = d[i]
+ for j=1,x do
+ local c = r[j]
+ n = n + 1 ; t[n] = chars[c[1]]
+ n = n + 1 ; t[n] = chars[c[2]]
+ n = n + 1 ; t[n] = chars[c[3]]
+ n = n + 1 ; t[n] = chars[c[4]]
+ -- n = n + 1 ; t[n] = char(c[1],c[2],c[3],c[4]) -- test this
+ end
+ end
+ end
+ return concat(t)
+ else
+ local z = d[0] and 0 or 1
+ if s == -1 then
+ local f = formatters["%02X"]
+ for i=z,#d do
+ n = n + 1 ; t[n] = f(d[i])
+ end
+ elseif s == -2 then
+ local f = formatters["%02X%02X%02X"]
+ for i=z,#d do
+ local c = d[i]
+ n = n + 1 ; t[n] = f(c[1],c[2],c[3])
+ end
+ elseif s == -3 then
+ local f = formatters["%02X%02X%02X%02X"]
+ for i=z,#d do
+ local c = d[i]
+ n = n + 1 ; t[n] = f(c[1],c[2],c[3],c[4])
+ end
+ end
+ return "<" .. concat(t," ") .. ">"
+ end
+ return ""
+ end
+
+ function injectors.bitmap(specification)
+ local data = specification.data
+ if not data then
+ return
+ end
+ local xsize = specification.xsize or 0
+ local ysize = specification.ysize or 0
+ if xsize == 0 or ysize == 0 then
+ return
+ end
+ local colorspace = specification.colorspace or 1
+ if colorspace == 1 then
+ colorspace = "DeviceGray"
+ elseif colorspace == 2 then
+ colorspace = "DeviceRGB"
+ elseif colorspace == 3 then
+ colorspace = "DeviceCMYK"
+ end
+ local colordepth = (specification.colordepth or 2) == 16 or 8
+ local index = specification.index
+ local content = pack(specification,index and "indexed" or "data")
+ local mask = specification.mask
+ local colorspace = pdfconstant(colorspace)
+ if index then
+ colorspace = pdfarray {
+ pdfconstant("Indexed"),
+ colorspace,
+ #index + (index[0] and 0 or -1), -- upper index
+ pdfverbose(pack(specification,"index"))
+ }
+ end
+ local xobject = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Image"),
+ BBox = pdfarray { 0, 0, xsize, ysize },
+ Width = xsize,
+ Height = ysize,
+ BitsPerComponent = colordepth,
+ ColorSpace = colorspace,
+ Length = #content, -- specification.length
+ }
+ if mask then
+ local d = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Image"),
+ Width = xsize,
+ Height = ysize,
+ BitsPerComponent = colordepth,
+ ColorSpace = pdfconstant("DeviceGray"),
+ }
+ xobject.SMask = pdfreference(pdfflushstreamobject(pack(specification,"mask"),d()))
+ end
+ return createimage {
+ bbox = { 0, 0, specification.width/xsize, specification.height/ysize }, -- mandate
+ -- nolength = true,
+ nobbox = true,
+ notype = true,
+ stream = content,
+ attr = xobject(),
+ }
+ end
+
+ backends.pdf.codeinjections.bitmap = injectors.bitmap
+
+end
+
+-- local function validcompression(data)
+-- local d = utilities.streams.openstring(data)
+-- local b1 = utilities.streams.readbyte(d)
+-- local b2 = utilities.streams.readbyte(d)
+-- print(b1,b2)
+-- if (b1 * 256 + b2) % 31 ~= 0 then
+-- return false, "no zlib compressed file"
+-- end
+-- local method = band(b1,15)
+-- if method ~= 8 then
+-- return false, "method 8 expected"
+-- end
+-- local detail = band(rshift(b1,4),15)
+-- if detail > 7 then
+-- return false, "window 32 expected"
+-- end
+-- local preset = band(rshift(b2,5),1)
+-- if preset ~= 0 then
+-- return false, "unexpected preset dictionary"
+-- end
+-- return true
+-- end
+
+backends.pdf.codeinjections.jpg = lpdf.injectors.jpg
+backends.pdf.codeinjections.jp2 = lpdf.injectors.jp2
+backends.pdf.codeinjections.png = lpdf.injectors.png
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-ini.lmt
new file mode 100644
index 00000000000..27625606aa6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-ini.lmt
@@ -0,0 +1,1363 @@
+if not modules then modules = { } end modules ['lpdf-ini'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- beware of "too many locals" here
+
+local setmetatable, getmetatable, type, next, tostring, tonumber, rawset = setmetatable, getmetatable, type, next, tostring, tonumber, rawset
+local char, byte, format, gsub, concat, match, sub, gmatch = string.char, string.byte, string.format, string.gsub, table.concat, string.match, string.sub, string.gmatch
+local utfchar, utfbyte, utfvalues = utf.char, utf.byte, utf.values
+local sind, cosd, max, min = math.sind, math.cosd, math.max, math.min
+local sort, sortedhash = table.sort, table.sortedhash
+local P, C, R, S, Cc, Cs, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs, lpeg.V
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local formatters = string.formatters
+local isboolean = string.is_boolean
+local rshift = bit32.rshift
+
+local report_objects = logs.reporter("backend","objects")
+local report_finalizing = logs.reporter("backend","finalizing")
+local report_blocked = logs.reporter("backend","blocked")
+
+local implement = interfaces.implement
+
+local context = context
+
+-- In ConTeXt MkIV we use utf8 exclusively so all strings get mapped onto a hex
+-- encoded utf16 string type between <>. We could probably save some bytes by using
+-- strings between () but then we end up with escaped ()\ too.
+
+pdf = type(pdf) == "table" and pdf or { }
+local factor = number.dimenfactors.bp
+
+local codeinjections = { }
+local nodeinjections = { }
+
+local backends = backends
+
+local pdfbackend = {
+ comment = "backend for directly generating pdf output",
+ nodeinjections = nodeinjections,
+ codeinjections = codeinjections,
+ registrations = { },
+ tables = { },
+}
+
+backends.pdf = pdfbackend
+
+lpdf = lpdf or { }
+local lpdf = lpdf
+lpdf.flags = lpdf.flags or { } -- will be filled later
+
+table.setmetatableindex(lpdf, function(t,k)
+ report_blocked("function %a is not accessible",k)
+ os.exit()
+end)
+
+local trace_finalizers = false trackers.register("backend.finalizers", function(v) trace_finalizers = v end)
+local trace_resources = false trackers.register("backend.resources", function(v) trace_resources = v end)
+
+local pdfreserveobject
+local pdfimmediateobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+ pdfimmediateobject = lpdf.immediateobject
+end)
+
+do
+
+ updaters.register("backend.update.lpdf",function()
+ job.positions.registerhandlers {
+ getpos = drivers.getpos,
+ getrpos = drivers.getrpos,
+ gethpos = drivers.gethpos,
+ getvpos = drivers.getvpos,
+ }
+ lpdf.getpos = drivers.getpos
+ end)
+
+ local pdfgetmatrix, pdfhasmatrix, pdfgetpos
+
+ updaters.register("backend.update.lpdf",function()
+ pdfgetmatrix = lpdf.getmatrix
+ pdfhasmatrix = lpdf.hasmatrix
+ pdfgetpos = lpdf.getpos
+ end)
+
+ -- local function transform(llx,lly,urx,ury,rx,sx,sy,ry)
+ -- local x1 = llx * rx + lly * sy
+ -- local y1 = llx * sx + lly * ry
+ -- local x2 = llx * rx + ury * sy
+ -- local y2 = llx * sx + ury * ry
+ -- local x3 = urx * rx + lly * sy
+ -- local y3 = urx * sx + lly * ry
+ -- local x4 = urx * rx + ury * sy
+ -- local y4 = urx * sx + ury * ry
+ -- llx = min(x1,x2,x3,x4);
+ -- lly = min(y1,y2,y3,y4);
+ -- urx = max(x1,x2,x3,x4);
+ -- ury = max(y1,y2,y3,y4);
+ -- return llx, lly, urx, ury
+ -- end
+ --
+ -- function lpdf.transform(llx,lly,urx,ury) -- not yet used so unchecked
+ -- if pdfhasmatrix() then
+ -- local sx, rx, ry, sy = pdfgetmatrix()
+ -- local w, h = urx - llx, ury - lly
+ -- return llx, lly, llx + sy*w - ry*h, lly + sx*h - rx*w
+ -- -- return transform(llx,lly,urx,ury,sx,rx,ry,sy)
+ -- else
+ -- return llx, lly, urx, ury
+ -- end
+ -- end
+
+ -- funny values for tx and ty
+
+ function lpdf.rectangle(width,height,depth,offset)
+ local tx, ty = pdfgetpos()
+ if offset then
+ tx = tx - offset
+ ty = ty + offset
+ width = width + 2*offset
+ height = height + offset
+ depth = depth + offset
+ end
+ if pdfhasmatrix() then
+ local rx, sx, sy, ry = pdfgetmatrix()
+ return
+ factor * tx,
+ factor * (ty - ry*depth + sx*width),
+ factor * (tx + rx*width - sy*height),
+ factor * (ty + ry*height - sx*width)
+ else
+ return
+ factor * tx,
+ factor * (ty - depth),
+ factor * (tx + width),
+ factor * (ty + height)
+ end
+ end
+
+end
+
+-- we could use a hash of predefined unicodes
+
+-- local function tosixteen(str) -- an lpeg might be faster (no table)
+-- if not str or str == "" then
+-- return "<feff>" -- not () as we want an indication that it's unicode
+-- else
+-- local r, n = { "<feff" }, 1
+-- for b in utfvalues(str) do
+-- n = n + 1
+-- if b < 0x10000 then
+-- r[n] = format("%04x",b)
+-- else
+-- r[n] = format("%04x%04x",rshift(b,10),b%1024+0xDC00)
+-- end
+-- end
+-- n = n + 1
+-- r[n] = ">"
+-- return concat(r)
+-- end
+-- end
+
+local tosixteen, fromsixteen, topdfdoc, frompdfdoc, toeight, fromeight
+
+do
+
+ local cache = table.setmetatableindex(function(t,k) -- can be made weak
+ local v = utfbyte(k)
+ if v < 0x10000 then
+ v = format("%04x",v)
+ else
+ v = v - 0x10000
+ v = format("%04x%04x",rshift(v,10)+0xD800,v%1024+0xDC00)
+ -- v = format("%04x%04x",rshift(v-0x10000,10)+0xD800,v%1024+0xDC00)
+ end
+ t[k] = v
+ return v
+ end)
+
+ local unified = Cs(Cc("<feff") * (lpeg.patterns.utf8character/cache)^1 * Cc(">"))
+
+ tosixteen = function(str) -- an lpeg might be faster (no table)
+ if not str or str == "" then
+ return "<feff>" -- not () as we want an indication that it's unicode
+ else
+ return lpegmatch(unified,str)
+ end
+ end
+
+ local more = 0
+
+ local pattern = C(4) / function(s) -- needs checking !
+ local now = tonumber(s,16)
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000
+ more = 0
+ return utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ return "" -- else the c's end up in the stream
+ else
+ return utfchar(now)
+ end
+ end
+
+ local pattern = P(true) / function() more = 0 end * Cs(pattern^0)
+
+ fromsixteen = function(str)
+ if not str or str == "" then
+ return ""
+ else
+ return lpegmatch(pattern,str)
+ end
+ end
+
+ local toregime = regimes.toregime
+ local fromregime = regimes.fromregime
+ local escaped = Cs(Cc("(") * (S("\\()\n\r\t\b\f")/"\\%0" + P(1))^0 * Cc(")"))
+
+ topdfdoc = function(str,default)
+ if not str or str == "" then
+ return ""
+ else
+ return lpegmatch(escaped,toregime("pdfdoc",str,default)) -- could be combined if needed
+ end
+ end
+
+ frompdfdoc = function(str)
+ if not str or str == "" then
+ return ""
+ else
+ return fromregime("pdfdoc",str)
+ end
+ end
+
+ if not toregime then topdfdoc = function(s) return s end end
+ if not fromregime then frompdfdoc = function(s) return s end end
+
+ toeight = function(str)
+ if not str or str == "" then
+ return "()"
+ else
+ return lpegmatch(escaped,str)
+ end
+ end
+
+ local b_pattern = Cs((P("\\")/"" * (
+ S("()")
+ + S("nrtbf") / { n = "\n", r = "\r", t = "\t", b = "\b", f = "\f" }
+ + lpegpatterns.octdigit^-3 / function(s) return char(tonumber(s,8)) end)
+ + P(1))^0)
+
+ fromeight = function(str)
+ if not str or str == "" then
+ return ""
+ else
+ return lpegmatch(unescape,str)
+ end
+ end
+
+ local u_pattern = lpegpatterns.utfbom_16_be * lpegpatterns.utf16_to_utf8_be -- official
+ + lpegpatterns.utfbom_16_le * lpegpatterns.utf16_to_utf8_le -- we've seen these
+
+ local h_pattern = lpegpatterns.hextobytes
+
+ local zero = S(" \n\r\t") + P("\\ ")
+ local one = C(4)
+ local two = P("d") * R("89","af") * C(2) * C(4)
+
+ local x_pattern = P { "start",
+ start = V("wrapped") + V("unwrapped") + V("original"),
+ original = Cs(P(1)^0),
+ wrapped = P("<") * V("unwrapped") * P(">") * P(-1),
+ unwrapped = P("feff")
+ * Cs( (
+ zero / ""
+ + two / function(a,b)
+ a = (tonumber(a,16) - 0xD800) * 1024
+ b = (tonumber(b,16) - 0xDC00)
+ return utfchar(a+b)
+ end
+ + one / function(a)
+ return utfchar(tonumber(a,16))
+ end
+ )^1 ) * P(-1)
+ }
+
+ function lpdf.frombytes(s,hex)
+ if not s or s == "" then
+ return ""
+ end
+ if hex then
+ local x = lpegmatch(x_pattern,s)
+ if x then
+ return x
+ end
+ local h = lpegmatch(h_pattern,s)
+ if h then
+ return h
+ end
+ else
+ local u = lpegmatch(u_pattern,s)
+ if u then
+ return u
+ end
+ end
+ return lpegmatch(b_pattern,s)
+ end
+
+ lpdf.tosixteen = tosixteen
+ lpdf.toeight = toeight
+ lpdf.topdfdoc = topdfdoc
+ lpdf.fromsixteen = fromsixteen
+ lpdf.fromeight = fromeight
+ lpdf.frompdfdoc = frompdfdoc
+
+end
+
+local tostring_a, tostring_d
+
+do
+
+ local f_key_null = formatters["/%s null"]
+ local f_key_value = formatters["/%s %s"]
+ -- local f_key_dictionary = formatters["/%s << % t >>"]
+ -- local f_dictionary = formatters["<< % t >>"]
+ local f_key_dictionary = formatters["/%s << %s >>"]
+ local f_dictionary = formatters["<< %s >>"]
+ -- local f_key_array = formatters["/%s [ % t ]"]
+ -- local f_array = formatters["[ % t ]"]
+ local f_key_array = formatters["/%s [ %s ]"]
+ local f_array = formatters["[ %s ]"]
+ local f_key_number = formatters["/%s %N"] -- always with max 9 digits and integer is possible
+ local f_tonumber = formatters["%N"] -- always with max 9 digits and integer is possible
+
+ tostring_d = function(t,contentonly,key)
+ if next(t) then
+ local r = { }
+ local n = 0
+ local e
+ for k, v in next, t do
+ if k == "__extra__" then
+ e = v
+ elseif k == "__stream__" then
+ -- do nothing (yet)
+ else
+ n = n + 1
+ r[n] = k
+ end
+ end
+ if n > 1 then
+ sort(r)
+ end
+ for i=1,n do
+ local k = r[i]
+ local v = t[k]
+ local tv = type(v)
+ -- mostly tables
+ if tv == "table" then
+ -- local mv = getmetatable(v)
+ -- if mv and mv.__lpdftype then
+ if v.__lpdftype__ then
+ -- if v == t then
+ -- report_objects("ignoring circular reference in dictionary")
+ -- r[i] = f_key_null(k)
+ -- else
+ r[i] = f_key_value(k,tostring(v))
+ -- end
+ elseif v[1] then
+ r[i] = f_key_value(k,tostring_a(v))
+ else
+ r[i] = f_key_value(k,tostring_d(v))
+ end
+ elseif tv == "string" then
+ r[i] = f_key_value(k,toeight(v))
+ elseif tv == "number" then
+ r[i] = f_key_number(k,v)
+ else
+ r[i] = f_key_value(k,tostring(v))
+ end
+ end
+ if e then
+ r[n+1] = e
+ end
+ r = concat(r," ")
+ if contentonly then
+ return r
+ elseif key then
+ return f_key_dictionary(key,r)
+ else
+ return f_dictionary(r)
+ end
+ elseif contentonly then
+ return ""
+ else
+ return "<< >>"
+ end
+ end
+
+ tostring_a = function(t,contentonly,key)
+ local tn = #t
+ if tn ~= 0 then
+ local r = { }
+ for k=1,tn do
+ local v = t[k]
+ local tv = type(v)
+ -- mostly numbers and tables
+ if tv == "number" then
+ r[k] = f_tonumber(v)
+ elseif tv == "table" then
+ -- local mv = getmetatable(v)
+ -- if mv and mv.__lpdftype then
+ if v.__lpdftype__ then
+ -- if v == t then
+ -- report_objects("ignoring circular reference in array")
+ -- r[k] = "null"
+ -- else
+ r[k] = tostring(v)
+ -- end
+ elseif v[1] then
+ r[k] = tostring_a(v)
+ else
+ r[k] = tostring_d(v)
+ end
+ elseif tv == "string" then
+ r[k] = toeight(v)
+ else
+ r[k] = tostring(v)
+ end
+ end
+ local e = t.__extra__
+ if e then
+ r[tn+1] = e
+ end
+ r = concat(r," ")
+ if contentonly then
+ return r
+ elseif key then
+ return f_key_array(key,r)
+ else
+ return f_array(r)
+ end
+ elseif contentonly then
+ return ""
+ else
+ return "[ ]"
+ end
+ end
+
+end
+
+local f_tonumber = formatters["%N"]
+
+local tostring_x = function(t) return concat(t," ") end
+local tostring_s = function(t) return toeight(t[1]) end
+local tostring_p = function(t) return topdfdoc(t[1],t[2]) end
+local tostring_u = function(t) return tosixteen(t[1]) end
+----- tostring_n = function(t) return tostring(t[1]) end -- tostring not needed
+local tostring_n = function(t) return f_tonumber(t[1]) end -- tostring not needed
+local tostring_c = function(t) return t[1] end -- already prefixed (hashed)
+local tostring_z = function() return "null" end
+local tostring_t = function() return "true" end
+local tostring_f = function() return "false" end
+local tostring_r = function(t) local n = t[1] return n and n > 0 and (n .. " 0 R") or "null" end
+
+local tostring_v = function(t)
+ local s = t[1]
+ if type(s) == "table" then
+ return concat(s)
+ else
+ return s
+ end
+end
+
+local tostring_l = function(t)
+ local s = t[1]
+ if not s or s == "" then
+ return "()"
+ elseif t[2] then
+ return "<" .. s .. ">"
+ else
+ return "(" .. s .. ")"
+ end
+end
+
+local function value_x(t) return t end
+local function value_s(t) return t[1] end
+local function value_p(t) return t[1] end
+local function value_u(t) return t[1] end
+local function value_n(t) return t[1] end
+local function value_c(t) return sub(t[1],2) end
+local function value_d(t) return tostring_d(t,true) end
+local function value_a(t) return tostring_a(t,true) end
+local function value_z() return nil end
+local function value_t(t) return t.value or true end
+local function value_f(t) return t.value or false end
+local function value_r(t) return t[1] or 0 end -- null
+local function value_v(t) return t[1] end
+local function value_l(t) return t[1] end
+
+local function add_to_d(t,v)
+ local k = type(v)
+ if k == "string" then
+ if t.__extra__ then
+ t.__extra__ = t.__extra__ .. " " .. v
+ else
+ t.__extra__ = v
+ end
+ elseif k == "table" then
+ for k, v in next, v do
+ t[k] = v
+ end
+ end
+ return t
+end
+
+local function add_to_a(t,v)
+ local k = type(v)
+ if k == "string" then
+ if t.__extra__ then
+ t.__extra__ = t.__extra__ .. " " .. v
+ else
+ t.__extra__ = v
+ end
+ elseif k == "table" then
+ local n = #t
+ for i=1,#v do
+ n = n + 1
+ t[n] = v[i]
+ end
+ end
+ return t
+end
+
+local function add_x(t,k,v) rawset(t,k,tostring(v)) end
+
+local mt_x = { __index = { __lpdftype__ = "stream" }, __tostring = tostring_x, __call = value_x, __newindex = add_x }
+local mt_d = { __index = { __lpdftype__ = "dictionary" }, __tostring = tostring_d, __call = value_d, __add = add_to_d }
+local mt_a = { __index = { __lpdftype__ = "array" }, __tostring = tostring_a, __call = value_a, __add = add_to_a }
+local mt_u = { __index = { __lpdftype__ = "unicode" }, __tostring = tostring_u, __call = value_u }
+local mt_s = { __index = { __lpdftype__ = "string" }, __tostring = tostring_s, __call = value_s }
+local mt_p = { __index = { __lpdftype__ = "docstring" }, __tostring = tostring_p, __call = value_p }
+local mt_n = { __index = { __lpdftype__ = "number" }, __tostring = tostring_n, __call = value_n }
+local mt_c = { __index = { __lpdftype__ = "constant" }, __tostring = tostring_c, __call = value_c }
+local mt_z = { __index = { __lpdftype__ = "null" }, __tostring = tostring_z, __call = value_z }
+local mt_t = { __index = { __lpdftype__ = "true" }, __tostring = tostring_t, __call = value_t }
+local mt_f = { __index = { __lpdftype__ = "false" }, __tostring = tostring_f, __call = value_f }
+local mt_r = { __index = { __lpdftype__ = "reference" }, __tostring = tostring_r, __call = value_r }
+local mt_v = { __index = { __lpdftype__ = "verbose" }, __tostring = tostring_v, __call = value_v }
+local mt_l = { __index = { __lpdftype__ = "literal" }, __tostring = tostring_l, __call = value_l }
+
+local function pdfstream(t) -- we need to add attributes
+ if t then
+ local tt = type(t)
+ if tt == "table" then
+ for i=1,#t do
+ t[i] = tostring(t[i])
+ end
+ elseif tt == "string" then
+ t = { t }
+ else
+ t = { tostring(t) }
+ end
+ end
+ return setmetatable(t or { },mt_x)
+end
+
+local function pdfdictionary(t)
+ return setmetatable(t or { },mt_d)
+end
+
+local function pdfarray(t)
+ if type(t) == "string" then
+ return setmetatable({ t },mt_a)
+ else
+ return setmetatable(t or { },mt_a)
+ end
+end
+
+local function pdfstring(str,default)
+ return setmetatable({ str or default or "" },mt_s)
+end
+
+local function pdfdocstring(str,default,defaultchar)
+ return setmetatable({ str or default or "", defaultchar or " " },mt_p)
+end
+
+local function pdfunicode(str,default)
+ return setmetatable({ str or default or "" },mt_u) -- could be a string
+end
+
+local function pdfliteral(str,hex) -- can also produce a hex <> instead of () literal
+ return setmetatable({ str, hex },mt_l)
+end
+
+local pdfnumber, pdfconstant
+
+do
+
+ local cache = { } -- can be weak
+
+ pdfnumber = function(n,default) -- 0-10
+ if not n then
+ n = default
+ end
+ local c = cache[n]
+ if not c then
+ c = setmetatable({ n },mt_n)
+ -- cache[n] = c -- too many numbers
+ end
+ return c
+ end
+
+ for i=-1,9 do cache[i] = pdfnumber(i) end
+
+ local replacer = S("\0\t\n\r\f ()[]{}/%%#\\") / {
+ ["\00"]="#00",
+ ["\09"]="#09",
+ ["\10"]="#0a",
+ ["\12"]="#0c",
+ ["\13"]="#0d",
+ [ " " ]="#20",
+ [ "#" ]="#23",
+ [ "%" ]="#25",
+ [ "(" ]="#28",
+ [ ")" ]="#29",
+ [ "/" ]="#2f",
+ [ "[" ]="#5b",
+ [ "\\"]="#5c",
+ [ "]" ]="#5d",
+ [ "{" ]="#7b",
+ [ "}" ]="#7d",
+ } + P(1)
+
+ local escaped = Cs(Cc("/") * replacer^0)
+
+ local cache = table.setmetatableindex(function(t,k)
+ local v = setmetatable({ lpegmatch(escaped,k) }, mt_c)
+ t[k] = v
+ return v
+ end)
+
+ pdfconstant = function(str,default)
+ if not str then
+ str = default or "none"
+ end
+ return cache[str]
+ end
+
+ local escaped = Cs(replacer^0)
+
+ function lpdf.escaped(str)
+ return lpegmatch(escaped,str) or str
+ end
+
+end
+
+local pdfnull, pdfboolean, pdfreference, pdfverbose
+
+do
+
+ local p_null = { } setmetatable(p_null, mt_z)
+ local p_true = { } setmetatable(p_true, mt_t)
+ local p_false = { } setmetatable(p_false,mt_f)
+
+ pdfnull = function()
+ return p_null
+ end
+
+ pdfboolean = function(b,default)
+ if type(b) == "boolean" then
+ return b and p_true or p_false
+ else
+ return default and p_true or p_false
+ end
+ end
+
+ -- print(pdfboolean(false),pdfboolean(false,false),pdfboolean(false,true))
+ -- print(pdfboolean(true),pdfboolean(true,false),pdfboolean(true,true))
+ -- print(pdfboolean(nil,true),pdfboolean(nil,false))
+
+ local r_zero = setmetatable({ 0 },mt_r)
+
+ pdfreference = function(r) -- maybe make a weak table
+ if r and r ~= 0 then
+ return setmetatable({ r },mt_r)
+ else
+ return r_zero
+ end
+ end
+
+ local v_zero = setmetatable({ 0 },mt_v)
+ local v_empty = setmetatable({ "" },mt_v)
+
+ pdfverbose = function(t) -- maybe check for type
+ if t == 0 then
+ return v_zero
+ elseif t == "" then
+ return v_empty
+ else
+ return setmetatable({ t },mt_v)
+ end
+ end
+
+end
+
+lpdf.stream = pdfstream -- THIS WILL PROBABLY CHANGE
+lpdf.dictionary = pdfdictionary
+lpdf.array = pdfarray
+lpdf.docstring = pdfdocstring
+lpdf.string = pdfstring
+lpdf.unicode = pdfunicode
+lpdf.number = pdfnumber
+lpdf.constant = pdfconstant
+lpdf.null = pdfnull
+lpdf.boolean = pdfboolean
+lpdf.reference = pdfreference
+lpdf.verbose = pdfverbose
+lpdf.literal = pdfliteral
+
+-- three priority levels, default=2
+
+local pagefinalizers = { { }, { }, { } }
+local documentfinalizers = { { }, { }, { } }
+
+local pageresources, pageattributes, pagesattributes
+
+local function resetpageproperties()
+ pageresources = pdfdictionary()
+ pageattributes = pdfdictionary()
+ pagesattributes = pdfdictionary()
+end
+
+function lpdf.getpageproperties()
+ return {
+ pageresources = pageresources,
+ pageattributes = pageattributes,
+ pagesattributes = pagesattributes,
+ }
+end
+
+resetpageproperties()
+
+local function addtopageresources (k,v) pageresources [k] = v end
+local function addtopageattributes (k,v) pageattributes [k] = v end
+local function addtopagesattributes(k,v) pagesattributes[k] = v end
+
+lpdf.addtopageresources = addtopageresources
+lpdf.addtopageattributes = addtopageattributes
+lpdf.addtopagesattributes = addtopagesattributes
+
+local function set(where,what,f,when,comment)
+ if type(when) == "string" then
+ when, comment = 2, when
+ elseif not when then
+ when = 2
+ end
+ local w = where[when]
+ w[#w+1] = { f, comment }
+ if trace_finalizers then
+ report_finalizing("%s set: [%s,%s]",what,when,#w)
+ end
+end
+
+local function run(where,what)
+ if trace_finalizers then
+ report_finalizing("start backend, category %a, n %a",what,#where)
+ end
+ for i=1,#where do
+ local w = where[i]
+ for j=1,#w do
+ local wj = w[j]
+ if trace_finalizers then
+ report_finalizing("%s finalizer: [%s,%s] %s",what,i,j,wj[2] or "")
+ end
+ wj[1]()
+ end
+ end
+ if trace_finalizers then
+ report_finalizing("stop finalizing")
+ end
+end
+
+local function registerpagefinalizer(f,when,comment)
+ set(pagefinalizers,"page",f,when,comment)
+end
+
+local function registerdocumentfinalizer(f,when,comment)
+ set(documentfinalizers,"document",f,when,comment)
+end
+
+lpdf.registerpagefinalizer = registerpagefinalizer
+lpdf.registerdocumentfinalizer = registerdocumentfinalizer
+
+function lpdf.finalizepage(shipout)
+ if shipout and not environment.initex then
+ -- resetpageproperties() -- maybe better before
+ run(pagefinalizers,"page")
+ resetpageproperties() -- maybe better before
+ end
+end
+
+function lpdf.finalizedocument()
+ if not environment.initex then
+ run(documentfinalizers,"document")
+ function lpdf.finalizedocument()
+ -- report_finalizing("serious error: the document is finalized multiple times")
+ function lpdf.finalizedocument() end
+ end
+ end
+end
+
+callbacks.register("finish_pdfpage", lpdf.finalizepage)
+callbacks.register("finish_pdffile", lpdf.finalizedocument)
+
+do
+
+ -- some minimal tracing, handy for checking the order
+
+ local function trace_set(what,key)
+ if trace_resources then
+ report_finalizing("setting key %a in %a",key,what)
+ end
+ end
+
+ local function trace_flush(what)
+ if trace_resources then
+ report_finalizing("flushing %a",what)
+ end
+ end
+
+ lpdf.protectresources = true
+
+ local catalog = pdfdictionary { Type = pdfconstant("Catalog") } -- nicer, but when we assign we nil the Type
+ local info = pdfdictionary { Type = pdfconstant("Info") } -- nicer, but when we assign we nil the Type
+ ----- names = pdfdictionary { Type = pdfconstant("Names") } -- nicer, but when we assign we nil the Type
+
+ local function checkcatalog()
+ if not environment.initex then
+ trace_flush("catalog")
+ return true
+ end
+ end
+
+ local function checkinfo()
+ if not environment.initex then
+ trace_flush("info")
+ if lpdf.majorversion() > 1 then
+ for k, v in next, info do
+ if k == "CreationDate" or k == "ModDate" then
+ -- mandate >= 2.0
+ else
+ info[k] = nil
+ end
+ end
+ end
+ return true
+ end
+ end
+
+ local function flushcatalog()
+ if checkcatalog() then
+ catalog.Type = nil
+-- pdfsetcatalog(catalog())
+ end
+ end
+
+ local function flushinfo()
+ if checkinfo() then
+ info.Type = nil
+ end
+ end
+
+ function lpdf.getcatalog()
+ if checkcatalog() then
+ catalog.Type = pdfconstant("Catalog")
+ return pdfreference(pdfimmediateobject(tostring(catalog)))
+ end
+ end
+
+ function lpdf.getinfo()
+ if checkinfo() then
+ return pdfreference(pdfimmediateobject(tostring(info)))
+ end
+ end
+
+ function lpdf.addtocatalog(k,v)
+ if not (lpdf.protectresources and catalog[k]) then
+ trace_set("catalog",k)
+ catalog[k] = v
+ end
+ end
+
+ function lpdf.addtoinfo(k,v)
+ if not (lpdf.protectresources and info[k]) then
+ trace_set("info",k)
+ info[k] = v
+ end
+ end
+
+ local names = pdfdictionary {
+ -- Type = pdfconstant("Names")
+ }
+
+ local function flushnames()
+ if next(names) and not environment.initex then
+ names.Type = pdfconstant("Names")
+ trace_flush("names")
+ lpdf.addtocatalog("Names",pdfreference(pdfimmediateobject(tostring(names))))
+ end
+ end
+
+ function lpdf.addtonames(k,v)
+ if not (lpdf.protectresources and names[k]) then
+ trace_set("names", k)
+ names [k] = v
+ end
+ end
+
+ local r_extgstates, r_colorspaces, r_patterns, r_shades
+ local d_extgstates, d_colorspaces, d_patterns, d_shades
+ local p_extgstates, p_colorspaces, p_patterns, p_shades
+
+ local function checkextgstates () if d_extgstates then addtopageresources("ExtGState", p_extgstates ) end end
+ local function checkcolorspaces() if d_colorspaces then addtopageresources("ColorSpace",p_colorspaces) end end
+ local function checkpatterns () if d_patterns then addtopageresources("Pattern", p_patterns ) end end
+ local function checkshades () if d_shades then addtopageresources("Shading", p_shades ) end end
+
+ local function flushextgstates () if d_extgstates then trace_flush("extgstates") pdfimmediateobject(r_extgstates, tostring(d_extgstates )) end end
+ local function flushcolorspaces() if d_colorspaces then trace_flush("colorspaces") pdfimmediateobject(r_colorspaces,tostring(d_colorspaces)) end end
+ local function flushpatterns () if d_patterns then trace_flush("patterns") pdfimmediateobject(r_patterns, tostring(d_patterns )) end end
+ local function flushshades () if d_shades then trace_flush("shades") pdfimmediateobject(r_shades, tostring(d_shades )) end end
+
+ -- patterns are special as they need resources to so we can get recursive references and in that case
+ -- acrobat doesn't show anything (other viewers handle it well)
+ --
+ -- todo: share them
+ -- todo: force when not yet set
+
+ local pdfgetfontobjectnumber
+
+ updaters.register("backend.update.lpdf",function()
+ pdfgetfontobjectnumber = lpdf.getfontobjectnumber
+ end)
+
+ local f_font = formatters["%s%d"]
+
+ function lpdf.collectedresources(options)
+ local ExtGState = d_extgstates and next(d_extgstates ) and p_extgstates
+ local ColorSpace = d_colorspaces and next(d_colorspaces) and p_colorspaces
+ local Pattern = d_patterns and next(d_patterns ) and p_patterns
+ local Shading = d_shades and next(d_shades ) and p_shades
+ local Font
+ if options and options.patterns == false then
+ Pattern = nil
+ end
+ local fonts = options and options.fonts
+ if fonts and next(fonts) then
+ local prefix = options.fontprefix or "F"
+ Font = pdfdictionary { }
+ for k, v in sortedhash(fonts) do
+ Font[f_font(prefix,v)] = pdfreference(pdfgetfontobjectnumber(k))
+ end
+ end
+ if ExtGState or ColorSpace or Pattern or Shading or Font then
+ local collected = pdfdictionary {
+ ExtGState = ExtGState,
+ ColorSpace = ColorSpace,
+ Pattern = Pattern,
+ Shading = Shading,
+ Font = Font,
+ }
+ if options and options.serialize == false then
+ return collected
+ else
+ return collected()
+ end
+ elseif options and options.notempty then
+ return nil
+ elseif options and options.serialize == false then
+ return pdfdictionary { }
+ else
+ return ""
+ end
+ end
+
+ function lpdf.adddocumentextgstate (k,v)
+ if not d_extgstates then
+ r_extgstates = pdfreserveobject()
+ d_extgstates = pdfdictionary()
+ p_extgstates = pdfreference(r_extgstates)
+ end
+ d_extgstates[k] = v
+ end
+
+ function lpdf.adddocumentcolorspace(k,v)
+ if not d_colorspaces then
+ r_colorspaces = pdfreserveobject()
+ d_colorspaces = pdfdictionary()
+ p_colorspaces = pdfreference(r_colorspaces)
+ end
+ d_colorspaces[k] = v
+ end
+
+ function lpdf.adddocumentpattern(k,v)
+ if not d_patterns then
+ r_patterns = pdfreserveobject()
+ d_patterns = pdfdictionary()
+ p_patterns = pdfreference(r_patterns)
+ end
+ d_patterns[k] = v
+ end
+
+ function lpdf.adddocumentshade(k,v)
+ if not d_shades then
+ r_shades = pdfreserveobject()
+ d_shades = pdfdictionary()
+ p_shades = pdfreference(r_shades)
+ end
+ d_shades[k] = v
+ end
+
+ registerdocumentfinalizer(flushextgstates,3,"extended graphic states")
+ registerdocumentfinalizer(flushcolorspaces,3,"color spaces")
+ registerdocumentfinalizer(flushpatterns,3,"patterns")
+ registerdocumentfinalizer(flushshades,3,"shades")
+
+ registerdocumentfinalizer(flushnames,3,"names") -- before catalog
+ registerdocumentfinalizer(flushcatalog,3,"catalog")
+ registerdocumentfinalizer(flushinfo,3,"info")
+
+ registerpagefinalizer(checkextgstates,3,"extended graphic states")
+ registerpagefinalizer(checkcolorspaces,3,"color spaces")
+ registerpagefinalizer(checkpatterns,3,"patterns")
+ registerpagefinalizer(checkshades,3,"shades")
+
+end
+
+-- in strc-bkm: lpdf.registerdocumentfinalizer(function() structures.bookmarks.place() end,1)
+
+function lpdf.rotationcm(a)
+ local s = sind(a)
+ local c = cosd(a)
+ return format("%.6F %.6F %.6F %.6F 0 0 cm",c,s,-s,c)
+end
+
+-- ! -> universaltime
+
+do
+
+ -- It's a bit of a historical mess here.
+
+ local osdate, ostime, ostimezone = os.date, os.time, os.timezone
+
+ local metadata = nil
+ local timestamp = osdate("%Y-%m-%dT%X") .. ostimezone(true)
+
+ function lpdf.getmetadata()
+ if not metadata then
+ local contextversion = environment.version
+ local luatexversion = format("%1.2f",LUATEXVERSION)
+ local luatexfunctionality = tostring(LUATEXFUNCTIONALITY)
+ metadata = {
+ producer = format("LuaTeX-%s",luatexversion),
+ creator = format("LuaTeX %s %s + ConTeXt LMTX %s",luatexversion,luatexfunctionality,contextversion),
+ luatexversion = luatexversion,
+ contextversion = contextversion,
+ luatexfunctionality = luatexfunctionality,
+ luaversion = tostring(LUAVERSION),
+ platform = os.platform,
+ time = timestamp,
+ }
+ end
+ return metadata
+ end
+
+ function lpdf.settime(n)
+ if n then
+ n = converters.totime(n)
+ if n then
+ converters.settime(n)
+ timestamp = osdate("%Y-%m-%dT%X") .. ostimezone(true) -- probably not ok
+ end
+ end
+ if metadata then
+ metadata.time = timestamp
+ end
+ return timestamp
+ end
+
+ lpdf.settime(tonumber(resolvers.variable("start_time")) or tonumber(resolvers.variable("SOURCE_DATE_EPOCH"))) -- bah
+
+ function lpdf.pdftimestamp(str)
+ local t = type(str)
+ if t == "string" then
+ local Y, M, D, h, m, s, Zs, Zh, Zm = match(str,"^(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([%+%-])(%d%d):(%d%d)$")
+ return Y and format("D:%s%s%s%s%s%s%s%s'%s'",Y,M,D,h,m,s,Zs,Zh,Zm)
+ else
+ return osdate("D:%Y%m%d%H%M%S",t == "number" and str or ostime()) -- maybe "!D..." : universal time
+ end
+ end
+
+ function lpdf.id(date)
+ local banner = environment.jobname or tex.jobname or "unknown"
+ if not date then
+ return banner
+ else
+ return format("%s | %s",banner,timestamp)
+ end
+ end
+
+end
+
+-- return nil is nicer in test prints
+
+function lpdf.checkedkey(t,key,variant)
+ local pn = t and t[key]
+ if pn ~= nil then
+ local tn = type(pn)
+ if tn == variant then
+ if variant == "string" then
+ if pn ~= "" then
+ return pn
+ end
+ elseif variant == "table" then
+ if next(pn) then
+ return pn
+ end
+ else
+ return pn
+ end
+ elseif tn == "string" then
+ if variant == "number" then
+ return tonumber(pn)
+ elseif variant == "boolean" then
+ return isboolean(pn,nil,true)
+ end
+ end
+ end
+ -- return nil
+end
+
+function lpdf.checkedvalue(value,variant) -- code not shared
+ if value ~= nil then
+ local tv = type(value)
+ if tv == variant then
+ if variant == "string" then
+ if value ~= "" then
+ return value
+ end
+ elseif variant == "table" then
+ if next(value) then
+ return value
+ end
+ else
+ return value
+ end
+ elseif tv == "string" then
+ if variant == "number" then
+ return tonumber(value)
+ elseif variant == "boolean" then
+ return isboolean(value,nil,true)
+ end
+ end
+ end
+end
+
+function lpdf.limited(n,min,max,default)
+ if not n then
+ return default
+ else
+ n = tonumber(n)
+ if not n then
+ return default
+ elseif n > max then
+ return max
+ elseif n < min then
+ return min
+ else
+ return n
+ end
+ end
+end
+
+-- The next variant of ActualText is what Taco and I could come up with
+-- eventually. As of September 2013 Acrobat copies okay, Sumatra copies a
+-- question mark, pdftotext injects an extra space and Okular adds a
+-- newline plus space.
+
+-- return formatters["BT /Span << /ActualText (CONTEXT) >> BDC [<feff>] TJ % t EMC ET"](code)
+
+do
+
+ local f_actual_text_p = formatters["BT /Span << /ActualText <feff%s> >> BDC %s EMC ET"]
+ local f_actual_text_b = formatters["BT /Span << /ActualText <feff%s> >> BDC"]
+ local s_actual_text_e = "EMC ET"
+ local f_actual_text_b_not = formatters["/Span << /ActualText <feff%s> >> BDC"]
+ local s_actual_text_e_not = "EMC"
+ local f_actual_text = formatters["/Span <</ActualText %s >> BDC"]
+
+ local context = context
+ local pdfdirect = nodes.pool.directliteral -- we can use nuts.write deep down
+ local tounicode = fonts.mappings.tounicode
+
+ function codeinjections.unicodetoactualtext(unicode,pdfcode)
+ return f_actual_text_p(type(unicode) == "string" and unicode or tounicode(unicode),pdfcode)
+ end
+
+ function codeinjections.startunicodetoactualtext(unicode)
+ return f_actual_text_b(type(unicode) == "string" and unicode or tounicode(unicode))
+ end
+
+ function codeinjections.stopunicodetoactualtext()
+ return s_actual_text_e
+ end
+
+ function codeinjections.startunicodetoactualtextdirect(unicode)
+ return f_actual_text_b_not(type(unicode) == "string" and unicode or tounicode(unicode))
+ end
+
+ function codeinjections.stopunicodetoactualtextdirect()
+ return s_actual_text_e_not
+ end
+
+ implement {
+ name = "startactualtext",
+ arguments = "string",
+ actions = function(str)
+ context(pdfdirect(f_actual_text(tosixteen(str))))
+ end
+ }
+
+ implement {
+ name = "stopactualtext",
+ actions = function()
+ context(pdfdirect("EMC"))
+ end
+ }
+
+ local setstate = nodes.nuts.pool.setstate
+
+ function nodeinjections.startalternate(str)
+ return setstate(f_actual_text(tosixteen(str)))
+ end
+
+ function nodeinjections.stopalternate()
+ return setstate("EMC")
+ end
+
+
+end
+
+-- interface
+
+implement { name = "lpdf_collectedresources", actions = { lpdf.collectedresources, context } }
+implement { name = "lpdf_addtocatalog", arguments = "2 strings", actions = lpdf.addtocatalog }
+implement { name = "lpdf_addtoinfo", arguments = "2 strings", actions = function(a,b,c) lpdf.addtoinfo(a,b,c) end } -- gets adapted
+implement { name = "lpdf_addtonames", arguments = "2 strings", actions = lpdf.addtonames }
+implement { name = "lpdf_addtopageattributes", arguments = "2 strings", actions = lpdf.addtopageattributes }
+implement { name = "lpdf_addtopagesattributes", arguments = "2 strings", actions = lpdf.addtopagesattributes }
+implement { name = "lpdf_addtopageresources", arguments = "2 strings", actions = lpdf.addtopageresources }
+implement { name = "lpdf_adddocumentextgstate", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentextgstate (a,pdfverbose(b)) end }
+implement { name = "lpdf_adddocumentcolorspace", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentcolorspace(a,pdfverbose(b)) end }
+implement { name = "lpdf_adddocumentpattern", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentpattern (a,pdfverbose(b)) end }
+implement { name = "lpdf_adddocumentshade", arguments = "2 strings", actions = function(a,b) lpdf.adddocumentshade (a,pdfverbose(b)) end }
+
+-- more helpers: copy from lepd to lpdf
+
+function lpdf.copyconstant(v)
+ if v ~= nil then
+ return pdfconstant(v)
+ end
+end
+
+function lpdf.copyboolean(v)
+ if v ~= nil then
+ return pdfboolean(v)
+ end
+end
+
+function lpdf.copyunicode(v)
+ if v then
+ return pdfunicode(v)
+ end
+end
+
+function lpdf.copyarray(a)
+ if a then
+ local t = pdfarray()
+ for i=1,#a do
+ t[i] = a(i)
+ end
+ return t
+ end
+end
+
+function lpdf.copydictionary(d)
+ if d then
+ local t = pdfdictionary()
+ for k, v in next, d do
+ t[k] = d(k)
+ end
+ return t
+ end
+end
+
+function lpdf.copynumber(v)
+ return v
+end
+
+function lpdf.copyinteger(v)
+ return v -- maybe checking or round ?
+end
+
+function lpdf.copyfloat(v)
+ return v
+end
+
+function lpdf.copystring(v)
+ if v then
+ return pdfstring(v)
+ end
+end
+
+do
+
+ -- This is obsolete but old viewers might still use it as directive
+ -- for what to send to a postscript printer.
+
+ local a_procset, d_procset
+
+ function lpdf.procset(dict)
+ if not a_procset then
+ a_procset = pdfarray {
+ pdfconstant("PDF"),
+ pdfconstant("Text"),
+ pdfconstant("ImageB"),
+ pdfconstant("ImageC"),
+ pdfconstant("ImageI"),
+ }
+ a_procset = pdfreference(pdfimmediateobject(tostring(a_procset)))
+ end
+ if dict then
+ if not d_procset then
+ d_procset = pdfdictionary {
+ ProcSet = a_procset
+ }
+ d_procset = pdfreference(pdfimmediateobject(tostring(d_procset)))
+ end
+ return d_procset
+ else
+ return a_procset
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-lmt.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-lmt.lmt
new file mode 100644
index 00000000000..fbc002a9327
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-lmt.lmt
@@ -0,0 +1,3019 @@
+if not modules then modules = { } end modules ['lpdf-lmt'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The code below was originally in back-lpd.lua but it makes more sense in
+-- this namespace. I will rename variables.
+--
+-- There is no way that a lua based backend can compete with the original one
+-- for relative simple text runs. And we're talking seconds here on say 500
+-- pages with paragraphs alternativng between three fonts and colors. But such
+-- documents are rare so in practice we are quite okay, especially because in
+-- ConTeXt we can gain quite a bit elsewhere. So, when we loose 30% on such
+-- simple documents, we break even on for instance the manual, and gain 30% on
+-- Thomas's turture test (also for other reasons). But .. who knows what magic
+-- I can cook up in due time.
+
+-- If you consider this complex, watch:
+--
+-- https://www.youtube.com/watch?v=6H-cAzfB2qo
+--
+-- or in distractionmode:
+--
+-- https://www.youtube.com/watch?v=TYuTE_1jvvE
+-- https://www.youtube.com/watch?v=nnicGKX3lvM
+--
+-- For the moment we have to support the built in backend as well as the alternative. So
+-- the next interface is suboptimal and will change at some time. At that moment I will
+-- also optimize and extend.
+
+local type, next, unpack, tonumber, rawget = type, next, unpack, tonumber, rawget
+local char, rep, find = string.char, string.rep, string.find
+local formatters, splitupstring = string.formatters, string.splitup
+local band, extract = bit32.band, bit32.extract
+local concat, sortedhash = table.concat, table.sortedhash
+local setmetatableindex = table.setmetatableindex
+local loaddata = io.loaddata
+
+local bpfactor = number.dimenfactors.bp
+
+-- local md5HEX = md5.HEX
+local osuuid = os.uuid
+local zlibcompress = (xzip or zlib).compress
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+
+local pdfreference = lpdf.reference
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfconstant = lpdf.constant
+local pdfliteral = lpdf.literal -- not to be confused with a whatsit!
+
+local pdfreserveobject
+local pdfpagereference
+local pdfflushobject
+local pdfsharedobject
+local pdfflushstreamobject
+local pdfdeferredobject
+local pdfimmediateobject
+
+local pdfgetfontname
+local pdfgetfontobjectnumber
+
+local pdfgetpagereference
+
+updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+ pdfpagereference = lpdf.pagereference
+ pdfflushobject = lpdf.flushobject
+ pdfsharedobject = lpdf.shareobjectreference
+ pdfflushstreamobject = lpdf.flushstreamobject
+ pdfdeferredobject = lpdf.deferredobject
+ pdfimmediateobject = lpdf.immediateobject
+ --
+ pdfgetfontname = lpdf.getfontname
+ pdfgetfontobjectnumber = lpdf.getfontobjectnumber
+ --
+ pdfgetpagereference = lpdf.getpagereference
+end)
+
+local pdf_pages = pdfconstant("Pages")
+local pdf_page = pdfconstant("Page")
+local pdf_xobject = pdfconstant("XObject")
+local pdf_form = pdfconstant("Form")
+
+local fonthashes = fonts.hashes
+local characters = fonthashes.characters
+local descriptions = fonthashes.descriptions
+local parameters = fonthashes.parameters
+local properties = fonthashes.properties
+
+local report = logs.reporter("backend")
+local report_objects = logs.reporter("backend","objects")
+
+local trace_objects = false trackers.register("backend.objects", function(v) trace_objects = v end)
+local trace_details = false trackers.register("backend.details", function(v) trace_details = v end)
+
+-- we collect them:
+
+local flushers = { }
+
+-- used variables
+
+local pdf_h, pdf_v
+local need_tm, need_tf, cur_tmrx, cur_factor, cur_f, cur_e
+local need_width, need_mode, done_width, done_mode
+local mode
+local f_pdf_cur, f_pdf, fs_cur, fs, f_cur, f_x_scale, f_y_scale
+local tj_delta, cw
+local usedfonts, usedxforms, usedximages, usedxgroups
+local getxformname, getximagename
+local boundingbox, shippingmode, objectnumber
+local tmrx, tmry, tmsx, tmsy, tmtx, tmty
+local cmrx, cmry, cmsx, cmsy, cmtx, cmty
+local tmef
+
+local function usefont(t,k) -- a bit redundant hash
+ local v = pdfgetfontname(k)
+ t[k] = v
+ return v
+end
+
+local function reset_variables(specification)
+ pdf_h, pdf_v = 0, 0
+ cmrx, cmry = 1.0, 1.0
+ cmsx, cmsy = 0.0, 0.0
+ cmtx, cmty = 0.0, 0.0
+ tmrx, tmry = 1.0, 1.0
+ tmsx, tmsy = 0.0, 0.0
+ tmtx, tmty = 0.0, 0.0
+ tmef = 1.0
+ need_tm = false
+ need_tf = false
+ need_width = 0
+ need_mode = 0
+ done_width = false
+ done_mode = false
+ mode = "page"
+ shippingmode = specification.shippingmode
+ objectnumber = specification.objectnumber
+ cur_tmrx = 0.0
+ f_cur = 0
+ f_pdf_cur = 0 -- nullfont
+ f_pdf = 0 -- nullfont
+ fs_cur = 0
+ fs = 0
+ f_x_scale = 1.0
+ f_y_scale = 1.0
+ cur_factor = 0
+ cur_f = false
+ cur_e = false
+ tj_delta = 0.0
+ cw = 0.0
+ usedfonts = setmetatableindex(usefont)
+ usedxforms = { }
+ usedximages = { }
+ -- usedxgroups = { }
+ boundingbox = specification.boundingbox
+end
+
+-- buffer
+
+local buffer = lua.newtable(1024,0) -- { }
+local b = 0
+
+local function reset_buffer()
+ b = 0
+end
+
+-- fonts
+
+local fontcharacters
+local fontdescriptions
+local fontparameters
+local fontproperties
+local usedcharacters = setmetatableindex("table")
+local pdfcharacters
+
+local horizontalmode = true
+local scalefactor = 1
+local threshold = 655360
+local thresfactor = 100
+local tjfactor = 100 / 65536
+
+lpdf.usedcharacters = usedcharacters
+
+function flushers.updatefontstate(font)
+ fontcharacters = characters[font]
+ fontdescriptions = descriptions[font]
+ fontparameters = parameters[font]
+ fontproperties = properties[font]
+ local size = fontparameters.size -- or bad news
+ local designsize = fontparameters.designsize or size
+ pdfcharacters = usedcharacters[font]
+ horizontalmode = fontparameters.writingmode ~= "vertical"
+ scalefactor = (designsize/size) * tjfactor
+ --
+ local fthreshold = fontproperties.threshold
+ threshold = (fthreshold and (size * fthreshold / 100)) or 655360
+ -- when we bolden the threshold should be smaller .. a hack .. i need to redo all this
+ if (fontparameters.extendfactor or 1) == 1 then
+ -- we're probably okay
+ elseif fontparameters.hshift or fontparameters.vshift then
+ -- we could be okay
+ else
+ -- some vf magic going on
+ threshold = threshold / 5
+ end
+end
+
+-- helpers
+
+local f_cm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"]
+local f_tm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N Tm"]
+
+local saved_text_pos_v = 0
+local saved_text_pos_h = 0
+
+local function begin_text()
+ saved_text_pos_h = pdf_h
+ saved_text_pos_v = pdf_v
+ b = b + 1 ; buffer[b] = "BT"
+ need_tf = true
+ need_width = 0
+ need_mode = 0
+ mode = "text"
+end
+
+local function end_text()
+ if done_width then
+ b = b + 1 ; buffer[b] = "0 w"
+ done_width = false
+ end
+ if done_mode then
+ b = b + 1 ; buffer[b] = "0 Tr"
+ done_mode = false
+ end
+ b = b + 1 ; buffer[b] = "ET"
+ pdf_h = saved_text_pos_h
+ pdf_v = saved_text_pos_v
+ mode = "page"
+end
+
+local saved_chararray_pos_h
+local saved_chararray_pos_v
+
+local saved_b = 0
+
+local function begin_chararray()
+ saved_chararray_pos_h = pdf_h
+ saved_chararray_pos_v = pdf_v
+ cw = horizontalmode and saved_chararray_pos_h or - saved_chararray_pos_v
+ tj_delta = 0
+ saved_b = b
+ b = b + 1 ; buffer[b] = " ["
+ mode = "chararray"
+end
+
+local function end_chararray()
+ b = b + 1 ; buffer[b] = "] TJ"
+ buffer[saved_b] = concat(buffer,"",saved_b,b)
+ b = saved_b
+ pdf_h = saved_chararray_pos_h
+ pdf_v = saved_chararray_pos_v
+ mode = "text"
+end
+
+local function begin_charmode()
+ b = b + 1 ; buffer[b] = "<"
+ mode = "char"
+end
+
+local function end_charmode()
+ b = b + 1 ; buffer[b] = ">"
+ mode = "chararray"
+end
+
+local function calc_pdfpos(h,v)
+ -- mostly char
+ if mode == "page" then
+ cmtx = h - pdf_h
+ cmty = v - pdf_v
+ return h ~= pdf_h or v ~= pdf_v
+ elseif mode == "text" then
+ tmtx = h - saved_text_pos_h
+ tmty = v - saved_text_pos_v
+ return h ~= pdf_h or v ~= pdf_v
+ elseif horizontalmode then
+ tmty = v - saved_text_pos_v
+ tj_delta = cw - h
+ return tj_delta ~= 0 or v ~= pdf_v
+ else
+ tmtx = h - saved_text_pos_h
+ tj_delta = cw + v
+ return tj_delta ~= 0 or h ~= pdf_h
+ end
+end
+
+local function pdf_set_pos(h,v)
+ local move = calc_pdfpos(h,v)
+ if move then
+ b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor)
+ pdf_h = pdf_h + cmtx
+ pdf_v = pdf_v + cmty
+ end
+end
+
+local function pdf_reset_pos()
+ if mode == "page" then
+ cmtx = - pdf_h
+ cmty = - pdf_v
+ if pdf_h == 0 and pdf_v == 0 then
+ return
+ end
+ elseif mode == "text" then
+ tmtx = - saved_text_pos_h
+ tmty = - saved_text_pos_v
+ if pdf_h == 0 and pdf_v == 0 then
+ return
+ end
+ elseif horizontalmode then
+ tmty = - saved_text_pos_v
+ tj_delta = cw
+ if tj_delta == 0 and pdf_v == 0 then
+ return
+ end
+ else
+ tmtx = - saved_text_pos_h
+ tj_delta = cw
+ if tj_delta == 0 and pdf_h == 0 then
+ return
+ end
+ end
+ b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor)
+ pdf_h = pdf_h + cmtx
+ pdf_v = pdf_v + cmty
+end
+
+local function pdf_set_pos_temp(h,v)
+ local move = calc_pdfpos(h,v)
+ if move then
+ b = b + 1 ; buffer[b] = f_cm(cmrx, cmsx, cmsy, cmry, cmtx*bpfactor, cmty*bpfactor)
+ end
+end
+
+-- these dummy returns makes using them a bit faster
+
+local function pdf_end_string_nl()
+ if mode == "char" then
+ end_charmode()
+ return end_chararray()
+ elseif mode == "chararray" then
+ return end_chararray()
+ end
+end
+
+local function pdf_goto_textmode()
+ if mode == "page" then
+ pdf_reset_pos()
+ return begin_text()
+ elseif mode ~= "text" then
+ if mode == "char" then
+ end_charmode()
+ return end_chararray()
+ else -- if mode == "chararray" then
+ return end_chararray()
+ end
+ end
+end
+
+local function pdf_goto_pagemode()
+ if mode ~= "page" then
+ if mode == "char" then
+ end_charmode()
+ end_chararray()
+ return end_text()
+ elseif mode == "chararray" then
+ end_chararray()
+ return end_text()
+ elseif mode == "text" then
+ return end_text()
+ end
+ end
+end
+
+local function pdf_goto_fontmode()
+ if mode == "char" then
+ end_charmode()
+ end_chararray()
+ end_text()
+ elseif mode == "chararray" then
+ end_chararray()
+ end_text()
+ elseif mode == "text" then
+ end_text()
+ end
+ pdf_reset_pos()
+ mode = "page"
+end
+
+-- characters
+do
+
+ local round = math.round
+
+ -- across pages ... todo: clean up because we don't need to pass the font
+ -- as fontparameters already has checked / set it we can also have a variable
+ -- for it so
+
+ local naturalwidth = nil
+ local hshift = false
+ local vshift = false
+
+ -- local naturalwidths = setmetatableindex(function(t,font)
+ -- local d = descriptions[font]
+ -- local c = characters[font]
+ -- local f = parameters[font].hfactor or parameters[font].factor
+ -- local v = setmetatableindex(function(t,char)
+ -- local w
+ -- local e = d and d[char]
+ -- if e then
+ -- w = e.width
+ -- if w then
+ -- w = w * f
+ -- end
+ -- end
+ -- if not w then
+ -- e = c and c[char]
+ -- if e then
+ -- w = e.width or 0
+ -- end
+ -- end
+ -- if not w then
+ -- w = 0
+ -- end
+ -- t[char] = w
+ -- return w
+ -- end)
+ -- t[font] = v
+ -- return v
+ -- end)
+
+ -- when changing this, check math: compact-001.tex (rule width)
+
+ local naturalwidths = setmetatableindex(function(t,font)
+ local d = descriptions[font]
+ local c = characters[font]
+ local f = parameters[font].hfactor or parameters[font].factor
+ local v = setmetatableindex(function(t,char)
+ local w
+ local e = c and c[char]
+ if e then
+ w = e.width or 0
+ end
+ if not w then
+ e = d and d[char]
+ if e then
+ w = e.width
+ if w then
+ w = w * f
+ end
+ end
+ end
+ if not w then
+ w = 0
+ end
+ t[char] = w
+ return w
+ end)
+ t[font] = v
+ return v
+ end)
+
+ local function setup_fontparameters(font,factor,f,e,sx,sy)
+ local slant = fontparameters.slantfactor or 0
+ local extend = fontparameters.extendfactor or 1
+ local squeeze = fontparameters.squeezefactor or 1
+ local expand = 1 + factor / 1000000
+ local format = fontproperties.format
+ if e then
+ extend = extend * e
+ end
+ tmef = expand
+ tmrx = expand * extend
+ tmsy = slant
+ tmry = squeeze
+ need_width = fontparameters.width or 0
+ need_mode = fontparameters.mode or 0
+ f_cur = font
+ f_pdf = usedfonts[font] -- cache
+ cur_factor = factor
+ cur_f = f
+ cur_e = e
+ tj_delta = 0
+ f_x_scale = 1.0
+ f_y_scale = 1.0
+ fs = fontparameters.size * bpfactor
+ if f then
+ fs = fs * f
+ end
+ -- kind of special:
+ if format == "opentype" or format == "type1" then
+ fs = fs * 1000 / fontparameters.units -- can we avoid this ?
+ end
+ --
+ f_x_scale = sx
+ if f_x_scale ~= 1.0 then
+ tmrx = tmrx * f_x_scale
+ end
+ f_y_scale = sy
+ if f_y_scale ~= 1.0 then
+ tmry = tmry * f_y_scale
+ end
+ --
+ naturalwidth = naturalwidths[font]
+ --
+ hshift = fontparameters.hshift
+ vshift = fontparameters.vshift
+ end
+
+ local f_width = formatters["%.6N w"]
+ local f_mode = formatters["%i Tr"] -- can be hash
+ local f_font = formatters["/F%i %.6N Tf"] -- can be hash
+
+ local s_width = "0 w"
+ local s_mode = "0 Tr"
+
+ local width_factor = 72.27 / 72000.0
+
+ local function set_font()
+ -- if need_width and need_width ~= 0 then
+ if need_width ~= 0 then
+ b = b + 1 ; buffer[b] = f_width(width_factor*need_width)
+ done_width = true
+ elseif done_width then
+ b = b + 1 ; buffer[b] = s_width
+ done_width = false
+ end
+ -- if need_mode and need_mode ~= 0 then
+ if need_mode ~= 0 then
+ b = b + 1 ; buffer[b] = f_mode(need_mode)
+ done_mode = true
+ elseif done_mode then
+ b = b + 1 ; buffer[b] = s_mode
+ done_mode = false
+ end
+ b = b + 1 ; buffer[b] = f_font(f_pdf,fs)
+ f_pdf_cur = f_pdf
+ fs_cur = fs
+ need_tf = false
+ need_tm = true
+ end
+
+ local function set_textmatrix(h,v)
+ local move = calc_pdfpos(h,v)
+ if need_tm or move then
+ b = b + 1 ; buffer[b] = f_tm(tmrx, tmsx, tmsy, tmry, tmtx*bpfactor, tmty*bpfactor)
+ pdf_h = saved_text_pos_h + tmtx
+ pdf_v = saved_text_pos_v + tmty
+ need_tm = false
+ end
+ cur_tmrx = tmrx
+ end
+
+ local f_hex_4 = formatters["%04X"]
+ local f_hex_2 = formatters["%02X"]
+
+ local h_hex_4 = setmetatableindex(function(t,k) -- we already have this somewhere
+ if k < 256 then -- maybe 512
+ -- not sparse in this range
+ for i=0,255 do
+ t[i] = f_hex_4(i)
+ end
+ return t[k]
+ else
+ local v = f_hex_4(k)
+ t[k] = v
+ return v
+ end
+ end)
+ local h_hex_2 = setmetatableindex(function(t,k) -- we already have this somewhere
+ local v = k < 256 and f_hex_2(k) or "00"
+ t[k] = v
+ return v
+ end)
+
+ -- local trace_threshold = false trackers.register("backends.pdf.threshold", function(v) trace_threshold = v end)
+
+ -- local f_skip = formatters["%.2N"]
+
+ -- I will redo this mess ... we no longer have the mkiv pdf generator that we used in
+ -- luatex (a precursor to lmtx and also for comparison) but only in lmtx now so ...
+ -- time to move on I guess.
+
+ flushers.character = function(current,pos_h,pos_v,pos_r,font,char,data,f,e,factor,sx,sy) -- ,naturalwidth,width)
+ if sx ~= f_x_scale or sy ~= f_y_scale or need_tf or font ~= f_cur or f_pdf ~= f_pdf_cur or fs ~= fs_cur or mode == "page" then
+ pdf_goto_textmode()
+ setup_fontparameters(font,factor,f,e,sx,sy)
+ set_font()
+ elseif cur_tmrx ~= tmrx or cur_factor ~= factor or cur_f ~= f or cur_e ~= e then
+ setup_fontparameters(font,factor,f,e,sx,sy)
+ need_tm = true
+ end
+
+ local move = calc_pdfpos(pos_h,pos_v)
+
+ -- if trace_threshold then
+ -- report(
+ -- "font %i, char %C, factor %i, naturalwidth %p, move %l, tm %l, hpos %p, delta %p, threshold %p, cw %p",
+ -- font,char,factor,naturalwidth[char],move,need_tm,pos_h,tj_delta,threshold,cw
+ -- )
+ -- end
+
+ if move or need_tm then
+ if not need_tm then
+ if horizontalmode then
+ if (saved_text_pos_v + tmty) ~= pdf_v then
+ need_tm = true
+ elseif tj_delta >= threshold or tj_delta <= -threshold then
+ need_tm = true
+ end
+ else
+ if (saved_text_pos_h + tmtx) ~= pdf_h then
+ need_tm = true
+ elseif tj_delta >= threshold or tj_delta <= -threshold then
+ need_tm = true
+ end
+ end
+ end
+
+ if hshift then pos_h = pos_h + hshift end
+ if vshift then pos_v = pos_v - vshift end
+
+ if need_tm then
+ pdf_goto_textmode()
+ set_textmatrix(pos_h,pos_v)
+ begin_chararray()
+ move = calc_pdfpos(pos_h,pos_v)
+ end
+ if move then
+ local d = tj_delta * scalefactor / f_x_scale
+ if d <= -0.5 or d >= 0.5 then
+ if mode == "char" then
+ end_charmode()
+ end
+ b = b + 1 ; buffer[b] = round(d) -- or f_skip(d)
+ end
+ cw = cw - tj_delta
+ end
+ end
+
+ if mode == "chararray" then
+ begin_charmode()
+ end
+
+ cw = cw + naturalwidth[char] * tmef * f_x_scale
+
+ local index = data.index or char
+
+ b = b + 1 ; buffer[b] = font > 0 and h_hex_4[index] or h_hex_2[index]
+
+ if not pdfcharacters[index] then
+ pdfcharacters[index] = true
+ end
+
+ end
+
+ flushers.fontchar = function(font,char,data)
+ local dummy = usedfonts[font]
+ local index = data.index or char
+ if not pdfcharacters[index] then
+ pdfcharacters[index] = true
+ end
+ return dummy
+ end
+
+end
+
+-- literals
+
+local flushliteral do
+
+ local nodeproperties = nodes.properties.data
+ local literalvalues = nodes.literalvalues
+
+ local originliteral_code = literalvalues.origin
+ local pageliteral_code = literalvalues.page
+ local alwaysliteral_code = literalvalues.always
+ local rawliteral_code = literalvalues.raw
+ local textliteral_code = literalvalues.text
+ local fontliteral_code = literalvalues.font
+
+ flushliteral = function(current,pos_h,pos_v)
+ local p = nodeproperties[current]
+ if p then
+ local str = p.data
+ if str and str ~= "" then
+ local mode = p.mode
+ if mode == originliteral_code then
+ pdf_goto_pagemode()
+ pdf_set_pos(pos_h,pos_v)
+ elseif mode == pageliteral_code then
+ pdf_goto_pagemode()
+ elseif mode == textliteral_code then
+ pdf_goto_textmode()
+ elseif mode == fontliteral_code then
+ pdf_goto_fontmode()
+ elseif mode == alwaysliteral_code then -- aka direct
+ pdf_end_string_nl()
+ need_tm = true
+ elseif mode == rawliteral_code then
+ pdf_end_string_nl()
+ else
+ report("invalid literal mode %a when flushing %a",mode,str)
+ return
+ end
+ b = b + 1 ; buffer[b] = str
+ end
+ end
+ end
+
+ flushers.literal = flushliteral
+
+ function lpdf.print(mode,str)
+ -- This only works inside objects, don't change this to flush
+ -- in between. It's different from luatex but okay.
+ if str then
+ mode = literalvalues[mode]
+ else
+ mode, str = originliteral_code, mode
+ end
+ if str and str ~= "" then
+ if mode == originliteral_code then
+ pdf_goto_pagemode()
+ -- pdf_set_pos(pdf_h,pdf_v)
+ elseif mode == pageliteral_code then
+ pdf_goto_pagemode()
+ elseif mode == textliteral_code then
+ pdf_goto_textmode()
+ elseif mode == fontliteral_code then
+ pdf_goto_fontmode()
+ elseif mode == alwaysliteral_code then
+ pdf_end_string_nl()
+ need_tm = true
+ elseif mode == rawliteral_code then
+ pdf_end_string_nl()
+ else
+ report("invalid literal mode %a when flushing %a",mode,str)
+ return
+ end
+ b = b + 1 ; buffer[b] = str
+ end
+ end
+
+end
+
+-- grouping & orientation
+
+do
+
+ local matrices = { }
+ local positions = { }
+ local nofpositions = 0
+ local nofmatrices = 0
+
+ local flushsave = function(current,pos_h,pos_v)
+ nofpositions = nofpositions + 1
+ positions[nofpositions] = { pos_h, pos_v, nofmatrices }
+ pdf_goto_pagemode()
+ pdf_set_pos(pos_h,pos_v)
+ b = b + 1 ; buffer[b] = "q"
+ end
+
+ local flushrestore = function(current,pos_h,pos_v)
+ if nofpositions < 1 then
+ return
+ end
+ local t = positions[nofpositions]
+ -- local h = pos_h - t[1]
+ -- local v = pos_v - t[2]
+ if shippingmode == "page" then
+ nofmatrices = t[3]
+ end
+ pdf_goto_pagemode()
+ pdf_set_pos(pos_h,pos_v)
+ b = b + 1 ; buffer[b] = "Q"
+ nofpositions = nofpositions - 1
+ end
+
+ local nodeproperties = nodes.properties.data
+
+ local s_matrix_0 = "1 0 0 1 0 0 cm"
+ local f_matrix_2 = formatters["%.6N 0 0 %.6N 0 0 cm"]
+ local f_matrix_4 = formatters["%.6N %.6N %.6N %.6N 0 0 cm"]
+
+ local flushsetmatrix = function(current,pos_h,pos_v)
+ local p = nodeproperties[current]
+ if p then
+ local m = p.matrix
+ if m then
+ local rx, sx, sy, ry = unpack(m)
+ local s
+ if not rx then
+ rx = 1
+ elseif rx == 0 then
+ rx = 0.0001
+ end
+ if not ry then
+ ry = 1
+ elseif ry == 0 then
+ ry = 0.0001
+ end
+ if not sx then
+ sx = 0
+ end
+ if not sy then
+ sy = 0
+ end
+ --
+ if sx == 0 and sy == 0 then
+ if rx == 1 and ry == 1 then
+ s = s_matrix_0
+ else
+ s = f_matrix_2(rx,ry)
+ end
+ else
+ s = f_matrix_4(rx,sx,sy,ry)
+ end
+ --
+ if shippingmode == "page" then
+ local tx = pos_h * (1 - rx) - pos_v * sy
+ local ty = pos_v * (1 - ry) - pos_h * sx
+ if nofmatrices > 0 then
+ local t = matrices[nofmatrices]
+ local r_x, s_x, s_y, r_y, te, tf = t[1], t[2], t[3], t[4], t[5], t[6]
+ rx, sx = rx * r_x + sx * s_y, rx * s_x + sx * r_y
+ sy, ry = sy * r_x + ry * s_y, sy * s_x + ry * r_y
+ tx, ty = tx * r_x + ty * s_y, tx * s_x + ty * r_y
+ end
+ nofmatrices = nofmatrices + 1
+ matrices[nofmatrices] = { rx, sx, sy, ry, tx, ty }
+ end
+ --
+ pdf_goto_pagemode()
+ pdf_set_pos(pos_h,pos_v)
+ --
+ b = b + 1
+ buffer[b] = s
+ end
+ end
+ end
+
+ flushers.setmatrix = flushsetmatrix
+ flushers.save = flushsave
+ flushers.restore = flushrestore
+
+ function lpdf.hasmatrix()
+ return nofmatrices > 0
+ end
+
+ function lpdf.getmatrix()
+ if nofmatrices > 0 then
+ return unpack(matrices[nofmatrices])
+ else
+ return 1, 0, 0, 1, 0, 0
+ end
+ end
+
+ flushers.pushorientation = function(orientation,pos_h,pos_v,pos_r)
+ pdf_goto_pagemode()
+ pdf_set_pos(pos_h,pos_v)
+ b = b + 1 ; buffer[b] = "q"
+ if orientation == 1 then
+ b = b + 1 ; buffer[b] = "0 -1 1 0 0 0 cm" -- 90
+ elseif orientation == 2 then
+ b = b + 1 ; buffer[b] = "-1 0 0 -1 0 0 cm" -- 180
+ elseif orientation == 3 then
+ b = b + 1 ; buffer[b] = "0 1 -1 0 0 0 cm" -- 270
+ end
+ end
+
+ flushers.poporientation = function(orientation,pos_h,pos_v,pos_r)
+ pdf_goto_pagemode()
+ pdf_set_pos(pos_h,pos_v)
+ b = b + 1 ; buffer[b] = "Q"
+ end
+
+ --
+
+ flushers.startmatrix = function(current,pos_h,pos_v)
+ flushsave(current,pos_h,pos_v)
+ flushsetmatrix(current,pos_h,pos_v)
+ end
+
+ flushers.stopmatrix = function(current,pos_h,pos_v)
+ flushrestore(current,pos_h,pos_v)
+ end
+
+ flushers.startscaling = function(current,pos_h,pos_v)
+ flushsave(current,pos_h,pos_v)
+ flushsetmatrix(current,pos_h,pos_v)
+ end
+
+ flushers.stopscaling = function(current,pos_h,pos_v)
+ flushrestore(current,pos_h,pos_v)
+ end
+
+ flushers.startrotation = function(current,pos_h,pos_v)
+ flushsave(current,pos_h,pos_v)
+ flushsetmatrix(current,pos_h,pos_v)
+ end
+
+ flushers.stoprotation = function(current,pos_h,pos_v)
+ flushrestore(current,pos_h,pos_v)
+ end
+
+ flushers.startmirroring = function(current,pos_h,pos_v)
+ flushsave(current,pos_h,pos_v)
+ flushsetmatrix(current,pos_h,pos_v)
+ end
+
+ flushers.stopmirroring = function(current,pos_h,pos_v)
+ flushrestore(current,pos_h,pos_v)
+ end
+
+ flushers.startclipping = function(current,pos_h,pos_v)
+ flushsave(current,pos_h,pos_v)
+ -- lpdf.print("origin",formatters["0 w %s W n"](nodeproperties[current].path))
+ pdf_goto_pagemode()
+ b = b + 1 ; buffer[b] = formatters["0 w %s W n"](nodeproperties[current].path)
+ end
+
+ flushers.stopclipping = function(current,pos_h,pos_v)
+ flushrestore(current,pos_h,pos_v)
+ end
+
+end
+
+do
+
+ local nodeproperties = nodes.properties.data
+
+ flushers.setstate = function(current,pos_h,pos_v)
+ local p = nodeproperties[current]
+ if p then
+ local d = p.data
+ if d and d ~= "" then
+ pdf_goto_pagemode()
+ b = b + 1 ; buffer[b] = d
+ end
+ end
+ end
+
+end
+
+-- rules
+
+local flushedxforms = { } -- actually box resources but can also be direct
+local localconverter = nil -- will be set
+
+local flushimage do
+
+ local rulecodes = nodes.rulecodes
+ local newrule = nodes.pool.rule
+
+ local setprop = nuts.setprop
+ local getprop = nuts.getprop
+
+ local getwhd = nuts.getwhd
+ local flushlist = nuts.flush_list
+ local getdata = nuts.getdata
+
+ local normalrule_code = rulecodes.normal
+ local boxrule_code = rulecodes.box
+ local imagerule_code = rulecodes.image
+ local emptyrule_code = rulecodes.empty
+ local userrule_code = rulecodes.user
+ local overrule_code = rulecodes.over
+ local underrule_code = rulecodes.under
+ local fractionrule_code = rulecodes.fraction
+ local radicalrule_code = rulecodes.radical
+ local outlinerule_code = rulecodes.outline
+
+ local processrule = nodes.rules.process
+
+ local f_fm = formatters["/Fm%d Do"]
+ local f_im = formatters["/Im%d Do"]
+ local f_gr = formatters["/Gp%d Do"]
+
+ local s_b = "q"
+ local s_e = "Q"
+
+ local f_v = formatters["[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S"]
+ local f_h = formatters["[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S"]
+
+ local f_f = formatters["0 0 %.6N %.6N re f"]
+ local f_o = formatters["[] 0 d 0 J 0 0 %.6N %.6N re S"]
+ local f_w = formatters["[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S"]
+
+ local f_b = formatters["%.6N w 0 %.6N %.6N %.6N re f"]
+ local f_x = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S"]
+ local f_y = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S %.6N 0 m %.6N 0 l S"]
+
+ -- Historically the index is an object which is kind of bad.
+
+ local boxresources, n = { }, 0
+
+ getxformname = function(index)
+ local l = boxresources[index]
+ if l then
+ return l.name
+ else
+ report("no box resource %S",index)
+ end
+ end
+
+ lpdf.getxformname = getxformname
+
+ local function saveboxresource(box,attributes,resources,immediate,kind,margin)
+ n = n + 1
+ local immediate = true
+ local margin = margin or 0 -- or dimension
+ local objnum = pdfreserveobject()
+ local list = tonut(type(box) == "number" and tex.takebox(box) or box)
+ --
+ local width, height, depth = getwhd(list)
+ --
+ local l = {
+ width = width,
+ height = height,
+ depth = depth,
+ margin = margin,
+ attributes = attributes,
+ resources = resources,
+ list = nil,
+ type = kind,
+ name = n,
+ index = objnum,
+ objnum = objnum,
+ }
+ boxresources[objnum] = l
+ if immediate then
+ localconverter(list,"xform",objnum,l)
+ flushedxforms[objnum] = { true , objnum }
+ flushlist(list)
+ else
+ l.list = list
+ end
+ return objnum
+ end
+
+ local function useboxresource(index,wd,ht,dp)
+ local l = boxresources[index]
+ if l then
+ if wd or ht or dp then
+ wd, ht, dp = wd or 0, ht or 0, dp or 0
+ else
+ wd, ht, dp = l.width, l.height, l.depth
+ end
+ local rule = newrule(wd,ht,dp) -- newboxrule
+ rule.subtype = boxrule_code
+ setprop(tonut(rule),"index",index)
+ return rule, wd, ht, dp
+ else
+ report("no box resource %S",index)
+ end
+ end
+
+ local function getboxresourcedimensions(index)
+ local l = boxresources[index]
+ if l then
+ return l.width, l.height, l.depth, l.margin
+ else
+ report("no box resource %S",index)
+ end
+ end
+
+ local function getboxresourcebox(index)
+ local l = boxresources[index]
+ if l then
+ return l.list
+ end
+ end
+
+ -- updaters.register("backend.update.tex",function()
+ updaters.register("backend.update.lpdf",function()
+ tex.saveboxresource = saveboxresource
+ tex.useboxresource = useboxresource
+ tex.getboxresourcedimensions = getboxresourcedimensions
+ tex.getboxresourcebox = getboxresourcebox
+ end)
+
+ -- a bit of a mess: index is now objnum but that has to change to a proper index
+ -- ... an engine inheritance
+
+ local function flushpdfxform(current,pos_h,pos_v,pos_r,size_h,size_v)
+ -- object properties
+ local objnum = getprop(current,"index")
+ local name = getxformname(objnum)
+ local info = flushedxforms[objnum]
+ local r = boxresources[objnum]
+ if not info then
+ info = { false , objnum }
+ flushedxforms[objnum] = info
+ end
+ local wd, ht, dp = getboxresourcedimensions(objnum)
+ -- or: wd, ht, dp = r.width, r.height, r.depth
+ -- sanity check
+ local htdp = ht + dp
+ if wd == 0 or size_h == 0 or htdp == 0 or size_v == 0 then
+ return
+ end
+ -- calculate scale
+ local rx, ry = 1, 1
+ if wd ~= size_h or htdp ~= size_v then
+ rx = size_h / wd
+ ry = size_v / htdp
+ end
+ -- flush the reference
+ usedxforms[objnum] = true
+ pdf_goto_pagemode()
+ calc_pdfpos(pos_h,pos_v)
+ tx = cmtx * bpfactor
+ ty = cmty * bpfactor
+ b = b + 1 ; buffer[b] = s_b
+ b = b + 1 ; buffer[b] = f_cm(rx,0,0,ry,tx,ty)
+ b = b + 1 ; buffer[b] = f_fm(name)
+ b = b + 1 ; buffer[b] = s_e
+ end
+
+ -- place image also used in vf but we can use a different one if we need it
+
+ local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream
+ local img_none = imagetypes.none
+ local img_pdf = imagetypes.pdf
+ local img_stream = imagetypes.stream
+
+ local one_bp = 65536 * bpfactor
+
+ local imageresources, n = { }, 0
+
+ getximagename = function(index) -- not used
+ local l = imageresources[index]
+ if l then
+ return l.name
+ else
+ report("no image resource %S",index)
+ end
+ end
+
+ -- Groups are flushed immediately but we can decide to make them into a
+ -- specific whatsit ... but not now. We could hash them if needed when
+ -- we use lot sof them in mp ... but not now.
+
+ usedxgroups = { }
+ local groups = 0
+ local group = nil
+
+ local flushgroup = function(content,bbox)
+ if not group then
+ group = pdfdictionary {
+ Type = pdfconstant("Group"),
+ S = pdfconstant("Transparency"),
+ }
+ end
+ local wrapper = pdfdictionary {
+ Type = pdf_xobject,
+ Subtype = pdf_form,
+ FormType = 1,
+ Group = group,
+ BBox = pdfarray(bbox),
+ Resources = lpdf.collectedresources { serialize = false },
+ }
+ local objnum = pdfflushstreamobject(content,wrapper,false)
+ groups = groups + 1
+ usedxgroups[groups] = objnum
+ return f_gr(groups)
+ end
+
+ flushers.group = flushgroup
+ lpdf.flushgroup = flushgroup -- todo: access via driver in mlib-pps
+
+ -- end of experiment
+
+ local pdfincludeimage
+
+ updaters.register("backend.update.lpdf",function()
+ pdfincludeimage = lpdf.includeimage
+ end)
+
+ local function flushpdfximage(current,pos_h,pos_v,pos_r,size_h,size_v)
+
+ local width,
+ height,
+ depth = getwhd(current)
+ local total = height + depth
+ local transform = getprop(current,"transform") or 0 -- we never set it ... so just use rotation then
+ local index = getprop(current,"index") or 0
+ local kind,
+ xorigin,
+ yorigin,
+ xsize,
+ ysize,
+ rotation, -- transform / orientation / rotation : it's a mess (i need to redo this)
+ objnum,
+ groupref = pdfincludeimage(index) -- needs to be sorted out, bad name (no longer mixed anyway)
+
+ if not kind then
+ report("invalid image %S",index)
+ return
+ end
+
+ local rx, sx, sy, ry, tx, ty = 1, 0, 0, 1, 0, 0
+
+ -- tricky: xsize and ysize swapped
+
+ if kind == img_pdf or kind == img_stream then
+ rx, ry, tx, ty = 1/xsize, 1/ysize, xorigin/xsize, yorigin/ysize
+ else
+ -- if kind == img_png then
+ -- -- if groupref > 0 and img_page_group_val == 0 then
+ -- -- img_page_group_val = groupref
+ -- -- end
+ -- end
+ rx, ry = bpfactor, bpfactor
+ end
+
+ if band(transform,7) > 3 then
+ -- mirror
+ rx, tx = -rx, -tx
+ end
+ local t = band(transform + rotation,3)
+ if t == 0 then
+ -- nothing
+ elseif t == 1 then
+ -- rotation over 90 degrees (counterclockwise)
+ rx, sx, sy, ry, tx, ty = 0, rx, -ry, 0, -ty, tx
+ elseif t == 2 then
+ -- rotation over 180 degrees (counterclockwise)
+ rx, ry, tx, ty = -rx, -ry, -tx, -ty
+ elseif t == 3 then
+ -- rotation over 270 degrees (counterclockwise)
+ rx, sx, sy, ry, tx, ty = 0, -rx, ry, 0, ty, -tx
+ end
+
+ rx = rx * width
+ sx = sx * total
+ sy = sy * width
+ ry = ry * total
+ tx = pos_h - tx * width
+ ty = pos_v - ty * total
+
+ local t = transform + rotation
+
+ if band(transform,7) > 3 then
+ t = t + 1
+ end
+
+ t = band(t,3)
+
+ if t == 0 then
+ -- no transform
+ elseif t == 1 then
+ -- rotation over 90 degrees (counterclockwise)
+ tx = tx + width
+ elseif t == 2 then
+ -- rotation over 180 degrees (counterclockwise)
+ tx = tx + width
+ ty = ty + total
+ elseif t == 3 then
+ -- rotation over 270 degrees (counterclockwise)
+ ty = ty + total
+ end
+
+ -- a flaw in original, can go:
+ --
+ -- if img_page_group_val == 0 then
+ -- img_page_group_val = group_ref
+ -- end
+
+ usedximages[index] = objnum -- hm
+
+ pdf_goto_pagemode()
+
+ calc_pdfpos(tx,ty)
+
+ tx = cmtx * bpfactor
+ ty = cmty * bpfactor
+
+ b = b + 1 ; buffer[b] = s_b
+ b = b + 1 ; buffer[b] = f_cm(rx,sx,sy,ry,tx,ty)
+ b = b + 1 ; buffer[b] = f_im(index)
+ b = b + 1 ; buffer[b] = s_e
+ end
+
+ flushimage = function(index,width,height,depth,pos_h,pos_v)
+
+ -- used in vf characters
+
+ local total = height + depth
+ local kind,
+ xorigin, yorigin,
+ xsize, ysize,
+ rotation,
+ objnum,
+ groupref = pdfincludeimage(index)
+
+ local rx = width / xsize
+ local sx = 0
+ local sy = 0
+ local ry = total / ysize
+ local tx = pos_h
+ -- to be sorted out
+ -- local ty = pos_v - depth
+ local ty = pos_v -- we assume that depth is dealt with in the caller (for now)
+
+ usedximages[index] = objnum
+
+ pdf_goto_pagemode()
+
+ calc_pdfpos(tx,ty)
+
+ tx = cmtx * bpfactor
+ ty = cmty * bpfactor
+
+ b = b + 1 ; buffer[b] = s_b
+ b = b + 1 ; buffer[b] = f_cm(rx,sx,sy,ry,tx,ty)
+ b = b + 1 ; buffer[b] = f_im(index)
+ b = b + 1 ; buffer[b] = s_e
+ end
+
+ flushers.image = flushimage
+
+ -- For the moment we need this hack because the engine checks the 'image'
+ -- command in virtual fonts (so we use lua instead).
+ --
+ -- These will be replaced by a new more advanced one ... some day ... or
+ -- never because the next are like the other engines and compensate for
+ -- small sizes which is needed for inaccurate viewers.
+
+ flushers.rule = function(current,pos_h,pos_v,pos_r,size_h,size_v,subtype)
+
+ if subtype == emptyrule_code then
+ return
+ elseif subtype == boxrule_code then
+ return flushpdfxform(current,pos_h,pos_v,pos_r,size_h,size_v)
+ elseif subtype == imagerule_code then
+ return flushpdfximage(current,pos_h,pos_v,pos_r,size_h,size_v)
+ end
+ if subtype == userrule_code or subtype >= overrule_code and subtype <= radicalrule_code then
+ pdf_goto_pagemode()
+ b = b + 1 ; buffer[b] = s_b
+ pdf_set_pos_temp(pos_h,pos_v)
+ processrule(current,size_h,size_v,pos_r) -- so we pass direction
+ b = b + 1 ; buffer[b] = s_e
+ return
+ end
+
+ pdf_goto_pagemode()
+
+ -- local saved_b = b
+
+ b = b + 1 ; buffer[b] = s_b
+
+ local dim_h = size_h * bpfactor
+ local dim_v = size_v * bpfactor
+ local rule
+
+ if dim_v <= one_bp then
+ pdf_set_pos_temp(pos_h,pos_v + 0.5 * size_v)
+ rule = f_v(dim_v,dim_h)
+ elseif dim_h <= one_bp then
+ pdf_set_pos_temp(pos_h + 0.5 * size_h,pos_v)
+ rule = f_h(dim_h,dim_v)
+ else
+ pdf_set_pos_temp(pos_h,pos_v)
+ if subtype == outlinerule_code then
+ local linewidth = getdata(current)
+ if linewidth > 0 then
+ rule = f_w(linewidth * bpfactor,dim_h,dim_v)
+ else
+ rule = f_o(dim_h,dim_v)
+ end
+ else
+ rule = f_f(dim_h,dim_v)
+ end
+ end
+
+ b = b + 1 ; buffer[b] = rule
+ b = b + 1 ; buffer[b] = s_e
+
+ -- buffer[saved_b] = concat(buffer," ",saved_b,b)
+ -- b = saved_b
+
+ end
+
+ flushers.simplerule = function(pos_h,pos_v,pos_r,size_h,size_v)
+ pdf_goto_pagemode()
+
+ b = b + 1 ; buffer[b] = s_b
+
+ local dim_h = size_h * bpfactor
+ local dim_v = size_v * bpfactor
+ local rule
+
+ if dim_v <= one_bp then
+ pdf_set_pos_temp(pos_h,pos_v + 0.5 * size_v)
+ rule = f_v(dim_v,dim_h)
+ elseif dim_h <= one_bp then
+ pdf_set_pos_temp(pos_h + 0.5 * size_h,pos_v)
+ rule = f_h(dim_h,dim_v)
+ else
+ pdf_set_pos_temp(pos_h,pos_v)
+ rule = f_f(dim_h,dim_v)
+ end
+
+ b = b + 1 ; buffer[b] = rule
+ b = b + 1 ; buffer[b] = s_e
+ end
+
+ flushers.specialrule = function(pos_h,pos_v,pos_r,width,height,depth,line,outline,baseline)
+ pdf_goto_pagemode()
+
+ b = b + 1 ; buffer[b] = s_b
+
+ local width = bpfactor * width
+ local height = bpfactor * height
+ local depth = bpfactor * depth
+ local total = height + depth
+ local line = bpfactor * line
+ local half = line / 2
+ local rule
+
+ if outline then
+ local d = -depth + half
+ local w = width - line
+ local t = total - line
+ if baseline then
+ rule = f_y(line,half,d,w,t,half,w)
+ else
+ rule = f_x(line,half,d,w,t)
+ end
+ else
+ rule = f_b(line,-depth,width,total)
+ end
+ pdf_set_pos_temp(pos_h,pos_v)
+
+ b = b + 1 ; buffer[b] = rule
+ b = b + 1 ; buffer[b] = s_e
+ end
+
+end
+
+--- basics
+
+local wrapup, registerpage do
+
+ local pages = { }
+ local maxkids = 10
+ local nofpages = 0
+ local pagetag = "unset"
+
+ registerpage = function(object)
+ nofpages = nofpages + 1
+ local objnum = pdfpagereference(nofpages)
+ pages[nofpages] = {
+ page = nofpages, -- original number, only for diagnostics
+ objnum = objnum,
+ object = object,
+ tag = pagetag,
+ }
+ end
+
+ function lpdf.setpagetag(tag)
+ pagetag = tag or "unset"
+ end
+
+ function lpdf.getnofpages()
+ return nofpages
+ end
+
+ function lpdf.getpagetags()
+ local list = { }
+ for i=1,nofpages do
+ list[i] = pages[i].tag
+ end
+ return list
+ end
+
+ function lpdf.setpageorder(mapping)
+ -- mapping can be a hash so:
+ local list = table.sortedkeys(mapping)
+ local n = #list
+ if n == nofpages then
+ local done = { }
+ local hash = { }
+ for i=1,n do
+ local order = mapping[list[i]]
+ if hash[order] then
+ report("invalid page order, duplicate entry %i",order)
+ return
+ elseif order < 1 or order > nofpages then
+ report("invalid page order, no page %i",order)
+ return
+ else
+ done[i] = pages[order]
+ hash[order] = true
+ end
+ end
+ pages = done
+ else
+ report("invalid page order, %i entries expected",nofpages)
+ end
+ end
+
+ -- We can have this, but then via codeinjections etc. Later.
+
+ -- function structures.pages.swapthem()
+ -- local n = lpdf.getnofpages()
+ -- local t = { }
+ -- for i=1,n do
+ -- t[i] = i
+ -- end
+ -- for i=2,math.odd(n) and n or (n-1),2 do
+ -- t[i] = i+1
+ -- t[i+1] = i
+ -- end
+ -- lpdf.setpageorder(t)
+ -- end
+
+ wrapup = function(driver)
+
+ -- hook (to reshuffle pages)
+ local pagetree = { }
+ local parent = nil
+ local minimum = 0
+ local maximum = 0
+ local current = 0
+ if #pages > 1.5 * maxkids then
+ repeat
+ local plist, pnode
+ if current == 0 then
+ plist, minimum = pages, 1
+ elseif current == 1 then
+ plist, minimum = pagetree, 1
+ else
+ plist, minimum = pagetree, maximum + 1
+ end
+ maximum = #plist
+ if maximum > minimum then
+ local kids
+ for i=minimum,maximum do
+ local p = plist[i]
+ if not pnode or #kids == maxkids then
+ kids = pdfarray()
+ parent = pdfreserveobject()
+ pnode = pdfdictionary {
+ objnum = parent,
+ Type = pdf_pages,
+ Kids = kids,
+ Count = 0,
+ }
+ pagetree[#pagetree+1] = pnode
+ end
+ kids[#kids+1] = pdfreference(p.objnum)
+ pnode.Count = pnode.Count + (p.Count or 1)
+ p.Parent = pdfreference(parent)
+ end
+ end
+ current = current + 1
+ until maximum == minimum
+ -- flush page tree
+ for i=1,#pagetree do
+ local entry = pagetree[i]
+ local objnum = entry.objnum
+ entry.objnum = nil
+ pdfflushobject(objnum,entry)
+ end
+ else
+ -- ugly
+ local kids = pdfarray()
+ local list = pdfdictionary {
+ Type = pdf_pages,
+ Kids = kids,
+ Count = nofpages,
+ }
+ parent = pdfreserveobject()
+ for i=1,nofpages do
+ local page = pages[i]
+ kids[#kids+1] = pdfreference(page.objnum)
+ page.Parent = pdfreference(parent)
+ end
+ pdfflushobject(parent,list)
+ end
+ for i=1,nofpages do
+ local page = pages[i]
+ local object = page.object
+ object.Parent = page.Parent
+ pdfflushobject(page.objnum,object)
+ end
+ lpdf.addtocatalog("Pages",pdfreference(parent))
+
+ end
+
+end
+
+pdf_h, pdf_v = 0, 0
+
+local function initialize(driver,details)
+ reset_variables(details)
+ reset_buffer()
+end
+
+-- This will all move and be merged and become less messy.
+
+-- todo: more clever resource management: a bit tricky as we can inject
+-- stuff in the page stream
+
+local compact = false
+
+do
+
+ -- This is more a convenience feature and it might even be not entirely robust.
+ -- It removes redundant color directives which makes the page stream look a bit
+ -- nicer (also when figuring out issues). I might add more here but there is
+ -- some additional overhead involved so runtime can be impacted.
+
+ local P, R, S, Cs, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.Cs, lpeg.match
+
+ local p_ds = (R("09") + S(" ."))^1
+ ----- p_nl = S("\n\r")^1
+ local p_nl = S("\n")^1
+ local p_eg = P("Q")
+
+ local p_cl = p_ds * (P("rg") + P("g") + P("k")) * p_ds * (P("RG") + P("G") + P("K"))
+ ----- p_cl = (p_ds * (P("rg") + P("g") + P("k") + P("RG") + P("G") + P("K")))^1
+ local p_tr = P("/Tr") * p_ds * P("gs")
+
+ local p_no_cl = (p_cl * p_nl) / ""
+ local p_no_tr = (p_tr * p_nl) / ""
+ local p_no_nl = 1 - p_nl
+
+ local p_do_cl = p_cl * p_nl
+ local p_do_tr = p_tr * p_nl
+
+ local p_do_eg = p_eg * p_nl
+
+ local pattern = Cs( (
+ (p_no_cl + p_no_tr)^0 * p_do_eg -- transparencies and colors before Q
+ + p_no_tr * p_no_cl * p_do_tr * p_do_cl -- transparencies and colors before others
+ + p_no_cl * p_do_cl -- successive colors
+ + p_no_tr * p_do_tr -- successive transparencies
+ + p_no_nl^1
+ + 1
+ )^1 )
+
+ local oldsize = 0
+ local newsize = 0
+
+ directives.register("pdf.compact", function(v)
+ compact = v and function(s)
+ oldsize = oldsize + #s
+ s = lpegmatch(pattern,s) or s
+ newsize = newsize + #s
+ return s
+ end
+ end)
+
+ statistics.register("pdf pagestream",function()
+ if oldsize ~= newsize then
+ return string.format("old size: %i, new size %i",oldsize,newsize)
+ end
+ end)
+
+
+end
+
+local flushdeferred -- defined later
+
+local level = 0
+
+local finalize do
+
+ local f_font = formatters["F%d"]
+
+ local f_form = formatters["Fm%d"]
+ local f_group = formatters["Gp%d"]
+ local f_image = formatters["Im%d"]
+
+ finalize = function(driver,details)
+
+ level = level + 1
+
+ pdf_goto_pagemode() -- for now
+
+ local objnum = details.objnum
+ local specification = details.specification
+
+ local content = concat(buffer,"\n",1,b)
+
+ if compact then
+ content = compact(content)
+ end
+
+ local fonts = nil
+ local xforms = nil
+
+ if next(usedfonts) then
+ fonts = pdfdictionary { }
+ for k, v in next, usedfonts do
+ fonts[f_font(v)] = pdfreference(pdfgetfontobjectnumber(k)) -- we can overload for testing
+ end
+ end
+
+ -- messy: use real indexes for both ... so we need to change some in the
+ -- full luatex part
+
+ if next(usedxforms) or next(usedximages) or next(usedxgroups) then
+ xforms = pdfdictionary { }
+ for k in sortedhash(usedxforms) do
+ xforms[f_form(getxformname(k))] = pdfreference(k)
+ end
+ for k, v in sortedhash(usedximages) do
+ xforms[f_image(k)] = pdfreference(v)
+ end
+ for k, v in sortedhash(usedxgroups) do
+ xforms[f_group(k)] = pdfreference(v)
+ end
+ end
+
+ reset_buffer()
+
+ -- finish_pdfpage_callback(shippingmode == "page")
+
+ if shippingmode == "page" then
+
+ local pageproperties = lpdf.getpageproperties()
+
+ local pageresources = pageproperties.pageresources
+ local pageattributes = pageproperties.pageattributes
+ local pagesattributes = pageproperties.pagesattributes
+
+ pageresources.Font = fonts
+ pageresources.XObject = xforms
+ pageresources.ProcSet = lpdf.procset()
+
+ local bbox = pdfarray {
+ boundingbox[1] * bpfactor,
+ boundingbox[2] * bpfactor,
+ boundingbox[3] * bpfactor,
+ boundingbox[4] * bpfactor,
+ }
+
+ local contentsobj = pdfflushstreamobject(content,false,false)
+
+ pageattributes.Type = pdf_page
+ pageattributes.Contents = pdfreference(contentsobj)
+ pageattributes.Resources = pageresources
+ -- pageattributes.Resources = pdfreference(pdfflushobject(pageresources))
+ -- pageattributes.MediaBox = bbox
+ pageattributes.MediaBox = pdfsharedobject(bbox)
+ pageattributes.Parent = nil -- precalculate
+ pageattributes.Group = nil -- todo
+
+ -- resources can be indirect
+
+ registerpage(pageattributes)
+
+ lpdf.finalizepage(true)
+
+ local TrimBox = pageattributes.TrimBox
+ local CropBox = pageattributes.CropBox
+ local BleedBox = pageattributes.BleedBox
+
+ -- Indirect objects don't work in all viewers.
+
+ if TrimBox then pageattributes.TrimBox = pdfsharedobject(TrimBox ) end
+ if CropBox then pageattributes.CropBox = pdfsharedobject(CropBox ) end
+ if BleedBox then pageattributes.BleedBox = pdfsharedobject(BleedBox) end
+
+ else
+
+ local xformtype = specification.type or 0
+ local margin = specification.margin or 0
+ local attributes = specification.attributes or ""
+ local resources = specification.resources or ""
+
+ local wrapper = nil
+
+ if xformtype == 0 then
+ wrapper = pdfdictionary {
+ Type = pdf_xobject,
+ Subtype = pdf_form,
+ FormType = 1,
+ BBox = nil,
+ Matrix = nil,
+ Resources = nil,
+ }
+ else
+ wrapper = pdfdictionary {
+ BBox = nil,
+ Matrix = nil,
+ Resources = nil,
+ }
+ end
+ if xformtype == 0 or xformtype == 1 or xformtype == 3 then
+ wrapper.BBox = pdfarray {
+ -margin * bpfactor,
+ -margin * bpfactor,
+ (boundingbox[3] + margin) * bpfactor,
+ (boundingbox[4] + margin) * bpfactor,
+ }
+ end
+ if xformtype == 0 or xformtype == 2 or xformtype == 3 then
+ -- can be shared too
+ wrapper.Matrix = pdfarray { 1, 0, 0, 1, 0, 0 }
+ end
+
+ -- todo: additional = resources
+
+ local boxresources = lpdf.collectedresources { serialize = false }
+ boxresources.Font = fonts
+ boxresources.XObject = xforms
+
+ -- todo: maybe share them
+ -- wrapper.Resources = pdfreference(pdfflushobject(boxresources))
+
+ if resources ~= "" then
+ boxresources = boxresources + resources
+ end
+ if attributes ~= "" then
+ wrapper = wrapper + attributes
+ end
+
+ wrapper.Resources = next(boxresources) and boxresources or nil
+ wrapper.ProcSet = lpdf.procset()
+
+ pdfflushstreamobject(content,wrapper,false,specification.objnum)
+
+ end
+
+ for objnum in sortedhash(usedxforms) do
+ local f = flushedxforms[objnum]
+ if f[1] == false then
+ f[1] = true
+ local objnum = f[2] -- specification.objnum
+ local specification = boxresources[objnum]
+ local list = specification.list
+ localconverter(list,"xform",f[2],specification)
+ end
+ end
+
+ pdf_h, pdf_v = 0, 0
+
+ if level == 1 then
+ flushdeferred()
+ end
+ level = level - 1
+
+ end
+
+end
+
+updaters.register("backend.update",function()
+ local saveboxresource = tex.boxresources.save
+ --
+ -- also in lpdf-res .. brrr .. needs fixing
+ --
+ backends.codeinjections.registerboxresource = function(n,offset)
+ local r = saveboxresource(n,nil,nil,false,0,offset or 0)
+ return r
+ end
+end)
+
+-- now comes the pdf file handling
+
+local objects = { }
+local streams = { } -- maybe just parallel to objects (no holes)
+local nofobjects = 0
+local offset = 0
+local f = false
+local flush = false
+local threshold = 40 -- also #("/Filter /FlateDecode") (compression threshold)
+local objectstream = true
+local compress = true
+local cache = false
+local info = ""
+local catalog = ""
+local lastdeferred = false
+local majorversion = 1
+local minorversion = 7
+local trailerid = true
+
+directives.register("backend.pdf.threshold",function(v)
+ if v then
+ threshold = tonumber(v) or 40
+ else
+ threshold = -1000
+ end
+end)
+
+local f_object = formatters["%i 0 obj\010%s\010endobj\010"]
+local f_stream_n_u = formatters["%i 0 obj\010<< /Length %i >>\010stream\010%s\010endstream\010endobj\010"]
+local f_stream_n_c = formatters["%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010"]
+local f_stream_d_u = formatters["%i 0 obj\010<< %s /Length %i >>\010stream\010%s\010endstream\010endobj\010"]
+local f_stream_d_c = formatters["%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010"]
+local f_stream_d_r = formatters["%i 0 obj\010<< %s >>\010stream\010%s\010endstream\010endobj\010"]
+
+----- f_object_b = formatters["%i 0 obj\010"]
+local f_stream_b_n_u = formatters["%i 0 obj\010<< /Length %i >>\010stream\010"]
+local f_stream_b_n_c = formatters["%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010"]
+local f_stream_b_d_u = formatters["%i 0 obj\010<< %s /Length %i >>\010stream\010"]
+local f_stream_b_d_c = formatters["%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010"]
+local f_stream_b_d_r = formatters["%i 0 obj\010<< %s >>\010stream\010"]
+
+----- s_object_e = "\010endobj\010"
+local s_stream_e = "\010endstream\010endobj\010"
+
+do
+
+ -- Versions can be set but normally are managed by the official standards. When possible
+ -- reading and writing should look at these values.
+
+ function lpdf.setversion(major,minor)
+ majorversion = tonumber(major) or majorversion
+ minorversion = tonumber(minor) or minorversion
+ end
+
+ function lpdf.getversion(major,minor)
+ return majorversion, minorversion
+ end
+
+ function lpdf.majorversion() return majorversion end
+ function lpdf.minorversion() return minorversion end
+
+ -- It makes no sense to support levels so we only enable and disable and stick to level 3
+ -- which is both fast and efficient.
+
+ local frozen = false
+ local clevel = 3
+ local olevel = 1
+
+ function lpdf.setcompression(level,objectlevel,freeze)
+ if not frozen then
+ compress = level and level ~= 0 and true or false
+ objectstream = objectlevel and objectlevel ~= 0 and true or false
+ frozen = freeze
+ end
+ end
+
+ function lpdf.getcompression()
+ return compress and olevel or 0, objectstream and clevel or 0
+ end
+
+ function lpdf.compresslevel()
+ return compress and olevel or 0
+ end
+
+ function lpdf.objectcompresslevel()
+ return objectstream and clevel or 0
+ end
+
+ if environment.arguments.nocompression then
+ lpdf.setcompression(0,0,true)
+ end
+
+end
+
+local addtocache, flushcache, cache do
+
+ local data, d = { }, 0
+ local list, l = { }, 0
+ local coffset = 0
+ local indices = { }
+
+ local maxsize = 32 * 1024 -- uncompressed
+ local maxcount = 0xFF
+
+ addtocache = function(n,str)
+ local size = #str
+ if size == 0 then
+ -- todo: message
+ return
+ end
+ if coffset + size > maxsize or d == maxcount then
+ flushcache()
+ end
+ if d == 0 then
+ nofobjects = nofobjects + 1
+ objects[nofobjects] = false
+ streams[nofobjects] = indices
+ cache = nofobjects
+ end
+ objects[n] = - cache
+ indices[n] = d
+ d = d + 1
+ -- can have a comment n 0 obj as in luatex
+ data[d] = str
+ l = l + 1 ; list[l] = n
+ l = l + 1 ; list[l] = coffset
+ coffset = coffset + size + 1
+ end
+
+ local p_ObjStm = pdfconstant("ObjStm")
+
+ flushcache = function() -- references cannot be stored
+ if l > 0 then
+ list = concat(list," ")
+ data[0] = list
+ data = concat(data,"\010",0,d)
+ local strobj = pdfdictionary {
+ Type = p_ObjStm,
+ N = d,
+ First = #list + 1,
+ }
+ objects[cache] = offset
+ local b = nil
+ local e = s_stream_e
+ if compress then
+ local comp = zlibcompress(data,3)
+ if comp and #comp < #data then
+ data = comp
+ b = f_stream_b_d_c(cache,strobj(),#data)
+ else
+ b = f_stream_b_d_u(cache,strobj(),#data)
+ end
+ else
+ b = f_stream_b_d_u(cache,strobj(),#data)
+ end
+ flush(f,b)
+ flush(f,data)
+ flush(f,e)
+ offset = offset + #b + #data + #e
+ data, d = { }, 0
+ list, l = { }, 0
+ coffset = 0
+ indices = { }
+ end
+ end
+
+end
+
+do
+
+ local names = { }
+ local cache = { }
+ local nofpages = 0
+
+ local texgetcount = tex.getcount
+
+ function lpdf.reserveobject(name)
+ nofobjects = nofobjects + 1
+ objects[nofobjects] = false
+ if name then
+ names[name] = nofobjects
+ if trace_objects then
+ report_objects("reserving number %a under name %a",nofobjects,name)
+ end
+ elseif trace_objects then
+ report_objects("reserving number %a",nofobjects)
+ end
+ return nofobjects
+ end
+
+ function lpdf.pagereference(n,complete) -- true | false | nil | n [true,false]
+ if nofpages == 0 then
+ nofpages = structures.pages.nofpages
+ if nofpages == 0 then
+ nofpages = 1
+ end
+ end
+ if n == true or not n then
+ complete = n
+ n = texgetcount("realpageno")
+ end
+ local r = n > nofpages and pdfgetpagereference(nofpages) or pdfgetpagereference(n)
+ return complete and pdfreference(r) or r
+ end
+
+ function lpdf.nofpages()
+ return structures.pages.nofpages
+ end
+
+ function lpdf.object(...)
+ pdfdeferredobject(...)
+ end
+
+ function lpdf.delayedobject(data,n)
+ if n then
+ pdfdeferredobject(n,data)
+ else
+ n = pdfdeferredobject(data)
+ end
+-- pdfreferenceobject(n)
+ return n
+ end
+
+ function lpdf.flushobject(name,data)
+ if data then
+ local named = names[name]
+ if named then
+ if not trace_objects then
+ elseif trace_details then
+ report_objects("flushing data to reserved object with name %a, data: %S",name,data)
+ else
+ report_objects("flushing data to reserved object with name %a",name)
+ end
+ return pdfimmediateobject(named,tostring(data))
+ else
+ if not trace_objects then
+ elseif trace_details then
+ report_objects("flushing data to reserved object with number %s, data: %S",name,data)
+ else
+ report_objects("flushing data to reserved object with number %s",name)
+ end
+ return pdfimmediateobject(name,tostring(data))
+ end
+ else
+ if trace_objects and trace_details then
+ report_objects("flushing data: %S",name)
+ end
+ return pdfimmediateobject(tostring(name))
+ end
+ end
+
+ function lpdf.flushstreamobject(data,dict,compressed,objnum) -- default compressed
+ if trace_objects then
+ report_objects("flushing stream object of %s bytes",#data)
+ end
+ local dtype = type(dict)
+ local kind = compressed == "raw" and "raw" or "stream"
+ local nolength = nil
+ if compressed == "raw" then
+ compressed = nil
+ nolength = true
+ -- data = string.formatters["<< %s >>stream\n%s\nendstream"](attr,data)
+ end
+ return pdfdeferredobject {
+ objnum = objnum,
+ immediate = true,
+ nolength = nolength,
+ compresslevel = compressed == false and 0 or nil,
+ type = "stream",
+ string = data,
+ attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil,
+ }
+ end
+
+ function lpdf.flushstreamfileobject(filename,dict,compressed,objnum) -- default compressed
+ if trace_objects then
+ report_objects("flushing stream file object %a",filename)
+ end
+ local dtype = type(dict)
+ return pdfdeferredobject {
+ objnum = objnum,
+ immediate = true,
+ compresslevel = compressed == false and 0 or nil,
+ type = "stream",
+ file = filename,
+ attr = (dtype == "string" and dict) or (dtype == "table" and dict()) or nil,
+ }
+ end
+
+ local shareobjectcache, shareobjectreferencecache = { }, { }
+
+ function lpdf.shareobject(content)
+ if content == nil then
+ -- invalid object not created
+ else
+ content = tostring(content)
+ local o = shareobjectcache[content]
+ if not o then
+ o = pdfimmediateobject(content)
+ shareobjectcache[content] = o
+ end
+ return o
+ end
+ end
+
+ function lpdf.shareobjectreference(content)
+ if content == nil then
+ -- invalid object not created
+ else
+ content = tostring(content)
+ local r = shareobjectreferencecache[content]
+ if not r then
+ local o = shareobjectcache[content]
+ if not o then
+ o = pdfimmediateobject(content)
+ shareobjectcache[content] = o
+ end
+ r = pdfreference(o)
+ shareobjectreferencecache[content] = r
+ end
+ return r
+ end
+ end
+
+end
+
+local pages = table.setmetatableindex(function(t,k)
+ local v = pdfreserveobject()
+ t[k] = v
+ return v
+end)
+
+function lpdf.getpagereference(n)
+ return pages[n]
+end
+
+local function flushnormalobj(data,n)
+ if not n then
+ nofobjects = nofobjects + 1
+ n = nofobjects
+ end
+ data = f_object(n,data)
+ if level == 0 then
+ objects[n] = offset
+ offset = offset + #data
+ flush(f,data)
+ else
+ if not lastdeferred then
+ lastdeferred = n
+ elseif n < lastdeferred then
+ lastdeferred = n
+ end
+ objects[n] = data
+ end
+ return n
+end
+
+local function flushstreamobj(data,n,dict,comp,nolength)
+ if not data then
+ report("no data for %S",dict)
+ return
+ end
+ if not n then
+ nofobjects = nofobjects + 1
+ n = nofobjects
+ end
+ local size = #data
+ if level == 0 then
+ local b = nil
+ local e = s_stream_e
+ if nolength then
+ b = f_stream_b_d_r(n,dict)
+ elseif comp ~= false and compress and size > threshold then
+ local compdata = zlibcompress(data,3)
+ if compdata then
+ local compsize = #compdata
+ if compsize > size - threshold then
+ b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size)
+ else
+ data = compdata
+ b = dict and f_stream_b_d_c(n,dict,compsize) or f_stream_b_n_c(n,compsize)
+ end
+ else
+ b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size)
+ end
+ else
+ b = dict and f_stream_b_d_u(n,dict,size) or f_stream_b_n_u(n,size)
+ end
+ flush(f,b)
+ flush(f,data)
+ flush(f,e)
+ objects[n] = offset
+ offset = offset + #b + #data + #e
+ else
+ if nolength then
+ data = f_stream_d_r(n,dict,data)
+ elseif comp ~= false and compress and size > threshold then
+ local compdata = zlibcompress(data,3)
+ if compdata then
+ local compsize = #compdata
+ if compsize > size - threshold then
+ data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data)
+ else
+ data = dict and f_stream_d_c(n,dict,compsize,compdata) or f_stream_n_c(n,compsize,compdata)
+ end
+ else
+ data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data)
+ end
+ else
+ data = dict and f_stream_d_u(n,dict,size,data) or f_stream_n_u(n,size,data)
+ end
+ if not lastdeferred then
+ lastdeferred = n
+ elseif n < lastdeferred then
+ lastdeferred = n
+ end
+ objects[n] = data
+ end
+ return n
+end
+
+flushdeferred = function() -- was forward defined
+ if lastdeferred then
+ for n=lastdeferred,nofobjects do
+ local o = objects[n]
+ if type(o) == "string" then
+ objects[n] = offset
+ offset = offset + #o
+ flush(f,o)
+ end
+ end
+ lastdeferred = false
+ end
+end
+
+function lpdf.immediateobject(a,b,c,d)
+ local kind --, immediate
+ local objnum, data, attr, filename
+ local compresslevel, objcompression, nolength
+ local argtype = type(a)
+ if argtype == "table" then
+ kind = a.type -- raw | stream
+ -- immediate = a.immediate
+ objnum = a.objnum
+ attr = a.attr
+ compresslevel = a.compresslevel
+ objcompression = a.objcompression
+ filename = a.file
+ data = a.string or a.stream or ""
+ nolength = a.nolength
+ if kind == "stream" then
+ if filename then
+ data = loaddata(filename) or ""
+ end
+ elseif kind == "raw"then
+ if filename then
+ data = loaddata(filename) or ""
+ end
+ elseif kind == "file"then
+ kind = "raw"
+ data = filename and loaddata(filename) or ""
+ elseif kind == "streamfile" then
+ kind = "stream"
+ data = filename and loaddata(filename) or ""
+ end
+ else
+ if argtype == "number" then
+ objnum = a
+ a, b, c = b, c, d
+ else
+ nofobjects = nofobjects + 1
+ objnum = nofobjects
+ end
+ if b then
+ if a == "stream" then
+ kind = "stream"
+ data = b
+ elseif a == "file" then
+ -- kind = "raw"
+ data = loaddata(b)
+ elseif a == "streamfile" then
+ kind = "stream"
+ data = loaddata(b)
+ else
+ data = "" -- invalid object
+ end
+ attr = c
+ else
+ -- kind = "raw"
+ data = a
+ end
+ end
+ if not objnum then
+ nofobjects = nofobjects + 1
+ objnum = nofobjects
+ end
+ -- todo: immediate
+ if kind == "stream" then
+ flushstreamobj(data,objnum,attr,compresslevel and compresslevel > 0 or nil,nolength)
+ elseif objectstream and objcompression ~= false then
+ addtocache(objnum,data)
+ else
+ flushnormalobj(data,objnum)
+ end
+ return objnum
+end
+
+lpdf.deferredobject = lpdf.immediateobject
+
+-- In lua 5.4 the methods are now moved one metalevel deeper so we need to get them
+-- from mt.__index instead. (I did get that at first.) It makes for a slightly (imo)
+-- nicer interface but no real gain in speed as we don't flush that often.
+
+local openfile, closefile do
+
+ -- I used to do <space><lf> but then figured out that when I open and save a file in a mode
+ -- that removes trailing spaces, the xref becomes invalid. The problem was then that a
+ -- reconstruction of the file by a viewer gives weird effects probably because percent symbols
+ -- gets interpreted then. Thanks to Ross Moore for noticing this side effect!
+
+ local f_used = formatters["%010i 00000 n\013\010"]
+ local f_link = formatters["%010i 00000 f\013\010"]
+ local f_first = formatters["%010i 65535 f\013\010"]
+
+ local f_pdf_tag = formatters["%%PDF-%i.%i\010"]
+ local f_xref = formatters["xref\0100 %i\010"]
+ local f_trailer_id = formatters["trailer\010<< %s /ID [ <%s> <%s> ] >>\010startxref\010%i\010%%%%EOF"]
+ local f_trailer_no = formatters["trailer\010<< %s >>\010startxref\010%i\010%%%%EOF"]
+ local f_startxref = formatters["startxref\010%i\010%%%%EOF"]
+
+ local inmemory = false
+ local close = false
+ local update = false
+
+ -- local removefile = os.remove
+
+ openfile = function(filename)
+ if inmemory then
+ local n = 0
+ f = { }
+ flush = function(f,s)
+ n = n + 1 f[n] = s
+ -- offset = offset + #s
+ end
+ close = function(f)
+ f = concat(f)
+ io.savedata(filename,f)
+ f = false
+ end
+ update = function(f,s)
+ f[1] = s
+ end
+ -- local n = 0
+ -- f = {
+ -- write = function(self,s)
+ -- n = n + 1 f[n] = s
+ -- end,
+ -- close = function(self)
+ -- f = concat(f)
+ -- io.savedata(filename,f)
+ -- f = false
+ -- end,
+ -- }
+ else
+ f = io.open(filename,"wb")
+ if not f then
+ -- message
+ os.exit()
+ end
+ -- f:setvbuf("full",64*1024)
+ local m = getmetatable(f)
+ flush = m.write or m.__index.write
+ close = m.close or m.__index.close
+ update = function(f,s)
+ f:seek("set",0)
+ f:write(s)
+ end
+ end
+ local v = f_pdf_tag(majorversion,minorversion)
+ -- local b = "%\xCC\xD5\xC1\xD4\xC5\xD8\xD0\xC4\xC6\010" -- LUATEXPDF (+128)
+ local b = "%\xC3\xCF\xCE\xD4\xC5\xD8\xD4\xD0\xC4\xC6\010" -- CONTEXTPDF (+128)
+ flush(f,v)
+ flush(f,b)
+ offset = offset + #v + #b
+ end
+
+ closefile = function(abort)
+ if abort then
+ close(f)
+ if not environment.arguments.nodummy then
+ f = io.open(abort,"wb")
+ if f then
+ local name = resolvers.findfile("context-lmtx-error.pdf")
+ if name then
+ local data = io.loaddata(name)
+ if data then
+ f:write(data)
+ f:close()
+ return
+ end
+ end
+ f:close()
+ end
+ end
+ os.remove(abort)
+ else
+ local xrefoffset = offset
+ local lastfree = 0
+ local noffree = 0
+ local catalog = lpdf.getcatalog()
+ local info = lpdf.getinfo()
+ if trailerid == true then
+ trailerid = md5.HEX(osuuid())
+ elseif trailerid and #trailerid > 32 then
+ trailerid = md5.HEX(trailerid)
+ else
+ trailerid = false
+ end
+ if objectstream then
+ flushdeferred()
+ flushcache()
+ --
+ xrefoffset = offset
+ --
+ nofobjects = nofobjects + 1
+ objects[nofobjects] = offset -- + 1
+ --
+ -- combine these three in one doesn't really give less code so
+ -- we go for the efficient ones
+ --
+ local nofbytes = 4
+ local c1, c2, c3, c4
+ if offset <= 0xFFFF then
+ nofbytes = 2
+ for i=1,nofobjects do
+ local o = objects[i]
+ if not o then
+ noffree = noffree + 1
+ else
+ local strm = o < 0
+ if strm then
+ o = -o
+ end
+ c1 = extract(o,8,8)
+ c2 = extract(o,0,8)
+ if strm then
+ objects[i] = char(2,c1,c2,streams[o][i])
+ else
+ objects[i] = char(1,c1,c2,0)
+ end
+ end
+ end
+ if noffree > 0 then
+ for i=nofobjects,1,-1 do
+ local o = objects[i]
+ if not o then
+ local f1 = extract(lastfree,8,8)
+ local f2 = extract(lastfree,0,8)
+ objects[i] = char(0,f1,f2,0)
+ lastfree = i
+ end
+ end
+ end
+ elseif offset <= 0xFFFFFF then
+ nofbytes = 3
+ for i=1,nofobjects do
+ local o = objects[i]
+ if not o then
+ noffree = noffree + 1
+ else
+ local strm = o < 0
+ if strm then
+ o = -o
+ end
+ c1 = extract(o,16,8)
+ c2 = extract(o, 8,8)
+ c3 = extract(o, 0,8)
+ if strm then
+ objects[i] = char(2,c1,c2,c3,streams[o][i])
+ else
+ objects[i] = char(1,c1,c2,c3,0)
+ end
+ end
+ end
+ if noffree > 0 then
+ for i=nofobjects,1,-1 do
+ local o = objects[i]
+ if not o then
+ local f1 = extract(lastfree,16,8)
+ local f2 = extract(lastfree, 8,8)
+ local f3 = extract(lastfree, 0,8)
+ objects[i] = char(0,f1,f2,f3,0)
+ lastfree = i
+ end
+ end
+ end
+ else
+ nofbytes = 4
+ for i=1,nofobjects do
+ local o = objects[i]
+ if not o then
+ noffree = noffree + 1
+ else
+ local strm = o < 0
+ if strm then
+ o = -o
+ end
+ c1 = extract(o,24,8)
+ c2 = extract(o,16,8)
+ c3 = extract(o, 8,8)
+ c4 = extract(o, 0,8)
+ if strm then
+ objects[i] = char(2,c1,c2,c3,c4,streams[o][i])
+ else
+ objects[i] = char(1,c1,c2,c3,c4,0)
+ end
+ end
+ end
+ if noffree > 0 then
+ for i=nofobjects,1,-1 do
+ local o = objects[i]
+ if not o then
+ local f1 = extract(lastfree,24,8)
+ local f2 = extract(lastfree,16,8)
+ local f3 = extract(lastfree, 8,8)
+ local f4 = extract(lastfree, 0,8)
+ objects[i] = char(0,f1,f2,f3,f4,0)
+ lastfree = i
+ end
+ end
+ end
+ end
+ objects[0] = rep("\0",1+nofbytes+1)
+ local data = concat(objects,"",0,nofobjects)
+ local xref = pdfdictionary {
+ Type = pdfconstant("XRef"),
+ Size = nofobjects + 1,
+ W = pdfarray { 1, nofbytes, 1 },
+ Root = catalog,
+ Info = info,
+ ID = trailerid and pdfarray { pdfliteral(trailerid,true), pdfliteral(trailerid,true) } or nil,
+ }
+ if compress then
+ local comp = zlibcompress(data,3)
+ if comp then
+ data = comp
+ flush(f,f_stream_b_d_c(nofobjects,xref(),#data))
+ else
+ flush(f,f_stream_b_d_u(nofobjects,xref(),#data))
+ end
+ else
+ flush(f,f_stream_b_d_u(nofobjects,xref(),#data))
+ end
+ flush(f,data)
+ flush(f,s_stream_e)
+ flush(f,f_startxref(xrefoffset))
+ else
+ flushdeferred()
+ xrefoffset = offset
+ flush(f,f_xref(nofobjects+1))
+ local trailer = pdfdictionary {
+ Size = nofobjects+1,
+ Root = catalog,
+ Info = info,
+ }
+ for i=1,nofobjects do
+ local o = objects[i]
+ if o then
+ objects[i] = f_used(o)
+ end
+ end
+ for i=nofobjects,1,-1 do
+ local o = objects[i]
+ if not o then
+ objects[i] = f_link(lastfree)
+ lastfree = i
+ end
+ end
+ objects[0] = f_first(lastfree)
+ flush(f,concat(objects,"",0,nofobjects))
+ trailer.Size = nofobjects + 1
+ if trailerid then
+ flush(f,f_trailer_id(trailer(),trailerid,trailerid,xrefoffset))
+ else
+ flush(f,f_trailer_no(trailer(),xrefoffset))
+ end
+ end
+ update(f,f_pdf_tag(majorversion,minorversion))
+ close(f)
+ end
+ io.flush()
+ closefile = function() end
+ end
+
+end
+
+-- For the moment we overload it here, although back-fil.lua eventually will
+-- be merged with back-pdf as it's pdf specific, or maybe back-imp-pdf or so.
+
+do -- updaters.register("backend.update.pdf",function()
+
+ -- We overload img but at some point it will even go away, so we just
+ -- reimplement what we need in context. This will change completely i.e.
+ -- we will drop the low level interface!
+
+ local codeinjections = backends.pdf.codeinjections
+
+ local imagetypes = images.types -- pdf png jpg jp2 jbig2 stream
+ local img_none = imagetypes.none
+
+ local rulecodes = nodes.rulecodes
+
+ local setprop = nodes.nuts.setprop
+
+ local report_images = logs.reporter("backend","images")
+
+ local lastindex = 0
+ local indices = { }
+
+ local bpfactor = number.dimenfactors.bp
+ local imagerule_code = rulecodes.image
+
+ function codeinjections.newimage(specification)
+ return specification
+ end
+
+ function codeinjections.copyimage(original)
+ return setmetatableindex(original)
+ end
+
+ function codeinjections.scanimgage(specification)
+ return specification
+ end
+
+ local function embedimage(specification)
+ if specification then
+ lastindex = lastindex + 1
+ index = lastindex
+ specification.index = index
+ local xobject = pdfdictionary { }
+ if not specification.notype then
+ xobject.Type = pdf_xobject
+ xobject.Subtype = pdf_form
+ xobject.FormType = 1
+ end
+ local bbox = specification.bbox
+ if bbox and not specification.nobbox then
+ xobject.BBox = pdfarray {
+ bbox[1] * bpfactor,
+ bbox[2] * bpfactor,
+ bbox[3] * bpfactor,
+ bbox[4] * bpfactor,
+ }
+ end
+ xobject = xobject + specification.attr
+ if bbox and not specification.width then
+ specification.width = bbox[3]
+ end
+ if bbox and not specification.height then
+ specification.height = bbox[4]
+ end
+ local dict = xobject()
+ --
+ nofobjects = nofobjects + 1
+ local objnum = nofobjects
+ local nolength = specification.nolength
+ local stream = specification.stream or specification.string
+ --
+ -- We cannot set type in native img so we need this hack or
+ -- otherwise we need to patch too much. Better that i write
+ -- a wrapper then. Anyway, it has to be done better: a key that
+ -- tells either or not to scale by xsize/ysize when flushing.
+ --
+ if not specification.type then
+ local kind = specification.kind
+ if kind then
+ -- take that one
+ elseif attr and find(attr,"BBox") then
+ kind = img_stream
+ else
+ -- hack: a bitmap
+ kind = img_none
+ end
+ specification.type = kind
+ specification.kind = kind
+ end
+ local compress = compresslevel and compresslevel > 0 or nil
+ flushstreamobj(stream,objnum,dict,compress,nolength)
+ specification.objnum = objnum
+ specification.rotation = specification.rotation or 0
+ specification.orientation = specification.orientation or 0
+ specification.transform = specification.transform or 0
+ specification.stream = nil
+ specification.attr = nil
+ specification.type = specification.kind or specification.type or img_none
+ indices[index] = specification -- better create a real specification
+ return specification
+ end
+ end
+
+ codeinjections.embedimage = embedimage
+
+ function codeinjections.wrapimage(specification)
+ --
+ local index = specification.index
+ if not index then
+ embedimage(specification)
+ end
+ --
+ local width = specification.width or 0
+ local height = specification.height or 0
+ local depth = specification.depth or 0
+ -- newimagerule
+ local n = nodes.pool.rule(width,height,depth)
+ n.subtype = imagerule_code
+ setprop(tonut(n),"index",specification.index)
+ return n
+ end
+
+ function lpdf.includeimage(index)
+ local specification = indices[index]
+ if specification then
+ local bbox = specification.bbox
+ local xorigin = bbox[1]
+ local yorigin = bbox[2]
+ local xsize = bbox[3] - xorigin -- we need the original ones, not the 'rotated' ones
+ local ysize = bbox[4] - yorigin -- we need the original ones, not the 'rotated' ones
+ local transform = specification.transform or 0
+ local objnum = specification.objnum or pdfreserveobject()
+ local groupref = nil
+ local kind = specification.kind or specification.type or img_none -- determines scaling type
+ return
+ kind,
+ xorigin, yorigin,
+ xsize, ysize,
+ transform,
+ objnum,
+ groupref
+ end
+ end
+
+end -- )
+
+do -- updaters.register("backend.update.lpdf",function()
+
+ -- todo: an md5 or sha2 hash can save space
+ -- todo: make a type 3 font instead
+ -- todo: move to lpdf namespace
+
+ local pdfimage
+ local newpdf
+ local openpdf
+ local closepdf
+ local copypage
+
+
+ updaters.register("backend.update.lpdf",function()
+ pdfimage = lpdf.epdf.image
+ newpdf = pdfimage.new
+ openpdf = pdfimage.open
+ closepdf = pdfimage.close
+ copypage = pdfimage.copy
+ end)
+
+ local embedimage = images.embed
+
+ local nofstreams = 0
+ local topdf = { }
+ local toidx = { }
+
+ local function storedata_s(pdf)
+ local idx = toidx[pdf]
+ if not idx then
+ nofstreams = nofstreams + 1
+ idx = nofstreams
+ toidx[pdf] = nofstreams
+ topdf[idx] = pdf
+ end
+ return idx
+ end
+
+ local function vfimage_s(id,wd,ht,dp,pos_h,pos_v)
+ local index = topdf[id]
+ if type(index) == "string" then
+ local pdfdoc = newpdf(index,#index)
+ local image = copypage(pdfdoc)
+ local bbox = image.bbox
+ image.width = bbox[3] - bbox[1]
+ image.height = bbox[4] - bbox[2]
+ embedimage(image)
+ index = image.index
+ topdf[id] = index
+ end
+ flushimage(index,wd,ht,dp,pos_h,pos_v)
+ end
+
+ local function storedata_n(name,page)
+ local idx = toidx[pdf]
+ if not idx then
+ nofstreams = nofstreams + 1
+ idx = nofstreams
+ toidx[pdf] = nofstreams
+ topdf[idx] = pdf
+ end
+ return idx
+ end
+
+ -- We need to have a way to close such a pdf ... esp for fonts.
+
+ local pdfdocs = { }
+
+ local function vfimage_n(name,page,wd,ht,dp,pos_h,pos_v)
+ local d = pdfdocs[name]
+ if not d then
+ d = { doc = openpdf(name), pages = { } }
+ pdfdocs[name] = d
+ end
+ local index = d.pages[page]
+ if not index then
+ local image = copypage(d.doc,page)
+ local bbox = image.bbox
+ image.width = bbox[3] - bbox[1]
+ image.height = bbox[4] - bbox[2]
+ embedimage(image)
+ index = image.index
+ d.pages[page] = index
+ end
+ flushimage(index,wd,ht,dp,pos_h,pos_v)
+ end
+
+ function lpdf.pdfvfimage(wd,ht,dp,data,name)
+ if type(data) == "number" then
+ return { "lua", function(font,char,pos_h,pos_v)
+ vfimage_n(name,data,wd,ht,dp,pos_h,pos_v)
+ end }
+ else
+ return { "lua", function(font,char,pos_h,pos_v)
+ local id = storedata_s(data)
+ vfimage_s(id,wd,ht,dp,pos_h,pos_v)
+ end }
+ end
+ end
+
+end -- )
+
+-- The driver.
+
+do
+
+ -- local addsuffix = file.addsuffix
+ local texgetbox = tex.getbox
+
+ local pdfname = nil
+ local converter = nil
+ local useddriver = nil -- a bit of a hack
+
+ local function outputfilename(driver)
+ return pdfname
+ end
+
+ -- todo: prevent twice
+
+ local function prepare(driver)
+ if not environment.initex then
+ -- install new functions in pdf namespace
+-- updaters.apply("backend.update.pdf")
+ -- install new functions in lpdf namespace
+ updaters.apply("backend.update.lpdf")
+ -- adapt existing shortcuts to lpdf namespace
+-- updaters.apply("backend.update.tex")
+-- -- adapt existing shortcuts to tex namespace
+ updaters.apply("backend.update")
+ --
+ pdfname = tex.jobname .. ".pdf"
+ openfile(pdfname)
+ --
+ luatex.registerstopactions(1,function()
+ if pdfname then
+ lpdf.finalizedocument()
+ closefile()
+ end
+ end)
+ --
+ luatex.registerpageactions(1,function()
+ if pdfname then
+ lpdf.finalizepage(true)
+ end
+ end)
+ -- --
+ lpdf.registerdocumentfinalizer(wrapup,nil,"wrapping up")
+ --
+ statistics.register("result saved in file", function()
+ local outputfilename = environment.outputfilename or environment.jobname or tex.jobname or "<unset>"
+ return string.format("%s.%s, compresslevel %s, objectcompresslevel %s",outputfilename,"pdf",lpdf.getcompression())
+ end)
+ --
+ end
+ converter = drivers.converters.lmtx
+ useddriver = driver
+ end
+
+ local function wrapup(driver)
+ if pdfname then
+ closefile()
+ pdfname = nil
+ end
+ end
+
+ local function cleanup(driver)
+ if pdfname then
+ closefile(pdfname)
+ pdfname = nil
+ end
+ end
+
+ local function convert(driver,boxnumber)
+ converter(driver,texgetbox(boxnumber),"page")
+ end
+
+ localconverter = function(...)
+ converter(useddriver,...)
+ end
+
+ drivers.install {
+ name = "pdf",
+ flushers = flushers,
+ actions = {
+ prepare = prepare,
+ wrapup = wrapup,
+ cleanup = cleanup,
+ --
+ initialize = initialize,
+ convert = convert,
+ finalize = finalize,
+ --
+ outputfilename = outputfilename,
+ },
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt
new file mode 100644
index 00000000000..afb149d8af7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-mis.lmt
@@ -0,0 +1,639 @@
+if not modules then modules = { } end modules ['lpdf-mis'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Although we moved most pdf handling to the lua end, we didn't change
+-- the overall approach. For instance we share all resources i.e. we
+-- don't make subsets for each xform or page. The current approach is
+-- quite efficient. A big difference between MkII and MkIV is that we
+-- now use forward references. In this respect the MkII code shows that
+-- it evolved over a long period, when backends didn't provide forward
+-- referencing and references had to be tracked in multiple passes. Of
+-- course there are a couple of more changes.
+
+local next, tostring, type = next, tostring, type
+local format, gsub, formatters = string.format, string.gsub, string.formatters
+local concat, flattened = table.concat, table.flattened
+local settings_to_array = utilities.parsers.settings_to_array
+
+local backends, lpdf, nodes = backends, lpdf, nodes
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+local registrations = backends.pdf.registrations
+
+local getpagedimensions = layouts.getpagedimensions
+local getcanvas = layouts.getcanvas
+
+local nuts = nodes.nuts
+local copy_node = nuts.copy
+
+local nodepool = nuts.pool
+local setstate = nodepool.setstate
+local register = nodepool.register
+
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfconstant = lpdf.constant
+local pdfreference = lpdf.reference
+local pdfunicode = lpdf.unicode
+local pdfverbose = lpdf.verbose
+local pdfstring = lpdf.string
+local pdfaction = lpdf.action
+
+local pdfflushobject
+local pdfflushstreamobject
+local pdfminorversion
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfflushstreamobject = lpdf.flushstreamobject
+ pdfminorversion = lpdf.minorversion
+end)
+
+local formattedtimestamp = lpdf.pdftimestamp
+local adddocumentextgstate = lpdf.adddocumentextgstate
+local addtocatalog = lpdf.addtocatalog
+local addtoinfo = lpdf.addtoinfo
+local addtopageattributes = lpdf.addtopageattributes
+local addtonames = lpdf.addtonames
+
+local pdfgetmetadata = lpdf.getmetadata
+
+local texset = tex.set
+
+local variables = interfaces.variables
+
+local v_stop = variables.stop
+local v_none = variables.none
+local v_max = variables.max
+local v_bookmark = variables.bookmark
+local v_fit = variables.fit
+local v_doublesided = variables.doublesided
+local v_singlesided = variables.singlesided
+local v_default = variables.default
+local v_auto = variables.auto
+local v_fixed = variables.fixed
+local v_landscape = variables.landscape
+local v_portrait = variables.portrait
+local v_page = variables.page
+local v_paper = variables.paper
+local v_attachment = variables.attachment
+local v_layer = variables.layer
+local v_lefttoright = variables.lefttoright
+local v_righttoleft = variables.righttoleft
+local v_title = variables.title
+local v_nomenubar = variables.nomenubar
+
+local positive = register(setstate("/GSpositive gs"))
+local negative = register(setstate("/GSnegative gs"))
+local overprint = register(setstate("/GSoverprint gs"))
+local knockout = register(setstate("/GSknockout gs"))
+
+local omitextraboxes = false
+
+directives.register("backend.omitextraboxes", function(v) omitextraboxes = v end)
+
+local function initializenegative()
+ local a = pdfarray { 0, 1 }
+ local g = pdfconstant("ExtGState")
+ local d = pdfdictionary {
+ FunctionType = 4,
+ Range = a,
+ Domain = a,
+ }
+ local negative = pdfdictionary { Type = g, TR = pdfreference(pdfflushstreamobject("{ 1 exch sub }",d)) } -- can be shared
+ local positive = pdfdictionary { Type = g, TR = pdfconstant("Identity") }
+ adddocumentextgstate("GSnegative", pdfreference(pdfflushobject(negative)))
+ adddocumentextgstate("GSpositive", pdfreference(pdfflushobject(positive)))
+ initializenegative = nil
+end
+
+local function initializeoverprint()
+ local g = pdfconstant("ExtGState")
+ local knockout = pdfdictionary { Type = g, OP = false, OPM = 0 }
+ local overprint = pdfdictionary { Type = g, OP = true, OPM = 1 }
+ adddocumentextgstate("GSknockout", pdfreference(pdfflushobject(knockout)))
+ adddocumentextgstate("GSoverprint", pdfreference(pdfflushobject(overprint)))
+ initializeoverprint = nil
+end
+
+function nodeinjections.overprint()
+ if initializeoverprint then initializeoverprint() end
+ return copy_node(overprint)
+end
+function nodeinjections.knockout ()
+ if initializeoverprint then initializeoverprint() end
+ return copy_node(knockout)
+end
+
+function nodeinjections.positive()
+ if initializenegative then initializenegative() end
+ return copy_node(positive)
+end
+function nodeinjections.negative()
+ if initializenegative then initializenegative() end
+ return copy_node(negative)
+end
+
+-- function codeinjections.addtransparencygroup()
+-- -- png: /CS /DeviceRGB /I true
+-- local d = pdfdictionary {
+-- S = pdfconstant("Transparency"),
+-- I = true,
+-- K = true,
+-- }
+-- lpdf.registerpagefinalizer(function() addtopageattributes("Group",d) end) -- hm
+-- end
+
+-- actions (todo: store and update when changed)
+
+local openpage, closepage, opendocument, closedocument
+
+function codeinjections.registerdocumentopenaction(open)
+ opendocument = open
+end
+
+function codeinjections.registerdocumentcloseaction(close)
+ closedocument = close
+end
+
+function codeinjections.registerpageopenaction(open)
+ openpage = open
+end
+
+function codeinjections.registerpagecloseaction(close)
+ closepage = close
+end
+
+local function flushdocumentactions()
+ if opendocument then
+ addtocatalog("OpenAction",pdfaction(opendocument))
+ end
+ if closedocument then
+ addtocatalog("CloseAction",pdfaction(closedocument))
+ end
+end
+
+local function flushpageactions()
+ if openpage or closepage then
+ local d = pdfdictionary()
+ if openpage then
+ d.O = pdfaction(openpage)
+ end
+ if closepage then
+ d.C = pdfaction(closepage)
+ end
+ addtopageattributes("AA",d)
+ end
+end
+
+lpdf.registerpagefinalizer (flushpageactions, "page actions")
+lpdf.registerdocumentfinalizer(flushdocumentactions,"document actions")
+
+--- info : this can change and move elsewhere
+
+local identity = { }
+
+function codeinjections.setupidentity(specification)
+ for k, v in next, specification do
+ if v ~= "" then
+ identity[k] = v
+ end
+ end
+end
+
+function codeinjections.getidentityvariable(name)
+ return identity[name]
+end
+
+local done = false -- using "setupidentity = function() end" fails as the meaning is frozen in register
+
+local function setupidentity()
+ if not done then
+ local metadata = pdfgetmetadata()
+ local creator = metadata.creator
+ local version = metadata.contextversion
+ local time = metadata.time
+ local jobname = environment.jobname or tex.jobname or "unknown"
+ --
+ local title = identity.title
+ if not title or title == "" then
+ title = tex.jobname
+ end
+ addtoinfo("Title", pdfunicode(title), title)
+ local subtitle = identity.subtitle or ""
+ if subtitle ~= "" then
+ addtoinfo("Subject", pdfunicode(subtitle), subtitle)
+ end
+ local author = identity.author or ""
+ if author ~= "" then
+ addtoinfo("Author", pdfunicode(author), author) -- '/Author' in /Info, 'Creator' in XMP
+ end
+ addtoinfo("Creator", pdfunicode(creator), creator)
+ addtoinfo("CreationDate", pdfstring(formattedtimestamp(time)))
+ local date = identity.date or ""
+ local pdfdate = date and formattedtimestamp(date)
+ if pdfdate then
+ addtoinfo("ModDate", pdfstring(pdfdate), date)
+ else
+ -- users should enter the date in 2010-01-19T23:27:50+01:00 format
+ -- and if not provided that way we use the creation time instead
+ addtoinfo("ModDate", pdfstring(formattedtimestamp(time)),time)
+ end
+ local keywords = identity.keywords or ""
+ if keywords ~= "" then
+ keywords = concat(settings_to_array(keywords), " ")
+ addtoinfo("Keywords", pdfunicode(keywords), keywords)
+ end
+ local id = lpdf.id()
+ addtoinfo("ID", pdfstring(id), id) -- needed for pdf/x
+ --
+ addtoinfo("ConTeXt.Version",version)
+ addtoinfo("ConTeXt.Time",os.date("%Y-%m-%d %H:%M"))
+ addtoinfo("ConTeXt.Jobname",jobname)
+ addtoinfo("ConTeXt.Url","www.pragma-ade.com")
+ addtoinfo("ConTeXt.Support","contextgarden.net")
+ addtoinfo("TeX.Support","tug.org")
+ --
+ done = true
+ else
+ -- no need for a message
+ end
+end
+
+lpdf.registerpagefinalizer(setupidentity,"identity")
+
+-- or when we want to be able to set things after pag e1:
+--
+-- lpdf.registerdocumentfinalizer(setupidentity,1,"identity")
+
+local function flushjavascripts()
+ local t = interactions.javascripts.flushpreambles()
+ if #t > 0 then
+ local a = pdfarray()
+ local pdf_javascript = pdfconstant("JavaScript")
+ for i=1,#t do
+ local ti = t[i]
+ local name = ti[1]
+ local script = ti[2]
+ local j = pdfdictionary {
+ S = pdf_javascript,
+ JS = pdfreference(pdfflushstreamobject(script)),
+ }
+ a[#a+1] = pdfstring(name)
+ a[#a+1] = pdfreference(pdfflushobject(j))
+ end
+ addtonames("JavaScript",pdfreference(pdfflushobject(pdfdictionary{ Names = a })))
+ end
+end
+
+lpdf.registerdocumentfinalizer(flushjavascripts,"javascripts")
+
+-- -- --
+
+local plusspecs = {
+ [v_max] = {
+ mode = "FullScreen",
+ },
+ [v_bookmark] = {
+ mode = "UseOutlines",
+ },
+ [v_attachment] = {
+ mode = "UseAttachments",
+ },
+ [v_layer] = {
+ mode = "UseOC",
+ },
+ [v_fit] = {
+ fit = true,
+ },
+ [v_doublesided] = {
+ layout = "TwoColumnRight",
+ },
+ [v_fixed] = {
+ fixed = true,
+ },
+ [v_landscape] = {
+ duplex = "DuplexFlipShortEdge",
+ },
+ [v_portrait] = {
+ duplex = "DuplexFlipLongEdge",
+ },
+ [v_page] = {
+ duplex = "Simplex" ,
+ },
+ [v_paper] = {
+ paper = true,
+ },
+ [v_title] ={
+ title = true,
+ },
+ [v_lefttoright] ={
+ direction = "L2R",
+ },
+ [v_righttoleft] ={
+ direction = "R2L",
+ },
+ [v_nomenubar] ={
+ nomenubar = true,
+ },
+}
+
+local pagespecs = {
+ --
+ [v_max] = plusspecs[v_max],
+ [v_bookmark] = plusspecs[v_bookmark],
+ [v_attachment] = plusspecs[v_attachment],
+ [v_layer] = plusspecs[v_layer],
+ [v_lefttoright] = plusspecs[v_lefttoright],
+ [v_righttoleft] = plusspecs[v_righttoleft],
+ [v_title] = plusspecs[v_title],
+ --
+ [v_none] = {
+ },
+ [v_fit] = {
+ mode = "UseNone",
+ fit = true,
+ },
+ [v_doublesided] = {
+ mode = "UseNone",
+ layout = "TwoColumnRight",
+ fit = true,
+ },
+ [v_singlesided] = {
+ mode = "UseNone"
+ },
+ [v_default] = {
+ mode = "UseNone",
+ layout = "auto",
+ },
+ [v_auto] = {
+ mode = "UseNone",
+ layout = "auto",
+ },
+ [v_fixed] = {
+ mode = "UseNone",
+ layout = "auto",
+ fixed = true, -- noscale
+ },
+ [v_landscape] = {
+ mode = "UseNone",
+ layout = "auto",
+ fixed = true,
+ duplex = "DuplexFlipShortEdge",
+ },
+ [v_portrait] = {
+ mode = "UseNone",
+ layout = "auto",
+ fixed = true,
+ duplex = "DuplexFlipLongEdge",
+ },
+ [v_page] = {
+ mode = "UseNone",
+ layout = "auto",
+ fixed = true,
+ duplex = "Simplex",
+ },
+ [v_paper] = {
+ mode = "UseNone",
+ layout = "auto",
+ fixed = true,
+ duplex = "Simplex",
+ paper = true,
+ },
+ [v_nomenubar] = {
+ mode = "UseNone",
+ layout = "auto",
+ nomenubar = true,
+ },
+}
+
+local function documentspecification()
+ local canvas = getcanvas()
+
+ -- move this to layo-ini ?
+
+ local pagespec = canvas.pagespec
+ if not pagespec or pagespec == "" then
+ pagespec = v_default
+ end
+ local settings = settings_to_array(pagespec)
+ -- so the first one detemines the defaults
+ local first = settings[1]
+ local defaults = pagespecs[first]
+ local spec = defaults or pagespecs[v_default]
+ -- successive keys can modify this
+ if spec.layout == "auto" then
+ if canvas.doublesided then
+ local s = pagespecs[v_doublesided] -- to be checked voor interfaces
+ for k, v in next, s do
+ spec[k] = v
+ end
+ else
+ spec.layout = false
+ end
+ end
+ -- we start at 2 when we have a valid first default set
+ for i=defaults and 2 or 1,#settings do
+ local s = plusspecs[settings[i]]
+ if s then
+ for k, v in next, s do
+ spec[k] = v
+ end
+ end
+ end
+
+ -- maybe interfaces.variables
+ local layout = spec.layout
+ local mode = spec.mode
+ local fit = spec.fit
+ local fixed = spec.fixed
+ local duplex = spec.duplex
+ local paper = spec.paper
+ local title = spec.title
+ local direction = spec.direction
+ local nomenubar = spec.nomenubar
+ if layout then
+ addtocatalog("PageLayout",pdfconstant(layout))
+ end
+ if mode then
+ addtocatalog("PageMode",pdfconstant(mode))
+ end
+ local prints = nil
+ local marked = canvas.marked
+ local copies = canvas.copies
+ if marked then
+ local pages = structures.pages
+ local marked = pages.allmarked(marked)
+ local nofmarked = marked and #marked or 0
+ if nofmarked > 0 then
+ -- the spec is wrong in saying that numbering starts at 1 which of course makes
+ -- sense as most real documents start with page 0 .. sigh
+ for i=1,#marked do marked[i] = marked[i] - 1 end
+ prints = pdfarray(flattened(pages.toranges(marked)))
+ end
+ end
+ if fit or fixed or duplex or copies or paper or prints or title or direction or nomenubar then
+ addtocatalog("ViewerPreferences",pdfdictionary {
+ FitWindow = fit and true or nil,
+ PrintScaling = fixed and pdfconstant("None") or nil,
+ Duplex = duplex and pdfconstant(duplex) or nil,
+ NumCopies = copies and copies or nil,
+ PickTrayByPDFSize = paper and true or nil,
+ PrintPageRange = prints or nil,
+ DisplayDocTitle = title and true or nil,
+ Direction = direction and pdfconstant(direction) or nil,
+ HideMenubar = nomenubar and true or nil,
+ })
+ end
+ addtoinfo ("Trapped", pdfconstant("False")) -- '/Trapped' in /Info, 'Trapped' in XMP
+ addtocatalog("Version", pdfconstant(format("1.%s",pdfminorversion())))
+ addtocatalog("Lang", pdfstring(tokens.getters.macro("currentmainlanguage")))
+end
+
+-- temp hack: the mediabox is not under our control and has a precision of 5 digits
+
+local factor = number.dimenfactors.bp
+local f_value = formatters["%.6N"]
+
+local function boxvalue(n) -- we could share them
+ return pdfverbose(f_value(factor * n))
+end
+
+local function pagespecification()
+ local canvas = getcanvas()
+ local paperwidth = canvas.paperwidth
+ local paperheight = canvas.paperheight
+ local leftoffset = canvas.leftoffset
+ local topoffset = canvas.topoffset
+ --
+ local llx = leftoffset
+ local urx = canvas.width - leftoffset
+ local lly = paperheight + topoffset - canvas.height
+ local ury = paperheight - topoffset
+ -- boxes can be cached
+ local function extrabox(WhatBox,offset,always)
+ if offset ~= 0 or always then
+ addtopageattributes(WhatBox, pdfarray {
+ boxvalue(llx + offset),
+ boxvalue(lly + offset),
+ boxvalue(urx - offset),
+ boxvalue(ury - offset),
+ })
+ end
+ end
+ if omitextraboxes then
+ -- only useful for testing / comparing
+ else
+ extrabox("CropBox",canvas.cropoffset,true) -- mandate for rendering
+ extrabox("TrimBox",canvas.trimoffset,true) -- mandate for pdf/x
+ extrabox("BleedBox",canvas.bleedoffset) -- optional
+ -- extrabox("ArtBox",canvas.artoffset) -- optional .. unclear what this is meant to do
+ end
+end
+
+lpdf.registerpagefinalizer(pagespecification,"page specification")
+lpdf.registerdocumentfinalizer(documentspecification,"document specification")
+
+-- Page Label support ...
+--
+-- In principle we can also support /P (prefix) as we can just use the verbose form
+-- and we can then forget about the /St (start) as we don't care about those few
+-- extra bytes due to lack of collapsing. Anyhow, for that we need a stupid prefix
+-- variant and that's not on the agenda now.
+
+local map = {
+ numbers = "D",
+ Romannumerals = "R",
+ romannumerals = "r",
+ Characters = "A",
+ characters = "a",
+}
+
+-- local function featurecreep()
+-- local pages, lastconversion, list = structures.pages.tobesaved, nil, pdfarray()
+-- local getstructureset = structures.sets.get
+-- for i=1,#pages do
+-- local p = pages[i]
+-- if not p then
+-- return -- fatal error
+-- else
+-- local numberdata = p.numberdata
+-- if numberdata then
+-- local conversionset = numberdata.conversionset
+-- if conversionset then
+-- local conversion = getstructureset("structure:conversions",p.block,conversionset,1,"numbers")
+-- if conversion ~= lastconversion then
+-- lastconversion = conversion
+-- list[#list+1] = i - 1 -- pdf starts numbering at 0
+-- list[#list+1] = pdfdictionary { S = pdfconstant(map[conversion] or map.numbers) }
+-- end
+-- end
+-- end
+-- if not lastconversion then
+-- lastconversion = "numbers"
+-- list[#list+1] = i - 1 -- pdf starts numbering at 0
+-- list[#list+1] = pdfdictionary { S = pdfconstant(map.numbers) }
+-- end
+-- end
+-- end
+-- addtocatalog("PageLabels", pdfdictionary { Nums = list })
+-- end
+
+local function featurecreep()
+ local pages = structures.pages.tobesaved
+ local list = pdfarray()
+ local getset = structures.sets.get
+ local stopped = false
+ local oldlabel = nil
+ local olconversion = nil
+ for i=1,#pages do
+ local p = pages[i]
+ if not p then
+ return -- fatal error
+ end
+ local label = p.viewerprefix or ""
+ if p.status == v_stop then
+ if not stopped then
+ list[#list+1] = i - 1 -- pdf starts numbering at 0
+ list[#list+1] = pdfdictionary {
+ P = pdfunicode(label),
+ }
+ stopped = true
+ end
+ oldlabel = nil
+ oldconversion = nil
+ stopped = false
+ else
+ local numberdata = p.numberdata
+ local conversion = nil
+ local number = p.number
+ if numberdata then
+ local conversionset = numberdata.conversionset
+ if conversionset then
+ conversion = getset("structure:conversions",p.block,conversionset,1,"numbers")
+ end
+ end
+ conversion = conversion and map[conversion] or map.numbers
+ if number == 1 or oldlabel ~= label or oldconversion ~= conversion then
+ list[#list+1] = i - 1 -- pdf starts numbering at 0
+ list[#list+1] = pdfdictionary {
+ S = pdfconstant(conversion),
+ St = number,
+ P = label ~= "" and pdfunicode(label) or nil,
+ }
+ end
+ oldlabel = label
+ oldconversion = conversion
+ stopped = false
+ end
+ end
+ addtocatalog("PageLabels", pdfdictionary { Nums = list })
+end
+
+lpdf.registerdocumentfinalizer(featurecreep,"featurecreep")
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-mov.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-mov.lmt
new file mode 100644
index 00000000000..42ba6fb008f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-mov.lmt
@@ -0,0 +1,68 @@
+if not modules then modules = { } end modules ['lpdf-mov'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format = string.format
+
+local lpdf = lpdf
+local context = context
+
+local nodeinjections = backends.pdf.nodeinjections
+local pdfconstant = lpdf.constant
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfborder = lpdf.border
+local write_node = node.write
+
+function nodeinjections.insertmovie(specification)
+ -- managed in figure inclusion: width, height, factor, repeat, controls, preview, label, foundname
+ local width = specification.width
+ local height = specification.height
+ local factor = specification.factor or number.dimenfactors.bp
+ local moviedict = pdfdictionary {
+ F = specification.foundname,
+ Aspect = pdfarray { factor * width, factor * height },
+ Poster = (specification.preview and true) or false,
+ }
+ local controldict = pdfdictionary {
+ ShowControls = (specification.controls and true) or false,
+ Mode = (specification["repeat"] and pdfconstant("Repeat")) or nil,
+ }
+ local bs, bc = pdfborder()
+ local action = pdfdictionary {
+ Subtype = pdfconstant("Movie"),
+ Border = bs,
+ C = bc,
+ T = format("movie %s",specification.label),
+ Movie = moviedict,
+ A = controldict,
+ }
+ write_node(nodeinjections.annotation(width,height,0,action())) -- test: context(...)
+end
+
+function nodeinjections.insertsound(specification)
+ -- managed in interaction: repeat, label, foundname
+ local soundclip = interactions.soundclips.soundclip(specification.label)
+ if soundclip then
+ local controldict = pdfdictionary {
+ Mode = (specification["repeat"] and pdfconstant("Repeat")) or nil
+ }
+ local sounddict = pdfdictionary {
+ F = soundclip.filename
+ }
+ local bs, bc = pdfborder()
+ local action = pdfdictionary {
+ Subtype = pdfconstant("Movie"),
+ Border = bs,
+ C = bc,
+ T = format("sound %s",specification.label),
+ Movie = sounddict,
+ A = controldict,
+ }
+ write_node(nodeinjections.annotation(0,0,0,action())) -- test: context(...)
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-pde.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-pde.lmt
new file mode 100644
index 00000000000..7fb14ada262
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-pde.lmt
@@ -0,0 +1,1211 @@
+if not modules then modules = { } end modules ['lpdf-epd'] = {
+ version = 1.001,
+ comment = "companion to lpdf-epa.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ history = "this one replaces the poppler/pdfe binding",
+}
+
+-- \enabledirectives[graphics.pdf.uselua]
+-- \enabledirectives[graphics.pdf.recompress]
+-- \enabledirectives[graphics.pdf.stripmarked]
+
+-- maximum integer : +2^32
+-- maximum real : +2^15
+-- minimum real : 1/(2^16)
+
+-- get_flagged : does that still work
+
+-- ppdoc_permissions (ppdoc *pdf);
+
+-- PPSTRING_ENCODED 1 << 0
+-- PPSTRING_DECODED 1 << 1
+-- PPSTRING_EXEC 1 << 2 postscript only
+-- PPSTRING_PLAIN 0
+-- PPSTRING_BASE16 1 << 3
+-- PPSTRING_BASE85 1 << 4
+-- PPSTRING_UTF16BE 1 << 5
+-- PPSTRING_UTF16LE 1 << 6
+
+-- PPDOC_ALLOW_PRINT 1 << 2 printing
+-- PPDOC_ALLOW_MODIFY 1 << 3 filling form fields, signing, creating template pages
+-- PPDOC_ALLOW_COPY 1 << 4 copying, copying for accessibility
+-- PPDOC_ALLOW_ANNOTS 1 << 5 filling form fields, copying, signing
+-- PPDOC_ALLOW_EXTRACT 1 << 9 contents copying for accessibility
+-- PPDOC_ALLOW_ASSEMBLY 1 << 10 no effect
+-- PPDOC_ALLOW_PRINT_HIRES 1 << 11 no effect
+
+-- PPCRYPT_NONE 0 no encryption, go ahead
+-- PPCRYPT_DONE 1 encryption present but password succeeded, go ahead
+-- PPCRYPT_PASS -1 encryption present, need non-empty password
+-- PPCRYPT_FAIL -2 invalid or unsupported encryption (eg. undocumented in pdf spec)
+
+local setmetatable, type, next = setmetatable, type, next
+local tostring, tonumber, unpack = tostring, tonumber, unpack
+local char, byte, find = string.char, string.byte, string.find
+local abs = math.abs
+local concat, swapped, sortedhash, sortedkeys = table.concat, table.swapped, table.sortedhash, table.sortedkeys
+local utfchar = string.char
+local setmetatableindex = table.setmetatableindex
+local ioopen = io.open
+
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local P, C, S, R, Ct, Cc, V, Carg, Cs, Cf, Cg = lpeg.P, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cc, lpeg.V, lpeg.Carg, lpeg.Cs, lpeg.Cf, lpeg.Cg
+
+if not lpdf then
+ require("lpdf-aux")
+end
+
+if not (number and number.dimenfactors) then
+ require("util-dim")
+end
+
+local pdfe = pdfe
+ lpdf = lpdf or { }
+local lpdf = lpdf
+local lpdf_epdf = { }
+ lpdf.epdf = lpdf_epdf
+
+local pdfopen = pdfe.open
+local pdfopenfile = pdfe.openfile
+local pdfnew = pdfe.new
+local pdfclose = pdfe.close
+
+local getcatalog = pdfe.getcatalog
+local getinfo = pdfe.getinfo
+local gettrailer = pdfe.gettrailer
+local getnofpages = pdfe.getnofpages
+local getversion = pdfe.getversion
+local getbox = pdfe.getbox
+local getstatus = pdfe.getstatus
+local unencrypt = pdfe.unencrypt
+
+local dictionarytotable = pdfe.dictionarytotable
+local arraytotable = pdfe.arraytotable
+local pagestotable = pdfe.pagestotable
+local readwholestream = pdfe.readwholestream
+
+local getfromreference = pdfe.getfromreference
+
+local report_epdf = logs.reporter("epdf")
+
+local allocate = utilities.storage.allocate
+
+local bpfactor = number.dimenfactors.bp
+
+local objectcodes = { [0] =
+ "none",
+ "null",
+ "bool",
+ "integer",
+ "number",
+ "name",
+ "string",
+ "array",
+ "dictionary",
+ "stream",
+ "reference",
+}
+
+local encryptioncodes = {
+ [0] = "notencrypted",
+ [1] = "unencrypted",
+ [-1] = "protected",
+ [-2] = "failure",
+}
+
+objectcodes = allocate(swapped(objectcodes,objectcodes))
+encryptioncodes = allocate(swapped(encryptioncodes,encryptioncodes))
+
+pdfe.objectcodes = objectcodes
+pdfe.encryptioncodes = encryptioncodes
+
+local null_object_code = objectcodes.null
+local reference_object_code = objectcodes.reference
+
+local none_object_code = objectcodes.none
+local null_object_code = objectcodes.null
+local bool_object_code = objectcodes.bool
+local integer_object_code = objectcodes.integer
+local number_object_code = objectcodes.number
+local name_object_code = objectcodes.name
+local string_object_code = objectcodes.string
+local array_object_code = objectcodes.array
+local dictionary_object_code = objectcodes.dictionary
+local stream_object_code = objectcodes.stream
+local reference_object_code = objectcodes.reference
+
+local checked_access
+local get_flagged -- from pdfe -> lpdf
+
+if lpdf.dictionary then
+
+ -- we're in context
+
+ local pdfdictionary = lpdf.dictionary
+ local pdfarray = lpdf.array
+ local pdfconstant = lpdf.constant
+ local pdfstring = lpdf.string
+ local pdfunicode = lpdf.unicode
+
+ get_flagged = function(t,f,k)
+ local tk = t[k] -- triggers resolve
+ local fk = f[k]
+ if not fk then
+ return tk
+ elseif fk == "name" then
+ return pdfconstant(tk)
+ elseif fk == "array" then
+ return pdfarray(tk)
+ elseif fk == "dictionary" then
+ return pdfarray(tk)
+ elseif fk == "rawtext" then
+ return pdfstring(tk)
+ elseif fk == "unicode" then
+ return pdfunicode(tk)
+ else
+ return tk
+ end
+ end
+
+else
+
+ get_flagged = function(t,f,k)
+ return t[k]
+ end
+
+end
+
+-- We need to convert the string from utf16 although there is no way to
+-- check if we have a regular string starting with a bom. So, we have
+-- na dilemma here: a pdf doc encoded string can be invalid utf.
+
+-- <hex encoded> : implicit 0 appended if odd
+-- (byte encoded) : \( \) \\ escaped
+--
+-- <FE><FF> : utf16be
+--
+-- \r \r \t \b \f \( \) \\ \NNN and \<newline> : append next line
+--
+-- the getString function gives back bytes so we don't need to worry about
+-- the hex aspect.
+
+local some_dictionary
+local some_array
+local some_stream
+local some_reference
+
+local some_string = lpdf.frombytes
+
+local function get_value(document,t,key)
+ if not key then
+ return
+ end
+ local value = t[key]
+ if not value then
+ return
+ end
+ if type(value) ~= "table" then
+ return value
+ end
+ -- we can assume names to be simple and strings to be tables
+ local kind = value[1]
+ if kind == name_object_code then
+ return value[2]
+ elseif kind == string_object_code then
+ return some_string(value[2],value[3])
+ elseif kind == array_object_code then
+ return some_array(value[2],document)
+ elseif kind == dictionary_object_code then
+ return some_dictionary(value[2],document)
+ elseif kind == stream_object_code then
+ return some_stream(value,document)
+ elseif kind == reference_object_code then
+ return some_reference(value,document)
+ end
+ return value
+end
+
+some_dictionary = function (d,document)
+ local f = dictionarytotable(d,true)
+ local t = setmetatable({ __raw__ = f, __type__ = dictionary_object_code }, {
+ __index = function(t,k)
+ return get_value(document,f,k)
+ end,
+ __call = function(t,k)
+ return get_flagged(t,f,k)
+ end,
+ } )
+ return t, "dictionary"
+end
+
+some_array = function (a,document)
+ local f = arraytotable(a,true)
+ local n = #f
+ local t = setmetatable({ __raw__ = f, __type__ = array_object_code, n = n }, {
+ __index = function(t,k)
+ return get_value(document,f,k)
+ end,
+ __call = function(t,k)
+ return get_flagged(t,f,k)
+ end,
+ __len = function(t,k)
+ return n
+ end,
+ } )
+ return t, "array"
+end
+
+some_stream = function(s,d,document)
+ local f = dictionarytotable(d,true)
+ local t = setmetatable({ __raw__ = f, __type__ = stream_object_code }, {
+ __index = function(t,k)
+ return get_value(document,f,k)
+ end,
+ __call = function(t,raw)
+ if raw == false then
+ return readwholestream(s,false) -- original
+ else
+ return readwholestream(s,true) -- uncompressed
+ end
+ end,
+ } )
+ return t, "stream"
+end
+
+some_reference = function(r,document)
+ local objnum = r[3]
+ local cached = document.__cache__[objnum]
+ if not cached then
+ local kind, object, b, c = getfromreference(r[2])
+ if kind == dictionary_object_code then
+ cached = some_dictionary(object,document)
+ elseif kind == array_object_code then
+ cached = some_array(object,document)
+ elseif kind == stream_object_code then
+ cached = some_stream(object,b,document)
+ else
+ cached = { kind, object, b, c }
+ -- really cache this?
+ end
+ document.__cache__[objnum] = cached
+ document.__xrefs__[cached] = objnum
+ end
+ return cached
+end
+
+local resolvers = { }
+lpdf_epdf.resolvers = resolvers
+
+local function resolve(document,k)
+ local resolver = resolvers[k]
+ if resolver then
+ local entry = resolver(document)
+ document[k] = entry
+ return entry
+ end
+end
+
+local function getnames(document,n,target) -- direct
+ if n then
+ local Names = n.Names
+ if Names then
+ if not target then
+ target = { }
+ end
+ for i=1,#Names,2 do
+ target[Names[i]] = Names[i+1]
+ end
+ else
+ local Kids = n.Kids
+ if Kids then
+ for i=1,#Kids do
+ target = getnames(document,Kids[i],target)
+ end
+ end
+ end
+ return target
+ end
+end
+
+local function getkids(document,n,target) -- direct
+ if n then
+ local Kids = n.Kids
+ if Kids then
+ for i=1,#Kids do
+ target = getkids(document,Kids[i],target)
+ end
+ elseif target then
+ target[#target+1] = n
+ else
+ target = { n }
+ end
+ return target
+ end
+end
+
+function resolvers.destinations(document)
+ local Names = document.Catalog.Names
+ return getnames(document,Names and Names.Dests)
+end
+
+function resolvers.javascripts(document)
+ local Names = document.Catalog.Names
+ return getnames(document,Names and Names.JavaScript)
+end
+
+function resolvers.widgets(document)
+ local Names = document.Catalog.AcroForm
+ return Names and Names.Fields
+end
+
+function resolvers.embeddedfiles(document)
+ local Names = document.Catalog.Names
+ return getnames(document,Names and Names.EmbeddedFiles)
+end
+
+-- /OCProperties <<
+-- /OCGs [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ]
+-- /D <<
+-- /Order [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ]
+-- /ON [ 15 0 R 17 0 R 19 0 R 21 0 R 23 0 R 25 0 R 27 0 R ]
+-- /OFF [ ]
+-- >>
+-- >>
+
+function resolvers.layers(document)
+ local properties = document.Catalog.OCProperties
+ if properties then
+ local layers = properties.OCGs
+ if layers then
+ local t = { }
+ for i=1,#layers do
+ local layer = layers[i]
+ t[i] = layer.Name
+ end
+ -- t.n = n
+ return t
+ end
+ end
+end
+
+function resolvers.structure(document)
+ -- this might become a tree
+ return document.Catalog.StructTreeRoot
+end
+
+function resolvers.pages(document)
+ local __data__ = document.__data__
+ local __xrefs__ = document.__xrefs__
+ local __cache__ = document.__cache__
+ --
+ local nofpages = document.nofpages
+ local pages = { }
+ local rawpages = pagestotable(__data__)
+ document.pages = pages
+ --
+ for pagenumber=1,nofpages do
+ local rawpagedata = rawpages[pagenumber]
+ if rawpagedata then
+ local pagereference = rawpagedata[3]
+ local pageobject = rawpagedata[1]
+ local pagedata = some_dictionary(pageobject,document)
+ if pagedata and pageobject then
+ pagedata.number = pagenumber
+ pagedata.MediaBox = getbox(pageobject,"MediaBox")
+ pagedata.CropBox = getbox(pageobject,"CropBox")
+ pagedata.BleedBox = getbox(pageobject,"BleedBox")
+ pagedata.ArtBox = getbox(pageobject,"ArtBox")
+ pagedata.TrimBox = getbox(pageobject,"TrimBox")
+ pages[pagenumber] = pagedata
+ __xrefs__[pagedata] = pagereference
+ __cache__[pagereference] = pagedata
+ else
+ report_epdf("missing pagedata for page %i, case %i",pagenumber,1)
+ end
+ else
+ report_epdf("missing pagedata for page %i, case %i",pagenumber,2)
+ end
+ end
+ --
+ -- pages.n = nofpages
+ --
+ return pages
+end
+
+local loaded = { }
+local nofloaded = 0
+
+function lpdf_epdf.load(filename,userpassword,ownerpassword,fromstring)
+ local document = loaded[filename]
+ if not document then
+ statistics.starttiming(lpdf_epdf)
+ local __data__
+ local __file__
+ if fromstring then
+ __data__ = pdfnew(filename,#filename)
+ elseif pdfopenfile then
+ __data__ = pdfopenfile(ioopen(filename,"rb"))
+ else
+ __data__ = pdfopen(filename)
+ end
+ if __data__ then
+ if userpassword and getstatus(__data__) < 0 then
+ unencrypt(__data__,userpassword,nil)
+ end
+ if ownerpassword and getstatus(__data__) < 0 then
+ unencrypt(__data__,nil,ownerpassword)
+ end
+ if getstatus(__data__) < 0 then
+ report_epdf("the document is encrypted, provide proper passwords",getstatus(__data__))
+ __data__ = false
+ end
+ if __data__ then
+ document = {
+ filename = filename,
+ nofcopied = 0,
+ copied = { },
+ __cache__ = { },
+ __xrefs__ = { },
+ __fonts__ = { },
+ __copied__ = { },
+ __data__ = __data__,
+ }
+ document.Catalog = some_dictionary(getcatalog(__data__),document)
+ document.Info = some_dictionary(getinfo(__data__),document)
+ document.Trailer = some_dictionary(gettrailer(__data__),document)
+ --
+ setmetatableindex(document,resolve)
+ --
+ document.majorversion, document.minorversion = getversion(__data__)
+ --
+ document.nofpages = getnofpages(__data__)
+ else
+ document = false
+ end
+ else
+ document = false
+ end
+ loaded[filename] = document
+ loaded[document] = document
+ statistics.stoptiming(lpdf_epdf)
+ -- print(statistics.elapsedtime(lpdf_epdf))
+ end
+ if document then
+ nofloaded = nofloaded + 1
+ end
+ return document or nil
+end
+
+function lpdf_epdf.unload(filename)
+ if type(filename) == "table" then
+ filename = filename.filename
+ end
+ if type(filename) == "string" then
+ local document = loaded[filename]
+ if document then
+ loaded[document] = nil
+ loaded[filename] = nil
+ pdfclose(document.__data__)
+ end
+ end
+end
+
+-- for k, v in expanded(t) do
+
+local function expanded(t)
+ local function iterator(raw,k)
+ local k, v = next(raw,k)
+ if v then
+ return k, t[k]
+ end
+ end
+ return iterator, t.__raw__, nil
+end
+
+---------.expand = expand
+lpdf_epdf.expanded = expanded
+
+-- we could resolve the text stream in one pass if we directly handle the
+-- font but why should we complicate things
+
+local spaces = lpegpatterns.whitespace^1
+local optspaces = lpegpatterns.whitespace^0
+local comment = P("%") * (1 - lpegpatterns.newline)^0
+local numchar = P("\\")/"" * (R("09")^3/function(s) return char(tonumber(s,8)) end)
+ + P("\\") * P(1)
+local key = P("/") * C(R("AZ","az","09","__")^1)
+local number = Ct(Cc("number") * (lpegpatterns.number/tonumber))
+local keyword = Ct(Cc("name") * key)
+local operator = C((R("AZ","az")+P("*")+P("'")+P('"'))^1)
+
+local grammar = P { "start",
+ start = (comment + keyword + number + V("dictionary") + V("array") + V("hexstring") + V("decstring") + spaces)^1,
+ keyvalue = key * optspaces * V("start"),
+ array = Ct(Cc("array") * P("[") * Ct(V("start")^1) * P("]")),
+ dictionary = Ct(Cc("dict") * P("<<") * Ct(V("keyvalue")^1) * P(">>")),
+ hexstring = Ct(Cc("hex") * P("<") * Cs(( 1-P(">"))^1) * P(">")),
+ decstring = Ct(Cc("dec") * P("(") * Cs((numchar+1-(P")"))^1) * P(")")), -- untested
+}
+
+local operation = Ct(grammar^1 * operator)
+local parser = Ct((operation + P(1))^1)
+
+-- todo: speed this one up
+
+local numchar = P("\\") * (R("09")^3 + P(1))
+local number = lpegpatterns.number
+local keyword = P("/") * R("AZ","az","09","__")^1
+local operator = (R("AZ","az")+P("*")+P("'")+P('"'))^1
+
+local skipstart = P("BDC") + P("BMC") + P("DP") + P("MP")
+local skipstop = P("EMC")
+local skipkeep = P("/ActualText")
+
+local grammar = P { "skip",
+ start = keyword + number + V("dictionary") + V("array") + V("hexstring") + V("decstring") + spaces,
+ keyvalue = optspaces * (keyword * optspaces * V("start") * optspaces)^1,
+ xeyvalue = optspaces * ((keyword - skipkeep) * optspaces * V("start") * optspaces)^1,
+ array = P("[") * V("start")^0 * P("]"),
+ dictionary = P("<<") * V("keyvalue")^0 * P(">>"),
+ xictionary = P("<<") * V("xeyvalue")^0 * P(">>"),
+ hexstring = P("<") * ( 1-P(">"))^0 * P(">"),
+ decstring = P("(") * (numchar+1-(P")"))^0 * P(")"),
+ skip = (optspaces * ( keyword * optspaces * V("xictionary") * optspaces * skipstart + skipstop) / "")
+ + V("start")
+ + operator
+}
+
+local stripper = Cs((grammar + P(1))^1)
+
+function lpdf_epdf.parsecontent(str)
+ return lpegmatch(parser,str)
+end
+
+function lpdf_epdf.stripcontent(str)
+ if find(str,"EMC") then
+ return lpegmatch(stripper,str)
+ else
+ return str
+ end
+end
+
+-- beginbfrange : <start> <stop> <firstcode>
+-- <start> <stop> [ <firstsequence> <firstsequence> <firstsequence> ]
+-- beginbfchar : <code> <newcodes>
+
+local fromsixteen = lpdf.fromsixteen -- maybe inline the lpeg ... but not worth it
+
+local function f_bfchar(t,a,b)
+ t[tonumber(a,16)] = fromsixteen(b)
+end
+
+local function f_bfrange_1(t,a,b,c)
+ print("todo 1",a,b,c)
+ -- c is string
+ -- todo t[tonumber(a,16)] = fromsixteen(b)
+end
+
+local function f_bfrange_2(t,a,b,c)
+ print("todo 2",a,b,c)
+ -- c is table
+ -- todo t[tonumber(a,16)] = fromsixteen(b)
+end
+
+local optionals = spaces^0
+local hexstring = optionals * P("<") * C((1-P(">"))^1) * P(">")
+local bfchar = Carg(1) * hexstring * hexstring / f_bfchar
+local bfrange = Carg(1) * hexstring * hexstring * hexstring / f_bfrange_1
+ + Carg(1) * hexstring * hexstring * optionals * P("[") * Ct(hexstring^1) * optionals * P("]") / f_bfrange_2
+local fromunicode = (
+ P("beginbfchar" ) * bfchar ^1 * optionals * P("endbfchar" ) +
+ P("beginbfrange") * bfrange^1 * optionals * P("endbfrange") +
+ spaces +
+ P(1)
+)^1 * Carg(1)
+
+local function analyzefonts(document,resources) -- unfinished, see mtx-pdf for better code
+ local fonts = document.__fonts__
+ if resources then
+ local fontlist = resources.Font
+ if fontlist then
+ for id, data in expanded(fontlist) do
+ if not fonts[id] then
+ -- a quick hack ... I will look into it more detail if I find a real
+ -- -application for it
+ local tounicode = data.ToUnicode()
+ if tounicode then
+ tounicode = lpegmatch(fromunicode,tounicode,1,{})
+ end
+ fonts[id] = {
+ tounicode = type(tounicode) == "table" and tounicode or { }
+ }
+ setmetatableindex(fonts[id],"self")
+ end
+ end
+ end
+ end
+ return fonts
+end
+
+lpdf_epdf.analyzefonts = analyzefonts
+
+local more = 0
+local unic = nil -- cheaper than passing each time as Carg(1)
+
+local p_hex_to_utf = C(4) / function(s) -- needs checking !
+ local now = tonumber(s,16)
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong
+ more = 0
+ return unic[now] or utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ -- return ""
+ else
+ return unic[now] or utfchar(now)
+ end
+end
+
+local p_dec_to_utf = C(1) / function(s) -- needs checking !
+ local now = byte(s)
+ return unic[now] or utfchar(now)
+end
+
+local p_hex_to_utf = P(true) / function() more = 0 end * Cs(p_hex_to_utf^1)
+local p_dec_to_utf = P(true) / function() more = 0 end * Cs(p_dec_to_utf^1)
+
+function lpdf_epdf.getpagecontent(document,pagenumber)
+
+ local page = document.pages[pagenumber]
+
+ if not page then
+ return
+ end
+
+ local fonts = analyzefonts(document,page.Resources)
+
+ local content = page.Contents() or ""
+ local list = lpegmatch(parser,content)
+ local font = nil
+ -- local unic = nil
+
+ for i=1,#list do
+ local entry = list[i]
+ local size = #entry
+ local operator = entry[size]
+ if operator == "Tf" then
+ font = fonts[entry[1][2]]
+ unic = font and font.tounicode or { }
+ elseif operator == "TJ" then
+ local data = entry[1] -- { "array", { ... } }
+ local list = data[2] -- { { ... }, { ... } }
+ for i=1,#list do
+ local li = list[i]
+-- if type(li) == "table" then
+ local kind = li[1]
+ if kind == "hex" then
+ list[i] = lpegmatch(p_hex_to_utf,li[2])
+ elseif kind == "string" then
+ list[i] = lpegmatch(p_dec_to_utf,li[2])
+ else
+ list[i] = li[2] -- kern
+ end
+-- else
+-- -- kern
+-- end
+ end
+ elseif operator == "Tj" or operator == "'" or operator == '"' then
+ -- { string, Tj } { string, ' } { n, m, string, " }
+ local data = entry[size-1]
+ local list = data[2]
+ local kind = list[1]
+ if kind == "hex" then
+ list[2] = lpegmatch(p_hex_to_utf,li[2])
+ elseif kind == "string" then
+ list[2] = lpegmatch(p_dec_to_utf,li[2])
+ end
+ end
+ end
+
+ unic = nil -- can be collected
+
+ return list
+
+end
+
+-- This is also an experiment. When I really need it I can improve it, for instance
+-- with proper position calculating. It might be usefull for some search or so.
+
+local softhyphen = utfchar(0xAD) .. "$"
+local linefactor = 1.3
+
+function lpdf_epdf.contenttotext(document,list) -- maybe signal fonts
+ local last_y = 0
+ local last_f = 0
+ local text = { }
+ local last = 0
+
+ for i=1,#list do
+ local entry = list[i]
+ local size = #entry
+ local operator = entry[size]
+ if operator == "Tf" then
+ last_f = entry[2][2] -- size
+ elseif operator == "TJ" then
+ local data = entry[1] -- { "array", { ... } }
+ local list = data[2] -- { { ... }, { ... } }
+ for i=1,#list do
+ local li = list[i]
+ local kind = type(li)
+ if kind == "string" then
+ last = last + 1
+ text[last] = li
+ elseif kind == "number" and li < -50 then
+ last = last + 1
+ text[last] = " "
+ end
+ end
+ elseif operator == "Tj" then
+ last = last + 1
+ local li = entry[size-1]
+ local kind = type(li)
+ if kind == "string" then
+ last = last + 1
+ text[last] = li
+ end
+ elseif operator == "cm" or operator == "Tm" then
+ local data = entry
+ local ty = entry[6][2]
+ local dy = abs(last_y - ty)
+ if dy > linefactor*last_f then
+ if last > 0 then
+ if find(text[last],softhyphen,1,true) then
+ -- ignore
+ else
+ last = last + 1
+ text[last] = "\n"
+ end
+ end
+ end
+ last_y = ty
+ end
+ end
+
+ return concat(text)
+end
+
+function lpdf_epdf.getstructure(document,list) -- just a test
+ local depth = 0
+ for i=1,#list do
+ local entry = list[i]
+ local size = #entry
+ local operator = entry[size]
+ if operator == "BDC" then
+ report_epdf("%w%s : %s",depth,entry[1] or "?",entry[2] and entry[2].MCID or "?")
+ depth = depth + 1
+ elseif operator == "EMC" then
+ depth = depth - 1
+ elseif operator == "TJ" then
+ local list = entry[1]
+ for i=1,#list do
+ local li = list[i]
+ if type(li) == "string" then
+ report_epdf("%w > %s",depth,li)
+ elseif li < -50 then
+ report_epdf("%w >",depth,li)
+ end
+ end
+ elseif operator == "Tj" then
+ report_epdf("%w > %s",depth,entry[size-1])
+ end
+ end
+end
+
+if images then do
+
+ -- This can be made a bit faster (just get raw data and pass it) but I will
+ -- do that later. In the end the benefit is probably neglectable.
+
+ local recompress = false
+ local stripmarked = false
+
+ local copydictionary = nil
+ local copyarray = nil
+
+ local pdfreference = lpdf.reference
+ local pdfconstant = lpdf.constant
+ local pdfarray = lpdf.array
+ local pdfdictionary = lpdf.dictionary
+ local pdfnull = lpdf.null
+ local pdfliteral = lpdf.literal
+
+ local pdfreserveobject
+ local shareobjectreference
+ local pdfflushobject
+ local pdfflushstreamobject
+
+ updaters.register("backend.update.lpdf",function()
+ pdfreserveobject = lpdf.reserveobject
+ shareobjectreference = lpdf.shareobjectreference
+ pdfflushobject = lpdf.flushobject
+ pdfflushstreamobject = lpdf.flushstreamobject
+ end)
+
+ local report = logs.reporter("backend","xobjects")
+
+ local factor = 65536 / (7200/7227) -- 1/number.dimenfactors.bp
+
+ local createimage = images.create
+
+ directives.register("graphics.pdf.recompress", function(v) recompress = v end)
+ directives.register("graphics.pdf.stripmarked", function(v) stripmarked = v end)
+
+ local function scaledbbox(b)
+ return { b[1]*factor, b[2]*factor, b[3]*factor, b[4]*factor }
+ end
+
+ local codecs = {
+ ASCIIHexDecode = true,
+ ASCII85Decode = true,
+ RunLengthDecode = true,
+ FlateDecode = true,
+ LZWDecode = true,
+ }
+
+ local function deepcopyobject(xref,copied,value)
+ -- no need for tables, just nested loop with obj
+ local objnum = xref[value]
+ if objnum then
+ local usednum = copied[objnum]
+ if usednum then
+ -- report("%s object %i is reused",kind,objnum)
+ else
+ usednum = pdfreserveobject()
+ copied[objnum] = usednum
+ local entry = value
+ local kind = entry.__type__
+ if kind == array_object_code then
+ local a = copyarray(xref,copied,entry)
+ pdfflushobject(usednum,tostring(a))
+ elseif kind == dictionary_object_code then
+ local d = copydictionary(xref,copied,entry)
+ pdfflushobject(usednum,tostring(d))
+ elseif kind == stream_object_code then
+ local d = copydictionary(xref,copied,entry)
+ local filter = d.Filter
+ if filter and codecs[filter] and recompress then
+ -- recompress
+ d.Filter = nil
+ d.Length = nil
+ d.DecodeParms = nil -- relates to filter
+ d.DL = nil -- needed?
+ local s = entry() -- get uncompressed stream
+ pdfflushstreamobject(s,d,true,usednum) -- compress stream
+ else
+ -- keep as-is, even Length which indicates the
+ -- decompressed length
+ local s = entry(false) -- get compressed stream
+ -- pdfflushstreamobject(s,d,false,usednum,true) -- don't compress stream
+ pdfflushstreamobject(s,d,"raw",usednum) -- don't compress stream
+ end
+ else
+ local t = type(value)
+ if t == "string" then
+ value = pdfconstant(value)
+ elseif t == "table" then
+ local kind = value[1]
+ local entry = value[2]
+ if kind == name_object_code then
+ value = pdfconstant(entry)
+ elseif kind == string_object_code then
+ value = pdfliteral(entry,value[3])
+ elseif kind == null_object_code then
+ value = pdfnull()
+ elseif kind == reference_object_code then
+ value = deepcopyobject(xref,copied,entry)
+ elseif entry == nil then
+ value = pdfnull()
+ else
+ value = tostring(entry)
+ end
+ end
+ pdfflushobject(usednum,value)
+ end
+ end
+ return pdfreference(usednum)
+ elseif kind == stream_object_code then
+ report("stream not done: %s", objectcodes[kind] or "?")
+ else
+ report("object not done: %s", objectcodes[kind] or "?")
+ end
+ end
+
+ local function copyobject(xref,copied,object,key,value)
+ if not value then
+ value = object.__raw__[key]
+ end
+ local t = type(value)
+ if t == "string" then
+ return pdfconstant(value)
+ elseif t ~= "table" then
+ return value
+ end
+ local kind = value[1]
+ if kind == name_object_code then
+ return pdfconstant(value[2])
+ elseif kind == string_object_code then
+ return pdfliteral(value[2],value[3])
+ elseif kind == array_object_code then
+ return copyarray(xref,copied,object[key])
+ elseif kind == dictionary_object_code then
+ return copydictionary(xref,copied,object[key])
+ elseif kind == null_object_code then
+ return pdfnull()
+ elseif kind == reference_object_code then
+ -- expand
+ return deepcopyobject(xref,copied,object[key])
+ else
+ report("weird: %s", objecttypes[kind] or "?")
+ end
+ end
+
+ copyarray = function (xref,copied,object)
+ local target = pdfarray()
+ local source = object.__raw__
+ for i=1,#source do
+ target[i] = copyobject(xref,copied,object,i,source[i])
+ end
+ return target
+ end
+
+ local plugins = nil
+
+ -- Sorting the hash slows down upto 5% bit but it is still as fast as the C
+ -- code. We could loop over the index instead but sorting might be nicer in
+ -- the end.
+
+ copydictionary = function (xref,copied,object)
+ local target = pdfdictionary()
+ local source = object.__raw__
+ -- for key, value in next, source do
+ for key, value in sortedhash(source) do
+ if plugins then
+ local p = plugins[key]
+ if p then
+ target[key] = p(xref,copied,object,key,value,copyobject) -- maybe a table of methods
+ else
+ target[key] = copyobject(xref,copied,object,key,value)
+ end
+ else
+ target[key] = copyobject(xref,copied,object,key,value)
+ end
+ end
+ return target
+ end
+
+ -- local function copyresources(pdfdoc,xref,copied,pagedata)
+ -- local Resources = pagedata.Resources
+ -- if Resources then
+ -- local r = pdfreserveobject()
+ -- local d = copydictionary(xref,copied,Resources)
+ -- pdfflushobject(r,tostring(d))
+ -- return pdfreference(r)
+ -- end
+ -- end
+
+ local function copyresources(pdfdoc,xref,copied,pagedata)
+ local Resources = pagedata.Resources
+ --
+ -- -- This needs testing:
+ --
+ -- if not Resources then
+ -- local Parent = page.Parent
+ -- while (Parent and (Parent.__type__ == dictionary_object_code or Parent.__type__ == reference_object_code) do
+ -- Resources = Parent.Resources
+ -- if Resources then
+ -- break
+ -- end
+ -- Parent = Parent.Parent
+ -- end
+ -- end
+ if Resources then
+ local d = copydictionary(xref,copied,Resources)
+ return shareobjectreference(d)
+ end
+ end
+
+ local openpdf = lpdf_epdf.load
+ local closepdf = lpdf_epdf.unload
+
+ -- todo: keep track of already open files
+
+ local function newpdf(str,userpassword,ownerpassword)
+ return openpdf(str,userpassword,ownerpassword,true)
+ end
+
+ local sizes = {
+ crop = "CropBox",
+ media = "MediaBox",
+ bleed = "BleedBox",
+ art = "ArtBox",
+ trim = "TrimBox",
+ }
+
+ local function querypdf(pdfdoc,pagenumber,size)
+ if pdfdoc then
+ if not pagenumber then
+ pagenumber = 1
+ end
+ local root = pdfdoc.Catalog
+ local page = pdfdoc.pages[pagenumber]
+ if page then
+ local sizetag = sizes[size or "crop"] or sizes.crop
+ local mediabox = page.MediaBox or { 0, 0, 0, 0 }
+ local cropbox = page[sizetag] or mediabox
+ return {
+ filename = pdfdoc.filename,
+ pagenumber = pagenumber,
+ nofpages = pdfdoc.nofpages,
+ boundingbox = scaledbbox(cropbox),
+ cropbox = cropbox,
+ mediabox = mediabox,
+ bleedbox = page.BleedBox or cropbox,
+ trimbox = page.TrimBox or cropbox,
+ artbox = page.ArtBox or cropbox,
+ rotation = page.Rotate or 0,
+ xsize = cropbox[3] - cropbox[1],
+ ysize = cropbox[4] - cropbox[2],
+ }
+ end
+ end
+ end
+
+ local function copypage(pdfdoc,pagenumber,attributes,compact,width,height,attr)
+ if pdfdoc then
+ local root = pdfdoc.Catalog
+ local page = pdfdoc.pages[pagenumber or 1]
+ local pageinfo = querypdf(pdfdoc,pagenumber)
+ local contents = page.Contents
+ if contents then
+ local xref = pdfdoc.__xrefs__
+ local copied = pdfdoc.__copied__
+ if compact and lpdf_epdf.plugin then
+ plugins = lpdf_epdf.plugin(pdfdoc,xref,copied,page)
+ end
+ local xobject = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Form"),
+ FormType = 1,
+ Group = copyobject(xref,copied,page,"Group"),
+ LastModified = copyobject(xref,copied,page,"LastModified"),
+ Metadata = copyobject(xref,copied,page,"Metadata"),
+ PieceInfo = copyobject(xref,copied,page,"PieceInfo"),
+ Resources = copyresources(pdfdoc,xref,copied,page),
+ SeparationInfo = copyobject(xref,copied,page,"SeparationInfo"),
+ } + attr
+ if attributes then
+ for k, v in expanded(attributes) do
+ page[k] = v -- maybe nested
+ end
+ end
+ local content = ""
+ local nolength = nil
+ local ctype = contents.__type__
+ -- we always recompress because image object streams can not be
+ -- influenced (yet)
+ if ctype == stream_object_code then
+ if stripmarked then
+ content = contents() -- uncompressed
+ local stripped = lpdf_epdf.stripcontent(content)
+ if stripped ~= content then
+ -- report("%i bytes stripped on page %i",#content-#stripped,pagenumber or 1)
+ content = stripped
+ end
+ elseif recompress then
+ content = contents() -- uncompressed
+ else
+ local Filter = copyobject(xref,copied,contents,"Filter")
+ local Length = copyobject(xref,copied,contents,"Length")
+ if Length and Filter then
+ nolength = true
+ xobject.Length = Length
+ xobject.Filter = Filter
+ content = contents(false) -- uncompressed
+ else
+ content = contents() -- uncompressed
+ end
+ end
+ elseif ctype == array_object_code then
+ content = { }
+ for i=1,#contents do
+ content[i] = contents[i]() -- uncompressed
+ end
+ content = concat(content," ")
+ end
+ -- still not nice: we double wrap now
+ plugins = nil
+ local rotation = pageinfo.rotation
+ local boundingbox = pageinfo.boundingbox
+ local transform = nil
+ if rotation == 90 then
+ transform = 3
+ elseif rotation == 180 then
+ transform = 2
+ elseif rotation == 270 then
+ transform = 1
+ elseif rotation > 1 and rotation < 4 then
+ transform = rotation
+ end
+ xobject.BBox = pdfarray {
+ boundingbox[1] * bpfactor,
+ boundingbox[2] * bpfactor,
+ boundingbox[3] * bpfactor,
+ boundingbox[4] * bpfactor,
+ }
+ -- maybe like bitmaps
+ return createimage { -- beware: can be a img.new or a dummy
+ bbox = boundingbox,
+ transform = transform,
+ nolength = nolength,
+ nobbox = true,
+ notype = true,
+ stream = content, -- todo: no compress, pass directly also length, filter etc
+ attr = xobject(),
+ kind = images.types.stream,
+ }
+ else
+ -- maybe report an error
+ end
+ end
+ end
+
+ lpdf_epdf.image = {
+ open = openpdf,
+ close = closepdf,
+ new = newpdf,
+ query = querypdf,
+ copy = copypage,
+ }
+
+-- lpdf.injectors.pdf = function(specification)
+-- local d = lpdf_epdf.load(specification.filename)
+-- print(d)
+-- end
+
+
+end end
+
+-- local d = lpdf_epdf.load("e:/tmp/oeps.pdf")
+-- inspect(d)
+-- inspect(d.Catalog.Lang)
+-- inspect(d.Catalog.OCProperties.D.AS[1].Event)
+-- inspect(d.Catalog.Metadata())
+-- inspect(d.Catalog.Pages.Kids[1])
+-- inspect(d.layers)
+-- inspect(d.pages)
+-- inspect(d.destinations)
+-- inspect(lpdf_epdf.getpagecontent(d,1))
+-- inspect(lpdf_epdf.contenttotext(document,lpdf_epdf.getpagecontent(d,1)))
+-- inspect(lpdf_epdf.getstructure(document,lpdf_epdf.getpagecontent(d,1)))
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-ren.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-ren.lmt
new file mode 100644
index 00000000000..3794250d9ea
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-ren.lmt
@@ -0,0 +1,399 @@
+if not modules then modules = { } end modules ['lpdf-ren'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- rendering
+
+local tostring, tonumber, next = tostring, tonumber, next
+local concat = table.concat
+local formatters = string.formatters
+local settings_to_array = utilities.parsers.settings_to_array
+local getrandom = utilities.randomizer.get
+
+local backends, lpdf, nodes, node = backends, lpdf, nodes, node
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+local registrations = backends.pdf.registrations
+local viewerlayers = attributes.viewerlayers
+
+local references = structures.references
+
+references.executers = references.executers or { }
+local executers = references.executers
+
+local variables = interfaces.variables
+
+local v_no = variables.no
+local v_yes = variables.yes
+local v_start = variables.start
+local v_stop = variables.stop
+local v_reset = variables.reset
+local v_auto = variables.auto
+local v_random = variables.random
+
+local pdfconstant = lpdf.constant
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfreference = lpdf.reference
+
+local pdfflushobject
+local pdfreserveobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfreserveobject = lpdf.reserveobject
+end)
+
+local addtopageattributes = lpdf.addtopageattributes
+local addtopageresources = lpdf.addtopageresources
+local addtocatalog = lpdf.addtocatalog
+
+local escaped = lpdf.escaped
+
+local nuts = nodes.nuts
+local copy_node = nuts.copy
+
+local nodepool = nuts.pool
+local register = nodepool.register
+local setstate = nodepool.setstate
+
+local pdf_ocg = pdfconstant("OCG")
+local pdf_ocmd = pdfconstant("OCMD")
+local pdf_off = pdfconstant("OFF")
+local pdf_on = pdfconstant("ON")
+local pdf_view = pdfconstant("View")
+local pdf_design = pdfconstant("Design")
+local pdf_toggle = pdfconstant("Toggle")
+local pdf_setocgstate = pdfconstant("SetOCGState")
+
+local pdf_print = {
+ [v_yes] = pdfdictionary { PrintState = pdf_on },
+ [v_no ] = pdfdictionary { PrintState = pdf_off },
+}
+
+local pdf_intent = {
+ [v_yes] = pdf_view,
+ [v_no] = pdf_design,
+}
+
+local pdf_export = {
+ [v_yes] = pdf_on,
+ [v_no] = pdf_off,
+}
+
+-- We can have references to layers before they are places, for instance from
+-- hide and vide actions. This is why we need to be able to force usage of layers
+-- at several moments.
+
+-- management
+
+local pdfln, pdfld = { }, { }
+local textlayers, hidelayers, videlayers = pdfarray(), pdfarray(), pdfarray()
+local pagelayers, pagelayersreference, cache = nil, nil, { }
+local alphabetic = { }
+
+local escapednames = table.setmetatableindex(function(t,k)
+ local v = escaped(k)
+ t[k] = v
+ return v
+end)
+
+local specifications = { }
+local initialized = { }
+
+function codeinjections.defineviewerlayer(specification)
+ if viewerlayers.supported and textlayers then
+ local tag = specification.tag
+ if not specifications[tag] then
+ specifications[tag] = specification
+ end
+ end
+end
+
+local function useviewerlayer(name) -- move up so that we can use it as local
+ if not environment.initex and not initialized[name] then
+ local specification = specifications[name]
+ if specification then
+ specifications[name] = nil -- or not
+ initialized [name] = true
+ if not pagelayers then
+ pagelayers = pdfdictionary()
+ pagelayersreference = pdfreserveobject()
+ end
+ local tag = specification.tag
+ -- todo: reserve
+ local nn = pdfreserveobject()
+ local nr = pdfreference(nn)
+ local nd = pdfdictionary {
+ Type = pdf_ocg,
+ Name = specification.title or "unknown",
+ Usage = {
+ Intent = pdf_intent[specification.editable or v_yes], -- disable layer hiding by user (useless)
+ Print = pdf_print [specification.printable or v_yes], -- printable or not
+ Export = pdf_export[specification.export or v_yes], -- export or not
+ },
+ }
+ cache[#cache+1] = { nn, nd }
+ pdfln[tag] = nr -- was n
+ local dn = pdfreserveobject()
+ local dr = pdfreference(dn)
+ local dd = pdfdictionary {
+ Type = pdf_ocmd,
+ OCGs = pdfarray { nr },
+ }
+ cache[#cache+1] = { dn, dd }
+ pdfld[tag] = dr
+ textlayers[#textlayers+1] = nr
+ alphabetic[tag] = nr
+ if specification.visible == v_start then
+ videlayers[#videlayers+1] = nr
+ else
+ hidelayers[#hidelayers+1] = nr
+ end
+ pagelayers[escapednames[tag]] = dr -- check
+ else
+ -- todo: message
+ end
+ end
+end
+
+codeinjections.useviewerlayer = useviewerlayer
+
+local function layerreference(name)
+ local r = pdfln[name]
+ if r then
+ return r
+ else
+ useviewerlayer(name)
+ return pdfln[name]
+ end
+end
+
+lpdf.layerreference = layerreference -- also triggered when a hide or vide happens
+
+local function flushtextlayers()
+ if viewerlayers.supported then
+ if pagelayers then
+ pdfflushobject(pagelayersreference,pagelayers)
+ end
+ for i=1,#cache do
+ local ci = cache[i]
+ pdfflushobject(ci[1],ci[2])
+ end
+ if textlayers and #textlayers > 0 then -- we can group them if needed, like: layout
+ local sortedlayers = { }
+ for k, v in table.sortedhash(alphabetic) do
+ sortedlayers[#sortedlayers+1] = v -- maybe do a proper numeric sort as well
+ end
+ local d = pdfdictionary {
+ OCGs = textlayers,
+ D = pdfdictionary {
+ Name = "Document",
+ -- Order = (viewerlayers.hasorder and textlayers) or nil,
+ Order = (viewerlayers.hasorder and sortedlayers) or nil,
+ ON = videlayers,
+ OFF = hidelayers,
+ BaseState = pdf_on,
+ AS = pdfarray {
+ pdfdictionary {
+ Category = pdfarray { pdfconstant("Print") },
+ Event = pdfconstant("Print"),
+ OCGs = (viewerlayers.hasorder and sortedlayers) or nil,
+ }
+ },
+ },
+ }
+ addtocatalog("OCProperties",d)
+ textlayers = nil
+ end
+ end
+end
+
+local function flushpagelayers() -- we can share these
+ if pagelayers then
+ addtopageresources("Properties",pdfreference(pagelayersreference)) -- we could cache this
+ end
+end
+
+lpdf.registerpagefinalizer (flushpagelayers,"layers")
+lpdf.registerdocumentfinalizer(flushtextlayers,"layers")
+
+local function setlayer(what,arguments)
+ -- maybe just a gmatch of even better, earlier in lpeg
+ arguments = (type(arguments) == "table" and arguments) or settings_to_array(arguments)
+ local state = pdfarray { what }
+ for i=1,#arguments do
+ local p = layerreference(arguments[i])
+ if p then
+ state[#state+1] = p
+ end
+ end
+ return pdfdictionary {
+ S = pdf_setocgstate,
+ State = state,
+ }
+end
+
+function executers.hidelayer (arguments) return setlayer(pdf_off, arguments) end
+function executers.videlayer (arguments) return setlayer(pdf_on, arguments) end
+function executers.togglelayer(arguments) return setlayer(pdf_toggle,arguments) end
+
+-- injection
+
+local f_bdc = formatters["/OC /%s BDC"]
+local s_emc = "EMC"
+
+function codeinjections.startlayer(name) -- used in mp
+ if not name then
+ name = "unknown"
+ end
+ useviewerlayer(name)
+ return f_bdc(escapednames[name])
+end
+
+function codeinjections.stoplayer(name) -- used in mp
+ return s_emc
+end
+
+local cache = { }
+local stop = nil
+
+function nodeinjections.startlayer(name)
+ local c = cache[name]
+ if not c then
+ useviewerlayer(name)
+ c = register(setstate(f_bdc(escapednames[name])))
+ cache[name] = c
+ end
+ return copy_node(c)
+end
+
+function nodeinjections.stoplayer()
+ if not stop then
+ stop = register(setstate(s_emc))
+ end
+ return copy_node(stop)
+end
+
+-- experimental stacker code (slow, can be optimized): !!!! TEST CODE !!!!
+
+local values = viewerlayers.values
+local startlayer = codeinjections.startlayer
+local stoplayer = codeinjections.stoplayer
+
+function nodeinjections.startstackedlayer(s,t,first,last)
+ local r = { }
+ for i=first,last do
+ r[#r+1] = startlayer(values[t[i]])
+ end
+ r = concat(r," ")
+ return setstate(r)
+end
+
+function nodeinjections.stopstackedlayer(s,t,first,last)
+ local r = { }
+ for i=last,first,-1 do
+ r[#r+1] = stoplayer()
+ end
+ r = concat(r," ")
+ return setstate(r)
+end
+
+function nodeinjections.changestackedlayer(s,t1,first1,last1,t2,first2,last2)
+ local r = { }
+ for i=last1,first1,-1 do
+ r[#r+1] = stoplayer()
+ end
+ for i=first2,last2 do
+ r[#r+1] = startlayer(values[t2[i]])
+ end
+ r = concat(r," ")
+ return setstate(r)
+end
+
+-- transitions
+
+local pagetransitions = {
+ {"split","in","vertical"}, {"split","in","horizontal"},
+ {"split","out","vertical"}, {"split","out","horizontal"},
+ {"blinds","horizontal"}, {"blinds","vertical"},
+ {"box","in"}, {"box","out"},
+ {"wipe","east"}, {"wipe","west"}, {"wipe","north"}, {"wipe","south"},
+ {"dissolve"},
+ {"glitter","east"}, {"glitter","south"},
+ {"fly","in","east"}, {"fly","in","west"}, {"fly","in","north"}, {"fly","in","south"},
+ {"fly","out","east"}, {"fly","out","west"}, {"fly","out","north"}, {"fly","out","south"},
+ {"push","east"}, {"push","west"}, {"push","north"}, {"push","south"},
+ {"cover","east"}, {"cover","west"}, {"cover","north"}, {"cover","south"},
+ {"uncover","east"}, {"uncover","west"}, {"uncover","north"}, {"uncover","south"},
+ {"fade"},
+}
+
+local mapping = {
+ split = { "S" , pdfconstant("Split") },
+ blinds = { "S" , pdfconstant("Blinds") },
+ box = { "S" , pdfconstant("Box") },
+ wipe = { "S" , pdfconstant("Wipe") },
+ dissolve = { "S" , pdfconstant("Dissolve") },
+ glitter = { "S" , pdfconstant("Glitter") },
+ replace = { "S" , pdfconstant("R") },
+ fly = { "S" , pdfconstant("Fly") },
+ push = { "S" , pdfconstant("Push") },
+ cover = { "S" , pdfconstant("Cover") },
+ uncover = { "S" , pdfconstant("Uncover") },
+ fade = { "S" , pdfconstant("Fade") },
+ horizontal = { "Dm" , pdfconstant("H") },
+ vertical = { "Dm" , pdfconstant("V") },
+ ["in"] = { "M" , pdfconstant("I") },
+ out = { "M" , pdfconstant("O") },
+ east = { "Di" , 0 },
+ north = { "Di" , 90 },
+ west = { "Di" , 180 },
+ south = { "Di" , 270 },
+}
+
+local last = 0
+
+-- n: number, "stop", "reset", "random", "a,b,c" delay: number, "none"
+
+function codeinjections.setpagetransition(specification)
+ local n, delay = specification.n, specification.delay
+ if not n or n == "" then
+ return -- let's forget about it
+ elseif n == v_auto then
+ if last >= #pagetransitions then
+ last = 0
+ end
+ n = last + 1
+ elseif n == v_stop then
+ return
+ elseif n == v_reset then
+ last = 0
+ return
+ elseif n == v_random then
+ n = getrandom("transition",1,#pagetransitions)
+ else
+ n = tonumber(n)
+ end
+ local t = n and pagetransitions[n] or pagetransitions[1]
+ if not t then
+ t = settings_to_array(n)
+ end
+ if t and #t > 0 then
+ local d = pdfdictionary()
+ for i=1,#t do
+ local m = mapping[t[i]]
+ d[m[1]] = m[2]
+ end
+ delay = tonumber(delay)
+ if delay and delay > 0 then
+ addtopageattributes("Dur",delay)
+ end
+ addtopageattributes("Trans",d)
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-res.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-res.lmt
new file mode 100644
index 00000000000..d3c591343fc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-res.lmt
@@ -0,0 +1,41 @@
+if not modules then modules = { } end modules ['lpdf-res'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local codeinjections = backends.codeinjections
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+
+local setwhd = nuts.setwhd
+local setlist = nuts.setlist
+
+local new_hlist = nuts.pool.hlist
+
+local boxresources = tex.boxresources
+local saveboxresource = boxresources.save
+local useboxresource = boxresources.use
+local getboxresourcedimensions = boxresources.getdimensions
+
+local pdfcollectedresources = lpdf.collectedresources
+
+function codeinjections.registerboxresource(n,offset)
+ local r = saveboxresource(n,nil,pdfcollectedresources(),true,0,offset or 0) -- direct, todo: accept functions as attr/resources
+ return r
+end
+
+function codeinjections.restoreboxresource(index)
+ local hbox = new_hlist()
+ local list, wd, ht, dp = useboxresource(index)
+ setlist(hbox,tonut(list))
+ setwhd(hbox,wd,ht,dp)
+ return hbox -- so we return a nut !
+end
+
+function codeinjections.boxresourcedimensions(index)
+ return getboxresourcedimensions(index)
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-rul.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-rul.lmt
new file mode 100644
index 00000000000..efcc6207372
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-rul.lmt
@@ -0,0 +1,388 @@
+if not modules then modules = { } end modules ['lpdf-rul'] = {
+ version = 1.001,
+ comment = "companion to grph-rul.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tonumber, next, type = tonumber, next, type
+local concat = table.concat
+
+local attributes = attributes
+local nodes = nodes
+
+local bpfactor = number.dimenfactors.bp
+
+local nuts = nodes.nuts
+local ruleactions = nuts.rules.ruleactions
+
+local getwhd = nuts.getwhd
+
+local lefttoright_code = nodes.dirvalues.lefttoright
+
+local mpcolor = attributes.colors.mpcolor
+
+local trace_mp = false trackers.register("rules.mp", function(v) trace_mp = v end)
+
+local report_mp = logs.reporter("rules","mp")
+
+local floor = math.floor
+local getrandom = utilities.randomizer.get
+local formatters = string.formatters
+
+-- This is very pdf specific. Maybe move some to lpdf-rul.lua some day.
+
+local pdfprint ; pdfprint = function(...) pdfprint = lpdf.print return pdfprint(...) end
+
+-- updaters.register("backend.update",function() pdfprint = lpdf.print end)
+
+do
+
+ local simplemetapost = metapost.simple
+ local cachesize = 0
+ local maxcachesize = 256*1024
+ local cachethreshold = 1024
+ local caching = false -- otherwise random issues so we need a dedicated randomizer first
+
+ -- local maxcachesize = 8*1024
+ -- local cachethreshold = 1024/2
+
+ local cache = table.setmetatableindex(function(t,k)
+ local v = simplemetapost("rulefun",k) -- w, h, d
+ cachesize = cachesize + #v
+ if cachesize > maxcachesize then
+ -- print("old",cachesize)
+ for k, v in next, t do
+ local n = #v
+ if n > cachethreshold then
+ t[k] = nil
+ cachesize = cachesize - n
+ end
+ end
+ -- print("new",cachesize)
+ end
+ -- print(cachesize,maxcachesize,cachethreshold,#v)
+ t[k] = v
+ return v
+ end)
+
+ local replacer = utilities.templates.replacer
+
+ -- todo: RuleColor -> just string ?
+
+ local predefined = {
+ ["fake:word"] = replacer [[
+FakeWord(%width%,%height%,%depth%,%line%,%color%);
+ ]],
+ ["fake:rule"] = replacer[[
+%initializations%
+FakeRule(%width%,%height%,%depth%,%line%,%color%);
+ ]],
+ ["fake:rest"] = replacer [[
+RuleDirection := "%direction%" ;
+RuleOption := "%option%" ;
+RuleWidth := %width% ;
+RuleHeight := %height% ;
+RuleDepth := %depth% ;
+RuleH := %h% ;
+RuleV := %v% ;
+RuleThickness := %line% ;
+RuleFactor := %factor% ;
+RuleOffset := %offset% ;
+def RuleColor = %color% enddef ;
+%data%;
+ ]]
+ }
+
+ local initialized = false ;
+
+ local rule_mp = function(p,h,v,i,n)
+ local name = p.name or "fake:rest"
+ local code = (predefined[name] or predefined["fake:rest"]) {
+ data = p.data or "",
+ width = p.width * bpfactor,
+ height = p.height * bpfactor,
+ depth = p.depth * bpfactor,
+ factor = (p.factor or 0) * bpfactor, -- needs checking
+ offset = p.offset or 0,
+ line = (p.line or 65536) * bpfactor,
+ color = mpcolor(p.ma,p.ca,p.ta),
+ option = p.option or "",
+ direction = p.direction or lefttoright_code,
+ h = h * bpfactor,
+ v = v * bpfactor,
+ }
+ if not initialized then
+ initialized = true
+ simplemetapost("rulefun",formatters["randomseed := %s;"](getrandom("rulefun",0,4095)))
+ end
+ -- we enable extensions but we could also consider to delegate colors
+ -- to the node finalizer
+ local pdf = caching and cache[code] or simplemetapost("rulefun",code,true)
+ if trace_mp then
+ report_mp("code: %s",code)
+ report_mp("pdf : %s",pdf)
+ end
+ if pdf and pdf ~= "" then
+ pdfprint("direct",pdf)
+ end
+ end
+
+ updaters.register("backend.update.lpdf",function()
+ ruleactions.mp = rule_mp
+ end)
+
+end
+
+do
+
+ -- This is the old oval method that we keep it for compatibility reasons. Of
+ -- course one can use mp instead. It could be improved but at the cost of more
+ -- code than I'm willing to add for something hardly used.
+
+ local function round(p,kind)
+ local method = tonumber(p.corner) or 0
+ if method < 0 or method > 27 then
+ method = 0
+ end
+ local width = p.width or 0
+ local height = p.height or 0
+ local depth = p.depth or 0
+ local total = height + depth
+ local radius = p.radius or 655360
+ local line = p.line or 65536
+ local how = (method > 8 or kind ~= "fill") and "S" or "f"
+ local half = line / 2
+ local xmin = half * bpfactor
+ local xmax = ( width - half) * bpfactor
+ local ymax = ( height - half) * bpfactor
+ local ymin = (-depth + half) * bpfactor
+ local full = ( radius + half)
+ local xxmin = full * bpfactor
+ local xxmax = ( width - full) * bpfactor
+ local yymax = ( height - full) * bpfactor
+ local yymin = (-depth + full) * bpfactor
+ line = line * bpfactor
+ if xxmin <= xxmax and yymin <= yymax then
+ local list = nil
+ if method == 0 then
+ list = {
+ "q", line, "w", xxmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y",
+ xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", xxmin, ymax, "l", xmin, ymax,
+ xmin, yymax, "y", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", "h", how, "Q",
+ }
+ elseif method == 1 then
+ list = {
+ "q", line, "w", xxmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y",
+ xmax, ymax, "l", xmin, ymax, "l", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y",
+ "h", how, "Q",
+ }
+ elseif method == 2 then
+ list = {
+ "q", line, "w", xxmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xxmin, ymax,
+ "l", xmin, ymax, xmin, yymax, "y", xmin, yymin, "l", xmin, ymin, xxmin, ymin,
+ "y", "h", how, "Q",
+ }
+ elseif method == 3 then
+ list = {
+ "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, yymax, "l", xmax, ymax,
+ xxmax, ymax, "y", xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", xmin, ymin,
+ "l", "h", how, "Q",
+ }
+
+ elseif method == 4 then
+ list = {
+ "q", line, "w", xmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y",
+ xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", xmin, ymax, "l", xmin, ymin, "l",
+ "h", how, "Q",
+ }
+ elseif method == 5 then
+ list = {
+ "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, yymax, "l", xmax, ymax,
+ xxmax, ymax, "y", xmin, ymax, "l", xmin, ymin, "l", "h", how, "Q",
+ }
+ elseif method == 6 then
+ list = {
+ "q", line, "w", xmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y",
+ xmax, ymax, "l", xmin, ymax, "l", xmin, ymin, "l", "h", how, "Q",
+ }
+ elseif method == 7 then
+ list = {
+ "q", line, "w", xxmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xmin, ymax,
+ "l", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", "h", how, "Q",
+ }
+ elseif method == 8 then
+ list = {
+ "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xxmin, ymax,
+ "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, "l", "h", how, "Q",
+ }
+ elseif method == 9 then
+ list = {
+ "q", line, "w", xmin, ymax, "m", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y",
+ xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", xmax, ymax, "l", how, "Q",
+ }
+ elseif method == 10 then
+ list = {
+ "q", line, "w", xmax, ymax, "m", xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y",
+ xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", xmax, ymin, "l", how, "Q",
+ }
+ elseif method == 11 then
+ list = {
+ "q", line, "w", xmax, ymin, "m", xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y",
+ xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, "l", how, "Q",
+ }
+ elseif method == 12 then
+ list = {
+ "q", line, "w", xmin, ymax, "m", xxmax, ymax, "l", xmax, ymax, xmax, yymax, "y",
+ xmax, yymin, "l", xmax, ymin, xxmax, ymin, "y", xmin, ymin, "l", how, "Q",
+ }
+ elseif method == 13 then
+ list = {
+ "q", line, "w", xmin, ymax, "m", xxmax, ymax, "l", xmax, ymax, xmax, yymax, "y",
+ xmax, ymin, "l", how, "Q",
+ }
+ elseif method == 14 then
+ list = {
+ "q", line, "w", xmax, ymax, "m", xmax, yymin, "l", xmax, ymin, xxmax, ymin, "y",
+ xmin, ymin, "l", how, "Q",
+ }
+ elseif method == 15 then
+ list = {
+ "q", line, "w", xmax, ymin, "m", xxmin, ymin, "l", xmin, ymin, xmin, yymin, "y",
+ xmin, ymax, "l", how, "Q",
+ }
+ elseif method == 16 then
+ list = {
+ "q", line, "w", xmin, ymin, "m", xmin, yymax, "l", xmin, ymax, xxmin, ymax, "y",
+ xmax, ymax, "l", how, "Q",
+ }
+ elseif method == 17 then
+ list = {
+ "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", how, "Q",
+ }
+ elseif method == 18 then
+ list = {
+ "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", how, "Q",
+ }
+ elseif method == 19 then
+ list = {
+ "q", line, "w", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", how, "Q",
+ }
+ elseif method == 20 then
+ list = {
+ "q", line, "w", xmin, yymax, "m", xmin, ymax, xxmin, ymax, "y", how, "Q",
+ }
+ elseif method == 21 then
+ list = {
+ "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmin, yymax, "m",
+ xmin, ymax, xxmin, ymax, "y", how, "Q",
+ }
+ elseif method == 22 then
+ list = {
+ "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmax, yymin, "m",
+ xmax, ymin, xxmax, ymin, "y", how, "Q",
+ }
+ elseif method == 23 then
+ list = {
+ "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", xxmin, ymin, "m",
+ xmin, ymin, xmin, yymin, "y", how, "Q",
+ }
+ elseif method == 24 then
+ list = {
+ "q", line, "w", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", xmin, yymax, "m",
+ xmin, ymax, xxmin, ymax, "y", how, "Q",
+ }
+ elseif method == 25 then
+ list = {
+ "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmax, yymin, "m",
+ xmax, ymin, xxmax, ymin, "y", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y",
+ xmin, yymax, "m", xmin, ymax, xxmin, ymax, "y", how, "Q",
+ }
+ elseif method == 26 then
+ list = {
+ "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", xmin, yymax, "m",
+ xmin, ymax, xxmin, ymax, "y", how, "Q",
+ }
+
+ elseif method == 27 then
+ list = {
+ "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xxmin, ymin, "m",
+ xmin, ymin, xmin, yymin, "y", how, "Q",
+ }
+ end
+ pdfprint("direct",concat(list," "))
+ end
+ end
+
+ local f_rectangle = formatters["%.6N w %.6N %.6N %.6N %.6N re %s"]
+ local f_baselined = formatters["%.6N w %.6N %.6N %.6N %.6N re s %.6N %.6N m %.6N %.6N l s"]
+ local f_dashlined = formatters["%.6N w %.6N %.6N %.6N %.6N re s [%.6N %.6N] 2 d %.6N %.6N m %.6N %.6N l s"]
+ local f_radtangle = formatters[
+[[%.6N w %.6N %.6N m
+%.6N %.6N l %.6N %.6N %.6N %.6N y
+%.6N %.6N l %.6N %.6N %.6N %.6N y
+%.6N %.6N l %.6N %.6N %.6N %.6N y
+%.6N %.6N l %.6N %.6N %.6N %.6N y
+h %s]]
+ ]
+
+ local rule_any = function(p,h,v,i,n)
+ if p.corner then
+ return round(p,i)
+ else
+ local l = (p.line or 65536)*bpfactor
+ local r = p and (p.radius or 0)*bpfactor or 0
+ local w = h * bpfactor
+ local h = v * bpfactor
+ local m = nil
+ local t = i == "fill" and "f" or "s"
+ local o = l / 2
+ if r > 0 then
+ w = w - o
+ h = h - o
+ m = f_radtangle(l, r,o, w-r,o, w,o,w,r, w,h-r, w,h,w-r,h, r,h, o,h,o,h-r, o,r, o,o,r,o, t)
+ else
+ w = w - l
+ h = h - l
+ m = f_rectangle(l,o,o,w,h,t)
+ end
+ pdfprint("direct",m)
+ end
+ end
+
+ local function rule_box(p,h,v,i,n)
+ local w, h, d = getwhd(n)
+ local line = p.line or 65536
+ local l = line *bpfactor
+ local w = w * bpfactor
+ local h = h * bpfactor
+ local d = d * bpfactor
+ local o = l / 2
+ if (d >= 0 and h >= 0) or (d <= 0 and h <= 0) then
+ local dashed = tonumber(p.dashed)
+ if dashed and dashed > 5*line then
+ dashed = dashed * bpfactor
+ local delta = (w - 2*dashed*floor(w/(2*dashed)))/2
+ pdfprint("direct",f_dashlined(l,o,o,w-l,h+d-l,dashed,dashed,delta,d,w-delta,d))
+ else
+ pdfprint("direct",f_baselined(l,o,o,w-l,h+d-l,0,d,w,d))
+ end
+ else
+ pdfprint("direct",f_rectangle(l,o,o,w-l,h+d-l))
+ end
+ end
+
+ updaters.register("backend.update.lpdf",function()
+ ruleactions.fill = rule_any
+ ruleactions.draw = rule_any
+ ruleactions.stroke = rule_any
+ ruleactions.box = rule_box
+ end)
+
+end
+
+
+ ruleactions.mp = unsupported
+
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-tag.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-tag.lmt
new file mode 100644
index 00000000000..44a84ea59bb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-tag.lmt
@@ -0,0 +1,743 @@
+if not modules then modules = { } end modules ['lpdf-tag'] = {
+ version = 1.001,
+ comment = "companion to lpdf-tag.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, type = next, type
+local format, match, gmatch = string.format, string.match, string.gmatch
+local concat, sortedhash = table.concat, table.sortedhash
+local lpegmatch, P, S, C = lpeg.match, lpeg.P, lpeg.S, lpeg.C
+local settings_to_hash = utilities.parsers.settings_to_hash
+local formatters = string.formatters
+
+local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end)
+local trace_info = false trackers.register("structures.tags.info", function(v) trace_info = v end)
+
+local report_tags = logs.reporter("backend","tags")
+
+local backends = backends
+local lpdf = lpdf
+local nodes = nodes
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfboolean = lpdf.boolean
+local pdfconstant = lpdf.constant
+local pdfreference = lpdf.reference
+local pdfunicode = lpdf.unicode
+local pdfmakenametree = lpdf.makenametree
+
+local addtocatalog = lpdf.addtocatalog
+local addtopageattributes = lpdf.addtopageattributes
+
+local pdfflushobject
+local pdfreserveobject
+local pdfpagereference
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfreserveobject = lpdf.reserveobject
+ pdfpagereference = lpdf.pagereference
+end)
+
+local texgetcount = tex.getcount
+
+local nodecodes = nodes.nodecodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glyph_code = nodecodes.glyph
+
+local a_tagged = attributes.private('tagged')
+local a_image = attributes.private('image')
+
+local nuts = nodes.nuts
+
+local nodepool = nuts.pool
+local setstate = nodepool.setstate
+local register = nodepool.register
+
+local getid = nuts.getid
+local getattr = nuts.getattr
+local getprev = nuts.getprev
+local getnext = nuts.getnext
+local getlist = nuts.getlist
+local getchar = nuts.getchar
+
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+
+local copy_node = nuts.copy
+local tosequence = nuts.tosequence
+
+local nextnode = nuts.traversers.node
+
+local structure_kids -- delayed
+local structure_ref -- delayed
+local parent_ref -- delayed
+local root -- delayed
+local names = { }
+local tree = { }
+local elements = { }
+
+local structurestags = structures.tags
+local taglist = structurestags.taglist
+local specifications = structurestags.specifications
+local usedlabels = structurestags.labels
+local properties = structurestags.properties
+local usewithcare = structurestags.usewithcare
+
+local usedmapping = { }
+
+----- tagsplitter = structurestags.patterns.splitter
+
+local embeddedtags = false -- true will id all, for tracing, otherwise table
+local f_tagid = formatters["%s-%04i"]
+local embeddedfilelist = pdfarray() -- /AF crap
+
+-- for testing, not that it was ever used:
+
+directives.register("structures.tags.embed",function(v)
+ if type(v) == "string" then
+ if type(embeddedtags) ~= "table" then
+ embeddedtags = { }
+ end
+ for s in gmatch(v,"([^, ]+)") do
+ embeddedtags[s] = true
+ end
+ elseif v and not embeddedtags then
+ embeddedtags = true
+ end
+end)
+
+-- for old times sake, not that it was ever used:
+
+directives.register("structures.tags.embedmath",function(v)
+ if not v then
+ -- only enable
+ elseif embeddedtags == true then
+ -- already all tagged
+ elseif embeddedtags then
+ embeddedtags.math = true
+ else
+ embeddedtags = { math = true }
+ end
+end)
+
+function codeinjections.maptag(original,target,kind)
+ mapping[original] = { target, kind or "inline" }
+end
+
+-- mostly the same as the annotations tree
+
+local function finishstructure()
+ if root and #structure_kids > 0 then
+ local nums = pdfarray()
+ local n = 0
+ for i=1,#tree do
+ n = n + 1 ; nums[n] = i - 1
+ n = n + 1 ; nums[n] = pdfreference(pdfflushobject(tree[i]))
+ end
+ local parenttree = pdfdictionary {
+ Nums = nums
+ }
+ local idtree = pdfmakenametree(names)
+ --
+ local rolemap = pdfdictionary()
+ for k, v in next, usedmapping do
+ k = usedlabels[k] or k
+ local p = properties[k]
+ rolemap[k] = pdfconstant(p and p.pdf or "Span") -- or "Div"
+ end
+ local structuretree = pdfdictionary {
+ Type = pdfconstant("StructTreeRoot"),
+ K = pdfreference(pdfflushobject(structure_kids)),
+ ParentTree = pdfreference(pdfflushobject(parent_ref,parenttree)),
+ IDTree = idtree,
+ RoleMap = rolemap, -- sorted ?
+ }
+ pdfflushobject(structure_ref,structuretree)
+ addtocatalog("StructTreeRoot",pdfreference(structure_ref))
+ --
+ if lpdf.majorversion() == 1 then
+ local markinfo = pdfdictionary {
+ Marked = pdfboolean(true) or nil,
+ -- UserProperties = pdfboolean(true), -- maybe some day
+ -- Suspects = pdfboolean(true) or nil,
+ -- AF = #embeddedfilelist > 0 and pdfreference(pdfflushobject(embeddedfilelist)) or nil,
+ }
+ addtocatalog("MarkInfo",pdfreference(pdfflushobject(markinfo)))
+ end
+ --
+ for fulltag, element in sortedhash(elements) do -- sorting is easier on comparing pdf
+ pdfflushobject(element.knum,element.kids)
+ end
+ end
+end
+
+lpdf.registerdocumentfinalizer(finishstructure,"document structure")
+
+local index, pageref, pagenum, list = 0, nil, 0, nil
+
+local pdf_mcr = pdfconstant("MCR")
+local pdf_struct_element = pdfconstant("StructElem")
+local pdf_s = pdfconstant("S")
+
+local function initializepage()
+ index = 0
+ pagenum = texgetcount("realpageno")
+ pageref = pdfreference(pdfpagereference(pagenum))
+ list = pdfarray()
+ tree[pagenum] = list -- we can flush after done, todo
+end
+
+local function finishpage()
+ -- flush what can be flushed
+ addtopageattributes("StructParents",pagenum-1)
+ -- there might be more
+ addtopageattributes("Tabs",s)
+end
+
+-- here we can flush and free elements that are finished
+
+local pdf_userproperties = pdfconstant("UserProperties")
+
+-- /O /Table
+-- /Headers [ ]
+
+local function makeattribute(t)
+ if t and next(t) then
+ local properties = pdfarray()
+ for k, v in sortedhash(t) do -- easier on comparing pdf
+ properties[#properties+1] = pdfdictionary {
+ N = pdfunicode(k),
+ V = pdfunicode(v),
+ }
+ end
+ return pdfdictionary {
+ O = pdf_userproperties,
+ P = properties,
+ }
+ end
+end
+
+local function makeelement(fulltag,parent)
+ local specification = specifications[fulltag]
+ local tagname = specification.tagname
+ local tagnameused = tagname
+ local attributes = nil
+ if tagname == "ignore" then
+ return false
+ elseif tagname == "mstackertop" or tagname == "mstackerbot" or tagname == "mstackermid"then
+ -- TODO
+ return true
+ elseif tagname == "tabulatecell" then
+ local d = structurestags.gettabulatecell(fulltag)
+ if d and d.kind == 1 then
+ tagnameused = "tabulateheadcell"
+ end
+ elseif tagname == "tablecell" then
+ -- will become a plugin model
+ local d = structurestags.gettablecell(fulltag)
+ if d then
+ if d.kind == 1 then
+ tagnameused = "tableheadcell"
+ end
+ local rows = d.rows or 1
+ local cols = d.columns or 1
+ if rows > 1 or cols > 1 then
+ attributes = pdfdictionary {
+ O = pdfconstant("Table"),
+ RowSpan = rows > 1 and rows or nil,
+ ColSpan = cols > 1 and cols or nil,
+ }
+ end
+
+ end
+ end
+ --
+ local detail = specification.detail
+ local userdata = specification.userdata
+ --
+ usedmapping[tagname] = true
+ --
+ -- specification.attribute is unique
+ --
+ local id = nil
+ local af = nil
+ if embeddedtags then
+ local tagindex = specification.tagindex
+ if embeddedtags == true or embeddedtags[tagname] then
+ id = f_tagid(tagname,tagindex)
+ af = job.fileobjreferences.collected[id]
+ if af then
+ local r = pdfreference(af)
+ af = pdfarray { r }
+ -- embeddedfilelist[#embeddedfilelist+1] = r
+ end
+ end
+ end
+ --
+ local k = pdfarray()
+ local r = pdfreserveobject()
+ local t = usedlabels[tagnameused] or tagnameused
+ -- local a = nil
+ local d = pdfdictionary {
+ Type = pdf_struct_element,
+ S = pdfconstant(t),
+ ID = id,
+ T = detail and detail or nil,
+ P = parent.pref,
+ Pg = pageref,
+ K = pdfreference(r),
+ -- A = a and makeattribute(a) or nil,
+ A = attributes,
+ -- Alt = " Who cares ",
+ -- ActualText = " Hi Hans ",
+ AF = af,
+ }
+ local s = pdfreference(pdfflushobject(d))
+ if id and names then
+ names[id] = s
+ end
+ local kids = parent.kids
+ kids[#kids+1] = s
+ local e = {
+ tag = t,
+ pref = s,
+ kids = k,
+ knum = r,
+ pnum = pagenum
+ }
+ elements[fulltag] = e
+ return e
+end
+
+local f_BDC = formatters["/%s <</MCID %s>> BDC"]
+
+local function makecontent(parent,id,specification)
+ local tag = parent.tag
+ local kids = parent.kids
+ local last = index
+ if id == "image" then
+ local list = specification.taglist
+ local data = usewithcare.images[list[#list]]
+ local label = data and data.label
+ local d = pdfdictionary {
+ Type = pdf_mcr,
+ Pg = pageref,
+ MCID = last,
+ Alt = pdfunicode(label ~= "" and label or "image"),
+ }
+ kids[#kids+1] = d
+ elseif pagenum == parent.pnum then
+ kids[#kids+1] = last
+ else
+ local d = pdfdictionary {
+ Type = pdf_mcr,
+ Pg = pageref,
+ MCID = last,
+ }
+ -- kids[#kids+1] = pdfreference(pdfflushobject(d))
+ kids[#kids+1] = d
+ end
+ --
+ index = index + 1
+ list[index] = parent.pref -- page related list
+ --
+ return f_BDC(tag,last)
+end
+
+local function makeignore(specification)
+ return "/Artifact BMC"
+end
+
+-- no need to adapt head, as we always operate on lists
+
+local EMCliteral = nil
+local visualize = nil
+
+function nodeinjections.addtags(head)
+
+ if not EMCliteral then
+ EMCliteral = register(setstate("EMC"))
+ end
+
+ local last = nil
+ local ranges = { }
+ local range = nil
+
+ if not root then
+ structure_kids = pdfarray()
+ structure_ref = pdfreserveobject()
+ parent_ref = pdfreserveobject()
+ root = { pref = pdfreference(structure_ref), kids = structure_kids }
+ names = pdfarray()
+ end
+
+ local function collectranges(head,list)
+ for n, id in nextnode, head do
+ if id == glyph_code then
+ -- maybe also disc
+if getchar(n) ~= 0 then
+ local at = getattr(n,a_tagged) or false -- false: pagebody or so, so artifact
+ -- if not at then
+ -- range = nil
+ -- elseif ...
+ if last ~= at then
+ range = { at, "glyph", n, n, list } -- attr id start stop list
+ ranges[#ranges+1] = range
+ last = at
+ elseif range then
+ range[4] = n -- stop
+ end
+end
+ elseif id == hlist_code or id == vlist_code then
+ local at = getattr(n,a_image)
+ if at then
+ local at = getattr(n,a_tagged) or false -- false: pagebody or so, so artifact
+ -- if not at then
+ -- range = nil
+ -- else
+ ranges[#ranges+1] = { at, "image", n, n, list } -- attr id start stop list
+ -- end
+ last = nil
+ else
+ local list = getlist(n)
+ if list then
+ collectranges(list,n)
+ end
+ end
+ end
+ end
+ end
+
+ initializepage()
+
+ collectranges(head)
+
+-- inspect(taglist)
+-- inspect(ranges)
+
+ if trace_tags then
+ for i=1,#ranges do
+ local range = ranges[i]
+ local attr = range[1]
+ local id = range[2]
+ local start = range[3]
+ local stop = range[4]
+ local tags = taglist[attr]
+ if tags then -- not ok ... only first lines
+ report_tags("%s => %s : %05i % t",tosequence(start,start),tosequence(stop,stop),attr,tags.taglist)
+ end
+ end
+ end
+
+ local top = nil
+ local noftop = 0
+
+ local function inject(start,stop,list,literal,left,right)
+ local prev = getprev(start)
+ if prev then
+ setlink(prev,literal)
+ end
+ if left then
+ setlink(literal,left,start)
+ else
+ setlink(literal,start)
+ end
+ if list and not prev then
+ setlist(list,literal)
+ end
+ local literal = copy_node(EMCliteral)
+ -- use insert instead:
+ local next = getnext(stop)
+ if next then
+ setlink(literal,next)
+ end
+ if right then
+ setlink(stop,right,literal)
+ else
+ setlink(stop,literal)
+ end
+ end
+
+ for i=1,#ranges do
+
+ local range = ranges[i]
+ local attr = range[1]
+ local id = range[2]
+ local start = range[3]
+ local stop = range[4]
+ local list = range[5]
+
+ if attr then
+
+ local specification = taglist[attr]
+ local taglist = specification.taglist
+ local noftags = #taglist
+ local common = 0
+ local literal = nil
+ local ignore = false
+
+ if top then
+ for i=1,noftags >= noftop and noftop or noftags do
+ if top[i] == taglist[i] then
+ common = i
+ else
+ break
+ end
+ end
+ end
+
+ local prev = common > 0 and elements[taglist[common]] or root
+
+ for j=common+1,noftags do
+ local tag = taglist[j]
+ local prv = elements[tag] or makeelement(tag,prev)
+ if prv == false then
+ -- ignore this one
+ prev = false
+ ignore = true
+ break
+ elseif prv == true then
+ -- skip this one
+ else
+ prev = prv
+ end
+ end
+ if prev then
+ literal = setstate(makecontent(prev,id,specification))
+ elseif ignore then
+ literal = setstate(makeignore(specification))
+ else
+ -- maybe also ignore or maybe better: comment or so
+ end
+
+ if literal then
+ local left,right
+ if trace_info then
+ local name = specification.tagname
+ if name then
+ if not visualize then
+ visualize = nodes.visualizers.register("tags")
+ end
+ left = visualize(name)
+ right = visualize()
+ end
+ end
+ inject(start,stop,list,literal,left,right)
+ end
+
+ top = taglist
+ noftop = noftags
+
+ else
+
+ local literal = setstate(makeignore(specification))
+
+ inject(start,stop,list,literal)
+
+ end
+
+ end
+
+ finishpage()
+
+ return head
+
+end
+
+-- variant: more structure but funny collapsing in viewer
+
+-- function nodeinjections.addtags(head)
+--
+-- local last, ranges, range = nil, { }, nil
+--
+-- local function collectranges(head,list)
+-- for n, id in nextnode, head do
+-- if id == glyph_code then
+-- local at = getattr(n,a_tagged)
+-- if not at then
+-- range = nil
+-- elseif last ~= at then
+-- range = { at, "glyph", n, n, list } -- attr id start stop list
+-- ranges[#ranges+1] = range
+-- last = at
+-- elseif range then
+-- range[4] = n -- stop
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local at = getattr(n,a_image)
+-- if at then
+-- local at = getattr(n,a_tagged)
+-- if not at then
+-- range = nil
+-- else
+-- ranges[#ranges+1] = { at, "image", n, n, list } -- attr id start stop list
+-- end
+-- last = nil
+-- else
+-- local nl = getlist(n)
+-- collectranges(nl,n)
+-- end
+-- end
+-- end
+-- end
+--
+-- initializepage()
+--
+-- collectranges(head)
+--
+-- if trace_tags then
+-- for i=1,#ranges do
+-- local range = ranges[i]
+-- local attr = range[1]
+-- local id = range[2]
+-- local start = range[3]
+-- local stop = range[4]
+-- local tags = taglist[attr]
+-- if tags then -- not ok ... only first lines
+-- report_tags("%s => %s : %05i % t",tosequence(start,start),tosequence(stop,stop),attr,tags.taglist)
+-- end
+-- end
+-- end
+--
+-- local top = nil
+-- local noftop = 0
+-- local last = nil
+--
+-- for i=1,#ranges do
+-- local range = ranges[i]
+-- local attr = range[1]
+-- local id = range[2]
+-- local start = range[3]
+-- local stop = range[4]
+-- local list = range[5]
+-- local specification = taglist[attr]
+-- local taglist = specification.taglist
+-- local noftags = #taglist
+-- local tag = nil
+-- local common = 0
+-- -- local prev = root
+--
+-- if top then
+-- for i=1,noftags >= noftop and noftop or noftags do
+-- if top[i] == taglist[i] then
+-- common = i
+-- else
+-- break
+-- end
+-- end
+-- end
+--
+-- local result = { }
+-- local r = noftop - common
+-- if r > 0 then
+-- for i=1,r do
+-- result[i] = "EMC"
+-- end
+-- end
+--
+-- local prev = common > 0 and elements[taglist[common]] or root
+--
+-- for j=common+1,noftags do
+-- local tag = taglist[j]
+-- local prv = elements[tag] or makeelement(tag,prev)
+-- -- if prv == false then
+-- -- -- ignore this one
+-- -- prev = false
+-- -- break
+-- -- elseif prv == true then
+-- -- -- skip this one
+-- -- else
+-- prev = prv
+-- r = r + 1
+-- result[r] = makecontent(prev,id)
+-- -- end
+-- end
+--
+-- if r > 0 then
+-- local literal = setstate(concat(result,"\n"))
+-- -- use insert instead:
+-- local literal = setstate(result)
+-- local prev = getprev(start)
+-- if prev then
+-- setlink(prev,literal)
+-- end
+-- setlink(literal,start)
+-- if list and getlist(list) == start then
+-- setlist(list,literal)
+-- end
+-- end
+--
+-- top = taglist
+-- noftop = noftags
+-- last = stop
+--
+-- end
+--
+-- if last and noftop > 0 then
+-- local result = { }
+-- for i=1,noftop do
+-- result[i] = "EMC"
+-- end
+-- local literal = setstate(concat(result,"\n"))
+-- -- use insert instead:
+-- local next = getnext(last)
+-- if next then
+-- setlink(literal,next)
+-- end
+-- setlink(last,literal)
+-- end
+--
+-- finishpage()
+--
+-- return head
+--
+-- end
+
+-- this belongs elsewhere (export is not pdf related)
+
+local permitted = true
+local enabled = false
+
+function codeinjections.settaggingsupport(option)
+ if option == false then
+ if enabled then
+ disableaction("shipouts","structures.tags.handler")
+ disableaction("shipouts","nodes.handlers.accessibility") -- maybe not this one
+ disableaction("math","noads.handlers.tags")
+ enabled = false
+ end
+ if permitted then
+ if trace_tags then
+ report_tags("blocking structure tags")
+ end
+ permitted = false
+ end
+ end
+end
+
+function codeinjections.enabletags()
+ if permitted and not enabled then
+ structures.tags.handler = nodeinjections.addtags
+ enableaction("shipouts","structures.tags.handler")
+ enableaction("shipouts","nodes.handlers.accessibility")
+ enableaction("math","noads.handlers.tags")
+ -- maybe also textblock
+ if trace_tags then
+ report_tags("enabling structure tags")
+ end
+ enabled = true
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-u3d.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-u3d.lmt
new file mode 100644
index 00000000000..6e02fde3044
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-u3d.lmt
@@ -0,0 +1,495 @@
+if not modules then modules = { } end modules ['lpdf-u3d'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The following code is based on a working prototype provided
+-- by Michael Vidiassov. It is rewritten using the lpdf library
+-- and different checking is used. The macro calls are adapted
+-- (and will eventually be removed). The user interface needs
+-- an overhaul. There are some messy leftovers that will be
+-- removed in future versions.
+
+-- For some reason no one really tested this code so at some
+-- point we will end up with a reimplementation. For instance
+-- it makes sense to add the same activation code as with swf.
+
+local tonumber = tonumber
+local formatters, find = string.formatters, string.find
+local cos, sin, sqrt, pi, atan2, abs = math.cos, math.sin, math.sqrt, math.pi, math.atan2, math.abs
+
+local backends, lpdf = backends, lpdf
+
+local nodeinjections = backends.pdf.nodeinjections
+
+local pdfconstant = lpdf.constant
+local pdfboolean = lpdf.boolean
+local pdfunicode = lpdf.unicode
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfnull = lpdf.null
+local pdfreference = lpdf.reference
+
+local pdfflushstreamobject
+local pdfflushstreamfileobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushstreamobject = lpdf.flushstreamobject
+ pdfflushstreamfileobject = lpdf.flushstreamfileobject
+end)
+
+local checkedkey = lpdf.checkedkey
+local limited = lpdf.limited
+
+local embedimage = images.embed
+
+local schemes = table.tohash {
+ "Artwork", "None", "White", "Day", "Night", "Hard",
+ "Primary", "Blue", "Red", "Cube", "CAD", "Headlamp",
+}
+
+local modes = table.tohash {
+ "Solid", "SolidWireframe", "Transparent", "TransparentWireframe", "BoundingBox",
+ "TransparentBoundingBox", "TransparentBoundingBoxOutline", "Wireframe",
+ "ShadedWireframe", "HiddenWireframe", "Vertices", "ShadedVertices", "Illustration",
+ "SolidOutline", "ShadedIllustration",
+}
+
+local function normalize(x, y, z)
+ local modulo = sqrt(x*x + y*y + z*z);
+ if modulo ~= 0 then
+ return x/modulo, y/modulo, z/modulo
+ else
+ return x, y, z
+ end
+end
+
+local function rotate(vect_x,vect_y,vect_z, tet, axis_x,axis_y,axis_z)
+ -- rotate vect by tet about axis counterclockwise
+ local c, s = cos(tet*pi/180), sin(tet*pi/180)
+ local r = 1 - c
+ local n = sqrt(axis_x*axis_x+axis_y*axis_y+axis_z*axis_z)
+ axis_x, axis_y, axis_z = axis_x/n, axis_y/n, axis_z/n
+ return
+ (axis_x*axis_x*r+c )*vect_x + (axis_x*axis_y*r-axis_z*s)*vect_y + (axis_x*axis_z*r+axis_y*s)*vect_z,
+ (axis_x*axis_y*r+axis_z*s)*vect_x + (axis_y*axis_y*r+c )*vect_y + (axis_y*axis_z*r-axis_x*s)*vect_z,
+ (axis_x*axis_z*r-axis_y*s)*vect_x + (axis_y*axis_z*r+axis_x*s)*vect_y + (axis_z*axis_z*r+c )*vect_z
+end
+
+local function make3dview(view)
+
+ local name = view.name
+ local name = pdfunicode(name ~= "" and name or "unknown view")
+
+ local viewdict = pdfdictionary {
+ Type = pdfconstant("3DView"),
+ XN = name,
+ IN = name,
+ NR = true,
+ }
+
+ local bg = checkedkey(view,"bg","table")
+ if bg then
+ viewdict.BG = pdfdictionary {
+ Type = pdfconstant("3DBG"),
+ C = pdfarray { limited(bg[1],1,1,1), limited(bg[2],1,1,1), limited(bg[3],1,1,1) },
+ }
+ end
+
+ local lights = checkedkey(view,"lights","string")
+ if lights and schemes[lights] then
+ viewdict.LS = pdfdictionary {
+ Type = pdfconstant("3DLightingScheme"),
+ Subtype = pdfconstant(lights),
+ }
+ end
+
+ -- camera position is taken from 3d model
+
+ local u3dview = checkedkey(view, "u3dview", "string")
+ if u3dview then
+ viewdict.MS = pdfconstant("U3D")
+ viewdict.U3DPath = u3dview
+ end
+
+ -- position the camera as given
+
+ local c2c = checkedkey(view, "c2c", "table")
+ local coo = checkedkey(view, "coo", "table")
+ local roo = checkedkey(view, "roo", "number")
+ local azimuth = checkedkey(view, "azimuth", "number")
+ local altitude = checkedkey(view, "altitude", "number")
+
+ if c2c or coo or roo or azimuth or altitude then
+
+ local pos = checkedkey(view, "pos", "table")
+ local dir = checkedkey(view, "dir", "table")
+ local upv = checkedkey(view, "upv", "table")
+ local roll = checkedkey(view, "roll", "table")
+
+ local coo_x, coo_y, coo_z = 0, 0, 0
+ local dir_x, dir_y, dir_z = 0, 0, 0
+ local trans_x, trans_y, trans_z = 0, 0, 0
+ local left_x, left_y, left_z = 0, 0, 0
+ local up_x, up_y, up_z = 0, 0, 0
+
+ -- point camera is aimed at
+
+ if coo then
+ coo_x, coo_y, coo_z = tonumber(coo[1]) or 0, tonumber(coo[2]) or 0, tonumber(coo[3]) or 0
+ end
+
+ -- distance from camera to target
+
+ if roo then
+ roo = abs(roo)
+ end
+ if not roo or roo == 0 then
+ roo = 0.000000000000000001
+ end
+
+ -- set it via camera position
+
+ if pos then
+ dir_x = coo_x - (tonumber(pos[1]) or 0)
+ dir_y = coo_y - (tonumber(pos[2]) or 0)
+ dir_z = coo_z - (tonumber(pos[3]) or 0)
+ if not roo then
+ roo = sqrt(dir_x*dir_x + dir_y*dir_y + dir_z*dir_z)
+ end
+ if dir_x == 0 and dir_y == 0 and dir_z == 0 then dir_y = 1 end
+ dir_x, dir_y, dir_z = normalize(dir_x,dir_y,dir_z)
+ end
+
+ -- set it directly
+
+ if dir then
+ dir_x, dir_y, dir_z = tonumber(dir[1] or 0), tonumber(dir[2] or 0), tonumber(dir[3] or 0)
+ if dir_x == 0 and dir_y == 0 and dir_z == 0 then dir_y = 1 end
+ dir_x, dir_y, dir_z = normalize(dir_x,dir_y,dir_z)
+ end
+
+ -- set it movie15 style with vector from target to camera
+
+ if c2c then
+ dir_x, dir_y, dir_z = - tonumber(c2c[1] or 0), - tonumber(c2c[2] or 0), - tonumber(c2c[3] or 0)
+ if dir_x == 0 and dir_y == 0 and dir_z == 0 then dir_y = 1 end
+ dir_x, dir_y, dir_z = normalize(dir_x,dir_y,dir_z)
+ end
+
+ -- set it with azimuth and altitutde
+
+ if altitude or azimuth then
+ dir_x, dir_y, dir_z = -1, 0, 0
+ if altitude then dir_x, dir_y, dir_z = rotate(dir_x,dir_y,dir_z, -altitude, 0,1,0) end
+ if azimuth then dir_x, dir_y, dir_z = rotate(dir_x,dir_y,dir_z, azimuth, 0,0,1) end
+ end
+
+ -- set it with rotation like in MathGL
+
+ if rot then
+ if dir_x == 0 and dir_y == 0 and dir_z == 0 then dir_z = -1 end
+ dir_x,dir_y,dir_z = rotate(dir_x,dir_y,dir_z, tonumber(rot[1]) or 0, 1,0,0)
+ dir_x,dir_y,dir_z = rotate(dir_x,dir_y,dir_z, tonumber(rot[2]) or 0, 0,1,0)
+ dir_x,dir_y,dir_z = rotate(dir_x,dir_y,dir_z, tonumber(rot[3]) or 0, 0,0,1)
+ end
+
+ -- set it with default movie15 orientation looking up y axis
+
+ if dir_x == 0 and dir_y == 0 and dir_z == 0 then dir_y = 1 end
+
+ -- left-vector
+ -- up-vector
+
+ if upv then
+ up_x, up_y, up_z = tonumber(upv[1]) or 0, tonumber(upv[2]) or 0, tonumber(upv[3]) or 0
+ else
+ -- set default up-vector
+ if abs(dir_x) == 0 and abs(dir_y) == 0 then
+ if dir_z < 0 then
+ up_y = 1 -- top view
+ else
+ up_y = -1 -- bottom view
+ end
+ else
+ -- other camera positions than top and bottom, up-vector = up_world - (up_world dot dir) dir
+ up_x, up_y, up_z = - dir_z*dir_x, - dir_z*dir_y, - dir_z*dir_z + 1
+ end
+ end
+
+ -- normalize up-vector
+
+ up_x, up_y, up_z = normalize(up_x,up_y,up_z)
+
+ -- left vector = up x dir
+
+ left_x, left_y, left_z = dir_z*up_y - dir_y*up_z, dir_x*up_z - dir_z*up_x, dir_y*up_x - dir_x*up_y
+
+ -- normalize left vector
+
+ left_x, left_y, left_z = normalize(left_x,left_y,left_z)
+
+ -- apply camera roll
+
+ if roll then
+ local sinroll = sin((roll/180.0)*pi)
+ local cosroll = cos((roll/180.0)*pi)
+ left_x = left_x*cosroll + up_x*sinroll
+ left_y = left_y*cosroll + up_y*sinroll
+ left_z = left_z*cosroll + up_z*sinroll
+ up_x = up_x*cosroll + left_x*sinroll
+ up_y = up_y*cosroll + left_y*sinroll
+ up_z = up_z*cosroll + left_z*sinroll
+ end
+
+ -- translation vector
+
+ trans_x, trans_y, trans_z = coo_x - roo*dir_x, coo_y - roo*dir_y, coo_z - roo*dir_z
+
+ viewdict.MS = pdfconstant("M")
+ viewdict.CO = roo
+ viewdict.C2W = pdfarray {
+ left_x, left_y, left_z,
+ up_x, up_y, up_z,
+ dir_x, dir_y, dir_z,
+ trans_x, trans_y, trans_z,
+ }
+
+ end
+
+ local aac = tonumber(view.aac) -- perspective projection
+ local mag = tonumber(view.mag) -- ortho projection
+
+ if aac and aac > 0 and aac < 180 then
+ viewdict.P = pdfdictionary {
+ Subtype = pdfconstant("P"),
+ PS = pdfconstant("Min"),
+ FOV = aac,
+ }
+ elseif mag and mag > 0 then
+ viewdict.P = pdfdictionary {
+ Subtype = pdfconstant("O"),
+ OS = mag,
+ }
+ end
+
+ local mode = modes[view.rendermode]
+ if mode then
+ pdfdictionary {
+ Type = pdfconstant("3DRenderMode"),
+ Subtype = pdfconstant(mode),
+ }
+ end
+
+ -- crosssection
+
+ local crosssection = checkedkey(view,"crosssection","table")
+ if crosssection then
+ local crossdict = pdfdictionary {
+ Type = pdfconstant("3DCrossSection")
+ }
+
+ local c = checkedkey(crosssection,"point","table") or checkedkey(crosssection,"center","table")
+ if c then
+ crossdict.C = pdfarray { tonumber(c[1]) or 0, tonumber(c[2]) or 0, tonumber(c[3]) or 0 }
+ end
+
+ local normal = checkedkey(crosssection,"normal","table")
+ if normal then
+ local x, y, z = tonumber(normal[1] or 0), tonumber(normal[2] or 0), tonumber(normal[3] or 0)
+ if sqrt(x*x + y*y + z*z) == 0 then
+ x, y, z = 1, 0, 0
+ end
+ crossdict.O = pdfarray {
+ pdfnull,
+ atan2(-z,sqrt(x*x + y*y))*180/pi,
+ atan2(y,x)*180/pi,
+ }
+ end
+
+ local orient = checkedkey(crosssection,"orient","table")
+ if orient then
+ crossdict.O = pdfarray {
+ tonumber(orient[1]) or 1,
+ tonumber(orient[2]) or 0,
+ tonumber(orient[3]) or 0,
+ }
+ end
+
+ crossdict.IV = cross.intersection or false
+ crossdict.ST = cross.transparent or false
+
+ viewdict.SA = next(crossdict) and pdfarray { crossdict } -- maybe test if # > 1
+ end
+
+ local nodes = checkedkey(view,"nodes","table")
+ if nodes then
+ local nodelist = pdfarray()
+ for i=1,#nodes do
+ local node = checkedkey(nodes,i,"table")
+ if node then
+ local position = checkedkey(node,"position","table")
+ nodelist[#nodelist+1] = pdfdictionary {
+ Type = pdfconstant("3DNode"),
+ N = node.name or ("node_" .. i), -- pdfunicode ?
+ M = position and #position == 12 and pdfarray(position),
+ V = node.visible or true,
+ O = node.opacity or 0,
+ RM = pdfdictionary {
+ Type = pdfconstant("3DRenderMode"),
+ Subtype = pdfconstant(node.rendermode or "Solid"),
+ },
+ }
+ end
+ end
+ viewdict.NA = nodelist
+ end
+
+ return viewdict
+
+end
+
+local stored_js, stored_3d, stored_pr, streams = { }, { }, { }, { }
+
+local f_image = formatters["q /GS gs %.6N 0 0 %.6N 0 0 cm /IM Do Q"]
+
+local function insert3d(spec) -- width, height, factor, display, controls, label, foundname
+
+ local width, height, factor = spec.width, spec.height, spec.factor or number.dimenfactors.bp
+ local display, controls, label, foundname = spec.display, spec.controls, spec.label, spec.foundname
+
+ local param = (display and parametersets[display]) or { }
+ local streamparam = (controls and parametersets[controls]) or { }
+ local name = "3D Artwork " .. (param.name or label or "Unknown")
+
+ local activationdict = pdfdictionary {
+ TB = pdfboolean(param.toolbar,true),
+ NP = pdfboolean(param.tree,false),
+ }
+
+ local stream = streams[label]
+ if not stream then
+
+ local subtype, subdata = "U3D", io.loaddata(foundname) or ""
+ if find(subdata,"^PRC") then
+ subtype = "PRC"
+ elseif find(subdata,"^U3D") then
+ subtype = "U3D"
+ elseif file.suffix(foundname) == "prc" then
+ subtype = "PRC"
+ end
+
+ local attr = pdfdictionary {
+ Type = pdfconstant("3D"),
+ Subtype = pdfconstant(subtype),
+ }
+ local streamviews = checkedkey(streamparam, "views", "table")
+ if streamviews then
+ local list = pdfarray()
+ for i=1,#streamviews do
+ local v = checkedkey(streamviews, i, "table")
+ if v then
+ list[#list+1] = make3dview(v)
+ end
+ end
+ attr.VA = list
+ end
+ if checkedkey(streamparam, "view", "table") then
+ attr.DV = make3dview(streamparam.view)
+ elseif checkedkey(streamparam, "view", "string") then
+ attr.DV = streamparam.view
+ end
+ local js = checkedkey(streamparam, "js", "string")
+ if js then
+ local jsref = stored_js[js]
+ if not jsref then
+ jsref = pdfflushstreamfileobject(js)
+ stored_js[js] = jsref
+ end
+ attr.OnInstantiate = pdfreference(jsref)
+ end
+ stored_3d[label] = pdfflushstreamfileobject(foundname,attr)
+ stream = 1
+ else
+ stream = stream + 1
+ end
+ streams[label] = stream
+
+ local name = pdfunicode(name)
+
+ local annot = pdfdictionary {
+ Subtype = pdfconstant("3D"),
+ T = name,
+ Contents = name,
+ NM = name,
+ ["3DD"] = pdfreference(stored_3d[label]),
+ ["3DA"] = activationdict,
+ }
+ if checkedkey(param,"view","table") then
+ annot["3DV"] = make3dview(param.view)
+ elseif checkedkey(param,"view","string") then
+ annot["3DV"] = param.view
+ end
+
+ local preview = checkedkey(param,"preview","string")
+ if preview then
+ activationdict.A = pdfconstant("XA")
+ local tag = formatters["%s:%s:%s"](label,stream,preview)
+ local ref = stored_pr[tag]
+ if not ref then
+ local figure = embedimage {
+ filename = preview,
+ width = width,
+ height = height
+ }
+ ref = figure.objnum
+ stored_pr[tag] = ref
+ end
+ if ref then -- see back-pdf ** .. here we have a local /IM !
+ local pw = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Form"),
+ FormType = 1,
+ BBox = pdfarray { 0, 0, pdfnumber(factor*width), pdfnumber(factor*height) },
+ Matrix = pdfarray { 1, 0, 0, 1, 0, 0 },
+ ProcSet = lpdf.procset(),
+ Resources = pdfdictionary {
+ XObject = pdfdictionary {
+ IM = pdfreference(ref)
+ }
+ },
+ ExtGState = pdfdictionary {
+ GS = pdfdictionary {
+ Type = pdfconstant("ExtGState"),
+ CA = 1,
+ ca = 1,
+ }
+ },
+ }
+ local pwd = pdfflushstreamobject(f_image(factor*width,factor*height),pw)
+ annot.AP = pdfdictionary {
+ N = pdfreference(pwd)
+ }
+ end
+ return annot, figure, ref
+ else
+ activationdict.A = pdfconstant("PV")
+ return annot, nil, nil
+ end
+end
+
+function nodeinjections.insertu3d(spec)
+ local annotation, preview, ref = insert3d { -- just spec
+ foundname = spec.foundname,
+ width = spec.width,
+ height = spec.height,
+ factor = spec.factor,
+ display = spec.display,
+ controls = spec.controls,
+ label = spec.label,
+ }
+ node.write(nodeinjections.annotation(spec.width,spec.height,0,annotation()))
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-wid.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-wid.lmt
new file mode 100644
index 00000000000..268ca119e02
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-wid.lmt
@@ -0,0 +1,789 @@
+if not modules then modules = { } end modules ['lpdf-wid'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- It's about time to give up on media in pdf and admit that pdf lost it to html.
+-- First we had movies and sound, quite easy to deal with, but obsolete now. Then we
+-- had renditions but they turned out to be unreliable from the start and look
+-- obsolete too or at least they are bound to the (obsolete) flash technology for
+-- rendering. They were already complex constructs. Now we have rich media which
+-- instead of providing a robust future proof framework for general media types
+-- again seems to depend on viewers built in (yes, also kind of obsolete) flash
+-- technology, and we cannot expect this non-open technology to show up in open
+-- browsers. So, in the end we can best just use links to external resources to be
+-- future proof. Just look at the viewer preferences pane to see how fragile support
+-- is. Interestingly u3d support is kind of built in, while e.g. mp4 support relies
+-- on wrapping in swf. We used to stay ahead of the pack with support of the fancy
+-- pdf features but it backfires and is not worth the trouble. And yes, for control
+-- (even simple like starting and stopping videos) one has to revert to JavaScript,
+-- the other fragile bit. And, now that adobe quits flash in 2020 we're without any
+-- video anyway. Also, it won't play on all platforms and devices so let's wait for
+-- html5 media in pdf then.
+
+local tonumber, next = tonumber, next
+local gmatch, gsub, find, lower = string.gmatch, string.gsub, string.find, string.lower
+local filenameonly, basefilename, filesuffix, addfilesuffix = file.nameonly, file.basename, file.suffix, file.addsuffix
+local isfile, modificationtime = lfs.isfile, lfs.modification
+local stripstring = string.strip
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys
+
+local report_media = logs.reporter("backend","media")
+local report_attachment = logs.reporter("backend","attachment")
+
+local backends = backends
+local lpdf = lpdf
+local nodes = nodes
+local context = context
+
+local texgetcount = tex.getcount
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+local registrations = backends.pdf.registrations
+
+local executers = structures.references.executers
+local variables = interfaces.variables
+
+local v_hidden = variables.hidden
+local v_auto = variables.auto
+local v_embed = variables.embed
+local v_max = variables.max
+local v_yes = variables.yes
+
+local pdfconstant = lpdf.constant
+local pdfnull = lpdf.null
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfreference = lpdf.reference
+local pdfunicode = lpdf.unicode
+local pdfstring = lpdf.string
+local pdfboolean = lpdf.boolean
+local pdfaction = lpdf.action
+local pdfborder = lpdf.border
+
+local pdftransparencyvalue = lpdf.transparencyvalue
+local pdfcolorvalues = lpdf.colorvalues
+
+local pdfflushobject
+local pdfflushstreamobject
+local pdfflushstreamfileobject
+local pdfreserveobject
+local pdfpagereference
+local pdfshareobjectreference
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushobject = lpdf.flushobject
+ pdfflushstreamobject = lpdf.flushstreamobject
+ pdfflushstreamfileobject = lpdf.flushstreamfileobject
+ pdfreserveobject = lpdf.reserveobject
+ pdfpagereference = lpdf.pagereference
+ pdfshareobjectreference = lpdf.shareobjectreference
+end)
+
+
+local hpack_node = node.hpack
+local write_node = node.write -- test context(...) instead
+
+-- symbols
+
+local presets = { } -- xforms
+
+local function registersymbol(name,n)
+ presets[name] = pdfreference(n)
+end
+
+local function registeredsymbol(name)
+ return presets[name]
+end
+
+local function presetsymbol(symbol)
+ if not presets[symbol] then
+ context.predefinesymbol { symbol }
+ end
+end
+
+local function presetsymbollist(list)
+ if list then
+ for symbol in gmatch(list,"[^, ]+") do
+ presetsymbol(symbol)
+ end
+ end
+end
+
+codeinjections.registersymbol = registersymbol
+codeinjections.registeredsymbol = registeredsymbol
+codeinjections.presetsymbol = presetsymbol
+codeinjections.presetsymbollist = presetsymbollist
+
+-- comments
+
+-- local symbols = {
+-- Addition = pdfconstant("NewParagraph"),
+-- Attachment = pdfconstant("Attachment"),
+-- Balloon = pdfconstant("Comment"),
+-- Check = pdfconstant("Check Mark"),
+-- CheckMark = pdfconstant("Check Mark"),
+-- Circle = pdfconstant("Circle"),
+-- Cross = pdfconstant("Cross"),
+-- CrossHairs = pdfconstant("Cross Hairs"),
+-- Graph = pdfconstant("Graph"),
+-- InsertText = pdfconstant("Insert Text"),
+-- New = pdfconstant("Insert"),
+-- Paperclip = pdfconstant("Paperclip"),
+-- RightArrow = pdfconstant("Right Arrow"),
+-- RightPointer = pdfconstant("Right Pointer"),
+-- Star = pdfconstant("Star"),
+-- Tag = pdfconstant("Tag"),
+-- Text = pdfconstant("Note"),
+-- TextNote = pdfconstant("Text Note"),
+-- UpArrow = pdfconstant("Up Arrow"),
+-- UpLeftArrow = pdfconstant("Up-Left Arrow"),
+-- }
+
+local attachment_symbols = {
+ Graph = pdfconstant("Graph"),
+ Paperclip = pdfconstant("Paperclip"),
+ Pushpin = pdfconstant("PushPin"),
+}
+
+attachment_symbols.PushPin = attachment_symbols.Pushpin
+attachment_symbols.Default = attachment_symbols.Pushpin
+
+function lpdf.attachmentsymbols()
+ return sortedkeys(comment_symbols)
+end
+
+local comment_symbols = {
+ Comment = pdfconstant("Comment"),
+ Help = pdfconstant("Help"),
+ Insert = pdfconstant("Insert"),
+ Key = pdfconstant("Key"),
+ Newparagraph = pdfconstant("NewParagraph"),
+ Note = pdfconstant("Note"),
+ Paragraph = pdfconstant("Paragraph"),
+}
+
+comment_symbols.NewParagraph = Newparagraph
+comment_symbols.Default = Note
+
+function lpdf.commentsymbols()
+ return sortedkeys(comment_symbols)
+end
+
+local function analyzesymbol(symbol,collection)
+ if not symbol or symbol == "" then
+ return collection and collection.Default, nil
+ elseif collection and collection[symbol] then
+ return collection[symbol], nil
+ else
+ local setn, setr, setd
+ local set = settings_to_array(symbol)
+ if #set == 1 then
+ setn, setr, setd = set[1], set[1], set[1]
+ elseif #set == 2 then
+ setn, setr, setd = set[1], set[1], set[2]
+ else
+ setn, setr, setd = set[1], set[2], set[3]
+ end
+ local appearance = pdfdictionary {
+ N = setn and registeredsymbol(setn),
+ R = setr and registeredsymbol(setr),
+ D = setd and registeredsymbol(setd),
+ }
+ local appearanceref = pdfshareobjectreference(appearance)
+ return nil, appearanceref
+ end
+end
+
+local function analyzenormalsymbol(symbol)
+ local appearance = pdfdictionary {
+ N = registeredsymbol(symbol),
+ }
+ local appearanceref = pdfshareobjectreference(appearance)
+ return appearanceref
+end
+
+codeinjections.analyzesymbol = analyzesymbol
+codeinjections.analyzenormalsymbol = analyzenormalsymbol
+
+local function analyzelayer(layer)
+ -- todo: (specification.layer ~= "" and pdfreference(specification.layer)) or nil, -- todo: ref to layer
+end
+
+local function analyzecolor(colorvalue,colormodel)
+ local cvalue = colorvalue and tonumber(colorvalue)
+ local cmodel = colormodel and tonumber(colormodel) or 3
+ return cvalue and pdfarray { pdfcolorvalues(cmodel,cvalue) } or nil
+end
+
+local function analyzetransparency(transparencyvalue)
+ local tvalue = transparencyvalue and tonumber(transparencyvalue)
+ return tvalue and pdftransparencyvalue(tvalue) or nil
+end
+
+-- Attachments
+
+local nofattachments = 0
+local attachments = { }
+local filestreams = { }
+local referenced = { }
+local ignorereferenced = true -- fuzzy pdf spec .. twice in attachment list, can become an option
+local tobesavedobjrefs = utilities.storage.allocate()
+local collectedobjrefs = utilities.storage.allocate()
+local permitted = true
+local enabled = true
+
+function codeinjections.setattachmentsupport(option)
+ if option == false then
+ permitted = false
+ enabled = false
+ end
+end
+
+local fileobjreferences = {
+ collected = collectedobjrefs,
+ tobesaved = tobesavedobjrefs,
+}
+
+job.fileobjreferences = fileobjreferences
+
+local function initializer()
+ collectedobjrefs = job.fileobjreferences.collected or { }
+ tobesavedobjrefs = job.fileobjreferences.tobesaved or { }
+end
+
+job.register('job.fileobjreferences.collected', tobesavedobjrefs, initializer)
+
+local function flushembeddedfiles()
+ if enabled and next(filestreams) then
+ local e = pdfarray()
+ local f = pdfarray()
+ for tag, reference in sortedhash(filestreams) do
+ if not reference then
+ report_attachment("unreferenced file, tag %a",tag)
+ elseif referenced[tag] == "hidden" then
+ e[#e+1] = pdfstring(tag)
+ e[#e+1] = reference -- already a reference
+ f[#f+1] = reference -- collect all file description references
+ else
+ -- messy spec ... when annot not in named else twice in menu list acrobat
+ f[#f+1] = reference
+ end
+ end
+ if #e > 0 then
+ lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e })))
+ end
+ if #f > 0 then -- PDF/A-2|3: all associated files must have a relationship to the PDF document (global or part)
+ lpdf.addtocatalog("AF", pdfreference(pdfflushobject(f))) -- global (Catalog)
+ end
+ end
+end
+
+lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles")
+
+function codeinjections.embedfile(specification)
+ if enabled then
+ local data = specification.data
+ local filename = specification.file
+ local name = specification.name or ""
+ local title = specification.title or ""
+ local hash = specification.hash or filename
+ local keepdir = specification.keepdir -- can change
+ local usedname = specification.usedname
+ local filetype = specification.filetype
+ local compress = specification.compress
+ local mimetype = specification.mimetype or specification.mime
+ if filename == "" then
+ filename = nil
+ end
+ if compress == nil then
+ compress = true
+ end
+ if data then
+ local r = filestreams[hash]
+ if r == false then
+ return nil
+ elseif r then
+ return r
+ elseif not filename then
+ filename = specification.tag
+ if not filename or filename == "" then
+ filename = specification.registered
+ end
+ if not filename or filename == "" then
+ filename = hash
+ end
+ end
+ else
+ if not filename then
+ return nil
+ end
+ local r = filestreams[hash]
+ if r == false then
+ return nil
+ elseif r then
+ return r
+ else
+ local foundname = resolvers.findbinfile(filename) or ""
+ if foundname == "" or not isfile(foundname) then
+ filestreams[filename] = false
+ return nil
+ else
+ specification.foundname = foundname
+ end
+ end
+ end
+ -- needs to be cleaned up:
+ usedname = usedname ~= "" and usedname or filename or name
+ local basename = keepdir == true and usedname or basefilename(usedname)
+ local basename = gsub(basename,"%./","")
+ local savename = name ~= "" and name or basename
+ local foundname = specification.foundname or filename
+ if not filetype or filetype == "" then
+ filetype = name and (filename and filesuffix(filename)) or "txt"
+ end
+ savename = addfilesuffix(savename,filetype) -- type is mandate for proper working in viewer
+ local a = pdfdictionary {
+ Type = pdfconstant("EmbeddedFile"),
+ Subtype = mimetype and mimetype ~= "" and pdfconstant(mimetype) or nil,
+ }
+ local f
+ if data then
+ f = pdfflushstreamobject(data,a)
+ specification.data = true -- signal that still data but already flushed
+ else
+ local attributes = lfs.attributes(foundname)
+ local modification = modificationtime(foundname)
+ a.Params = {
+ Size = attributes.size,
+ ModDate = lpdf.pdftimestamp(modification),
+ }
+ f = pdfflushstreamfileobject(foundname,a,compress)
+ end
+ local d = pdfdictionary {
+ Type = pdfconstant("Filespec"),
+ F = pdfstring(savename),
+ -- UF = pdfstring(savename),
+ UF = pdfunicode(savename),
+ EF = pdfdictionary { F = pdfreference(f) },
+ Desc = title ~= "" and pdfunicode(title) or nil,
+ AFRelationship = pdfconstant("Unspecified"), -- Supplement, Data, Source, Alternative, Data
+ }
+ local r = pdfreference(pdfflushobject(d))
+ filestreams[hash] = r
+ return r
+ end
+end
+
+function nodeinjections.attachfile(specification)
+ if enabled then
+ local registered = specification.registered or "<unset>"
+ local data = specification.data
+ local hash
+ local filename
+ if data then
+ hash = md5.HEX(data)
+ else
+ filename = specification.file
+ if not filename or filename == "" then
+ report_attachment("no file specified, using registered %a instead",registered)
+ filename = registered
+ specification.file = registered
+ end
+ local foundname = resolvers.findbinfile(filename) or ""
+ if foundname == "" or not isfile(foundname) then
+ report_attachment("invalid filename %a, ignoring registered %a",filename,registered)
+ return nil
+ else
+ specification.foundname = foundname
+ end
+ hash = filename
+ end
+ specification.hash = hash
+ nofattachments = nofattachments + 1
+ local registered = specification.registered or ""
+ local title = specification.title or ""
+ local subtitle = specification.subtitle or ""
+ local author = specification.author or ""
+ local onlyname = filename and filenameonly(filename) or ""
+ if registered == "" then
+ registered = filename
+ end
+ if author == "" and title ~= "" then
+ author = title
+ title = onlyname or ""
+ end
+ if author == "" then
+ author = onlyname or "<unknown>"
+ end
+ if title == "" then
+ title = registered
+ end
+ if title == "" and filename then
+ title = onlyname
+ end
+ local aref = attachments[registered]
+ if not aref then
+ aref = codeinjections.embedfile(specification)
+ attachments[registered] = aref
+ end
+ local reference = specification.reference
+ if reference and aref then
+ tobesavedobjrefs[reference] = aref[1]
+ end
+ if not aref then
+ report_attachment("skipping attachment, registered %a",registered)
+ -- already reported
+ elseif specification.method == v_hidden then
+ referenced[hash] = "hidden"
+ else
+ referenced[hash] = "annotation"
+ local name, appearance = analyzesymbol(specification.symbol,attachment_symbols)
+ local flags = specification.flags or 0 -- to keep it expandable
+ local d = pdfdictionary {
+ Subtype = pdfconstant("FileAttachment"),
+ FS = aref,
+ Contents = pdfunicode(title),
+ Name = name,
+ NM = pdfstring("attachment:"..nofattachments),
+ T = author ~= "" and pdfunicode(author) or nil,
+ Subj = subtitle ~= "" and pdfunicode(subtitle) or nil,
+ C = analyzecolor(specification.colorvalue,specification.colormodel),
+ CA = analyzetransparency(specification.transparencyvalue),
+ AP = appearance,
+ OC = analyzelayer(specification.layer),
+ -- F = pdfnull(), -- another rediculous need to satisfy validation
+ F = bit32.band(bit32.bor(flags,4),(1023-1-2-32-256)), -- set 3, clear 1,2,6,9; PDF 32000-1, p385
+ }
+ local width = specification.width or 0
+ local height = specification.height or 0
+ local depth = specification.depth or 0
+ local box = hpack_node(nodeinjections.annotation(width,height,depth,d()))
+ box.width = width
+ box.height = height
+ box.depth = depth
+ return box
+ end
+ end
+end
+
+function codeinjections.attachmentid(filename) -- not used in context
+ return filestreams[filename]
+end
+
+-- Comments
+
+local nofcomments = 0
+local usepopupcomments = false
+
+local defaultattributes = {
+ ["xmlns"] = "http://www.w3.org/1999/xhtml",
+ ["xmlns:xfa"] = "http://www.xfa.org/schema/xfa-data/1.0/",
+ ["xfa:contentType"] = "text/html",
+ ["xfa:APIVersion"] = "Acrobat:8.0.0",
+ ["xfa:spec"] = "2.4",
+}
+
+local function checkcontent(text,option)
+ if option and option.xml then
+ local root = xml.convert(text)
+ if root and not root.er then
+ xml.checkbom(root)
+ local body = xml.first(root,"/body")
+ if body then
+ local at = body.at
+ for k, v in next, defaultattributes do
+ if not at[k] then
+ at[k] = v
+ end
+ end
+ -- local content = xml.textonly(root)
+ local richcontent = xml.tostring(root)
+ return nil, pdfunicode(richcontent)
+ end
+ end
+ end
+ return pdfunicode(text)
+end
+
+function nodeinjections.comment(specification) -- brrr: seems to be done twice
+ nofcomments = nofcomments + 1
+ local text = specification.data or ""
+ if specification.space ~= v_yes then
+ text = stripstring(text)
+ text = gsub(text,"[\n\r] *","\n")
+ end
+ text = gsub(text,"\r","\n")
+ local name, appearance = analyzesymbol(specification.symbol,comment_symbols)
+ local tag = specification.tag or "" -- this is somewhat messy as recent
+ local title = specification.title or "" -- versions of acrobat see the title
+ local subtitle = specification.subtitle or "" -- as author
+ local author = specification.author or ""
+ local option = settings_to_hash(specification.option or "")
+ if author ~= "" then
+ if subtitle == "" then
+ subtitle = title
+ elseif title ~= "" then
+ subtitle = subtitle .. ", " .. title
+ end
+ title = author
+ end
+ if title == "" then
+ title = tag
+ end
+ local content, richcontent = checkcontent(text,option)
+ local d = pdfdictionary {
+ Subtype = pdfconstant("Text"),
+ Open = option[v_max] and pdfboolean(true) or nil,
+ Contents = content,
+ RC = richcontent,
+ T = title ~= "" and pdfunicode(title) or nil,
+ Subj = subtitle ~= "" and pdfunicode(subtitle) or nil,
+ C = analyzecolor(specification.colorvalue,specification.colormodel),
+ CA = analyzetransparency(specification.transparencyvalue),
+ OC = analyzelayer(specification.layer),
+ Name = name,
+ NM = pdfstring("comment:"..nofcomments),
+ AP = appearance,
+ }
+ local width = specification.width or 0
+ local height = specification.height or 0
+ local depth = specification.depth or 0
+ local box
+ if usepopupcomments then
+ -- rather useless as we can hide/vide
+ local nd = pdfreserveobject()
+ local nc = pdfreserveobject()
+ local c = pdfdictionary {
+ Subtype = pdfconstant("Popup"),
+ Parent = pdfreference(nd),
+ }
+ d.Popup = pdfreference(nc)
+ box = hpack_node(
+ nodeinjections.annotation(0,0,0,d(),nd),
+ nodeinjections.annotation(width,height,depth,c(),nc)
+ )
+ else
+ box = hpack_node(nodeinjections.annotation(width,height,depth,d()))
+ end
+ box.width = width -- redundant
+ box.height = height -- redundant
+ box.depth = depth -- redundant
+ return box
+end
+
+-- rendering stuff
+--
+-- object_1 -> <</Type /Rendition /S /MR /C << /Type /MediaClip ... >> >>
+-- object_2 -> <</Type /Rendition /S /MR /C << /Type /MediaClip ... >> >>
+-- rendering -> <</Type /Rendition /S /MS [objref_1 objref_2]>>
+--
+-- we only work foreward here (currently)
+-- annotation is to be packed at the tex end
+
+-- aiff audio/aiff
+-- au audio/basic
+-- avi video/avi
+-- mid audio/midi
+-- mov video/quicktime
+-- mp3 audio/x-mp3 (mpeg)
+-- mp4 audio/mp4
+-- mp4 video/mp4
+-- mpeg video/mpeg
+-- smil application/smil
+-- swf application/x-shockwave-flash
+
+-- P media play parameters (evt /BE for controls etc
+-- A boolean (audio)
+-- C boolean (captions)
+-- O boolean (overdubs)
+-- S boolean (subtitles)
+-- PL pdfconstant("ADBE_MCI"),
+
+-- F = flags,
+-- T = title,
+-- Contents = rubish,
+-- AP = irrelevant,
+
+-- sound is different, no window (or zero) so we need to collect them and
+-- force them if not set
+
+local ms, mu, mf = { }, { }, { }
+
+local function delayed(label)
+ local a = pdfreserveobject()
+ mu[label] = a
+ return pdfreference(a)
+end
+
+local function insertrenderingwindow(specification)
+ local label = specification.label
+ -- local openpage = specification.openpage
+ -- local closepage = specification.closepage
+ if specification.option == v_auto then
+ if openpageaction then
+ -- \handlereferenceactions{\v!StartRendering{#2}}
+ end
+ if closepageaction then
+ -- \handlereferenceactions{\v!StopRendering {#2}}
+ end
+ end
+ local actions = nil
+ if openpage or closepage then
+ actions = pdfdictionary {
+ PO = (openpage and lpdfaction(openpage )) or nil,
+ PC = (closepage and lpdfaction(closepage)) or nil,
+ }
+ end
+ local page = tonumber(specification.page) or texgetcount("realpageno") -- todo
+ local r = mu[label] or pdfreserveobject() -- why the reserve here?
+ local a = pdfdictionary {
+ S = pdfconstant("Rendition"),
+ R = mf[label],
+ OP = 0,
+ AN = pdfreference(r),
+ }
+ local bs, bc = pdfborder()
+ local d = pdfdictionary {
+ Subtype = pdfconstant("Screen"),
+ P = pdfreference(pdfpagereference(page)),
+ A = a, -- needed in order to make the annotation clickable (i.e. don't bark)
+ Border = bs,
+ C = bc,
+ AA = actions,
+ }
+ local width = specification.width or 0
+ local height = specification.height or 0
+ if height == 0 or width == 0 then
+ -- todo: sound needs no window
+ end
+ write_node(nodeinjections.annotation(width,height,0,d(),r)) -- save ref
+ return pdfreference(r)
+end
+
+-- some dictionaries can have a MH (must honor) or BE (best effort) capsule
+
+local function insertrendering(specification)
+ local label = specification.label
+ local option = settings_to_hash(specification.option)
+ if not mf[label] then
+ local filename = specification.filename
+ local isurl = find(filename,"://",1,true)
+ local mimetype = specification.mimetype or specification.mime
+ -- local start = pdfdictionary {
+ -- Type = pdfconstant("MediaOffset"),
+ -- S = pdfconstant("T"), -- time
+ -- T = pdfdictionary { -- time
+ -- Type = pdfconstant("Timespan"),
+ -- S = pdfconstant("S"),
+ -- V = 3, -- time in seconds
+ -- },
+ -- }
+ -- local start = pdfdictionary {
+ -- Type = pdfconstant("MediaOffset"),
+ -- S = pdfconstant("F"), -- frame
+ -- F = 100 -- framenumber
+ -- }
+ -- local start = pdfdictionary {
+ -- Type = pdfconstant("MediaOffset"),
+ -- S = pdfconstant("M"), -- mark
+ -- M = "somemark",
+ -- }
+ -- local parameters = pdfdictionary {
+ -- BE = pdfdictionary {
+ -- B = start,
+ -- }
+ -- }
+ -- local parameters = pdfdictionary {
+ -- Type = pdfconstant(MediaPermissions),
+ -- TF = pdfstring("TEMPALWAYS") }, -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS
+ -- }
+ local descriptor = pdfdictionary {
+ Type = pdfconstant("Filespec"),
+ F = filename,
+ }
+ if isurl then
+ descriptor.FS = pdfconstant("URL")
+ elseif option[v_embed] then
+ descriptor.EF = codeinjections.embedfile {
+ file = filename,
+ mimetype = mimetype, -- yes or no
+ compress = false,
+ }
+ end
+ local clip = pdfdictionary {
+ Type = pdfconstant("MediaClip"),
+ S = pdfconstant("MCD"),
+ N = label,
+ CT = mimetype,
+ Alt = pdfarray { "", "file not found" }, -- language id + message
+ D = pdfreference(pdfflushobject(descriptor)),
+ -- P = pdfreference(pdfflushobject(parameters)),
+ }
+ local rendition = pdfdictionary {
+ Type = pdfconstant("Rendition"),
+ S = pdfconstant("MR"),
+ N = label,
+ C = pdfreference(pdfflushobject(clip)),
+ }
+ mf[label] = pdfreference(pdfflushobject(rendition))
+ end
+end
+
+local function insertrenderingobject(specification) -- todo
+ local label = specification.label
+ if not mf[label] then
+ report_media("unknown medium, label %a",label)
+ local clip = pdfdictionary { -- does not work that well one level up
+ Type = pdfconstant("MediaClip"),
+ S = pdfconstant("MCD"),
+ N = label,
+ D = pdfreference(unknown), -- not label but objectname, hm .. todo?
+ }
+ local rendition = pdfdictionary {
+ Type = pdfconstant("Rendition"),
+ S = pdfconstant("MR"),
+ N = label,
+ C = pdfreference(pdfflushobject(clip)),
+ }
+ mf[label] = pdfreference(pdfflushobject(rendition))
+ end
+end
+
+function codeinjections.processrendering(label)
+ local specification = interactions.renderings.rendering(label)
+ if not specification then
+ -- error
+ elseif specification.type == "external" then
+ insertrendering(specification)
+ else
+ insertrenderingobject(specification)
+ end
+end
+
+function codeinjections.insertrenderingwindow(specification)
+ local label = specification.label
+ codeinjections.processrendering(label)
+ ms[label] = insertrenderingwindow(specification)
+end
+
+local function set(operation,arguments)
+ codeinjections.processrendering(arguments)
+ return pdfdictionary {
+ S = pdfconstant("Rendition"),
+ OP = operation,
+ R = mf[arguments],
+ AN = ms[arguments] or delayed(arguments),
+ }
+end
+
+function executers.startrendering (arguments) return set(0,arguments) end
+function executers.stoprendering (arguments) return set(1,arguments) end
+function executers.pauserendering (arguments) return set(2,arguments) end
+function executers.resumerendering(arguments) return set(3,arguments) end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lpdf-xmp.lmt b/Master/texmf-dist/tex/context/base/mkxl/lpdf-xmp.lmt
new file mode 100644
index 00000000000..313488a3945
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lpdf-xmp.lmt
@@ -0,0 +1,311 @@
+if not modules then modules = { } end modules ['lpdf-xmp'] = {
+ version = 1.001,
+ comment = "companion to lpdf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ comment = "with help from Peter Rolf",
+}
+
+local tostring, type = tostring, type
+local format, gsub = string.format, string.gsub
+local utfchar = utf.char
+local xmlfillin = xml.fillin
+local md5HEX = md5.HEX
+
+local trace_xmp = false trackers.register("backend.xmp", function(v) trace_xmp = v end)
+local trace_info = false trackers.register("backend.info", function(v) trace_info = v end)
+
+local report_xmp = logs.reporter("backend","xmp")
+local report_info = logs.reporter("backend","info")
+
+local backends, lpdf = backends, lpdf
+
+local codeinjections = backends.pdf.codeinjections -- normally it is registered
+
+local pdfdictionary = lpdf.dictionary
+local pdfconstant = lpdf.constant
+local pdfreference = lpdf.reference
+
+local pdfgetmetadata = lpdf.getmetadata
+
+local pdfflushstreamobject
+
+updaters.register("backend.update.lpdf",function()
+ pdfflushstreamobject = lpdf.flushstreamobject
+end)
+
+-- The XMP packet wrapper is kind of fixed, see page 10 of XMPSpecificationsPart1.pdf from
+-- XMP-Toolkit-SDK-CC201607.zip. So we hardcode the id.
+
+local xpacket = format ( [[
+<?xpacket begin="%s" id="W5M0MpCehiHzreSzNTczkc9d"?>
+
+%%s
+
+<?xpacket end="w"?>]], utfchar(0xFEFF) )
+
+local mapping = {
+ -- user defined keys (pdfx:)
+ ["ConTeXt.Jobname"] = { "context", "rdf:Description/pdfx:ConTeXt.Jobname" },
+ ["ConTeXt.Time"] = { "date", "rdf:Description/pdfx:ConTeXt.Time" },
+ ["ConTeXt.Url"] = { "context", "rdf:Description/pdfx:ConTeXt.Url" },
+ ["ConTeXt.Support"] = { "context", "rdf:Description/pdfx:ConTeXt.Support" },
+ ["ConTeXt.Version"] = { "context", "rdf:Description/pdfx:ConTeXt.Version" },
+ ["ConTeXt.LMTX"] = { "context", "rdf:Description/pdfx:ConTeXt.LMTX" },
+ ["TeX.Support"] = { "metadata","rdf:Description/pdfx:TeX.Support" },
+ ["LuaTeX.Version"] = { "metadata","rdf:Description/pdfx:LuaTeX.Version" },
+ ["LuaTeX.Functionality"] = { "metadata","rdf:Description/pdfx:LuaTeX.Functionality" },
+ ["LuaTeX.LuaVersion"] = { "metadata","rdf:Description/pdfx:LuaTeX.LuaVersion" },
+ ["LuaTeX.Platform"] = { "metadata","rdf:Description/pdfx:LuaTeX.Platform" },
+ ["ID"] = { "id", "rdf:Description/pdfx:ID" }, -- has date
+ -- Adobe PDF schema
+ ["Keywords"] = { "metadata","rdf:Description/pdf:Keywords" },
+ ["Producer"] = { "metadata","rdf:Description/pdf:Producer" },
+ -- ["Trapped"] = { "pdf", "rdf:Description/pdf:Trapped" }, -- '/False' in /Info, but 'False' in XMP
+ -- Dublin Core schema
+ ["Author"] = { "metadata","rdf:Description/dc:creator/rdf:Seq/rdf:li" },
+ ["Format"] = { "metadata","rdf:Description/dc:format" }, -- optional, but nice to have
+ ["Subject"] = { "metadata","rdf:Description/dc:description/rdf:Alt/rdf:li" },
+ ["Title"] = { "metadata","rdf:Description/dc:title/rdf:Alt/rdf:li" },
+ -- XMP Basic schema
+ ["CreateDate"] = { "date", "rdf:Description/xmp:CreateDate" },
+ ["CreationDate"] = { "date", "rdf:Description/xmp:CreationDate" }, -- dummy
+ ["Creator"] = { "metadata","rdf:Description/xmp:CreatorTool" },
+ ["MetadataDate"] = { "date", "rdf:Description/xmp:MetadataDate" },
+ ["ModDate"] = { "date", "rdf:Description/xmp:ModDate" }, -- dummy
+ ["ModifyDate"] = { "date", "rdf:Description/xmp:ModifyDate" },
+ -- XMP Media Management schema
+ ["DocumentID"] = { "id", "rdf:Description/xmpMM:DocumentID" }, -- uuid
+ ["InstanceID"] = { "id", "rdf:Description/xmpMM:InstanceID" }, -- uuid
+ ["RenditionClass"] = { "pdf", "rdf:Description/xmpMM:RenditionClass" }, -- PDF/X-4
+ ["VersionID"] = { "pdf", "rdf:Description/xmpMM:VersionID" }, -- PDF/X-4
+ -- additional entries
+ -- PDF/X
+ ["GTS_PDFXVersion"] = { "pdf", "rdf:Description/pdfxid:GTS_PDFXVersion" },
+ -- optional entries
+ -- all what is visible in the 'document properties --> additional metadata' window
+ -- XMP Rights Management schema (optional)
+ ["Marked"] = { "pdf", "rdf:Description/xmpRights:Marked" },
+ -- ["Owner"] = { "metadata", "rdf:Description/xmpRights:Owner/rdf:Bag/rdf:li" }, -- maybe useful (not visible)
+ -- ["UsageTerms"] = { "metadata", "rdf:Description/xmpRights:UsageTerms" }, -- maybe useful (not visible)
+ ["WebStatement"] = { "metadata", "rdf:Description/xmpRights:WebStatement" },
+ -- Photoshop PDF schema (optional)
+ ["AuthorsPosition"] = { "metadata", "rdf:Description/photoshop:AuthorsPosition" },
+ ["Copyright"] = { "metadata", "rdf:Description/photoshop:Copyright" },
+ ["CaptionWriter"] = { "metadata", "rdf:Description/photoshop:CaptionWriter" },
+}
+
+local included = backends.included
+local lpdfid = lpdf.id
+
+function lpdf.id() -- overload of ini
+ return lpdfid(included.date)
+end
+
+local trailerid = nil
+local dates = nil
+
+local function update()
+ if trailer_id then
+ local b = toboolean(trailer_id) or trailer_id == ""
+ if b then
+ trailer_id = "This file is processed by ConTeXt and LuaTeX."
+ else
+ trailer_id = tostring(trailer_id)
+ end
+ local h = md5HEX(trailer_id)
+ if b then
+ report_info("using frozen trailer id")
+ else
+ report_info("using hashed trailer id %a (%a)",trailer_id,h)
+ end
+ trailerid = format("[<%s> <%s>]",h,h)
+ end
+ --
+ local t = type(dates)
+ if t == "number" or t == "string" then
+ local d = converters.totime(dates)
+ if d then
+ included.date = true
+ included.id = "fake"
+ report_info("forced date/time information %a will be used",lpdf.settime(d))
+ trailerid = false
+ elseif t == "string" then
+ dates = toboolean(dates)
+ included.date = dates
+ if dates ~= false then
+ included.id = true
+ else
+ report_info("no date/time but fake id information will be added")
+ trailerid = true
+ included.id = "fake"
+ end
+ end
+ end
+end
+
+lpdf.registerdocumentfinalizer(update,"trailer id and dates",1)
+
+directives.register("backend.trailerid", function(v) trailerid = v end)
+directives.register("backend.date", function(v) dates = v end)
+
+local function permitdetail(what)
+ local m = mapping[what]
+ if m then
+ return included[m[1]] and m[2]
+ else
+ return included[what] and true or false
+ end
+end
+
+lpdf.permitdetail = permitdetail
+
+-- maybe some day we will load the xmp file at runtime
+
+local xmp, xmpfile, xmpname = nil, nil, "lpdf-pdx.xml"
+
+local function setxmpfile(name)
+ if xmp then
+ report_xmp("discarding loaded file %a",xmpfile)
+ xmp = nil
+ end
+ xmpfile = name ~= "" and name
+end
+
+codeinjections.setxmpfile = setxmpfile
+
+interfaces.implement {
+ name = "setxmpfile",
+ arguments = "string",
+ actions = setxmpfile
+}
+
+local function valid_xmp()
+ if not xmp then
+ -- local xmpfile = xmpfile or resolvers.findfile(xmpname) or ""
+ if xmpfile and xmpfile ~= "" then
+ xmpfile = resolvers.findfile(xmpfile) or ""
+ end
+ if not xmpfile or xmpfile == "" then
+ xmpfile = resolvers.findfile(xmpname) or ""
+ end
+ if xmpfile ~= "" then
+ report_xmp("using file %a",xmpfile)
+ end
+ local xmpdata = xmpfile ~= "" and io.loaddata(xmpfile) or ""
+ xmp = xml.convert(xmpdata)
+ end
+ return xmp
+end
+
+function lpdf.addxmpinfo(tag,value,check)
+ local pattern = permitdetail(tag)
+ if type(pattern) == "string" then
+ xmlfillin(xmp or valid_xmp(),pattern,value,check)
+ end
+end
+
+-- redefined
+
+local pdfaddtoinfo = lpdf.addtoinfo
+local pdfaddxmpinfo = lpdf.addxmpinfo
+
+function lpdf.addtoinfo(tag,pdfvalue,strvalue)
+ local pattern = permitdetail(tag)
+ if pattern then
+ pdfaddtoinfo(tag,pdfvalue)
+ end
+ if type(pattern) == "string" then
+ local value = strvalue or gsub(tostring(pdfvalue),"^%((.*)%)$","%1") -- hack
+ if trace_info then
+ report_info("set %a to %a",tag,value)
+ end
+ xmlfillin(xmp or valid_xmp(),pattern,value,check)
+ end
+end
+
+local pdfaddtoinfo = lpdf.addtoinfo -- used later
+
+-- for the do-it-yourselvers
+
+function lpdf.insertxmpinfo(pattern,whatever,prepend)
+ xml.insert(xmp or valid_xmp(),pattern,whatever,prepend)
+end
+
+function lpdf.injectxmpinfo(pattern,whatever,prepend)
+ xml.inject(xmp or valid_xmp(),pattern,whatever,prepend)
+end
+
+-- flushing
+
+local add_xmp_blob = true directives.register("backend.xmp",function(v) add_xmp_blob = v end)
+
+local function flushxmpinfo()
+ commands.pushrandomseed()
+ commands.setrandomseed(os.time())
+
+ local documentid = "no unique document id here"
+ local instanceid = "no unique instance id here"
+ local metadata = pdfgetmetadata()
+ local time = metadata.time
+ local producer = metadata.producer
+ local creator = metadata.creator
+
+ if included.id ~= "fake" then
+ documentid = "uuid:" .. os.uuid()
+ instanceid = "uuid:" .. os.uuid()
+ end
+
+ pdfaddtoinfo("Producer",producer)
+ pdfaddtoinfo("Creator",creator)
+ pdfaddtoinfo("CreationDate",time)
+ pdfaddtoinfo("ModDate",time)
+
+ if add_xmp_blob then
+
+ pdfaddxmpinfo("DocumentID",documentid)
+ pdfaddxmpinfo("InstanceID",instanceid)
+ pdfaddxmpinfo("Producer",producer)
+ pdfaddxmpinfo("CreatorTool",creator)
+ pdfaddxmpinfo("CreateDate",time)
+ pdfaddxmpinfo("ModifyDate",time)
+ pdfaddxmpinfo("MetadataDate",time)
+ pdfaddxmpinfo("LuaTeX.Version",metadata.luatexversion)
+ pdfaddxmpinfo("LuaTeX.Functionality",metadata.luatexfunctionality)
+ pdfaddxmpinfo("LuaTeX.LuaVersion",metadata.luaversion)
+ pdfaddxmpinfo("LuaTeX.Platform",metadata.platform)
+
+ local blob = xml.tostring(xml.first(xmp or valid_xmp(),"/x:xmpmeta"))
+ local md = pdfdictionary {
+ Subtype = pdfconstant("XML"),
+ Type = pdfconstant("Metadata"),
+ }
+ if trace_xmp then
+ report_xmp("data flushed, see log file")
+ logs.pushtarget("logfile")
+ report_xmp("start xmp blob")
+ logs.newline()
+ logs.writer(blob)
+ logs.newline()
+ report_xmp("stop xmp blob")
+ logs.poptarget()
+ end
+ blob = format(xpacket,blob)
+ if not verbose and lpdf.compresslevel() > 0 then
+ blob = gsub(blob,">%s+<","><")
+ end
+ local r = pdfflushstreamobject(blob,md,false) -- uncompressed
+ lpdf.addtocatalog("Metadata",pdfreference(r))
+ end
+
+ commands.poprandomseed() -- hack
+end
+
+-- this will be enabled when we can inhibit compression for a stream at the lua end
+
+lpdf.registerdocumentfinalizer(flushxmpinfo,1,"metadata")
+
+directives.register("backend.verbosexmp", function(v)
+ verbose = v
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-bas.mkxl b/Master/texmf-dist/tex/context/base/mkxl/luat-bas.mkxl
new file mode 100644
index 00000000000..0add4ce6926
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-bas.mkxl
@@ -0,0 +1,52 @@
+%D \module
+%D [ file=luat-bas, % moved from luat-lib,
+%D version=2006.09.11,
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Basic \LUA\ Libraries,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Macros / Basic Lua Libraries}
+
+\registerctxluafile{l-bit32} {} % before sandbox
+\registerctxluafile{l-lua} {} % before sandbox
+\registerctxluafile{l-macro} {}
+\registerctxluafile{l-sandbox} {}
+\registerctxluafile{l-package} {}
+\registerctxluafile{l-lpeg} {}
+\registerctxluafile{l-function}{}
+\registerctxluafile{l-string} {}
+\registerctxluafile{l-table} {}
+\registerctxluafile{l-boolean} {}
+\registerctxluafile{l-number} {}
+\registerctxluafile{l-math} {}
+%registerctxluafile{l-aux} {}
+\registerctxluafile{l-io} {}
+\registerctxluafile{l-os} {}
+\registerctxluafile{l-file} {}
+\registerctxluafile{l-gzip} {}
+\registerctxluafile{l-md5} {}
+\registerctxluafile{l-sha} {}
+\registerctxluafile{l-dir} {}
+\registerctxluafile{l-unicode} {optimize}
+%registerctxluafile{l-utils} {}
+\registerctxluafile{l-url} {}
+\registerctxluafile{l-set} {}
+
+\registerctxluafile{l-macro-imp-optimize}{}
+
+% \registerctxluafile{socket}{}
+% \registerctxluafile{ltn12} {}
+% \registerctxluafile{mime} {}
+% \registerctxluafile{http} {}
+% \registerctxluafile{url} {}
+% \registerctxluafile{tp} {}
+% \registerctxluafile{ftp} {}
+% %registerctxluafile{smtp} {}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-cbk.lmt b/Master/texmf-dist/tex/context/base/mkxl/luat-cbk.lmt
new file mode 100644
index 00000000000..744d12e279a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-cbk.lmt
@@ -0,0 +1,287 @@
+if not modules then modules = { } end modules ['luat-cbk'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local insert, remove, concat = table.insert, table.remove, table.concat
+local find, format = string.find, string.format
+local collectgarbage, type, next = collectgarbage, type, next
+local round = math.round
+local sortedhash, sortedkeys, tohash = table.sortedhash, table.sortedkeys, table.tohash
+
+--[[ldx--
+<p>Callbacks are the real asset of <l n='luatex'/>. They permit you to hook
+your own code into the <l n='tex'/> engine. Here we implement a few handy
+auxiliary functions.</p>
+--ldx]]--
+
+callbacks = callbacks or { }
+local callbacks = callbacks
+
+--[[ldx--
+<p>When you (temporarily) want to install a callback function, and after a
+while wants to revert to the original one, you can use the following two
+functions. This only works for non-frozen ones.</p>
+--ldx]]--
+
+local trace_callbacks = false trackers.register("system.callbacks", function(v) trace_callbacks = v end)
+local trace_calls = false -- only used when analyzing performance and initializations
+local trace_checking = false trackers.register("memory.checking", function(v) trace_checking = v end)
+
+local report_system = logs.reporter("system")
+local report_callbacks = logs.reporter("system","callbacks")
+local report_memory = logs.reporter("system","memory")
+
+local register_callback = callback.register
+local find_callback = callback.find
+local list_callbacks = callback.list
+local register_usercall = false
+local original_register = register_callback
+
+local frozen = { }
+local stack = { }
+local list = callbacks.list
+local permit_overloads = false
+local block_overloads = false
+
+--[[ldx--
+<p>By now most callbacks are frozen and most provide a way to plug in your own code. For instance
+all node list handlers provide before/after namespaces and the file handling code can be extended
+by adding schemes and if needed I can add more hooks. So there is no real need to overload a core
+callback function. It might be ok for quick and dirty testing but anyway you're on your own if
+you permanently overload callback functions.</p>
+--ldx]]--
+
+-- This might become a configuration file only option when it gets abused too much.
+
+directives.register("system.callbacks.permitoverloads", function(v)
+ if block_overloads or permit_overloads then
+ -- once bad news, always bad news
+ elseif v then
+ permit_overloads = { }
+ report_system()
+ report_system("The callback system has been brought in an unprotected state. As a result of directly")
+ report_system("setting of callbacks subsystems of ConTeXt can stop working. There is no support for")
+ report_system("bugs resulting from this state. It's better to use the official extension mechanisms.")
+ report_system()
+ end
+end)
+
+sandbox.initializer {
+ category = "functions",
+ action = function()
+ block_overloads = true
+ end
+}
+
+if not list then -- otherwise counters get reset
+
+ list = utilities.storage.allocate(list_callbacks())
+
+ local supported = { }
+
+ for k in next, list do
+ list[k] = 0
+ supported[k] = true
+ end
+
+ callbacks.list = list
+ callbacks.supported = supported
+
+end
+
+local delayed = tohash {
+ "buildpage_filter",
+}
+
+if trace_calls then
+
+ local functions = { }
+
+ register_callback = function(name,func)
+ if type(func) == "function" then
+ if functions[name] then
+ functions[name] = func
+ return find_callback(name)
+ else
+ functions[name] = func
+ local cnuf = function(...)
+ list[name] = list[name] + 1
+ return functions[name](...)
+ end
+ return original_register(name,cnuf)
+ end
+ else
+ return original_register(name,func)
+ end
+ end
+
+end
+
+-- temporary, not public:
+
+callbacks.functions = { }
+
+-- till here
+
+local reported = { }
+
+local function register_usercall(what,name,func)
+ if list[name] then
+ if trace_callbacks or not reported[name] then
+ report_system()
+ report_system("disabling core code by %s user function into callback '%s' (reported only once)",what,name)
+ report_system()
+ reported[name] = true
+ end
+ permit_overloads[name] = true
+ return original_register(name,function(...)
+ if trace_callbacks then
+ report_callbacks("calling user function from '%s'",name)
+ end
+ return func(...)
+ end)
+ else
+ report_callbacks("not %s function into invalid callback '%s'",name)
+ return nil, format("unknown callback '%s'",name)
+ end
+end
+
+local function frozen_callback(name)
+ report_callbacks("not %s frozen %a","registering",name)
+ return nil, format("callback '%s' is frozen",name) -- no formatter yet
+end
+
+local function state(name)
+ local f = find_callback(name)
+ if f == false then
+ return "disabled"
+ elseif f then
+ return "enabled"
+ else
+ return "undefined"
+ end
+end
+
+function callbacks.known(name)
+ return list[name]
+end
+
+function callbacks.report()
+ for name, _ in sortedhash(list) do
+ local str = frozen[name]
+ if str then
+ report_callbacks("%s: %s -> %s",state(name),name,str)
+ else
+ report_callbacks("%s: %s",state(name),name)
+ end
+ end
+end
+
+function callbacks.freeze(name,freeze)
+ if not permit_overloads then
+ freeze = type(freeze) == "string" and freeze
+ if find(name,"*",1,true) then
+ local pattern = name
+ for name, _ in next, list do
+ if find(name,pattern) then
+ frozen[name] = freeze or frozen[name] or "frozen"
+ end
+ end
+ else
+ frozen[name] = freeze or frozen[name] or "frozen"
+ end
+ end
+end
+
+function callbacks.register(name,func,freeze)
+ if frozen[name] then
+ if permit_overloads then
+ return register_usercall("registering",name,func)
+ else
+ return frozen_callback(name)
+ end
+ elseif freeze then
+ frozen[name] = type(freeze) == "string" and freeze or "registered"
+ end
+ if delayed[name] and environment.initex then
+ return nil
+ end
+ return register_callback(name,func)
+end
+
+function callback.register(name,func) -- original
+ if not frozen[name] then
+ return register_callback(name,func)
+ elseif permit_overloads then
+ return register_usercall("registering",name,func)
+ else
+ return frozen_callback(name)
+ end
+end
+
+function callbacks.push(name,func)
+ if not frozen[name] or permit_overloads then
+ local sn = stack[name]
+ if not sn then
+ sn = { }
+ stack[name] = sn
+ end
+ insert(sn,find_callback(name))
+ if permit_overloads then
+ register_usercall("pushing",name,func)
+ else
+ register_callback(name,func)
+ end
+ else
+ report_callbacks("not %s frozen %a","pushing",name)
+ end
+end
+
+function callbacks.pop(name)
+ if not frozen[name] or permit_overloads then
+ local sn = stack[name]
+ if not sn or #sn == 0 then
+ -- some error
+ register_callback(name,nil) -- ! really needed
+ else
+ -- this fails: register_callback(name, remove(stack[name]))
+ local func = remove(sn)
+ register_callback(name,func)
+ end
+ end
+end
+
+if trace_calls then
+ statistics.register("callback details", function()
+ local t = { } -- todo: pass function to register and quit at nil
+ for name, n in sortedhash(list) do
+ if n > 0 then
+ t[#t+1] = format("%s -> %s",name,n)
+ end
+ end
+ return concat(t," ")
+ end)
+end
+
+statistics.register("callbacks overloaded by user", function()
+ if permit_overloads then
+ return concat(sortedkeys(permit_overloads)," ")
+ end
+end)
+
+-- this will move to a module
+
+commands = commands or { }
+
+function commands.showcallbacks()
+ local NC, NR, verbatim = context.NC, context.NR, context.type
+ context.starttabulate { "|l|l|p|" }
+ for name, _ in sortedhash(list) do
+ NC() verbatim(name) NC() verbatim(state(name)) NC() context(frozen[name] or "") NC() NR()
+ end
+ context.stoptabulate()
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-cod.lmt b/Master/texmf-dist/tex/context/base/mkxl/luat-cod.lmt
new file mode 100644
index 00000000000..49d90777df6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-cod.lmt
@@ -0,0 +1,308 @@
+if not modules then modules = { } end modules ['luat-cod'] = {
+ version = 1.001,
+ comment = "companion to luat-cod.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, loadfile, tonumber = type, loadfile, tonumber
+local match, gsub, find, format, gmatch = string.match, string.gsub, string.find, string.format, string.gmatch
+
+local texconfig, lua = texconfig, lua
+
+-- maybe pick up from commandline:
+--
+-- texconfig.interaction: 0=batchmode 1=nonstopmode 2=scrollmode 3=errornonstopmode 4=normal
+
+-- some basic housekeeping
+
+texconfig.kpse_init = false -- not needed in lmtx
+texconfig.shell_escape = 't' -- not needed in lmtx
+
+texconfig.max_in_open = 2000
+texconfig.nest_size = 10000
+texconfig.param_size = 100000
+texconfig.save_size = 500000
+texconfig.stack_size = 100000
+texconfig.buffer_size = 10000000
+texconfig.token_size = 10000000
+texconfig.node_size = 50000000
+
+texconfig.max_print_line = 100000
+texconfig.max_strings = 500000
+texconfig.max_pool = 10000000
+
+texconfig.hash_extra = 200000
+
+texconfig.expand_depth = 10000
+texconfig.function_size = 32768
+texconfig.properties_size = 10000
+texconfig.error_line = 250
+texconfig.half_error_line = 125
+
+texconfig.level_max = 500
+texconfig.level_chr = 46 -- period
+
+-- registering bytecode chunks
+
+local bytedata = lua.bytedata or { }
+local bytedone = lua.bytedone or { }
+
+---.bytecode = bytecode
+lua.bytedata = bytedata
+lua.bytedone = bytedone
+
+local setbytecode = lua.setbytecode
+----- getbytecode = lua.getbytecode
+
+lua.firstbytecode = 501
+lua.lastbytecode = lua.lastbytecode or (lua.firstbytecode - 1) -- as we load ourselves again ... maybe return earlier
+
+function lua.registeredcodes()
+ return lua.lastbytecode - lua.firstbytecode + 1
+end
+
+-- no file.* and utilities.parsers.* functions yet
+
+local strip = false if arg then for i=-1,#arg do if arg[i] == "--c:strip" then strip = true break end end end
+
+function lua.registercode(filename,options)
+ local barename = gsub(filename,"%.[%a%d]+$","")
+ local basename = match(barename,"^.+[/\\](.-)$") or barename
+ if not bytedone[basename] then
+ local opts = { }
+ if type(options) == "string" and options ~= "" then
+ for s in gmatch(options,"([a-z]+)") do
+ opts[s] = true
+ end
+ end
+ if barename == filename then
+ filename = filename .. (opts.autosuffix and ".lmt" or ".lua")
+ end
+ local code = environment.luafilechunk(filename,false,opts.optimize)
+ if code then
+ bytedone[basename] = true
+ if environment.initex and not opts.initexonly then
+ local n = lua.lastbytecode + 1
+ bytedata[n] = { name = barename, options = opts }
+ if strip or opts.strip then
+ setbytecode(n,code,true)
+ else
+ setbytecode(n,code)
+ end
+ lua.lastbytecode = n
+ end
+ elseif environment.initex then
+ texio.write_nl(format("\nerror loading file: %s (aborting)",filename))
+ os.exit()
+ end
+ end
+end
+
+local finalizers = { }
+
+function lua.registerfinalizer(f,comment)
+ comment = comment or "unknown"
+ if type(f) == "function" then
+ finalizers[#finalizers+1] = { action = f, comment = comment }
+ else
+ print(format("\nfatal error: invalid finalizer, action: %s\n",comment))
+ os.exit()
+ end
+end
+
+function lua.finalize(logger)
+ for i=1,#finalizers do
+ local finalizer = finalizers[i]
+ finalizer.action()
+ if logger then
+ logger("finalize action: %s",finalizer.comment)
+ end
+ end
+end
+
+-- A first start with environments. This will be overloaded later.
+
+environment = environment or { }
+local environment = environment
+
+-- no string.unquoted yet
+
+local sourcefile = gsub(arg and arg[1] or "","^\"(.*)\"$","%1")
+local sourcepath = find(sourcefile,"/",1,true) and gsub(sourcefile,"/[^/]+$","") or ""
+local targetpath = "."
+
+-- delayed (via metatable):
+--
+-- environment.jobname = tex.jobname
+-- environment.version = tostring(tex.toks.contextversiontoks)
+
+-- traditionally the revision has been a one character string and only
+-- pdftex went beyond "9" but anyway we test for it
+
+if LUATEXENGINE == nil then
+ LUATEXENGINE = status.luatex_engine and string.lower(status.luatex_engine) or "luametatex"
+end
+
+if LUATEXVERSION == nil then
+ LUATEXVERSION = status.luatex_version/100 + tonumber(status.luatex_revision)/10000
+end
+
+if CONTEXTLMTXMODE == nil then
+ CONTEXTLMTXMODE = 1
+end
+
+if LUATEXFUNCTIONALITY == nil then
+ LUATEXFUNCTIONALITY = status.development_id or 6346
+end
+
+if LUATEXFORMATID == nil then
+ LUATEXFORMATID = status.format_id or 0
+end
+
+if JITSUPPORTED == nil then
+ JITSUPPORTED = false
+end
+
+if INITEXMODE == nil then
+ INITEXMODE = status.run_state == 0 -- initializing updating production
+end
+
+environment.luatexengine = LUATEXENGINE
+environment.luatexversion = LUATEXVERSION
+environment.luatexfunctionality = LUATEXFUNCTIONALITY
+environment.jitsupported = JITSUPPORTED
+environment.initex = INITEXMODE
+environment.initexmode = INITEXMODE
+
+-- if INITEXMODE then
+-- -- we have about that amount so we preallocate then which gives less
+-- -- reallocations (we're talking tiny record so no real gain)
+-- lua.setbytecode(1024,nil)
+-- -- but we don't want to lie about the amount either so ...
+-- end
+
+if not environment.luafilechunk then
+
+ function environment.luafilechunk(filename)
+ local fullname = filename
+ if sourcepath ~= "" then
+ fullname = sourcepath .. "/" .. filename
+ end
+ if not lfs.isfile(fullname) then
+ fullname = gsub(fullname,"mkxl","mkiv")
+ end
+ local data = loadfile(fullname)
+ texio.write("terminal_and_logfile","<",data and "+ " or "- ",fullname,">")
+ if data then
+-- package.loaded[gsub(filename,"%..-$"] =
+ data()
+ end
+ return data
+ end
+
+end
+
+if not environment.engineflags then -- raw flags
+
+ local engineflags = { }
+
+ for i=-10,#arg do
+ local a = arg[i]
+ if a then
+ local flag, content = match(a,"^%-%-([^=]+)=?(.-)$")
+ if flag then
+ engineflags[flag] = content or ""
+ end
+ end
+ end
+
+ environment.engineflags = engineflags
+
+end
+
+-- We need a few premature callbacks in the format generator. We
+-- also do this when the format is loaded as otherwise we get
+-- a kpse error when disabled. This is an engine issue that will
+-- be sorted out in due time.
+
+-- print("!!!!!!!!!!!!!!!!!!!!!!",lfs.isfile) os.exit()
+
+if not lfs.isfile then
+
+ local attributes = lfs.attributes
+
+ function lfs.isdir(name)
+ return attributes(name,"mode") == "directory"
+ end
+
+ function lfs.isfile(name)
+ local a = attributes(name,"mode")
+ return a == "file" or a == "link" or nil
+ end
+
+end
+
+local isfile = lfs.isfile
+
+local function source_file(name)
+ local fullname = sourcepath .. "/" .. name
+ if isfile(fullname) then
+ return fullname
+ end
+ fullname = fullname .. ".tex"
+ if isfile(fullname) then
+ return fullname
+ end
+ if isfile(name) then
+ return name
+ end
+ name = name .. ".tex"
+ if isfile(name) then
+ return name
+ end
+ return nil
+end
+
+local function target_file(name)
+ return targetpath .. "/" .. name
+end
+
+local function find_log_file(name)
+ return target_file(name)
+end
+
+local function find_data_file(name)
+ return source_file(name)
+end
+
+local function open_data_file(name)
+ local fullname = source_file(name)
+ if fullname then
+ local f = io.open(fullname,'rb')
+ return {
+ reader = function()
+ return f:read("*line")
+ end
+ }
+ else
+ return false
+ end
+end
+
+callback.register('find_log_file', find_log_file)
+callback.register('find_data_file' , find_data_file )
+callback.register('open_data_file' , open_data_file )
+
+callback.register("trace_memory", function(what,success)
+ success = success and "succeeded" or "failed"
+ if logs then
+ logs.report("tex memory","bumping category %a %s, details: %s",
+ what,success,table.sequenced(status["get"..what.."state"]()))
+ elseif texio then
+ texio.write_nl(format("bumping tex '%s' memory %s",
+ what,success))
+ end
+ -- os.exit()
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-cod.mkxl b/Master/texmf-dist/tex/context/base/mkxl/luat-cod.mkxl
new file mode 100644
index 00000000000..ed4a1398119
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-cod.mkxl
@@ -0,0 +1,48 @@
+%D \module
+%D [ file=luat-cod,
+%D version=2005.05.26,
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Code,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% \writestatus{loading}{ConTeXt Lua Macros / Code}
+
+\unprotect
+
+\newif\ifproductionrun
+
+\permanent\let\ctxdirectlua \directlua
+\permanent\def\ctxsprint #1{\directlua{tex.sprint(tex.ctxcatcodes,#1)}} % saves tokens
+\permanent\def\ctxwrite #1{\directlua{tex.write(#1)}} % saves tokens
+\permanent\def\ctxcommand #1{\directlua{commands.#1}} % saves tokens
+\permanent\def\ctxdirectcommand#1{\directlua{commands.#1}} % saves tokens
+\permanent\def\ctxreport #1{\directlua{logs.writer[[#1]]}}
+
+%D Take your choice \unknown
+
+\aliased\let\ctxlua \ctxdirectlua
+\aliased\let\luacode \ctxdirectlua
+\aliased\let\directluacode\ctxdirectlua
+
+%D Reporting the version of \LUA\ that we use is done as follows:
+
+\permanent\def\luaversion{\ctxwrite{LUAVERSION}}
+
+\permanent\def\registerctxluafile#1#2{\ctxlua{lua.registercode("#1","#2")}}
+\permanent\def\ctxloadluafile #1{\ctxlua{lua.registercode("#1")}}
+
+\registerctxluafile{luat-cod.lmt}{autosuffix}
+
+\toksapp \everydump {%
+ \permanent\let\ctxlatelua \latelua
+ \permanent\def\ctxlatecommand#1{\latelua{commands.#1}}%
+ \aliased\let\lateluacode \ctxlatelua
+} % no \appendtoks yet
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-fio.lmt b/Master/texmf-dist/tex/context/base/mkxl/luat-fio.lmt
new file mode 100644
index 00000000000..c70ed54e4aa
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-fio.lmt
@@ -0,0 +1,107 @@
+if not modules then modules = { } end modules ['luat-fio'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format = string.format
+local concat = table.concat
+
+texconfig.kpse_init = false -- can go away
+texconfig.shell_escape = 't' -- can go away
+texconfig.max_print_line = 100000 -- can go away
+texconfig.max_in_open = 1000 -- can go away
+
+if not resolvers.initialized() then
+
+ resolvers.reset()
+
+ -- we now load the file database as we might need files other than
+ -- tex and lua file on the given path
+
+ resolvers.load()
+
+ if callback then
+
+ local findbinfile = resolvers.findbinfile
+ local findtexfile = resolvers.findtexfile
+ local opentexfile = resolvers.opentexfile
+ local register = callbacks.register
+
+ local ioflush = io.flush
+ local ioread = io.read
+ local writenl = texio.write_nl
+
+ local function terminal()
+ writenl("\ntex console > ")
+ ioflush()
+ local line = ioread()
+ writenl("")
+ ioflush()
+ return line
+ end
+
+ -- local function find_data_file(name)
+ -- if not name or name == "" then
+ -- return "context terminal"
+ -- else
+ -- return findbinfile(name,"tex")
+ -- end
+ -- end
+ --
+ -- local function open_data_file(name)
+ -- if not name or name == "" or name == "context terminal" then
+ -- return {
+ -- reader = terminal,
+ -- noflines = 1,
+ -- filename = name,
+ -- }
+ -- else
+ -- name = opentexfile(name)
+ -- return name ~= "" and name or false
+ -- end
+ -- end
+
+ local function open_data_file(name)
+ if not name or name == "" then
+ return {
+ reader = terminal,
+ noflines = 1,
+ filename = "context terminal"
+ }
+ else
+ local fullname = findbinfile(name,"tex")
+ if fullname then
+ return opentexfile(fullname)
+ else
+ return false
+ end
+ end
+ end
+
+ local function find_any_file(name)
+ return name
+ end
+
+ register('find_log_file' , find_any_file, true)
+ register('find_format_file', find_any_file, true)
+ -- register('find_data_file' , find_data_file, true)
+ register('open_data_file' , open_data_file, true)
+
+ end
+
+end
+
+statistics.register("resource resolver", function()
+ local scandata = resolvers.scandata()
+ return format("loadtime %s seconds, %s scans with scantime %s seconds, %s shared scans, %s found files, scanned paths: %s",
+ resolvers.loadtime(),
+ scandata.n,
+ scandata.time,
+ scandata.shared,
+ #resolvers.foundintrees(),
+ #scandata.paths > 0 and concat(scandata.paths," ") or "<none>"
+ )
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/luat-ini.lmt
new file mode 100644
index 00000000000..3bfd191b981
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-ini.lmt
@@ -0,0 +1,46 @@
+if not modules then modules = { } end modules ['luat-ini'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>We cannot load anything yet. However what we will do us reserve a few tables.
+These can be used for runtime user data or third party modules and will not be
+cluttered by macro package code.</p>
+--ldx]]--
+
+userdata = userdata or { } -- for users (e.g. functions etc)
+thirddata = thirddata or { } -- only for third party modules
+moduledata = moduledata or { } -- only for development team
+documentdata = documentdata or { } -- for users (e.g. raw data)
+parametersets = parametersets or { } -- for special purposes
+
+table.setmetatableindex(moduledata,"table")
+table.setmetatableindex(thirddata, "table")
+
+if not global then
+ global = _G
+end
+
+LUATEXVERSION = status.luatex_version/100
+ + tonumber(status.luatex_revision)/10000
+
+LUATEXENGINE = status.luatex_engine and string.lower(status.luatex_engine)
+ or (string.find(status.banner,"LuajitTeX",1,true) and "luajittex" or "luatex")
+
+LUATEXFUNCTIONALITY = status.development_id or 6346
+
+LUATEXFORMATID = status.format_id or 0
+
+JITSUPPORTED = LUATEXENGINE == "luajittex" or jit
+
+INITEXMODE = status.ini_version
+
+CONTEXTLMTXMODE = 1
+
+function os.setlocale()
+ -- no need for a message
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/luat-ini.mkxl
new file mode 100644
index 00000000000..9a78c3934c4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-ini.mkxl
@@ -0,0 +1,299 @@
+%D \module
+%D [ file=luat-ini,
+%D version=2005.08.11,
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Macros / Initialization}
+
+\unprotect
+
+%D Loading lua code can be done using \type {startup.lua}. The following method uses
+%D the \TEX\ input file locator of kpse. At least we need to use that way of loading
+%D when we haven't yet define our own code, which we keep outside the format. We
+%D will keep code outside \TEX\ files as much as possible. The solutions evolved
+%D with the engine but one can find some history in the \MKIV\ files and articles.
+
+%ifdefined\setnaturalcatcodes \else \let\setnaturalcatcodes\relax \fi
+\ifdefined\obeylualines \else \let\obeylualines \relax \fi
+\ifdefined\obeyluatokens \else \let\obeyluatokens \relax \fi
+
+%D A few more goodies:
+
+\aliased\let\stoplua \relax % tex catcodes
+\aliased\let\stopluacode\relax % lua catcodes
+
+%D In the meantime the \LUATEX\ interface has become stable but we still
+%D see traces of old here (needs checking).
+
+\permanent\protected\def\startlua % \stoplua
+ {\begingroup
+ \obeylualines
+ \luat_start_lua_indeed}
+
+\def\luat_start_lua_indeed#1\stoplua
+ {\normalexpanded{\endgroup\noexpand\directlua{#1}}}
+
+\permanent\protected\def\startluacode % \stopluacode
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \luat_start_lua_code_indeed}
+
+\def\luat_start_lua_code_indeed#1\stopluacode
+ %{\normalexpanded{\endgroup\noexpand\directlua{#1}}}
+ {\directlua{#1\beginlocalcontrol\endgroup\endlocalcontrol}}
+
+%D Some delayed definitions:
+
+\ifdefined\obeylines \else \let\obeylines \relax \fi
+\ifdefined\obeyedline \else \let\obeyedline \relax \fi
+\ifdefined\obeyspaces \else \let\obeyspaces \relax \fi
+\ifdefined\obeyedspace \else \let\obeyedspace \relax \fi
+
+\aliased\let\obeylualines\relax % not used, will go away
+
+\newtoks\everyluacode
+
+%D It is nicer for checking with \type {s-system-macros} if we have some meaning:
+
+\pushoverloadmode
+ \let\-\explicitdiscretionary
+\popoverloadmode
+
+\ifdefined\n \else \mutable\def\n{n} \fi \ifdefined\r \else \mutable\def\r{r} \fi
+\ifdefined\f \else \mutable\def\f{f} \fi \ifdefined\t \else \mutable\def\t{t} \fi
+\ifdefined\a \else \mutable\def\a{a} \fi \ifdefined\b \else \mutable\def\b{b} \fi
+\ifdefined\v \else \mutable\def\v{v} \fi \ifdefined\s \else \mutable\def\s{s} \fi
+
+\ifdefined\+ \else \mutable\def\+{+} \fi \ifdefined\- \else \mutable\def\-{-} \fi
+
+\ifdefined\? \else \mutable\def\?{?} \fi \ifdefined\! \else \mutable\def\!{!} \fi % bonus
+
+\ifdefined\1 \else \mutable\def\1{1} \fi \ifdefined\2 \else \mutable\def\2{2} \fi
+\ifdefined\3 \else \mutable\def\3{3} \fi \ifdefined\4 \else \mutable\def\4{4} \fi
+\ifdefined\5 \else \mutable\def\5{5} \fi \ifdefined\6 \else \mutable\def\6{6} \fi
+\ifdefined\7 \else \mutable\def\7{7} \fi \ifdefined\8 \else \mutable\def\8{8} \fi
+\ifdefined\9 \else \mutable\def\9{9} \fi \ifdefined\0 \else \mutable\def\0{0} \fi
+
+\immutable\edef\lua_letter_backslash{\string\\}
+\immutable\edef\lua_letter_bar {\string\|} \immutable\edef\lua_letter_dash {\string\-}
+\immutable\edef\lua_letter_lparent {\string\(} \immutable\edef\lua_letter_rparent {\string\)}
+\immutable\edef\lua_letter_lbrace {\string\{} \immutable\edef\lua_letter_rbrace {\string\}}
+\immutable\edef\lua_letter_squote {\string\'} \immutable\edef\lua_letter_dquote {\string\"}
+\immutable\edef\lua_letter_n {\string\n} \immutable\edef\lua_letter_r {\string\r}
+\immutable\edef\lua_letter_f {\string\f} \immutable\edef\lua_letter_t {\string\t}
+\immutable\edef\lua_letter_a {\string\a} \immutable\edef\lua_letter_b {\string\b}
+\immutable\edef\lua_letter_v {\string\v} \immutable\edef\lua_letter_s {\string\s}
+\immutable\edef\lua_letter_one {\string\1} \immutable\edef\lua_letter_two {\string\2}
+\immutable\edef\lua_letter_three {\string\3} \immutable\edef\lua_letter_four {\string\4}
+\immutable\edef\lua_letter_five {\string\5} \immutable\edef\lua_letter_six {\string\6}
+\immutable\edef\lua_letter_seven {\string\7} \immutable\edef\lua_letter_eight {\string\8}
+\immutable\edef\lua_letter_nine {\string\9} \immutable\edef\lua_letter_zero {\string\0}
+
+\everyluacode {% \appendtoks
+ \enforced\let\\\lua_letter_backslash
+ \enforced\let\|\lua_letter_bar \enforced\let\-\lua_letter_dash
+ \enforced\let\(\lua_letter_lparent \enforced\let\)\lua_letter_rparent
+ \enforced\let\{\lua_letter_lbrace \enforced\let\}\lua_letter_rbrace
+ \enforced\let\'\lua_letter_squote \enforced\let\"\lua_letter_dquote
+ \enforced\let\n\lua_letter_n \enforced\let\r\lua_letter_r
+ \enforced\let\f\lua_letter_f \enforced\let\t\lua_letter_t
+ \enforced\let\a\lua_letter_a \enforced\let\b\lua_letter_b
+ \enforced\let\v\lua_letter_v \enforced\let\s\lua_letter_s
+ \enforced\let\1\lua_letter_one \enforced\let\2\lua_letter_two
+ \enforced\let\3\lua_letter_three \enforced\let\4\lua_letter_four
+ \enforced\let\5\lua_letter_five \enforced\let\6\lua_letter_six
+ \enforced\let\7\lua_letter_seven \enforced\let\8\lua_letter_eight
+ \enforced\let\9\lua_letter_nine \enforced\let\0\lua_letter_zero
+} % \to \everyluacode
+
+\permanent\protected\def\obeyluatokens
+ {\setcatcodetable\luacatcodes
+ \the\everyluacode}
+
+\immutable\edef\luamajorversion{\ctxwrite{LUAMINORVERSION}}
+\immutable\edef\luaminorversion{\ctxwrite{LUAMAJORVERSION}}
+
+%D We need a way to pass strings safely to \LUA\ without the need for tricky
+%D escaping. Compare:
+%D
+%D \starttyping
+%D \ctxlua {something("anything tricky can go here")}
+%D \ctxlua {something([\luastringsep[anything tricky can go here]\luastringsep])}
+%D \stoptyping
+
+\def\luastringsep{===} % this permits \typefile{self} otherwise nested b/e sep problems
+
+\edef\!!bs{[\luastringsep[} % later redefined as mutable
+\edef\!!es{]\luastringsep]}
+
+%D We have a the following available as primitive so there is no need for it:
+%D
+%D \starttyping
+%D \edef\luaescapestring#1{\!!bs#1\!!es}
+%D \stoptyping
+
+\permanent\protected\def\setdocumentfilename #1#2{\clf_setdocumentfilename\numexpr#1\relax{#2}}
+\permanent \def\getdocumentfilename #1{\clf_getdocumentfilename\numexpr#1\relax}
+\permanent\protected\def\setdocumentargument #1#2{\clf_setdocumentargument{#1}{#2}}
+\permanent \def\getdocumentargument #1{\clf_getdocumentargument{#1}{}}
+\permanent\protected\def\setdocumentargumentdefault#1#2{\clf_setdocumentdefaultargument{#1}{#2}}
+\permanent \def\getdocumentargumentdefault#1#2{\clf_getdocumentargument{#1}{#2}}
+
+% seldom used so no need for speedy variants:
+
+\permanent\def\doifelsedocumentargument #1{\doifelsesomething{\clf_getdocumentargument{#1}}}
+\permanent\def\doifdocumentargument #1{\doifsomething {\clf_getdocumentargument{#1}}}
+\permanent\def\doifnotdocumentargument #1{\doifnothing {\clf_getdocumentargument{#1}}}
+\permanent\def\doifelsedocumentfilename #1{\doifelsesomething{\clf_getdocumentfilename\numexpr#1\relax}}
+\permanent\def\doifdocumentfilename #1{\doifsomething {\clf_getdocumentfilename\numexpr#1\relax}}
+\permanent\def\doifnotdocumentfilename #1{\doifnothing {\clf_getdocumentfilename\numexpr#1\relax}}
+
+\aliased\let\doifdocumentargumentelse\doifelsedocumentargument
+\aliased\let\doifdocumentfilenameelse\doifelsedocumentfilename
+
+%D A handy helper:
+
+\permanent\def\luaexpanded#1{\luaescapestring\expandafter{\normalexpanded{#1}}}
+
+%D Experimental:
+
+\permanent\protected\def\startluaparameterset[#1]% \stopluaparameterset
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \luat_start_lua_parameter_set{#1}}
+
+\def\luat_start_lua_parameter_set#1#2\stopluaparameterset
+ {\ctxlua{parametersets["#1"]={#2}}%
+ \endgroup}
+
+\aliased\let\stopluaparameterset\relax
+
+\permanent\def\luaparameterset#1#2{\ctxlua{parametersets["#1"]={#2} context("#1")}}
+
+% todo: \mergeparameterset
+
+% usage:
+%
+% \startluaparameterset [u3d:myset:display:1]
+% toolbar=false,
+% tree=true
+% \stopluaparameterset
+%
+% option=u3d:myset:display:1
+%
+% or:
+%
+% option=\luaparameterset{u3d:myset:display:1}{toolbar=false,tree=true}
+
+%D A Handy helper:
+
+\permanent\def\luaconditional#1{\ifcase#1tru\else fals\fi e}
+
+%D Goodie:
+%D
+%D \starttyping
+%D \ctxluacode{context("%0.5f",1/3)}
+%D \stoptyping
+
+\permanent\protected\def\ctxluacode
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \catcode\leftbraceasciicode \plusone
+ \catcode\rightbraceasciicode\plustwo
+ \afterassignment\luat_lua_code
+ \scratchtoks=}
+
+% Hm, are we sure that the \* commands work out okay here? We could probably
+% use \setcatcodetable\luacatcodes instead of \obeyluatokens now.
+
+\def\luat_lua_code
+ {\normalexpanded{\endgroup\noexpand\directlua\expandafter{\the\scratchtoks}}} % \zerocount is default
+
+% \startctxfunction MyFunctionA
+% context(" A1 ")
+% \stopctxfunction
+%
+% \startctxfunctiondefinition MyFunctionB
+% context(" B2 ")
+% \stopctxfunctiondefinition
+%
+% \starttext
+% \dorecurse{10000}{\ctxfunction{MyFunctionA}} \page
+% \dorecurse{10000}{\MyFunctionB} \page
+% \dorecurse{10000}{\ctxlua{context(" C3 ")}} \page
+% \stoptext
+
+\installsystemnamespace{ctxfunction}
+
+\permanent\protected\def\startctxfunctiondefinition #1 % \stopctxfunctiondefinition
+ {\begingroup \obeylualines \obeyluatokens \luat_start_lua_function_definition_indeed{#1}}
+
+\installsystemnamespace{luafunction}
+
+\def\luat_start_lua_function_definition_indeed#1#2\stopctxfunctiondefinition
+ {\endgroup
+ \expandafter\chardef\csname\??luafunction#1\endcsname\ctxcommand{ctxfunction(\!!bs#2\!!es)}\relax
+ \edefcsname#1\endcsname{\noexpand\luafunction\csname\??luafunction#1\endcsname}}
+
+\permanent\protected\def\setctxluafunction#1#2% experiment
+ {\expandafter\chardef\csname\??luafunction#1\endcsname#2\relax
+ \edefcsname#1\endcsname{\noexpand\luafunction\csname\??luafunction#1\endcsname}}
+
+\aliased\let\stopctxfunctiondefinition\relax
+
+\permanent\protected\def\startctxfunction #1 % \stopctxfunction
+ {\begingroup \obeylualines \obeyluatokens \luat_start_lua_function_indeed{#1}}
+
+\def\luat_start_lua_function_indeed#1#2\stopctxfunction
+ {\endgroup\edefcsname\??ctxfunction#1\endcsname{\noexpand\luafunction\ctxcommand{ctxfunction(\!!bs#2\!!es)}\relax}}
+
+\aliased\let\stopctxfunction\relax
+
+\permanent\def\ctxfunction#1%
+ {\begincsname\??ctxfunction#1\endcsname}
+
+%D In theory this is faster due to the call not being wrapped in a function but in
+%D practice the speedup can't be noticed. The actions called for often have lots of
+%D lookups so an extra one doesn't matter much. The kind of calls differs a lot per
+%D document and often there are other ways to optimize a style. For instance we can
+%D gain a lot when defining a font, but when a frozen definition is used that gain
+%D gets completely lost. For some calls (take list writers) it can get worse if only
+%D because readability gets worse and passing is already efficient due to selective
+%D flushing, while with the token scanners one has to scan all of them.
+
+% \startctxfunctiondefinition foo commands.foo() \stopctxfunctiondefinition
+%
+% \installctxfunction\foo{commands.foo}
+
+\permanent\protected\def\installctxfunction#1#2% expandable
+ {\edef\m_syst_name{\csstring#1}%
+ \global\expandafter\normalluadef\csname\m_syst_name\endcsname\ctxcommand{ctxfunction(\!!bs#2\!!es,true)}\relax}
+
+\permanent\protected\def\installctxscanner#1#2% expandable
+ {\edef\m_syst_name{\csstring#1}%
+ \global\expandafter\normalluadef\csname\m_syst_name\endcsname\ctxcommand{ctxscanner("\m_syst_name",\!!bs#2\!!es,true)}\relax}
+
+\permanent\protected\def\installprotectedctxfunction#1#2% protected
+ {\edef\m_syst_name{\csstring#1}%
+ \global\protected\expandafter\normalluadef\csname\m_syst_name\endcsname\ctxcommand{ctxfunction(\!!bs#2\!!es,true)}\relax}
+
+\permanent\protected\def\installprotectedctxscanner#1#2% protected
+ {\edef\m_syst_name{\csstring#1}%
+ \global\protected\expandafter\normalluadef\csname\m_syst_name\endcsname\ctxcommand{ctxscanner("\m_syst_name",\!!bs#2\!!es,true)}\relax}
+
+\permanent\protected\def\resetctxscanner#1%
+ {\edef\m_syst_name{\csstring#1}%
+ \gletcsname\m_syst_name\endcsname\relax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-lib.mkxl b/Master/texmf-dist/tex/context/base/mkxl/luat-lib.mkxl
new file mode 100644
index 00000000000..61f9ccfd0fd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-lib.mkxl
@@ -0,0 +1,104 @@
+%D \module
+%D [ file=luat-lib,
+%D version=2006.09.11,
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Libraries,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Macros / Libraries}
+
+\registerctxluafile{util-str}{}
+\registerctxluafile{util-tab}{}
+\registerctxluafile{util-fil}{optimize}
+\registerctxluafile{util-sac}{optimize}
+\registerctxluafile{util-sto}{} % could also be done in trac-deb.mkiv
+\registerctxluafile{util-pck}{}
+\registerctxluafile{util-prs}{}
+\registerctxluafile{util-fmt}{}
+\registerctxluafile{util-dim}{}
+
+\registerctxluafile{trac-set}{}
+\registerctxluafile{luat-log}{autosuffix}
+\registerctxluafile{trac-inf}{autosuffix}
+\registerctxluafile{util-lua}{}
+\registerctxluafile{util-deb}{} % could also be done in trac-deb.mkiv
+
+\registerctxluafile{util-tpl}{} % needs tracker
+\registerctxluafile{util-seq}{}
+
+\registerctxluafile{util-sta}{}
+
+\registerctxluafile{util-sbx}{} % needs tracker and templates
+
+\registerctxluafile{util-soc-imp-reset} {}
+\registerctxluafile{util-soc-imp-socket} {}
+\registerctxluafile{util-soc-imp-ltn12} {}
+\registerctxluafile{util-soc-imp-mime} {}
+\registerctxluafile{util-soc-imp-url} {}
+\registerctxluafile{util-soc-imp-headers}{}
+\registerctxluafile{util-soc-imp-http} {}
+\registerctxluafile{util-soc-imp-tp} {}
+
+\registerctxluafile{util-zip}{}
+
+\registerctxluafile{data-ini}{}
+\registerctxluafile{data-exp}{}
+\registerctxluafile{data-env}{}
+\registerctxluafile{data-tmp}{}
+\registerctxluafile{data-met}{}
+\registerctxluafile{data-res}{}
+\registerctxluafile{data-inp}{}
+\registerctxluafile{data-out}{}
+\registerctxluafile{data-fil}{} % opener gets overloaded in data-tex
+
+\registerctxluafile{data-pre}{} % after data-res
+\registerctxluafile{data-bin}{} % before data-tex
+\registerctxluafile{data-tex}{} % after data-pre
+\registerctxluafile{data-vir}{}
+\registerctxluafile{data-zip}{}
+%registerctxluafile{data-crl}{}
+\registerctxluafile{data-sch}{}
+\registerctxluafile{data-tre}{}
+\registerctxluafile{data-lua}{}
+\registerctxluafile{data-ctx}{}
+\registerctxluafile{data-con}{}
+\registerctxluafile{data-use}{}
+\registerctxluafile{data-aux}{}
+
+\registerctxluafile{luat-cbk}{autosuffix}
+\registerctxluafile{luat-run}{}
+\registerctxluafile{luat-fio}{autosuffix}
+\registerctxluafile{luat-cnf}{}
+\registerctxluafile{luat-lua}{}
+\registerctxluafile{luat-sto}{}
+\registerctxluafile{luat-ini}{autosuffix}
+\registerctxluafile{util-env}{}
+\registerctxluafile{luat-env}{}
+\registerctxluafile{luat-exe}{} % simplified
+\registerctxluafile{luat-iop}{} % simplified
+%registerctxluafile{luat-bwc}{}
+\registerctxluafile{trac-lmx}{} % might become l-lmx or luat-lmx
+\registerctxluafile{luat-mac}{}
+%registerctxluafile{luat-prp}{} % for the moment of not much use
+
+\registerctxluafile{lxml-tab}{}
+\registerctxluafile{lxml-lpt}{}
+\registerctxluafile{lxml-xml}{}
+\registerctxluafile{lxml-aux}{}
+\registerctxluafile{lxml-mis}{}
+
+% \pushoverloadmode
+%
+% \ifdefined\writestatus \else \protected\def\writestatus#1#2{\ctxlua{logs.status([==[#1]==],[==[#2]==])}} \fi
+% \ifdefined\writestring \else \protected\def\writestring #1{\ctxlua{logs.writer([==[#1]==],"\string\n")}} \fi
+% \ifdefined\writeline \else \protected\def\writeline {\ctxlua{logs.newline()}} \fi
+%
+% \popoverloadmode
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-log.lmt b/Master/texmf-dist/tex/context/base/mkxl/luat-log.lmt
new file mode 100644
index 00000000000..522f4ac4ff6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-log.lmt
@@ -0,0 +1,739 @@
+if not modules then modules = { } end modules ['luat-log'] = {
+ version = 1.001,
+ comment = "companion to trac-log.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- In fact all writes could go through lua and we could write the console and
+-- terminal handler in lua then. Ok, maybe it's slower then, so a no-go.
+
+-- This used to be combined in trac-log but as we also split between mkiv and lmtx
+-- we now have dedicated files. A side effect is a smaller format and a smaller
+-- mtxrun.
+--
+-- We use different targets: "terminal" "logfile", "both" and have no number
+-- channel.
+
+local next, type, select, print = next, type, select, print
+local format, gmatch, find = string.format, string.gmatch, string.find
+local concat, insert, remove = table.concat, table.insert, table.remove
+local topattern = string.topattern
+local utfchar = utf.char
+local datetime = os.date
+local openfile = io.open
+
+local write_nl = texio.write_nl
+local write = texio.write
+
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+local settings_to_hash = utilities.parsers.settings_to_hash
+local sortedkeys = table.sortedkeys
+
+-- variant is set now
+
+local variant = "default"
+----- variant = "ansi"
+
+logs = logs or { }
+local logs = logs
+
+-- we extend the formatters:
+
+formatters.add (
+ formatters, "unichr",
+ [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]]
+)
+
+formatters.add (
+ formatters, "chruni",
+ [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]]
+)
+
+-- basic loggers
+
+local function ignore() end
+
+setmetatableindex(logs, function(t,k) t[k] = ignore ; return ignore end)
+
+local report, subreport, status, settarget, setformats, settranslations
+
+local direct, subdirect, writer, pushtarget, poptarget, setlogfile, settimedlog, setprocessor, setformatters, newline
+
+-- we use formatters but best check for % then because for simple messages but
+-- we don't want this overhead for single messages (not that there are that
+-- many; we could have a special weak table)
+
+local function ansisupported(specification)
+ if specification ~= "ansi" and specification ~= "ansilog" then
+ return false
+ elseif os and os.enableansi then
+ return os.enableansi()
+ else
+ return false
+ end
+end
+
+do
+
+ if arg and ansisupported then
+ -- we're don't have environment.arguments yet
+ for k, v in next, arg do -- k can be negative !
+ if v == "--ansi" or v == "--c:ansi" then
+ if ansisupported("ansi") then
+ variant = "ansi"
+ end
+ break
+ elseif v == "--ansilog" or v == "--c:ansilog" then
+ if ansisupported("ansilog") then
+ variant = "ansilog"
+ end
+ break
+ end
+ end
+ end
+
+ local whereto = "both"
+ local target = nil
+ local targets = nil
+
+ local formats = table.setmetatableindex("self")
+ local translations = table.setmetatableindex("self")
+
+ local report_yes, subreport_yes, direct_yes, subdirect_yes, status_yes
+ local report_nop, subreport_nop, direct_nop, subdirect_nop, status_nop
+
+ local variants = {
+ default = {
+ formats = {
+ report_yes = formatters["%-15s > %s\n"],
+ report_nop = formatters["%-15s >\n"],
+ direct_yes = formatters["%-15s > %s"],
+ direct_nop = formatters["%-15s >"],
+ subreport_yes = formatters["%-15s > %s > %s\n"],
+ subreport_nop = formatters["%-15s > %s >\n"],
+ subdirect_yes = formatters["%-15s > %s > %s"],
+ subdirect_nop = formatters["%-15s > %s >"],
+ status_yes = formatters["%-15s : %s\n"],
+ status_nop = formatters["%-15s :\n"],
+ },
+ targets = setmetatableindex( {
+ logfile = "logfile",
+ log = "logfile",
+ file = "logfile",
+ console = "terminal",
+ terminal = "terminal",
+ both = "terminal_and_logfile",
+ }, function(t,k) t[k] = "terminal_and_logfile" end),
+ },
+ ansi = {
+ formats = {
+ report_yes = formatters["%-15s > %s\n"],
+ report_nop = formatters["%-15s >\n"],
+ direct_yes = formatters["%-15s > %s"],
+ direct_nop = formatters["%-15s >"],
+ subreport_yes = formatters["%-15s > %s > %s\n"],
+ subreport_nop = formatters["%-15s > %s >\n"],
+ subdirect_yes = formatters["%-15s > %s > %s"],
+ subdirect_nop = formatters["%-15s > %s >"],
+ status_yes = formatters["%-15s : %s\n"],
+ status_nop = formatters["%-15s :\n"],
+ },
+ targets = setmetatableindex( {
+ logfile = false,
+ log = false,
+ file = false,
+ console = "terminal",
+ terminal = "terminal",
+ both = "terminal",
+ }, function(t,k) t[k] = "terminal" end),
+ }
+ }
+
+ variants.ansilog = {
+ formats = variants.ansi.formats,
+ targets = variants.default.targets,
+ }
+
+ logs.flush = io.flush
+
+ writer = function(...)
+ if target then
+ write_nl(target,...)
+ end
+ end
+
+ newline = function()
+ if target then
+ write_nl(target,"\n")
+ end
+ end
+
+ report = function(a,b,c,...)
+ if not target then
+ -- ignore
+ elseif c ~= nil then
+ write_nl(target,report_yes(translations[a],formatters[formats[b]](c,...)))
+ elseif b then
+ write_nl(target,report_yes(translations[a],formats[b]))
+ elseif a then
+ write_nl(target,report_nop(translations[a]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ direct = function(a,b,c,...)
+ if not target then
+ return ""
+ elseif c ~= nil then
+ return direct_yes(translations[a],formatters[formats[b]](c,...))
+ elseif b then
+ return direct_yes(translations[a],formats[b])
+ elseif a then
+ return direct_nop(translations[a])
+ else
+ return ""
+ end
+ end
+
+ subreport = function(a,s,b,c,...)
+ if not target then
+ -- ignore
+ elseif c ~= nil then
+ write_nl(target,subreport_yes(translations[a],translations[s],formatters[formats[b]](c,...)))
+ elseif b then
+ write_nl(target,subreport_yes(translations[a],translations[s],formats[b]))
+ elseif a then
+ write_nl(target,subreport_nop(translations[a],translations[s]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ subdirect = function(a,s,b,c,...)
+ if not target then
+ return ""
+ elseif c ~= nil then
+ return subdirect_yes(translations[a],translations[s],formatters[formats[b]](c,...))
+ elseif b then
+ return subdirect_yes(translations[a],translations[s],formats[b])
+ elseif a then
+ return subdirect_nop(translations[a],translations[s])
+ else
+ return ""
+ end
+ end
+
+ status = function(a,b,c,...)
+ if not target then
+ -- ignore
+ elseif c ~= nil then
+ write_nl(target,status_yes(translations[a],formatters[formats[b]](c,...)))
+ elseif b then
+ write_nl(target,status_yes(translations[a],formats[b]))
+ elseif a then
+ write_nl(target,status_nop(translations[a]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ settarget = function(askedwhereto)
+ whereto = askedwhereto or whereto or "both"
+ target = targets[whereto]
+ if not target then
+ whereto = "both"
+ target = targets[whereto]
+ end
+ if target == "terminal" or target == "terminal_and_logfile" then
+ logs.flush = io.flush
+ else
+ logs.flush = ignore
+ end
+ end
+
+ local stack = { }
+
+ pushtarget = function(newtarget)
+ insert(stack,target)
+ settarget(newtarget)
+ end
+
+ poptarget = function()
+ if #stack > 0 then
+ settarget(remove(stack))
+ end
+ end
+
+ setformats = function(f)
+ formats = f
+ end
+
+ settranslations = function(t)
+ translations = t
+ end
+
+ setprocessor = function(f)
+ local writeline = write_nl
+ write_nl = function(target,...)
+ if target then
+ writeline(target,f(...))
+ end
+ end
+ end
+
+ setformatters = function(specification)
+ local t = nil
+ local f = nil
+ local d = variants.default
+ if not specification then
+ --
+ elseif type(specification) == "table" then
+ t = specification.targets
+ f = specification.formats or specification
+ else
+ if not ansisupported(specification) then
+ specification = "default"
+ end
+ local v = variants[specification]
+ if v then
+ t = v.targets
+ f = v.formats
+ variant = specification
+ end
+ end
+ targets = t or d.targets
+ target = targets[whereto]
+ if f then
+ d = d.formats
+ else
+ f = d.formats
+ d = f
+ end
+ setmetatableindex(f,d)
+ report_yes = f.report_yes
+ report_nop = f.report_nop
+ subreport_yes = f.subreport_yes
+ subreport_nop = f.subreport_nop
+ direct_yes = f.direct_yes
+ direct_nop = f.direct_nop
+ subdirect_yes = f.subdirect_yes
+ subdirect_nop = f.subdirect_nop
+ status_yes = f.status_yes
+ status_nop = f.status_nop
+ settarget(whereto)
+ end
+
+ setformatters(variant)
+
+ setlogfile = ignore
+ settimedlog = ignore
+
+end
+
+logs.report = report
+logs.subreport = subreport
+logs.status = status
+logs.settarget = settarget
+logs.pushtarget = pushtarget
+logs.poptarget = poptarget
+logs.setformats = setformats
+logs.settranslations = settranslations
+
+logs.setlogfile = setlogfile
+logs.settimedlog = settimedlog
+logs.setprocessor = setprocessor
+logs.setformatters = setformatters
+
+logs.direct = direct
+logs.subdirect = subdirect
+logs.writer = writer
+logs.newline = newline
+
+local data = { }
+local states = nil
+local force = false
+
+function logs.reporter(category,subcategory)
+ local logger = data[category]
+ if not logger then
+ local state = states == true
+ if not state and type(states) == "table" then
+ for c, _ in next, states do
+ if find(category,c) then
+ state = true
+ break
+ end
+ end
+ end
+ logger = {
+ reporters = { },
+ state = state,
+ }
+ data[category] = logger
+ end
+ local reporter = logger.reporters[subcategory or "default"]
+ if not reporter then
+ if subcategory then
+ reporter = function(...)
+ if force or not logger.state then
+ subreport(category,subcategory,...)
+ end
+ end
+ logger.reporters[subcategory] = reporter
+ else
+ local tag = category
+ reporter = function(...)
+ if force or not logger.state then
+ report(category,...)
+ end
+ end
+ logger.reporters.default = reporter
+ end
+ end
+ return reporter
+end
+
+logs.new = logs.reporter -- for old times sake
+
+-- context specicific: this ends up in the macro stream
+
+local ctxreport = logs.writer
+
+function logs.setmessenger(m)
+ ctxreport = m
+end
+
+function logs.messenger(category,subcategory)
+ -- we need to avoid catcode mess (todo: fast context)
+ if subcategory then
+ return function(...)
+ ctxreport(subdirect(category,subcategory,...))
+ end
+ else
+ return function(...)
+ ctxreport(direct(category,...))
+ end
+ end
+end
+
+-- so far
+
+local function setblocked(category,value) -- v.state == value == true : disable
+ if category == true or category == "all" then
+ -- lock all
+ category, value = "*", true
+ elseif category == false then
+ -- unlock all
+ category, value = "*", false
+ elseif value == nil then
+ -- lock selective
+ value = true
+ end
+ if category == "*" then
+ states = value
+ for k, v in next, data do
+ v.state = value
+ end
+ else
+ alllocked = false
+ states = settings_to_hash(category,type(states)=="table" and states or nil)
+ for c in next, states do
+ local v = data[c]
+ if v then
+ v.state = value
+ else
+ c = topattern(c,true,true)
+ for k, v in next, data do
+ if find(k,c) then
+ v.state = value
+ end
+ end
+ end
+ end
+ end
+end
+
+function logs.disable(category,value)
+ setblocked(category,value == nil and true or value)
+end
+
+function logs.enable(category)
+ setblocked(category,false)
+end
+
+function logs.categories()
+ return sortedkeys(data)
+end
+
+function logs.show()
+ local n, c, s, max = 0, 0, 0, 0
+ for category, v in table.sortedpairs(data) do
+ n = n + 1
+ local state = v.state
+ local reporters = v.reporters
+ local nc = #category
+ if nc > c then
+ c = nc
+ end
+ for subcategory, _ in next, reporters do
+ local ns = #subcategory
+ if ns > c then
+ s = ns
+ end
+ local m = nc + ns
+ if m > max then
+ max = m
+ end
+ end
+ local subcategories = concat(sortedkeys(reporters),", ")
+ if state == true then
+ state = "disabled"
+ elseif state == false then
+ state = "enabled"
+ else
+ state = "unknown"
+ end
+ -- no new here
+ report("logging","category %a, subcategories %a, state %a",category,subcategories,state)
+ end
+ report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max)
+end
+
+local delayed_reporters = { }
+
+setmetatableindex(delayed_reporters,function(t,k)
+ local v = logs.reporter(k.name)
+ t[k] = v
+ return v
+end)
+
+function utilities.setters.report(setter,...)
+ delayed_reporters[setter](...)
+end
+
+directives.register("logs.blocked", function(v)
+ setblocked(v,true)
+end)
+
+directives.register("logs.target", function(v)
+ settarget(v)
+end)
+
+do
+
+ local report = logs.reporter("pages") -- not needed but saves checking when we grep for it
+ local texgetcount = tex and tex.getcount
+
+ local real, user, sub = 0, 0, 0
+
+ function logs.start_page_number()
+ real = texgetcount("realpageno")
+ user = texgetcount("userpageno")
+ sub = texgetcount("subpageno")
+ end
+
+ local timing = false
+ local usage = false
+ local lasttime = nil
+
+ logs.private = {
+ enablepagetiming = function()
+ usage = true
+ end,
+ getpagetiming = function()
+ return type(usage) == "table" and usage
+ end,
+ }
+
+ trackers.register("pages.timing", function() timing = "" end)
+
+ function logs.stop_page_number() -- the first page can includes the initialization so we omit this in average
+ if timing or usage then
+ local elapsed = statistics.currenttime(statistics)
+ local average, page
+ if not lasttime or real < 2 then
+ average = elapsed
+ page = elapsed
+ else
+ average = elapsed / (real - 1)
+ page = elapsed - lasttime
+ end
+ lasttime = elapsed
+ if timing then
+ timing = formatters[", total %0.03f, page %0.03f, average %0.03f"](elapsed,page,average)
+ end
+ if usage then
+ usage = {
+ page = {
+ real = real,
+ user = user,
+ sub = sub,
+ },
+ time = {
+ elapsed = elapsed,
+ page = page,
+ average = average,
+ }
+ }
+ end
+ end
+ if real <= 0 then
+ report("flushing page%s",timing)
+ elseif user <= 0 then
+ report("flushing realpage %s%s",real,timing)
+ elseif sub <= 0 then
+ report("flushing realpage %s, userpage %s%s",real,user,timing)
+ else
+ report("flushing realpage %s, userpage %s, subpage %s%s",real,user,sub,timing)
+ end
+ logs.flush()
+ end
+
+end
+
+-- we don't have show_open and show_close callbacks yet
+
+do
+ local texerror = tex and tex.error or print
+ local formatters = string.formatters
+
+ function logs.texerrormessage(fmt,first,...) -- for the moment we put this function here
+ texerror(first and formatters[fmt](first,...) or fmt)
+ end
+
+end
+
+-- this is somewhat slower but prevents out-of-order messages when print is mixed
+-- with texio.write
+
+-- io.stdout:setvbuf('no')
+-- io.stderr:setvbuf('no')
+
+-- windows: > nul 2>&1
+-- unix : > null 2>&1
+
+if package.helpers.report then
+ package.helpers.report = logs.reporter("package loader") -- when used outside mtxrun
+end
+
+do
+
+ local finalactions = { }
+ local fatalerrors = { }
+ local possiblefatal = { }
+ local loggingerrors = false
+
+ function logs.loggingerrors()
+ return loggingerrors
+ end
+
+ directives.register("logs.errors",function(v)
+ loggingerrors = v
+ if type(v) == "string" then
+ fatalerrors = settings_to_hash(v)
+ else
+ fatalerrors = { }
+ end
+ end)
+
+ function logs.registerfinalactions(...)
+ insert(finalactions,...) -- so we can force an order if needed
+ end
+
+ local what = nil
+ local report = nil
+ local state = nil
+ local target = nil
+
+ local function startlogging(t,r,w,s)
+ target = t
+ state = force
+ force = true
+ report = type(r) == "function" and r or logs.reporter(r)
+ what = w
+ pushtarget(target)
+ newline()
+ if s then
+ report("start %s: %s",what,s)
+ else
+ report("start %s",what or "")
+ end
+ if target == "logfile" then
+ newline()
+ end
+ return report
+ end
+
+ local function stoplogging()
+ if target == "logfile" then
+ newline()
+ end
+ report("stop %s",what or "")
+ if target == "logfile" then
+ newline()
+ end
+ poptarget()
+ state = oldstate
+ end
+
+ function logs.startfilelogging(...)
+ return startlogging("logfile", ...)
+ end
+
+ logs.stopfilelogging = stoplogging
+
+ local done = false
+
+ function logs.starterrorlogging(r,w,...)
+ if not done then
+ pushtarget("terminal")
+ newline()
+ logs.report("error logging","start possible issues")
+ poptarget()
+ done = true
+ end
+ if fatalerrors[w] then
+ possiblefatal[w] = true
+ end
+ return startlogging("terminal",r,w,...)
+ end
+
+ logs.stoperrorlogging = stoplogging
+
+ function logs.finalactions()
+ if #finalactions > 0 then
+ for i=1,#finalactions do
+ finalactions[i]()
+ end
+ if done then
+ pushtarget("terminal")
+ newline()
+ logs.report("error logging","stop possible issues")
+ poptarget()
+ end
+ return next(possiblefatal) and sortedkeys(possiblefatal) or false
+ end
+ end
+
+end
+
+-- just in case we load from context
+
+local dummy = function() end
+
+function logs.application(t)
+ return {
+ name = t.name or tex.jobname,
+ banner = t.banner,
+ report = logs.reporter(t.name),
+ moreinfo = dummy,
+ export = dummy,
+ help = dummy,
+ identify = dummy,
+ version = dummy,
+ }
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-soc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/luat-soc.mkxl
new file mode 100644
index 00000000000..b2ce7048395
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-soc.mkxl
@@ -0,0 +1,52 @@
+%D \module
+%D [ file=luat-soc,
+%D version=2018.08.05,
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Socket Libraries,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Macros / Socket Libraries}
+
+%D In \LUATEX\ we provide the socket library that is more or less the standard one
+%D for \LUA. It has been around for a while and seems to be pretty stable. The
+%D binary module is copmpiled into \LUATEX\ and the accompanying .lua files are
+%D preloaded. These files are mostly written by Diego Nehab, Andre Carregal, Javier
+%D Guerra, and Fabio Mascarenhas with contributions from Diego Nehab, Mike Pall,
+%D David Burgess, Leonardo Godinho, Thomas Harning Jr., and Gary NG. The originals
+%D are part of and copyrighted by the Kepler project.
+%D
+%D Here we reload a slightly reworked version of these \type {.lua} files. We keep
+%D the same (documented) interface but streamlined some fo the code. No more
+%D modules, no more pre 5.2 \LUA, etc. Also, as it loads into the \CONTEXT
+%D ecosystem, we plug in some logging. (and maybe tracing in the future). As we
+%D don't support serial ports in \LUATEX, related code has been dropped.
+%D
+%D The files are reformatted so that we can more easilly add additional features
+%D and|/|or tracing options. Any error introduced there is our fault! The url module
+%D might be replaced by the one in \CONTEXT. When we need mbox a suitable variant
+%D will be provided.
+
+%D Currently we preload the related \LUA\ code in \LUATEX, but that might change at
+%D some point. We're prepared for that.
+
+% \registerctxluafile{util-soc-imp-reset} {}
+%
+% \registerctxluafile{util-soc-imp-socket} {}
+% % registerctxluafile{util-soc-imp-copas} {}
+% \registerctxluafile{util-soc-imp-ltn12} {}
+% % registerctxluafile{util-soc-imp-mbox} {}
+% \registerctxluafile{util-soc-imp-mime} {}
+% \registerctxluafile{util-soc-imp-url} {}
+% \registerctxluafile{util-soc-imp-headers}{}
+% \registerctxluafile{util-soc-imp-http} {}
+% \registerctxluafile{util-soc-imp-tp} {}
+% % registerctxluafile{util-soc-imp-ftp} {}
+% % registerctxluafile{util-soc-imp-smtp} {}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-usr.lmt b/Master/texmf-dist/tex/context/base/mkxl/luat-usr.lmt
new file mode 100644
index 00000000000..4b876b93539
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-usr.lmt
@@ -0,0 +1,177 @@
+if not modules then modules = { } end modules ['luat-usr'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local global = global
+
+local moduledata = moduledata
+local thirddata = thirddata
+local userdata = userdata
+local documentdata = documentdata
+
+local context = context
+local tostring = tostring
+local tonumber = tonumber
+local print = print
+
+local string = string
+local table = table
+local lpeg = lpeg
+local math = math
+local io = io
+local os = os
+local lpeg = lpeg
+
+local setmetatableindex = table.setmetatableindex
+local load = load
+local xpcall = xpcall
+local instance_banner = string.formatters["=[instance: %s]"] -- the = controls the lua error / see: lobject.c
+local tex_errormessage = context.errmessage
+
+local implement = interfaces.implement
+local reporter = logs.reporter
+
+local report_instance = reporter("lua instance")
+local report_script = reporter("lua script")
+local report_thread = reporter("lua thread")
+local newline = logs.newline
+
+lua.numbers = lua.numbers or { }
+lua.messages = lua.messages or { }
+
+local numbers = lua.numbers
+local messages = lua.messages
+
+storage.register("lua/numbers", numbers, "lua.numbers" )
+storage.register("lua/messages", messages, "lua.messages")
+
+-- First we implement a pure lua version of directlua and a persistent
+-- variant of it:
+
+local function runscript(code)
+ local done, message = loadstring(code)
+ if done then
+ done()
+ else
+ newline()
+ report_script("error : %s",message or "unknown")
+ report_script()
+ report_script("code : %s",code)
+ newline()
+ end
+end
+
+local threads = setmetatableindex(function(t,k)
+ local v = setmetatableindex({},global)
+ t[k] = v
+ return v
+end)
+
+local function runthread(name,code)
+ if not code or code == "" then
+ threads[name] = nil
+ else
+ local thread = threads[name]
+ local done, message = loadstring(code,nil,nil,thread)
+ if done then
+ done()
+ else
+ newline()
+ report_thread("thread: %s",name)
+ report_thread("error : %s",message or "unknown")
+ report_thread()
+ report_thread("code : %s",code)
+ newline()
+ end
+ end
+end
+
+interfaces.implement {
+ name = "luascript",
+ actions = runscript,
+ public = true,
+ protected = true,
+ arguments = "string"
+}
+
+interfaces.implement {
+ name = "luathread",
+ actions = runthread,
+ public = true,
+ protected = true,
+ arguments = "2 strings",
+}
+
+local function registername(name,message)
+ if not name or name == "" then
+ report_instance("no valid name given")
+ return
+ end
+ if not message or message == "" then
+ message = name
+ end
+ local lnn = numbers[name]
+ if not lnn then
+ lnn = #messages + 1
+ messages[lnn] = message
+ numbers[name] = lnn
+ end
+ local report = reporter("lua instance",message)
+ local proxy = {
+ -- we can access all via:
+ global = global, -- or maybe just a metatable
+ -- some protected data
+ moduledata = setmetatableindex(moduledata),
+ thirddata = setmetatableindex(thirddata),
+ -- less protected data
+ userdata = userdata,
+ documentdata = documentdata,
+ -- always there fast
+ context = context,
+ --
+ tostring = tostring,
+ tonumber = tonumber,
+ -- standard lua modules
+ string = string,
+ table = table,
+ lpeg = lpeg,
+ math = math,
+ io = io,
+ os = os,
+ lpeg = lpeg,
+ --
+ print = print,
+ report = report,
+ }
+ return function(code)
+ local code, message = load(code,nil,nil,proxy)
+ if not code then
+ report_instance("error: %s",message or code)
+ elseif not xpcall(code,report) then
+ tex_errormessage("hit return to continue or quit this run")
+ end
+ end
+end
+
+lua.registername = registername
+
+implement {
+ name = "registernamedlua",
+ arguments = "3 strings",
+ actions = function(name,message,csname)
+ if csname and csname ~= "" then
+ implement {
+ name = csname,
+ arguments = "string",
+ actions = registername(name,message) or report,
+ scope = "private",
+ }
+ else
+ report_instance("unvalid csname for %a",message or name or "?")
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/luat-usr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/luat-usr.mkxl
new file mode 100644
index 00000000000..199d22b8e6f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/luat-usr.mkxl
@@ -0,0 +1,121 @@
+%D \module
+%D [ file=luat-usr,
+%D version=2005.08.11,% moved from luat-ini
+%D title=\CONTEXT\ Lua Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Lua Macros / Userspace}
+
+\unprotect
+
+\registerctxluafile{luat-usr}{autosuffix}
+
+%D A few goodies:
+%D
+%D \startbuffer
+%D \luascript { context("foo 1:") context(i) }
+%D \luathread {test} { i = 10 context("bar 1:") context(i) }
+%D \luathread {test} { context("bar 2:") context(i) }
+%D \luathread {test} {}
+%D \luathread {test} { context("bar 3:") context(i) }
+%D \luascript { context("foo 2:") context(i) }
+%D \stopbuffer
+%D
+%D \typebuffer \startlines \getbuffer \stoplines
+
+% \luascript % defined at the lua end
+% \luathread % defined at the lua end
+
+%D \macros
+%D {definenamedlua}
+%D
+%D We provide an interface for defining instances. We don't have the definers yet
+%D so we do it low level:
+
+\def\s!lua {lua}
+\def\s!code {code}
+\def\s!data {data}
+\def\s!start{start}
+\def\s!stop {stop}
+
+%D Beware: because \type {\expanded} is een convert command, the error message will
+%D show \type{<inserted text>} as part of the message.
+
+\installsystemnamespace{luacode}
+
+\permanent\tolerant\protected\def\definenamedlua[#1]#*[#2]%
+ {\ifarguments \orelse \ifcsname\??luacode#1\endcsname \else
+ \bgroup
+ %
+ \edef\m_syst_lua_name{lua_code_#1}%
+ %
+ \clf_registernamedlua{#1}{#2}{\m_syst_lua_name}%
+ %
+ \frozen\instance\protected\xdefcsname\s!start#1\s!code\endcsname
+ {\begingroup
+ \obeylualines
+ \obeyluatokens
+ \csname\??luacode#1\endcsname}%
+ %
+ \aliased\gletcsname\s!stop#1\s!code\endcsname\relax
+ %
+ \normalexpanded{\xdefcsname\??luacode#1\endcsname##1\csname\s!stop#1\s!code\endcsname}%
+ {\noexpand\expandafter\endgroup
+ \noexpand\expandafter
+ \expandafter\noexpand\csname clf_\m_syst_lua_name\endcsname
+ \noexpand\expandafter{\noexpand\normalexpanded{##1}}}%
+ \frozen\instance\gletcsname#1\s!code\expandafter\endcsname\csname clf_\m_syst_lua_name\endcsname
+ \egroup
+ \fi}
+
+%D We predefine a few.
+
+% \definenamedlua[module][module instance] % not needed
+
+\appendtoks
+ \definenamedlua[user] [private user]
+ \definenamedlua[third] [third party module]
+ \definenamedlua[isolated][isolated]
+\to \everyjob
+
+%D In practice this works out as follows:
+%D
+%D \startbuffer
+%D \startluacode
+%D context("LUA")
+%D \stopluacode
+%D
+%D \startusercode
+%D global.context("USER 1")
+%D context.par()
+%D context("USER 2")
+%D context.par()
+%D if characters then
+%D context("ACCESS directly")
+%D elseif global.characters then
+%D context("ACCESS via global")
+%D else
+%D context("NO ACCESS at all")
+%D end
+%D context.par()
+%D if bogus then
+%D context("ACCESS directly")
+%D elseif global.bogus then
+%D context("ACCESS via global")
+%D else
+%D context("NO ACCESS at all")
+%D end
+%D context.par()
+%D \stopusercode
+%D \stopbuffer
+%D
+%D \typebuffer
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lxml-css.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lxml-css.mkxl
new file mode 100644
index 00000000000..325516e16fc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lxml-css.mkxl
@@ -0,0 +1,76 @@
+%D \module
+%D [ file=lxml-css,
+%D version=2010.01.28,
+%D title=\CONTEXT\ Modules,
+%D subtitle=Css Helpers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{lxml-css}{}
+
+\unprotect
+
+\permanent\def\ctxmodulecss#1{\ctxlua{moduledata.css.#1}}
+
+% No stable interface yet.
+
+% \edef\CellPadding{\xmlatt{#1}{cellpadding}}
+% \ifempty\CellPadding
+% \edef\CellPadding{.25ex}
+% \else
+% \edef\CellPadding{\cssgetsinglepadding{\xmlatt{#1}{cellpadding}}}
+% \fi
+%
+% \starttexdefinition cssgetsinglepadding #1
+% \ctxlua {
+% context((moduledata.css.padding(
+% "#1",
+% \number\dimexpr0.1ex,
+% \number\dimexpr0.01\hsize,
+% \number\dimexpr1ex,
+% \number\dimexpr1em
+% ))) % returns 4 values therefore ()
+% }sp
+% \stoptexdefinition
+
+% \startxmlsetups html:settings
+% \xmlsetsetup{#1}{p}{html:p}
+% \stopxmlsetups
+%
+% \xmlmapvalue{ctx-before} {one} {\page BEFORE\par}
+% \xmlmapvalue{ctx-after} {two} {\par AFTER\page}
+% \xmlmapvalue{text-decoration}{underline}{U}
+% \xmlmapvalue{text-decoration}{overline} {O}
+%
+% \startxmlsetups html:p
+% \testfeatureonce{100000}{
+% \edef\foo{\xmlcssstylevalue{#1}{ctx-before}\xmlcssstylevalue{#1}{ctx-after}}
+% }
+% \page {\tttf style="\xmlatt{#1}{style}"} : \elapsedtime\ s \page
+% \xmlvalue{ctx-before}{\xmlcssstylevalue{#1}{ctx-before}}{}
+% \xmlflush{#1}
+% (\xmlcssstylevalue{#1}{text-decoration})
+% (\xmlcssmappedstylevalue{#1}{text-decoration}{text-decoration})
+% \xmlvalue{ctx-after} {\xmlcssstylevalue{#1}{ctx-after}}{}
+% \stopxmlsetups
+%
+% \startbuffer[temp]
+% <body>
+% <p style='ctx-before: one; text-decoration: underline overline; ctx-after: two;'>foo 1</p>
+% <p>foo 2</p>
+% </body>
+% \stopbuffer
+%
+% \xmlregistersetup{html:settings}
+% \xmlprocessbuffer{main}{temp}{}
+
+% \xmlcssstylevalue % defined at lua end
+% \xmlcssmappedstylevalue % defined at lua end
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lxml-ctx.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lxml-ctx.mkxl
new file mode 100644
index 00000000000..f4b2edc798f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lxml-ctx.mkxl
@@ -0,0 +1,58 @@
+%D \module
+%D [ file=lxml-ctx,
+%D version=2007.08.17,
+%D title=\CONTEXT\ \XML\ Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Experimental. This might change! Also, it might become a module
+%D instead if core code.
+
+\writestatus{loading}{ConTeXt XML Support / Goodies}
+
+\registerctxluafile{lxml-ctx}{}
+
+\unprotect
+
+\settrue \xmllshowbuffer
+\setfalse\xmllshowtitle
+\settrue \xmllshowwarning
+
+\definehead
+ [lshowtitle]
+ [subsubsubsubsubject]
+
+\setuphead
+ [lshowtitle]
+ [\c!style=\tta]
+
+\permanent\protected\def\xmllshow#1%
+ {\ctxlua{xml.ctx.tshow {
+ pattern = \!!bs\detokenize{#1}\!!es,
+ \ifconditional\xmllshowtitle
+ title = "lshowtitle",
+ \fi
+ \ifconditional\xmllshowwarning
+ warning = true,
+ \fi
+ } }}
+
+\permanent\protected\def\xmllshowbuffer#1#2#3%
+ {\ctxlua{xml.ctx.tshow {
+ pattern = \!!bs\detokenize{#2}\!!es,
+ \ifconditional\xmllshowbuffer
+ xmlroot = "#1",
+ attribute = "#3",
+ \fi
+ \ifconditional\xmllshowwarning
+ warning = true,
+ \fi
+ } }}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lxml-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lxml-ini.mkxl
new file mode 100644
index 00000000000..01d0f883f96
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lxml-ini.mkxl
@@ -0,0 +1,448 @@
+%D \module
+%D [ file=lxml-ini,
+%D version=2007.08.17,
+%D title=\CONTEXT\ \XML\ Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Todo: auto apply setups (manage at lua end)
+%D Todo: manuak: \xmlinclusion \xmlinclusions
+
+\writestatus{loading}{ConTeXt XML Support / Initialization}
+
+%registerctxluafile{lxml-tab}{} % loader
+%registerctxluafile{lxml-lpt}{} % parser
+%registerctxluafile{lxml-xml}{} % xml finalizers
+%registerctxluafile{lxml-aux}{} % extras using parser
+%registerctxluafile{lxml-mis}{} % extras independent of parser
+\registerctxluafile{lxml-ent}{} % entity hacks
+\registerctxluafile{lxml-tex}{} % tex finalizers
+\registerctxluafile{lxml-dir}{} % ctx hacks
+\registerctxluafile{lxml-ini}{} % interface
+
+\unprotect
+
+% todo: { } mandate so that we can alias
+
+% undocumented:
+
+\permanent\def\ctxlxml#1{\ctxlua{lxml.#1}}
+
+% for now indirect .. this will change when we have updated luatex (scan_argument)
+
+\permanent\def\xmlconcat #1#2#3{\clf_xmlconcat {#1}{#2}{\detokenize{#3}}}
+\permanent\def\xmlconcatrange #1#2#3#4#5{\clf_xmlconcatrange{#1}{#2}{#3}{#4}{\detokenize{#5}}}
+
+%D Maybe I should add \type {\protected} here:
+
+\permanent\def\xmlloadfile #1#2{\clf_xmlloadfile {#1}{#2}{\directxmlparameter\c!compress}}
+\permanent\def\xmlloadbuffer#1#2{\clf_xmlloadbuffer{#1}{#2}{\directxmlparameter\c!compress}}
+\permanent\def\xmlloaddata #1#2{\clf_xmlloaddata {#1}{#2}{\directxmlparameter\c!compress}}
+
+\aliased\let\xmlload\xmlloadfile
+
+%D These are defined at the \LUA\ end:
+
+% \xmlall \xmlatt \xmlattdef \xmlattribute \xmlattributedef \xmlbadinclusions
+% \xmlchainatt \xmlchainattdef \xmlchecknamespace \xmlcommand \xmlcontext \xmlcount
+% \xmldelete \xmldirect \xmldirectives \xmldirectivesafter \xmldirectivesbefore
+% \xmldisplayverbatim \xmlelement \xmlfilter \xmlfilterlist \xmlfirst \xmlflush
+% \xmlflushcontext \xmlflushlinewise \xmlflushpure \xmlflushspacewise \xmlflushtext
+% \xmlfunction \xmlinclude \xmlincludeoptions \xmlinclusion \xmlinclusionbase
+% \xmlinclusions \xmlindex \xmlinlineverbatim \xmllast \xmllastatt \xmllastmatch
+% \xmllastpar \xmlloaddirectives \xmlmain \xmlmatch \xmlname \xmlnamespace
+% \xmlnonspace \xmlpar \xmlparam \xmlpath \xmlpopmatch \xmlpos \xmlpure
+% \xmlpushmatch \xmlraw \xmlrefatt \xmlregisterns \xmlremapname \xmlremapnamespace
+% \xmlsave \xmlsetatt \xmlsetattribute \xmlsetpar \xmlsetparam \xmlsetsetup
+% \xmlsnippet \xmlstrip \xmlstripanywhere \xmlstripnolines \xmlstripped
+% \xmlstrippednolines \xmltag \xmltext \xmltobuffer \xmltobuffertextonly
+% \xmltobufferverbose \xmltofile \xmltoparameters \xmlverbatim
+
+\aliased\let\xmlposition\xmlindex
+
+\permanent\protected\def\xmlinfo#1{\hbox{\ttxx[\xmlname{#1}]}}
+\permanent\protected\def\xmlshow#1{\startpacked\ttx\xmlverbatim{#1}\stoppacked}
+
+% the next one is handy for mode runs because it enforces a consistent
+% #1 indexing (needed when using \xmltext{main:123}{...} like calls
+
+%let\xmladdindex \clf_xmladdindex
+
+% we need to pass the last argument as function, so
+
+\permanent\protected\def\xmlsetfunction#1#2#3{\ctxcommand{xmlsetfunction("#1",\!!bs#2\!!es,#3)}}
+
+% goodie:
+
+\immutable\def\xmltempbuffername{xml-temp}
+
+\permanent\protected\def\prettyprintbuffer#1#2% only used here
+ {\ifdefined\scitebuffer
+ \scitebuffer[#2][#1]%
+ \else
+ \typebuffer[#1][\c!option=#2]%
+ \fi}
+
+\permanent\protected\def\xmlprettyprint#1#2%
+ {\xmltobufferverbose{#1}{.}{\xmltempbuffername}%
+ \prettyprintbuffer\xmltempbuffername{#2}}
+
+\permanent\protected\def\xmlprettyprinttext#1#2%
+ {\xmltobuffertextonly{#1}{.}{\xmltempbuffername}%
+ \prettyprintbuffer\xmltempbuffername{#2}}
+
+\permanent\protected\def\inlineprettyprintbuffer#1#2% only used here
+ {\ifdefined\sciteinlinebuffer
+ \sciteinlinebuffer[#2][#1]%
+ \else
+ \typeinlinebuffer[#1][\c!option=#2]%
+ \fi}
+
+\permanent\protected\def\xmlinlineprettyprint#1#2%
+ {\xmltobufferverbose{#1}{.}{\xmltempbuffername}%
+ \inlineprettyprintbuffer\xmltempbuffername{#2}}
+
+\permanent\protected\def\xmlinlineprettyprinttext#1#2%
+ {\xmltobuffertextonly{#1}{.}{\xmltempbuffername}%
+ \inlineprettyprintbuffer\xmltempbuffername{#2}}
+
+% kind of special:
+
+\aliased\let\xmlstartraw\clf_xmlstartraw
+\aliased\let\xmlstopraw \clf_xmlstopraw
+
+\aliased\let\startxmlraw\clf_xmlstartraw
+\aliased\let\stopxmlraw \clf_xmlstopraw
+
+% These are expandable and defined at the \LUA\ end:
+
+% \xmldoif \xmldoifnot \xmldoifelse \xmldoiftext \xmldoifnottext \xmldoifelsetext
+% \xmldoifatt \xmldoifnotatt \xmldoifelseatt \xmldoifempty \xmldoifnotempty
+% \xmldoifelseempty \xmldoifselfempty \xmldoifnotselfempty \xmldoifelseselfempty
+
+\aliased\let\xmldoiftextelse \xmldoifelsetext
+\aliased\let\xmldoifemptyelse \xmldoifelseempty
+\aliased\let\xmldoifselfemptyelse \xmldoifelseselfempty
+
+% \startxmlsetups xml:include
+% \xmlinclude{main}{include}{filename|href}
+% \stopxmlsetups
+%
+% \xmlprependsetup{xml:include}
+
+% \let\xmlgrab\xmlsetsetup % obsolete
+% \let\xmlself\s!unknown % obsolete
+
+\aliased\let\xmlsetup\setupwithargumentswapped
+\aliased\let\xmls \setupwithargumentswapped % hardly any faster
+\aliased\let\xmlw \setupwithargument % hardly any faster
+
+% todo: 1:xml:whatever always before 3:xml:something
+
+\permanent\protected\def\xmlprependsetup #1{\clf_xmlprependsetup{*}{#1}}
+\permanent\protected\def\xmlappendsetup #1{\clf_xmlappendsetup {*}{#1}}
+\permanent\protected\def\xmlbeforesetup #1#2{\clf_xmlbeforesetup {*}{#1}{#2}}
+\permanent\protected\def\xmlaftersetup #1#2{\clf_xmlaftersetup {*}{#1}{#2}}
+\permanent\protected\def\xmlremovesetup #1{\clf_xmlremovesetup {*}{#1}}
+\permanent\protected\def\xmlresetsetups {\clf_xmlresetsetups {*}}
+
+\permanent\protected\def\xmlprependdocumentsetup #1#2{\clf_xmlprependsetup{#1}{#2}}
+\permanent\protected\def\xmlappenddocumentsetup #1#2{\clf_xmlappendsetup {#1}{#2}}
+\permanent\protected\def\xmlbeforedocumentsetup #1#2#3{\clf_xmlbeforesetup {#1}{#2}{#3}}
+\permanent\protected\def\xmlafterdocumentsetup #1#2#3{\clf_xmlaftersetup {#1}{#2}{#3}}
+\permanent\protected\def\xmlremovedocumentsetup #1#2{\clf_xmlremovesetup {#1}{#2}}
+\permanent\protected\def\xmlresetdocumentsetups #1{\clf_xmlresetsetups {#1}}
+
+\permanent\protected\def\xmlflushsetups #1{\clf_xmlflushsetups {#1}{*}{}} % #1 == id where to apply *
+\permanent\protected\def\xmlflushdocumentsetups #1#2{\clf_xmlflushsetups {#1}{*}{#2}} % #1 == id where to apply * and #2
+
+\aliased\let\xmlregistersetup \xmlappendsetup
+\aliased\let\xmlregisterdocumentsetup\xmlappenddocumentsetup
+
+\mutable\def\xmldocument{main}
+
+\permanent\protected\def\xmlregisteredsetups
+ {\xmlstarttiming
+ \xmlflushsetups\xmldocument
+ \xmldefaulttotext\xmldocument % after include
+ \xmlstoptiming}
+
+\permanent\protected\def\xmlregistereddocumentsetups#1#2% id setups
+ {\xmlstarttiming
+ % todo: test for duplicates !
+ \xmlflushdocumentsetups{#1}{#2}%
+ \xmldefaulttotext{#1}% after include
+ \xmlstoptiming}
+
+\permanent\protected\def\xmlstarttiming{\clf_xmlstarttiming} % undocumented
+\permanent\protected\def\xmlstoptiming {\clf_xmlstoptiming} % undocumented
+
+\def\lxml_process#1#2#3#4#5% flag \loader id name what initializersetup
+ {\begingroup
+ \edef\xmldocument{#3}% #2 can be \xmldocument and set as such
+ %xmlpushdocument{#3}%
+ #2{#3}{#4}%
+ \setcatcodetable\notcatcodes
+ \doifelsenothing{#5}%
+ {\xmlsetup{#3}{xml:process}}%
+ {\xmlsetup{#3}{#5}}%
+ %xmlpopdocument
+ \endgroup}
+
+\permanent\protected\def\xmlprocessfile {\lxml_process\plusone \xmlload}
+\permanent\protected\def\xmlprocessdata {\lxml_process\zerocount\xmlloaddata}
+\permanent\protected\def\xmlprocessbuffer{\lxml_process\zerocount\xmlloadbuffer}
+\aliased \let\xmlprocess \xmlprocessfile
+
+\startxmlsetups xml:flush
+ \xmlflush{#1}
+\stopxmlsetups
+
+\startxmlsetups xml:process
+ \xmlregistereddocumentsetups{#1}{#1}
+ \xmlmain{#1}
+\stopxmlsetups
+
+\permanent\protected\def\xmlloadonly#1#2#3%
+ {\xmlload{#1}{#2}%
+ \xmlregistereddocumentsetups{#1}{#3}}
+
+% replaced by concat
+%
+% \protected\def\xmlconnect#1#2#3% inefficient
+% {\scratchcounter\xmlcount{#1}{#2}\relax
+% \ifcase\scratchcounter \or
+% \xmlall{#1}{#2}%
+% \else
+% \dorecurse \scratchcounter
+% {\ifnum\recurselevel>\plusone#3\fi
+% \xmlidx{#1}{#2}\recurselevel}%
+% \fi}
+
+\permanent\protected\def\xmlcdataobeyedline {\obeyedline}
+\permanent\protected\def\xmlcdataobeyedspace{\strut\obeyedspace}
+\permanent\protected\def\xmlcdatabefore {\begingroup\tt}
+\permanent\protected\def\xmlcdataafter {\endgroup}
+
+% verbatim (dodo:pre/post whitespace, maybe splot verbatim and
+% cdata commands), experimental:
+%
+% \xmlsetfunction{main}{verbatim}{lxml.displayverbatim}
+% \xmlsetfunction{main}{verb} {lxml.inlineverbatim}
+
+% we use an xml: namespace so one has to define a suitable verbatim, say
+%
+% \definetyping[xml:verbatim][typing]
+%
+% this is experimental!
+
+\permanent\tolerant\protected\def\startxmldisplayverbatim[#1]%
+ {\startpacked
+ \edef\currenttyping{#1}%
+ \ifempty\currenttyping
+ \let\currenttyping\v!typing
+ \else % maybe test for existence
+ \edef\currenttyping{xml:\currenttyping}%
+ \fi
+ \enforced\protected\def\stopxmldisplayverbatim
+ {\endofverbatimlines
+ \stoppacked}%
+ \doinitializeverbatim
+ \beginofverbatimlines}
+
+\aliased\let\stopxmldisplayverbatim\relax
+
+\permanent\tolerant\protected\def\startxmlinlineverbatim[#1]%
+ {\begingroup
+ \edef\currenttype{#1}%
+ \ifempty\currenttype
+ \let\currenttype\v!type
+ \else % maybe test for existence
+ \edef\currenttype{xml:\currenttype}%
+ \fi
+ \enforced\let\stopxmlinlineverbatim\endgroup
+ \doinitializeverbatim}
+
+\aliased\let\stopxmlinlineverbatim\relax
+
+% processing instructions
+
+\permanent\protected\def\xmlinstalldirective#1#2%
+ {\clf_xmlinstalldirective{#1}{\csstring#2}}
+
+% an example:
+
+% <?context-tex-directive bgroup ?>
+
+\appendtoks
+ \xmlinstalldirective{tex}{xmltexcommand}%
+\to \everyjob
+
+\permanent\def\xmltexcommand#1{\begincsname#1\endcsname}
+
+% \def\xmlcontextdirective#1% kind class key value
+% {\executeifdefined{xml#1directive}\gobblethreearguments}
+
+% setting up xml:
+%
+% \setupxml[\c!default=] % mkiv only == text
+% \setupxml[\c!default=\v!none] % mkiv only, undefined -> hidden
+% \setupxml[\c!default=\v!text] % mkiv only, undefined -> text
+
+% \def\xmlctxdirective#1#2#3{\doif{#1}{clue}{\doif{#2}{page}}{\page[#3]}}
+
+\newconstant\xmlprocessingmode % 0=unset, 1=text, 2=hidden
+
+\installcorenamespace{xml}
+\installcorenamespace{xmldefaults}
+\installcorenamespace{xmlmapvalue}
+
+\installdirectcommandhandler \??xml {xml}
+
+\letvalue{\??xmldefaults\v!normal}\zerocount
+\letvalue{\??xmldefaults\v!none }\zerocount
+\letvalue{\??xmldefaults\v!text }\plusone
+\letvalue{\??xmldefaults\v!hidden}\plustwo
+
+\permanent\protected\def\xmldefaulttotext
+ {\ifcase\xmlprocessingmode
+ \expandafter\gobbleoneargument % 0 (none)
+ \or
+ \expandafter\clf_xmlsetcommandtotext % 1 (normal)
+ \or
+ \expandafter\clf_xmlsetcommandtonone % 2 (hidden)
+ \else
+ \expandafter\gobbleoneargument % (none)
+ \fi}
+
+\appendtoks
+ \xmlprocessingmode\executeifdefined{\??xmldefaults\directxmlparameter\c!default}\plusone
+\to \everysetupxml
+
+\setupxml
+ [\c!default=, % flush all
+ \c!compress=\v!no, % strip comment
+ \c!entities=\v!no] % load big entity file
+
+\appendtoks
+ \doif{\directxmlparameter\c!entities}\v!yes\clf_xmlloadentities
+\to \everysetupxml
+
+\permanent\def\xmlmapvalue #1#2#3{\setvalue{\??xmlmapvalue#1:#2}{#3}} % keep #3 to grab spaces
+\permanent\def\xmldoifelsevalue #1#2{\ifcsname\??xmlmapvalue#1:#2\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}
+
+\permanent\def\xmlvalue#1#2% #3
+ {\ifcsname\??xmlmapvalue#1:#2\endcsname
+ \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\def\xmlmappedvalue#1#2#3%
+ {\ifcsname\??xmlmapvalue#1:#2\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \csname\??xmlmapvalue#1:#3\expandafter\endcsname
+ \fi}
+
+\aliased\let\xmldoifvalueelse\xmldoifelsevalue
+
+\aliased\let\xmlmapval\xmlmapvalue
+\aliased\let\xmlval \xmlvalue
+
+%D Experimental:
+
+\permanent\def\xmlgetindex #1{\clf_xmlgetindex {\xmldocument}{#1}}
+\permanent\def\xmlwithindex #1#2{\clf_xmlwithindex{\xmldocument}{#1}{#2}}
+\permanent\def\xmlreference #1#2{\string\xmlwithindex{#1}{#2}}
+
+%D Entities:
+%D
+%D \starttyping
+%D \xmlsetentity{tex}{\TEX{}} % {} needed
+%D \stoptyping
+
+\permanent\protected\def\xmlsetentity#1#2{\clf_xmlsetentity{#1}{\detokenize{#2}}}
+\permanent\protected\def\xmltexentity#1#2{\clf_xmltexentity{#1}{\detokenize{#2}}}
+
+%D The following might change (or even disappear) so we keep it undocumented.
+
+\setnewconstant\xmlautoentities\plusone % 0=off, 1=upper, 2=upper,lower
+
+\permanent\protected\def\xmle
+ {\ifcase\xmlautoentities
+ \expandafter\lxml_e_none
+ \or
+ \expandafter\lxml_e_upper
+ \or
+ \expandafter\lxml_e_upperlower
+ \else
+ \expandafter\lxml_e_none
+ \fi}
+
+\def\lxml_e_none#1#2% safe
+ {#1}
+
+\def\lxml_e_upper#1#2% can be abbreviation
+ {\ifcsname#2\endcsname
+ \csname#2\expandafter\endcsname
+ \else
+ #1%
+ \fi}
+
+\def\lxml_e_upperlower#1#2% can be anything, so unsafe
+ {\ifcsname#2\endcsname
+ \csname#2\expandafter\endcsname
+ \orelse\ifcsname#1\endcsname
+ \csname#1\expandafter\endcsname
+ \else
+ #1%
+ \fi}
+
+%D We keep these around as there are also MP, LUA and TEX variants but they are not
+%D the same as in \MKII.
+
+\permanent\tolerant\protected\def\processXMLbuffer[#1]{\xmlprocessbuffer{temp}{#1}{}}
+\permanent \protected\def\processXMLfile #1{\xmlprocessfile {temp}{#1}{}}
+\permanent \protected\def\XMLdata #1{\xmlprocessdata {temp}{#1}{}}
+
+\aliased\let\processxmlbuffer\processXMLbuffer
+\aliased\let\processxmlfile \processXMLfile
+\aliased\let\xmldata \XMLdata
+
+\permanent\protected\def\xmlsetinjectors [#1]{\clf_xmlsetinjectors{#1}}
+\permanent\protected\def\xmlresetinjectors {\clf_xmlresetinjectors{}}
+
+% \def\xmlinjector#1{\executeifdefined{#1}\donothing}
+
+\permanent\def\xmlinjector#1{\fastsetup{xml:directive:injector:#1}}
+
+\startsetups xml:directive:injector:page
+ \page
+\stopsetups
+
+\startsetups xml:directive:injector:column
+ \column
+\stopsetups
+
+\startsetups xml:directive:injector:blank
+ \blank
+\stopsetups
+
+\startsetups xml:directive:injector:noline
+ \vskip-\lineheight
+\stopsetups
+
+\aliased\let\xmlapplyselectors\clf_xmlapplyselectors
+
+% In the\MKIV\ file there is some commented code wrt handing entities with active
+% characters ... very old and obsolete.
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/lxml-sor.mkxl b/Master/texmf-dist/tex/context/base/mkxl/lxml-sor.mkxl
new file mode 100644
index 00000000000..798047e0423
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/lxml-sor.mkxl
@@ -0,0 +1,99 @@
+%D \module
+%D [ file=lxml-sor,
+%D version=2009.08.24,
+%D title=\CONTEXT\ \XML\ Support,
+%D subtitle=Sorting,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D THIS IS VERY EXPERIMENTAL!
+
+\writestatus{loading}{ConTeXt XML Support / Sorting}
+
+\registerctxluafile{lxml-sor}{}
+
+\unprotect
+
+%D The flusher is unexpandable so that it can be used in tables (noalign
+%D interferences).
+
+% todo: public implementors
+
+\permanent\protected\def\xmlresetsorter #1{\ctxlxml{sorters.reset("#1")}}
+\permanent\protected\def\xmladdsortentry#1#2#3{\ctxlxml{sorters.add("#1","#2",\!!bs#3\!!es)}}
+\permanent\protected\def\xmlshowsorter #1{\ctxlxml{sorters.show("#1")}}
+\permanent \def\xmlflushsorter #1#2{\ctxlxml{sorters.flush("#1","#2")}}
+\permanent\protected\def\xmlsortentries #1{\ctxlxml{sorters.sort("#1")}}
+
+\protect \endinput
+
+\startbuffer[test]
+<demo>
+ <entry>
+ <category>one</category>
+ <key>alpha</key>
+ <content>alpha indeed</content>
+ </entry>
+ <entry>
+ <category>one</category>
+ <key>gamma</key>
+ <content>gamma indeed</content>
+ </entry>
+ <entry>
+ <category>one</category>
+ <key>beta</key>
+ <content>beta indeed</content>
+ </entry>
+ <entry>
+ <category>two</category>
+ <key>alpha</key>
+ <content>alpha again</content>
+ </entry>
+ <entry>
+ <category>two</category>
+ <key>gamma</key>
+ <content>gamma again</content>
+ </entry>
+ <entry>
+ <category>two</category>
+ <key>beta</key>
+ <content>beta again</content>
+ </entry>
+</demo>
+\stopbuffer
+
+\startxmlsetups xml:mysetups
+ \xmlsetsetup{\xmldocument}{demo|entry|content}{xml:*}
+\stopxmlsetups
+
+\xmlregistersetup{xml:mysetups}
+
+\startxmlsetups xml:demo
+ \xmlresetsorter{demo}
+ \xmlfilter{#1}{entry/command(xml:entry:getkeys)}
+ \blank sortkeys: \blank\xmlshowsorter{demo}\blank
+ \xmlsortentries{demo}
+ \xmlflushsorter{demo}{xml:entry:flush}
+\stopxmlsetups
+
+\startxmlsetups xml:entry:getkeys
+ \xmladdsortentry{demo}{#1}{\xmltext{#1}{category}}
+ \xmladdsortentry{demo}{#1}{\xmltext{#1}{key|entry}}
+\stopxmlsetups
+
+\startxmlsetups xml:entry:flush
+ \xmltext{#1}{content}\par
+\stopxmlsetups
+
+\startxmlsetups xml:entry
+ \xmltext{#1}{content}\par
+\stopxmlsetups
+
+\starttext
+ \xmlprocessbuffer{main}{test}{}
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/m-mkivmkxl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/m-mkivmkxl.mkxl
new file mode 100644
index 00000000000..9aea14eb21f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/m-mkivmkxl.mkxl
@@ -0,0 +1,3 @@
+% mkiv spacing before figure
+
+% \let\checkprevdepth\baselinecorrection
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-acc.mklx b/Master/texmf-dist/tex/context/base/mkxl/math-acc.mklx
new file mode 100644
index 00000000000..9d86566302b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-acc.mklx
@@ -0,0 +1,153 @@
+%D \module
+%D [ file=math-acc,
+%D version=2013.07.31,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Accents,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Accents}
+
+% There are probably errors ... too distracted by amazing (piano) music videos running
+% on top of scite ... so: experimental code.
+
+\unprotect
+
+% This module permits overloading of accents so that we can do fancy things. The
+% implementation is similar to stackers. By default accenst are defined in a simple
+% way. Contrary to extensibles accents cannot grow indefinitely. Alas the
+% implementation of accents is different too, in the sense that they are
+% prepositioned i.e. are already raised. (In my opinion for no real reason as they
+% need to adapt anyway).
+%
+% $ \ruledhbox{$H$} \hat{H} \ruledhbox{$\widehat{H}$} \widehat{H} $
+%
+% One alternative is:
+%
+% \definemathoverextensible [top] [hoed] ["FE302]
+% \definemathoverextensible [top] [slang] ["FE303]
+%
+% $ \hoed{H} \ruledhbox{$\hoed{H}$} \ruledhbox{$\hoed{\tf H}$} \slang{H} $
+%
+% But that nills the italic correction (and I'm in nood to mess with that again).
+%
+% \definemathaccents [myaccents] [color=darkred]
+% \definemathtopaccent [myaccents] [mywidehat] ["0302]
+%
+% $ \hat{H} \widehat{H} \mywidehat{H} $
+
+%D Older implementations are kept in the \MKII\ and \MKIV\ modules for educational
+%D purposes.
+
+\installcorenamespace{mathaccents}
+
+\installcommandhandler \??mathaccents {mathaccents} \??mathaccents
+
+\setupmathaccents
+ [\c!top=,
+ \c!bottom=,
+ \c!mathstyle=,
+ \c!color=,
+ \c!define=\v!yes] % not used
+
+\definemathaccents
+ [\v!both]
+
+\definemathaccents
+ [\v!top]
+ [\v!both]
+
+\definemathaccents
+ [\v!bottom]
+ [\v!both]
+
+\permanent\tolerant\protected\def\definemathtopaccent[#1]#*[#2]#*[#3]% class name top
+ {\ifparameter#3\or
+ \frozen\instance\setuevalue{#2}{\math_accents_make_double {#1}\plusone{\number#3}\zerocount}%
+ \else
+ \frozen\instance\setuevalue{#1}{\math_accents_make_double\noexpand\currentmathaccents\plusone{\number#2}\zerocount}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemathbottomaccent[#1]#*[#2]#*[#3]% class name bottom
+ {\ifparameter#3\or
+ \frozen\instance\setuevalue{#2}{\math_accents_make_double {#1}\plustwo\zerocount{\number#3}}%
+ \else
+ \frozen\instance\setuevalue{#1}{\math_accents_make_double\noexpand\currentmathaccents\plustwo\zerocount{\number#2}}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemathdoubleaccent[#1]#*[#2]#*[#3]#*[#4]% class name top bottom
+ {\ifparameter#4\or
+ \frozen\instance\setuevalue{#2}{\math_accents_make_double {#1}\plusthree{\number#3}{\number#4}}%
+ \else
+ \frozen\instance\setuevalue{#1}{\math_accents_make_double\noexpand\currentmathaccents\plusthree{\number#2}{\number#3}}%
+ \fi}
+
+\let\math_accents_color_pop\donothing
+
+\def\math_accents_color_push_yes
+ {\pushcolor[\p_math_accent_color]%
+ \let\math_accents_color_pop\popcolor}
+
+\def\math_accents_color_push_nop
+ {\let\math_accents_color_pop\donothing}
+
+\protected\def\math_accents_make_double#class#kind#top#bottom#content%
+ {\begingroup
+ \edef\currentmathaccents {#class}%
+ \edef\p_math_accent_color{\mathaccentsparameter\c!color}%
+ \startusemathstyleparameter\mathaccentsparameter
+ \ifempty\p_math_accent_color
+ \math_accents_color_push_nop
+ \else
+ \math_accents_color_push_yes
+ \fi
+ \ifcase#kind\or
+ \Umathaccent \fam\zerocount#top
+ \or
+ \Umathaccent bottom \fam\zerocount#bottom
+ \or
+ \Umathaccent both \fam\zerocount#top
+ \fam\zerocount#bottom
+ \fi
+ {\math_accents_color_pop#content}%
+ \stopusemathstyleparameter
+ \endgroup}
+
+%D Relative new:
+
+\newconditional\c_math_accents_auto_dotless \settrue\c_math_accents_auto_dotless % cf opentype math
+
+\aliased\let\normalgrave\grave \permanent\protected\def\dotlessgrave#1{\normalgrave{\mathdotless#1}}
+\aliased\let\normalddot \ddot \permanent\protected\def\dotlessddot #1{\normalddot {\mathdotless#1}}
+\aliased\let\normalbar \bar \permanent\protected\def\dotlessbar #1{\normalbar {\mathdotless#1}}
+\aliased\let\normalacute\acute \permanent\protected\def\dotlessacute#1{\normalacute{\mathdotless#1}}
+\aliased\let\normalhat \hat \permanent\protected\def\dotlesshat #1{\normalhat {\mathdotless#1}}
+\aliased\let\normalcheck\check \permanent\protected\def\dotlesscheck#1{\normalcheck{\mathdotless#1}}
+\aliased\let\normalbreve\breve \permanent\protected\def\dotlessbreve#1{\normalbreve{\mathdotless#1}}
+\aliased\let\normaldot \dot \permanent\protected\def\dotlessdot #1{\normaldot {\mathdotless#1}}
+\aliased\let\normalring \ring \permanent\protected\def\dotlessring #1{\normalring {\mathdotless#1}}
+\aliased\let\normaltilde\tilde \permanent\protected\def\dotlesstilde#1{\normaltilde{\mathdotless#1}}
+\aliased\let\normaldddot\dddot \permanent\protected\def\dotlessdddot#1{\normaldddot{\mathdotless#1}}
+
+\def\math_accents_auto_dotless#1#2{\ifconditional\c_math_accents_auto_dotless\expandafter#2\else\expandafter#1\fi}
+
+\permanent\protected\def\grave {\math_accents_auto_dotless\normalgrave \dotlessgrave }
+\permanent\protected\def\ddot {\math_accents_auto_dotless\normalddot \dotlessddot }
+\permanent\protected\def\bar {\math_accents_auto_dotless\normalbar \dotlessbar }
+\permanent\protected\def\acute {\math_accents_auto_dotless\normalacute \dotlessacute }
+\permanent\protected\def\hat {\math_accents_auto_dotless\normalhat \dotlesshat }
+\permanent\protected\def\check {\math_accents_auto_dotless\normalcheck \dotlesscheck }
+\permanent\protected\def\breve {\math_accents_auto_dotless\normalbreve \dotlessbreve }
+\permanent\protected\def\dot {\math_accents_auto_dotless\normaldot \dotlessdot }
+\permanent\protected\def\ring {\math_accents_auto_dotless\normalring \dotlessring }
+\permanent\protected\def\tilde {\math_accents_auto_dotless\normaltilde \dotlesstilde }
+\permanent\protected\def\dddot {\math_accents_auto_dotless\normaldddot \dotlessdddot }
+
+\aliased\let\mathring\ring % for a while
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt b/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt
new file mode 100644
index 00000000000..9da79976484
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt
@@ -0,0 +1,478 @@
+if not modules then modules = { } end modules ['math-act'] = {
+ version = 1.001,
+ comment = "companion to math-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Here we tweak some font properties (if needed). The commented sections
+-- have been removed (no longer viable) but can be found in the .lua variant.
+
+local type, next = type, next
+local fastcopy, insert, remove = table.fastcopy, table.insert, table.remove
+local formatters = string.formatters
+
+local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
+local trace_collecting = false trackers.register("math.collecting", function(v) trace_collecting = v end)
+
+local report_math = logs.reporter("mathematics","initializing")
+
+local context = context
+local commands = commands
+local mathematics = mathematics
+local texsetdimen = tex.setdimen
+local abs = math.abs
+
+local helpers = fonts.helpers
+local upcommand = helpers.commands.up
+local rightcommand = helpers.commands.right
+local charcommand = helpers.commands.char
+local prependcommands = helpers.prependcommands
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+local fontchars = fonts.hashes.characters
+local fontproperties = fonts.hashes.properties
+
+local mathfontparameteractions = sequencers.new {
+ name = "mathparameters",
+ arguments = "target,original",
+}
+
+appendgroup("mathparameters","before") -- user
+appendgroup("mathparameters","system") -- private
+appendgroup("mathparameters","after" ) -- user
+
+function fonts.constructors.assignmathparameters(original,target)
+ local runner = mathfontparameteractions.runner
+ if runner then
+ runner(original,target)
+ end
+end
+
+function mathematics.initializeparameters(target,original)
+ local mathparameters = original.mathparameters
+ if mathparameters and next(mathparameters) then
+ mathparameters = mathematics.dimensions(mathparameters)
+ if not mathparameters.SpaceBeforeScript then
+ mathparameters.SpaceBeforeScript = mathparameters.SpaceAfterScript
+ end
+ target.mathparameters = mathparameters
+ end
+end
+
+sequencers.appendaction("mathparameters","system","mathematics.initializeparameters")
+
+local how = {
+ -- RadicalKernBeforeDegree = "horizontal",
+ -- RadicalKernAfterDegree = "horizontal",
+ ScriptPercentScaleDown = "unscaled",
+ ScriptScriptPercentScaleDown = "unscaled",
+ RadicalDegreeBottomRaisePercent = "unscaled",
+ NoLimitSupFactor = "unscaled",
+ NoLimitSubFactor = "unscaled",
+}
+
+function mathematics.scaleparameters(target,original)
+ if not target.properties.math_is_scaled then
+ local mathparameters = target.mathparameters
+ if mathparameters and next(mathparameters) then
+ local parameters = target.parameters
+ local factor = parameters.factor
+ local hfactor = parameters.hfactor
+ local vfactor = parameters.vfactor
+ for name, value in next, mathparameters do
+ local h = how[name]
+ if h == "unscaled" then
+ -- kept
+ elseif h == "horizontal" then
+ value = value * hfactor
+ elseif h == "vertical"then
+ value = value * vfactor
+ else
+ value = value * factor
+ end
+ mathparameters[name] = value
+ end
+ end
+ target.properties.math_is_scaled = true
+ end
+end
+
+-- AccentBaseHeight vs FlattenedAccentBaseHeight
+
+function mathematics.checkaccentbaseheight(target,original)
+ local mathparameters = target.mathparameters
+ if mathparameters and mathparameters.AccentBaseHeight == 0 then
+ mathparameters.AccentBaseHeight = target.parameters.x_height -- needs checking
+ end
+end
+
+function mathematics.checkprivateparameters(target,original)
+ local mathparameters = target.mathparameters
+ if mathparameters then
+ local parameters = target.parameters
+ local properties = target.properties
+ if parameters then
+ local size = parameters.size
+ if size then
+ if not mathparameters.FractionDelimiterSize then
+ mathparameters.FractionDelimiterSize = 1.01 * size
+ end
+ if not mathparameters.FractionDelimiterDisplayStyleSize then
+ mathparameters.FractionDelimiterDisplayStyleSize = 2.40 * size
+ end
+ elseif properties then
+ report_math("invalid parameters in font %a",properties.fullname or "?")
+ else
+ report_math("invalid parameters in font")
+ end
+ elseif properties then
+ report_math("no parameters in font %a",properties.fullname or "?")
+ else
+ report_math("no parameters and properties in font")
+ end
+ end
+end
+
+function mathematics.overloadparameters(target,original)
+ local mathparameters = target.mathparameters
+ if mathparameters and next(mathparameters) then
+ local goodies = target.goodies
+ if goodies then
+ for i=1,#goodies do
+ local goodie = goodies[i]
+ local mathematics = goodie.mathematics
+ local parameters = mathematics and mathematics.parameters
+ if parameters then
+ if trace_defining then
+ report_math("overloading math parameters in %a @ %p",target.properties.fullname,target.parameters.size)
+ end
+ for name, value in next, parameters do
+ local tvalue = type(value)
+ if tvalue == "string" then
+ report_math("comment for math parameter %a: %s",name,value)
+ else
+ local oldvalue = mathparameters[name]
+ local newvalue = oldvalue
+ if oldvalue then
+ if tvalue == "number" then
+ newvalue = value
+ elseif tvalue == "function" then
+ newvalue = value(oldvalue,target,original)
+ elseif not tvalue then
+ newvalue = nil
+ end
+ if trace_defining and oldvalue ~= newvalue then
+ report_math("overloading math parameter %a: %S => %S",name,oldvalue,newvalue)
+ end
+ else
+ report_math("invalid math parameter %a",name)
+ end
+ mathparameters[name] = newvalue
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function applytweaks(when,target,original)
+ local goodies = original.goodies
+ if goodies then
+ for i=1,#goodies do
+ local goodie = goodies[i]
+ local mathematics = goodie.mathematics
+ local tweaks = mathematics and mathematics.tweaks
+ if type(tweaks) == "table" then
+ tweaks = tweaks[when]
+ if type(tweaks) == "table" then
+ if trace_defining then
+ report_math("tweaking math of %a @ %p (%s)",target.properties.fullname,target.parameters.size,when)
+ end
+ for i=1,#tweaks do
+ local tweak= tweaks[i]
+ local tvalue = type(tweak)
+ if tvalue == "function" then
+ tweak(target,original)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+function mathematics.tweakbeforecopyingfont(target,original)
+ local mathparameters = target.mathparameters -- why not hasmath
+ if mathparameters then
+ applytweaks("beforecopying",target,original)
+ end
+end
+
+function mathematics.tweakaftercopyingfont(target,original)
+ local mathparameters = target.mathparameters -- why not hasmath
+ if mathparameters then
+ applytweaks("aftercopying",target,original)
+ end
+end
+
+sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
+sequencers.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead
+sequencers.appendaction("mathparameters","system","mathematics.checkprivateparameters") -- after scaling !
+sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
+
+sequencers.appendaction("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
+sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
+
+-- no, it's a feature now (see good-mth):
+--
+-- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.overloaddimensions")
+
+-- a couple of predefined tweaks:
+
+local tweaks = { }
+mathematics.tweaks = tweaks
+
+-- helpers
+
+local setmetatableindex = table.setmetatableindex
+
+local getfontoffamily = tex.getfontoffamily
+
+local fontcharacters = fonts.hashes.characters
+local extensibles = utilities.storage.allocate()
+fonts.hashes.extensibles = extensibles
+
+local chardata = characters.data
+local extensibles = mathematics.extensibles
+
+-- we use numbers at the tex end (otherwise we could stick to chars)
+
+local e_left = extensibles.left
+local e_right = extensibles.right
+local e_horizontal = extensibles.horizontal
+local e_mixed = extensibles.mixed
+local e_unknown = extensibles.unknown
+
+local unknown = { e_unknown, false, false }
+
+local function extensiblecode(font,unicode)
+ local characters = fontcharacters[font]
+ local character = characters[unicode]
+ if not character then
+ return unknown
+ end
+ local first = character.next
+ local code = unicode
+ local next = first
+ while next do
+ code = next
+ character = characters[next]
+ next = character.next
+ end
+ local char = chardata[unicode]
+ if not char then
+ return unknown
+ end
+ if character.horiz_variants then
+ if character.vert_variants then
+ return { e_mixed, code, character }
+ else
+ local m = char.mathextensible
+ local e = m and extensibles[m]
+ return e and { e, code, character } or unknown
+ end
+ elseif character.vert_variants then
+ local m = char.mathextensible
+ local e = m and extensibles[m]
+ return e and { e, code, character } or unknown
+ elseif first then
+ -- assume accent (they seldom stretch .. sizes)
+ local m = char.mathextensible or char.mathstretch
+ local e = m and extensibles[m]
+ return e and { e, code, character } or unknown
+ else
+ return unknown
+ end
+end
+
+setmetatableindex(extensibles,function(extensibles,font)
+ local codes = { }
+ setmetatableindex(codes, function(codes,unicode)
+ local status = extensiblecode(font,unicode)
+ codes[unicode] = status
+ return status
+ end)
+ extensibles[font] = codes
+ return codes
+end)
+
+local function extensiblecode(family,unicode)
+ return extensibles[getfontoffamily(family or 0)][unicode][1]
+end
+
+-- left : [head] ...
+-- right : ... [head]
+-- horizontal : [head] ... [head]
+--
+-- abs(right["start"] - right["end"]) | right.advance | characters[right.glyph].width
+
+local function horizontalcode(family,unicode)
+ local font = getfontoffamily(family or 0)
+ local data = extensibles[font][unicode]
+ local kind = data[1]
+ local loffset = 0
+ local roffset = 0
+ if kind == e_left then
+ local charlist = data[3].horiz_variants
+ if charlist then
+ local left = charlist[1]
+ loffset = abs((left["start"] or 0) - (left["end"] or 0))
+ end
+ elseif kind == e_right then
+ local charlist = data[3].horiz_variants
+ if charlist then
+ local right = charlist[#charlist]
+ roffset = abs((right["start"] or 0) - (right["end"] or 0))
+ end
+ elseif kind == e_horizontal then
+ local charlist = data[3].horiz_variants
+ if charlist then
+ local left = charlist[1]
+ local right = charlist[#charlist]
+ loffset = abs((left ["start"] or 0) - (left ["end"] or 0))
+ roffset = abs((right["start"] or 0) - (right["end"] or 0))
+ end
+ end
+ return kind, loffset, roffset
+end
+
+mathematics.extensiblecode = extensiblecode
+mathematics.horizontalcode = horizontalcode
+
+interfaces.implement {
+ name = "extensiblecode",
+ arguments = { "integer", "integer" },
+ actions = { extensiblecode, context }
+}
+
+interfaces.implement {
+ name = "horizontalcode",
+ arguments = { "integer", "integer" },
+ actions = function(family,unicode)
+ local kind, loffset, roffset = horizontalcode(family,unicode)
+ texsetdimen("scratchleftoffset", loffset)
+ texsetdimen("scratchrightoffset",roffset)
+ context(kind)
+ end
+}
+
+
+local stack = { }
+
+function mathematics.registerfallbackid(n,id,name)
+ if trace_collecting then
+ report_math("resolved fallback font %i, name %a, id %a, used %a",
+ n,name,id,fontproperties[id].fontname)
+ end
+ stack[#stack][n] = id
+end
+
+interfaces.implement { -- will be shared with text
+ name = "registerfontfallbackid",
+ arguments = { "integer", "integer", "string" },
+ actions = mathematics.registerfallbackid,
+}
+
+function mathematics.resolvefallbacks(target,specification,fallbacks)
+ local definitions = fonts.collections.definitions[fallbacks]
+ if definitions then
+ local size = specification.size -- target.size
+ local list = { }
+ insert(stack,list)
+ context.pushcatcodes("prt") -- context.unprotect()
+ for i=1,#definitions do
+ local definition = definitions[i]
+ local name = definition.font
+ local features = definition.features or ""
+ local size = size * (definition.rscale or 1)
+ context.font_fallbacks_register_math(i,name,features,size)
+ if trace_collecting then
+ report_math("registering fallback font %i, name %a, size %a, features %a",i,name,size,features)
+ end
+ end
+ context.popcatcodes()
+ end
+end
+
+function mathematics.finishfallbacks(target,specification,fallbacks)
+ local list = remove(stack)
+ if list and #list > 0 then
+ local definitions = fonts.collections.definitions[fallbacks]
+ if definitions and #definitions > 0 then
+ if trace_collecting then
+ report_math("adding fallback characters to font %a",specification.hash)
+ end
+ local definedfont = fonts.definers.internal
+ local copiedglyph = fonts.handlers.vf.math.copy_glyph
+ local fonts = target.fonts
+ local size = specification.size -- target.size
+ local characters = target.characters
+ if not fonts then
+ fonts = { }
+ target.fonts = fonts
+ end
+ if #fonts == 0 then
+ fonts[1] = { id = 0, size = size } -- self, will be resolved later
+ end
+ local done = { }
+ for i=1,#definitions do
+ local definition = definitions[i]
+ local name = definition.font
+ local start = definition.start
+ local stop = definition.stop
+ local gaps = definition.gaps
+ local check = definition.check
+ local force = definition.force
+ local rscale = definition.rscale or 1
+ local offset = definition.offset or start
+ local id = list[i]
+ if id then
+ local index = #fonts + 1
+ fonts[index] = { id = id, size = size }
+ local chars = fontchars[id]
+ local function remap(unic,unicode,gap)
+ if check and not chars[unicode] then
+ return
+ end
+ if force or (not done[unic] and not characters[unic]) then
+ if trace_collecting then
+ report_math("replacing math character %C by %C using vector %a and font id %a for %a%s%s",
+ unic,unicode,fallbacks,id,fontproperties[id].fontname,check and ", checked",gap and ", gap plugged")
+ end
+ characters[unic] = copiedglyph(target,characters,chars,unicode,index)
+ done[unic] = true
+ end
+ end
+ local step = offset - start
+ for unicode = start, stop do
+ remap(unicode + step,unicode,false)
+ end
+ if gaps then
+ for unic, unicode in next, gaps do
+ remap(unic,unicode,true)
+ remap(unicode,unicode,true)
+ end
+ end
+ end
+ end
+ elseif trace_collecting then
+ report_math("no fallback characters added to font %a",specification.hash)
+ end
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-ali.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-ali.mkxl
new file mode 100644
index 00000000000..2007ed83ccf
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-ali.mkxl
@@ -0,0 +1,1964 @@
+%D \module
+%D [ file=math-ali,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Math Alignments,
+%D author={Hans Hagen, Taco Hoekwater \& Aditya Mahajan},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Math Alignments}
+
+\unprotect
+
+%D The code here has been moved from other files. Beware: the \MKII\ and \MKIV\ code
+%D is not gathered in files with the same name. In the meantime this code has been
+%D adapted to \MKIV\ but more is possible. The code is somewhat complicated by the
+%D fact that alignments are tricky with rspect to tagging.
+
+% export:
+%
+% alignment : ok
+% cases :
+% matrix : ok
+% substack :
+
+%D \macros
+%D {definemathalignment, setupmathalignment, startmathalignment}
+%D
+%D Modules may provide additional alignment features. The following kind of plain
+%D mechanisms are provided by the core.
+
+\newtoks\t_math_align_a
+\newtoks\t_math_align_b
+\newtoks\t_math_align_c
+
+\newskip\d_math_eqalign_distance
+\newskip\d_math_eqalign_rulethickness
+
+\protected\def\math_eqalign_distance
+ {\relax
+ \ifdim\d_math_eqalign_distance>\zeropoint
+ % \hskip\d_math_eqalign_distance
+ \tabskip\d_math_eqalign_distance
+ \fi
+ \mathalignmentparameter\c!separator
+ \relax}
+
+\mutable\def\displayopenupvalue{.25\bodyfontsize}
+
+\def\math_build_eqalign
+ {\scratchtoks\emptytoks
+ \d_math_eqalign_distance\mathalignmentparameter\c!distance\relax
+ \scratchcounterone\mathalignmentparameter\c!m
+ \scratchcountertwo\mathalignmentparameter\c!n
+ \etoksapp\scratchtoks{\the\t_math_align_a}%
+ \scratchcounter\plusone
+ \dorecurse{\numexpr\scratchcounterone*\scratchcountertwo-\plusone\relax}
+ {\ifnum\scratchcounter=\scratchcountertwo
+ \scratchcounter\plusone
+ \etoksapp\scratchtoks{\math_eqalign_distance}%
+ \etoksapp\scratchtoks{\global\c_math_eqalign_column\zerocount}%
+ \else
+ \advance\scratchcounter\plusone
+ \fi
+ \etoksapp\scratchtoks{\the\t_math_align_b}}%
+ \etoksapp\scratchtoks{\the\t_math_align_c}}
+
+\def\math_math_in_eqalign#1%
+ {\startforceddisplaymath
+ \tabskip\zeropoint
+ \everycr\emptytoks
+ {{}#1{}}%
+ \stopforceddisplaymath}
+
+\def\math_text_in_eqalign#1%
+ {\startimath
+ \tabskip\zeropoint
+ \everycr\emptytoks
+ #1%
+ \stopimath}
+
+\permanent\protected\def\eqalign#1% why no halign here, probably because of displaywidth
+ {\emptyhbox % why no \dontleavehmode
+ \mskip\thinmuskip
+ \vcenter
+ {\math_openup\displayopenupvalue % was: \openup\jot
+ \mathsurround\zeropoint
+ \ialign{%
+ \strut
+ \hfil
+ \startforceddisplaymath{\alignmark\alignmark}\stopforceddisplaymath
+ \aligntab
+ \startforceddisplaymath{{}\alignmark\alignmark{}}\stopforceddisplaymath
+ \hfil\crcr
+ #1\crcr}%
+ }%
+ \mskip\thinmuskip}
+
+% preamble is scanned for tabskips so we need the span to prevent an error message
+
+\setnewconstant\eqalignmode\plusone
+
+% use zeroskipplusfill
+
+\def\math_prepare_r_eqalign_no
+ {\t_math_align_a
+ {\strut
+ \tabskip\zeropoint
+ \alignmark\alignmark % for picking up the number
+ \aligntab
+ \math_first_in_eqalign
+ \hfil
+ \math_left_of_eqalign
+ \span
+ \math_math_in_eqalign{\alignmark\alignmark}%
+ \math_right_of_eqalign
+ \tabskip\zeropoint}%
+ \t_math_align_b
+ {\aligntab
+ \math_next_in_eqalign
+ \math_left_of_eqalign
+ \span
+ \math_math_in_eqalign{\alignmark\alignmark}%
+ \math_right_of_eqalign
+ \tabskip\zeropoint}%
+ \ifnum\mathraggedstatus=\plusone
+ \t_math_align_c
+ {\hfil
+ \aligntab
+ \span
+ \math_text_in_eqalign{\alignmark\alignmark}%
+ \tabskip\zeropoint}%
+ \orelse\ifnum\mathraggedstatus=\plusthree
+ \t_math_align_c
+ {\hfil
+ \tabskip\zeropoint\s!plus 1\s!fill
+ \aligntab
+ \span
+ \math_text_in_eqalign{\alignmark\alignmark}%
+ \tabskip\zeropoint}%
+ \else
+ \t_math_align_c
+ {\hfil
+ \tabskip\centering
+ \aligntab
+ \span
+ \llap{\math_text_in_eqalign{\alignmark\alignmark}}%
+ \tabskip\zeropoint}%
+ \fi
+ \math_build_eqalign
+ \the\mathdisplayaligntweaks
+ \tabskip\centering}
+
+\def\math_prepare_l_eqalign_no % \checkeddisplaymath
+ {\t_math_align_a
+ {\strut
+ \tabskip\zeropoint
+ \alignmark\alignmark % for picking up the number
+ \aligntab
+ \math_first_in_eqalign
+ \hfil
+ \math_left_of_eqalign
+ \span
+ \math_math_in_eqalign{\alignmark\alignmark}%
+ \math_right_of_eqalign
+ \tabskip\zeropoint}%
+ \t_math_align_b
+ {\aligntab
+ \math_next_in_eqalign
+ \math_left_of_eqalign
+ \span
+ \math_math_in_eqalign{\alignmark\alignmark}%
+ \math_right_of_eqalign
+ \tabskip\zeropoint}%
+ \ifnum\mathraggedstatus=\plusone
+ \t_math_align_c
+ {\hfil
+ \aligntab
+ \kern-\displaywidth
+ \span
+ \rlap{\math_text_in_eqalign{\alignmark\alignmark}}%
+ \tabskip\displaywidth}%
+ \orelse\ifnum\mathraggedstatus=\plusthree
+ \t_math_align_c
+ {\hfil
+ \tabskip\zeropoint\s!plus 1\s!fill
+ \aligntab
+ \kern-\displaywidth
+ \span
+ \math_rlap{\math_text_in_eqalign{\alignmark\alignmark}}%
+ \tabskip\displaywidth}%
+ \else
+ \t_math_align_c
+ {\hfil
+ \tabskip\centering
+ \aligntab
+ \kern-\displaywidth
+ \span
+ \rlap{\math_text_in_eqalign{\alignmark\alignmark}}%
+ \tabskip\displaywidth}%
+ \fi
+ \math_build_eqalign
+ \the\mathdisplayaligntweaks
+ \tabskip\centering}
+
+\def\math_halign_checked
+ {\halign \ifcase\eqalignmode \or to \checkeddisplaywidth \fi}
+
+\def\math_both_eqalign_no_normal#1#2%
+ {\ifmmode
+ \the\mathdisplayaligntweaks
+ \vcenter\bgroup
+ \enforced\let\math_finish_eqalign_no\egroup
+ \else
+ \enforced\let\math_finish_eqalign_no\relax
+ \fi
+ #1%
+ \math_halign_checked\expandafter\bgroup\the\scratchtoks\crcr#2\crcr\egroup
+ \math_finish_eqalign_no}
+
+\installcorenamespace {mathalignlocation}
+
+\defcsname\??mathalignlocation\v!top \endcsname{\let\math_alignment_halign_method\halign\tpack}
+\defcsname\??mathalignlocation\v!bottom\endcsname{\let\math_alignment_halign_method\halign\vpack}
+\defcsname\??mathalignlocation\v!center\endcsname{\let\math_alignment_halign_method\halign\vcenter}
+
+\let\math_alignment_halign_method\relax
+
+\def\math_both_eqalign_no_aligned#1%
+ {\let\math_alignment_halign_method\math_halign_checked
+ \ifmmode
+ \the\mathdisplayaligntweaks
+ \global\mathnumberstatus\plusone
+ \ifcase\mathraggedstatus
+ \def\math_finish_eqalign_no{\crcr\egroup}%
+ \else % we're in a mathbox
+ \ifcsname\??mathalignlocation\mathalignmentparameter\c!location\endcsname
+ \lastnamedcs % top|bottom|center as suggested by HM
+ \else
+ \vcenter
+ \fi
+ \bgroup
+ \def\math_finish_eqalign_no{\crcr\egroup\egroup}%
+ \fi
+ \fi
+ #1%
+ \math_alignment_halign_method\expandafter\bgroup\the\scratchtoks\crcr}
+
+\def\math_rlap#1%
+ {\setbox\scratchbox\hbox{#1}%
+ \ifdim\wd\scratchbox>\d_math_number_correction
+ \global\d_math_number_correction\wd\scratchbox
+ \fi
+ \box\scratchbox
+ \global\mathnumberstatus\plustwo}
+
+\def\math_handle_eqalign_no_r_normal {\math_both_eqalign_no_normal \math_prepare_r_eqalign_no}
+\def\math_handle_eqalign_no_l_normal {\math_both_eqalign_no_normal \math_prepare_l_eqalign_no}
+\def\math_handle_eqalign_no_r_aligned{\math_both_eqalign_no_aligned\math_prepare_r_eqalign_no}
+\def\math_handle_eqalign_no_l_aligned{\math_both_eqalign_no_aligned\math_prepare_l_eqalign_no}
+\def\math_finish_eqalign_no {\crcr\egroup}
+
+\aliased\let\reqalignno\relax % just in case someone used that
+\aliased\let\leqalignno\relax % just in case someone used that
+\aliased\let\eqalignno \relax % just in case someone used that
+
+%D Here we implement the user interface part. We start with basic math alignments:
+
+\newcount \c_math_eqalign_column
+\newconditional\c_math_eqalign_first
+
+\newtoks \everymathalignment
+\newtoks \everymathalignmentdone
+
+\permanent\tolerant\protected\def\math_alignment_NN[#1]#*[#2]%
+ {\aligntab
+ \strc_formulas_place_number_nested{#1}{#2}}
+
+\permanent\tolerant\protected\def\math_alignment_NR[#1]#*[#2]%
+ {\aligntab
+ \dostoptagged % finish cell
+ \strc_formulas_place_number_nested{#1}{#2}%
+ \math_number_right_of_eqalign
+ \global\settrue\c_math_eqalign_first
+ \crcr
+ \noalign{\dostoptagged}} % finish row
+
+\permanent\protected\def\math_alignment_NC
+ {\relax
+ \ifconditional\c_math_eqalign_first
+ \ifx\p_math_alignment_number\v!auto
+ \strc_formulas_place_number_nested{+}{}%
+ \fi
+ \global\setfalse\c_math_eqalign_first
+ \fi
+ \math_number_left_of_eqalign
+ \aligntab}
+
+\permanent\protected\def\math_alignment_EQ
+ {\NC=}
+
+\noaligned\tolerant\protected\def\math_common_TB[#1]%
+ {\noalign{\blank[#1]}}
+
+\installmacrostack\NC % maybe more to shared table definitions
+\installmacrostack\NN % maybe more to shared table definitions
+\installmacrostack\EQ % maybe more to shared table definitions
+\installmacrostack\NR % maybe more to shared table definitions
+\installmacrostack\BC % maybe more to shared table definitions
+\installmacrostack\EC % maybe more to shared table definitions
+\installmacrostack\TB % maybe more to shared table definitions
+
+\appendtoks
+ \push_macro_NC
+ \push_macro_NN
+ \push_macro_EQ
+ \push_macro_NR
+ \push_macro_TB
+ \enforced\let\NC\math_alignment_NC
+ \enforced\let\NN\math_alignment_NN
+ \enforced\let\EQ\math_alignment_EQ
+ \enforced\let\NR\math_alignment_NR
+ \enforced\let\TB\math_common_TB
+ \global\settrue\c_math_eqalign_first
+\to \everymathalignment
+
+\appendtoks
+ \pop_macro_TB
+ \pop_macro_NR
+ \pop_macro_EQ
+ \pop_macro_NN
+ \pop_macro_NC
+\to \everymathalignmentdone
+
+\let\math_alignment_snap_start\relax
+\let\math_alignment_snap_stop \relax
+
+% % experimental:
+%
+% \def\math_alignment_snap_start
+% {\ifgridsnapping
+% \edef\p_math_alignment_grid{\mathalignmentparameter\c!grid}%
+% \ifx\p_math_alignment_grid\v!no\else
+% \snaptogrid[\p_math_alignment_grid]\vbox\bgroup
+% \fi
+% \fi}
+%
+% \def\math_alignment_snap_stop
+% {\ifgridsnapping
+% \ifx\p_math_alignment_grid\v!no\else
+% \egroup
+% \fi
+% \fi}
+%
+% % doesn't work well, so:
+
+\let\math_alignment_snap_start\relax
+\let\math_alignment_snap_stop \relax
+
+% end of experimental
+
+\newconditional\c_math_alignment_auto_number
+
+% \begingroup not permitted ($$...assignments...\halign... ).. check in luametatex
+
+\permanent\tolerant\protected\def\math_alignment_start[#1]#*[#2]%
+ {\edef\currentmathalignment{#1}%
+ \ifarguments\or\or
+ \setupmathalignment[#1][#2]% bad! ungrouped
+ \fi
+ \math_alignment_snap_start
+ \the\everymathalignment
+ \c_math_eqalign_column\zerocount
+ \edef\p_math_alignment_number{\mathalignmentparameter\c!number}%
+ \processcommacommand
+ [\mathalignmentparameter\c!align]%
+ {\advance\c_math_eqalign_column\plusone\math_eqalign_set_column}% takes argument
+ \global\c_math_eqalign_column\plusone
+ \dostarttagged\t!math\empty
+ \dostarttagged\t!mtable\currentmathalignment
+ \numberedeqalign}
+
+\def\math_alignment_stop % can be protected
+ {\math_finish_eqalign_no
+ \dostoptagged
+ \dostoptagged
+ \the\everymathalignmentdone
+ \math_alignment_snap_stop}
+
+\installcorenamespace{mathalignment}
+\installcorenamespace{mathalignmentvariant}
+
+\installcommandhandler \??mathalignment {mathalignment} \??mathalignment
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!start\currentmathalignment}{\math_alignment_start[\currentmathalignment]}%
+ \frozen\instance\setvalue {\e!stop \currentmathalignment}{\math_alignment_stop}%
+\to \everydefinemathalignment
+
+\setupmathalignment
+ [\c!n=2,
+ \c!m=1,
+ \c!distance=\emwidth,
+ \c!grid=\v!math]
+
+\definemathalignment[align] % default case (this is what amstex users expect)
+\definemathalignment[\v!mathalignment] % prefered case (this is cleaner, less clashing)
+
+% special case.. in case one mistypes ..
+
+\ifdefined \startalignment
+
+ \pushoverloadmode
+
+ \aliased\let\align_math_normal_start\startalign
+ \aliased\let\align_math_normal_stop \stopalign
+
+ \aliased\let\align_text_normal_start\startalignment
+ \aliased\let\align_text_normal_stop \stopalignment
+
+ \permanent\overloaded\protected\def\startalign
+ {\ifmmode
+ \enforced\let\stopalign\align_math_normal_stop % cannot be a protected def ... lookahead in align
+ \expandafter\align_math_normal_start
+ \orelse\ifinformula
+ \enforced\let\stopalign\align_math_normal_stop
+ \expandafter\align_math_normal_start
+ \else
+ \enforced\let\stopalign\align_text_normal_stop
+ \expandafter\align_text_normal_start
+ \fi}
+
+ \aliased\let\stopalign\relax
+
+ \permanent\overloaded\protected\def\startalignment
+ {\ifmmode
+ \enforced\let\stopalignment\align_math_normal_stop % cannot be a protected def ... lookahead in align
+ \expandafter\align_math_normal_start
+ \orelse\ifinformula
+ \enforced\let\stopalignment\align_math_normal_stop % cannot be a protected def ... lookahead in align
+ \expandafter\align_math_normal_start
+ \else
+ \enforced\let\stopalignment\align_text_normal_stop
+ \expandafter\align_text_normal_start
+ \fi}
+
+ \aliased\let\stopalignment\relax
+
+ \pushoverloadmode
+
+\fi
+
+%
+
+\permanent\protected\def\numberedeqalign
+ {\doifelse{\formulaparameter\c!location}\v!left
+ \math_handle_eqalign_no_l_aligned
+ \math_handle_eqalign_no_r_aligned}
+
+\def\math_first_in_eqalign
+ {\global\c_math_eqalign_column\plusone
+ \dostarttagged\t!mtablerow \empty
+ \dostarttagged\t!mtablecell\empty}
+
+\def\math_next_in_eqalign
+ {\global\advance\c_math_eqalign_column\plusone
+ \dostoptagged % finish cell
+ \dostarttagged\t!mtablecell\empty}
+
+\def\math_left_of_eqalign
+ {\ifcsname\??mathalignmentvariant\number\c_math_eqalign_column\endcsname
+ \ifcase\lastnamedcs \or \relax \or \hfill \or \hfill \fi
+ \fi}
+
+\def\math_right_of_eqalign
+ {\ifcsname\??mathalignmentvariant\number\c_math_eqalign_column\endcsname
+ \ifcase\lastnamedcs \or \hfill \or \relax \or \hfill \fi
+ \fi}
+
+\newconditional\c_math_alignment_local_number % not used but when true puts in front (todo)
+
+\def\math_number_right_of_eqalign
+ {\ifcase\wd\b_strc_formulas_number\else
+ \ifconditional\c_math_alignment_local_number
+ \ifcase\c_strc_math_number_location\or\or
+ \box\b_strc_formulas_number
+ \fi
+ \else
+ \box\b_strc_formulas_number
+ \fi
+ \fi}
+
+\def\math_number_left_of_eqalign
+ {\ifcase\wd\b_strc_formulas_number\else
+ \ifconditional\c_math_alignment_local_number
+ \ifcase\c_strc_math_number_location\or
+ \box\b_strc_formulas_number
+ \fi
+ \fi
+ \fi}
+
+\def\math_eqalign_set_column#1% we could just add to the preamble (as with other alignments)
+ {\expandafter\chardef\csname\??mathalignmentvariant\number\c_math_eqalign_column\expandafter\expandafter\expandafter\endcsname
+ \ifcsname\??mathalignmentvariant#1\endcsname\lastnamedcs\else\zerocount\fi\relax}
+
+\letvalue{\??mathalignmentvariant\v!normal}\zerocount
+\letvalue{\??mathalignmentvariant\v!left }\plusone
+\letvalue{\??mathalignmentvariant\v!right }\plustwo
+\letvalue{\??mathalignmentvariant\v!middle}\plusthree
+
+\permanent\protected\def\math_align_NR_generic[#1][#2]%
+ {\strc_formulas_place_number_nested{#1}{#2}\crcr}
+
+%D \starttyping
+%D \placeformula[eqn0]\startformula \startalign[n=1] a\NR \stopalign \stopformula See \in[eqn0]
+%D \placeformula[eqn1]\startformula \startalign[n=1] a\NR \stopalign \stopformula See \in[eqn1]
+%D \placeformula \startformula \startalign[n=1] a\NR[eqn2] \stopalign \stopformula See \in[eqn2]
+%D \placeformula[eqn3]\startformula \startalign[n=1] a\NR[+] \stopalign \stopformula See \in[eqn3]
+%D \stoptyping
+
+%D \startbuffer
+%D \placeformula \startformula \eqalignno {
+%D a &= b & \formulanumber \cr
+%D c &= d \cr
+%D &= e \cr
+%D &= f & \formulanumber
+%D } \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startalign
+%D \NC a \EQ b \NR[+]
+%D \NC c \EQ d \NR
+%D \NC \EQ f \NR[for:demo-a-1]
+%D \NC \EQ g \NR[for:demo-a-2][a]
+%D \NC \EQ h \NR[for:demo-a-3][b]
+%D \NC \EQ i \NR
+%D \stopalign \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startalign
+%D \NC a \EQ b \NR[+]
+%D \NC c \EQ d \NR
+%D \NC \EQ f \NR
+%D \NC \EQ g \NR
+%D \NC \EQ h \NR
+%D \NC \EQ i \NR[+]
+%D \stopalign \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startalign
+%D \NC a \NC \eq b \NR[+]
+%D \NC c \NC \neq d \NR
+%D \NC \NC \neq f \NR[for:demo-b-1]
+%D \NC \NC \geq g \NR[for:demo-b-2][a]
+%D \NC \NC \leq h \NR[for:demo-b-3][b]
+%D \NC \NC \neq i \NR
+%D \stopalign \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startalign[n=3,align={left,middle,right}]
+%D \NC l \NC = \NC r \NR
+%D \NC left \NC = \NC right \NR
+%D \stopalign \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startalign[n=3,align={right,middle,left}]
+%D \NC l \NC = \NC r \NR
+%D \NC left \NC = \NC right \NR
+%D \stopalign \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startalign[n=3,align={middle,middle,middle}]
+%D \NC l \NC = \NC r \NR
+%D \NC left \NC = \NC right \NR
+%D \stopalign \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula
+%D \startformula
+%D \startalign[n=3,align={middle,middle,middle}]
+%D \NC a \NC = \NC b \NR[+]
+%D \NC 2a \NC = \NC 2b \NR
+%D \stopalign
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula
+%D \startformulas
+%D \setupmathalignment[n=3,align={middle,middle,middle}]%
+%D \startformula
+%D \startalign
+%D \NC a \NC = \NC b \NR[+]
+%D \NC 2a \NC = \NC 2b \NR
+%D \stopalign
+%D \stopformula
+%D \startformula
+%D \startalign
+%D \NC a \NC = \NC b \NR[+]
+%D \NC 2a \NC = \NC 2b \NR
+%D \stopalign
+%D \stopformula
+%D \stopformulas
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula
+%D \startformulas
+%D \dorecurse{5}{\startformula
+%D \startalign[n=3,align={middle,middle,middle}]
+%D \NC a \NC = \NC b \NR[+]
+%D \NC 2a \NC = \NC 2b \NR
+%D \stopalign
+%D \stopformula}
+%D \stopformulas
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {definemathcases, setupmathcases, startmathcases}
+%D
+%D Another wish \unknown
+
+\installcorenamespace{mathcases}
+
+\installcommandhandler \??mathcases {mathcases} \??mathcases
+
+\setupmathcases
+ [\c!distance=\emwidth,
+ \c!strut=\v!yes, % new
+ \c!numberdistance=2.5\emwidth,
+ \c!left={\left\{\mskip\thinmuskip},
+ \c!right={\right.}]
+
+\appendtoks
+ \frozen\instance\protected\edefcsname\e!start\currentmathcases\endcsname{\math_cases_start[\currentmathcases]}%
+ \frozen\instance \defcsname \e!stop \currentmathcases\endcsname{\math_cases_stop}%
+\to \everydefinemathcases
+
+\permanent\protected\def\math_cases_NC_zero
+ {\math_cases_NC}
+
+\permanent\protected\def\math_cases_MC_zero
+ {\math_cases_NC
+ \ifmmode\else
+ \startimath
+ \enforced\let\math_cases_end_math\stopimath
+ \fi}
+
+\let\math_cases_end_math\relax
+
+\permanent\protected\def\math_cases_NR_zero
+ {\unskip
+ \math_cases_end_math
+ \aligntab
+ \global\enforced\let\math_cases_NC\math_cases_NC_first
+ \dodirectdoubleempty\math_cases_NR}
+
+\permanent\protected\def\math_cases_NC_first
+ {\global\enforced\let\math_cases_NC\math_cases_NC_second}
+
+\permanent\protected\def\math_cases_NC_second
+ {\math_cases_end_math\aligntab}
+
+\let\math_cases_NR\math_align_NR_generic
+
+\installmacrostack\math_cases_NC
+
+\let\math_cases_strut\relax
+
+\permanent\tolerant\protected\def\math_cases_start[#1]#*[#2]%
+ {\begingroup
+ \edef\currentmathcases{#1}%
+ \ifarguments\or\or
+ \setupcurrentmathcases[#2]%
+ \fi
+ \edef\p_strut{\mathcasesparameter\c!strut}%
+ \ifx\p_strut\v!yes
+ \enforced\let\math_cases_strut\strut
+ \else
+ \enforced\let\math_cases_strut\relax
+ \fi
+ \mathcasesparameter\c!left
+ \vcenter\bgroup
+ \push_macro_math_cases_NC
+ % \enforced\let\endmath\relax
+ \enforced\let\NC\math_cases_NC_zero
+ \enforced\let\MC\math_cases_MC_zero
+ \enforced\let\NR\math_cases_NR_zero
+ \enforced\let\TB\math_common_TB
+ \global\enforced\let\math_cases_NC\math_cases_NC_first
+ \normalbaselines
+ \mathsurround\zeropoint
+ \everycr\emptytoks
+ \tabskip\zeropoint
+ \global\c_math_eqalign_column\plusone
+ \halign\bgroup
+ \startimath
+ \mathcasesparameter\c!style
+ \alignmark\alignmark
+ \stopimath
+ \hfil
+ \aligntab
+ \hskip\mathcasesparameter\c!distance\relax
+ \pop_macro_math_cases_NC
+ \math_cases_strut % looks better
+ \alignmark\alignmark
+ \hfil
+ \aligntab
+ \hskip\mathcasesparameter\c!numberdistance\relax
+ % \let\formuladistance\!!zeropoint
+ \span\math_text_in_eqalign{\alignmark\alignmark}%
+ \crcr} % todo: number
+
+% When we have just protected we get an extra row but we can no flag
+% this as a proper alignment related command which means that uit gets
+% expanded.
+
+\noaligned\permanent\protected\def\math_cases_stop
+ {\crcr
+ \egroup
+ \pop_macro_math_cases_NC
+ \egroup
+ \mathcasesparameter\c!right
+ \endgroup}
+
+\definemathcases[cases]
+\definemathcases[\v!mathcases]
+
+%D \startbuffer
+%D \placeformula \startformula \startcases
+%D \NC 2 \NC $ y > 0 $ \NR
+%D \NC 7 \NC $ x = 7 $ \NR[+]
+%D \NC 4 \NC otherwise \NR
+%D \stopcases \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula x \startcases
+%D \NC 2 \NC $ y > 0 $ \NR[+]
+%D \NC 7 \NC $ x = 7 $ \NR
+%D \NC 4 \NC otherwise \NR
+%D \stopcases \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula \startcases
+%D \NC 2 \NC $ y > 0 $ \NR
+%D \NC 7 \NC $ x = 7 $ \NR
+%D \NC 4 \NC otherwise \NR
+%D \stopcases \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \placeformula \startformula x \startcases
+%D \NC 2 \NC $ y > 0 $ \NR
+%D \NC 7 \NC $ x = 7 $ \NR
+%D \NC 4 \NC otherwise \NR
+%D \stopcases \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {definemathmatrix, setupmathmatrix, startmathmatrix}
+%D
+%D Yet another one \unknown. This time we implement the lot a bit
+%D different which is a side effect of getting the tagging right. In
+%D retrospect the main alignment could be done this way but \unknown
+
+\installcorenamespace{mathmatrix}
+
+\installcommandhandler \??mathmatrix {mathmatrix} \??mathmatrix
+
+\setupmathmatrix
+ [\c!distance=\emwidth,
+ \c!left=,
+ \c!right=,
+ \c!align=\v!middle,
+ \c!rulecolor=,
+ \c!rulethickness=\linewidth]
+
+\appendtoks
+ \frozen\instance\protected\edefcsname\e!start\currentmathmatrix\endcsname{\math_matrix_start[\currentmathmatrix]}%
+ % \noaligned\protected should work here:
+ \frozen\instance \defcsname \e!stop \currentmathmatrix\endcsname{\math_matrix_stop}% no u else lookahead problem
+\to \everydefinemathmatrix
+
+\def\math_matrix_start_table
+ {\global\c_math_eqalign_column\zerocount
+ \dostarttagged\t!math\empty
+ \dostarttagged\t!mtable\empty}
+
+\def\math_matrix_stop_table
+ {\dostoptagged
+ \dostoptagged}
+
+% \def\math_matrix_start_row
+% {\noalign{\global\c_math_eqalign_column\zerocount}%
+% \dostarttagged\t!mtablerow\empty}
+%
+% \def\math_matrix_stop_row
+% {\dostoptagged}
+
+\def\math_matrix_start_row
+ {\beginlocalcontrol
+ \global\c_math_eqalign_column\zerocount
+ \dostarttagged\t!mtablerow\empty
+ \endlocalcontrol}
+
+\def\math_matrix_stop_row
+ {\beginlocalcontrol
+ \dostoptagged
+ \endlocalcontrol}
+
+\protected\def\math_matrix_start_cell
+ {\dostarttagged\t!mtablecell\empty
+ \hss
+ \math_left_of_eqalign
+ \startimath
+ \math_matrix_set_style
+ \tabskip\zeropoint
+ \everycr\emptytoks}
+
+\protected\def\math_matrix_stop_cell
+ {\stopimath
+ \math_right_of_eqalign
+ \hss
+ \dostoptagged}
+
+% We could construct a preamble with alignment and such embedded but the number
+% of matrices with many rows is normally so low that it doesn't pay of at all.
+
+\protected\def\math_matrix_distance
+ {\relax
+ \ifdim\d_math_eqalign_distance>\zeropoint
+ \hskip\d_math_eqalign_distance
+ \fi
+ \relax}
+
+\def\math_matrix_preamble
+ {\math_matrix_strut
+ \global\advance\c_math_eqalign_column\plusone
+ \math_matrix_start_cell
+ \alignmark\alignmark
+ \math_matrix_stop_cell
+ \aligntab
+ \aligntab
+ \math_matrix_distance
+ \global\advance\c_math_eqalign_column\plusone
+ \math_matrix_start_cell
+ \alignmark\alignmark
+ \math_matrix_stop_cell}
+
+\newconditional\c_math_matrix_first
+
+\permanent\protected\def\math_matrix_NR
+ {\aligntab\omit
+ \math_matrix_stop_row
+ \math_matrix_pickup
+ \crcr
+ \math_matrix_start_row}
+
+\permanent\protected\def\math_matrix_NC
+ {\ifconditional\c_math_matrix_first
+ \expandafter\math_matrix_NC_yes
+ \else
+ \expandafter\math_matrix_NC_nop
+ \fi}
+
+\permanent\protected\def\math_matrix_pickup{\global\settrue \c_math_matrix_first}
+\permanent\protected\def\math_matrix_NC_yes{\global\setfalse\c_math_matrix_first}
+\permanent\protected\def\math_matrix_NC_nop{\aligntab} % avoids lookahead
+
+% \def\math_matrix_stop_wrapup
+% {\crcr
+% \strut
+% \crcr
+% \noalign{\vskip-\struthtdp}}
+
+\def\math_matrix_check_rule_step#1%
+ {\doifelsenumber{#1}
+ {\scratchdimen#1\d_math_eqalign_rulethickness}
+ {\edef\p_rulecolor{#1}}}
+
+\def\math_matrix_check_rule[#1]%
+ {\d_math_eqalign_rulethickness\mathmatrixparameter\c!rulethickness\relax
+ \scratchdimen\d_math_eqalign_rulethickness
+ \edef\p_rulecolor{\mathmatrixparameter\c!rulecolor}%
+ \iftok{#1}\emptytoks\else
+ \rawprocesscommalist[#1]\math_matrix_check_rule_step
+ \fi
+ \ifempty\p_rulecolor\else
+ \dousecolorparameter\p_rulecolor
+ \fi}
+
+\noaligned\permanent\tolerant\protected\def\math_matrix_HL[#1]#*%
+ {\noalign\bgroup
+ \math_matrix_check_rule[#1]%
+ \divide\scratchdimen\plustwo
+ \autorule\s!height\scratchdimen\s!depth\scratchdimen\relax
+ \egroup}
+
+\permanent\tolerant\protected\def\math_matrix_VL[#1]#*%
+ {\NC
+ \math_matrix_check_rule[#1]%
+ \divide\d_math_eqalign_distance\plustwo
+ \hskip-\d_math_eqalign_distance
+ \autorule\s!width\scratchdimen\relax
+ \hskip-\d_math_eqalign_distance
+ \NC}
+
+\newtoks\everymathmatrix
+
+\appendtoks
+ \enforced\let\NR\math_matrix_NR
+ \enforced\let\NC\math_matrix_NC
+ \enforced\let\MC\math_matrix_NC
+ \enforced\let\HL\math_matrix_HL
+ \enforced\let\VL\math_matrix_VL
+ \enforced\let\TB\math_common_TB
+\to \everymathmatrix
+
+\def\math_matrix_start_processing
+ {\dontleavehmode
+ \bgroup
+ \tabskip\zeropoint
+ \math_matrix_pickup
+ \the\everymathmatrix
+ %
+ % \enforced\let\endmath\relax
+ %
+ \setbox\nextbox\vbox\bgroup
+ \math_matrix_start_table
+ \halign \bgroup
+ % preamble
+ \span\math_matrix_preamble
+ % done
+ \crcr
+ \math_matrix_start_row}
+
+\def\math_matrix_stop_processing
+ {%\math_matrix_stop_wrapup % optional
+ \math_matrix_stop_row
+ \egroup
+ \math_matrix_stop_table
+ \egroup
+ \mathmatrixleft
+ \math_matrix_finish_nextbox
+ \mathmatrixright
+ \egroup}
+
+\let\math_matrix_strut \strut
+\let\math_matrix_set_style\relax
+
+\def\math_matrix_check_settings
+ {\edef\p_strut{\mathmatrixparameter\c!strut}%
+ \ifx\p_strut\v!no
+ \enforced\let\math_matrix_strut\relax
+ \else
+ \enforced\let\math_matrix_strut\strut
+ \ifx\p_strut\v!yes\else
+ \spacing\p_strut
+ \fi
+ \fi
+ \d_math_eqalign_distance\mathmatrixparameter\c!distance\relax
+ % \d_math_eqalign_rulethickness\mathmatrixparameter\c!rulethickness\relax
+ % \edef\p_rulecolor{\mathmatrixparameter\c!rulecolor}
+ \edef\math_matrix_set_style{\mathmatrixparameter\c!style}}
+
+\def\math_matrix_set_defaults
+ {\normalbaselines % hm, spacing ?
+ \mathsurround\zeropoint
+ \tabskip\zeropoint}
+
+\def\math_matrix_set_columns_step
+ {\advance\c_math_eqalign_column\plusone
+ %\c_math_matrix_columns\c_math_eqalign_column
+ \math_eqalign_set_column}
+
+\def\math_matrix_set_columns
+ {\c_math_eqalign_column\zerocount
+ \rawprocesscommacommand[\mathmatrixparameter\c!align]\math_matrix_set_columns_step}
+
+\newcount\c_math_eqalign_column_saved
+
+% \installglobalmacrostack\c_math_matrix_first
+
+\tolerant\protected\def\math_matrix_start[#1]#*[#2]%
+ {\begingroup
+ \globalpushmacro\c_math_matrix_first % hm, does that work?
+ \c_math_eqalign_column_saved\c_math_eqalign_column
+ \globalpushmacro\c_math_eqalign_first
+ \edef\currentmathmatrix{#1}%
+ \setupcurrentmathmatrix[#2]%
+ \math_matrix_check_settings
+ \math_matrix_set_defaults
+ \math_matrix_set_columns
+ \math_matrix_start_processing}
+
+\def\math_matrix_stop
+ {\math_matrix_stop_processing
+ \globalpushmacro\c_math_eqalign_first
+ \global\c_math_eqalign_column\c_math_eqalign_column_saved
+ \globalpopmacro\c_math_matrix_first
+ \endgroup}
+
+% vcenter:
+%
+% delta = (height(v) + depth(v))/2
+% axis = math_axis_size(cur_size)
+% height(v) = delta + axis
+% depth(v) = delta - axis
+
+\installcorenamespace{mathmatrixalignlocation}
+
+\mutable\let\mathmatrixleft \empty % experimental hook
+\mutable\let\mathmatrixright\empty % experimental hook
+
+\setvalue{\??mathmatrixalignlocation\v!top }{\raise\dimexpr(\nextboxdp-\nextboxht)/2 +\mathaxisheight\mathstyle\relax}
+\setvalue{\??mathmatrixalignlocation\v!high }{\raise\dimexpr(\nextboxdp-\nextboxht)/2\relax}
+\setvalue{\??mathmatrixalignlocation\v!center}{\relax}
+\setvalue{\??mathmatrixalignlocation\v!lohi} {\relax}
+\setvalue{\??mathmatrixalignlocation\v!normal}{\relax}
+\setvalue{\??mathmatrixalignlocation\v!bottom}{\lower\dimexpr(\nextboxdp-\nextboxht)/2 +\mathaxisheight\mathstyle\relax}
+\setvalue{\??mathmatrixalignlocation\v!low }{\lower\dimexpr(\nextboxdp-\nextboxht)/2\relax}
+
+\def\math_matrix_finish_nextbox
+ {\begincsname\??mathmatrixalignlocation\mathmatrixparameter\c!location\endcsname\hbox\bgroup
+ \normalstartimath
+ \mathmatrixparameter\c!left
+ \vcenter{\box\nextbox}%
+ \mathmatrixparameter\c!right
+ \normalstopimath
+ \egroup}
+
+\definemathmatrix[matrix]
+\definemathmatrix[\v!mathmatrix]
+
+%D \startbuffer
+%D \placeformula \startformula[-] \startmatrix
+%D \NC 1 \NC x \NC a \NR
+%D \NC 2 \NC y \NC b \NR
+%D \NC 3 \NC z \NC c \NR
+%D \stopmatrix \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \definemathmatrix[bmatrix][left={\left[\mskip\thinmuskip},right={\mskip\thinmuskip\right]},strut=1.25]
+%D
+%D \startbuffer
+%D \placeformula \startformula[-] \startbmatrix
+%D \NC 1 \NC x \NC a \NR
+%D \NC 2 \NC y \NC b \NR
+%D \NC 3 \NC z \NC c \NR
+%D \stopbmatrix \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Taco added some code (dedicated to Aditya Mahajan) that gives more
+%D control over aligments:
+
+%D \startbuffer
+%D \startformula
+%D \startmatrix
+%D \NC a + x \NC = \NC a + d \NR
+%D \NC y \NC = \NC d \NR
+%D \stopmatrix
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \startbuffer
+%D \startformula
+%D \startmatrix [distance=3pt,align={right,left}]
+%D \NC a + x \NC = a + d \NR
+%D \NC y \NC = d \NR
+%D \stopmatrix
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \startbuffer
+%D \startformula
+%D \startmatrix [left=\left(,right=\right)]
+%D \NC a + x \NR
+%D \NC y \NR
+%D \stopmatrix
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D A bit more complex code:
+%D
+%D \startbuffer
+%D \startformula
+%D \text{Let }{\cal R} = \bigcup_{P_{X_1},P_{X_2}}
+%D \left\{ (R_1, R_2) :
+%D \startmatrix[distance=1em,align={left,left,right}]
+%D \NC R_1 \NC < I(X_1 ; Y \mid X_2) \NC R_1 \NR
+%D \NC \hfill Q_2 \NC < I(X_2 ; Y \mid X_1) \NC R_2 \NR
+%D \NC R_1 + R_2 \NC < I(X_1 ; Y) \NC R_1 + R_2 \NR
+%D \stopmatrix
+%D \right\}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {startmatrices}
+%D
+%D Just a handy keystroke safer:
+
+\permanent\protected\def\startmatrices
+ {\begingroup
+ \setupmathmatrix}
+
+\permanent\protected\def\stopmatrices
+ {\endgroup}
+
+%D \startbuffer
+%D \startformula
+%D \startmatrix[left={\left(},right={\right)}]
+%D \NC A \NC B \NR \NC C \NC D \NR
+%D \stopmatrix
+%D =
+%D \startmatrix[left={\left(},right={\right)},location=low]
+%D \NC A \NC B \NR \NC C \NC D \NR
+%D \stopmatrix
+%D =
+%D \startmatrix[left={\left(},right={\right)},location=high]
+%D \NC A \NC B \NR \NC C \NC D \NR
+%D \stopmatrix
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \startformula
+%D \startmatrices[left={\left(},right={\right)}]
+%D \startmatrix
+%D \NC A \NC B \NR \NC C \NC D \NR
+%D \stopmatrix
+%D =
+%D \startmatrix[location=bottom]
+%D \NC A \NC B \NR \NC C \NC D \NR
+%D \stopmatrix
+%D =
+%D \startmatrix[location=top]
+%D \NC A \NC B \NR \NC C \NC D \NR
+%D \stopmatrix
+%D \stopmatrices
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer % does not run well: \getbuffer
+
+%D Handy for the \type {m-matrix} module:
+
+\permanent\tolerant\protected\def\startnamedmatrix[#1]#*[#2]%
+ {\begingroup
+ \edef\currentmathmatrix{#1}%
+ \setupcurrentmathmatrix[#2]%
+ \math_matrix_start[\currentmathmatrix]}
+
+\noaligned\permanent\protected\def\stopnamedmatrix
+ {\math_matrix_stop
+ \endgroup}
+
+%D The following code is derived from Aditya's simplematrix prototype but
+%D adapted to regular mathmatrices (which saves some code so it can go into
+%D the core):
+
+\def\math_matrix_simple_row#1%
+ {\rawprocesscommalist[#1]\math_matrix_simple_col
+ \toksapp\scratchtoks{\NR}}
+
+\def\math_matrix_simple_col#1%
+ {\toksapp\scratchtoks{\NC#1}}
+
+\permanent\tolerant\protected\def\math_matrix_simple[#1]#*[#2]#:#3%
+ {\begingroup
+ \edef\currentmathmatrix{#1}%
+ \setupcurrentmathmatrix[#2]%
+ \scratchtoks\emptytoks
+ \processlist[];\math_matrix_simple_row[#3]%
+ \math_matrix_start[\currentmathmatrix]%
+ \the\scratchtoks
+ \math_matrix_stop
+ \endgroup}
+
+%D We hook it into the normal mathmatrix code:
+
+\appendtoks
+ \edef\p_simplecommand{\mathmatrixparameter\c!simplecommand}%
+ \ifempty\p_simplecommand\else
+ \frozen\instance\setuevalue{\p_simplecommand}{\math_matrix_simple[\currentmathmatrix]}%
+ \fi
+\to \everydefinemathmatrix
+
+%D And predefine some matrices:
+
+\definemathmatrix[matrix:parentheses][\c!left={\left(\mskip\thinmuskip},\c!right={\mskip\thinmuskip\right)},\c!align=\v!middle]
+\definemathmatrix[matrix:brackets] [\c!left={\left[\mskip\thinmuskip},\c!right={\mskip\thinmuskip\right]},\c!align=\v!middle]
+\definemathmatrix[matrix:bars] [\c!left={\left|\mskip\thinmuskip},\c!right={\mskip\thinmuskip\right|},\c!align=\v!middle]
+
+\definemathmatrix[thematrix][matrix:parentheses][\c!simplecommand=thematrix]
+
+%D \startbuffer
+%D \startformula
+%D \thematrix{1,2,3,4;5,6,7,8;9,10,11,12}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \startformula
+%D \startthematrix
+%D \NC 1\NC 2\NC 3\NC 4\NR
+%D \NC 5\NC 6\NC 7\NC 8\NR
+%D \NC 9\NC10\NC11\NC12\NR
+%D \stopthematrix
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {startintertext}
+%D
+%D Preliminary feature:
+%D
+%D {\em example code}
+%D
+%D The intertext commands have to be expandable (in aligment lookahead) so
+%D we cannot use \type {\protected}.
+
+\permanent\def\startintertext#1\stopintertext
+ {\noalign{\math_intertext{#1}}}
+
+\permanent\let\stopintertext\relax
+
+\permanent\def\intertext#1%
+ {\noalign{\math_intertext{#1}}}
+
+\protected\def\math_intertext#1%
+ {\penalty\postdisplaypenalty
+ \afterdisplayspace
+ \vbox{\forgetall\noindent#1\par}%
+ \penalty\predisplaypenalty
+ \beforedisplayspace}
+
+%D \macros
+%D {substack}
+%D
+%D Preliminary code:
+%D
+%D \startbuffer
+%D \startformula
+%D \sum_{%
+%D \startsubstack
+%D i = 1 \NR
+%D i \neq n \NR
+%D i \neq m
+%D \stopsubstack
+%D }a_i
+%D \stopformula
+%D \stopbuffer
+%D
+%D \getbuffer which was typed as \typebuffer
+%D
+%D Notice that these macros give the correct spacing for
+%D subscripts. Compare for example
+%D
+%D \startbuffer
+%D \startformula
+%D \sum_{\startsubstack a \NR b \NR \stopsubstack}
+%D \text{ and }
+%D \sum_{\scriptstyle a \atop \scriptstyle}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer which gives \getbuffer
+
+% no tagging yet : how is it supposed to be coded?
+
+\permanent\protected\def\startsubstack
+ {\begingroup
+ \vcenter\bgroup
+ \baselineskip\mathstacktotal
+ \lineskip\mathstackvgap
+ \lineskiplimit\lineskip
+ \mathsurround\zeropoint
+ \everycr\emptytoks
+ \enforced\let\NC\relax
+ \enforced\let\MC\relax
+ \enforced\let\NR\crcr
+ \halign\bgroup\hfil\normalstartimath\scriptstyle\alignmark\alignmark\normalstopimath\hfil\crcr}
+
+\noaligned\permanent\protected\def\stopsubstack
+ {\crcr
+ \egroup
+ \egroup
+ \endgroup}
+
+% %D \macros
+% %D {bordermatrix}
+% %D
+% %D In \PLAIN\ \TEX\ the width of a parenthesis is stored in
+% %D the \DIMENSION\ \type{\mathparentwd}. This value is derived from
+% %D the width of \type{\tenrm B}, so let's take care of it now:
+%
+% \ifdefined\mathparentwd \else \newdimen\mathparentwd \fi
+%
+% \let\normalbordermatrix\bordermatrix % move that code to here instead
+%
+% \protected\def\bordermatrix
+% {\begingroup
+% \setbox\scratchbox\hbox{\mr\char"239C}%
+% \global\mathparentwd\wd\scratchbox
+% \endgroup
+% \normalbordermatrix}
+%
+% \def\bordermatrix
+% {\begingroup
+% \mr
+% \global\mathparentwd\fontcharwd\font"239C\relax
+% \endgroup
+% \normalbordermatrix}
+
+%D \macros{overset, underset}
+%D
+%D The macros \type{\overset} and \type{\underset} are provided by
+%D \AMS\ packages in \LATEX. These macro allows you to place a symbol
+%D above or below another symbol, irrespective of whether the other
+%D symbol is a relation or something else, and without influencing the
+%D spacing. For most cases there is a better way to do such things
+%D (declaring a math command with limop option, or using accents), but
+%D occasionally these macros can be useful, for example:
+%D
+%D \startbuffer
+%D \startformula
+%D \overset{*}{X} \underset{*}{X}
+%D \stopformula
+%D \stopbuffer
+%D \typebuffer \getbuffer
+%D
+%D Use these macros sparingly. Remember, \TEX\ was designed for
+%D mathematics, so there is usually a proper method for typesetting
+%D common math notation.
+%D
+%D These macros are a cleaner version of \type {\binrel@} and
+%D \type {\binrel@@} macros in \AMSTEX\ packages.
+
+\def\math_binrel_apply#1%
+ {\begingroup
+ \setbox\scratchbox\hbox
+ {\thinmuskip 0mu
+ \medmuskip -1mu
+ \thickmuskip -1mu
+ \setbox\scratchbox\hbox{\normalstartimath#1\mathsurround\zeropoint\normalstopimath}%
+ \kern-\wd\scratchbox
+ \normalstartimath{}#1{}\mathsurround\zeropoint\normalstopimath}%
+ \ifdim\wd\scratchbox<\zeropoint
+ \endgroup
+ \expandafter\mathbin
+ \orelse\ifdim\wd\scratchbox>\zeropoint
+ \endgroup
+ \doubleexpandafter\mathrel
+ \else
+ \endgroup
+ \doubleexpandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\overset#1#2%
+ {\math_binrel_apply{#2}{\mathop{\kern\zeropoint#2}\limits\normalsuperscript{#1}}}
+
+\permanent\protected\def\underset#1#2%
+ {\math_binrel_apply{#2}{\mathop{\kern\zeropoint#2}\limits\normalsubscript {#1}}}
+
+%D The following code comes from \type {math-str.mkiv}.
+%D
+%D Here we implement a basic math alignment mechanism. Numbers are also handled. The macros
+%D \type {\startinnermath} and \type {\stopinnermath} can be overloaded in specialized
+%D modules.
+
+\installcorenamespace{mathinnerstart}
+\installcorenamespace{mathinnerstop}
+
+% \protected\def\startinnermath{\csname\??mathinnerstart\formulaparameter\c!align\endcsname}
+% \protected\def\stopinnermath {\csname\??mathinnerstop \formulaparameter\c!align\endcsname}
+
+\permanent\protected\def\startinnermath{\expandnamespaceparameter\??mathinnerstart\formulaparameter\c!align\v!normal}
+\permanent\protected\def\stopinnermath {\expandnamespaceparameter\??mathinnerstop \formulaparameter\c!align\v!normal}
+
+\permanent\protected\def\mathinnerstrut
+ {\doif{\formulaparameter\c!strut}\v!yes\strut}
+
+\permanent\protected\def\defineinnermathhandler#1#2#3%
+ {\defcsname\??mathinnerstart#1\endcsname{#2}%
+ \defcsname\??mathinnerstop #1\endcsname{#3}}
+
+\installtextracker
+ {formulas.boxes}
+ {\enforced\let\math_hbox\ruledhbox}
+ {\enforced\let\math_hbox\hbox}
+
+\let\math_hbox\hbox
+
+\newconstant\mathraggedstatus % normal left center right
+\newconstant\mathnumberstatus % nothing normal shift_right
+
+\newdimen\d_math_number_correction
+
+\def\math_box_llapped_math_no
+ {\ifcase\mathraggedstatus\or
+ \box\b_strc_formulas_number
+ \or
+ \llap{\box\b_strc_formulas_number}%
+ \or
+ \llap{\box\b_strc_formulas_number}%
+ \fi}
+
+\def\math_box_rlapped_math_no
+ {\ifcase\mathraggedstatus\or
+ \rlap{\box\b_strc_formulas_number}%
+ \or
+ \rlap{\box\b_strc_formulas_number}%
+ \or
+ \box\b_strc_formulas_number
+ \fi}
+
+\newconditional\c_strc_math_has_number
+\newconditional\c_strc_math_display_overflow
+\newconstant \c_strc_math_number_location
+\newdimen \d_strc_math_number_width
+\newdimen \d_strc_math_display_width
+\newbox \b_strc_math_display
+\newconstant \c_strc_formulas_frame_mode
+\newdimen \d_strc_math_indent
+\newconditional\c_strc_math_indent
+
+\newdimen\d_strc_math_framed_width
+
+\setvalue{\??formulaoption\v!frame}%
+ {\edef\p_frame{\formulaparameter\c!frame}%
+ \ifx\p_frame\v!number
+ \c_strc_formulas_frame_mode\plustwo % inside frame
+ \else
+ \c_strc_formulas_frame_mode\plusone % outside frame
+ \fi}
+
+% mode: 0=no frame | 1=number inside frame | 2=number outside frame
+
+\def\strc_math_flush_aligned
+ {\ifcase\c_strc_math_vertical
+ \ifcase\mathraggedstatus\or\hfill\or\hfill\fi
+ \box\b_strc_math_display
+ \ifcase\mathraggedstatus\or\or\hfill\or\hfill\fi
+ \else
+ \ifconditional\c_strc_math_indent
+ \ifzeropt\d_strc_math_indent\else
+ \hangafter\plusone
+ \hangindent\d_strc_math_indent
+ \fi
+ \fi
+ \edef\p_interlinespace{\formulaparameter\c!interlinespace}%
+ \ifempty\p_interlinespace\else\baselineskip\p_interlinespace\fi
+ \global\d_strc_math_indent\zeropoint
+ \ifcase\mathraggedstatus\or\raggedleft\or\raggedcenter\or\raggedright\fi
+ \unhbox\b_strc_math_display
+ \fi}
+
+\def\strc_math_flush_box_normal
+ {\ifcase\c_strc_math_vertical
+ \hbox to \displaywidth\bgroup
+ \strc_math_flush_aligned
+ \egroup
+ \else
+ \strc_math_flush_aligned
+ \fi}
+
+\def\strc_math_flush_box_framed_common
+ {\d_strc_math_framed_width\displaywidth
+ \setformulaframedparameter\c!align{\formulaparameter\c!align}%
+ \letformulaframedparameter\c!strut\v!no
+ \d_framed_formula\ht\b_strc_math_display
+ \ifcase\mathraggedstatus\or\hfill\or\hfill \fi
+ \inheritedformulaframedframed{\box\b_strc_math_display}%
+ \ifcase\mathraggedstatus\or \or\hfill\or\hfill\fi}
+
+\def\strc_math_flush_box_framed_display
+ {\let\currentformulaframed\currentformula
+ \letformulaframedparameter\c!location\v!formula
+ \setformulaframedparameter\c!width{\d_strc_math_framed_width}%
+ \strc_math_flush_box_framed_common}
+
+\def\strc_math_flush_box_framed_fit_inline
+ {\let\currentformulaframed\currentformula
+ \letformulaframedparameter\c!location\empty
+ \letformulaframedparameter\c!width\v!fit
+ \strc_math_flush_box_framed_common}
+
+\def\strc_math_flush_box_framed_fit_display
+ {\let\currentformulaframed\currentformula
+ \letformulaframedparameter\c!location\v!formula
+ \letformulaframedparameter\c!width\v!fit
+ \strc_math_flush_box_framed_common}
+
+% combiners
+
+\def\strc_math_flush_box
+ {\ifcase\c_strc_formulas_frame_mode
+ \strc_math_flush_box_normal
+ \else
+ \strc_math_flush_box_framed_display
+ \fi}
+
+\def\strc_math_number_right_normal
+ {\strc_math_flush_aligned
+ \hss % hss makes room for number
+ \math_box_llapped_math_no}
+
+\def\strc_math_number_left_normal
+ {\math_box_rlapped_math_no
+ \strc_math_flush_aligned
+ \hss} % hss makes room for number
+
+\def\strc_math_number_right_normal_outside
+ {\ifconditional\c_strc_formulas_tight
+ \strc_math_flush_box_framed_fit_display
+ \else
+ \strc_math_flush_box_framed_display
+ \fi
+ \hss % hss makes room for number
+ \math_box_llapped_math_no}
+
+\def\strc_math_number_left_normal_outside
+ {\math_box_rlapped_math_no
+ \hss % hss makes room for number
+ \ifconditional\c_strc_formulas_tight
+ \strc_math_flush_box_framed_fit_display
+ \else
+ \strc_math_flush_box_framed_display
+ \fi}
+
+\def\strc_math_number_right_normal_inside
+ {\setbox\b_strc_math_display\hpack to \dimexpr\displaywidth-\d_framed_locator_lo-\d_framed_locator_ro\relax\bgroup
+ \strc_math_flush_aligned
+ \hss
+ \math_box_llapped_math_no
+ \egroup
+ \strc_math_flush_box_framed_fit_inline}
+
+\def\strc_math_number_left_normal_inside
+ {\setbox\b_strc_math_display\hpack to \dimexpr\displaywidth-\d_framed_locator_lo-\d_framed_locator_ro\relax\bgroup
+ \math_box_rlapped_math_no
+ \hss
+ \strc_math_flush_aligned
+ \egroup
+ \strc_math_flush_box_framed_fit_inline}
+
+\def\strc_math_number_right_overflow
+ {\vpack\bgroup
+ \strc_math_flush_box
+ \par
+ \hpack to \displaywidth\bgroup
+ \hss
+ \math_box_llapped_math_no
+ \egroup
+ \egroup}
+
+\def\strc_math_number_left_overflow
+ {\vpack\bgroup
+ \hpack to \displaywidth\bgroup
+ \math_box_rlapped_math_no
+ \hss
+ \egroup
+ \strc_math_flush_box
+ \egroup}
+
+\def\strc_math_number_right_overflow_outside
+ {\vpack\bgroup
+ \strc_math_flush_box_framed_fit_inline
+ %\hskip\zeropoint % nicely breaks the line without introducing funny vertical spacing ... why o why
+ \hpack to \displaywidth\bgroup
+ \hss
+ \math_box_llapped_math_no
+ \egroup
+ \egroup}
+
+\def\strc_math_number_left_overflow_outside
+ {\vpack\bgroup
+ \hpack to \dimexpr\displaywidth-\d_framed_locator_lo\relax\bgroup
+ \math_box_rlapped_math_no
+ \hss
+ \egroup
+ \hskip\zeropoint % nicely breaks the line without introducing funny vertical spacing ... why o why
+ \strc_math_flush_box_framed_fit_inline
+ \egroup}
+
+\def\strc_math_number_right_overflow_inside
+ {\setbox\b_strc_math_display\vpack\bgroup
+ \box\b_strc_math_display
+ \hpack to \displaywidth\bgroup
+ \hss
+ \math_box_llapped_math_no
+ \hskip\d_framed_locator_ro
+ \egroup
+ \egroup
+ \strc_math_flush_box_framed_fit_inline}
+
+\def\strc_math_number_left_overflow_inside
+ {\setbox\b_strc_math_display\vpack\bgroup
+ \hpack to \displaywidth\bgroup
+ % \hskip\d_framed_locator_lo
+ \math_box_rlapped_math_no
+ \hss
+ \egroup
+ \box\b_strc_math_display
+ \egroup
+ \strc_math_flush_box_framed_fit_inline}
+
+% checkers
+
+\def\strc_math_number_check
+ {\d_strc_math_display_width\wd\b_strc_math_display
+ \ifconditional\c_strc_formulas_tight
+ \ifdim\d_strc_math_display_width>\displaywidth
+ \settrue\c_strc_math_display_overflow
+ \else
+ \displaywidth\d_strc_math_display_width
+ \setfalse\c_strc_math_display_overflow
+ \fi
+ \else
+ \ifdim\d_strc_math_display_width>\displaywidth
+ \settrue\c_strc_math_display_overflow
+ \else
+ \setfalse\c_strc_math_display_overflow
+ \fi
+ \fi}
+
+\def\strc_math_number_check_outside
+ {\d_strc_math_display_width\naturalwd\b_strc_math_display
+ \ifdim\dimexpr\d_strc_math_display_width+\d_framed_locator_lo+\d_framed_locator_ro\relax>\displaywidth
+ \settrue\c_strc_math_display_overflow
+ \else
+ \setfalse\c_strc_math_display_overflow
+ \fi
+ % still ok?
+ \ifnum\mathraggedstatus=\plustwo
+ \d_strc_math_framed_width\dimexpr\displaywidth-2\wd\b_strc_formulas_number\relax
+ \else
+ \d_strc_math_framed_width\dimexpr\displaywidth- \wd\b_strc_formulas_number\relax
+ \fi}
+
+\let\strc_math_number_check_inside\strc_math_number_check_outside
+
+% offsets
+
+\def\strc_math_number_check_offsets
+ {\begingroup
+ \setbox\scratchbox\hbox
+ {\inheritedformulaframedframed
+ {\pack_framed_locator_set_lo\pack_framed_locator_set_ro}}%
+ \endgroup}
+
+% tracing
+
+\def\strc_math_traced_state_yes
+ {\llap{\setbox\scratchbox\hbox{\infofont
+ \ifcase\mathraggedstatus unset\or right\or middle\or left\fi
+ \space
+ \ifcase\c_strc_formulas_frame_mode no\or out\or in\fi
+ \space
+ \ifconditional\c_strc_math_display_overflow overflow\else fit\fi
+ \quad}\ht\scratchbox\zeropoint\dp\scratchbox\zeropoint\box\scratchbox}}
+
+\let\strc_math_traced_state\relax
+
+\installtextracker
+ {formulas.framed}
+ {\let\strc_math_traced_state\strc_math_traced_state_yes}
+ {\let\strc_math_traced_state\relax}
+
+% packaging
+
+\protected\def\strc_math_box_start#1%
+ {\hsize\displaywidth % \checkeddisplaymath
+ \global\mathnumberstatus\plusone
+ \mathraggedstatus#1\relax
+ %
+ \global\d_math_number_correction\zeropoint
+ %
+ \edef\p_location{\formulaparameter\c!location}%
+ \useformulacolorparameter\c!color
+ \c_strc_math_number_location\ifx\p_location\v!left\plusone\orelse\ifx\p_location\v!right\plustwo\else\zerocount\fi
+ %
+ %\strc_formulas_place_number % not here as we can have inner alignment numbers
+ \dontcomplain
+ \setbox\b_strc_math_display\math_hbox\bgroup % \checkeddisplaymath
+ \mathinnerstrut
+ \startforceddisplaymath}
+
+\def\strc_math_flush_number_no
+ {\ifcase\c_strc_math_vertical
+ \ifconditional\c_strc_math_display_overflow
+ \ifcase\c_strc_formulas_frame_mode
+ \strc_math_flush_box_normal
+ \else
+ \strc_math_flush_box_framed_fit_inline
+ \fi
+ \else
+ \ifcase\c_strc_formulas_frame_mode
+ %\ifconditional\c_strc_formulas_tight
+ % \strc_math_flush_box_normal
+ %\else
+ \strc_math_flush_box_normal
+ %\fi
+ \else
+ \ifconditional\c_strc_formulas_tight
+ \strc_math_flush_box_framed_fit_inline
+ \else
+ \strc_math_flush_box_framed_display
+ \fi
+ \fi
+ \fi
+ \else
+ \strc_math_flush_box
+ \fi}
+
+\def\strc_math_flush_number_left
+ {\ifcase\c_strc_math_vertical
+ \ifconditional\c_strc_math_display_overflow
+ \ifcase\c_strc_formulas_frame_mode
+ \strc_math_number_left_overflow
+ \or
+ \strc_math_number_left_overflow_outside
+ \or
+ \strc_math_number_left_overflow_inside
+ \fi
+ \else
+ \ifcase\c_strc_formulas_frame_mode
+ \strc_math_number_left_normal
+ \or
+ \strc_math_number_left_normal_outside
+ \or
+ \strc_math_number_left_normal_inside
+ \fi
+ \fi
+ \else
+ \box\b_strc_formulas_number
+ \hfill
+ \strc_math_flush_aligned
+ \fi}
+
+\def\strc_math_flush_number_right
+ {\ifcase\c_strc_math_vertical
+ \ifconditional\c_strc_math_display_overflow
+ \ifcase\c_strc_formulas_frame_mode
+ \strc_math_number_right_overflow
+ \or
+ \strc_math_number_right_overflow_outside
+ \or
+ \strc_math_number_right_overflow_inside
+ \fi
+ \else
+ \ifcase\c_strc_formulas_frame_mode
+ \strc_math_number_right_normal
+ \or
+ \strc_math_number_right_normal_outside
+ \or
+ \strc_math_number_right_normal_inside
+ \fi
+ \fi
+ \else
+ \strc_math_flush_aligned
+ \hfill
+ \box\b_strc_formulas_number
+ \fi}
+
+\protected\def\strc_math_box_stop
+ {\stopforceddisplaymath
+ \egroup
+ % check number
+ \d_strc_math_number_width\wd\b_strc_formulas_number
+ %
+ \ifcase\mathnumberstatus
+ \setfalse\c_strc_math_has_number
+ \or\ifzeropt\d_strc_math_number_width
+ \setfalse\c_strc_math_has_number
+ \else
+ \settrue\c_strc_math_has_number
+ \fi\fi
+ % preroll left and right offsets
+ \ifcase\c_strc_formulas_frame_mode
+ % no frame
+ \else
+ \strc_math_number_check_offsets
+ \fi
+ \ifcase\c_strc_formulas_frame_mode
+ \strc_math_number_check
+ \or
+ \strc_math_number_check_outside
+ \else
+ \strc_math_number_check_inside
+ \fi
+ \noindent % \noindentation % not \dontleavehmode
+ \hskip\d_strc_formulas_display_margin_left % was kern but that doesn't indent
+ \strc_math_traced_state
+ \ifcase\c_strc_math_vertical
+ \hbox to \displaywidth \bgroup
+ \or
+ \vbox \bgroup \hsize\displaywidth
+ \or
+ \bgroup \hsize\displaywidth
+ \fi
+ \ifcase\mathnumberstatus
+ \strc_math_flush_box
+ \or % status 1
+ \ifcase\c_strc_math_number_location
+ \strc_math_flush_box
+ \or % number left
+ \ifzeropt\wd\b_strc_formulas_number
+ \strc_math_flush_number_no
+ \else
+ \strc_math_flush_number_left
+ \fi
+ \else % number right
+ \ifzeropt\wd\b_strc_formulas_number
+ \strc_math_flush_number_no
+ \else
+ \strc_math_flush_number_right
+ \fi
+ \fi
+ \or % status 2
+ \hskip\d_math_number_correction % probably no longer used
+ \strc_math_flush_box
+ \hss
+ \else
+ \strc_math_flush_box
+ \fi
+ \ifcase\c_strc_math_vertical
+ \or
+ \or
+ \par
+ \fi
+ \egroup}
+
+\defineinnermathhandler\v!left {\strc_math_box_start\plusone }{\strc_math_box_stop}
+\defineinnermathhandler\v!middle {\strc_math_box_start\plustwo }{\strc_math_box_stop}
+\defineinnermathhandler\v!right {\strc_math_box_start\plusthree}{\strc_math_box_stop}
+\defineinnermathhandler\v!flushleft {\strc_math_box_start\plusthree}{\strc_math_box_stop}
+\defineinnermathhandler\v!center {\strc_math_box_start\plustwo }{\strc_math_box_stop}
+\defineinnermathhandler\v!flushright{\strc_math_box_start\plusone }{\strc_math_box_stop}
+\defineinnermathhandler\v!normal {\strc_math_box_start\plustwo }{\strc_math_box_stop}
+
+%D Some inline math tweak.
+
+\appendtoks
+ \ifcase\mathnestinglevel\or
+ % 4=disable 6=only when no spaces
+ \mathsurroundskip\mathematicsparameter\c!textdistance\relax
+ \ifzeropt\mathsurroundskip
+ \ifzeropt\gluestretch\mathsurroundskip
+ \ifzeropt\glueshrink\mathsurroundskip
+ \mathsurroundmode\plussix
+ \else
+ \mathsurroundskip\zeropoint
+ \mathsurroundmode\plusfour
+ \fi
+ \else
+ \mathsurroundmode\plussix
+ \fi
+ \else
+ \mathsurroundmode\plussix
+ \fi
+ \else
+ \mathsurroundmode\plusfour
+ \mathsurroundskip\zeropoint
+ \fi
+\to \everymathematics
+
+\setupmathematics
+ [\c!textdistance=\zeropoint]
+
+%D This is an experiment. No fancy spacing and alignments here. If we ever
+%D go that route it might result in incompatible rendering.
+
+\permanent\protected\def\startsplitformula
+ {\ifhmode
+ \par
+ \fi
+ \begingroup
+ \beforedisplayspace
+ % subset of \everydisplay:
+ \c_attr_mathmode\plusone
+ \settrue \indisplaymath
+ % end of subset
+ \informulatrue}
+
+\permanent\protected\def\stopsplitformula
+ {\afterdisplayspace
+ \endgroup}
+
+\protect \endinput
+
+% \placeformula \startformula[-] \startmatrix
+% \NC 1 \NC x \NC a \NR
+% \NC 2 \NC y \NC b \NR
+% \NC 3 \NC z \NC c \NR
+% \stopmatrix \stopformula
+
+% \definemathmatrix[bordermatrix][left={\left[\mskip\thinmuskip},right={\mskip\thinmuskip\right]}]
+
+% \placeformula \startformula[-] \startbordermatrix
+% \NC 1 \NC x \NC a \NR
+% \NC 2 \NC y \NC b \NR
+% \NC 3 \NC z \NC c \NR
+% \stopbordermatrix \stopformula
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-def.mkxl
new file mode 100644
index 00000000000..1f3da649d1d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-def.mkxl
@@ -0,0 +1,172 @@
+ %D \module
+%D [ file=math-tex,
+%D version=2001.04.12,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Definitions,
+%D author={Hans Hagen, Taco Hoekwater \& Aditya Mahajan},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Definitions}
+
+\unprotect
+
+\pushoverloadmode % cap etc
+
+\startluacode
+ mathematics.define(\number\defaultmathfamily)
+ -- mathematics.xml.registerentities()
+\stopluacode
+
+\popoverloadmode
+
+\activatemathcharacters
+
+%D The \mfunction macro is an alternative for \hbox with a controlable font
+%D switch.
+
+\definemathcommand [arccos] [nolop] {\mfunctionlabeltext{arccos}}
+\definemathcommand [arcsin] [nolop] {\mfunctionlabeltext{arcsin}}
+\definemathcommand [arctan] [nolop] {\mfunctionlabeltext{arctan}}
+\definemathcommand [arccosh] [nolop] {\mfunctionlabeltext{arccosh}}
+\definemathcommand [arcsinh] [nolop] {\mfunctionlabeltext{arcsinh}}
+\definemathcommand [arctanh] [nolop] {\mfunctionlabeltext{arctanh}}
+\definemathcommand [acos] [nolop] {\mfunctionlabeltext{acos}}
+\definemathcommand [asin] [nolop] {\mfunctionlabeltext{asin}}
+\definemathcommand [atan] [nolop] {\mfunctionlabeltext{atan}}
+\definemathcommand [arg] [nolop] {\mfunctionlabeltext{arg}}
+\definemathcommand [cosh] [nolop] {\mfunctionlabeltext{cosh}}
+\definemathcommand [cos] [nolop] {\mfunctionlabeltext{cos}}
+\definemathcommand [coth] [nolop] {\mfunctionlabeltext{coth}}
+\definemathcommand [cot] [nolop] {\mfunctionlabeltext{cot}}
+\definemathcommand [csc] [nolop] {\mfunctionlabeltext{csc}}
+\definemathcommand [deg] [nolop] {\mfunctionlabeltext{deg}}
+\definemathcommand [det] [limop] {\mfunctionlabeltext{det}}
+\definemathcommand [dim] [nolop] {\mfunctionlabeltext{dim}}
+\definemathcommand [exp] [nolop] {\mfunctionlabeltext{exp}}
+\definemathcommand [gcd] [limop] {\mfunctionlabeltext{gcd}}
+\definemathcommand [hom] [nolop] {\mfunctionlabeltext{hom}}
+\definemathcommand [inf] [limop] {\mfunctionlabeltext{inf}}
+\definemathcommand [injlim] [limop] {\mfunctionlabeltext{injlim}}
+\definemathcommand [ker] [nolop] {\mfunctionlabeltext{ker}}
+\definemathcommand [lg] [nolop] {\mfunctionlabeltext{lg}}
+\definemathcommand [liminf] [limop] {\mfunctionlabeltext{liminf}}
+\definemathcommand [limsup] [limop] {\mfunctionlabeltext{limsup}}
+\definemathcommand [lim] [limop] {\mfunctionlabeltext{lim}}
+\definemathcommand [ln] [nolop] {\mfunctionlabeltext{ln}}
+\definemathcommand [log] [nolop] {\mfunctionlabeltext{log}}
+\definemathcommand [median] [limop] {\mfunctionlabeltext{median}}
+\definemathcommand [max] [limop] {\mfunctionlabeltext{max}}
+\definemathcommand [min] [limop] {\mfunctionlabeltext{min}}
+\definemathcommand [mod] [limop] {\mfunctionlabeltext{mod}}
+%definemathcommand [div] [limop] {\mfunctionlabeltext{div}} % overloads \div symbol
+\definemathcommand [projlim] [limop] {\mfunctionlabeltext{projlim}}
+\definemathcommand [Pr] [limop] {\mfunctionlabeltext{Pr}}
+\definemathcommand [sec] [nolop] {\mfunctionlabeltext{sec}}
+\definemathcommand [sinh] [nolop] {\mfunctionlabeltext{sinh}}
+\definemathcommand [sin] [nolop] {\mfunctionlabeltext{sin}}
+\definemathcommand [sup] [limop] {\mfunctionlabeltext{sup}}
+\definemathcommand [tanh] [nolop] {\mfunctionlabeltext{tanh}}
+\definemathcommand [tan] [nolop] {\mfunctionlabeltext{tan}}
+\definemathcommand [diff] {\mfunctionlabeltext{diff}}
+
+\aliased\let\normalmatharg\arg % todo: maybe automatically
+
+%D This needs checking:
+
+\permanent\protected\def\setoperatorlimits#1#2% operator limits
+ {\savenormalmeaning{#1}%
+ %frozen\protected\expandafter\def\expandafter#1\expandafter{\csname normal\csstring#1\endcsname#2}}
+ \frozen\protected\edef#1{\expandafter\noexpand\csname\s!normal\csstring#1\endcsname\noexpand#2}}
+
+\pushoverloadmode
+
+\setoperatorlimits \int \intlimits
+\setoperatorlimits \iint \intlimits
+\setoperatorlimits \iiint \intlimits
+\setoperatorlimits \oint \intlimits
+\setoperatorlimits \oiint \intlimits
+\setoperatorlimits \oiiint \intlimits
+\setoperatorlimits \intclockwise \intlimits
+\setoperatorlimits \ointclockwise \intlimits
+\setoperatorlimits \ointctrclockwise \intlimits
+
+\popoverloadmode
+
+%D \macros
+%D {centercolon, colonminus, minuscolon, colonequals, equalscolon,
+%D colonapprox, approxcolon, colonsim, simcolon, coloncolon,
+%D coloncolonminus, minuscoloncolon, coloncolonequals,
+%D equalscoloncolon, coloncolonapprox, approxcoloncolon,
+%D colonsim, simcoloncolon}
+%D
+%D The following colon related definitions are provided by Aditya
+%D Mahajan who derived them from \type {mathtools.sty} and \type
+%D {colonequals.sty}. This will be redone as part of the overhaul
+%D and font updates.
+%D
+%D In $a := b$ the colon is not vertically centered with the equal
+%D to. Also the distance between colon and equal to is a bit large.
+%D So, we define a vertically centered colon \tex {centercolon} and
+%D a few macros for colon and double colon relation symbols.
+%D
+%D \startlines
+%D \formula {A \centercolon B}
+%D \formula {A \colonminus B}
+%D \formula {A \minuscolon B}
+%D \formula {A \colonequals B}
+%D \formula {A \equalscolon B}
+%D \formula {A \colonapprox B}
+%D \formula {A \approxcolon B}
+%D \formula {A \colonsim B}
+%D \formula {A \simcolon B}
+%D \formula {A \coloncolon B}
+%D \formula {A \coloncolonminus B}
+%D \formula {A \minuscoloncolon B}
+%D \formula {A \coloncolonequals B}
+%D \formula {A \equalscoloncolon B}
+%D \formula {A \coloncolonapprox B}
+%D \formula {A \approxcoloncolon B}
+%D \formula {A \colonsim B}
+%D \formula {A \simcoloncolon B}
+%D \stoplines
+%D
+%D The next macros take care of the space between the colon and the
+%D relation symbol.
+
+\pushoverloadmode
+
+\definemathcommand [colonsep] {\mkern-1.2mu}
+\definemathcommand [doublecolonsep] {\mkern-0.9mu}
+
+\definemathcommand [centercolon] [rel] {\mathstylevcenteredhbox\colon}
+\definemathcommand [colonminus] [rel] {\centercolon\colonsep\mathrel{-}}
+%definemathcommand [minuscolon] [rel] {\mathrel{-}\colonsep\centercolon} % native char
+%definemathcommand [colonequals] [rel] {\centercolon\colonsep=} % native char
+%definemathcommand [equalscolon] [rel] {=\centercolon\colonsep} % native char
+\definemathcommand [colonapprox] [rel] {\centercolon\colonsep\approx}
+\definemathcommand [approxcolon] [rel] {\approx\centercolon\colonsep}
+\definemathcommand [colonsim] [rel] {\centercolon\colonsep\sim}
+\definemathcommand [simcolon] [rel] {\sim\centercolon\colonsep}
+
+\definemathcommand [coloncolon] [rel] {\centercolon\doublecolonsep\centercolon}
+\definemathcommand [coloncolonminus] [rel] {\coloncolon\colonsep\mathrel{-}}
+\definemathcommand [minuscoloncolon] [rel] {\mathrel{-}\colonsep\coloncolon}
+\definemathcommand [coloncolonequals] [rel] {\coloncolon\colonsep=} % native char
+\definemathcommand [equalscoloncolon] [rel] {=\coloncolon\colonsep}
+\definemathcommand [coloncolonapprox] [rel] {\coloncolon\colonsep\approx}
+\definemathcommand [approxcoloncolon] [rel] {\approx\coloncolon\colonsep}
+\definemathcommand [coloncolonsim] [rel] {\coloncolon\colonsep\sim}
+\definemathcommand [simcoloncolon] [rel] {\sim\coloncolon\colonsep}
+
+\popoverloadmode
+
+% \appendtoks
+% \def\over{\primitive\over}%
+% \to \everymathematics
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-del.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-del.mkxl
new file mode 100644
index 00000000000..07e6d794874
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-del.mkxl
@@ -0,0 +1,164 @@
+%D \module
+%D [ file=math-del,
+%D version=2007.07.19,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Delimiters,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Delimiters}
+
+\unprotect
+
+%D Old code that will be redone:
+%D
+%D \macros
+%D {checkdelimiters, fakeleftdelimiter, fakerightdelimiter}
+%D
+%D Handy for non matching situations (as with mathml):
+%D
+%D \starttyping
+%D \checkdelimiters{... bla bla ...}
+%D \fakeleftdelimiter
+%D ... bla bla ...
+%D \fakerightdelimiter
+%D \stoptyping
+
+% Actually we can do better now with lua hacks.
+
+\newcount\c_math_delimiter_nesting
+
+\permanent\protected\def\leftfakedelimiter {\advance\c_math_delimiter_nesting\minusone\gobbleoneargument}
+\permanent\protected\def\rightfakedelimiter{\advance\c_math_delimiter_nesting\plusone \gobbleoneargument}
+
+\permanent\protected\def\checkdelimiters#1%
+ {\c_math_delimiter_nesting\zerocount
+ \setbox\scratchbox\hbox\bgroup
+ \let\left \leftfakedelimiter
+ \let\right\rightfakedelimiter
+ \normalstartimath#1\expandafter\normalstopimath\expandafter
+ \egroup
+ \expandafter\c_math_delimiter_nesting\the\c_math_delimiter_nesting\relax}
+
+\permanent\protected\def\fakeleftdelimiter {\ifnum\c_math_delimiter_nesting>\zerocount\left .\fi}
+\permanent\protected\def\fakerightdelimiter{\ifnum\c_math_delimiter_nesting<\zerocount\right.\fi}
+
+%D The following macros are used in the MathML interpreter, so there is a good
+%D change of them never being documented for other usage.
+
+\aliased\let\normalordelimiter\secondoftwoarguments
+\aliased\let\normalorfiller \firstoftwoarguments
+
+\permanent\protected\def\enabledelimiter {\enforced\let\normalordelimiter\secondoftwoarguments}
+\permanent\protected\def\disabledelimiter{\enforced\let\normalordelimiter\firstoftwoarguments}
+
+\permanent\protected\def\enablefiller {\enforced\let\normalorfiller\secondoftwoarguments}
+\permanent\protected\def\disablefiller {\enforced\let\normalorfiller\firstoftwoarguments}
+
+\permanent\protected\def\mathopnolimits#1{\mathop{\mathrm#1}\nolimits} % was \rm, which follows text fonts (used in mml parser, check!)
+\permanent\protected\def\mathopdolimits#1{\mathop{\mathrm#1}} % was \rm, which follows text fonts (used in mml parser, check!)
+
+% A new experiment:
+
+% Hm, we already have \definemathextensible so we need to preserve that one. We now
+% use \definemathdelimiter.
+
+\installcorenamespace {mathdelimiter}
+
+\installcommandhandler \??mathdelimiter {mathdelimiter} \??mathdelimiter
+
+\aliased\let\setupmathdelimiters\setupmathdelimiter
+
+\setupmathdelimiters
+ [\c!symbol=0,
+ \c!define=\v!yes,
+ \c!factor=1.5,
+ \c!axis=\v!yes,
+ % \c!exact=\v!yes
+ \c!height=\exheight,
+ \c!depth=\exheight]
+
+\appendtoks
+ \ifcstok{\mathdelimiterparameter\c!define}\v!yes
+ \frozen\instance\setuevalue\currentmathdelimiter{\mathdelimiter[\currentmathdelimiter]}%
+ \fi
+\to \everydefinemathdelimiter
+
+\permanent\tolerant\protected\def\mathdelimiter[#1]#*[#2]%
+ {\mathop
+ {\edef\currentmathdelimiter{#1}%
+ \iftok{#2}\emptytoks
+ \edef\p_factor{\mathdelimiterparameter\c!factor}%
+ \orelse\ifhastok={#2}%
+ \setupcurrentmathdelimiter[#2]%
+ \edef\p_factor{\mathdelimiterparameter\c!factor}%
+ \else
+ \edef\p_factor{#2}%
+ \fi
+ \edef\p_exact{\mathdelimiterparameter\c!exact}%
+ \edef\p_axis {\mathdelimiterparameter\c!axis}%
+ \edef\p_leftoffset{\mathdelimiterparameter\c!leftoffset}%
+ \edef\p_rightoffset{\mathdelimiterparameter\c!rightoffset}%
+ \ifempty\p_leftoffset\else
+ \mskip\p_leftoffset
+ \fi
+ \Uvextensible
+ \ifx\p_exact\v!yes exact \fi
+ \ifx\p_axis \v!yes axis \fi
+ height \p_factor\dimexpr\mathdelimiterparameter\c!height\relax
+ depth \p_factor\dimexpr\mathdelimiterparameter\c!depth \relax
+ \Udelimiter\zerocount\zerocount\mathdelimiterparameter\c!symbol
+ \relax
+ \ifempty\p_rightoffset\else
+ \mskip\p_rightoffset
+ \fi}}
+
+\definemathdelimiter[integral][\c!symbol="222B]
+
+% \setupmathdelimiter[integral][rightoffset=-3mu,exact=yes,factor=2]
+%
+% \let\inlineint \int
+% \let\displayint\integral
+%
+% \protected\def\int{\ifmmode\inlineordisplaymath\inlineint\displayint\else\normalint\fi}
+%
+% \startlines
+% \ruledhbox{$\integral f\frac{1}{2}$}
+% \ruledhbox{$\integral[factor=1] f\frac{1}{2}$}
+% \ruledhbox{$\integral[factor=3] f\frac{1}{2}$}
+% \ruledhbox{$\int f\frac{1}{2}$}
+% \stoplines
+
+\permanent\protected\def\autointegral#1#2#3%
+ {\ifmmode
+ \setbox\nextbox\mathstylehbox{#3}%
+ \scratchdimen\ifdim\nextboxht>\nextboxdp\nextboxht\else\nextboxdp\fi
+ \mathlimop{%
+ \Uvextensible
+ height \scratchdimen
+ depth \scratchdimen
+ exact%
+ axis%
+ \Udelimiter \plusfour \zerocount "222B%
+ }%
+ \limits % nolimits needs more work: kerning and so
+ \normalsuperscript{#1}%
+ \normalsubscript{#2}%
+ \box\nextbox
+ \else
+ \char"222B\relax
+ \fi}
+
+% \startformula
+% a =
+% \autointegral{t}{b}1 +
+% \autointegral{t}{b}{\frac{\frac{3}{4}}{\frac{1}{2}}} +
+% \autointegral{t}{b}{\frac{\frac{\frac{\frac{1}{2}}{2}}{2}}{2}}
+% \stopformula
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-dis.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-dis.mkxl
new file mode 100644
index 00000000000..5b70970f3cc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-dis.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=math-ali,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Display,
+%D author={Hans Hagen, Taco Hoekwater \& Aditya Mahajan},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Display}
+
+\unprotect
+
+% display spacing code will move here
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-fen.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-fen.mkxl
new file mode 100644
index 00000000000..8aa0e62a8cb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-fen.mkxl
@@ -0,0 +1,841 @@
+%D \module
+%D [ file=math-fen,
+%D version=2012.02.18,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Fences,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Fences}
+
+\unprotect
+
+% maybe always "method=auto" for:
+%
+% \switchtobodyfont[cambria]
+% \ruledhbox{$f(x)$}
+% \ruledhbox{\mathdelimitersmode6$f\left(x\right)$}
+% \ruledhbox{\mathdelimitersmode7$f\left(x\right)$}
+% \ruledhbox{$f\left(\frac{1}{x}\right)$}
+
+% todo: mathstyle
+
+% \definemathfence [fancybracket] [bracket] [command=yes,color=blue]
+%
+% test $|x|$ test \par
+% test $||x||$ test (okay) \par
+% test $a\left|\frac{1}{b}\right|c$ test \par
+% test $a\left||\frac{1}{b}\right||c$ test (not okay) \par
+%
+% \setupmathfences [color=red]
+%
+% test $a\fenced[bar]{\frac{1}{b}}c$ test \par
+% test $a\fenced[doublebar]{\frac{1}{b}}c$ test \par
+% test $a\fenced[bracket]{\frac{1}{b}}c$ test \par
+% test $a\fancybracket{\frac{1}{b}}c$ test \par
+
+\installcorenamespace{mathfences}
+
+\installcommandhandler \??mathfences {mathfence} \??mathfences
+
+\aliased\let\setupmathfences\setupmathfence
+
+\setupmathfences
+ [\c!method=, % maybe always \v!auto
+ \c!left=,
+ \c!right=,
+ \c!middle=,
+ \c!mathstyle=,
+ \c!color=,
+ \c!command=,
+ \c!mathclass=,
+ \c!factor=\v!auto]
+
+\appendtoks
+ \edef\p_command{\mathfenceparameter\c!command}%
+ \ifx\p_command\v!yes
+ \setuevalue\currentmathfence{\math_fenced_fenced[\currentmathfence]}%
+ \fi
+\to \everydefinemathfence
+
+% we need the direct use of \Udelimiter because of { etc
+
+%D So we can do:
+%D
+%D \starttyping
+%D $ a + \fenced[bar][size=1] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=2] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=3] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=4] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=big] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=Big] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=bigg]{\frac {b} {c}} + d $
+%D $ a + \fenced[bar][size=Bigg]{\frac {b} {c}} + d $
+%D $ a + \fenced[bar][factor=1] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][factor=2] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar][factor=4] {\frac {b} {c}} + d $
+%D $ a + \fenced[bar] {\frac {b} {c}} + d $
+%D \stoptyping
+
+\newconditional\c_math_fenced_mirror \settrue \c_math_fenced_mirror
+\newconditional\c_math_fenced_sized \setfalse\c_math_fenced_sized
+
+\installcorenamespace {mathfencesize}
+
+\setvalue{\??mathfencesize big}{1}
+\setvalue{\??mathfencesize Big}{2}
+\setvalue{\??mathfencesize bigg}{3}
+\setvalue{\??mathfencesize Bigg}{4}
+
+\def\math_fenced_force_size#1#2%
+ {\c_attr_mathsize\numexpr
+ #1*\plushundred
+ +\ifcsname\??mathfencesize#2\endcsname\lastnamedcs\else#2\fi
+ \relax}
+
+\protected\def\math_fenced_inject#1#2#3#4%
+ {\ifx#1\empty
+ #2.%
+ \else
+ \edef\p_mathclass{\mathfenceparameter\c!mathclass}%
+ \ifconditional\c_math_fenced_sized
+ \let\p_factor\v!fixed
+ \else
+ \edef\p_factor{\mathfenceparameter\c!factor}%
+ \fi
+ \ifempty\p_factor
+ \ifempty\p_mathclass
+ #2%
+ \else
+ #3%
+ \s!class\p_mathclass
+ \fi
+ \orelse\ifx\p_factor\v!auto
+ \ifempty\p_mathclass
+ #2%
+ \else
+ #3%
+ \s!class\p_mathclass
+ \fi
+ \orelse\ifx\p_factor\v!none
+ #3%
+ \s!height\zeropoint % redundant with "exact"
+ \s!depth\zeropoint % redundant with "exact"
+ \ifempty\p_mathclass\else
+ \s!class\p_mathclass
+ \fi
+ \s!axis
+ \s!exact % new
+ % #2%
+ \else
+ %\scratchdimen\ifx\p_factor\v!fixed\scaledpoint\else\dimexpr\p_factor\bodyfontsize/2\relax\fi
+ \scratchdimen\ifx\p_factor\v!fixed\zeropoint\else\dimexpr\p_factor\bodyfontsize/2\relax\fi
+ #3%
+ \s!height\scratchdimen
+ \s!depth\scratchdimen
+ \ifempty\p_mathclass\else
+ \s!class\p_mathclass
+ \fi
+ \s!axis
+ \s!exact
+ \fi
+ \Udelimiter#4\fam#1\relax
+ \fi}
+
+\permanent\def\math_fenced_left
+ {\edef\p_left
+ {\ifconditional\c_math_fenced_mirror
+ \ifconditional\c_math_right_to_left
+ \mathfenceparameter\c!right
+ \else
+ \mathfenceparameter\c!left
+ \fi
+ \else
+ \mathfenceparameter\c!left
+ \fi}%
+ \math_fenced_color_push
+ \math_fenced_inject\p_left\normalleft\Uleft\plusfour
+ \math_fenced_color_pop}
+
+\permanent\def\math_fenced_middle
+ {\edef\p_middle{\mathfenceparameter\c!middle}%
+ \mskip\thinmuskip
+ \math_fenced_color_push
+ \math_fenced_inject\p_middle\normalmiddle\Umiddle\plusfour
+ \math_fenced_color_pop
+ \mskip\thinmuskip}
+
+\permanent\def\math_fenced_right
+ {\edef\p_right
+ {\ifconditional\c_math_fenced_mirror
+ \ifconditional\c_math_right_to_left
+ \mathfenceparameter\c!left
+ \else
+ \mathfenceparameter\c!right
+ \fi
+ \else
+ \mathfenceparameter\c!right
+ \fi}%
+ \math_fenced_color_push
+ \math_fenced_inject\p_right\normalright\Uright\plusfive
+ \math_fenced_color_pop}
+
+\def\math_fenced_color_do_push{\pushcolor[\p_math_fenced_color]}
+\let\math_fenced_color_do_pop \popcolor
+
+\let\math_fenced_color_push\donothing
+\let\math_fenced_color_pop \donothing
+
+\aliased\let\fence \relax
+\aliased\let\fenced\relax
+
+\newcount\c_math_fenced_nesting
+
+\protected\def\math_fenced_fenced_common
+ {\startusemathstyleparameter\mathfenceparameter
+ \enforced\let\fence\math_fenced_middle
+ \edef\p_math_fenced_color{\mathfenceparameter\c!color}%
+ \ifempty\p_math_fenced_color
+ \let\math_fenced_color_push\donothing
+ \let\math_fenced_color_pop \donothing
+ \else
+ \let\math_fenced_color_push\math_fenced_color_do_push
+ \let\math_fenced_color_pop \math_fenced_color_do_pop
+ \fi}
+
+\protected\def\math_fenced_fenced_start#1%
+ {\advance\c_math_fenced_nesting\plusone
+ \begingroup
+ \edef\currentmathfence{#1}%
+ \math_fenced_fenced_common
+ \math_fenced_left}
+
+\protected\def\math_fenced_fenced_stop#1%
+ {\edef\currentmathfence{#1}%
+ \math_fenced_right
+ \stopusemathstyleparameter
+ \endgroup
+ \advance\c_math_fenced_nesting\minusone}
+
+\tolerant\protected\def\math_fenced_fenced[#1]#*[#2]%
+ {\advance\c_math_fenced_nesting\plusone
+ \begingroup
+ \edef\currentmathfence{#1}%
+% under test:
+\c_attr_mathsize\attributeunsetvalue
+\letmathfenceparameter\c!size\empty
+\letmathfenceparameter\c!factor\v!auto
+% so far
+ \ifparameter#2\or\setupcurrentmathfence[#2]\fi
+ \math_fenced_fenced_common
+ \edef\p_size{\mathfenceparameter\c!size}%
+ \ifempty\p_size
+ \expandafter\math_fenced_fenced_indeed_adapt
+ \else
+ \expandafter\math_fenced_fenced_indeed_fixed
+ \fi}
+
+\protected\def\math_fenced_fenced_indeed_finish
+ {\stopusemathstyleparameter
+ \endgroup
+ \advance\c_math_fenced_nesting\minusone}
+
+\protected\def\math_fenced_fenced_indeed_fixed#1%
+ {\math_fenced_force_size\bigmathdelimitervariant\p_size
+ \settrue\c_math_fenced_sized
+ \math_fenced_left
+ \setfalse\c_math_fenced_sized
+ #1%
+ \settrue\c_math_fenced_sized
+ \math_fenced_right
+ \setfalse\c_math_fenced_sized
+ \math_fenced_fenced_indeed_finish}
+
+\protected\def\math_fenced_fenced_indeed_adapt#1%
+ {\setfalse\c_math_fenced_sized
+ \math_fenced_left
+ #1%
+ \setfalse\c_math_fenced_sized
+ \math_fenced_right
+ \math_fenced_fenced_indeed_finish}
+
+\appendtoks
+ \enforced\let\fenced\math_fenced_fenced
+\to \everymathematics
+
+% todo: | in mathmode letter
+%
+% \appendtoks
+% \let\bar\letterbar
+% \to \everymathematics
+%
+% but then we don't have it in embedded text too so ...
+
+\definemathfence [parenthesis] [\c!left="0028,\c!right="0029]
+\definemathfence [bracket] [\c!left="005B,\c!right="005D]
+\definemathfence [brace] [\c!left="007B,\c!right="007D]
+\definemathfence [bar] [\c!left="007C,\c!right="007C]
+\definemathfence [doublebar] [\c!left="2016,\c!right="2016]
+\definemathfence [triplebar] [\c!left="2980,\c!right="2980]
+\definemathfence [angle] [\c!left="27E8,\c!right="27E9]
+\definemathfence [doubleangle] [\c!left="27EA,\c!right="27EB]
+\definemathfence [solidus] [\c!left="2044,\c!right="2044]
+\definemathfence [ceiling] [\c!left="2308,\c!right="2309]
+\definemathfence [floor] [\c!left="230A,\c!right="230B]
+\definemathfence [moustache] [\c!left="23B0,\c!right="23B1]
+\definemathfence [uppercorner] [\c!left="231C,\c!right="231D]
+\definemathfence [lowercorner] [\c!left="231E,\c!right="231F]
+\definemathfence [group] [\c!left="27EE,\c!right="27EF]
+\definemathfence [openbracket] [\c!left="27E6,\c!right="27E7]
+
+\definemathfence [nothing]
+
+\definemathfence [mirrored] % \v!mirrored
+
+\definemathfence [mirroredparenthesis] [mirrored] [\c!right="0028,\c!left="0029]
+\definemathfence [mirroredbracket] [mirrored] [\c!right="005B,\c!left="005D]
+\definemathfence [mirroredbrace] [mirrored] [\c!right="007B,\c!left="007D]
+\definemathfence [mirroredbar] [mirrored] [\c!right="007C,\c!left="007C]
+\definemathfence [mirroreddoublebar] [mirrored] [\c!right="2016,\c!left="2016]
+\definemathfence [mirroredtriplebar] [mirrored] [\c!right="2980,\c!left="2980]
+\definemathfence [mirroredangle] [mirrored] [\c!right="27E8,\c!left="27E9]
+\definemathfence [mirroreddoubleangle] [mirrored] [\c!right="27EA,\c!left="27EB]
+\definemathfence [mirroredsolidus] [mirrored] [\c!right="2044,\c!left="2044]
+\definemathfence [mirroredceiling] [mirrored] [\c!right="2308,\c!left="2309]
+\definemathfence [mirroredfloor] [mirrored] [\c!right="230A,\c!left="230B]
+\definemathfence [mirroredmoustache] [mirrored] [\c!right="23B0,\c!left="23B1]
+\definemathfence [mirroreduppercorner] [mirrored] [\c!right="231C,\c!left="231D]
+\definemathfence [mirroredlowercorner] [mirrored] [\c!right="231E,\c!left="231F]
+\definemathfence [mirroredgroup] [mirrored] [\c!right="27EE,\c!left="27EF]
+\definemathfence [mirroredopenbracket] [mirrored] [\c!right="27E6,\c!left="27E7]
+
+\definemathfence [mirrorednothing] [mirrored]
+
+%D A bonus:
+
+\pushoverloadmode
+
+\immutable\protected\def\Lparenthesis {\math_fenced_fenced_start{parenthesis}} \immutable\protected\def\Rparenthesis {\math_fenced_fenced_stop {parenthesis}}
+\immutable\protected\def\Lbracket {\math_fenced_fenced_start{bracket}} \immutable\protected\def\Rbracket {\math_fenced_fenced_stop {bracket}}
+\immutable\protected\def\Lbrace {\math_fenced_fenced_start{brace}} \immutable\protected\def\Rbrace {\math_fenced_fenced_stop {brace}}
+\immutable\protected\def\Langle {\math_fenced_fenced_start{angle}} \immutable\protected\def\Rangle {\math_fenced_fenced_stop {angle}}
+\immutable\protected\def\Ldoubleangle {\math_fenced_fenced_start{doubleangle}} \immutable\protected\def\Rdoubleangle {\math_fenced_fenced_stop {doubleangle}}
+\immutable\protected\def\Lbar {\math_fenced_fenced_start{bar}} \immutable\protected\def\Rbar {\math_fenced_fenced_stop {bar}}
+\immutable\protected\def\Ldoublebar {\math_fenced_fenced_start{doublebar}} \immutable\protected\def\Rdoublebar {\math_fenced_fenced_stop {doublebar}}
+\immutable\protected\def\Ltriplebar {\math_fenced_fenced_start{triplebar}} \immutable\protected\def\Rtriplebar {\math_fenced_fenced_stop {triplebar}}
+\immutable\protected\def\Lsolidus {\math_fenced_fenced_start{solidus}} \immutable\protected\def\Rsolidus {\math_fenced_fenced_stop {solidus}}
+\immutable\protected\def\Lfloor {\math_fenced_fenced_start{floor}} \immutable\protected\def\Rfloor {\math_fenced_fenced_stop {floor}}
+\immutable\protected\def\Lceiling {\math_fenced_fenced_start{ceiling}} \immutable\protected\def\Rceiling {\math_fenced_fenced_stop {ceiling}}
+\immutable\protected\def\Lmoustache {\math_fenced_fenced_start{moustache}} \immutable\protected\def\Rmoustache {\math_fenced_fenced_stop {moustache}}
+\immutable\protected\def\Luppercorner {\math_fenced_fenced_start{uppercorner}} \immutable\protected\def\Ruppercorner {\math_fenced_fenced_stop {uppercorner}}
+\immutable\protected\def\Llowercorner {\math_fenced_fenced_start{lowercorner}} \immutable\protected\def\Rlowercorner {\math_fenced_fenced_stop {lowercorner}}
+\immutable\protected\def\Lgroup {\math_fenced_fenced_start{group}} \immutable\protected\def\Rgroup {\math_fenced_fenced_stop {group}}
+\immutable\protected\def\Lopenbracket {\math_fenced_fenced_start{openbracket}} \immutable\protected\def\Ropenbracket {\math_fenced_fenced_stop {openbracket}}
+\immutable\protected\def\Lnothing {\math_fenced_fenced_start{nothing}} \immutable\protected\def\Rnothing {\math_fenced_fenced_stop {nothing}}
+
+\immutable\protected\def\Lparenthesismirrored {\math_fenced_fenced_stop {mirroredparenthesis}} \immutable\protected\def\Rparenthesismirrored {\math_fenced_fenced_start{mirroredparenthesis}}
+\immutable\protected\def\Lbracketmirrored {\math_fenced_fenced_stop {mirroredbracket}} \immutable\protected\def\Rbracketmirrored {\math_fenced_fenced_start{mirroredbracket}}
+\immutable\protected\def\Lbracemirrored {\math_fenced_fenced_stop {mirroredbrace}} \immutable\protected\def\Rbracemirrored {\math_fenced_fenced_start{mirroredbrace}}
+\immutable\protected\def\Langlemirrored {\math_fenced_fenced_stop {mirroredangle}} \immutable\protected\def\Ranglemirrored {\math_fenced_fenced_start{mirroredangle}}
+\immutable\protected\def\Ldoubleanglemirrored {\math_fenced_fenced_stop {mirroreddoubleangle}} \immutable\protected\def\Rdoubleanglemirrored {\math_fenced_fenced_start{mirroreddoubleangle}}
+\immutable\protected\def\Lbarmirrored {\math_fenced_fenced_stop {mirroredbar}} \immutable\protected\def\Rbarmirrored {\math_fenced_fenced_start{mirroredbar}}
+\immutable\protected\def\Ldoublebarmirrored {\math_fenced_fenced_stop {mirroreddoublebar}} \immutable\protected\def\Rdoublebarmirrored {\math_fenced_fenced_start{mirroreddoublebar}}
+\immutable\protected\def\Ltriplebarmirrored {\math_fenced_fenced_stop {mirroredtriplebar}} \immutable\protected\def\Rtriplebarmirrored {\math_fenced_fenced_start{mirroredtriplebar}}
+\immutable\protected\def\Lsolidusmirrored {\math_fenced_fenced_stop {mirroredsolidus}} \immutable\protected\def\Rsolidusmirrored {\math_fenced_fenced_start{mirroredsolidus}}
+\immutable\protected\def\Lfloormirrored {\math_fenced_fenced_stop {mirroredfloor}} \immutable\protected\def\Rfloormirrored {\math_fenced_fenced_start{mirroredfloor}}
+\immutable\protected\def\Lceilingmirrored {\math_fenced_fenced_stop {mirroredceiling}} \immutable\protected\def\Rceilingmirrored {\math_fenced_fenced_start{mirroredceiling}}
+\immutable\protected\def\Lmoustachemirrored {\math_fenced_fenced_stop {mirroredmoustache}} \immutable\protected\def\Rmoustachemirrored {\math_fenced_fenced_start{mirroredmoustache}}
+\immutable\protected\def\Luppercornermirrored {\math_fenced_fenced_stop {mirroreduppercorner}} \immutable\protected\def\Ruppercornermirrored {\math_fenced_fenced_start{mirroreduppercorner}}
+\immutable\protected\def\Llowercornermirrored {\math_fenced_fenced_stop {mirroredlowercorner}} \immutable\protected\def\Rlowercornermirrored {\math_fenced_fenced_start{mirroredlowercorner}}
+\immutable\protected\def\Lgroupmirrored {\math_fenced_fenced_stop {mirroredgroup}} \immutable\protected\def\Rgroupmirrored {\math_fenced_fenced_start{mirroredgroup}}
+\immutable\protected\def\Lopenbracketmirrored {\math_fenced_fenced_stop {mirroredopenbracket}} \immutable\protected\def\Ropenbracketmirrored {\math_fenced_fenced_start{mirroredopenbracket}}
+\immutable\protected\def\Lnothingmirrored {\math_fenced_fenced_stop {mirrorednothing}} \immutable\protected\def\Rnothingmirrored {\math_fenced_fenced_start{mirrorednothing}}
+
+\definemathfence [interval] [\c!left="2997,\c!right="2998]
+\definemathfence [openinterval] [interval] [\c!left="2998,\c!right="2998]
+\definemathfence [leftopeninterval] [interval] [\c!left="2997,\c!right="2997]
+\definemathfence [rightopeninterval] [interval] [\c!left="2998,\c!right="2998]
+
+\immutable\protected\def\Linterval {\math_fenced_fenced_start{interval}}
+\immutable\protected\def\Lointerval {\math_fenced_fenced_start{openinterval}}
+\immutable\protected\def\Llointerval {\math_fenced_fenced_start{leftopeninterval}}
+\immutable\protected\def\Lrointerval {\math_fenced_fenced_start{rightopeninterval}}
+
+\immutable\protected\def\Rinterval {\math_fenced_fenced_stop {interval}}
+\immutable\protected\def\Rointerval {\math_fenced_fenced_stop {openinterval}}
+\immutable\protected\def\Rlointerval {\math_fenced_fenced_stop {leftopeninterval}}
+\immutable\protected\def\Rrointerval {\math_fenced_fenced_stop {rightopeninterval}}
+
+% \startformula
+% \left{ \frac{1}{a} \right}
+% \left[ \frac{1}{b} \right]
+% \left( \frac{1}{c} \right)
+% \left< \frac{1}{d} \right>
+% \left| \frac{1}{e} \right|
+% \stopformula
+
+\popoverloadmode
+
+\installcorenamespace{mathleft}
+\installcorenamespace{mathright}
+\installcorenamespace{mathmiddle}
+
+\pushoverloadmode
+
+\protected\def\left {\afterassignment\math_left \let\nexttoken}
+\protected\def\right {\afterassignment\math_right \let\nexttoken}
+\protected\def\middle{\afterassignment\math_middle\let\nexttoken}
+
+\popoverloadmode
+
+\let\leftorright\relax
+
+\newconditional\c_math_fenced_done
+\newconditional\c_math_fenced_unknown \settrue\c_math_fenced_unknown
+
+\permanent\protected\def\installmathfencepair#1#2#3#4%
+ {\letcsname\??mathleft \normalmeaning#1\endcsname#2%
+ \letcsname\??mathright\normalmeaning#3\endcsname#4}
+
+\def\math_unknown_left {\setfalse\c_math_fenced_done\ifconditional\c_math_fenced_unknown\normalleft \nexttoken\fi}
+\def\math_unknown_right {\setfalse\c_math_fenced_done\ifconditional\c_math_fenced_unknown\normalright \nexttoken\fi}
+\def\math_unknown_middle{\setfalse\c_math_fenced_done\ifconditional\c_math_fenced_unknown\normalmiddle\nexttoken\fi}
+
+\letvalue{\??mathleft \s!unknown}\math_unknown_left
+\letvalue{\??mathright \s!unknown}\math_unknown_right
+\letvalue{\??mathmiddle\s!unknown}\math_unknown_middle
+
+\def\math_left
+ {\settrue\c_math_fenced_done
+ \ifcsname\??mathleft\normalmeaning\nexttoken\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\math_unknown_left
+ \fi}
+
+\def\math_right
+ {\settrue\c_math_fenced_done
+ \ifcsname\??mathright\normalmeaning\nexttoken\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\math_unknown_right
+ \fi}
+
+\def\math_middle
+ {\settrue\c_math_fenced_done
+ \ifcsname\??mathmiddle\normalmeaning\nexttoken\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\math_unknown_middle
+ \fi}
+
+\permanent\protected\def\lfence#1%
+ {\settrue\c_math_fenced_done
+ \let\nexttoken#1%
+ \ifcsname\??mathleft\normalmeaning\nexttoken\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\math_unknown_left
+ \fi}
+
+\permanent\protected\def\rfence#1%
+ {\settrue\c_math_fenced_done
+ \let\nexttoken#1%
+ \ifcsname\??mathright\normalmeaning\nexttoken\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\math_unknown_right
+ \fi}
+
+\permanent\protected\def\mfence#1%
+ {\settrue\c_math_fenced_done
+ \let\nexttoken#1%
+ \ifcsname\??mathmiddle\normalmeaning\nexttoken\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\math_unknown_middle
+ \fi}
+
+\normalexpanded{\installmathfencepair {|\detokenize {|}} \Ldoublebar {|\detokenize {|}} \Rdoublebar}
+\normalexpanded{\installmathfencepair {|\detokenize{||}} \Ltriplebar {|\detokenize{||}} \Rtriplebar}
+
+\installmathfencepair \bgroup \Lbrace \egroup \Rbrace
+\installmathfencepair \egroup \Rbracemirrored \bgroup \Lbracemirrored
+
+\installmathfencepair \letteropenbrace \Lbrace \letterclosebrace \Rbrace % as we escape in mp textexts
+
+\installmathfencepair . \Lnothing . \Rnothing
+\installmathfencepair . \Rnothingmirrored . \Lnothingmirrored
+
+\installmathfencepair [ \Lbracket ] \Rbracket
+\installmathfencepair ] \Rbracketmirrored [ \Lbracketmirrored
+
+\installmathfencepair ( \Lparenthesis ) \Rparenthesis
+\installmathfencepair ) \Rparenthesismirrored ( \Lparenthesismirrored
+
+\installmathfencepair < \Langle > \Rangle
+\installmathfencepair > \Ranglemirrored < \Langlemirrored
+
+\installmathfencepair / \Lsolidus / \Rsolidus
+%installmathfencepair / \Rsolidusmirrored / \Lsolidusmirrored
+
+\installmathfencepair | \Lbar | \Rbar
+%installmathfencepair | \Rbarmirrored | \Lbarmirrored
+
+\installmathfencepair ⌊ \Lfloor ⌋ \Rfloor
+\installmathfencepair ⌋ \Rfloormirrored ⌊ \Lfloormirrored
+\installmathfencepair ⌈ \Lceiling ⌉ \Rceiling
+\installmathfencepair ⌉ \Rceilingmirrored ⌈ \Lceilingmirrored
+
+\installmathfencepair ⟨ \Langle ⟩ \Rangle
+\installmathfencepair ⟩ \Ranglemirrored ⟨ \Langlemirrored
+
+\installmathfencepair ⟪ \Ldoubleangle ⟫ \Rdoubleangle
+\installmathfencepair ⟫ \Rdoubleanglemirrored ⟪ \Ldoubleanglemirrored
+
+\installmathfencepair ‖ \Ldoublebar ‖ \Rdoublebar
+%installmathfencepair ‖ \Rdoublebarmirrored ‖ \Ldoublebarmirrored
+
+\installmathfencepair ⦀ \Ltriplebar ⦀ \Rtriplebar
+%installmathfencepair ⦀ \Rtriplebarmirrored ⦀ \Ltriplebarmirrored
+
+% \installmathfencepair { \Lbrace } \Rbrace
+% \installmathfencepair } \Rbracemirrored { \Lbracemirrored
+
+\installmathfencepair ⦗ \Linterval ⦘ \Rinterval
+
+\appendtoks
+ \ignorediscretionaries % so $\mtext{a|b}$ works, this is ok because it's an \hbox
+\to \everymathematics
+
+% We unofficial support some synonyms as we need them for some fuzzy web related math.
+
+% The names in char-def.lua (historic mess):
+
+\pushoverloadmode
+
+\aliased\let\lbrack \lbracket
+\aliased\let\rbrack \rbracket
+\aliased\let\lceiling \lceil
+\aliased\let\rceiling \rceil
+\aliased\let\lparenthesis\lparent
+\aliased\let\rparenthesis\rparent
+\aliased\let\lparen \lparent
+\aliased\let\rparen \rparent
+
+% Just defined:
+
+\aliased\let\Lceil \Lceiling
+\aliased\let\Rceil \Rceiling
+\aliased\let\Lparent\Lparenthesis
+\aliased\let\Rparent\Rparenthesis
+\aliased\let\Lparen \Lparenthesis
+\aliased\let\Rparen \Rparenthesis
+
+\installmathfencepair \lbrace \Lbrace \rbrace \Rbrace
+\installmathfencepair \lbracket \Lbracket \rbracket \Rbracket
+\installmathfencepair \lbrack \Lbracket \rbracket \Rbracket
+\installmathfencepair \lparenthesis \Lparenthesis \rparenthesis \Rparenthesis
+\installmathfencepair \lparent \Lparenthesis \rparent \Rparenthesis
+\installmathfencepair \lparen \Lparenthesis \rparen \Rparenthesis
+\installmathfencepair \langle \Langle \rangle \Rangle
+\installmathfencepair \llangle \Ldoubleangle \rrangle \Rdoubleangle
+%installmathfencepair \lbar \Lbar \rbar \Rbar
+\installmathfencepair \lVert \Ldoublebar \rVert \Rdoublebar
+\installmathfencepair \vert \Lbar \vert \Rbar
+\installmathfencepair \solidus \Lsolidus \solidus \Rsolidus
+\installmathfencepair \lfloor \Lfloor \rfloor \Rfloor
+\installmathfencepair \lceiling \Lceiling \rceiling \Rceiling
+\installmathfencepair \lceil \Lceiling \rceil \Rceiling
+
+\installmathfencepair \ulcorner \Luppercorner \urcorner \Ruppercorner
+\installmathfencepair \llcorner \Llowercorner \lrcorner \Rlowercorner
+\installmathfencepair \lmoustache \Lmoustache \rmoustache \Rmoustache
+\installmathfencepair \llbracket \Lopenbracket \rrbracket \Ropenbracket
+\installmathfencepair \lgroup \Lgroup \rgroup \Rgroup
+
+\installmathfencepair \linterval \Linterval \rinterval \Rinterval
+%installmathfencepair \linterv \Linterval \rinterv \Rinterval
+\installmathfencepair \lointerval \Linterval \rointerval \Rinterval
+\installmathfencepair \llointerval \Llointerval \rlointerval \Rlointerval
+\installmathfencepair \lrointerval \Lrointerval \rrointerval \Rrointerval
+
+\aliased\let\textlbar\lbar \aliased\let\mathlbar\Lbar
+\aliased\let\textrbar\lbar \aliased\let\mathrbar\Rbar
+
+\immutable\protected\def\lbar{\mathortext\mathlbar\textlbar}
+\immutable\protected\def\rbar{\mathortext\mathrbar\textrbar}
+
+% \setupmathfences[color=darkgreen]
+
+\permanent\protected\def\{{\mathortext\lbrace \letterleftbrace } % or maybe a chardef
+\permanent\protected\def\}{\mathortext\rbrace \letterrightbrace } % or maybe a chardef
+\permanent\protected\def\[{\mathortext\lbracket \letterleftbracket } % or maybe a chardef
+\permanent\protected\def\]{\mathortext\rbracket \letterrightbracket } % or maybe a chardef
+\permanent\protected\def\({\mathortext\lparenthesis\letterleftparenthesis } % or maybe a chardef
+\permanent\protected\def\){\mathortext\rparenthesis\letterrightparenthesis} % or maybe a chardef
+\permanent\protected\def\|{\mathortext\vert \letterbar } % or maybe a chardef
+%permanent\protected\def\/{\mathortext\solidus \letterslash } % or maybe a chardef
+
+\installmathfencepair \{ \Lbrace \} \Rbrace
+\installmathfencepair \[ \Lbracket \] \Rbracket
+\installmathfencepair \( \Lparenthesis \) \Rparenthesis
+\installmathfencepair \< \Langle \> \Rangle
+\installmathfencepair \| \Lbar \| \Rbar
+
+\popoverloadmode
+
+%D As we have overloaded \type {\left} and \type {\right} we also need a more
+%D clever version of the following:
+
+% methods:
+%
+% 1: none
+% 2: lua
+% 3: tex
+
+% variants:
+%
+% 1: step 1
+% 2: step 2
+% 3: htdp * 1.33^n
+% 4: size * 1.33^n
+
+\setnewconstant\bigmathdelimitermethod \plustwo % \plusone
+\setnewconstant\bigmathdelimitervariant\plusthree
+
+\appendtoks
+ \bigmathdelimitermethod \mathfenceparameter\c!method\relax
+ \bigmathdelimitervariant\mathfenceparameter\c!alternative\relax
+\to \everysetupmathfence
+
+\setupmathfence[\c!method=1,\c!alternative=2]
+
+% \protected\def\plainbigdelimiters % traditional method
+% {\bigmathdelimitermethod\plustwo}
+%
+% \plainbigdelimiters % is default for the moment but not so nice
+
+\installcorenamespace{mathbig}
+
+\setvalue{\??mathbig1}{0.85}
+\setvalue{\??mathbig2}{1.15}
+\setvalue{\??mathbig3}{1.45}
+\setvalue{\??mathbig4}{1.75}
+
+\permanent\protected\def\choosemathbig#1#2% so we accept \big{||} as well
+ {{\naturalhbox\bgroup
+ \startimath
+ \ifcase\bigmathdelimitermethod
+ \math_fenced_step#2\relax
+ \or
+ \math_fenced_force_size\bigmathdelimitervariant{#1}\relax
+ \math_fenced_step#2\relax
+ \else
+ \math_fenced_step#2{\vpack to\csname\??mathbig#1\endcsname\bodyfontsize{}}%
+ \fi
+ \nulldelimiterspace\zeropoint\relax
+ \mathsurround\zeropoint
+ \stopimath
+ \egroup}}
+
+% needs testing:
+%
+% \protected\def\choosemathbig#1#2% so we accept \big{||} as well
+% {{\naturalhbox\bgroup
+% \startimath
+% \ifcase\bigmathdelimitermethod
+% \Uvextensible#2\relax
+% \or
+% \math_fenced_force_size\bigmathdelimitervariant{#1}\relax
+% \Uvextensible#2\relax
+% \else
+% \scratchdimen.5\dimexpr\csname\??mathbig\number#1\endcsname\bodyfontsize+1.1\exheight\relax
+% \Uvextensible\s!axis \s!depth \scratchdimen \s!height \scratchdimen #2\relax
+% \fi
+% \nulldelimiterspace\zeropoint\relax
+% \mathsurround\zeropoint
+% \stopimath
+% \egroup}}
+
+\def\math_fenced_step#1#2%
+ {\setfalse\c_math_fenced_unknown
+ \setfalse\c_math_fenced_done
+ \left#1\relax
+ \ifconditional\c_math_fenced_done
+ #2%
+ \right.\relax
+ \else
+ \left.\relax
+ #2%
+ \setfalse\c_math_fenced_done
+ \right#1\relax
+ \ifconditional\c_math_fenced_done
+ \else
+ \right.\relax
+ \fi
+ \fi}
+
+\permanent\protected\def\mathdelimiterstep#1#2%
+ {\begingroup
+ \math_fenced_force_size\plusone{#1}%
+ \math_fenced_step#2\relax
+ \endgroup}
+
+\definemathcommand [big] {\choosemathbig1}
+\definemathcommand [Big] {\choosemathbig2}
+\definemathcommand [bigg] {\choosemathbig3}
+\definemathcommand [Bigg] {\choosemathbig4}
+
+\definemathcommand [bigl] [open] [one] {\big}
+\definemathcommand [bigm] [rel] [one] {\big}
+\definemathcommand [bigr] [close] [one] {\big}
+\definemathcommand [Bigl] [open] [one] {\Big}
+\definemathcommand [Bigm] [rel] [one] {\Big}
+\definemathcommand [Bigr] [close] [one] {\Big}
+\definemathcommand [biggl] [open] [one] {\bigg}
+\definemathcommand [biggm] [rel] [one] {\bigg}
+\definemathcommand [biggr] [close] [one] {\bigg}
+\definemathcommand [Biggl] [open] [one] {\Bigg}
+\definemathcommand [Biggm] [rel] [one] {\Bigg}
+\definemathcommand [Biggr] [close] [one] {\Bigg}
+
+% \definemathfence [integral] [\c!left="222B]
+%
+% \protected\def\Lintegral {\math_fenced_fenced_start{integral}}
+% \protected\def\Rintegral {\math_fenced_fenced_stop {integral}}
+%
+% \installmathfencepair \lintegral \Lintegral \rintegral \Rintegral
+%
+% \left\lintegral
+% \vrule height 3cm depth 3cm
+% \right\rintegral
+
+%definemathfence [fancybracket] [bracket] [command=yes,color=red]
+
+% experimental accents:
+%
+% \definemathoverextensible [top] [hoed] ["FE302]
+% \definemathoverextensible [top] [slang] ["FE303]
+
+%D The nested fences recovery code is needed for mathml and the original
+%D code can still be found in the mkiv file.
+
+\definesystemattribute[mathautofence][public]
+
+\let\math_fences_normal_left \left
+\let\math_fences_normal_right \right
+\let\math_fences_normal_middle\middle
+\let\math_fences_normal_both \leftorright
+
+\protected\def\math_fences_auto_left #1{\c_attr_mathautofence\plusone #1\c_attr_mathautofence\attributeunsetvalue}
+\protected\def\math_fences_auto_right #1{\c_attr_mathautofence\plustwo #1\c_attr_mathautofence\attributeunsetvalue}
+\protected\def\math_fences_auto_middle#1{\c_attr_mathautofence\plusthree#1\c_attr_mathautofence\attributeunsetvalue}
+\protected\def\math_fences_auto_both #1{\c_attr_mathautofence\plusfour #1\c_attr_mathautofence\attributeunsetvalue}
+
+\permanent\let\autofenceopen \math_fences_auto_left % for testing
+\permanent\let\autofenceclose \math_fences_auto_right % for testing
+\permanent\let\autofenceleft \math_fences_auto_left % for testing
+\permanent\let\autofenceright \math_fences_auto_right % for testing
+\permanent\let\autofencemiddle\math_fences_auto_middle % for testing
+\permanent\let\autofenceboth \math_fences_auto_both % for testing
+
+% fences are used not that often (i.e. no performance issue) so we can use a state
+% instead of \let ... also some state variable can come in handy in the future
+
+\newconditional\c_math_fences_auto
+
+\permanent\protected\def\enableautofences
+ {\clf_enableautofences
+ \glet\clf_enableautofences\relax % onlyonce anyway
+ \enforced\permanent\protected\gdef\enableautofences{\settrue\c_math_fences_auto}%
+ \enableautofences}
+
+\permanent\protected\def\disableautofences
+ {\setfalse\c_math_fences_auto}
+
+\protected\def\math_fences_used_left
+ {\ifconditional\c_math_fences_auto
+ \expandafter\math_fences_auto_left
+ \else
+ \expandafter\math_fences_normal_left
+ \fi}
+
+\protected\def\math_fences_used_right
+ {\ifconditional\c_math_fences_auto
+ \expandafter\math_fences_auto_right
+ \else
+ \expandafter\math_fences_normal_right
+ \fi}
+
+\protected\def\math_fences_used_middle
+ {\ifconditional\c_math_fences_auto
+ \expandafter\math_fences_auto_middle
+ \else
+ \expandafter\math_fences_normal_middle
+ \fi}
+
+\protected\def\math_fences_used_both
+ {\ifconditional\c_math_fences_auto
+ \expandafter\math_fences_auto_both
+ \else
+ \expandafter\math_fences_normal_both
+ \fi}
+
+\permanent\let\left \math_fences_used_left
+\permanent\let\right \math_fences_used_right
+\permanent\let\middle \math_fences_used_middle
+\permanent\let\leftorright\math_fences_used_both
+
+% wrappers
+
+\permanent\protected\def\startcheckedfences
+ {\begingroup
+ \enableautofences}
+
+\permanent\protected\def\stopcheckedfences
+ {\endgroup}
+
+% \appendtoks
+ % maybe: safeguard against overloading
+ %
+ % \let\left \math_fences_used_left
+ % \let\right \math_fences_used_right
+ % \let\middle \math_fences_used_middle
+ % \let\leftorright\math_fences_used_both
+% \to \everymathematics
+
+\appendtoks
+ \ifempty\currentmathfence
+ \doifelse{\mathfenceparameter\c!state}\v!auto\enableautofences\disableautofences
+ \fi
+\to \everysetupmathfence
+
+\newconstant\c_math_fences_delimiters_mode \c_math_fences_delimiters_mode"16 % \numexpr"02+"04+"10\relax
+
+%permanent\protected\def\enableautofencemode {\mathdelimitersmode\plussix} % the shift (1) is too fragile
+\permanent\protected\def\enableautofencemode {\mathdelimitersmode\c_math_fences_delimiters_mode}
+\permanent\protected\def\disableautofencemode{\mathdelimitersmode\zerocount}
+
+\appendtoks
+ \ifempty\currentmathfence
+ \doifelse{\mathfenceparameter\c!method}\v!auto\enableautofencemode\disableautofencemode
+ \fi
+\to \everysetupmathfence
+
+% some day default: \setupmathfences[\c!state=\v!auto]
+
+%D The next characters were used for constructing nicer extensibles but
+%D nowadays we have real characters.
+
+\immutable\Umathchardef\braceld\zerocount \defaultmathfamily "FF07A
+\immutable\Umathchardef\bracerd\zerocount \defaultmathfamily "FF07B
+\immutable\Umathchardef\bracelu\zerocount \defaultmathfamily "FF07C
+\immutable\Umathchardef\braceru\zerocount \defaultmathfamily "FF07D
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-for.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-for.mkxl
new file mode 100644
index 00000000000..e6ea402de99
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-for.mkxl
@@ -0,0 +1,47 @@
+%D \module
+%D [ file=strc-mat,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Math Numbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Math Formulas}
+
+%D This module only provides the code for defining formulas and
+%D fetching parameters. The action takes place later.
+
+\unprotect
+
+%D \macros
+%D {setupformulas,setupsubformulas,setupformulaframed}
+
+\installcorenamespace{formula}
+\installcorenamespace{subformula}
+\installcorenamespace{formulaframed}
+\installcorenamespace{formulaoption}
+
+\installcommandhandler \??formula {formula} \??formula
+\installcommandhandler \??subformula {subformula} \??subformula % maybe just setuphandler (no childs used yet)
+\installframedcommandhandler \??formulaframed {formulaframed} \??formulaframed
+
+\aliased\let\setupformulas \setupformula
+\aliased\let\setupsubformulas\setupsubformula
+
+\appendtoks
+ \normalexpanded{\defineformulaframed[\currentformula][\currentformulaparent]}%
+\to \everydefineformula
+
+\def\strc_formulas_option#1%
+ {\ifcsname\??formulaoption#1\endcsname
+ \lastnamedcs
+ \else
+ \font_basics_switchtobodyfont{#1}% for old time sake, might go away, only pt so maybe dimension and small test
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-frc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-frc.mkxl
new file mode 100644
index 00000000000..8295195b5d6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-frc.mkxl
@@ -0,0 +1,833 @@
+%D \module
+%D [ file=math-frc,
+%D version=2013.04.06, % 2007.07.19,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Fractions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Fractions}
+
+\unprotect
+
+%D I need to check it all again as there was a bug in luatex with dimensions that could
+%D resulted in side effects that made me mess with spacing.
+
+\permanent\protected\def\topstrut{\vrule\s!width\zeropoint\s!height\strutht\s!depth\zeropoint\relax}
+\permanent\protected\def\botstrut{\vrule\s!width\zeropoint\s!height\zeropoint\s!depth\strutdp\relax}
+
+\permanent\protected\def\mathtopstrut{\setbox\scratchbox\mathstylehbox{(}\vrule\s!width\zeropoint\s!height\ht\scratchbox\s!depth\zeropoint\relax}
+\permanent\protected\def\mathbotstrut{\setbox\scratchbox\mathstylehbox{(}\vrule\s!width\zeropoint\s!height\zeropoint\s!depth\dp\scratchbox\relax}
+
+%D This module is reimplemented in \MKIV\ style.
+
+\registerctxluafile{math-frc}{}
+
+%D \macros
+%D {frac, xfrac, xxfrac}
+%D
+%D This is another one Tobias asked for. It replaces the primitive \type
+%D {\over}. We also take the opportunity to handle math style restoring,
+%D which makes sure units and chemicals come out ok. The \type {\frac}
+%D macro kind of replaces the awkward \type {\over} primitive. Say that
+%D we have the following formulas:
+%D
+%D \startbuffer[sample]
+%D test $\frac {1}{2}$ test $$1 + \frac {1}{2} = 1.5$$
+%D test $\xfrac {1}{2}$ test $$1 + \xfrac {1}{2} = 1.5$$
+%D test $\xxfrac{1}{2}$ test $$1 + \xxfrac{1}{2} = 1.5$$
+%D \stopbuffer
+%D
+%D \typebuffer[sample]
+%D
+%D With the most straightforward definitions, we get:
+%D
+%D \startbuffer[code]
+%D \def\dofrac#1#2#3{\relax\mathematics{{{#1{#2}}\over{#1{#3}}}}}
+%D
+%D \def\frac {\dofrac\mathstyle}
+%D \def\xfrac {\dofrac\scriptstyle}
+%D \def\xxfrac{\dofrac\scriptscriptstyle}
+%D \stopbuffer
+%D
+%D \typebuffer[code] \getbuffer[code,sample]
+%D
+%D Since this does not work well, we can try:
+%D
+%D \startbuffer[code]
+%D \def\xfrac #1#2{\hbox{$\dofrac\scriptstyle {#1}{#2}$}}
+%D \def\xxfrac#1#2{\hbox{$\dofrac\scriptscriptstyle{#1}{#2}$}}
+%D \stopbuffer
+%D
+%D \typebuffer[code] \getbuffer[code,sample]
+%D
+%D This for sure looks better than:
+%D
+%D \startbuffer[code]
+%D \def\xfrac #1#2{{\scriptstyle \dofrac\relax{#1}{#2}}}
+%D \def\xxfrac#1#2{{\scriptscriptstyle\dofrac\relax{#1}{#2}}}
+%D \stopbuffer
+%D
+%D \typebuffer[code] \getbuffer[code,sample]
+%D
+%D So we stick to the next definitions (watch the local overloading of
+%D \type {\xfrac}).
+%D
+%D In the meantime, in \LUATEX, we have better control over styles so the
+%D following macros are different from the \MKII\ ones.
+
+% obsolete, is now c!mathstyle
+%
+% 0=auto, 1=displaystyle, 2=textstyle, 3=scriptstyle, 4=scriptscriptstyle, 5=mathstyle
+%
+% $\mathfracmode0 \frac{1}{2}$
+% $\mathfracmode1 \frac{1}{2}$
+% $\mathfracmode2 \frac{1}{2}$
+% $\mathfracmode3 \frac{1}{2}$
+% $\mathfracmode4 \frac{1}{2}$
+% $\mathfracmode5 \frac{1}{2}$
+%
+% we keep the constant for a while
+
+\setnewconstant\mathfracmode\zerocount
+
+\installcorenamespace{mathfractions}
+\installcorenamespace{mathfractionstyle}
+\installcorenamespace{mathfractionalternative}
+
+\installcommandhandler \??mathfractions {mathfraction} \??mathfractions
+
+\aliased\let\setupmathfractions\setupmathfraction
+
+% color only applies to rule, use regular color for rest
+
+\setupmathfractions
+ [\c!mathstyle=,
+ \c!alternative=\v!inner,
+ \c!margin=\zeropoint,
+ \c!rulethickness=.25\exheight,
+ \c!left=0x2E,
+ \c!right=0x2E,
+ \c!strut=\v!yes,
+ \c!topdistance=,
+ \c!bottomdistance=,
+ \c!rule=\v!auto]
+
+\appendtoks
+ \instance\frozen\protected\edefcsname\currentmathfraction\endcsname{\math_frac{\currentmathfraction}}%
+\to \everydefinemathfraction
+
+% Sometimes users want control over the distances:
+
+\let\math_fraction_set_distance\relax
+
+\appendtoks
+ \math_fraction_set_distance
+\to \everymathematics
+
+% why only displaystyle .. a bit weak
+
+\protected\def\math_fraction_set_distance_top
+ {\Umathfractionnumup \displaystyle\m_math_fraction_distance_top
+ \relax}
+
+\protected\def\math_fraction_set_distance_bot
+ {\Umathfractiondenomdown\displaystyle\m_math_fraction_distance_bot
+ \relax}
+
+\protected\def\math_fraction_set_distance_all
+ {\Umathfractionnumup \displaystyle\m_math_fraction_distance_top
+ \Umathfractiondenomdown\displaystyle\m_math_fraction_distance_bot
+ \relax}
+
+\appendtoks
+ \ifempty\currentmathfraction
+ \edef\m_math_fraction_distance_top{\mathfractionparameter\c!topdistance}%
+ \edef\m_math_fraction_distance_bot{\mathfractionparameter\c!bottomdistance}%
+ \ifempty\m_math_fraction_distance_top
+ \ifempty\m_math_fraction_distance_bot
+ \let\math_fraction_set_distance\relax
+ \else
+ \let\math_fraction_set_distance\math_fraction_set_distance_bot
+ \fi
+ \else
+ \ifempty\m_math_fraction_distance_bot
+ \let\math_fraction_set_distance\math_fraction_set_distance_top
+ \else
+ \let\math_fraction_set_distance\math_fraction_set_distance_all
+ \fi
+ \fi
+ \fi
+\to \everysetupmathfraction
+
+% So far for control.
+
+\installcorenamespace{mathfractionstrut}
+
+\def\math_frac_no_strut
+ {\enforced\let\m_fractions_strut_top\relax
+ \enforced\let\m_fractions_strut_bot\relax}
+
+\setvalue{\??mathfractionstrut\v!yes}%
+ {\enforced\let\m_fractions_strut_top\mathstrut
+ \enforced\let\m_fractions_strut_bot\mathstrut}
+
+\setvalue{\??mathfractionstrut\v!math}%
+ {\enforced\let\m_fractions_strut_top\mathstrut
+ \enforced\let\m_fractions_strut_bot\mathstrut}
+
+\letvalue{\??mathfractionstrut\v!no}\math_frac_no_strut
+
+\setvalue{\??mathfractionstrut\v!tight}%
+ {\enforced\let\m_fractions_strut_top\mathbotstrut % indeed swapped name
+ \enforced\let\m_fractions_strut_bot\mathtopstrut} % indeed swapped name
+
+\math_frac_no_strut
+
+\newdimen\d_math_fraction_margin
+
+\protected\def\math_frac#1%
+ {\begingroup
+ \edef\currentmathfraction{#1}%
+ %
+ \edef\p_math_fraction_fences{\mathfractionparameter\c!fences}%
+ \ifempty\p_math_fraction_fences \else
+ \math_fenced_fenced_start\p_math_fraction_fences
+ \fi
+ %
+ \d_math_fraction_margin\mathfractionparameter\c!margin
+ %
+ \edef\p_math_fractions_color{\mathfractionparameter\c!color}%
+ %
+ \edef\p_math_fractions_strut{\mathfractionparameter\c!strut}%
+ \ifcsname\??mathfractionstrut\p_math_fractions_strut\endcsname
+ \lastnamedcs
+ \else
+ \math_frac_no_strut
+ \fi
+ %
+ \ifempty\p_math_fractions_color
+ \expandafter\math_frac_normal
+ \else
+ \expandafter\math_frac_colored
+ \fi}
+
+\protected\def\math_frac_wrapup
+ {\ifempty\p_math_fraction_fences \else
+ \math_fenced_fenced_stop\p_math_fraction_fences
+ \fi
+ \endgroup}
+
+\protected\def\math_frac_colored#1#2%
+ {\savecolor
+ \colo_helpers_activate\p_math_fractions_color
+ \math_frac_normal{\restorecolor#1}{\restorecolor#2}}
+
+\protected\def\math_frac_normal
+ {\expandnamespaceparameter\??mathfractionalternative\mathfractionparameter\c!alternative\v!inner}
+
+% we use utfchar anyway so we can as well do all at the lua end
+
+\def\math_frac_no_delim{0x2E}
+
+\def\math_frac_command
+ {\clf_mathfraction
+ {\mathfractionparameter\c!rule}%
+ \ifempty\p_math_fraction_fences
+ \mathfractionparameter\c!left \space
+ \mathfractionparameter\c!right\space
+ \else
+ \math_frac_no_delim\space
+ \math_frac_no_delim\space
+ \fi
+ \dimexpr\mathfractionparameter\c!rulethickness\relax
+ \relax}
+
+% Having a \withmarginornot{#1}{#2} makes not much sense nor do 4 tests or 4 redundant
+% kerns (longer node lists plus possible interference). A split in normal and margin
+% also makes testing easier. When left and right margins are needed we might merge the
+% variants again. After all, these are not real installers.
+
+% the denominator is in cramped!
+
+\setvalue{\??mathfractionalternative\v!inner}%
+ {\ifcase\d_math_fraction_margin
+ \expandafter\math_fraction_inner_normal
+ \else
+ \expandafter\math_fraction_inner_margin
+ \fi}
+
+\setvalue{\??mathfractionalternative\v!outer}%
+ {\ifcase\d_math_fraction_margin
+ \expandafter\math_fraction_outer_normal
+ \else
+ \expandafter\math_fraction_outer_margin
+ \fi}
+
+\setvalue{\??mathfractionalternative\v!both}%
+ {\ifcase\d_math_fraction_margin
+ \expandafter\math_fraction_both_normal
+ \else
+ \expandafter\math_fraction_both_margin
+ \fi}
+
+% todo: store first state and reuse second time
+
+% \def\math_fraction_inner_normal#1#2%
+% {\Ustack{%
+% {%
+% {\usemathstyleparameter\mathfractionparameter{\m_fractions_strut_top#1}}%
+% \math_frac_command
+% {\usemathstyleparameter\mathfractionparameter{\m_fractions_strut_bot#2}}%
+% }%
+% }%
+% \math_frac_wrapup}
+%
+% \def\math_fraction_outer_normal#1#2%
+% {\Ustack{%
+% \usemathstyleparameter\mathfractionparameter
+% {%
+% {\m_fractions_strut_top#1}%
+% \math_frac_command
+% {\m_fractions_strut_bot#2}%
+% }%
+% }%
+% \math_frac_wrapup}
+%
+% \def\math_fraction_both_normal#1#2%
+% {\Ustack{%
+% \usemathstyleparameter\mathfractionparameter
+% {%
+% {\usemathstyleparameter\mathfractionparameter\m_fractions_strut_top#1}%
+% \math_frac_command
+% {\usemathstyleparameter\mathfractionparameter\m_fractions_strut_bot#2}%
+% }%
+% }%
+% \math_frac_wrapup}
+%
+% \def\math_fraction_inner_margin#1#2%
+% {\Ustack{%
+% {%
+% {\kern\d_math_fraction_margin
+% \usemathstyleparameter\mathfractionparameter{\m_fractions_strut_top#1}%
+% \kern\d_math_fraction_margin}%
+% \math_frac_command
+% {\kern\d_math_fraction_margin
+% \usemathstyleparameter\mathfractionparameter{\m_fractions_strut_bot#2}%
+% \kern\d_math_fraction_margin}%
+% }%
+% }%
+% \math_frac_wrapup}
+%
+% \def\math_fraction_outer_margin#1#2%
+% {\Ustack{%
+% \usemathstyleparameter\mathfractionparameter
+% {%
+% {\kern\d_math_fraction_margin
+% \m_fractions_strut_top#1%
+% \kern\d_math_fraction_margin}%
+% \math_frac_command
+% {\kern\d_math_fraction_margin
+% \m_fractions_strut_bot#2%
+% \kern\d_math_fraction_margin}%
+% }%
+% }%
+% \math_frac_wrapup}
+%
+% \def\math_fraction_both_margin#1#2%
+% {\Ustack{%
+% \usemathstyleparameter\mathfractionparameter
+% {%
+% {\kern\d_math_fraction_margin
+% \usemathstyleparameter\mathfractionparameter\m_fractions_strut_top#1%
+% \kern\d_math_fraction_margin}%
+% \math_frac_command
+% {\kern\d_math_fraction_margin
+% \usemathstyleparameter\mathfractionparameter\m_fractions_strut_bot#2%
+% \kern\d_math_fraction_margin}%
+% }%
+% }%
+% \math_frac_wrapup}
+
+\def\math_frac_command_u
+ {\clf_umathfraction
+ {\mathfractionparameter\c!rule}%
+ \ifempty\p_math_fraction_fences
+ \mathfractionparameter\c!left \space
+ \mathfractionparameter\c!right\space
+ \else
+ \math_frac_no_delim\space
+ \math_frac_no_delim\space
+ \fi
+ \dimexpr\mathfractionparameter\c!rulethickness\relax
+ \relax}
+
+\def\math_fraction_inner_normal#1#2%
+ {\Ustack{% forces num style
+ \math_frac_command_u
+ {\usemathstyleparameter\mathfractionparameter{\m_fractions_strut_top#1}}%
+ {\usemathstyleparameter\mathfractionparameter{\m_fractions_strut_bot#2}}%
+ }%
+ \math_frac_wrapup}
+
+\def\math_fraction_outer_normal#1#2%
+ {\Ustack{% forces num style
+ \usemathstyleparameter\mathfractionparameter
+ \math_frac_command_u
+ {\m_fractions_strut_top#1}%
+ {\m_fractions_strut_bot#2}%
+ }%
+ \math_frac_wrapup}
+
+\def\math_fraction_both_normal#1#2%
+ {\Ustack{% forces num style
+ \usemathstyleparameter\mathfractionparameter
+ \math_frac_command_u
+ {\usemathstyleparameter\mathfractionparameter\m_fractions_strut_top#1}%
+ {\usemathstyleparameter\mathfractionparameter\m_fractions_strut_bot#2}%
+ }%
+ \math_frac_wrapup}
+
+\def\math_fraction_inner_margin#1#2%
+ {\Ustack{%
+ {%
+ \math_frac_command_u
+ {\kern\d_math_fraction_margin
+ \usemathstyleparameter\mathfractionparameter{\m_fractions_strut_top#1}%
+ \kern\d_math_fraction_margin}%
+ {\kern\d_math_fraction_margin
+ \usemathstyleparameter\mathfractionparameter{\m_fractions_strut_bot#2}%
+ \kern\d_math_fraction_margin}%
+ }%
+ }%
+ \math_frac_wrapup}
+
+\def\math_fraction_outer_margin#1#2%
+ {\Ustack{%
+ \usemathstyleparameter\mathfractionparameter
+ {%
+ \math_frac_command_u
+ {\kern\d_math_fraction_margin
+ \m_fractions_strut_top#1%
+ \kern\d_math_fraction_margin}%
+ {\kern\d_math_fraction_margin
+ \m_fractions_strut_bot#2%
+ \kern\d_math_fraction_margin}%
+ }%
+ }%
+ \math_frac_wrapup}
+
+\def\math_fraction_both_margin#1#2%
+ {\Ustack{%
+ \usemathstyleparameter\mathfractionparameter
+ {%
+ \math_frac_command_u
+ {\kern\d_math_fraction_margin
+ \usemathstyleparameter\mathfractionparameter\m_fractions_strut_top#1%
+ \kern\d_math_fraction_margin}%
+ {\kern\d_math_fraction_margin
+ \usemathstyleparameter\mathfractionparameter\m_fractions_strut_bot#2%
+ \kern\d_math_fraction_margin}%
+ }%
+ }%
+ \math_frac_wrapup}
+
+\definemathfraction[xfrac] [\c!alternative=\v!inner,\c!mathstyle=\s!script]
+\definemathfraction[xxfrac][\c!alternative=\v!inner,\c!mathstyle=\s!scriptscript]
+
+\aliased\let\normalxfrac\xfrac
+
+\permanent\protected\def\xfrac#1#2{\normalxfrac{\enforced\let\xfrac\xxfrac#1}{\enforced\let\xfrac\xxfrac#2}}
+
+%D Spacing (sensitive for definition of \triggermathstyle:
+
+% \ifcase\contextlmtxmode
+
+\permanent\protected\def\nomathfractiongaps {\normalexpanded{\math_no_fraction_gaps \mathstyletrigger\mathstyle}} % maybe collect settings
+\permanent\protected\def\overlaymathfractiongaps{\normalexpanded{\math_overlay_fraction_gaps\mathstyletrigger\mathstyle}} % maybe collect settings
+
+\protected\def\math_no_fraction_gaps#1%
+ {\Umathfractionnumup #1\zeropoint
+ \Umathfractiondenomdown#1\zeropoint}
+
+\protected\def\math_overlay_fraction_gaps#1%
+ {\Umathfractionnumup #1\zeropoint
+ \Umathfractionnumvgap #1\zeropoint
+ %Umathfractionrule #1\zeropoint
+ \Umathfractiondenomvgap#1\zeropoint
+ \Umathfractiondenomdown#1\zeropoint}
+
+% \else
+
+% \protected\def\math_no_fraction_gaps
+% {\Umathfractionnumup \mathstyle\zeropoint
+% \Umathfractiondenomdown\mathstyle\zeropoint}
+%
+% \protected\def\math_overlay_fraction_gaps
+% {\Umathfractionnumup \mathstyle\zeropoint
+% \Umathfractionnumvgap \mathstyle\zeropoint
+% %Umathfractionrule \mathstyle\zeropoint
+% \Umathfractiondenomvgap\mathstyle\zeropoint
+% \Umathfractiondenomdown\mathstyle\zeropoint}
+
+% \fi
+
+\installcorenamespace{mathfractiondistance}
+
+\letvalue{\??mathfractiondistance\v!none }\nomathfractiongaps
+\letvalue{\??mathfractiondistance\v!no }\nomathfractiongaps
+\letvalue{\??mathfractiondistance\v!overlay}\overlaymathfractiongaps
+
+\setupmathfractions
+ [\c!distance=\v!none]
+
+\appendtoks
+ \edef\p_distance{\rootmathfractionparameter\c!distance}%
+ \ifempty\p_distance\else
+ \ifcsname\??mathfractiondistance\p_distance\endcsname
+ \lastnamedcs
+ \fi
+ \fi
+\to \everymathematics
+
+% theshold is new!
+
+\let\math_fraction_set_threshold_inline \relax
+\let\math_fraction_set_threshold_display\relax
+
+\appendtoks
+ \math_fraction_set_threshold_inline
+ \math_fraction_set_threshold_display
+\to \everymathematics
+
+\appendtoks
+ \ifempty\currentmathfraction
+ \edef\p_threshold{\mathfractionparameter\c!inlinethreshold}%
+ \ifempty\p_threshold
+ \let\math_fraction_set_threshold_inline\relax
+ \orelse\ifx\p_threshold\v!auto
+ \let\math_fraction_set_threshold_inline\relax
+ \else
+ \let\math_fraction_set_threshold_inline\math_fraction_set_theshold_inline
+ \fi
+ \edef\p_threshold{\mathfractionparameter\c!displaythreshold}%
+ \ifempty\p_threshold
+ \let\math_fraction_set_threshold_display\relax
+ \orelse\ifx\p_threshold\v!auto
+ \let\math_fraction_set_threshold_display\relax
+ \else
+ \let\math_fraction_set_threshold_display\math_fraction_set_theshold_display
+ \fi
+ \fi
+\to \everysetupmathfraction
+
+\def\math_fraction_set_theshold_inline
+ {\edef\p_threshold{\mathfractionparameter\c!inlinethreshold}%
+ \Umathfractiondelsize\textstyle \p_threshold\dimexpr\textface\relax
+ \Umathfractiondelsize\scriptstyle \p_threshold\dimexpr\scriptface\relax
+ \Umathfractiondelsize\scriptscriptstyle\p_threshold\dimexpr\scriptscriptface\relax}
+
+\def\math_fraction_set_theshold_display
+ {\edef\p_threshold{\mathfractionparameter\c!displaythreshold}%
+ \Umathfractiondelsize\displaystyle \p_threshold\dimexpr\textface\relax}
+
+%D \macros
+%D {dfrac, tfrac, frac, dbinom, tbinom, binom}
+%D
+%D \startbuffer
+%D $\dfrac {1}{2} \tfrac {1}{2} \frac {1}{2}$
+%D $\dbinom{1}{2} \tbinom{1}{2} \binom{1}{2}$
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \getbuffer
+
+% $$
+% {{a}\over{b}} +
+% {{a}\overwithdelims(){b}} +
+% {{a}\atopwithdelims(){b}} +
+% {{a}\abovewithdelims()\zeropoint{b}} +
+% \left({{a}\over{b}}\right)
+% $$
+
+% \dorecurse {10} { % weird
+% $$ {{a}\abovewithdelims()#1pt{b}} $$
+% }
+
+\definemathfraction[i:frac] [\c!alternative=\v!inner,\c!mathstyle=] % was script and then small but nothing needed
+\definemathfraction[i:tfrac][\c!alternative=\v!inner,\c!mathstyle=\s!text] % was script (before luatex fix)
+\definemathfraction[i:sfrac][\c!alternative=\v!inner,\c!mathstyle=\s!scriptscript]
+\definemathfraction[i:dfrac][\c!alternative=\v!inner,\c!mathstyle=\s!display]
+
+\definemathfraction[d:frac] [\c!alternative=\v!inner,\c!mathstyle=\s!cramped] % was cramped,text
+\definemathfraction[d:tfrac][\c!alternative=\v!both ,\c!mathstyle={\s!cramped,\s!text}] % was cramped,script (before luatex fix)
+\definemathfraction[d:sfrac][\c!alternative=\v!both ,\c!mathstyle={\s!cramped,\s!scriptscript}]
+\definemathfraction[d:dfrac][\c!alternative=\v!inner,\c!mathstyle=\s!display]
+
+%D \protected\def\ShowMathFractions#1#2%
+%D {\dontleavehmode
+%D \begingroup
+%D \showmathstruts
+%D \mathematics{x+\tfrac{#1}{#2}+1+\frac{#1}{#2}+2+\sfrac{#1}{#2}+g}%
+%D \endgroup}
+%D
+%D The default \type {tfrac}, \type {frac} and \type \sfrac} look like this:
+%D
+%D \blank
+%D \ShowMathFractions{a}{a}\par
+%D \ShowMathFractions{1}{x}\par
+%D \ShowMathFractions{a}{b}\par
+%D \ShowMathFractions{1}{b}\par
+%D \blank
+
+\permanent\protected\def\frac {\csname\inlineordisplaymath id:frac\endcsname}
+\permanent\protected\def\tfrac{\csname\inlineordisplaymath id:tfrac\endcsname}
+\permanent\protected\def\sfrac{\csname\inlineordisplaymath id:sfrac\endcsname}
+\permanent\protected\def\dfrac{\csname\inlineordisplaymath id:dfrac\endcsname}
+
+\definemathfraction[ams] [\c!strut=\v!no,\c!alternative=\v!outer]
+\definemathfraction[i:ams:frac][ams][\c!mathstyle={\s!cramped,\s!text}]
+\definemathfraction[d:ams:frac][ams][\c!mathstyle={\s!cramped,\s!display}]
+
+\permanent\protected\def\ctxfrac{\csname\inlineordisplaymath id:frac\endcsname}
+\permanent\protected\def\amsfrac{\csname\inlineordisplaymath id:ams:frac\endcsname}
+
+% \appendtoks
+% \doifelse{\mathfractionparameter\c!option}{ams}%
+% {\let\frac\amsfrac}%
+% {\let\frac\ctxfrac}%
+% \to \everysetupmathfraction
+
+\appendtoks
+ \doifelse{\mathematicsparameter\c!fractions}{ams}%
+ {\enforced\let\frac\amsfrac}%
+ {\enforced\let\frac\ctxfrac}%
+\to \everysetupmathematics
+
+% \definemathfraction[ddfrac][\c!mathstyle=\s!display]
+% \definemathfraction[ttfrac][\c!mathstyle=\s!text]
+% \definemathfraction[ssfrac][\c!mathstyle=\s!script]
+
+% \protected\def\binom #1#2{{\Ustack {{#1}\normalabovewithdelims()\zeropoint{#2}}}}
+% \protected\def\dbinom#1#2{{\displaystyle{{#1}\normalabovewithdelims()\zeropoint{#2}}}}
+% \protected\def\tbinom#1#2{{\textstyle {{#1}\normalabovewithdelims()\zeropoint{#2}}}}
+
+\definemathfraction
+ [binom]
+ [\c!alternative=\v!outer,
+ \c!rule=\v!no,
+ \c!left=0x28,
+ \c!right=0x29,
+ \c!mathstyle=\s!auto]
+
+\definemathfraction
+ [dbinom]
+ [binom]
+ [\c!mathstyle=\s!display]
+
+\definemathfraction
+ [tbinom]
+ [binom]
+ [\c!mathstyle=\s!text]
+
+%D \macros
+%D {cfrac}
+%D
+%D \startbuffer
+%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$
+%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D Now we can align every combination we want:
+%D
+%D \startbuffer
+%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$
+%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$
+%D $\cfrac[cl]{12}{3} \cfrac[cc]{12}{3} \cfrac[cr]{12}{3}$
+%D $\cfrac[lc]{1}{23} \cfrac[cc]{1}{23} \cfrac[rc]{1}{23}$
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+
+\permanent\protected\def\cfrac
+ {\doifelsenextoptionalcs\math_cfrac_yes\math_cfrac_nop}
+
+\def\math_cfrac_nop {\math_cfrac_indeed[cc]}
+\def\math_cfrac_yes[#1]{\math_cfrac_indeed[#1cc]}
+
+\def\math_cfrac_indeed[#1#2#3]#4#5%
+ {{\displaystyle
+ \frac
+ {\strut
+ \ifx r#1\hfill\fi#4\ifx l#1\hfill\fi}%
+ {\ifx r#2\hfill\fi#5\ifx l#2\hfill\fi}%
+ \kern-\nulldelimiterspace}}
+
+%D \macros
+%D {splitfrac, splitdfrac}
+%D
+%D Occasionally one needs to typeset multi||line fractions.
+%D These commands use \tex{genfrac} to create such fractions.
+%D
+%D \startbuffer
+%D \startformula
+%D a=\frac{
+%D \splitfrac{xy + xy + xy + xy + xy}
+%D {+ xy + xy + xy + xy}
+%D }
+%D {z}
+%D =\frac{
+%D \splitdfrac{xy + xy + xy + xy + xy}
+%D {+ xy + xy + xy + xy}
+%D }
+%D {z}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D These macros are based on Michael J.~Downes posting on
+%D comp.text.tex on 2001/12/06 but adapted a bit.
+
+\permanent\protected\def\splitfrac #1#2{{\textstyle {{\textstyle#1\quad\hfill}\normalabove\zeropoint{\textstyle\hfill\quad\mathstrut#2}}}}
+\permanent\protected\def\splitdfrac#1#2{{\displaystyle{{ #1\quad\hfill}\normalabove\zeropoint{ \hfill\quad\mathstrut#2}}}}
+
+%D For testing:
+
+% \protected\def\ShowMathFractions#1#2%
+% {\mathematics{x+\tfrac{#1}{#2}+1+\frac{#1}{#2}+2+\sfrac{#1}{#2}+g}}
+
+%D More fracking (for Alan):
+
+\protected\def\math_frac_colored_vulgar#1#2%
+ {\savecolor
+ \colo_helpers_activate\p_math_fractions_color
+ {\restorecolor#1}\Uskewed/{\restorecolor#2}}
+
+\protected\def\math_frac_normal_vulgar#1#2%
+ {{#1}\Uskewed/{#2}}
+
+\permanent\protected\def\vfrac#1#2%
+ {\bgroup
+ \edef\p_math_fractions_color{\namedmathfractionparameter\s!vfrac\c!color}%
+ \ifempty\p_math_fractions_color
+ \expandafter\math_frac_normal_vulgar
+ \else
+ \expandafter\math_frac_colored_vulgar
+ \fi
+ {#1}%
+ {#2}%
+ \egroup}
+
+\appendtoks
+ \edef\p_hfactor{\namedmathfractionparameter\s!vfrac\c!hfactor}%
+ \edef\p_vfactor{\namedmathfractionparameter\s!vfrac\c!vfactor}%
+ \Umathskewedfractionhgap\textstyle \p_hfactor\fontemwidth \mathstylefont\textstyle
+ \Umathskewedfractionhgap\scriptstyle \p_hfactor\fontemwidth \mathstylefont\scriptstyle
+ \Umathskewedfractionhgap\scriptscriptstyle\p_hfactor\fontemwidth \mathstylefont\scriptscriptstyle
+ \Umathskewedfractionvgap\textstyle \p_vfactor\fontexheight\mathstylefont\textstyle
+ \Umathskewedfractionvgap\scriptstyle \p_vfactor\fontexheight\mathstylefont\scriptstyle
+ \Umathskewedfractionvgap\scriptscriptstyle\p_vfactor\fontexheight\mathstylefont\scriptscriptstyle
+\to \everysetupmathfraction
+
+\setupmathfraction
+ [\s!vfrac]
+ [\c!hfactor=.2,
+ \c!vfactor=.1]
+
+\protect \endinput
+
+% I have no clue what \mthfrac and \mthsqrt are supposed to do but
+% I guess that it can be done with tweaking luatex's math parameters.
+% Otherwise I'll write something from scratch.
+
+% \def\math_stylebuilders_frac#1#2#3#4#5#6#7%
+% {\begingroup
+% \mathsurround\zeropoint
+% \setbox0\hbox{$#1 #6$}%
+% \setbox2\hbox{$#1 #7$}%
+% \dimen0\wd\ifdim\wd2>\wd0 2\else 0\fi
+% \setbox4\hbox to \dimen0{\leaders\hbox{#4}\hss#5}%
+% \mathord{\vcenter{{\offinterlineskip
+% \hbox to \dimen0{\hss\box0\hss}%
+% \kern\ht4%
+% \hbox to \dimen0{\hss\copy4\hss}%
+% \kern\ht4%
+% \hbox to \dimen0{\hss\box2\hss}}}}%
+% \endgroup}
+%
+% \def\math_stylebuilders_sqrt#1#2#3#4#5%
+% {\begingroup
+% \mathsurround\zeropoint
+% \setbox0\hbox{$#1 #5$}%
+% \ht0\dimexpr1.05\ht0+\onepoint\relax
+% \dp0\dimexpr1.05\dp0+\onepoint\relax
+% \setbox4\hbox to \wd0{\mr#2\leaders\hbox{#3}\hfill#4}%
+% \delimitershortfall\zeropoint
+% \nulldelimiterspace\zeropoint
+% \setbox2\hbox{$\left\delimiter"0270370 \vrule \s!height\ht0 \s!depth \dp0 \s!width\zeropoint\right.$}% is this the right code point?
+% \mathord{\vcenter{\hbox{\copy2\rlap{\raise\dimexpr\ht2-\ht4\relax\copy4}\copy0}}}%
+% \endgroup}
+%
+% \def\mthfrac#1#2#3#4#5{\mathchoice
+% {\math_stylebuilders_frac\displaystyle \textface {#1}{#2}{#3}{#4}{#5}}%
+% {\math_stylebuilders_frac\textstyle \textface {#1}{#2}{#3}{#4}{#5}}%
+% {\math_stylebuilders_frac\scriptstyle \scriptface {#1}{#2}{#3}{#4}{#5}}%
+% {\math_stylebuilders_frac\scriptscriptstyle\scriptscriptface{#1}{#2}{#3}{#4}{#5}}}
+%
+% \def\mthsqrt#1#2#3{\mathchoice
+% {\math_stylebuilders_sqrt\displaystyle \textface{#1}{#2}{#3}}%
+% {\math_stylebuilders_sqrt\textstyle \textface{#1}{#2}{#3}}%
+% {\math_stylebuilders_sqrt\scriptstyle \textface{#1}{#2}{#3}}%
+% {\math_stylebuilders_sqrt\scriptscriptstyle\textface{#1}{#2}{#3}}}
+
+% \protected\def\mthfrac#1#2#3{[mthfrac: #1 #2 #3]}
+% \protected\def\mthsqrt#1#2#3{[mthsqrt: #1 #2 #3]}
+
+% used for prototyping \Uskewed
+%
+% \protected\def\skewedfractiona#1#2{%
+% \raise
+% \Umathskewedfractionvgap\textstyle
+% \hbox\bgroup
+% $\scriptstyle#1\hskip\dimexpr\Umathskewedfractionhgap\scriptstyle/2\relax$%
+% \egroup
+% \hbox to \zeropoint\bgroup
+% \hss$\textstyle/$\hss
+% \egroup
+% \lower
+% \Umathskewedfractionvgap\textstyle
+% \hbox\bgroup
+% $\hskip\dimexpr\Umathskewedfractionhgap\scriptstyle/2\relax\scriptstyle#2$%
+% \egroup
+% }
+%
+% \protected\def\skewedfractionb#1#2{%
+% \raise
+% \Umathskewedfractionvgap\textstyle
+% \hbox\bgroup
+% $\scriptstyle#1\hskip\dimexpr\Umathskewedfractionhgap\textstyle/2\relax$%
+% \egroup
+% \hbox to \zeropoint\bgroup
+% \hss$\textstyle/$\hss
+% \egroup
+% \lower
+% \Umathskewedfractionvgap\textstyle
+% \hbox\bgroup
+% $\hskip\dimexpr\Umathskewedfractionhgap\textstyle/2\relax\scriptstyle#2$%
+% \egroup
+% }
+%
+% $\skewedfractiona{1}{2}$
+% $\skewedfractionb{1}{2}$
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-inc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-inc.mkxl
new file mode 100644
index 00000000000..4db75e37e11
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-inc.mkxl
@@ -0,0 +1,69 @@
+%D \module
+%D [ file=math-inc,
+%D version=2018.06.23,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=XML inclusion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / XML inclusion}
+
+\registerctxluafile{math-inc}{}
+
+%D I had some doubt about including this in \CONTEXT\ but it might serve some users
+%D anyway. It's always the question to what extent one can be really roundtrip. I
+%D might improve it when I need it.
+
+% Talking about creating from a source ... June Lee's transcription of
+% Close to You by Jacob Collier is an amazing example:
+%
+% https://www.youtube.com/watch?v=hdBVN-HMuqI
+
+\unprotect
+
+\definesymbol[mmlattachment][{\infofont\darkred mml}]
+\definesymbol[mmlcomment] [{\infofont\darkblue mml}]
+
+\permanent\protected\def\lxml_add_mml_blob#1#2%
+ {\relax
+ \clf_xmlformulatobuffer\number\c_strc_formulas_n{temp-xml-export}%
+ #2%
+ [\c!symbol=#1,%
+ \c!space=\v!yes,%
+ \c!buffer=temp-xml-export,%
+ \c!name={formula-\number\c_strc_formulas_n.xml}]%
+ \relax}
+
+\permanent\protected\def\xmlattachmml
+ {\iftrialtypesetting\orelse\ifexporting\iflocation
+ \dostarttagged\t!ignore\empty
+ \lxml_add_mml_blob{mmlattachment}\attachment
+ \dostoptagged
+ \fi\fi}
+
+\permanent\protected\def\xmlcommentmml
+ {\iftrialtypesetting\orelse\ifexporting\iflocation
+ \dostarttagged\t!ignore\empty
+ \lxml_add_mml_blob{mmlcomment}\comment
+ \dostoptagged
+ \fi\fi}
+
+%D This kind of feature creep is not yet configurable, nor documented.
+
+\permanent\protected\def\xmladdmmlsource
+ {\iftrialtypesetting\orelse\ifexporting\iflocation
+ \dostarttagged\t!ignore\empty
+ \inleftmargin{%
+ \lxml_add_mml_blob{mmlattachment}\attachment
+ \quad
+ \lxml_add_mml_blob{mmlcomment}\comment
+ }%
+ \dostoptagged
+ \fi\fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-ini.mkxl
new file mode 100644
index 00000000000..9f541c3512b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-ini.mkxl
@@ -0,0 +1,3022 @@
+%D \module
+%D [ file=math-ini,
+%D version=2008.01.02,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Initializations,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Initializations}
+
+% todo: hpack as we skip math anyway
+%
+% todo: 0x2061 : function application
+% todo: 0x2062 : invisible times
+% todo: 0x2063 : invisible comma
+
+% a bit tricky way to set ... no (pseudo) registers but math hash values:
+%
+% \normalexpanded{\Umathlimitabovevgap\displaystyle=40\dimexpr\the\Umathlimitabovevgap\displaystyle\relax}
+
+% Todo in luatex maincontrol.w: also accept a number here:
+%
+% case set_math_param_cmd:
+% p = cur_chr;
+% get_token();
+% if (cur_cmd != math_style_cmd) {
+%
+% plus two new math styles: larger/smaller
+%
+% \protected\def\Umathshow#1%
+% {\hbox{\infofont(\string#1:\the#1\textstyle,\the#1\scriptstyle,\the#1\scriptscriptstyle)}}
+
+%D This module provides namespaces for math fonts, thereby permitting mixed usage of
+%D math fonts. Although not strictly needed, we also provide a family name mapping
+%D mechanism as used in the (original) AMS math definition files, but here these
+%D names can recursively be remapped and if needed, dynamically be changed. We've
+%D tried to minimize the number of definition commands and use plain \TEX\
+%D definitions as fallback. We've tried to follow a couple of conventions from plain
+%D and AMS math in order to achieve backward compatinility. We also kept an eye on
+%D future usage of these modules in the perspective of MathML and unicode fonts.
+
+%D There is a subtle issue with grouping: the \type {\begingroup} method will not
+%D restore a changed mathstyle so best avoid that one. However, there are cases where
+%D we really need to use such grouping.
+
+% Weird, these fail, maybe amp is solved in a later state from char noads (needs a
+% fix in luatex):
+%
+% $\char"26$
+% $\a$
+% $\string&$
+
+% mathop applied to characters centers it vertically
+
+\unprotect
+
+%D We move these definitions into the format:
+
+% test [[\char948 \cldcontext{utf.char(948)}]]
+% test $[[\char948 \cldcontext{utf.char(948)}]]$
+
+\registerctxluafile{math-ini}{}
+\registerctxluafile{math-dim}{}
+\registerctxluafile{math-act}{autosuffix}
+\registerctxluafile{math-ext}{}
+\registerctxluafile{math-vfu}{autosuffix}
+\registerctxluafile{math-ttv}{}
+\registerctxluafile{math-map}{optimize}
+\registerctxluafile{math-ren}{}
+\registerctxluafile{math-noa}{autosuffix,optimize}
+\registerctxluafile{math-tag}{autosuffix}
+\registerctxluafile{math-fbk}{}
+\registerctxluafile{math-dir}{}
+\registerctxluafile{math-spa}{}
+
+%D A starter:
+%D
+%D \startbuffer
+%D \mathsurround 10pt
+%D \mathsurroundskip30pt
+%D x $x + \ruledhbox{$x$} + x$ x
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \start \blank \getbuffer \blank \stop
+
+\newcount\mathnestinglevel
+
+\appendtoks
+ \advance\mathnestinglevel\plusone
+\to \everymathematics
+
+%D A few compatibility helpers:
+
+\permanent\def\Umathbotaccent{\Umathaccent \s!bottom }
+\permanent\def\Umathtopaccent{\Umathaccent \s!top }
+\permanent\def\Umathaccents {\Umathaccent \s!both } % strange highlighting
+
+\ifdefined\Umathcharclass \else
+ \permanent\def\Umathcharclass{\numexpr\cldcontext{tex.getmathcode(token.scan_int())[1]}\relax}
+ \permanent\def\Umathcharfam {\numexpr\cldcontext{tex.getmathcode(token.scan_int())[2]}\relax}
+ \permanent\def\Umathcharslot {\numexpr\cldcontext{tex.getmathcode(token.scan_int())[3]}\relax}
+\fi
+
+%D The attributes that we will use (todo: pack some into one but uglier code):
+
+\definesystemattribute[mathalphabet] [public]
+\definesystemattribute[mathsize] [public]
+\definesystemattribute[mathpunctuation][public]
+\definesystemattribute[mathgreek] [public] % will become generic
+\definesystemattribute[mathalternate] [public]
+\definesystemattribute[mathrendering] [public]
+\definesystemattribute[mathcategory] [public]
+\definesystemattribute[mathmode] [public]
+\definesystemattribute[mathitalics] [public]
+\definesystemattribute[mathkernpairs] [public]
+\definesystemattribute[mathbidi] [public]
+\definesystemattribute[mathdomain] [public]
+\definesystemattribute[mathcollapsing] [public]
+\definesystemattribute[mathunstack] [public]
+
+\definesystemattribute[displaymath] [public]
+
+\mathflattenmode 31
+
+\ifdefined\ordlimits\else
+ \let\ordlimits\limits
+\fi
+
+\appendtoks
+ \c_attr_mathmode\plusone
+\to \everydisplay
+
+\appendtoks
+ \c_attr_mathmode \plusone
+ \c_attr_displaymath\plusone
+\to \everybeforedisplayformula
+
+\defaultmathfamily \zerocount % 255
+
+\permanent\protected\def\resetmathattributes{\clf_resetmathattributes}
+
+% handy
+
+\newconditional\indisplaymath
+
+\appendtoks
+ \setfalse\indisplaymath
+\to \everymath
+
+\appendtoks
+ \settrue\indisplaymath
+\to \everydisplay
+
+\permanent\def\inlineordisplaymath{\ifconditional\indisplaymath\expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments\fi}
+
+\permanent\protected\def\forcedisplaymath
+ {\ifmmode
+ \displaystyle
+ \settrue\indisplaymath
+ \fi}
+
+\permanent\protected\def\startforceddisplaymath
+ {\startimath
+ \displaystyle
+ \begingroup
+ \settrue\indisplaymath}
+
+\permanent\protected\def\stopforceddisplaymath
+ {\endgroup
+ \stopimath}
+
+\permanent\protected\def\startpickupmath % for the moment private
+ {\ifconditional\indisplaymath
+ \startforceddisplaymath
+ \enforced\let\stoppickupmath\stopforceddisplaymath
+ \else
+ \startimath
+ \enforced\let\stoppickupmath\stopimath
+ \fi}
+
+\aliased\let\stoppickupmath\relax
+
+% \protected\def\rawmathcharacter#1% slow but only for tracing
+% {\begingroup
+% \ifmmode
+% \resetmathattributes\Uchar#1%
+% \else
+% \startimath\resetmathattributes\Uchar#1\stopimath
+% \fi
+% \endgroup}
+
+\permanent\protected\def\rawmathematics#1% slow but only for tracing
+ {\begingroup
+ \ifmmode
+ \clf_resetmathattributes#1%
+ \else
+ \startimath\clf_resetmathattributes#1\stopimath
+ \fi
+ \endgroup}
+
+%D Some measures (maybe spac-mth):
+
+% \def\mathskipsmall {\mskip\thinmuskip}
+% \def\mathskipmedium{\mskip\medmuskip}
+% \def\mathskipbig {\mskip\thickmuskip}
+
+%D \macros
+%D {setupmathematics}
+%D
+%D Configuration for integrals. (If needed we can speed this up and make it
+%D installable; no processaction is needed then).
+
+\installcorenamespace{mathematics}
+
+\installswitchcommandhandler \??mathematics {mathematics} \??mathematics
+
+\installmacrostack\currentmathematics
+
+% \permanent\protected\def\startmathematics % no grouping, if ever then also an optional second
+% {\doifelsenextoptionalcs\math_mathematics_start_yes\math_mathematics_start_nop}
+%
+% \protected\def\math_mathematics_start_yes[#1]%
+% {\push_macro_currentmathematics
+% \edef\currentmathematics{#1}% check for valid
+% \the\everyswitchmathematics}
+%
+% \protected\def\math_mathematics_start_nop
+% {\push_macro_currentmathematics
+% \let\currentmathematics\empty
+% \the\everyswitchmathematics}
+
+\permanent\protected\tolerant\def\startmathematics[#1]% no grouping, if ever then also an optional second
+ {\push_macro_currentmathematics
+ \edef\currentmathematics{#1}% check for valid
+ \the\everyswitchmathematics}
+
+\permanent\protected\def\stopmathematics
+ {\pop_macro_currentmathematics
+ \the\everyswitchmathematics} % only needed at level zero
+
+\definemathematics[\v!default] % not needed, but nicer when nesting back to normal
+
+% Now we redefine \type {\mathematics} and \type {\m}:
+
+\pushoverloadmode
+
+ \permanent\protected\def\mathematics{\doifelsenextoptionalcs\math_m_yes\math_m_nop}
+
+ \aliased\let\m \mathematics % we keep the simple versions
+ \aliased\let\math\mathematics % we keep the simple versions
+
+\popoverloadmode
+
+\def\math_m_yes
+ {\relax
+ \ifmmode
+ \expandafter\math_m_yes_math
+ \else
+ \expandafter\math_m_yes_text
+ \fi}
+
+\def\math_m_yes_math[#1]#2%
+ {#2}
+
+\def\math_m_yes_text[#1]%
+ {\begingroup
+ \ifcondition\validassignment{#1}%
+ \setupcurrentmathematics[#1]%
+ \else
+ \edef\currentmathematics{#1}% check for valid
+ \fi
+ \edef\p_openup{\mathematicsparameter\c!openup}%
+ \ifx\p_openup\v!yes
+ \expandafter\math_m_yes_text_openedup
+ \else
+ \expandafter\math_m_yes_text_normal
+ \fi}
+
+\def\math_m_yes_text_openedup#1%
+ {\setbox\scratchbox\hbox\bgroup
+ \the\everyswitchmathematics\relax
+ \normalstartimath
+ #1%
+ \normalstopimath
+ \egroup
+ \ifdim\ht\scratchbox>\strutht
+ \math_inline_openup_start_yes
+ \orelse\ifdim\dp\scratchbox>\strutdp
+ \math_inline_openup_start_yes
+ \else
+ \math_inline_openup_start_nop
+ \fi
+ \unhbox\scratchbox % \normalstartimath#1\normalstopimath
+ \math_inline_openup_stop
+ \endgroup}
+
+\def\math_m_yes_text_normal#1%
+ {\the\everyswitchmathematics\relax
+ \normalstartimath
+ #1%
+ \normalstopimath
+ \endgroup}
+
+\def\math_m_nop#1%
+ {\relax
+ \ifmmode
+ #1%
+ \else
+ \normalstartimath
+ #1%
+ \normalstopimath
+ \fi}
+
+% e.g.: \definemathematics[i:mp][setups=i:tight,openup=yes]
+
+\immutable\mugluespecdef\defaultthickmuskip 5mu plus 5mu
+\immutable\mugluespecdef\defaultmedmuskip 4mu plus 2mu minus 4mu
+\immutable\mugluespecdef\defaultthinmuskip 3mu
+
+\immutable\mugluespecdef\halfthickmuskip 2.5mu plus 2.5mu
+\immutable\mugluespecdef\halfmedmuskip 2.0mu plus 1.0mu minus 2.0mu
+\immutable\mugluespecdef\halfthinmuskip 1.5mu
+
+\immutable\mugluespecdef\hairmuskip .15mu
+
+\immutable\integerdef \defaultrelpenalty 500
+\immutable\integerdef \defaultbinoppenalty 700
+\immutable\integerdef \defaultprerelpenalty -100
+\immutable\integerdef \defaultprebinoppenalty -100
+
+% we need to control these otherwise:
+%
+% \prerelpenalty \defaultprerelpenalty
+% \prebinoppenalty\defaultprebinoppenalty
+
+\startsetups math:spacing:default
+ \thickmuskip \defaultthickmuskip
+ \medmuskip \defaultmedmuskip
+ \thinmuskip \defaultthinmuskip
+ \relpenalty \defaultrelpenalty
+ \binoppenalty \defaultbinoppenalty
+ \prebinoppenalty\maxdimen
+ \prerelpenalty \maxdimen
+\stopsetups
+
+\startsetups math:spacing:split
+ \thickmuskip \defaultthickmuskip
+ \medmuskip \defaultmedmuskip
+ \thinmuskip \defaultthinmuskip
+ \relpenalty \defaultrelpenalty
+ \binoppenalty \defaultbinoppenalty
+ \prebinoppenalty\defaultprebinoppenalty
+ \prerelpenalty \defaultprerelpenalty
+\stopsetups
+
+\startsetups math:spacing:half
+ \thickmuskip \halfthickmuskip
+ \medmuskip \halfmedmuskip
+ \thinmuskip \halfthinmuskip
+ \relpenalty \defaultrelpenalty
+ \binoppenalty \defaultbinoppenalty
+ \prebinoppenalty\maxdimen
+ \prerelpenalty \maxdimen
+\stopsetups
+
+\startsetups math:spacing:tight
+ \ifcase\raggedstatus
+ \thickmuskip \halfthickmuskip
+ \medmuskip \halfmedmuskip
+ \thinmuskip \halfthinmuskip
+ \else
+ \thickmuskip 1\halfthickmuskip
+ \medmuskip 1\halfmedmuskip
+ \thinmuskip 1\halfthinmuskip
+ \fi
+ \relpenalty \defaultrelpenalty
+ \binoppenalty \maxdimen
+ \prebinoppenalty\maxdimen
+ \prerelpenalty \maxdimen
+\stopsetups
+
+\startsetups math:spacing:fixed
+ \ifcase\raggedstatus
+ \thickmuskip \halfthickmuskip
+ \medmuskip \halfmedmuskip
+ \thinmuskip \halfthinmuskip
+ \else
+ \thickmuskip 1\halfthickmuskip
+ \medmuskip 1\halfmedmuskip
+ \thinmuskip 1\halfthinmuskip
+ \fi
+ \relpenalty \maxdimen
+ \binoppenalty \maxdimen
+ \prebinoppenalty\maxdimen
+ \prerelpenalty \maxdimen
+\stopsetups
+
+% \dorecurse{80}{test \m[i:tight]{\red \fakeformula} test }
+
+\definemathematics[i:default][\c!setups=math:spacing:equal]
+\definemathematics[i:half] [\c!setups=math:spacing:half]
+\definemathematics[i:tight] [\c!setups=math:spacing:tight]
+\definemathematics[i:fixed] [\c!setups=math:spacing:fixed]
+
+% Normally the next is applied to only one character.
+%
+% $ABC$ $\cal ABC$ $\mathaltcal ABC$
+
+% todo: only in mmode
+% these commands are semi-public but should not be used directly (lua names wil change)
+
+\permanent\protected\def\setmathattribute #1#2{\ifmmode\clf_setmathattribute{#1}{#2}\fi}
+\permanent\protected\def\setmathalphabet #1{\ifmmode\clf_setmathalphabet{#1}\fi}
+\permanent\protected\def\setmathfontstyle #1{\ifmmode\clf_setmathstyle{#1}\fi}
+\permanent\protected\def\setmathfontalternate#1{\ifmmode\clf_setmathalternate\defaultmathfamily{#1}\fi}
+
+\installcorenamespace{mathstylealternative} % might become a setuphandler
+
+\permanent\protected\def\setmathfontstylealternate#1%
+ {\ifcsname\??mathstylealternative\fontclass:#1\endcsname
+ \expandafter\setmathfontalternate\lastnamedcs
+ \orelse\ifcsname\??mathstylealternative#1\endcsname
+ \expandafter\setmathfontalternate\lastnamedcs
+ \fi}
+
+\permanent\tolerant\protected\def\setupmathrendering[#1]#*[#2]% the name might change
+ {\ifparameter#2\or
+ \getparameters[\??mathstylealternative#1:][#2]%
+ \else
+ \getparameters[\??mathstylealternative][#1]%
+ \fi}
+
+\appendtoks
+ \edef\p_stylealternative{\mathematicsparameter\c!stylealternative}%
+ \ifempty\p_stylealternative\else
+ \clf_presetmathalternate\defaultmathfamily{\p_stylealternative}%
+ \fi
+\to \everymathematics
+
+% if there were many features we could have a feature pass over math nodes but it makes no
+% sense now so we have commands to deal with it
+
+% \enabletrackers[math.alternates]
+% \setupbodyfont[lucidaot]
+%
+% \startTEXpage
+% \setupmathematics[stylealternative={reset}]$x+\mathcal A$\par
+% \setupmathematics[stylealternative={reset,calligraphic}]$x+\mathcal A$\par
+% \setupmathematics[stylealternative={reset,italic}]$x+\mathcal A$\par
+% \setupmathematics[stylealternative={reset,calligraphic,italic}]$x+\mathcal A$
+% \stopTEXpage
+
+% no longer needed now we have enforce so the next few lines will go
+
+% \pushoverloadmode
+% \enforced\let\dotlessi\dotlessi
+% \enforced\let\dotlessj\dotlessj
+% \popoverloadmode
+
+\permanent\protected\def\mathaltcalligraphic{\setmathfontalternate{calligraphic}\cal} % set via goody file
+\permanent\protected\def\mathaltitalic {\setmathfontalternate{italic}} % set via goody file
+\permanent\protected\def\mathslashedzero {\begingroup\setmathfontalternate{zero}∅\endgroup} % set via goody file or automatic
+\permanent\protected\def\mathdotless {\setmathfontalternate{dotless}} % set via goody file or automatic
+\permanent\protected\def\mathdotlessi {\begingroup\mathdotless i\endgroup}
+\permanent\protected\def\mathdotlessj {\begingroup\mathdotless j\endgroup}
+
+\aliased\let\mathaltcal\mathaltcalligraphic
+\aliased\let\mathaltit \mathaltitalic
+
+%aliased\let\textslashedzero\slashedzero \permanent\protected\def\autoslashedzero{\mathortext\mathslashedzero\textslashedzero}
+\aliased\let\textdotlessi \dotlessi \permanent\protected\def\autodotlessi {\mathortext\mathdotlessi \textdotlessi}
+\aliased\let\textdotlessj \dotlessj \permanent\protected\def\autodotlessj {\mathortext\mathdotlessj \textdotlessj}
+
+\appendtoks
+ %enforced\let\slashedzero\autoslashedzero
+ \enforced\let\dotlessi\autodotlessi
+ \enforced\let\dotlessj\autodotlessj
+\to \everymathematics
+
+\aliased\let\setmathfontalternative \setmathfontalternate
+\aliased\let\setmathfontstylealternative\setmathfontstylealternate
+\aliased\let\mathalternate \setmathfontalternate
+
+\permanent\protected\def\mathupright {\setmathattribute\s!regular\s!tf\setmathfontstylealternate\s!tf}
+\permanent\protected\def\mathitalic {\setmathattribute\s!regular\s!it\setmathfontstylealternate\s!it}
+\permanent\protected\def\mathscript {\setmathalphabet \s!script \setmathfontstylealternate\s!script}
+\permanent\protected\def\mathfraktur {\setmathalphabet \s!fraktur \setmathfontstylealternate\s!fraktur}
+\permanent\protected\def\mathblackboard{\setmathalphabet \s!blackboard \setmathfontstylealternate\s!blackboard}
+
+\permanent\protected\def\mathrm {\setmathattribute\s!rm\s!tf \setmathfontstylealternate\s!tf}
+\permanent\protected\def\mathss {\setmathattribute\s!ss\s!tf \setmathfontstylealternate\s!tf}
+\permanent\protected\def\mathtt {\setmathattribute\s!tt\s!tf \setmathfontstylealternate\s!tf}
+
+\permanent\protected\def\mathtf {\setmathfontstyle\s!tf \setmathfontstylealternate\s!tf}
+\permanent\protected\def\mathsl {\setmathfontstyle\s!it \setmathfontstylealternate\s!it} % no sl
+\permanent\protected\def\mathit {\setmathfontstyle\s!it \setmathfontstylealternate\s!it}
+
+\permanent\protected\def\mathbf {\setmathfontstyle\s!bf \setmathfontstylealternate\s!bf}
+\permanent\protected\def\mathbs {\setmathfontstyle\s!bi \setmathfontstylealternate\s!bi} % no sl
+\permanent\protected\def\mathbi {\setmathfontstyle\s!bi \setmathfontstylealternate\s!bi}
+
+\aliased\let\mathdefault\mathitalic
+
+\appendtoks
+ \edef\p_default{\mathematicsparameter\c!default}%
+ \ifx\p_default\v!normal
+ \enforced\let\mathdefault\mathupright
+ \else
+ \enforced\let\mathdefault\mathitalic
+ \fi
+\to \everysetupmathematics
+
+\setupmathematics
+ [\c!default=\v!italic]
+
+\aliased\let\tfmath\mathtf % maybe a grouped command
+\aliased\let\slmath\mathsl
+\aliased\let\itmath\mathit
+
+\aliased\let\bfmath\mathbf
+\aliased\let\bsmath\mathbs
+\aliased\let\bimath\mathbi
+
+\aliased\let\Bbb\mathblackboard
+
+\permanent\protected\def\frak {\ifmmode\expandafter\mathfraktur \fi}
+\permanent\protected\def\cal {\ifmmode\expandafter\mathscript \fi}
+\permanent\protected\def\bbd {\ifmmode\expandafter\mathblackboard\fi}
+\permanent\protected\def\blackboard{\ifmmode\expandafter\mathblackboard\fi}
+\permanent\protected\def\fraktur {\ifmmode\expandafter\mathfraktur \fi}
+\permanent\protected\def\gothic {\ifmmode\expandafter\mathfraktur \fi}
+
+\permanent\protected\def\mathcal #1{{\mathscript #1}} % for AMS compatibility
+\permanent\protected\def\mathfrak#1{{\mathfraktur #1}} % for AMS compatibility
+\permanent\protected\def\mathbb #1{{\mathblackboard#1}} % for AMS compatibility
+
+\ifdefined\normaltf \else \permanent\let\normaltf\tf \fi \permanent\protected\def\tf{\ifmmode\mathtf\else\normaltf\fi}
+\ifdefined\normalbf \else \permanent\let\normalbf\bf \fi \permanent\protected\def\bf{\ifmmode\mathbf\else\normalbf\fi}
+\ifdefined\normalit \else \permanent\let\normalit\it \fi \permanent\protected\def\it{\ifmmode\mathit\else\normalit\fi}
+\ifdefined\normalsl \else \permanent\let\normalsl\sl \fi \permanent\protected\def\sl{\ifmmode\mathsl\else\normalsl\fi}
+\ifdefined\normalbi \else \permanent\let\normalbi\bi \fi \permanent\protected\def\bi{\ifmmode\mathbi\else\normalbi\fi}
+\ifdefined\normalbs \else \permanent\let\normalbs\bs \fi \permanent\protected\def\bs{\ifmmode\mathbs\else\normalbs\fi}
+
+\permanent\protected\def\rm{\ifmmode\mathrm\else\normalrm\fi}
+\permanent\protected\def\ss{\ifmmode\mathss\else\normalss\fi}
+\permanent\protected\def\tt{\ifmmode\mathtt\else\normaltt\fi}
+
+\ifdefined\mr \else \let\mr\relax \fi % hm ... permanent
+\ifdefined\mb \else \let\mb\relax \fi % hm ... permanent
+
+% 1: $\setmathattribute{ss}{bf}3$
+% 2: $\setmathattribute{ss}{bf}\setmathfontstylealternate{bf}3$
+% 3: $\setmathattribute{ss}{bf}\setmathfontstyle{bf}3$
+% 4: $\setmathattribute{ss}{bf}\setmathfontstyle{bf}\setmathfontstylealternate{bf}3$
+% 5: $e=mc^2 \quad \mb e=mc^2$
+
+\prependtoks
+ \mathdefault
+\to \everymathematics
+
+%D We could set the rendering attribute at the \LUA\ end but as there can be many
+%D small math snippets we keep track of the state at the \TEX\ end (mapping is
+%D export safe).
+%D
+%D \starttyping
+%D \startformula
+%D \reals {\mathbf R} \utfchar{"0211D} \utfchar{"1D411}
+%D \stopformula
+%D
+%D \setupmathematics
+%D [symbolset=blackboard-to-bold]
+%D
+%D \startformula
+%D \reals {\mathbf R} \utfchar{"0211D} \utfchar{"1D411}
+%D \stopformula
+%D \stoptyping
+
+\newcount\c_math_renderings_attribute
+
+\appendtoks
+ \c_math_renderings_attribute\clf_mathrenderset{\mathematicsparameter\c!symbolset}\relax
+\to \everysetupmathematics % only in mathematics
+
+\appendtoks
+ \ifcase\c_math_renderings_attribute\else
+ \c_attr_mathrendering\c_math_renderings_attribute
+ \fi
+\to \everymathematics
+
+\setupmathematics
+ [\c!symbolset=]
+
+%D \macros
+%D {boldsymbol}
+%D
+%D To be done.
+
+\mutable\let\mathboldsymbol\relax % yet unsupported, will be
+
+\permanent\protected\def\boldsymbol
+ {\mathortext\mathboldsymbol\bold}
+
+%D Helpers
+
+\permanent\def\utfmathclass #1{\clf_utfmathclass {#1}}
+\permanent\def\utfmathstretch#1{\clf_utfmathstretch{#1}}
+\permanent\def\utfmathcommand#1{\clf_utfmathcommand{#1}}
+\permanent\def\utfmathfiller #1{\clf_utfmathfiller {#1}}
+
+\permanent\def\utfmathcommandabove #1{\clf_utfmathcommandabove {#1}}
+\permanent\def\utfmathcommandbelow #1{\clf_utfmathcommandbelow {#1}}
+\permanent\def\utfmathcommandfiller#1{\clf_utfmathcommandfiller{#1}}
+
+\permanent\protected\def\doifelseutfmathaccent#1{\clf_doifelseutfmathaccent{#1}}
+\permanent\protected\def\doifelseutfmathabove #1{\clf_doifelseutfmathabove {#1}}
+\permanent\protected\def\doifelseutfmathbelow #1{\clf_doifelseutfmathbelow {#1}}
+\permanent\protected\def\doifelseutfmathfiller#1{\clf_doifelseutfmathfiller{#1}}
+\permanent\protected\def\doifelseutfmathlimop #1{\clf_doifelseutfmathlimop {#1}}
+
+\aliased\let\doifutfmathaccentelse \doifelseutfmathaccent
+\aliased\let\doifutfmathaboveelse \doifelseutfmathabove
+\aliased\let\doifutfmathbelowelse \doifelseutfmathbelow
+\aliased\let\doifutfmathfillerelse \doifelseutfmathfiller
+\aliased\let\doifutfmathlimopelse \doifelseutfmathlimop
+
+%D Not used that much:
+
+\installcorenamespace{mathcodecommand}
+
+\permanent\protected\def\mathlimop#1{\mathop{#1}} %no \limits
+\permanent\protected\def\mathbox #1{\dontleavehmode\hbox\Ustartmath\mathsurround\zeropoint#1\Ustopmath}
+\permanent\protected\def\mathnolop#1{\mathop{#1}\nolimits}
+
+\aliased\let\mathnothing\firstofoneunexpanded
+\aliased\let\mathalpha \firstofoneunexpanded
+
+\setnewconstant\mathordcode \zerocount \letvalue{\??mathcodecommand ord}\mathord
+\setnewconstant\mathopcode \plusone \letvalue{\??mathcodecommand op}\mathop
+\setnewconstant\mathbincode \plustwo \letvalue{\??mathcodecommand bin}\mathbin
+\setnewconstant\mathrelcode \plusthree \letvalue{\??mathcodecommand rel}\mathrel
+\setnewconstant\mathopencode \plusfour \letvalue{\??mathcodecommand open}\mathopen
+\setnewconstant\mathclosecode \plusfive \letvalue{\??mathcodecommand close}\mathclose
+\setnewconstant\mathpunctcode \plussix \letvalue{\??mathcodecommand punct}\mathpunct
+\setnewconstant\mathalphacode \plusseven \letvalue{\??mathcodecommand alpha}\mathalpha
+\setnewconstant\mathinnercode \zerocount \letvalue{\??mathcodecommand inner}\mathinner
+\setnewconstant\mathnothingcode \zerocount \letvalue{\??mathcodecommand nothing}\mathnothing
+\setnewconstant\mathlimopcode \plusone \letvalue{\??mathcodecommand limop}\mathlimop
+\setnewconstant\mathnolopcode \plusone \letvalue{\??mathcodecommand nolop}\mathnolop
+\setnewconstant\mathboxcode \zerocount \letvalue{\??mathcodecommand box}\mathbox
+\setnewconstant\mathchoicecode \zerocount %letvalue{\??mathcodecommand choice}\mathnothing
+
+\setnewconstant\mathaccentcode \pluseight
+\setnewconstant\mathradicalcode \plusnine
+
+\permanent \def\mathcodenumber #1{\the\csname math#1code\endcsname}
+\permanent\protected\def\mathcodecommand#1{\csname\??mathcodecommand#1\endcsname}
+\permanent \def\mathcodechecked#1{\ifcsname math#1code\endcsname\lastnamedcs\else#1\fi}
+
+% \startlines
+% $\mathopnolimits{\rm d}x$
+% $\mathopnolimits{\kern\zeropoint \rm d}x$
+% $\mathcodecommand{nolop}{\rm d}x$
+% $\mathcodecommand{nolop}{\kern\zeropoint\rm d}x$
+% \blank
+% $\mathcodecommand{nolop}{\mr d}x$
+% $\mathcodecommand{nolop}{\kern\zeropoint\mr d}x$
+% $\mathop{\kern\zeropoint\mr d}x$
+% $\mathopnolimits{\kern\zeropoint d}x$
+% \stoplines
+
+\installcorenamespace{mathcommand}
+
+% todo: define these commands as frozen!
+
+\permanent\tolerant\protected\def\definemathcommand[#1]#*[#2]#*[#3]#:#*#4% command class args meaning
+ {\ifparameter#3\or
+ \edef\scratchstringone{#3}% make this one m_arguments
+ \ifx\scratchstringone\v!one
+ \frozen\protected\defcsname\??mathcommand#1\endcsname##1{\mathcodecommand{#2}{#4{##1}}}%
+ \orelse\ifx\scratchstringone\v!two
+ \frozen\protected\defcsname\??mathcommand#1\endcsname##1##2{\mathcodecommand{#2}{#4{##1}{##2}}}%
+ \else
+ \frozen\protected\defcsname\??mathcommand#1\endcsname{\mathcodecommand{#2}{#4}}%
+ \fi
+ \orelse\ifparameter#2\or
+ \frozen\protected\defcsname\??mathcommand#1\endcsname{\mathcodecommand{#2}{#4}}%
+ \else
+ \frozen\protected\defcsname\??mathcommand#1\endcsname{\mathcodecommand{nothing}{#4}}%
+ \fi
+ \ifparameter#1\or % safeguard
+ \letcsname#1\expandafter\endcsname\csname\??mathcommand#1\endcsname
+ \fi}
+
+\permanent\protected\def\mathcommand#1%
+ {\begincsname\??mathcommand#1\endcsname}
+
+%D Let's define a few comands here:
+
+%definemathcommand [mathstrut] {\vphantom{(}}
+%definemathcommand [joinrel] {\mathrel{\mkern-3mu}}
+\definemathcommand [joinrel] [rel] {\mkern-3mu}
+
+\immutable\chardef\c_math_strut"28
+
+% \protected\def\math_strut_htdp#1%
+% {\s!height\fontcharht#1\c_math_strut
+% \s!depth \fontchardp#1\c_math_strut}
+%
+% now we need this (kind of inefficient):
+
+\protected\def\math_strut_htdp#1% nasty!
+ {\s!height\dimexpr\mathscale#1\fontcharht#1\c_math_strut/\glyphscale\relax
+ \s!depth \dimexpr\mathscale#1\fontchardp#1\c_math_strut/\glyphscale\relax}
+
+\protected\def\math_strut_normal
+ {\vrule
+ \s!width \zeropoint
+ \normalexpanded{\math_strut_htdp{\mathstylefont\normalmathstyle}}%
+ \relax}
+
+\protected\def\math_strut_visual
+ {\hskip-.01\emwidth
+ \vrule
+ \s!width .02\emwidth
+ \normalexpanded{\math_strut_htdp{\mathstylefont\normalmathstyle}}%
+ \relax
+ \hskip-.01\emwidth}
+
+\permanent\protected\def\showmathstruts % let's not overload \math_strut_normal
+ {\enforced\let\math_strut\math_strut_visual}
+
+\let\math_strut\math_strut_normal
+
+% \protected\def\mathstrut{\mathcodecommand{nothing}{\math_strut}}
+
+\pushoverloadmode
+
+ \definemathcommand [mathstrut] {\math_strut}
+
+\popoverloadmode
+
+%D We could have a arg variant \unknown\ but not now.
+
+\permanent\protected\def\mathopwithlimits#1#2{\mathop{#1{#2}}\limits}
+\permanent\protected\def\stackrel #1#2{\mathrel{\mathop{#2}\limits^{#1}}}
+
+%D Moved from font-ini.mkiv:
+%D
+%D \macros
+%D {mf,mbox,enablembox,mathop}
+%D
+%D Todo:
+
+\permanent\protected\def\mf{\begincsname\fontalternative\endcsname} % todo: \frozen
+
+% \let\normalmathop\mathop % already defined
+
+% no longer needed as we no longer switch fonts
+%
+% \protected\def\mathop
+% {\normalmathop
+% \bgroup
+% % no: \let\rm\mf
+% \afterassignment\math_op\let\nexttoken=}
+%
+% \def\math_op{\ifx\nexttoken\bgroup\else\nexttoken\egroup\fi}
+
+% this one too: \letvalue{\??mathcodecommand op}\mathop ?
+
+\permanent\protected\def\normalmbox
+ {\normalhbox\bgroup
+ \usemathematicsstyleandcolor\c!textstyle\c!textcolor % new
+ \dowithnextboxcs\math_mbox_finish\normalhbox}
+
+\def\math_mbox_finish
+ {\flushnextbox
+ \egroup}
+
+\permanent\protected\def\mbox % we cannot add \dontleavehmode ... else no \setbox0\mbox possible
+ {\ifmmode\normalmbox\else\normalhbox\fi}
+
+\permanent\protected\def\enablembox
+ {\toksapp\everymathematics{\math_enable_mbox}}
+
+\permanent\def\math_enable_mbox % brrrr
+ {\enforced\let\hbox\mbox}
+
+\permanent\protected\def\snappedmath#1% sort of \struttedbox
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\normalhbox\bgroup
+ \startimath#1\stopimath
+ \egroup
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox
+ \endgroup}
+
+\permanent\protected\def\mtext#1%
+ {\text{\usemathematicsstyleandcolor\c!textstyle\c!textcolor#1}}
+
+%D The next hack is needed needed for sine, cosine etc.
+
+\aliased\let\mathfunction\firstofoneunexpanded
+
+\let\math_tags_function \firstofoneunexpanded
+\let\math_tags_functionlabeltext\mathlabeltext
+
+\let\math_tags_mo\firstofoneunexpanded
+\let\math_tags_mi\firstofoneunexpanded
+
+\protected\def\math_tags_mn#1{\begingroup\mathupright\math_set_o_both#1\endgroup}
+\protected\def\math_tags_ms#1{\begingroup\mathupright\math_set_p_both#1\endgroup} % why not just \text
+
+% Once this is stable we can store the number at the tex end which is
+% faster. Functions getnumbers >= 1000.
+
+% \setupmathematics[functionstyle=normal] % will give ligatures and kerning
+
+\setupmathematics
+ [\c!textstyle=, % rm ss etc i.e. known alternatives, otherwise math
+ \c!textcolor=,
+ \c!functionstyle=, % rm ss etc i.e. known alternatives, otherwise math
+ \c!functioncolor=]
+
+\protected\def\math_mfunction_styled
+ {\begingroup
+ \usemathematicscolorparameter\c!functioncolor
+ \edef\p_functionstyle{\mathematicsparameter\c!functionstyle}%
+ \ifempty\p_functionstyle
+ \expandafter\math_mfunction_styled_none
+ \orelse\ifcsname\??alternativestyle\p_functionstyle\endcsname
+ \expandafter\math_mfunction_styled_text
+ \else
+ \expandafter\math_mfunction_styled_math
+ \fi}
+
+\protected\def\math_mfunction_styled_text#1%
+ %{\mathoptext{\csname\??alternativestyle\p_functionstyle\endcsname#1}%
+ {\expandafter\mathoptext\expandafter{\lastnamedcs#1}%
+ \endgroup}
+
+\protected\def\math_mfunction_styled_math#1%
+ {\p_functionstyle
+ #1%
+ \endgroup}
+
+\protected\def\math_mfunction_styled_none#1%
+ {\mathupright
+ #1%
+ \endgroup}
+
+\permanent\protected\def\mfunction#1%
+ {\begingroup
+ \math_tags_mfunctiontxt{#1}\c_apply_function
+ \math_mfunction_styled{#1}%
+ \endgroup}
+
+\permanent\protected\def\mfunctionlabeltext#1%
+ {\begingroup
+ \math_tags_mfunctionlab{#1}\c_apply_function
+ \math_mfunction_styled{\mathlabeltext{#1}}%
+ \endgroup}
+
+\let\math_tags_mfunctiontxt\gobbletwoarguments
+\let\math_tags_mfunctionlab\gobbletwoarguments
+
+\permanent\protected\def\math_tags_mo_indeed#1{\begingroup \c_attr_mathcategory\plusone #1\endgroup}
+\permanent\protected\def\math_tags_mi_indeed#1{\begingroup \c_attr_mathcategory\plustwo #1\endgroup}
+\permanent\protected\def\math_tags_mn_indeed#1{\begingroup\mathupright\c_attr_mathcategory\plusthree\math_set_o_both#1\endgroup}
+\permanent\protected\def\math_tags_ms_indeed#1{\begingroup\mathupright\c_attr_mathcategory\plusfour \math_set_p_both#1\endgroup} % todo: mathoptext
+
+\newconditional\c_apply_function
+
+\permanent\protected\def\math_tags_apply#1#2%
+ {\begingroup
+ \settrue\c_apply_function
+ #1%
+ \endgroup
+ \begingroup
+ % todo: auto ()
+ #2%
+ \endgroup}
+
+\ifdefined\apply \else \aliased\let\apply\relax \fi
+
+\mutable\let\mo\relax
+\mutable\let\mi\relax
+\mutable\let\mn\relax
+\mutable\let\ms\relax
+
+\appendtoks
+ \enforced\let\math_tags_mfunctiontxt\clf_tagmfunctiontxt
+ \enforced\let\math_tags_mfunctionlab\clf_tagmfunctionlab
+ \enforced\let\math_tags_mo \math_tags_mo_indeed
+ \enforced\let\math_tags_mi \math_tags_mi_indeed
+ \enforced\let\math_tags_mn \math_tags_mn_indeed
+ \enforced\let\math_tags_ms \math_tags_ms_indeed
+\to \everyenableelements
+
+\appendtoks
+ \enforced\let\mo \math_tags_mo
+ \enforced\let\mi \math_tags_mi
+ \enforced\let\mn \math_tags_mn
+ \enforced\let\ms \math_tags_ms
+ \enforced\let\apply\math_tags_apply
+\to\everymathematics
+
+% \def\mlimitsfunction #1{\mathlimopcomm{{\mr#1}}
+% \def\mnolimitsfunction#1{\mathnolopcomm{{\mr#1}}
+
+% %D Taco posted this solution as response to a mail by Olivier, so let's integrate
+% %D it here.
+%
+% \def\currentmscaledstyle{rm} % will be plugged into the typeface text=ss option
+%
+% \protected\def\math_function_style_opnolimits #1{\mathop{\mscaledtext{#1}}\nolimits}
+% \protected\def\math_function_style_mfunction #1{\mscaledtext{\math_tags_function{#1}}}
+% \protected\def\math_function_style_mfunctionlabeltext#1{\mscaledtext{\math_tags_functionlabeltext{#1}}}
+%
+% \protected\def\setmathfunctionstyle#1% rm ss tt (can be made faster if needed)
+% {\doifsomething{#1}
+% {\def\currentmscaledstyle{#1}%
+% \let\mathopnolimits \math_function_style_opnolimits
+% \let\mfunction \math_function_style_mfunction
+% \let\mfunctionlabeltext\math_function_style_mfunctionlabeltext}}
+
+\mutable\def\currentmscaledstyle{rm} % will be plugged into the typeface text=ss option
+
+\permanent\protected\def\mscaledtext#1%
+ {\mathchoice
+ {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}}
+ {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}}
+ {\hbox{\csname\currentmscaledstyle\endcsname\tfx #1}}
+ {\hbox{\csname\currentmscaledstyle\endcsname\tfxx#1}}}
+
+\permanent\protected\def\setmathfunctionstyle#1%
+ {\setupmathematics[\c!functionstyle=#1]} % for old times sake
+
+%D Usage:
+%D
+%D \starttyping
+%D \setmathfunctionstyle\fontstyle % or {rm} or {ss} or ..
+%D \rm test $\sin{(x^{\sin(x^{\sin(x)})})}$ test
+%D \ss test $\sin{(x^{\sin(x^{\sin(x)})})}$ test
+%D \tt test $\sin{(x^{\sin(x^{\sin(x)})})}$ test
+%D \stoptyping
+
+%D \macros
+%D {nonknuthmode, donknuthmode}
+%D
+%D The underscore is frequently used in manuals but unfortunately \TEX\ prefers
+%D it to be a math specific character. And since computer modern fonts didn't
+%D have an underscore, one had to use commands to fake one. Nowadays we do
+%D have underscores in latin modern, and since all other fonts have them, we
+%D decided to get away from the restriction to use the underscore character in
+%D text mode.
+%D
+%D \starttyping
+%D \def\test#1{#1}
+%D
+%D \nonknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2}
+%D
+%D \donknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2}
+%D \stoptyping
+%D
+%D The result is as expected: the first line typesets ok, while the second
+%D one triggers an error message.
+
+\setnewconstant\activemathcharcode "8000
+
+\newtoks\activatedmathcharacters
+
+\permanent\protected\def\activatemathcharacter#1%
+ {\appendtoks
+ \global\mathcode#1=\activemathcharcode
+ \to \activatedmathcharacters}
+
+\permanent\def\activatemathcharacters
+ {\the\activatedmathcharacters}
+
+% beware, not runtime, so has to happen at format generation
+
+\activatemathcharacter\circumflexasciicode
+\activatemathcharacter\underscoreasciicode
+\activatemathcharacter\ampersandasciicode
+
+\permanent\def\normalmathaligntab{&} % \let\normalmathaligntab\aligntab does to work well in a let to & (a def works ok)
+
+\appendtoks
+ \edef\p_ampersand{\mathematicsparameter\s!ampersand}%
+ \ifx\p_ampersand\v!normal
+ \enforced\let\specialmathaligntab\normalmathaligntab
+ \else
+ \enforced\let\specialmathaligntab\mathampersand
+ \fi
+\to \everysetupmathematics
+
+%D A simplified version of this code is:
+%D
+%D \starttyping
+%D \catcode"26=12
+%D
+%D \bgroup
+%D \global\mathcode"26="8000
+%D
+%D \catcode"26=4
+%D
+%D \xdef\normalmathaligntab{&}
+%D
+%D \catcode"26=13
+%D
+%D \global\everymath{\def&{\normalmathaligntab}}
+%D \egroup
+%D \stoptyping
+%D
+%D The following works okay:
+%D
+%D \starttyping
+%D A & B
+%D \stoptyping
+%D
+%D As does:
+%D
+%D \starttyping
+%D $A \Umathchar"2"0"26 B$
+%D \stoptyping
+%D
+%D But the next code:
+%D
+%D \starttyping
+%D $A \char"26 B$
+%D \stoptyping
+%D
+%D fails with: \type{Misplaced alignment tab character &} and here is the
+%D reason.
+%D
+%D When we have a letter or other category a check happens for an active
+%D character and when it has one then it gets expanded and fed back into the
+%D scanner (sort of).
+%D
+%D A \type {\char} is also fed back as raw character and again when it's letter
+%D of other goes through the same process.
+%D
+%D This means that we cannot have a definition like:
+%D
+%D \starttyping
+%D \def\AND{\char"26\relax}
+%D \stoptyping
+%D
+%D that can be used in math mode, which is why the cweb macros do:
+%D
+%D \starttyping
+%D \def\AND{\def\AND{\mathchar"2026\relax}\AND}
+%D \stoptyping
+%D
+%D Maybe we need an option to treat chars like chars.
+
+% \activatemathcharacter\primeasciicode
+
+% not used:
+%
+% \mathcode\spaceasciicode\activemathcharcode
+%
+% not used:
+%
+% \bgroup
+% \catcode\underscoreasciicode\activecatcode
+% \doglobal\appendtoks
+% \mathcode\underscoreasciicode\activemathcharcode
+% \let_\activemathunderscore
+% \to \everymathematics
+% \egroup
+
+% Here follows some plain legacy: primes.
+%
+% The \let\prime\math_prime_indeed might become an obsolete as we have \doubleprime
+% and \tripleprime and collapsing can nicely handle the script then.
+%
+% Collapsing to 0x2033 and 0x2034 happens elsewhere.
+%
+% \switchtobodyfont[modern]
+% \switchtobodyfont[cambria]
+% \switchtobodyfont[xits]
+% \switchtobodyfont[minion]
+% \setupbodyfont[dejavu]
+%
+% \startbuffer
+% \def\SampleLine#1{%
+% \NC#1
+% \NC\switchtobodyfont[#1]$f^2$ % 1
+% \NC\switchtobodyfont[#1]$f\prime^2$ % 2
+% \NC\switchtobodyfont[#1]$f\prime\prime^2$ % 3
+% \NC\switchtobodyfont[#1]$f\prime\prime\prime^2$ % 4
+% \NC\switchtobodyfont[#1]$f{\prime}^2$ % 5
+% \NC\switchtobodyfont[#1]$f{\prime\prime}^2$ % 6
+% \NC\switchtobodyfont[#1]$f{\prime\prime\prime}^2$ % 7
+% \NC\switchtobodyfont[#1]$f'(x)$ % 8
+% \NC\switchtobodyfont[#1]$f''(x)$ % 9
+% \NC\switchtobodyfont[#1]$f'''(x)$ % 10
+% \NC\NR
+% }
+%
+% \starttabulate[|Tl|Tc|Tc|Tc|Tc|Tc|Tc|Tc|Tc|Tc|Tc|]
+% \NC\NC1\NC2\NC3\NC4\NC5\NC6\NC7\NC8\NC9\NC10\NC\NR
+% \SampleLine{modern}
+% \SampleLine{cambria}
+% \SampleLine{xits}
+% \SampleLine{minion}
+% \stoptabulate
+% \stopbuffer
+%
+% \typebuffer \getbuffer
+
+% most math fonts have messed up primes, just test this: $\prime^{\prime^{\prime}}$
+
+{ \catcode\circumflexasciicode\othercatcode \immutable\glet\othercircumflextoken ^ }
+{ \catcode\circumflexasciicode\superscriptcatcode \immutable\glet\superscriptcircumflextoken^ }
+
+\ifdefined \prime \else
+ \Umathchardef\prime "0 "0 "2032
+\fi
+
+% \let\math_prime_indeed_normal\prime
+%
+% \appendtoks
+% \let\math_prime_indeed_normal\prime % gets defined later
+% \let\mathfontprime\prime % for tracing
+% \let\prime\math_prime_indeed % so this is needed
+% \to \everydump
+%
+% \protected\def\math_prime_indeed
+% {\iffontchar\textfont\zerocount"FE325\relax
+% ^\bgroup
+% \expandafter\math_prime_indeed_virtual % virtual mess (using funny signal)
+% \else
+% % \expandafter\math_prime_indeed_normal % gets collapsed
+% \expandafter\math_prime_indeed_crapped % gets collapsed
+% \fi}
+%
+% \def\math_prime_indeed_crapped
+% {{^{\math_prime_indeed_normal}}}
+%
+% % \let\prime\math_prime_indeed
+%
+% \def\math_prime_indeed_virtual
+% {\math_prime_indeed_normal
+% \futurelet\nexttoken\math_prime_indeed_choice}
+%
+% \installcorenamespace{mathprime}
+%
+% \def\math_prime_indeed_choice
+% {\csname\??mathprime
+% \ifx '\nexttoken a\else
+% \ifx \math_prime_indeed_normal\nexttoken a\else
+% \ifx \prime\nexttoken a\else
+% \ifx\superscriptcircumflextoken\nexttoken b\else
+% \ifx\othercircumflextoken \nexttoken b\else
+% c\fi\fi\fi\fi\fi
+% \endcsname}
+%
+% \setvalue{\??mathprime a}#1{\math_prime_indeed_virtual}
+% \setvalue{\??mathprime b}#1#2{#2\egroup}
+% \setvalue{\??mathprime c}{\egroup}
+%
+% \let\activemathprime\math_prime_indeed
+%
+% \bgroup
+%
+% \catcode\primeasciicode\activecatcode
+%
+% \global\everymathematics\expandafter{\the\everymathematics\let'\math_prime_indeed} % todo: do this at the lua end
+%
+% \egroup
+
+% $\char26$ gives an error because it expands to an active character that
+% then becomes an & which is then seen as an alignment character; anyway,
+% even when we switch like this the different meaning only happens when
+% we're in math mode which can be delayed till we're in a cell
+
+\bgroup
+
+ \catcode\underscoreasciicode\activecatcode
+ \catcode\circumflexasciicode\activecatcode
+ \catcode\ampersandasciicode \activecatcode
+
+ \aliased\glet\specialmathaligntab\normalmathaligntab
+
+ \permanent\protected\gdef\obeymathcatcodes{%
+ \enforced\let _\normalsubscript
+ \enforced\let ^\normalsuperscript
+ \enforced\def &\specialmathaligntab
+ }
+
+ \doglobal\appendtoks
+ \enforced\let _\normalsubscript
+ \enforced\let ^\normalsuperscript
+ \enforced\let &\specialmathaligntab
+ \to \everymathematics
+
+ % \permanent\protected\gdef\normalmathampersands
+ % {\let\specialmathaligntab\mathampersand}
+
+\egroup
+
+\newtoks\everydonknuthmode
+\newtoks\everynonknuthmode
+
+\newconditional \knuthmode
+
+\aliased\let\nonknuthmode\relax % no longer needed in MkIV
+\aliased\let\donknuthmode\relax % no longer needed in MkIV
+
+% \def\nonknuthmode
+% {\pushcatcodetable
+% \setcatcodetable\ctxcatcodes
+% \the\everynonknuthmode
+% \let\nonknuthmode\relax
+% \popcatcodetable}
+%
+% \def\donknuthmode
+% {\pushcatcodetable
+% \setcatcodetable\ctxcatcodes
+% \the\everydonknuthmode
+% \popcatcodetable}
+%
+% \bgroup
+%
+% \catcode\underscoreasciicode\activecatcode
+% \catcode\circumflexasciicode\activecatcode
+% \catcode\ampersandasciicode \activecatcode
+%
+% \global \everynonknuthmode {\appendtoks
+% \let_\normalsubscript
+% \let^\normalsuperscript
+% \let&\normalmathaligntab % use \def when it's \aligntab
+% \to \everymathematics}
+%
+% \egroup
+%
+% \appendtoks
+% \setfalse\knuthmode
+% \catcode\underscoreasciicode\othercatcode
+% \catcode\circumflexasciicode\othercatcode
+% \catcode\ampersandasciicode \othercatcode
+% \to \everynonknuthmode
+%
+% \appendtoks
+% \settrue\knuthmode
+% \catcode\underscoreasciicode\subscriptcatcode
+% \catcode\circumflexasciicode\superscriptcatcode
+% \catcode\ampersandasciicode \alignmentcatcode
+% \to \everydonknuthmode
+%
+% \appendtoks
+% \startextendcatcodetable\ctxcatcodes
+% \catcode\underscoreasciicode\othercatcode
+% \catcode\circumflexasciicode\othercatcode
+% \catcode\ampersandasciicode \othercatcode
+% \stopextendcatcodetable
+% \to \everynonknuthmode
+%
+% \appendtoks
+% \startextendcatcodetable\ctxcatcodes
+% \catcode\underscoreasciicode\subscriptcatcode
+% \catcode\circumflexasciicode\superscriptcatcode
+% \catcode\ampersandasciicode \alignmentcatcode
+% \stopextendcatcodetable
+% \to \everydonknuthmode
+
+%D Even more drastic (this code will move as nonknuthmode is default now)
+
+% \protected\def\enableasciimode
+% {\ctxlua{resolvers.macros.enablecomment()}%
+% \glet\enableasciimode\relax}
+%
+% \protected\def\asciimode
+% {\catcodetable\txtcatcodes
+% \enableasciimode
+% \nonknuthmode}
+%
+% \protected\def\startasciimode
+% {\pushcatcodetable
+% \catcodetable\txtcatcodes
+% \enableasciimode
+% \nonknuthmode}
+%
+% \protected\def\stopasciimode
+% {\popcatcodetable
+% \ifconditional\knuthmode\else\donknuthmode\fi}
+
+\permanent\protected\def\enableasciimode
+ {\clf_enableasciimode} % relaxes itself
+
+\permanent\protected\def\asciimode
+ {\catcodetable\txtcatcodes
+ \clf_enableasciimode}
+
+\permanent\protected\def\startasciimode
+ {\pushcatcodetable
+ \catcodetable\txtcatcodes
+ \clf_enableasciimode}
+
+\permanent\protected\def\stopasciimode
+ {\popcatcodetable}
+
+%D Needed for unicode:
+
+\permanent\def\nulloperator{\mathortext{\mathop{\emptyhbox}}{\emptyhbox}}
+
+%D Memory saver:
+
+\def\math_basics_check_compact
+ {\doifelse{\mathematicsparameter\c!compact}\v!yes
+ \enabledirectives\disabledirectives[math.virtual.optional]}
+
+\appendtoks
+ \ifempty\currentmathematics
+ \math_basics_check_compact % less tracing
+ \fi
+\to \everysetupmathematics
+
+\setupmathematics
+ [\c!compact=\v!no]
+
+% \enabletrackers[typesetters.directions.math]
+
+%D Right||to||left typesetting in math is supported by the \type {align} parameter
+%D with as option the \type {bidi} parameter. Of course support for special symbols
+%D like square roots depends on the font as well. We probably need to mirror a few
+%D more characters.
+%D
+%D \startbuffer
+%D \removeunwantedspaces
+%D \m{ ( 1 = 1) }\quad
+%D \m{ (123 = 123) }\quad
+%D \m{ a ( 1 = 1) b }\quad
+%D \m{ a (123 = 123) b }\quad
+%D \m{ x = 123 y + (1 / \sqrt {x}) }
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \starttabulate[|T|T||]
+%D \HL
+%D \NC align \NC bidi \NC \NC \NR
+%D \HL
+%D \NC l2r \NC no \NC \setupmathematics[bidi=no] \getbuffer \NC \NR
+%D \NC l2r \NC yes \NC \setupmathematics[bidi=yes] \getbuffer \NC \NR
+%D \NC r2l \NC no \NC \setupmathematics[align=r2l,bidi=no] \getbuffer \NC \NR
+%D \NC r2l \NC yes \NC \setupmathematics[align=r2l,bidi=yes] \getbuffer \NC \NR
+%D \HL
+%D \stoptabulate
+
+% We will use proper constants when we go numbers instead of XXX.
+
+\newconditional\c_math_right_to_left
+
+\installcorenamespace{mathaligndirection}
+
+\setvalue{\??mathaligndirection r2l}{\settrue\c_math_right_to_left}
+\setvalue{\??mathaligndirection\v!righttoleft}{\settrue\c_math_right_to_left}
+
+\appendtoks
+ \ifcsname\??mathaligndirection\mathematicsparameter\c!align\endcsname
+ \lastnamedcs
+ \else
+ \setfalse\c_math_right_to_left
+ \fi
+\to \everyswitchmathematics
+
+\protected\def\math_basics_synchronize_direction
+ {\mathdirection\ifconditional\c_math_right_to_left\directionrighttoleft\else\directionlefttoright\fi}
+
+% Not \everymathematics as it comes too late and I'm not in the mood for a mixed mode
+% kludge now (should be a property of beginmath nodes and passed to callbacks).
+
+\appendtoks
+ \math_basics_synchronize_direction
+\to \everyswitchmathematics
+
+% experimental (needed for an article)
+
+\installcorenamespace {mathbidi}
+
+\newcount\c_math_bidi
+
+\def\math_bidi_enable {\clf_setmathdirection\plusone \relax\c_math_bidi\plusone}
+\def\math_bidi_disable{\clf_setmathdirection\zerocount\relax\c_math_bidi\attributeunsetvalue}
+
+\letvalue{\??mathbidi\v!yes}\math_bidi_enable
+\letvalue{\??mathbidi\v!no }\math_bidi_disable
+
+\appendtoks
+ \edef\p_bidi{\mathematicsparameter\c!bidi}% still needed ?
+ \ifcsname\??mathbidi\p_bidi\endcsname\lastnamedcs\else\math_bidi_disable\fi
+\to \everysetupmathematics
+
+\appendtoks
+ \c_attr_mathbidi\ifconditional\c_math_right_to_left\c_math_bidi\else\attributeunsetvalue\fi
+\to \everyswitchmathematics
+
+%D Delayed: greek.
+%D
+%D \starttyping
+%D \usetypescript[cambria]\setupbodyfont[cambria]
+%D \startTEXpage
+%D $\alpha \mathgreekupright \alpha \mathgreekitalic \alpha$
+%D \stopTEXpage
+%D \stoptyping
+
+% [lc uc] normal (upright) = 2, italic = 3, none = 0/1
+
+% We can move the setting to the lua end and use abstract numbers instead
+% if funny ones here.
+
+\installcorenamespace{mathgreek}
+
+\newconstant\c_math_greek_attribute
+
+\setvalue{\??mathgreek\v!none }{1}
+\setvalue{\??mathgreek\v!normal}{2}
+\setvalue{\??mathgreek\v!italic}{3}
+
+% \appendtoks
+% \edef\p_sygreek{\mathematicsparameter\s!sygreek}%
+% \edef\p_lcgreek{\mathematicsparameter\s!lcgreek}%
+% \edef\p_ucgreek{\mathematicsparameter\s!ucgreek}%
+% \c_math_greek_attribute"% hex digits
+% \csname\??mathgreek\ifcsname\??mathgreek\p_sygreek\endcsname\p_sygreek\else\v!none\fi\endcsname
+% \csname\??mathgreek\ifcsname\??mathgreek\p_lcgreek\endcsname\p_lcgreek\else\v!none\fi\endcsname
+% \csname\??mathgreek\ifcsname\??mathgreek\p_ucgreek\endcsname\p_ucgreek\else\v!none\fi\endcsname
+% \relax
+% \ifcase\c_math_greek_attribute
+% \c_math_greek_attribute\attributeunsetvalue
+% \fi
+% \to \everyswitchmathematics
+
+\appendtoks
+ \edef\p_sygreek{\mathematicsparameter\s!sygreek}% still needed ?
+ \edef\p_lcgreek{\mathematicsparameter\s!lcgreek}% still needed ?
+ \edef\p_ucgreek{\mathematicsparameter\s!ucgreek}% still needed ?
+ \c_math_greek_attribute"% hex digits
+ \ifcsname\??mathgreek\p_sygreek\endcsname\lastnamedcs\else1\fi
+ \ifcsname\??mathgreek\p_lcgreek\endcsname\lastnamedcs\else1\fi
+ \ifcsname\??mathgreek\p_ucgreek\endcsname\lastnamedcs\else1\fi
+ \relax
+ \ifcase\c_math_greek_attribute
+ \c_math_greek_attribute\attributeunsetvalue
+ \fi
+\to \everyswitchmathematics
+
+% only used local
+
+\permanent\protected\def\mathgreekupright{\c_attr_mathgreek"222\relax}
+\permanent\protected\def\mathgreekitalic {\c_attr_mathgreek"333\relax}
+\permanent\protected\def\mathgreekdefault{\c_attr_mathgreek"000\relax}
+
+\aliased\let\mathgreeknormal\mathgreekupright
+\aliased\let\mathgreeknone \mathgreekdefault
+
+\appendtoks
+ \c_attr_mathgreek\c_math_greek_attribute
+\to \everymathematics
+
+\setupmathematics
+ [\s!sygreek=\v!normal,
+ \s!lcgreek=\v!italic,
+ \s!ucgreek=\v!normal] % was: none
+
+%D Math collapsing (ligatures)
+
+\installcorenamespace{mathcollapsing}
+
+\setnewconstant\c_math_collapsing_attribute\attributeunsetvalue
+
+\letvalue{\??mathcollapsing 1}\plusone % specials
+\letvalue{\??mathcollapsing 2}\plustwo % specials + mathlist
+\letvalue{\??mathcollapsing 3}\plusthree % mathlist + specials
+\letvalue{\??mathcollapsing\v!none }\attributeunsetvalue
+\letvalue{\??mathcollapsing\v!reset}\attributeunsetvalue
+
+\def\math_collapsing_initialize
+ {\ifnum\c_math_collapsing_attribute=\attributeunsetvalue \else
+ \clf_initializemathcollapsing % one time
+ \glet\math_collapsing_initialize\relax
+ \fi}
+
+\appendtoks
+ \edef\p_collapsing{\mathematicsparameter\s!collapsing}%
+ \c_math_collapsing_attribute
+ \ifcsname\??mathcollapsing\p_collapsing\endcsname\lastnamedcs\else\attributeunsetvalue\fi
+ \relax
+\to \everyswitchmathematics % only in mathematics
+
+\appendtoks
+ \math_collapsing_initialize
+ \c_attr_mathcollapsing\c_math_collapsing_attribute
+\to \everymathematics
+
+\setupmathematics
+ [\s!collapsing=1] % so that we at least do primes
+
+%D Math italics (experiment)
+
+%D We need keys but what names to use and because we have hardcoded solution
+%D we can stick to numbers.
+
+\installcorenamespace{mathitalics}
+
+\setnewconstant\c_math_italics_attribute\attributeunsetvalue
+
+\letvalue{\??mathitalics 1}\plusone % fontitalics
+\letvalue{\??mathitalics 2}\plustwo % fontdata
+\letvalue{\??mathitalics 3}\plusthree % quad based
+\letvalue{\??mathitalics 4}\plusfour % combination of 1 and 3
+\letvalue{\??mathitalics\v!none }\attributeunsetvalue
+\letvalue{\??mathitalics\v!reset}\attributeunsetvalue
+
+\def\math_italics_initialize
+ {\ifnum\c_math_italics_attribute=\attributeunsetvalue \else
+ \clf_initializemathitalics % one time
+ \glet\math_italics_initialize\relax
+ \fi}
+
+\appendtoks
+ \edef\p_italics{\mathematicsparameter\s!italics}%
+ \c_math_italics_attribute
+ \ifcsname\??mathitalics\p_italics\endcsname\lastnamedcs\else\attributeunsetvalue\fi
+ \relax
+ % \math_italics_initialize
+\to \everyswitchmathematics % only in mathematics
+
+\appendtoks
+ \math_italics_initialize
+ \c_attr_mathitalics\c_math_italics_attribute
+\to \everymathematics
+
+% \setupmathematics % done later
+% [\s!italics=3] % 4 is probably better
+
+% looks nicer but can generate bogus csnames
+%
+% \setvalue{\??mathitalics1}{\math_italics_initialize\c_math_italics_attribute\plusone } % fontitalics
+% \setvalue{\??mathitalics2}{\math_italics_initialize\c_math_italics_attribute\plustwo } % fontdata
+% \setvalue{\??mathitalics3}{\math_italics_initialize\c_math_italics_attribute\plusthree} % quad based
+% \setvalue{\??mathitalics4}{\math_italics_initialize\c_math_italics_attribute\plusfour } % combination of 1 and 3
+%
+% \appendtoks
+% \c_math_italics_attribute\attributeunsetvalue
+% \csname\??mathitalics\mathematicsparameter\s!italics\endcsname
+% \to \everyswitchmathematics % only in mathematics
+
+%D Math kerns (experiment)
+
+\installcorenamespace{mathkernpairs}
+
+\setnewconstant\c_math_kernpairs_attribute\attributeunsetvalue % no real need for an extra constant
+
+\def\math_kernpairs_initialize
+ {\ifnum\c_math_kernpairs_attribute=\attributeunsetvalue \else
+ \clf_initializemathkernpairs % one time
+ \glet\math_kernpairs_initialize\relax
+ \fi}
+
+\appendtoks
+ \edef\p_kernpairs{\mathematicsparameter\s!kernpairs}%
+ \c_math_kernpairs_attribute\ifx\p_kernpairs\v!yes\plusone\else\attributeunsetvalue\fi\relax
+\to \everyswitchmathematics % only in mathematics
+
+\appendtoks
+ \math_kernpairs_initialize
+ \c_attr_mathkernpairs\c_math_kernpairs_attribute
+\to \everymathematics
+
+\setupmathematics
+ [\s!kernpairs=\v!no]
+
+%D \macros
+%D {enablemathpunctuation,disablemathpunctuation}
+%D
+%D \startbuffer
+%D \enablemathpunctuation$(1,2) (1, 2) (1{,}2) \hbox{foo, not bar}$
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \blank{\getbuffer}\blank
+
+% \newconditional\automathpunctuation
+%
+% \protected\def\enablemathpunctuation {\settrue \automathpunctuation}
+% \protected\def\disablemathpunctuation{\setfalse\automathpunctuation}
+%
+% \appendtoks
+% \doifelse{\mathematicsparameter\v!autopunctuation}\v!yes\settrue\setfalse\automathpunctuation
+% \to \everyswitchmathematics
+%
+% \setupmathematics
+% [\v!autopunctuation=\v!no]
+%
+% \def\math_punctuation_next{\ifx\nexttoken\blankspace\signalcharacter\fi}
+%
+% \protected\def\math_punctuation_comma {\textcomma \futurelet\nexttoken\math_punctuation_next}
+% \protected\def\math_punctuation_period{\textperiod\futurelet\nexttoken\math_punctuation_next}
+%
+% \setnewconstant\c_math_comma "002C
+% \setnewconstant\c_math_period "002E
+% \setnewconstant\c_math_special"8000
+%
+% \bgroup
+%
+% \catcode\c_math_comma \activecatcode
+% \catcode\c_math_period\activecatcode
+%
+% \protected\gdef\math_punctuation_initialize_indeed
+% {\mathcode\c_math_comma \c_math_special
+% \mathcode\c_math_period\c_math_special
+% \let,\math_punctuation_comma
+% \let.\math_punctuation_period
+% \c_attr_mathpunctuation\plustwo}
+%
+% \protected\gdef\math_punctuation_initialize_yes
+% {\c_attr_mathpunctuation\plustwo}
+%
+% \protected\gdef\math_punctuation_initialize_nop
+% {\c_attr_mathpunctuation\plusone}
+%
+% \egroup
+%
+% \appendtoks
+% \ifconditional\automathpunctuation
+% \math_punctuation_initialize_indeed
+% \math_punctuation_initialize_yes
+% \let\enablemathpunctuation \math_punctuation_initialize_yes
+% \let\disablemathpunctuation\math_punctuation_initialize_nop
+% \fi
+% \to \everymathematics
+
+% Later I will look again into a \LUATEX\ based solution. It only makes sense
+% to delegate to \LUA\ when we have more variants and need analysis (experimental
+% trickery removed for a while).
+
+% \def\math_punctuation_comma_next {\ifx\nexttoken\blankspace\mathpunct\else\mathord\fi{\textcomma }}
+% \def\math_punctuation_period_next{\ifx\nexttoken\blankspace\mathpunct\else\mathord\fi{\textperiod}}
+%
+% \protected\def\math_punctuation_nop_comma {\mathpunct{\textcomma}}
+% \protected\def\math_punctuation_nop_period{\mathord{\textperiod}}
+%
+% \protected\def\math_punctuation_all_comma {\futurelet\nexttoken\math_punctuation_comma_next}
+% \protected\def\math_punctuation_all_period{\futurelet\nexttoken\math_punctuation_period_next}
+%
+% \let\math_punctuation_yes_comma \math_punctuation_all_comma
+% \let\math_punctuation_yes_period\math_punctuation_nop_period
+%
+% The next one is more efficient as it produces more flat noad lists for numbers.
+
+\setnewconstant\c_math_comma "002C
+\setnewconstant\c_math_period "002E
+%setnewconstant\c_math_colon "003A
+\setnewconstant\c_math_semicolon"003B
+\setnewconstant\c_math_special "8000
+
+% todo: use \Umathclass\c_math_comma\mathpunctcode etc for temporary switching
+
+\def\math_set_o_comma {\Umathcode\c_math_comma \mathordcode \zerocount\c_math_comma}
+\def\math_set_p_comma {\Umathcode\c_math_comma \mathpunctcode\zerocount\c_math_comma}
+\def\math_set_o_period {\Umathcode\c_math_period \mathordcode \zerocount\c_math_period}
+\def\math_set_p_period {\Umathcode\c_math_period \mathpunctcode\zerocount\c_math_period}
+\def\math_set_o_semicolon{\Umathcode\c_math_semicolon\mathordcode \zerocount\c_math_semicolon}
+\def\math_set_p_semicolon{\Umathcode\c_math_semicolon\mathpunctcode\zerocount\c_math_semicolon}
+
+\edef\math_set_o_both {\math_set_o_period\math_set_o_comma}
+\edef\math_set_p_both {\math_set_p_period\math_set_p_comma}
+
+\protected\def\math_punctuation_nop_comma {\begingroup\math_set_p_comma ,\endgroup}
+\protected\def\math_punctuation_nop_period {\begingroup\math_set_o_period .\endgroup}
+\protected\def\math_punctuation_nop_semicolon{\begingroup\math_set_p_semicolon;\endgroup}
+
+% todo: use new lookahead stuff
+
+\protected\def\math_punctuation_all_comma {\futurelet\nexttoken\math_punctuation_comma_next}
+\protected\def\math_punctuation_all_period {\futurelet\nexttoken\math_punctuation_period_next}
+\protected\def\math_punctuation_all_semicolon{\futurelet\nexttoken\math_punctuation_semicolon_next}
+
+ \let\math_punctuation_yes_comma \math_punctuation_all_comma
+ \let\math_punctuation_yes_period \math_punctuation_nop_period
+ \let\math_punctuation_yes_semicolon\math_punctuation_all_semicolon
+
+\def\math_punctuation_comma_next {\begingroup\Umathcode\c_math_comma \ifx\nexttoken\blankspace\mathordcode\else\mathordcode\fi\zerocount\c_math_comma ,\endgroup}
+\def\math_punctuation_period_next {\begingroup\Umathcode\c_math_period \ifx\nexttoken\blankspace\mathordcode\else\mathordcode\fi\zerocount\c_math_period .\endgroup}
+\def\math_punctuation_semicolon_next{\begingroup\Umathcode\c_math_semicolon\ifx\nexttoken\blankspace\mathordcode\else\mathordcode\fi\zerocount\c_math_semicolon;\endgroup}
+
+\installcorenamespace {mathautopunctuation}
+
+\bgroup
+
+ \catcode\c_math_comma \activecatcode
+ \catcode\c_math_period \activecatcode
+ \catcode\c_math_semicolon\activecatcode
+
+ \setgvalue{\??mathautopunctuation\v!no}%
+ {\let,\math_punctuation_nop_comma
+ \let.\math_punctuation_nop_period
+ \let;\math_punctuation_nop_semicolon}
+
+ % more efficient list:
+ %
+ % \setgvalue{\??mathautopunctuation\v!no}%
+ % {\Umathcode\c_math_period\mathordcode \zerocount\c_math_period
+ % \Umathcode\c_math_comma \mathpunctcode\zerocount\c_math_comma }
+
+ \setgvalue{\??mathautopunctuation\v!yes}%
+ {\let,\math_punctuation_yes_comma
+ \let.\math_punctuation_yes_period
+ \let;\math_punctuation_nop_semicolon}
+
+ \setgvalue{\??mathautopunctuation\v!all}%
+ {\let,\math_punctuation_all_comma
+ \let.\math_punctuation_all_period
+ \let;\math_punctuation_nop_semicolon}
+
+ \setgvalue{\??mathautopunctuation comma}%
+ {\let,\math_punctuation_yes_comma
+ \let.\math_punctuation_yes_period
+ \let;\math_punctuation_nop_semicolon}
+
+ \setgvalue{\??mathautopunctuation\v!yes\string,semicolon}%
+ {\let,\math_punctuation_yes_comma
+ \let.\math_punctuation_yes_period
+ \let;\math_punctuation_yes_semicolon}
+
+ \setgvalue{\??mathautopunctuation comma\string,semicolon}%
+ {\let,\math_punctuation_yes_comma
+ \let.\math_punctuation_yes_period
+ \let;\math_punctuation_yes_semicolon}
+
+ \setgvalue{\??mathautopunctuation\v!all\string,semicolon}%
+ {\let,\math_punctuation_all_comma
+ \let.\math_punctuation_all_period
+ \let;\math_punctuation_all_semicolon}
+
+\egroup
+
+% \appendtoks
+% \global\mathcode\c_math_comma \c_math_special
+% \global\mathcode\c_math_period \c_math_special
+% \global\mathcode\c_math_semicolon\c_math_special
+% \to \everyjob
+
+% \activatemathcharacter\c_math_comma
+% \activatemathcharacter\c_math_period
+% \activatemathcharacter\c_math_semicolon
+
+\appendtoks
+ \mathcode\c_math_comma \c_math_special
+ \mathcode\c_math_period \c_math_special
+ \mathcode\c_math_semicolon\c_math_special
+ \begincsname\??mathautopunctuation\mathematicsparameter\v!autopunctuation\endcsname
+\to \everymathematics
+
+\appendtoks
+ \ifcsname\??mathautopunctuation\mathematicsparameter\v!autopunctuation\endcsname \else
+ \letmathematicsparameter\v!autopunctuation\v!no
+ \fi
+\to \everysetupmathematics
+
+\permanent\protected\def\enablemathpunctuation {\csname\??mathautopunctuation\v!no \endcsname}
+\permanent\protected\def\disablemathpunctuation{\csname\??mathautopunctuation\v!yes\endcsname}
+
+\setupmathematics
+ [\v!autopunctuation=\v!no] % no | yes | all | comma | yes,semicolon | all,semicolon
+
+%D The consequences of setting this are as follows:
+%D
+%D \def\TestA#1#2#3%
+%D {\ifnum#1=0 \type{#2}\else\setupmathematics[autopunctuation={#2}]$#3$\fi}
+%D \def\TestB#1#2%
+%D {\NC \TestA{#1}{no} {#2}
+%D \NC \TestA{#1}{yes} {#2}
+%D \NC \TestA{#1}{yes,semicolon}{#2}
+%D \NC \TestA{#1}{all} {#2}
+%D \NC \TestA{#1}{all,semicolon}{#2}
+%D \NC \NR}
+%D \starttabulate[|c|c|c|c|c|]
+%D \TestB{0}{}
+%D \TestB{1}{(1,2)=(1, 2)}
+%D \TestB{1}{(1.2)=(1. 2)}
+%D \TestB{1}{(1;2)=(1; 2)}
+%D \stoptabulate
+
+%D \macros
+%D {mathstyle}
+%D
+%D If one want to be sure that something is typeset in the appropriate style, \type
+%D {\mathstyle} can be used:
+%D
+%D \starttyping
+%D \mathstyle{something}
+%D \stoptyping
+
+% \def\mathstyle#1%
+% {\mathchoice
+% {\displaystyle #1}%
+% {\textstyle #1}%
+% {\scriptstyle #1}%
+% {\scriptscriptstyle#1}}
+%
+% We now have a primitive operation for this. As the macro overloads a new
+% primitive introduced in \LUATEX, we need to use \type {\normalmathstyle} when we
+% consult the current math style.
+%
+% \let \mathstyle \Ustack % spoils cramped
+%
+% \let \mathstyle \firstofoneargument
+%
+% 0 = display
+% 1 = crampeddisplay
+% 2 = text
+% 3 = crampedtext
+% 4 = script
+% 5 = crampedscript
+% 6 = scriptscript
+% 7 = crampedscriptscript
+
+\permanent\def\uncramped#1%
+ {{\ifmathstyle
+ \or \displaystyle \or
+ \or \textstyle \or
+ \or \scriptstyle \or
+ \or \scriptscriptstyle \fi
+ #1}}
+
+\permanent\def\cramped#1%
+ {{\ifmathstyle
+ \crampeddisplaystyle \or \or % 0 -> 1
+ \crampedtextstyle \or \or % 2 -> 3
+ \crampedscriptstyle \or \or % 4 -> 5
+ \crampedscriptscriptstyle \fi % 6 -> 7
+ #1}}
+
+\permanent\def\triggermathstyle#1{\Ustyle\numexpr#1\relax}
+%permanent\def\mathstyletrigger#1{\numexpr#1\relax}
+\permanent\let\mathstyletrigger \firstofoneargument
+
+\permanent\def\triggeredmathstyleparameter#1% to bypass the relax
+ {\ifcase\numexpr\normalmathstyle\relax
+ #1\displaystyle \or % 0
+ #1\crampeddisplaystyle \or % 1
+ #1\textstyle \or % 2
+ #1\crampedtextstyle \or % 3
+ #1\scriptstyle \or % 4
+ #1\crampedscriptstyle \or % 5
+ #1\scriptscriptstyle \or % 6
+ #1\crampedscriptscriptstyle \else
+ % error
+ \fi}
+
+\permanent\def\mathstylefont#1% #1 is number (\normalmathstyle)
+ {\ifcase\numexpr#1\relax
+ \textfont \or
+ \textfont \or
+ \textfont \or
+ \textfont \or
+ \scriptfont \or
+ \scriptfont \or
+ \scriptscriptfont \or
+ \scriptscriptfont \else
+ \textfont
+ \fi\fam} % was \zerocount
+
+\permanent\def\somemathstylefont#1% #1 is number (\normalmathstyle)
+ {\ifcase\numexpr#1\relax
+ \textfont \or
+ \textfont \or
+ \textfont \or
+ \textfont \or
+ \scriptfont \or
+ \scriptfont \or
+ \scriptscriptfont \or
+ \scriptscriptfont \else
+ \textfont
+ \fi}
+
+\permanent\def\mathsmallstylefont#1% #1 is number (\normalmathstyle)
+ {\ifcase\numexpr#1\relax
+ \scriptfont \or
+ \scriptfont \or
+ \scriptfont \or
+ \scriptfont \or
+ \scriptscriptfont \or
+ \scriptscriptfont \or
+ \scriptscriptfont \or
+ \scriptscriptfont \else
+ \scriptfont
+ \fi\fam} % was \zerocount
+
+\permanent\def\mathstyleface#1% #1 is number (\normalmathstyle)
+ {\ifcase\numexpr#1\relax
+ \textface \or
+ \textface \or
+ \textface \or
+ \textface \or
+ \scriptface \or
+ \scriptface \or
+ \scriptscriptface \or
+ \scriptscriptface \else
+ \textface
+ \fi}
+
+\permanent\def\mathsmallstyleface#1% #1 is number (\normalmathstyle)
+ {\ifcase\numexpr#1\relax
+ \scriptface \or
+ \scriptface \or
+ \scriptface \or
+ \scriptface \or
+ \scriptscriptface \or
+ \scriptscriptface \or
+ \scriptscriptface \or
+ \scriptscriptface \else
+ \scriptface
+ \fi}
+
+\permanent\def\mathstylecommand#1#2#3%
+ {\ifcase\numexpr\normalmathstyle\relax
+ \expandafter#1\or
+ \expandafter#1\or
+ \expandafter#1\or
+ \expandafter#1\or
+ \expandafter#2\or
+ \expandafter#2\or
+ \expandafter#3\or
+ \expandafter#3\else
+ \expandafter#1\fi}
+
+% \permanent\def\textstyleface#1% #1 is number (\normalmathstyle)
+% {\ifcase\numexpr#1\relax
+% \tf \or
+% \tf \or
+% \tf \or
+% \tf \or
+% \tfx \or
+% \tfx \or
+% \tfxx \or
+% \tfxx \else
+% \tf
+% \fi}
+
+\permanent\protected\def\verbosemathstyle#1% #1 is number (\normalmathstyle)
+ {{\normalexpanded{\relax\darkgray\ttxx[\number#1:\ifcase\numexpr#1\relax
+ display\or % 0
+ crampeddisplay\or % 1
+ text\or % 2
+ crampedtext\or % 3
+ script\or % 4
+ crampedscript\or % 5
+ scriptscript\or % 6
+ crampedscriptscript\else % 7
+ unknown\fi]}}}
+
+\permanent\protected\def\showmathstyle{\verbosemathstyle\normalmathstyle}
+
+%D Handy too:
+
+\permanent\def\mathcharwd{\fontcharwd\mathstylefont\normalmathstyle}
+\permanent\def\mathcharht{\fontcharht\mathstylefont\normalmathstyle}
+\permanent\def\mathchardp{\fontchardp\mathstylefont\normalmathstyle}
+
+%D Some dimension fun:
+
+\aliased \let\mathexheight \exheight % also in math-ini.mkiv
+\permanent\def\mathemwidth {\Umathquad\normalmathstyle} % also in math-ini.mkiv
+
+%D A plain inheritance:
+
+\permanent\def\mathpalette#1#2%
+ {\mathchoice
+ {#1\displaystyle {#2}}%
+ {#1\textstyle {#2}}%
+ {#1\scriptstyle {#2}}%
+ {#1\scriptscriptstyle{#2}}}
+
+%D \macros
+%D {cramedllap, crampedrlap, crampedclap}
+%D
+%D In many cases, one wants to use the \tex {clap} macro in math mode while using
+%D subscripts. Normally, subscripts are in cramped mode, and these macros allow you
+%D to go to cramped mode. For example:
+%D
+%D \startbuffer
+%D \startformula
+%D \sum_{a^2 < b^2 < c^2} \quad
+%D \sum_{\clap{a^2 < b^2 < c^2}} \quad
+%D \sum_{\crampedclap{a^2 < b^2 < c^2}}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \start
+%D \typebuffer
+%D \switchtobodyfont[24pt]
+%D \getbuffer
+%D \stop
+%D
+%D This used to be in \type {supp-math.mkxl}:
+
+\permanent\protected\def\crampedllap#1{\llap{\cramped#1}}
+\permanent\protected\def\crampedrlap#1{\rlap{\cramped#1}}
+\permanent\protected\def\crampedclap#1{\clap{\cramped#1}}
+
+%D Often we can use:
+%D
+%D \startbuffer
+%D $x^{\mathstylehbox{x^{\mathstylehbox{x}}}}$
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+% to be tested: {#1} but it could have side effects
+
+% \protected\def\mathstylehbox#1% sensitive for: a \over b => {a\over b} or \frac{a}{b}
+% {\normalexpanded{\hbox\bgroup
+% \startimath\triggermathstyle\normalmathstyle}\mathsurround\zeropoint#1\stopimath\egroup}
+
+\permanent\protected\def\mathstylehbox#1#% sensitive for: a \over b => {a\over b} or \frac{a}{b}
+ {\math_style_hbox{#1}}
+
+\protected\def\math_style_hbox#1#2% sensitive for: a \over b => {a\over b} or \frac{a}{b}
+ {\normalexpanded{\hbox#1\bgroup
+ \startimath\triggermathstyle\normalmathstyle}\mathsurround\zeropoint#2\stopimath\egroup}
+
+\permanent\protected\def\mathstylevbox#1%
+ {\normalexpanded{\vbox\bgroup
+ \startimath\triggermathstyle\normalmathstyle}\mathsurround\zeropoint#1\stopimath\egroup}
+
+\permanent\protected\def\mathstylevcenter#1%
+ {\normalexpanded{\vcenter\bgroup
+ \startimath\triggermathstyle\normalmathstyle}\mathsurround\zeropoint#1\stopimath\egroup}
+
+\permanent\protected\def\mathstylevcenteredhbox#1%
+ {\normalexpanded{\vcenter\bgroup\hbox\bgroup
+ \startimath\triggermathstyle\normalmathstyle}\mathsurround\zeropoint#1\stopimath\egroup\egroup}
+
+\permanent\protected\def\mathstylevcenteredvbox#1%
+ {\normalexpanded{\vcenter\bgroup\vbox\bgroup
+ \startimath\triggermathstyle\normalmathstyle}\mathsurround\zeropoint#1\stopimath\egroup\egroup}
+
+\permanent\protected\def\setmathsmalltextbox#1#2#%
+ {\normalizebodyfontsize\m_math_text_choice_face{\mathsmallstyleface\normalmathstyle}%
+ \setbox#1#2\bgroup
+ \font_basics_switchtobodyfont\m_math_text_choice_face
+ \let\next}
+
+\permanent\protected\def\setmathtextbox#1#2#%
+ {\normalizebodyfontsize\m_math_text_choice_face{\mathstyleface\normalmathstyle}%
+ \setbox#1#2\bgroup
+ \font_basics_switchtobodyfont\m_math_text_choice_face
+ \let\next}
+
+%D Here is the new mechanism ... it might replace some of the above but we will do
+%D that stepwise. Keep in mind that cramped only affects superscripts and even then,
+%D only when in a smaller size than normal.
+%D
+%D \def\TestMe#1%
+%D {\NC \ttbf #1
+%D \NC \ruledhbox{$\setupmathstyle[#1]x + x_j^2 + x_i^{e^2} + \frac{1}{x}$}
+%D \NC \ruledhbox{$\setupmathstyle[#1,small]x + x_j^2 + x_i^{e^2} + \frac{1}{x}$}
+%D \NC \NR}
+%D
+%D \starttabulate[|l|r|l|]
+%D \HL
+%D \NC \NC \NC \ttbf ...,small \NC \NR
+%D \HL
+%D \TestMe{text} \TestMe{text,cramped}
+%D \TestMe{script} \TestMe{script,cramped}
+%D \TestMe{scriptscript} \TestMe{scriptscript,cramped}
+%D \TestMe{display} \TestMe{display,cramped}
+%D \HL
+%D \stoptabulate
+
+\permanent\def\triggerdisplaystyle
+ {\ifmathstyle
+ \displaystyle \or
+ \crampeddisplaystyle \or
+ \displaystyle \or
+ \crampeddisplaystyle \or
+ \displaystyle \or
+ \crampeddisplaystyle \or
+ \displaystyle \or
+ \crampeddisplaystyle \or
+ \fi}
+
+\permanent\def\triggertextstyle
+ {\ifmathstyle
+ \textstyle \or
+ \crampedtextstyle \or
+ \textstyle \or
+ \crampedtextstyle \or
+ \textstyle \or
+ \crampedtextstyle \or
+ \textstyle \or
+ \crampedtextstyle \else
+ \fi}
+
+\permanent\def\triggerscriptstyle
+ {\ifmathstyle
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \fi}
+
+\permanent\def\triggerscriptscriptstyle
+ {\ifmathstyle
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \fi}
+
+\permanent\def\triggeruncrampedstyle
+ {\ifmathstyle
+ \or \displaystyle \or
+ \or \textstyle \or
+ \or \scriptstyle \or
+ \or \scriptscriptstyle \fi}
+
+\permanent\def\triggercrampedstyle
+ {\ifmathstyle
+ \crampeddisplaystyle \or \or
+ \crampedtextstyle \or \or
+ \crampedscriptstyle \or \or
+ \crampedscriptscriptstyle \fi}
+
+\permanent\def\triggersmallstyle
+ {\ifmathstyle
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \fi}
+
+\permanent\def\triggeruncrampedsmallstyle
+ {\ifmathstyle
+ \scriptstyle \or
+ \scriptstyle \or
+ \scriptstyle \or
+ \scriptstyle \or
+ \scriptscriptstyle \or
+ \scriptscriptstyle \or
+ \scriptscriptstyle \or
+ \scriptscriptstyle \or
+ \fi}
+
+\permanent\def\triggercrampedsmallstyle
+ {\ifmathstyle
+ \crampedscriptstyle \or
+ \crampedscriptstyle \or
+ \crampedscriptstyle \or
+ \crampedscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \crampedscriptscriptstyle \or
+ \fi}
+
+\permanent\def\triggerbigstyle
+ {\ifmathstyle
+ \displaystyle \or
+ \crampeddisplaystyle \or
+ \textstyle \or
+ \crampedtextstyle \or
+ \textstyle \or
+ \crampedtextstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \or
+ \fi}
+
+\permanent\def\triggeruncrampedbigstyle
+ {\ifmathstyle
+ \displaystyle \or
+ \displaystyle \or
+ \textstyle \or
+ \textstyle \or
+ \textstyle \or
+ \textstyle \or
+ \scriptstyle \or
+ \scriptstyle \or
+ \fi}
+
+\permanent\def\triggercrampedbigstyle
+ {\ifmathstyle
+ \crampeddisplaystyle \or
+ \crampeddisplaystyle \or
+ \crampedtextstyle \or
+ \crampedtextstyle \or
+ \crampedtextstyle \or
+ \crampedtextstyle \or
+ \crampedscriptstyle \or
+ \crampedscriptstyle \or
+ \fi}
+
+\newcount\c_math_saved_style
+
+\permanent\protected\def\pushmathstyle % assumes begingroup .. endgroup
+ {\c_math_saved_style\mathstyle}
+
+\permanent\protected\def\popmathstyle
+ {\ifnum\mathstyle=\c_math_saved_style\else
+ \triggermathstyle\c_math_saved_style
+ \fi}
+
+\installcorenamespace{mathstylecommand}
+\installcorenamespace{mathstylecache}
+
+\newconstant\c_math_styles_state_style
+\newconstant\c_math_styles_state_cramped
+\newconstant\c_math_styles_state_size
+
+\def\math_style_add_to_cache_choice {%
+ \ifcase\c_math_styles_state_size
+ \ifcase\c_math_styles_state_style
+ \ifcase\c_math_styles_state_cramped
+ \relax \or
+ \noexpand\triggeruncrampedstyle \or
+ \noexpand\triggercrampedstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerdisplaystyle \or
+ \displaystyle \or
+ \crampeddisplaystyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggertextstyle \or
+ \textstyle \or
+ \crampedtextstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \fi
+ \fi
+ \or % small
+ \ifcase\c_math_styles_state_style
+ \ifcase\c_math_styles_state_cramped
+ \noexpand\triggersmallstyle \or
+ \noexpand\triggeruncrampedsmallstyle \or
+ \noexpand\triggercrampedsmallstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptscriptstyle \or
+ \scriptscriptstyle \or
+ \crampedscriptscriptstyle \fi
+ \fi
+ \or % large
+ \ifcase\c_math_styles_state_style
+ \ifcase\c_math_styles_state_cramped
+ \noexpand\triggerbigstyle \or
+ \noexpand\triggeruncrampedbigstyle \or
+ \noexpand\triggercrampedbigstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerdisplaystyle \or
+ \displaystyle \or
+ \crampeddisplaystyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggertextstyle \or
+ \textstyle \or
+ \crampedtextstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggertextstyle \or
+ \textstyle \or
+ \crampedtextstyle \fi
+ \or\ifcase\c_math_styles_state_cramped
+ \noexpand\triggerscriptstyle \or
+ \scriptstyle \or
+ \crampedscriptstyle \fi
+ \fi
+ \fi
+}
+
+\permanent\tolerant\protected\def\setmathstyle[#1]#;#=% so we accept [] and {}
+ {\edef\m_math_style_asked{#1#2}%
+ \ifempty\m_math_style_asked \else
+ \math_style_set_indeed
+ \fi}
+
+\permanent\def\installmathstyle#1#2%
+ {\ifcsname\??mathstylecommand#1\endcsname \else
+ \setvalue{\??mathstylecommand#1}{#2}%
+ \fi}
+
+\def\math_style_collect#1%
+ {\csname\??mathstylecommand#1\endcsname}
+
+\setvalue{\??mathstylecommand\s!display }{\c_math_styles_state_style \plusone}
+\setvalue{\??mathstylecommand\s!text }{\c_math_styles_state_style \plustwo}
+\setvalue{\??mathstylecommand\s!script }{\c_math_styles_state_style \plusthree}
+\setvalue{\??mathstylecommand\s!scriptscript}{\c_math_styles_state_style \plusfour}
+
+\setvalue{\??mathstylecommand\s!uncramped }{\c_math_styles_state_cramped\plusone}
+\setvalue{\??mathstylecommand\s!cramped }{\c_math_styles_state_cramped\plustwo}
+\setvalue{\??mathstylecommand\v!normal }{\c_math_styles_state_cramped\plusone}
+\setvalue{\??mathstylecommand\v!packed }{\c_math_styles_state_cramped\plustwo}
+
+\setvalue{\??mathstylecommand\v!small }{\c_math_styles_state_size \plusone}
+\setvalue{\??mathstylecommand\v!big }{\c_math_styles_state_size \plustwo}
+
+\permanent\protected\def\setupmathstyle[#1]%
+ {\edef\m_math_style_asked{#1}%
+ \ifempty\m_math_style_asked \else
+ \math_style_set_indeed
+ \fi}
+
+\permanent\protected\def\usemathstyleparameter#1% faster local variant
+ {\edef\m_math_style_asked{#1\c!mathstyle}%
+ \ifempty\m_math_style_asked \else
+ \math_style_set_indeed
+ \fi}
+
+%D \startbuffer
+%D \definemathstyle[mystyle][scriptscript]
+%D
+%D $text\startmathstyle[mystyle]scriptscript\stopmathstyle text$
+%D \stopbuffer
+%D
+%D \typebuffer \blank \start \getbuffer \stop \blank
+
+\installcorenamespace {mathstyle}
+
+\permanent\tolerant\protected\def\definemathstyle[#1]#*[#2]%
+ {\c_math_styles_state_style \zerocount
+ \c_math_styles_state_cramped\zerocount
+ \c_math_styles_state_size \zerocount
+ \rawprocesscommacommand[#2]\math_style_collect
+ \letcsname\??mathstyle#1\normalexpanded{\endcsname\math_style_add_to_cache_choice}}
+
+\def\math_style_set_indeed
+ {\ifcsname\??mathstyle\m_math_style_asked\endcsname
+ \lastnamedcs
+ \else
+ \math_style_set_mathstyle_mathstyle
+ \fi}
+
+\def\math_style_set_mathstyle_mathstyle
+ {\ifcsname\??mathstylecache\m_math_style_asked\endcsname
+ \lastnamedcs
+ \else
+ \math_style_set_mathstyle_mathstylecache
+ \fi}
+
+\def\math_style_set_mathstyle_mathstylecache
+ {\c_math_styles_state_style \zerocount
+ \c_math_styles_state_cramped\zerocount
+ \c_math_styles_state_size \zerocount
+ \rawprocesscommacommand[\m_math_style_asked]\math_style_collect
+ \gletcsname\??mathstylecache\m_math_style_asked\normalexpanded{\endcsname\math_style_add_to_cache_choice}%
+ \csname\??mathstylecache\m_math_style_asked\endcsname}
+
+\letvalue{\??mathstyle \??mathstyle }\math_style_set_mathstyle_mathstyle % still needed?
+\letvalue{\??mathstylecache\??mathstylecache}\math_style_set_mathstyle_mathstylecache % still needed?
+
+%D \startbuffer
+%D $x\begingroup\setupmathstyle[script]x\endgroup x$
+%D $x{\setupmathstyle[script]x}x$
+%D $x\startmathstyle[script]x\stopmathstyle x$
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\protected\def\startmathstyle[#1]%
+ {\edef\m_math_style_asked{#1}%
+ \ifempty\m_math_style_asked
+ \enforced\aliased\let\stopmathstyle\relax
+ \else
+ \bgroup
+ \math_style_set_indeed
+ \enforced\aliased\let\stopmathstyle\egroup
+ \fi}
+
+\aliased\let\stopmathstyle\relax
+
+\permanent\protected\def\startusemathstyleparameter#1%
+ {\edef\m_math_style_asked{#1\c!mathstyle}%
+ \ifempty\m_math_style_asked
+ \enforced\let\stopusemathstyleparameter\relax
+ \else
+ \bgroup
+ \math_style_set_indeed
+ \enforced\let\stopusemathstyleparameter\egroup
+ \fi}
+
+\aliased\let\stopusemathstyleparameter\relax
+
+%D Something similar can be used in the (re|)|definition of \type {\text}. This
+%D version is a variation on the one in the math module (see \type{m-math} and|/|or
+%D \type {m-newmat}).
+
+\let\m_math_text_choice_face\relax
+
+% if needed we can get rid of the normalize (predo in font code)
+
+% \def\math_text_choice_font#1#2#%
+% {\normalizebodyfontsize\m_math_text_choice_face{\mathstyleface\normalmathstyle}%
+% \hbox#2\bgroup
+% \font_basics_switchtobodyfont\m_math_text_choice_face
+% #1%
+% \let\next}
+
+% \def\math_text_choice_word#1#2#%
+% {\normalizebodyfontsize\m_math_text_choice_face{\mathstyleface\normalmathstyle}%
+% \hbox#2\bgroup
+% \font_basics_switchtobodyfont\m_math_text_choice_face
+% #1%
+% \nospacing % \normalnospaces\plusone
+% \let\next}
+
+%D We accept a low level box specification so that one can make helpers:
+%D
+%D \startbuffer
+%D \startformula
+%D \startalign[m=2,align={middle}]
+%D \NC \text to 6cm{One\hfill} \NC a = 1 \NR
+%D \NC \text to 6cm{One Two\hfill} \NC b = 2 \NR
+%D \NC \text to 6cm{One Two Three\hfill} \NC c = 3 \NR
+%D \stopalign
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\def\math_text_choice_font#1#2#%
+ {\normalizebodyfontsize\m_math_text_choice_face{\mathstyleface\normalmathstyle}%
+ \hbox#2\bgroup
+ \bgroup
+ \aftergroup\hss
+ \aftergroup\egroup
+ \hss
+ \font_basics_switchtobodyfont\m_math_text_choice_face
+ #1%
+ \let\next}
+
+\def\math_text_choice_word#1#2#%
+ {\normalizebodyfontsize\m_math_text_choice_face{\mathstyleface\normalmathstyle}%
+ \hbox#2\bgroup
+ \bgroup
+ \aftergroup\hss
+ \aftergroup\egroup
+ \hss
+ \font_basics_switchtobodyfont\m_math_text_choice_face
+ #1%
+ \nospacing % \normalnospaces\plusone
+ \let\next}
+
+% \ruledhbox{$\mathtext{abc ffi}$}
+% \ruledhbox{$\mathword{abc ffi}$}
+
+% I need to decide:
+%
+%mathscriptboxmode \zerocount % no kerning
+%mathscriptboxmode \plusone % lists
+\mathscriptboxmode \plustwo % lists and boxes
+\mathscriptcharmode \plusone % lists and boxes
+%mathscriptboxmode \plusthree % lists and boxes with \boundary=1 (also for testing and demo)
+\mathrulethicknessmode\plusone % adaptive
+
+\permanent\protected\def\mathtext {\mathortext{\math_text_choice_font\relax}\hbox}
+\permanent\protected\def\mathword {\mathortext{\math_text_choice_word\relax}\hbox}
+
+\permanent\protected\def\mathtexttf{\mathortext{\math_text_choice_font\tf}\hbox}
+\permanent\protected\def\mathtextit{\mathortext{\math_text_choice_font\it}\hbox}
+\permanent\protected\def\mathtextsl{\mathortext{\math_text_choice_font\sl}\hbox}
+\permanent\protected\def\mathtextbf{\mathortext{\math_text_choice_font\bf}\hbox}
+\permanent\protected\def\mathtextbi{\mathortext{\math_text_choice_font\bi}\hbox}
+\permanent\protected\def\mathtextbs{\mathortext{\math_text_choice_font\bs}\hbox}
+
+\permanent\protected\def\mathwordtf{\mathortext{\math_text_choice_word\tf}\hbox}
+\permanent\protected\def\mathwordit{\mathortext{\math_text_choice_word\it}\hbox}
+\permanent\protected\def\mathwordsl{\mathortext{\math_text_choice_word\sl}\hbox}
+\permanent\protected\def\mathwordbf{\mathortext{\math_text_choice_word\bf}\hbox}
+\permanent\protected\def\mathwordbi{\mathortext{\math_text_choice_word\bi}\hbox}
+\permanent\protected\def\mathwordbs{\mathortext{\math_text_choice_word\bs}\hbox}
+
+%D Safeguard against redefinitions:
+
+\appendtoks
+ \enforced\let\_\normalunderscore % is textunderscore or fakeunderscore
+\to \everymathematics
+
+%D Because we may overload \type {\text} in other (structuring) macros, we say:
+
+\appendtoks
+ \enforced\let\text\mathtext
+\to \everymathematics
+
+%D The next code is derived from plain \TEX. The names will change!
+
+\newcount\interdisplaylinepenalty \interdisplaylinepenalty\plushundred
+
+% Actually, not using an if saves one macro so there is no penalty
+% for splitting up this macro.
+%
+% \newif\ifdt@p
+%
+% \def\displ@y
+% {\global\dt@ptrue
+% \math_openup\displayopenupvalue % was \openup\jot
+% \everycr
+% {\noalign
+% {\ifdt@p
+% \global\dt@pfalse
+% \ifdim\prevdepth>-\thousandpoint
+% \vskip-\lineskiplimit
+% \vskip\normallineskiplimit
+% \fi
+% \else
+% \penalty\interdisplaylinepenalty
+% \fi}}}
+
+\newtoks\mathdisplayaligntweaks
+
+% this can become an option:
+
+\let\math_display_align_hack_indeed\relax
+
+\protected\def\math_display_align_hack % I don't like the global, maybe we should push and pop
+ {\glet\math_display_align_hack_indeed\math_display_align_hack_remove_skip
+ \math_openup\displayopenupvalue % was \math_openup\jot
+ \everycr{\noalign{\math_display_align_hack_indeed}}}
+
+\def\math_display_align_hack_remove_skip
+ {\ifdim\prevdepth>-\thousandpoint
+ \vskip\dimexpr-\lineskiplimit+\normallineskiplimit\relax
+ \fi
+ \glet\math_display_align_hack_indeed\math_display_align_hack_insert_penalty}
+
+\def\math_display_align_hack_insert_penalty
+ {\penalty\interdisplaylinepenalty}
+
+\appendtoks
+ \math_display_align_hack
+\to \mathdisplayaligntweaks
+
+%D Text in math:
+
+\permanent\protected\def\mathortext
+ {\ifmmode
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+% \defineactivecharacter _ {\mathortext{_}{\_}} text_text $a^2$
+
+% force text mode, will be overloaded later
+
+\ifdefined\text\else \aliased\let\text\hbox \fi
+
+% \protected\def\mathoptext#1{\mathop{\text{#1}}}
+
+\permanent\protected\def\mathoptext
+ {\normalizebodyfontsize\m_math_text_choice_face{\mathstyleface\normalmathstyle}%
+ %\showmathstyle
+ \dowithnextbox
+ {\mathop{\box\nextbox}}%
+ \hbox\bgroup\font_basics_switchtobodyfont\m_math_text_choice_face\let\next}
+
+% new:
+
+% \startsetups math:morespacing
+% \Umathordordspacing\textstyle 1mu plus .5mu minus .25mu\relax
+% \stopsetups
+%
+% \setupmathematics
+% [setups=math:morespacing]
+
+\appendtoks
+ \edef\p_setups{\mathematicsparameter\c!setups}%
+ \ifempty\p_setups\else
+ \directsetup\p_setups
+ \fi
+\to \everyswitchmathematics
+
+% new:
+
+\permanent\protected\def\smallmathsymbol#1%
+ {\mathematics{\mathbin{\normalexpanded{\raise.15\exheight\hbox{\normalstartimath
+ \triggermathstyle{\the\numexpr\normalmathstyle+2\relax}#1%
+ \normalstopimath}}}}}
+
+% this should be a primitive:
+
+\permanent\def\mathextensiblecode#1#2{\clf_extensiblecode\numexpr#1\relax\numexpr#2\relax}
+\permanent\def\mathhorizontalcode#1#2{\clf_horizontalcode\numexpr#1\relax\numexpr#2\relax}
+
+% experimental:
+
+%D \starttyping
+%D \enabletrackers[math.openedup]
+%D
+%D \dorecurse{10}{\dorecurse{#1}{whatever }}
+%D
+%D \startitemize[packed]
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \stopitemize
+%D \startitemize[packed,columns]
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \stopitemize
+%D
+%D \dorecurse{5}{\dorecurse{#1}{whatever }\openedupimath{\frac{1}{2}} }
+%D
+%D \startitemize[packed,columns]
+%D \startitem whatever \openedupimath{1+2} whatever
+%D \startitem whatever \openedupimath{1+2} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{1+2} whatever
+%D \startitem whatever \openedupimath{1+2} whatever
+%D \stopitemize
+%D
+%D \dorecurse{5}{\dorecurse{#1}{whatever }\openedupimath{1+2} }
+%D
+%D \startitemize[packed]
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \startitem whatever \openedupimath{\frac{1}{2}} whatever
+%D \stopitemize
+%D
+%D \dorecurse{10}{whatever }
+%D \dorecurse {5}{\dorecurse{#1}{whatever }\openedupimath{\frac{1}{2}} }
+%D \dorecurse{10}{whatever }
+%D \stoptyping
+
+\def\m_math_inline_openup_ht{\dimexpr\ifinsidecolumns\strutdp\else\lineheight\fi/\plusfour\relax}
+\def\m_math_inline_openup_dp{\dimexpr\ifinsidecolumns\strutdp\else\lineheight\fi/\plusfour\relax}
+
+% \def\m_math_inline_openup_ht{\dimexpr\lineheight/\ifinsidecolumns\pluseight\else\plusfour\fi\relax}
+% \def\m_math_inline_openup_dp{\dimexpr\lineheight/\ifinsidecolumns\pluseight\else\plusfour\fi\relax}
+
+\installtextracker
+ {math.openedup}
+ {\let\math_inline_openup_start_yes\math_inline_openup_traced_start}
+ {\let\math_inline_openup_start_yes\math_inline_openup_normal_start}
+
+\protected\def\math_inline_openup_normal_start
+ {\scratchheight\dimexpr\ht\scratchbox+\m_math_inline_openup_ht\relax
+ \scratchdepth \dimexpr\dp\scratchbox+\m_math_inline_openup_dp\relax
+ \vrule\s!width\zeropoint\s!height\scratchheight\s!depth\scratchdepth\relax
+ \begingroup
+ \let\math_inline_openup_stop\math_inline_openup_normal_stop}
+
+\protected\def\math_inline_openup_normal_stop
+ {\endgroup
+ \vrule\s!width\zeropoint\s!height\scratchheight\s!depth\scratchdepth\relax}
+
+\protected\def\math_inline_openup_traced_start
+ {\scratchtopoffset \ht\scratchbox
+ \scratchbottomoffset\dp\scratchbox
+ \scratchheight \dimexpr\scratchtopoffset +\m_math_inline_openup_ht\relax
+ \scratchdepth \dimexpr\scratchbottomoffset+\m_math_inline_openup_dp\relax
+ \vrule\s!width\zeropoint\s!height\scratchheight\s!depth\scratchdepth\relax
+ \begingroup
+ \dousecolorparameter{darkred}%
+ \vrule\s!width\emwidth\s!height\scratchheight\s!depth-\scratchtopoffset\relax
+ \endgroup
+ \kern-\emwidth
+ \begingroup
+ \let\math_inline_openup_stop\math_inline_openup_traced_stop}
+
+\protected\def\math_inline_openup_traced_stop
+ {\endgroup
+ \kern-\emwidth
+ \begingroup
+ \dousecolorparameter{darkblue}%
+ \vrule\s!width\emwidth\s!height-\scratchbottomoffset\s!depth\scratchdepth\relax
+ \endgroup
+ \vrule\s!width\zeropoint\s!height\scratchheight\s!depth\scratchdepth\relax}
+
+\let\math_inline_openup_start_yes\math_inline_openup_normal_start
+\let\math_inline_openup_stop \relax
+
+\def\math_inline_openup_start_nop
+ {\let\math_inline_openup_stop\relax}
+
+\permanent\protected\def\openedupimath
+ {\dontleavehmode
+ \begingroup
+ \ifmmode
+ \expandafter\openedupimath_math
+ \else
+ \expandafter\openedupimath_text
+ \fi}
+
+\protected\def\openedupimath_math#1%
+ {\setbox\scratchbox\mathstylehbox{#1}%
+ \ifdim\ht\scratchbox>\strutht
+ \math_inline_openup_start_yes
+ \orelse\ifdim\dp\scratchbox>\strutdp
+ \math_inline_openup_start_yes
+ \else
+ \math_inline_openup_start_nop
+ \fi
+ #1%
+ \math_inline_openup_stop
+ \endgroup}
+
+\protected\def\openedupimath_text#1%
+ {\setbox\scratchbox\hbox{\startimath#1\stopimath}%
+ \ifdim\ht\scratchbox>\strutht
+ \math_inline_openup_start_yes
+ \orelse\ifdim\dp\scratchbox>\strutdp
+ \math_inline_openup_start_yes
+ \else
+ \math_inline_openup_start_nop
+ \fi
+ \startimath
+ #1%
+ \stopimath
+ \math_inline_openup_stop
+ \endgroup}
+
+% Also handy, especially for units:
+%
+% 0x002B=plus 0x2212=minus 0x2013=endash
+
+\permanent\protected\def\mathplus {+}
+\permanent\protected\def\mathminus {\ifmmode –\orelse\iffontchar\font`−−\else –\fi}
+\permanent\protected\def\mathplusminus{±}
+
+%D The \type {\displaywidth} is only known inside a display formula, so we need to catch
+%D it when still zero.
+
+\permanent\def\checkeddisplaywidth % hsize if zero
+ {\dimexpr
+ \ifzeropt\displaywidth
+ \hsize
+ \else
+ \displaywidth
+ \fi
+ \relax}
+
+\permanent\def\maximizeddisplaywidth % larger than zero but within hsize
+ {\dimexpr
+ \ifzeropt\displaywidth
+ \hsize
+ \orelse\ifdim\displaywidth>\hsize
+ \hsize
+ \else
+ \displaywidth
+ \fi
+ \relax}
+
+%D Experiment: (todo: same switch as italic, using \type {\everyswitchmathematics}).
+
+\newcount\c_math_domain_attribute
+
+\def\math_domain_initialize
+ {\ifnum\c_math_domain_attribute=\attributeunsetvalue \else
+ \clf_initializemathdomain % one time
+ \glet\math_domain_initialize\relax
+ \fi}
+
+\appendtoks
+ \edef\p_domain{\mathematicsparameter\c!domain}%
+ \ifempty\p_domain
+ \c_math_domain_attribute\attributeunsetvalue
+ \else
+ \c_math_domain_attribute\clf_getmathdomain\p_domain\relax
+ \math_domain_initialize
+ \fi
+\to \everyswitchmathematics % only in mathematics
+
+\appendtoks
+ \c_attr_mathdomain\c_math_domain_attribute
+\to \everymathematics
+
+\setupmathematics
+ [\s!italics=3] % for the moment only this one makes sense .. still experimental
+
+%D For special purposed we set this one:
+
+\installcorenamespace{mathrules}
+
+\permanent\protected\def\enablemathrules{\letgvalue{\??mathrules\fontclass}\plusone}
+
+\appendtoks
+ \mathrulesmode\ifcsname\??mathrules\fontclass\endcsname
+ \lastnamedcs
+ \else
+ \zerocount
+ \fi
+ \mathrulesfam\zerocount
+\to \everymathematics
+
+%D Maybe:
+
+% \starttabulate[|||c|c|]
+% \BC positive \BC negative \BC text \BC math \NC \NR
+% \NC \tex {f1} \tex {hairspace} \tex{,} \NC \tex {b1} \tex {neghairspace} \NC {\darkred\vl}\f1{\darkblue\vl} \NC ${\darkred\vl}\f1{\darkblue\vl}$ \NC \NR
+% \NC \tex {f2} \tex {thinspace} \tex{:} \NC \tex {b2} \tex {negthinspace} \tex{!} \NC {\darkred\vl}\f2{\darkblue\vl} \NC ${\darkred\vl}\f2{\darkblue\vl}$ \NC \NR
+% \NC \tex {f3} \tex {medspace} \tex{;} \NC \tex {b3} \tex {negmedspace} \NC {\darkred\vl}\f3{\darkblue\vl} \NC ${\darkred\vl}\f3{\darkblue\vl}$ \NC \NR
+% \NC \tex {f4} \tex {thickspace} \NC \tex {b4} \tex {negthickspace} \NC {\darkred\vl}\f4{\darkblue\vl} \NC ${\darkred\vl}\f4{\darkblue\vl}$ \NC \NR
+% \NC \tex {f5} \tex {enspace} \NC \tex {b5} \NC {\darkred\vl}\f5{\darkblue\vl} \NC ${\darkred\vl}\f5{\darkblue\vl}$ \NC \NR
+% \NC \tex {f6} \tex {emspace} \NC \tex {b6} \NC {\darkred\vl}\f6{\darkblue\vl} \NC ${\darkred\vl}\f6{\darkblue\vl}$ \NC \NR
+% \stoptabulate
+
+% \protected\def\negenspace{\kern-.5\emwidth}
+% \protected\def\negemspace{\kern- \emwidth}
+%
+% \protected\def\math_f#1%
+% {\ifcase#1\or
+% \hairspace
+% \or
+% \thinspace
+% \or
+% \medspace
+% \or
+% \thickspace
+% \or
+% \enspace
+% \or
+% \emspace
+% \fi}
+%
+% \protected\def\math_b#1%
+% {\ifcase#1\or
+% \neghairspace
+% \or
+% \negthinspace
+% \or
+% \negmedspace
+% \or
+% \negthickspace
+% \or
+% \negenspace
+% \or
+% \negemspace
+% \fi}
+%
+% \appendtoks
+% \let\f\math_f
+% \let\b\math_b
+% \to \everymathematics
+
+%D Experiment
+
+\permanent\protected\def\math_scripts_stack
+ {\c_attr_mathunstack\attributeunsetvalue}
+
+\permanent\protected\def\math_scripts_unstack
+ {\clf_enablescriptunstacking
+ \c_attr_mathunstack\plusone}
+
+\ifdefined\stackscripts \else \aliased\let\stackscripts \relax \fi
+\ifdefined\unstackscripts \else \aliased\let\unstackscripts\relax \fi
+
+\appendtoks
+ \enforced\let\stackscripts \math_scripts_stack
+ \enforced\let\unstackscripts\math_scripts_unstack
+\to \everymathematics
+
+%D Expensive (tracing and inject) but a primitive (using factors) is tricky as we
+%D want this frozen support.
+
+% \def\math_openup_parameter#1#2%
+% {\ifzeropt#1\displaystyle \orelse\ifdim#1\displaystyle =\maxdimen\else\frozen#1\displaystyle #2#1\displaystyle \fi
+% \ifzeropt#1\crampeddisplaystyle \orelse\ifdim#1\crampeddisplaystyle =\maxdimen\else\frozen#1\crampeddisplaystyle #2#1\crampeddisplaystyle \fi
+% \ifzeropt#1\textstyle \orelse\ifdim#1\textstyle =\maxdimen\else\frozen#1\textstyle #2#1\textstyle \fi
+% \ifzeropt#1\crampedtextstyle \orelse\ifdim#1\crampedtextstyle =\maxdimen\else\frozen#1\crampedtextstyle #2#1\crampedtextstyle \fi
+% \ifzeropt#1\scriptstyle \orelse\ifdim#1\scriptstyle =\maxdimen\else\frozen#1\scriptstyle #2#1\scriptstyle \fi
+% \ifzeropt#1\crampedscriptstyle \orelse\ifdim#1\crampedscriptstyle =\maxdimen\else\frozen#1\crampedscriptstyle #2#1\crampedscriptstyle \fi
+% \ifzeropt#1\scriptscriptstyle \orelse\ifdim#1\scriptscriptstyle =\maxdimen\else\frozen#1\scriptscriptstyle #2#1\scriptscriptstyle \fi
+% \ifzeropt#1\crampedscriptscriptstyle\orelse\ifdim#1\crampedscriptscriptstyle=\maxdimen\else\frozen#1\crampedscriptscriptstyle#2#1\crampedscriptscriptstyle\fi}
+
+% \def\mdim#1#2%
+% {\ifcase\ifzeropt#1#2\plusone\orelse\ifdim#1#2=\maxdimen\plusone\else\zerocount\fi}
+%
+% \def\mdim
+% {\afterassignment\mmdim\scratchdimen}
+%
+% \def\mmdim
+% {\ifcase\ifzeropt\scratchdimen\plusone\orelse\ifdim\scratchdimen=\maxdimen\plusone\else\zerocount\fi}
+%
+% \def\math_openup_parameter#1#2%
+% {\ifcondition\mdim#1\displaystyle \frozen#1\displaystyle #2\dimexpr#1\displaystyle \relax\fi
+% \ifcondition\mdim#1\crampeddisplaystyle \frozen#1\crampeddisplaystyle #2\dimexpr#1\crampeddisplaystyle \relax\fi
+% \ifcondition\mdim#1\textstyle \frozen#1\textstyle #2\dimexpr#1\textstyle \relax\fi
+% \ifcondition\mdim#1\crampedtextstyle \frozen#1\crampedtextstyle #2\dimexpr#1\crampedtextstyle \relax\fi
+% \ifcondition\mdim#1\scriptstyle \frozen#1\scriptstyle #2\dimexpr#1\scriptstyle \relax\fi
+% \ifcondition\mdim#1\crampedscriptstyle \frozen#1\crampedscriptstyle #2\dimexpr#1\crampedscriptstyle \relax\fi
+% \ifcondition\mdim#1\scriptscriptstyle \frozen#1\scriptscriptstyle #2\dimexpr#1\scriptscriptstyle \relax\fi
+% \ifcondition\mdim#1\crampedscriptscriptstyle\frozen#1\crampedscriptscriptstyle#2\dimexpr#1\crampedscriptscriptstyle\relax\fi}
+
+% \ifmathparameter : 0=zero, 1=set, 2=unset (les stracing clutter this way)
+
+%D New stuff:
+%D
+%D \starttyping
+%D $\mathopenupparameter\Umathradicalvgap{10.5}\sqrt[3]{x}$
+%D \stoptyping
+
+\permanent\protected\def\mathopenupparameter#1#2%
+ {\ifmathparameter#1\displaystyle \or\frozen#1\displaystyle #2#1\displaystyle \fi
+ \ifmathparameter#1\crampeddisplaystyle \or\frozen#1\crampeddisplaystyle #2#1\crampeddisplaystyle \fi
+ \ifmathparameter#1\textstyle \or\frozen#1\textstyle #2#1\textstyle \fi
+ \ifmathparameter#1\crampedtextstyle \or\frozen#1\crampedtextstyle #2#1\crampedtextstyle \fi
+ \ifmathparameter#1\scriptstyle \or\frozen#1\scriptstyle #2#1\scriptstyle \fi
+ \ifmathparameter#1\crampedscriptstyle \or\frozen#1\crampedscriptstyle #2#1\crampedscriptstyle \fi
+ \ifmathparameter#1\scriptscriptstyle \or\frozen#1\scriptscriptstyle #2#1\scriptscriptstyle \fi
+ \ifmathparameter#1\crampedscriptscriptstyle\or\frozen#1\crampedscriptscriptstyle#2#1\crampedscriptscriptstyle\fi}
+
+%D New stuff:
+%D
+%D \startbuffer
+%D $x\mathopen {!}+123+\mathclose {!}x$
+%D $x\tomathopen ! +123+\tomathclose ! x$
+%D $x\mathclose {!}+123+\mathopen {!}x$
+%D $x\tomathclose ! +123+\tomathopen ! x$
+%D $x ! +123+ ! x$
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+\permanent\protected\def\tomathord {\Umathclass\zerocount}
+\permanent\protected\def\tomathop {\Umathclass\plusone } % \mathinfix
+\permanent\protected\def\tomathbin {\Umathclass\plustwo }
+\permanent\protected\def\tomathrel {\Umathclass\plusthree}
+\permanent\protected\def\tomathopen {\Umathclass\plusfour } % \mathprefix
+\permanent\protected\def\tomathclose{\Umathclass\plusfive } % \mathsuffix
+\permanent\protected\def\tomathpunct{\Umathclass\plussix }
+
+%D Prescripts. These work in \CONTEXT\ because we have the super- and subscript
+%D triggers enabled as active characters but when we would have
+%D
+%D \starttyping
+%D \catcode\underscoreasciicode\subscriptcatcode
+%D \catcode\circumflexasciicode\superscriptcatcode
+%D \stoptyping
+%D
+%D we also need the next line to disable character escaping via \type {^} in
+%D math mode:
+%D
+%D \startbuffer
+%D \blank \hbox{
+%D \ruledhbox{${\tf X} ^1 _2 ^^3 __4$}
+%D \ruledhbox{${\tf X} ^1 $}
+%D \ruledhbox{${\tf X} _2 $}
+%D \ruledhbox{${\tf X} ^^3 $}
+%D \ruledhbox{${\tf X} __4$}
+%D \ruledhbox{${\tf X} ^1 _2 $}
+%D \ruledhbox{${\tf X} ^1 ^^3 $}
+%D \ruledhbox{${\tf X} ^1 __4$}
+%D \ruledhbox{${\tf X} _2 ^^3 $}
+%D \ruledhbox{${\tf X} _2 __4$}
+%D \ruledhbox{${\tf X} ^1 _2 ^^3 $}
+%D \ruledhbox{${\tf X} ^1 _2 __4$}
+%D \ruledhbox{${\tf X} _2 ^^3 $}
+%D \ruledhbox{${\tf X} _2 ^^3 __4$}
+%D \ruledhbox{${\tf X} ^^3 __4$}
+%D } \blank
+%D
+%D \typebuffer \getbuffer
+
+\normalsupmarkmode\plusone % 2 also disable ^[^+] in text mode
+
+\protect \endinput
+
+% % not used (yet)
+%
+% \newtoks \everystartimath
+% \newtoks \everystopimath
+%
+% \protected\def\startimath{\Ustartmath\the\everystartimath}
+% \protected\def\stopimath {\the\everystopimath\Ustopmath}
+%
+% \protected\def\m%
+% {\relax
+% \ifmmode\expandafter\math_m_stay\else\expandafter\math_m_math\fi}
+%
+% \protected\def\math_m_math#1%
+% {\startimath#1\stopimath}
+%
+% \let\math_m_stay\firstofoneargument
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-inl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-inl.mkxl
new file mode 100644
index 00000000000..2c7284458a5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-inl.mkxl
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=math-inl,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Inline,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Inline}
+
+\unprotect
+
+% The \MKII\ math grid snapping code has gone. A pity as it was rather effective
+% and worked quite well (for a couple of projects where it was demanded). Other code
+% might show up / move here.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-int.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-int.mkxl
new file mode 100644
index 00000000000..a8dcd3c9525
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-int.mkxl
@@ -0,0 +1,141 @@
+%D \module
+%D [ file=math-int,
+%D version=2007.07.19,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Scripts,
+%D author={Hans Hagen \& Taco Hoekwater \& Aditya Mahajan},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Integrals}
+
+% todo: int and sum etc can be stackers
+
+\unprotect
+
+%D \startbuffer
+%D $\int _a^b f(x) dx $ and also
+%D $\iint _a^b f(x,y) dxdy$,
+%D $\iiint _a^b f(x,y) dxdy$,
+%D \startformula
+%D \int _a^b f(x) dx \quad
+%D \iint _a^b f(x) dx \quad
+%D \iiint _a^b f(x) dx \quad
+%D \stopformula
+%D \stopbuffer
+%D
+%D Default: \getbuffer
+%D
+%D Displaylimits: \setupmathematics[integral=displaylimits] \getbuffer
+%D
+%D Limits: \setupmathematics[integral=limits] \getbuffer
+
+%D Adapted to mkiv by HH from code by AM.
+
+\installcorenamespace{mathintegral}
+
+\newconstant\mathintlimitmode
+
+\permanent\def\intlimits % also used elsewhere
+ {\ifcase\mathintlimitmode
+ \nolimits
+ \or
+ \displaylimits
+ \or
+ \limits
+ \or
+ \ifnum\attribute\mathmodeattribute=\plusone
+ \limits
+ \else
+ \nolimits
+ \fi
+ \else
+ % none
+ \fi}
+
+\letvalue{\??mathintegral nolimits}\zerocount
+\letvalue{\??mathintegral displaylimits}\plusone
+\letvalue{\??mathintegral limits}\plustwo
+\letvalue{\??mathintegral autolimits}\plusthree
+\letvalue{\??mathintegral none}\plusfour
+
+\appendtoks
+ \mathintlimitmode\executeifdefined{\??mathintegral\mathematicsparameter\s!integral}\zerocount
+\to \everyswitchmathematics
+
+\setupmathematics
+% [\v!integral=nolimits]
+ [\v!integral=autolimits]
+
+%D The following code is used for fallbacks and might become obsolete once
+%D we have enough \OPENTYPE\ math fonts.
+
+% \def\math_repeated_integal_i
+% {\int}
+
+% \def\math_repeated_integal_ii
+% {\math_repeated_integal_i
+% \math_repeated_integral_kern
+% \math_repeated_integal_i
+% \math_repeat_integral_finish
+% \intlimits}
+
+% \def\math_repeated_integal_iii
+% {\math_repeated_integal_i
+% \math_repeated_integral_kern
+% \math_repeated_integal_ii}
+%
+% \def\math_repeated_integal_iiii
+% {\math_repeated_integal_i
+% \math_repeated_integral_kern
+% \math_repeated_integal_iii}
+%
+% \protected\def\math_repeat_integral#1%
+% {\let\math_repeat_integral_finish\donothing
+% \iffontchar\textfont\zerocount#1\relax
+% \expandafter\math_repeat_integral_real
+% \else
+% \expandafter\math_repeat_integral_fake
+% \fi}
+%
+% \def\math_repeat_integral_fake#1#2%
+% {\let\math_repeat_integral_fake_symbol#2%
+% \futurelet\next\math_repeat_integral_fake_indeed}
+%
+% \def\math_repeat_integral_real#1#2%
+% {#1}
+%
+% \definemathcommand [iint] {\math_repeat_integral{"0222C}\normalint \math_repeated_integal_ii } % double
+% \definemathcommand [iiint] {\math_repeat_integral{"0222D}\normaliint \math_repeated_integal_iii } % tripple
+% \definemathcommand [iiiint] {\math_repeat_integral{"FFFFF}\normaliiint\math_repeated_integal_iiii} % quadruple
+%
+% \def\math_repeated_integral_kern
+% {\mkern-6mu\mathchoice{\mkern-3mu}{}{}{}}
+%
+% \def\math_repeat_integral_fake_indeed
+% {\ifx\next\limits
+% \math_repeated_integral_correction
+% \orelse\ifx\next\displaylimits
+% \math_repeated_integral_correction
+% \orelse\ifx\next\nolimits
+% % nothing
+% \orelse\ifcase\mathintlimitmode
+% % nothing
+% \else
+% \math_repeated_integral_correction
+% \fi
+% \math_repeat_integral_fake_symbol}
+%
+% \def\math_repeated_integral_correction
+% {\mkern-7mu\mathchoice{\mkern-2mu}{}{}{}%
+% \mathop\bgroup\mkern7mu\mathchoice{\mkern2mu}{}{}{}\let\math_repeat_integral_finish\egroup}
+%
+% %D If the \type{\limits} option is used after \type {\iint}, use \type
+% %D {\mathop} and fudge the left hand space a bit to make the subscript
+% %D visually centered.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-mis.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-mis.mkxl
new file mode 100644
index 00000000000..a824c593332
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-mis.mkxl
@@ -0,0 +1,60 @@
+%D \module
+%D [ file=math-mis,
+%D version=2013.04.06, % 2007.07.19,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Miscellaneous,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Miscellaneous}
+
+\unprotect
+
+%D This file contains left-overs moved from other files. It's mostly old stuff
+%D that we keep around for compatbility reasons.
+
+%D \macros
+%D {qedsymbol}
+%D
+%D [HH] The general Quod Erat Demonstrandum symbol is defined in such a way that
+%D we can configure it. Because this symbol is also used in text mode, we make it
+%D a normal text symbol with special behavior.
+
+\permanent\protected\def\qedsymbol#1%
+ {\ifhmode
+ \unskip\nobreakspace\hfill#1\par
+ \orelse\ifmmode
+ #1\relax % leading \eqno removed
+ \else
+ \dontleavehmode\emptyhbox\hfill#1\par
+ \fi}
+
+\definesymbol [qed] [\qedsymbol{\mathematics{\square}}]
+
+%D \macros
+%D {QED}
+%D
+%D [HH] For compatbility reasons we also provide the \type {\QED} command. In case
+%D this command is overloaded, we still have the symbol available. \symbol[qed]
+
+\permanent\protected\def\QED{\symbol[qed]}
+
+%D \macros
+%D {boxed}
+%D
+%D [HH] Another macro that users might expect (slightly adapted):
+
+\permanent\protected\def\boxed % maybe obsolete
+ {\ifmmode\expandafter\mframed\else\expandafter\framed\fi}
+
+%D Something low level for scientific calculator notation:
+
+\permanent\protected\def\scinot#1#2%
+ {#1\times10^{#2}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-noa.lmt b/Master/texmf-dist/tex/context/base/mkxl/math-noa.lmt
new file mode 100644
index 00000000000..118ab18267a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-noa.lmt
@@ -0,0 +1,2412 @@
+if not modules then modules = { } end modules ['math-noa'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to math-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- beware: this is experimental code and there will be a more generic (attribute value
+-- driven) interface too but for the moment this is ok (sometime in 2015-2016 i will
+-- start cleaning up as by then the bigger picture is clear and code has been used for
+-- years; the main handlers will get some extensions)
+--
+-- we will also make dedicated processors (faster)
+--
+-- beware: names will change as we wil make noads.xxx.handler i.e. xxx
+-- subnamespaces
+
+-- 20D6 -> 2190
+-- 20D7 -> 2192
+
+-- todo: most is mathchar_code so we can have simple dedicated loops
+
+-- nota bene: uunderdelimiter uoverdelimiter etc are radicals (we have 5 types)
+
+local next, tonumber = next, tonumber
+local utfchar, utfbyte = utf.char, utf.byte
+local formatters, gmatch = string.formatters, string.gmatch
+local sortedhash = table.sortedhash
+local insert, remove = table.insert, table.remove
+local div, round = math.div, math.round
+local bor, band = bit32.bor, bit32.band
+
+local fonts = fonts
+local nodes = nodes
+local node = node
+local mathematics = mathematics
+local context = context
+
+local otf = fonts.handlers.otf
+local otffeatures = fonts.constructors.features.otf
+local registerotffeature = otffeatures.register
+
+local privateattribute = attributes.private
+local registertracker = trackers.register
+local registerdirective = directives.register
+local logreporter = logs.reporter
+local setmetatableindex = table.setmetatableindex
+
+local colortracers = nodes.tracers.colors
+
+local trace_remapping = false registertracker("math.remapping", function(v) trace_remapping = v end)
+local trace_processing = false registertracker("math.processing", function(v) trace_processing = v end)
+local trace_analyzing = false registertracker("math.analyzing", function(v) trace_analyzing = v end)
+local trace_normalizing = false registertracker("math.normalizing", function(v) trace_normalizing = v end)
+local trace_collapsing = false registertracker("math.collapsing", function(v) trace_collapsing = v end)
+local trace_fixing = false registertracker("math.fixing", function(v) trace_foxing = v end)
+local trace_patching = false registertracker("math.patching", function(v) trace_patching = v end)
+local trace_goodies = false registertracker("math.goodies", function(v) trace_goodies = v end)
+local trace_variants = false registertracker("math.variants", function(v) trace_variants = v end)
+local trace_alternates = false registertracker("math.alternates", function(v) trace_alternates = v end)
+local trace_italics = false registertracker("math.italics", function(v) trace_italics = v end)
+local trace_kernpairs = false registertracker("math.kernpairs", function(v) trace_kernpairs = v end)
+local trace_domains = false registertracker("math.domains", function(v) trace_domains = v end)
+local trace_families = false registertracker("math.families", function(v) trace_families = v end)
+local trace_fences = false registertracker("math.fences", function(v) trace_fences = v end)
+local trace_unstacking = false registertracker("math.unstack", function(v) trace_unstacking = v end)
+
+local check_coverage = true registerdirective("math.checkcoverage", function(v) check_coverage = v end)
+
+local report_processing = logreporter("mathematics","processing")
+local report_remapping = logreporter("mathematics","remapping")
+local report_normalizing = logreporter("mathematics","normalizing")
+local report_collapsing = logreporter("mathematics","collapsing")
+local report_fixing = logreporter("mathematics","fixing")
+local report_patching = logreporter("mathematics","patching")
+local report_goodies = logreporter("mathematics","goodies")
+local report_variants = logreporter("mathematics","variants")
+local report_alternates = logreporter("mathematics","alternates")
+local report_italics = logreporter("mathematics","italics")
+local report_kernpairs = logreporter("mathematics","kernpairs")
+local report_domains = logreporter("mathematics","domains")
+local report_families = logreporter("mathematics","families")
+local report_fences = logreporter("mathematics","fences")
+local report_unstacking = logreporter("mathematics","unstack")
+
+local a_mathrendering = privateattribute("mathrendering")
+local a_exportstatus = privateattribute("exportstatus")
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+local tonut = nuts.tonut
+local nutstring = nuts.tostring
+
+local setfield = nuts.setfield
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local setchar = nuts.setchar
+local setfam = nuts.setfam
+local setsubtype = nuts.setsubtype
+local setattr = nuts.setattr
+local setattrlist = nuts.setattrlist
+local setwidth = nuts.setwidth
+local setheight = nuts.setheight
+local setdepth = nuts.setdepth
+
+local getfield = nuts.getfield
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getchar = nuts.getchar
+local getfont = nuts.getfont
+local getfam = nuts.getfam
+local getattr = nuts.getattr
+local getattrs = nuts.getattrs
+local getlist = nuts.getlist
+local getwidth = nuts.getwidth
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+
+local getnucleus = nuts.getnucleus
+local getsub = nuts.getsub
+local getsup = nuts.getsup
+local getsubpre = nuts.getsubpre
+local getsuppre = nuts.getsuppre
+
+local setnucleus = nuts.setnucleus
+local setsub = nuts.setsub
+local setsup = nuts.setsup
+local setsubpre = nuts.setsubpre
+local setsuppre = nuts.setsuppre
+
+local flush_node = nuts.flush
+local copy_node = nuts.copy
+local slide_nodes = nuts.slide
+local set_visual = nuts.setvisual
+
+local mlist_to_hlist = nuts.mlist_to_hlist
+
+local new_kern = nodepool.kern
+local new_submlist = nodepool.submlist
+local new_noad = nodepool.noad
+local new_delimiter = nodepool.delimiter
+local new_fence = nodepool.fence
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontcharacters = fonthashes.characters
+local fontitalics = fonthashes.italics
+
+local variables = interfaces.variables
+local texsetattribute = tex.setattribute
+local texgetattribute = tex.getattribute
+local getfontoffamily = tex.getfontoffamily
+local unsetvalue = attributes.unsetvalue
+local implement = interfaces.implement
+
+local v_reset = variables.reset
+
+local chardata = characters.data
+
+noads = noads or { } -- todo: only here
+local noads = noads
+
+noads.processors = noads.processors or { }
+local processors = noads.processors
+
+noads.handlers = noads.handlers or { }
+local handlers = noads.handlers
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+local setaction = tasks.setaction
+
+local nodecodes = nodes.nodecodes
+local noadcodes = nodes.noadcodes
+local fencecodes = nodes.fencecodes
+
+local ordnoad_code = noadcodes.ord
+local opdisplaylimitsnoad_code = noadcodes.opdisplaylimits
+local oplimitsnoad_code = noadcodes.oplimits
+local opnolimitsnoad_code = noadcodes.opnolimits
+local binnoad_code = noadcodes.bin
+local relnode_code = noadcodes.rel
+local opennoad_code = noadcodes.open
+local closenoad_code = noadcodes.close
+local punctnoad_code = noadcodes.punct
+local innernoad_code = noadcodes.inner
+local undernoad_code = noadcodes.under
+local overnoad_code = noadcodes.over
+local vcenternoad_code = noadcodes.vcenter
+local ordlimitsnoad_code = noadcodes.ordlimits or oplimitsnoad_code
+
+local noad_code = nodecodes.noad -- attr nucleus sub sup
+local accent_code = nodecodes.accent -- attr nucleus sub sup accent
+local radical_code = nodecodes.radical -- attr nucleus sub sup left degree
+local fraction_code = nodecodes.fraction -- attr nucleus sub sup left right
+local subbox_code = nodecodes.subbox -- attr list
+local submlist_code = nodecodes.submlist -- attr list
+local mathchar_code = nodecodes.mathchar -- attr fam char
+local mathtextchar_code = nodecodes.mathtextchar -- attr fam char
+local delimiter_code = nodecodes.delimiter -- attr small_fam small_char large_fam large_char
+----- style_code = nodecodes.style -- attr style
+----- parameter_code = nodecodes.parameter -- attr style
+local math_choice = nodecodes.choice -- attr display text script scriptscript
+local fence_code = nodecodes.fence -- attr subtype
+
+local leftfence_code = fencecodes.left
+local middlefence_code = fencecodes.middle
+local rightfence_code = fencecodes.right
+
+-- local mathclasses = mathematics.classes
+-- local fenceclasses = {
+-- [leftfence_code] = mathclasses.open,
+-- [middlefence_code] = mathclasses.middle,
+-- [rightfence_code] = mathclasses.close,
+-- }
+
+-- this initial stuff is tricky as we can have removed and new nodes with the same address
+-- the only way out is a free-per-page list of nodes (not bad anyway)
+
+-- local gf = getfield local gt = setmetatableindex("number") getfield = function(n,f) gt[f] = gt[f] + 1 return gf(n,f) end mathematics.GETFIELD = gt
+-- local sf = setfield local st = setmetatableindex("number") setfield = function(n,f,v) st[f] = st[f] + 1 sf(n,f,v) end mathematics.SETFIELD = st
+
+local function process(start,what,n,parent)
+
+ if n then
+ n = n + 1
+ else
+ n = 0
+ end
+ --
+ local initial = start
+ --
+ slide_nodes(start) -- we still miss a prev in noads -- fences test code
+ --
+ while start do
+ local id = getid(start)
+ if trace_processing then
+ if id == noad_code then
+ report_processing("%w%S, class %a",n*2,nutstring(start),noadcodes[getsubtype(start)])
+ elseif id == mathchar_code then
+ local char = getchar(start)
+ local font = getfont(start)
+ local fam = getfam(start)
+ report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,nutstring(start),fam,font,char,char)
+ else
+ report_processing("%w%S",n*2,nutstring(start))
+ end
+ end
+ local proc = what[id]
+ if proc then
+ -- report_processing("start processing")
+ local done, newstart, newinitial = proc(start,what,n,parent) -- prev is bugged: or getprev(start)
+ if newinitial then
+ initial = newinitial -- temp hack .. we will make all return head
+ if newstart then
+ start = newstart
+ -- report_processing("stop processing (new start)")
+ else
+ -- report_processing("quit processing (done)")
+ break
+ end
+ else
+ if newstart then
+ start = newstart
+ -- report_processing("stop processing (new start)")
+ else
+ -- report_processing("stop processing")
+ end
+ end
+ elseif id == noad_code then
+ -- single characters are like this
+ local noad = getnucleus(start) if noad then process(noad,what,n,start) end -- list
+ noad = getsup (start) if noad then process(noad,what,n,start) end -- list
+ noad = getsub (start) if noad then process(noad,what,n,start) end -- list
+ if getsubpre then
+ noad = getsuppre (start) if noad then process(noad,what,n,start) end -- list
+ noad = getsubpre (start) if noad then process(noad,what,n,start) end -- list
+ end
+ elseif id == mathchar_code or id == mathtextchar_code or id == delimiter_code then
+ break
+ elseif id == subbox_code or id == submlist_code then
+ local noad = getlist(start) if noad then process(noad,what,n,start) end -- list (not getlist !)
+ elseif id == fraction_code then
+ local noad = getfield(start,"num") if noad then process(noad,what,n,start) end -- list
+ noad = getfield(start,"denom") if noad then process(noad,what,n,start) end -- list
+ noad = getfield(start,"left") if noad then process(noad,what,n,start) end -- delimiter
+ noad = getfield(start,"right") if noad then process(noad,what,n,start) end -- delimiter
+ elseif id == math_choice then
+ local noad = getfield(start,"display") if noad then process(noad,what,n,start) end -- list
+ noad = getfield(start,"text") if noad then process(noad,what,n,start) end -- list
+ noad = getfield(start,"script") if noad then process(noad,what,n,start) end -- list
+ noad = getfield(start,"scriptscript") if noad then process(noad,what,n,start) end -- list
+ elseif id == fence_code then
+ local noad = getfield(start,"delimiter") if noad then process(noad,what,n,start) end -- delimiter
+ elseif id == radical_code then
+ local noad = getnucleus(start) if noad then process(noad,what,n,start) end -- list
+ noad = getsup (start) if noad then process(noad,what,n,start) end -- list
+ noad = getsub (start) if noad then process(noad,what,n,start) end -- list
+ if getsubpre then
+ noad = getsuppre (start) if noad then process(noad,what,n,start) end -- list
+ noad = getsubpre (start) if noad then process(noad,what,n,start) end -- list
+ end
+ noad = getfield(start,"left") if noad then process(noad,what,n,start) end -- delimiter
+ noad = getfield(start,"degree") if noad then process(noad,what,n,start) end -- list
+ elseif id == accent_code then
+ local noad = getnucleus(start) if noad then process(noad,what,n,start) end -- list
+ noad = getsup (start) if noad then process(noad,what,n,start) end -- list
+ noad = getsub (start) if noad then process(noad,what,n,start) end -- list
+ if getsubpre then
+ noad = getsuppre (start) if noad then process(noad,what,n,start) end -- list
+ noad = getsubpre (start) if noad then process(noad,what,n,start) end -- list
+ end
+ noad = getfield(start,"accent") if noad then process(noad,what,n,start) end -- list
+ noad = getfield(start,"bot_accent") if noad then process(noad,what,n,start) end -- list
+ -- elseif id == style_code then
+ -- -- has a next
+ -- elseif id == parameter_code then
+ -- -- has a next
+ -- else
+ -- -- glue, penalty, etc
+ end
+ start = getnext(start)
+ end
+ if not parent then
+ return initial -- only first level -- for now
+ end
+end
+
+local function processnested(current,what,n)
+ local noad = nil
+ local id = getid(current)
+ if id == noad_code then
+ noad = getnucleus(current) if noad then process(noad,what,n,current) end -- list
+ noad = getsup (current) if noad then process(noad,what,n,current) end -- list
+ noad = getsub (current) if noad then process(noad,what,n,current) end -- list
+ if getsubpre then
+ noad = getsuppre (current) if noad then process(noad,what,n,current) end -- list
+ noad = getsubpre (current) if noad then process(noad,what,n,current) end -- list
+ end
+ elseif id == subbox_code or id == submlist_code then
+ noad = getlist(current) if noad then process(noad,what,n,current) end -- list (not getlist !)
+ elseif id == fraction_code then
+ noad = getfield(current,"num") if noad then process(noad,what,n,current) end -- list
+ noad = getfield(current,"denom") if noad then process(noad,what,n,current) end -- list
+ noad = getfield(current,"left") if noad then process(noad,what,n,current) end -- delimiter
+ noad = getfield(current,"right") if noad then process(noad,what,n,current) end -- delimiter
+ elseif id == math_choice then
+ noad = getfield(current,"display") if noad then process(noad,what,n,current) end -- list
+ noad = getfield(current,"text") if noad then process(noad,what,n,current) end -- list
+ noad = getfield(current,"script") if noad then process(noad,what,n,current) end -- list
+ noad = getfield(current,"scriptscript") if noad then process(noad,what,n,current) end -- list
+ elseif id == fence_code then
+ noad = getfield(current,"delimiter") if noad then process(noad,what,n,current) end -- delimiter
+ elseif id == radical_code then
+ noad = getnucleus(current) if noad then process(noad,what,n,current) end -- list
+ noad = getsup (current) if noad then process(noad,what,n,current) end -- list
+ noad = getsub (current) if noad then process(noad,what,n,current) end -- list
+ if getsubpre then
+ noad = getsuppre (current) if noad then process(noad,what,n,current) end -- list
+ noad = getsubpre (current) if noad then process(noad,what,n,current) end -- list
+ end
+ noad = getfield(current,"left") if noad then process(noad,what,n,current) end -- delimiter
+ noad = getfield(current,"degree") if noad then process(noad,what,n,current) end -- list
+ elseif id == accent_code then
+ noad = getnucleus(current) if noad then process(noad,what,n,current) end -- list
+ noad = getsup (current) if noad then process(noad,what,n,current) end -- list
+ noad = getsub (current) if noad then process(noad,what,n,current) end -- list
+ if getsubpre then
+ noad = getsuppre (current) if noad then process(noad,what,n,current) end -- list
+ noad = getsubpre (current) if noad then process(noad,what,n,current) end -- list
+ end
+ noad = getfield(current,"accent") if noad then process(noad,what,n,current) end -- list
+ noad = getfield(current,"bot_accent") if noad then process(noad,what,n,current) end -- list
+ end
+end
+
+local function processstep(current,process,n,id)
+ local noad = nil
+ local id = id or getid(current)
+ if id == noad_code then
+ noad = getnucleus(current) if noad then process(noad,n,current) end -- list
+ noad = getsup (current) if noad then process(noad,n,current) end -- list
+ noad = getsub (current) if noad then process(noad,n,current) end -- list
+ if getsubpre then
+ noad = getsuppre (current) if noad then process(noad,n,current) end -- list
+ noad = getsubpre (current) if noad then process(noad,n,current) end -- list
+ end
+ elseif id == subbox_code or id == submlist_code then
+ noad = getlist(current) if noad then process(noad,n,current) end -- list (not getlist !)
+ elseif id == fraction_code then
+ noad = getfield(current,"num") if noad then process(noad,n,current) end -- list
+ noad = getfield(current,"denom") if noad then process(noad,n,current) end -- list
+ noad = getfield(current,"left") if noad then process(noad,n,current) end -- delimiter
+ noad = getfield(current,"right") if noad then process(noad,n,current) end -- delimiter
+ elseif id == math_choice then
+ noad = getfield(current,"display") if noad then process(noad,n,current) end -- list
+ noad = getfield(current,"text") if noad then process(noad,n,current) end -- list
+ noad = getfield(current,"script") if noad then process(noad,n,current) end -- list
+ noad = getfield(current,"scriptscript") if noad then process(noad,n,current) end -- list
+ elseif id == fence_code then
+ noad = getfield(current,"delimiter") if noad then process(noad,n,current) end -- delimiter
+ elseif id == radical_code then
+ noad = getnucleus(current) if noad then process(noad,n,current) end -- list
+ noad = getsup (current) if noad then process(noad,n,current) end -- list
+ noad = getsub (current) if noad then process(noad,n,current) end -- list
+ if getsubpre then
+ noad = getsuppre (current) if noad then process(noad,n,current) end -- list
+ noad = getsubpre (current) if noad then process(noad,n,current) end -- list
+ end
+ noad = getfield(current,"left") if noad then process(noad,n,current) end -- delimiter
+ noad = getfield(current,"degree") if noad then process(noad,n,current) end -- list
+ elseif id == accent_code then
+ noad = getnucleus(current) if noad then process(noad,n,current) end -- list
+ noad = getsup (current) if noad then process(noad,n,current) end -- list
+ noad = getsub (current) if noad then process(noad,n,current) end -- list
+ if getsubpre then
+ noad = getsuppre (current) if noad then process(noad,n,current) end -- list
+ noad = getsubpre (current) if noad then process(noad,n,current) end -- list
+ end
+ noad = getfield(current,"accent") if noad then process(noad,n,current) end -- list
+ noad = getfield(current,"bot_accent") if noad then process(noad,n,current) end -- list
+ end
+end
+
+local function processnoads(head,actions,banner)
+ if trace_processing then
+ report_processing("start %a",banner)
+ head = process(head,actions)
+ report_processing("stop %a",banner)
+ else
+ head = process(head,actions)
+ end
+ return head
+end
+
+noads.process = processnoads
+noads.processnested = processnested
+noads.processouter = process
+
+-- experiment (when not present fall back to fam 0) -- needs documentation
+
+local unknowns = { }
+local checked = { } -- simple case
+local tracked = false trackers.register("fonts.missing", function(v) tracked = v end)
+local cached = setmetatableindex("table") -- complex case
+
+local function errorchar(font,char)
+ local done = unknowns[char]
+ if done then
+ unknowns[char] = done + 1
+ else
+ unknowns[char] = 1
+ end
+ if tracked then
+ -- slower as we check each font too and we always replace as math has
+ -- more demands than text
+ local fake = cached[font][char]
+ if fake then
+ return fake
+ else
+ local kind, fake = fonts.checkers.placeholder(font,char)
+ if not fake or kind ~= "char" then
+ fake = 0x3F
+ end
+ cached[font][char] = fake
+ return fake
+ end
+ else
+ -- only simple checking, report at the end so one should take
+ -- action anyway ... we can miss a few checks but that is ok
+ -- as there is at least one reported
+ if not checked[char] then
+ if trace_normalizing then
+ report_normalizing("character %C is not available",char)
+ end
+ checked[char] = true
+ end
+ return 0x3F
+ end
+end
+
+-- 0-2 regular
+-- 3-5 bold
+-- 6-8 pseudobold
+
+-- this could best be integrated in the remapper, and if we run into problems, we
+-- might as well do this
+
+do
+
+ local families = { }
+ local a_mathfamily = privateattribute("mathfamily")
+ local boldmap = mathematics.boldmap
+
+ local familymap = { [0] =
+ "regular",
+ "regular",
+ "regular",
+ "bold",
+ "bold",
+ "bold",
+ "pseudobold",
+ "pseudobold",
+ "pseudobold",
+ }
+
+ families[fraction_code] = function(pointer,what,n,parent)
+ local a = getattr(pointer,a_mathfamily)
+ if a and a >= 0 then
+ if a > 0 then
+ setattr(pointer,a_mathfamily,0)
+ if a > 5 then
+ a = a - 3
+ end
+ end
+ setfam(pointer,a)
+ end
+ processnested(pointer,families,n+1)
+ end
+
+ families[noad_code] = function(pointer,what,n,parent)
+ local a = getattr(pointer,a_mathfamily)
+ if a and a >= 0 then
+ if a > 0 then
+ setattr(pointer,a_mathfamily,0)
+ if a > 5 then
+ a = a - 3
+ end
+ end
+ setfam(pointer,a)
+ end
+ processnested(pointer,families,n+1)
+ end
+
+ families[mathchar_code] = function(pointer)
+ if getfam(pointer) == 0 then
+ local a = getattr(pointer,a_mathfamily)
+ if a and a > 0 then
+ setattr(pointer,a_mathfamily,0)
+ if a > 5 then
+ local char = getchar(pointer)
+ local bold = boldmap[char]
+ local newa = a - 3
+ if not bold then
+ if trace_families then
+ report_families("no bold replacement for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])
+ end
+ setfam(pointer,newa)
+ elseif not fontcharacters[getfontoffamily(newa)][bold] then
+ if trace_families then
+ report_families("no bold character for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])
+ end
+ if newa > 3 then
+ setfam(pointer,newa-3)
+ end
+ else
+ setattr(pointer,a_exportstatus,char)
+ setchar(pointer,bold)
+ if trace_families then
+ report_families("replacing %C by bold %C, family %s with remap %s becomes %s with remap %s",char,bold,a,familymap[a],newa,familymap[newa])
+ end
+ setfam(pointer,newa)
+ end
+ else
+ local char = getchar(pointer)
+ if not fontcharacters[getfontoffamily(a)][char] then
+ if trace_families then
+ report_families("no bold replacement for %C",char)
+ end
+ else
+ if trace_families then
+ report_families("family of %C becomes %s with remap %s",char,a,familymap[a])
+ end
+ setfam(pointer,a)
+ end
+ end
+ end
+ end
+ end
+ families[delimiter_code] = function(pointer)
+ if getfield(pointer,"small_fam") == 0 then
+ local a = getattr(pointer,a_mathfamily)
+ if a and a > 0 then
+ setattr(pointer,a_mathfamily,0)
+ if a > 5 then
+ -- no bold delimiters in unicode
+ a = a - 3
+ end
+ local char = getfield(pointer,"small_char")
+ local okay = fontcharacters[getfontoffamily(a)][char]
+ if okay then
+ setfield(pointer,"small_fam",a)
+ elseif a > 2 then
+ setfield(pointer,"small_fam",a-3)
+ end
+ local char = getfield(pointer,"large_char")
+ local okay = fontcharacters[getfontoffamily(a)][char]
+ if okay then
+ setfield(pointer,"large_fam",a)
+ elseif a > 2 then
+ setfield(pointer,"large_fam",a-3)
+ end
+ else
+ setfield(pointer,"small_fam",0)
+ setfield(pointer,"large_fam",0)
+ end
+ end
+ end
+
+ -- will become:
+
+ -- families[delimiter_code] = function(pointer)
+ -- if getfam(pointer) == 0 then
+ -- local a = getattr(pointer,a_mathfamily)
+ -- if a and a > 0 then
+ -- setattr(pointer,a_mathfamily,0)
+ -- if a > 5 then
+ -- -- no bold delimiters in unicode
+ -- a = a - 3
+ -- end
+ -- local char = getchar(pointer)
+ -- local okay = fontcharacters[getfontoffamily(a)][char]
+ -- if okay then
+ -- setfam(pointer,a)
+ -- elseif a > 2 then
+ -- setfam(pointer,a-3)
+ -- end
+ -- else
+ -- setfam(pointer,0)
+ -- end
+ -- end
+ -- end
+
+ families[mathtextchar_code] = families[mathchar_code]
+
+ function handlers.families(head,style,penalties)
+ processnoads(head,families,"families")
+ return true -- not needed
+ end
+
+end
+
+-- character remapping
+
+do
+
+ local a_mathalphabet = privateattribute("mathalphabet")
+ local a_mathgreek = privateattribute("mathgreek")
+
+ local relocate = { }
+
+ local remapalphabets = mathematics.remapalphabets
+ local fallbackstyleattr = mathematics.fallbackstyleattr
+ local setnodecolor = colortracers.set
+
+ local function report_remap(tag,id,old,new,extra)
+ if new then
+ report_remapping("remapping %s in font (%s,%s) from %C to %C%s",
+ tag,id,fontdata[id].properties.fontname or "",old,new,extra)
+ else
+ -- error !
+ report_remapping("remapping %s in font (%s,%s) from %C to ?",
+ tag,id,fontdata[id].properties.fontname or "",old)
+ end
+ end
+
+ local function checked(pointer)
+ local char = getchar(pointer)
+ local font = getfont(pointer)
+ local data = fontcharacters[font]
+ if not data[char] then
+ local specials = characters.data[char].specials
+ if specials and (specials[1] == "char" or specials[1] == "font") then
+ local newchar = specials[#specials]
+ if trace_remapping then
+ report_remap("fallback",font,char,newchar)
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:isol")
+ end
+ setattr(pointer,a_exportstatus,char) -- testcase: exponentiale
+ setchar(pointer,newchar)
+ return true
+ end
+ end
+ end
+
+ -- We can optimize this if we really think that math is a bottleneck which it never
+ -- really is. Beware: the font is the text font in the family, so we only check the
+ -- text font here.
+
+ relocate[mathchar_code] = function(pointer)
+ local g = getattr(pointer,a_mathgreek) or 0
+ local a = getattr(pointer,a_mathalphabet) or 0
+ -- local g, a = getattrs(pointer,a_mathgreek,a_mathalphabet)
+ -- if not a then a = 0 end
+ -- if not g then g = 0 end
+ local char = getchar(pointer)
+ local font = getfont(pointer)
+ local characters = fontcharacters[font]
+ if a > 0 or g > 0 then
+ if a > 0 then
+ -- not really critital but we could use properties
+ setattr(pointer,a_mathgreek,0)
+ end
+ if g > 0 then
+ -- not really critital but we could use properties
+ setattr(pointer,a_mathalphabet,0)
+ end
+ local newchar = remapalphabets(char,a,g)
+ if newchar then
+ local newchardata = characters[newchar]
+ if newchardata then
+ if trace_remapping then
+ report_remap("char",font,char,newchar,newchardata.commands and " (virtual)" or "")
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:isol")
+ end
+ setchar(pointer,newchar)
+ return true
+ else
+ local fallback = fallbackstyleattr(a)
+ if fallback then
+ local newchar = remapalphabets(char,fallback,g)
+ if newchar then
+ if characters[newchar] then
+ if trace_remapping then
+ report_remap("char",font,char,newchar," (fallback remapping used)")
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:isol")
+ end
+ setchar(pointer,newchar)
+ return true
+ elseif trace_remapping then
+ report_remap("char",font,char,newchar," fails (no fallback character)")
+ end
+ elseif trace_remapping then
+ report_remap("char",font,char,newchar," fails (no fallback remap character)")
+ end
+ elseif trace_remapping then
+ report_remap("char",font,char,newchar," fails (no fallback style)")
+ end
+ end
+ elseif trace_remapping then
+ local chardata = characters[char]
+ if chardata and chardata.commands then
+ report_remap("char",font,char,char," (virtual)")
+ end
+ end
+ end
+ if not characters[char] then
+ local fam = getfam(pointer)
+ local fnt = getfontoffamily(fam,1)
+ setchar(pointer,errorchar(font,char))
+ if font ~= fnt then
+ errorchar(fnt,char)
+ errorchar(getfontoffamily(fam,2),char)
+ end
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:medi")
+ end
+ if check_coverage then
+ return checked(pointer)
+ end
+ end
+
+ relocate[mathtextchar_code] = function(pointer)
+ if trace_analyzing then
+ setnodecolor(pointer,"font:init")
+ end
+ end
+
+ relocate[delimiter_code] = function(pointer)
+ if trace_analyzing then
+ setnodecolor(pointer,"font:fina")
+ end
+ end
+
+ function handlers.relocate(head,style,penalties)
+ processnoads(head,relocate,"relocate")
+ return true -- not needed
+ end
+
+end
+
+-- rendering (beware, not exported)
+
+do
+
+ local render = { }
+
+ local rendersets = mathematics.renderings.numbers or { } -- store
+
+ render[mathchar_code] = function(pointer)
+ local attr = getattr(pointer,a_mathrendering)
+ if attr and attr > 0 then
+ local char = getchar(pointer)
+ local renderset = rendersets[attr]
+ if renderset then
+ local newchar = renderset[char]
+ if newchar then
+ local font = getfont(pointer)
+ local characters = fontcharacters[font]
+ if characters and characters[newchar] then
+ setchar(pointer,newchar)
+ setattr(pointer,a_exportstatus,char)
+ end
+ end
+ end
+ end
+ end
+
+ function handlers.render(head,style,penalties)
+ processnoads(head,render,"render")
+ return true -- not needed
+ end
+
+end
+
+-- some resize options (this works ok because the content is
+-- empty and no larger next will be forced)
+--
+-- beware: we don't use \delcode but \Udelcode and as such have
+-- no large_fam; also, we need to check for subtype and/or
+-- small_fam not being 0 because \. sits in 0,0 by default
+--
+-- todo: just replace the character by an ord noad
+-- and remove the right delimiter as well
+
+do
+
+ local a_mathsize = privateattribute("mathsize") -- this might move into other fence code
+ local resize = { }
+
+ resize[fence_code] = function(pointer)
+ local subtype = getsubtype(pointer)
+ if subtype == leftfence_code or subtype == rightfence_code then
+ local a = getattr(pointer,a_mathsize)
+ if a and a > 0 then
+ local method = div(a,100)
+ local size = a % 100
+ setattr(pointer,a_mathsize,0)
+ local delimiter = getfield(pointer,"delimiter")
+ local chr = getchar(delimiter)
+ if chr > 0 then
+ local fam = getfam(delimiter)
+ local id = getfontoffamily(fam)
+ if id > 0 then
+ local data = fontdata[id]
+ local char = mathematics.big(data,chr,size,method)
+ local ht = getheight(pointer)
+ local dp = getdepth(pointer)
+ if ht == 1 or dp == 1 then -- 1 scaled point is a signal
+ local chardata = data.characters[char]
+ if ht == 1 then
+ setheight(pointer,chardata.height)
+ end
+ if dp == 1 then
+ setdepth(pointer,chardata.depth)
+ end
+ end
+ if trace_fences then
+ report_fences("replacing %C by %C using method %a and size %a",chr,char,method,size)
+ end
+ setchar(delimiter,char)
+ end
+ end
+ end
+ end
+ end
+
+ function handlers.resize(head,style,penalties)
+ processnoads(head,resize,"resize")
+ return true -- not needed
+ end
+
+end
+
+-- still not perfect:
+
+do
+
+ local a_autofence = privateattribute("mathautofence")
+ local autofences = { }
+ local dummyfencechar = 0x2E
+
+ local function makefence(what,char)
+ local d = new_delimiter() -- todo: attr
+ local f = new_fence() -- todo: attr
+ if char then
+ local sym = getnucleus(char)
+ local chr = getchar(sym)
+ local fam = getfam(sym)
+ if chr == dummyfencechar then
+ chr = 0
+ end
+ setchar(d,chr)
+ setfam(d,fam)
+ flush_node(sym)
+ end
+ setattrlist(d,char)
+ setattrlist(f,char)
+ setsubtype(f,what)
+ setfield(f,"delimiter",d)
+ setfield(f,"class",-1) -- tex itself does this, so not fenceclasses[what]
+ return f
+ end
+
+ local function show(where,pointer)
+ print("")
+ local i = 0
+ for n in nuts.traverse(pointer) do
+ i = i + 1
+ print(i,where,nuts.tonode(n))
+ end
+ print("")
+ end
+
+ local function makelist(middle,noad,f_o,o_next,c_prev,f_c)
+-- report_fences(
+-- "middle %s, noad %s, open %s, opennext %s, closeprev %s, close %s",
+-- middle or "?",
+-- noad or "?",
+-- f_o or "?",
+-- o_next or "?",
+-- c_prev or "?",
+-- f_c or "?"
+-- )
+ local list = new_submlist()
+ setsubtype(noad,innernoad_code)
+ setnucleus(noad,list)
+ setlist(list,f_o)
+ setlink(f_o,o_next) -- prev of list is nil
+ setlink(c_prev,f_c) -- next of list is nil
+-- show("list",f_o)
+ if middle and next(middle) then
+ local prev = f_o
+ local current = o_next
+ while current ~= f_c do
+ local midl = middle[current]
+ local next = getnext(current)
+ if midl then
+ local fence = makefence(middlefence_code,current)
+ setnucleus(current)
+ flush_node(current)
+ middle[current] = nil
+ -- replace_node
+ setlink(prev,fence,next)
+ prev = fence
+ else
+ prev = current
+ end
+ current = next
+ end
+ end
+ return noad
+ end
+
+ -- relinking is now somewhat overdone
+
+ local function convert_both(open,close,middle)
+ local o_next = getnext(open)
+ if o_next == close then
+ return close
+ else
+ local c_prev, c_next = getboth(close)
+ local f_o = makefence(leftfence_code,open)
+ local f_c = makefence(rightfence_code,close)
+ makelist(middle,open,f_o,o_next,c_prev,f_c)
+ setnucleus(close)
+ flush_node(close)
+ -- open is now a list
+ setlink(open,c_next)
+ return open
+ end
+ end
+
+ local function convert_open(open,last,middle) -- last is really last (final case)
+ local f_o = makefence(leftfence_code,open)
+ local f_c = makefence(rightfence_code)
+ local o_next = getnext(open)
+ makelist(middle,open,f_o,o_next,last,nil)
+ -- open is now a list
+ setlink(open,l_next)
+ return open
+ end
+
+ local function convert_close(first,close,middle)
+ local f_o = makefence(leftfence_code)
+ local f_c = makefence(rightfence_code,close)
+ local c_prev = getprev(close)
+ local f_next = getnext(first)
+ makelist(middle, close, f_o,f_next,c_prev,f_c)
+ -- close is now a list
+ if c_prev ~= first then
+ setlink(first,close)
+ end
+ return close
+ end
+
+ local stacks = setmetatableindex("table")
+
+ -- 1=open 2=close 3=middle 4=both
+
+ local function processfences(pointer,n,parent)
+ local current = pointer
+ local last = pointer
+ local start = pointer
+ local done = false
+ local initial = pointer
+ local stack = nil
+ local middle = nil -- todo: use properties
+ while current do
+-- show("before",pointer)
+ local id = getid(current)
+ if id == noad_code then
+ local a = getattr(current,a_autofence)
+ if a and a > 0 then
+ local stack = stacks[n]
+ setattr(current,a_autofence,0) -- hm, better use a property
+ local level = #stack
+ if a == 1 then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"open","open")
+ end
+ insert(stack,current)
+ elseif a == 2 then
+ local open = remove(stack)
+ if open then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"close","both")
+ end
+ current = convert_both(open,current,middle)
+ elseif current == start then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"close","skip")
+ end
+ else
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"close","close")
+ end
+ current = convert_close(initial,current,middle)
+ if not parent then
+ initial = current
+ end
+ end
+ elseif a == 3 then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"middle","middle")
+ end
+ if middle then
+ middle[current] = last
+ else
+ middle = { [current] = last }
+ end
+ elseif a == 4 then
+ if not stack or #stack == 0 then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"both","open")
+ end
+ insert(stack,current)
+ else
+ local open = remove(stack)
+ if open then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"both","both")
+ end
+ current = convert_both(open,current,middle)
+ elseif current == start then
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"both","skip")
+ end
+ else
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,level,"both","close")
+ end
+ current = convert_close(initial,current,middle)
+ if not parent then
+ initial = current
+ end
+ end
+ end
+ end
+ done = true
+ else
+ processstep(current,processfences,n+1,id)
+ end
+ else
+ -- next at current level
+ processstep(current,processfences,n,id)
+ end
+-- show("after",pointer)
+ last = current
+ current = getnext(current)
+ end
+ if done then
+ local stack = stacks[n]
+ local s = #stack
+ if s > 0 then
+ for i=1,s do
+ local open = remove(stack)
+ if trace_fences then
+ report_fences("%2i: level %i, handling %s, action %s",n,#stack,"flush","open")
+ end
+ last = convert_open(open,last,middle)
+ end
+-- show("done",pointer)
+ end
+ end
+ end
+
+ -- we can have a first changed node .. an option is to have a leading dummy node in math
+ -- lists like the par node as it can save a lot of mess
+
+ local enabled = false
+
+ implement {
+ name = "enableautofences",
+ onlyonce = true,
+ actions = function()
+ enableaction("math","noads.handlers.autofences")
+ enabled = true
+ end
+ }
+
+ function handlers.autofences(head,style,penalties)
+ if enabled then -- tex.modes.c_math_fences_auto
+ -- inspect(nodes.totree(head))
+ processfences(head,1)
+ -- inspect(nodes.totree(head))
+ end
+ end
+
+end
+
+-- normalize scripts
+
+do
+
+ local unscript = { } noads.processors.unscript = unscript
+ local superscripts = characters.superscripts
+ local subscripts = characters.subscripts
+ local fractions = characters.fractions
+ local replaced = { }
+
+ local function replace(pointer,what,n,parent)
+ pointer = parent -- we're following the parent list (chars trigger this)
+ local next = getnext(pointer)
+ local start_super, stop_super, start_sub, stop_sub
+ local mode = "unset"
+ while next and getid(next) == noad_code do
+ local nextnucleus = getnucleus(next)
+ if nextnucleus and getid(nextnucleus) == mathchar_code and not getsub(next) and not getsup(next) then
+ local char = getchar(nextnucleus)
+ local s = superscripts[char]
+ if s then
+ if not start_super then
+ start_super = next
+ mode = "super"
+ elseif mode == "sub" then
+ break
+ end
+ stop_super = next
+ next = getnext(next)
+ setchar(nextnucleus,s)
+ replaced[char] = (replaced[char] or 0) + 1
+ if trace_normalizing then
+ report_normalizing("superscript %C becomes %C",char,s)
+ end
+ else
+ local s = subscripts[char]
+ if s then
+ if not start_sub then
+ start_sub = next
+ mode = "sub"
+ elseif mode == "super" then
+ break
+ end
+ stop_sub = next
+ next = getnext(next)
+ setchar(nextnucleus,s)
+ replaced[char] = (replaced[char] or 0) + 1
+ if trace_normalizing then
+ report_normalizing("subscript %C becomes %C",char,s)
+ end
+ else
+ break
+ end
+ end
+ else
+ break
+ end
+ end
+ if start_super then
+ if start_super == stop_super then
+ setsup(pointer,getnucleus(start_super))
+ else
+ local list = new_submlist() -- todo attr
+ setlist(list,start_super)
+ setsup(pointer,list)
+ end
+ if mode == "super" then
+ setnext(pointer,getnext(stop_super))
+ end
+ setnext(stop_super)
+ end
+ if start_sub then
+
+-- if mode == "sub" then
+-- local sup = getsup(pointer)
+-- if sup and not getsub(pointer) then
+-- local nxt = getnext(pointer)
+-- local new = new_noad(pointer)
+-- setnucleus(new,new_submlist())
+-- setlink(pointer,new,nxt)
+-- pointer = new
+-- end
+-- end
+
+ if start_sub == stop_sub then
+ setsub(pointer,getnucleus(start_sub))
+ else
+ local list = new_submlist() -- todo attr
+ setlist(list,start_sub)
+ setsub(pointer,list)
+ end
+ if mode == "sub" then
+ setnext(pointer,getnext(stop_sub))
+ end
+ setnext(stop_sub)
+ end
+ -- we could return stop
+ end
+
+ unscript[mathchar_code] = replace -- not noads as we need to recurse
+
+ function handlers.unscript(head,style,penalties)
+ processnoads(head,unscript,"unscript")
+ return true -- not needed
+ end
+
+end
+
+do
+
+ local unstack = { } noads.processors.unstack = unstack
+ local enabled = false
+ local a_unstack = privateattribute("mathunstack")
+
+ unstack[noad_code] = function(pointer)
+ if getattr(pointer,a_unstack) then
+ local sup = getsup(pointer)
+ local sub = getsub(pointer)
+ if sup and sub then
+ -- if trace_unstacking then
+ -- report_unstacking() -- todo ... what to show ...
+ -- end
+ local nxt = getnext(pointer)
+ local new = new_noad(pointer)
+ setnucleus(new,new_submlist())
+ setsub(pointer)
+ setsub(new,sub)
+ setlink(pointer,new,nxt)
+ end
+ end
+ end
+
+ function handlers.unstack(head,style,penalties)
+ if enabled then
+ processnoads(head,unstack,"unstack")
+ return true -- not needed
+ end
+ end
+
+ implement {
+ name = "enablescriptunstacking",
+ onlyonce = true,
+ actions = function()
+ enableaction("math","noads.handlers.unstack")
+ enabled = true
+ end
+ }
+
+end
+
+do
+
+ local function collected(list)
+ if list and next(list) then
+ local n, t = 0, { }
+ for k, v in sortedhash(list) do
+ n = n + 1
+ t[n] = formatters["%C"](k)
+ end
+ return formatters["% t (n=%s)"](t,n)
+ end
+ end
+
+ statistics.register("math script replacements", function()
+ return collected(replaced)
+ end)
+
+ statistics.register("unknown math characters", function()
+ return collected(unknowns)
+ end)
+
+end
+
+-- math alternates: (in xits lgf: $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$)
+-- math alternates: (in lucidaot lgf: $ABC \mathalternate{italic} ABC$)
+
+-- todo: set alternate for specific symbols
+-- todo: no need to do this when already loaded
+-- todo: use a fonts.hashes.mathalternates
+
+do
+
+ local last = 0
+
+ local known = setmetatableindex(function(t,k)
+ local v = bor(0,2^last)
+ t[k] = v
+ last = last + 1
+ return v
+ end)
+
+ local defaults = {
+ dotless = { feature = 'dtls', value = 1, comment = "Mathematical Dotless Forms" },
+ -- zero = { feature = 'zero', value = 1, comment = "Slashed or Dotted Zero" }, -- in no math font (yet)
+ }
+
+ local function initializemathalternates(tfmdata)
+ local goodies = tfmdata.goodies
+ local autolist = defaults -- table.copy(defaults)
+
+ local function setthem(newalternates)
+ local resources = tfmdata.resources -- was tfmdata.shared
+ local mathalternates = resources.mathalternates
+ local alternates, attributes, registered, presets
+ if mathalternates then
+ alternates = mathalternates.alternates
+ attributes = mathalternates.attributes
+ registered = mathalternates.registered
+ else
+ alternates, attributes, registered = { }, { }, { }
+ mathalternates = {
+ attributes = attributes,
+ alternates = alternates,
+ registered = registered,
+ presets = { },
+ resets = { },
+ hashes = setmetatableindex("table")
+ }
+ resources.mathalternates = mathalternates
+ end
+ --
+ for name, data in sortedhash(newalternates) do
+ if alternates[name] then
+ -- ignore
+ else
+ local attr = known[name]
+ attributes[attr] = data
+ alternates[name] = attr
+ registered[#registered+1] = attr
+ end
+ end
+ end
+
+ if goodies then
+ local done = { }
+ for i=1,#goodies do
+ -- first one counts
+ -- we can consider sharing the attributes ... todo (only once scan)
+ local mathgoodies = goodies[i].mathematics
+ local alternates = mathgoodies and mathgoodies.alternates
+ if alternates then
+ if trace_goodies then
+ report_goodies("loading alternates for font %a",tfmdata.properties.name)
+ end
+ for k, v in next, autolist do
+ if not alternates[k] then
+ alternates[k] = v
+ end
+ end
+ setthem(alternates)
+ return
+ end
+ end
+ end
+
+ if trace_goodies then
+ report_goodies("loading default alternates for font %a",tfmdata.properties.name)
+ end
+ setthem(autolist)
+
+ end
+
+ registerotffeature {
+ name = "mathalternates",
+ description = "additional math alternative shapes",
+ initializers = {
+ base = initializemathalternates,
+ node = initializemathalternates,
+ }
+ }
+
+ -- local getalternate = otf.getalternate (runtime new method so ...)
+
+ -- todo: not shared but copies ... one never knows
+
+ local a_mathalternate = privateattribute("mathalternate")
+ local alternate = { } -- processors.alternate = alternate
+ local fontdata = fonts.hashes.identifiers
+ local fontresources = fonts.hashes.resources
+
+ local function getalternate(fam,tag,current)
+ local resources = fontresources[getfontoffamily(fam)]
+ local attribute = unsetvalue
+ if resources then
+ local mathalternates = resources.mathalternates
+ if mathalternates then
+ local presets = mathalternates.presets
+ if presets then
+ local resets = mathalternates.resets
+ attribute = presets[tag]
+ if not attribute then
+ attribute = 0
+ local alternates = mathalternates.alternates
+ for s in gmatch(tag,"[^, ]+") do
+ if s == v_reset then
+ resets[tag] = true
+ current = unsetvalue
+ else
+ local a = alternates[s] -- or known[s]
+ if a then
+ attribute = bor(attribute,a)
+ end
+ end
+ end
+ if attribute == 0 then
+ attribute = unsetvalue
+ end
+ presets[tag] = attribute
+ elseif resets[tag] then
+ current = unsetvalue
+ end
+ end
+ end
+ end
+ if attribute > 0 and current and current > 0 then
+ return bor(current,attribute)
+ else
+ return attribute
+ end
+ end
+
+ local function presetalternate(fam,tag)
+ texsetattribute(a_mathalternate,getalternate(fam,tag))
+ end
+
+ implement {
+ name = "presetmathalternate",
+ actions = presetalternate,
+ arguments = { "integer", "string" }
+ }
+
+ local function setalternate(fam,tag)
+ local a = texgetattribute(a_mathalternate)
+ local v = getalternate(fam,tag,a)
+ texsetattribute(a_mathalternate,v)
+ end
+
+ implement {
+ name = "setmathalternate",
+ actions = setalternate,
+ arguments = { "integer", "string" }
+ }
+
+ alternate[mathchar_code] = function(pointer) -- slow
+ local a = getattr(pointer,a_mathalternate)
+ if a and a > 0 then
+ setattr(pointer,a_mathalternate,0)
+ local fontid = getfont(pointer)
+ local resources = fontresources[fontid]
+ if resources then
+ local mathalternates = resources.mathalternates
+ if mathalternates then
+ local attributes = mathalternates.attributes
+ local registered = mathalternates.registered
+ local hashes = mathalternates.hashes
+ for i=1,#registered do
+ local r = registered[i]
+ if band(a,r) ~= 0 then
+ local char = getchar(pointer)
+ local alt = hashes[i][char]
+ if alt == nil then
+ local what = attributes[r]
+ alt = otf.getalternate(fontdata[fontid],char,what.feature,what.value) or false
+ if alt == char then
+ alt = false
+ end
+ hashes[i][char] = alt
+ end
+ if alt then
+ if trace_alternates then
+ local what = attributes[r]
+ report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U",
+ tostring(what.feature),tostring(what.value),getchar(pointer),alt)
+ end
+ setchar(pointer,alt)
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ function handlers.alternates(head,style,penalties)
+ processnoads(head,alternate,"alternate")
+ return true -- not needed
+ end
+
+end
+
+-- italics: we assume that only characters matter
+--
+-- = we check for correction first because accessing nodes is slower
+-- = the actual glyph is not that important (we can control it with numbers)
+
+-- Italic correction in luatex math is (was) a mess. There are all kind of assumptions based on
+-- old fonts and new fonts. Eventually there should be a flag that can signal to ignore all
+-- those heuristics. We want to deal with it ourselves also in the perspective of mixed math
+-- and text. Also, for a while in context we had to deal with a mix of virtual math fonts and
+-- real ones.
+
+-- in opentype the italic correction of a limop is added to the width and luatex does
+-- some juggling that we want to avoid but we need to do something here (in fact, we could
+-- better fix the width of the character)
+
+do
+
+ local a_mathitalics = privateattribute("mathitalics")
+
+ local italics = { }
+ local default_factor = 1/20
+
+ local setcolor = colortracers.set
+ local resetcolor = colortracers.reset
+ local italic_kern = new_kern
+
+ local c_positive_d = "trace:dg"
+ local c_negative_d = "trace:dr"
+
+ local function insert_kern(current,kern)
+ local sub = new_submlist() -- todo: attr
+ local noad = new_noad() -- todo: attr
+ setlist(sub,kern)
+ setnext(kern,noad)
+ setnucleus(noad,current)
+ return sub
+ end
+
+ registertracker("math.italics.visualize", function(v)
+ if v then
+ italic_kern = function(k)
+ local n = new_kern(k) -- todo: attr
+ set_visual(n,"italic")
+ return n
+ end
+ else
+ italic_kern = new_kern
+ end
+ end)
+
+ local function getcorrection(method,font,char) -- -- or character.italic -- (this one is for tex)
+
+ local visual = chardata[char].visual
+
+ if method == 1 then
+ -- check on state
+ local italics = fontitalics[font]
+ if italics then
+ local character = fontcharacters[font][char]
+ if character then
+ local correction = character.italic
+ if correction and correction ~= 0 then
+ return correction, visual
+ end
+ end
+ end
+ elseif method == 2 then
+ -- no check
+ local character = fontcharacters[font][char]
+ if character then
+ local correction = character.italic
+ if correction and correction ~= 0 then
+ return correction, visual
+ end
+ end
+ elseif method == 3 then
+ -- check on visual
+ if visual == "it" or visual == "bi" then
+ local character = fontcharacters[font][char]
+ if character then
+ local correction = character.italic
+ if correction and correction ~= 0 then
+ return correction, visual
+ end
+ end
+ end
+ elseif method == 4 then
+ -- combination of 1 and 3
+ local italics = fontitalics[font]
+ if italics and (visual == "it" or visual == "bi") then
+ local character = fontcharacters[font][char]
+ if character then
+ local correction = character.italic
+ if correction and correction ~= 0 then
+ return correction, visual
+ end
+ end
+ end
+ end
+
+ end
+
+ italics[mathchar_code] = function(pointer,what,n,parent)
+ local method = getattr(pointer,a_mathitalics)
+ if method and method > 0 and method < 100 then
+ local char = getchar(pointer)
+ local font = getfont(pointer)
+ local correction, visual = getcorrection(method,font,char)
+ if correction and correction ~= 0 then
+ local next_noad = getnext(parent)
+ if not next_noad then
+ if n == 1 then
+ -- only at the outer level .. will become an option (always,endonly,none)
+ if trace_italics then
+ report_italics("method %a, flagging italic correction %p between %C and end math",method,correction,char)
+ end
+ if correction > 0 then
+ correction = correction + 100
+ else
+ correction = correction - 100
+ end
+ correction = round(correction)
+ setattr(pointer,a_mathitalics,correction)
+ setattr(parent,a_mathitalics,correction)
+ return -- so no reset later on
+ end
+ end
+ end
+ end
+ setattr(pointer,a_mathitalics,unsetvalue)
+ end
+
+ function handlers.italics(head,style,penalties)
+ processnoads(head,italics,"italics")
+ return true -- not needed
+ end
+
+ local enable = function()
+ enableaction("math", "noads.handlers.italics")
+ if trace_italics then
+ report_italics("enabling math italics")
+ end
+ -- we enable math (unless already enabled elsewhere)
+ typesetters.italics.enablemath()
+ enable = false
+ end
+
+ -- best do this only on math mode (less overhead)
+
+ function mathematics.setitalics(name)
+ if enable then
+ enable()
+ end
+ texsetattribute(a_mathitalics,name and name ~= v_reset and tonumber(name) or unsetvalue) -- maybe also v_none
+ end
+
+ function mathematics.getitalics(name)
+ if enable then
+ enable()
+ end
+ context(name and name ~= v_reset and tonumber(name) or unsetvalue)
+ end
+
+ function mathematics.resetitalics()
+ texsetattribute(a_mathitalics,unsetvalue)
+ end
+
+ implement {
+ name = "initializemathitalics",
+ actions = enable,
+ onlyonce = true,
+ }
+
+ implement {
+ name = "setmathitalics",
+ actions = mathematics.setitalics,
+ arguments = "string",
+ }
+
+ implement {
+ name = "getmathitalics",
+ actions = mathematics.getitalics,
+ arguments = "string",
+ }
+
+ implement {
+ name = "resetmathitalics",
+ actions = mathematics.resetitalics
+ }
+
+end
+
+do
+
+ -- math kerns (experiment) in goodies:
+ --
+ -- mathematics = {
+ -- kernpairs = {
+ -- [0x1D44E] = {
+ -- [0x1D44F] = 400, -- 𝑎𝑏
+ -- }
+ -- },
+ -- }
+
+ local a_kernpairs = privateattribute("mathkernpairs")
+ local kernpairs = { }
+
+ local function enable()
+ enableaction("math", "noads.handlers.kernpairs")
+ if trace_kernpairs then
+ report_kernpairs("enabling math kern pairs")
+ end
+ enable = false
+ end
+
+ implement {
+ name = "initializemathkernpairs",
+ actions = enable,
+ onlyonce = true,
+ }
+
+ local hash = setmetatableindex(function(t,font)
+ local g = fontdata[font].goodies
+ local m = g and g[1] and g[1].mathematics
+ local k = m and m.kernpairs
+ t[font] = k
+ return k
+ end)
+
+ -- no correction after prime because that moved to a superscript
+
+ kernpairs[mathchar_code] = function(pointer,what,n,parent)
+ if getattr(pointer,a_kernpairs) == 1 then
+ local font = getfont(pointer)
+ local list = hash[font]
+ if list then
+ local first = getchar(pointer)
+ local found = list[first]
+ if found then
+ local next = getnext(parent)
+ if next and getid(next) == noad_code then
+ pointer = getnucleus(next)
+ if pointer then
+ if getfont(pointer) == font then
+ local second = getchar(pointer)
+ local kern = found[second]
+ if kern then
+ kern = kern * fonts.hashes.parameters[font].hfactor
+ if trace_kernpairs then
+ report_kernpairs("adding %p kerning between %C and %C",kern,first,second)
+ end
+ setlink(parent,new_kern(kern),getnext(parent)) -- todo: attr
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ function handlers.kernpairs(head,style,penalties)
+ processnoads(head,kernpairs,"kernpairs")
+ return true -- not needed
+ end
+
+end
+
+-- primes and such
+
+do
+
+ -- is validpair stil needed?
+
+ local a_mathcollapsing = privateattribute("mathcollapsing")
+ local collapse = { }
+ local mathlists = characters.mathlists
+ local validpair = {
+ [ordnoad_code] = true,
+ [opdisplaylimitsnoad_code] = true,
+ [oplimitsnoad_code] = true,
+ [opnolimitsnoad_code] = true,
+ [binnoad_code] = true, -- new
+ [relnode_code] = true,
+ [opennoad_code] = true, -- new
+ [closenoad_code] = true, -- new
+ [punctnoad_code] = true, -- new
+ [innernoad_code] = false,
+ [undernoad_code] = false,
+ [overnoad_code] = false,
+ [vcenternoad_code] = false,
+ [ordlimitsnoad_code] = true,
+ }
+
+ local reported = setmetatableindex("table")
+
+ collapse[mathchar_code] = function(pointer,what,n,parent)
+
+ if parent and mathlists[getchar(pointer)] then
+ local found, last, lucleus, lsup, lsub, category
+ local tree = mathlists
+ local current = parent
+ while current and validpair[getsubtype(current)] do
+ local nucleus = getnucleus(current) -- == pointer
+ local sub = getsub(current)
+ local sup = getsup(current)
+ local char = getchar(nucleus)
+ if char then
+ local match = tree[char]
+ if match then
+ local method = getattr(current,a_mathcollapsing)
+ if method and method > 0 and method <= 3 then
+ local specials = match.specials
+ local mathlist = match.mathlist
+ local ligature
+ if method == 1 then
+ ligature = specials
+ elseif method == 2 then
+ ligature = specials or mathlist
+ else -- 3
+ ligature = mathlist or specials
+ end
+ if ligature then
+ category = mathlist and "mathlist" or "specials"
+ found = ligature
+ last = current
+ lucleus = nucleus
+ lsup = sup
+ lsub = sub
+ end
+ tree = match
+ if sub or sup then
+ break
+ else
+ current = getnext(current)
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if found and last and lucleus then
+ local id = getfont(lucleus)
+ local characters = fontcharacters[id]
+ local replace = characters and characters[found]
+ if not replace then
+ if not reported[id][found] then
+ reported[id][found] = true
+ report_collapsing("%s ligature %C from %s","ignoring",found,category)
+ end
+ elseif trace_collapsing then
+ report_collapsing("%s ligature %C from %s","creating",found,category)
+ end
+ setchar(pointer,found)
+ local l = getnext(last)
+ local c = getnext(parent)
+ if lsub then
+ setsub(parent,lsub)
+ setsub(last)
+ end
+ if lsup then
+ setsup(parent,lsup)
+ setsup(last)
+ end
+ while c ~= l do
+ local n = getnext(c)
+ flush_node(c)
+ c = n
+ end
+ setlink(parent,l)
+ end
+ end
+ end
+
+ function noads.handlers.collapse(head,style,penalties)
+ processnoads(head,collapse,"collapse")
+ return true -- not needed
+ end
+
+ local enable = function()
+ enableaction("math", "noads.handlers.collapse")
+ if trace_collapsing then
+ report_collapsing("enabling math collapsing")
+ end
+ enable = false
+ end
+
+ implement {
+ name = "initializemathcollapsing",
+ actions = enable,
+ onlyonce = true,
+ }
+
+end
+
+do
+ -- inner under over vcenter
+
+ local fixscripts = { }
+ local movesub = {
+ -- primes
+ [0x2032] = 0xFE932,
+ [0x2033] = 0xFE933,
+ [0x2034] = 0xFE934,
+ [0x2057] = 0xFE957,
+ -- reverse primes
+ [0x2035] = 0xFE935,
+ [0x2036] = 0xFE936,
+ [0x2037] = 0xFE937,
+ }
+
+ mathematics.virtualize(movesub)
+
+ local options_supported = tokens.defined("Unosuperscript")
+
+ local function fixsupscript(parent,current,current_char,new_char)
+ if new_char ~= current_char and new_char ~= true then
+ setchar(current,new_char)
+ if trace_fixing then
+ report_fixing("fixing subscript, replacing superscript %U by %U",current_char,new_char)
+ end
+ else
+ if trace_fixing then
+ report_fixing("fixing subscript, superscript %U",current_char)
+ end
+ end
+ if options_supported then
+ setfield(parent,"options",0x08+0x22)
+ end
+ end
+
+ -- local function movesubscript(parent,current_nucleus,oldchar,newchar)
+ -- local prev = getprev(parent)
+ -- if prev and getid(prev) == noad_code then
+ -- local psup = getsup(prev)
+ -- local psub = getsub(prev)
+ -- if not psup and not psub then
+ -- fixsupscript(prev,current_nucleus,oldchar,newchar)
+ -- local nucleus = getnucleus(parent)
+ -- local sub = getsub(parent)
+ -- setsup(prev,nucleus)
+ -- setsub(prev,sub)
+ -- local dummy = copy_node(nucleus)
+ -- setchar(dummy,0)
+ -- setnucleus(parent,dummy)
+ -- setsub(parent)
+ -- elseif not psup then
+ -- fixsupscript(prev,current_nucleus,oldchar,newchar)
+ -- local nucleus = getnucleus(parent)
+ -- setsup(prev,nucleus)
+ -- local dummy = copy_node(nucleus)
+ -- setchar(dummy,0)
+ -- setnucleus(parent,dummy)
+ -- end
+ -- end
+ -- end
+
+ local function move_none_none(parent,prev,nuc,oldchar,newchar)
+ fixsupscript(prev,nuc,oldchar,newchar)
+ local sub = getsub(parent)
+ setsup(prev,nuc)
+ setsub(prev,sub)
+ local dummy = copy_node(nuc)
+ setchar(dummy,0)
+ setnucleus(parent,dummy)
+ setsub(parent)
+ end
+
+ local function move_none_psub(parent,prev,nuc,oldchar,newchar)
+ fixsupscript(prev,nuc,oldchar,newchar)
+ setsup(prev,nuc)
+ local dummy = copy_node(nuc)
+ setchar(dummy,0)
+ setnucleus(parent,dummy)
+ end
+
+ fixscripts[mathchar_code] = function(pointer,what,n,parent,nested) -- todo: switch to turn in on and off
+ if parent then
+ local oldchar = getchar(pointer)
+ local newchar = movesub[oldchar]
+ if newchar then
+ local nuc = getnucleus(parent)
+ if pointer == nuc then
+ local sub = getsub(pointer)
+ local sup = getsup(pointer)
+ if sub then
+ if sup then
+ -- print("[char] sub sup")
+ else
+ -- print("[char] sub ---")
+ end
+ elseif sup then
+ -- print("[char] --- sup")
+ else
+ local prev = getprev(parent)
+ if prev and getid(prev) == noad_code then
+ local psub = getsub(prev)
+ local psup = getsup(prev)
+ if psub then
+ if psup then
+ -- print("sub sup [char] --- ---")
+ else
+ -- print("sub --- [char] --- ---")
+ move_none_psub(parent,prev,nuc,oldchar,newchar)
+ end
+ elseif psup then
+ -- print("--- sup [char] --- ---")
+ else
+ -- print("[char] --- ---")
+ move_none_none(parent,prev,nuc,oldchar,newchar)
+ end
+ else
+ -- print("no prev [char]")
+ end
+ end
+ else
+ -- print("[char]")
+ end
+ end
+ end
+ end
+
+ function noads.handlers.fixscripts(head,style,penalties)
+ processnoads(head,fixscripts,"fixscripts")
+ return true -- not needed
+ end
+
+end
+
+-- variants
+
+do
+
+ local variants = { }
+ local validvariants = { -- fast check on valid
+ [0x2229] = 0xFE00, [0x222A] = 0xFE00,
+ [0x2268] = 0xFE00, [0x2269] = 0xFE00,
+ [0x2272] = 0xFE00, [0x2273] = 0xFE00,
+ [0x228A] = 0xFE00, [0x228B] = 0xFE00,
+ [0x2293] = 0xFE00, [0x2294] = 0xFE00,
+ [0x2295] = 0xFE00,
+ [0x2297] = 0xFE00,
+ [0x229C] = 0xFE00,
+ [0x22DA] = 0xFE00, [0x22DB] = 0xFE00,
+ [0x2A3C] = 0xFE00, [0x2A3D] = 0xFE00,
+ [0x2A9D] = 0xFE00, [0x2A9E] = 0xFE00,
+ [0x2AAC] = 0xFE00, [0x2AAD] = 0xFE00,
+ [0x2ACB] = 0xFE00, [0x2ACC] = 0xFE00,
+ }
+
+ variants[mathchar_code] = function(pointer,what,n,parent) -- also set export value
+ local char = getchar(pointer)
+ local selector = validvariants[char]
+ if selector then
+ local next = getnext(parent)
+ if next and getid(next) == noad_code then
+ local nucleus = getnucleus(next)
+ if nucleus and getid(nucleus) == mathchar_code and getchar(nucleus) == selector then
+ local variant
+ local tfmdata = fontdata[getfont(pointer)]
+ local mathvariants = tfmdata.resources.variants -- and variantdata
+ if mathvariants then
+ mathvariants = mathvariants[selector]
+ if mathvariants then
+ variant = mathvariants[char]
+ end
+ end
+ if variant then
+ setchar(pointer,variant)
+ setattr(pointer,a_exportstatus,char) -- we don't export the variant as it's visual markup
+ if trace_variants then
+ report_variants("variant (%U,%U) replaced by %U",char,selector,variant)
+ end
+ else
+ if trace_variants then
+ report_variants("no variant (%U,%U)",char,selector)
+ end
+ end
+ setprev(next,pointer)
+ setnext(parent,getnext(next))
+ flush_node(next)
+ end
+ end
+ end
+ end
+
+ function handlers.variants(head,style,penalties)
+ processnoads(head,variants,"unicode variant")
+ return true -- not needed
+ end
+
+end
+
+-- for manuals
+
+do
+
+ local classes = { }
+ local colors = {
+ [relnode_code] = "trace:dr",
+ [ordnoad_code] = "trace:db",
+ [binnoad_code] = "trace:dg",
+ [opennoad_code] = "trace:dm",
+ [closenoad_code] = "trace:dm",
+ [punctnoad_code] = "trace:dc",
+ -- [opdisplaylimitsnoad_code] = "",
+ -- [oplimitsnoad_code] = "",
+ -- [opnolimitsnoad_code] = "",
+ -- [ordlimitsnoad_code] = "",
+ -- [innernoad_code = "",
+ -- [undernoad_code] = "",
+ -- [overnoad_code] = "",
+ -- [vcenternoad_code] = "",
+ }
+
+ local setcolor = colortracers.set
+ local resetcolor = colortracers.reset
+
+ classes[mathchar_code] = function(pointer,what,n,parent)
+ local color = colors[getsubtype(parent)]
+ if color then
+ setcolor(pointer,color)
+ else
+ resetcolor(pointer)
+ end
+ end
+
+ function handlers.classes(head,style,penalties)
+ processnoads(head,classes,"classes")
+ return true -- not needed
+ end
+
+ registertracker("math.classes",function(v)
+ setaction("math","noads.handlers.classes",v)
+ end)
+
+end
+
+-- experimental
+
+do
+
+ -- mathematics.registerdomain {
+ -- name = "foo",
+ -- parents = { "bar" },
+ -- characters = {
+ -- [0x123] = { char = 0x234, class = binary },
+ -- },
+ -- }
+
+ local domains = { }
+ local categories = { }
+ local numbers = { }
+ local a_mathdomain = privateattribute("mathdomain")
+ mathematics.domains = categories
+ local permitted = {
+ ordinary = ordnoad_code,
+ binary = binnoad_code,
+ relation = relnode_code,
+ punctuation = punctnoad_code,
+ inner = innernoad_code,
+ }
+
+ function mathematics.registerdomain(data)
+ local name = data.name
+ if not name then
+ return
+ end
+ local attr = #numbers + 1
+ categories[name] = data
+ numbers[attr] = data
+ data.attribute = attr
+ -- we delay hashing
+ return attr
+ end
+
+ local enable
+
+ enable = function()
+ enableaction("math", "noads.handlers.domains")
+ if trace_domains then
+ report_domains("enabling math domains")
+ end
+ enable = false
+ end
+
+ function mathematics.setdomain(name)
+ if enable then
+ enable()
+ end
+ local data = name and name ~= v_reset and categories[name]
+ texsetattribute(a_mathdomain,data and data.attribute or unsetvalue)
+ end
+
+ function mathematics.getdomain(name)
+ if enable then
+ enable()
+ end
+ local data = name and name ~= v_reset and categories[name]
+ context(data and data.attribute or unsetvalue)
+ end
+
+ implement {
+ name = "initializemathdomain",
+ actions = enable,
+ onlyonce = true,
+ }
+
+ implement {
+ name = "setmathdomain",
+ arguments = "string",
+ actions = mathematics.setdomain,
+ }
+
+ implement {
+ name = "getmathdomain",
+ arguments = "string",
+ actions = mathematics.getdomain,
+ }
+
+ local function makehash(data)
+ local hash = { }
+ local parents = data.parents
+ if parents then
+ local function merge(name)
+ if name then
+ local c = categories[name]
+ if c then
+ local hash = c.hash
+ if not hash then
+ hash = makehash(c)
+ end
+ for k, v in next, hash do
+ hash[k] = v
+ end
+ end
+ end
+ end
+ if type(parents) == "string" then
+ merge(parents)
+ elseif type(parents) == "table" then
+ for i=1,#parents do
+ merge(parents[i])
+ end
+ end
+ end
+ local characters = data.characters
+ if characters then
+ for k, v in next, characters do
+ -- local chr = n.char
+ local cls = v.class
+ if cls then
+ v.code = permitted[cls]
+ else
+ -- invalid class
+ end
+ hash[k] = v
+ end
+ end
+ data.hash = hash
+ return hash
+ end
+
+ domains[mathchar_code] = function(pointer,what,n,parent)
+ local attr = getattr(pointer,a_mathdomain)
+ if attr then
+ local domain = numbers[attr]
+ if domain then
+ local hash = domain.hash
+ if not hash then
+ hash = makehash(domain)
+ end
+ local char = getchar(pointer)
+ local okay = hash[char]
+ if okay then
+ local chr = okay.char
+ local cls = okay.code
+ if chr and chr ~= char then
+ setchar(pointer,chr)
+ end
+ if cls and cls ~= getsubtype(parent) then
+ setsubtype(parent,cls)
+ end
+ end
+ end
+ end
+ end
+
+ function handlers.domains(head,style,penalties)
+ processnoads(head,domains,"domains")
+ return true -- not needed
+ end
+
+end
+
+-- just for me
+
+function handlers.showtree(head,style,penalties)
+ inspect(nodes.totree(tonut(head)))
+end
+
+registertracker("math.showtree",function(v)
+ setaction("math","noads.handlers.showtree",v)
+end)
+
+-- also for me
+
+do
+
+ local applyvisuals = nuts.applyvisuals
+ local visual = false
+
+ function handlers.makeup(head)
+ applyvisuals(head,visual)
+ end
+
+ registertracker("math.makeup",function(v)
+ visual = v
+ setaction("math","noads.handlers.makeup",v)
+ end)
+
+end
+
+-- the normal builder
+
+do
+
+ local force_penalties = false
+
+ -- registertracker("math.penalties",function(v)
+ -- force_penalties = v
+ -- end)
+
+ function builders.kernel.mlist_to_hlist(head,style,penalties)
+ return mlist_to_hlist(head,style,force_penalties or penalties)
+ end
+
+ -- function builders.kernel.mlist_to_hlist(head,style,penalties)
+ -- local h = mlist_to_hlist(head,style,force_penalties or penalties)
+ -- inspect(nodes.totree(h,true,true,true))
+ -- return h
+ -- end
+
+ implement {
+ name = "setmathpenalties",
+ arguments = "integer",
+ actions = function(p)
+ force_penalties = p > 0
+ end,
+ }
+
+end
+
+local actions = tasks.actions("math") -- head, style, penalties
+
+local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
+
+function processors.mlist_to_hlist(head,style,penalties)
+ starttiming(noads)
+ head = actions(head,style,penalties)
+ stoptiming(noads)
+ return head
+end
+
+callbacks.register('mlist_to_hlist',processors.mlist_to_hlist,"preprocessing math list")
+
+-- tracing
+
+statistics.register("math processing time", function()
+ return statistics.elapsedseconds(noads)
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-pln.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-pln.mkxl
new file mode 100644
index 00000000000..81008f37b22
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-pln.mkxl
@@ -0,0 +1,237 @@
+%D \module
+%D [ file=math-pln,
+%D version=2001.11.16,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Plain Helpers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Here we collect some Plain \TEX\ leftovers.
+
+\writestatus{loading}{ConTeXt Math Macros / Plain Helpers}
+
+\unprotect
+
+% we need proper unicode:
+
+\def\relbar{\mathrel{\smash-}} % - has the same height as + .. no clue what this refers to
+
+% might change
+
+\permanent\protected\def\hrulefill{\leaders\hrule\hfill}
+%protected\def\dotfill {\cleaders\hbox{\normalstartimath\mathsurround\zeropoint\mkern1.5mu.\mkern1.5mu\normalstopimath}\hfill}
+
+% will move
+
+\permanent\def\oalign#1%
+ {\leavevmode % plain tex uses this
+ \vtop
+ {\baselineskip\zeroskip
+ \lineskip.25\exheight
+ \ialign{\alignmark\alignmark\crcr#1\crcr}}}
+
+\permanent\def\ooalign % chars over each other
+ {\lineskiplimit-\maxdimen
+ \oalign}
+
+% needs checking
+
+\permanent\protected\def\choose{\atopwithdelims()}
+\permanent\protected\def\brack {\atopwithdelims[]}
+\permanent\protected\def\brace {\atopwithdelims\{\}}
+
+% needs checking
+
+\permanent\protected\def\buildrel#1\over#2%
+ {\mathrel{\mathop{\kern\zeropoint#2}\limits^{#1}}}
+
+\permanent\protected\def\bmod
+ {\nonscript
+ \mskip-\medmuskip
+ \mkern5mu
+ \mfunction{mod}%
+ \penalty900
+ \mkern5mu
+ \nonscript
+ \mskip-\medmuskip}
+
+\permanent\protected\def\pmod#1%
+ {\allowbreak
+ \mkern18mu
+ (\mfunction{mod}\mskip\medmuskip#1)}
+
+\permanent\protected\def\cases#1%
+ {\left\{%
+ \mskip\thinmuskip
+ \vcenter
+ {\normalbaselines
+ \mathsurround\zeropoint
+ \ialign{%
+ \normalstartimath\alignmark\alignmark\hfil\normalstopimath
+ \aligntab
+ \quad\alignmark\alignmark\hfil
+ \crcr
+ #1%
+ \crcr}}%
+ \right.}
+
+\permanent\protected\def\matrix#1%
+ {\emptyhbox
+ \mskip\thinmuskip
+ \vcenter
+ {\normalbaselines
+ \mathsurround\zeropoint
+ \ialign{%
+ \hfil\normalstartimath\alignmark\alignmark\normalstopimath\hfil
+ \aligntab
+ \aligntab
+ \quad\hfil\normalstartimath\alignmark\alignmark\normalstopimath\hfil
+ \crcr
+ \mathstrut
+ \crcr
+ \noalign{\kern-\baselineskip}
+ #1\crcr
+ \mathstrut
+ \crcr
+ \noalign{\kern-\baselineskip}}}%
+ \mskip\thinmuskip}
+
+\permanent\protected\def\pmatrix#1%
+ {\left(\matrix{#1}\right)}
+
+\permanent\protected\def\math_openup
+ {\afterassignment\math_openup_indeed\scratchdimen}
+
+\aliased\let\math_closeup\relax
+
+\def\math_openup_indeed
+ {\enforced\permanent\protected\edef\math_closeup
+ {\lineskip \the\lineskip
+ \baselineskip \the\baselineskip
+ \lineskiplimit\the\lineskiplimit
+ \relax}%
+ \advance \lineskip \scratchdimen
+ \advance \baselineskip \scratchdimen
+ \advance \lineskiplimit \scratchdimen}
+
+\permanent\protected\def\openup {\math_openup } % the low level ones are used elsewhere
+\permanent\protected\def\closeup{\math_closeup} % the low level ones are used elsewhere
+
+\permanent\protected\def\displaylines#1%
+ {\the\mathdisplayaligntweaks
+ \tabskip\zeroskip
+ \halign
+ {\hbox to \displaywidth{%
+ \normalstartimath\tabskip\zeroskip\everycr\emptytoks\hfil\displaystyle\alignmark\alignmark\hfil\normalstopimath}\crcr
+ #1\crcr}}
+
+%D This comes from plain but I gave it a \CONTEXT\ touch:
+
+\permanent\def\math_border_NC_indeed
+ {\enforced\global\def\math_border_NC{\aligntab}}
+
+\aliased\let\math_border_NC\math_border_NC_indeed
+
+\permanent\def\math_border_NR
+ {\enforced\global\let\math_border_NC\math_border_NC_indeed
+ \cr}
+
+\protected\def\math_border_matrix#1#2#3% adapted plain
+ {\begingroup
+ %
+ \setbox\scratchbox\hbox\bgroup
+ \mr B%
+ \egroup
+ \scratchdimenone\wd\scratchbox
+ %
+ \scratchdimentwo.2\bodyfontsize
+ %
+ \mathsurround\zeropoint
+ %
+ \enforced\def\NC{\math_border_NC}%
+ \enforced\def\NR{\math_border_NR}%
+ %
+ \gdef\m_math_border_matrix
+ {\kern\scratchdimentwo
+ \glet\m_math_border_matrix\relax}%
+ %
+ \setbox\scratchboxone\vbox\bgroup
+ \everycr{\noalign\bgroup\m_math_border_matrix\egroup}%
+ \tabskip \zeroskip
+ \halign{% \ialign resets everycr
+ \startimath\alignmark\alignmark\stopimath
+ \hfil
+ \kern\dimexpr\scratchdimentwo+\scratchdimenone\relax
+ \aligntab
+ \thinspace
+ \hfil
+ \startimath\alignmark\alignmark\stopimath
+ \hfil
+ \aligntab
+ \aligntab
+ \quad
+ \hfil
+ \startimath\alignmark\alignmark\stopimath
+ \hfil
+ \crcr
+ \omit
+ \strut
+ \hfil
+ \crcr
+ \noalign{\kern-\baselineskip}%
+ #3\crcr
+ \omit
+ \strut
+ \cr}%
+ \egroup
+ \setbox\scratchboxtwo\vbox\bgroup
+ \unvcopy\scratchboxone
+ \global\setbox\globalscratchbox\lastbox
+ \egroup
+ \setbox\scratchboxtwo\hbox\bgroup
+ \unhbox\globalscratchbox
+ \unskip
+ \global\setbox\globalscratchbox\lastbox
+ \egroup
+ \setbox\scratchboxtwo\hbox\bgroup
+ \startimath
+ \kern\dimexpr\wd\globalscratchbox-\scratchdimenone\relax
+ \left#1\relax
+ \kern-\wd\globalscratchbox
+ \global\setbox\globalscratchbox\vbox\bgroup
+ \box\globalscratchbox
+ \kern\scratchdimentwo
+ \egroup
+ \vcenter\bgroup
+ \kern-\ht\globalscratchbox
+ \unvbox\scratchboxone
+ \kern-\baselineskip
+ \egroup
+ \thinspace
+ \right#2\relax
+ \stopimath
+ \egroup
+ \null
+ \thickspace
+ \vbox\bgroup
+ \kern\ht\globalscratchbox
+ \box\scratchboxtwo
+ \egroup
+ %
+ \endgroup}
+
+\permanent\protected\def\bordermatrix {\math_border_matrix()}
+\permanent\protected\def\bbordermatrix{\math_border_matrix[]}
+
+\permanent\protected\def\startbordermatrix #1\stopbordermatrix {\math_border_matrix(){#1}}
+\permanent\protected\def\startbbordermatrix#1\stopbbordermatrix{\math_border_matrix[]{#1}}
+
+\permanent\let\stopbordermatrix \relax
+\permanent\let\stopbbordermatrix\relax
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-rad.mklx b/Master/texmf-dist/tex/context/base/mkxl/math-rad.mklx
new file mode 100644
index 00000000000..8906efdc85a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-rad.mklx
@@ -0,0 +1,271 @@
+%D \module
+%D [ file=math-rad,
+%D version=2013.07.13,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Radicals,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Radicals}
+
+\unprotect
+
+%D \starttyping
+%D $\sqrt[3]{10}$
+%D \stoptyping
+%D
+%D This root command will be overloaded later:
+
+%D Old stuff:
+
+% \def\rootradical{\Uroot \defaultmathfamily "221A } % can be done in char-def
+% \def\surdradical{\Uradical \defaultmathfamily "221A } % can be done in char-def
+
+\permanent\protected\def\root#1\of{\rootradical{#1}} % #2
+
+\protected\def\sqrt{\doifelsenextoptionalcs\rootwithdegree\rootwithoutdegree} % will be redefined
+
+\permanent\protected\def\styledrootradical#1#2% so that \text works ok ... \rootradical behaves somewhat weird
+ {\normalexpanded{\rootradical
+ {\normalunexpanded{#1}}%
+ {\noexpand\triggermathstyle{\number\normalmathstyle}%
+ \normalunexpanded{#2}}}}
+
+% experimental new keyword: (maybe keywords should come after the symbol)
+%
+% \permanent\protected\def\styledrootradical#1#2% so that \text works ok ... \rootradical behaves somewhat weird
+% {\Uroot style \normalmathstyle "0 "221A {#1}{#2}}
+
+\permanent\protected\def\rootwithdegree[#1]{\rootradical{#1}}
+\permanent\protected\def\rootwithoutdegree {\rootradical {}}
+
+%D Even older stuff:
+
+% % is now a ordinary character
+%
+% \let\normalsurd\surd % \Uradical "0 "221A
+% \permanent\protected\def\surd{\normalsurd{}}
+
+%D The real thing. If needed one can control matters with one of the many
+%D \type {\Umath...} parameters.
+%D
+%D \starttyping
+%D \def\R {\Umathradicaldegreeafter\textstyle0pt}
+%D \def\RR{\Umathradicaldegreeafter\textstyle\dimexpr
+%D \Umathradicaldegreeafter\textstyle+.1em\relax}
+%D \def\RRR{\frozen\Umathradicaldegreeafter\textstyle\dimexpr
+%D \Umathradicaldegreeafter\textstyle+.2em\relax}
+%D
+%D $ \sqrt[3]{5} \RR\sqrt[3]{5} \sqrt[3]{5} $\par
+%D $ \sqrt[3]{5} {\RRR\sqrt[3]{5}} \sqrt[3]{5} $\par
+%D $ \RR\sqrt[3]{5} {\RRR\sqrt[3]{5}} \sqrt[3]{5} $\par
+%D
+%D \def\R {\Umathradicaldegreeafter\textstyle
+%D 0pt}
+%D \def\RR {\Umathradicaldegreeafter\textstyle
+%D 0.95\Umathradicaldegreeafter\textstyle}
+%D \def\RRR{\frozen\Umathradicaldegreeafter\textstyle
+%D 0.9\Umathradicaldegreeafter\textstyle}
+%D
+%D $ \sqrt[3]{5} \RR\sqrt[3]{5} \sqrt[3]{5} $\par
+%D $ \sqrt[3]{5} {\RRR\sqrt[3]{5}} \sqrt[3]{5} $\par
+%D $ \RR\sqrt[3]{5} {\RRR\sqrt[3]{5}} \sqrt[3]{5} $\par
+%D
+%D \def\RR {\mathopenupparameter\Umathradicaldegreeafter{.1}}
+%D
+%D $ \sqrt[3]{5} \RR\sqrt[3]{5} \sqrt[3]{5} $\par
+%D $ \sqrt[3]{5} {\RR\sqrt[3]{5}} \sqrt[3]{5} $\par
+%D \stoptyping
+
+\installcorenamespace{mathradical}
+\installcorenamespace{mathradicalalternative}
+
+\installcommandhandler \??mathradical {mathradical} \??mathradical
+
+\setupmathradical
+ [\c!alternative=\v!normal,
+ \c!mpoffset=.25\exheight]
+
+\appendtoks
+ \frozen\protected\instance\edefcsname\currentmathradical\endcsname{\math_radical_handle{\currentmathradical}}
+\to \everydefinemathradical
+
+\mutable\let\currentmathradical \empty
+\mutable\let\currentmathradicaldegree\empty
+
+\tolerant\protected\def\math_radical_handle#tag#*[#degree]#:#body%
+ {\begingroup
+ \edef\currentmathradical{#tag}%
+ \edef\currentmathradicaldegree{#degree}%
+ \ifempty\currentmathradicaldegree
+ \let\currentmathradicaldegree\m_math_no_degree
+ \fi
+ \math_radical_alternative{#body}%
+ \endgroup}
+
+\def\m_math_no_degree{{}}
+
+\def\math_radical_alternative{\csname\??mathradicalalternative\mathradicalparameter\c!alternative\endcsname}
+
+\setvalue{\??mathradicalalternative\v!default}% #body%
+ {\rootradical{\currentmathradicaldegree}} % {#body}}
+
+\setvalue{\??mathradicalalternative\v!normal}#body%
+ {\edef\p_color{\mathradicalparameter\c!color}%
+ \ifempty\p_color
+ \styledrootradical{\currentmathradicaldegree}{#body}% {} really needed as \rootradical expands first
+ \orelse\ifempty\currentmathradicaldegree
+ \pushcolor[\p_color]%
+ \styledrootradical{\currentmathradicaldegree}%
+ {\popcolor#body}%
+ \else
+ \pushcolor[\p_color]%
+ \styledrootradical{\popcolor\currentmathradicaldegree\pushcolor[\p_color]}%
+ {\popcolor#body}%
+ \fi}
+
+\startMPextensions
+ vardef math_radical_simple(expr w,h,d,o) =
+ (-h/2-o,h/2-o) --
+ (-h/4-o,-d-o) --
+ (-o,h+o) --
+ (w+o,h+o) --
+ (w+o,h-h/10+o)
+ enddef ;
+\stopMPextensions
+
+\startuniqueMPgraphic{minifun::math:radical:default}%{...}
+ draw
+ math_radical_simple(OverlayWidth,OverlayHeight,OverlayDepth,OverlayOffset)
+ % withpen pencircle xscaled (2OverlayLineWidth) yscaled (3OverlayLineWidth/4) rotated 30
+ withpen pencircle scaled OverlayLineWidth
+ % dashed evenly
+ withcolor OverlayLineColor ;
+\stopuniqueMPgraphic
+
+% todo: spacing .. this is just an experiment (article driven)
+
+\setvalue{\??mathradicalalternative\v!mp}#body% we could use dowithnextbox
+ {\begingroup
+ \scratchoffset\mathradicalparameter\c!mpoffset
+ \setbox\nextbox\mathstylehbox{#body}%
+ % we use the \overlay variables as these are passes anyway and
+ % it's more efficient than using parameters
+ \d_overlay_width \wd\nextbox
+ \d_overlay_height \ht\nextbox
+ \d_overlay_depth \dp\nextbox
+ \d_overlay_offset \scratchoffset
+ \d_overlay_linewidth\the\dimexpr\triggeredmathstyleparameter\Umathfractionrule
+ %
+ \edef\overlaylinecolor{\mathradicalparameter\c!color}%
+ %
+ \edef\p_mp{\mathradicalparameter\c!mp}%
+ %
+ \setbox\scratchbox\hpack\bgroup % todo: tag this box as sqrt
+ \uniqueMPgraphic
+ {\p_mp}%
+ %{...}%
+ \egroup
+ \scratchdimen \wd\scratchbox
+ \scratchtopoffset \dimexpr\scratchoffset+\dp\nextbox\relax
+ \scratchbottomoffset\dimexpr\scratchoffset+\ht\nextbox/2\relax
+ \hpack to \scratchdimen{\hss\box\nextbox\hskip\scratchoffset}%
+ \hskip-\scratchdimen
+ \lower\dimexpr\scratchtopoffset\box\scratchbox%
+ \ifx\currentmathradicaldegree\empty \else
+ \setbox\scratchbox\mathstylehbox{\scriptscriptstyle\currentmathradicaldegree\hss}%
+ \wd\scratchbox\scratchdimen
+ \hskip-\scratchdimen
+ \raise\dimexpr\scratchbottomoffset\box\scratchbox
+ \fi
+ \endgroup}
+
+\definemathradical[sqrt][mp=minifun::math:radical:default]
+
+% \setupmathradical[sqrt][alternative=normal,color=darkblue]
+% \setupmathradical[sqrt][alternative=mp,color=darkgreen]
+
+%D Because I wanted to illustrate some more fun stuff another mechanism
+%D is provided as well ... let's put some dangerous tools in the hand of
+%D math jugglers like Aditya.
+
+\installcorenamespace{mathornament}
+\installcorenamespace{mathornamentalternative}
+
+\installcommandhandler \??mathornament {mathornament} \??mathornament
+
+\setupmathornament
+ [\c!alternative=\v!mp, % currently mp only .. maybe some day layer too
+ \c!mpoffset=.25\exheight]
+
+\appendtoks
+ \frozen\instance\setuevalue{\currentmathornament}{\math_ornament_handle{\currentmathornament}}%
+\to \everydefinemathornament
+
+\protected\def\math_ornament_handle#tag#body%
+ {\begingroup
+ \edef\currentmathornament{#tag}%
+ \csname\??mathornamentalternative\mathornamentparameter\c!alternative\endcsname{#body}%
+ \endgroup}
+
+\setvalue{\??mathornamentalternative\v!mp}#body% we could use dowithnextbox
+ {\begingroup
+ \scratchoffset\mathornamentparameter\c!mpoffset
+ \setbox\nextbox\mathstylehbox{#body}%
+ \d_overlay_width \wd\nextbox
+ \d_overlay_height \ht\nextbox
+ \d_overlay_depth \dp\nextbox
+ \d_overlay_offset \scratchoffset
+ \d_overlay_linewidth\linewidth
+ \edef\overlaylinecolor{\mathornamentparameter\c!color}%
+ \edef\p_mp{\mathornamentparameter\c!mp}%
+ % the width of the graphic determines the width of the final result
+ \setbox\scratchbox\hpack{\uniqueMPgraphic{\p_mp}}% todo: add code key + tag
+ \hpack to \wd\scratchbox{\hss\box\nextbox\hss}%
+ \hskip-\wd\scratchbox
+ \box\scratchbox
+ \endgroup}
+
+% \startMPextensions
+% vardef math_ornament_hat(expr w,h,d,o,l) =
+% image ( path p ; p :=
+% (w/2,h + 10l) --
+% (o + w,h + o) --
+% (w/2,h + 7l) --
+% (-o,h + o) --
+% cycle ;
+% fill p ;
+% setbounds currentpicture to (-o,0) -- (w+o,0) -- (w+o,h+2o) -- (-o,h+2o) -- cycle ;
+% )
+% enddef ;
+% \stopMPextensions
+%
+% \startuniqueMPgraphic{math:ornament:hat}
+% draw
+% math_ornament_hat(
+% OverlayWidth,
+% OverlayHeight,
+% OverlayDepth,
+% OverlayOffset,
+% OverlayLineWidth
+% )
+% withpen
+% pencircle
+% xscaled (2OverlayLineWidth)
+% yscaled (3OverlayLineWidth/4)
+% rotated 30
+% withcolor
+% OverlayLineColor ;
+% draw boundingbox currentpicture;
+% \stopuniqueMPgraphic
+%
+% \definemathornament [mathhat] [mp=math:ornament:hat]
+%
+% \dorecurse{8}{$\mathhat{\blackrule[width=#1ex,color=gray]}$ }
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-scr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-scr.mkxl
new file mode 100644
index 00000000000..555f549f133
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-scr.mkxl
@@ -0,0 +1,58 @@
+%D \module
+%D [ file=math-scr,
+%D version=2007.07.19,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Scripts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Scripts}
+
+\unprotect
+
+%D These macros were first needed by Frits (Gajes) Spijkers (of the Math4All project
+%D that we participate in), and used for typesetting the minus sign that is keyed
+%D into scientific calculators. We use the monospaced signs, that we also define as
+%D symbol, so that they can be overloaded. We can probably do it nicer now (in lmtx).
+
+\def\math_scripts_raised_symbol#1%
+ {\mathchoice
+ {\math_scripts_raised_symbol_indeed5\tf {#1}}%
+ {\math_scripts_raised_symbol_indeed5\tf {#1}}%
+ {\math_scripts_raised_symbol_indeed4\tx {#1}}%
+ {\math_scripts_raised_symbol_indeed3\txx{#1}}}
+
+\def\math_scripts_raised_symbol_indeed#1#2#3%
+ {\mathord{{#2\raise.#1ex\hbox{#2\symbol[#3]}}}}
+
+\def\math_scripts_raised_number#1%
+ {\mathchoice
+ {\math_scripts_raised_number_indeed\tf {#1}}%
+ {\math_scripts_raised_number_indeed\tf {#1}}%
+ {\math_scripts_raised_number_indeed\tx {#1}}%
+ {\math_scripts_raised_number_indeed\txx{#1}}}
+
+\def\math_scripts_raised_number_indeed#1#2%
+ {\setbox\scratchbox\hbox{0}%
+ \mathord{\hbox to \wd\scratchbox{\hss#1\symbol[#2]\hss}}}
+
+\definesymbol[positive] [\getglyph{Mono}{+}]
+\definesymbol[negative] [\getglyph{Mono}{-}]
+\definesymbol[zeroamount][\getglyph{Mono}{-}]
+
+\permanent\protected\def\negative {\math_scripts_raised_symbol{negative}}
+\permanent\protected\def\positive {\math_scripts_raised_symbol{positive}}
+\permanent\protected\def\zeroamount{\math_scripts_raised_number{zeroamount}}
+
+%D How negative such a symbol looks is demonstrated in: $\negative 10^{\negative
+%D 10^{\negative 10}}$.
+
+% \setuptextformulas % why here
+% [\c!size=\v!normal]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-stc.mklx b/Master/texmf-dist/tex/context/base/mkxl/math-stc.mklx
new file mode 100644
index 00000000000..944185c23c4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-stc.mklx
@@ -0,0 +1,1379 @@
+%D \module
+%D [ file=math-stc,
+%D version=2012.12.29,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Stackers,
+%D comment=This replaces math-arr and friends,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Stackers}
+
+\unprotect
+
+%D WARNING: If the code here changes, the export needs to be checked! Stackers are
+%D rather special because the order in mathml matters, so we flush in [base under
+%D over] order. We also do some analysis at the \TEX\ end (passing the right
+%D variant). It's easy in the export to deal with it but in the pdf stream less
+%D trivial as we don't actually analyze there.
+%D
+%D At some point the \MKII\ arrow mechanism has been converted to \MKIV, but we kept
+%D most of the logic. We now have a more generic variant dealing with extensibles.
+%D There are a few demands than we need to meet:
+%D
+%D \startitemize
+%D \startitem
+%D The width of the extensible need to adapt itself automatically.
+%D \stopitem
+%D \startitem
+%D We need to be able to control horizontal and vertical offsets.
+%D \stopitem
+%D \startitem
+%D We best have a math as well as a text variant (which is handy for chemistry).
+%D \stopitem
+%D \startitem
+%D For historic reasons we need to deal with optional arguments in a special
+%D (reverse) way.
+%D \stopitem
+%D \startitem
+%D We need alternatives for extensibles on top, in the middle and at the bottom.
+%D \stopitem
+%D \stopitemize
+%D
+%D After I had experimented a bit with virtual characters for two headed arrows I
+%D discussed the issue with the Gyre folks and we came to the conclusion that it
+%D made sense to have real extensibles instead of constructing them out of snippets.
+%D After all, \OPENTYPE\ math provides for it. So, in December 2013 beta versions of
+%D Latin Modern and Gyre fonts came available that had these! Because we still want
+%D to support the traditional Latin Modern Virtual math font those were extended
+%D with a couple of virtual extensibles as well.
+%D
+%D {\em For the moment we still have some mess here: we can deal with known
+%D dimensions, but fillers (like \type {\rightarrowfil} don't work with \OPENTYPE\
+%D extensibles yet because there is no way to let them stretch like leaders. At some
+%D point \LUATEX\ might provide a auto||fit||to||encapsulated||box and if not I will
+%D cook up a \LUA\ based variant.}
+%D
+%D We could mess with something like \type {$mid\limits^{top}_{bottom}$} but we like
+%D a bit more control. At some point we need to add some hacks to get exports
+%D working well.
+%D
+%D In the end we have a more flexible mechanism which also handles text variants.
+
+%D When wrapping up some math developments I decided to add mp support here as well.
+%D A nice evening job with Joe Bonamassa performing live on the big screen (real
+%D nice bluray's). See meta-imp-mat.mkiv for examples.
+
+% possible improvements:
+%
+% - we could skip the left/right offsets when offset=normal, this saves some access time
+% at the lua end and some checking: use \mathhorizontalcode or \mathextensiblecode
+% but in practice arrows etc are not used that often
+
+\installcorenamespace {mathextensiblefallbacks}
+
+% currently no italic correction ... problem is that we don't know yet if we have an italic
+% below so we we need to postpone
+
+% \def\math_stackers_fallback
+% {\hbox to \scratchwidth{\csname\??mathextensiblefallbacks\ifcsname\??mathextensiblefallbacks\number\scratchunicode\endcsname\number\scratchunicode\fi\endcsname}}
+% %{\csname\??mathextensiblefallbacks\ifcsname\??mathextensiblefallbacks\number\scratchunicode\endcsname\number\scratchunicode\fi\endcsname }
+
+\def\math_stackers_fallback
+ {\mathstylehbox to \scratchwidth{\usemathstackerscolorparameter\c!color
+ \hss
+ \hskip\mathstackersparameter\c!topoffset\relax % for manual italic correction
+ \ifcsname\??mathextensiblefallbacks\number\scratchunicode\endcsname
+ \lastnamedcs
+ \else
+ \Umathchar \fam \zerocount \scratchunicode
+ \fi
+ \hss}}
+
+\def\math_stackers_regular
+ {\mathstylehbox{\usemathstackerscolorparameter\c!color
+ \hskip\d_math_stackers_offset_l
+ \Umathaccent\fam\zerocount\scratchunicode
+ {\hskip\dimexpr\scratchwidth-\d_math_stackers_offset_l-\d_math_stackers_offset_r}%
+ \hskip\d_math_stackers_offset_r
+ }}
+
+\def\math_stackers_stretch % we don't have that one yet
+ {\mathstylehbox{\usemathstackerscolorparameter\c!color
+ \hskip\d_math_stackers_offset_l
+ \Umathaccent\fam\zerocount\scratchunicode
+ {\hskip\dimexpr\hsize-\d_math_stackers_offset_l-\d_math_stackers_offset_r}%
+ \hskip\d_math_stackers_offset_r
+ }}
+
+% these delimiters are a unuseable as they don't center for small arguments:
+%
+% $\Umathaccent 0 0 "2190{x}$ \par $\Umathaccent 0 0 "27F8{x}$\par
+% $\Udelimiterunder 0 "2190{x}$ \par $\Udelimiterunder 0 "27F8{x}$\par
+
+\setvalue{\??mathextensiblefallbacks}%
+ {\hpack{\vrule\s!width\scratchwidth\s!height.1\mathexheight\s!depth\zeropoint}}
+
+% \def\math_stackers_with_fallback#codepoint%
+% {\begingroup
+% \scratchunicode#codepoint\relax
+% \ifcase\mathextensiblecode\fam\scratchunicode\relax
+% \math_stackers_fallback
+% \else
+% \math_stackers_stretch
+% \fi
+% \endgroup}
+
+%D We don't really need this because we can assume that fonts have the right
+%D extensibles. If needed I will make a general virtual extender for \OPENTYPE\
+%D fonts.
+%D
+%D Because we have quite some control over positioning, we have somewhat extensive
+%D tracing built in.
+
+\let\math_stackers_top \relax
+\let\math_stackers_middle\relax
+\let\math_stackers_bottom\relax
+\let\math_stackers_skip \hskip
+
+\installtextracker
+ {math.stackers.texts}
+ {\let\math_stackers_top \filledhboxb
+ \let\math_stackers_middle\filledhboxr
+ \let\math_stackers_bottom\filledhboxg
+ \let\math_stackers_skip \math_stackers_skip_indeed}
+ {\let\math_stackers_top \relax
+ \let\math_stackers_middle\relax
+ \let\math_stackers_bottom\relax
+ \let\math_stackers_skip \hskip}
+
+\def\math_stackers_skip_indeed#amount%
+ {\filledhboxk{\unsetteststrut\strut\hskip#amount}} % \dontshowstruts
+
+\let\math_stackers_start_tagged_mid\relax
+\let\math_stackers_start_tagged_top\relax
+\let\math_stackers_start_tagged_bot\relax
+\let\math_stackers_stop_tagged \relax
+
+\appendtoks
+ \def\math_stackers_start_tagged_mid{\dostarttagged\t!mstackermid\empty\hbox\bgroup}%
+ \def\math_stackers_start_tagged_top{\dostarttagged\t!mstackertop\empty\hbox\bgroup}%
+ \def\math_stackers_start_tagged_bot{\dostarttagged\t!mstackerbot\empty\hbox\bgroup}%
+ \def\math_stackers_stop_tagged {\egroup\dostoptagged}%
+\to \everysetuptagging
+
+%D We define a full featured command handler.
+
+\installcorenamespace {mathstackers}
+
+\installcommandhandler \??mathstackers {mathstackers} \??mathstackers
+
+\setupmathstackers
+ [%c!alternative=\v!text, % text | mathematics
+ \c!left=,
+ \c!right=,
+ \c!mathclass=\s!rel,
+ \c!alternative=\v!normal,
+ \c!voffset=.25\mathexheight,
+ \c!hoffset=\zeropoint,
+ \c!topoffset=\zeropoint, % for manual italic correction
+ \c!distance=\mathstackersparameter\c!voffset, % distance between symbol and base (can be different from voffset)
+ \c!minheight=\mathexheight,
+ \c!mindepth=\zeropoint,
+ \c!minwidth=.5\mathemwidth,
+ \c!order=\v!normal,
+ \c!strut=,
+ \c!color=, % todo: when I need it
+ \c!topcommand=,
+ \c!middlecommand=,
+ \c!bottomcommand=,
+ \c!offset=\v!normal, % normal | min | max
+ \c!location=\v!top] % none | normal | small | medium | big
+
+%D We assume that the middle characters (that can be an extensible) to sit on
+%D top of the baseline by default.
+
+\installcorenamespace {mathstackerslocation}
+\installcorenamespace {mathstackersalternative}
+
+\letvalue{\??mathstackerslocation\v!top }\plusone % on top of baseline
+\letvalue{\??mathstackerslocation\v!high }\plustwo % 25 % down
+\letvalue{\??mathstackerslocation\v!middle }\plusthree % centered
+\letvalue{\??mathstackerslocation\v!low }\plusfour % 75 % down
+\letvalue{\??mathstackerslocation\v!bottom }\plusfive % below baseline
+\letvalue{\??mathstackerslocation }\zerocount
+
+%D First we implement the helper that deals with an extensible in the middle and
+%D top and|/|or bottom texts:
+
+\let\m_math_stackers_text_top \empty
+\let\m_math_stackers_text_bottom\empty
+\let\m_math_stackers_text_middle\empty
+
+\def\math_stackers_flushtext#command#text%
+ {\ifdim\scratchleftoffset >\zeropoint\math_stackers_skip\scratchleftoffset \fi
+ \ifx\p_strut\v!no \else
+ \strut
+ \fi
+ \mathstackersparameter#command#text%
+ \ifdim\scratchrightoffset>\zeropoint\math_stackers_skip\scratchrightoffset\fi}
+
+\def\math_stackers_toptext {\math_stackers_flushtext\c!topcommand \m_math_stackers_text_top }
+\def\math_stackers_bottomtext{\math_stackers_flushtext\c!bottomcommand\m_math_stackers_text_bottom}
+\def\math_stackers_middletext{\math_stackers_flushtext\c!middlecommand\m_math_stackers_text_middle}
+
+\def\math_stackers_content
+ {\ifcase\scratchcounter
+ \math_stackers_fallback
+ \or % left
+ \math_stackers_regular
+ \or % right
+ \math_stackers_regular
+ \or % horizontal
+ \math_stackers_regular
+ \else
+ \math_stackers_fallback
+ \fi}
+
+% no checking, we assume sane use
+
+\letvalue{\??mathstackersalternative\v!normal }\math_stackers_content
+\letvalue{\??mathstackersalternative\v!default}\math_stackers_content
+
+\setupmathstackers
+ [\c!mp=math:stacker:\number\scratchunicode,
+ \c!mpheight=\mathcharht\scratchunicode,
+ \c!mpdepth=\mathchardp\scratchunicode,
+ \c!mpoffset=.25\mathexheight]
+
+\setvalue{\??mathstackersalternative\v!mp}%
+ {\normalexpanded{\math_stackers_mp_box
+ {\the\dimexpr\mathstackersparameter\c!mpheight}%
+ {\the\dimexpr\mathstackersparameter\c!mpdepth}%
+ {\the\dimexpr\mathstackersparameter\c!mpoffset}%
+ {\the\dimexpr\triggeredmathstyleparameter\Umathfractionrule}%
+ {\the\dimexpr\triggeredmathstyleparameter\Umathaxis}%
+ {\the\mathexheight}%
+ {\the\mathemwidth}%
+ }}
+
+\protected\def\math_stackers_mp_box#1#2#3#4#5#6#7%
+ {\hpack\bgroup % todo: add code key + tag
+ % we can speed up \mathexheight expansion a bit
+ \d_overlay_width \scratchwidth
+ \d_overlay_height #1\relax
+ \d_overlay_depth #2\relax
+ \d_overlay_offset #3\relax
+ \d_overlay_linewidth#4\relax
+ \edef\overlaylinecolor{\mathstackersparameter\c!color}%
+ \edef\p_mp{\mathstackersparameter\c!mp}%
+ \uniqueMPgraphic{\p_mp}{axis=#5,ex=#6,em=#7}%
+ \egroup}
+
+\def\math_stackers_check_unicode#codepoint%
+ {\scratchunicode#codepoint\relax
+ \scratchhoffset\mathstackersparameter\c!hoffset\relax
+ \scratchvoffset\mathstackersparameter\c!voffset\relax
+ \scratchcounter\mathhorizontalcode\fam\scratchunicode\relax % also sets \leftscratchoffset and \rightscratchoffset
+ \ifx\p_offset\v!max
+ % heads/tails + hoffset
+ \orelse\ifx\p_offset\v!min
+ % heads/tails - hoffset
+ \advance\scratchleftoffset -\scratchhoffset
+ \advance\scratchrightoffset-\scratchhoffset
+ \else % \v!normal
+ % hoffset
+ \scratchleftoffset\zeropoint
+ \scratchrightoffset\zeropoint
+ \fi
+ \ifdim\scratchleftoffset<\zeropoint
+ \scratchleftoffset\zeropoint
+ \fi
+ \ifdim\scratchrightoffset<\zeropoint
+ \scratchrightoffset\zeropoint
+ \fi}
+
+\def\math_stackers_normalize_three
+ {\scratchheight\ht\scratchboxthree
+ \scratchdepth \dp\scratchboxthree
+ \scratchtopoffset \scratchheight
+ \scratchbottomoffset\scratchdepth
+ \scratchdimen\mathstackersparameter\c!minheight\relax
+ \ifdim\scratchheight<\scratchdimen
+ \scratchheight\scratchdimen
+ \ht\scratchboxthree\scratchheight
+ \fi
+ \scratchdimen\mathstackersparameter\c!mindepth\relax
+ \ifdim\scratchdepth<\scratchdimen
+ \scratchdepth\scratchdimen
+ \dp\scratchboxthree\scratchdepth
+ \fi
+ \advance\scratchtopoffset -\scratchheight
+ \advance\scratchbottomoffset-\scratchdepth
+ \ifdim\scratchtopoffset<\zeropoint
+ \scratchtopoffset\zeropoint
+ \fi
+ \ifdim\scratchbottomoffset<\zeropoint
+ \scratchbottomoffset\zeropoint
+ \fi}
+
+\protected\def\math_stackers_triplet#method#category#codepoint#toptext#bottomtext%
+ {\begingroup
+ \edef\currentmathstackers{#category}%
+ \mathstackersparameter\c!left\relax
+ \dostarttagged\t!mstacker\currentmathstackers
+ \ifmmode\math_class_by_parameter\mathstackersparameter\else\dontleavehmode\fi
+ {\edef\p_offset {\mathstackersparameter\c!offset}%
+ \edef\p_location {\mathstackersparameter\c!location}%
+ \edef\p_strut {\mathstackersparameter\c!strut}%
+ \edef\p_alternative{\mathstackersparameter\c!alternative}%
+ \edef\m_math_stackers_text_top {#toptext}%
+ \edef\m_math_stackers_text_bottom{#bottomtext}%
+ \ifparameter#bottomtext\or
+ \edef\p_order{\mathstackersparameter\c!order}%
+ \ifx\p_order\v!reverse
+ \swapmacros\m_math_stackers_text_top\m_math_stackers_text_bottom
+ \fi
+ \fi
+ \scratchleftoffset \zeropoint
+ \scratchrightoffset\zeropoint
+ \ifcase#method\relax
+ \math_stackers_check_unicode{#codepoint}%
+ \else
+ \edef\m_math_stackers_text_middle{#codepoint}%
+ \fi
+ \ifempty\m_math_stackers_text_top
+ \setbox\scratchboxone\emptyhbox
+ \else
+ \setmathsmalltextbox\scratchboxone\hbox{\math_stackers_toptext}%
+ \fi
+ \ifempty\m_math_stackers_text_bottom
+ \setbox\scratchboxtwo\emptyhbox
+ \else
+ \setmathsmalltextbox\scratchboxtwo\hbox{\math_stackers_bottomtext}%
+ \fi
+ %
+ \ifcase#method\relax
+ % e.g. extensible
+ %\scratchwidth\wd
+ % \ifdim\wd\scratchboxone>\wd\scratchboxtwo
+ % \scratchboxone
+ % \else
+ % \scratchboxtwo
+ % \fi
+ %\relax
+ \scratchwidth\mathcharwd\scratchunicode
+ \ifdim\wd\scratchboxone>\scratchwidth
+ \scratchwidth\wd\scratchboxone
+ \orelse\ifdim\wd\scratchboxtwo>\scratchwidth
+ \scratchwidth\wd\scratchboxtwo
+ \fi
+ \else
+ \ifempty\m_math_stackers_text_middle
+ \setbox\scratchboxthree\emptyhbox
+ \else
+ \setmathtextbox\scratchboxthree\hbox{\math_stackers_middletext}%
+ \fi
+ \scratchwidth\wd
+ \ifdim\wd\scratchboxone>\wd\scratchboxtwo
+ \ifdim\wd\scratchboxone>\wd\scratchboxthree
+ \scratchboxone
+ \else
+ \scratchboxthree
+ \fi
+ \orelse\ifdim\wd\scratchboxtwo>\wd\scratchboxthree
+ \scratchboxtwo
+ \else
+ \scratchboxthree
+ \fi
+ \relax
+ \fi
+ %
+ \scratchdimen\mathstackersparameter\c!minwidth\relax
+ \ifdim\scratchwidth<\scratchdimen
+ \scratchwidth\scratchdimen
+ \fi
+ \advance\scratchwidth2\scratchhoffset
+ %
+ \ifcase#method\relax
+ \dostarttagged\t!mstackermid\empty
+ \setbox\scratchboxthree\csname\??mathstackersalternative\p_alternative\endcsname
+ \dostoptagged
+ \fi
+ %
+ \ifdim\wd\scratchboxone<\scratchwidth
+ \setbox\scratchboxone\hpack to \scratchwidth{\hss\unhbox\scratchboxone\hss}% unhboxing makes leaders work
+ \fi
+ \ifdim\wd\scratchboxtwo<\scratchwidth
+ \setbox\scratchboxtwo\hpack to \scratchwidth{\hss\unhbox\scratchboxtwo\hss}%
+ \fi
+ \ifdim\wd\scratchboxthree<\scratchwidth
+ \setbox\scratchboxthree\hpack to \scratchwidth{\hss\unhbox\scratchboxthree\hss}%
+ \fi
+ %
+ \ifcsname\??mathstackerslocation\p_location\endcsname
+ \ifcase\csname\??mathstackerslocation\p_location\endcsname\relax
+ \scratchdistance\zeropoint
+ \or % top
+ \scratchdistance\zeropoint
+ \or % high
+ \scratchdistance.25\htdp\scratchboxthree
+ \or % centered
+ \scratchdistance.5\htdp\scratchboxthree
+ \or % low
+ \scratchdistance.75\htdp\scratchboxthree
+ \or % bottom
+ \scratchdistance\htdp\scratchboxthree
+ \else
+ \scratchdistance\zeropoint
+ \fi
+ \else
+ \scratchdistance\p_location\htdp\scratchboxthree
+ \fi
+ %
+ \ifzeropt\scratchdistance\else
+ \setbox\scratchboxthree\hpack{\lower\scratchdistance\box\scratchboxthree}%
+ \fi
+ %
+ \math_stackers_normalize_three
+ % analysis
+ \ifdim\htdp\scratchboxtwo>\zeropoint
+ \ifdim\htdp\scratchboxone>\zeropoint
+ \dosettagproperty\s!subtype\t!munderover
+ \else
+ \dosettagproperty\s!subtype\t!munder
+ \fi
+ \else
+ \ifdim\htdp\scratchboxone>\zeropoint
+ \dosettagproperty\s!subtype\t!mover
+ \else
+ % brrr
+ \fi
+ \fi
+ % base
+ \math_stackers_start_tagged_mid
+ \math_stackers_middle\bgroup
+ \box\scratchboxthree
+ \egroup
+ \math_stackers_stop_tagged
+ % under
+ \ifdim\htdp\scratchboxtwo>\zeropoint
+ \math_stackers_start_tagged_bot
+ \scratchoffset\scratchvoffset
+ \kern-\scratchwidth
+ \math_stackers_bottom\bgroup
+ \lower\dimexpr\ht\scratchboxtwo+\scratchdepth+\scratchoffset+\scratchbottomoffset\relax
+ \box\scratchboxtwo
+ \egroup
+ \math_stackers_stop_tagged
+ \fi
+ % over
+ \ifdim\htdp\scratchboxone>\zeropoint
+ \math_stackers_start_tagged_top
+ \scratchoffset\scratchvoffset
+ \kern-\scratchwidth
+ \math_stackers_top\bgroup
+ \raise\dimexpr\dp\scratchboxone+\scratchheight+\scratchoffset+\scratchtopoffset\relax
+ \box\scratchboxone
+ \egroup
+ \math_stackers_stop_tagged
+ \fi
+ %
+ }%
+ \dostoptagged
+ \mathstackersparameter\c!right\relax
+ \endgroup}
+ %\math_stackers_stop_group}
+
+\permanent\tolerant\protected\def\definemathextensible[#1]#*[#2]#*[#3]% category name unicode
+ {\ifarguments\or\or
+ \frozen\setuevalue{#1}{\math_stackers_auto_normal\noexpand\currentmathstackers{\number#2}}%
+ \or
+ \frozen\setuevalue{#2}{\math_stackers_auto_normal{#1}{\number#3}}%
+ \fi}
+
+\tolerant\protected\def\math_stackers_auto_normal#1#2#*[#3]#:#=#*#=%
+ {\begingroup
+ \scratchcounter#2\relax
+ \edef\currentmathstackers{\ifparameter#3\or#3\else#1\fi}%
+ \math_stackers_triplet\zerocount\currentmathstackers\scratchcounter{#4}{#5}%
+ \endgroup}
+
+%D A few direct accessors (in the meantime we redefined \mathextensible so we renamed the
+%D following):
+
+\permanent\tolerant\protected\def\directmathextensible[#category]%
+ {\begingroup
+ \math_stackers_handle_extensible{\ifparameter#category\or#category\else\v!mathematics\fi}} % will be defined later on
+
+\permanent\tolerant\protected\def\directtextextensible[#category]%
+ {\begingroup
+ \math_stackers_handle_extensible{\ifparameter#category\or#category\else\v!text\fi}} % will be defined later on
+
+\aliased\let\mathstacker\directmathextensible
+\aliased\let\textstacker\directtextextensible
+
+\def\math_stackers_handle_extensible#category#codepoint#toptext#bottomtext%
+ {\math_stackers_triplet\zerocount{#category}{#codepoint}{#toptext}{#bottomtext}%
+ \endgroup}
+
+%D The next one deals with under and over extensibles (arrows mostly):
+
+\installcorenamespace {mathclasses}
+
+\letvalue{\??mathclasses }\mathord
+\letvalue{\??mathclasses rel}\mathrel
+\letvalue{\??mathclasses ord}\mathord
+
+\def\math_class_by_parameter#1%
+ {\normalexpanded{\noexpand\math_class_by_parameter_indeed{#1\c!mathclass}}}
+
+\def\math_class_by_parameter_indeed#1%
+ {\csname\??mathclasses\ifcsname\??mathclasses#1\endcsname#1\fi\endcsname}
+
+% 1 0 name n 0 | 0 1 name n 0 | 1 1 name n n
+
+\let\math_stackers_stop_group\endgroup
+
+\protected\def\math_stackers_start_group#category%
+ {\begingroup
+ \edef\currentmathstackers{#category}%
+ \edef\p_limits{\mathstackersparameter\c!mathlimits}%
+ \ifx\p_limits\v!yes
+ \def\math_stackers_stop_group{\egroup\endgroup\ordlimits}%
+ \mathop\bgroup
+ \else
+ \let\math_stackers_stop_group\endgroup
+ \fi}
+
+\newconstant\c_math_stackers_top
+\newconstant\c_math_stackers_bottom
+\newconstant\c_math_stackers_codepoint
+\newconstant\c_math_stackers_extracode
+\newdimen \d_math_stackers_offset_l
+\newdimen \d_math_stackers_offset_r
+
+\setupmathstackers[lt=\zeropoint,rt=\zeropoint,lb=\zeropoint,rb=\zeropoint]
+
+\tolerant\protected\def\math_stackers_make_double#top#bottom#category#codepoint#codeextra#spacer[#settings]#:#text%
+ {\math_stackers_start_group{#category}%
+ \c_math_stackers_top #top\relax
+ \c_math_stackers_bottom #bottom\relax
+ \c_math_stackers_codepoint#codepoint\relax
+ \c_math_stackers_extracode#codeextra\relax
+ \ifparameter#settings\or
+ \setupcurrentmathstackers[#settings]%
+ \fi
+ \mathstackersparameter\c!left\relax
+ \dostarttagged\t!mstacker\currentmathstackers
+ \ifmmode\math_class_by_parameter\mathstackersparameter\else\dontleavehmode\fi
+ {\edef\m_math_stackers_text_middle {#text}%
+ %
+ \edef\p_offset {\mathstackersparameter\c!offset}%
+ \edef\p_location {\mathstackersparameter\c!location}%
+ \edef\p_strut {\mathstackersparameter\c!strut}%
+ \edef\p_alternative{\mathstackersparameter\c!alternative}%
+ %
+ \scratchleftoffset \zeropoint
+ \scratchrightoffset\zeropoint
+ %
+ \math_stackers_check_unicode\c_math_stackers_codepoint
+ %
+ \ifempty\math_stackers_middle
+ \setbox\scratchboxthree\emptyhbox
+ \else
+ \setmathtextbox\scratchboxthree\hbox{\math_stackers_middletext}%
+ \fi
+ \scratchwidth\wd\scratchboxthree
+ %
+ \scratchdimen\mathstackersparameter\c!minwidth\relax
+ \ifdim\scratchwidth<\scratchdimen
+ \scratchwidth\scratchdimen
+ \fi
+ \advance\scratchwidth2\scratchhoffset
+ %
+ %\scratchunicode\c_math_stackers_codepoint
+ \ifcase\c_math_stackers_bottom
+ \d_math_stackers_offset_l\mathstackersparameter{lt}%
+ \d_math_stackers_offset_r\mathstackersparameter{rt}%
+ \orelse\ifcase\c_math_stackers_top
+ \d_math_stackers_offset_l\mathstackersparameter{lb}%
+ \d_math_stackers_offset_r\mathstackersparameter{rb}%
+ \else
+ \d_math_stackers_offset_l\mathstackersparameter{lt}%
+ \d_math_stackers_offset_r\mathstackersparameter{rt}%
+ \fi
+ \setbox\scratchboxtwo\csname\??mathstackersalternative\p_alternative\endcsname
+ \setbox\scratchboxthree\hpack to \scratchwidth{\hss\box\scratchboxthree\hss}%
+ %
+ \ifcase\c_math_stackers_extracode\else
+ \scratchunicode\c_math_stackers_extracode
+ \d_math_stackers_offset_l\mathstackersparameter{lb}%
+ \d_math_stackers_offset_r\mathstackersparameter{rb}%
+ \setbox\scratchboxone\csname\??mathstackersalternative\p_alternative\endcsname
+ \fi
+ %
+ \math_stackers_normalize_three
+ % analysis
+ \ifcase\c_math_stackers_bottom
+ \ifcase\c_math_stackers_top
+ \dosettagproperty\s!subtype\t!munderover
+ \else
+ \dosettagproperty\s!subtype\t!mover
+ \fi
+ \else
+ \ifcase\c_math_stackers_top
+ \dosettagproperty\s!subtype\t!munder
+ \else
+ % brrr
+ \fi
+ \fi
+ % base
+ \math_stackers_start_tagged_mid
+ \math_stackers_middle\bgroup
+ \box\scratchboxthree
+ \egroup
+ \math_stackers_stop_tagged
+ %
+ \ifdim\htdp\scratchboxtwo>\zeropoint
+ \ifcase\c_math_stackers_bottom\else
+ \kern-\scratchwidth
+ % under
+ \math_stackers_start_tagged_bot
+ \math_stackers_bottom\bgroup
+ \lower\dimexpr
+ \scratchdepth
+ +\ht\scratchboxtwo
+ +\mathstackersparameter\c!distance % was \c!voffset
+ \relax
+ \ifcase\c_math_stackers_top
+ \box\scratchboxtwo
+ \else
+ \box\scratchboxone
+ \fi
+ \egroup
+ \math_stackers_stop_tagged
+ \fi
+ \ifcase\c_math_stackers_top\else
+ \kern-\scratchwidth
+ % over
+ \math_stackers_start_tagged_top
+ \math_stackers_top\bgroup
+ \raise\dimexpr
+ \scratchheight
+ +\dp\scratchboxtwo % new
+ +\mathstackersparameter\c!distance % was \c!voffset
+ \relax
+ \box\scratchboxtwo
+ \egroup
+ \math_stackers_stop_tagged
+ \fi
+ \fi}%
+ \dostoptagged
+ \mathstackersparameter\c!right\relax
+ \math_stackers_stop_group}
+
+\permanent\tolerant\protected\def\definemathoverextensible[#1]#*[#2]#*[#3]%
+ {\ifparameter#3\or
+ \frozen\setuevalue{#2}{\math_stackers_make_double\plusone \zerocount{#1}{\number#3}{0}}%
+ \else
+ \frozen\setuevalue{#1}{\math_stackers_make_double\plusone \zerocount\noexpand\currentmathstackers{\number#2}{0}}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemathunderextensible[#1]#*[#2]#*[#3]%
+ {\ifparameter#3\or
+ \frozen\setuevalue{#2}{\math_stackers_make_double\zerocount\plusone{#1}{\number#3}{0}}%
+ \else
+ \frozen\setuevalue{#1}{\math_stackers_make_double\zerocount\plusone\noexpand\currentmathstackers{\number#2}{0}}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemathdoubleextensible[#1]#*[#2]#*[#3]#*[#4]%
+ {\ifparameter#4\or
+ \frozen\setuevalue{#2}{\math_stackers_make_double\plusone \plusone{#1}{\number#3}{\number#4}}%
+ \else
+ \frozen\setuevalue{#1}{\math_stackers_make_double\plusone \plusone\noexpand\currentmathstackers{\number#2}{\number#3}}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemathover[#category]#spacer[#command]#spacer[#topcode]%
+ {\frozen\setuvalue{#command}{\math_stackers_handle_direct\plusone\zerocount{#category}{#topcode}{0}}}
+
+\permanent\tolerant\protected\def\definemathunder[#category]#spacer[#command]#spacer[#bottomcode]%
+ {\frozen\setuvalue{#command}{\math_stackers_handle_direct\zerocount\plusone{#category}{#bottomcode}{0}}}
+
+\permanent\tolerant\protected\def\definemathdouble[#category]#spacer[#command]#spacer[#topcode]#spacer[#bottomcode]%
+ {\frozen\setuvalue{#command}{\math_stackers_handle_direct\plusone\plusone{#category}{#topcode}{#bottomcode}}}
+
+\permanent\tolerant\protected\def\mathover[#category]#spacer[#settings]#:#topcode#text%
+ {\begingroup
+ \edef\currentmathstackers{\ifparameter#category\or#category\else\v!top\fi}%
+ \ifparameter#settings\or
+ \setupcurrentmathstackers[#settings]%
+ \fi
+ \math_stackers_make_double\plusone\zerocount
+ {\currentmathstackers}%
+ {#topcode}%
+ {0}%
+ {#text}%
+ \endgroup}
+
+\permanent\tolerant\protected\def\mathunder[#category]#spacer[#settings]#:#bottomcode#text%
+ {\begingroup
+ \edef\currentmathstackers{\ifparameter#category\or#category\else\v!bottom\fi}%
+ \ifparameter#settings\or
+ \setupcurrentmathstackers[#settings]%
+ \fi
+ \math_stackers_make_double\zerocount\plusone
+ {\currentmathstackers}%
+ {#bottomcode}%
+ {0}%
+ {#text}%
+ \endgroup}
+
+\permanent\tolerant\protected\def\mathdouble[#category]#spacer[#settings]#:#topcode#bottomcode#text%
+ {\begingroup
+ \edef\currentmathstackers{\ifparameter#category\or#category\else\v!both\fi}%
+ \ifparameter#settings\or
+ \setupcurrentmathstackers[#settings]%
+ \fi
+ \math_stackers_make_double\plusone\plusone
+ {\currentmathstackers}%
+ {#topcode}%
+ {#bottomcode}%
+ {#text}%
+ \endgroup}
+
+\def\math_stackers_handle_direct#top#bottom#category#topcode#bottomcode#text%
+ {\begingroup
+ \math_stackers_make_double#top#bottom{#category}{#topcode}{#bottomcode}{#text}%
+ \endgroup}
+
+%D A relative new one is a combination of accents and text (as needed in mathml):
+
+\protected\def\math_stackers_make_double_text#where#category#codepoint#text#extra%
+ {\math_stackers_start_group{#category}%
+ \mathstackersparameter\c!left\relax
+ \dostarttagged\t!mstacker\currentmathstackers
+ \ifmmode\math_class_by_parameter\mathstackersparameter\else\dontleavehmode\fi
+ {\edef\currentmathstackers{#category}%
+ %
+ \edef\p_offset {\mathstackersparameter\c!offset}%
+ \edef\p_location {\mathstackersparameter\c!location}%
+ \edef\p_strut {\mathstackersparameter\c!strut}%
+ \edef\p_alternative{\mathstackersparameter\c!alternative}%
+ %
+ \scratchleftoffset \zeropoint
+ \scratchrightoffset\zeropoint
+ %
+ \edef\m_math_stackers_text_middle{#text}%
+ \math_stackers_check_unicode{#codepoint}%
+ \scratchunicode#codepoint\relax
+ %
+ \ifempty\math_stackers_middle
+ \setbox\scratchboxthree\emptyhbox
+ \else
+ \setmathtextbox\scratchboxthree\hbox{\math_stackers_middletext}%
+ \fi
+ %
+ \ifcase#where\relax
+ \edef\m_math_stackers_text_top{#extra}%
+ \ifempty\math_stackers_top
+ \setbox\scratchboxone\emptyhbox
+ \else
+ \setmathsmalltextbox\scratchboxone\hbox{\math_stackers_toptext}%
+ \fi
+ \else
+ \edef\m_math_stackers_text_bottom{#extra}%
+ \ifempty\math_stackers_bottom
+ \setbox\scratchboxone\emptyhbox
+ \else
+ \setmathsmalltextbox\scratchboxone\hbox{\math_stackers_bottomtext}%
+ \fi
+ \fi
+ %
+ \scratchwidth\wd
+ \ifdim\wd\scratchboxone>\wd\scratchboxthree
+ \scratchboxone
+ \else
+ \scratchboxthree
+ \fi
+ \relax
+ \scratchdimen\mathstackersparameter\c!minwidth\relax
+ \ifdim\scratchwidth<\scratchdimen
+ \scratchwidth\scratchdimen
+ \fi
+ \advance\scratchwidth2\scratchhoffset
+ %
+ \ifdim\wd\scratchboxone<\scratchwidth
+ \setbox\scratchboxone\hpack to \scratchwidth{\hss\unhbox\scratchboxone\hss}%
+ \fi
+ \ifdim\wd\scratchboxthree<\scratchwidth
+ \setbox\scratchboxthree\hpack to \scratchwidth{\hss\unhbox\scratchboxthree\hss}%
+ \fi
+ %
+ \math_stackers_normalize_three
+ % analysis
+ \dosettagproperty\s!subtype\t!munderover
+ % base
+ \math_stackers_start_tagged_mid
+ \math_stackers_middle\bgroup
+ \box\scratchboxthree
+ \egroup
+ \math_stackers_stop_tagged
+ %
+ \setbox\scratchboxtwo\csname\??mathstackersalternative\p_alternative\endcsname
+ \kern-\scratchwidth
+ \ifcase#where\relax
+ % under
+ \math_stackers_start_tagged_bot
+ \math_stackers_bottom\bgroup
+ \lower\dimexpr
+ \scratchdepth
+ +\ht\scratchboxtwo
+ +\mathstackersparameter\c!distance
+ \relax
+ \box\scratchboxtwo % accent
+ \egroup
+ \math_stackers_stop_tagged
+ \kern-\scratchwidth
+ % over
+ \math_stackers_start_tagged_top
+ \math_stackers_top\bgroup
+ \raise\dimexpr
+ \scratchheight
+ +\dp\scratchboxone
+ +\mathstackersparameter\c!voffset
+ \relax
+ \box\scratchboxone % toptext
+ \egroup
+ \math_stackers_stop_tagged
+ \else
+ % under
+ \math_stackers_start_tagged_bot
+ \math_stackers_bottom\bgroup
+ \lower\dimexpr
+ \scratchdepth
+ +\ht\scratchboxone
+ +\mathstackersparameter\c!voffset
+ \relax
+ \box\scratchboxone % bottext
+ \egroup
+ \math_stackers_stop_tagged
+ \kern-\scratchwidth
+ % over
+ \math_stackers_start_tagged_top
+ \math_stackers_top\bgroup
+ \raise\dimexpr
+ \scratchheight
+ +\dp\scratchboxtwo % new
+ +\mathstackersparameter\c!distance
+ \relax
+ \box\scratchboxtwo % accent
+ \egroup
+ \math_stackers_stop_tagged
+ \fi
+ }%
+ \dostoptagged
+ \mathstackersparameter\c!right\relax
+ \math_stackers_stop_group}
+
+\permanent\tolerant\protected\def\definemathovertextextensible[#1]#*[#2]#*[#3]%
+ {\ifparameter#3\or
+ \frozen\setuevalue{#2}{\math_stackers_make_double_text\plusone{#1}{\number#3}}%
+ \else
+ \frozen\setuevalue{#1}{\math_stackers_make_double_text\plusone\noexpand\currentmathstackers{\number#2}}%
+ \fi}
+
+\permanent\tolerant\protected\def\definemathundertextextensible[#1]#*[#2]#*[#3]%
+ {\ifparameter#3\or
+ \frozen\setuevalue{#2}{\math_stackers_make_double_text\zerocount{#1}{\number#3}}%
+ \else
+ \frozen\setuevalue{#1}{\math_stackers_make_double_text\zerocount\noexpand\currentmathstackers{\number#2}}%
+ \fi}
+
+\permanent\tolerant\protected\def\mathovertext[#category]%
+ {\begingroup
+ \math_stackers_direct_double_text\plusone {\ifarguments#category\or#category\else\v!top\fi}}
+
+\permanent\tolerant\protected\def\mathundertext[#category]%
+ {\begingroup
+ \math_stackers_direct_double_text\zerocount{\ifarguments#category\or#category\else\v!bottom\fi}}
+
+\def\math_stackers_direct_double_text#where#category#codepoint#text#extra%%
+ {\math_stackers_make_double_text#where{#category}{#codepoint}{#text}{#extra}%
+ \endgroup}
+
+%D Here is a bonus macro that takes three texts. It can be used to get consistent
+%D mixed usage.
+
+\permanent\tolerant\protected\def\mathtriplet[#category]#:#middletext#toptext#bottomtext%
+ {\begingroup
+ \math_stackers_triplet\plusone{\ifarguments#category\or#category\else\currentmathstackers\fi}{#middletext}{#toptext}{#bottomtext}%
+ \endgroup}
+
+\permanent\tolerant\protected\def\definemathtriplet[#1]#*[#2]#*[#3]% category name default
+ {\ifarguments\or
+ \frozen\setuevalue{#1}{\math_stackers_auto_triplet_nop[\noexpand\currentmathstackers]}%
+ \or
+ \frozen\setuevalue{#2}{\math_stackers_auto_triplet_nop[#1]}%
+ \or
+ \frozen\setuevalue{#2}{\math_stackers_auto_triplet_yes[#1][#3]}%
+ \fi}
+
+\tolerant\protected\def\math_stackers_auto_triplet_yes[#1][#2]#*[#3]#:#=#*#=% [#2]% #2 gobble spaces
+ {\begingroup
+ \edef\currentmathstackers{#1}%
+ \def \m_math_stackers_text_middle{#2}%
+ \ifarguments#3\or\edef\currentmathstackers{#3}\fi
+ \math_stackers_triplet\plusone\currentmathstackers\m_math_stackers_text_middle{#4}{#5}%
+ \endgroup}
+
+\tolerant\protected\def\math_stackers_auto_triplet_nop[#1]#*[#2]#:#=#*#=#*#=% [#2]% #2 gobble spaces%
+ {\begingroup
+ \edef\currentmathstackers{#1}%
+ \ifarguments#2\or\edef\currentmathstackers{#2}\fi
+ \math_stackers_triplet\plusone\currentmathstackers{#3}{#4}{#5}%
+ \endgroup}
+
+%D Definitions:
+
+\definemathstackers
+ [\v!mathematics]
+ [\c!topcommand=\mathematics,
+ \c!middlecommand=\mathematics,
+ \c!bottomcommand=\mathematics]
+
+\definemathstackers
+ [\s!math]
+ [\v!mathematics]
+
+\definemathstackers
+ [\v!text]
+ [\v!mathematics]
+ [\c!topcommand=,
+ \c!middlecommand=\mathematics,
+ \c!bottomcommand=]
+
+\definemathstackers
+ [\v!reverse]
+ [\v!mathematics]
+ [\c!order=\v!reverse]
+
+\definemathstackers
+ [\v!both]
+ [\v!mathematics]
+ [\c!location=\v!top, % ?
+ \c!strut=\v!no,
+ \c!middlecommand=\mathematics,
+ \c!hoffset=\zeropoint]
+
+\definemathstackers
+ [\v!top]
+ [\v!both]
+
+\definemathstackers
+ [\v!bottom]
+ [\v!both]
+
+\definemathstackers
+ [\v!vfenced]
+ [\v!both]
+ [\c!mathclass=\s!ord,
+ \c!mathlimits=\v!yes]
+
+% these are needed for mathml:
+
+% \setupmathstackers
+% [\v!both]
+% [\c!hoffset=1pt,
+% \c!voffset=1pt]
+
+\definemathstackers
+ [\v!bothtext]
+ [\v!both]
+ [\c!strut=\v!yes]
+
+% These are compatibity definitions, math only.
+
+% todo: top= bottom= middle= is nicer (compare math-fen)
+
+%D We save a few definitions that we automatically got from the \type {char-def.lua}
+%D database.
+
+% Be careful in choosing what accents you take (the code below uses a combining
+% one):
+%
+% \startbuffer
+% % $\Umathaccent top 0 0 "20D7 {example}$
+% % $\Umathaccent top fixed 0 0 "20D7 {example}$
+% $\Umathaccent 0 0 "20D7 {example}$
+% $\Umathaccent fixed 0 0 "20D7 {example}$
+% $\Umathaccent bottom 0 0 "20D7 {example}$
+% $\Umathaccent bottom fixed 0 0 "20D7 {example}$
+% $\Umathaccent both 0 0 "20D7
+% 0 0 "20D7 {example}$
+% $\Umathaccent both fixed 0 0 "20D7
+% fixed 0 0 "20D7 {example}$
+% $\Umathaccent both 0 0 "20D7
+% fixed 0 0 "20D7 {example}$
+% $\Umathaccent both fixed 0 0 "20D7
+% 0 0 "20D7 {example}$
+% \stopbuffer
+%
+% \setupbodyfont[modern] \getbuffer
+% \setupbodyfont[xits] \getbuffer
+% \setupbodyfont[cambria] \getbuffer
+
+\immutable\protected\def\normaldoublebrace {\Umathaccents 0 \defaultmathfamily "23DE 0 \defaultmathfamily "23DF }
+\immutable\protected\def\normaldoubleparent{\Umathaccents 0 \defaultmathfamily "23DC 0 \defaultmathfamily "23DD }
+
+% let's keep this
+
+\aliased\let\normaloverbrace \overbrace
+\aliased\let\normalunderbrace \underbrace
+\aliased\let\normaloverparent \overparent
+\aliased\let\normalunderparent \underparent
+\aliased\let\normaloverbracket \overbracket
+\aliased\let\normalunderbracket \underbracket
+\aliased\let\normalunderleftarrow \underleftarrow
+\aliased\let\normaloverleftarrow \overleftarrow
+\aliased\let\normalunderrightarrow\underrightarrow
+\aliased\let\normaloverrightarrow \overrightarrow
+
+%D Here come the new ones:
+
+\definemathstackers [\v!none] [\v!mathematics] [\c!hoffset=\zeropoint]
+\definemathstackers [\v!normal] [\v!mathematics] [\c!hoffset=0.5\mathemwidth] % the default
+\definemathstackers [\v!small] [\v!mathematics] [\c!hoffset=1\mathemwidth]
+\definemathstackers [\v!medium] [\v!mathematics] [\c!hoffset=1.5\mathemwidth]
+\definemathstackers [\v!big] [\v!mathematics] [\c!hoffset=2\mathemwidth]
+
+\definemathextensible [\v!reverse] [xrel] ["002D]
+\definemathextensible [\v!reverse] [xequal] ["003D]
+\definemathextensible [\v!reverse] [xleftarrow] ["2190] % ["27F5]
+\definemathextensible [\v!reverse] [xrightarrow] ["2192] % ["27F6]
+\definemathextensible [\v!reverse] [xleftrightarrow] ["27F7]
+\definemathextensible [\v!reverse] [xLeftarrow] ["27F8]
+\definemathextensible [\v!reverse] [xRightarrow] ["27F9]
+\definemathextensible [\v!reverse] [xLeftrightarrow] ["27FA]
+\definemathextensible [\v!reverse] [xtwoheadleftarrow] ["219E]
+\definemathextensible [\v!reverse] [xtwoheadrightarrow] ["21A0]
+\definemathextensible [\v!reverse] [xmapsto] ["21A6]
+\definemathextensible [\v!reverse] [xhookleftarrow] ["21A9]
+\definemathextensible [\v!reverse] [xhookrightarrow] ["21AA]
+\definemathextensible [\v!reverse] [xleftharpoondown] ["21BD]
+\definemathextensible [\v!reverse] [xleftharpoonup] ["21BC]
+\definemathextensible [\v!reverse] [xrightharpoondown] ["21C1]
+\definemathextensible [\v!reverse] [xrightharpoonup] ["21C0]
+\definemathextensible [\v!reverse] [xrightoverleftarrow] ["21C4]
+\definemathextensible [\v!reverse] [xleftrightharpoons] ["21CB]
+\definemathextensible [\v!reverse] [xrightleftharpoons] ["21CC]
+\definemathextensible [\v!reverse] [xtriplerel] ["2261]
+
+\definemathextensible [\v!mathematics] [mrel] ["002D]
+\definemathextensible [\v!mathematics] [mequal] ["003D]
+\definemathextensible [\v!mathematics] [mleftarrow] ["2190] % ["27F5]
+\definemathextensible [\v!mathematics] [mrightarrow] ["2192] % ["27F6]
+\definemathextensible [\v!mathematics] [mleftrightarrow] ["27F7]
+\definemathextensible [\v!mathematics] [mLeftarrow] ["27F8]
+\definemathextensible [\v!mathematics] [mRightarrow] ["27F9]
+\definemathextensible [\v!mathematics] [mLeftrightarrow] ["27FA]
+\definemathextensible [\v!mathematics] [mtwoheadleftarrow] ["219E]
+\definemathextensible [\v!mathematics] [mtwoheadrightarrow] ["21A0]
+\definemathextensible [\v!mathematics] [mmapsto] ["21A6]
+\definemathextensible [\v!mathematics] [mhookleftarrow] ["21A9]
+\definemathextensible [\v!mathematics] [mhookrightarrow] ["21AA]
+\definemathextensible [\v!mathematics] [mleftharpoondown] ["21BD]
+\definemathextensible [\v!mathematics] [mleftharpoonup] ["21BC]
+\definemathextensible [\v!mathematics] [mrightharpoondown] ["21C1]
+\definemathextensible [\v!mathematics] [mrightharpoonup] ["21C0]
+\definemathextensible [\v!mathematics] [mrightoverleftarrow] ["21C4]
+\definemathextensible [\v!mathematics] [mleftrightharpoons] ["21CB]
+\definemathextensible [\v!mathematics] [mrightleftharpoons] ["21CC]
+\definemathextensible [\v!mathematics] [mtriplerel] ["2261]
+
+\definemathextensible [\v!mathematics] [eleftarrowfill] ["2190] % ["27F5]
+\definemathextensible [\v!mathematics] [erightarrowfill] ["2192] % ["27F6]
+\definemathextensible [\v!mathematics] [eleftrightarrowfill] ["27F7]
+\definemathextensible [\v!mathematics] [etwoheadrightarrowfill] ["27F9]
+\definemathextensible [\v!mathematics] [eleftharpoondownfill] ["21BD]
+\definemathextensible [\v!mathematics] [eleftharpoonupfill] ["21BC]
+\definemathextensible [\v!mathematics] [erightharpoondownfill] ["21C1]
+\definemathextensible [\v!mathematics] [erightharpoonupfill] ["21C0]
+
+\definemathextensible [\v!mathematics] [eoverbarfill] ["FE33E]
+\definemathextensible [\v!mathematics] [eunderbarfill] ["FE33F]
+\definemathextensible [\v!mathematics] [eoverbracefill] ["FE3DE]
+\definemathextensible [\v!mathematics] [eunderbracefill] ["FE3DF]
+\definemathextensible [\v!mathematics] [eoverparentfill] ["FE3DC]
+\definemathextensible [\v!mathematics] [eunderparentfill] ["FE3DD]
+\definemathextensible [\v!mathematics] [eoverbracketfill] ["FE3B4]
+\definemathextensible [\v!mathematics] [eunderbracketfill] ["FE3B5]
+
+\definemathextensible [\v!text] [trel] ["002D]
+\definemathextensible [\v!text] [tequal] ["003D]
+\definemathextensible [\v!text] [tmapsto] ["21A6]
+\definemathextensible [\v!text] [tleftarrow] ["2190] % ["27F5]
+\definemathextensible [\v!text] [trightarrow] ["2192] % ["27F6]
+\definemathextensible [\v!text] [tleftrightarrow] ["27F7]
+\definemathextensible [\v!text] [tLeftarrow] ["27F8]
+\definemathextensible [\v!text] [tRightarrow] ["27F9]
+\definemathextensible [\v!text] [tLeftrightarrow] ["27FA]
+\definemathextensible [\v!text] [ttwoheadleftarrow] ["219E]
+\definemathextensible [\v!text] [ttwoheadrightarrow] ["21A0]
+\definemathextensible [\v!text] [thookleftarrow] ["21A9]
+\definemathextensible [\v!text] [thookrightarrow] ["21AA]
+\definemathextensible [\v!text] [tleftharpoondown] ["21BD]
+\definemathextensible [\v!text] [tleftharpoonup] ["21BC]
+\definemathextensible [\v!text] [trightharpoondown] ["21C1]
+\definemathextensible [\v!text] [trightharpoonup] ["21C0]
+\definemathextensible [\v!text] [trightoverleftarrow] ["21C4]
+\definemathextensible [\v!text] [tleftrightharpoons] ["21CB]
+\definemathextensible [\v!text] [trightleftharpoons] ["21CC]
+\definemathextensible [\v!text] [ttriplerel] ["2261]
+
+\definemathoverextensible [\v!top] [overleftarrow] ["2190] % ["27F5]
+\definemathoverextensible [\v!top] [overrightarrow] ["2192] % ["27F6]
+\definemathoverextensible [\v!top] [overleftrightarrow] ["27F7]
+\definemathoverextensible [\v!top] [overtwoheadleftarrow] ["27F8]
+\definemathoverextensible [\v!top] [overtwoheadrightarrow] ["27F9]
+\definemathoverextensible [\v!top] [overleftharpoondown] ["21BD]
+\definemathoverextensible [\v!top] [overleftharpoonup] ["21BC]
+\definemathoverextensible [\v!top] [overrightharpoondown] ["21C1]
+\definemathoverextensible [\v!top] [overrightharpoonup] ["21C0]
+
+\definemathunderextensible [\v!bottom] [underleftarrow] ["2190] % ["27F5]
+\definemathunderextensible [\v!bottom] [underrightarrow] ["2192] % ["27F6]
+\definemathunderextensible [\v!bottom] [underleftrightarrow] ["27F7]
+\definemathunderextensible [\v!bottom] [undertwoheadleftarrow] ["27F8]
+\definemathunderextensible [\v!bottom] [undertwoheadrightarrow] ["27F9]
+\definemathunderextensible [\v!bottom] [underleftharpoondown] ["21BD]
+\definemathunderextensible [\v!bottom] [underleftharpoonup] ["21BC]
+\definemathunderextensible [\v!bottom] [underrightharpoondown] ["21C1]
+\definemathunderextensible [\v!bottom] [underrightharpoonup] ["21C0]
+
+%D We don't use overline and underline. This is one of the overlooked aspects of
+%D unicode cq. opentype math: why treat rules different than e.g. arrows and
+%D accents. It is a bit unfortunate that the opportunity to move math to new
+%D technologies happened outside the tex domain (and/or some aspects were kept while
+%D in fact they were side effects of limitations of traditional fonts). From the
+%D unicode aware tex engines' implementation point of view things could have been
+%D done a bit nicer but then: the community didn't seem to care too much and just
+%D has to follow now.
+%D
+%D Anyhow, we use a character based approach so that at least we get unicode stuff
+%D in the backend (okay, we still need to deal with some cut and paste issues but at
+%D least we now know what we deal with.
+
+% alternatively we can move the original to FE*
+
+\definemathoverextensible [\v!vfenced] [overbar] ["FE33E] % ["203E]
+\definemathunderextensible [\v!vfenced] [underbar] ["FE33F] % ["203E]
+\definemathdoubleextensible [\v!vfenced] [doublebar] ["FE33E] ["FE33F]
+
+\definemathoverextensible [\v!vfenced] [overbrace] ["FE3DE] % ["023DE]
+\definemathunderextensible [\v!vfenced] [underbrace] ["FE3DF] % ["023DF]
+\definemathdoubleextensible [\v!vfenced] [doublebrace] ["FE3DE] ["FE3DF]
+
+\definemathoverextensible [\v!vfenced] [overparent] ["FE3DC] % ["023DC]
+\definemathunderextensible [\v!vfenced] [underparent] ["FE3DD] % ["023DD]
+\definemathdoubleextensible [\v!vfenced] [doubleparent] ["FE3DC] ["FE3DD]
+
+\definemathoverextensible [\v!vfenced] [overbracket] ["FE3B4] % ["023B4]
+\definemathunderextensible [\v!vfenced] [underbracket] ["FE3B5] % ["023B5]
+\definemathdoubleextensible [\v!vfenced] [doublebracket] ["FE3B4] ["FE3B5]
+
+% \protected\def\mathopwithlimits#1#2{\mathop{#1{#2}}\limits}
+
+%D For mathml:
+
+\definemathdoubleextensible [\v!both] [overbarunderbar] ["FE33E] ["FE33F]
+\definemathdoubleextensible [\v!both] [overbraceunderbrace] ["FE3DE] ["FE3DF]
+\definemathdoubleextensible [\v!both] [overparentunderparent] ["FE3DC] ["FE3DD]
+\definemathdoubleextensible [\v!both] [overbracketunderbracket] ["FE3B4] ["FE3B5]
+
+\definemathovertextextensible [\v!bothtext] [overbartext] ["FE33E]
+\definemathundertextextensible [\v!bothtext] [underbartext] ["FE33F]
+\definemathovertextextensible [\v!bothtext] [overbracetext] ["FE3DE]
+\definemathundertextextensible [\v!bothtext] [underbracetext] ["FE3DF]
+\definemathovertextextensible [\v!bothtext] [overparenttext] ["FE3DC]
+\definemathundertextextensible [\v!bothtext] [underparenttext] ["FE3DD]
+\definemathovertextextensible [\v!bothtext] [overbrackettext] ["FE3B4]
+\definemathundertextextensible [\v!bothtext] [underbrackettext] ["FE3B5]
+
+%D Some bonus ones (for the moment here):
+
+\definemathstackers
+ [\v!chemistry]
+ [\c!offset=\v!max,
+ \c!left=\enspace,
+ \c!right=\enspace,
+ \c!hoffset=.5\mathemwidth]
+
+\definemathextensible [\v!chemistry] [cleftarrow] ["2190]
+\definemathextensible [\v!chemistry] [crightarrow] ["2192]
+\definemathextensible [\v!chemistry] [crightoverleftarrow] ["21C4]
+
+% for the moment:
+
+\def\math_stackers_hacked_fill#1#2#3%
+ {\mathematics
+ {\begingroup
+ \mathsurround\zeropoint
+ \thickmuskip \zeromuskip
+ \medmuskip \zeromuskip
+ \thinmuskip \zeromuskip
+ #1%
+ \mkern-7\onemuskip
+ \cleaders\mathstylehbox{\mkern-2\onemuskip#2\mkern-2\onemuskip}\hfill
+ \mkern-7\onemuskip
+ #3%
+ \endgroup}}
+
+% These will be defined in char-def as well once we have \leaders<number>
+
+\immutable\protected\def\rightarrowfill {\math_stackers_hacked_fill \relbar \relbar \rightarrow}
+\immutable\protected\def\leftarrowfill {\math_stackers_hacked_fill \leftarrow \relbar \relbar }
+\immutable\protected\def\rightoverleftarrowfill{\math_stackers_hacked_fill \ctxdoublearrowfillleftend\ctxdoublearrowfillmiddlepart\ctxdoublearrowfillrightend}
+\immutable\protected\def\equalfill {\math_stackers_hacked_fill \Relbar \Relbar \Relbar}
+\immutable\protected\def\Rightarrowfill {\math_stackers_hacked_fill \Relbar \Relbar \Rightarrow}
+\immutable\protected\def\Leftarrowfill {\math_stackers_hacked_fill \Leftarrow \Relbar \Relbar}
+\immutable\protected\def\Leftrightarrowfill {\math_stackers_hacked_fill \Leftarrow \Relbar \Rightarrow}
+\immutable\protected\def\leftrightarrowfill {\math_stackers_hacked_fill \leftarrow \relbar \rightarrow}
+\immutable\protected\def\mapstofill {\math_stackers_hacked_fill{\mapstochar\relbar} \relbar \rightarrow}
+\immutable\protected\def\twoheadrightarrowfill {\math_stackers_hacked_fill \relbar \relbar \twoheadrightarrow}
+\immutable\protected\def\twoheadleftarrowfill {\math_stackers_hacked_fill \twoheadleftarrow \relbar \relbar}
+\immutable\protected\def\rightharpoondownfill {\math_stackers_hacked_fill \relbar \relbar \rightharpoondown}
+\immutable\protected\def\rightharpoonupfill {\math_stackers_hacked_fill \relbar \relbar \rightharpoonup}
+\immutable\protected\def\leftharpoondownfill {\math_stackers_hacked_fill \leftharpoondown \relbar \relbar}
+\immutable\protected\def\leftharpoonupfill {\math_stackers_hacked_fill \leftharpoonup \relbar \relbar}
+\immutable\protected\def\hookleftfill {\math_stackers_hacked_fill \leftarrow \relbar {\relbar\joinrel\rhook}}
+\immutable\protected\def\hookrightfill {\math_stackers_hacked_fill{\lhook\joinrel\relbar} \relbar \rightarrow}
+\immutable\protected\def\relfill {\math_stackers_hacked_fill \relbar \relbar \relbar}
+\immutable\protected\def\triplerelfill {\math_stackers_hacked_fill \equiv \equiv \equiv}
+
+% \permanent\protected\def\singlebond{{\xrel}} % or \def\singlebond{{\xrel[2]}}
+% \permanent\protected\def\doublebond{{\xequal}}
+% \permanent\protected\def\triplebond{{\xtriplerel}}
+
+%D For the moment (needs checking):
+
+\permanent\tolerant\protected\def\defineextensiblefiller[#1]#*[#2]%
+ {\immutable\letcsname\??mathextensiblefallbacks\number#2\expandafter\endcsname\csname#1\endcsname
+ %\immutable\letcsname#1\expandafter\endcsname\csname#1\endcsname
+ } % huh?
+
+%defineextensiblefiller [barfill] ["203E] % yet undefined
+\defineextensiblefiller [relfill] ["002D]
+\defineextensiblefiller [equalfill] ["003D]
+\defineextensiblefiller [leftarrowfill] ["2190]
+\defineextensiblefiller [rightarrowfill] ["2192]
+\defineextensiblefiller [twoheadleftarrowfill] ["219E]
+\defineextensiblefiller [twoheadrightarrowfill] ["21A0]
+\defineextensiblefiller [mapstofill] ["21A6]
+%defineextensiblefiller [hookleftarrowfill] ["21A9]
+%defineextensiblefiller [hookrightarrowfill] ["21AA]
+\defineextensiblefiller [leftharpoondownfill] ["21BD]
+\defineextensiblefiller [leftharpoonupfill] ["21BC]
+\defineextensiblefiller [rightharpoondownfill] ["21C1]
+\defineextensiblefiller [rightharpoonupfill] ["21C0]
+\defineextensiblefiller [rightoverleftarrowfill] ["21C4]
+%defineextensiblefiller [leftrightharpoonsfill] ["21CB] % yet undefined
+%defineextensiblefiller [rightleftharpoonsfill] ["21CC] % yet undefined
+\defineextensiblefiller [triplerelfill] ["2261]
+\defineextensiblefiller [leftrightarrowfill] ["27F7]
+\defineextensiblefiller [Leftarrowfill] ["27F8]
+\defineextensiblefiller [Rightarrowfill] ["27F9]
+\defineextensiblefiller [Leftrightarrowfill] ["27FA]
+%\defineextensiblefiller[Rightleftarrowfill] [.....]
+
+% These are fishy ... we need to check this because now these commands relax (there
+% are some entries in char-def.)
+%
+% %definemathoverextensible [overbarfill] ["FE33E]
+% %definemathunderextensible [underbarfill] ["FE33F]
+% \definemathoverextensible [overbracefill] ["FE3DE]
+% \definemathunderextensible [underbracefill] ["FE3DF]
+% \definemathoverextensible [overparentfill] ["FE3DC]
+% \definemathunderextensible [underparentfill] ["FE3DD]
+% \definemathoverextensible [overbracketfill] ["FE3B4]
+% \definemathunderextensible [underbracketfill] ["FE3B5]
+%
+% %defineextensiblefiller [overbarfill] ["FE33E]
+% %defineextensiblefiller [underbarfill] ["FE33F]
+% \defineextensiblefiller [overbracefill] ["FE3DE]
+% \defineextensiblefiller [underbracefill] ["FE3DF]
+% \defineextensiblefiller [overparentfill] ["FE3DC]
+% \defineextensiblefiller [underparentfill] ["FE3DD]
+% \defineextensiblefiller [overbracketfill] ["FE3B4]
+% \defineextensiblefiller [underbracketfill] ["FE3B5]
+
+%D Extra:
+
+\permanent\protected\edef\singlebond{\mathematics{\mathsurround\zeropoint\char\number"002D\relax}}
+\permanent\protected\edef\doublebond{\mathematics{\mathsurround\zeropoint\char\number"003D\relax}}
+\permanent\protected\edef\triplebond{\mathematics{\mathsurround\zeropoint\char\number"2261\relax}}
+
+% \mathchardef\singlebond"002D
+% \mathchardef\doublebond"003D
+% \mathchardef\triplebond"2261
+
+%D Also handy:
+
+\permanent\tolerant\protected\def\definemathunstacked[#1]#*[#2]#*[#3]% category name unicode
+ {\ifarguments\or\or
+ \frozen\setuevalue{#1}{\math_stackers_unstacked_normal\noexpand\currentmathstackers{\number#2}}%
+ \else
+ \frozen\setuevalue{#2}{\math_stackers_unstacked_normal{#1}{\number#3}}%
+ \fi}
+
+\protected\def\math_stackers_unstacked_normal#category#codepoint%
+ {\begingroup
+ \edef\currentmathstackers{#category}%
+ \edef\p_moffset{\mathstackersparameter\c!moffset}%
+ \ifempty\p_moffset \else
+ \mskip\p_moffset\relax
+ \fi
+ \ifmmode\math_class_by_parameter\mathstackersparameter\else\dontleavehmode\fi
+ {\usemathstackerscolorparameter\c!color
+ \Umathchar\zerocount\defaultmathfamily#codepoint}%
+ \ifempty\p_moffset \else
+ \mskip\p_moffset\relax
+ \fi
+ \endgroup}
+
+\definemathstackers [\v!wide] [\c!moffset=\thickmuskip,\c!mathclass=\s!rel]
+
+\definemathunstacked [\v!wide] [And] ["0026] % \mathrel{\;&\;}
+\definemathunstacked [\v!wide] [impliedby] ["27F8] % \mathrel{\;\Longleftarrow\;}
+\definemathunstacked [\v!wide] [implies] ["27F9] % \mathrel{\;\Longrightarrow\;}
+\definemathunstacked [\v!wide] [iff] ["27FA] % \mathrel{\;\Longleftrightarrow\;}
+
+% New (an example of using \mathexheight):
+
+\definemathstackers
+ [\v!symbol]
+ [\c!voffset=-.3\mathexheight,
+ \c!hoffset=\zeropoint,
+ \c!mathclass=ord,
+ \c!topoffset=.4\mathemwidth, % poor man's italic correction
+ \c!middlecommand=\mathematics]
+
+\definemathover[\v!symbol][interiorset]["2218]
+
+\protect \endinput
+
+% \mathrel{\mathop{\hbox to \dimen0{\hss\copy4\hss}}
+% \limits\normalsuperscript{\box0}\normalsubscript{\box2}}%
+
+% $\Uoverdelimiter \defaultmathfamily "2194 {xxxx}$
+% $\Uunderdelimiter\defaultmathfamily "2194 {xxxx}$
+% $\Udelimiterover \defaultmathfamily "2194 {xxxx}$
+% $\Udelimiterunder\defaultmathfamily "2194 {xxxx}$
+% $\Udelimiterover \defaultmathfamily "219A {\Udelimiterunder \defaultmathfamily "219B {xxxx}}$
+
+% $a \mathrel{\mathop{\filledhboxr{mid}}}\limits^{\filledhboxg{\strut top}}_{\filledhboxb{\strut bottom}} b$
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-tag.lmt b/Master/texmf-dist/tex/context/base/mkxl/math-tag.lmt
new file mode 100644
index 00000000000..eb58b5450d5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-tag.lmt
@@ -0,0 +1,596 @@
+if not modules then modules = { } end modules ['math-tag'] = {
+ version = 1.001,
+ comment = "companion to math-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: have a local list with local tags that then get appended
+-- todo: use tex.getmathcodes (no table)
+
+-- use lpeg matchers
+
+local find, match = string.find, string.match
+local insert, remove, concat = table.insert, table.remove, table.concat
+
+local attributes = attributes
+local nodes = nodes
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getchar = nuts.getchar
+local getfont = nuts.getfont
+local getlist = nuts.getlist
+local getfield = nuts.getfield
+local getdisc = nuts.getdisc
+local getsubtype = nuts.getsubtype
+local getattr = nuts.getattr
+local getattrlist = nuts.getattrlist
+local setattr = nuts.setattr
+----- getcomponents = nuts.getcomponents -- not really needed
+local getwidth = nuts.getwidth
+
+local getnucleus = nuts.getnucleus
+local getsub = nuts.getsub
+local getsup = nuts.getsup
+
+local set_attributes = nuts.setattributes
+
+local nextnode = nuts.traversers.node
+
+local nodecodes = nodes.nodecodes
+
+local noad_code = nodecodes.noad -- attr nucleus sub sup
+local accent_code = nodecodes.accent -- attr nucleus sub sup accent
+local radical_code = nodecodes.radical -- attr nucleus sub sup left degree
+local fraction_code = nodecodes.fraction -- attr nucleus sub sup left right
+local subbox_code = nodecodes.subbox -- attr list
+local submlist_code = nodecodes.submlist -- attr list
+local mathchar_code = nodecodes.mathchar -- attr fam char
+local mathtextchar_code = nodecodes.mathtextchar -- attr fam char
+local delimiter_code = nodecodes.delimiter -- attr small_fam small_char large_fam large_char
+local style_code = nodecodes.style -- attr style
+local choice_code = nodecodes.choice -- attr display text script scriptscript
+local fence_code = nodecodes.fence -- attr subtype
+
+local accentcodes = nodes.accentcodes
+local fencecodes = nodes.fencecodes
+
+local fixedtopaccent_code = accentcodes.fixedtop
+local fixedbottomaccent_code = accentcodes.fixedbottom
+local fixedbothaccent_code = accentcodes.fixedboth
+
+local leftfence_code = fencecodes.left
+local middlefence_code = fencecodes.middle
+local rightfence_code = fencecodes.right
+
+local kerncodes = nodes.kerncodes
+
+local fontkern_code = kerncodes.fontkern
+local italickern_code = kerncodes.italickern
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local kern_code = nodecodes.kern
+local math_code = nodecodes.math
+
+local processnoads = noads.process
+
+local a_tagged = attributes.private('tagged')
+local a_mathcategory = attributes.private('mathcategory')
+local a_mathmode = attributes.private('mathmode')
+
+local tags = structures.tags
+
+local start_tagged = tags.start
+local restart_tagged = tags.restart
+local stop_tagged = tags.stop
+local taglist = tags.taglist
+
+local chardata = characters.data
+
+local getmathcodes = tex.getmathcodes
+local mathcodes = mathematics.codes
+local ordinary_mathcode = mathcodes.ordinary
+local variable_mathcode = mathcodes.variable
+
+local fromunicode16 = fonts.mappings.fromunicode16
+local fontcharacters = fonts.hashes.characters
+
+local report_tags = logs.reporter("structure","tags")
+
+local process
+
+local function processsubsup(start)
+ -- At some point we might need to add an attribute signaling the
+ -- super- and subscripts because TeX and MathML use a different
+ -- order. The mrows are needed to keep mn's separated.
+ local nucleus = getnucleus(start)
+ local sup = getsup(start)
+ local sub = getsub(start)
+ if sub then
+ if sup then
+ setattr(start,a_tagged,start_tagged("msubsup"))
+ -- start_tagged("mrow")
+ process(nucleus)
+ -- stop_tagged()
+ start_tagged("mrow", { subscript = true })
+ process(sub)
+ stop_tagged()
+ start_tagged("mrow", { superscript = true })
+ process(sup)
+ stop_tagged()
+ stop_tagged()
+ else
+ setattr(start,a_tagged,start_tagged("msub"))
+ -- start_tagged("mrow")
+ process(nucleus)
+ -- stop_tagged()
+ start_tagged("mrow")
+ process(sub)
+ stop_tagged()
+ stop_tagged()
+ end
+ elseif sup then
+ setattr(start,a_tagged,start_tagged("msup"))
+ -- start_tagged("mrow")
+ process(nucleus)
+ -- stop_tagged()
+ start_tagged("mrow")
+ process(sup)
+ stop_tagged()
+ stop_tagged()
+ else
+ process(nucleus)
+ end
+end
+
+-- todo: check function here and keep attribute the same
+
+-- todo: variants -> original
+
+local actionstack = { }
+local fencesstack = { }
+
+-- glyph nodes and such can happen in under and over stuff
+
+-- local function getunicode(n) -- instead of getchar
+-- local char = getchar(n)
+-- -- local font = getfontoffamily(getfield(n,"fam"))
+-- local font = getfont(n)
+-- local data = fontcharacters[font][char]
+-- return data.unicode or char
+-- end
+
+local function getunicode(n) -- instead of getchar
+ -- local char, font = isglyph(n) -- no, we have a mathchar
+ local char, font = getchar(n), getfont(n)
+ local data = fontcharacters[font][char]
+ return data.unicode or char -- can be a table but unlikely for math characters
+end
+
+-------------------
+
+local content = { }
+local found = false
+
+content[mathchar_code] = function() found = true end
+
+local function hascontent(head)
+ found = false
+ processnoads(head,content,"content")
+ return found
+end
+
+--------------------
+
+-- todo: use properties
+
+-- local function showtag(n,id,old)
+-- local attr = getattr(n,a_tagged)
+-- local curr = tags.current()
+-- report_tags("%s, node %s, attr %s:%s (%s), top %s (%s)",
+-- old and "before" or "after ",
+-- nodecodes[id],
+-- getattrlist(n),
+-- attr or "?",attr and taglist[attr].tagname or "?",
+-- curr or "?",curr and taglist[curr].tagname or "?"
+-- )
+-- end
+
+process = function(start) -- we cannot use the processor as we have no finalizers (yet)
+ local mtexttag = nil
+ while start do
+ local id = getid(start)
+-- showtag(start,id,true)
+ if id == glyph_code or id == disc_code then
+ if not mtexttag then
+ mtexttag = start_tagged("mtext")
+ end
+ setattr(start,a_tagged,mtexttag)
+ elseif mtexttag and id == kern_code and (getsubtype(start) == fontkern_code or getsubtype(start) == italickern_code) then -- italickern
+ setattr(start,a_tagged,mtexttag)
+ else
+ if mtexttag then
+ stop_tagged()
+ mtexttag = nil
+ end
+ if id == mathchar_code then
+ local char = getchar(start)
+ local code = getmathcodes(char)
+ local tag
+ if code == ordinary_mathcode or code == variable_mathcode then
+ local ch = chardata[char]
+ local mc = ch and ch.mathclass
+ if mc == "number" then
+ tag = "mn"
+ elseif mc == "variable" or not mc then -- variable is default
+ tag = "mi"
+ else
+ tag = "mo"
+ end
+ else
+ tag = "mo"
+ end
+ local a = getattr(start,a_mathcategory)
+ if a then
+ setattr(start,a_tagged,start_tagged(tag,{ mathcategory = a }))
+ else
+ setattr(start,a_tagged,start_tagged(tag)) -- todo: a_mathcategory
+ end
+ stop_tagged()
+ -- showtag(start,id,false)
+ break -- okay?
+ elseif id == mathtextchar_code then -- or id == glyph_code
+ -- check for code
+ local a = getattr(start,a_mathcategory)
+ if a then
+ setattr(start,a_tagged,start_tagged("ms",{ mathcategory = a })) -- mtext
+ else
+ setattr(start,a_tagged,start_tagged("ms")) -- mtext
+ end
+ stop_tagged()
+ -- showtag(start,id,false)
+ break
+ elseif id == delimiter_code then
+ -- check for code
+ setattr(start,a_tagged,start_tagged("mo"))
+ stop_tagged()
+ -- showtag(start,id,false)
+ break
+ elseif id == style_code then
+ -- has a next
+ elseif id == noad_code then
+ -- setattr(start,a_tagged,tags.current())
+ processsubsup(start)
+ elseif id == subbox_code or id == hlist_code or id == vlist_code then
+ -- keep an eye on subbox_code and see what ends up in there
+ local attr = getattr(start,a_tagged)
+ if not attr then
+ -- just skip
+ else
+ local specification = taglist[attr]
+ if specification then
+ local tag = specification.tagname
+ if tag == "formulacaption" then
+ -- skip
+ elseif tag == "mstacker" then
+ local list = getlist(start)
+ if list then
+ process(list)
+ end
+ else
+ if tag ~= "mstackertop" and tag ~= "mstackermid" and tag ~= "mstackerbot" then
+ tag = "mtext"
+ end
+ local text = start_tagged(tag)
+ setattr(start,a_tagged,text)
+ local list = getlist(start)
+ if not list then
+ -- empty list
+ elseif not attr then
+ -- box comes from strange place
+ set_attributes(list,a_tagged,text) -- only the first node ?
+ else
+ -- Beware, the first node in list is the actual list so we definitely
+ -- need to nest. This approach is a hack, maybe I'll make a proper
+ -- nesting feature to deal with this at another level. Here we just
+ -- fake structure by enforcing the inner one.
+ --
+ -- todo: have a local list with local tags that then get appended
+ --
+ local tagdata = specification.taglist
+ local common = #tagdata + 1
+ local function runner(list,depth) -- quite inefficient
+ local cache = { } -- we can have nested unboxed mess so best local to runner
+ local keep = nil
+ -- local keep = { } -- win case we might need to move keep outside
+ for n, id, subtype in nextnode, list do
+ local mth = id == math_code and subtype
+ if mth == 0 then -- hm left_code
+ -- insert(keep,text)
+ keep = text
+ text = start_tagged("mrow")
+ common = common + 1
+ end
+ local aa = getattr(n,a_tagged)
+ if aa then
+ local ac = cache[aa]
+ if not ac then
+ local tagdata = taglist[aa].taglist
+ local extra = #tagdata
+ if common <= extra then
+ for i=common,extra do
+ ac = restart_tagged(tagdata[i]) -- can be made faster
+ end
+ for i=common,extra do
+ stop_tagged() -- can be made faster
+ end
+ else
+ ac = text
+ end
+ cache[aa] = ac
+ end
+ setattr(n,a_tagged,ac)
+ else
+ setattr(n,a_tagged,text)
+ end
+ if id == hlist_code or id == vlist_code then
+ runner(getlist(n),depth+1)
+ elseif id == glyph_code then
+ -- this should not be needed
+ -- local components = getcomponents(n) -- unlikely set
+ -- if components then
+ -- runner(getcomponent,depth+1)
+ -- end
+ elseif id == disc_code then
+ -- this should not be needed
+ local pre, post, replace = getdisc(n)
+ if pre then
+ runner(pre,depth+1)
+ end
+ if post then
+ runner(post,depth+1)
+ end
+ if replace then
+ runner(replace,depth+1)
+ end
+ end
+ if mth == 1 then
+ stop_tagged()
+ -- text = remove(keep)
+ text = keep
+ common = common - 1
+ end
+ end
+ end
+ runner(list,0)
+ end
+ stop_tagged()
+ end
+ end
+ end
+ elseif id == submlist_code then -- normally a hbox
+ local list = getlist(start)
+ if list then
+ local attr = getattr(start,a_tagged)
+ local last = attr and taglist[attr]
+ if last then
+ local tag = last.tagname
+ local detail = last.detail
+ if tag == "maction" then
+ if detail == "" then
+ setattr(start,a_tagged,start_tagged("mrow"))
+ process(list)
+ stop_tagged()
+ elseif actionstack[#actionstack] == action then
+ setattr(start,a_tagged,start_tagged("mrow"))
+ process(list)
+ stop_tagged()
+ else
+ insert(actionstack,action)
+ setattr(start,a_tagged,start_tagged("mrow",{ detail = action }))
+ process(list)
+ stop_tagged()
+ remove(actionstack)
+ end
+ elseif tag == "mstacker" then -- or tag == "mstackertop" or tag == "mstackermid" or tag == "mstackerbot" then
+ -- looks like it gets processed twice
+ -- do we still end up here ?
+ setattr(start,a_tagged,restart_tagged(attr)) -- so we just reuse the attribute
+ process(list)
+ stop_tagged()
+ else
+ setattr(start,a_tagged,start_tagged("mrow"))
+ process(list)
+ stop_tagged()
+ end
+ else -- never happens, we're always document
+ setattr(start,a_tagged,start_tagged("mrow"))
+ process(list)
+ stop_tagged()
+ end
+ end
+ elseif id == fraction_code then
+ local num = getfield(start,"num")
+ local denom = getfield(start,"denom")
+ local left = getfield(start,"left")
+ local right = getfield(start,"right")
+ if left then
+ setattr(left,a_tagged,start_tagged("mo"))
+ process(left)
+ stop_tagged()
+ end
+ setattr(start,a_tagged,start_tagged("mfrac"))
+ process(num)
+ process(denom)
+ stop_tagged()
+ if right then
+ setattr(right,a_tagged,start_tagged("mo"))
+ process(right)
+ stop_tagged()
+ end
+ elseif id == choice_code then
+ local display = getfield(start,"display")
+ local text = getfield(start,"text")
+ local script = getfield(start,"script")
+ local scriptscript = getfield(start,"scriptscript")
+ if display then
+ process(display)
+ end
+ if text then
+ process(text)
+ end
+ if script then
+ process(script)
+ end
+ if scriptscript then
+ process(scriptscript)
+ end
+ elseif id == fence_code then
+ local subtype = getsubtype(start)
+ local delim = getfield(start,"delimiter")
+ if subtype == leftfence_code then
+ -- left
+ local properties = { }
+ insert(fencesstack,properties)
+ setattr(start,a_tagged,start_tagged("mfenced",properties)) -- needs checking
+ if delim then
+ start_tagged("ignore")
+ local chr = getchar(delim)
+ if chr ~= 0 then
+ properties.left = chr
+ end
+ process(delim)
+ stop_tagged()
+ end
+ start_tagged("mrow") -- begin of subsequence
+ elseif subtype == middlefence_code then
+ -- middle
+ if delim then
+ start_tagged("ignore")
+ local top = fencesstack[#fencesstack]
+ local chr = getchar(delim)
+ if chr ~= 0 then
+ local mid = top.middle
+ if mid then
+ mid[#mid+1] = chr
+ else
+ top.middle = { chr }
+ end
+ end
+ process(delim)
+ stop_tagged()
+ end
+ stop_tagged() -- end of subsequence
+ start_tagged("mrow") -- begin of subsequence
+ elseif subtype == rightfence_code then
+ local properties = remove(fencesstack)
+ if not properties then
+ report_tags("missing right fence")
+ properties = { }
+ end
+ if delim then
+ start_tagged("ignore")
+ local chr = getchar(delim)
+ if chr ~= 0 then
+ properties.right = chr
+ end
+ process(delim)
+ stop_tagged()
+ end
+ stop_tagged() -- end of subsequence
+ stop_tagged()
+ else
+ -- can't happen
+ end
+ elseif id == radical_code then
+ local left = getfield(start,"left")
+ local degree = getfield(start,"degree")
+ if left then
+ start_tagged("ignore")
+ process(left) -- root symbol, ignored
+ stop_tagged()
+ end
+ if degree and hascontent(degree) then
+ setattr(start,a_tagged,start_tagged("mroot"))
+ processsubsup(start)
+ process(degree)
+ stop_tagged()
+ else
+ setattr(start,a_tagged,start_tagged("msqrt"))
+ processsubsup(start)
+ stop_tagged()
+ end
+ elseif id == accent_code then
+ local subtype = getsubtype(start)
+ local accent = getfield(start,"accent")
+ local bot_accent = getfield(start,"bot_accent")
+ if bot_accent then
+ if accent then
+ setattr(start,a_tagged,start_tagged("munderover", {
+ accent = true,
+ top = getunicode(accent),
+ bottom = getunicode(bot_accent),
+ topfixed = subtype == fixedtopaccent_code or subtype == fixedbothaccent_code,
+ bottomfixed = subtype == fixedbottomaccent_code or subtype == fixedbothaccent_code,
+ }))
+ processsubsup(start)
+ process(bot_accent)
+ process(accent)
+ stop_tagged()
+ else
+ setattr(start,a_tagged,start_tagged("munder", {
+ accent = true,
+ bottom = getunicode(bot_accent),
+ bottomfixed = subtype == fixedbottomaccent_code or subtype == fixedbothaccent_code,
+ }))
+ processsubsup(start)
+ process(bot_accent)
+ stop_tagged()
+ end
+ elseif accent then
+ setattr(start,a_tagged,start_tagged("mover", {
+ accent = true,
+ top = getunicode(accent),
+ topfixed = subtype == fixedtopaccent_code or subtype == fixedbothaccent_code,
+ }))
+ processsubsup(start)
+ process(accent)
+ stop_tagged()
+ else
+ processsubsup(start)
+ end
+ elseif id == glue_code then
+ -- setattr(start,a_tagged,start_tagged("mspace",{ width = getwidth(start) }))
+ setattr(start,a_tagged,start_tagged("mspace"))
+ stop_tagged()
+ else
+ setattr(start,a_tagged,start_tagged("merror", { detail = nodecodes[i] }))
+ stop_tagged()
+ end
+ end
+-- showtag(start,id,false)
+ start = getnext(start)
+ end
+ if mtexttag then
+ stop_tagged()
+ end
+end
+
+function noads.handlers.tags(head,style,penalties)
+ start_tagged("math", { mode = (getattr(head,a_mathmode) == 1) and "display" or "inline" })
+ setattr(head,a_tagged,start_tagged("mrow"))
+-- showtag(head,getid(head),true)
+ process(head)
+-- showtag(head,getid(head),false)
+ stop_tagged()
+ stop_tagged()
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-toy.mkxl b/Master/texmf-dist/tex/context/base/mkxl/math-toy.mkxl
new file mode 100644
index 00000000000..510872e3b62
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-toy.mkxl
@@ -0,0 +1,54 @@
+%D \module
+%D [ file=math-toy,
+%D version=2020.08.31,
+%D title=\CONTEXT\ Math Macros,
+%D subtitle=Toy math,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Math Macros / Toy}
+
+\unprotect
+
+%D Just an example:
+%D
+%D \starttyping
+%D \enabletrackers[math.toysymbol]
+%D
+%D \definemathtoysymbol[Plus] [bin][+]
+%D \definemathtoysymbol[Minus][bin][-]
+%D \definemathtoysymbol[Equal][rel][=]
+%D
+%D $\dorecurse{199}{x_{#1} \ifodd#1\relax\Plus\else\Minus\fi} x_{200} \Equal n$ \blank
+%D \stoptyping
+
+\permanent\tolerant\protected\def\definemathtoysymbol[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or\or
+ \setuevalue{#1}{\math_toy_define_symbol_indeed{\mathcodechecked{#2}}{#3}}%
+ \fi}
+
+\protected\def\math_toy_define_symbol_traced#1#2%
+ {\normalexpanded{\discretionary class #1 %
+ {$\Ustyle\number\mathstyle \darkred #2$}%
+ {$\Ustyle\number\mathstyle \darkblue #2$}%
+ {$\Ustyle\number\mathstyle \darkgreen #2$}}}
+
+\protected\def\math_toy_define_symbol_normal#1#2%
+ {\normalexpanded{\discretionary class #1 %
+ {$\Ustyle\number\mathstyle #2$}%
+ {$\Ustyle\number\mathstyle #2$}%
+ {$\Ustyle\number\mathstyle #2$}}}
+
+\installtextracker
+ {math.toysymbol}
+ {\let\math_toy_define_symbol_indeed\math_toy_define_symbol_traced}
+ {\let\math_toy_define_symbol_indeed\math_toy_define_symbol_normal}
+
+\let\math_toy_define_symbol_indeed\math_toy_define_symbol_normal
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-vfu.lmt b/Master/texmf-dist/tex/context/base/mkxl/math-vfu.lmt
new file mode 100644
index 00000000000..6584b0158d5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-vfu.lmt
@@ -0,0 +1,1153 @@
+if not modules then modules = { } end modules ['math-vfu'] = {
+ version = 1.001,
+ comment = "companion to math-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- All these math vectors .. thanks to Aditya and Mojca they become better and
+-- better. If you have problems with math fonts or miss characters report it to the
+-- ConTeXt mailing list. Also thanks to Boguslaw for finding a couple of errors.
+-- This mechanism will eventually disappear.
+
+-- 20D6 -> 2190
+-- 20D7 -> 2192
+
+local type, next, tonumber = type, next, tonumber
+local max = math.max
+local fastcopy = table.copy
+
+local fonts, nodes, mathematics = fonts, nodes, mathematics
+
+local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end)
+local trace_timings = false trackers.register("math.timings", function(v) trace_timings = v end)
+
+local add_optional = false directives.register("math.virtual.optional",function(v) add_optional = v end)
+
+local report_virtual = logs.reporter("fonts","virtual math")
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+local chardata = characters.data
+
+local mathencodings = allocate()
+fonts.encodings.math = mathencodings -- better is then: fonts.encodings.vectors
+local vfmath = allocate()
+fonts.handlers.vf.math = vfmath
+
+local helpers = fonts.helpers
+local vfcommands = helpers.commands
+local rightcommand = vfcommands.right
+local leftcommand = vfcommands.left
+local downcommand = vfcommands.down
+local upcommand = vfcommands.up
+local push = vfcommands.push
+local pop = vfcommands.pop
+
+local shared = { }
+
+-- local back = { "slot", 1, 0x2215 }
+--
+-- local function negate(main,characters,id,size,unicode,basecode)
+-- if not characters[unicode] then
+-- local basechar = characters[basecode]
+-- if basechar then
+-- local ht, wd = basechar.height, basechar.width
+-- characters[unicode] = {
+-- width = wd,
+-- height = ht,
+-- depth = basechar.depth,
+-- italic = basechar.italic,
+-- kerns = basechar.kerns,
+-- commands = {
+-- { "slot", 1, basecode },
+-- push,
+-- downcommand[ht/5],
+-- leftcommand[wd/2],
+-- back,
+-- push,
+-- }
+-- }
+-- end
+-- end
+-- end
+--
+-- \Umathchardef\braceld="0 "1 "FF07A
+-- \Umathchardef\bracerd="0 "1 "FF07B
+-- \Umathchardef\bracelu="0 "1 "FF07C
+-- \Umathchardef\braceru="0 "1 "FF07D
+
+local function brace(main,characters,id,size,unicode,first,rule,left,right,rule,last)
+ if not characters[unicode] then
+ characters[unicode] = {
+ horiz_variants = {
+ { extender = 0, glyph = first },
+ { extender = 1, glyph = rule },
+ { extender = 0, glyph = left },
+ { extender = 0, glyph = right },
+ { extender = 1, glyph = rule },
+ { extender = 0, glyph = last },
+ }
+ }
+ end
+end
+
+local function extension(main,characters,id,size,unicode,first,middle,last)
+ local chr = characters[unicode]
+ if not chr then
+ return -- skip
+ end
+ local fw = characters[first]
+ if not fw then
+ return
+ end
+ local mw = characters[middle]
+ if not mw then
+ return
+ end
+ local lw = characters[last]
+ if not lw then
+ return
+ end
+ fw = fw.width
+ mw = mw.width
+ lw = lw.width
+ if fw == 0 then
+ fw = 1
+ end
+ if lw == 0 then
+ lw = 1
+ end
+ chr.horiz_variants = {
+ { extender = 0, glyph = first, ["end"] = fw/2, start = 0, advance = fw },
+ { extender = 1, glyph = middle, ["end"] = mw/2, start = mw/2, advance = mw },
+ { extender = 0, glyph = last, ["end"] = 0, start = lw/2, advance = lw },
+ }
+end
+
+local function parent(main,characters,id,size,unicode,first,rule,last)
+ if not characters[unicode] then
+ characters[unicode] = {
+ horiz_variants = {
+ { extender = 0, glyph = first },
+ { extender = 1, glyph = rule },
+ { extender = 0, glyph = last },
+ }
+ }
+ end
+end
+
+local step = 0.2 -- 0.1 is nicer but gives larger files
+
+local function make(main,characters,id,size,n,m)
+ local old = 0xFF000 + n
+ local c = characters[old]
+ if c then
+ local upslot = 0xFF100 + n
+ local dnslot = 0xFF200 + n
+ local uprule = 0xFF300 + m
+ local dnrule = 0xFF400 + m
+ local xu = main.parameters.x_height + 0.3*size
+ local xd = 0.3*size
+ local w = c.width or 0
+ local h = c.height or 0
+ local d = c.depth or 0
+ local thickness = h - d
+ local rulewidth = step*size -- we could use an overlap
+ local slot = { "slot", id, old }
+ local rule = { "rule", thickness, rulewidth }
+ local up = upcommand[xu]
+ local dn = downcommand[xd]
+ local ht = xu + 3*thickness
+ local dp = 0
+ if not characters[uprule] then
+ characters[uprule] = {
+ width = rulewidth,
+ height = ht,
+ depth = dp,
+ commands = { push, up, rule, pop },
+ }
+ end
+ characters[upslot] = {
+ width = w,
+ height = ht,
+ depth = dp,
+ commands = { push, up, slot, pop },
+ }
+ local ht = 0
+ local dp = xd + 3*thickness
+ if not characters[dnrule] then
+ characters[dnrule] = {
+ width = rulewidth,
+ height = ht,
+ depth = dp,
+ commands = { push, dn, rule, pop }
+ }
+ end
+ characters[dnslot] = {
+ width = w,
+ height = ht,
+ depth = dp,
+ commands = { push, dn, slot, pop },
+ }
+ end
+end
+
+local function clipped(main,characters,id,size,unicode,original) -- push/pop needed?
+ local minus = characters[original]
+ if minus then
+ local mu = size/18
+ local step = 3*mu
+ local width = minus.width
+ if width > step then
+ width = width - step
+ step = step / 2
+ else
+ width = width / 2
+ step = width
+ end
+ characters[unicode] = {
+ width = width,
+ height = minus.height,
+ depth = minus.depth,
+ commands = {
+ push,
+ leftcommand[step],
+ { "slot", id, original },
+ pop,
+ }
+ }
+ end
+end
+
+local function raise(main,characters,id,size,unicode,private,n,id_of_smaller) -- this is a real fake mess
+ local raised = fonts.hashes.characters[main.fonts[id_of_smaller].id][private] -- characters[private]
+ if raised then
+ local up = 0.85 * main.parameters.x_height
+ local slot = { "slot", id_of_smaller, private }
+ local commands = {
+ push, upcommand[up], slot,
+ }
+ for i=2,n do
+ commands[#commands+1] = slot
+ end
+ commands[#commands+1] = pop
+ characters[unicode] = {
+ width = n * raised.width,
+ height = (raised.height or 0) + up,
+ depth = (raised.depth or 0) - up,
+ italic = raised.italic,
+ commands = commands,
+ }
+ end
+end
+
+local function dots(main,characters,id,size,unicode)
+ local c = characters[0x002E]
+ if c then
+ local w = c.width
+ local h = c.height
+ local d = c.depth
+ local mu = size/18
+ local right3mu = rightcommand[3*mu]
+ local right1mu = rightcommand[1*mu]
+ local up1size = upcommand[.1*size]
+ local up4size = upcommand[.4*size]
+ local up7size = upcommand[.7*size]
+ local right2muw = rightcommand[2*mu + w]
+ local slot = { "slot", id, 0x002E }
+ if unicode == 0x22EF then
+ local c = characters[0x022C5]
+ if c then
+ local width = c.width
+ local height = c.height
+ local depth = c.depth
+ local slot = { "slot", id, 0x022C5 }
+ characters[unicode] = {
+ width = 3*width + 2*3*mu,
+ height = height,
+ depth = depth,
+ commands = {
+ push, slot, right3mu, slot, right3mu, slot, pop,
+ }
+ }
+ end
+ elseif unicode == 0x22EE then
+ -- weird height !
+ characters[unicode] = {
+ width = w,
+ height = h+(1.4)*size,
+ depth = 0,
+ commands = {
+ push, push, slot, pop, up4size, push, slot, pop, up4size, slot, pop,
+ }
+ }
+ elseif unicode == 0x22F1 then
+ characters[unicode] = {
+ width = 3*w + 6*size/18,
+ height = 1.5*size,
+ depth = 0,
+ commands = {
+ push,
+ right1mu,
+ push, up7size, slot, pop,
+ right2muw,
+ push, up4size, slot, pop,
+ right2muw,
+ push, up1size, slot, pop,
+ right1mu,
+ pop
+ }
+ }
+ elseif unicode == 0x22F0 then
+ characters[unicode] = {
+ width = 3*w + 6*size/18,
+ height = 1.5*size,
+ depth = 0,
+ commands = {
+ push,
+ right1mu,
+ push, up1size, slot, pop,
+ right2muw,
+ push, up4size, slot, pop,
+ right2muw,
+ push, up7size, slot, pop,
+ right1mu,
+ pop
+ }
+ }
+ else
+ characters[unicode] = {
+ width = 3*w + 2*3*mu,
+ height = h,
+ depth = d,
+ commands = {
+ push, slot, right3mu, slot, right3mu, slot, pop,
+ }
+ }
+ end
+ end
+end
+
+local function vertbar(main,characters,id,size,parent,scale,unicode)
+ local cp = characters[parent]
+ if cp then
+ local sc = scale * size
+ local pc = { "slot", id, parent }
+ characters[unicode] = {
+ width = cp.width,
+ height = cp.height + sc,
+ depth = cp.depth + sc,
+ next = cp.next, -- can be extensible
+ commands = {
+ push, upcommand [sc], pc, pop,
+ push, downcommand[sc], pc, pop,
+ pc,
+ },
+ }
+ cp.next = unicode
+ end
+end
+
+local function jointwo(main,characters,id,size,unicode,u1,d12,u2,what)
+ local c1 = characters[u1]
+ local c2 = characters[u2]
+ if c1 and c2 then
+ local w1 = c1.width
+ local w2 = c2.width
+ local mu = size/18
+ characters[unicode] = {
+ width = w1 + w2 - d12 * mu,
+ height = max(c1.height or 0, c2.height or 0),
+ depth = max(c1.depth or 0, c2.depth or 0),
+ commands = {
+ { "slot", id, u1 },
+ leftcommand[d12*mu],
+ { "slot", id, u2 },
+ },
+ }
+ end
+end
+
+local function jointhree(main,characters,id,size,unicode,u1,d12,u2,d23,u3)
+ local c1 = characters[u1]
+ local c2 = characters[u2]
+ local c3 = characters[u3]
+ if c1 and c2 and c3 then
+ local w1 = c1.width
+ local w2 = c2.width
+ local w3 = c3.width
+ local mu = size/18
+ characters[unicode] = {
+ width = w1 + w2 + w3 - d12*mu - d23*mu,
+ height = max(c1.height or 0, c2.height or 0, c3.height or 0),
+ depth = max(c1.depth or 0, c2.depth or 0, c3.depth or 0),
+ commands = {
+ { "slot", id, u1 },
+ leftcommand[d12*mu],
+ { "slot", id, u2 },
+ leftcommand[d23*mu],
+ { "slot", id, u3 },
+ }
+ }
+ end
+end
+
+local function stack(main,characters,id,size,unicode,u1,d12,u2)
+ local c1 = characters[u1]
+ if not c1 then
+ return
+ end
+ local c2 = characters[u2]
+ if not c2 then
+ return
+ end
+ local w1 = c1.width or 0
+ local h1 = c1.height or 0
+ local d1 = c1.depth or 0
+ local w2 = c2.width or 0
+ local h2 = c2.height or 0
+ local d2 = c2.depth or 0
+ local mu = size/18
+ characters[unicode] = {
+ width = w1,
+ height = h1 + h2 + d12,
+ depth = d1,
+ commands = {
+ { "slot", id, u1 },
+ leftcommand[w1/2 + w2/2],
+ downcommand[-h1 + d2 -d12*mu],
+ { "slot", id, u2 },
+ }
+ }
+end
+
+local function repeated(main,characters,id,size,unicode,u,n,private,fraction) -- math-fbk.lua
+ local c = characters[u]
+ if c then
+ local width = c.width
+ local italic = fraction*width -- c.italic or 0 -- larger ones have funny italics
+ local tc = { "slot", id, u }
+ local tr = leftcommand[italic] -- see hack elsewhere
+ local commands = { }
+ for i=1,n-1 do
+ commands[#commands+1] = tc
+ commands[#commands+1] = tr
+ end
+ commands[#commands+1] = tc
+ local next = c.next
+ if next then
+ repeated(main,characters,id,size,private,next,n,private+1,fraction)
+ next = private
+ end
+ characters[unicode] = {
+ width = width + (n-1)*(width-italic),
+ height = c.height,
+ depth = c.depth,
+ italic = italic,
+ commands = commands,
+ next = next,
+ }
+ end
+end
+
+local function cloned(main,characters,id,size,source,target)
+ local data = characters[source]
+ if data then
+ characters[target] = data
+ return true
+ end
+end
+
+-- we use the fact that context defines the smallest sizes first .. a real dirty and ugly hack
+
+local data_of_smaller = nil
+local size_of_smaller = 0
+
+function vfmath.addmissing(main,id,size)
+
+ local id_of_smaller = nil
+
+ if size < size_of_smaller or size_of_smaller == 0 then
+ data_of_smaller = main.fonts[id]
+ id_of_smaller = id
+ else
+ id_of_smaller = #main.fonts + 1
+ main.fonts[id_of_smaller] = data_of_smaller
+ end
+
+ -- here id is the index in fonts (normally 14 or so) and that slot points to self
+
+ local characters = main.characters
+ local shared = main.shared
+ local variables = main.goodies.mathematics and main.goodies.mathematics.variables or { }
+ local joinrelfactor = variables.joinrelfactor or 3
+
+ for i=0x7A,0x7D do
+ make(main,characters,id,size,i,1)
+ end
+
+ brace (main,characters,id,size,0x23DE,0xFF17A,0xFF301,0xFF17D,0xFF17C,0xFF301,0xFF17B)
+ brace (main,characters,id,size,0x23DF,0xFF27C,0xFF401,0xFF27B,0xFF27A,0xFF401,0xFF27D)
+
+ parent (main,characters,id,size,0x23DC,0xFF17A,0xFF301,0xFF17B)
+ parent (main,characters,id,size,0x23DD,0xFF27C,0xFF401,0xFF27D)
+
+ -- negate (main,characters,id,size,0x2260,0x003D)
+ dots (main,characters,id,size,0x2026) -- ldots
+ dots (main,characters,id,size,0x22EE) -- vdots
+ dots (main,characters,id,size,0x22EF) -- cdots
+ dots (main,characters,id,size,0x22F1) -- ddots
+ dots (main,characters,id,size,0x22F0) -- udots
+
+ vertbar (main,characters,id,size,0x0007C,0.10,0xFF601) -- big : 0.85 bodyfontsize
+ vertbar (main,characters,id,size,0xFF601,0.30,0xFF602) -- Big : 1.15 bodyfontsize
+ vertbar (main,characters,id,size,0xFF602,0.30,0xFF603) -- bigg : 1.45 bodyfontsize
+ vertbar (main,characters,id,size,0xFF603,0.30,0xFF604) -- Bigg : 1.75 bodyfontsize
+ vertbar (main,characters,id,size,0x02016,0.10,0xFF605)
+ vertbar (main,characters,id,size,0xFF605,0.30,0xFF606)
+ vertbar (main,characters,id,size,0xFF606,0.30,0xFF607)
+ vertbar (main,characters,id,size,0xFF607,0.30,0xFF608)
+
+ clipped (main,characters,id,size,0xFF501,0x0002D) -- minus
+ clipped (main,characters,id,size,0xFF502,0x02190) -- lefthead
+ clipped (main,characters,id,size,0xFF503,0x02192) -- righthead
+ clipped (main,characters,id,size,0xFF504,0xFE321) -- mapsto
+ clipped (main,characters,id,size,0xFF505,0xFE322) -- lhook
+ clipped (main,characters,id,size,0xFF506,0xFE323) -- rhook
+ clipped (main,characters,id,size,0xFF507,0xFE324) -- mapsfrom
+ clipped (main,characters,id,size,0xFF508,0x021D0) -- double lefthead
+ clipped (main,characters,id,size,0xFF509,0x021D2) -- double righthead
+ clipped (main,characters,id,size,0xFF50A,0x0003D) -- equal
+ clipped (main,characters,id,size,0xFF50B,0x0219E) -- lefttwohead
+ clipped (main,characters,id,size,0xFF50C,0x021A0) -- righttwohead
+ clipped (main,characters,id,size,0xFF50D,0xFF350) -- lr arrow combi snippet
+ clipped (main,characters,id,size,0xFF50E,0xFF351) -- lr arrow combi snippet
+ clipped (main,characters,id,size,0xFF50F,0xFF352) -- lr arrow combi snippet
+ clipped (main,characters,id,size,0xFF510,0x02261) -- equiv
+
+ extension(main,characters,id,size,0x2190,0xFF502,0xFF501,0xFF501) -- \leftarrow
+ extension(main,characters,id,size,0x2192,0xFF501,0xFF501,0xFF503) -- \rightarrow
+
+ extension(main,characters,id,size,0x002D,0xFF501,0xFF501,0xFF501) -- \rel
+ extension(main,characters,id,size,0x003D,0xFF50A,0xFF50A,0xFF50A) -- \equal
+ extension(main,characters,id,size,0x2261,0xFF510,0xFF510,0xFF510) -- \equiv
+
+ jointwo (main,characters,id,size,0x21A6,0xFE321,0,0x02192) -- \mapstochar\rightarrow
+ jointwo (main,characters,id,size,0x21A9,0x02190,joinrelfactor,0xFE323) -- \leftarrow\joinrel\rhook
+ jointwo (main,characters,id,size,0x21AA,0xFE322,joinrelfactor,0x02192) -- \lhook\joinrel\rightarrow
+ jointwo (main,characters,id,size,0x27F5,0x02190,joinrelfactor,0x0002D) -- \leftarrow\joinrel\relbar
+ jointwo (main,characters,id,size,0x27F6,0x0002D,joinrelfactor,0x02192,2) -- \relbar\joinrel\rightarrow
+ jointwo (main,characters,id,size,0x27F7,0x02190,joinrelfactor,0x02192) -- \leftarrow\joinrel\rightarrow
+ jointwo (main,characters,id,size,0x27F8,0x021D0,joinrelfactor,0x0003D) -- \Leftarrow\joinrel\Relbar
+ jointwo (main,characters,id,size,0x27F9,0x0003D,joinrelfactor,0x021D2) -- \Relbar\joinrel\Rightarrow
+ jointwo (main,characters,id,size,0x27FA,0x021D0,joinrelfactor,0x021D2) -- \Leftarrow\joinrel\Rightarrow
+ jointhree(main,characters,id,size,0x27FB,0x02190,joinrelfactor,0x0002D,0,0xFE324) -- \leftarrow\joinrel\relbar\mapsfromchar
+ jointhree(main,characters,id,size,0x27FC,0xFE321,0,0x0002D,joinrelfactor,0x02192) -- \mapstochar\relbar\joinrel\rightarrow
+
+ extension(main,characters,id,size,0x21A6,0xFF504,0xFF501,0xFF503) -- \mapstochar\rightarrow
+ extension(main,characters,id,size,0x21A9,0xFF502,0xFF501,0xFF506) -- \leftarrow\joinrel\rhook
+ extension(main,characters,id,size,0x21AA,0xFF505,0xFF501,0xFF503) -- \lhook\joinrel\rightarrow
+ extension(main,characters,id,size,0x27F5,0xFF502,0xFF501,0xFF501) -- \leftarrow\joinrel\relbar
+ extension(main,characters,id,size,0x27F6,0xFF501,0xFF501,0xFF503) -- \relbar\joinrel\rightarrow
+ extension(main,characters,id,size,0x27F7,0xFF502,0xFF501,0xFF503) -- \leftarrow\joinrel\rightarrow
+ extension(main,characters,id,size,0x27F8,0xFF508,0xFF50A,0xFF50A) -- \Leftarrow\joinrel\Relbar
+ extension(main,characters,id,size,0x27F9,0xFF50A,0xFF50A,0xFF509) -- \Relbar\joinrel\Rightarrow
+ extension(main,characters,id,size,0x27FA,0xFF508,0xFF50A,0xFF509) -- \Leftarrow\joinrel\Rightarrow
+ extension(main,characters,id,size,0x27FB,0xFF502,0xFF501,0xFF507) -- \leftarrow\joinrel\relbar\mapsfromchar
+ extension(main,characters,id,size,0x27FC,0xFF504,0xFF501,0xFF503) -- \mapstochar\relbar\joinrel\rightarrow
+
+ extension(main,characters,id,size,0x219E,0xFF50B,0xFF501,0xFF501) -- \twoheadleftarrow\joinrel\relbar
+ extension(main,characters,id,size,0x21A0,0xFF501,0xFF501,0xFF50C) -- \relbar\joinrel\twoheadrightarrow
+ extension(main,characters,id,size,0x21C4,0xFF50D,0xFF50E,0xFF50F) -- leftoverright
+
+ -- 21CB leftrightharpoon
+ -- 21CC rightleftharpoon
+
+ stack(main,characters,id,size,0x2259,0x0003D,3,0x02227) -- \buildrel\wedge\over=
+
+ jointwo(main,characters,id,size,0x22C8,0x022B3,joinrelfactor,0x022B2) -- \mathrel\triangleright\joinrel\mathrel\triangleleft (4 looks better than 3)
+ jointwo(main,characters,id,size,0x22A7,0x0007C,joinrelfactor,0x0003D) -- \mathrel|\joinrel=
+ jointwo(main,characters,id,size,0x2260,0x00338,0,0x0003D) -- \not\equal
+ jointwo(main,characters,id,size,0x2284,0x00338,0,0x02282) -- \not\subset
+ jointwo(main,characters,id,size,0x2285,0x00338,0,0x02283) -- \not\supset
+ jointwo(main,characters,id,size,0x2209,0x00338,0,0x02208) -- \not\in
+ jointwo(main,characters,id,size,0x2254,0x03A,0,0x03D) -- := (≔)
+
+ repeated(main,characters,id,size,0x222C,0x222B,2,0xFF800,1/3)
+ repeated(main,characters,id,size,0x222D,0x222B,3,0xFF810,1/3)
+
+ if cloned(main,characters,id,size,0x2032,0xFE325) then
+ raise(main,characters,id,size,0x2032,0xFE325,1,id_of_smaller) -- prime
+ raise(main,characters,id,size,0x2033,0xFE325,2,id_of_smaller) -- double prime
+ raise(main,characters,id,size,0x2034,0xFE325,3,id_of_smaller) -- triple prime
+ -- to satisfy the prime resolver
+ characters[0xFE932] = characters[0x2032]
+ characters[0xFE933] = characters[0x2033]
+ characters[0xFE934] = characters[0x2034]
+ end
+
+ -- there are more (needs discussion first):
+
+ -- characters[0x20D6] = characters[0x2190]
+ -- characters[0x20D7] = characters[0x2192]
+
+ characters[0x02B9] = characters[0x2032] -- we're nice
+
+ data_of_smaller = main.fonts[id]
+ size_of_smaller = size
+
+end
+
+local unique = 0 -- testcase: \startTEXpage \math{!\text{-}\text{-}\text{-}} \stopTEXpage
+
+local reported = { }
+local reverse = { } -- index -> unicode
+
+setmetatableindex(reverse, function(t,name)
+ if trace_virtual then
+ report_virtual("initializing math vector %a",name)
+ end
+ local m = mathencodings[name]
+ local r = { }
+ for u, i in next, m do
+ r[i] = u
+ end
+ reverse[name] = r
+ return r
+end)
+
+local function copy_glyph(main,target,original,unicode,slot)
+ local addprivate = fonts.helpers.addprivate
+ local olddata = original[unicode]
+ if olddata then
+ local newdata = {
+ width = olddata.width,
+ height = olddata.height,
+ depth = olddata.depth,
+ italic = olddata.italic,
+ kerns = olddata.kerns,
+ tounicode = olddata.tounicode,
+ commands = { { "slot", slot, unicode } },
+ }
+ local glyphdata = newdata
+ local nextglyph = olddata.next
+ while nextglyph do
+ local oldnextdata = original[nextglyph]
+ if oldnextdata then
+ local newnextdata = {
+ width = oldnextdata.width,
+ height = oldnextdata.height,
+ depth = oldnextdata.depth,
+ tounicode = olddata.tounicode,
+ commands = { { "slot", slot, nextglyph } },
+ }
+ local newnextglyph = addprivate(main,formatters["M-N-%H"](nextglyph),newnextdata)
+ newdata.next = newnextglyph
+ local nextnextglyph = oldnextdata.next
+ if nextnextglyph == nextglyph then
+ break
+ else
+ olddata = oldnextdata
+ newdata = newnextdata
+ nextglyph = nextnextglyph
+ end
+ else
+ break -- safeguard (when testing stuff)
+ end
+ end
+ local hv = olddata.horiz_variants
+ if hv then
+ hv = fastcopy(hv)
+ newdata.horiz_variants = hv
+ for i=1,#hv do
+ local hvi = hv[i]
+ local oldglyph = hvi.glyph
+ local olddata = original[oldglyph]
+ local newdata = {
+ width = olddata.width,
+ height = olddata.height,
+ depth = olddata.depth,
+ tounicode = olddata.tounicode,
+ commands = { { "slot", slot, oldglyph } },
+ }
+ hvi.glyph = addprivate(main,formatters["M-H-%H"](oldglyph),newdata)
+ end
+ end
+ local vv = olddata.vert_variants
+ if vv then
+ vv = fastcopy(vv)
+ newdata.vert_variants = vv
+ for i=1,#vv do
+ local vvi = vv[i]
+ local oldglyph = vvi.glyph
+ local olddata = original[oldglyph]
+ local newdata = {
+ width = olddata.width,
+ height = olddata.height,
+ depth = olddata.depth,
+ tounicode = olddata.tounicode,
+ commands = { { "slot", slot, oldglyph } },
+ }
+ vvi.glyph = addprivate(main,formatters["M-V-%H"](oldglyph),newdata)
+ end
+ end
+ return newdata
+ end
+end
+
+vfmath.copy_glyph = copy_glyph
+
+-- It's time to get rid of this type 1 mess ... take iwona: uppercase /A .. Z but
+-- lowercase /a.math ... /z.math ... anyway, we now follow a slightly different
+-- route: use the "order" field. I can probably make it a bit leaner but it's not
+-- worth spending much time on now.
+
+function vfmath.define(specification,set,goodies)
+ local name = specification.name -- symbolic name
+ local size = specification.size -- given size
+ local loaded = { }
+ local fontlist = { }
+ local names = { }
+ local main = nil
+ local start = (trace_virtual or trace_timings) and os.clock()
+ local okset = { }
+ local n = 0
+ for s=1,#set do
+ local ss = set[s]
+ local ssname = ss.name
+ if add_optional and ss.optional then
+ if trace_virtual then
+ report_virtual("loading font %a subfont %s with name %a at %p is skipped",name,s,ssname,size)
+ end
+ else
+ if ss.features then
+ ssname = ssname .. "*" .. ss.features
+ end
+ if ss.main then
+ main = s
+ end
+ local alreadyloaded = names[ssname] -- for px we load one twice (saves .04 sec)
+ local f, id
+ if alreadyloaded then
+ f, id = alreadyloaded.f, alreadyloaded.id
+ if trace_virtual then
+ report_virtual("loading font %a subfont %s with name %a is reused",name,s,ssname)
+ end
+ else
+ f, id = fonts.constructors.readanddefine(ssname,size)
+ names[ssname] = { f = f, id = id }
+ end
+ if not f or id == 0 then
+ report_virtual("loading font %a subfont %s with name %a at %p is skipped, not found",name,s,ssname,size)
+ else
+ n = n + 1
+ okset[n] = ss
+ loaded[n] = f
+ fontlist[n] = { id = id, size = size }
+ if not shared[s] then
+ shared[n] = { }
+ end
+ if trace_virtual then
+ report_virtual("loading font %a subfont %s with name %a at %p as id %s using encoding %a",name,s,ssname,size,id,ss.vector)
+ end
+ end
+ end
+ end
+ -- beware, loaded[1] is already passed to tex (we need to make a simple copy then .. todo)
+ local parent = loaded[1] or { } -- a text font
+ local characters = { }
+ local parameters = { }
+ local mathparameters = { }
+ local descriptions = { }
+ local metadata = { }
+ local properties = {
+ hasitalics = true,
+ hasmath = true,
+ }
+ local goodies = { }
+ local main = {
+ metadata = metadata,
+ properties = properties,
+ characters = characters,
+ descriptions = descriptions,
+ parameters = parameters,
+ mathparameters = mathparameters,
+ fonts = fontlist,
+ goodies = goodies,
+ --
+ fullname = properties.fullname,
+ nomath = false,
+ }
+ --
+ for key, value in next, parent do
+ if type(value) ~= "table" then
+ main[key] = value
+ end
+ end
+ --
+ if parent.characters then
+ for unicode, character in next, parent.characters do
+ characters[unicode] = character
+ end
+ else
+ report_virtual("font %a has no characters",name)
+ end
+ --
+ if parent.parameters then
+ for key, value in next, parent.parameters do
+ parameters[key] = value
+ end
+ else
+ report_virtual("font %a has no parameters",name)
+ end
+ --
+ local description = { name = "<unset>" }
+ setmetatableindex(descriptions,function() return description end)
+ --
+ if parent.properties then
+ setmetatableindex(properties,parent.properties)
+ end
+ --
+ if parent.goodies then
+ setmetatableindex(goodies,parent.goodies)
+ end
+ --
+ local fullname = properties.fullname -- parent via mt
+ if fullname then
+ unique = unique + 1
+ properties.fullname = fullname .. "-" .. unique
+ end
+ --
+ if not parameters.x_height then
+ parameters.x_height = 0
+ end
+ --
+ local already_reported = false
+ local parameters_done = false
+ for s=1,n do
+ local ss, fs = okset[s], loaded[s]
+ if not fs then
+ -- skip, error
+ elseif add_optional and ss.optional then
+ -- skip, redundant
+ else
+ local newparameters = fs.parameters
+ local newmathparameters = fs.mathparameters
+ if newmathparameters then
+ if not parameters_done or ss.parameters then
+ mathparameters = newmathparameters
+ parameters_done = true
+ end
+ elseif not newparameters then
+ report_virtual("no parameters set in font %a",name)
+ elseif ss.extension then
+ mathparameters.math_x_height = newparameters.x_height or 0 -- math_x_height : height of x
+ mathparameters.default_rule_thickness = newparameters[ 8] or 0 -- default_rule_thickness : thickness of \over bars
+ mathparameters.big_op_spacing1 = newparameters[ 9] or 0 -- big_op_spacing1 : minimum clearance above a displayed op
+ mathparameters.big_op_spacing2 = newparameters[10] or 0 -- big_op_spacing2 : minimum clearance below a displayed op
+ mathparameters.big_op_spacing3 = newparameters[11] or 0 -- big_op_spacing3 : minimum baselineskip above displayed op
+ mathparameters.big_op_spacing4 = newparameters[12] or 0 -- big_op_spacing4 : minimum baselineskip below displayed op
+ mathparameters.big_op_spacing5 = newparameters[13] or 0 -- big_op_spacing5 : padding above and below displayed limits
+ -- report_virtual("loading and virtualizing font %a at size %p, setting ex parameters",name,size)
+ elseif ss.parameters then
+ mathparameters.x_height = newparameters.x_height or mathparameters.x_height
+ mathparameters.x_height = mathparameters.x_height or fp.x_height or 0 -- x_height : height of x
+ mathparameters.num1 = newparameters[ 8] or 0 -- num1 : numerator shift-up in display styles
+ mathparameters.num2 = newparameters[ 9] or 0 -- num2 : numerator shift-up in non-display, non-\atop
+ mathparameters.num3 = newparameters[10] or 0 -- num3 : numerator shift-up in non-display \atop
+ mathparameters.denom1 = newparameters[11] or 0 -- denom1 : denominator shift-down in display styles
+ mathparameters.denom2 = newparameters[12] or 0 -- denom2 : denominator shift-down in non-display styles
+ mathparameters.sup1 = newparameters[13] or 0 -- sup1 : superscript shift-up in uncramped display style
+ mathparameters.sup2 = newparameters[14] or 0 -- sup2 : superscript shift-up in uncramped non-display
+ mathparameters.sup3 = newparameters[15] or 0 -- sup3 : superscript shift-up in cramped styles
+ mathparameters.sub1 = newparameters[16] or 0 -- sub1 : subscript shift-down if superscript is absent
+ mathparameters.sub2 = newparameters[17] or 0 -- sub2 : subscript shift-down if superscript is present
+ mathparameters.sup_drop = newparameters[18] or 0 -- sup_drop : superscript baseline below top of large box
+ mathparameters.sub_drop = newparameters[19] or 0 -- sub_drop : subscript baseline below bottom of large box
+ mathparameters.delim1 = newparameters[20] or 0 -- delim1 : size of \atopwithdelims delimiters in display styles
+ mathparameters.delim2 = newparameters[21] or 0 -- delim2 : size of \atopwithdelims delimiters in non-displays
+ mathparameters.axis_height = newparameters[22] or 0 -- axis_height : height of fraction lines above the baseline
+ -- report_virtual("loading and virtualizing font %a at size %p, setting sy parameters",name,size)
+ end
+ if ss.overlay then
+ local fc = fs.characters
+ local first = ss.first
+ if first then
+ local last = ss.last or first
+ for unicode = first, last do
+ characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
+ end
+ else
+ for unicode, data in next, fc do
+ characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
+ end
+ end
+ else
+ local vectorname = ss.vector
+ if vectorname then
+ local offset = 0xFF000 -- todo: -- private
+ local vector = mathencodings[vectorname]
+ local rotcev = reverse[vectorname]
+ local isextension = ss.extension
+ if vector and rotcev then
+ local fc = fs.characters
+ local fd = fs.descriptions
+ local si = shared[s]
+ local fontname = fs.properties.name or "unknown"
+ local skewchar = ss.skewchar
+ local backmap = ss.backmap
+ -- we need to know the original order because the loader has made a
+ -- unicode font of it and weird glyphnames have spoiled that a bit
+ if backmap then
+ backmap = { }
+ for unicode, character in next, fc do
+ backmap[character.order or character.index or unicode] = unicode
+ end
+ end
+ for unicode, i in next, vector do
+ -- So, here we can have an extra remapping (compared to mkiv).
+ local index = backmap and backmap[i] or i
+ local fci = fc[index]
+ if not fci then
+ local rf = reported[fontname]
+ if not rf then rf = { } reported[fontname] = rf end
+ local rv = rf[vectorname]
+ if not rv then rv = { } rf[vectorname] = rv end
+ local ru = rv[unicode]
+ if not ru then
+ if trace_virtual then
+ local d = chardata[unicode].description
+ if index then
+ report_virtual("character %C has no index %H in vector %a for font %a (%S)",unicode,index,vectorname,fontname,d)
+ else
+ report_virtual("character %C has no entry in vector %a for font %a (%S)",unicode,vectorname,fontname,d)
+ end
+ elseif not already_reported then
+ report_virtual("the mapping is incomplete for %a at %p",name,size)
+ already_reported = true
+ end
+ rv[unicode] = true
+ end
+ else
+ local ref = si[index]
+ if not ref then
+ ref = { { 'slot', s, index } }
+ si[index] = ref
+ end
+ local kerns = fci.kerns
+ local width = fci.width
+ local italic = fci.italic
+-- if trace_virtual then
+-- report_virtual("character %C uses index %H in vector %a for font %a, %s, %s",
+-- unicode,index,vectorname,fontname,
+-- kerns and "adding kerns" or "no kerns",
+-- kerns and "adding italic" or "no italic"
+-- )
+-- end
+ if italic and italic > 0 then
+ -- int_a^b
+ if isextension then
+ width = width + italic -- for obscure reasons the integral as a width + italic correction
+ end
+ end
+ if kerns then
+ local krn = { }
+ for k, v in next, kerns do -- kerns is sparse
+ local rk = rotcev[k]
+ if rk then
+ krn[rk] = v -- kerns[k]
+ end
+ end
+ if not next(krn) then
+ krn = nil
+ end
+ local t = {
+ width = width,
+ height = fci.height,
+ depth = fci.depth,
+ italic = italic,
+ kerns = krn,
+ commands = ref,
+ }
+ if skewchar then
+ local k = kerns[skewchar]
+ if k then
+ t.top_accent = width/2 + k
+ end
+ end
+ characters[unicode] = t
+ else
+ characters[unicode] = {
+ width = width,
+ height = fci.height,
+ depth = fci.depth,
+ italic = italic,
+ commands = ref,
+ }
+ end
+ end
+ end
+ if isextension then
+ -- todo: if multiple ex, then 256 offsets per instance
+ local extension = mathencodings["large-to-small"]
+ local variants_done = fs.variants_done
+ for index, fci in next, fc do -- the raw ex file
+ if type(index) == "number" then
+ local ref = si[index]
+ if not ref then
+ ref = { { 'slot', s, index } }
+ si[index] = ref
+ end
+ local italic = fci.italic
+ local t = {
+ width = fci.width,
+ height = fci.height,
+ depth = fci.depth,
+ italic = italic,
+ commands = ref,
+ }
+ local n = fci.next
+ if n then
+ t.next = offset + n
+ elseif variants_done then
+ local vv = fci.vert_variants
+ if vv then
+ t.vert_variants = vv
+ end
+ local hv = fci.horiz_variants
+ if hv then
+ t.horiz_variants = hv
+ end
+ else
+ local vv = fci.vert_variants
+ if vv then
+ for i=1,#vv do
+ local vvi = vv[i]
+ vvi.glyph = vvi.glyph + offset
+ end
+ t.vert_variants = vv
+ end
+ local hv = fci.horiz_variants
+ if hv then
+ for i=1,#hv do
+ local hvi = hv[i]
+ hvi.glyph = hvi.glyph + offset
+ end
+ t.horiz_variants = hv
+ end
+ end
+ characters[offset + index] = t
+ end
+ end
+ fs.variants_done = true
+ for unicode, index in next, extension do
+ local cu = characters[unicode]
+ if cu then
+ cu.next = offset + index
+ else
+ local fci = fc[index]
+ if not fci then
+ -- do nothing
+ else
+ -- probably never entered
+ local ref = si[index]
+ if not ref then
+ ref = { { 'slot', s, index } }
+ si[index] = ref
+ end
+ local kerns = fci.kerns
+ if kerns then
+ local krn = { }
+ -- for k=1,#kerns do
+ -- krn[offset + k] = kerns[k]
+ -- end
+ for k, v in next, kerns do -- is kerns sparse?
+ krn[offset + k] = v
+ end
+ characters[unicode] = {
+ width = fci.width,
+ height = fci.height,
+ depth = fci.depth,
+ italic = fci.italic,
+ commands = ref,
+ kerns = krn,
+ next = offset + index,
+ }
+ else
+ characters[unicode] = {
+ width = fci.width,
+ height = fci.height,
+ depth = fci.depth,
+ italic = fci.italic,
+ commands = ref,
+ next = offset + index,
+ }
+ end
+ end
+ end
+ end
+ end
+ else
+ report_virtual("error in loading %a, problematic vector %a",name,vectorname)
+ end
+ end
+ end
+ mathematics.extras.copy(main) --not needed here (yet)
+ end
+ end
+-- inspect(characters[0x1D465])
+-- inspect(fonts.encodings.math["tex-it"])
+-- inspect(fontlist)
+ --
+ main.mathparameters = mathparameters -- still traditional ones
+ -- This should change (some day) as it's the only place where we look forward,
+ -- so better is to also reserve the id already which then involves some more
+ -- management (so not now).
+ fontlist[#fontlist+1] = {
+ -- id = font.nextid(),
+ id = 0, -- self
+ size = size,
+ }
+ vfmath.addmissing(main,#fontlist,size)
+ --
+ mathematics.addfallbacks(main)
+ --
+ main.properties.math_is_scaled = true -- signal
+ fonts.constructors.assignmathparameters(main,main)
+ --
+ main.MathConstants = main.mathparameters -- we directly pass it to TeX (bypasses the scaler) so this is needed
+ --
+ if trace_virtual or trace_timings then
+ report_virtual("loading and virtualizing font %a at size %p took %0.3f seconds",name,size,os.clock()-start)
+ end
+ --
+ main.oldmath = true
+ return main
+end
+
+function mathematics.makefont(name,set,goodies)
+ fonts.definers.methods.variants[name] = function(specification)
+ return vfmath.define(specification,set,goodies)
+ end
+end
+
+-- helpers
+
+function vfmath.setletters(font_encoding, name, uppercase, lowercase)
+ local enc = font_encoding[name]
+ for i = 0,25 do
+ enc[uppercase+i] = i + 0x41
+ enc[lowercase+i] = i + 0x61
+ end
+end
+
+function vfmath.setdigits(font_encoding, name, digits)
+ local enc = font_encoding[name]
+ for i = 0,9 do
+ enc[digits+i] = i + 0x30
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-blb.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-blb.mkxl
new file mode 100644
index 00000000000..247d3a34e3f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-blb.mkxl
@@ -0,0 +1,53 @@
+%D \module
+%D [ file=meta-blb,
+%D version=2018.04.12,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Blobs,
+%D author=Hans Hagen,
+%D date=\ currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Graphics / Blobs}
+
+\registerctxluafile{meta-blb}{}
+
+\unprotect
+
+\permanent\protected\def\MPLIBgetblobscaledcm#1#2#3#4#5#6#7#8#9%
+ {\clf_mpgetblob\MPtextbox#1\relax
+ \setbox\MPbox\hpack\bgroup
+ \dotransformnextbox{#2}{#3}{#4}{#5}{#6}{#7}%
+ \vpack to \zeropoint\bgroup
+ \vss
+ \hpack to \zeropoint \bgroup
+ % \fastsxsy{#8}{#9}{\raise\dp\MPtextbox\box\MPtextbox}%
+ \fastsxsy{#8}{#9}{\box\MPtextbox}%
+ \hss
+ \egroup
+ \egroup
+ \egroup
+ \smashbox\MPbox
+ \box\MPbox}
+
+%D An example of usage:
+
+\definefontfeature[followtext][liga=no]
+
+\permanent\protected\def\MPLIBfollowtext#1#2%
+ {\begingroup
+ \scratchcounter#1\relax
+ \setbox\scratchbox\hbox{\addff{followtext}#2}%
+ \clf_MPLIBconvertfollowtext\scratchcounter\scratchbox
+ \endgroup}
+
+%D New:
+
+\newtoks\mpblobtext
+
+\permanent\protected\mpblobtext{\global\setbox\mptextbox\vbox{\clf_mpblobtext}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-fig.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-fig.mkxl
new file mode 100644
index 00000000000..b8a22f6feff
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-fig.mkxl
@@ -0,0 +1,69 @@
+%D \module
+%D [ file=meta-fig,
+%D version=2000.09.07,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Stand Alone Graphics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Graphics / Stand Alone Graphics}
+
+\unprotect
+
+%D This module implements a method for defining stand||alone||graphics, that is,
+%D each graphic gets is own page. Because graphics are wrapped in a \type {\framed},
+%D you can add overlays to the graphic directly, and since the whole \CONTEXT\
+%D machinery is available, you can also add page backgrounds.
+%D
+%D \starttyping
+%D \setupMPpage
+%D [offset=1pt,
+%D background=color,
+%D backgroundcolor=green]
+%D
+%D \startMPpage
+%D fill fullcircle scaled 10cm withcolor red ;
+%D \stopMPpage
+%D
+%D \startMPpage
+%D fill fullsquare rotated 45 scaled 8cm withcolor blue ;
+%D \stopMPpage
+%D \stoptyping
+%D
+%D Although this is hardly of any use, you can mix these definitions with the text
+%D flow, since all settings are kept local. The page is clipped to the image size.
+
+\definefittingpage
+ [MPpage]
+ [\c!align=,
+ \c!command=\meta_process_graphic_command,
+ \c!instance=]
+
+\permanent\protected\def\setupMPpage
+ {\setupfittingpage[MPpage]}
+
+\protected\def\meta_process_graphic_command
+ {\meta_process_graphic_instance{\fittingpageparameter\c!instance}}
+
+%D \macros
+%D {MPfigure}
+%D
+%D A bit out of place, here but nevertheless:
+
+\permanent\protected\def\MPfigure#1#2% test for dup figure, can be replaced by a textext
+ {\bgroup
+ \getfiguredimensions[#1]% [\c!object=\v!no] already set
+ \startMPcode
+ draw externalfigure "#1"
+ xsized \the\dimexpr\figurewidth \relax\space % must be points
+ ysized \the\dimexpr\figureheight\relax\space % must be points
+ #2 ;
+ \stopMPcode
+ \egroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-fnt.lmt b/Master/texmf-dist/tex/context/base/mkxl/meta-fnt.lmt
new file mode 100644
index 00000000000..91acd9c0268
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-fnt.lmt
@@ -0,0 +1,150 @@
+if not modules then modules = { } end modules ['meta-fnt'] = {
+ version = 1.001,
+ comment = "companion to meta-fnt.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This was one of the demo modules that never was used for real so the \MKIV\
+-- variant has been replaced by one using the newer \LMTX\ interface, but only
+-- because we want to be (sort of) compatible. Instead of doing the work at the
+-- \LUA\ end we do it at the \TEX end.
+
+metapost.fonts = metapost.fonts or { }
+
+function metapost.fonts.define(specification)
+ local fontname = specification.fontname
+ local filename = specification.filename
+ local fontsize = specification.size
+ context.definemetafont (
+ { fontname },
+ { filename },
+ { size and { "at " .. size .. " sp" } or "" }
+ )
+end
+
+-- metapost.fonts.define {
+-- fontname = "bidi",
+-- filename = "bidi-symbols.mp",
+-- }
+
+-- okay, let's for the moment put this here:
+
+local tonumber = tonumber
+local find = string.find
+
+local context = context
+
+local fastserialize = table.fastserialize
+local settings_to_hash = utilities.parsers.settings_to_hash
+local settings_to_array = utilities.parsers.settings_to_array
+
+local addcharacters = fonts.constructors.addcharacters
+local fontdata = fonts.hashes.identifiers
+
+-- This is a prelude to a more advance mechanism: when we are in mp we can construct the
+-- whole composed character there.
+
+function fonts.helpers.combineglyphs(fnt, specification)
+ local hash = fastserialize(specification)
+ local fontid = fnt or font.current()
+ local tfmdata = fontdata[fontid]
+ local combhash = tfmdata.combhash or { }
+ local unicode = combhash[hash]
+ if not unicode then
+ local t = { }
+ local h = 0
+ local d = 0
+ local w = 0
+ local o = 0
+ local characters = tfmdata.characters
+ for i=1,#specification do
+ local s = specification[i]
+ local l = find(s,"=") and settings_to_hash(s)
+ local n = tonumber(l and l.unicode or s) or tonumber(s)
+ if n then
+ local data = characters[n]
+ if data then
+ local bb = data.boundingbox
+ if bb then
+ local llx = bb[1]
+ local lly = bb[2]
+ local urx = bb[3]
+ local ury = bb[4]
+ if ury > h then
+ h = ury
+ elseif - ury > d then
+ d = - ury
+ end
+ if - lly > d then
+ d = - lly
+ elseif lly > h then
+ h = lly
+ end
+ if llx < o then
+ o = llx
+ end
+ -- could be an extension to the "offset" command
+ local c = l and l.color
+ if c then
+ t[#t+1] = { "startcolor", c }
+ end
+ t[#t+1] = { "offset", 0, 0, n }
+ if c then
+ t[#t+1] = { "stopcolor" }
+ end
+ -- t[#t+1] = { "push" }
+ -- if c then
+ -- t[#t+1] = { "startcolor", c }
+ -- end
+ -- t[#t+1] = { "right", 0 }
+ -- t[#t+1] = { "char", n }
+ -- if c then
+ -- t[#t+1] = { "stopcolor" }
+ -- end
+ -- t[#t+1] = { "pop" }
+ if urx > w then
+ w = urx
+ end
+ else
+ local ht = data.height or 0 if ht > h then h = ht end
+ local dp = data.depth or 0 if dp > d then d = dp end
+ local wd = data.width or 0 if wd > w then w = wd end
+ t[#t+1] = { "char", n }
+ end
+ end
+ end
+ end
+ for i=1,#t do
+-- if t[i][1] == "right" then
+ if t[i][1] == "offset" then
+ t[i][2] = -o
+-- t[i][2] = o
+ end
+ end
+ -- cheat one: we get a private slot
+ unicode = fonts.helpers.addprivate(tfmdata,nil,{
+ commands = t,
+ width = w - o,
+ height = h,
+ depth = d,
+ })
+ -- cheat two: we overload it later
+ addcharacters(fontid,{ characters = { [unicode] = characters[unicode] } })
+ tfmdata.combhash = combhash
+ combhash[hash] = unicode
+ end
+ return unicode
+end
+
+interfaces.implement {
+ name = "combineglyphs",
+ public = true,
+ arguments = "string",
+ actions = function(list)
+ local list = settings_to_array(list)
+ local unicode = fonts.helpers.combineglyphs(font.current(),list)
+ context(unicode)
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-fnt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-fnt.mkxl
new file mode 100644
index 00000000000..45d1db2988e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-fnt.mkxl
@@ -0,0 +1,44 @@
+%D \module
+%D [ file=meta-fnt,
+%D version=2013.09.06,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Fonts,
+%D author=Hans Hagen,
+%D date=\ currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Graphics / Fonts}
+
+\registerctxluafile{meta-fnt}{autosuffix}
+\registerctxluafile{font-mpf}{autosuffix}
+
+\unprotect
+
+%D Contrary to the \MKIV\ variant we dont' use the \LUA\ call but the \LUA\ call
+%D uses this here:
+
+\permanent\tolerant\protected\def\definemetafont[#1]#*[#2]#*[#3]%
+ {\doonlyonce{#2}{%
+ \definefontfeature[metafont-#2][metapost=#2]
+ \startMPcalculation{simplefun} input "#2" ; \stopMPcalculation
+ }%
+ \definefont[#1][Mono*metafont-#2 #3]}
+
+%D Demo:
+%D
+%D \starttyping
+%D \setupbodyfont[dejavu]
+%D \starttext
+%D \showglyphs
+%D \startTEXpage[offset=10pt]
+%D \definemetafont[MyDemoA][demo-symbols][at 10pt]\MyDemoA\char9754
+%D \definemetafont[MyDemoB][demo-symbols][at 30pt]\MyDemoB\char9754
+%D \stopTEXpage
+%D \stoptext
+%D \stoptyping
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-fun.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-fun.mkxl
new file mode 100644
index 00000000000..e30660392f1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-fun.mkxl
@@ -0,0 +1,25 @@
+%D \module
+%D [ file=meta-fun,
+%D version=2009.06.02,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Goodies,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Graphics / Goodies}
+
+\registerctxluafile{meta-fun}{}
+
+\unprotect
+
+%D Just a few handy helpers:
+
+\ifdefined\MPtoks\else \newtoks\MPtoks \fi
+\ifdefined\MPbox \else \newbox \MPbox \fi
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-grd.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-grd.mkxl
new file mode 100644
index 00000000000..009149e46ed
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-grd.mkxl
@@ -0,0 +1,105 @@
+%D \module
+%D [ file=meta-grd,
+%D version=2012.06.28,
+%D title=\METAPOST\ Graphics,
+%D subtitle=grids,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Graphics / Grids}
+
+\unprotect
+
+%D This used to be a \TEX\ method, and a rather old one too. We keep it around but
+%D in a more modern way.
+%D
+%D \startbuffer
+%D \basegrid
+%D [nx=8,ny=5,
+%D dx=.5,dy=.25,
+%D unit=cm,scale=2,factor=1,
+%D offset=1ex,xstep=2,ystep=1,
+%D align=middle,style=\tt\tx]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \placefigure
+%D {An example of a grid.}
+%D {\getbuffer}
+
+\startuseMPgraphic{meta:grd:basegrid}
+ begingroup ;
+
+ save sc, dx, dy, wd, ht, ox, oy, ax, ay ;
+
+ sc = \directdummyparameter\c!factor*\directdummyparameter\c!scale ;
+ dx = \MPdpar\directdummyparameter\c!dx*sc ;
+ dy = \MPdpar\directdummyparameter\c!dy*sc ;
+ wd = \directdummyparameter\c!nx*dx ;
+ ht = \directdummyparameter\c!ny*dy ;
+ ox = \MPdpar\directdummyparameter\c!xoffset\relax ;
+ oy = \MPdpar\directdummyparameter\c!yoffset\relax ;
+
+ if "\directdummyparameter\c!align" = "\v!middle" :
+ ax = \MPdpar\directdummyparameter\c!dx/2
+ ay = \MPdpar\directdummyparameter\c!dy/2
+ else :
+ ax = 0 ;
+ ay = 0 ;
+ fi ;
+
+ draw image (
+ for i=0 step dx until wd : draw (i,0) -- (i,ht) ; endfor ;
+ for i=0 step dy until ht : draw (0,i) -- (wd,i) ; endfor ;
+ )
+ withpen pencircle scaled \MPdpar\directdummyparameter\c!rulethickness\space
+ withcolor "\directdummyparameter\c!rulecolor" ;
+
+ draw image (
+ if \directdummyparameter\c!xstep > 0 :
+ for i=1 step \directdummyparameter\c!xstep until \directdummyparameter\c!nx :
+ draw thetextext.bot(decimal i,(i*dx-ax,oy));
+ endfor ;
+ fi ;
+ if \directdummyparameter\c!ystep > 0 :
+ for i=1 step \directdummyparameter\c!ystep until \directdummyparameter\c!ny :
+ draw thetextext.lft(decimal i,(ox,i*dy-ay)) ;
+ endfor ;
+ fi ;
+ )
+ withcolor "\directdummyparameter\c!color" ;
+
+ endgroup ;
+\stopuseMPgraphic
+
+\permanent\tolerant\protected\def\basegrid[#1]%
+ {\dontleavehmode
+ \hbox\bgroup
+ \getdummyparameters
+ [\c!nx=10,\c!ny=10,%
+ \c!dx=.5,\c!dy=.5,%
+ \c!xstep=0,\c!ystep=0,%
+ \c!unit=\s!cm,\c!scale=1,\c!factor=1,%
+ \c!offset=.25\exheight,%
+ \c!xoffset=\directdummyparameter\c!offset,%
+ \c!yoffset=\directdummyparameter\c!offset,%
+ \c!align=,%
+ \c!color=\currentcolorname,%
+ \c!rulecolor=\currentcolorname,%
+ \c!rulethickness=\linewidth,%
+ #1]%
+ \usedummystyleandcolor\c!style\c!color
+ \setexpandeddummyparameter\c!dx{\directdummyparameter\c!dx\directdummyparameter\c!unit}%
+ \setexpandeddummyparameter\c!dy{\directdummyparameter\c!dy\directdummyparameter\c!unit}%
+ \useMPgraphic{meta:grd:basegrid}%
+ \egroup}
+
+\aliased\let\grid\basegrid
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-clock.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-clock.mkxl
new file mode 100644
index 00000000000..0612113ea42
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-clock.mkxl
@@ -0,0 +1,141 @@
+%D \module
+%D [ file=meta-imp-clock,
+%D version=2021.01.21,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Demo Clock Font,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is just a demo of defining a font in \METAPOST. The rendering as well as definitions
+%D are delayed till we actually need the glyph. A crazy examples made in crazy times.
+
+\startMPcalculation{simplefun}
+
+ % the 0 in getparameterdefault makes sure we convert to a number
+
+ picture DemoClocks[];
+
+ vardef ClockCircle =
+ image(draw fullcircle scaled 10 ;)
+ enddef ;
+
+ vardef ClockPoints =
+ image (
+ for i=1 upto 12 :
+ draw (0,5) rotated ((360 * i) / 12) withpen pencircle scaled 1 ;
+ endfor ;
+ )
+ enddef ;
+
+ vardef ClockTicks =
+ image (
+ for i=1 upto 12 :
+ draw ((0,4.5)--(0,5.5)) rotated ((360 * i) / 12) ;
+ endfor ;
+ )
+ enddef ;
+
+ vardef ClockCenter =
+ image (
+ draw origin withpen pencircle scaled 1 ;
+ )
+ enddef ;
+
+ vardef ClockHour =
+ image(
+ draw (origin -- (0,4)) rotated (
+ - 360 * (getparameterdefault "mpsfont" "hour" 0) / 12
+ - 360 * (getparameterdefault "mpsfont" "minute" 0) / (60 * 12)
+
+ ) )
+ enddef ;
+
+ vardef ClockMinute =
+ image(
+ draw (origin -- (0,5)) rotated (
+ - 360 * (getparameterdefault "mpsfont" "minute" 0) / 60
+ )
+ )
+ enddef ;
+
+ vardef DemoClock =
+ composeglyph(DemoClocks) ;
+ enddef ;
+
+ lmt_registerglyphs [
+ name = "clock",
+ units = 10,
+ width = 10,
+ height = 10,
+ depth = 0,
+ ] ;
+
+ lmt_registerglyph [
+ category = "clock",
+ ] ;
+
+\stopMPcalculation
+
+% \startluacode
+% metapost.metafonts.clock = { }
+% \stopluacode
+
+\continueifinputfile{meta-imp-clock.mkxl}
+
+% \enableexperiments[fonts.compact]
+
+\nopdfcompression
+
+\setuplayout[tight]
+
+\setupbodyfont[dejavu]
+
+\definefontfeature[clock][metapost=clock,metafont=clock]
+
+\definefont[ClockFont][Serif*clock]
+
+\starttext
+
+\unexpanded\def\MyClock#1#2%
+ {\begingroup
+ \ClockFont
+ \iffontchar\font\privatecharactercode{CLOCK:#1:#2}\else
+ % We could move this out to a helper \MyClockMake{#1}{#2} but normally
+ % it's efficient enough. (If we trace a lot it matters more.)
+ \setmetaglyph {
+ category {clock}
+ name {CLOCK:#1:#2}
+ code {DemoClock}
+ hour {#1}
+ minute {#2}
+ shapes {
+ { shape {ClockCircle} color {darkred} }
+ { shape {ClockTicks} color {middlegray} }
+ % { shape {ClockBullets} color {middlegray} }
+ { shape {ClockHour} color {darkgreen} }
+ { shape {ClockMinute} color {darkblue} }
+ { shape {ClockCenter} color {middlegray} }
+ }
+ }%
+ \fi
+ \setalternate{\twodigits{#1}:\twodigits{#2}}% cut'n'paste in acrobat
+ \privatecharacter{CLOCK:#1:#2}%
+ \endgroup}
+
+\dorecurse{8}{
+ \begingroup
+ \glyphxscale \numexpr 1000+#100\relax
+ \glyphyscale \glyphxscale
+ \veryraggedright
+ \dostepwiserecurse{0}{60}{1}{
+ \MyClock{3}{##1}\hskip\zeropoint plus \onepoint\allowbreak% space
+ }
+ \blank
+ \endgroup
+}
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-demo.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-demo.mkxl
new file mode 100644
index 00000000000..b89a4771bec
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-demo.mkxl
@@ -0,0 +1,233 @@
+%D \module
+%D [ file=meta-imp-demo,
+%D version=2021.01.21,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Demo Font,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is just a demo of defining a font in \METAPOST. The rendering as well as definitions
+%D are delayed till we actually need the glyph.
+
+\startMPcalculation{simplefun}
+
+ picture DemoSnippets[];
+
+ vardef DemoCircle = image(fill fullcircle scaled 10 ;) enddef ;
+ vardef DemoCenter = image(fill fullcircle scaled 3 ;) enddef ;
+
+ vardef DemoLeft = image(fill fullcircle scaled 3 shifted (-5, 0) ;) enddef ;
+ vardef DemoRight = image(fill fullcircle scaled 3 shifted ( 5, 0) ;) enddef ;
+ vardef DemoUp = image(fill fullcircle scaled 3 shifted ( 0, 5) ;) enddef ;
+ vardef DemoDown = image(fill fullcircle scaled 3 shifted ( 0,-5) ;) enddef ;
+ vardef DemoLowerLeft = image(fill fullcircle scaled 3 shifted (-5,-5) ;) enddef ;
+ vardef DemoLowerRight = image(fill fullcircle scaled 3 shifted ( 5,-5) ;) enddef ;
+ vardef DemoUpperRight = image(fill fullcircle scaled 3 shifted ( 5, 5) ;) enddef ;
+ vardef DemoUpperLeft = image(fill fullcircle scaled 3 shifted (-5, 5) ;) enddef ;
+
+ vardef DemoGlyph =
+ composeglyph(DemoSnippets) ;
+ enddef ;
+
+ lmt_registerglyphs [
+ name = "demo",
+ units = 10,
+ width = 10,
+ height = 10,
+ depth = 0,
+ ] ;
+
+ % we need to set up some basics (signals to the backend and such) at definition
+ % time so we just force a bogus shape (zero)
+
+ lmt_registerglyph [
+ category = "demo",
+ ] ;
+
+\stopMPcalculation
+
+\startluacode
+
+metapost.metafonts.demo = {
+ [utf.byte("g")] = {
+ code = "DemoGlyph",
+ shapes = {
+ { shape = "DemoCircle", color = "gray" },
+ { shape = "DemoLeft", color = "cyan" },
+ { shape = "DemoUp", color = "magenta" },
+ { shape = "DemoCenter", color = "yellow" },
+ }
+ },
+ [utf.byte("h")] = {
+ code = "DemoGlyph",
+ shapes = {
+ { shape = "DemoCircle", color = "gray" },
+ { shape = "DemoRight", color = "red" },
+ { shape = "DemoDown", color = "green" },
+ { shape = "DemoCenter", color = "blue" },
+ }
+ },
+ [19] = {
+ code = "DemoGlyph",
+ shapes = {
+ { shape = "DemoCircle", color = "gray" },
+ { shape = "DemoUp", color = "darkgray" },
+ { shape = "DemoDown", color = "darkgray" },
+ { shape = "DemoLeft", color = "darkgray" },
+ { shape = "DemoRight", color = "darkgray" },
+ { shape = "DemoCenter", color = "darkgray" },
+ }
+ },
+ ["weirdsymbol"] = {
+ code = "DemoGlyph",
+ shapes = {
+ { shape = "DemoCircle", color = "middlegray" },
+ { shape = "DemoUp", color = "red" },
+ { shape = "DemoDown", color = "green" },
+ { shape = "DemoLeft", color = "blue" },
+ { shape = "DemoRight", color = "magenta" },
+ { shape = "DemoCenter", color = "yellow" },
+ }
+ },
+ ["weirdersymbol"] = {
+ code = "DemoGlyph",
+ shapes = {
+ -- { shape = "DemoCircle", color = "gray" },
+ { shape = "DemoCenter", color = "darkgray" },
+ { shape = "DemoLowerLeft", color = "red" },
+ { shape = "DemoLowerRight", color = "green" },
+ { shape = "DemoUpperRight", color = "blue" },
+ { shape = "DemoUpperLeft", color = "yellow" },
+ { shape = "DemoUp", color = "darkred" },
+ { shape = "DemoDown", color = "darkgreen" },
+ { shape = "DemoLeft", color = "darkblue" },
+ { shape = "DemoRight", color = "darkmagenta" },
+ }
+ }
+}
+
+\stopluacode
+
+\continueifinputfile{meta-imp-demo.mkxl}
+
+\setuplayout[tight]
+
+\setupbodyfont[dejavu]
+
+\definefontfeature[demo][metapost=demo,metafont=demo]
+
+\definefont[DemoA][Serif*demo]
+\definefont[DemoB][Serif*demo sa .5]
+\definefont[DemoC][Serif*demo sa .25]
+
+\starttext
+
+ \startbuffer
+ x\ruledhbox{gh}%
+ x\ruledhbox{\char19}%
+ x\privatecharacter{weirdsymbol}%
+ x\privatecharacter{weirdersymbol}%
+ x%
+ \stopbuffer
+
+ \startTEXpage[offset=2pt]
+ \DemoA\getbuffer\par
+ \DemoB\getbuffer\quad\DemoC\getbuffer\par
+ \stopTEXpage
+
+ {\DemoA\ctxlua{fonts.helpers.setmetaglyph {
+ category = "demo",
+ name = "whatever",
+ code = "DemoGlyph",
+ shapes = {
+ { shape = "DemoCenter", color = "darkgray" },
+ { shape = "DemoLowerLeft", color = "darkred" },
+ { shape = "DemoLowerRight", color = "darkgreen" },
+ { shape = "DemoUpperRight", color = "darkblue" },
+ { shape = "DemoUpperLeft", color = "darkyellow" },
+ { shape = "DemoUp", color = "red" },
+ { shape = "DemoDown", color = "green" },
+ { shape = "DemoLeft", color = "blue" },
+ { shape = "DemoRight", color = "magenta" },
+ }
+ }}}
+
+ {\DemoA\setmetaglyph {
+ category {demo}
+ name {forever}
+ code {DemoGlyph}
+ shapes {
+ { shape {DemoCenter} color {middlegray} }
+ { shape {DemoLowerLeft} color {darkgray} }
+ { shape {DemoLowerRight} color {darkgray} }
+ { shape {DemoUpperRight} color {darkgray} }
+ { shape {DemoUpperLeft} color {darkgray} }
+ { shape {DemoUp} color {lightgray} }
+ { shape {DemoDown} color {lightgray} }
+ { shape {DemoLeft} color {lightgray} }
+ { shape {DemoRight} color {lightgray} }
+ }
+ }}
+
+ \startTEXpage[offset=2pt]
+ \DemoA
+ \privatecharacter{whatever}
+ \privatecharacter{forever}
+ \stopTEXpage
+
+ \DemoA\setupinterlinespace \dorecurse{10}{\dorecurse{1000}{g h }\par}\page
+ \DemoB\setupinterlinespace \dorecurse{20}{\dorecurse{1000}{g h }\par}\page
+ \DemoC\setupinterlinespace \dorecurse{30}{\dorecurse{1000}{g h }\par}\page
+
+ \unexpanded\def\MyChar#1%
+ {\begingroup
+ \DemoA
+ \iffontchar\font\privatecharactercode{forever-#1}\else
+ \setmetaglyph {
+ category {demo}
+ name {forever-#1}
+ code {DemoGlyph}
+ shapes {
+ { shape {DemoCenter} color {middlegray} }
+ { shape {DemoLowerLeft} color {dark#1} }
+ { shape {DemoLowerRight} color {dark#1} }
+ { shape {DemoUpperRight} color {dark#1} }
+ { shape {DemoUpperLeft} color {dark#1} }
+ { shape {DemoUp} color {light#1} }
+ { shape {DemoDown} color {light#1} }
+ { shape {DemoLeft} color {light#1} }
+ { shape {DemoRight} color {light#1} }
+ }
+ }%
+ \fi
+ \privatecharacter{forever-#1}%
+ \endgroup}
+
+ \dontcomplain
+
+ \dorecurse{1000}{%
+ \MyChar{red}\space
+ \MyChar{green}\space
+ \MyChar{blue}\space
+ \MyChar{yellow}\space
+ }
+
+ \page
+
+ \start
+ \glyphxscale 1200
+ \glyphyscale 800
+ \dorecurse{1000}{%
+ \MyChar{red}\allowbreak
+ \MyChar{green}\allowbreak
+ \MyChar{blue}\allowbreak
+ \MyChar{yellow}\allowbreak
+ }
+ \stop
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-experiments.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-experiments.mkxl
new file mode 100644
index 00000000000..644a8fff031
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-experiments.mkxl
@@ -0,0 +1,358 @@
+%D \module
+%D [ file=meta-imp-experiments,
+%D version=2020.03.18,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Experimental Graphics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This library implements some experimental functionality that eventually
+%D might end up someplace else.
+
+\startluacode
+
+ -- I decided to play with this kind of graphics after I read an article in a Dutch
+ -- popular science journal. It was an article about virusus in the mids of the time
+ -- when covid raged, so it also was a distraction.
+
+ -- This is a typical case of first prototyping it in Lua using the 'context' command
+ -- to generate MetaPost code and when that worked okay do it the following way. The
+ -- way data is passed is a bit messy.
+
+ do
+
+ local function sum(t)
+ local n = 0
+ for i=1,#t do
+ n = n + t[i]
+ end
+ return n
+ end
+
+ local function checked(data)
+ if data then
+ local n = #data
+ for i=1,n do
+ data[i][i] = 0
+ end
+ for i=1,n do
+ local di = data[i]
+ for j=i+1,n do
+ local dj = data[j]
+ local dji = dj[i]
+ if not dji then
+ dji = 0
+ dj[i] = 0
+ end
+ di[j] = dji
+ end
+ end
+ return data
+ else
+ return { }
+ end
+ end
+
+ local data, list = nil, nil
+
+ function mp.lmt_overlap_prepare()
+ data = checked(metapost.getparameter { "data" })
+ list = { }
+ for i=1,#data do
+ list[i] = sum(data[i])
+ end
+ end
+
+ function mp.lmt_overlap_reset()
+ data = nil
+ list = nil
+ end
+
+ function mp.lmt_overlap_n()
+ return #list
+ end
+
+ function mp.lmt_overlap_data(i, j)
+ if j then
+ return data[i][j]
+ else
+ return list[i]
+ end
+ end
+
+ local injectstring = mp.inject.string
+
+ function mp.lmt_overlap_text(i, j)
+ injectstring(data[i][j] or "")
+ end
+
+ function mp.lmt_overlap_label(i)
+ local labels = metapost.getparameter { "labels" }
+ injectstring(labels and labels[i] or "")
+ end
+
+ function mp.lmt_overlap_color(i)
+ local colors = metapost.getparameter { "colors" }
+ injectstring(colors and colors[i] or "darkgray")
+ end
+
+ function mp.lmt_overlap_total()
+ return sum(list)
+ end
+
+ end
+\stopluacode
+
+\startMPextensions
+
+presetparameters "overlap" [
+ options = "paths,lines",
+ gap = 4,
+ subgap = 2,
+ offset = 8,
+ color = "darkgray",
+ alternative = "circular",
+ colors = {
+ "darkred",
+ "darkgreen",
+ "darkblue",
+ "darkyellow",
+ "darkmagenta",
+ "darkcyan"
+ },
+] ;
+
+def lmt_overlap = applyparameters "overlap" "lmt_do_overlap" enddef ;
+
+vardef lmt_do_overlap_circular =
+
+ astep := 360 / steps ;
+
+ p := fullcircle scaled steps ;
+ r := origin -- (2*steps,0) ;
+
+ start := 0 ;
+ stop := 0 ;
+ for i=1 upto n:
+ stop := start + lua.mp.lmt_overlap_data(i) ;
+ first := start ;
+ last := stop ;
+ for j=1 upto n:
+ if i <> j :
+ last := first + lua.mp.lmt_overlap_data(i,j) ;
+ a := p intersectionpoint (r rotated (first * astep + 0.1)) ; % the 0.1 is somehow needed, why
+ b := p intersectionpoint (r rotated (last * astep - 0.1)) ; % the 0.1 is somehow needed, why
+ qq[i][j] := (p cutafter b) cutbefore a ;
+ first := last + subgap ;
+ fi ;
+ endfor ;
+ start := stop + gap + (n - 1) * subgap ;
+ endfor ;
+
+ if hasoption "options" "paths" :
+
+ for i=1 upto n :
+ for j=1 upto n :
+ if i <> j :
+ q := qq[i][j] ;
+ freelabeloffset := getparameter "offset" ;
+ freelabel(lua.mp.lmt_overlap_text(i,j), point .5 along q, origin) ;
+ if i < j :
+ s := qq[j][i] ;
+ a := point length(q) of q ;
+ b := point 0 of s ;
+ c := point length(s) of s ;
+ d := point 0 of q ;
+ q := q & a .. controls origin and origin .. b & s & c .. controls origin and origin .. d -- cycle ;
+ fill q withcolor lua.mp.lmt_overlap_color(i) withtransparency (1,.8) ;
+ fi ;
+ fi ;
+ endfor ;
+ endfor ;
+
+ fi ;
+
+ if hasoption "options" "lines" :
+
+ start := 0 ;
+ stop := 0 ;
+ for i=1 upto n:
+ stop := start + lua.mp.lmt_overlap_data(i) + (n - 2) * subgap ;
+ a := p intersectionpoint (r rotated (start * astep)) ;
+ b := p intersectionpoint (r rotated (stop * astep)) ;
+ q := (p cutbefore a) cutafter b ;
+ freelabeloffset := getparameterdefault "textoffset" (4 * getparameter "offset") ;
+ freelabel(lua.mp.lmt_overlap_label(i), point .5 along q, origin) ;
+ draw q withcolor white withpen pencircle scaled 5 ;
+ draw q withcolor getparameter "color" withpen pencircle scaled 5 ;
+ start := stop + gap + subgap ;
+ endfor ;
+
+ fi ;
+
+enddef ;
+
+vardef lmt_do_overlap_linear =
+ astep := 1 ; % 1.25
+
+ p := origin -- (astep * steps,0) ;
+ r := origin -- (0,astep * steps) ;
+
+ start := 0 ;
+ stop := 0 ;
+ for i=1 upto n:
+ stop := start + lua.mp.lmt_overlap_data(i) ;
+ first := start ;
+ last := stop ;
+ for j=1 upto n:
+ if i <> j :
+ last := first + lua.mp.lmt_overlap_data(i,j) ;
+ qq[i][j] := (first * astep,0) -- (last * astep,0) ;
+ first := last + subgap ;
+ fi ;
+ endfor ;
+ start := stop + gap + (n - 1) * subgap ;
+ endfor ;
+
+ if hasoption "options" "paths" :
+
+ for i=1 upto n :
+ for j=1 upto n :
+ if i < j :
+ qq[i][j] := qq[i][j] { up } .. { down } qq[j][i] { up } .. { down} cycle ;
+ fill qq[i][j] withcolor lua.mp.lmt_overlap_color(i) withtransparency (1,.8) ;
+ fi ;
+ endfor ;
+ endfor ;
+
+ for i=1 upto n :
+ for j=1 upto n :
+ if i < j :
+ t := thelabel(lua.mp.lmt_overlap_text(i,j), (center topboundary qq[i][j]) ) ;
+ fill boundingbox t enlarged (ExHeight/2) withcolor white ;
+ draw t ;
+ fi ;
+ endfor ;
+ endfor ;
+
+ fi ;
+
+ if hasoption "options" "lines" :
+
+ start := 0 ;
+ stop := 0 ;
+ for i=1 upto n:
+ stop := start + lua.mp.lmt_overlap_data(i) + (n - 2) * subgap ;
+ q := (start * astep,0) -- (stop * astep,0) ;
+ freelabeloffset := getparameterdefault "textoffset" (4 * getparameter "offset") ;
+ label.bot(lua.mp.lmt_overlap_label(i), (point .5 along q) shifted (0,- freelabeloffset/4)) ;
+ draw q withcolor white withpen pencircle scaled 5 ;
+ draw q withcolor getparameter "color" withpen pencircle scaled 5 ;
+ start := stop + gap + subgap ;
+ endfor ;
+
+ fi ;
+
+enddef ;
+
+vardef lmt_do_overlap =
+ image (
+
+ pushparameters "overlap" ;
+
+ save p, q, r, s, qq, a, b, c, d, t, n, gap, subgap, steps, astep, start, stop, first, last ;
+ path p, q, r, s, qq[][] ;
+ pair a, b, c, d ;
+ picture t ;
+ numeric n, gap, subgap, steps, astep, start, stop, first, last ;
+ save freelabeloffset; freelabeloffset := 8 ;
+ interim linecap := butt;
+ interim linejoin := squared;
+
+ lua.mp.lmt_overlap_prepare() ;
+
+ n := lua.mp.lmt_overlap_n();
+ gap := getparameter "gap" ;
+ subgap := getparameter "subgap" ;
+ steps := lua.mp.lmt_overlap_total() + (n * gap) + n * (n - 1) * subgap ;
+
+ if ((getparameter "alternative") = "linear") or ((getparameter "alternative") = "line") :
+ lmt_do_overlap_linear ;
+ else :
+ lmt_do_overlap_circular ;
+ fi ;
+
+ lua.mp.lmt_overlap_reset() ;
+
+ popparameters ;
+ )
+enddef ;
+
+\stopMPextensions
+
+\continueifinputfile{meta-imp-experiments.mkxl}
+
+% \useMPlibrary[experiments]
+
+\usemodule[article-basic]
+
+\starttext
+
+% todo: datafile: { data = { }, labels = { } }
+
+% % data = {
+% % { 0, 10, 30, 10, 20 },
+% % { 10, 0, 10, 20, 10 },
+% % { 30, 10, 0, 5, 15 },
+% % { 10, 20, 5, 0, 5 },
+% % { 20, 10, 15, 5, 0 }
+% % },
+% % data = {
+% % { 0 },
+% % { 10, 0 },
+% % { 30, 10, 0 },
+% % { 10, 20, 5, 0 },
+% % { 20, 10, 15, 5, 0 }
+% % },
+% data = {
+% { },
+% { 10 },
+% { 30, 10 },
+% { 10, 20, 5 },
+% { 20, 10, 15, 5 }
+% },
+
+\startbuffer
+\startMPcode{doublefun}
+ draw lmt_overlap [
+ alternative = "circular",
+ data = { { }, { 10 }, { 30, 10 }, { 10, 20, 5 }, { 20, 10, 15, 5 } },
+ labels = { "one", "two", "three", "four", "five" }
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startlinecorrection[blank]
+ \getbuffer
+\stoplinecorrection
+
+\startbuffer
+\startMPcode{doublefun}
+ draw lmt_overlap [
+ alternative = "linear",
+ data = { { }, { 10 }, { 30, 10 }, { 10, 20, 5 }, { 20, 10, 15, 5 } },
+ labels = { "one", "two", "three", "four", "five" }
+ ] ;
+\stopMPcode
+\stopbuffer
+
+\startlinecorrection[blank]
+ \getbuffer
+\stoplinecorrection
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-gamesymbols.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-gamesymbols.mkxl
new file mode 100644
index 00000000000..d79e59f69f4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-gamesymbols.mkxl
@@ -0,0 +1,349 @@
+%D \module
+%D [ file=meta-imp-gamesymbols,
+%D version=2019.10.10,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Game Symbols,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The \METAFUN\ code here is not optimal and efficient but as it serves as an
+%D example I decided to keep it simple. Also, performance is not really an issue
+%D here anyway. Inspired by the classic \quote {Concrete Mathematics} by Don Knuth
+%D we define dice and dominos, which happen to be \UNICODE\ characters now. We
+%D use this opportunity to demonstrate slightly different approached.
+
+%D Each dice gets it's own macro at the \METAPOST\ end. Watch how we use the \type
+%D {simplefun} instance in combination with a calculation wrapper. This will just
+%D execute the code and leave no traces in the text flow. The code itself is not
+%D that spectacular:
+
+\startMPcalculation{simplefun}
+
+ def DiceFrame =
+ pickup pencircle scaled 1/2 ;
+ draw unitsquare scaled 8 ;
+ pickup pencircle scaled 3/2 ;
+ enddef ;
+
+ vardef DiceOne =
+ DiceFrame ;
+ draw (4,4) ;
+ enddef ;
+ vardef DiceTwoA =
+ DiceFrame ;
+ draw (2,6) ; draw (6,2) ;
+ enddef ;
+ vardef DiceTwoB =
+ DiceFrame ;
+ draw (6,6) ; draw (2,2) ;
+ enddef ;
+ vardef DiceTwo =
+ if hasoption "mpsfont" "option" "reverse" :
+ DiceTwoB
+ else :
+ DiceTwoA
+ fi ;
+ enddef ;
+ vardef DiceThreeA =
+ DiceFrame ;
+ draw (2,6) ; draw (4,4) ; draw (6,2) ;
+ enddef ;
+ vardef DiceThreeB =
+ DiceFrame ;
+ draw (6,6) ; draw (4,4) ; draw (2,2) ;
+ enddef ;
+ vardef DiceThree =
+ if hasoption "mpsfont" "option" "reverse" :
+ DiceThreeB
+ else :
+ DiceThreeA
+ fi ;
+ enddef ;
+ vardef DiceFour =
+ DiceFrame ;
+ draw (2,6) ; draw (6,6) ; draw (2,2) ; draw (6,2) ;
+ enddef ;
+ vardef DiceFive =
+ DiceFrame ;
+ draw (2,6) ; draw (6,6) ; draw (4,4) ; draw (2,2) ; draw (6,2) ;
+ enddef ;
+ vardef DiceSix =
+ DiceFrame ;
+ draw (2,6) ; draw (6,6) ; draw (2,4) ; draw (6,4) ; draw (2,2) ; draw (6,2) ;
+ enddef ;
+
+ vardef DiceBad =
+ pickup pencircle scaled 1/2 ;
+ draw unitsquare scaled 8 ;
+ draw (1,7) -- (7,1) ; draw (1,1) -- (7,7) ;
+ enddef ;
+
+ lmt_registerglyphs [
+ name = "dice",
+ units = 12,
+ width = 8,
+ height = 8,
+ depth = 0,
+ usecolor = true,
+ ] ;
+
+ lmt_registerglyph [ category = "dice", unicode = "0x2680", code = "DiceOne;" ] ;
+ lmt_registerglyph [ category = "dice", unicode = "0x2681", code = "DiceTwo;" ] ;
+ lmt_registerglyph [ category = "dice", unicode = "0x2682", code = "DiceThree;" ] ;
+ lmt_registerglyph [ category = "dice", unicode = "0x2683", code = "DiceFour;" ] ;
+ lmt_registerglyph [ category = "dice", unicode = "0x2684", code = "DiceFive;" ] ;
+ lmt_registerglyph [ category = "dice", unicode = "0x2685", code = "DiceSix;" ] ;
+
+ lmt_registerglyph [ category = "dice", private = "invaliddice", code = "DiceBad;" ] ;
+
+\stopMPcalculation
+
+%D At the \TEX\ end we define three features. The reverse is just there for fun. The
+%D digits features will map digits onto dice.
+
+\definefontfeature
+ [dice:normal]
+ [default]
+ [metapost={category=dice}]
+
+\definefontfeature
+ [dice:reverse]
+ [default]
+ [metapost={category=dice,option=reverse}]
+
+\definefontfeature
+ [dice:digits]
+ [dice:digits=yes]
+
+%D The mapping to dice happens here:
+
+\startluacode
+
+ -- local invalid = fonts.helpers.privateslot("invaliddice")
+
+ fonts.handlers.otf.addfeature("dice:digits", {
+ type = "substitution",
+ order = { "dice:digits" },
+ nocheck = true,
+ data = {
+ [0x30] = "invaliddice",
+ [0x31] = 0x2680,
+ [0x32] = 0x2681,
+ [0x33] = 0x2682,
+ [0x34] = 0x2683,
+ [0x35] = 0x2684,
+ [0x36] = 0x2685,
+ [0x37] = "invaliddice",
+ [0x38] = "invaliddice",
+ [0x39] = "invaliddice",
+ },
+ } )
+
+\stopluacode
+
+%D An example is given at the the end of this file so we now move forward with
+%D the dominos.
+
+\startMPcalculation{simplefun}
+
+ %D Because there are so many dominos we predefine the shapes and then register
+ %D the lot in a loop.
+
+ picture Dominos[] ;
+
+ Dominos[0] := image() ;
+ Dominos[1] := image(draw(4,4);) ;
+ Dominos[2] := image(draw(2,6);draw(6,2););
+ Dominos[3] := image(draw(2,6);draw(4,4);draw(6,2););
+ Dominos[4] := image(draw(2,6);draw(6,6);draw(2,2);draw(6,2););
+ Dominos[5] := image(draw(2,6);draw(6,6);draw(4,4);draw(2,2);draw(6,2););
+ Dominos[6] := image(draw(2,6);draw(4,6);draw(6,6);draw(2,2);draw(4,2);draw(6,2););
+ % Dominos[7] := Dominos[0] ;
+ % Dominos[8] := Dominos[0] ;
+ % Dominos[9] := Dominos[0] ;
+
+ %D Defining the font properties is straightforward:
+
+ lmt_registerglyphs [
+ name = "dominos",
+ units = 12,
+ width = 16,
+ height = 8,
+ depth = 0,
+ usecolor = true,
+ ] ;
+
+ %D We have horizontal dominos:
+
+ def DrawDominoH(expr a, b) =
+ draw image (
+ pickup pencircle scaled 1/2 ;
+ if (getparameterdefault "mpsfont" "color" "") = "black" :
+ fillup unitsquare xyscaled (16,8) ;
+ draw (8,.5) -- (8,7.5) withcolor white ;
+ pickup pencircle scaled 3/2 ;
+ draw Dominos[a]
+ withpen currentpen
+ withcolor white ;
+ draw Dominos[b] shifted (8,0)
+ withpen currentpen
+ withcolor white ;
+ else :
+ draw unitsquare xyscaled (16,8) ;
+ draw (8,0) -- (8,8) ;
+ pickup pencircle scaled 3/2 ;
+ draw Dominos[a]
+ withpen currentpen ;
+ draw Dominos[b] shifted (8,0)
+ withpen currentpen ;
+ fi ;
+ ) ;
+ enddef ;
+
+ %D and vertical ones. We could use the above macro and rotate and shift and reflect
+ %D but why bother with it:
+
+ def DrawDominoV(expr a, b) = % is H rotated and shifted
+ draw image (
+ pickup pencircle scaled 1/2 ;
+ if (getparameterdefault "mpsfont" "color" "") = "black" :
+ fillup unitsquare xyscaled (8,16) ;
+ draw (.5,8) -- (7.5,8) withcolor white ;
+ pickup pencircle scaled 3/2 ;
+ draw Dominos[a] rotatedaround(center Dominos[a],90)
+ withpen currentpen
+ withcolor white ;
+ draw Dominos[b] rotatedaround(center Dominos[b],90) shifted (0,8)
+ withpen currentpen
+ withcolor white ;
+ else :
+ draw unitsquare xyscaled (8,16) ;
+ draw (0,8) -- (8,8) ;
+ pickup pencircle scaled 3/2 ;
+ draw Dominos[a] rotatedaround(center Dominos[a],90)
+ withpen currentpen ;
+ draw Dominos[b] rotatedaround(center Dominos[b],90) shifted (0,8)
+ withpen currentpen ;
+ fi ;
+ ) ;
+ enddef ;
+
+ %D We have two simple loops that define the horizontal range:
+
+ save unicode ; numeric unicode ; unicode := 127025 ; % 1F031
+
+ for i=0 upto 6 :
+ for j=0 upto 6 :
+ lmt_registerglyph [
+ category = "dominos",
+ unicode = unicode,
+ code = "DrawDominoH(" & decimal i & "," & decimal j & ");",
+ width = 16,
+ height = 8,
+ ] ;
+ unicode := unicode + 1 ;
+ endfor ;
+ endfor ;
+
+ %D and the vertical range:
+
+ save unicode ; numeric unicode ; unicode := 127075 ;
+
+ for i=0 upto 6 :
+ for j=0 upto 6 :
+ lmt_registerglyph [
+ category = "dominos",
+ unicode = unicode,
+ code = "DrawDominoV(" & decimal i & "," & decimal j & ");",
+ width = 8,
+ height = 16,
+ ] ;
+ unicode := unicode + 1 ;
+ endfor ;
+ endfor ;
+
+ %D Now we're done at the \METAFUN\ end.
+
+\stopMPcalculation
+
+%D We predefine two features:
+
+\definefontfeature
+ [dominos:white]
+ [default]
+ [metapost={category=dominos}]
+
+\definefontfeature
+ [dominos:black]
+ [default]
+ [metapost={category=dominos,color=black}]
+
+\definefontfeature
+ [dominos:digits]
+ [dominos:digits=yes]
+
+%D This last feature is yet to be defined. We could deal with the invalid
+%D dominos with some substitution trickery but let's keep it simple.
+
+\startluacode
+
+ local ligatures = { }
+ local unicode = 127025
+
+ for i=0x30,0x36 do
+ for j=0x30,0x36 do
+ ligatures[unicode] = { i, j }
+ unicode = unicode + 1 ;
+ end
+ end
+
+ fonts.handlers.otf.addfeature("dominos:digits", {
+ type = "ligature",
+ order = { "dominos:digits" },
+ nocheck = true,
+ data = ligatures,
+ } )
+
+\stopluacode
+
+\continueifinputfile{meta-imp-gamesymbols.mkxl}
+
+\starttext
+
+ \definefont[DominoW][Serif*dominos:white]
+ \definefont[DominoB][Serif*dominos:black]
+ \definefont[DominoD][Serif*dominos:white,dominos:digits]
+
+ \startTEXpage[offset=3pt]
+ \DominoW
+ \char"1F043\quad 🀱\quad
+ \char"1F052\quad 🀲\quad
+ \char"1F038\quad 🀳\quad
+ \darkgreen\char"1F049\quad \char"1F07B\quad
+ \DominoB
+ \char"1F087\quad
+ \char"1F088\quad
+ \char"1F089\quad
+ \DominoD
+ \darkred 12\quad56\quad64%
+ \stopTEXpage
+
+ \definefont[DiceN][Serif*dice:normal]
+ \definefont[DiceR][Serif*dice:reverse]
+ \definefont[DiceD][Serif*dice:normal,dice:digits]
+
+ \startTEXpage[offset=3pt]
+ \DiceN
+ \dostepwiserecurse{"2680}{"2685}{1}{\char#1\quad}%
+ \DiceN
+ \dostepwiserecurse{"2680}{"2685}{1}{\char#1\quad}%
+ \DiceD
+ \darkblue 2\quad5\quad3\quad0
+ \stopTEXpage
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-punk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-punk.mkxl
new file mode 100644
index 00000000000..b03977f39b3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-punk.mkxl
@@ -0,0 +1,272 @@
+%D \module
+%D [ file=meta-imp-punk,
+%D version=2020.01.16,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Punk,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% This file is a merge of the original punk files by Donald Knuth, who
+% added this comment:
+%
+% Font inspired by Gerard and Marjan Unger's lectures, Feb 1985
+%
+% The regular punk files are part of TeXLive and in metafont format. All
+% errors introduced are ours. We also changed the encoding to unicode. In
+% due time we might add a few more more characters. We still need to
+% improve some of the metrics which involves a bit of trial and error. The
+% font just covers basic latin shapes but in ConTeXt MkIV we add virtual
+% composed shapes. There is a module m-punk.tex that implements this. This
+% derivate is also used in mk.tex (mk.pdf) which is one of our tests for
+% LuaTeX. We published an article on it in the MAPS (NTG magazine).
+%
+% 2008, mkiv variant: Taco Hoekwater & Hans Hagen
+% 2020, lmtx variant: Hans Hagen (playground)
+
+\startMPcalculation{simplefun}
+
+ picture PunkShapes[][];
+
+ numeric PunkId ; PunkId := 0 ;
+ numeric PunkBase ; PunkBase := 0 ;
+ string PunkName ; PunkName := "punk" ;
+ numeric PunkUsed ; PunkUsed := 0 ;
+ numeric PunkSlant ; PunkSlant := 0 ;
+ numeric PunkWeight ; PunkWeight := 1 ;
+ numeric PunkSqueeze ; PunkSqueeze := 1 ;
+ numeric PunkExtend ; PunkExtend := 1 ;
+ numeric PunkVariants ; PunkVariants := 0 ;
+
+ numeric PunkRatio ; PunkRatio := 2/3 ;
+ numeric PunkScale ; PunkScale := 20 ; % when zero we use the knuth values
+ numeric PunkN ; PunkN := 0 ;
+
+ def beginpunkchar(expr c, n, hor, ver) =
+ begingroup ;
+ save uc, w, h, d, hdev, vdev ;
+ hdev := hor * dev ; % modify horizontal amounts of deviation
+ vdev := ver * dev ; % modify vertical amounts of deviation
+ uc := if string c : utfnum(c) else : c fi ;
+ h := if PunkScale > 0 : PunkN else : n fi / PunkSqueeze ;
+ w := n * PunkExtend ;
+ d := 0 ;
+ clearxy ;
+ clearit ;
+ enddef ;
+
+ def endpunkchar =
+ if PunkSlant <> 0 :
+ currentpicture := currentpicture slanted PunkSlant ;
+ fi ;
+ save llx, lly, uly ;
+ llx := xpart llcorner currentpicture ;
+ lly := ypart llcorner currentpicture ;
+ uly := ypart ulcorner currentpicture ;
+
+ currentpicture := currentpicture shifted (- llx + 1, 0);
+ w := bbwidth(currentpicture) + 2 ;
+ h := uly ;
+ d := if lly < 0 : - lly else : 0 fi ;
+
+ PunkUsed := if PunkBase > 0 : PunkBase else : uc fi;
+ PunkShapes[PunkId][PunkUsed] := currentpicture ;
+ lmt_registerglyph [
+ category = PunkName,
+ unicode = PunkUsed,
+ tounicode = uc,
+ width = w,
+ height = h,
+ depth = d,
+ code = "draw PunkShapes[" & decimal PunkId & "][" & decimal PunkUsed & "]",
+ ] ;
+ if PunkBase > 0 :
+ PunkBase := PunkBase + 1 ;
+ fi ;
+ endgroup ;
+ enddef ;
+
+ def initialize_punk_upper =
+ PunkN := PunkScale ;
+ dev := 1 ;
+ enddef ;
+
+ def initialize_punk_lower =
+ PunkN := PunkScale * PunkRatio ;
+ dev := .5 ;
+ enddef ;
+
+ def revert_punk_lower =
+ PunkN := PunkScale ;
+ dev := 1 ;
+ enddef ;
+
+ def beginpunkfont =
+
+ begingroup ;
+
+ save dev;
+
+ save u ; u := 1 ;
+
+ PunkBase := 0 ;
+
+ pen linepen ; linepen := pencircle scaled (PunkWeight * u) ;
+ pen dotpen ; dotpen := linepen scaled 2 slanted -PunkSlant ;
+
+ pickup linepen ;
+
+ save pp ; vardef pp expr z =
+ z + (hdev * normaldeviate, vdev * normaldeviate)
+ enddef ;
+
+ save pd ; def pd expr z =
+ drawdot z withpen dotpen ;
+ enddef ;
+
+ PunkId := PunkId + 1 ;
+
+ lmt_registerglyphs [
+ name = PunkName, % todo: when we do name = name we get ['"name"'] = name
+ units = 25,
+ width = 25,
+ height = 25,
+ depth = 0,
+ usecolor = true,
+ ] ;
+
+ def endchar = endpunkchar enddef ;
+
+ enddef ;
+
+ def endpunkfont =
+ endgroup;
+ enddef ;
+
+ % We started out with immediate:
+
+ % def MakePunkFont (expr name, filename, slant, weight, squeeze, extend) =
+ % begingroup ;
+ % save PunkName, PunkFileName, PunkSlant, PunkWeight, PunkSqueeze, PunkExend, PunkVariants ;
+ % string PunkName, PunkFileName ;
+ % PunkFileName := filename ;
+ % PunkName := name ;
+ % PunkSlant := slant ;
+ % PunkWeight := weight ;
+ % PunkSqueeze := squeeze ;
+ % PunkExtend := extend ;
+ % PunkVariants := 0 ;
+ % beginpunkfont ;
+ % loadfile(PunkFileName) ;
+ % endpunkfont ;
+ % endgroup ;
+ % enddef ;
+ %
+ % MakePunkFont("punk", "punkfont-characters.mp", 1, 0 ) ;
+ % MakePunkFont("punkbold", "punkfont-characters.mp", 2, 0 ) ;
+ % MakePunkFont("punkslanted", "punkfont-characters.mp", 1, 0.15) ;
+ % MakePunkFont("punkboldslanted", "punkfont-characters.mp", 2, 0.15) ;
+
+ % And ended up with delayed:
+
+ def punkpreroll =
+ begingroup ;
+ save PunkName, PunkFileName, PunkSlant, PunkWeight, PunkSqueeze, PunkExtend, PunkVariants ;
+ string PunkName, PunkFileName ;
+ PunkName := getparameterdefault "mpsfont" "name" "punk" ;
+ PunkFileName := getparameterdefault "mpsfont" "filename" "punkfont-characters.mp" ;
+ PunkSlant := scantokens(getparameterdefault "mpsfont" "slant" "0") ;
+ PunkWeight := scantokens(getparameterdefault "mpsfont" "weight" "1") ;
+ PunkSqueeze := scantokens(getparameterdefault "mpsfont" "squeeze" "1") ;
+ PunkExtend := scantokens(getparameterdefault "mpsfont" "extend" "1") ;
+ PunkVariants := scantokens(getparameterdefault "mpsfont" "variants" "0");
+ beginpunkfont ;
+ loadfile(PunkFileName) ;
+ if PunkVariants > 0 :
+ PunkBase := 57344 ; % 0xE000 ; 0xF0000 ;
+ for i=1 upto PunkVariants :
+ loadfile(PunkFileName) ;
+ endfor ;
+ fi ;
+ endpunkfont ;
+ endgroup ;
+ enddef ;
+
+\stopMPcalculation
+
+% Are these names okay? If so it can go into the core.
+
+\permanent\protected\def\enablerandomvariants{\setcharactercasing[randomvariant]}
+\permanent\protected\def\randomvariants {\groupedcommand\enablerandomvariants\donothing}
+\permanent\protected\def\startrandomvariants {\begingroup\enablerandomvariants}
+\permanent\protected\def\stoprandomvariants {\endgroup}
+
+\definefontfeature [punk] [metapost={category=punk,preroll=punkpreroll}]
+\definefontfeature [punkrandom] [metapost={category=punk,preroll=punkpreroll,variants=10}]
+\definefontfeature [punkbold] [metapost={category=punk,preroll=punkpreroll,weight=2}]
+\definefontfeature [punkslanted] [metapost={category=punk,preroll=punkpreroll,slant=.15}]
+\definefontfeature [punkboldslanted] [metapost={category=punk,preroll=punkpreroll,slant=.15,weight=2}]
+\definefontfeature [punkveryslanted] [metapost={category=punk,preroll=punkpreroll,slant=-.15}]
+\definefontfeature [punksqueezed] [metapost={category=punk,preroll=punkpreroll,squeeze=.5}]
+\definefontfeature [punkextended] [metapost={category=punk,preroll=punkpreroll,extend=1.3}]
+
+\continueifinputfile{meta-imp-punk.mkxl}
+
+% immediate:
+%
+% \definefontfeature [punk] [metapost=punk]
+% \definefontfeature [punkbold] [metapost=punkbold]
+% \definefontfeature [punkslanted] [metapost=punkslanted]
+% \definefontfeature [punkboldslanted] [metapost=punkboldslanted]
+
+% delayed:
+
+% name=punk
+% filename=punkfont-characters.mp
+
+\setupbodyfont[dejavu]
+
+\definefont[PunkA][Serif*punk]
+\definefont[PunkB][Serif*punkbold]
+\definefont[PunkC][Serif*punkslanted]
+\definefont[PunkD][Serif*punkboldslanted]
+\definefont[PunkE][Serif*punkveryslanted]
+\definefont[PunkF][Serif*punkrandom]
+\definefont[PunkG][Serif*punksqueezed]
+\definefont[PunkH][Serif*punkextended]
+
+\starttext
+
+\startTEXpage[offset=10pt,width=30cm]
+ % \showglyphs
+ {\darkcyan \PunkA \samplefile{tufte} \par}
+ {\darkmagenta \PunkB \samplefile{tufte} \par}
+ {\darkred \PunkC \samplefile{tufte} \par}
+ {\darkgreen \PunkD \samplefile{tufte} \par}
+ {\darkblue \PunkE \samplefile{tufte} \par}
+ {\darkyellow \PunkF \enablerandomvariants \samplefile{tufte} \par}
+ {\darkgray \PunkG \samplefile{tufte} \par}
+ {\darkorange \PunkH \samplefile{tufte} \par}
+\stopTEXpage
+
+\startTEXpage[offset=10pt,width=20cm]
+ \PunkF \enablerandomvariants
+ \dostepwiserecurse{1}{2000}{1}{\glyphscale#1\relax f }
+\stopTEXpage
+
+% \setupbodyfont[dejavu] % one first needs to load a bodyfont !
+
+\setupbodyfont[punk]
+
+\startTEXpage[offset=10pt,width=20cm]
+ {\darkcyan \tf \samplefile{zapf} \par}
+ {\darkmagenta \bf \samplefile{zapf} \par}
+ {\darkred \sl \samplefile{zapf} \par}
+ {\darkgreen \bs \samplefile{zapf} \par}
+\stopTEXpage
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-symbols.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-symbols.mkxl
new file mode 100644
index 00000000000..f88fd6f8787
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-symbols.mkxl
@@ -0,0 +1,99 @@
+%D \module
+%D [ file=meta-imp-symbols,
+%D version=2019.10.01,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Symbols,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This is an (experimental) prelude to more.
+
+\startMPcalculation{simplefun}
+
+ pen SymbolPen ; SymbolPen := pencircle scaled 1/4 ;
+
+ vardef SymbolBullet = fill unitcircle scaled 3 shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x2022 •
+ vardef SymbolDash = draw (origin--right) scaled 3 shifted (1.5,3.0) withpen SymbolPen scaled 2 enddef ; % 0x2013 –
+ vardef SymbolTriangle = draw unittriangle scaled 3 shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x25B6 ⊳
+ vardef SymbolCircle = fill unitcircle scaled 3 shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x2218 ∘
+ vardef SymbolSquare = draw unitsquare scaled (3-1/16) shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x25A1 □
+ vardef SymbolDiamond = draw unitdiamond scaled 3 shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x22C4 ⋄
+ vardef SymbolBlackTriangle = fillup unittriangle scaled (3-1/16) shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x25B6 ▶
+ vardef SymbolBlackSquare = fillup unitsquare scaled (3-1/16) shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x25A0 ■
+ vardef SymbolBlackDiamond = fillup unitdiamond scaled (3-1/16) shifted (1.5,1.5) withpen SymbolPen enddef ; % 0x25C6 ◆
+
+ vardef SymbolNotDef =
+ draw center unitcircle
+ scaled 3
+ shifted (1.5,1.5)
+ withpen SymbolPen scaled 4
+ enddef ;
+
+ vardef SymbolStar = % 0x22C6 % ⋆
+ SymbolNotDef
+ enddef ;
+
+ vardef SymbolCheckmark = % 0x2713 ✓
+ SymbolNotDef
+ enddef ;
+
+ vardef SymbolAsterisk = % 0x2217 ∗
+ SymbolNotDef
+ enddef ;
+
+ % We could set code directly to the meaning of the macro.
+
+ lmt_registerglyphs [
+ name = "symbols",
+ units = 10,
+ usecolor = true,
+ width = 6,
+ height = 6,
+ depth = 0,
+ code = "SymbolNotDef ;",
+ ] ;
+
+ lmt_registerglyph [ category = "symbols", unicode = "0x2022", code = "SymbolBullet ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x2013", code = "SymbolDash ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x22C6" ] ; % , code = "SymbolStar ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x22B3", code = "SymbolTriangle ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x2218", code = "SymbolCircle ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x25A1", code = "SymbolSquare ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x22C4", code = "SymbolDiamond ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x2713" ] ; % , code = "SymbolCheckmark ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x2217" ] ; % , code = "SymbolAsterisk ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x25B6", code = "SymbolBlackTriangle ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x25A0", code = "SymbolBlackSquare ;" ] ;
+ lmt_registerglyph [ category = "symbols", unicode = "0x25C6", code = "SymbolBlackDiamond ;" ] ;
+
+\stopMPcalculation
+
+\continueifinputfile{meta-imp-symbols.mkxl}
+
+\usemodule[article-basic] \setupbodyfont[pagella]
+
+\starttext
+
+\definefontfeature[metasymbols][metapost=symbols]
+
+\definefont[MyFont] [Serif*default,metasymbols sa 1]
+
+Some metasymbols:
+
+\startitem {\MyFont • □ ◆}\quad Regular rendering. \stopitem
+\startitem {\MyFont\red • □ ◆}\quad Rendering with color. \stopitem
+\startitem {\MyFont\blue\showglyphs • □ ◆}\quad Idem but with boundingboxes shown. \stopitem
+
+And some more:
+
+\startlines
+ • – ⋆ ⊳ ∘ □ ⋄ ✓ ∗ ▶ ■ ◆
+ {\MyFont • – ⋆ ⊳ ∘ □ ⋄ ✓ ∗ ▶ ■ ◆}
+\stoplines
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-threesix.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-threesix.mkxl
new file mode 100644
index 00000000000..56a400b45c0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-threesix.mkxl
@@ -0,0 +1,246 @@
+%D \module
+%D [ file=meta-imp-threesix,
+%D version=2019.00.00,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Don Knuths ThreeSix font,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The design of the font is if course by Don Knuth and can be found in facs9b.ps. I
+%D wish I was clever enough to understand all the good stuff in there.
+
+\startluacode
+ local unpack = unpack
+ local concat = table.concat
+ local gsub, gmatch, utfbyte = string.gsub, string.gmatch, utf.byte
+
+ local font36 = {
+ ["0"] = [[00111100 01111110 11000011 11000011 11000011 11000011 01111110 00111100]],
+ ["1"] = [[00011100 11111100 11101100 00001100 00001100 00001100 11111111 11111111]],
+ ["2"] = [[00111110 01110011 01110011 00000011 00001110 00111000 11110001 11111111]],
+ ["3"] = [[01111110 01100110 00000110 00111100 00000110 11100111 11100111 01111110]],
+ ["4"] = [[00001110 00011110 00110110 01100110 11111111 11111111 00000110 00001110]],
+ ["5"] = [[01111110 01111110 01000000 01111100 01000111 00000011 11000111 11111110]],
+ ["6"] = [[01111110 01100110 11000000 11011100 11100110 11000011 01100011 01111110]],
+ ["7"] = [[11111111 11111111 10000111 10001110 00011100 00011100 00011100 00011100]],
+ ["8"] = [[01111110 01100110 01100110 00111100 11000011 11000011 11000011 01111110]],
+ ["9"] = [[01111110 11000110 11000011 01100111 00111011 00000011 01100110 01111110]],
+ ["A"] = [[000110000 000111000 001111100 001101100 011001110 011111110 010000110 111100111]],
+ ["B"] = [[1111100 1110110 0110110 0111100 0110011 0110011 1110011 1111100]],
+ ["C"] = [[01111101 11110011 11100001 11100001 11100000 11100000 11100001 01111110]],
+ ["D"] = [[11111100 11100010 01100011 01100011 01100011 01100011 11100010 11111100]],
+ ["E"] = [[1111111 1110001 0110101 0111100 0110100 0110001 1110001 1111111]],
+ ["F"] = [[11111111 11100001 01100101 01111100 01100100 01100000 11111000 11111000]],
+ ["G"] = [[01111010 11100110 11100010 11100000 11100111 11100010 11100010 01111100]],
+ ["H"] = [[11100111 11100111 01000010 01111110 01000010 01000010 11100111 11100111]],
+ ["I"] = [[1111111 1111111 0011000 0011000 0011000 0011000 1111111 1111111]],
+ ["J"] = [[01111111 01111111 00000110 00000110 11110110 01100110 01100110 00111100]],
+ ["K"] = [[11101110 11100100 01101000 01110000 01111000 01101100 11100110 11101111]],
+ ["L"] = [[111111000 111111000 011000000 011000000 011000000 011000011 111000011 111111111]],
+ ["M"] = [[1100000011 1110000111 0111111110 0100110010 0100110010 0100000010 1100000011 1100000011]],
+ ["N"] = [[11000011 11100011 01110010 01111010 01011110 01001110 11100110 11100010]],
+ ["O"] = [[01111110 11000011 11000011 11000011 11000011 11000011 11000011 01111110]],
+ ["P"] = [[11111110 11100011 01100011 01111110 01100000 01100000 11111000 11111000]],
+ ["Q"] = [[01111100 11000110 11000110 11000110 11000110 11001110 11000110 01111101]],
+ ["R"] = [[11111100 11100110 01100110 01111100 01101000 01100100 11100010 11100111]],
+ ["S"] = [[01111110 11100001 11100001 01111000 00011110 10000111 11000011 10111110]],
+ ["T"] = [[1111111111 1100110011 1100110011 0000110000 0000110000 0000110000 0001111000 0001111000]],
+ ["U"] = [[111101111 111101111 011000010 011000010 011000010 011000010 011000010 001111100]],
+ ["V"] = [[11111000111 11111000111 01110000010 00110000100 00111000100 00011001000 00001101000 00001110000]],
+ ["W"] = [[111000000111 111000000111 110000000010 011000000100 011001000100 001101101000 001101101000 000110110000]],
+ ["X"] = [[1111001110 1111000110 0001101000 0000110000 0000110000 0001011000 0110001111 0111001111]],
+ ["Y"] = [[111100011 111100011 011000010 001110100 000111000 000011000 001111110 001111110]],
+ ["Z"] = [[11111111 10000111 00001110 00011100 00111000 01110000 11100001 11111111]],
+ }
+
+ local f_code = string.formatters["ThreeSix(%s);"]
+ local replace = { ["0"] = "N;", ["1"] = "Y;", [" "] = "L;" }
+
+ local function remap(str)
+ -- permit abundant spacing (bonus)
+ str = gsub(str,"%s+", " ")
+ -- remap what we got to macro calls
+ str = gsub(str,".",replace)
+ -- return the result
+ return str
+ end
+
+ function MP.registerthreesix(name)
+ fonts.dropins.registerglyphs {
+ name = name,
+ units = 12,
+ usecolor = true,
+ preamble = "InitializeThreeSix;",
+ }
+ for u, v in table.sortedhash(font36) do
+ local data = remap(v)
+ local ny = 8
+ local nx = ((#v + 1) // ny) - 1
+ local height = ny * 1.1 - 0.1
+ local width = nx * 1.1 - 0.1
+ fonts.dropins.registerglyph {
+ category = name,
+ unicode = utfbyte(u),
+ width = width,
+ height = height,
+ code = f_code(data),
+ }
+ end
+ end
+ MP.registerthreesix("fontthreesix")
+\stopluacode
+
+\startMPcalculation{simplefun}
+ def InitializeThreeSix =
+ save Y, N, L, S ;
+ save shape, fillcolor, mypen, random, threesixpen, spread, hoffset ;
+ string shape, fillcolor, mypen ; boolean random ; pen threesixpen ;
+ shape := getparameterdefault "mpsfont" "shape" "circle" ;
+ random := hasoption "mpsfont" "random" "true" ;
+ fillcolor := getparameterdefault "mpsfont" "color" "" ;
+ mypen := getparameterdefault "mpsfont" "pen" "" ;
+ spread := getparameterdefault "mpsfont" "spread" 0 ;
+ hoffset := 12 * spread / 2 ;
+ threesixpen := pencircle
+ if mypen = "fancy" :
+ xscaled 1/20 yscaled 2/20 rotated 45
+ else :
+ scaled 1/20
+ fi ;
+ if shape == "square" :
+ def S =
+ unitsquare if random : randomized 1/10 fi
+ shifted (nx,ny)
+ enddef ;
+ elseif shape = "diamond" :
+ def S =
+ unitdiamond if random : randomized 1/10 fi
+ shifted (nx,ny)
+ enddef ;
+ else :
+ def S =
+ unitcircle if random : randomizedcontrols 1/20 fi
+ shifted (nx,ny)
+ enddef ;
+ fi ;
+ if fillcolor = "" :
+ def N =
+ nx := nx + dx ;
+ draw S ;
+ enddef ;
+ else :
+ def N =
+ nx := nx + dx ;
+ draw S withcolor "lightgray" ;
+ enddef ;
+ fi ;
+ if fillcolor = "random" :
+ def Y =
+ nx := nx + dx ;
+ fillup S withcolor white randomized (2/3,2/3,2/3) ;
+ enddef ;
+ elseif fillcolor = "" :
+ def Y =
+ nx := nx + dx ;
+ fillup S ;
+ enddef ;
+ else :
+ def Y =
+ nx := nx + dx ;
+ fillup S withcolor fillcolor ;
+ enddef ;
+ fi ;
+ def L =
+ nx := - dx ;
+ ny := ny + dy ;
+ enddef ;
+ enddef ;
+
+ vardef ThreeSix (text code) =
+ save dx, dy, nx, ny ;
+ dx := 11/10 ;
+ dy := - 11/10 ;
+ nx := - dx ;
+ ny := 0 ;
+ pickup threesixpen ;
+ draw image (code) shifted (hoffset,-ny) ;
+ enddef ;
+\stopMPcalculation
+
+\definefontfeature % black and white, with some spread
+ [fontthreesix]
+ [default]
+ [metapost=fontthreesix]
+
+\definefontfeature % color, with some spread
+ [fontthreesix-tweak]
+ [default]
+ [metapost={category=fontthreesix,spread=.1}]
+
+\definefontfeature % color, with some spread
+ [fontthreesix-color]
+ [default]
+ [metapost={category=fontthreesix,shape=diamond,color=random,pen=fancy,spread=.1}]
+
+\definefontfeature % color, tight
+ [fontthreesix-initial]
+ [metapost={category=fontthreesix,color=random,shape=circle}] % units?
+
+\definefont[DEKFontA][Serif*fontthreesix]
+\definefont[DEKFontB][Serif*fontthreesix-color]
+\definefont[DEKFontC][Serif*fontthreesix-initial]
+\definefont[DEKFontD][Serif*fontthreesix-tweak]
+
+\continueifinputfile{meta-imp-threesix.mkxl}
+
+\starttext
+
+\definefontfeature % color, with some spread
+ [fontthreesix-color]
+ [default]
+ [metapost={category=fontthreesix,shape=diamond,color=random,pen=fancy,spread=.1,random=yes}]
+
+\definefont[DEKFontA][Serif*fontthreesix-color @ 200pt]
+\definefont[DEKFontB][Serif*fontthreesix-color @ 60pt]
+
+% Or course:
+
+\startTEXpage
+ \DEKFontA TEX
+\stopTEXpage
+
+\startTEXpage
+ \DEKFontA T\kern-0.05em\lower.75\exheight\hbox{E}\kern-.05emX
+\stopTEXpage
+
+% One of the \CONTEXT\ 2020 posters (inspired by NewYork State AC's press
+% conferences as these keywords apply quite well to the audience of the
+% meeting):
+
+% \startTEXpage[align={lohi,middle,nothyphenated},offset=20pt]
+% \offinterlineskip \vskip10pt
+% \DEKFontA T\kern-0.05em\lower.75\exheight\hbox{E}\kern-.05emX \vskip60pt
+% \DEKFontB TOUGH \vskip20pt
+% SMART \vskip20pt
+% UNITED \vskip20pt
+% DISCIPLINED \vskip20pt
+% LOVING \vskip10pt
+% \stopTEXpage
+
+\startpagemakeup
+ \setupalign[middle,nothyphenated]
+ \offinterlineskip \vfil\vfil
+ \DEKFontA T\kern-0.05em\lower.75\exheight\hbox{E}\kern-.05emX \vfil\vfil\vfil
+ \DEKFontB TOUGH \vfil
+ SMART \vfil
+ UNITED \vfil
+ DISCIPLINED \vfil
+ LOVING \vfil
+\stoppagemakeup
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-imp-txt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-txt.mkxl
new file mode 100644
index 00000000000..c7586264ec3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-imp-txt.mkxl
@@ -0,0 +1,277 @@
+%D \module
+%D [ file=meta-txt,
+%D version=2000.07.06,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Text Tricks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D See comments in the \MKIV\ module. This is an upgraded version for \LMTX,
+%D where we can so some things nicer.
+
+\unprotect
+
+%D Text in shapes. Here we use the option to prune a parshape when a new paragraph
+%D is seen. It's an experimental feature so I need to keep an eye on how it evolves.
+%D This time we combine \TEX, \METAPOST\ and \LUA, so we do't need a temporary file
+%D to communicate from \METAPOST\ to \TEX. We just store information in \LUA\ tables.
+
+\ifdefined\startshapetext \else
+ \writestatus{system}{something is wrong here}
+\fi
+
+%D Following:
+
+% \doifundefined{RotFont}{\definefont[RotFont][RegularBold*default]}
+%
+% \unexpanded\def\getfollowtoken#1%
+% {\hbox\bgroup
+% \strut
+% \ctxlua{mp.follow_text(#1)}%
+% \egroup}
+
+\definefontfeature[mp:tp][liga=no]
+
+% if unknown RotPath : path RotPath ; RotPath := origin ; fi ;
+% if unknown TraceRot : boolean TraceRot ; TraceRot := false ; fi ;
+% if unknown ExtraRot : numeric ExtraRot ; ExtraRot := 0 ; fi ;
+% if al < len[n]:
+% RotPath := RotPath scaled ((len[n]+ExtraRot)/al) ;
+% al := arclength RotPath ;
+% fi ;
+% if TraceRot :
+% draw RotPath withpen pencircle scaled 1pt withcolor blue ;
+% fi ;
+% if TraceRot :
+% draw boundingbox currentpicture withpen pencircle scaled .25pt withcolor blue ;
+% fi ;
+
+\protected\def\dofollowtokens#1#2%
+ {\dontleavehmode
+ \vpack\bgroup
+ \forgetall
+ \dontcomplain
+ \addff{mp:tp}%
+ \startMPcode
+ \includeMPgraphic{followtokens}
+ draw lmt_followtext [ path = RotPath, text = "#2", spread = #1 ] ;
+ \stopMPcode
+ \egroup}
+
+\protected\def\followtokens {\dofollowtokens{true}}
+\protected\def\followtokenscentered{\dofollowtokens{false}}
+
+%D Fuzzy counters:
+
+\startuseMPgraphic{fuzzycount}
+ begingroup
+ save height, span, drift, d, cp ;
+ height := 3/ 5 * LineHeight ;
+ span := 1/ 3 * height ;
+ drift := 1/10 * height ;
+ pickup pencircle scaled (1/12 * height) ;
+ def d = (uniformdeviate drift) enddef ;
+ for i := 1 upto \MPvar{n} :
+ draw
+ if (i mod 5)=0 : ((-d - 4.5span,d) -- (d -0.5span,height - d))
+ else : ((-d, d) -- (d, height - d)) fi
+ shifted (span*i,d-drift) ;
+ endfor;
+ picture cp ; cp := currentpicture ; % for readability
+ setbounds currentpicture to
+ (llcorner cp shifted (0,-ypart llcorner cp) --
+ lrcorner cp shifted (0,-ypart lrcorner cp) --
+ urcorner cp --
+ ulcorner cp -- cycle) ;
+ endgroup ;
+\stopuseMPgraphic
+
+\setupMPvariables
+ [fuzzycount]
+ [n=10]
+
+\protected\def\fuzzycount#1%
+ {\dontleavehmode\bgroup
+ \tx
+ \useMPgraphic{fuzzycount}{n=#1}%
+ \egroup}
+
+\defineconversion[fuzzy][\fuzzycount]
+
+%D English rules:
+
+\setupMPvariables
+ [EnglishRule]
+ [height=1ex,
+ width=\localhsize,
+ color=darkgray]
+
+\defineblank
+ [EnglishRule]
+ [medium]
+
+\startuniqueMPgraphic{EnglishRule}{height,width,color}
+ x1 = 0 ; x3 = \MPvar{width} ; x2 = x4 = .5x3 ;
+ y1 = y3 = 0 ; y2 = -y4 = \MPvar{height} / 2 ;
+ fill z1 .. z2 .. z3 & z3 .. z4 .. z1 & cycle withcolor \MPvar{color} ;
+\stopuniqueMPgraphic
+
+\protected\def\EnglishRule
+ {\startlinecorrection[EnglishRule]%
+ \setlocalhsize
+ \noindentation
+ \reuseMPgraphic{EnglishRule}%
+ \stoplinecorrection}
+
+%D Tight text (for old times sake): the following macro returns a tight bound
+%D character sequence.
+%D
+%D \useMPlibrary[txt]
+%D
+%D \startlinecorrection
+%D \TightText{\ss\bf 123}{0cm}{3cm}{red}
+%D \stoplinecorrection
+
+\protected\def\TightText#1#2#3#4%
+ {\hpack
+ {\startMPcode
+ picture p ; p := image (graphictext "#1" withfillcolor red) ;
+ draw p xsized #2 ysized #3 withcolor \MPcolor{#4} ;
+ \stopMPcode}}
+
+\protected\def\TightText#1#2#3#4%
+ {\dontleavehmode
+ \startMPcode
+ draw outlinetext.f ("#1") (\iftok{#4}\emptytoks \else withcolor \MPcolor{#4} \fi)
+ \iftok{#2}\emptytoks \else xsized #2 \fi
+ \iftok{#3}\emptytoks \else ysized #3 \fi
+ ;
+ \stopMPcode}
+
+\protect
+
+\continueifinputfile{meta-imp-txt.mkxl}
+
+\setupbodyfont[pagella,10pt]
+
+% \useMPlibrary[txt]
+
+\starttext
+
+%D Shapes:
+
+\startuseMPgraphic{test 1}
+ path p ; p := fullcircle scaled 6cm ;
+
+ build_parshape(p,BodyFontSize/2,0,0,LineHeight,StrutHeight,StrutDepth,StrutHeight) ;
+
+ draw p withpen pencircle scaled 1pt ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{test 2}
+ path p ; p := fullsquare rotated 45 scaled 5cm ;
+
+ build_parshape(p,BodyFontSize/2,0,0,LineHeight,StrutHeight,StrutDepth,StrutHeight) ;
+
+ draw p withpen pencircle scaled 1pt ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{test 3}
+ numeric w, h ; w := h := 6cm ;
+ path p ; p := (.5w,h) -- (0,h) -- (0,0) -- (w,0) &
+ (w,0) .. (.75w,.5h) .. (w,h) & (w,h) -- cycle ;
+
+ build_parshape(p,BodyFontSize/2,0,0,LineHeight,StrutHeight,StrutDepth,StrutHeight) ;
+
+ draw p withpen pencircle scaled 1pt ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{test 4}
+ numeric w, h, o, d ;
+
+ def shape = (o,o) -- (w-o,o) & (w-o,o) .. (.75w-o,.5h) ..
+ (w-2o,h-o) & (w-2o,h-o) -- (o,h-o) -- cycle
+ enddef ;
+
+ d := BodyFontSize/2;
+
+ w := h := 6cm ; o := d ; path p ; p := shape ;
+ w := h := 6cm ; o := 0 ; path q ; q := shape ;
+
+ build_parshape(p,q,d,d,LineHeight,StrutHeight,StrutDepth,StrutHeight) ;
+
+ draw q withpen pencircle scaled 1pt ;
+\stopuseMPgraphic
+
+\defineoverlay[test 1][\useMPgraphic{test 1}]
+\defineoverlay[test 2][\useMPgraphic{test 2}]
+\defineoverlay[test 3][\useMPgraphic{test 3}]
+\defineoverlay[test 4][\useMPgraphic{test 4}]
+
+\startbuffer
+\startshapetext[test 1,test 2,test 3,test 4]
+ \setupalign[verytolerant,stretch,normal]%
+ \samplefile{douglas} % Douglas R. Hofstadter
+\stopshapetext
+\startTEXpage[offset=10pt]
+ \startcombination[2*2]
+ {\framed[offset=overlay,frame=off,background=test 1]{\getshapetext}} {test 1}
+ {\framed[offset=overlay,frame=off,background=test 2]{\getshapetext}} {test 2}
+ {\framed[offset=overlay,frame=off,background=test 3]{\getshapetext}} {test 3}
+ {\framed[offset=overlay,frame=off,background=test 4]{\getshapetext}} {test 4}
+ \stopcombination
+\stopTEXpage
+\stopbuffer
+
+\getbuffer
+
+\startMPextensions
+ boolean trace_parshape ; trace_parshape := true ;
+\stopMPextensions
+
+\getbuffer
+
+%D Fuzzycount (maybe handy):
+
+\startitemize[fuzzy,nostopper]
+ \startitem This is real fuzzy. \stopitem
+ \startitem But it can be even more fuzzier. \stopitem
+ \startitem And how about this. \stopitem
+\stopitemize
+
+\EnglishRule
+
+\startitemize[continue,broad]
+ \startitem This is real fuzzy. \stopitem
+ \startitem But it can be even more fuzzier. \stopitem
+ \startitem And how about this. \stopitem
+\stopitemize
+
+%D Tight text (manual stuff):
+
+\ruledhbox{\TightText{oeps wat doet dit}{10cm}{1cm}{red}} \blank
+\ruledhbox{\TightText{oeps wat doet dit}{10cm}{} {green}} \blank
+\ruledhbox{\TightText{oeps wat doet dit}{} {1cm}{blue}} \blank
+\ruledhbox{\TightText{oeps wat doet dit}{} {} {}} \blank
+
+%D Old fashioned:
+
+\startuseMPgraphic{followtokens}
+ path RotPath ; RotPath := reverse halfcircle xyscaled 3cm ;
+ draw RotPath ;
+\stopuseMPgraphic
+
+\startTEXpage
+ \followtokens{{\hskip1em}some text{\hskip1em}}
+\stopTEXpage
+
+\startTEXpage
+ \followtokenscentered{{\hskip1em}some text{\hskip1em}}
+\stopTEXpage
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/meta-ini.lmt
new file mode 100644
index 00000000000..ed7b2264f5d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-ini.lmt
@@ -0,0 +1,161 @@
+if not modules then modules = { } end modules ['meta-ini'] = {
+ version = 1.001,
+ comment = "companion to meta-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tonumber = tonumber
+local format = string.format
+local concat = table.concat
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local P, Cs, R, S, C, Cc = lpeg.P, lpeg.Cs, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc
+
+metapost = metapost or { }
+local metapost = metapost
+local context = context
+
+local colorhash = attributes.list[attributes.private('color')]
+local textype = tex.type
+local MPcolor = context.MPcolor
+
+do
+
+ local dimenorname =
+ lpegpatterns.validdimen / function(s)
+ context("\\the\\dimexpr %s",s)
+ end
+ + (C(lpegpatterns.float) + Cc(1)) * lpegpatterns.space^0 * P("\\") * C(lpegpatterns.letter^1) / function(f,s)
+ local t = textype(s)
+ if t == "dimen" then
+ context("\\the\\dimexpr %s\\%s\\relax",f,s)
+ elseif t == "count" then
+ context("\\the\\numexpr \\%s * %s\\relax",s,f) -- <n>\scratchcounter is not permitted
+ end
+ end
+
+ local splitter = lpeg.splitat("::",true)
+
+ interfaces.implement {
+ name = "prepareMPvariable",
+ arguments = "string",
+ actions = function(v)
+ if v == "" then
+ -- MPcolor("black")
+ context("black")
+ else
+ local typ, var = lpegmatch(splitter,v)
+ if not var then
+ -- parse
+ if colorhash[v] then
+ -- MPcolor(v)
+ context("%q",var)
+ elseif tonumber(v) then
+ context(v)
+ elseif not lpegmatch(dimenorname,v) then
+ context("\\number %s",v) -- 0.4 ...
+ end
+ elseif typ == "d" then -- to be documented
+ -- dimension
+ context("\\the\\dimexpr %s\\relax",var)
+ elseif typ == "n" then -- to be documented
+ -- number
+ context("\\the\\numexpr %s\\relax",var)
+ elseif typ == "s" then -- to be documented
+ -- string
+ -- context(var)
+ context("%q",var)
+ elseif typ == "c" then -- to be documented
+ -- color
+ -- MPcolor(var)
+ context("%q",var)
+ else
+ context(var)
+ end
+ end
+ end
+ }
+
+end
+
+do
+
+ local ctx_mathematics = context.mathematics
+
+ -- function metapost.formatnumber(f,n) -- just lua format
+ -- f = gsub(f,"@(%d)","%%.%1")
+ -- f = gsub(f,"@","%%")
+ -- f = format(f,tonumber(n) or 0)
+ -- f = gsub(f,"e([%+%-%d]+)",function(s)
+ -- return format("\\times10^{%s}",tonumber(s) or s) -- strips leading zeros
+ -- end)
+ -- context.mathematics(f)
+ -- end
+
+ -- formatters["\\times10^{%N}"](s) -- strips leading zeros too
+
+ local one = Cs((P("@")/"%%." * (R("09")^1) + P("@")/"%%" + 1)^0)
+ local two = Cs((P("e")/"" * ((S("+-")^0 * R("09")^1) / function(s)
+ -- return format("\\times10^{%s}",tonumber(s) or s)
+ return "\\times10^{" .. (tonumber(s) or s) .."}"
+ end) + 1)^1)
+
+ -- local two = Cs((P("e")/"" * ((S("+-")^0 * R("09")^1) / formatters["\\times10^{%N}"]) + 1)^1)
+
+ function metapost.formatnumber(fmt,n) -- just lua format
+ ctx_mathematics(lpegmatch(two,format(lpegmatch(one,fmt),n)))
+ end
+
+end
+
+do
+
+ -- this is an old pass-data-to-tex mechanism
+
+ local ctx_printtable = context.printtable
+
+ local data = false
+
+ function mp.mf_start_saving_data(n)
+ data = { }
+ end
+
+ function mp.mf_stop_saving_data()
+ if data then
+ -- nothing
+ end
+ end
+
+ function mp.mf_finish_saving_data()
+ if data then
+ -- nothing
+ end
+ end
+
+ function mp.mf_save_data(str)
+ if data then
+ data[#data+1] = str
+ end
+ end
+
+ local function getdata()
+ if data then
+ ctx_printtable(data,"\r")
+ end
+ end
+
+ interfaces.implement {
+ name = "getMPdata",
+ public = true,
+ protected = true,
+ actions = getdata,
+ }
+
+ interfaces.implement {
+ name = "rawMPdata",
+ public = true,
+ actions = getdata,
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-ini.mkxl
new file mode 100644
index 00000000000..823d369f0af
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-ini.mkxl
@@ -0,0 +1,1336 @@
+%D \module
+%D [ file=meta-ini,
+%D version=2008.03.25,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\ currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Some lmtx upgrading work needs to be done here.
+%D
+%D For real \METAFUN\ magic see \type {https://art-aleatoire.com/}.
+
+% initializations:
+%
+% - pass settings from tex to mp (delayed expansion)
+% - used by context core (and modules)
+% - cummulative definitions
+% - flushed each graphic
+% - can be disabled per instance
+% - managed at the tex end
+%
+% extensions:
+%
+% - add mp functionality (immediate expansion)
+% - cummulative
+% - all instances or subset of instances
+% - can be disabled per instance
+% - managed at the lua/mp end
+% - could be managed at the tex end but no real reason and also messy
+%
+% definitions:
+%
+% - add mp functionality (delayed expansion)
+% - cummulative
+% - per instance
+% - managed at the tex end
+%
+% inclusions:
+%
+% - add mp functionality (delayed expansion)
+% - cummulative only when [+]
+% - per instance
+% - managed at the tex end
+%
+% order of execution:
+%
+% definitions
+% extensions
+% inclusions
+% beginfig
+% initializations
+% graphic
+% endfig
+
+% The instance will be implemented stepwise ... I should redo some code in order to
+% make the macros look better than they do now.
+
+\writestatus{loading}{MetaPost Graphics / Initializations}
+
+\registerctxluafile{meta-ini}{autosuffix}
+
+\unprotect
+
+\newtoks \everyMPgraphic % mp % public or not ?
+
+\appendtoks
+ \restoreendofline % see interferences-001.tex
+\to \everyMPgraphic
+
+\mutable \def\MPruntimefile {mprun}
+\mutable \def\currentMPformat {metafun}
+\immutable\def\defaultMPinstance{metafun}
+
+\installcorenamespace{mpinstance}
+\installcorenamespace{mpinclusions}
+\installcorenamespace{mpdefinitions}
+\installcorenamespace{mpgraphic}
+\installcorenamespace{mpstaticgraphic}
+\installcorenamespace{mpclip}
+
+\newtoks \t_meta_initializations % tex, each
+
+\permanent\def\t_meta_inclusions {\csname\??mpinclusions \currentMPinstance\endcsname} % token register
+\permanent\def\t_meta_definitions{\csname\??mpdefinitions\currentMPinstance\endcsname} % token register
+
+%D The next command is, of course, dedicated to Mojca, who needs it for gnuplot.
+%D Anyway, the whole multiple engine mechanism is to keep her gnuplot from
+%D interfering.
+
+\permanent\tolerant\protected\def\startMPdefinitions#=#:#2\stopMPdefinitions
+ {\let\m_meta_saved_instance\currentMPinstance
+ \edef\currentMPinstance{#1}%
+ \ifempty\currentMPinstance
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \gtoksapp\t_meta_definitions{#2}%
+ \let\currentMPinstance\m_meta_saved_instance}
+
+\aliased\let\stopMPdefinitions\relax
+
+\permanent\tolerant\protected\def\startMPextensions#=#:#2\stopMPextensions % we could use buffers instead
+ {\clf_setmpextensions{#1}{#2}}
+
+\aliased\let\stopMPextensions\relax
+
+\permanent\protected\def\startMPinitializations#1\stopMPinitializations % for all instances, when enabled
+ {\gtoksapp\t_meta_initializations{#1}}
+
+\aliased\let\stopMPinitializations\relax
+
+\permanent\tolerant\protected\def\startMPinclusions[#1]#*#=#:#3\stopMPinclusions
+ {\let\m_meta_saved_instance\currentMPinstance % \pushmacro
+ \edef\currentMPinstance{#2}%
+ \ifempty\currentMPinstance
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \iftok{#1}{+}\else
+ \global\t_meta_inclusions\emptytoks
+ \fi
+ \gtoksapp\t_meta_inclusions{#3}%
+ \let\currentMPinstance\m_meta_saved_instance} % \popmacro
+
+\aliased\let\stopMPinclusions\relax
+
+% The next was broken since we added instances so it will go away!
+
+\permanent\tolerant\protected\def\MPinclusions[#1]#:#*#=#:#*#=%
+ {\let\m_meta_saved_instance\currentMPinstance
+ \iftok{#1}{+}\else
+ \global\t_meta_inclusions\emptytoks
+ \fi
+ \ifparameter#3\or
+ \edef\currentMPinstance{#2}%
+ \ifempty\currentMPinstance
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \gtoksapp\t_meta_inclusions{#3}%
+ \else
+ \let\currentMPinstance\defaultMPinstance
+ \gtoksapp\t_meta_inclusions{#2}%
+ \fi
+ \let\currentMPinstance\m_meta_saved_instance}
+
+% so far
+
+\installcommandhandler \??mpinstance {MPinstance} \??mpinstance
+
+\setupMPinstance
+ [\s!format=metafun,
+ \s!extensions=\v!no,
+ \s!initializations=\v!no,
+ \c!method=\s!default,
+ \c!textstyle=,
+ \c!textcolor=]
+
+\appendtoks
+ \ifcsname\??mpdefinitions\currentMPinstance\endcsname \else
+ \expandafter\newtoks\csname\??mpdefinitions\currentMPinstance\endcsname
+ \fi
+ \ifcsname\??mpinclusions\currentMPinstance\endcsname \else
+ \expandafter\newtoks\csname\??mpinclusions\currentMPinstance\endcsname
+ \fi
+ \t_meta_definitions\emptytoks % in case we redefine
+ \t_meta_inclusions \emptytoks % in case we redefine
+\to \everydefineMPinstance
+
+\permanent\protected\def\resetMPinstance[#1]%
+ {\writestatus\m!metapost{reset will be implemented when needed}}
+
+\def\meta_analyze_graphicname[#1]%
+ %{\normalexpanded{\meta_analyze_graphicname_indeed[#1::::]}}
+ {\normalexpanded{\meta_analyze_graphicname_indeed[#1}::::]}
+
+\def\meta_show_properties_indeed
+ {\writestatus{metapost}{name: \currentMPgraphicname, instance: \currentMPinstance, format: \currentMPformat}}
+
+\let\meta_show_properties\donothing
+
+\installtextracker
+ {metapost.properties}
+ {\let\meta_show_properties\meta_show_properties_indeed}
+ {\let\meta_show_properties\donothing}
+
+\protected\def\meta_analyze_graphicname_indeed[#1::#2::#3]% instance ::
+ {\edef\currentMPgraphicname{#2}%
+ \ifempty\currentMPgraphicname
+ \edef\currentMPgraphicname{#1}%
+ \let\currentMPinstance\defaultMPinstance
+ \orelse\ifcsname\??mpdefinitions#1\endcsname
+ \edef\currentMPinstance{#1}%
+ \else
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \edef\currentMPformat{\MPinstanceparameter\s!format}%
+ \meta_show_properties}
+
+\mutable\def\currentMPgraphicname{\s!unknown}
+\mutable\def\currentMPinstance {\defaultMPinstance}
+\mutable\def\currentMPformat {\currentMPinstance}
+
+\defineMPinstance[metafun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes]
+\defineMPinstance[minifun] [\s!format=minifun,\s!extensions=\v!yes,\s!initializations=\v!yes]
+\defineMPinstance[extrafun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes]
+\defineMPinstance[lessfun] [\s!format=metafun]
+\defineMPinstance[doublefun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes,\c!method=\s!double]
+\defineMPinstance[binaryfun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes,\c!method=\s!binary]
+\defineMPinstance[decimalfun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes,\c!method=\s!decimal]
+
+\defineMPinstance[mprun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes]
+
+\defineMPinstance[metapost] [\s!format=mpost]
+\defineMPinstance[nofun] [\s!format=mpost]
+\defineMPinstance[doublepost] [\s!format=mpost,\c!method=\s!double]
+\defineMPinstance[binarypost] [\s!format=mpost,\c!method=\s!binary]
+\defineMPinstance[decimalpost][\s!format=mpost,\c!method=\s!decimal]
+
+\defineMPinstance[simplefun] [\s!format=metafun,\c!method=\s!double] % maybe use minifun
+
+%defineMPinstance[megapost] [\s!format=mpost,\c!method=\s!decimal]
+
+\newconditional\c_meta_include_initializations
+
+\def\meta_begin_graphic_group#1%
+ {\begingroup
+ \meta_analyze_graphicname[#1]}
+
+\def\meta_end_graphic_group
+ {\endgroup}
+
+\mutable\def\MPaskedfigure{false}
+
+\def\meta_flush_current_initializations
+ {\ifconditional\c_meta_include_initializations
+ \the\t_meta_initializations
+ \fi}
+
+\def\meta_flush_current_inclusions
+ {\the\t_meta_inclusions}
+
+\def\meta_flush_current_definitions
+ {\the\t_meta_definitions}
+
+\def\meta_start_current_graphic
+ {\begingroup
+ \meta_enable_include
+ \the\everyMPgraphic
+ \edef\p_initializations{\MPinstanceparameter\s!initializations}%
+ \ifx\p_initializations\v!yes
+ \settrue \c_meta_include_initializations
+ \else
+ \setfalse\c_meta_include_initializations
+ \fi
+ \edef\p_setups{\MPinstanceparameter\c!setups}%
+ \ifempty\p_setups \else
+ \setups[\p_setups]%
+ \fi
+ \useMPinstancestyleparameter\c!textstyle}
+
+\def\meta_set_current_color
+ {\useMPinstancecolorparameter\c!textcolor}
+
+\def\meta_stop_current_graphic
+ {\global\t_meta_definitions\emptytoks
+ \global\t_meta_inclusions\emptytoks
+ \endgroup}
+
+\def\meta_process_graphic_start
+ {\pushMPboundingbox
+ \setbox\b_meta_graphic\hpack\bgroup}
+
+\let\meta_relocate_graphic\relax % experimental hook
+
+\def\meta_process_graphic_stop
+ {\egroup
+ \meta_place_graphic
+ \meta_relocate_graphic
+ \popMPboundingbox}
+
+\protected\def\meta_process_graphic_instance#1%
+ {\edef\currentMPinstance{#1}%
+ \ifempty\currentMPinstance
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \edef\currentMPformat{\MPinstanceparameter\s!format}%
+ \meta_process_graphic}
+
+\protected\def\meta_process_graphic#1% todo: extensions and inclusions outside beginfig
+ {\meta_start_current_graphic
+ \forgetall
+ \edef\p_extensions{\MPinstanceparameter\s!extensions}%
+ \meta_process_graphic_start
+ \normalexpanded{\noexpand\clf_mpgraphic
+ instance {\currentMPinstance}%
+ format {\currentMPformat}%
+ data {#1;}%
+ initializations {\meta_flush_current_initializations}%
+ \ifx\p_extensions\v!yes
+ extensions {\clf_getmpextensions{\currentMPinstance}}% goes through tex again
+ \fi
+ inclusions {\meta_flush_current_inclusions}%
+ definitions {\meta_flush_current_definitions}%
+ figure {\MPaskedfigure}%
+ method {\MPinstanceparameter\c!method}%
+ namespace {\??graphicvariable\currentmpvariableclass:}%
+ \relax}%
+ \meta_process_graphic_stop
+ \meta_stop_current_graphic}
+
+\let\meta_process_graphic_figure_start\relax
+\let\meta_process_graphic_figure_stop \relax
+
+\let\normal_meta_process_graphic_start\relax
+\let\normal_meta_process_graphic_stop \relax
+
+\permanent\protected\def\processMPfigurefile#1% special case: obeys beginfig .. endfig and makes pages
+ {\begingroup
+ \let\normal_meta_process_graphic_start\meta_process_graphic_start
+ \let\normal_meta_process_graphic_stop \meta_process_graphic_stop
+ \let\meta_process_graphic_start\relax
+ \let\meta_process_graphic_stop \relax
+ \def\meta_process_graphic_figure_start{\startTEXpage[\c!offset=\v!overlay,\c!align=]\normal_meta_process_graphic_start}%
+ \def\meta_process_graphic_figure_stop {\normal_meta_process_graphic_stop\stopTEXpage}
+ \def\MPaskedfigure{all}%
+ \meta_process_graphic{input "#1" ;}%
+ \endgroup}
+
+%D Calling up previously defined graphics.
+
+% \def\includeMPgraphic#1% gets expanded !
+% {\ifcsname\??mpgraphic#1\endcsname
+% \csname\??mpgraphic#1\endcsname ; % ; is safeguard
+% \fi}
+%
+% \protected\def\meta_enable_include % public
+% {\let\meta_handle_use_graphic \thirdofthreearguments
+% \let\meta_handle_reusable_graphic\thirdofthreearguments}
+%
+% but ... we want this too:
+%
+% \startuseMPgraphic{x}
+% draw textext("\externalfigure[foo.pdf]") ;
+% \stopuseMPgraphic
+%
+% \useMPgraphic{x}
+%
+% so we cannot overload unless we let back to the original meanings each graphic
+% ... a better solution is:
+
+\permanent\def\includeMPgraphic#1% gets expanded !
+ {\ifcsname\??mpgraphic#1\endcsname
+ \doubleexpandafter\fourthoffourarguments\lastnamedcs ; % ; is safeguard
+ \fi}
+
+\let\meta_enable_include\relax
+
+%D Drawings (stepwise built):
+
+\newif\ifMPdrawingdone \MPdrawingdonefalse
+
+\permanent\protected\def\finalizeMPdrawing
+ {\MPdrawingdonetrue}
+
+\mutable\let\MPdrawingdata\empty
+
+\permanent\protected\def\resetMPdrawing
+ {\glet\MPdrawingdata\empty
+ \global\MPdrawingdonefalse}
+
+\permanent\protected\def\pushMPdrawing
+ {\globalpushmacro\MPdrawingdata
+ \glet\MPdrawingdata\empty}
+
+\permanent\protected\def\popMPdrawing
+ {\globalpopmacro\MPdrawingdata}
+
+\permanent\protected\def\getMPdrawing
+ {\ifMPdrawingdone
+ \expandafter\meta_process_graphic\expandafter{\MPdrawingdata}% is this expansion still needed?
+ \fi}
+
+\permanent\tolerant\def\startMPdrawing[#1]% todo: use pickup #:
+ {\meta_start_drawing{#1}}
+
+\def\meta_start_drawing#1#2\stopMPdrawing
+ {\relax
+ \bgroup
+ \meta_enable_include
+ \doifelse{#1}{-}
+ {\xdef\MPdrawingdata{\MPdrawingdata\detokenize{#2}}}%
+ {\xdef\MPdrawingdata{\MPdrawingdata#2}}%
+ \egroup}
+
+\aliased\let\stopMPdrawing\relax
+
+\permanent\def\MPdrawing#1%
+ {\relax
+ \bgroup
+ \meta_enable_include
+ \xdef\MPdrawingdata{\MPdrawingdata#1}%
+ \egroup}
+
+\permanent\protected\def\startMPclip#1#2\stopMPclip % todo: store at the lua end or just store less
+ {\gdefcsname\??mpclip#1\endcsname{#2}}
+
+\aliased\let\stopMPclip\relax
+
+\mutable\def\width {0 } % left-over
+\mutable\def\height{0 } % left-over
+
+\mutable\let\currentMPclip\empty
+
+\protected\def\meta_grab_clip_path#1#2#3%
+ {\begingroup
+ \d_overlay_width #2\relax
+ \d_overlay_height#3\relax
+ \edef\width {\the\d_overlay_width \space}% hm
+ \edef\height{\the\d_overlay_height\space}% hm
+ \edef\currentMPclip{#1}%
+ \ifcsname\??mpclip\currentMPclip\endcsname
+ \meta_grab_clip_path_yes
+ \else
+ \meta_grab_clip_path_nop
+ \fi
+ \endgroup}
+
+\def\meta_grab_clip_path_yes
+ {\meta_start_current_graphic
+ \normalexpanded{\noexpand\clf_mpsetclippath
+ instance {\currentMPinstance}%
+ format {\currentMPformat}%
+ data {\csname\??mpclip\currentMPclip\endcsname}%
+ initializations {\meta_flush_current_initializations}%
+ useextensions {\MPinstanceparameter\s!extensions}%
+ inclusions {\meta_flush_current_inclusions}%
+ method {\MPinstanceparameter\c!method}%
+ width \d_overlay_width
+ height \d_overlay_height
+ \relax}%
+ \meta_stop_current_graphic}
+
+\def\meta_grab_clip_path_nop
+ {\clf_mpsetclippath
+ width \d_overlay_width
+ height \d_overlay_height
+ \relax}
+
+%D Since we want labels to follow the document settings, we also set the font
+%D related variables.
+
+\permanent\protected\def\MPfontsizehskip#1%
+ {\dontleavehmode
+ \begingroup
+ \definedfont[#1]%
+ \hskip\clf_currentdesignsize\scaledpoint\relax
+ \endgroup}
+
+\definefontsynonym[MetafunDefault][Regular*default]
+
+\startMPinitializations % scale is not yet ok
+ defaultfont:="\truefontname{MetafunDefault}";
+ % defaultscale:=\the\bodyfontsize/10pt; % only when hard coded 10pt
+ % defaultscale:=1;
+\stopMPinitializations
+
+%D A signal that we're in combined \CONTEXT||\METAFUN mode:
+
+\startMPextensions
+ string contextversion;contextversion:="\contextversion"; % expanded
+\stopMPextensions
+
+%D \macros
+%D {setupMPvariables}
+%D
+%D When we build collections of \METAPOST\ graphics, like background and buttons,
+%D the need for passing settings arises. By (mis|)|using the local prefix that
+%D belongs to \type {\framed}, we get a rather natural interface to backgrounds. To
+%D prevent conflicts, we will use the \type {-} in \METAPOST\ specific variables,
+%D like:
+%D
+%D \starttyping
+%D \setupMPvariables[meta:button][size=20pt]
+%D \stoptyping
+
+% \lineheight 2pt 2 \scratchcounter red 0.4 .5\bodyfontsize
+%
+% see cont-loc for test code
+
+%D Currently the inheritance of backgrounds does not work and we might drop it
+%D anyway (too messy)
+
+\newbox\b_meta_variable_box
+
+\mutable\let \currentmpvariableclass \empty % public
+
+\let \m_meta_current_variable \empty
+\let \m_meta_current_variable_template\empty
+
+% \installcorenamespace{graphicvariable} % already set
+
+\def\meta_prepare_variable_default{\MPcolor{black}} % just to be sure we use a color but ...
+
+\permanent\tolerant\protected\def\setupMPvariables[#1]#*[#2]%
+ {\ifarguments
+ \or
+ \getrawparameters[\??graphicvariable:][#1]%
+ \else
+ \getrawparameters[\??graphicvariable#1:][#2]%
+ \fi}
+
+\permanent\tolerant\protected\def\presetMPvariable[#1]#*[#2=#3]%
+ {\ifcsname\??graphicvariable#1:#2\endcsname \else
+ \defcsname\??graphicvariable#1:#2\endcsname{#3}%
+ \fi}
+
+\permanent\def\MPrawvar#1#2% no checking
+ {\begincsname\??graphicvariable#1:#2\endcsname}
+
+\permanent\def\MPvariable#1% todo: could be a framed chain
+ {\begincsname\??graphicvariable\currentmpvariableclass:#1\endcsname}
+
+\permanent\tolerant\protected\def\useMPvariables[#1]#*[#2]%
+ {\edef\currentmpvariableclass{#1}%
+ \meta_prepare_variables{#2}}
+
+\protected\def\meta_prepare_variables#1%
+ {\processcommalist[#1]\meta_prepare_variable}
+
+\protected\def\meta_prepare_variable#1%
+ {\edef\m_meta_current_variable_template
+ {\??graphicvariable\currentmpvariableclass:#1}%
+ \edef\m_meta_current_variable
+ {\begincsname\m_meta_current_variable_template\endcsname}%
+ \ifempty\m_meta_current_variable
+ \expandafter\meta_prepare_variable_nop
+ \else
+ \expandafter\meta_prepare_variable_yes
+ \fi}
+
+\protected\def\meta_prepare_instance_variables
+ {\expandafter\processcommalist\expandafter[\m_meta_instance_variables]\meta_prepare_instance_variable}
+
+\protected\def\meta_prepare_instance_variable#1%
+ {\edef\m_meta_current_variable_template
+ {\??graphicvariable\currentmpvariableclass:#1}%
+ \edef\m_meta_current_variable
+ {\ifcsname\m_meta_current_variable_template\endcsname
+ \lastnamedcs
+ \else
+ \begincsname\??graphicvariable\currentMPgraphicname:#1\endcsname
+ \fi}%
+ \ifempty\m_meta_current_variable
+ \expandafter\meta_prepare_variable_nop
+ \else
+ \expandafter\meta_prepare_variable_yes
+ \fi}
+
+\def\meta_prepare_variable_nop
+ {\expandafter\letcsname\m_meta_current_variable_template\endcsname\meta_prepare_variable_default}
+
+\def\meta_prepare_variable_color % we use the attribute so we dont' go through namedcolor (why not)
+ {\expandafter\edefcsname\m_meta_current_variable_template\endcsname{\MPcolor\m_meta_current_variable}}
+
+\def\meta_prepare_variable_number
+ {\expandafter\edefcsname\m_meta_current_variable_template\endcsname{\number\m_meta_current_variable}}% also accepts 0.number
+
+\def\meta_prepare_variable_dimension
+ {\expandafter\edefcsname\m_meta_current_variable_template\endcsname{\the\dimexpr\m_meta_current_variable}}
+
+\def\meta_prepare_variable_yes
+ {\ifchkdim\m_meta_current_variable\or
+ \meta_prepare_variable_dimension
+ \orelse\ifchknum\m_meta_current_variable\or
+ \meta_prepare_variable_number
+ \else
+ \doifelsecolor\m_meta_current_variable
+ \meta_prepare_variable_color
+ \meta_prepare_variable_dimension
+ \fi}
+
+\aliased\let\MPvar \MPvariable
+\aliased\let\setMPvariables\setupMPvariables
+
+%D \macros
+%D {startuniqueMPgraphic, uniqueMPgraphic}
+%D
+%D This macros is probably of most use to myself, since I like to use graphics that
+%D adapt themselves. The next \METAPOST\ kind of graphic is both unique and reused
+%D when possible.
+%D
+%D \starttyping
+%D \defineoverlay[example][\uniqueMPgraphic{test}]
+%D
+%D \startuniqueMPgraphic {test}
+%D draw OverlayBox ;
+%D \stopuniqueMPgraphic
+%D
+%D \startuniqueMPgraphic {test}
+%D draw OverlayBox ;
+%D \stopuniqueMPgraphic
+%D \stoptyping
+
+% todo: frozen or not?
+
+\permanent\def\overlaystamp % watch the \MPcolor, since colors can be redefined
+ {\the\d_overlay_width :%
+ \the\d_overlay_height :%
+ \the\d_overlay_depth :%
+ \the\d_overlay_offset :%
+ \the\d_overlay_linewidth:%
+ \MPcolor\overlaycolor :% % todo, expand once \m_overlaycolor
+ \MPcolor\overlaylinecolor} % todo, expand once \m_overlaylinecolor
+
+%D A better approach is to let additional variables play a role in determining the
+%D uniqueness. In the next macro, the second, optional, argument is used to
+%D guarantee the uniqueness, as well as prepare variables for passing them to
+%D \METAPOST.
+%D
+%D \starttyping
+%D \startuniqueMPgraphic{meta:hash}{gap,angle,...}
+%D \stoptyping
+%D
+%D The calling macro also accepts a second argument. For convenient use in overlay
+%D definitions, we use \type {{}} instead of \type {[]}.
+%D
+%D \starttyping
+%D \uniqueMPgraphic{meta:hash}{gap=10pt,angle=30}
+%D \stoptyping
+
+\newcount\c_meta_object_counter
+\newbox \b_meta_graphic
+
+% hm, isn't this already done elsewhere?
+
+\protected\def\meta_obey_box_depth
+ {\setbox\b_meta_graphic\hpack\bgroup
+ \raise\MPlly\box\b_meta_graphic
+ \egroup}
+
+\protected\def\meta_ignore_box_depth
+ {\normalexpanded
+ {\meta_obey_box_depth % hence the \protected
+ \wd\b_meta_graphic\the\wd\b_meta_graphic
+ \ht\b_meta_graphic\the\ht\b_meta_graphic
+ \dp\b_meta_graphic\the\dp\b_meta_graphic}}
+
+\protected\def\meta_obey_box_origin
+ {\setbox\b_meta_graphic\hpack\bgroup
+ \kern\MPllx\raise\MPlly\box\b_meta_graphic
+ \egroup}
+
+\permanent\protected\def\obeyMPboxdepth {\let\meta_relocate_box\meta_obey_box_depth}
+\permanent\protected\def\ignoreMPboxdepth{\let\meta_relocate_box\meta_ignore_box_depth}
+\permanent\protected\def\obeyMPboxorigin {\let\meta_relocate_box\meta_obey_box_origin}
+\permanent\protected\def\normalMPboxdepth{\let\meta_relocate_box\relax}
+
+\let\meta_relocate_box\relax
+
+\protected\def\meta_place_graphic % the converter also displaces so in fact we revert
+ {\meta_relocate_box
+ \box\b_meta_graphic}
+
+\protected\def\meta_reuse_box#1#2#3#4#5% space delimiting would save some tokens
+ {\MPllx#2\MPlly#3\MPurx#4\MPury#5%
+ \hpack container{\forcecolorhack\getobject{MP}{#1}}} % else no proper color intent
+
+\protected\def\meta_use_box
+ {\setunreferencedobject{MP}}
+
+\def\meta_handle_unique_graphic#1#2#3% when there are too many, we can store data at the lua end, although,
+ {\begingroup % when there are that many they're probably not that unique anyway
+ \edef\currentmpvariableclass{#1}%
+ \extendMPoverlaystamp{#2}% incl prepare
+ \ifcsname\??mpgraphic\overlaystamp:#1\endcsname
+ \lastnamedcs
+ \else
+ \meta_enable_include % redundant
+ \global\advance\c_meta_object_counter\plusone
+ \meta_use_box{\number\c_meta_object_counter}\hpack{\meta_process_graphic{#3}}% was vbox, graphic must end up as hbox
+ \xdefcsname\??mpgraphic\overlaystamp:#1\endcsname{\meta_reuse_box{\number\c_meta_object_counter}{\the\MPllx}{\the\MPlly}{\the\MPurx}{\the\MPury}}%
+ \csname\??mpgraphic\overlaystamp:#1\endcsname\empty
+ \fi
+ \endgroup}
+
+% todo: we partially expand
+
+\permanent\protected\def\startuniqueMPgraphic
+ {\dodoublegroupempty\meta_start_unique_graphic}
+
+\aliased\let\stopuniqueMPgraphic\relax
+
+\def\meta_start_unique_graphic#1%
+ {\normalexpanded{\meta_start_unique_graphic_indeed{#1}}}
+
+\permanent\protected\def\meta_start_unique_graphic_indeed#1#2#3\stopuniqueMPgraphic
+ {\gdefcsname\??mpgraphic#1\endcsname{\meta_handle_unique_graphic{#1}{#2}{#3}}}
+
+\permanent\tolerant\protected\def\uniqueMPgraphic#=#*#=%
+ {\meta_begin_graphic_group{#1}%
+ \setupMPvariables[#1][#2]%
+ \begincsname\??mpgraphic#1\endcsname\empty
+ \meta_end_graphic_group}
+
+\def\meta_handle_use_graphic#1#2#3%
+ {\begingroup
+ \edef\currentmpvariableclass{#1}%
+ \edef\m_meta_instance_variables{#2}%
+ \ifempty\m_meta_instance_variables \else
+ \meta_prepare_instance_variables
+ \fi
+ \meta_enable_include % redundant
+ \meta_process_graphic{#3}%
+ \endgroup}
+
+\permanent\protected\def\startuseMPgraphic % todo, modernize
+ {\dodoublegroupempty\meta_start_use_graphic}
+
+\aliased\let\stopuseMPgraphic\relax
+
+\def\meta_start_use_graphic#1%
+ {\normalexpanded{\meta_start_use_graphic_indeed{#1}}}
+
+\protected\def\meta_start_use_graphic_indeed#1#2#3\stopuseMPgraphic
+ {\gdefcsname\??mpgraphic#1\endcsname{\meta_handle_use_graphic{#1}{#2}{#3}}}
+
+\permanent\protected\def\startusableMPgraphic % redundant but handy % todo, modernize
+ {\dodoublegroupempty\meta_start_usable_graphic}
+
+\aliased\let\stopusableMPgraphic\relax
+
+\def\meta_start_usable_graphic#1%
+ {\normalexpanded{\meta_start_usable_graphic_indeed{#1}}}
+
+\protected\def\meta_start_usable_graphic_indeed#1#2#3\stopusableMPgraphic
+ {\gdefcsname\??mpgraphic#1\endcsname{\meta_handle_use_graphic{#1}{#2}{#3}}}
+
+\def\meta_handle_reusable_graphic#1#2#3%
+ {\begingroup
+ \edef\currentmpvariableclass{#1}%
+ \edef\m_meta_instance_variables{#2}%
+ \ifempty\m_meta_instance_variables \else
+ \meta_prepare_instance_variables
+ \fi
+ \meta_enable_include % redundant
+ \global\advance\c_meta_object_counter\plusone
+ \meta_use_box{\number\c_meta_object_counter}\hpack{\meta_process_graphic{#3}}% was vbox, graphic must end up as hbox
+ \xdefcsname\??mpgraphic#1\endcsname{\meta_reuse_box{\number\c_meta_object_counter}{\the\MPllx}{\the\MPlly}{\the\MPurx}{\the\MPury}}%
+ \csname\??mpgraphic#1\endcsname\empty
+ \endgroup}
+
+\permanent\protected\def\startreusableMPgraphic % todo, modernize
+ {\dodoublegroupempty\meta_start_reusable_graphic}
+
+\aliased\let\stopreusableMPgraphic\relax
+
+\def\meta_start_reusable_graphic#1%
+ {\normalexpanded{\meta_start_reusable_graphic_indeed{#1}}}
+
+\protected\def\meta_start_reusable_graphic_indeed#1#2#3\stopreusableMPgraphic
+ {\gdefcsname\??mpgraphic#1\endcsname{\meta_handle_reusable_graphic{#1}{#2}{#3}}}
+
+\mutable\let\usedMPgraphicname\empty
+
+\permanent\tolerant\protected\def\useMPgraphic#=#*#=%
+ {\meta_begin_graphic_group{#1}%
+ \ifcsname\??mpgraphic#1\endcsname
+ \edef\usedMPgraphicname{#1}%
+ \orelse\ifcsname\??mpgraphic\currentMPgraphicname\endcsname
+ \let\usedMPgraphicname\currentMPgraphicname
+ \else
+ \let\usedMPgraphicname\empty
+ \fi
+ \ifempty\usedMPgraphicname
+ % message
+ \else
+ \doifsomething{#2}{\setupMPvariables[\usedMPgraphicname][#2]}%
+ \csname\??mpgraphic\usedMPgraphicname\endcsname
+ \fi
+ \meta_end_graphic_group}
+
+\aliased\let\reuseMPgraphic \useMPgraphic % we can save a setup here if needed
+\aliased\let\reusableMPgraphic\reuseMPgraphic % we can save a setup here if needed
+
+%D \macros
+%D {startuniqueMPpagegraphic,uniqueMPpagegraphic}
+%D
+%D Experimental.
+
+\def\m_meta_page_prefix{\doifelseoddpage oe}
+
+\permanent\def\overlaypagestamp
+ {\m_meta_page_prefix :%
+ \the\d_overlay_width :%
+ \the\d_overlay_height :%
+ \the\d_overlay_depth :%
+ \the\d_overlay_offset :%
+ \the\d_overlay_linewidth:%
+ \MPcolor\overlaycolor :%
+ \MPcolor\overlaylinecolor}
+
+\permanent\tolerant\protected\def\startuniqueMPpagegraphic % todo the modern way
+ {\dodoublegroupempty\meta_start_unique_page_graphic}
+
+\aliased\let\stopuniqueMPpagegraphic\relax
+
+\def\meta_start_unique_page_graphic#1%
+ {\normalexpanded{\meta_start_unique_page_graphic_indeed{#1}}}
+
+\protected\def\meta_start_unique_page_graphic_indeed#1#2#3\stopuniqueMPpagegraphic % inefficient, double storage
+ {\gdefcsname\??mpgraphic o:#1\endcsname{\meta_handle_unique_graphic{o:#1}{#2}{#3}}% % but these also keep the state
+ \gdefcsname\??mpgraphic e:#1\endcsname{\meta_handle_unique_graphic{e:#1}{#2}{#3}}} % and meaning will be redefined
+
+\permanent\tolerant\protected\def\uniqueMPpagegraphic#=#=%
+ {\meta_begin_graphic_group{#1}%
+ \enforced\let\overlaystamp\overlaypagestamp
+ \setupMPvariables[\m_meta_page_prefix:#1][#2]% prefix is new here
+ \csname\??mpgraphic\m_meta_page_prefix:#1\endcsname\empty
+ \meta_end_graphic_group}
+
+%D One way of defining a stamp is:
+%D
+%D \starttyping
+%D \def\extendMPoverlaystamp#1%
+%D {\def\docommand##1%
+%D {\edef\overlaystamp{\overlaystamp:\MPvariable{##1}}}%
+%D \processcommalist[#1]\docommand}
+%D \stoptyping
+%D
+%D Since we need to feed \METAPOST\ with expanded dimensions, we introduce a
+%D dedicated expansion engine. We redefine \type {\extendMPoverlaystamp} to
+
+\permanent\protected\def\extendMPoverlaystamp#1%
+ {\processcommalist[#1]\meta_extend_overlay_stamp}
+
+\def\meta_extend_overlay_stamp#1%
+ {\meta_prepare_instance_variable{#1}%
+ \enforced\permanent\edef\overlaystamp{\overlaystamp:\MPvariable{#1}}}
+
+% \getMPdata % define at the lua end
+% \rawMPdata % define at the lua end
+
+%D We need this trick because we need to make sure that the tex scanner sees
+%D newlines and does not quit. Also, we do need to flush the buffer under a normal
+%D catcode regime in order to expand embedded tex macros. As usual with buffers,
+%D \type {#1} can be a list.
+
+\permanent\tolerant\protected\def\processMPbuffer[#1]%
+ {\meta_begin_graphic_group{#1}%
+ \meta_process_graphic{\clf_feedback{\currentMPgraphicname}}%
+ \meta_end_graphic_group}
+
+\permanent\tolerant\protected\def\runMPbuffer[#1]%
+ {\startnointerference\processMPbuffer[#1]\stopnointerference}
+
+%D \macros
+%D {startMPenvironment, resetMPenvironment}
+%D
+%D In order to synchronize the main \TEX\ run and the runs local to \METAPOST,
+%D environments can be passed.
+
+\permanent\protected\def\startMPenvironment
+ {\begingroup
+ \catcode\endoflineasciicode\ignorecatcode
+ \meta_start_environment}
+
+\tolerant\def\meta_start_environment[#1]#:#2\stopMPenvironment
+ {\endgroup
+ \edef\m_meta_option{#1}
+ \ifx\m_meta_option\s!reset
+ \resetMPenvironment % reset mp toks
+ \orelse\ifx\m_meta_option\v!global
+ #2% % use in main doc too
+ \orelse\ifx\m_meta_option\!!plustoken
+ #2% % use in main doc too
+ \fi
+ \clf_mptexset{\detokenize{#2}}}
+
+\aliased\let\stopMPenvironment\relax
+
+\permanent\protected\def\resetMPenvironment
+ {\clf_mptexreset}
+
+\permanent\protected\def\useMPenvironmentbuffer[#1]%
+ {\clf_mptexsetfrombuffer{#1}}
+
+%D This command takes \type {[reset]} as optional argument.
+%D
+%D \starttyping
+%D \startMPenvironment
+%D \setupbodyfont[pos,14.4pt]
+%D \stopMPenvironment
+%D
+%D \startMPcode
+%D draw btex \sl Hans Hagen etex scaled 5 ;
+%D \stopMPcode
+%D \stoptyping
+%D
+%D The most simple case:
+
+\permanent\tolerant\protected\def\startMPcode#=#:#2\stopMPcode
+ {\begingroup
+ \edef\currentMPinstance{#1}%
+ \ifempty\currentMPinstance
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \let\currentMPgraphicname\empty
+ \edef\currentMPformat{\MPinstanceparameter\s!format}%
+ \meta_enable_include
+ \meta_process_graphic{#2}%
+ \endgroup}
+
+\aliased\let\stopMPcode\relax
+
+%D This one is not optimized because it's only used for small snippets, if it
+%D is used at all.
+
+\permanent\tolerant\protected\def\MPcode#=#:#*#=%
+ {\ifparameter#1\or
+ \meta_begin_graphic_group{#1}%
+ \meta_enable_include
+ \meta_process_graphic{#2}%
+ \meta_end_graphic_group
+ \else
+ \let\currentMPinstance\defaultMPinstance
+ \meta_enable_include
+ \meta_process_graphic{#1}%
+ \fi}
+
+% a bit nasty (also needed for compatibility:
+
+% \startMPrun input mp-www.mp ; \stopMPrun
+% \externalfigure[mprun.3][width=10cm,height=8cm]
+
+% \startMPrun{mprun} input mp-www.mp ; \stopMPrun % instance
+% \externalfigure[mprun.4][width=10cm,height=8cm]
+
+\mutable\let\MPruninstance\defaultMPinstance
+
+\permanent\protected\def\useMPrun#1#2% name n
+ {\begingroup
+ \def\MPaskedfigure{#2}%
+ \normalexpanded{\useMPgraphic{\iftok{#1}\emptytoks mprun\else#1\fi}}%
+ \endgroup}
+
+\permanent\tolerant\protected\def\startMPrun#=#:#2\stopMPrun
+ {\normalexpanded{\startuseMPgraphic{\iftok{#1}\emptytoks mprun\else#1\fi}}#2\stopuseMPgraphic}
+
+\aliased\let\stopMPrun\relax
+
+%D The \type {\resetMPenvironment} is a quick way to erase
+%D the token list.
+%D
+%D You should be aware of independencies. For instance, if you use a font
+%D in a graphic that is not used in the main document, you need to load the
+%D typescript at the outer level (either directly or by using the global
+%D option).
+%D
+%D \starttyping
+%D \usetypescript[palatino][texnansi]
+%D
+%D \startMPenvironment
+%D \usetypescript[palatino][texnansi]
+%D \enableregime[utf]
+%D \setupbodyfont[palatino]
+%D \stopMPenvironment
+%D
+%D \startMPpage
+%D draw btex aap‒noot coördinatie – één etex ;
+%D \stopMPpage
+%D \stoptyping
+
+%D Loading specific \METAPOST\ related definitions is
+%D accomplished by:
+
+\permanent\protected\def\useMPlibrary[#1]{\clf_useMPlibrary{#1}}
+
+%D \macros
+%D {setMPtext, MPtext, MPstring, MPbetex}
+%D
+%D To be documented:
+%D
+%D \starttyping
+%D \setMPtext{identifier}{text}
+%D
+%D \MPtext {identifier}
+%D \MPstring{identifier}
+%D \MPbetex {identifier}
+%D \stoptyping
+
+\installcorenamespace{mptext}
+
+\permanent\protected\def\setMPtext#1#2{\dodoglobal\edefcsname\??mptext#1\endcsname{\detokenize{#2}}}
+\permanent \def\MPtext #1{\begincsname\??mptext#1\endcsname\empty}
+\permanent \def\MPstring #1{"\begincsname\??mptext#1\endcsname\empty"}
+\permanent \def\MPbetex #1{btex \begincsname\??mptext#1\endcsname\space etex}
+
+%D In order to communicate conveniently with the \TEX\ engine, we introduce some
+%D typesetting variables.
+
+% \setupcolors[state=stop,conversion=never] % quite tricky ... type mismatch
+
+\startMPinitializations
+ CurrentLayout:="\currentlayout";
+\stopMPinitializations
+
+%D A dirty trick, ** in colo-ini.lua (mpcolor). We cannot use a vardef, because
+%D that fails with spot colors.
+
+\startMPinitializations
+ def OverlayLineColor=\ifempty\overlaylinecolor black \else\MPcolor{\overlaylinecolor} \fi enddef;
+ def OverlayColor =\ifempty\overlaycolor black \else\MPcolor{\overlaycolor} \fi enddef;
+\stopMPinitializations
+
+\appendtoks
+ \disablediscretionaries
+ %\disablecompoundcharacters
+\to \everyMPgraphic
+
+\appendtoks
+ \baselineskip1\baselineskip
+ \lineheight 1\lineheight
+ \topskip 1\topskip
+\to \everyMPgraphic
+
+\appendtoks
+ \enforced\let\#\letterhash
+ \enforced\let\_\letterunderscore
+ \enforced\let\&\letterampersand
+ \enforced\let\{\letteropenbrace
+ \enforced\let\}\letterclosebrace
+\to \everyMPgraphic
+
+%D \macros
+%D {PDFMPformoffset}
+%D
+%D In \PDF, forms are clipped and therefore we have to take precautions to get this
+%D right. Since this is related to objects, we use the same offset as used there.
+
+\def\PDFMPformoffset{\objectoffset}
+
+\newtoks\everyinsertMPfile
+
+\startMPextensions
+ def initialize_form_numbers =
+ do_initialize_numbers;
+ enddef;
+\stopMPextensions
+
+\startMPextensions
+ vardef ForegroundBox =
+ unitsquare xysized(HSize,VSize)
+ enddef ;
+\stopMPextensions
+
+\startMPdefinitions {metapost}
+ if unknown context_bare : input mp-bare.mpiv ; fi ;
+\stopMPdefinitions
+\startMPdefinitions {binarypost}
+ if unknown context_bare : input mp-bare.mpiv ; fi ;
+\stopMPdefinitions
+\startMPdefinitions {decimalpost}
+ if unknown context_bare : input mp-bare.mpiv ; fi ;
+\stopMPdefinitions
+\startMPdefinitions {doublepost}
+ if unknown context_bare : input mp-bare.mpiv ; fi ;
+\stopMPdefinitions
+
+% \startMPdefinitions {nofun}
+% if unknown context_bare : input mp-bare.mpiv ; fi ;
+% \stopMPdefinitions
+
+%D And some more. These are not really needed since we don't use the normal figure
+%D inclusion macros any longer.
+
+\appendtoks
+ \externalfigurepostprocessors\emptytoks % safeguard
+\to \everyinsertMPfile
+
+%D We also take care of disabling fancy figure features, that can terribly interfere
+%D when dealing with symbols, background graphics and running (postponed) graphics.
+%D You won't believe me if I tell you what funny side effects can occur. One took me
+%D over a day to uncover when processing the screen version of the \METAFUN\ manual.
+
+\permanent\def\doifelseMPgraphic#1%
+ {\ifcsname\??mpgraphic #1\endcsname\expandafter\firstoftwoarguments \orelse
+ \ifcsname\??mpgraphic o:#1\endcsname\expandafter\firstoftwoarguments \orelse
+ \ifcsname\??mpgraphic e:#1\endcsname\expandafter\firstoftwoarguments \else
+ \expandafter\secondoftwoarguments\fi}
+
+\aliased\let\doifMPgraphicelse\doifelseMPgraphic
+
+%D New:
+
+\definelayerpreset % no dx,dy - else nasty non-mp placement
+ [mp]
+ [\c!y=-\MPury,
+ \c!x=\MPllx,
+ \c!method=\v!fit]
+
+\definelayer
+ [mp]
+ [\c!preset=mp]
+
+%D Usage:
+%D
+%D \starttyping
+%D \defineviewerlayer[one][state=start]
+%D \defineviewerlayer[two][state=stop]
+%D
+%D \startuseMPgraphic{step-1}
+%D fill fullcircle scaled 10cm withcolor red ;
+%D \stopuseMPgraphic
+%D
+%D \startuseMPgraphic{step-2}
+%D fill fullcircle scaled 5cm withcolor green ;
+%D \stopuseMPgraphic
+%D
+%D \setlayer[mp]{\viewerlayer[one]{\useMPgraphic{step-1}}}
+%D \setlayer[mp]{\viewerlayer[two]{\useMPgraphic{step-2}}}
+%D
+%D \ruledhbox{\flushlayer[mp]}
+%D \stoptyping
+%D
+%D Reusing graphics is also possible (now):
+%D
+%D \starttyping
+%D \startreusableMPgraphic{axis}
+%D tickstep := 1cm ; ticklength := 2mm ;
+%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ;
+%D tickstep := tickstep/2 ; ticklength := ticklength/2 ;
+%D drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ;
+%D \stopreusableMPgraphic
+%D
+%D \startuseMPgraphic{demo}
+%D drawpoint "1cm,1.5cm" ;
+%D \stopuseMPgraphic
+%D
+%D \definelayer[mp][preset=mp]
+%D \setlayer[mp]{\reuseMPgraphic{axis}}
+%D \setlayer[mp]{\useMPgraphic{demo}}
+%D \ruledhbox{\flushlayer[mp]}
+%D \stoptyping
+
+%D \macros
+%D {startstaticMPfigure,useMPstaticfigure}
+%D
+%D Static figures are processed only when there has been something changed. Here is
+%D Aditya Mahajan's testcase:
+%D
+%D \startbuffer
+%D \startstaticMPfigure{circle}
+%D fill fullcircle scaled 1cm withcolor blue;
+%D \stopstaticMPfigure
+%D
+%D \startstaticMPfigure{axis}
+%D drawarrow (0,0)--(2cm,0) ;
+%D drawarrow (0,0)--(0,2cm) ;
+%D label.llft(textext("(0,0)") ,origin) ;
+%D \stopstaticMPfigure
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\protected\def\startstaticMPfigure#1#2\stopstaticMPfigure
+ {\startreusableMPgraphic{\??mpstaticgraphic#1}#2\stopreusableMPgraphic}
+
+\permanent\tolerant\protected\def\startstaticMPgraphic#=#:#=#:#3\stopstaticMPgraphic
+ {\startreusableMPgraphic{\??mpstaticgraphic#1}{#2}#3\stopreusableMPgraphic}
+
+\aliased\let\stopstaticMPfigure \relax
+\aliased\let\stopstaticMPgraphic\relax
+
+\permanent\tolerant\protected\def\usestaticMPfigure[#1]#*[#2]%
+ {\ifarguments\or
+ \reuseMPgraphic{\??mpstaticgraphic#1}%
+ \else
+ \scale[#2]{\reuseMPgraphic{\??mpstaticgraphic#1}}%
+ \fi}
+
+%D Goody for preventing overflows:
+
+%frozen\def\MPdivten[#1]{\withoutpt\the\dimexpr#1pt/10\relax}
+\permanent\def\MPdivten[#1]{\thewithoutunit\dimexpr#1pt/10\relax}
+
+%D There is no way to distinguish the black color that you get when you issue a
+%D \type {draw} without color specification from a color that has an explicit black
+%D specification unless you set the variable \type {defaultcolormodel} to 1.
+%D Hoewever, in that case you cannot distinguish that draw from one with a \type
+%D {withoutcolor} specification. This means that we have to provide multiple
+%D variants of inheritance.
+%D
+%D In any case we need to tell the converter what the inherited color is to start
+%D with. Case~3 is kind of unpredictable as it closely relates to the order in which
+%D paths are flushed. If you want to inherit automatically from the surrounding, you
+%D can best stick to variant 1. Variant 0 (an isolated graphic) is the default.
+%D
+%D \startbuffer
+%D \startuseMPgraphic{test}
+%D drawoptions(withpen pencircle scaled 1pt) ;
+%D def shift_cp = currentpicture := currentpicture shifted (-15pt,0) ; enddef ;
+%D draw fullcircle scaled 10pt withoutcolor ; shift_cp ;
+%D fill fullcircle scaled 10pt ; shift_cp ;
+%D draw fullcircle scaled 10pt withoutcolor ; shift_cp ;
+%D fill fullcircle scaled 10pt withcolor red ; shift_cp ;
+%D draw fullcircle scaled 10pt withoutcolor ; shift_cp ;
+%D fill fullcircle scaled 10pt ; shift_cp ;
+%D \stopuseMPgraphic
+%D
+%D \starttabulate
+%D \NC 0\quad \NC \MPcolormethod0 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR
+%D \NC 1\quad \NC \MPcolormethod1 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR
+%D \NC 2\quad \NC \MPcolormethod2 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR
+%D \NC 3\quad \NC \MPcolormethod3 \green XX\quad \useMPgraphic{test}\quad XX \NC \NR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\newconstant\MPcolormethod
+
+\appendtoks
+ \clf_mpsetoutercolor
+ \MPcolormethod\space
+ \c_attr_colormodel\space
+ \c_attr_color\space
+ \dogetattribute{transparency}\relax
+\to \everyMPgraphic
+
+% \startMPinitializations
+% defaultcolormodel := \ifcase\MPcolormethod1\or1\or3\else3\fi;
+% \stopMPinitializations
+
+%D macros
+%D {mprunvar,mpruntab,mprunset}
+%D
+%D \starttyping
+%D \startMPcode
+%D passvariable("version","1.0") ;
+%D passvariable("number",123) ;
+%D passvariable("string","whatever") ;
+%D passvariable("point",(1.5,2.8)) ;
+%D passvariable("triplet",(1/1,1/2,1/3)) ;
+%D passvariable("quad",(1.1,2.2,3.3,4.4)) ;
+%D passvariable("boolean",false) ;
+%D passvariable("path",fullcircle scaled 1cm) ;
+%D draw fullcircle scaled 20pt ;
+%D \stopMPcode
+%D
+%D \ctxlua{inspect(metapost.variables)}
+%D
+%D \MPrunvar{version} \MPruntab{quad}{3} (\MPrunset{triplet}{,})
+%D
+%D $(x,y) = (\MPruntab{point}{1},\MPruntab{point}{2})$
+%D $(x,y) = (\MPrunset{point}{,})$
+%D \stoptyping
+
+\permanent\def\MPrunvar #1{\clf_mprunvar{#1}} \aliased\let\mprunvar\MPrunvar
+\permanent\def\MPruntab#1#2{\clf_mpruntab{#1}#2\relax} \aliased\let\mpruntab\MPruntab % #2 is number
+\permanent\def\MPrunset#1#2{\clf_mprunset{#1}{#2}} \aliased\let\mprunset\MPrunset
+
+\prependtoks \clf_mppushvariables \to \everybeforepagebody
+\appendtoks \clf_mppopvariables \to \everyafterpagebody
+
+\aliased\let\MPpushvariables\clf_mppushvariables
+\aliased\let\MPpopvariables \clf_mppopvariables
+
+%D We also provide an outputless run:
+
+\permanent\tolerant\protected\def\startMPcalculation#=#:#2\stopMPcalculation
+ {\begingroup
+ \setbox\nextbox\hpack\bgroup
+ \edef\currentMPinstance{#1}%
+ \ifempty\currentMPinstance
+ \let\currentMPinstance\defaultMPinstance
+ \fi
+ \let\currentMPgraphicname\empty
+ \edef\currentMPformat{\MPinstanceparameter\s!format}%
+ \meta_enable_include
+ \meta_process_graphic{#2;draw origin}%
+ \egroup
+ \endgroup}
+
+\aliased\let\stopMPcalculation\relax
+
+%D \macros
+%D {setupMPgraphics}
+%D
+%D Here is a generic setup command:
+
+\installcorenamespace{MPgraphics}
+
+\installsetuponlycommandhandler \??MPgraphics {MPgraphics}
+
+%D Here we hook in the outer color. When \type {color} is set to \type {global} we
+%D get the outer color automatically. If you change this setting, you should do it
+%D grouped in order not to make other graphics behave in unexpected ways.
+
+\appendtoks
+ \doifelse{\directMPgraphicsparameter\c!color}\v!global{\MPcolormethod\plusone}{\MPcolormethod\zerocount}%
+\to \everysetupMPgraphics
+
+\setupMPgraphics
+ [\c!color=\v!local]
+
+% %D This can save some runtime: rename the mpy file from a first run (when stable) to
+% %D another file and reuse it. One can also use the original filename, but a copy is
+% %D often better.
+% %D
+% %D \starttyping
+% %D \setupMPgraphics
+% %D [mpy=\jobname.mpy]
+% %D \stoptyping
+%
+% \mutable\let\MPdataMPYfile\empty
+%
+% \appendtoks
+% \edef\p_mpy{\directMPgraphicsparameter{mpy}}%
+% \ifempty\p_mpy \else
+% \let\MPdataMPYfile\p_mpy
+% \clf_registermpyfile{\p_mpy}%
+% \fi
+% \to \everysetupMPgraphics
+
+%D Some more helpers (see \type {meta-grd.mkiv} for an example of usage):
+
+\permanent\def\MPdpar#1#2{\the\dimexpr#1#2\relax\empty}
+\permanent\def\MPnpar#1#2{\the\numexpr#1#2\relax\empty}
+\permanent\def\MPspar#1#2{"#1#2"}
+
+%D Done.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-lua.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-lua.mkxl
new file mode 100644
index 00000000000..e06677d5eef
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-lua.mkxl
@@ -0,0 +1,95 @@
+%D \module
+%D [ file=meta-lua,
+%D version=2012.07.23,
+%D title=\METAPOST\ Integrated Graphics,
+%D subtitle=Templates,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Don't use this code yet. I use it in some experimental rendering of graphics
+%D based on output from database queries. It's not that pretty but will be
+%D considered when the (similar) lmx code is redone. Also, dropping the print
+%D variant makes it nicer. This experiment is part of playing with several template
+%D mechanisms. (Also see trac-lmx.)
+%D
+%D Note for myself: see if the (bar)chart code use in q2p can use this kind of
+%D magic. Basically we then need a channel to data.
+
+\writestatus{loading}{MetaPost Library Graphics / Templates}
+
+\registerctxluafile{meta-lua}{}
+
+\continueifinputfile{meta-lua.mkiv}
+
+\starttext
+
+% conforming btex ... etex
+
+\startbuffer[test-a]
+ blua for i=1,100,5 do elua
+ draw fullcircle scaled (blua p(i) elua * cm) withcolor green withpen pencircle scaled 4 ;
+ blua end elua
+
+ blua for i=1,100,10 do elua
+ draw fullcircle scaled (blua p(i) elua * cm) withcolor red withpen pencircle scaled 2 ;
+ blua end elua
+\stopbuffer
+
+\startMPpage[offset=10pt]
+ input "mpstemplate://buffer?name=test-a" ;
+\stopMPpage
+
+% conforming lmx
+
+\startbuffer[test-b]
+ <?lua for i=1,100,5 do ?>
+ draw fullcircle scaled (<?lua p(i) ?> * cm) withcolor green withpen pencircle scaled 4 ;
+ <?lua end ?>
+
+ <?lua for i=1,100,10 do ?>
+ draw fullcircle scaled (<?lua p(i) ?> * cm) withcolor red withpen pencircle scaled 2 ;
+ <?lua end ?>
+\stopbuffer
+
+\startMPpage[offset=10pt]
+ input "mpstemplate://buffer?name=test-b" ;
+\stopMPpage
+
+\startMPpage[offset=10pt]
+ picture p[] ; % we can't input nested
+ input "mpstemplate://buffer?name=test-a&method=metapost" ;
+ p[1] := currentpicture ; currentpicture := nullpicture ;
+ input "mpstemplate://buffer?name=test-b&method=xml" ;
+ p[2] := currentpicture ; currentpicture := nullpicture ;
+ draw p[1] ysized 3cm ;
+ draw p[2] ysized 4cm shifted (4cm,0) ;
+\stopMPpage
+
+% a mixture (using a wrapped input)
+
+\startMPpage[offset=10pt]
+ draw image (loadfile("mpstemplate://buffer?name=test-a&method=metapost")) ysized 3cm shifted (4cm,0cm) ;
+ draw image (loadfile("mpstemplate://buffer?name=test-b&method=xml")) ysized 3cm shifted (0cm,4cm) ;
+ draw loadimage ("mpstemplate://buffer?name=test-a&method=metapost") ysized 4cm shifted (4cm,4cm) ;
+ draw loadimage ("mpstemplate://buffer?name=test-b&method=xml") ysized 4cm shifted (0cm,0cm) ;
+\stopMPpage
+
+% conforming myself
+
+\startluacode
+ context.startMPpage { offset = "10pt" }
+ for i=1,100,5 do
+ context("draw fullcircle scaled (%s * cm) withcolor green withpen pencircle scaled 4 ;",i)
+ end
+ for i=1,100,10 do
+ context("draw fullcircle scaled (%s * cm) withcolor red withpen pencircle scaled 2 ;",i)
+ end
+ context.stopMPpage()
+\stopluacode
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-mac.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-mac.mkxl
new file mode 100644
index 00000000000..76b7396ae9e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-mac.mkxl
@@ -0,0 +1,45 @@
+%D \module
+%D [ file=meta-scn,
+%D version=2019.07.19,
+%D title=\METAPOST\ Graphics,
+%D subtitle=LMTX support,
+%D author=Hans Hagen,
+%D date=\ currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\permanent\protected\def\defineMPparameterset[#1]{\clf_lmt_parameters_define{#1}}
+\permanent\protected\def\presetMPparameters [#1]{\clf_lmt_parameters_preset{#1}}
+\permanent\protected\def\resetMPparameters [#1]{\clf_lmt_parameters_reset {#1}}
+
+\permanent\tolerant\protected\def\useMPmacro[#1]#*[#2]#*[#3]%
+ {\ifarguments\or
+ \let\currentMPinstance\defaultMPinstance
+ \meta_enable_include
+ \meta_process_graphic{lmt_#1;}%
+ \or
+ \doifelseassignment{#2}
+ {\let\currentMPinstance\defaultMPinstance
+ \meta_enable_include
+ \clf_lmt_parameters_preset{#1}[#2]%
+ \meta_process_graphic{lmt_#1;}%
+ \clf_lmt_parameters_reset{#1}}%
+ {\meta_begin_graphic_group{#1}%
+ \meta_enable_include
+ \meta_process_graphic{lmt_#1;}%
+ \meta_end_graphic_group}%
+ \or
+ \meta_begin_graphic_group{#1}%
+ \meta_enable_include
+ \clf_lmt_parameters_preset{#2}[#3]%
+ \meta_process_graphic{lmt_#2;}%
+ \clf_lmt_parameters_reset{#2}%
+ \meta_end_graphic_group
+ \fi}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-nod.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-nod.mkxl
new file mode 100644
index 00000000000..0fbfefde0c8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-nod.mkxl
@@ -0,0 +1,248 @@
+%D \module
+%D [ file=meta-nod,
+%D version=2016.11.23,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Nodes,
+%D author={Alan Braslau and Hans Hagen},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{meta-nod}{}
+
+\unprotect
+
+\defineMPinstance
+ [nodes]
+ [\s!format=metafun,
+ \s!extensions=\v!yes,
+ \s!initializations=\v!yes,
+ \c!method=\s!double]
+
+\defineframed
+ [node]
+ [\c!frame=\v!off]
+
+\defineframed
+ [smallnode]
+ [node]
+ [\c!foregroundstyle=\small]
+
+% \startMPdefinitions{nodes}
+% loadmodule "node" ;
+% \stopMPdefinitions
+
+\protect
+
+%D This module provides a simple \TEX\ layer on top of the nodes library that is
+%D preloaded in \METAFUN. More information can be found in the manual and article.
+%D
+%D We strongly advice to use the \METAPOST\ interface and only provide this \TEX\
+%D variant as proof of concept. There are no plans to extend this module because we
+%D see no advantage in using a \TEX\ interface over a \METAPOST\ one.
+%D
+%D \startbuffer
+%D \startnodes
+%D \placenode[0,0]{A}
+%D \placenode[1,0]{B}
+%D \connectnodes [0,1] [option=doublearrow]
+%D \stopnodes
+%D
+%D \startnodes[dx=2cm,rotation=45]
+%D \placenode[0,0]{A}
+%D \placenode[1,0]{B}
+%D \placenode[1,1]{C}
+%D \placenode[0,1]{D}
+%D \connectnodes [0,2] [option=doublearrow,label=a,offset=.05]
+%D \connectnodes [1,3] [option=doublearrows]
+%D \stopnodes
+%D
+%D \startnodes[dx=2cm,dy=2cm]
+%D \nodeMPcode{ahlength := 12pt ; ahangle := 30 ; ahvariant := 1 ;}
+%D \mpcode{ahlength := 12pt ; ahangle := 30 ; ahvariant := 1 ;}
+%D \placenode[0,1]{\node{A}}
+%D \placenode[1,1]{\node{B}}
+%D \placenode[0,0]{\node{C}}
+%D \placenode[1,0]{\node{D}}
+%D \connectnodes [0,3] [option=arrow,label={ }]
+%D \connectnodes [1,2] [option=arrow]
+%D \stopnodes
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection \getbuffer \stoplinecorrection
+
+\unprotect
+
+\installcorenamespace{metanodes}
+\installcorenamespace{metanodesoption}
+\installcorenamespace{metanodesalternative}
+\installcorenamespace{metanodesposition}
+
+% todo: maybe instances
+
+\installparameterhandler \??metanodes {metanodes}
+\installsetuphandler \??metanodes {metanodes}
+
+\setupmetanodes
+ [\c!option =,
+ \c!alternative =,
+ \c!offset =0,
+ \c!position =,
+ \c!label =,
+ \c!dx =2\emwidth,
+ \c!dy =2\emwidth,
+ \c!rotation =90,
+ \c!rulethickness =.5pt,
+ \c!command =]
+
+\newtoks\t_every_meta_nodes
+\newtoks\t_meta_nodes
+
+%D Hm, we started out simple but it now quickly becomes the usual mess of \TEX,
+%D \METAPOST\ and \LUA. Hard to understand.
+
+\newcount\c_meta_nodes_n
+
+\aliased\let\stopnodes\relax
+
+\permanent\tolerant\protected\def\startnodes[#1]#:#2\stopnodes
+ {\hpack\bgroup
+ \setupcurrentmetanodes[#1]%
+ \edef\p_meta_option{\metanodesparameter\c!option}%
+ \edef\p_meta_alternative{\metanodesparameter\c!alternative}%
+ \the\t_every_meta_nodes
+ \c_meta_nodes_n\zerocount
+ \t_meta_nodes\emptytoks
+ #2\removeunwantedspaces
+ % for alan, will be commented:
+ \writestatus{metanodes}{\detokenize\expandafter{\the\t_meta_nodes}}%
+ \clf_grph_nodes_initialize
+ \startMPcode
+ mfun_node_init(%
+ \the\dimexpr\metanodesparameter\c!dx\relax,%
+ \the\dimexpr\metanodesparameter\c!dy\relax,%
+ \metanodesparameter\c!rotation%
+ ) ;
+ \the\t_meta_nodes ;
+ mfun_node_flush ;
+ \stopMPcode
+ \clf_grph_nodes_reset
+ \egroup}
+
+\permanent\tolerant\protected\def\grph_nodes_node[#1]#*[#2]%
+ {\grph_nodes_node_indeed[#1][#2]}
+
+\def\grph_nodes_node_indeed[#1,#2][#3]#4%
+ {\begingroup
+ \setupcurrentmetanodes[#3]%
+ \edef\p_label{#4}%
+ \edef\p_reference{\metanodesparameter\c!reference}%
+ \ifempty\p_reference\else
+ \clf_grph_nodes_register{\p_reference}\c_meta_nodes_n\relax
+ \fi
+ \normalexpanded{\endgroup\noexpand\etoksapp\t_meta_nodes{%
+ mfun_node_make(\number#1,\number#2%
+ \ifempty\p_label
+ ,""%
+ \else
+ ,"\metanodesparameter\c!command{\p_label}"%
+ \fi
+ );%
+ }}%
+ \advance\c_meta_nodes_n\plusone}
+
+\aliased\let\placenode\relax
+
+\appendtoks
+ \enforced\let\placenode\grph_nodes_node
+\to \t_every_meta_nodes
+
+\permanent\tolerant\protected\def\grph_nodes_fromto[#1]#*[#2]%
+ {\grph_nodes_fromto_indeed[#1][#2]}% get rid of {n,m} % todo: we can use #_ or so
+
+\letvalue{\??metanodesposition }\empty
+\setvalue{\??metanodesposition top}{.top}
+\setvalue{\??metanodesposition bottom}{.bot}
+\setvalue{\??metanodesposition left}{.lft}
+\setvalue{\??metanodesposition right}{.rt}
+\setvalue{\??metanodesposition upperleft}{.ulft}
+\setvalue{\??metanodesposition topleft}{.ulft}
+\setvalue{\??metanodesposition lefttop}{.ulft}
+\setvalue{\??metanodesposition upperright}{.urt}
+\setvalue{\??metanodesposition topright}{.urt}
+\setvalue{\??metanodesposition righttop}{.urt}
+\setvalue{\??metanodesposition lowerleft}{.llft}
+\setvalue{\??metanodesposition bottomleft}{.llft}
+\setvalue{\??metanodesposition leftbottom}{.llft}
+\setvalue{\??metanodesposition lowerright}{.lrt}
+\setvalue{\??metanodesposition bottomright}{.lrt}
+\setvalue{\??metanodesposition rightbottom}{.lrt}
+
+\def\grph_nodes_fromto_indeed[#1,#2][#3]% we can't group because etoksapp doesn't like that
+ {\begingroup
+ \setupcurrentmetanodes[#3]%
+ \edef\p_label {\metanodesparameter\c!label}%
+ \edef\p_rulethickness{\metanodesparameter\c!rulethickness}%
+ \edef\p_command {\metanodesparameter\c!command}% better get an error early
+ \normalexpanded{\endgroup\noexpand\etoksapp\t_meta_nodes{%
+ \ifcsname\??metanodesalternative\metanodesparameter\c!alternative\endcsname
+ \lastnamedcs
+ \else
+ \csname\??metanodesalternative\endcsname
+ \fi
+ \space
+ mfun_nodes_fromto\begincsname\??metanodesposition\metanodesparameter\c!position\endcsname(%
+ \metanodesparameter\c!offset,%
+ % \number#1,\number#2%
+ \clf_grph_nodes_resolve{#1},\clf_grph_nodes_resolve{#2}%
+ \ifempty\p_label
+ ,""%
+ \else
+ ,"\ifempty\p_command\p_label\else\p_command{\p_label}\fi"%
+ \fi
+ )%
+ \space
+ \ifcsname\??metanodesoption\metanodesparameter\c!option\endcsname
+ \lastnamedcs
+ \else
+ \csname\??metanodesoption\endcsname
+ \fi
+ \space
+ \ifempty\p_rulethickness \else
+ withpen pencircle scaled \the\dimexpr\p_rulethickness\relax
+ \fi
+ ;%
+ }}}
+
+\setvalue{\??metanodesalternative }{draw}
+\setvalue{\??metanodesalternative arrow}{drawarrow}
+\setvalue{\??metanodesalternative doublearrow}{drawdblarrow}
+\setvalue{\??metanodesalternative doublearrows}{drawdoublearrows}
+
+\setvalue{\??metanodesoption }{}
+\setvalue{\??metanodesoption dashed}{dashed evenly}
+\setvalue{\??metanodesoption dotted}{dashed withdots scaled .5}
+
+\aliased\let\connectnodes\relax
+
+\appendtoks
+ \enforced\let\connectnodes\grph_nodes_fromto
+\to \t_every_meta_nodes
+
+\permanent\protected\def\grph_nodes_code#1%
+ {\etoksapp\t_meta_nodes{#1}}
+
+\aliased\let\mpcode \relax
+\aliased\let\nodeMPcode\relax
+
+\appendtoks
+ \enforced\let\nodeMPcode\grph_nodes_code
+ \enforced\let\mpcode \grph_nodes_code
+\to \t_every_meta_nodes
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-pag.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-pag.mkxl
new file mode 100644
index 00000000000..86de11eeb5c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-pag.mkxl
@@ -0,0 +1,26 @@
+%D \module
+%D [ file=meta-pag,
+%D version=1999.07.10,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D These definitions used to be part of the old \type {core-mps} file, later changed
+%D into \type {meta-ini}, but keeping them separate is cleaner. In the meantime not
+%D that much is left here.
+
+\writestatus{loading}{MetaPost Graphics / Page Data Management}
+
+\unprotect
+
+\prependtoks
+ \calculatereducedvsizes % bah, this is really needed
+\to \everyMPgraphic
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-pdf.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-pdf.mkxl
new file mode 100644
index 00000000000..d53ff7780f6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-pdf.mkxl
@@ -0,0 +1,95 @@
+%D \module
+%D [ file=meta-pdf,
+%D version=2006.06.07,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Conversion to \PDF,
+%D author=Hans Hagen \& others (see text),
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{meta-pdf}{}
+
+\writestatus{loading}{MetaPost Graphics / MPS to PDF}
+
+\unprotect
+
+%D \macros
+%D {convertMPtoPDF}
+%D
+%D The next set of macros implements \METAPOST\ to \PDF\ conversion. The traditional
+%D method is in the MkII files and the older \MKIV\ method is kept in meta-pdh
+%D files. The main conversion command is still the same but the scale parameters are
+%D ignored in \MKIV:
+%D
+%D \starttyping
+%D \convertMPtoPDF {filename} {x scale} {y scale}
+%D \stoptyping
+%D
+%D Much code has gone due to the fact that we now use pre and post scripts and no
+%D specials. This also means that you should use a modern \METAPOST\ or reprocess
+%D graphics. The color control flags are also gone as we now integrate into the
+%D regular colorspace handling (i.e.\ no longer independent configurations.)
+
+\permanent\def\PDFMPformoffset{\ifdefined\objectoffset\objectoffset\else\zeropoint\fi} % obsolete, will go
+
+\mutable\let\MPfilename\empty
+
+\permanent\protected\def\convertMPtoPDF#1#2#3% scaling no longer supported at this level (so #2 & #3 are ignored)
+ {\dostarttagged\t!mpgraphic\empty
+ \naturalvpack attr \imageattribute 1 \bgroup
+ \message{[MP to PDF]}%
+ \xdef\MPfilename{#1}%
+ \resetMPboundingbox
+ \forgetall
+ \offinterlineskip
+ \setbox\MPbox\vpack\bgroup
+ \clf_convertmpstopdf{\MPfilename}%
+ \removeunwantedspaces % not that needed
+ \egroup
+ \finalizeMPbox
+ \box\MPbox
+ \egroup
+ \dostoptagged}
+
+\protect \endinput
+
+%D Test code:
+%D
+%D \startbuffer
+%D \startMPcode
+%D fill fullcircle scaled 3cm withcolor red ;
+%D fill fullcircle scaled 2cm withcolor green ;
+%D fill fullcircle scaled 1cm withcolor blue ;
+%D currentpicture := currentpicture shifted (-4cm,0) ;
+%D fill fullcircle scaled 3cm withcolor cmyk(0,0,1,0) ;
+%D fill fullcircle scaled 2cm withcolor cmyk(0,1,0,0) ;
+%D fill fullcircle scaled 1cm withcolor cmyk(0,0,1,0) ;
+%D currentpicture := currentpicture shifted (-4cm,0) ;
+%D draw fullcircle scaled 3cm dashed evenly ;
+%D draw fullcircle scaled 2cm dashed withdots ;
+%D draw origin withpen pencircle scaled 3mm;
+%D currentpicture := currentpicture shifted (-4cm,0) ;
+%D fill fullcircle scaled 2cm shifted (-.5cm,+.5cm) withcolor transparent(1,.5,red);
+%D fill fullcircle scaled 2cm shifted (-.5cm,-.5cm) withcolor transparent(1,.5,red);
+%D fill fullcircle scaled 2cm shifted (+.5cm,+.5cm) withcolor transparent(1,.5,green);
+%D fill fullcircle scaled 2cm shifted (+.5cm,-.5cm) withcolor transparent(1,.5,cmyk(1,0,1,.5));
+%D currentpicture := currentpicture shifted (12cm,-4cm) ;
+%D draw "o e p s" infont defaultfont scaled 2 shifted (-1cm,0) ;
+%D currentpicture := currentpicture shifted (-4cm,0) ;
+%D % bug: shift
+%D draw fullcircle scaled 3cm withpen pencircle yscaled 3mm xscaled 2mm rotated 30 ;
+%D draw fullcircle scaled 2cm withpen pencircle yscaled 3mm xscaled 2mm rotated 20 withcolor red ;
+%D filldraw fullcircle scaled 1cm withpen pencircle yscaled 3mm xscaled 2mm rotated 10 withcolor green ;
+%D currentpicture := currentpicture shifted (-4cm,0) ;
+%D % shade cannot handle shift
+%D circular_shade(fullcircle scaled 3cm,0,.2red,.9green) ;
+%D circular_shade(fullcircle scaled 3cm shifted(+4cm,0),0,cmyk(1,0,0,0),cmyk(0,1,0,0)) ;
+%D filldraw boundingbox currentpicture enlarged -3cm withpen pencircle scaled 1pt withcolor .5white ;
+%D \stopMPcode
+%D \stopbuffer
+%D
+%D \typebuffer \startlinecorrection \getbuffer \stoplinecorrection
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-pdh.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-pdh.mkxl
new file mode 100644
index 00000000000..e2d0e15b62c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-pdh.mkxl
@@ -0,0 +1,716 @@
+%D \module
+%D [ file=meta-pdf,
+%D version=2006.06.07,
+%D title=\METAPOST\ Graphics,
+%D subtitle=Conversion to \PDF,
+%D author=Hans Hagen \& others (see text),
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\endinput
+
+%D Formerly known as supp-pdf.tex and supp-mpe.tex and meta-pdf.mkiv.
+%D
+%D Beware: this file is not used but kept for historic purposed! Never
+%D mix this one into mkiv again as it uses other variables.
+
+% \useMPgraphic{1}
+% \testfeatureonce{250}{\setbox0\hbox{\convertMPtoPDF{test-mps-mpgraph.1}{1}{1}}}
+%
+% 8.4 : mkii, direct parsing by tex
+% 11.8 : mkiv, dirty conversion (10.8 with dirty tricks)
+% 14.5 : mkiv, clean conversion
+% 7.4 : mkiv, simulated clean direct lua from mp
+% 0.3 : time taken by tex to handle converted code
+%
+% timings may differ now that we revamped the backend
+
+\registerctxluafile{meta-pdf}{}
+
+%D We will clean up the color mess later.
+
+\writestatus{loading}{MetaPost Graphics / MPS to PDF}
+
+\unprotect
+
+%D First we define a handy constant:
+
+\bgroup \catcode\commentasciicode\othercatcode \xdef\letterpercent{\string%} \egroup
+
+%D \macros
+%D {convertMPtoPDF}
+%D
+%D The next set of macros implements \METAPOST\ to \PDF\
+%D conversion. The traditional method is in the MkII file.
+%D
+%D The main conversion command is:
+%D
+%D \starttyping
+%D \convertMPtoPDF {filename} {x scale} {y scale}
+%D \stoptyping
+%D
+%D The dimensions are derived from the bounding box. So we
+%D only have to say:
+%D
+%D \starttyping
+%D \convertMPtoPDF{mp-pra-1.eps}{1}{1}
+%D \convertMPtoPDF{mp-pra-1.eps}{.5}{.5}
+%D \stoptyping
+
+%D \macros
+%D {makeMPintoPDFobject,lastPDFMPobject}
+%D
+%D For experts there are a few more options. When attributes
+%D are to be added, the code must be embedded in an object
+%D accompanied with the appropriate directives. One can
+%D influence this process with \type {\makeMPintoPDFobject}.
+%D
+%D This option defaults to~0, because \CONTEXT\ takes care
+%D of objects at another level, which saves some bytes.
+%D
+%D \starttabulate[|l|l|p|]
+%D \NC 0 \NC never \NC don't use an object \NC\NR
+%D \NC 1 \NC always \NC always use an object \NC\NR
+%D \NC 2 \NC optional \NC use object when needed \NC\NR
+%D \stoptabulate
+%D
+%D The last object number used is avaliable in the macro
+%D \type {\lastPDFMPobject}.
+
+\ifdefined\makeMPintoPDFobject \else \newconstant\makeMPintoPDFobject \fi
+\ifdefined\everyMPtoPDFconversion \else \newtoks \everyMPtoPDFconversion \fi
+
+\let\lastPDFMPobject \!!zerocount
+\let\currentPDFresources\empty
+\let\setMPextensions \relax
+
+\def\PDFMPformoffset{\ifdefined\objectoffset\objectoffset\else\zeropoint\fi}
+
+\def\resetMPvariables#1#2#3%
+ {\glet\MPwidth \!!zeropoint
+ \glet\MPheight \!!zeropoint
+ \glet\MPllx \!!zerocount
+ \glet\MPlly \!!zerocount
+ \glet\MPurx \!!zerocount
+ \glet\MPury \!!zerocount
+ \xdef\MPxscale {#2}\ifempty\MPxscale\let\MPxscale\!!plusone\fi
+ \xdef\MPyscale {#3}\ifempty\MPyscale\let\MPyscale\!!plusone\fi
+ \xdef\MPfilename {#1}}
+
+%D The main macro:
+
+\def\convertMPtoPDF#1#2#3% watch the transparency reset
+ {\resetMPvariables{#1}{#2}{#3}%
+ \vpack\bgroup
+ \forgetall
+ \offinterlineskip
+ \setbox\scratchbox\vpack\bgroup
+ \setnormalcatcodes % we can be in verbatim or so
+ \message{[MP to PDF]}%
+ \startMPresources
+ \pdfliteral{\letterpercent\space mps begin}%
+ \pdfliteral{q 1 0 0 1 0 0 cm}%
+ \ctxlua{metapost.mptopdf.convertmpstopdf("\MPfilename")}\removeunwantedspaces
+ \pdfliteral{Q}%
+ \pdfliteral{\letterpercent\space mps end}%
+ \stopMPresources
+ \egroup
+ \setbox\scratchbox\hpack\bgroup
+ \hskip-\MPllx\onebasepoint
+ \raise-\MPlly\onebasepoint
+ \box\scratchbox
+ \egroup
+ \setbox\scratchbox\vpack to \MPheight\bgroup
+ \vfill
+ \hsize\MPwidth
+ \smashbox\scratchbox
+ \box\scratchbox
+ \egroup
+ \wd\scratchbox\MPwidth
+ \ht\scratchbox\MPheight
+ \dopackageMPgraphic\scratchbox
+ \egroup}
+
+\let\processMPtoPDFfile\convertMPtoPDF
+
+%D A common hook.
+
+\let\MPfshowcommand\empty
+
+%D Objects.
+
+\def\dopackageMPgraphic#1% #1 = boxregister
+ {\ifcase\makeMPintoPDFobject\or\or\ifempty\currentPDFresources\else
+ % an existing value of 2 signals object support (set elsewhere)
+ \makeMPintoPDFobject\plusone
+ \fi\fi
+ \ifcase\makeMPintoPDFobject
+ \box#1%
+ \or
+ \scratchdimen\PDFMPformoffset\relax
+ \ifdim\scratchdimen>\zeropoint % compensate for error
+ \setbox#1\vpack spread 2\scratchdimen
+ {\forgetall\vss\hpack spread 2\scratchdimen{\hss\box#1\hss}\vss}%
+ \fi
+ \setMPPDFobject{\currentPDFresources}{#1}%
+ \ifdim\scratchdimen>\zeropoint % compensate for error
+ \vpack to \MPheight
+ {\forgetall\vss\hpack to \MPwidth{\hss\getMPPDFobject\hss}\vss}%
+ \else
+ \getMPPDFobject
+ \fi
+ \glet\currentPDFresources\empty
+ \else
+ \box#1%
+ \fi}
+
+\def\setMPPDFobject#1#2% resources boxnumber
+ {\ifdefined\everyPDFxform\the\everyPDFxform\fi
+ \immediate\saveboxresource resources{#1}#2%
+ \edef\getMPPDFobject{\noexpand\useboxresource\the\lastsavedboxresourceindex}}
+
+\let\getMPPDFobject\relax
+
+%D \macros
+%D {deleteMPgraphic,
+%D startMPresources,
+%D stopMPresources}
+
+\ifdefined\deleteMPgraphic \else
+ \def\deleteMPgraphic#1{}
+\fi
+
+\ifdefined\startMPresources \else
+ \let\startMPresources\relax
+ \let\stopMPresources\relax
+\fi
+
+%D We implement extensions by using the \METAPOST\ special
+%D mechanism. Opposite to \TEX's specials, the \METAPOST\ ones
+%D are flushed before or after the graphic data, but thereby
+%D are no longer connected to a position.
+%D
+%D We implement specials by overloading the \type {fill}
+%D operator. By counting the fills, we can let the converter
+%D treat the appropriate fill in a special way. The
+%D specification of the speciality can have two forms,
+%D determined by the setting of a boolean variable:
+%D
+%D \starttyping
+%D _inline_specials_ := false ; % comment like code (default)
+%D _inline_specials_ := true ; % command like code
+%D \stoptyping
+%D
+%D When the specification is embedded as comment, it looks
+%D like:
+%D
+%D \starttyping
+%D %%MetaPostSpecial <size> <data> <number> <identifier>
+%D \stoptyping
+%D
+%D The in||line alternative is more tuned for \POSTSCRIPT,
+%D since it permits us to define a macro \type {special}.
+%D
+%D \starttyping
+%D inline : <data> <number> <identifier> <size> special
+%D \stoptyping
+%D
+%D The \type {identifier} determines what to do, and the data
+%D can be used to accomplish this. A type~2 shading function
+%D has identifier~2. Alltogether, the number of parameters is
+%D specified in \type {size}. The \type {number} is the number
+%D of the fill that needs the special treatment. For a type~2
+%D and~3 shaded fill, the datablock contains the following
+
+%D data:
+%D
+%D \starttyping
+%D from to n inner_r g b x y outer_r g b x y
+%D from to n inner_r g b x y radius outer_r g b x y radius
+%D \stoptyping
+
+\newconditional\manyMPspecials \settrue\manyMPspecials
+
+%D In case of \PDF, we need to prepare resourcs.
+
+\newtoks\MPstartresources
+\newtoks\MPstopresources
+
+\def\startMPresources
+ {\the\MPstartresources}
+
+\def\stopMPresources
+ {\the\MPstopresources}
+
+%D Some day we may consider collecting local resources.
+
+\appendtoks
+ \glet\currentPDFresources\empty % kind of redundant
+\to \MPstartresources
+
+% \appendtoks
+% \collectPDFresources
+% \glet\currentPDFresources\collectedPDFresources
+% \to \MPstopresources
+
+\appendtoksonce
+ \the\everyPDFxform
+\to \MPstopresources
+
+%D Special number~1 is dedicated to \CMYK\ support. If you
+%D want to know why: look at this:
+%D
+%D \startbuffer[mp]
+%D fill fullcircle xyscaled (3cm,1cm) withcolor \MPcolor{test} ;
+%D \stopbuffer
+%D
+%D \startbuffer[cmyk]
+%D \startcombination[4*1]
+%D {\definecolor[test][c=1,y=.3,k=.3] \processMPbuffer[mp]} {c=1 y=.3 k=.3}
+%D {\definecolor[test][c=.9,y=.15] \processMPbuffer[mp]} {c=.9 y=.15}
+%D {\definecolor[test][c=.25,y=.8] \processMPbuffer[mp]} {c=.25 y=.8}
+%D {\definecolor[test][c=.45,y=.1] \processMPbuffer[mp]} {c=.45 y=.1}
+%D \stopcombination
+%D \stopbuffer
+%D
+%D \placefigure
+%D {\CMYK\ support disabled,
+%D conversion to \RGB.}
+%D {\setupcolors[cmyk=nee,state=start]\getbuffer[cmyk]}
+%D
+%D \placefigure
+%D {\CMYK\ support enabled,
+%D no support in \METAPOST.}
+%D {\setupcolors[cmyk=ja,mpcmyk=nee,state=start]\getbuffer[cmyk]}
+%D
+%D \placefigure
+%D {\CMYK\ support enabled,
+%D no conversion to \RGB,
+%D support in \METAPOST}
+%D {\setupcolors[cmyk=ja,state=start]\getbuffer[cmyk]}
+
+% \let\revokeMPtransparencyspecial\relax
+
+%D Transparency support used specials 60 (rgb) and 61
+%D (cmyk).
+%D
+%D \startbuffer
+%D u := 2cm ; path p ; p := fullcircle scaled u shifted (u/4,0);
+%D
+%D fill p rotated 90 withcolor transparent(1,.5,yellow) ;
+%D fill p rotated 210 withcolor transparent(1,.5,green) ;
+%D fill p rotated 330 withcolor transparent(1,.5,blue) ;
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection \processMPbuffer \stoplinecorrection
+%D
+%D One can also communicate colors between \CONTEXT\ and
+%D \METAPOST:
+%D
+%D \startbuffer
+%D \definecolor[tcyan] [c=1,k=.2,t=.5]
+%D \definecolor[tmagenta][m=1,k=.2,t=.5]
+%D \definecolor[tyellow] [y=1,k=.2,t=.5]
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D u := 2cm ; path p ; p := fullcircle scaled u shifted (u/4,0);
+%D
+%D fill p rotated 90 withcolor \MPcolor{tcyan} ;
+%D fill p rotated 210 withcolor \MPcolor{tmagenta} ;
+%D fill p rotated 330 withcolor \MPcolor{tyellow} ;
+%D \stopbuffer
+%D
+%D \startlinecorrection \processMPbuffer \stoplinecorrection
+
+%D Shading is an example of a more advanced graphic feature,
+%D but users will seldom encounter those complications. Here
+%D we only show a few simple examples, but many other
+%D alternatives are possible by setting up the functions built
+%D in \PDF\ in the appropriate way.
+%D
+%D Shading has to do with interpolation between two or more
+%D points or user supplied ranges. In \PDF, the specifications
+%D of a shade has to be encapsulated in objects and passed on
+%D as resources. This is a \PDF\ level 1.3. feature. One can
+%D simulate three dimensional shades as well and define simple
+%D functions using a limited set of \POSTSCRIPT\ primitives.
+%D Given the power of \METAPOST\ and these \PDF\ features, we
+%D can achieve superb graphic effects.
+%D
+%D Since everything is hidden in \TEX\ and \METAPOST\ graphics,
+%D we can stick to high level \CONTEXT\ command, as shown in
+%D the following exmples.
+%D
+%D \startbuffer
+%D \startuniqueMPgraphic{CircularShade}
+%D path p ; p := unitsquare xscaled OverlayWidth yscaled OverlayHeight ;
+%D circular_shade(p,0,.2red,.9red) ;
+%D \stopuniqueMPgraphic
+%D
+%D \startuniqueMPgraphic{LinearShade}
+%D path p ; p := unitsquare xscaled OverlayWidth yscaled OverlayHeight ;
+%D linear_shade(p,0,.2blue,.9blue) ;
+%D \stopuniqueMPgraphic
+%D
+%D \startuniqueMPgraphic{DuotoneShade}
+%D path p ; p := unitsquare xscaled OverlayWidth yscaled OverlayHeight ;
+%D linear_shade(p,2,.5green,.5red) ;
+%D \stopuniqueMPgraphic
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D These graphics can be hooked into the overlay mechanism,
+%D which is available in many commands.
+%D
+%D \startbuffer
+%D \defineoverlay[demo 1][\uniqueMPgraphic{CircularShade}]
+%D \defineoverlay[demo 2][\uniqueMPgraphic {LinearShade}]
+%D \defineoverlay[demo 3][\uniqueMPgraphic {DuotoneShade}]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D These backgrounds can for instance be applied to \type
+%D {\framed}:
+%D
+%D \startbuffer
+%D \setupframed[width=3cm,height=2cm,frame=off]
+%D \startcombination[3*1]
+%D {\framed[backgroundachtergrond=demo 1]{\bfd \white Demo 1}} {}
+%D {\framed[backgroundachtergrond=demo 2]{\bfd \white Demo 2}} {}
+%D {\framed[backgroundachtergrond=demo 3]{\bfd \white Demo 3}} {}
+%D \stopcombination
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D There are a few more alternatives, determined by the second
+%D parameter passed to \type {circular_shade} and alike.
+%D
+%D \def\SomeShade#1#2#3#4#5%
+%D {\startuniqueMPgraphic{Shade-#1}
+%D width := OverlayWidth ;
+%D height := OverlayHeight ;
+%D path p ; p := unitsquare xscaled width yscaled height ;
+%D #2_shade(p,#3,#4,#5) ;
+%D \stopuniqueMPgraphic
+%D \defineoverlay[Shade-#1][\uniqueMPgraphic{Shade-#1}]%
+%D \framed[backgroundachtergrond=Shade-#1,width=2cm,height=2cm,frame=off]{}}
+%D
+%D \startlinecorrection
+%D \startcombination[5*1]
+%D {\SomeShade{10}{circular}{0}{.3blue}{.9blue}} {circular 0}
+%D {\SomeShade{11}{circular}{1}{.3blue}{.9blue}} {circular 1}
+%D {\SomeShade{12}{circular}{2}{.3blue}{.9blue}} {circular 2}
+%D {\SomeShade{13}{circular}{3}{.3blue}{.9blue}} {circular 3}
+%D {\SomeShade{14}{circular}{4}{.3blue}{.9blue}} {circular 4}
+%D \stopcombination
+%D \stoplinecorrection
+%D
+%D \blank
+%D
+%D \startlinecorrection
+%D \startcombination[5*1]
+%D {\SomeShade{20}{circular}{0}{.9green}{.3green}} {circular 0}
+%D {\SomeShade{21}{circular}{1}{.9green}{.3green}} {circular 1}
+%D {\SomeShade{22}{circular}{2}{.9green}{.3green}} {circular 2}
+%D {\SomeShade{23}{circular}{3}{.9green}{.3green}} {circular 3}
+%D {\SomeShade{24}{circular}{4}{.9green}{.3green}} {circular 4}
+%D \stopcombination
+%D \stoplinecorrection
+%D
+%D \blank
+%D
+%D \startlinecorrection
+%D \startcombination[4*1]
+%D {\SomeShade{30}{linear}{0}{.3red}{.9red}} {linear 0}
+%D {\SomeShade{31}{linear}{1}{.3red}{.9red}} {linear 1}
+%D {\SomeShade{32}{linear}{2}{.3red}{.9red}} {linear 2}
+%D {\SomeShade{33}{linear}{3}{.3red}{.9red}} {linear 3}
+%D \stopcombination
+%D \stoplinecorrection
+%D
+%D These macros closely cooperate with the \METAPOST\ module
+%D \type {mp-spec.mp}, which is part of the \CONTEXT\
+%D distribution.
+%D
+%D The low level (\PDF) implementation is based on the \TEX\
+%D based \METAPOST\ to \PDF\ converter. Shading is supported
+%D by overloading the \type {fill} operator as implemented
+%D earlier. In \PDF\ type~2 and~3 shading functions are
+%D specified in terms of:
+%D
+%D \starttabulate[|Tl|l|]
+%D \NC /Domain \NC sort of meeting range \NC \NR
+%D \NC /C0 \NC inner shade \NC \NR
+%D \NC /C1 \NC outer shade \NC \NR
+%D \NC /N \NC smaller values, bigger inner circles \NC \NR
+%D \stoptabulate
+
+% \newcount\currentPDFshade % 0 % global (document wide) counter
+%
+% \def\dosetMPsomePDFshade#1#2%
+% {\immediate\pdfobj
+% {<</FunctionType 2
+% /Domain [\gMPs1 \gMPs2]
+% /C0 [\MPshadeA]
+% /C1 [\MPshadeB]
+% /N \gMPs3>>}%
+% \immediate\pdfobj
+% {<</ShadingType #1
+% /ColorSpace /\MPresolvedspace
+% /Function \the\pdflastobj\space 0 R
+% /Coords [\MPshadeC]
+% /Extend [true true]>>}%
+% \global\advance\currentPDFshade \plusone
+% \ctxlua{lpdf.adddocumentshade("Sh\the\currentPDFshade",lpdf.reference(\the\pdflastobj))}%
+% \setxvalue{\@@MPSK#2}{\noexpand\dohandleMPshade{\the\currentPDFshade}}}
+%
+% \def\dosetMPlinearshade {\dosetMPsomePDFshade2}% #1
+% \def\dosetMPcircularshade{\dosetMPsomePDFshade3}% #1
+%
+% \defineMPspecial{30}
+% {\normalexpanded{\noexpand\resolveMPrgbcolor{\gMPs4}{\gMPs5}{\gMPs6}}\to\MPshadeA
+% \normalexpanded{\noexpand\resolveMPrgbcolor{\gMPs{9}}{\gMPs{10}}{\gMPs{11}}}\to\MPshadeB
+% \edef\MPshadeC{\gMPs7 \gMPs8 \gMPs{12} \gMPs{13}}%
+% \dosetMPlinearshade{\gMPs{14}}}
+%
+% \defineMPspecial{31}
+% {\normalexpanded{\noexpand\resolveMPrgbcolor{\gMPs4}{\gMPs5}{\gMPs6}}\to\MPshadeA
+% \normalexpanded{\noexpand\resolveMPrgbcolor{\gMPs{10}}{\gMPs{11}}{\gMPs{12}}}\to\MPshadeB
+% \edef\MPshadeC{\gMPs7 \gMPs8 \gMPs9 \gMPs{13} \gMPs{14} \gMPs{15}}%
+% \dosetMPcircularshade{\gMPs{16}}}
+%
+% \defineMPspecial{32}
+% {\normalexpanded{\noexpand\resolveMPcmykcolor{\gMPs4}{\gMPs5}{\gMPs6}{\gMPs7}}\to\MPshadeA
+% \normalexpanded{\noexpand\resolveMPcmykcolor{\gMPs{10}}{\gMPs{11}}{\gMPs{12}}{\gMPs{13}}}\to\MPshadeB
+% \edef\MPshadeC{\gMPs8 \gMPs9 \gMPs{14} \gMPs{15}}%
+% \dosetMPlinearshade{\gMPs{16}}}
+%
+% \defineMPspecial{33}
+% {\normalexpanded{\noexpand\resolveMPcmykcolor{\gMPs4}{\gMPs5}{\gMPs6}{\gMPs7}}\to\MPshadeA
+% \normalexpanded{\noexpand\resolveMPcmykcolor{\gMPs{11}}{\gMPs{12}}{\gMPs{13}}{\gMPs{14}}}\to\MPshadeB
+% \edef\MPshadeC{\gMPs8 \gMPs9 \gMPs{10} \gMPs{15} \gMPs{16} \gMPs{17}}%
+% \dosetMPcircularshade{\gMPs{18}}}
+%
+% \defineMPspecial{34}
+% {\normalexpanded{\noexpand\resolveMPspotcolor{\gMPs4}{\gMPs5}{\gMPs6}{\gMPs7}}\to\MPshadeA
+% \normalexpanded{\noexpand\resolveMPspotcolor{\gMPs{10}}{\gMPs{11}}{\gMPs{12}}{\gMPs{13}}}\to\MPshadeB
+% \edef\MPshadeC{\gMPs8 \gMPs9 \gMPs{14} \gMPs{15}}%
+% \dosetMPlinearshade{\gMPs{16}}}
+%
+% \defineMPspecial{35}
+% {\normalexpanded{\noexpand\resolveMPcmykcolor{\gMPs4}{\gMPs5}{\gMPs6}{\gMPs7}}\to\MPshadeA
+% \normalexpanded{\noexpand\resolveMPcmykcolor{\gMPs{11}}{\gMPs{12}}{\gMPs{13}}{\gMPs{14}}}\to\MPshadeB
+% \edef\MPshadeC{\gMPs8 \gMPs9 \gMPs{10} \gMPs{15} \gMPs{16} \gMPs{17}}%
+% \dosetMPcircularshade{\gMPs{18}}}
+%
+% \newconditional\ignoreMPpath
+%
+% \def\dohandleMPshade#1%
+% {\revokeMPtransparencyspecial
+% \settrue\ignoreMPpath
+% \def\extraMPpathcode{/Sh#1 sh Q}%
+% \pdfliteral{q /Pattern cs}}
+%
+% \defineMPspecial{10}
+% {\setxvalue{\@@MPSK\gMPs8}%
+% {\noexpand\handleMPfigurespecial{\gMPs1}{\gMPs2}{\gMPs3}{\gMPs4}{\gMPs5}{\gMPs6}{\gMPs7}{\gMPs8}}}
+%
+% \def\handleMPfigurespecial#1#2#3#4#5#6#7#8% todo : combine with ext fig
+% {\letgvalueempty{\@@MPSK#8}%
+% \vbox to \zeropoint
+% {\vss
+% \hbox to \zeropoint
+% {\ifcase\pdfoutput\or % will be hooked into the special driver
+% \doiffileelse{#7}
+% {\doifundefinedelse{mps:x:#7}
+% {\immediate\pdfximage\s!width\onebasepoint\s!height\onebasepoint{#7}%
+% \setxvalue{mps:x:#7}{\pdfrefximage\the\pdflastximage}}%
+% {\message{[reusing figure #7]}}%
+% \pdfliteral{q #1 #2 #3 #4 #5 #6 cm}%
+% \rlap{\getvalue{mps:x:#7}}%
+% \pdfliteral{Q}}
+% {\message{[unknown figure #7]}}%
+% \fi
+% \hss}}}
+
+%D An example of using both special features is the
+%D following.
+%D
+%D \starttyping
+%D \startMPpage
+%D externalfigure "hakker1b.png" scaled 22cm rotated 10 shifted (-2cm,0cm);
+%D externalfigure "hakker1b.png" scaled 10cm rotated -10 ;
+%D externalfigure "hakker1b.png" scaled 7cm rotated 45 shifted (8cm,12cm) ;
+%D path p ; p := unitcircle xscaled 15cm yscaled 20cm;
+%D path q ; q := p rotatedaround(center p,90) ;
+%D path r ; r := buildcycle(p,q) ; clip currentpicture to r ;
+%D path s ; s := boundingbox currentpicture enlarged 5mm ;
+%D picture c ; c := currentpicture ; currentpicture := nullpicture ;
+%D circular_shade(s,0,.2red,.9red) ;
+%D addto currentpicture also c ;
+%D \stopMPpage
+%D \stoptyping
+
+% \defineMPspecial{20}
+% {\setxvalue{\@@MPSK\gMPs6}%
+% {\noexpand\handleMPhyperlink{\gMPs1}{\gMPs2}{\gMPs3}{\gMPs4}{\gMPs5}{\gMPs6}}}
+%
+% \def\handleMPhyperlink#1#2#3#4#5#6%
+% {\letgvalueempty{\@@MPSK#6}%
+% \setbox\scratchbox\hbox
+% {\setbox\scratchbox\emptyhbox
+% \wd\scratchbox\dimexpr-#1\onebasepoint+#3\onebasepoint\relax
+% \ht\scratchbox\dimexpr-#2\onebasepoint+#4\onebasepoint\relax
+% \gotobox{\box\scratchbox}[#5]}%
+% \setbox\scratchbox\hbox
+% {\hskip\dimexpr\MPxoffset\onebasepoint+#1\onebasepoint\relax
+% \raise\dimexpr\MPyoffset\onebasepoint+#2\onebasepoint\relax
+% \box\scratchbox}%
+% \smashbox\scratchbox
+% \box\scratchbox}
+
+%D This special (number 50) passes positions to a tex file.
+%D This method uses a two||pass approach an (mis|)|used the
+%D context positioning macros. In \type {core-pos} we will
+%D implement the low level submacro needed.
+%D
+%D \startbuffer
+%D \definelayer[test]
+%D
+%D \setlayer
+%D [test]
+%D [x=\MPx{somepos-1},y=\MPy{somepos-1}]
+%D {Whatever we want here!}
+%D
+%D \setlayer
+%D [test]
+%D [x=\MPx{somepos-2},y=\MPy{somepos-2}]
+%D {Whatever we need there!}
+%D
+%D \startuseMPgraphic{oeps}
+%D draw fullcircle scaled 6cm withcolor red ;
+%D register ("somepos-1",1cm,2cm,center currentpicture) ;
+%D register ("somepos-2",4cm,3cm,(-1cm,-2cm)) ;
+%D \stopuseMPgraphic
+%D
+%D \framed[background=test,offset=overlay]{\useMPgraphic{oeps}}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Here the width and height are not realy used, but one can
+%D imagine situations where tex has to work with values
+%D calculated by \METAPOST.
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D Later we will implement a more convenient macro:
+%D
+%D \starttyping
+%D \setMPlayer [test] [somepos-1] {Whatever we want here!}
+%D \setMPlayer [test] [somepos-2] {Whatever we need there!}
+%D \stoptyping
+
+\startMPinitializations
+ mp_shade_version := 2 ;
+\stopMPinitializations
+
+%D This is done much cleaner in \MPLIB.
+
+\def\MPStextext#1#2#3#4#5% if we clean up this plugin model, we can
+ {\def\MPtextdata{#3}% % delegate the splitter to lua + redesign
+ \def\MPtextsize{#2}%
+ \def\lastMPmoveX{#4}%
+ \def\lastMPmoveY{#5}%
+ \defconvertedcommand\MPtextdata\MPtextdata % no edef
+ \splitstring\MPtextdata\at::::\to\MPtexttag\and\MPtextnumber
+ \executeifdefined{handleMPtext\MPtexttag}
+ {\setbox\scratchbox\hbox % text
+ {\font\temp=#1\space at #2\onebasepoint
+ \let\c\char
+ \temp
+ \MPfshowcommand{#3}}%
+ \setbox\scratchbox\hpack
+ {\hskip#4\onebasepoint
+ \raise#5\onebasepoint
+ \box\scratchbox}%
+ \smashbox\scratchbox
+ \box\scratchbox}}
+
+We save the special variables on a stack. It's not that fast, but it make
+implementing the special more convenient.
+
+%D The boundingbox.
+
+\def\MPSboundingbox#1#2#3#4%
+ {\xdef\MPllx {#1}\xdef\MPlly{#2}\xdef\MPurx{#3}\xdef\MPury{#4}%
+ \xdef\MPwidth {\the\dimexpr#3\onebasepoint-#1\onebasepoint\relax}%
+ \xdef\MPheight{\the\dimexpr#4\onebasepoint-#2\onebasepoint\relax}}
+
+\MPSboundingbox0000
+
+%D Test code:
+
+% \startMPcode
+% fill fullcircle scaled 3cm withcolor red ;
+% fill fullcircle scaled 2cm withcolor green ;
+% fill fullcircle scaled 1cm withcolor blue ;
+% currentpicture := currentpicture shifted (-4cm,0) ;
+% fill fullcircle scaled 3cm withcolor cmyk(0,0,1,0) ;
+% fill fullcircle scaled 2cm withcolor cmyk(0,1,0,0) ;
+% fill fullcircle scaled 1cm withcolor cmyk(0,0,1,0) ;
+% currentpicture := currentpicture shifted (-4cm,0) ;
+% draw fullcircle scaled 3cm dashed evenly ;
+% draw fullcircle scaled 2cm dashed withdots ;
+% draw origin withpen pencircle scaled 3mm;
+% currentpicture := currentpicture shifted (-4cm,0) ;
+% fill fullcircle scaled 2cm shifted (-.5cm,+.5cm) withcolor transparent(1,.5,red);
+% fill fullcircle scaled 2cm shifted (-.5cm,-.5cm) withcolor transparent(1,.5,red);
+% fill fullcircle scaled 2cm shifted (+.5cm,+.5cm) withcolor transparent(1,.5,green);
+% fill fullcircle scaled 2cm shifted (+.5cm,-.5cm) withcolor transparent(1,.5,cmyk(1,0,1,.5));
+% currentpicture := currentpicture shifted (12cm,-4cm) ;
+% draw "o e p s" infont defaultfont scaled 2 shifted (-1cm,0) ;
+% currentpicture := currentpicture shifted (-4cm,0) ;
+% % bug: shift
+% draw fullcircle scaled 3cm withpen pencircle yscaled 3mm xscaled 2mm rotated 30 ;
+% draw fullcircle scaled 2cm withpen pencircle yscaled 3mm xscaled 2mm rotated 20 withcolor red ;
+% filldraw fullcircle scaled 1cm withpen pencircle yscaled 3mm xscaled 2mm rotated 10 withcolor green ;
+% currentpicture := currentpicture shifted (-4cm,0) ;
+% % shade cannot handle shift
+% circular_shade(fullcircle scaled 3cm,0,.2red,.9green) ;
+% circular_shade(fullcircle scaled 3cm shifted(+4cm,0),0,cmyk(1,0,0,0),cmyk(0,1,0,0)) ;
+% filldraw boundingbox currentpicture enlarged -3cm withpen pencircle scaled 1pt withcolor .5white ;
+% \stopMPcode
+
+% We cannot use attributes for switching colors in mp literals because
+% grouping (qQ) interferes.
+
+% \def\dohandleMPshade#1%
+% {\revokeMPtransparencyspecial
+% \settrue\ignoreMPpath
+% \def\extraMPpathcode{/#1 sh Q}%
+% \pdfliteral{q /Pattern cs}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-tex.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-tex.mkxl
new file mode 100644
index 00000000000..5937ce4083f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-tex.mkxl
@@ -0,0 +1,235 @@
+%D \module
+%D [ file=meta-tex,
+%D version=2006.06.07,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=\METAPOST\ fast text insertion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{meta-tex}{}
+
+\unprotect
+
+% Ok, we support this in MkIV because Mojca kept the pressure on. It
+% looks a bit like a hack. But in fact this method is obsolete and
+% eventually might go away.
+
+\installcorenamespace{graphictextext}
+\installcorenamespace{graphictexdepth}
+\installcorenamespace{graphictexarguments}
+
+\permanent\protected\def\startTeXtexts#1\stopTeXtexts
+ {#1}
+
+\aliased\let\stopTeXtexts\relax
+
+\permanent\tolerant\protected\def\TeXtext[#1]#:#2#3% contrary to mkii we don't process yet but we do expand
+ {\setxvalue{\??graphictextext#2}{\meta_textext_indeed{#1}{#3}}}
+
+\protected\def\meta_textext_indeed#1#2%
+ {\begingroup
+ \setbox\nextbox\hbox{#2}%
+ \ifcsname\??graphictexdepth#1\endcsname
+ \lastnamedcs
+ \else
+ \syst_boxes_lower_nextbox_dp
+ \fi
+ \box\nextbox
+ \endgroup}
+
+\permanent\def\getTeXtext#1%
+ {\begincsname\??graphictextext#1\endcsname}
+
+\letvalue{\??graphictexdepth d}\syst_boxes_lower_nextbox_dp
+\letvalue{\??graphictexdepth \s!depth}\syst_boxes_lower_nextbox_dp
+\letvalue{\??graphictexdepth n}\donothing
+\letvalue{\??graphictexdepth\s!nodepth}\donothing
+
+% old hackery, slightly modernized
+
+\permanent\protected\def\definetextext[#1]%
+ {\tolerant\defcsname\??graphictexarguments#1\endcsname}
+
+\permanent\tolerant\def\sometxt[#1]#*[#2]%
+ {\ifarguments
+ \expandafter\meta_some_txt_a
+ \or
+ \expandafter\meta_some_txt_b
+ \or
+ \expandafter\meta_some_txt_c
+ \or
+ \fi{#1}{#2}}
+
+\def\meta_some_txt_a#1#2#3{textext.drt("\clf_metapostescaped{#3}")}
+\def\meta_some_txt_b#1#2#3{textext.drt("\mpsometxt{#1}{\clf_metapostescaped{#3}}")}
+\def\meta_some_txt_c#1#2#3{textext.drt("\mpsometxt{#1}[#2]{\clf_metapostescaped{#3}}")}
+
+\permanent\protected\def\mpsometxt#1%
+ {\begincsname\??graphictexarguments#1\endcsname}
+
+% \definetextext[framed]{\framed}
+%
+% \startMPcode
+% draw \sometxt[framed]{black} rotated 45 ;
+% \stopMPcode
+%
+% But Mojca wanted more! Two arguments.
+%
+% \definetextext[framed]{\framed}
+%
+% \startMPcode
+% draw \sometxt{This is for} rotated 45 ;
+% draw \sometxt[framed][foregroundstyle=bold]{Mojca's};
+% draw \sometxt[framed]{eyes only!} rotated -45 ;
+% \stopMPcode
+%
+% \definetextext[framed][#1]#2{\framed[width=4cm]{\switchtobodyfont[#1]#2}}
+% \definetextext[simple]{\framed[width=8cm]}
+%
+% \starttext
+%
+% \startMPcode
+% draw \sometxt{This is for} rotated 45 ;
+% draw \sometxt[framed][ss,16pt] {Mojca's};
+% draw \sometxt[framed]{eyes only!} rotated -45 ;
+% draw \sometxt[simple]{Quotes "indeed" work!} rotated 180 ;
+% \stopMPcode
+%
+% \stoptext
+%
+% a lua one:
+%
+% \protected\def\definetextext[#1]#2{\setvalue{@@st@@#1}{#2}}
+%
+% \def\sometxt #1#{\dosometxt{#1}} % grab optional [args]
+% \def\dosometxt #1#2{\ctxlua{metapost.sometxt(\!!bs#1\!!es,\!!bs#2\!!es)}}
+%
+% \def\sometxta #1{textext.drt("#1")}
+% \def\sometxtb #1#2{textext.drt("\getvalue{@@st@@#1}{#2}")}
+% \def\sometxtc #1#2#3{textext.drt("\getvalue{@@st@@#1}{\switchtobodyfont[#2]#3}")}
+
+% Best use the mp macro instead since it provides positioning.
+%
+% \startuseMPgraphic{testgraphic}
+% draw \sometxt{\ruledhbox{\strut hans hagen}} scaled 3;
+% % draw textext.drt("\ruledhbox{\strut hans hagen}") scaled 3;
+% % draw textext.dlft("\ruledhbox{\strut hans hagen}") scaled 3;
+% % draw textext.lft("\ruledhbox{\strut hans hagen}") scaled 3;
+% draw llcorner currentpicture -- urcorner currentpicture withcolor yellow;
+% draw lrcorner currentpicture -- ulcorner currentpicture withcolor yellow;
+% draw boundingbox currentpicture withcolor blue ;
+% draw origin withcolor red withpen pencircle scaled 1pt;
+% \stopuseMPgraphic
+%
+% {\showstruts\useMPgraphic{testgraphic}}
+
+\permanent\protected\def\MPexponent #1#2{\mathematics{#1\times10^{#2}}}
+\permanent\protected\def\MPformatted #1#2{\clf_metapostformatted{#1}{#2}}
+\permanent\protected\def\MPgraphformat#1#2{\clf_metapostgraphformat{#1}{#2}}
+
+%D Some new fun (but exprimental for a while):
+
+\permanent\protected\def\MPLIBoutlinetext#1#2#3% index kind text
+ {\begingroup
+ \setbox\scratchbox\hbox{#3}%
+ \clf_MPLIBconvertoutlinetext\numexpr#1\relax{#2}\scratchbox
+ \endgroup}
+
+%D For now here. We don't do the whole thing, because then one can as
+%D well just export to pdf. We assume consistent integration.
+
+\definefontsynonym[serif-normal-normal] [\s!Serif]
+\definefontsynonym[serif-normal-italic] [\s!SerifItalic]
+\definefontsynonym[serif-normal-oblique][\s!SerifSlanted]
+\definefontsynonym[serif-bold-normal] [\s!SerifBold]
+\definefontsynonym[serif-bold-italic] [\s!SerifBoldItalic]
+\definefontsynonym[serif-bold-oblique] [\s!SerifBoldSlanted]
+
+\definefontsynonym[sans-normal-normal] [\s!Sans]
+\definefontsynonym[sans-normal-italic] [\s!SansItalic]
+\definefontsynonym[sans-normal-oblique] [\s!SansSlanted]
+\definefontsynonym[sans-bold-normal] [\s!SansBold]
+\definefontsynonym[sans-bold-italic] [\s!SansBoldItalic]
+\definefontsynonym[sans-bold-oblique] [\s!SansBoldSlanted]
+
+\definefontsynonym[mono-normal-normal] [\s!Mono]
+\definefontsynonym[mono-normal-italic] [\s!MonoItalic]
+\definefontsynonym[mono-normal-oblique] [\s!MonoSlanted]
+\definefontsynonym[mono-bold-normal] [\s!MonoBold]
+\definefontsynonym[mono-bold-italic] [\s!MonoBoldItalic]
+\definefontsynonym[mono-bold-oblique] [\s!MonoBoldSlanted]
+
+\installcorenamespace {svgfamily}
+
+\setvalue{\??svgfamily }{serif}
+\setvalue{\??svgfamily serif}{serif}
+\setvalue{\??svgfamily sans}{sans}
+\setvalue{\??svgfamily mono}{mono}
+
+%D This can't change because otherwise \type {\scale[width=4cm]{graphic}} fails.
+
+\permanent\protected\def\svgscaled#1%
+ {\fastsxsy{#1}{#1}\svgnormal}
+
+\permanent\protected\def\svgnormal#1#2#3#4% family weight style text
+ {\bgroup
+ \edef\p_family{#1}%
+ \ifempty\p_family\else
+ \edef\p_font{\ifcsname\??svgfamily#1\endcsname\lastnamedcs\else#1\fi-#2-#3}%
+ \predefinedfont[\s!spec:\p_font*\s!default\space @ 10bp]%
+ \fi
+ #4%
+ \egroup}
+
+\permanent\protected\def\svgcolored#1#2#3#4% r g b text
+ {\colored[r=#1,g=#2,b=#3]{#4}}
+
+% \permanent\protected\def\svgplaced#1#2#3#4% dx dy width text
+% {\doifelsenothing{#3}%
+% {\hpack}%
+% {\hpack to #3}%
+% {\raise#2\onebasepoint\hbox{\kern#1\onebasepoint\relax#4}}}
+
+\permanent\protected\def\svgplaced#1#2#3#4% dx dy width text
+ {\doifelsenothing{#3}%
+ {\hpack}%
+ {\hpack to #3}%
+ {\offsetbox[\c!y=#2]\hbox{\kern#1\relax#4}}}
+
+\newdimen\grph_svg_scratchwidth % maybe \startsvgpos etc
+
+\permanent\protected\def\svgposchar#1#2#3% dx dy char
+ {\setbox\scratchbox\hpack\bgroup
+ \raise#2\onebasepoint\hbox\bgroup
+ \kern\dimexpr#1\onebasepoint-\grph_svg_scratchwidth\relax
+ \char#3\relax
+ \egroup
+ \egroup
+ \advance\grph_svg_scratchwidth\wd\scratchbox
+ \box\scratchbox}
+
+\permanent\protected\def\svgchar#1% char
+ {\char#1\relax}
+
+\definelayer[svgmps][\c!method=\v!fit]
+
+\permanent\protected\def\svgstartlayer
+ {\resetlayer[svgmps]%
+ \setlayer[svgmps]\bgroup}
+
+\permanent\protected\def\svgstoplayer
+ {\egroup
+ \flushlayer[svgmps]}
+
+\permanent\protected\def\svgsetlayer#1#2%
+ {\setlayer[svgmps][\c!x=#1\onebasepoint,\c!y=#2\onebasepoint]} % {#3}
+
+\permanent\protected\def\svghashed#1%
+ {\clf_svghashed#1\relax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/meta-xml.mkxl b/Master/texmf-dist/tex/context/base/mkxl/meta-xml.mkxl
new file mode 100644
index 00000000000..bd93bda8c5b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/meta-xml.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=meta-xml,
+%D version=2002.11.27,
+%D title=\METAPOST\ Graphics,
+%D subtitle=XML Hacks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Graphics / XML Hacks}
+
+\unprotect
+
+% not needed in mkiv
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-cnt.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-cnt.lmt
new file mode 100644
index 00000000000..bddcb33b854
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-cnt.lmt
@@ -0,0 +1,2022 @@
+if not modules then modules = { } end modules ['mlib-cnt'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- The only useful reference that I could find about this topic is in wikipedia:
+--
+-- https://en.wikipedia.org/wiki/Marching_squares
+--
+-- I derived the edge code from:
+--
+-- https://physiology.arizona.edu/people/secomb/contours
+--
+-- and also here:
+--
+-- https://github.com/secomb/GreensV4
+--
+-- which has the banner:
+--
+-- TWS, November 1989. Converted to C September 2007. Revised February 2009.
+--
+-- and has a liberal licence. I optimized the code so that it runs a bit faster in Lua and
+-- there's probably more room for optimization (I tried several variants). For instance I
+-- don't use that many tables because access is costly. We don't have a compiler that does
+-- some optimizing (even then the c code can probably be made more efficient).
+--
+-- We often have the same node so it's cheaper to reuse the wsp tables and reconstruct
+-- the point in the path then to alias the original point. We can be more clever:
+-- straighten, but it's more work so maybe I will do that later; for now I only added
+-- a test for equality. There are some experiments in this file too and not all might
+-- work. It's a playground for me.
+--
+-- The code is meant for metafun so it is not general purpose. The bitmap variant is
+-- relatively fast and bitmaps compress well. When all is settled I might make a couple
+-- of helpers in C for this purpose but not now.
+--
+-- I removed optimization code (more aggressive flattening and such because it didn't really
+-- pay off now that we use combined paths with just line segments. I also moved some other
+-- code to a experimental copy. So we now only have the bare helpers needed here.
+
+-- Todo: look into zero case (lavel 1) for shapes ... omiting the background calculation
+-- speeds up quite a bit.
+
+local next, type, tostring = next, type, tostring
+local round, abs, min, max, floor = math.round, math.abs, math.min, math.max, math.floor
+local concat, move = table.concat, table.move
+
+local bor = bit32.bor -- it's really time to ditch support for luajit
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local resettiming = statistics.resettiming
+local elapsedtiming = statistics.elapsed
+
+local formatters = string.formatters
+local setmetatableindex = table.setmetatableindex
+local sortedkeys = table.sortedkeys
+local sequenced = table.sequenced
+
+local metapost = metapost or { }
+local mp = mp or { }
+
+local getparameterset = metapost.getparameterset
+
+local mpflush = mp.flush
+local mpcolor = mp.color
+local mpstring = mp.string
+local mpdraw = mp.draw
+local mpfill = mp.fill
+local mpflatten = mp.flatten
+
+local report = logs.reporter("mfun contour")
+
+local version = 0.007
+
+-- we iterate using integers so that we get a better behaviour at zero
+
+local f_function_n = formatters [ [[
+ local math = math
+ local round = math.round
+ %s
+ return function(data,nx,ny,nxmin,nxmax,xstep,nymin,nymax,ystep)
+ local sx = nxmin
+ for mx=1,nx do
+ local dx = data[mx]
+ local x = sx * xstep
+ local sy = nymin
+ for my=1,ny do
+ local y = sy * ystep
+ dx[my] = %s
+ sy = sy + 1
+ end
+ sx = sx + 1
+ end
+ return 0
+ end
+]] ]
+
+local f_function_y = formatters [ [[
+ local math = math
+ local round = math.round
+ local nan = NaN
+ local inf = math.huge
+ %s
+ return function(data,nx,ny,nxmin,nxmax,xstep,nymin,nymax,ystep,dnan,dinf,report)
+ local sx = nxmin
+ local er = 0
+ for mx=nxmin,nxmax do
+ local dx = data[mx]
+ local x = sx * xstep
+ local sy = nymin
+ for my=nymin,nymax do
+ local y = sy * ystep
+ local n = %s
+ if n == nan then
+ er = er + 1
+ if er < 10 then
+ report("nan at (%s,%s)",x,y)
+ end
+ n = dnan
+ elseif n == inf then
+ er = er + 1
+ if er < 10 then
+ report("inf at (%s,%s)",x,y)
+ end
+ n = dinf
+ end
+ dx[my] = n
+ sy = sy + 1
+ end
+ sx = sx + 1
+ end
+ return er
+ end
+]] ]
+
+local f_color = formatters [ [[
+ local math = math
+ local min = math.min
+ local max = math.max
+ local n = %s
+ local minz = %s
+ local maxz = %s
+
+ local color_value = 0
+ local color_step = mp.lmt_color_functions.step
+ local color_shade = mp.lmt_color_functions.shade
+
+ local function step(...)
+ return color_step(color_value,n,...)
+ end
+ local function shade(...)
+ return color_shade(color_value,n,...)
+ end
+ local function lin(l)
+ return l/n
+ end
+ %s
+ return function(l)
+ color_value = l
+ return %s
+ end
+]] ]
+
+local inbetween = attributes and attributes.colors.helpers.inbetween
+
+mp.lmt_color_functions = {
+ step = function(l,n,r,g,b,s)
+ if not s then
+ s = 1
+ end
+ local f = l / n
+ local r = s * 1.5 - 4 * abs(f-r)
+ local g = s * 1.5 - 4 * abs(f-g)
+ local b = s * 1.5 - 4 * abs(f-b)
+ return min(max(r,0),1), min(max(g,0),1), min(max(b,0),1)
+ end,
+ shade = function(l,n,one,two)
+ local f = l / n
+ local r = inbetween(one,two,1,f)
+ local g = inbetween(one,two,2,f)
+ local b = inbetween(one,two,3,f)
+ return min(max(r,0),1), min(max(g,0),1), min(max(b,0),1)
+ end,
+}
+
+local f_box = formatters [ [[draw rawtexbox("contour",%s) xysized (%s,%s) ;]] ]
+
+local n_box = 0
+
+-- todo: remove old one, so we need to store old hashes
+
+local nofcontours = 0
+
+-- We don't want cosmetics like axis and labels to trigger a calculation,
+-- especially a slow one.
+
+local hashfields = {
+ "xmin", "xmax", "xstep", "ymin", "ymax", "ystep",
+ "levels", "colors", "level", "preamble", "function", "functions", "color", "values",
+ "background", "foreground", "linewidth", "backgroundcolor", "linecolor",
+}
+
+local function prepare(p)
+ local h = { }
+ for i=1,#hashfields do
+ local f = hashfields[i]
+ h[f] = p[f]
+ end
+ local hash = md5.HEX(sequenced(h))
+ local name = formatters["%s-m-c-%03i.lua"](tex.jobname,nofcontours)
+ return name, hash
+end
+
+local function getcache(p)
+ local cache = p.cache
+ if cache then
+ local name, hash = prepare(p)
+ local data = table.load(name)
+ if data and data.hash == hash and data.version == version then
+ p.result = data
+ return true
+ else
+ return false, hash, name
+ end
+ end
+ return false, nil, nil
+end
+
+local function setcache(p)
+ local result = p.result
+ local name = result.name
+ if name then
+ if result.bitmap then
+ result.bitmap = nil
+ else
+ result.data = nil
+ end
+ result.color = nil
+ result.action = nil
+ result.cached = nil
+ io.savedata(name, table.fastserialize(result))
+ else
+ os.remove((prepare(p)))
+ end
+end
+
+function mp.lmt_contours_start()
+
+ resettiming("lmt_contours")
+ starttiming("lmt_contours")
+
+ nofcontours = nofcontours + 1
+
+ local p = getparameterset()
+
+ local xmin = p.xmin
+ local xmax = p.xmax
+ local ymin = p.ymin
+ local ymax = p.ymax
+ local xstep = p.xstep
+ local ystep = p.ystep
+ local levels = p.levels
+ local colors = p.colors
+ local nx = 0
+ local ny = 0
+ local means = setmetatableindex("number")
+ local values = setmetatableindex("number")
+ local data = setmetatableindex("table")
+ local minmean = false
+ local maxmean = false
+
+ local cached, hash, name = getcache(p)
+
+ local function setcolors(preamble,levels,minz,maxz,color,nofvalues)
+ if colors then
+ local function f(k)
+ return #colors[1] == 1 and 0 or { 0, 0, 0 }
+ end
+ setmetatableindex(colors, function(t,k)
+ local v = f(k)
+ t[k] = v
+ return v
+ end)
+ local c = { }
+ local n = 1
+ for i=1,nofvalues do
+ c[i] = colors[n]
+ n = n + 1
+ end
+ return c, f
+ else
+ local tcolor = f_color(levels,minz,maxz,preamble,color)
+ local colors = { }
+ local fcolor = load(tcolor)
+ if type(fcolor) ~= "function" then
+ report("error in color function, case %i: %s",1,color)
+ fcolor = false
+ else
+ fcolor = fcolor()
+ if type(fcolor) ~= "function" then
+ report("error in color function, case %i: %s",2,color)
+ fcolor = false
+ end
+ end
+ if not fcolor then
+ local color_step = mp.lmt_color_functions.step
+ fcolor = function(l)
+ return color_step(l,levels,0.25,0.50,0.75)
+ end
+ end
+ for i=1,nofvalues do
+ colors[i] = { fcolor(i) }
+ end
+ if attributes.colors.model == "cmyk" then
+ for i=1,#colors do
+ local c = colors[i]
+ colors[i] = { 1 - c[1], 1 - c[2], 1 - c[3], 0 }
+ end
+ end
+ return colors, fcolor
+ end
+ end
+
+ if cached then
+ local result = p.result
+ local colors, color = setcolors(
+ p.preamble,
+ p.levels,
+ result.minz,
+ result.maxz,
+ p.color,
+ result.nofvalues
+ )
+ result.color = color
+ result.colors = colors
+ result.cached = true
+ return
+ end
+
+ local functioncode = p["function"]
+ local functionrange = p.range
+ local functionlist = functionrange and p.functions
+ local preamble = p.preamble
+
+ if xstep == 0 then xstep = (xmax - xmin)/100 end
+ if ystep == 0 then ystep = (ymax - ymin)/100 end
+
+ local nxmin = round(xmin/xstep)
+ local nxmax = round(xmax/xstep)
+ local nymin = round(ymin/ystep)
+ local nymax = round(ymax/ystep)
+ local nx = nxmax - nxmin + 1
+ local ny = nymax - nymin + 1
+
+ local function executed(data,code)
+ local fcode = p.check and f_function_y or f_function_n
+ fcode = fcode(preamble,code)
+ fcode = load(fcode)
+ if type(fcode) == "function" then
+ fcode = fcode()
+ end
+ if type(fcode) == "function" then
+ local er = fcode(
+ data, nx, ny,
+ nxmin, nxmax, xstep,
+ nymin, nymax, ystep,
+ p.defaultnan, p.defaultinf, report
+ )
+ if er > 0 then
+ report("%i errors in: %s",er,code)
+ end
+ return true
+ else
+ return false -- fatal error
+ end
+ end
+
+ -- for i=1,nx do
+ -- data[i] = lua.newtable(ny,0)
+ -- end
+
+ if functionlist then
+
+ local datalist = { }
+ local minr = functionrange[1]
+ local maxr = functionrange[2] or minr
+ local size = #functionlist
+
+ for l=1,size do
+
+ local func = setmetatableindex("table")
+ if not executed(func,functionlist[l]) then
+ report("error in executing function %i from functionlist",l)
+ return
+ end
+
+ local bit = l -- + 1
+
+ if l == 1 then
+ for i=1,nx do
+ local di = data[i]
+ local fi = func[i]
+ for j=1,ny do
+ local f = fi[j]
+ if f >= minr and f <= maxr then
+ di[j] = bit
+ else
+ di[j] = 0
+ end
+ end
+ end
+ else
+ for i=1,nx do
+ local di = data[i]
+ local fi = func[i]
+ for j=1,ny do
+ local f = fi[j]
+ if f >= minr and f <= maxr then
+ di[j] = bor(di[j],bit)
+ end
+ end
+ end
+ end
+
+ end
+
+ -- we can simplify the value mess below
+
+ elseif functioncode then
+
+ if not executed(data,functioncode) then
+ report("error in executing function")
+ return -- fatal error
+ end
+
+ else
+
+ report("no valid function(s)")
+ return -- fatal error
+
+ end
+
+ minz = data[1][1]
+ maxz = minz
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ local v = d[j]
+ if v < minz then
+ minz = v
+ elseif v > maxz then
+ maxz = v
+ end
+ end
+ end
+
+ if functionlist then
+
+ for i=minz,maxz do
+ values[i] = i
+ end
+
+ levels = maxz
+
+ minmean = minz
+ maxmean = maxz
+
+ else
+
+ local snap = (maxz - minz + 1) / levels
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ local dj = d[j]
+ local v = round((dj - minz) / snap)
+ values[v] = values[v] + 1
+ means [v] = means [v] + dj
+ d[j] = v
+ end
+ end
+
+ local list = sortedkeys(values)
+ local count = values
+ local remap = { }
+
+ values = { }
+
+ for i=1,#list do
+ local v = list[i]
+ local m = means[v] / count[v]
+ remap [v] = i
+ values[i] = m
+ if not minmean then
+ minmean = m
+ maxmean = m
+ elseif m < minmean then
+ minmean = m
+ elseif m > maxmean then
+ maxmean = m
+ end
+ end
+
+ for i=1,nx do
+ local d = data[i]
+ for j=1,ny do
+ d[j] = remap[d[j]]
+ end
+ end
+
+ end
+
+ -- due to rounding we have values + 1 so we can have a floor, ceil, round
+ -- option as well as levels -1
+
+ local nofvalues = #values
+
+ local colors = setcolors(
+ p.preamble,levels,minz,maxz,p.color,nofvalues
+ )
+
+ p.result = {
+ version = version,
+ values = values,
+ nofvalues = nofvalues,
+ minz = minz,
+ maxz = maxz,
+ minmean = minmean,
+ maxmean = maxmean,
+ data = data,
+ color = color,
+ nx = nx,
+ ny = ny,
+ colors = colors,
+ name = name,
+ hash = hash,
+ islist = functionlist and true or false,
+ }
+
+ report("index %i, nx %i, ny %i, levels %i", nofcontours, nx, ny, nofvalues)
+end
+
+function mp.lmt_contours_stop()
+ local p = getparameterset()
+ stoptiming("lmt_contours")
+ setcache(p)
+ p.result = nil
+ local f = p["function"]
+ local l = p.functions
+ report("index %i, %0.3f seconds for: %s", nofcontours, elapsedtiming("lmt_contours"), "[ " .. concat(l or { f } ," ] [ ") .. " ]")
+end
+
+function mp.lmt_contours_bitmap_set()
+ local p = getparameterset()
+ local result = p.result
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local rawdata = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local colors = result.colors
+ local depth = #colors[1] -- == 3 and "rgb" or "gray"
+
+ -- i need to figure out this offset of + 1
+
+ local bitmap = graphics.bitmaps.new(
+ nx, ny,
+ (depth == 3 and "rgb") or (depth == 4 and "cmyk") or "gray",
+ 1, false, true
+ )
+
+ local palette = bitmap.index or { } -- has to start at 0
+ local data = bitmap.data
+ local p = 0
+
+ if depth == 3 or depth == 4 then
+ for i=1,nofvalues do
+ local c = colors[i]
+ local r = round((c[1] or 0) * 255)
+ local g = round((c[2] or 0) * 255)
+ local b = round((c[3] or 0) * 255)
+ local k = depth == 4 and round((c[4] or 0) * 255) or nil
+ palette[p] = {
+ (r > 255 and 255) or (r < 0 and 0) or r,
+ (g > 255 and 255) or (g < 0 and 0) or g,
+ (b > 255 and 255) or (b < 0 and 0) or b,
+ k
+ }
+ p = p + 1
+ end
+ else
+ for i=1,nofvalues do
+ local s = colors[i][1]
+ local s = round((s or 0) * 255)
+ palette[p] = (
+ (s > 255 and 255) or (s < 0 and 0) or s
+ )
+ p = p + 1
+ end
+ end
+
+ -- As (1,1) is the left top corner so we need to flip of we start in
+ -- the left bottom (we cannot loop reverse because we want a properly
+ -- indexed table.
+
+ local k = 0
+ for y=ny,1,-1 do
+ k = k + 1
+ local d = data[k]
+ for x=1,nx do
+ d[x] = rawdata[x][y] - 1
+ end
+ end
+
+ result.bitmap = bitmap
+end
+
+function mp.lmt_contours_bitmap_get()
+ local p = getparameterset()
+ local result = p.result
+ local bitmap = result.bitmap
+ local box = nodes.hpack(graphics.bitmaps.flush(bitmap))
+ n_box = n_box + 1
+ nodes.boxes.savenode("contour",tostring(n_box),box)
+ return f_box(n_box,bitmap.xsize,bitmap.ysize)
+end
+
+function mp.lmt_contours_cleanup()
+ nodes.boxes.reset("contour")
+ n_box = 0
+end
+
+function mp.lmt_contours_edge_set()
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+
+ local xmin = p.xmin
+ local xmax = p.xmax
+ local ymin = p.ymin
+ local ymax = p.ymax
+ local xstep = p.xstep
+ local ystep = p.ystep
+
+ local wsp = { }
+ local edges = { }
+
+ for value=1,nofvalues do
+
+ local iwsp = 0
+ local di = data[1]
+ local dc
+ local edge = { }
+ local e = 0
+ -- the next loop is fast
+ for i=1,nx do
+ local di1 = data[i+1]
+ local dij = di[1]
+ local d = dij - value
+ local dij1
+ for j=1,ny do
+ if j < ny then
+ dij1 = di[j+1]
+ dc = dij1 - value
+ if (d >= 0 and dc < 0) or (d < 0 and dc >= 0) then
+ iwsp = iwsp + 1
+ local y = (d * (j+1) - dc * j) / (d - dc)
+ if i == 1 then
+ wsp[iwsp] = { i, y, 0, (i + (j-1)*nx) }
+ elseif i == nx then
+ wsp[iwsp] = { i, y, (i - 1 + (j-1)*nx), 0 }
+ else
+ local jx = (i + (j-1)*nx)
+ wsp[iwsp] = { i, y, jx - 1, jx }
+ end
+ end
+ end
+ if i < nx then
+ local dc = di1[j] - value
+ if (d >= 0 and dc < 0) or (d < 0 and dc >= 0) then
+ iwsp = iwsp + 1
+ local x = (d * (i+1) - dc * i) / (d - dc)
+ if j == 1 then
+ wsp[iwsp] = { x, j, 0, (i + (j-1)*nx) }
+ elseif j == ny then
+ wsp[iwsp] = { x, j, (i + (j-2)*nx), 0 }
+ else
+ local jx = i + (j-1)*nx
+ wsp[iwsp] = { x, j, jx - nx, jx }
+ end
+ end
+ end
+ dij = dij1
+ d = dc
+ end
+ di = di1
+ end
+ -- the next loop takes time
+ for i=1,iwsp do
+ local wspi = wsp[i]
+ for isq=3,4 do
+ local nsq = wspi[isq]
+ if nsq ~= 0 then
+ local px = wspi[1]
+ local py = wspi[2]
+ local p = { px, py }
+ local pn = 2
+ wspi[isq] = 0
+ while true do
+ for ii=1,iwsp do
+ local w = wsp[ii]
+ local n1 = w[3]
+ local n2 = w[4]
+ if n1 == nsq then
+ local x = w[1]
+ local y = w[2]
+ if x ~= px or y ~= py then
+ pn = pn + 1
+ p[pn] = x
+ pn = pn + 1
+ p[pn] = y
+ px = x
+ py = y
+ end
+ nsq = n2
+ w[3] = 0
+ w[4] = 0
+ if nsq == 0 then
+ if pn == 1 then
+ pn = pn + 1
+ p[pn] = w
+ end
+ goto flush
+ end
+ elseif n2 == nsq then
+ local x = w[1]
+ local y = w[2]
+ if x ~= px or y ~= py then
+ pn = pn + 1
+ p[pn] = x
+ pn = pn + 1
+ p[pn] = y
+ px = x
+ py = y
+ end
+ nsq = n1
+ w[3] = 0
+ w[4] = 0
+ if nsq == 0 then
+ goto flush
+ end
+ end
+ end
+ end
+ ::flush::
+ e = e + 1
+ edge[e] = p
+ if mpflatten then
+ mpflatten(p)
+ end
+ end
+ end
+ end
+
+
+ edges[value] = edge
+
+ end
+
+ result.edges = edges
+
+end
+
+function mp.lmt_contours_shade_set(edgetoo)
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local color = result.color
+
+ local edges = setmetatableindex("table")
+ local shades = setmetatableindex("table")
+
+ local sqtype = setmetatableindex("table")
+
+ local xspoly = { 0, 0, 0, 0, 0, 0 }
+ local yspoly = { 0, 0, 0, 0, 0, 0 }
+ local xrpoly = { }
+ local yrpoly = { }
+
+ local xrpoly = { } -- lua.newtable(2000,0)
+ local yrpoly = { } -- lua.newtable(2000,0)
+
+ -- for i=1,2000 do
+ -- xrpoly[i] = 0
+ -- yrpoly[i] = 0
+ -- end
+
+ -- Unlike a c compiler lua will not optimize loops to run in parallel so we expand
+ -- some of the loops and make sure we don't calculate when not needed. Not that nice
+ -- but not that bad either. Maybe I should just write this from scratch.
+
+-- local i = 0
+-- local j = 0
+
+ -- Analyze each rectangle separately. Overwrite lower colors
+
+ -- Unrolling the loops and copying code and using constants is faster and doesn't
+ -- produce much more code in the end, also because we then can leave out the not
+ -- seen branches. One can argue about the foundit2* blobs but by stepwise optimizing
+ -- this is the result.
+
+ shades[1] = { { 0, 0, nx - 1, 0, nx - 1, ny - 1, 0, ny - 1 } }
+ edges [1] = { { } }
+
+ -- this is way too slow ... i must have messed up some loop .. what is this with value 1
+
+ for value=1,nofvalues do
+-- for value=2,nofvalues do
+
+ local edge = { }
+ local nofe = 0
+ local shade = { }
+ local nofs = 0
+
+ for i=1,nx-1 do
+ local s = sqtype[i]
+ for j=1,ny-1 do
+ s[j] = 0
+ end
+ end
+
+ local nrp = 0
+
+ local function addedge(a,b,c,d)
+ nofe = nofe + 1 edge[nofe] = a
+ nofe = nofe + 1 edge[nofe] = b
+ nofe = nofe + 1 edge[nofe] = c
+ nofe = nofe + 1 edge[nofe] = d
+ end
+ while true do
+ -- search for a square of type 0 with >= 1 corner above contour level
+ local i
+ local j
+ local d0 = data[1]
+ local d1 = data[2]
+ for ii=1,nx do
+ local s = sqtype[ii]
+ for jj=1,ny do
+ if s[jj] == 0 then
+ if d0[jj] > value then i = ii j = jj goto foundit end
+ if d1[jj] > value then i = ii j = jj goto foundit end
+ local j1 = jj + 1
+ if d1[j1] > value then i = ii j = jj goto foundit end
+ if d0[j1] > value then i = ii j = jj goto foundit end
+ end
+ end
+ d0 = d1
+ d1 = data[ii+1]
+ end
+ break
+ ::foundit::
+ -- initialize r-polygon (nrp seems to be 1 or 2)
+ nrp = nrp + 1
+
+ local first = true
+ local nrpoly = 0
+ local nspoly = 0
+ local nrpm = -nrp
+ -- this is the main loop
+ while true do
+ -- search for a square of type -nrp
+ if first then
+ first = false
+ if sqtype[i][j] == 0 then -- true anyway
+ goto foundit1
+ end
+ end
+ for ii=1,nx do
+ local s = sqtype[ii]
+ for jj=1,ny do
+ if s[jj] == nrpm then
+ i = ii
+ j = jj
+ goto foundit1
+ end
+ end
+ end
+ break
+ ::foundit1::
+ while true do
+
+ -- search current then neighboring squares for square type 0, with a corner in common with current square above contour level
+
+ -- top/bottom ... a bit cheating here
+
+ local i_l, i_c, i_r -- i left current right
+ local j_b, j_c, j_t -- j bottom current top
+
+ local i_n = i + 1 -- i next (right)
+ local j_n = j + 1 -- j next (top)
+
+ local i_p = i - 1 -- i previous (bottom)
+ local j_p = j - 1 -- j previous (right)
+
+ local d_c = data[i]
+ local d_r = data[i_n]
+
+ local sq
+
+ i_c = i ; j_c = j ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_c] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit21 end
+ if d_c[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit22 end
+ if d_r[j_c] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit23 end
+ if d_r[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j_p ; j_t = j_n ; goto foundit24 end
+ end end
+
+ i_c = i_n ; j_c = j ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_r[j_c] > value then i_l = i ; i_r = i_n + 1 ; j_b = j_p ; j_t = j_n ; d_c = d_r ; d_r = data[i_r] ; goto foundit21 end
+ if d_r[j_n] > value then i_l = i ; i_r = i_n + 1 ; j_b = j_p ; j_t = j_n ; d_c = d_r ; d_r = data[i_r] ; goto foundit22 end
+ end end
+
+ i_c = i ; j_c = j_n ; if i_c < nx and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j ; j_t = j_n + 1 ; goto foundit21 end
+ if d_r[j_n] > value then i_l = i_p ; i_r = i_n ; j_b = j ; j_t = j_n + 1 ; goto foundit23 end
+ end end
+
+ i_c = i_p ; j_c = j ; if i_c > 0 and j_c < ny then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j_c] > value then i_l = i_p - 1 ; i_r = i ; j_b = j_p ; j_t = j_n ; d_r = d_c ; d_c = data[i_p] ; goto foundit23 end
+ if d_c[j_n] > value then i_l = i_p - 1 ; i_r = i ; j_b = j_p ; j_t = j_n ; d_r = d_c ; d_c = data[i_p] ; goto foundit24 end
+ end end
+
+ i_c = i ; j_c = j_p ; if i < nx and j_c > 0 then sq = sqtype[i_c] if sq[j_c] == 0 then
+ if d_c[j] > value then i_l = i_p ; i_r = i_n ; j_b = j_p - 1 ; j_t = j ; goto foundit22 end
+ if d_r[j] > value then i_l = i_p ; i_r = i_n ; j_b = j_p - 1 ; j_t = j ; goto foundit24 end
+ end end
+
+ -- not found
+
+ sqtype[i][j] = nrp
+
+ break
+
+ -- define s-polygon for found square (i_c,j_c) - may have up to 6 sides
+
+ ::foundit21:: -- 1 2 3 4
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_l ; yspoly[1] = j_b
+ xspoly[2] = i_c ; yspoly[2] = j_b
+ if d_r[j_c] > value then -- dd2
+ xspoly[3] = i_c ; yspoly[3] = j_c
+ if d_r[j_t] > value then -- dd3
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ if d_c[j_t] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ end
+ elseif d_c[j_t] > value then -- dd4
+ xspoly[4] = i_c ; yspoly[4] = j_c ;
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ else
+ xspoly[4] = i_l ; yspoly[4] = j_c ; nspoly = 4
+ if edgetoo then addedge(i_c, j_c, i_l, j_c) end
+ end
+ elseif d_r[j_t] > value then -- dd3
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_c
+ if d_c[j_t] > value then -- dd4
+ xspoly[5] = i_l ; yspoly[5] = j_c ; nspoly = 5
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_c ;
+ xspoly[6] = i_l ; yspoly[6] = j_c ; nspoly = 6
+ end
+ elseif d_c[j_t] > value then -- dd4
+ if edgetoo then addedge(i_c, j_b, i_c, j_c) end
+ xspoly[3] = i_c ; yspoly[3] = j_c ;
+ xspoly[4] = i_l ; yspoly[4] = j_c ; nspoly = 4
+ else
+ if edgetoo then addedge(i_c, j_b, i_l, j_c) end
+ xspoly[3] = i_l ; yspoly[3] = j_c ; nspoly = 3
+ end
+ goto done
+
+ ::foundit22:: -- 4 1 2 3
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_l ; yspoly[1] = j_c
+ xspoly[2] = i_l ; yspoly[2] = j_b
+ if d_c[j_c] > value then -- dd2
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ if d_r[j_c] > value then -- dd3
+ xspoly[4] = i_c ; yspoly[4] = j_c
+ if d_r[j_t] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_c ; yspoly[5] = j_c ; nspoly = 5 -- suspicious, the same
+ end
+ elseif d_r[j_t] > value then -- dd4
+ xspoly[4] = i_c ; yspoly[4] = j_b ;
+ xspoly[5] = i_c ; yspoly[5] = j_c ; nspoly = 5
+ else
+ if edgetoo then addedge(i_c, j_b, i_c, j_c) end
+ xspoly[4] = i_c ; yspoly[4] = j_c ; nspoly = 4
+ end
+ elseif d_r[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b
+ xspoly[5] = i_c ; yspoly[5] = j_c
+ if d_r[j_t] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_c ; yspoly[6] = j_c ; nspoly = 6
+ end
+ elseif d_r[j_t] > value then -- dd4
+ if edgetoo then addedge(i_l, j_b, i_c, j_b) end
+ xspoly[3] = i_c ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_c ; nspoly = 4
+ else
+ if edgetoo then addedge(i_l, j_b, i_c, j_c) end
+ xspoly[3] = i_c ; yspoly[3] = j_c ; nspoly = 3
+ end
+ goto done
+
+ ::foundit23:: -- 2 3 4 1
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_c ; yspoly[1] = j_b
+ xspoly[2] = i_c ; yspoly[2] = j_c
+ if d_r[j_t] > value then -- dd2
+ xspoly[3] = i_l ; yspoly[3] = j_c
+ if d_c[j_t] > value then -- dd3
+ xspoly[4] = i_l ; yspoly[4] = j_b
+ if d_c[j_c] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_l ; yspoly[5] = j_b ; nspoly = 5
+ end
+ elseif d_c[j_c] > value then -- dd4
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ xspoly[5] = i_l ; yspoly[5] = j_b ; nspoly = 5
+ else
+ if edgetoo then addedge(i_l, j_c, i_l, j_b) end
+ xspoly[4] = i_l ; yspoly[4] = j_b ; nspoly = 4
+ end
+ elseif d_c[j_t] > value then -- dd3
+ xspoly[3] = i_c ; yspoly[3] = j_c
+ xspoly[4] = i_l ; yspoly[4] = j_c
+ xspoly[5] = i_l ; yspoly[5] = j_b
+ if d_c[j_c] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_l ; yspoly[6] = j_b ; nspoly = 6
+ end
+ elseif d_c[j_c] > value then -- dd4
+ if edgetoo then addedge(i_c, j_c, i_l, j_c) end
+ xspoly[3] = i_l ; yspoly[3] = j_c ;
+ xspoly[4] = i_l ; yspoly[4] = j_b ; nspoly = 4
+ else
+ if edgetoo then addedge(i_c, j_c, i_l, j_b) end
+ xspoly[3] = i_l ; yspoly[3] = j_b ; nspoly = 3
+ end
+ goto done
+
+ ::foundit24:: -- 3 4 1 2
+
+ sq[j_c] = nrpm
+
+ xspoly[1] = i_c ; yspoly[1] = j_c
+ xspoly[2] = i_l ; yspoly[2] = j_c
+ if d_c[j_t] > value then -- dd2
+ if d_c[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b
+ if d_r[j_c] > value then -- dd4
+ nspoly = 4
+ else
+ xspoly[5] = i_c ; yspoly[5] = j_b ; nspoly = 5
+ end
+ else
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ if d_r[j_c] > value then -- dd4
+
+ local xv34 = (dd3*i_c-dd4*i_l)/(dd3 - dd4) -- probably i_l
+ print("4.4 : xv34",xv34,i_c,i_l)
+
+ -- if edgetoo then addedge(i_l, j_b, xv34, j_b) end
+ xspoly[4] = xv34 ; yspoly[4] = j_b ;
+ xspoly[5] = i_c ; yspoly[5] = j_b ; nspoly = 5
+ else
+ if edgetoo then addedge(i_l, j_b, i_c, j_b) end
+ xspoly[4] = i_c ; yspoly[4] = j_b ; nspoly = 4
+ end
+ end
+ elseif d_c[j_c] > value then -- dd3
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_l ; yspoly[4] = j_b
+ xspoly[5] = i_c ; yspoly[5] = j_b
+ if d_r[j_c] > value then -- dd4
+ nspoly = 5
+ else
+ xspoly[6] = i_c ; yspoly[6] = j_b ; nspoly = 6
+ end
+ elseif d_r[j_c] > value then -- dd4
+ if edgetoo then addedge(i_l, j_c, i_l, j_b) end
+ xspoly[3] = i_l ; yspoly[3] = j_b
+ xspoly[4] = i_c ; yspoly[4] = j_b ; nspoly = 4
+ else
+ if edgetoo then addedge(i_l, j_c, i_c, j_b) end
+ xspoly[3] = i_c ; yspoly[3] = j_b ; nspoly = 3
+ end
+ -- goto done
+
+ ::done::
+ -- combine s-polygon with existing r-polygon, eliminating redundant segments
+
+ if nrpoly == 0 then
+ -- initiate r-polygon
+ for i=1,nspoly do
+ xrpoly[i] = xspoly[i]
+ yrpoly[i] = yspoly[i]
+ end
+ nrpoly = nspoly
+ else
+ -- search r-polygon and s-polygon for one side that matches
+ --
+ -- this is a bottleneck ... we keep this variant here but next go for a faster
+ -- alternative
+ --
+ -- local ispoly, irpoly
+ -- for r=nrpoly,1,-1 do
+ -- local r1
+ -- for s=1,nspoly do
+ -- local s1 = s % nspoly + 1
+ -- if xrpoly[r] == xspoly[s1] and yrpoly[r] == yspoly[s1] then
+ -- if not r1 then
+ -- r1 = r % nrpoly + 1
+ -- end
+ -- if xrpoly[r1] == xspoly[s] and yrpoly[r1] == yspoly[s] then
+ -- ispoly = s
+ -- irpoly = r
+ -- goto foundit3
+ -- end
+ -- end
+ -- end
+ -- end
+ --
+ -- local ispoly, irpoly
+ -- local xr1 = xrpoly[1]
+ -- local yr1 = yrpoly[1]
+ -- for r0=nrpoly,1,-1 do
+ -- for s0=1,nspoly do
+ -- if xr1 == xspoly[s0] and yr1 == yspoly[s0] then
+ -- if s0 == nspoly then
+ -- if xr0 == xspoly[1] and yr0 == yspoly[1] then
+ -- ispoly = s0
+ -- irpoly = r0
+ -- goto foundit3
+ -- end
+ -- else
+ -- local s1 = s0 + 1
+ -- if xr0 == xspoly[s1] and yr0 == yspoly[s1] then
+ -- ispoly = s0
+ -- irpoly = r0
+ -- goto foundit3
+ -- end
+ -- end
+ -- end
+ -- end
+ -- xr1 = xrpoly[r0]
+ -- yr1 = yrpoly[r0]
+ -- end
+ --
+ -- but ...
+ --
+ local minx = xspoly[1]
+ local miny = yspoly[1]
+ local maxx = xspoly[1]
+ local maxy = yspoly[1]
+ for i=1,nspoly do
+ local y = yspoly[i]
+ if y < miny then
+ miny = y
+ elseif y > maxy then
+ maxy = y
+ end
+ local x = xspoly[i]
+ if x < minx then
+ minx = y
+ elseif x > maxx then
+ maxx = x
+ end
+ end
+ -- we can delay accessing y ...
+ local ispoly, irpoly
+ local xr1 = xrpoly[1]
+ local yr1 = yrpoly[1]
+ for r0=nrpoly,1,-1 do
+ if xr1 >= minx and xr1 <= maxx and yr1 >= miny and yr1 <= maxy then
+ local xr0 = xrpoly[r0]
+ local yr0 = yrpoly[r0]
+ for s0=1,nspoly do
+ if xr1 == xspoly[s0] and yr1 == yspoly[s0] then
+ if s0 == nspoly then
+ if xr0 == xspoly[1] and yr0 == yspoly[1] then
+ ispoly = s0
+ irpoly = r0
+ goto foundit3
+ end
+ else
+ local s1 = s0 + 1
+ if xr0 == xspoly[s1] and yr0 == yspoly[s1] then
+ ispoly = s0
+ irpoly = r0
+ goto foundit3
+ end
+ end
+ end
+ end
+ xr1 = xr0
+ yr1 = yr0
+ else
+ xr1 = xrpoly[r0]
+ yr1 = yrpoly[r0]
+ end
+ end
+ --
+ goto nomatch3
+ ::foundit3::
+ local match1 = 0
+ local rpoly1 = irpoly + nrpoly
+ local spoly1 = ispoly - 1
+ for i=2,nspoly-1 do
+ -- search for further matches nearby
+ local ir = (rpoly1 - i) % nrpoly + 1
+ local is = (spoly1 + i) % nspoly + 1
+ if xrpoly[ir] == xspoly[is] and yrpoly[ir] == yspoly[is] then
+ match1 = match1 + 1
+ else
+ break -- goto nomatch1
+ end
+ end
+ ::nomatch1::
+ local match2 = 0
+ local rpoly2 = irpoly - 1
+ local spoly2 = ispoly + nspoly
+ for i=2,nspoly-1 do
+ -- search other way for further matches nearby
+ local ir = (rpoly2 + i) % nrpoly + 1
+ local is = (spoly2 - i) % nspoly + 1
+ if xrpoly[ir] == xspoly[is] and yrpoly[ir] == yspoly[is] then
+ match2 = match2 + 1
+ else
+ break -- goto nomatch2
+ end
+ end
+ ::nomatch2::
+ -- local dnrpoly = nspoly - 2 - 2*match1 - 2*match2
+ local dnrpoly = nspoly - 2*(match1 + match2 + 1)
+ local ispolystart = (ispoly + match1) % nspoly + 1 -- first node of s-polygon to include
+ local irpolyend = (rpoly1 - match1 - 1) % nrpoly + 1 -- last node of s-polygon to include
+ if dnrpoly ~= 0 then
+ local irpolystart = (irpoly + match2) % nrpoly + 1 -- first node of s-polygon to include
+ if irpolystart > irpolyend then
+ -- local ispolyend = (spoly1 - match2 + nspoly)%nspoly + 1 -- last node of s-polygon to include
+ if dnrpoly > 0 then
+ -- expand the arrays xrpoly and yrpoly
+ for i=nrpoly,irpolystart,-1 do
+ local k = i + dnrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ else -- if dnrpoly < 0 then
+ -- contract the arrays xrpoly and yrpoly
+ for i=irpolystart,nrpoly do
+ local k = i + dnrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ end
+ end
+ nrpoly = nrpoly + dnrpoly
+ end
+ if nrpoly < irpolyend then
+ for i=irpolyend,nrpoly+1,-1 do
+ -- otherwise these values get lost!
+ local k = i - nrpoly
+ xrpoly[k] = xrpoly[i]
+ yrpoly[k] = yrpoly[i]
+ end
+ end
+ local n = nspoly - 2 - match1 - match2
+ if n == 1 then
+ local irpoly1 = irpolyend % nrpoly + 1
+ local ispoly1 = ispolystart % nspoly + 1
+ xrpoly[irpoly1] = xspoly[ispoly1]
+ yrpoly[irpoly1] = yspoly[ispoly1]
+ elseif n > 0 then
+ -- often 2
+ for i=1,n do
+ local ii = i - 1
+ local ir = (irpolyend + ii) % nrpoly + 1
+ local is = (ispolystart + ii) % nspoly + 1
+ xrpoly[ir] = xspoly[is]
+ yrpoly[ir] = yspoly[is]
+ end
+ end
+ ::nomatch3::
+ end
+ end
+ end
+
+ if nrpoly > 0 then
+ local t = { }
+ local n = 0
+ for i=1,nrpoly do
+ n = n + 1 t[n] = xrpoly[i]
+ n = n + 1 t[n] = yrpoly[i]
+ end
+ if mpflatten then
+ mpflatten(t) -- maybe integrate
+ end
+ nofs = nofs + 1
+ shade[nofs] = t
+ -- print(value,nrpoly,#t,#t-nrpoly*2)
+ end
+
+ end
+
+ edges [value+1] = edge
+ shades[value+1] = shade
+-- edges [value] = edge
+-- shades[value] = shade
+ end
+
+ result.shades = shades
+ result.shapes = edges
+
+end
+
+-- accessors
+
+function mp.lmt_contours_nx (i) return getparameterset().result.nx end
+function mp.lmt_contours_ny (i) return getparameterset().result.ny end
+
+function mp.lmt_contours_nofvalues() return getparameterset().result.nofvalues end
+function mp.lmt_contours_value (i) return getparameterset().result.values[i] end
+
+function mp.lmt_contours_minz (i) return getparameterset().result.minz end
+function mp.lmt_contours_maxz (i) return getparameterset().result.maxz end
+
+function mp.lmt_contours_minmean (i) return getparameterset().result.minmean end
+function mp.lmt_contours_maxmean (i) return getparameterset().result.maxmean end
+
+function mp.lmt_contours_xrange () local p = getparameterset() mpstring(formatters["x = [%.3N,%.3N] ;"](p.xmin,p.xmax)) end
+function mp.lmt_contours_yrange () local p = getparameterset() mpstring(formatters["y = [%.3N,%.3N] ;"](p.ymin,p.ymax)) end
+
+function mp.lmt_contours_format()
+ local p = getparameterset()
+ return mpstring(p.result.islist and "@i" or p.zformat or p.format)
+end
+
+function mp.lmt_contours_function()
+ local p = getparameterset()
+ return mpstring(p.result.islist and concat(p["functions"], ", ") or p["function"])
+end
+
+function mp.lmt_contours_range()
+ local p = getparameterset()
+ local r = p.result.islist and p.range
+ if not r or #r == 0 then
+ return mpstring("")
+ elseif #r == 1 then
+ return mpstring(r[1])
+ else
+ return mpstring(formatters["z = [%s,%s]"](r[1],r[2]))
+ end
+end
+
+function mp.lmt_contours_edge_paths(value)
+ mpdraw(getparameterset().result.edges[value],true)
+ mpflush()
+end
+
+function mp.lmt_contours_shape_paths(value)
+ mpdraw(getparameterset().result.shapes[value],false)
+ mpflush()
+end
+
+function mp.lmt_contours_shade_paths(value)
+ mpfill(getparameterset().result.shades[value],true)
+ mpflush()
+end
+
+function mp.lmt_contours_color(value)
+ local p = getparameterset()
+ local color = p.result.colors[value]
+ if color then
+ mpcolor(color)
+ end
+end
+
+-- The next code is based on the wikipedia page. It was a bit tedius job to define the
+-- coordinates but hupefully I made no errors. I rendered all shapes independently and
+-- tripple checked bit one never knows ...
+
+-- maybe some day write from scatch, like this (axis are swapped):
+
+local d = 1/2
+
+local paths = {
+ { 0, d, d, 0 },
+ { 1, d, d, 0 },
+ { 0, d, 1, d },
+ { 1, d, d, 1 },
+ { 0, d, d, 1, d, 0, 1, d }, -- saddle
+ { d, 0, d, 1 },
+ { 0, d, d, 1 },
+ { 0, d, d, 1 },
+ { d, 0, d, 1 },
+ { 0, d, d, 0, 1, d, d, 1 }, -- saddle
+ { 1, d, d, 1 },
+ { 0, d, 1, d },
+ { d, 0, 1, d },
+ { d, 0, 0, d },
+}
+
+local function whatever(data,nx,ny,threshold)
+ local edges = { }
+ local e = 0
+ local d0 = data[1]
+ for j=1,ny-1 do
+ local d1 = data[j+1]
+ local k = j + 1
+ for i=1,nx-1 do
+ local v = 0
+ local l = i + 1
+ local c1 = d0[i]
+ if c1 < threshold then
+ v = v + 8
+ end
+ local c2 = d0[l]
+ if c2 < threshold then
+ v = v + 4
+ end
+ local c3 = d1[l]
+ if c3 < threshold then
+ v = v + 2
+ end
+ local c4 = d1[i]
+ if c4 < threshold then
+ v = v + 1
+ end
+ if v > 0 and v < 15 then
+ if v == 5 or v == 10 then
+ local a = (c1 + c2 + c3 + c4) / 4
+ if a < threshold then
+ v = v == 5 and 10 or 5
+ end
+ local p = paths[v]
+ e = e + 1 edges[e] = k - p[2]
+ e = e + 1 edges[e] = i + p[1]
+ e = e + 1 edges[e] = k - p[4]
+ e = e + 1 edges[e] = i + p[3]
+ e = e + 1 edges[e] = k - p[6]
+ e = e + 1 edges[e] = i + p[5]
+ e = e + 1 edges[e] = k - p[8]
+ e = e + 1 edges[e] = i + p[7]
+ else
+ local p = paths[v]
+ e = e + 1 edges[e] = k - p[2]
+ e = e + 1 edges[e] = i + p[1]
+ e = e + 1 edges[e] = k - p[4]
+ e = e + 1 edges[e] = i + p[3]
+ end
+ end
+ end
+ d0 = d1
+ end
+ return edges
+end
+
+-- todo: just fetch when needed, no need to cache
+
+function mp.lmt_contours_edge_set_by_cell()
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local lines = { }
+ result.lines = lines
+ for value=1,nofvalues do
+ lines[value] = whatever(data,ny,nx,value)
+ end
+end
+
+function mp.lmt_contours_edge_get_cell(value)
+ mpdraw(getparameterset().result.lines[value])
+ mpflush()
+end
+
+local singles = {
+ { d, 0, 0, 0, 0, d }, -- 1 0001
+ { d, 0, 0, d }, -- 2 0002
+ { 1, d, 1, 0, d, 0 }, -- 3 0010
+ { 1, d, 1, 0, 0, 0, 0, d }, -- 4 0011
+ { 1, d, 1, 0, d, 0, 0, d }, -- 5 0012
+ { 1, d, d, 0 }, -- 6 0020
+ { 1, d, d, 0, 0, 0, 0, d }, -- 7 0021
+ { 1, d, 0, d }, -- 8 0022
+ { d, 1, 1, 1, 1, d }, -- 9 0100
+ false, -- 10 0101
+ false, -- 11 0102
+ { d, 1, 1, 1, 1, 0, d, 0 }, -- 12 0110
+ { d, 1, 1, 1, 1, 0, 0, 0, 0, d }, -- 13 0111
+ { d, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 14 0112
+ { d, 1, 1, 1, 1, d, d, 0 }, -- 15 0120
+ { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, -- 16 0121
+ { d, 1, 1, 1, 1, d, 0, d }, -- 17 0122
+ { d, 1, 1, d }, -- 18 0200
+ false, -- 19 0201
+ false, -- 20 0202
+ { d, 1, 1, d, 1, 0, d, 0 }, -- 21 0210
+ { d, 1, 1, d, 1, 0, 0, 0, 0, d }, -- 22 0211
+ false, -- 23 0212
+ { d, 1, d, 0 }, -- 24 0220
+ { d, 1, d, 0, 0, 0, 0, d }, -- 25 0221
+ { d, 1, 0, d }, -- 26 0222
+ { 0, 1, d, 1, 0, d }, -- 27 1000
+ { 0, 1, d, 1, d, 0, 0, 0 }, -- 28 1001
+ { 0, 1, d, 1, d, 0, 0, d }, -- 29 1002
+ false, -- 30 1010
+ { 0, 1, d, 1, 1, d, 1, 0, 0, 0 }, -- 31 1011
+ { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, -- 32 1012
+ false, -- 33 1020
+ { 0, 1, d, 1, 1, d, d, 0, 0, 0 }, -- 34 1021
+ { 0, 1, d, 1, 1, d, 0, d }, -- 35 1022
+ { 0, 1, 1, 1, 1, d, 0, d }, -- 36 1100
+ { 0, 1, 1, 1, 1, d, d, 0, 0, 0 }, -- 37 1101
+ { 0, 1, 1, 1, 1, d, d, 0, 0, d }, -- 38 1102
+ { 0, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 39 1110
+ { 0, 1, 1, 1, 1, 0, 0, 0 }, -- 40 1111
+ { 0, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 41 1112
+ { 0, 1, 1, 1, 1, d, d, 0, 0, d }, -- 42 1120
+ { 0, 1, 1, 1, 1, d, d, 0, 0, 0 }, -- 43 1121
+ { 0, 1, 1, 1, 1, d, 0, d }, -- 44 1122
+ { 0, 1, d, 1, 1, d, 0, d }, -- 45 1200
+ { 0, 1, d, 1, 1, d, d, 0, 0, 0 }, -- 46 1201
+ false, -- 47 1202
+ { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, -- 48 1210
+ { 0, 1, d, 1, 1, d, 1, 0, 0, 0 }, -- 49 1211
+ false, -- 50 1212
+ { 0, 1, d, 1, d, 0, 0, d }, -- 51 1220
+ { 0, 1, d, 1, d, 0, 0, 0 }, -- 52 1221
+ { 0, 1, d, 1, 0, d }, -- 53 1222
+ { d, 1, 0, d }, -- 54 2000
+ { d, 1, d, 0, 0, 0, 0, d }, -- 55 2001
+ { d, 1, d, 0 }, -- 56 2002
+ false, -- 57 2010
+ { d, 1, 1, d, 1, 0, 0, 0, 0, d }, -- 58 2011
+ { d, 1, 1, d, 1, 0, d, 0 }, -- 59 2012
+ false, -- 60 2020
+ false, -- 61 2021
+ { d, 1, 1, d }, -- 62 2022
+ { d, 1, 1, 1, 1, d, 0, d }, -- 63 2100
+ { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, -- 64 2101
+ { d, 1, 1, 1, 1, d, d, 0 }, -- 65 2102
+ { d, 1, 1, 1, 1, 0, d, 0, 0, d }, -- 66 2110
+ { d, 1, 1, 1, 1, 0, 0, 0, 0, d }, -- 67 2111
+ { d, 1, 1, 1, 1, 0, d, 0 }, -- 68 2112
+ false, -- 69 2120
+ false, -- 70 2121
+ { d, 1, 1, 1, 1, d }, -- 71 2122
+ { 1, d, 0, d }, -- 72 2200
+ { 1, d, d, 0, 0, 0, 0, d }, -- 73 2201
+ { 1, d, d, 0 }, -- 74 2202
+ { 1, d, 1, 0, d, 0, 0, d }, -- 75 2210
+ { 1, d, 1, 0, 0, 0, 0, d }, -- 76 2211
+ { 1, d, 1, 0, d, 0 }, -- 77 2212
+ { d, 0, 0, d }, -- 78 2220
+ { 0, d, 0, 0, d, 0 }, -- 79 2221
+}
+
+local sadles = {
+ false, false, false, false, false, false, false, false, false,
+ { { d, 1, 1, 1, 1, d }, { d, 0, 0, 0, 0, d }, { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, false, false, false }, -- 10 0101
+ { { d, 1, 1, 1, 1, d }, { d, 0, 0, d }, { d, 1, 1, 1, 1, d, d, 0, 0, d }, false, false, false }, -- 11 0102
+ false, false, false, false, false, false, false,
+ { { d, 1, 1, d }, { d, 0, 0, 0, 0, d }, { d, 1, 1, d, d, 0, 0, 0, 0, d }, false, false, false }, -- 19 0201
+ { { d, 1, 1, d }, { d, 0, 0, d }, { d, 1, 1, d, d, 0, 0, d }, false, { d, 1, 0, d }, { 1, d, d, 0 } }, -- 20 0202
+ false, false,
+ { false, false, { d, 1, 1, d, 1, 0, d, 0, 0, d }, false, { d, 1, 0,d, }, { 1, d, 1, 0,d, 0 } }, -- 23 0212
+ false, false, false, false, false, false,
+ { { 0, 1, d, 1, 0, d }, { 1, d, 1, 0, d, 0 }, { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, false, false, false }, -- 30 1010
+ false, false,
+ { { 1, 0, d, 0, 0, d, }, { 1, d, d, 0 }, { 0, 1, d, 1, 1, d, d, 0, 0, d }, false, false, false }, -- 33 1020
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ { false, false, { 0,1, d, 1, 1, d, d, 0, 0, d }, false, { 0,1, d, 1, 0, d }, {1, d, d, 0 } }, -- 47 1202
+ false, false,
+ { false, false, { 0, 1, d, 1, 1, d, 1, 0, d, 0, 0, d }, false, { 0, 1, d, 1, 0, d }, { 1, d, 1, 0, d, 0 } }, -- 50 1212
+ false, false, false, false, false, false,
+ { { d, 1, 0, d }, { 1, d, 1, 0, 0, d }, { d, 1, 1, d, 1, 0, d, 0, 0, d }, false, false, false }, -- 57 2010
+ false, false,
+ { { d, 1, 0,d }, { 1, d, d, 0 }, { d, 1, 1, d, d, 0, 0, d }, false, { d, 1, 1, d }, { d, 0, 0, d } }, -- 60 2020
+ { false, false, { d, 1, 1, d, d, 0, 0, 0, 0, d }, false, { d, 1, 1, d }, { d, 0, 0, 0, 0, d } }, -- 61 2021
+ false, false, false, false, false, false, false,
+ { false, false, { d, 1, 1, 1, 1, d, d, 0, 0, d }, false, { d, 1, 1, 1, 1, d }, { d, 0,0,d } }, -- 69 2120
+ { false, false, { d, 1, 1, 1, 1, d, d, 0, 0, 0, 0, d }, false, { d, 1, 1, 1, 1, d }, { d, 0, 0, 0, 0, d } }, -- 70 2121
+}
+
+local function whatever(data,nx,ny,threshold,background)
+
+ if background then
+
+ local llx = 1/2
+ local lly = llx
+ local urx = ny + llx
+ local ury = nx + lly
+
+ return { { llx, lly, urx, 0, urx, ury, 0, ury } }
+
+ else
+
+ local bands = { }
+ local b = 0
+
+ local function band(s,n,x,y) -- simple. no closure so fast
+ if n == 6 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ }
+ elseif n == 8 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7],
+ }
+ elseif n == 10 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7], x - s[10], y + s[ 9],
+ }
+ elseif n == 4 then
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3],
+ }
+ else -- 12
+ return {
+ x - s[ 2], y + s[ 1], x - s[ 4], y + s[ 3], x - s[ 6], y + s[ 5],
+ x - s[ 8], y + s[ 7], x - s[10], y + s[ 9], x - s[12], y + s[11],
+ }
+ end
+ end
+
+ local pp = { }
+
+ local d0 = data[1]
+ for j=1,ny-1 do
+ local d1 = data[j+1]
+ local k = j + 1
+ local p = false
+ for i=1,nx-1 do
+ local v = 0
+ local l = i + 1
+ local c1 = d0[i]
+ if c1 == threshold then
+ v = v + 27
+ elseif c1 > threshold then
+ v = v + 54
+ end
+ local c2 = d0[l]
+ if c2 == threshold then
+ v = v + 9
+ elseif c2 > threshold then
+ v = v + 18
+ end
+ local c3 = d1[l]
+ if c3 == threshold then
+ v = v + 3
+ elseif c3 > threshold then
+ v = v + 6
+ end
+ local c4 = d1[i]
+ if c4 == threshold then
+ v = v + 1
+ elseif c4 > threshold then
+ v = v + 2
+ end
+ if v > 0 and v < 80 then
+ if v == 40 then
+ -- a little optimization: full areas appended horizontally
+ if p then
+ p[4] = l -- i + 1
+ p[6] = l -- i + 1
+ else
+ -- x-0 y+1 x-1 y+1 x-1 y+0 x-0 y+0
+ p = { j, i, j, l, k, l, k, i }
+ b = b + 1 ; bands[b] = p
+ end
+ else
+ local s = singles[v]
+ if s then
+ b = b + 1 ; bands[b] = band(s,#s,k,i)
+ else
+ local s = sadles[v]
+ if s then
+ local m = (c1 + c2 + c3 + c4) / 4
+ if m < threshold then
+ local s1 = s[1] if s1 then b = b + 1 ; bands[b] = band(s1,#s1,i,j) end
+ local s2 = s[2] if s2 then b = b + 1 ; bands[b] = band(s2,#s2,i,j) end
+ elseif m == threshold then
+ local s3 = s[3] if s3 then b = b + 1 ; bands[b] = band(s3,#s3,i,j) end
+ local s4 = s[4] if s4 then b = b + 1 ; bands[b] = band(s4,#s4,i,j) end
+ else
+ local s5 = s[5] if s5 then b = b + 1 ; bands[b] = band(s5,#s5,i,j) end
+ local s6 = s[6] if s6 then b = b + 1 ; bands[b] = band(s6,#s6,i,j) end
+ end
+ end
+ end
+ p = false
+ end
+ else
+ p = false
+ end
+ end
+ d0 = d1
+ end
+ return bands
+ end
+end
+
+function mp.lmt_contours_edge_set_by_band(value)
+ local p = getparameterset()
+ local result = p.result
+
+ if result.cached then return end
+
+ local values = result.values
+ local nofvalues = result.nofvalues
+ local data = result.data
+ local nx = result.nx
+ local ny = result.ny
+ local bands = { }
+ result.bands = bands
+ for value=1,nofvalues do
+ bands[value] = whatever(data,ny,nx,value,value == 1)
+ end
+end
+
+function mp.lmt_contours_edge_get_band(value)
+ mpfill(getparameterset().result.bands[value],true)
+ mpflush()
+end
+
+-- Because we share some code surface plots also end up here. When working on the
+-- contour macros by concidence I ran into a 3D plot in
+--
+-- https://staff.science.uva.nl/a.j.p.heck/Courses/mptut.pdf
+--
+-- The code is pure MetaPost and works quite well. With a bit of optimization
+-- performance is also ok, but in the end a Lua solution is twice as fast and also
+-- permits some more tweaking at no cost. So, below is an adaptation of an example
+-- in the mentioned link. It's one of these cases where access to pseudo arrays
+-- is slowing down MP.
+
+local sqrt, sin, cos = math.sqrt, math.sin, math.cos
+
+local f_fill_rgb = formatters["F (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N) ;"]
+local f_draw_rgb = formatters["D (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor %.3F ;"]
+local f_mesh_rgb = formatters["U (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N) ;"]
+local f_fill_cmy = formatters["F (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N,0) ;"]
+local f_draw_cmy = formatters["D (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor %.3F ;"]
+local f_mesh_cmy = formatters["U (%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--(%.6N,%.6N)--C withcolor (%.3N,%.3N,%.3N,0) ;"]
+
+local f_function_n = formatters [ [[
+ local math = math
+ local round = math.round
+ %s
+ return function(x,y)
+ return %s
+ end
+]] ]
+
+local f_function_y = formatters [ [[
+ local math = math
+ local round = math.round
+ local nan = NaN
+ local inf = math.huge
+ local er = 0
+ %s
+ return function(x,y,dnan,dinf,report)
+ local n = %s
+ if n == nan then
+ er = er + 1
+ if er < 10 then
+ report("nan at (%s,%s)",x,y)
+ end
+ n = dnan
+ elseif n == inf then
+ er = er + 1
+ if er < 10 then
+ report("inf at (%s,%s)",x,y)
+ end
+ n = dinf
+ end
+ dx[my] = n
+ sy = sy + 1
+ end
+ return n, er
+end
+]] ]
+
+local f_color = formatters [ [[
+ local math = math
+ return function(f)
+ return %s
+ end
+]] ]
+
+function mp.lmt_surface_do(specification)
+ --
+ -- The projection and color brightness calculation have been inlined. We also store
+ -- differently.
+ --
+ -- todo: ignore weird paths
+ --
+ -- The prototype is now converted to use lmt parameter sets.
+ --
+ local p = getparameterset("surface")
+ --
+ local preamble = p.preamble or ""
+ local code = p.code or "return x + y"
+ local colorcode = p.color or "return f, f, f"
+ local linecolor = p.linecolor or 1
+ local xmin = p.xmin or -1
+ local xmax = p.xmax or 1
+ local ymin = p.ymin or -1
+ local ymax = p.ymax or 1
+ local xstep = p.xstep or .1
+ local ystep = p.ystep or .1
+ local bf = p.brightness or 100
+ local clip = p.clip or false
+ local lines = p.lines
+ local ha = p.snap or 0.01
+ local hb = 2 * ha
+ --
+ if lines == nil then lines = true end
+ --
+ if xstep == 0 then xstep = (xmax - xmin)/100 end
+ if ystep == 0 then ystep = (ymax - ymin)/100 end
+
+ local nxmin = round(xmin/xstep)
+ local nxmax = round(xmax/xstep)
+ local nymin = round(ymin/ystep)
+ local nymax = round(ymax/ystep)
+ local nx = nxmax - nxmin + 1
+ local ny = nymax - nymin + 1
+ --
+ local xvector = p.xvector or { -0.7, -0.7 }
+ local yvector = p.yvector or { 1, 0 }
+ local zvector = p.zvector or { 0, 1 }
+ local light = p.light or { 3, 3, 10 }
+ --
+ local xrx, xry = xvector[1], xvector[2]
+ local yrx, yry = yvector[1], yvector[2]
+ local zrx, zry = zvector[1], zvector[2]
+ local xp, yp, zp = light[1], light[2], light[3]
+ --
+ local data = setmetatableindex("table")
+ local dx = (xmax - xmin) / nx
+ local dy = (ymax - ymin) / ny
+ local xt = xmin
+ --
+ local minf, maxf
+ --
+ -- similar as contours but no data loop here
+ --
+ local fcode = load((p.check and f_function_y or f_function_n)(preamble,code))
+ local func = type(fcode) == "function" and fcode()
+ if type(func) ~= "function" then
+ return false -- fatal error
+ end
+ --
+ local ccode = load(f_color(colorcode))
+ local color = type(ccode) == "function" and ccode()
+ if type(color) ~= "function" then
+ return false -- fatal error
+ end
+ --
+ for i=0,nx do
+ local yt = ymin
+ for j=0,ny do
+ local zt = func(xt,yt)
+ -- projection from 3D to 2D coordinates
+ local x = xt * xrx + yt * yrx + zt * zrx
+ local y = xt * xry + yt * yry + zt * zry
+ local z = zt
+ -- numerical derivatives by central differences
+ local dfx = (func(xt+ha,yt) - func(xt-ha,yt)) / hb
+ local dfy = (func(xt,yt+ha) - func(xt,yt-ha)) / hb
+ -- compute brightness factor at a point
+ local ztp = zt - zp
+ local ytp = yt - yp
+ local xtp = xt - xp
+ local ztp = zt - zp
+ local ytp = yt - yp
+ local xtp = xt - xp
+ local ca = -ztp + dfy*ytp + dfx*xtp
+ local cb = sqrt(1+dfx*dfx+dfy*dfy)
+ local cc = sqrt(ztp*ztp + ytp*ytp + xtp*xtp)
+ local fac = bf*ca/(cb*cc*cc*cc)
+ -- addition: check range
+ if not minf then
+ minf = fac
+ maxf = fac
+ elseif fac < minf then
+ minf = fac
+ elseif fac > maxf then
+ maxf = fac
+ end
+ --
+ data[i][j] = { x, y, fac }
+ --
+ yt = yt + dy
+ end
+ xt = xt + dx
+ end
+ local result = { }
+ local r = 0
+ local range = maxf - minf
+ local cl = linecolor or 1
+ local enforce = attributes.colors.model == "cmyk"
+ for i=0,nx-1 do
+ for j=0,ny-1 do
+ -- points
+ local z1 = data[i] [j]
+ local z2 = data[i] [j+1]
+ local z3 = data[i+1][j+1]
+ local z4 = data[i+1][j]
+ -- color
+ local cf = z1[3]
+ if clip then
+ -- best clip here if needed
+ if cf < 0 then
+ cf = 0
+ elseif cf > 1 then
+ cf = 1
+ end
+ else
+ -- or remap when we want to
+ cf = (z1[3] - minf) / range
+ end
+ local z11 = z1[1]
+ local z12 = z1[2]
+ local z21 = z2[1]
+ local z22 = z2[2]
+ local z31 = z3[1]
+ local z32 = z3[2]
+ local z41 = z4[1]
+ local z42 = z4[2]
+ -- if lines then
+ -- -- fill first and draw then, previous shapes can be covered
+ -- else
+ -- -- fill and draw in one go to prevent artifacts
+ -- end
+ local cr, cg, cb = color(cf)
+ if not cr then cr = 0 end
+ if not cg then cg = 0 end
+ if not cb then cb = 0 end
+ if enforce then
+ cr, cg, cb = 1 - cr, 1 - cg, 1 - cb
+ r = r + 1
+ if lines then
+ result[r] = f_fill_cmy(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ r = r + 1
+ result[r] = f_draw_cmy(z11,z12,z21,z22,z31,z32,z41,z42,cl)
+ else
+ result[r] = f_mesh_cmy(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ end
+ else
+ r = r + 1
+ if lines then
+ result[r] = f_fill_rgb(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ r = r + 1
+ result[r] = f_draw_rgb(z11,z12,z21,z22,z31,z32,z41,z42,cl)
+ else
+ result[r] = f_mesh_rgb(z11,z12,z21,z22,z31,z32,z41,z42,cr,cg,cb)
+ end
+ end
+ end
+ end
+ mp.direct(concat(result))
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.lmt
new file mode 100644
index 00000000000..2569a6066b2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.lmt
@@ -0,0 +1,420 @@
+if not modules then modules = { } end modules ['mlib-ctx'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local type, tostring = type, tostring
+local format, concat = string.format, table.concat
+local settings_to_hash = utilities.parsers.settings_to_hash
+local formatters = string.formatters
+
+local report_metapost = logs.reporter ("metapost")
+local status_metapost = logs.messenger("metapost")
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local trace_graphic = false
+
+trackers.register("metapost.graphics",
+ function(v) trace_graphic = v end
+);
+
+local mplib = mplib
+
+metapost = metapost or { }
+local metapost = metapost
+local context = context
+
+local setters = tokens.setters
+local setmacro = setters.macro
+local implement = interfaces.implement
+
+local v_no = interfaces.variables.no
+
+local extensiondata = metapost.extensiondata or storage.allocate { }
+metapost.extensiondata = extensiondata
+
+storage.register("metapost/extensiondata",extensiondata,"metapost.extensiondata")
+
+function metapost.setextensions(instances,data)
+ if data and data ~= "" then
+ extensiondata[#extensiondata+1] = {
+ usedinall = not instances or instances == "",
+ instances = settings_to_hash(instances or ""),
+ extensions = data,
+ }
+ end
+end
+
+function metapost.getextensions(instance,state)
+ if state and state == v_no then
+ return ""
+ else
+ local t = { }
+ for i=1,#extensiondata do
+ local e = extensiondata[i]
+ local status = e.instances[instance]
+ if (status ~= true) and (e.usedinall or status) then
+ t[#t+1] = e.extensions
+ e.instances[instance] = true
+ end
+ end
+ return concat(t," ")
+ end
+end
+
+implement {
+ name = "setmpextensions",
+ actions = metapost.setextensions,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "getmpextensions",
+ actions = { metapost.getextensions, context } ,
+ arguments = "string"
+}
+
+local patterns = {
+ "meta-imp-%s.mkxl",
+ "meta-imp-%s.mkiv",
+ "meta-imp-%s.tex",
+ -- obsolete:
+ "meta-%s.mkiv",
+ "meta-%s.tex"
+}
+
+local function action(name,foundname)
+ commands.loadlibrary(name,foundname,false)
+ status_metapost("library %a is loaded",name)
+end
+
+local function failure(name)
+ report_metapost("library %a is unknown or invalid",name)
+end
+
+implement {
+ name = "useMPlibrary",
+ arguments = "string",
+ actions = function(name)
+ resolvers.uselibrary {
+ name = name,
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = true,
+ }
+ end
+}
+
+-- metapost.variables = { } -- to be stacked
+
+implement {
+ name = "mprunvar",
+ arguments = "string",
+ actions = function(name)
+ local value = metapost.variables[name]
+ if value ~= nil then
+ local tvalue = type(value)
+ if tvalue == "table" then
+ context(concat(value," "))
+ elseif tvalue == "number" or tvalue == "boolean" then
+ context(tostring(value))
+ elseif tvalue == "string" then
+ context(value)
+ end
+ end
+ end
+}
+
+implement {
+ name = "mpruntab",
+ arguments = { "string", "integer" },
+ actions = function(name,n)
+ local value = metapost.variables[name]
+ if value ~= nil then
+ local tvalue = type(value)
+ if tvalue == "table" then
+ context(value[n])
+ elseif tvalue == "number" or tvalue == "boolean" then
+ context(tostring(value))
+ elseif tvalue == "string" then
+ context(value)
+ end
+ end
+ end
+}
+
+implement {
+ name = "mprunset",
+ arguments = "2 strings",
+ actions = function(name,connector)
+ local value = metapost.variables[name]
+ if value ~= nil then
+ local tvalue = type(value)
+ if tvalue == "table" then
+ context(concat(value,connector))
+ elseif tvalue == "number" or tvalue == "boolean" then
+ context(tostring(value))
+ elseif tvalue == "string" then
+ context(value)
+ end
+ end
+ end
+}
+
+-- we need to move more from pps to here as pps is the plugin .. the order is a mess
+-- or just move the scanners to pps
+
+function metapost.graphic(specification)
+ metapost.pushformat(specification)
+ metapost.graphic_base_pass(specification)
+ metapost.popformat()
+end
+
+function metapost.startgraphic(t)
+ if not t then
+ t = { }
+ end
+ if not t.instance then
+ t.instance = metapost.defaultinstance
+ end
+ if not t.format then
+ t.format = metapost.defaultformat
+ end
+ if not t.method then
+ t.method = metapost.defaultmethod
+ end
+ t.data = { }
+ return t
+end
+
+function metapost.stopgraphic(t)
+ if t then
+ t.data = concat(t.data or { },"\n")
+ if trace_graphic then
+ report_metapost("\n"..t.data.."\n")
+ end
+ metapost.graphic(t)
+ end
+end
+
+function metapost.tographic(t,f,s,...)
+ local d = t.data
+ d[#d+1] = s and formatters[f](s,...) or f
+end
+
+implement {
+ name = "mpgraphic",
+ actions = metapost.graphic,
+ arguments = {
+ {
+ { "instance" },
+ { "format" },
+ { "data" },
+ { "initializations" },
+ { "extensions" },
+ { "inclusions" },
+ { "definitions" },
+ { "figure" },
+ { "method" },
+ { "namespace" },
+ }
+ }
+}
+
+implement {
+ name = "mpsetoutercolor",
+ actions = function(...) metapost.setoutercolor(...) end, -- not yet implemented
+ arguments = { "integer", "integer", "integer", "integer" }
+}
+
+-- this has to become a codeinjection
+
+function metapost.getclippath(specification) -- why not a special instance for this
+ local mpx = metapost.pushformat(specification)
+ local data = specification.data or ""
+ if mpx and data ~= "" then
+ starttiming(metapost)
+ starttiming(metapost.exectime)
+ local result = mpx:execute ( format ( "%s;%s;beginfig(1);%s;%s;endfig;",
+ specification.extensions or "",
+ specification.inclusions or "",
+ specification.initializations or "",
+ data
+ ) )
+ stoptiming(metapost.exectime)
+ if result.status > 0 then
+ report_metapost("%s: %s", result.status, result.error or result.term or result.log)
+ result = nil
+ else
+ result = metapost.filterclippath(result)
+ end
+ stoptiming(metapost)
+ metapost.popformat()
+ return result
+ else
+ metapost.popformat()
+ end
+end
+
+function metapost.filterclippath(result)
+ if result then
+ local figures = result.fig
+ if figures and #figures > 0 then
+ local figure = figures[1]
+ local objects = figure:objects()
+ if objects then
+ local lastclippath
+ for o=1,#objects do
+ local object = objects[o]
+ if object.type == "start_clip" then
+ lastclippath = object.path
+ end
+ end
+ return lastclippath
+ end
+ end
+ end
+end
+
+function metapost.theclippath(...)
+ local result = metapost.getclippath(...)
+ if result then -- we could just print the table
+ return concat(metapost.flushnormalpath(result)," ")
+ else
+ return ""
+ end
+end
+
+implement {
+ name = "mpsetclippath",
+ actions = function(specification)
+ local p = specification.data and metapost.theclippath(specification)
+ if not p or p == "" then
+ local b = number.dimenfactors.bp
+ local w = b * (specification.width or 0)
+ local h = b * (specification.height or 0)
+ p = formatters["0 0 m %.6N 0 l %.6N %.6N l 0 %.6N l"](w,w,h,h)
+ end
+ setmacro("MPclippath",p,"global")
+ end,
+ arguments = {
+ {
+ { "instance" },
+ { "format" },
+ { "data" },
+ { "initializations" },
+ { "useextensions" },
+ { "inclusions" },
+ { "method" },
+ { "namespace" },
+ { "width", "dimension" },
+ { "height", "dimension" },
+ },
+ }
+}
+
+statistics.register("metapost", function()
+ local n = metapost.nofruns
+ if n and n > 0 then
+ local elapsedtime = statistics.elapsedtime
+ local elapsed = statistics.elapsed
+ local runs, stats = metapost.nofscriptruns()
+ local instances,
+ memory = metapost.getstatistics(true)
+ return format("%s seconds, loading: %s, execution: %s, n: %s, average: %s, instances: %i, luacalls: %s, memory: %0.3f M",
+ elapsedtime(metapost), elapsedtime(mplib), elapsedtime(metapost.exectime), n,
+ elapsedtime((elapsed(metapost) + elapsed(mplib) + elapsed(metapost.exectime)) / n),
+ instances, stats and stats or runs, memory/(1024*1024))
+ else
+ return nil
+ end
+end)
+
+-- only used in graphictexts
+
+metapost.tex = metapost.tex or { }
+local mptex = metapost.tex
+
+local environments = { }
+
+function mptex.set(str)
+ environments[#environments+1] = str
+end
+
+function mptex.setfrombuffer(name)
+ environments[#environments+1] = buffers.getcontent(name)
+end
+
+function mptex.get()
+ return concat(environments,"\n")
+end
+
+function mptex.reset()
+ environments = { }
+end
+
+implement {
+ name = "mppushvariables",
+ actions = metapost.pushvariables,
+}
+
+implement {
+ name = "mppopvariables",
+ actions = metapost.popvariables,
+}
+
+implement {
+ name = "mptexset",
+ arguments = "string",
+ actions = mptex.set
+}
+
+implement {
+ name = "mptexsetfrombuffer",
+ arguments = "string",
+ actions = mptex.setfrombuffer
+}
+
+implement {
+ name = "mptexget",
+ actions = { mptex.get, context }
+}
+
+implement {
+ name = "mptexreset",
+ actions = mptex.reset
+}
+
+-- moved from mlib-lua:
+
+mp = mp or { -- system namespace
+ set = { },
+ get = { },
+ aux = { },
+ scan = { },
+ skip = { },
+ inject = { },
+}
+
+MP = MP or { } -- user namespace
+
+-- We had this:
+--
+-- table.setmetatablecall(mp,function(t,k) mpprint(k) end)
+--
+-- but the next one is more interesting because we cannot use calls like:
+--
+-- lua.mp.somedefdname("foo")
+--
+-- which is due to expansion of somedefdname during suffix creation. So:
+--
+-- lua.mp("somedefdname","foo")
+
+table.setmetatablecall(mp,function(t,k,...) return t[k](...) end)
+table.setmetatablecall(MP,function(t,k,...) return t[k](...) end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.mkxl
new file mode 100644
index 00000000000..fb069b6ea88
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-ctx.mkxl
@@ -0,0 +1,34 @@
+%D \module
+%D [ file=mlib-ctx,
+%D version=2008.03.25,
+%D title=\METAPOST\ Integrated Graphics,
+%D subtitle=Basics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{MetaPost Library Graphics / Initializations}
+
+\installcorenamespace{graphicvariable}
+
+\registerctxluafile{mlib-fio}{autosuffix}
+\registerctxluafile{mlib-run}{autosuffix}
+\registerctxluafile{mlib-ctx}{autosuffix}
+\registerctxluafile{mlib-lua}{autosuffix}
+\registerctxluafile{mlib-mpf}{autosuffix}
+\registerctxluafile{mlib-scn}{autosuffix}
+\registerctxluafile{mlib-mat}{autosuffix}
+\registerctxluafile{mlib-ran}{autosuffix}
+\registerctxluafile{mlib-lmp}{autosuffix}
+\registerctxluafile{mlib-int}{autosuffix}
+\registerctxluafile{mlib-lmt}{autosuffix}
+\registerctxluafile{mlib-cnt}{autosuffix,optimize}
+\registerctxluafile{mlib-svg}{autosuffix,optimize}
+
+\unprotect
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-fio.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-fio.lmt
new file mode 100644
index 00000000000..9de61affef7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-fio.lmt
@@ -0,0 +1,289 @@
+if not modules then modules = { } end modules ['mlib-fio'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local type = type
+local find = string.find
+local concat = table.concat
+local suffix, addsuffix = file.suffix, file.addsuffix
+
+local findfile = resolvers.findfile
+local new_instance = mplib.new
+
+local trace_terminal = false trackers.register("metapost.terminal", function(v) trace_terminal = v end)
+
+local report_metapost = logs.reporter("metapost")
+local report_terminal = logs.reporter("metapost","terminal")
+local report_logger = logs.reporter("metapost","log")
+local report_error = logs.reporter("metapost","error")
+
+mplib.realtimelogging = false
+
+local l, nl, dl = { }, 0, false
+local t, nt, dt = { }, 0, false
+local e, ne, de = { }, 0, false
+
+local function logger(target,str)
+ if target == 1 then
+ -- log
+ elseif target == 2 or target == 3 then
+ -- term
+ if str == "\n" then
+ mplib.realtimelogging = true
+ if nl > 0 then
+ report_logger(concat(l,"",1,nl))
+ nl, dl = 0, false
+ elseif not dl then
+ report_logger("")
+ dl = true
+ end
+ else
+ nl = nl + 1
+ l[nl] = str
+ end
+ elseif target == 4 then
+ report_error(str)
+ end
+end
+
+local finders = { }
+mplib.finders = finders -- also used in meta-lua.lua
+
+local function validftype(ftype)
+ if ftype == "mp" then
+ return "mp"
+ else
+ return nil
+ end
+end
+
+-- We can have a list!
+
+local suffixlist = { "mpxl", "mpiv", "mp" } -- no "mf"
+
+local function findmpfile(name,ftype)
+ local validtyp = validftype(ftype)
+ local fullname = findfile(name,validtyp)
+ if fullname and fullname ~= "" then
+ return fullname
+ elseif suffix(name) == "" then
+ for i=1,#suffixlist do
+ fullname = findfile(addsuffix(name,suffixlist[i]),validtyp)
+ if fullname and fullname ~= "" then
+ return fullname
+ end
+ end
+ end
+ return nil
+end
+
+finders.file = function(specification,name,mode,kind)
+ if mode == "r" then
+ return findmpfile(name,kind)
+ elseif file.is_writable(name) then
+ return name
+ else
+ return nil
+ end
+end
+
+local function finder(name,mode,kind) -- fake message for mpost.map and metafun.mpvi
+ local specification = url.hashed(name)
+ local finder = finders[specification.scheme] or finders.file
+ local found = finder(specification,name,mode,validftype(ftype))
+ return found
+end
+
+local findtexfile = resolvers.findtexfile
+local opentexfile = resolvers.opentexfile
+local splitlines = string.splitlines
+
+local function writetoterminal(terminaldata,maxterm,d)
+ local t = type(d)
+ local n = 0
+ if t == "string" then
+ d = splitlines(d)
+ n = #d
+ for i=1,#d do
+ maxterm = maxterm + 1
+ terminaldata[maxterm] = d[i]
+ end
+ elseif t == "table" then
+ for i=1,#d do
+ local l = d[i]
+ if not l then
+ -- just ignore
+ elseif find(l,"[\n\r]") then
+ local s = splitlines(l)
+ local m = #s
+ for i=1,m do
+ maxterm = maxterm + 1
+ terminaldata[maxterm] = s[i]
+ end
+ n = n + m
+ else
+ maxterm = maxterm + 1
+ terminaldata[maxterm] = d[i]
+ n = 1
+ end
+ end
+ end
+ if trace_terminal then
+ report_metapost("writing %i lines, in cache %s",n,maxterm)
+ end
+ return maxterm
+end
+
+local function readfromterminal(terminaldata,maxterm,nowterm)
+ if nowterm >= maxterm then
+ terminaldata[nowterm] = false
+ maxterm = 0
+ nowterm = 0
+ if trace_terminal then
+ report_metapost("resetting, maxcache %i",#terminaldata)
+ end
+ return maxterm, nowterm, nil
+ else
+ if nowterm > 0 then
+ terminaldata[nowterm] = false
+ end
+ nowterm = nowterm + 1
+ local s = terminaldata[nowterm]
+ if trace_terminal then
+ report_metapost("reading line %i: %s",nowterm,s)
+ end
+ return maxterm, nowterm, s
+ end
+end
+
+local function fileopener()
+
+ -- these can go into the table itself
+
+ local terminaldata = { }
+ local maxterm = 0
+ local nowterm = 0
+
+ local terminal = {
+ name = "terminal",
+ close = function()
+ -- terminal = { }
+ -- maxterm = 0
+ -- nowterm = 0
+ end,
+ reader = function()
+ local line
+ maxterm, nowterm, line = readfromterminal(terminaldata,maxterm,nowterm)
+ return line
+ end,
+ writer = function(d)
+ maxterm = writetoterminal(terminaldata,maxterm,d)
+ end,
+ }
+
+ return function(name,mode,kind)
+ if name == "terminal" then
+ -- report_metapost("opening terminal")
+ return terminal
+ elseif mode == "w" then
+ -- we need an extra check here for permissions
+ local f = io.open(name,"wb")
+ if f then
+ -- report_metapost("opening file %a for writing",full)
+ return {
+ name = full,
+ writer = function(s) return f:write(s) end, -- io.write(f,s)
+ close = function() f:close() end,
+ }
+ end
+ else
+ local full = findtexfile(name,validftype(ftype))
+ if full then
+ -- report_metapost("opening file %a for reading",full)
+ return opentexfile(full)
+ end
+ end
+ end
+
+end
+
+local overloadmode = "warning"
+
+directives.register("metapost.overloadmode",function(v)
+ if v == "warning" or v == "error" then
+ overloadmode = v
+ else
+ overloadmode= false
+ end
+end)
+
+local propertycodes = {
+ [-3] = "mutable",
+ [ 1] = "primitive",
+ [ 2] = "permanent",
+ [ 3] = "immutable",
+}
+
+mplib.propertycodes = propertycodes
+
+local report = logs.reporter("metafun", "log")
+
+local function overload(property,name)
+ if overloadmode and property >= 0 then
+ -- turn of warning after format is loaded
+ local code = propertycodes[property] or "unknown"
+ report("overloading %s %a",code, name)
+ -- no overload permitted
+ if overloadmode == "error" then
+ luatex.abort()
+ end
+ return false
+ else
+ -- overload permitted
+ return true
+ end
+end
+
+local showcontext = mplib.showcontext
+
+local function handleerror(instance, message, helpinfo, interaction)
+ report()
+ report("error: %s", message)
+ report()
+ showcontext(instance)
+ report()
+ report(helpinfo)
+ report()
+ if interaction == 5 then
+ -- todo
+-- luatex.abort()
+ end
+end
+
+local function handlewarning(instance, message)
+ report()
+ report("warning: %s", message)
+ report()
+end
+
+function mplib.new(specification)
+ local instance
+ local openfile = fileopener()
+ specification.find_file = finder
+ specification.run_logger = logger
+ specification.run_overload = overload
+ specification.run_error = function(...) handleerror (instance,...) end
+ specification.run_warning = function(...) handlewarning(instance,...) end
+ specification.open_file = openfile
+ specification.interaction = "silent"
+ specification.job_name = tex.jobname -- mandate in order to get something back
+ specification.halt_on_error = true
+ instance = new_instance(specification)
+ return instance, openfile("terminal")
+end
+
+mplib.finder = finder
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-int.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-int.lmt
new file mode 100644
index 00000000000..a936e55924e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-int.lmt
@@ -0,0 +1,160 @@
+if not modules then modules = { } end modules ['mlib-int'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local factor = number.dimenfactors.bp
+local mpstring = mp.string
+local mpboolean = mp.boolean
+local getdimen = tex.getdimen
+local getcount = tex.getcount
+local getmacro = tokens.getters.macro
+local get = tex.get
+local emwidths = fonts.hashes.emwidths
+local exheights = fonts.hashes.exheights
+
+local registerscript = metapost.registerscript
+
+local on_right_page = structures.pages.on_right
+local is_odd_page = structures.pages.is_odd
+local in_body_page = structures.pages.in_body
+local page_fraction = structures.pages.fraction
+local layout_has_changed = structures.pages.has_changed
+
+local function defaultcolormodel() -- can be helper
+ local colormethod = getcount("MPcolormethod")
+ return (colormethod == 0 or colormethod == 1) and 1 or 3
+end
+
+do
+
+ local t = os.date("*t") -- maybe this should be a very early on global
+
+ -- If we want to do a vardef then we first need to catch an endgroup and
+ -- that then fails because we have a variable sitting there, so they need
+ -- to be def's at the mp end.
+
+ local assignment_code = metapost.codes.assignment
+
+ local mpscannext = mp.scan.next
+ local mpscaninteger = mp.scan.integer
+
+ local function item(name)
+ local n = mpscannext(true) -- keep
+ if n == assignment_code then
+ mpscannext()
+ t[name] = mpscaninteger()
+ else
+ return t[name]
+ end
+ end
+
+ registerscript("year", function() return item("year") end)
+ registerscript("month", function() return item("month") end)
+ registerscript("day", function() return item("day") end)
+ registerscript("hour", function() return item("hour") end)
+ registerscript("minute", function() return item("min") end)
+ registerscript("second", function() return item("sec") end)
+
+end
+
+registerscript("PaperHeight", function() return getdimen("paperheight") * factor end)
+registerscript("PaperWidth", function() return getdimen("paperwidth") * factor end)
+registerscript("PrintPaperHeight", function() return getdimen("printpaperheight") * factor end)
+registerscript("PrintPaperWidth", function() return getdimen("printpaperwidth") * factor end)
+registerscript("TopSpace", function() return getdimen("topspace") * factor end)
+registerscript("BottomSpace", function() return getdimen("bottomspace") * factor end)
+registerscript("BackSpace", function() return getdimen("backspace") * factor end)
+registerscript("CutSpace", function() return getdimen("cutspace") * factor end)
+registerscript("MakeupHeight", function() return getdimen("makeupheight") * factor end)
+registerscript("MakeupWidth", function() return getdimen("makeupwidth") * factor end)
+registerscript("TopHeight", function() return getdimen("topheight") * factor end)
+registerscript("TopDistance", function() return getdimen("topdistance") * factor end)
+registerscript("HeaderHeight", function() return getdimen("headerheight") * factor end)
+registerscript("HeaderDistance", function() return getdimen("headerdistance") * factor end)
+registerscript("TextHeight", function() return getdimen("textheight") * factor end)
+registerscript("FooterDistance", function() return getdimen("footerdistance") * factor end)
+registerscript("FooterHeight", function() return getdimen("footerheight") * factor end)
+registerscript("BottomDistance", function() return getdimen("bottomdistance") * factor end)
+registerscript("BottomHeight", function() return getdimen("bottomheight") * factor end)
+registerscript("LeftEdgeWidth", function() return getdimen("leftedgewidth") * factor end)
+registerscript("LeftEdgeDistance", function() return getdimen("leftedgedistance") * factor end)
+registerscript("LeftMarginWidth", function() return getdimen("leftmarginwidth") * factor end)
+registerscript("LeftMarginDistance", function() return getdimen("leftmargindistance") * factor end)
+registerscript("TextWidth", function() return getdimen("textwidth") * factor end)
+registerscript("RightMarginDistance", function() return getdimen("rightmargindistance") * factor end)
+registerscript("RightMarginWidth", function() return getdimen("rightmarginwidth") * factor end)
+registerscript("RightEdgeDistance", function() return getdimen("rightedgedistance") * factor end)
+registerscript("RightEdgeWidth", function() return getdimen("rightedgewidth") * factor end)
+registerscript("InnerMarginDistance", function() return getdimen("innermargindistance") * factor end)
+registerscript("InnerMarginWidth", function() return getdimen("innermarginwidth") * factor end)
+registerscript("OuterMarginDistance", function() return getdimen("outermargindistance") * factor end)
+registerscript("OuterMarginWidth", function() return getdimen("outermarginwidth") * factor end)
+registerscript("InnerEdgeDistance", function() return getdimen("inneredgedistance") * factor end)
+registerscript("InnerEdgeWidth", function() return getdimen("inneredgewidth") * factor end)
+registerscript("OuterEdgeDistance", function() return getdimen("outeredgedistance") * factor end)
+registerscript("OuterEdgeWidth", function() return getdimen("outeredgewidth") * factor end)
+registerscript("PageOffset", function() return getdimen("pagebackgroundoffset") * factor end)
+registerscript("PageDepth", function() return getdimen("pagebackgrounddepth") * factor end)
+registerscript("LayoutColumns", function() return getcount("layoutcolumns") end)
+registerscript("LayoutColumnDistance", function() return getdimen("layoutcolumndistance") * factor end)
+registerscript("LayoutColumnWidth", function() return getdimen("layoutcolumnwidth") * factor end)
+registerscript("SpineWidth", function() return getdimen("spinewidth") * factor end)
+registerscript("PaperBleed", function() return getdimen("paperbleed") * factor end)
+
+registerscript("RealPageNumber", function() return getcount("realpageno") end)
+registerscript("LastPageNumber", function() return getcount("lastpageno") end)
+
+registerscript("PageNumber", function() return getcount("pageno") end)
+registerscript("NOfPages", function() return getcount("lastpageno") end)
+
+registerscript("SubPageNumber", function() return getcount("subpageno") end)
+registerscript("NOfSubPages", function() return getcount("lastsubpageno") end)
+
+registerscript("CurrentColumn", function() return getcount("mofcolumns") end)
+registerscript("NOfColumns", function() return getcount("nofcolumns") end)
+
+registerscript("BaseLineSkip", function() return get ("baselineskip",true) * factor end)
+registerscript("LineHeight", function() return getdimen("lineheight") * factor end)
+registerscript("BodyFontSize", function() return getdimen("bodyfontsize") * factor end)
+
+registerscript("TopSkip", function() return get ("topskip",true) * factor end)
+registerscript("StrutHeight", function() return getdimen("strutht") * factor end)
+registerscript("StrutDepth", function() return getdimen("strutdp") * factor end)
+
+registerscript("PageNumber", function() return getcount("pageno") end)
+registerscript("RealPageNumber", function() return getcount("realpageno") end)
+registerscript("NOfPages", function() return getcount("lastpageno") end)
+
+registerscript("CurrentWidth", function() return get ("hsize") * factor end)
+registerscript("CurrentHeight", function() return get ("vsize") * factor end)
+
+registerscript("EmWidth", function() return emwidths [false] * factor end)
+registerscript("ExHeight", function() return exheights[false] * factor end)
+
+registerscript("HSize", function() return get ("hsize") * factor end)
+registerscript("VSize", function() return get ("vsize") * factor end)
+registerscript("LastPageNumber", function() return getcount("lastpageno") end)
+
+registerscript("OverlayWidth", function() return getdimen("d_overlay_width") * factor end)
+registerscript("OverlayHeight", function() return getdimen("d_overlay_height") * factor end)
+registerscript("OverlayDepth", function() return getdimen("d_overlay_depth") * factor end)
+registerscript("OverlayLineWidth", function() return getdimen("d_overlay_linewidth") * factor end)
+registerscript("OverlayOffset", function() return getdimen("d_overlay_offset") * factor end)
+registerscript("OverlayRegion", function() mpstring(getmacro("m_overlay_region")) end)
+--------------("CurrentLayout", function() mpstring(getmacro("currentlayout")) end)
+
+-- should be direct
+--
+-- registerscript("PageFraction", page_fraction)
+
+registerscript("PageFraction", function() return mpboolean(page_fraction ()) end)
+registerscript("OnRightPage", function() return mpboolean(on_right_page ()) end)
+registerscript("OnOddPage", function() return mpboolean(is_odd_page ()) end)
+registerscript("InPageBody", function() return mpboolean(in_body_page ()) end)
+registerscript("LayoutHasChanged", function() return mpboolean(layout_has_changed()) end)
+
+registerscript("defaultcolormodel", defaultcolormodel)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-lmp.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-lmp.lmt
new file mode 100644
index 00000000000..2b3e910babd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-lmp.lmt
@@ -0,0 +1,104 @@
+if not modules then modules = { } end modules ['mlib-lmp'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- path relates stuff ... todo: use a stack (or numeric index to list)
+
+local type = type
+
+local aux = mp.aux
+local mpnumeric = aux.numeric
+local mppair = aux.pair
+
+local registerscript = metapost.registerscript
+
+local scan = mp.scan
+local get = mp.get
+local scannumber = scan.number
+local scanstring = scan.string
+local scanpath = scan.path
+local scanproperty = scan.property
+local gethashentry = get.hashentry
+
+local p = nil
+local n = 0
+
+registerscript("pathreset", function()
+ p = nil
+ n = 0
+end)
+
+registerscript("pathlengthof", function()
+ p = scanpath()
+ n = p and #p or 1
+ mpnumeric(n)
+end)
+
+registerscript("pathpointof", function()
+ local i = scannumber()
+ if i > 0 and i <= n then
+ local pi = p[i]
+ mppair(pi[1],pi[2])
+ end
+end)
+
+registerscript("pathleftof", function()
+ local i = scannumber()
+ if i > 0 and i <= n then
+ local pi = p[i]
+ mppair(pi[5],pi[6])
+ end
+end)
+
+registerscript("pathrightof", function()
+ local i = scannumber()
+ if i > 0 and i <= n then
+ local pn
+ if i == 1 then
+ pn = p[2] or p[1]
+ else
+ pn = p[i+1] or p[1]
+ end
+ mppair(pn[3],pn[4])
+ end
+end)
+
+local report = logs.reporter("metapost", "log")
+local codes = metapost.codes
+local types = metapost.types
+local procodes = mplib.propertycodes
+
+registerscript("showproperty", function()
+ local k, s, p, d = scanproperty()
+ if k then
+ report("name %a, property %a, command %a, detail %a",s,procodes[p] or "-",codes[k] or "-",types[d] or "-")
+ end
+end)
+
+registerscript("showhashentry", function()
+ local s = scanstring()
+ if s then
+ local k, p, d = gethashentry(s)
+ if k then
+ report("name %a, property %a, command %a, detail %a",s,procodes[p] or "-",codes[k] or "-",types[d] or "-")
+ end
+ end
+end)
+
+local getmacro = tokens.getters.macro
+local mpgnamespace = getmacro("??graphicvariable")
+
+local scanmpstring = mp.scan.string
+
+local injectnumeric = mp.inject.numeric
+local injectstring = mp.inject.string
+
+-- registerscript("mpv_numeric", function() injectnumeric (getmacro(mpgnamespace .. getmacro("currentmpvariableclass") .. ":" .. scanmpstring())) end)
+-- registerscript("mpv_dimension", function() return getmacro(mpgnamespace .. getmacro("currentmpvariableclass") .. ":" .. scanmpstring()) end)
+-- registerscript("mpv_string", function() injectstring (getmacro(mpgnamespace .. getmacro("currentmpvariableclass") .. ":" .. scanmpstring())) end)
+
+registerscript("mpvar", function() return getmacro(mpgnamespace .. getmacro("currentmpvariableclass") .. ":" .. scanmpstring(), true) end) -- Isn't it already edef'd?
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-lmt.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-lmt.lmt
new file mode 100644
index 00000000000..b7c869c3bac
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-lmt.lmt
@@ -0,0 +1,199 @@
+if not modules then modules = { } end modules ['mlib-lmt'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- todo: check for possible inject usage
+
+local type = type
+local round = math.round
+
+local aux = mp.aux
+local mpdirect = aux.direct
+local mppath = mp.path
+
+local scan = mp.scan
+local scannumeric = scan.numeric
+local scanpath = scan.path
+
+local injectpath = mp.inject.path
+local injectcolor = mp.inject.color
+
+local getparameter = metapost.getparameter
+local registerscript = metapost.registerscript
+
+function mp.lmt_function_x(xmin,xmax,xstep,code,shape) -- experimental
+ local code = "return function(x) return " .. code .. " end"
+ local action = load(code)
+ local points = { }
+ local nofpoints = 0
+ if action then
+ action = action()
+ end
+ if shape == "steps" then
+ local halfx = xstep / 2
+ local lastx = xmin
+ local lasty = action(xmin)
+ for xi = xmin, xmax, xstep do
+ local yi = action(xi)
+ local xx = lastx + halfx
+ nofpoints = nofpoints + 1 ; points[nofpoints] = { xx, lasty }
+ nofpoints = nofpoints + 1 ; points[nofpoints] = { xx, yi }
+ lastx = xi
+ lasty = yi
+ end
+ if points[nofpoints][1] ~= xmax then
+ local yi = action(xmax)
+ local xx = lastx + halfx
+ nofpoints = nofpoints + 1 ; points[nofpoints] = { xx, lasty }
+ nofpoints = nofpoints + 1 ; points[nofpoints] = { xx, yi }
+ lastx = xi
+ lasty = yi
+ end
+ else
+ for xi = xmin, xmax, xstep do
+ nofpoints = nofpoints + 1 ; points[nofpoints] = { xi, action(xi) }
+ end
+ if points[nofpoints][1] ~= xmax then
+ nofpoints = nofpoints + 1 ; points[nofpoints] = { xmax, action(xmax) }
+ end
+ end
+ mppath(points,shape == "curve" and ".." or "--",false)
+end
+
+function mp.lmt_mesh_set()
+ local mesh = getparameter { "mesh", "paths" }
+ structures.references.currentset.mesh = mesh
+end
+
+function mp.lmt_mesh_update()
+ local mesh = getparameter { "paths" } or getparameter { "mesh", "paths" }
+ mesh[scannumeric()] = scanpath(true)
+end
+
+-- moved here
+
+function mp.lmt_svg_include()
+ local labelfile = metapost.getparameter { "labelfile" }
+ if labelfile and labelfile ~= "" then
+ local labels = table.load(labelfile) -- todo: same path as svg file
+ if type(labels) == "table" then
+ for i=1,#labels do
+ metapost.remaptext(labels[i])
+ end
+ end
+ end
+ local fontname = metapost.getparameter { "fontname" }
+ if fontname and fontname ~= "" then
+ local unicode = metapost.getparameter { "unicode" }
+ if unicode then
+ mpdirect (
+ metapost.svgglyphtomp(fontname,math.round(unicode))
+ )
+ end
+ return
+ end
+ local colorfile = metapost.getparameter { "colormap" }
+ local colormap = false
+ if colorfile and colorfile ~= "" then
+ colormap = metapost.svgcolorremapper(colorfile)
+ end
+ local filename = metapost.getparameter { "filename" }
+ if filename and filename ~= "" then
+ mpdirect ( metapost.svgtomp {
+ data = io.loaddata(filename),
+ remap = true,
+ colormap = colormap,
+ id = filename,
+ } )
+ else
+ local buffer = metapost.getparameter { "buffer" }
+ if buffer then
+ mpdirect ( metapost.svgtomp {
+ data = buffers.getcontent(buffer),
+ -- remap = true,
+ colormap = colormap,
+ id = buffer or "buffer",
+ } )
+ else
+ local code = metapost.getparameter { "code" }
+ if code then
+ mpdirect ( metapost.svgtomp {
+ data = code,
+ colormap = colormap,
+ id = "code",
+ } )
+ end
+ end
+ end
+end
+
+registerscript("remaptext", function()
+ local parameters = metapost.scanparameters()
+ if parameters and parameters.label then
+ metapost.remaptext(parameters)
+ end
+end)
+
+do
+
+ local dropins = fonts.dropins
+ local registerglyph = dropins.registerglyph
+ local registerglyphs = dropins.registerglyphs
+
+ registerscript("registerglyph", function() registerglyph (metapost.getparameterset("mpsglyph")) end)
+ registerscript("registerglyphs",function() registerglyphs(metapost.getparameterset("mpsglyphs")) end)
+
+end
+
+todecimal = xdecimal and xdecimal.new or tonumber -- bonus
+
+-- mail on list by Mikael Sundqvist and Taco's analysis of near duplicate points (2021/02/11+)
+
+registerscript("scrutenized", function()
+ local p = scanpath()
+ local d = 10^scannumeric()
+ for i=1,#p do
+ local pi = p[i]
+ pi[1] = round(pi[1] * d) / d
+ pi[2] = round(pi[2] * d) / d
+ end
+ local x1 = round(p[1][1])
+ local y1 = round(p[1][2])
+ local n = 1
+ local m = #p
+ local t = { p[1], cycle = p.cycle }
+ for i=2,m-1 do
+ local pi = p[i]
+ local x2 = r(pi[1])
+ local y2 = r(pi[2])
+ if x1 ~= x2 or y1 ~= y2 then
+ n = n + 1
+ t[n] = p[i]
+ x1 = x2
+ y1 = y2
+ end
+ end
+ local x1 = r(p[1][1])
+ local y1 = r(p[1][2])
+ local x2 = r(p[m][1])
+ local y2 = r(p[m][2])
+ if x1 ~= x2 or y1 ~= y2 then
+ n = n + 1
+ t[n] = p[m]
+ end
+ mp.path(t)
+ -- injectpath(t)
+end)
+
+-- A goodie, mostly a side effect of updating the metafun manual.
+
+
+local labtorgb = attributes.colors.labtorgb
+
+registerscript("labtorgb", function()
+ injectcolor(labtorgb(scannumeric(),scannumeric(),scannumeric()))
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-lua.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-lua.lmt
new file mode 100644
index 00000000000..068d17f32f9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-lua.lmt
@@ -0,0 +1,210 @@
+if not modules then modules = { } end modules ['mlib-lua'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local type = type
+local insert, remove = table.insert, table.remove
+
+local trace = false trackers.register("metapost.instance",function(v) trace = v end)
+
+local report = logs.reporter("metapost","instance")
+
+local codes = mplib.getcodes()
+local types = mplib.gettypes()
+
+-- for k,v in next, mplib do if type(v) == "function" then local f = v mplib[k] = function(...) print(k) return v(...) end end end
+
+table.hashed(codes)
+table.hashed(types)
+
+metapost.codes = codes
+metapost.types = types
+
+local scan = mp.scan
+local skip = mp.skip
+local get = mp.get
+local inject = mp.inject
+
+local currentmpx = nil
+local stack = { }
+
+local function reports(s) report("%a scan %s", tostring(currentmpx),s) end -- temporary, till we're okay
+local function reporti(s) report("%a inject %s",tostring(currentmpx),s) end -- temporary, till we're okay
+
+local scan_next = mplib.scan_next
+local scan_expression = mplib.scan_expression
+local scan_token = mplib.scan_token
+local scan_symbol = mplib.scan_symbol
+local scan_property = mplib.scan_property
+local scan_numeric = mplib.scan_numeric
+local scan_integer = mplib.scan_integer
+local scan_boolean = mplib.scan_boolean
+local scan_string = mplib.scan_string
+local scan_pair = mplib.scan_pair
+local scan_color = mplib.scan_color
+local scan_cmykcolor = mplib.scan_cmykcolor
+local scan_transform = mplib.scan_transform
+local scan_path = mplib.scan_path
+local scan_pen = mplib.scan_pen
+
+local skip_token = mplib.skip_token
+
+local get_hashentry = mplib.gethashentry
+
+scan.next = function(k) if trace then reporti("next") end return scan_next (currentmpx,k) end
+scan.expression = function(k) if trace then reporti("expression") end return scan_expression(currentmpx,k) end
+scan.token = function(k) if trace then reporti("token") end return scan_token (currentmpx,k) end
+scan.symbol = function(k,e) if trace then reporti("symbol") end return scan_symbol (currentmpx,k,e) end
+scan.property = function(k) if trace then reporti("property") end return scan_property (currentmpx,k) end
+scan.numeric = function() if trace then reporti("numeric") end return scan_numeric (currentmpx) end
+scan.integer = function() if trace then reporti("integer") end return scan_integer (currentmpx) end
+scan.boolean = function() if trace then reporti("boolean") end return scan_boolean (currentmpx) end
+scan.string = function() if trace then reporti("string") end return scan_string (currentmpx) end
+scan.pair = function(t) if trace then reporti("pair") end return scan_pair (currentmpx,t) end
+scan.color = function(t) if trace then reporti("color") end return scan_color (currentmpx,t) end
+scan.cmykcolor = function(t) if trace then reporti("cmykcolor") end return scan_cmykcolor (currentmpx,t) end
+scan.transform = function(t) if trace then reporti("transform") end return scan_transform (currentmpx,t) end
+scan.path = function(t) if trace then reporti("path") end return scan_path (currentmpx,t) end
+scan.pen = function(t) if trace then reporti("pen") end return scan_pen (currentmpx,t) end
+
+skip.token = function(t) return skip_token (currentmpx,t) end
+
+get.hashentry = function(n) return get_hashentry (currentmpx,n) end
+
+local solvepath = mplib.solvepath
+local getstatus = mplib.getstatus
+
+mp.solve = function(...)
+ return solvepath(currentmpx,...)
+end
+
+local inject_path = mplib.inject_path
+local inject_numeric = mplib.inject_numeric
+local inject_pair = mplib.inject_pair
+local inject_boolean = mplib.inject_boolean
+local inject_integer = mplib.inject_integer
+local inject_string = mplib.inject_string
+local inject_color = mplib.inject_color
+local inject_cmykcolor = mplib.inject_cmykcolor
+local inject_transform = mplib.inject_transform
+local inject_whatever = mplib.inject_whatever
+
+
+------.path = function(t,cycle,curled) if trace then reporti("path") end return inject_path (currentmpx,t,cycle,curled) end
+inject.numeric = function(n) if trace then reporti("numeric") end return inject_numeric (currentmpx,n) end
+inject.pair = function(x,y) if trace then reporti("pair") end return inject_pair (currentmpx,x,y) end
+inject.boolean = function(b) if trace then reporti("boolean") end return inject_boolean (currentmpx,b) end
+inject.integer = function(i) if trace then reporti("integer") end return inject_integer (currentmpx,i) end
+inject.string = function(s) if trace then reporti("string") end return inject_string (currentmpx,s) end
+inject.color = function(r,g,b) if trace then reporti("color") end return inject_color (currentmpx,r,g,b) end
+inject.cmykcolor = function(c,m,y,k) if trace then reporti("cmykcolor") end return inject_cmykcolor(currentmpx,c,m,y,k) end
+inject.transform = function(x,y,xx,xy,yx,yy) if trace then reporti("transform") end return inject_transform(currentmpx,x,y,xx,xy,yx,yy) end
+inject.whatever = function(...) if trace then reporti("whatever") end return inject_whatever (currentmpx,...) end
+
+inject.triplet = inject.color
+inject.quadruplet = inject.cmykcolor
+
+local function same(p,n)
+ local f = p[1]
+ local l = p[n]
+ local nf = #f
+ local nl = #l
+ if nf == nl then
+ for i=1,nf do
+ if f[i] ~= l[i] then
+ return false
+ end
+ end
+ return true
+ end
+ return false
+end
+
+-- local p = mp.scan.path()
+-- mp.inject.path(p,true,true)
+
+function inject.path(p,close,connector)
+ local curled = false
+ local n = #p
+ if p.close or p.cycle then
+ close = true
+ end
+ if n > 1 then
+ -- [ ../true | --/false | nil/auto ]
+ if connector == nil or connector == "auto" then
+ connector = #p[1] > 2
+ end
+ if connector == false or connector == "--" then
+ curled = true
+ elseif connector == true or connector == ".." then
+ if close and not same(p,n) then
+ p[n+1] = p[1]
+ end
+ end
+ end
+ if trace then reporti("path") end
+ return inject_path(currentmpx,p,close,curled)
+end
+
+-- bonus:
+
+scan .number = scan .numeric
+inject.number = inject.numeric
+
+table.setmetatablecall(inject,function(t,...)
+ inject_whatever(currentmpx,...)
+end)
+
+-- experiment
+
+function mp.autoinject(m)
+ local t = type(m)
+ if t == "table" then
+ local n = #t
+ if n == 2 then
+ inject_pair(currentmpx,m)
+ elseif n == 3 then
+ inject_color(currentmpx,m)
+ elseif n == 4 then
+ inject_cmykcolor(currentmpx,m)
+ elseif n == 6 then
+ inject_transform(currentmpx,m)
+ end
+ elseif t == "number" then
+ inject_numeric(currentmpx,m)
+ elseif t == "string" then
+ inject_string(currentmpx,m)
+ elseif t == "boolean" then
+ inject_boolean(currentmpx,m)
+ end
+end
+
+function metapost.pushscriptrunner(mpx)
+ if trace then
+ report("%a => %a",tostring(currentmpx),tostring(mpx))
+ end
+ insert(stack,currentmpx)
+ currentmpx = mpx
+end
+
+function metapost.popscriptrunner()
+ local mpx = remove(stack)
+ if trace then
+ report("%a <= %a",tostring(mpx),tostring(currentmpx))
+ end
+ currentmpx = mpx
+end
+
+function metapost.currentmpx()
+ return currentmpx
+end
+
+-- mplib.getstates(): zero is "normal"
+
+function metapost.currentmpxstatus()
+ return getstatus(currentmpx) or 0
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-mat.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-mat.lmt
new file mode 100644
index 00000000000..3a63b32c784
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-mat.lmt
@@ -0,0 +1,139 @@
+if not modules then modules = { } end modules ['mlib-mat'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local scanners = mp.scan
+local injectors = mp.inject
+
+local scannumeric = scanners.numeric
+local scanpair = scanners.pair
+local scancolor = scanners.color
+
+local mppair = injectors.pair or mp.pair
+
+local registerscript = metapost.registerscript
+
+local m = xmath
+local c = xcomplex
+
+local m_acos = m.acos registerscript("m_acos", function() return m_acos (scannumeric()) end)
+local m_acosh = m.acosh registerscript("m_acosh", function() return m_acosh (scannumeric()) end)
+local m_asin = m.asin registerscript("m_asin", function() return m_asin (scannumeric()) end)
+local m_asinh = m.asinh registerscript("m_asinh", function() return m_asinh (scannumeric()) end)
+local m_atan = m.atan registerscript("m_atan", function() return m_atan (scannumeric()) end)
+local m_atan2 = m.atan2 registerscript("m_atan2", function() return m_atan2 (scanpair ()) end)
+local m_atanh = m.atanh registerscript("m_atanh", function() return m_atanh (scannumeric()) end)
+local m_cbrt = m.cbrt registerscript("m_cbrt", function() return m_cbrt (scannumeric()) end)
+local m_ceil = m.ceil registerscript("m_ceil", function() return m_ceil (scannumeric()) end)
+local m_copysign = m.copysign registerscript("m_copysign", function() return m_copysign (scanpair ()) end)
+local m_cos = m.cos registerscript("m_cos", function() return m_cos (scannumeric()) end)
+local m_cosh = m.cosh registerscript("m_cosh", function() return m_cosh (scannumeric()) end)
+local m_deg = m.deg registerscript("m_deg", function() return m_deg (scannumeric()) end)
+local m_erf = m.erf registerscript("m_erf", function() return m_erf (scannumeric()) end)
+local m_erfc = m.erfc registerscript("m_erfc", function() return m_erfc (scannumeric()) end)
+local m_exp = m.exp registerscript("m_exp", function() return m_exp (scannumeric()) end)
+local m_exp2 = m.exp2 registerscript("m_exp2", function() return m_exp2 (scannumeric()) end)
+local m_expm1 = m.expm1 registerscript("m_expm1", function() return m_expm1 (scannumeric()) end)
+local m_fabs = m.fabs registerscript("m_fabs", function() return m_fabs (scannumeric()) end)
+local m_fdim = m.fdim registerscript("m_fdim", function() return m_fdim (scanpair ()) end)
+local m_floor = m.floor registerscript("m_floor", function() return m_floor (scannumeric()) end)
+local m_fma = m.fma registerscript("m_fma", function() return m_fma (scancolor ()) end)
+local m_fmax = m.fmax registerscript("m_fmax", function() return m_fmax (scannumeric()) end)
+local m_fmin = m.fmin registerscript("m_fmin", function() return m_fmin (scannumeric()) end)
+local m_fmod = m.fmod registerscript("m_fmod", function() return m_fmod (scanpair ()) end)
+local m_frexp = m.frexp registerscript("m_frexp", function() return m_frexp (scannumeric()) end)
+local m_gamma = m.gamma registerscript("m_gamma", function() return m_gamma (scannumeric()) end)
+local m_hypot = m.hypot registerscript("m_hypot", function() return m_hypot (scanpair ()) end)
+local m_isfinite = m.isfinite registerscript("m_isfinite", function() return m_isfinite (scannumeric()) end)
+local m_isinf = m.isinf registerscript("m_isinf", function() return m_isinf (scannumeric()) end)
+local m_isnan = m.isnan registerscript("m_isnan", function() return m_isnan (scannumeric()) end)
+local m_isnormal = m.isnormal registerscript("m_isnormal", function() return m_isnormal (scannumeric()) end)
+local m_j0 = m.j0 registerscript("m_j0", function() return m_j0 (scannumeric()) end)
+local m_j1 = m.j1 registerscript("m_j1", function() return m_j1 (scannumeric()) end)
+local m_jn = m.jn registerscript("m_jn", function() return m_jn (scanpair ()) end)
+local m_ldexp = m.ldexp registerscript("m_ldexp", function() return m_ldexp (scanpair ()) end)
+local m_lgamma = m.lgamma registerscript("m_lgamma", function() return m_lgamma (scannumeric()) end)
+local m_log = m.log registerscript("m_log", function() return m_log (scannumeric()) end)
+local m_log10 = m.log10 registerscript("m_log10", function() return m_log10 (scannumeric()) end)
+local m_log1p = m.log1p registerscript("m_log1p", function() return m_log1p (scannumeric()) end)
+local m_log2 = m.log2 registerscript("m_log2", function() return m_log2 (scannumeric()) end)
+local m_logb = m.logb registerscript("m_logb", function() return m_logb (scannumeric()) end)
+local m_modf = m.modf registerscript("m_modf", function() return m_modf (scannumeric()) end)
+local m_nearbyint = m.nearbyint registerscript("m_nearbyint", function() return m_nearbyint(scannumeric()) end)
+local m_nextafter = m.nextafter registerscript("m_nextafter", function() return m_nextafter(scanpair ()) end)
+local m_pow = m.pow registerscript("m_pow", function() return m_pow (scanpair ()) end)
+local m_rad = m.rad registerscript("m_rad", function() return m_rad (scannumeric()) end)
+local m_remainder = m.remainder registerscript("m_remainder", function() return m_remainder(scanpair ()) end)
+local m_remquo = m.remquo registerscript("m_remquo", function() return m_remquo (scannumeric()) end)
+local m_round = m.round registerscript("m_round", function() return m_round (scannumeric()) end)
+local m_scalbn = m.scalbn registerscript("m_scalbn", function() return m_scalbn (scanpair ()) end)
+local m_sin = m.sin registerscript("m_sin", function() return m_sin (scannumeric()) end)
+local m_sinh = m.sinh registerscript("m_sinh", function() return m_sinh (scannumeric()) end)
+local m_sqrt = m.sqrt registerscript("m_sqrt", function() return m_sqrt (scannumeric()) end)
+local m_tan = m.tan registerscript("m_tan", function() return m_tan (scannumeric()) end)
+local m_tanh = m.tanh registerscript("m_tanh", function() return m_tanh (scannumeric()) end)
+local m_tgamma = m.tgamma registerscript("m_tgamma", function() return m_tgamma (scannumeric()) end)
+local m_trunc = m.trunc registerscript("m_trunc", function() return m_trunc (scannumeric()) end)
+local m_y0 = m.y0 registerscript("m_y0", function() return m_y0 (scannumeric()) end)
+local m_y1 = m.y1 registerscript("m_y1", function() return m_y1 (scannumeric()) end)
+local m_yn = m.yn registerscript("m_yn", function() return m_yn (scanpair ()) end)
+
+if not (c and c.sin) then
+ return
+end
+
+local c_topair = c.topair
+local c_new = c.new
+
+local c_sin = c.sin registerscript("c_sin", function() return mppair(c_topair(c_sin (c_new(scanpair())))) end)
+local c_cos = c.cos registerscript("c_cos", function() return mppair(c_topair(c_cos (c_new(scanpair())))) end)
+local c_tan = c.tan registerscript("c_tan", function() return mppair(c_topair(c_tan (c_new(scanpair())))) end)
+local c_sinh = c.sinh registerscript("c_sinh", function() return mppair(c_topair(c_sinh (c_new(scanpair())))) end)
+local c_cosh = c.cosh registerscript("c_cosh", function() return mppair(c_topair(c_cosh (c_new(scanpair())))) end)
+local c_tanh = c.tanh registerscript("c_tanh", function() return mppair(c_topair(c_tanh (c_new(scanpair())))) end)
+
+local c_asin = c.asin registerscript("c_asin", function() return mppair(c_topair(c_sin (c_new(scanpair())))) end)
+local c_acos = c.acos registerscript("c_acos", function() return mppair(c_topair(c_cos (c_new(scanpair())))) end)
+local c_atan = c.atan registerscript("c_atan", function() return mppair(c_topair(c_tan (c_new(scanpair())))) end)
+local c_asinh = c.asinh registerscript("c_asinh", function() return mppair(c_topair(c_sinh (c_new(scanpair())))) end)
+local c_acosh = c.acosh registerscript("c_acosh", function() return mppair(c_topair(c_cosh (c_new(scanpair())))) end)
+local c_atanh = c.atanh registerscript("c_atanh", function() return mppair(c_topair(c_tanh (c_new(scanpair())))) end)
+
+local c_sqrt = c.sqrt registerscript("c_sqrt", function() return mppair(c_topair(c_sqrt (c_new(scanpair())))) end)
+local c_abs = c.abs registerscript("c_abs", function() return c_topair(c_abs (c_new(scanpair()))) end)
+local c_arg = c.arg registerscript("c_arg", function() return c_topair(c_arg (c_new(scanpair()))) end)
+local c_conj = c.conj registerscript("c_conj", function() return mppair(c_topair(c_conj (c_new(scanpair())))) end)
+local c_exp = c.exp registerscript("c_exp", function() return mppair(c_topair(c_exp (c_new(scanpair())))) end)
+local c_log = c.log registerscript("c_log", function() return mppair(c_topair(c_log (c_new(scanpair())))) end)
+local c_proj = c.proj registerscript("c_proj", function() return mppair(c_topair(c_proj (c_new(scanpair())))) end)
+
+local c_erf = c.erf registerscript("c_erf", function() return mppair(c_topair(c_erf (c_new(scanpair())))) end)
+local c_erfc = c.erfc registerscript("c_erfc", function() return mppair(c_topair(c_erfc (c_new(scanpair())))) end)
+local c_erfcx = c.erfcx registerscript("c_erfcx", function() return mppair(c_topair(c_erfcx (c_new(scanpair())))) end)
+local c_erfi = c.erfi registerscript("c_erfi", function() return mppair(c_topair(c_erfi (c_new(scanpair())))) end)
+local c_dawson = c.dawson registerscript("c_dawson", function() return mppair(c_topair(c_dawson(c_new(scanpair())))) end)
+
+local c_voigt = c.voigt
+local c_voigt_hwhm = c.voigt_hwhm
+
+registerscript("c_voigt", function()
+ return mppair(c_topair(c_voigt(c_new(scanpair()),c_new(scanpair()),c_new(scanpair()))))
+end)
+
+registerscript("c_voigt_hwhm", function()
+ return mppair(c_topair(c_voigt_hwhm(c_new(scanpair()),c_new(scanpair()))))
+end)
+
+local c_pow = c.pow registerscript("c_pow", function() return mppair(c_topair(c_pow(c_new(scanpair()),c_new(scanpair())))) end)
+local c_add = c.add registerscript("c_add", function() return mppair(c_topair(c_add(c_new(scanpair()),c_new(scanpair())))) end)
+local c_sub = c.sub registerscript("c_sub", function() return mppair(c_topair(c_sub(c_new(scanpair()),c_new(scanpair())))) end)
+local c_mul = c.mul registerscript("c_mul", function() return mppair(c_topair(c_mul(c_new(scanpair()),c_new(scanpair())))) end)
+local c_div = c.div registerscript("c_div", function() return mppair(c_topair(c_div(c_new(scanpair()),c_new(scanpair())))) end)
+
+local c_imag = c.imag registerscript("c_imag", function() return c_topair(c_imag(c_new(scanpair()))) end)
+local c_real = c.real registerscript("c_real", function() return c_topair(c_real(c_new(scanpair()))) end)
+local c_neg = c.neg registerscript("c_new", function() return c_topair(c_neg (c_new(scanpair()))) end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-mpf.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-mpf.lmt
new file mode 100644
index 00000000000..f24d7fde09c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-mpf.lmt
@@ -0,0 +1,1260 @@
+if not modules then modules = { } end modules ['mlib-mpf'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- moved from mlib-lua:
+
+local type, tostring, tonumber, select, loadstring = type, tostring, tonumber, select, loadstring
+local find, match, gsub, gmatch = string.find, string.match, string.gsub, string.gmatch
+local concat = table.concat
+
+local formatters = string.formatters
+local lpegmatch = lpeg.match
+local lpegpatterns = lpeg.patterns
+
+local P, S, Ct, Cs, Cc, C = lpeg.P, lpeg.S, lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.C
+
+local report_luarun = logs.reporter("metapost","lua")
+local report_script = logs.reporter("metapost","script")
+local report_message = logs.reporter("metapost")
+
+local trace_luarun = false trackers.register("metapost.lua", function(v) trace_luarun = v end)
+local trace_script = false trackers.register("metapost.script",function(v) trace_script = v end)
+
+local be_tolerant = true directives.register("metapost.lua.tolerant", function(v) be_tolerant = v end)
+
+local set = mp.set
+local get = mp.get
+local aux = mp.aux
+local scan = mp.scan
+local skip = mp.skip
+local inject = mp.inject
+
+do
+
+ -- serializers
+
+ local f_integer = formatters["%i"]
+ local f_numeric = formatters["%F"]
+
+ -- no %n as that can produce -e notation and that is not so nice for scaled butmaybe we
+ -- should then switch between ... i.e. make a push/pop for the formatters here ... not now.
+
+ local f_integer = formatters["%i"]
+ local f_numeric = formatters["%F"]
+ local f_pair = formatters["(%F,%F)"]
+ local f_ctrl = formatters["(%F,%F) .. controls (%F,%F) and (%F,%F)"]
+ local f_triplet = formatters["(%F,%F,%F)"]
+ local f_quadruple = formatters["(%F,%F,%F,%F)"]
+ local f_transform = formatters["totransform(%F,%F,%F,%F,%F,%F)"]
+ local f_pen = formatters["(pencircle transformed totransform(%F,%F,%F,%F,%F,%F))"]
+
+ local f_points = formatters["%p"]
+ local f_pair_pt = formatters["(%p,%p)"]
+ local f_ctrl_pt = formatters["(%p,%p) .. controls (%p,%p) and (%p,%p)"]
+ local f_triplet_pt = formatters["(%p,%p,%p)"]
+ local f_quadruple_pt = formatters["(%p,%p,%p,%p)"]
+
+ local r = P('%') / "percent"
+ + P('"') / "dquote"
+ + P('\n') / "crlf"
+ -- + P(' ') / "space"
+ local a = Cc("&")
+ local q = Cc('"')
+ local p = Cs(q * (r * a)^-1 * (a * r * (P(-1) + a) + P(1))^0 * q)
+
+ mp.cleaned = function(s) return lpegmatch(p,s) or s end
+
+ -- management
+
+ -- sometimes we gain (e.g. .5 sec on the sync test)
+
+ local cache = table.makeweak()
+
+ local runscripts = { }
+ local runnames = { }
+ local nofscripts = 0
+
+ function metapost.registerscript(name,f)
+ nofscripts = nofscripts + 1
+ if f then
+ runscripts[nofscripts] = f
+ runnames[name] = nofscripts
+ else
+ runscripts[nofscripts] = name
+ end
+ if trace_script then
+ report_script("registering script %a as %i",name,nofscripts)
+ end
+ return nofscripts
+ end
+
+ function metapost.scriptindex(name)
+ local index = runnames[name] or 0
+ if trace_script then
+ report_script("fetching scriptindex %i of %a",index,name)
+ end
+ return index
+ end
+
+ -- The gbuffer sharing and such is not really needed now but make a dent when
+ -- we have a high volume of simpel calls (loops) so we keep it around for a
+ -- while.
+
+ local nesting = 0
+ local runs = 0
+ local gbuffer = { }
+ local buffer = gbuffer
+ local n = 0
+
+ local function mpdirect1(a)
+ n = n + 1 buffer[n] = a
+ end
+ local function mpdirect2(a,b)
+ n = n + 1 buffer[n] = a
+ n = n + 1 buffer[n] = b
+ end
+ local function mpdirect3(a,b,c)
+ n = n + 1 buffer[n] = a
+ n = n + 1 buffer[n] = b
+ n = n + 1 buffer[n] = c
+ end
+ local function mpdirect4(a,b,c,d)
+ n = n + 1 buffer[n] = a
+ n = n + 1 buffer[n] = b
+ n = n + 1 buffer[n] = c
+ n = n + 1 buffer[n] = d
+ end
+ local function mpdirect5(a,b,c,d,e)
+ n = n + 1 buffer[n] = a
+ n = n + 1 buffer[n] = b
+ n = n + 1 buffer[n] = c
+ n = n + 1 buffer[n] = d
+ n = n + 1 buffer[n] = e
+ end
+
+ local function mpflush(separator)
+ buffer[1] = concat(buffer,separator or "",1,n)
+ n = 1
+ end
+
+ function metapost.runscript(code)
+ nesting = nesting + 1
+ runs = runs + 1
+
+ local index = type(code) == "number"
+ local f
+ local result
+
+ if index then
+ f = runscripts[code]
+ if not f then
+ report_luarun("%i: bad index: %s",nesting,code)
+ elseif trace_luarun then
+ report_luarun("%i: index: %i",nesting,code)
+ end
+ else
+ if trace_luarun then
+ report_luarun("%i: code: %s",nesting,code)
+ end
+ f = cache[code]
+ if not f then
+ f = loadstring("return " .. code)
+ if f then
+ cache[code] = f
+ elseif be_tolerant then
+ f = loadstring(code)
+ if f then
+ cache[code] = f
+ end
+ end
+ end
+ end
+
+ -- returning nil is more efficient and a signal not to scan in mp
+
+ if f then
+
+ local lbuffer, ln
+
+ if nesting == 1 then
+ buffer = gbuffer
+ n = 0
+ else
+ lbuffer = buffer
+ ln = n
+ buffer = { }
+ n = 0
+ end
+
+ result = f()
+ if result then
+ local t = type(result)
+ -- we can consider to use the injector for tables but then we need to
+ -- check if concatination is expected so best keep this!
+ if t == "number" or t == "boolean" then
+ -- native types
+ elseif t == "string" or t == "table" then
+ -- (concatenated) passed to scantokens
+ else
+ -- scantokens
+ result = tostring(result)
+ end
+ if trace_luarun then
+ report_luarun("%i: %s result: %s",nesting,t,result)
+ end
+ elseif n == 0 then
+ -- result = ""
+ result = nil -- no scantokens done then
+ if trace_luarun then
+ report_luarun("%i: no buffered result",nesting)
+ end
+ elseif n == 1 then
+ result = buffer[1]
+ if trace_luarun then
+ report_luarun("%i: 1 buffered result: %s",nesting,result)
+ end
+ else
+ -- the space is why we sometimes have collectors
+ if nesting == 1 then
+ -- if we had no space we could pass result directly in lmtx
+ result = concat(buffer," ",1,n)
+ if n > 500 or #result > 10000 then
+ gbuffer = { } -- newtable(20,0)
+ lbuffer = gbuffer
+ end
+ else
+ -- if we had no space we could pass result directly in lmtx
+ result = concat(buffer," ")
+ end
+ if trace_luarun then
+ report_luarun("%i: %i buffered results: %s",nesting,n,result)
+ end
+ end
+
+ if nesting == 1 then
+ n = 0
+ else
+ buffer = lbuffer
+ n = ln
+ end
+
+ else
+ report_luarun("%i: no result, invalid code: %s",nesting,code)
+ result = ""
+ end
+
+ nesting = nesting - 1
+
+ return result
+ end
+
+ function metapost.nofscriptruns()
+ local c = mplib.getcallbackstate()
+ return c.count, string.format(
+ "%s (file: %s, text: %s, script: %s, log: %s)",
+ c.count, c.file, c.text, c.script, c.log
+ )
+ end
+
+ -- writers
+
+ local function mpp(value)
+ n = n + 1
+ local t = type(value)
+ if t == "number" then
+ buffer[n] = f_numeric(value)
+ elseif t == "string" then
+ buffer[n] = value
+ elseif t == "table" then
+ if #t == 6 then
+ buffer[n] = "totransform(" .. concat(value,",") .. ")"
+ else
+ buffer[n] = "(" .. concat(value,",") .. ")"
+ end
+ else -- boolean or whatever
+ buffer[n] = tostring(value)
+ end
+ end
+
+ local function mpprint(first,second,...)
+ if second == nil then
+ if first ~= nil then
+ mpp(first)
+ end
+ else
+ for i=1,select("#",first,second,...) do
+ local value = (select(i,first,second,...))
+ if value ~= nil then
+ mpp(value)
+ end
+ end
+ end
+ end
+
+ local function mpp(value)
+ n = n + 1
+ local t = type(value)
+ if t == "number" then
+ buffer[n] = f_numeric(value)
+ elseif t == "string" then
+ buffer[n] = lpegmatch(p,value)
+ elseif t == "table" then
+ if #t > 4 then
+ buffer[n] = ""
+ else
+ buffer[n] = "(" .. concat(value,",") .. ")"
+ end
+ else -- boolean or whatever
+ buffer[n] = tostring(value)
+ end
+ end
+
+ local function mpvprint(first,second,...) -- variable print
+ if second == nil then
+ if first ~= nil then
+ mpp(first)
+ end
+ else
+ for i=1,select("#",first,second,...) do
+ local value = (select(i,first,second,...))
+ if value ~= nil then
+ mpp(value)
+ end
+ end
+ end
+ end
+
+ local function mpstring(value)
+ n = n + 1
+ buffer[n] = lpegmatch(p,value)
+ end
+
+ local function mpboolean(b)
+ n = n + 1
+ buffer[n] = b and "true" or "false"
+ end
+
+ local function mpnumeric(f)
+ n = n + 1
+ if not f or f == 0 then
+ buffer[n] = "0"
+ else
+ buffer[n] = f_numeric(f)
+ end
+ end
+
+ local function mpinteger(i)
+ n = n + 1
+ -- buffer[n] = i and f_integer(i) or "0"
+ buffer[n] = i or "0"
+ end
+
+ local function mppoints(i)
+ n = n + 1
+ if not i or i == 0 then
+ buffer[n] = "0pt"
+ else
+ buffer[n] = f_points(i)
+ end
+ end
+
+ local function mppair(x,y)
+ n = n + 1
+ if type(x) == "table" then
+ buffer[n] = f_pair(x[1],x[2])
+ else
+ buffer[n] = f_pair(x,y)
+ end
+ end
+
+ local function mppairpoints(x,y)
+ n = n + 1
+ if type(x) == "table" then
+ buffer[n] = f_pair_pt(x[1],x[2])
+ else
+ buffer[n] = f_pair_pt(x,y)
+ end
+ end
+
+ local function mptriplet(x,y,z)
+ n = n + 1
+ if type(x) == "table" then
+ buffer[n] = f_triplet(x[1],x[2],x[3])
+ else
+ buffer[n] = f_triplet(x,y,z)
+ end
+ end
+
+ local function mptripletpoints(x,y,z)
+ n = n + 1
+ if type(x) == "table" then
+ buffer[n] = f_triplet_pt(x[1],x[2],x[3])
+ else
+ buffer[n] = f_triplet_pt(x,y,z)
+ end
+ end
+
+ local function mpquadruple(w,x,y,z)
+ n = n + 1
+ if type(w) == "table" then
+ buffer[n] = f_quadruple(w[1],w[2],w[3],w[4])
+ else
+ buffer[n] = f_quadruple(w,x,y,z)
+ end
+ end
+
+ local function mpquadruplepoints(w,x,y,z)
+ n = n + 1
+ if type(w) == "table" then
+ buffer[n] = f_quadruple_pt(w[1],w[2],w[3],w[4])
+ else
+ buffer[n] = f_quadruple_pt(w,x,y,z)
+ end
+ end
+
+ local function mptransform(x,y,xx,xy,yx,yy)
+ n = n + 1
+ if type(x) == "table" then
+ buffer[n] = f_transform(x[1],x[2],x[3],x[4],x[5],x[6])
+ else
+ buffer[n] = f_transform(x,y,xx,xy,yx,yy)
+ end
+ end
+
+ local function mpcolor(c,m,y,k)
+ n = n + 1
+ if type(c) == "table" then
+ local l = #c
+ if l == 4 then
+ buffer[n] = f_quadruple(c[1],c[2],c[3],c[4])
+ elseif l == 3 then
+ buffer[n] = f_triplet(c[1],c[2],c[3])
+ else
+ buffer[n] = f_numeric(c[1])
+ end
+ else
+ if k then
+ buffer[n] = f_quadruple(c,m,y,k)
+ elseif y then
+ buffer[n] = f_triplet(c,m,y)
+ else
+ buffer[n] = f_numeric(c)
+ end
+ end
+ end
+
+ -- we have three kind of connectors:
+ --
+ -- .. ... -- (true)
+
+ local function mp_path(f2,f6,t,connector,cycle)
+ if type(t) == "table" then
+ local tn = #t
+ if tn == 1 then
+ local t1 = t[1]
+ n = n + 1
+ if t.pen then
+ buffer[n] = f_pen(unpack(t1))
+ else
+ buffer[n] = f2(t1[1],t1[2])
+ end
+ elseif tn > 0 then
+ if connector == true or connector == nil then
+ connector = ".."
+ elseif connector == false then
+ connector = "--"
+ end
+ if cycle == nil then
+ cycle = t.cycle
+ if cycle == nil then
+ cycle = true
+ end
+ end
+ local six = connector == ".." -- otherwise we use whatever gets asked for
+ local controls = connector -- whatever
+ local a = t[1]
+ local b = t[2]
+ n = n + 1
+ buffer[n] = "("
+ n = n + 1
+ if six and #a == 6 and #b == 6 then
+ buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4])
+ controls = ".."
+ else
+ buffer[n] = f2(a[1],a[2])
+ controls = connector
+ end
+ for i=2,tn-1 do
+ a = b
+ b = t[i+1]
+ n = n + 1
+ buffer[n] = connector
+ n = n + 1
+ if six and #a == 6 and #b == 6 then
+ buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4])
+ controls = ".."
+ else
+ buffer[n] = f2(a[1],a[2])
+ controls = connector
+ end
+ end
+ n = n + 1
+ buffer[n] = connector
+ a = b
+ b = t[1]
+ n = n + 1
+ if cycle then
+ if six and #a == 6 and #b == 6 then
+ buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4])
+ controls = ".."
+ else
+ buffer[n] = f2(a[1],a[2])
+ controls = connector
+ end
+ n = n + 1
+ buffer[n] = connector
+ n = n + 1
+ buffer[n] = "cycle"
+ else
+ buffer[n] = f2(a[1],a[2])
+ end
+ n = n + 1
+ buffer[n] = ")"
+ end
+ end
+ end
+
+ local function mppath(...)
+ mp_path(f_pair,f_ctrl,...)
+ end
+
+ local function mppathpoints(...)
+ mp_path(f_pair_pt,f_ctrl_pt,...)
+ end
+
+ local function mpsize(t)
+ n = n + 1
+ buffer[n] = type(t) == "table" and f_numeric(#t) or "0"
+ end
+
+ local replacer = lpeg.replacer("@","%%")
+
+ local function mpfprint(fmt,...)
+ n = n + 1
+ if not find(fmt,"%",1,true) then
+ fmt = lpegmatch(replacer,fmt)
+ end
+ buffer[n] = formatters[fmt](...)
+ end
+
+ local function mpquoted(fmt,s,...)
+ if s then
+ n = n + 1
+ if not find(fmt,"%",1,true) then
+ fmt = lpegmatch(replacer,fmt)
+ end
+ -- buffer[n] = '"' .. formatters[fmt](s,...) .. '"'
+ buffer[n] = lpegmatch(p,formatters[fmt](s,...))
+ elseif fmt then
+ n = n + 1
+ -- buffer[n] = '"' .. fmt .. '"'
+ buffer[n] = lpegmatch(p,fmt)
+ else
+ -- something is wrong
+ end
+ end
+
+ aux.direct = mpdirect1
+ aux.direct1 = mpdirect1
+ aux.direct2 = mpdirect2
+ aux.direct3 = mpdirect3
+ aux.direct4 = mpdirect4
+ aux.flush = mpflush
+
+ aux.print = mpprint
+ aux.vprint = mpvprint
+ aux.boolean = mpboolean
+ aux.string = mpstring
+ aux.numeric = mpnumeric
+ aux.number = mpnumeric
+ aux.integer = mpinteger
+ aux.points = mppoints
+ aux.pair = mppair
+ aux.pairpoints = mppairpoints
+ aux.triplet = mptriplet
+ aux.tripletpoints = mptripletpoints
+ aux.quadruple = mpquadruple
+ aux.quadruplepoints = mpquadruplepoints
+ aux.path = mppath
+ aux.pathpoints = mppathpoints
+ aux.size = mpsize
+ aux.fprint = mpfprint
+ aux.quoted = mpquoted
+ aux.transform = mptransform
+ aux.color = mpcolor
+
+ -- for the moment
+
+ local function mpdraw(lines,list) -- n * 4
+ if list then
+ local c = #lines
+ for i=1,c do
+ local ci = lines[i]
+ local ni = #ci
+ n = n + 1 buffer[n] = i < c and "d(" or "D("
+ for j=1,ni,2 do
+ local l = j + 1
+ n = n + 1 buffer[n] = ci[j]
+ n = n + 1 buffer[n] = ","
+ n = n + 1 buffer[n] = ci[l]
+ n = n + 1 buffer[n] = l < ni and ")--(" or ");"
+ end
+ end
+ else
+ local l = #lines
+ local m = l - 4
+ for i=1,l,4 do
+ n = n + 1 buffer[n] = i < m and "d(" or "D("
+ n = n + 1 buffer[n] = lines[i]
+ n = n + 1 buffer[n] = ","
+ n = n + 1 buffer[n] = lines[i+1]
+ n = n + 1 buffer[n] = ")--("
+ n = n + 1 buffer[n] = lines[i+2]
+ n = n + 1 buffer[n] = ","
+ n = n + 1 buffer[n] = lines[i+3]
+ n = n + 1 buffer[n] = ");"
+ end
+ end
+ end
+
+ local function mpfill(lines,list)
+ if list then
+ local c = #lines
+ for i=1,c do
+ local ci = lines[i]
+ local ni = #ci
+ n = n + 1 buffer[n] = i < c and "f(" or "F("
+ for j=1,ni,2 do
+ local l = j + 1
+ n = n + 1 buffer[n] = ci[j]
+ n = n + 1 buffer[n] = ","
+ n = n + 1 buffer[n] = ci[l]
+ n = n + 1 buffer[n] = l < ni and ")--(" or ")--C;"
+ end
+ end
+ else
+ local l = #lines
+ local m = l - 4
+ for i=1,l,4 do
+ n = n + 1 buffer[n] = i < m and "f(" or "F("
+ n = n + 1 buffer[n] = lines[i]
+ n = n + 1 buffer[n] = ","
+ n = n + 1 buffer[n] = lines[i+1]
+ n = n + 1 buffer[n] = ")--("
+ n = n + 1 buffer[n] = lines[i+2]
+ n = n + 1 buffer[n] = ","
+ n = n + 1 buffer[n] = lines[i+3]
+ n = n + 1 buffer[n] = ")--C;"
+ end
+ end
+ end
+
+ aux.draw = mpdraw
+ aux.fill = mpfill
+
+ for k, v in next, aux do mp[k] = v end
+
+end
+
+do
+
+ -- Another experimental feature:
+
+ local mpnumeric = mp.numeric
+ local scanstring = scan.string
+ local scriptindex = metapost.scriptindex
+
+ function mp.mf_script_index(name)
+ local index = scriptindex(name)
+ -- report_script("method %i, name %a, index %i",1,name,index)
+ mpnumeric(index)
+ end
+
+ -- once bootstrapped ... (needs pushed mpx instances)
+
+ metapost.registerscript("scriptindex",function()
+ local name = scanstring()
+ local index = scriptindex(name)
+ -- report_script("method %i, name %a, index %i",2,name,index)
+ mpnumeric(index)
+ end)
+
+end
+
+-- the next will move to mlib-lmp.lua
+
+do
+
+ local mpnamedcolor = attributes.colors.mpnamedcolor
+ local mpprint = aux.print
+ local scanstring = scan.string
+
+ mp.mf_named_color = function(str)
+ mpprint(mpnamedcolor(str))
+ end
+
+ -- todo: we can inject but currently we always get a string back so then
+ -- we need to deal with it upstream in the color module ... not now
+
+ metapost.registerscript("namedcolor",function()
+ mpprint(mpnamedcolor(scanstring()))
+ end)
+
+end
+
+function mp.n(t) -- used ?
+ return type(t) == "table" and #t or 0
+end
+
+do
+
+ -- experiment: names can change
+
+ local mppath = aux.path
+ local mpsize = aux.size
+
+ local whitespace = lpegpatterns.whitespace
+ local newline = lpegpatterns.newline
+ local setsep = newline^2
+ local comment = (S("#%") + P("--")) * (1-newline)^0 * (whitespace - setsep)^0
+ local value = (1-whitespace)^1 / tonumber
+ local entry = Ct( value * whitespace * value)
+ local set = Ct((entry * (whitespace-setsep)^0 * comment^0)^1)
+ local series = Ct((set * whitespace^0)^1)
+
+ local pattern = whitespace^0 * series
+
+ local datasets = { }
+ mp.datasets = datasets
+
+ function mp.dataset(str)
+ return lpegmatch(pattern,str)
+ end
+
+ function datasets.load(tag,filename)
+ if not filename then
+ tag, filename = file.basename(tag), tag
+ end
+ local data = lpegmatch(pattern,io.loaddata(filename) or "")
+ datasets[tag] = {
+ data = data,
+ line = function(n) mppath(data[n or 1]) end,
+ size = function() mpsize(data) end,
+ }
+ end
+
+ table.setmetatablecall(datasets,function(t,k,f,...)
+ local d = datasets[k]
+ local t = type(d)
+ if t == "table" then
+ d = d[f]
+ if type(d) == "function" then
+ d(...)
+ else
+ mpvprint(...)
+ end
+ elseif t == "function" then
+ d(f,...)
+ end
+ end)
+
+end
+
+-- \startluacode
+-- local str = [[
+-- 10 20 20 20
+-- 30 40 40 60
+-- 50 10
+--
+-- 10 10 20 30
+-- 30 50 40 50
+-- 50 20 -- the last one
+--
+-- 10 20 % comment
+-- 20 10
+-- 30 40 # comment
+-- 40 20
+-- 50 10
+-- ]]
+--
+-- MP.myset = mp.dataset(str)
+--
+-- inspect(MP.myset)
+-- \stopluacode
+--
+-- \startMPpage
+-- color c[] ; c[1] := red ; c[2] := green ; c[3] := blue ;
+-- for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+-- draw lua("mp.path(MP.myset[" & decimal i & "])") withcolor c[i] ;
+-- endfor ;
+-- \stopMPpage
+
+-- texts:
+
+do
+
+ local mptriplet = mp.triplet
+
+ local bpfactor = number.dimenfactors.bp
+ local textexts = nil
+ local mptriplet = mp.triplet
+ local nbdimensions = nodes.boxes.dimensions
+
+ function mp.mf_tt_initialize(tt)
+ textexts = tt
+ end
+
+ function mp.mf_tt_dimensions(n)
+ local box = textexts and textexts[n]
+ if box then
+ -- could be made faster with nuts but not critical
+ mptriplet(box.width*bpfactor,box.height*bpfactor,box.depth*bpfactor)
+ else
+ mptriplet(0,0,0)
+ end
+ end
+
+ function mp.mf_tb_dimensions(category,name)
+ local w, h, d = nbdimensions(category,name)
+ mptriplet(w*bpfactor,h*bpfactor,d*bpfactor)
+ end
+
+ function mp.report(a,b,c,...)
+ if c then
+ report_message("%s : %s",a,formatters[(gsub(b,"@","%%"))](c,...))
+ elseif b then
+ report_message("%s : %s",a,b)
+ elseif a then
+ report_message("%s : %s","message",a)
+ end
+ end
+
+end
+
+do
+
+ local mpprint = aux.print
+ local modes = tex.modes
+ local systemmodes = tex.systemmodes
+
+ function mp.mode(s)
+ mpprint(modes[s] and true or false)
+ end
+
+ function mp.systemmode(s)
+ mpprint(systemmodes[s] and true or false)
+ end
+
+ mp.processingmode = mp.mode
+
+end
+
+-- for alan's nodes:
+
+do
+
+ local mpprint = aux.print
+ local mpquoted = aux.quoted
+
+ function mp.isarray(str)
+ mpprint(find(str,"%d") and true or false)
+ end
+
+ function mp.prefix(str)
+ mpquoted(match(str,"^(.-)[%d%[]") or str)
+ end
+
+ -- function mp.dimension(str)
+ -- local n = 0
+ -- for s in gmatch(str,"%[?%-?%d+%]?") do --todo: lpeg
+ -- n = n + 1
+ -- end
+ -- mpprint(n)
+ -- end
+
+ mp.dimension = lpeg.counter(P("[") * lpegpatterns.integer * P("]") + lpegpatterns.integer,mpprint)
+
+ -- faster and okay as we don't have many variables but probably only
+ -- basename makes sense and even then it's not called that often
+
+ -- local hash = table.setmetatableindex(function(t,k)
+ -- local v = find(k,"%d") and true or false
+ -- t[k] = v
+ -- return v
+ -- end)
+ --
+ -- function mp.isarray(str)
+ -- mpprint(hash[str])
+ -- end
+ --
+ -- local hash = table.setmetatableindex(function(t,k)
+ -- local v = '"' .. (match(k,"^(.-)%d") or k) .. '"'
+ -- t[k] = v
+ -- return v
+ -- end)
+ --
+ -- function mp.prefix(str)
+ -- mpprint(hash[str])
+ -- end
+
+end
+
+do
+
+ local scanstring = scan.string
+ local scannumeric = scan.numeric
+ local skiptoken = skip.token
+
+ local injectstring = inject.string
+ local injectnumeric = inject.numeric
+
+ local registerscript = metapost.registerscript
+
+ local comma_code = metapost.codes.comma
+
+ local getmacro = tokens.getters.macro
+ local setmacro = tokens.setters.macro
+
+ local getdimen = tex.getdimen
+ local getcount = tex.getcount
+ local gettoks = tex.gettoks
+ local setdimen = tex.setdimen
+ local setcount = tex.setcount
+ local settoks = tex.settoks
+
+ local bpfactor = number.dimenfactors.bp
+
+ -- more helpers
+
+ registerscript("getmacro", function() injectstring (getmacro(scanstring())) end)
+ registerscript("getdimen", function() injectnumeric(getdimen(scanstring())*bpfactor) end)
+ registerscript("getcount", function() injectnumeric(getcount(scanstring())) end)
+ registerscript("gettoks", function() injectstring (gettoks (scanstring())) end)
+
+ registerscript("setmacro", function() setmacro(scanstring(),scanstring()) end)
+ registerscript("setdimen", function() setdimen(scanstring(),scannumeric()/bpfactor) end)
+ registerscript("setcount", function() setcount(scanstring(),scannumeric()) end)
+ registerscript("settoks", function() settoks (scanstring(),scanstring()) end)
+
+ registerscript("setglobalmacro", function() setmacro(scanstring(),scanstring(),"global") end)
+ registerscript("setglobaldimen", function() setdimen("global",scanstring(),scannumeric()/bpfactor) end)
+ registerscript("setglobalcount", function() setcount("global",scanstring(),scannumeric()) end)
+ registerscript("setglobaltoks", function() settoks ("global",scanstring(),scanstring()) end)
+
+
+ local utfnum = utf.byte
+ local utflen = utf.len
+ local utfsub = utf.sub
+
+ registerscript("utfnum", function()
+ injectnumeric(utfnum(scanstring()))
+ end)
+
+ registerscript("utflen", function()
+ injectnumeric(utflen(scanstring()))
+ end)
+
+ registerscript("utfsub", function() -- we have an optional third argument so we explicitly scan a text argument
+ injectstring(utfsub(scanstring(),skiptoken(comma_code) and scannumeric(),skiptoken(comma_code) and scannumeric()))
+ end)
+
+end
+
+-- position fun
+
+do
+
+ local mpprint = mp.print
+ local mpfprint = mp.fprint
+ local mpquoted = mp.quoted
+ local jobpositions = job.positions
+ local getwhd = jobpositions.whd
+ local getxy = jobpositions.xy
+ local getposition = jobpositions.position
+ local getpage = jobpositions.page
+ local getregion = jobpositions.region
+ local getmacro = tokens.getters.macro
+
+ function mp.positionpath(name)
+ local w, h, d = getwhd(name)
+ if w then
+ mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",0,-d,w,-d,w,h,0,h)
+ else
+ mpprint("(origin--cycle)")
+ end
+ end
+
+ function mp.positioncurve(name)
+ local w, h, d = getwhd(name)
+ if w then
+ mpfprint("((%p,%p)..(%p,%p)..(%p,%p)..(%p,%p)..cycle)",0,-d,w,-d,w,h,0,h)
+ else
+ mpprint("(origin--cycle)")
+ end
+ end
+
+ function mp.positionbox(name)
+ local p, x, y, w, h, d = getposition(name)
+ if p then
+ mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h)
+ else
+ mpprint("(%p,%p)",x,y)
+ end
+ end
+
+ function mp.positionxy(name)
+ local x, y = getxy(name)
+ if x then
+ mpfprint("(%p,%p)",x,y)
+ else
+ mpprint("origin")
+ end
+ end
+
+ function mp.positionpage(name)
+ mpfprint("%i",getpage(name) or 0)
+ end
+
+ function mp.positionregion(name)
+ local r = getregion(name)
+ if r then
+ mpquoted(r)
+ else
+ mpquoted("unknown")
+ end
+ end
+
+ function mp.positionwhd(name)
+ local w, h, d = getwhd(name)
+ if w then
+ mpfprint("(%p,%p,%p)",w,h,d)
+ else
+ mpprint("(0,0,0)")
+ end
+ end
+
+ function mp.positionpxy(name)
+ local p, x, y = getposition(name)
+ if p then
+ mpfprint("(%p,%p,%p)",p,x,y)
+ else
+ mpprint("(0,0,0)")
+ end
+ end
+
+ function mp.positionanchor()
+ mpquoted(getmacro("MPanchorid"))
+ end
+
+end
+
+do
+
+ -- local mppair = mp.pair
+ --
+ -- function mp.textextanchor(s)
+ -- local x, y = match(s,"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg
+ -- if x and y then
+ -- x = tonumber(x)
+ -- y = tonumber(y)
+ -- end
+ -- mppair(x or 0,y or 0)
+ -- end
+
+ local injectpair = inject.pair
+ local scanstring = scan.string
+
+ metapost.registerscript("textextanchor", function()
+ local x, y = match(scanstring(),"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg
+ if x and y then
+ x = tonumber(x)
+ y = tonumber(y)
+ end
+ injectpair(x or 0,y or 0)
+ end)
+
+end
+
+do
+
+ local mpprint = mp.print
+ local mpquoted = mp.quoted
+ local getmacro = tokens.getters.macro
+
+ function mp.texvar(name)
+ mpprint(getmacro(metapost.namespace .. name))
+ end
+
+ function mp.texstr(name)
+ mpquoted(getmacro(metapost.namespace .. name))
+ end
+
+end
+
+do
+
+ local mpprint = aux.print
+ local mpvprint = aux.vprint
+
+ local hashes = { }
+
+ function mp.newhash(name)
+ if name then
+ hashes[name] = { }
+ else
+ for i=1,#hashes+1 do
+ if not hashes[i] then
+ hashes[i] = { }
+ mpvprint(i)
+ return
+ end
+ end
+ end
+ end
+
+ function mp.disposehash(n)
+ if tonumber(n) then
+ hashes[n] = false
+ else
+ hashes[n] = nil
+ end
+ end
+
+ function mp.inhash(n,key)
+ local h = hashes[n]
+ mpvprint(h and h[key] and true or false)
+ end
+
+ function mp.tohash(n,key,value)
+ local h = hashes[n]
+ if h then
+ if value == nil then
+ h[key] = true
+ else
+ h[key] = value
+ end
+ end
+ end
+
+ function mp.fromhash(n,key)
+ local h = hashes[n]
+ mpvprint(h and h[key] or false)
+ end
+
+ interfaces.implement {
+ name = "MPfromhash",
+ arguments = "2 strings",
+ actions = function(name,key)
+ local h = hashes[name] or hashes[tonumber(name)]
+ if h then
+ local v = h[key] or h[tonumber(key)]
+ if v then
+ context(v)
+ end
+ end
+ end
+ }
+
+end
+
+do
+
+ -- a bit overkill: just a find(str,"mf_object=") can be enough
+ --
+ -- todo : share with mlib-pps.lua metapost,isobject
+
+ local mpboolean = aux.boolean
+
+ local p1 = P("mf_object=")
+ local p2 = lpegpatterns.eol * p1
+ local pattern = (1-p2)^0 * p2 + p1
+
+ function mp.isobject(str)
+ mpboolean(pattern and str ~= "" and lpegmatch(pattern,str))
+ end
+
+end
+
+function mp.flatten(t)
+ local tn = #t
+
+ local t1 = t[1]
+ local t2 = t[2]
+ local t3 = t[3]
+ local t4 = t[4]
+
+ for i=1,tn-5,2 do
+ local t5 = t[i+4]
+ local t6 = t[i+5]
+ if t1 == t3 and t3 == t5 and ((t2 <= t4 and t4 <= t6) or (t6 <= t4 and t4 <= t2)) then
+ t[i+3] = t2
+ t4 = t2
+ t[i] = false
+ t[i+1] = false
+ elseif t2 == t4 and t4 == t6 and ((t1 <= t3 and t3 <= t5) or (t5 <= t3 and t3 <= t1)) then
+ t[i+2] = t1
+ t3 = t1
+ t[i] = false
+ t[i+1] = false
+ end
+ t1 = t3
+ t2 = t4
+ t3 = t5
+ t4 = t6
+ end
+
+ -- remove duplicates
+
+ local t1 = t[1]
+ local t2 = t[2]
+ for i=1,tn-2,2 do
+ local t3 = t[i+2]
+ local t4 = t[i+3]
+ if t1 == t3 and t2 == t4 then
+ t[i] = false
+ t[i+1] = false
+ end
+ t1 = t3
+ t2 = t4
+ end
+
+ -- move coordinates
+
+ local m = 0
+ for i=1,tn,2 do
+ if t[i] then
+ m = m + 1 t[m] = t[i]
+ m = m + 1 t[m] = t[i+1]
+ end
+ end
+
+ -- prune the table (not gc'd)
+
+ for i=tn,m+1,-1 do
+ t[i] = nil
+ end
+
+ -- safeguard so that we have at least one segment
+
+ if m == 2 then
+ t[3] = t[1]
+ t[4] = t[2]
+ end
+
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.lmt
new file mode 100644
index 00000000000..c383d52ded1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.lmt
@@ -0,0 +1,742 @@
+if not modules then modules = { } end modules ['mlib-pdf'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local gsub = string.gsub
+local concat, insert, remove = table.concat, table.insert, table.remove
+local abs, sqrt, round = math.abs, math.sqrt, math.round
+local setmetatable, rawset, tostring, tonumber, type = setmetatable, rawset, tostring, tonumber, type
+local P, S, C, Ct, Cc, Cg, Cf, Carg = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Carg
+local lpegmatch = lpeg.match
+local formatters = string.formatters
+
+local report_metapost = logs.reporter("metapost")
+
+local trace_variables = false trackers.register("metapost.variables",function(v) trace_variables = v end)
+
+local mplib = mplib
+local context = context
+
+local allocate = utilities.storage.allocate
+
+local copy_node = node.copy
+local write_node = node.write
+
+local pen_info = mplib.pen_info
+local getfields = mplib.getfields or mplib.fields -- todo: in lmtx get them once and then use gettype
+
+local save_table = false
+local force_stroke = false
+
+metapost = metapost or { }
+local metapost = metapost
+
+metapost.flushers = metapost.flushers or { }
+local pdfflusher = { }
+metapost.flushers.pdf = pdfflusher
+
+metapost.n = 0
+
+local mpsliteral = nodes.pool.originliteral
+
+local f_f = formatters["%.6N"]
+local f_m = formatters["%.6N %.6N m"]
+local f_c = formatters["%.6N %.6N %.6N %.6N %.6N %.6N c"]
+local f_l = formatters["%.6N %.6N l"]
+local f_cm = formatters["%.6N %.6N %.6N %.6N %.6N %.6N cm"]
+local f_M = formatters["%.6N M"]
+local f_j = formatters["%i j"]
+local f_J = formatters["%i J"]
+local f_d = formatters["[%s] %.6N d"]
+local f_w = formatters["%.6N w"]
+
+directives.register("metapost.savetable",function(v)
+ if type(v) == "string" then
+ save_table = file.addsuffix(v,"mpl")
+ elseif v then
+ save_table = file.addsuffix(environment.jobname .. "-graphic","mpl")
+ else
+ save_table = false
+ end
+end)
+
+trackers.register("metapost.forcestroke",function(v)
+ force_stroke = v
+end)
+
+-- Because in MKiV we always have two passes, we save the objects. When an extra
+-- mp run is done (due to for instance texts identifier in the parse pass), we
+-- get a new result table and the stored objects are forgotten. Otherwise they
+-- are reused.
+
+local function getobjects(result,figure,index)
+ return figure:objects()
+end
+
+function metapost.convert(specification,result)
+ local flusher = specification.flusher
+ local askedfig = specification.askedfig
+ if save_table then
+ table.save(save_table,metapost.totable(result,1)) -- direct
+ end
+ metapost.flush(specification,result)
+ return true -- done
+end
+
+function pdfflusher.tocomment(message)
+ if message then
+ return formatters["%% mps graphic %s: %s"](metapost.n,message)
+ else
+ return formatters["%% mps graphic %s"](metapost.n)
+ end
+end
+
+function pdfflusher.startfigure(n,llx,lly,urx,ury,message)
+ metapost.n = metapost.n + 1
+ context.startMPLIBtoPDF(f_f(llx),f_f(lly),f_f(urx),f_f(ury))
+end
+
+function pdfflusher.stopfigure(message)
+ context.stopMPLIBtoPDF()
+end
+
+function pdfflusher.flushfigure(pdfliterals) -- table
+ if #pdfliterals > 0 then
+ pdfliterals = concat(pdfliterals,"\n")
+ context(mpsliteral(pdfliterals))
+ end
+end
+
+function pdfflusher.textfigure(font,size,text,width,height,depth) -- we could save the factor
+ text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost (i have to check if this is true for mplib)
+ context.MPtextext(font,size,text,0,-number.dimenfactors.bp*depth)
+end
+
+local bend_tolerance = 131/65536
+
+local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1
+
+local function pen_characteristics(object)
+ local t = pen_info(object)
+ rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty
+ divider = sx*sy - rx*ry
+ return not (sx == 1 and rx == 0 and ry == 0 and sy == 1 and tx == 0 and ty == 0), t.width
+end
+
+local function mpconcat(px, py) -- no tx, ty here / we can move this one inline if needed
+ return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider
+end
+
+local function curved(ith,pth)
+ local d = pth.left_x - ith.right_x
+ if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then
+ d = pth.left_y - ith.right_y
+ if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then
+ return false
+ end
+ end
+ return true
+end
+
+local function flushnormalpath(path, t, open)
+ local pth, ith, nt
+ local length = #path
+ if t then
+ nt = #t
+ else
+ t = { }
+ nt = 0
+ end
+ for i=1,length do
+ nt = nt + 1
+ pth = path[i]
+ if not ith then
+ t[nt] = f_m(pth.x_coord,pth.y_coord)
+-- elseif curved(ith,pth) then
+elseif pth.curved then
+ t[nt] = f_c(ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
+ else
+ t[nt] = f_l(pth.x_coord,pth.y_coord)
+ end
+ ith = pth
+ end
+ if not open then
+ nt = nt + 1
+ local one = path[1]
+-- if curved(pth,one) then
+if one.curved then
+ t[nt] = f_c(pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord)
+ else
+ t[nt] = f_l(one.x_coord,one.y_coord)
+ end
+ elseif length == 1 then
+ -- special case .. draw point
+ local one = path[1]
+ nt = nt + 1
+ t[nt] = f_l(one.x_coord,one.y_coord)
+ end
+ return t
+end
+
+local function flushconcatpath(path, t, open)
+ local pth, ith, nt
+ local length = #path
+ if t then
+ nt = #t
+ else
+ t = { }
+ nt = 0
+ end
+ nt = nt + 1
+ t[nt] = f_cm(sx,rx,ry,sy,tx,ty)
+ for i=1,length do
+ nt = nt + 1
+ pth = path[i]
+ if not ith then
+ t[nt] = f_m(mpconcat(pth.x_coord,pth.y_coord))
+-- elseif curved(ith,pth) then
+elseif pth.curved then
+ local a, b = mpconcat(ith.right_x,ith.right_y)
+ local c, d = mpconcat(pth.left_x,pth.left_y)
+ t[nt] = f_c(a,b,c,d,mpconcat(pth.x_coord,pth.y_coord))
+ else
+ t[nt] = f_l(mpconcat(pth.x_coord, pth.y_coord))
+ end
+ ith = pth
+ end
+ if not open then
+ nt = nt + 1
+ local one = path[1]
+-- if curved(pth,one) then
+if one.curved then
+ local a, b = mpconcat(pth.right_x,pth.right_y)
+ local c, d = mpconcat(one.left_x,one.left_y)
+ t[nt] = f_c(a,b,c,d,mpconcat(one.x_coord, one.y_coord))
+ else
+ t[nt] = f_l(mpconcat(one.x_coord,one.y_coord))
+ end
+ elseif length == 1 then
+ -- special case .. draw point
+ nt = nt + 1
+ local one = path[1]
+ t[nt] = f_l(mpconcat(one.x_coord,one.y_coord))
+ end
+ return t
+end
+
+local function toboundingbox(path)
+ local size = #path
+ if size == 4 then
+ local pth = path[1]
+ local x = pth.x_coord
+ local y = pth.y_coord
+ local llx, lly, urx, ury = x, y, x, y
+ for i=2,size do
+ pth = path[i]
+ x = pth.x_coord
+ y = pth.y_coord
+ if x < llx then
+ llx = x
+ elseif x > urx then
+ urx = x
+ end
+ if y < lly then
+ lly = y
+ elseif y > ury then
+ ury = y
+ end
+ end
+ return { llx, lly, urx, ury }
+ else
+ return { 0, 0, 0, 0 }
+ end
+end
+
+metapost.flushnormalpath = flushnormalpath
+
+-- The flusher is pdf based, if another backend is used, we need to overload the
+-- flusher; this is beta code, the organization will change (already upgraded in
+-- sync with mplib)
+--
+-- We can avoid the before table but I like symmetry. There is of course a small
+-- performance penalty, but so is passing extra arguments (result, flusher, after)
+-- and returning stuff.
+--
+-- This variable stuff will change in lmtx.
+
+local ignore = function() end
+
+local space = P(" ")
+local equal = P("=")
+local key = C((1-equal)^1) * equal
+local newline = S("\n\r")^1
+local number = (((1-space-newline)^1) / tonumber) * (space^0)
+
+local p_number = number
+local p_string = C((1-newline)^0)
+local p_boolean = P("false") * Cc(false) + P("true") * Cc(true)
+local p_set = Ct(number^1)
+local p_path = Ct(Ct(number * number^-5)^1)
+
+local variable =
+ P("1:") * p_number
+ + P("2:") * p_string
+ + P("3:") * p_boolean
+ + S("4568") * P(":") * p_set
+ + P("7:") * p_path
+
+local pattern_tab = Cf ( Carg(1) * (Cg(variable * newline^0)^0), rawset)
+
+local variable =
+ P("1:") * p_number
+ + P("2:") * p_string
+ + P("3:") * p_boolean
+ + S("4568") * P(":") * number^1
+ + P("7:") * (number * number^-5)^1
+
+local pattern_lst = (variable * newline^0)^0
+
+metapost.variables = { } -- currently across instances
+metapost.properties = { } -- to be stacked
+
+function metapost.untagvariable(str,variables) -- will be redone
+ if variables == false then
+ return lpegmatch(pattern_lst,str)
+ else
+ return lpegmatch(pattern_tab,str,1,variables or { })
+ end
+end
+
+-- function metapost.processspecial(str)
+-- lpegmatch(pattern_key,object.prescript,1,variables)
+-- end
+
+-- function metapost.processspecial(str)
+-- local code = loadstring(str)
+-- if code then
+-- if trace_variables then
+-- report_metapost("executing special code: %s",str)
+-- end
+-- code()
+-- else
+-- report_metapost("invalid special code: %s",str)
+-- end
+-- end
+
+local stack = { }
+
+local function pushproperties(figure)
+ -- maybe there will be getters in lmtx
+ local boundingbox = figure:boundingbox()
+ local slot = figure:charcode() or 0
+ local properties = {
+ llx = boundingbox[1],
+ lly = boundingbox[2],
+ urx = boundingbox[3],
+ ury = boundingbox[4],
+ slot = slot,
+ width = figure:width(),
+ height = figure:height(),
+ depth = figure:depth(),
+ italic = figure:italcorr(), -- figure:italic() in lmtx
+ number = slot,
+ }
+ insert(stack,properties)
+ metapost.properties = properties
+ return properties
+end
+
+local function popproperties()
+ metapost.properties = remove(stack)
+end
+
+function metapost.flush(specification,result)
+ if result then
+ local flusher = specification.flusher
+ local askedfig = specification.askedfig
+ local incontext = specification.incontext
+ local figures = result.fig
+ if figures then
+ flusher = flusher or pdfflusher
+ local resetplugins = metapost.resetplugins or ignore -- before figure
+ local processplugins = metapost.processplugins or ignore -- each object
+ local synchronizeplugins = metapost.synchronizeplugins or ignore
+ local pluginactions = metapost.pluginactions or ignore -- before / after
+ local startfigure = flusher.startfigure
+ local stopfigure = flusher.stopfigure
+ local flushfigure = flusher.flushfigure
+ local textfigure = flusher.textfigure
+ -- local processspecial = flusher.processspecial or metapost.processspecial
+ local tocomment = flusher.tocomment
+ for index=1,#figures do
+ local figure = figures[index]
+ local properties = pushproperties(figure)
+ if askedfig == "direct" or askedfig == "all" or askedfig == properties.number then
+ local objects = getobjects(result,figure,index)
+ local result = { }
+ local miterlimit = -1
+ local linecap = -1
+ local linejoin = -1
+ local dashed = false
+ local linewidth = false
+ local llx = properties.llx
+ local lly = properties.lly
+ local urx = properties.urx
+ local ury = properties.ury
+ if urx < llx then
+ -- invalid
+ startfigure(properties.number,0,0,0,0,"invalid",figure)
+ if tocomment then
+ result[#result+1] = tocomment("invalid")
+ end
+ stopfigure()
+ else
+
+ -- we need to be indirect if we want the one-pass solution
+
+ local groupstack = { }
+
+ local function processfigure()
+ if tocomment then
+ result[#result+1] = tocomment("begin")
+ end
+ result[#result+1] = "q"
+ if objects then
+ -- resetplugins(result) -- we should move the colorinitializer here
+ local savedpath = nil
+ local savedhtap = nil
+ for o=1,#objects do
+ local object = objects[o]
+ local objecttype = object.type
+ if objecttype == "fill" or objecttype == "outline" then
+ -- we use an indirect table as we want to overload
+ -- entries but this is not possible in userdata
+ --
+ -- can be optimized if no path
+ --
+ local original = object
+ local object = { }
+ setmetatable(object, {
+ __index = original
+ })
+ local before,
+ after = processplugins(object)
+ local evenodd = false
+ local collect = false
+ local both = false
+ local flush = false
+ local postscript = object.postscript
+ if not object.istext then
+ if postscript == "evenodd" then
+ evenodd = true
+ elseif postscript == "collect" then
+ collect = true
+ elseif postscript == "flush" then
+ flush = true
+ elseif postscript == "both" then
+ both = true
+ elseif postscript == "eoboth" then
+ evenodd = true
+ both = true
+ end
+ end
+ --
+ if flush and not savedpath then
+ -- forget about it
+ elseif collect then
+ if not savedpath then
+ savedpath = { object.path or false }
+ savedhtap = { object.htap or false }
+ else
+ savedpath[#savedpath+1] = object.path or false
+ savedhtap[#savedhtap+1] = object.htap or false
+ end
+ else
+ local objecttype = object.type -- can have changed
+ if before then
+ result = pluginactions(before,result,flushfigure)
+ end
+ local ml = object.miterlimit
+ if ml and ml ~= miterlimit then
+ miterlimit = ml
+ result[#result+1] = f_M(ml)
+ end
+ local lj = object.linejoin
+ if lj and lj ~= linejoin then
+ linejoin = lj
+ result[#result+1] = f_j(lj)
+ end
+ local lc = object.linecap
+ if lc and lc ~= linecap then
+ linecap = lc
+ result[#result+1] = f_J(lc)
+ end
+ if both then
+ if dashed ~= false then -- was just dashed test
+ result[#result+1] = "[] 0 d"
+ dashed = false
+ end
+ else
+ local dl = object.dash
+ if dl then
+ local d = f_d(concat(dl.dashes or {}," "),dl.offset)
+ if d ~= dashed then
+ dashed = d
+ result[#result+1] = d
+ end
+ elseif dashed ~= false then -- was just dashed test
+ result[#result+1] = "[] 0 d"
+ dashed = false
+ end
+ end
+ local path = object.path -- newpath
+ local transformed = false
+ local penwidth = 1
+ local open = path and path[1].left_type and path[#path].right_type -- at this moment only "end_point"
+ local pen = object.pen
+ if pen then
+ if pen.type == "elliptical" then
+ transformed, penwidth = pen_characteristics(original) -- boolean, value
+ if penwidth ~= linewidth then
+ result[#result+1] = f_w(penwidth)
+ linewidth = penwidth
+ end
+ if objecttype == "fill" then
+ objecttype = "both"
+ end
+ else -- calculated by mplib itself
+ objecttype = "fill"
+ end
+ end
+ if transformed then
+ result[#result+1] = "q"
+ end
+ if path then
+ if savedpath then
+ for i=1,#savedpath do
+ local path = savedpath[i]
+ if transformed then
+ flushconcatpath(path,result,open)
+ else
+ flushnormalpath(path,result,open)
+ end
+ end
+ savedpath = nil
+ end
+ if flush then
+ -- ignore this path
+ elseif transformed then
+ flushconcatpath(path,result,open)
+ else
+ flushnormalpath(path,result,open)
+ end
+ if force_stroke then
+ result[#result+1] = open and "S" or "h S"
+ elseif objecttype == "fill" then
+ result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo
+ elseif objecttype == "outline" then
+ if both then
+ result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo
+ else
+ result[#result+1] = open and "S" or "h S"
+ end
+ elseif objecttype == "both" then
+ result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo -- b includes closepath
+ end
+ end
+ if transformed then
+ result[#result+1] = "Q"
+ end
+ local path = object.htap
+ if path then
+ if transformed then
+ result[#result+1] = "q"
+ end
+ if savedhtap then
+ for i=1,#savedhtap do
+ local path = savedhtap[i]
+ if transformed then
+ flushconcatpath(path,result,open)
+ else
+ flushnormalpath(path,result,open)
+ end
+ end
+ savedhtap = nil
+ evenodd = true
+ end
+ if transformed then
+ flushconcatpath(path,result,open)
+ else
+ flushnormalpath(path,result,open)
+ end
+ if force_stroke then
+ result[#result+1] = open and "S" or "h S"
+ elseif objecttype == "fill" then
+ result[#result+1] = evenodd and "h f*" or "h f" -- f* = eo
+ elseif objecttype == "outline" then
+ result[#result+1] = open and "S" or "h S"
+ elseif objecttype == "both" then
+ result[#result+1] = evenodd and "h B*" or "h B" -- B* = eo -- b includes closepath
+ end
+ if transformed then
+ result[#result+1] = "Q"
+ end
+ end
+ if after then
+ result = pluginactions(after,result,flushfigure)
+ end
+ end
+ if object.grouped then
+ -- can be qQ'd so changes can end up in groups
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ end
+ elseif objecttype == "start_clip" then
+ local evenodd = not object.istext and object.postscript == "evenodd"
+ result[#result+1] = "q"
+ flushnormalpath(object.path,result,false)
+ result[#result+1] = evenodd and "W* n" or "W n"
+ elseif objecttype == "stop_clip" then
+ result[#result+1] = "Q"
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then
+ -- skip
+ elseif objecttype == "start_group" then
+ if lpdf.flushgroup then
+ local before, after = processplugins(object)
+ if before then
+ result[#result+1] = "q"
+ result = pluginactions(before,result,flushfigure)
+ insert(groupstack, {
+ after = after,
+ result = result,
+ bbox = toboundingbox(object.path),
+ })
+ result = { }
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ else
+ insert(groupstack,false)
+ end
+ else
+ insert(groupstack,false)
+ end
+ elseif objecttype == "stop_group" then
+ local data = remove(groupstack)
+ if data then
+ local reference = lpdf.flushgroup(concat(result,"\r"),data.bbox)
+ result = data.result
+ result[#result+1] = reference
+ result = pluginactions(data.after,result,flushfigure)
+ result[#result+1] = "Q"
+ miterlimit, linecap, linejoin, dashed, linewidth = -1, -1, -1, "", false
+ end
+ -- if objecttype == "text" then
+ -- result[#result+1] = "q"
+ -- local ot = object.transform -- 3,4,5,6,1,2
+ -- result[#result+1] = f_cm(ot[3],ot[4],ot[5],ot[6],ot[1],ot[2])
+ -- flushfigure(result) -- flush accumulated literals
+ -- result = { }
+ -- textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
+ -- result[#result+1] = "Q"
+ -- elseif objecttype == "special" then
+ -- if processspecial then
+ -- processspecial(object.prescript)
+ -- end
+ -- else
+ else
+ -- error
+ end
+ end
+ end
+ result[#result+1] = "Q"
+ if tocomment then
+ result[#result+1] = tocomment("end")
+ end
+ flushfigure(result)
+ end
+ startfigure(properties.number,llx,lly,urx,ury,"begin",figure)
+ if incontext then
+ context(function() processfigure() end)
+ else
+ processfigure()
+ end
+ stopfigure("end")
+ end
+ if askedfig ~= "all" then
+ break
+ end
+ end
+ popproperties()
+ end
+ resetplugins(result) -- we should move the colorinitializer here
+ end
+ end
+end
+
+-- tracing:
+
+do
+
+ local result = { }
+
+ local flusher = {
+ startfigure = function()
+ result = { }
+ context.startnointerference()
+ end,
+ flushfigure = function(literals)
+ local n = #result
+ for i=1,#literals do
+ result[n+i] = literals[i]
+ end
+ end,
+ stopfigure = function()
+ context.stopnointerference()
+ end
+ }
+
+ local specification = {
+ flusher = flusher,
+ -- incontext = true,
+ }
+
+ function metapost.pdfliterals(result)
+ metapost.flush(specification,result)
+ return result
+ end
+
+end
+
+function metapost.totable(result,askedfig)
+ local askedfig = askedfig or 1
+ local figure = result and result.fig and result.fig[1]
+ if figure then
+ local results = { }
+ local objects = getobjects(result,figure,askedfig)
+ for o=1,#objects do
+ local object = objects[o]
+ local result = { }
+ local fields = getfields(object) -- hm, is this the whole list, if so, we can get it once
+ for f=1,#fields do
+ local field = fields[f]
+ result[field] = object[field]
+ end
+ results[o] = result
+ end
+ local boundingbox = figure:boundingbox()
+ return {
+ boundingbox = {
+ llx = boundingbox[1],
+ lly = boundingbox[2],
+ urx = boundingbox[3],
+ ury = boundingbox[4],
+ },
+ objects = results
+ }
+ else
+ return nil
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.mkxl
new file mode 100644
index 00000000000..6ded235145c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-pdf.mkxl
@@ -0,0 +1,157 @@
+%D \module
+%D [ file=mlib-pdf,
+%D version=2008.03.25,
+%D title=\METAPOST\ Integrated Graphics,
+%D subtitle=Conversion to PDF,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D We use bit more code that needed because we want to limit the amount of boxing.
+
+\registerctxluafile{mlib-pdf}{autosuffix}
+
+%D Some code is shared between MPLIB and MPS. The following variables are also
+%D available for introspection and other purposes.
+
+\ifdefined\MPwidth \else \newdimen\MPwidth \fi
+\ifdefined\MPheight \else \newdimen\MPheight \fi
+
+\ifdefined\MPllx \else \newdimen\MPllx \fi
+\ifdefined\MPlly \else \newdimen\MPlly \fi
+\ifdefined\MPurx \else \newdimen\MPurx \fi
+\ifdefined\MPury \else \newdimen\MPury \fi
+
+\ifdefined\MPbox \else \newbox \MPbox \fi
+
+\permanent\protected\def\setMPboundingbox#1#2#3#4% at some point we might pass them as base or scaled points
+ {\global\MPllx #1\onebasepoint
+ \global\MPlly #2\onebasepoint
+ \global\MPurx #3\onebasepoint
+ \global\MPury #4\onebasepoint
+ \global\MPwidth \dimexpr\MPurx-\MPllx\relax
+ \global\MPheight\dimexpr\MPury-\MPlly\relax}
+
+\permanent\protected\def\resetMPboundingbox
+ {\global\MPwidth \zeropoint
+ \global\MPheight\zeropoint
+ \global\MPllx \zeropoint
+ \global\MPlly \zeropoint
+ \global\MPurx \zeropoint
+ \global\MPury \zeropoint}
+
+\aliased\let\popMPboundingbox\relax
+
+\permanent\protected\def\pushMPboundingbox
+ {\enforced\permanent\protected\edef\popMPboundingbox
+ {\global\MPwidth \the\MPwidth
+ \global\MPheight\the\MPheight
+ \global\MPllx \the\MPllx
+ \global\MPlly \the\MPlly
+ \global\MPurx \the\MPurx
+ \global\MPury \the\MPury
+ \relax}}
+
+% we need to combine these
+
+\permanent\protected\def\repositionMPboxindeed
+ {\setbox\MPbox\hpack\bgroup
+ \kern-\MPllx
+ \raise-\MPlly
+ \box\MPbox
+ \egroup}
+
+\permanent\protected\def\repositionMPbox
+ {\ifzeropt\MPllx
+ \ifzeropt\MPlly
+ % okay
+ \else
+ \repositionMPboxindeed
+ \fi
+ \else
+ \repositionMPboxindeed
+ \fi}
+
+% combined
+
+\permanent\protected\def\finalizeMPbox
+ {\boxxoffset\MPbox-\MPllx\relax
+ \boxyoffset\MPbox\dimexpr\boxyoffset\MPbox-\MPheight-\MPlly\relax
+ \wd\MPbox\MPwidth
+ \ht\MPbox\MPheight}
+
+% MPLIB specific:
+
+\permanent\protected\def\startMPLIBtoPDF#1#2#3#4%
+ {\meta_process_graphic_figure_start
+ \dostarttagged\t!mpgraphic\empty
+ \naturalhpack attr \imageattribute \plusone \bgroup
+ \dousecolorparameter\s!black\forcecolorhack
+ \setMPboundingbox{#1}{#2}{#3}{#4}%
+ \setbox\MPbox\vpack\bgroup
+ % \forgetall % already done elsewhere
+ \noindent} % forces the proper cm in the backend
+
+\permanent\protected\def\stopMPLIBtoPDF
+ {\egroup
+ \finalizeMPbox
+ \box\MPbox
+ \egroup
+ \dostoptagged
+ \meta_process_graphic_figure_stop}
+
+%D Kind of special:
+%
+% test.mp:
+%
+% beginfig(1) fill fullcircle scaled 10cm withcolor red ; endfig ;
+% beginfig(2) draw fullcircle scaled 5cm withcolor blue ; endfig ;
+% beginfig(3) draw textext("just some text") ; endfig ;
+%
+% standalone pages:
+%
+% \starttext
+% \directMPgraphic{input "test.mp" ;}
+% \stoptext
+
+\aliased\let\normalstartMPLIBtoPDF\startMPLIBtoPDF
+\aliased\let\normalstopMPLIBtoPDF \stopMPLIBtoPDF
+
+\permanent\protected\def\directstartMPLIBtoPDF{\startTEXpage\normalstartMPLIBtoPDF}
+\permanent\protected\def\directstopMPLIBtoPDF {\normalstopMPLIBtoPDF\stopTEXpage}
+
+% can become \permanent\protected\def\directMPgraphic#=#*#=% but best test first
+
+\permanent\protected\def\directMPgraphic
+ {\dodoublegroupempty\mlib_direct_graphic}
+
+\def\mlib_direct_graphic#1#2% makes pages (todo: make boxes)
+ {\meta_begin_graphic_group{#1}%
+ \enforced\let\startMPLIBtoPDF\directstartMPLIBtoPDF
+ \enforced\let\stopMPLIBtoPDF \directstopMPLIBtoPDF
+ \meta_start_current_graphic
+ \forgetall
+ \edef\p_extensions{\MPinstanceparameter\s!extensions}%
+ \normalexpanded{\noexpand\clf_mpgraphic
+ instance {\currentMPinstance}%
+ format {\currentMPformat}%
+ data {#2;}%
+ initializations {\meta_flush_current_initializations}%
+ \ifx\p_extensions\v!yes
+ extensions {\clf_getmpextensions{\currentMPinstance}}%
+ \fi
+ inclusions {\meta_flush_current_inclusions}%
+ definitions {\meta_flush_current_definitions}%
+ figure {all}%
+ method {\MPinstanceparameter\c!method}%
+ \relax}%
+ \meta_stop_current_graphic
+ \meta_end_graphic_group}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-pps.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-pps.lmt
new file mode 100644
index 00000000000..32f23c39d27
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-pps.lmt
@@ -0,0 +1,1647 @@
+if not modules then modules = { } end modules ['mlib-pps'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local format, gmatch, match, split, gsub = string.format, string.gmatch, string.match, string.split, string.gsub
+local tonumber, type, unpack, next, select = tonumber, type, unpack, next, select
+local round, sqrt, min, max = math.round, math.sqrt, math.min, math.max
+local insert, remove, concat = table.insert, table.remove, table.concat
+local Cs, Cf, C, Cg, Ct, P, S, V, Carg = lpeg.Cs, lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.V, lpeg.Carg
+local lpegmatch, tsplitat, tsplitter = lpeg.match, lpeg.tsplitat, lpeg.tsplitter
+local formatters = string.formatters
+local exists, savedata = io.exists, io.savedata
+
+local mplib = mplib
+local metapost = metapost
+local lpdf = lpdf
+local context = context
+
+local scan = mp.scan
+local inject = mp.inject
+
+local mpscanstring = scan.string
+local mpscannumeric = scan.numeric
+
+local injecttriplet = inject.triplet
+
+local registerscript = metapost.registerscript
+
+local implement = interfaces.implement
+local setmacro = interfaces.setmacro
+
+local texsetbox = tex.setbox
+local textakebox = tex.takebox -- or: nodes.takebox
+local texrunlocal = tex.runlocal
+local copy_list = node.copy_list
+local flush_list = node.flush_list
+local setmetatableindex = table.setmetatableindex
+local sortedhash = table.sortedhash
+
+local new_hlist = nodes.pool.hlist
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local trace_runs = false trackers.register("metapost.runs", function(v) trace_runs = v end)
+local trace_textexts = false trackers.register("metapost.textexts", function(v) trace_textexts = v end)
+local trace_scripts = false trackers.register("metapost.scripts", function(v) trace_scripts = v end)
+local trace_btexetex = false trackers.register("metapost.btexetex", function(v) trace_btexetex = v end)
+
+local report_metapost = logs.reporter("metapost")
+local report_textexts = logs.reporter("metapost","textexts")
+local report_scripts = logs.reporter("metapost","scripts")
+
+local colors = attributes.colors
+local defineprocesscolor = colors.defineprocesscolor
+local definespotcolor = colors.definespotcolor
+local definemultitonecolor = colors.definemultitonecolor
+local colorvalue = colors.value
+
+local transparencies = attributes.transparencies
+local registertransparency = transparencies.register
+local transparencyvalue = transparencies.value
+
+local rgbtocmyk = colors.rgbtocmyk -- or function() return 0,0,0,1 end
+local cmyktorgb = colors.cmyktorgb -- or function() return 0,0,0 end
+local rgbtogray = colors.rgbtogray -- or function() return 0 end
+local cmyktogray = colors.cmyktogray -- or function() return 0 end
+
+local nooutercolor = "0 g 0 G"
+local nooutertransparency = "/Tr0 gs" -- only when set
+local outercolormode = 0
+local outercolormodel = 1
+local outercolor = nooutercolor
+local outertransparency = nooutertransparency
+local innercolor = nooutercolor
+local innertransparency = nooutertransparency
+
+local pdfcolor = lpdf.color
+local pdftransparency = lpdf.transparency
+
+function metapost.setoutercolor(mode,colormodel,colorattribute,transparencyattribute)
+ -- has always to be called before conversion
+ -- todo: transparency (not in the mood now)
+ outercolormode = mode
+ outercolormodel = colormodel
+ if mode == 1 or mode == 3 then
+ -- inherit from outer (registered color)
+ outercolor = pdfcolor(colormodel,colorattribute) or nooutercolor
+ outertransparency = pdftransparency(transparencyattribute) or nooutertransparency
+ elseif mode == 2 then
+ -- stand alone (see m-punk.tex)
+ outercolor = ""
+ outertransparency = ""
+ else -- 0
+ outercolor = nooutercolor
+ outertransparency = nooutertransparency
+ end
+ innercolor = outercolor
+ innertransparency = outertransparency -- not yet used
+end
+
+-- todo: get this from the lpdf module
+
+local f_f = formatters["%.6N"]
+local f_f3 = formatters["%.3N"]
+local f_gray = formatters["%.3N g %.3N G"]
+local f_rgb = formatters["%.3N %.3N %.3N rg %.3N %.3N %.3N RG"]
+local f_cmyk = formatters["%.3N %.3N %.3N %.3N k %.3N %.3N %.3N %.3N K"]
+local f_cm_b = formatters["q %.6N %.6N %.6N %.6N %.6N %.6N cm"]
+local f_scn = formatters["%.3N"]
+
+local f_shade = formatters["MpSh%s"]
+local f_spot = formatters["/%s cs /%s CS %s SCN %s scn"]
+local s_cm_e = "Q"
+
+local function checked_color_pair(color,...)
+ if not color then
+ return innercolor, outercolor
+ end
+ if outercolormode == 3 then
+ innercolor = color(...)
+ return innercolor, innercolor
+ else
+ return color(...), outercolor
+ end
+end
+
+function metapost.colorinitializer()
+ innercolor = outercolor
+ innertransparency = outertransparency
+ return outercolor, outertransparency
+end
+
+--~
+
+local specificationsplitter = tsplitat(" ")
+local colorsplitter = tsplitter(":",tonumber) -- no need for :
+local domainsplitter = tsplitter(" ",tonumber)
+local centersplitter = domainsplitter
+local coordinatesplitter = domainsplitter
+
+-- thanks to taco's reading of the postscript manual:
+--
+-- x' = sx * x + ry * y + tx
+-- y' = rx * x + sy * y + ty
+
+local nofshades = 0 -- todo: hash resources, start at 1000 in order not to clash with older
+
+local function normalize(ca,cb)
+ if #cb == 1 then
+ if #ca == 4 then
+ cb[1], cb[2], cb[3], cb[4] = 0, 0, 0, 1-cb[1]
+ else
+ cb[1], cb[2], cb[3] = cb[1], cb[1], cb[1]
+ end
+ elseif #cb == 3 then
+ if #ca == 4 then
+ cb[1], cb[2], cb[3], cb[4] = rgbtocmyk(cb[1],cb[2],cb[3])
+ else
+ cb[1], cb[2], cb[3] = cmyktorgb(cb[1],cb[2],cb[3],cb[4])
+ end
+ end
+end
+
+
+local commasplitter = tsplitat(",")
+
+local function checkandconvertspot(n_a,f_a,c_a,v_a,n_b,f_b,c_b,v_b)
+ -- must be the same but we don't check
+ local name = f_shade(nofshades)
+ local ca = lpegmatch(commasplitter,v_a)
+ local cb = lpegmatch(commasplitter,v_b)
+ if #ca == 0 or #cb == 0 then
+ return { 0 }, { 1 }, "DeviceGray", name
+ else
+ for i=1,#ca do ca[i] = tonumber(ca[i]) or 0 end
+ for i=1,#cb do cb[i] = tonumber(cb[i]) or 1 end
+ --~ spotcolorconverter(n_a,f_a,c_a,v_a) -- not really needed
+ return ca, cb, n_a or n_b, name
+ end
+end
+
+local function checkandconvert(ca,cb,model)
+ local name = f_shade(nofshades)
+ if not ca or not cb or type(ca) == "string" then
+ return { 0 }, { 1 }, "DeviceGray", name
+ else
+ if #ca > #cb then
+ normalize(ca,cb)
+ elseif #ca < #cb then
+ normalize(cb,ca)
+ end
+ if not model then
+ model = colors.currentnamedmodel()
+ end
+ if model == "all" then
+ model= (#ca == 4 and "cmyk") or (#ca == 3 and "rgb") or "gray"
+ end
+ if model == "rgb" then
+ if #ca == 4 then
+ ca = { cmyktorgb(ca[1],ca[2],ca[3],ca[4]) }
+ cb = { cmyktorgb(cb[1],cb[2],cb[3],cb[4]) }
+ elseif #ca == 1 then
+ local a = 1 - ca[1]
+ local b = 1 - cb[1]
+ ca = { a, a, a }
+ cb = { b, b, b }
+ end
+ return ca, cb, "DeviceRGB", name, model
+ elseif model == "cmyk" then
+ if #ca == 3 then
+ ca = { rgbtocmyk(ca[1],ca[2],ca[3]) }
+ cb = { rgbtocmyk(cb[1],cb[2],cb[3]) }
+ elseif #ca == 1 then
+ ca = { 0, 0, 0, ca[1] }
+ cb = { 0, 0, 0, ca[1] }
+ end
+ return ca, cb, "DeviceCMYK", name, model
+ else
+ if #ca == 4 then
+ ca = { cmyktogray(ca[1],ca[2],ca[3],ca[4]) }
+ cb = { cmyktogray(cb[1],cb[2],cb[3],cb[4]) }
+ elseif #ca == 3 then
+ ca = { rgbtogray(ca[1],ca[2],ca[3]) }
+ cb = { rgbtogray(cb[1],cb[2],cb[3]) }
+ end
+ -- backend specific (will be renamed)
+ return ca, cb, "DeviceGray", name, model
+ end
+ end
+end
+
+-- We keep textexts in a shared list (as it's easier that way and we also had that in
+-- the beginning). Each graphic gets its own (1 based) subtable so that we can also
+-- handle multiple conversions in one go which is needed when we process mp files
+-- directly.
+
+local stack = { } -- quick hack, we will pass topofstack around
+local top = nil
+local nofruns = 0 -- askedfig: "all", "first", number
+
+local function preset(t,k)
+ -- references to textexts by mp index
+ local v = {
+ textrial = 0,
+ texfinal = 0,
+ texslots = { },
+ texorder = { },
+ texhash = { },
+ }
+ t[k] = v
+ return v
+end
+
+local function startjob(plugmode,kind,mpx)
+ insert(stack,top)
+ top = {
+ textexts = { }, -- all boxes, optionally with a different color
+ texstrings = { },
+ texregimes = { },
+ mapstrings = { },
+ mapindices = { },
+ mapmoves = { },
+ texlast = 0,
+ texdata = setmetatableindex({},preset), -- references to textexts in order or usage
+ plugmode = plugmode, -- some day we can then skip all pre/postscripts
+ extradata = mpx and metapost.getextradata(mpx),
+ }
+ if trace_runs then
+ report_metapost("starting %s run at level %i in %s mode",
+ kind,#stack+1,plugmode and "plug" or "normal")
+ end
+ return top
+end
+
+local function stopjob()
+ if top then
+ for slot, content in next, top.textexts do
+ if content then
+ flush_list(content)
+ if trace_textexts then
+ report_textexts("freeing text %s",slot)
+ end
+ end
+ end
+ if trace_runs then
+ report_metapost("stopping run at level %i",#stack+1)
+ end
+ top = remove(stack)
+ return top
+ end
+end
+
+function metapost.getjobdata()
+ return top
+end
+
+-- end of new
+
+local settext = function(box,slot,str)
+ if top then
+ -- if trace_textexts then
+ -- report_textexts("getting text %s from box %s",slot,box)
+ -- end
+ top.textexts[slot] = textakebox(box)
+ end
+end
+
+local gettext = function(box,slot)
+ if top then
+ texsetbox(box,top.textexts[slot])
+ top.textexts[slot] = false
+ -- if trace_textexts then
+ -- report_textexts("putting text %s in box %s",slot,box)
+ -- end
+ end
+end
+
+metapost.settext = settext
+metapost.gettext = gettext
+
+implement { name = "mpsettext", actions = settext, arguments = { "integer", "integer" } } -- box slot
+implement { name = "mpgettext", actions = gettext, arguments = { "integer", "integer" } } -- box slot
+
+-- rather generic pdf, so use this elsewhere too it no longer pays
+-- off to distinguish between outline and fill (we now have both
+-- too, e.g. in arrows)
+
+metapost.reducetogray = true
+
+local models = { }
+
+function models.all(cr)
+ local n = #cr
+ if n == 0 then
+ return checked_color_pair()
+ elseif metapost.reducetogray then
+ if n == 1 then
+ local s = cr[1]
+ return checked_color_pair(f_gray,s,s)
+ elseif n == 3 then
+ local r = cr[1]
+ local g = cr[2]
+ local b = cr[3]
+ if r == g and g == b then
+ return checked_color_pair(f_gray,r,r)
+ else
+ return checked_color_pair(f_rgb,r,g,b,r,g,b)
+ end
+ else
+ local c = cr[1]
+ local m = cr[2]
+ local y = cr[3]
+ local k = cr[4]
+ if c == m and m == y and y == 0 then
+ k = 1 - k
+ return checked_color_pair(f_gray,k,k)
+ else
+ return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ end
+ end
+ elseif n == 1 then
+ local s = cr[1]
+ return checked_color_pair(f_gray,s,s)
+ elseif n == 3 then
+ local r = cr[1]
+ local g = cr[2]
+ local b = cr[3]
+ return checked_color_pair(f_rgb,r,g,b,r,g,b)
+ else
+ local c = cr[1]
+ local m = cr[2]
+ local y = cr[3]
+ local k = cr[4]
+ return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ end
+end
+
+function models.rgb(cr)
+ local n = #cr
+ if n == 0 then
+ return checked_color_pair()
+ elseif metapost.reducetogray then
+ if n == 1 then
+ local s = cr[1]
+ checked_color_pair(f_gray,s,s)
+ elseif n == 3 then
+ local r = cr[1]
+ local g = cr[2]
+ local b = cr[3]
+ if r == g and g == b then
+ return checked_color_pair(f_gray,r,r)
+ else
+ return checked_color_pair(f_rgb,r,g,b,r,g,b)
+ end
+ else
+ local c = cr[1]
+ local m = cr[2]
+ local y = cr[3]
+ local k = cr[4]
+ if c == m and m == y and y == 0 then
+ k = 1 - k
+ return checked_color_pair(f_gray,k,k)
+ else
+ local r, g, b = cmyktorgb(c,m,y,k)
+ return checked_color_pair(f_rgb,r,g,b,r,g,b)
+ end
+ end
+ elseif n == 1 then
+ local s = cr[1]
+ return checked_color_pair(f_gray,s,s)
+ else
+ local r = cr[1]
+ local g = cr[2]
+ local b = cr[3]
+ local r, g, b
+ if n == 3 then
+ r, g, b = cmyktorgb(r,g,b,cr[4])
+ end
+ return checked_color_pair(f_rgb,r,g,b,r,g,b)
+ end
+end
+
+function models.cmyk(cr)
+ local n = #cr
+ if n == 0 then
+ return checked_color_pair()
+ elseif metapost.reducetogray then
+ if n == 1 then
+ local s = cr[1]
+ return checked_color_pair(f_gray,s,s)
+ elseif n == 3 then
+ local r = cr[1]
+ local g = cr[2]
+ local b = cr[3]
+ if r == g and g == b then
+ return checked_color_pair(f_gray,r,r)
+ else
+ local c, m, y, k = rgbtocmyk(r,g,b)
+ return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ end
+ else
+ local c = cr[1]
+ local m = cr[2]
+ local y = cr[3]
+ local k = cr[4]
+ if c == m and m == y and y == 0 then
+ k = 1 - k
+ return checked_color_pair(f_gray,k,k)
+ else
+ return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ end
+ end
+ elseif n == 1 then
+ local s = cr[1]
+ return checked_color_pair(f_gray,s,s)
+ else
+ local c = cr[1]
+ local m = cr[2]
+ local y = cr[3]
+ local k = cr[4]
+ if n == 3 then
+ if c == m and m == y then
+ k, c, m, y = 1 - c, 0, 0, 0
+ else
+ c, m, y, k = rgbtocmyk(c,m,y)
+ end
+ end
+ return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ end
+end
+
+function models.gray(cr)
+ local n = #cr
+ local s = 0
+ if n == 0 then
+ return checked_color_pair()
+ elseif n == 4 then
+ s = cmyktogray(cr[1],cr[2],cr[3],cr[4])
+ elseif n == 3 then
+ s = rgbtogray(cr[1],cr[2],cr[3])
+ else
+ s = cr[1]
+ end
+ return checked_color_pair(f_gray,s,s)
+end
+
+models[1] = models.all
+models[2] = models.gray
+models[3] = models.rgb
+models[4] = models.cmyk
+
+setmetatableindex(models, function(t,k)
+ local v = models.gray
+ t[k] = v
+ return v
+end)
+
+local function colorconverter(cs)
+ return models[outercolormodel](cs)
+end
+
+local factor = 65536*(7227/7200)
+
+implement {
+ name = "mpsetsxsy",
+ arguments = { "dimen", "dimen", "dimen" },
+ actions = function(wd,ht,dp)
+ local hd = ht + dp
+ setmacro("mlib_sx",wd ~= 0 and factor/wd or 0)
+ setmacro("mlib_sy",hd ~= 0 and factor/hd or 0)
+ end
+}
+
+local function sxsy(wd,ht,dp) -- helper for text
+ local hd = ht + dp
+ return (wd ~= 0 and factor/wd) or 0, (hd ~= 0 and factor/hd) or 0
+end
+
+metapost.sxsy = sxsy
+
+-- for stock mp we need to declare the booleans first
+
+local do_begin_fig = "; beginfig(1) ; "
+local do_end_fig = "; endfig ;"
+local do_safeguard = ";"
+
+function metapost.preparetextextsdata()
+ local textexts = top.textexts
+ local collected = { }
+ for k, data in sortedhash(top.texdata) do -- sort is nicer in trace
+ local texorder = data.texorder
+ for n=1,#texorder do
+ local box = textexts[texorder[n]]
+ if box then
+ collected[n] = box
+ else
+ break
+ end
+ end
+ end
+ mp.mf_tt_initialize(collected)
+end
+
+local runmetapost = metapost.run
+
+local function checkaskedfig(askedfig) -- return askedfig, wrappit
+ if not askedfig then
+ return "direct", true
+ elseif askedfig == "all" then
+ return "all", false
+ elseif askedfig == "direct" then
+ return "all", true
+ else
+ askedfig = tonumber(askedfig)
+ if askedfig then
+ return askedfig, false
+ else
+ return "direct", true
+ end
+ end
+end
+
+-- This one is called from the \TEX\ end so the specification is different
+-- from the specification to metapost,run cum suis! The definitions and
+-- extension used to be handled here but are now delegated to the format
+-- initializers because we need to accumulate them for nested instances (a
+-- side effect of going single pass).
+
+function metapost.graphic_base_pass(specification)
+ local mpx = specification.mpx -- mandate
+ local top = startjob(true,"base",mpx)
+ local data = specification.data or ""
+ local inclusions = specification.inclusions or ""
+ local initializations = specification.initializations or ""
+ local askedfig,
+ wrappit = checkaskedfig(specification.figure)
+ nofruns = nofruns + 1
+ top.askedfig = askedfig
+ top.wrappit = wrappit
+ top.nofruns = nofruns
+ metapost.namespace = specification.namespace or ""
+ top.mpx = mpx
+ top.data = data
+ top.initializations = initializations
+ if trace_runs then
+ report_metapost("running job %s, asked figure %a",nofruns,askedfig)
+ end
+ runmetapost {
+ mpx = mpx,
+ askedfig = askedfig,
+ incontext = true,
+ data = {
+ inclusions,
+ wrappit and do_begin_fig or "",
+ initializations,
+ do_safeguard,
+ data,
+ wrappit and do_end_fig or "",
+ },
+ }
+ context(stopjob)
+end
+
+local function oldschool(mpx, data, trial_run, flusher, was_multi_pass, is_extra_pass, askedfig, incontext)
+ metapost.process {
+ mpx = mpx,
+ flusher = flusher,
+ askedfig = askedfig,
+ useplugins = incontext,
+ incontext = incontext,
+ data = data,
+ }
+end
+
+function metapost.process(specification,...)
+ if type(specification) ~= "table" then
+ oldschool(specification,...)
+ else
+ startjob(specification.incontext or specification.useplugins,"process",false)
+ runmetapost(specification)
+ stopjob()
+ end
+end
+
+-- -- the new plugin handler -- --
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+local resetteractions = sequencers.new { arguments = "t" }
+local processoractions = sequencers.new { arguments = "object,prescript,before,after" }
+
+appendgroup(resetteractions, "system")
+appendgroup(processoractions,"system")
+
+-- later entries come first
+
+local scriptsplitter = Ct ( Ct (
+ C((1-S("= "))^1) * S("= ")^1 * C((1-S("\n\r"))^0) * S("\n\r")^0
+)^0 )
+
+local function splitprescript(script)
+ local hash = lpegmatch(scriptsplitter,script)
+ for i=#hash,1,-1 do
+ local h = hash[i]
+ if h == "reset" then
+ for k, v in next, hash do
+ if type(k) ~= "number" then
+ hash[k] = nil
+ end
+ end
+ else
+ hash[h[1]] = h[2]
+ end
+ end
+ if trace_scripts then
+ report_scripts(table.serialize(hash,"prescript"))
+ end
+ return hash
+end
+
+metapost.splitprescript = splitprescript
+
+-- -- not used:
+--
+-- local function splitpostscript(script)
+-- local hash = lpegmatch(scriptsplitter,script)
+-- for i=1,#hash do
+-- local h = hash[i]
+-- hash[h[1]] = h[2]
+-- end
+-- if trace_scripts then
+-- report_scripts(table.serialize(hash,"postscript"))
+-- end
+-- return hash
+-- end
+
+function metapost.pluginactions(what,t,flushfigure) -- before/after object, depending on what
+ if top and top.plugmode then -- hm, what about other features
+ for i=1,#what do
+ local wi = what[i]
+ if type(wi) == "function" then
+ -- assume injection
+ flushfigure(t) -- to be checked: too many 0 g 0 G
+ t = { }
+ wi()
+ else
+ t[#t+1] = wi
+ end
+ end
+ return t
+ end
+end
+
+function metapost.resetplugins(t) -- intialize plugins, before figure
+ if top and top.plugmode then
+ outercolormodel = colors.currentmodel() -- currently overloads the one set at the tex end
+ resetteractions.runner(t)
+ end
+end
+
+function metapost.processplugins(object) -- each object (second pass)
+ if top and top.plugmode then
+ local prescript = object.prescript -- specifications
+ if prescript and #prescript > 0 then
+ local before = { }
+ local after = { }
+ processoractions.runner(object,splitprescript(prescript) or { },before,after)
+ return #before > 0 and before, #after > 0 and after
+ else
+ local c = object.color
+ if c and #c > 0 then
+ local b, a = colorconverter(c)
+ return { b }, { a }
+ end
+ end
+ end
+end
+
+-- helpers
+
+local basepoints = number.dimenfactors["bp"]
+
+local function cm(object)
+ local op = object.path
+ if op then
+ local first = op[1]
+ local second = op[2]
+ local fourth = op[4]
+ if fourth then
+ local tx = first.x_coord
+ local ty = first.y_coord
+ local sx = second.x_coord - tx
+ local sy = fourth.y_coord - ty
+ local rx = second.y_coord - ty
+ local ry = fourth.x_coord - tx
+ if sx == 0 then sx = 0.00001 end
+ if sy == 0 then sy = 0.00001 end
+ return sx, rx, ry, sy, tx, ty
+ end
+ end
+ return 1, 0, 0, 1, 0, 0 -- weird case
+end
+
+metapost.cm = cm
+
+-- color
+
+local function cl_reset(t)
+ t[#t+1] = metapost.colorinitializer() -- only color
+end
+
+-- text
+
+local tx_reset, tx_process do
+
+ local eol = S("\n\r")^1
+ local cleaner = Cs((P("@@")/"@" + P("@")/"%%" + P(1))^0)
+ local splitter = Ct(
+ ( (
+ P("s:") * C((1-eol)^1)
+ + P("n:") * ((1-eol)^1/tonumber)
+ + P("b:") * ((1-eol)^1/toboolean)
+ ) * eol^0 )^0)
+
+ local function applyformat(s)
+ local t = lpegmatch(splitter,s)
+ if #t == 1 then
+ return s
+ else
+ local f = lpegmatch(cleaner,t[1])
+ return formatters[f](unpack(t,2))
+ end
+ end
+
+ local fmt = formatters["%s %s %s % t"]
+ ----- pat = tsplitat(":")
+ local pat = lpeg.tsplitter(":",tonumber) -- so that %F can do its work
+
+ local f_gray_yes = formatters["s=%.3N,a=%i,t=%.3N"]
+ local f_gray_nop = formatters["s=%.3N"]
+ local f_rgb_yes = formatters["r=%.3N,g=%.3N,b=%.3N,a=%.3N,t=%.3N"]
+ local f_rgb_nop = formatters["r=%.3N,g=%.3N,b=%.3N"]
+ local f_cmyk_yes = formatters["c=%.3N,m=%.3N,y=%.3N,k=%.3N,a=%.3N,t=%.3N"]
+ local f_cmyk_nop = formatters["c=%.3N,m=%.3N,y=%.3N,k=%.3N"]
+
+ local ctx_MPLIBsetNtext = context.MPLIBsetNtextX
+ local ctx_MPLIBsetCtext = context.MPLIBsetCtextX
+ local ctx_MPLIBsettext = context.MPLIBsettextX
+
+ local bp = number.dimenfactors.bp
+
+ local mp_index = 0
+ local mp_target = 0
+ local mp_c = nil
+ local mp_a = nil
+ local mp_t = nil
+
+ local function processtext()
+ local mp_text = top.texstrings[mp_index]
+ local mp_regime = top.texregimes[mp_index]
+ if mp_regime and tonumber(mp_regime) >= 0 then
+ mp_text = function()
+ context.sprint(mp_regime,top.texstrings[mp_index] or "")
+ end
+ end
+ if not mp_text then
+ report_textexts("missing text for index %a",mp_index)
+ elseif not mp_c then
+ ctx_MPLIBsetNtext(mp_target,mp_text)
+ elseif #mp_c == 1 then
+ if mp_a and mp_t then
+ ctx_MPLIBsetCtext(mp_target,f_gray_yes(mp_c[1],mp_a,mp_t),mp_text)
+ else
+ ctx_MPLIBsetCtext(mp_target,f_gray_nop(mp_c[1]),mp_text)
+ end
+ elseif #mp_c == 3 then
+ if mp_a and mp_t then
+ ctx_MPLIBsetCtext(mp_target,f_rgb_yes(mp_c[1],mp_c[2],mp_c[3],mp_a,mp_t),mp_text)
+ else
+ ctx_MPLIBsetCtext(mp_target,f_rgb_nop(mp_c[1],mp_c[2],mp_c[3]),mp_text)
+ end
+ elseif #mp_c == 4 then
+ if mp_a and mp_t then
+ ctx_MPLIBsetCtext(mp_target,f_cmyk_yes(mp_c[1],mp_c[2],mp_c[3],mp_c[4],mp_a,mp_t),mp_text)
+ else
+ ctx_MPLIBsetCtext(mp_target,f_cmyk_nop(mp_c[1],mp_c[2],mp_c[3],mp_c[4]),mp_text)
+ end
+ else
+ -- can't happen
+ ctx_MPLIBsetNtext(mp_target,mp_text)
+ end
+ end
+
+ local madetext = nil
+
+ local function mf_some_text(index,str,regime)
+ mp_target = index
+ mp_index = index
+ mp_c = nil
+ mp_a = nil
+ mp_t = nil
+ top.texstrings[mp_index] = str
+ top.texregimes[mp_index] = regime or -1
+ texrunlocal("mptexttoks")
+ local box = textakebox("mptextbox")
+ top.textexts[mp_target] = box
+ injecttriplet(bp*box.width,bp*box.height,bp*box.depth)
+ madetext = nil
+ end
+
+ local function mf_made_text(index)
+ mf_some_text(index,madetext,catcodes.numbers.ctxcatcodes) -- btex/etex ..
+ end
+
+ registerscript("sometextext", function() mf_some_text(mpscannumeric(),mpscanstring(),mpscannumeric()) end)
+ registerscript("madetextext", function() mf_made_text(mpscannumeric()) end)
+
+ -- a label can be anything, also something mp doesn't like in strings
+ -- so we return an index instead
+
+ function metapost.processing()
+ return top and true or false
+ end
+
+ function metapost.remaptext(replacement)
+ if top then
+ local mapstrings = top.mapstrings
+ local mapindices = top.mapindices
+ local label = replacement.label
+ local index = 0
+ if label then
+ local found = mapstrings[label]
+ if found then
+ setmetatableindex(found,replacement)
+ index = found.index
+ else
+ index = #mapindices + 1
+ replacement.index = index
+ mapindices[index] = replacement
+ mapstrings[label] = replacement
+ end
+ end
+ return index
+ else
+ return 0
+ end
+ end
+
+ function metapost.remappedtext(what)
+ return top and (top.mapstrings[what] or top.mapindices[tonumber(what)])
+ end
+
+ function mp.mf_map_move(index)
+ mp.triplet(top.mapmoves[index])
+ end
+
+ function mp.mf_map_text(index,str,regime)
+ local map = top.mapindices[tonumber(str)]
+ if type(map) == "table" then
+ local text = map.text
+ local overload = map.overload
+ local offset = 0
+ local width = 0
+ local where = nil
+ --
+ mp_index = index
+ -- the image text
+ if overload then
+ top.texstrings[mp_index] = map.template or map.label or "error"
+ top.texregimes[mp_index] = regime or -1
+ texrunlocal("mptexttoks")
+ local box = textakebox("mptextbox") or new_hlist()
+ width = bp * box.width
+ where = overload.where
+ end
+ -- the real text
+ top.texstrings[mp_index] = overload and overload.text or text or "error"
+ top.texregimes[mp_index] = regime or -1
+ texrunlocal("mptexttoks")
+ local box = textakebox("mptextbox") or new_hlist()
+ local twd = bp * box.width
+ local tht = bp * box.height
+ local tdp = bp * box.depth
+ -- the check
+ if where then
+ local scale = 1 -- / (map.scale or 1)
+ if where == "l" or where == "left" then
+ offset = scale * (twd - width)
+ elseif where == "m" or where == "middle" then
+ offset = scale * (twd - width) / 2
+ end
+ end
+ -- the result
+ top.textexts[mp_index] = box
+ top.mapmoves[mp_index] = { offset, map.dx or 0, map.dy or 0 }
+ --
+ mp.triplet(twd,tht,tdp)
+ madetext = nil
+ return
+ else
+ map = type(map) == "string" and map or str
+ return mf_some_text(index,context.escape(map) or map)
+ end
+ end
+
+ -- This is a bit messy. In regular metapost it's a kind of immediate replacement
+ -- so embedded btex ... etex is not really working as one would expect. We now have
+ -- a mix: it's immediate when we are at the outer level (rawmadetext) and indirect
+ -- (with the danger of stuff that doesn't work well in strings) when we are for
+ -- instance in a macro definition (rawtextext (pass back string)) ... of course one
+ -- should use textext so this is just a catch. When not in lmtx it's never immediate.
+
+ local reported = false
+ local alwayswrap = false -- CONTEXTLMTXMODE <= 1 -- was always nil due to typo
+
+ function metapost.maketext(s,mode)
+ if not reported then
+ reported = true
+ report_metapost("use 'textext(.....)' instead of 'btex ..... etex'")
+ end
+ if mode and mode == 1 then
+ if trace_btexetex then
+ report_metapost("ignoring verbatimtex: [[%s]]",s)
+ end
+ elseif alwayswrap then
+ if trace_btexetex then
+ report_metapost("rewrapping btex ... etex [[%s]]",s)
+ end
+ return 'rawtextext("' .. gsub(s,'"','"&ditto&"') .. '")' -- nullpicture
+ elseif metapost.currentmpxstatus() ~= 0 then
+ if trace_btexetex then
+ report_metapost("rewrapping btex ... etex at the outer level [[%s]]",s)
+ end
+ return 'rawtextext("' .. gsub(s,'"','"&ditto&"') .. '")' -- nullpicture
+ else
+ if trace_btexetex then
+ report_metapost("handling btex ... etex: [[%s]]",s)
+ end
+ -- madetext = utilities.strings.collapse(s)
+ madetext = s
+ return "rawmadetext" -- is assuming immediate processing
+ end
+ end
+
+ function mp.mf_formatted_text(index,fmt,...)
+ local t = { }
+ for i=1,select("#",...) do
+ local ti = select(i,...)
+ if type(ti) ~= "table" then
+ t[#t+1] = ti
+ end
+ end
+ local f = lpegmatch(cleaner,fmt)
+ local s = formatters[f](unpack(t)) or ""
+ mf_some_text(index,s)
+ end
+
+ interfaces.implement {
+ name = "mptexttoks",
+ actions = processtext,
+ }
+
+ tx_reset = function()
+ if top then
+ top.texhash = { }
+ top.texlast = 0
+ end
+ end
+
+ tx_process = function(object,prescript,before,after)
+ local data = top.texdata[metapost.properties.number] -- the current figure number, messy
+ local index = tonumber(prescript.tx_index)
+ if index then
+ if trace_textexts then
+ report_textexts("using index %a",index)
+ end
+ --
+ mp_c = object.color
+ if #mp_c == 0 then
+ local txc = prescript.tx_color
+ if txc then
+ mp_c = lpegmatch(pat,txc)
+ end
+ end
+ mp_a = tonumber(prescript.tr_alternative)
+ mp_t = tonumber(prescript.tr_transparency)
+ --
+ mp_index = index
+ mp_target = top.texlast - 1
+ top.texlast = mp_target
+ --
+ local mp_text = top.texstrings[mp_index]
+ local mp_hash = prescript.tx_cache
+ local box
+ if mp_hash == "no" then
+ texrunlocal("mptexttoks")
+ box = textakebox("mptextbox")
+ else
+ local cache = data.texhash
+ if mp_hash then
+ mp_hash = tonumber(mp_hash)
+ end
+ if mp_hash then
+ local extradata = top.extradata
+ if extradata then
+ cache = extradata.globalcache
+ if not cache then
+ cache = { }
+ extradata.globalcache = cache
+ end
+ if trace_runs then
+ if cache[mp_hash] then
+ report_textexts("reusing global entry %i",mp_hash)
+ else
+ report_textexts("storing global entry %i",mp_hash)
+ end
+ end
+ else
+ mp_hash = nil
+ end
+ end
+ if not mp_hash then
+ mp_hash = fmt(mp_text,mp_a or "-",mp_t or "-",mp_c or "-")
+ end
+ box = cache[mp_hash]
+ if box then
+ box = copy_list(box)
+ else
+ texrunlocal("mptexttoks")
+ box = textakebox("mptextbox")
+ cache[mp_hash] = box
+ end
+ end
+ top.textexts[mp_target] = box
+ --
+ if box then
+ -- we need to freeze the variables outside the function
+ local sx, rx, ry, sy, tx, ty = cm(object)
+ local target = mp_target
+ before[#before+1] = function()
+ context.MPLIBgettextscaledcm(target,
+ f_f(sx), -- bah ... %s no longer checks
+ f_f(rx), -- bah ... %s no longer checks
+ f_f(ry), -- bah ... %s no longer checks
+ f_f(sy), -- bah ... %s no longer checks
+ f_f(tx), -- bah ... %s no longer checks
+ f_f(ty), -- bah ... %s no longer checks
+ sxsy(box.width,box.height,box.depth))
+ end
+ else
+ before[#before+1] = function()
+ report_textexts("unknown %s",index)
+ end
+ end
+ if not trace_textexts then
+ object.path = false -- else: keep it
+ end
+ object.color = false
+ object.grouped = true
+ object.istext = true
+ end
+ end
+
+end
+
+-- we could probably redo normal textexts in the next way but as it's rather optimized
+-- we keep away from that (at least for now)
+
+local function bx_process(object,prescript,before,after)
+ local bx_category = prescript.bx_category
+ local bx_name = prescript.bx_name
+ if bx_category and bx_name then
+ if trace_textexts then
+ report_textexts("category %a, name %a",bx_category,bx_name)
+ end
+ local sx, rx, ry, sy, tx, ty = cm(object) -- needs to be frozen outside the function
+ local wd, ht, dp = nodes.boxes.dimensions(bx_category,bx_name)
+ before[#before+1] = function()
+ context.MPLIBgetboxscaledcm(bx_category,bx_name,
+ f_f(sx), -- bah ... %s no longer checks
+ f_f(rx), -- bah ... %s no longer checks
+ f_f(ry), -- bah ... %s no longer checks
+ f_f(sy), -- bah ... %s no longer checks
+ f_f(tx), -- bah ... %s no longer checks
+ f_f(ty), -- bah ... %s no longer checks
+ sxsy(wd,ht,dp))
+ end
+ if not trace_textexts then
+ object.path = false -- else: keep it
+ end
+ object.color = false
+ object.grouped = true
+ object.istext = true
+ end
+end
+
+-- graphics (we use the given index because pictures can be reused)
+
+
+local gt_reset, gt_process do
+
+ local graphics = { }
+
+
+ local mp_index = 0
+ local mp_str = ""
+
+ function mp.mf_graphic_text(index,str)
+ if not graphics[index] then
+ mp_index = index
+ mp_str = str
+ texrunlocal("mpgraphictexttoks")
+ end
+ end
+
+ interfaces.implement {
+ name = "mpgraphictexttoks",
+ actions = function()
+ context.MPLIBgraphictext(mp_index,mp_str)
+ end,
+ }
+
+end
+
+-- shades
+
+local function sh_process(object,prescript,before,after)
+ local sh_type = prescript.sh_type
+ if sh_type then
+ nofshades = nofshades + 1
+ local domain = lpegmatch(domainsplitter,prescript.sh_domain or "0 1")
+ local centera = lpegmatch(centersplitter,prescript.sh_center_a or "0 0")
+ local centerb = lpegmatch(centersplitter,prescript.sh_center_b or "0 0")
+ local transform = toboolean(prescript.sh_transform or "yes",true)
+ -- compensation for scaling
+ local sx = 1
+ local sy = 1
+ local sr = 1
+ local dx = 0
+ local dy = 0
+ if transform then
+ local first = lpegmatch(coordinatesplitter,prescript.sh_first or "0 0")
+ local setx = lpegmatch(coordinatesplitter,prescript.sh_set_x or "0 0")
+ local sety = lpegmatch(coordinatesplitter,prescript.sh_set_y or "0 0")
+
+ local x = setx[1] -- point that has different x
+ local y = sety[1] -- point that has different y
+
+ if x == 0 or y == 0 then
+ -- forget about it
+ else
+ local path = object.path
+ local path1x = path[1].x_coord
+ local path1y = path[1].y_coord
+ local path2x = path[x].x_coord
+ local path2y = path[y].y_coord
+
+ local dxa = path2x - path1x
+ local dya = path2y - path1y
+ local dxb = setx[2] - first[1]
+ local dyb = sety[2] - first[2]
+
+ if dxa == 0 or dya == 0 or dxb == 0 or dyb == 0 then
+ -- forget about it
+ else
+ sx = dxa / dxb ; if sx < 0 then sx = - sx end -- yes or no
+ sy = dya / dyb ; if sy < 0 then sy = - sy end -- yes or no
+
+ sr = sqrt(sx^2 + sy^2)
+
+ dx = path1x - sx*first[1]
+ dy = path1y - sy*first[2]
+ end
+ end
+ end
+
+ local steps = tonumber(prescript.sh_step) or 1
+ local sh_color_a = prescript.sh_color_a_1 or prescript.sh_color_a or "1"
+ local sh_color_b = prescript.sh_color_b_1 or prescript.sh_color_b or "1" -- sh_color_b_<sh_steps>
+ local ca, cb, colorspace, name, model, separation, fractions
+ if prescript.sh_color == "into" and prescript.sp_name then
+ -- some spotcolor
+ local value_a, components_a, fractions_a, name_a
+ local value_b, components_b, fractions_b, name_b
+ for i=1,#prescript do
+ -- { "sh_color_a", "1" },
+ -- { "sh_color", "into" },
+ -- { "sh_radius_b", "0" },
+ -- { "sh_radius_a", "141.73225" },
+ -- { "sh_center_b", "425.19676 141.73225" },
+ -- { "sh_center_a", "425.19676 0" },
+ -- { "sh_factor", "1" },
+ local tag = prescript[i][1]
+ if not name_a and tag == "sh_color_a" then
+ value_a = prescript[i-5][2]
+ components_a = prescript[i-4][2]
+ fractions_a = prescript[i-3][2]
+ name_a = prescript[i-2][2]
+ elseif not name_b and tag == "sh_color_b" then
+ value_b = prescript[i-5][2]
+ components_b = prescript[i-4][2]
+ fractions_b = prescript[i-3][2]
+ name_b = prescript[i-2][2]
+ end
+ if name_a and name_b then
+ break
+ end
+ end
+ ca, cb, separation, name = checkandconvertspot(
+ name_a,fractions_a,components_a,value_a,
+ name_b,fractions_b,components_b,value_b
+ )
+ else
+ local colora = lpegmatch(colorsplitter,sh_color_a)
+ local colorb = lpegmatch(colorsplitter,sh_color_b)
+ ca, cb, colorspace, name, model = checkandconvert(colora,colorb)
+ -- test:
+ if steps > 1 then
+ ca = { ca }
+ cb = { cb }
+ fractions = { tonumber(prescript[formatters["sh_fraction_%i"](1)]) or 0 }
+ for i=2,steps do
+ local colora = lpegmatch(colorsplitter,prescript[formatters["sh_color_a_%i"](i)])
+ local colorb = lpegmatch(colorsplitter,prescript[formatters["sh_color_b_%i"](i)])
+ ca[i], cb[i] = checkandconvert(colora,colorb,model)
+ fractions[i] = tonumber(prescript[formatters["sh_fraction_%i"](i)]) or (i/steps)
+ end
+ end
+ end
+ if not ca or not cb then
+ ca, cb, colorspace, name = checkandconvert()
+ steps = 1
+ end
+ if sh_type == "linear" then
+ local coordinates = { dx + sx*centera[1], dy + sy*centera[2], dx + sx*centerb[1], dy + sy*centerb[2] }
+ lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates,separation,steps>1 and steps,fractions) -- backend specific (will be renamed)
+ elseif sh_type == "circular" then
+ local factor = tonumber(prescript.sh_factor) or 1
+ local radiusa = factor * tonumber(prescript.sh_radius_a)
+ local radiusb = factor * tonumber(prescript.sh_radius_b)
+ local coordinates = { dx + sx*centera[1], dy + sy*centera[2], sr*radiusa, dx + sx*centerb[1], dy + sy*centerb[2], sr*radiusb }
+ lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates,separation,steps>1 and steps,fractions) -- backend specific (will be renamed)
+ else
+ -- fatal error
+ end
+ before[#before+1] = "q /Pattern cs"
+ after [#after+1] = formatters["W n /%s sh Q"](name)
+ -- false, not nil, else mt triggered
+ object.colored = false -- hm, not object.color ?
+ object.type = false
+ object.grouped = true
+ end
+end
+
+-- bitmaps
+
+local function bm_process(object,prescript,before,after)
+ local bm_xresolution = prescript.bm_xresolution
+ if bm_xresolution then
+ before[#before+1] = f_cm_b(cm(object))
+ before[#before+1] = function()
+ figures.bitmapimage {
+ xresolution = tonumber(bm_xresolution),
+ yresolution = tonumber(prescript.bm_yresolution),
+ width = 1/basepoints,
+ height = 1/basepoints,
+ data = object.postscript
+ }
+ end
+ before[#before+1] = s_cm_e
+ object.path = false
+ object.color = false
+ object.grouped = true
+ end
+end
+
+-- positions
+
+local function ps_process(object,prescript,before,after)
+ local ps_label = prescript.ps_label
+ if ps_label then
+ local op = object.path
+ local first = op[1]
+ local third = op[3]
+ local x = first.x_coord
+ local y = first.y_coord
+ local w = third.x_coord - x
+ local h = third.y_coord - y
+ local properties = metapost.properties
+ x = x - properties.llx
+ y = properties.ury - y
+ before[#before+1] = function()
+ context.MPLIBpositionwhd(ps_label,x,y,w,h)
+ end
+ object.path = false
+ end
+end
+
+-- figures
+
+-- local sx, rx, ry, sy, tx, ty = cm(object)
+-- sxsy(box.width,box.height,box.depth))
+
+function mp.mf_external_figure(filename)
+ local f = figures.getinfo(filename)
+ local w = 0
+ local h = 0
+ if f then
+ local u = f.used
+ if u and u.fullname then
+ w = u.width or 0
+ h = u.height or 0
+ end
+ else
+ report_metapost("external figure %a not found",filename)
+ end
+ mp.triplet(w/65536,h/65536,0)
+end
+
+local function fg_process(object,prescript,before,after)
+ local fg_name = prescript.fg_name
+ if fg_name then
+ before[#before+1] = f_cm_b(cm(object)) -- beware: does not use the cm stack
+ before[#before+1] = function()
+ context.MPLIBfigure(fg_name,prescript.fg_mask or "")
+ end
+ before[#before+1] = s_cm_e
+ object.path = false
+ object.grouped = true
+ end
+end
+
+-- color and transparency
+
+local value = Cs ( (
+ (Carg(1) * C((1-P(","))^1)) / function(a,b) return f_f3(a * tonumber(b)) end
+ + P(","))^1
+)
+
+-- should be codeinjections
+
+local t_list = attributes.list[attributes.private('transparency')]
+local c_list = attributes.list[attributes.private('color')]
+
+local remappers = {
+ [1] = formatters["s=%s"],
+ [3] = formatters["r=%s,g=%s,b=%s"],
+ [4] = formatters["c=%s,m=%s,y=%s,k=%s"],
+}
+
+local processlast = 0
+local processhash = setmetatableindex(function(t,k)
+ processlast = processlast + 1
+ local v = formatters["mp_%s"](processlast)
+ defineprocesscolor(v,k,true,true)
+ t[k] = v
+ return v
+end)
+
+local function checked_transparency(alternative,transparency,before,after)
+ alternative = tonumber(alternative) or 1
+ transparency = tonumber(transparency) or 0
+ before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,alternative,transparency,true))
+ after [#after +1] = "/Tr0 gs" -- outertransparency
+end
+
+local function tr_process(object,prescript,before,after)
+ -- before can be shortcut to t
+ local tr_alternative = prescript.tr_alternative
+ if tr_alternative then
+ checked_transparency(tr_alternative,prescript.tr_transparency,before,after)
+ end
+ local cs = object.color
+ if cs and #cs > 0 then
+ local c_b, c_a
+ local sp_type = prescript.sp_type
+ if not sp_type then
+ c_b, c_a = colorconverter(cs)
+ else
+ local sp_name = prescript.sp_name or "black"
+ if sp_type == "spot" then
+ local sp_value = prescript.sp_value or "1"
+ local components = split(sp_value,":")
+ local specification = remappers[#components]
+ if specification then
+ specification = specification(unpack(components))
+ else
+ specification = "s=0"
+ end
+ local sp_spec = processhash[specification]
+ definespotcolor(sp_name,sp_spec,"p=1",true)
+ sp_type = "named"
+ elseif sp_type == "multitone" then -- (fractions of a multitone) don't work well in mupdf
+ local sp_value = prescript.sp_value or "1"
+ local sp_specs = { }
+ local sp_list = split(sp_value," ")
+ for i=1,#sp_list do
+ local sp_value = sp_list[i]
+ local components = split(sp_value,":")
+ local specification = remappers[#components]
+ if specification then
+ specification = specification(unpack(components))
+ else
+ specification = "s=0"
+ end
+ local sp_spec = processhash[specification]
+ sp_specs[i] = formatters["%s=1"](sp_spec)
+ end
+ sp_specs = concat(sp_specs,",")
+ definemultitonecolor(sp_name,sp_specs,"","")
+ sp_type = "named"
+ end
+ if sp_type == "named" then
+ -- we might move this to another namespace .. also, named can be a spotcolor
+ -- so we need to check for that too ... also we need to resolve indirect
+ -- colors so we might need the second pass for this (draw dots with \MPcolor)
+ if not tr_alternative then
+ -- todo: sp_name is not yet registered at this time
+ local t = t_list[sp_name] -- string or attribute
+ local v = t and transparencyvalue(t)
+ if v then
+ checked_transparency(v[1],v[2],before,after)
+ end
+ end
+ local c = c_list[sp_name] -- string or attribute
+ local v = c and colorvalue(c)
+ if v then
+ -- all=1 gray=2 rgb=3 cmyk=4
+ local colorspace = v[1]
+ local factor = cs[1]
+ if colorspace == 2 then
+ local s = factor * v[2]
+ c_b, c_a = checked_color_pair(f_gray,s,s)
+ elseif colorspace == 3 then
+ local r = factor * v[3]
+ local g = factor * v[4]
+ local b = factor * v[5]
+ c_b, c_a = checked_color_pair(f_rgb,r,g,b,r,g,b)
+ elseif colorspace == 4 or colorspace == 1 then
+ local c = factor * v[6]
+ local m = factor * v[7]
+ local y = factor * v[8]
+ local k = factor * v[9]
+ c_b, c_a = checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ elseif colorspace == 5 then
+ -- not all viewers show the fractions ok
+ local name = v[10]
+ local value = split(v[13],",")
+ if factor ~= 1 then
+ for i=1,#value do
+ value[i] = f_scn(factor * (tonumber(value[i]) or 1))
+ end
+ end
+ value = concat(value," ")
+ c_b, c_a = checked_color_pair(f_spot,name,name,value,value)
+ else
+ local s = factor *v[2]
+ c_b, c_a = checked_color_pair(f_gray,s,s)
+ end
+ end
+ end
+ end
+ if c_a and c_b then
+ before[#before+1] = c_b
+ after [#after +1] = c_a
+ end
+ end
+end
+
+-- layers (nasty: we need to keep the 'grouping' right
+
+local function la_process(object,prescript,before,after)
+ local la_name = prescript.la_name
+ if la_name then
+ before[#before+1] = backends.codeinjections.startlayer(la_name)
+ insert(after,1,backends.codeinjections.stoplayer())
+ end
+end
+
+-- groups
+
+local function gr_process(object,prescript,before,after)
+ local gr_state = prescript.gr_state
+ if not gr_state then
+ return
+ elseif gr_state == "start" then
+ local gr_type = utilities.parsers.settings_to_set(prescript.gr_type)
+ local path = object.path
+ local p1 = path[1]
+ local p2 = path[2]
+ local p3 = path[3]
+ local p4 = path[4]
+ local llx = min(p1.x_coord,p2.x_coord,p3.x_coord,p4.x_coord)
+ local lly = min(p1.y_coord,p2.y_coord,p3.y_coord,p4.y_coord)
+ local urx = max(p1.x_coord,p2.x_coord,p3.x_coord,p4.x_coord)
+ local ury = max(p1.y_coord,p2.y_coord,p3.y_coord,p4.y_coord)
+ before[#before+1] = function()
+ context.MPLIBstartgroup(
+ gr_type.isolated and 1 or 0,
+ gr_type.knockout and 1 or 0,
+ llx, lly, urx, ury
+ )
+ end
+ elseif gr_state == "stop" then
+ after[#after+1] = function()
+ context.MPLIBstopgroup()
+ end
+ end
+ object.path = false
+ object.color = false
+ object.grouped = true
+end
+
+-- outlines
+
+local ot_reset, ot_process do
+
+ local outlinetexts = { } -- also in top data
+
+ ot_reset = function ()
+ outlinetexts = { }
+ end
+
+ local mp_index = 0
+ local mp_kind = ""
+ local mp_str = ""
+
+ function mp.mf_outline_text(index,str,kind)
+ if not outlinetexts[index] then
+ mp_index = index
+ mp_kind = kind
+ mp_str = str
+ texrunlocal("mpoutlinetoks")
+ end
+ end
+
+ interfaces.implement {
+ name = "mpoutlinetoks",
+ actions = function()
+ context.MPLIBoutlinetext(mp_index,mp_kind,mp_str)
+ end,
+ }
+
+ implement {
+ name = "MPLIBconvertoutlinetext",
+ arguments = { "integer", "string", "integer" },
+ actions = function(index,kind,box)
+ local boxtomp = fonts.metapost.boxtomp
+ if boxtomp then
+ outlinetexts[index] = boxtomp(box,kind)
+ else
+ outlinetexts[index] = ""
+ end
+ end
+ }
+
+ function mp.mf_get_outline_text(index) -- maybe we need a more private namespace
+ mp.print(outlinetexts[index] or "draw origin;")
+ end
+
+end
+
+-- mf_object=<string>
+
+local p1 = P("mf_object=")
+local p2 = lpeg.patterns.eol * p1
+local pattern = (1-p2)^0 * p2 + p1
+
+function metapost.isobject(str)
+ return pattern and str ~= "" and lpegmatch(p,str) and true or false
+end
+
+local function installplugin(specification)
+ local reset = specification.reset
+ local process = specification.process
+ local object = specification.object
+ if reset then
+ appendaction(resetteractions,"system",reset)
+ end
+ if process then
+ appendaction(processoractions,"system",process)
+ end
+end
+
+metapost.installplugin = installplugin
+
+-- definitions
+
+installplugin { name = "outline", reset = ot_reset, process = ot_process }
+installplugin { name = "color", reset = cl_reset, process = cl_process }
+installplugin { name = "text", reset = tx_reset, process = tx_process }
+installplugin { name = "group", reset = gr_reset, process = gr_process }
+installplugin { name = "graphictext", reset = gt_reset, process = gt_process }
+installplugin { name = "shade", reset = sh_reset, process = sh_process }
+installplugin { name = "bitmap", reset = bm_reset, process = bm_process }
+installplugin { name = "box", reset = bx_reset, process = bx_process }
+installplugin { name = "position", reset = ps_reset, process = ps_process }
+installplugin { name = "figure", reset = fg_reset, process = fg_process }
+installplugin { name = "layer", reset = la_reset, process = la_process }
+installplugin { name = "transparency", reset = tr_reset, process = tr_process }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-pps.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mlib-pps.mkxl
new file mode 100644
index 00000000000..2fa721445e8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-pps.mkxl
@@ -0,0 +1,228 @@
+%D \module
+%D [ file=mlib-pps,
+%D version=2008.03.25,
+%D title=\METAPOST\ Integrated Graphics,
+%D subtitle=Basics,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: remove MPenvironment code
+
+\unprotect
+
+\registerctxluafile{mlib-pps}{autosuffix}
+
+%D Todo: catch nested graphics like external figures with dummies.
+
+% todo: figure out why this is shifted
+%
+% \setupbodyfont[palatino]
+%
+% \starttext
+% \startMPpage
+% draw textext("123") ;
+% \stopMPpage
+% \stoptext
+%
+% A slightly larger picture works ok so maybe there is some kind
+% of interference with the page builder.
+
+\newbox \MPtextbox
+\newtoks\everyMPLIBsettext % not used
+
+\permanent\protected\def\mlib_flush_environment
+ {%\writestatus\m!metapost{flushing environment}%
+ \clf_mptexget
+ \enforced\let\MPLIBflushenvironment\relax}% MPenvironments are depricated}
+
+\aliased\let\MPLIBflushenvironment\mlib_flush_environment
+
+\permanent\protected\def\MPLIBsetNtext#1% #2% box text
+ {\MPLIBflushenvironment
+ \dowithnextbox{\clf_mpsettext\nextbox #1}\hbox\bgroup % text
+ \meta_set_current_color
+ \enforced\let\MPLIBflushenvironment\mlib_flush_environment
+ \let\next} % gobble open brace
+
+\permanent\protected\def\MPLIBsetCtext#1#2% #3% box colorspec text
+ {\MPLIBflushenvironment
+ \dowithnextbox{\clf_mpsettext\nextbox #1}\hbox\bgroup % text
+ \directcolored[#2]%
+ \meta_set_current_color % so, textcolor wins !
+ \enforced\let\MPLIBflushenvironment\mlib_flush_environment
+ \let\next} % gobble open brace
+
+\aliased\let\MPLIBsettext\MPLIBsetNtext
+
+\permanent\protected\def\MPLIBsetNtextX#1% #2% box text
+ {\MPLIBflushenvironment
+ \hbox\bgroup % text
+ \meta_set_current_color
+ \enforced\let\MPLIBflushenvironment\mlib_flush_environment
+ \let\next}
+
+\permanent\protected\def\MPLIBsetCtextX#1#2% #3% box colorspec text
+ {\MPLIBflushenvironment
+ \hbox\bgroup % text
+ \directcolored[#2]%
+ \meta_set_current_color % so, textcolor wins !
+ \enforced\let\MPLIBflushenvironment\mlib_flush_environment
+ \let\next}
+
+\aliased\let\MPLIBsettextX\MPLIBsetNtextX
+
+\permanent\permanent\protected\def\MPLIBgettextscaled#1#2#3% why a copy .. can be used more often
+ {\clf_mpgettext\MPtextbox #1%
+ \vpack to \zeropoint
+ {\vss
+ %\hpack to \zeropoint \s!container
+ \hcontainer to \zeropoint
+ {\scale
+ [\c!sx=#2,\c!sy=#3]%
+ {\raise\dp\MPtextbox\box\MPtextbox}\forcecolorhack\hss}}}
+
+\let\mlib_sx\!!zerocount % set at the lua end
+\let\mlib_sy\!!zerocount % set at the lua end
+
+\permanent\protected\def\MPLIBfigure#1#2%
+ {\setbox\scratchbox\hpack{\externalfigure[#1][\c!mask=#2]}%
+ \clf_mpsetsxsy\wd\scratchbox\ht\scratchbox\zeropoint
+ \vpack to \zeropoint
+ {\vss
+ \hpack to \zeropoint
+ {\fastsxsy{\mlib_sx}{\mlib_sy}{\box\scratchbox}\hss}}}
+
+% horrible (we could inline scale and matrix code):
+
+\permanent\protected\def\MPLIBgettextscaledcm#1#2#3#4#5#6#7#8#9% 2-7: sx,rx,ry,sy,tx,ty
+ {\clf_mpgettext\MPtextbox #1%
+ \setbox\MPbox\hpack\bgroup
+ \dotransformnextbox{#2}{#3}{#4}{#5}{#6}{#7}% does push pop ... will be changed to proper lua call (avoid small numbers)
+ \vpack to \zeropoint\bgroup
+ \vss
+ %\hpack to \zeropoint \s!container \bgroup
+ \hcontainer to \zeropoint \bgroup
+ % \scale[\c!sx=#8,\c!sy=#9]{\raise\dp\MPtextbox\box\MPtextbox}%
+ % \scale[\c!sx=#8,\c!sy=#9,\c!depth=\v!no]{\box\MPtextbox}%
+ \fastsxsy{#8}{#9}{\raise\dp\MPtextbox\box\MPtextbox}%
+ % This gives: LuaTeX warning: Misplaced \pdfrestore .. don't ask me why.
+ % but I'll retry it some day soon.
+ % \dostartscaling{#8}{#9}%
+ % \raise\dp\MPtextbox\box\MPtextbox
+ % \dostopscaling
+ \forcecolorhack % can go away ... already in the scale macro
+ \hss
+ \egroup
+ \egroup
+ \egroup
+ \smashbox\MPbox
+ \box\MPbox}
+
+% \putnextboxincache{hans}{1}\hbox{foo}
+%
+% \startMPcode
+% draw boundingbox rawtexbox("hans",1) ;
+% draw rawtexbox("hans",1) ;
+% \stopMPcode
+
+\permanent\protected\def\MPLIBgetboxscaledcm#1#2%
+ {\begingroup
+ \copyboxfromcache{#1}{#2}\MPtextbox % can be \clf_
+ \mlib_get_box_scaled_cm_next}
+
+\protected\def\mlib_get_box_scaled_cm_next#1#2#3#4#5#6#7#8% 1-6: sx,rx,ry,sy,tx,ty
+ {\setbox\MPbox\hpack\bgroup
+ \dotransformnextbox{#1}{#2}{#3}{#4}{#5}{#6}%
+ \vpack to \zeropoint\bgroup
+ \vss
+ \hpack to \zeropoint \bgroup
+ \fastsxsy{#7}{#8}{\raise\dp\MPtextbox\box\MPtextbox}%
+ \hss
+ \egroup
+ \egroup
+ \egroup
+ \smashbox\MPbox
+ \box\MPbox
+ \endgroup}
+
+\permanent\protected\def\MPLIBgraphictext#1% use at mp end
+ {\startTEXpage[\c!scale=10000]#1\stopTEXpage}
+
+%D \startbuffer
+%D \definelayer[test]
+%D
+%D \setlayerframed
+%D [test]
+%D [x=\MPx{somepos-1},y=\MPy{somepos-1}]
+%D [width=\MPw{somepos-1},height=\MPh{somepos-1}]
+%D {Whatever we want here!}
+%D
+%D \setlayerframed
+%D [test]
+%D [x=\MPx{somepos-2},y=\MPy{somepos-2}]
+%D [width=\MPw{somepos-2},height=\MPh{somepos-2}]
+%D {Whatever we need there!}
+%D
+%D \startuseMPgraphic{oeps}
+%D draw fullcircle scaled 6cm withcolor red ;
+%D register ("somepos-1",5cm,1cm,center currentpicture) ;
+%D register ("somepos-2",4cm,3cm,(-1cm,-2cm)) ;
+%D \stopuseMPgraphic
+%D
+%D \framed[background=test,offset=overlay]{\useMPgraphic{oeps}}
+%D \stopbuffer
+%D
+%D \typebuffer \startlinecorrection \getbuffer \stoplinecorrection
+
+\permanent\protected\def\MPLIBpositionwhd#1#2#3#4#5% bp !
+ {\dosavepositionwhd{#1}\zerocount{#2\onebasepoint}{#3\onebasepoint}{#4\onebasepoint}{#5\onebasepoint}\zeropoint}
+
+\def\mlib_stop_group#1#2#3#4#5#6% some day this might happen elsewhere
+ {\egroup
+ \setbox\scratchbox\hpack{\kern\onebasepoint\box\scratchbox}% weird correction
+ \wd\scratchbox \dimexpr#5\onebasepoint-#3\onebasepoint+2\onebasepoint\relax
+ \ht\scratchbox #6\onebasepoint
+ \dp\scratchbox-#4\onebasepoint
+ \setbox\scratchbox\hpack\bgroup
+ \kern-#3\onebasepoint
+ \box\scratchbox
+ \egroup
+ \saveboxresource
+ attr {/Group << /S /Transparency /I \ifnum#1=1 true \else false \fi /K \ifnum#2=1 true \else false \fi >>}
+ resources {\pdfbackendcurrentresources}
+ \scratchbox
+ \setbox\scratchbox\hpack\bgroup
+ \kern#3\onebasepoint
+ \kern-\onebasepoint
+ \useboxresource\lastsavedboxresourceindex
+ \egroup
+ \wd\scratchbox\zeropoint
+ \ht\scratchbox\zeropoint
+ \dp\scratchbox\zeropoint
+ \box\scratchbox
+ \endgroup}
+
+\aliased\let\MPLIBstopgroup\relax
+
+\permanent\protected\def\MPLIBstartgroup#1#2#3#4#5#6% isolated 0/1, knockout 0/1 llx lly urx ury
+ {\begingroup
+ \setbox\scratchbox\hpack\bgroup
+ \enforced\permanent\protected\def\MPLIBstopgroup{\mlib_stop_group{#1}{#2}{#3}{#4}{#5}{#6}}}
+
+% For now here ... will be cleaned up:
+
+\newtoks\mptexttoks
+\newbox \mptextbox
+\newtoks\mpoutlinetoks
+\newtoks\mpgraphictexttoks
+
+\mptexttoks {\global\setbox\mptextbox\hbox{\clf_mptexttoks}}
+\mpoutlinetoks {\global\setbox\mptextbox\vbox{\clf_mpoutlinetoks}}
+\mpgraphictexttoks{\global\setbox\mptextbox\vbox{\clf_mpgraphictexttoks}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-ran.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-ran.lmt
new file mode 100644
index 00000000000..a6d4fbe5bff
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-ran.lmt
@@ -0,0 +1,237 @@
+if not modules then modules = { } end modules ['mlib-ran'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local next = next
+local ceil, floor, random, sqrt, cos, sin, pi, max, min = math.ceil, math.floor, math.random, math.sqrt, math.cos, math.sin, math.pi, math.min, math.max
+local remove = table.remove
+
+-- Below is a bit of rainy saturday afternoon hobyism, while listening to Judith
+-- Owens redisCOVERed (came there via Leland Sklar who I have on a few live blurays;
+-- and who is also on YT). (Also nice: https://www.youtube.com/watch?v=GXqasIRaxlA)
+
+-- When Aditya pointed me to an article on mazes I ended up at poison distributions
+-- which to me looks nicer than what I normally do, fill a grid and then randomize
+-- the resulting positions. With some hooks this can be used for interesting patterns
+-- too. A few links:
+--
+-- https://bost.ocks.org/mike/algorithms/#maze-generation
+-- https://extremelearning.com.au/
+-- https://www.jasondavies.com/maps/random-points/
+-- http://devmag.org.za/2009/05/03/poisson-disk-sampling
+
+-- The next function is quite close to what us discribed in the poisson-disk-sampling
+-- link mentioned before. One can either use a one dimensional grid array or a two
+-- dimensional one. The example code uses some classes dealing with points. In the
+-- process I added some more control.
+
+-- we could do without the samplepoints list
+
+local function poisson(width, height, mindist, newpointscount, initialx, initialy)
+ local starttime = os.clock()
+ local cellsize = mindist / sqrt(2)
+ local nofwidth = ceil(width // cellsize)
+ local nofheight = ceil(height // cellsize)
+ local grid = lua.newtable(nofwidth,0) -- table.setmetatableindex("table")
+ local firstx = initialx or random() * width
+ local firsty = initialy or random() * height
+ local firstpoint = { firstx, firsty, 1 }
+ -- local samplepoints = { firstpoint }
+ local processlist = { firstpoint }
+ local nofprocesslist = 1
+ local nofsamplepoints = 1
+ local twopi = 2 * pi
+
+ for i=1,nofwidth do
+ local g = lua.newindex(nofheight,false)
+ grid[i] = g
+ end
+
+ local x = floor(firstx // cellsize) + 1 -- lua indices
+ local y = floor(firsty // cellsize) + 1 -- lua indices
+
+ x = max(1, min(x, width - 1))
+ y = max(1, min(y, height - 1))
+
+ grid[x][y] = firstpoint
+
+ -- The website shows graphic for this 5*5 grid snippet, if we use a one dimentional
+ -- array then we could have one loop; a first version used a metatable trick so we
+ -- had grid[i+gx][j+gy] but we no we also return the grid, so ... we now just check.
+
+ -- There is no need for the samplepoints list as we can get that from the grid but
+ -- instead we can store the index with the grid.
+
+ while nofprocesslist > 0 do
+ local point = remove(processlist,random(1,nofprocesslist))
+ nofprocesslist = nofprocesslist - 1
+ for i=1,newpointscount do -- we start at 1
+ local radius = mindist * (random() + 1)
+ local angle = twopi * random()
+ local nx = point[1] + radius * cos(angle)
+ local ny = point[2] + radius * sin(angle)
+ if nx > 1 and ny > 1 and nx <= width and ny <= height then -- lua indices
+ local gx = floor(nx // cellsize)
+ local gy = floor(ny // cellsize)
+ -- the 5x5 cells around the point
+ for i=-2,2 do
+ for j=-2,2 do
+ local cell = grid[i + gx]
+ if cell then
+ cell = cell[j + gy]
+ if cell and sqrt((cell[1] - nx)^2 + (cell[2] - ny)^2) < mindist then
+ goto next
+ end
+ end
+ end
+ end
+ -- local newpoint = { nx, ny }
+ nofprocesslist = nofprocesslist + 1
+ nofsamplepoints = nofsamplepoints + 1
+ local newpoint = { nx, ny, nofsamplepoints }
+ processlist [nofprocesslist] = newpoint
+ -- samplepoints[nofsamplepoints] = newpoint
+ grid[gx][gy] = newpoint
+ end
+ ::next::
+ end
+ end
+
+ return {
+ count = nofsamplepoints,
+ -- points = samplepoints,
+ grid = grid,
+ time = os.clock() - starttime,
+ }
+end
+
+-- For now:
+
+local randomizers = utilities.randomizers or { }
+utilities.randomizers = randomizers
+randomizers.poisson = poisson
+
+-- The MetaFun interface:
+
+local formatters = string.formatters
+local concat = table.concat
+
+local f_macro = formatters["%s(%N,%N);"]
+
+local f_macros = {
+ [2] = formatters["%s(%N,%N);"],
+ [3] = formatters["%s(%N,%N,%i);"],
+ [4] = formatters["%s(%N,%N,%i,%i);"],
+}
+
+local function grid_to_mp(t,f,n)
+ local grid = t.grid
+ local count = t.count
+ local result = { }
+ local r = 0
+ local macro = f or "draw"
+ local runner = f_macros[n or 2] or f_macros[2]
+ for i=1,#grid do
+ local g = grid[i]
+ if g then
+ for j=1,#g do
+ local v = g[j]
+ if v then
+ r = r + 1
+ result[r] = runner(macro,v[1],v[2],v[3],count)
+ end
+ end
+ end
+ end
+ return concat(result, " ")
+end
+
+local getparameter = metapost.getparameter
+
+local function lmt_poisson()
+ local initialx = getparameter { "initialx" }
+ local initialy = getparameter { "initialy" }
+ local width = getparameter { "width" }
+ local height = getparameter { "height" }
+ local distance = getparameter { "distance" }
+ local count = getparameter { "count" }
+
+ local result = poisson (
+ width, height, distance, count,
+ initialx > 0 and initialx or false,
+ initialy > 0 and initialy or false
+ )
+
+ if result then
+ logs.report("poisson","w=%N, h=%N, d=%N, c=%N, n=%i, runtime %.3f",
+ width, height, distance, count, result.count, result.time
+ )
+ end
+
+ return result
+end
+
+function mp.lmt_poisson_generate()
+ local result = lmt_poisson()
+ if result then
+ return grid_to_mp (
+ result,
+ getparameter { "macro" },
+ getparameter { "arguments" }
+ )
+ end
+end
+
+-- -- some playing around showed no benefit
+--
+-- function points_to_mp(t,f)
+-- local points = t.points
+-- local count = t.count
+-- local result = { }
+-- local r = 0
+-- local macro = f or "draw"
+-- local runner = f_macros[n or 2] or f_macros[2]
+-- for i=1,count do
+-- local v = points[i]
+-- r = r + 1
+-- result[r] = runner(macro,v[1],v[2],v[3],count)
+-- end
+-- return concat(result, " ")
+-- end
+--
+-- local result = false
+-- local i, j, n = 0, 0, 0
+--
+-- function mp.lmt_poison_start()
+-- result = lmt_poisson()
+-- end
+--
+-- function mp.lmt_poisson_stop()
+-- result = false
+-- end
+--
+-- function mp.lmt_poisson_count()
+-- return result and result.count or 0
+-- end
+--
+-- function mp.lmt_poisson_get(i)
+-- if result then
+-- return mp.pair(result.points[i])
+-- end
+-- end
+--
+-- function mp.lmt_poisson_generate()
+-- mp.lmt_poisson_start()
+-- if result then
+-- return grid_to_mp (
+-- result,
+-- getparameter { "macro" },
+-- getparameter { "arguments" }
+-- )
+-- end
+-- mp.lmt_poisson_stop()
+-- end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt
new file mode 100644
index 00000000000..eacb148cc6c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-run.lmt
@@ -0,0 +1,722 @@
+if not modules then modules = { } end modules ['mlib-run'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- cmyk -> done, native
+-- spot -> done, but needs reworking (simpler)
+-- multitone ->
+-- shade -> partly done, todo: cm
+-- figure -> done
+-- hyperlink -> low priority, easy
+
+-- new * run
+-- or
+-- new * execute^1 * finish
+
+-- a*[b,c] == b + a * (c-b)
+
+--[[ldx--
+<p>The directional helpers and pen analysis are more or less translated from the
+<l n='c'/> code. It really helps that Taco know that source so well. Taco and I spent
+quite some time on speeding up the <l n='lua'/> and <l n='c'/> code. There is not
+much to gain, especially if one keeps in mind that when integrated in <l n='tex'/>
+only a part of the time is spent in <l n='metapost'/>. Of course an integrated
+approach is way faster than an external <l n='metapost'/> and processing time
+nears zero.</p>
+--ldx]]--
+
+local type, tostring, tonumber, next = type, tostring, tonumber, next
+local find, striplines = string.find, utilities.strings.striplines
+local concat, insert, remove, sortedkeys = table.concat, table.insert, table.remove, table.sortedkeys
+
+local emptystring = string.is_empty
+
+local trace_graphics = false trackers.register("metapost.graphics", function(v) trace_graphics = v end)
+local trace_tracingall = false trackers.register("metapost.tracingall", function(v) trace_tracingall = v end)
+
+local report_metapost = logs.reporter("metapost")
+local texerrormessage = logs.texerrormessage
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local formatters = string.formatters
+
+local mplib = mplib
+metapost = metapost or { }
+local metapost = metapost
+
+metapost.showlog = false
+metapost.lastlog = ""
+metapost.texerrors = false
+metapost.exectime = metapost.exectime or { } -- hack
+metapost.nofruns = 0
+
+local mpxformats = { }
+local nofformats = 0
+local mpxpreambles = { }
+local mpxterminals = { }
+local mpxextradata = { }
+
+-- The flatten hack is needed because the library currently barks on \n\n and the
+-- collapse because mp cannot handle snippets due to grouping issues.
+
+-- todo: pass tables to executempx instead of preparing beforehand,
+-- as it's more efficient for the terminal
+
+local function flatten(source,target)
+ for i=1,#source do
+ local d = source[i]
+ if type(d) == "table" then
+ flatten(d,target)
+ elseif d and d ~= "" then
+ target[#target+1] = d
+ end
+ end
+ return target
+end
+
+local function prepareddata(data)
+ if data and data ~= "" then
+ if type(data) == "table" then
+ data = flatten(data,{ })
+ data = #data > 1 and concat(data,"\n") or data[1]
+ end
+ return data
+ end
+end
+
+-- local function executempx(mpx,data)
+-- local terminal = mpxterminals[mpx]
+-- if terminal then
+-- terminal.writer(data)
+-- data = ""
+-- elseif type(data) == "table" then
+-- data = prepareddata(data,collapse)
+-- end
+-- metapost.nofruns = metapost.nofruns + 1
+-- return mpx:execute(data)
+-- end
+
+local function executempx(mpx,data)
+ local terminal = mpxterminals[mpx]
+ if terminal then
+ terminal.writer(data)
+ data = nil
+ elseif type(data) == "table" then
+ data = prepareddata(data,collapse)
+ end
+ metapost.nofruns = metapost.nofruns + 1
+ return mpx:execute(data)
+end
+
+directives.register("mplib.texerrors", function(v) metapost.texerrors = v end)
+trackers.register ("metapost.showlog", function(v) metapost.showlog = v end)
+
+function metapost.resetlastlog()
+ metapost.lastlog = ""
+end
+
+local new_instance = mplib.new
+local find_file = mplib.finder
+
+function metapost.reporterror(result)
+ if not result then
+ report_metapost("error: no result object returned")
+ return true
+ elseif result.status == 0 then
+ return false
+ elseif mplib.realtimelogging then
+ return false -- we already reported
+ else
+ local t = result.term
+ local e = result.error
+ local l = result.log
+ local report = metapost.texerrors and texerrormessage or report_metapost
+ if t and t ~= "" then
+ report("mp error: %s",striplines(t))
+ end
+ if e == "" or e == "no-error" then
+ e = nil
+ end
+ if e then
+ report("mp error: %s",striplines(e))
+ end
+ if not t and not e and l then
+ metapost.lastlog = metapost.lastlog .. "\n" .. l
+ report_metapost("log: %s",l)
+ else
+ report_metapost("error: unknown, no error, terminal or log messages")
+ end
+ return true
+ end
+end
+
+local f_preamble = formatters [ [[
+ boolean mplib ; mplib := true ;
+ let dump = endinput ;
+ input "%s" ;
+ randomseed:=%s;
+]] ]
+
+local methods = {
+ double = "double",
+ scaled = "scaled",
+ -- binary = "binary",
+ binary = "double",
+ decimal = "decimal",
+ default = "scaled",
+}
+
+function metapost.runscript(code)
+ return ""
+end
+
+function metapost.scripterror(str)
+ report_metapost("script error: %s",str)
+end
+
+-- todo: random_seed
+
+local seed = nil
+
+function metapost.load(name,method)
+ starttiming(mplib)
+ if not seed then
+ seed = job.getrandomseed()
+ if seed <= 1 then
+ seed = seed % 1000
+ elseif seed > 4095 then
+ seed = seed % 4096
+ end
+ end
+ method = method and methods[method] or "scaled"
+ local mpx, terminal = new_instance {
+-- noninteractive = true;
+-- ini_version = true,
+ math_mode = method,
+ run_script = metapost.runscript,
+ script_error = metapost.scripterror,
+ make_text = metapost.maketext,
+ extensions = 1,
+ -- random_seed = seed,
+ utf8_mode = true,
+ text_mode = true,
+ }
+ report_metapost("initializing number mode %a",method)
+ local result
+ if not mpx then
+ result = { status = 99, error = "out of memory"}
+ else
+ mpxterminals[mpx] = terminal
+ -- pushing permits advanced features
+ metapost.pushscriptrunner(mpx)
+ result = executempx(mpx,f_preamble(file.addsuffix(name,"mp"),seed))
+ metapost.popscriptrunner()
+ end
+ stoptiming(mplib)
+ metapost.reporterror(result)
+ return mpx, result
+end
+
+function metapost.checkformat(mpsinput,method)
+ local mpsinput = mpsinput or "metafun"
+ local foundfile = ""
+ if file.suffix(mpsinput) ~= "" then
+ foundfile = find_file(mpsinput) or ""
+ end
+ -- if foundfile == "" then
+ -- foundfile = find_file(file.replacesuffix(mpsinput,"mpvi")) or ""
+ -- end
+ if foundfile == "" then
+ foundfile = find_file(file.replacesuffix(mpsinput,"mpxl")) or ""
+ end
+ if foundfile == "" then
+ foundfile = find_file(file.replacesuffix(mpsinput,"mpiv")) or ""
+ end
+ if foundfile == "" then
+ foundfile = find_file(file.replacesuffix(mpsinput,"mp")) or ""
+ end
+ if foundfile == "" then
+ report_metapost("loading %a fails, format not found",mpsinput)
+ else
+ report_metapost("loading %a as %a using method %a",mpsinput,foundfile,method or "default")
+ local mpx, result = metapost.load(foundfile,method)
+ if mpx then
+ return mpx
+ else
+ report_metapost("error in loading %a",mpsinput)
+ metapost.reporterror(result)
+ end
+ end
+end
+
+function metapost.unload(mpx)
+ starttiming(mplib)
+ if mpx then
+ mpx:finish()
+ end
+ stoptiming(mplib)
+end
+
+metapost.defaultformat = "metafun"
+metapost.defaultinstance = "metafun"
+metapost.defaultmethod = "default"
+
+function metapost.getextradata(mpx)
+ return mpxextradata[mpx]
+end
+
+function metapost.pushformat(specification,f,m) -- was: instance, name, method
+ if type(specification) ~= "table" then
+ specification = {
+ instance = specification,
+ format = f,
+ method = m,
+ }
+ end
+ local instance = specification.instance
+ local format = specification.format
+ local method = specification.method
+ local definitions = specification.definitions
+ local extensions = specification.extensions
+ local preamble = nil
+ if not instance or instance == "" then
+ instance = metapost.defaultinstance
+ specification.instance = instance
+ end
+ if not format or format == "" then
+ format = metapost.defaultformat
+ specification.format = format
+ end
+ if not method or method == "" then
+ method = metapost.defaultmethod
+ specification.method = method
+ end
+ if definitions and definitions ~= "" then
+ preamble = definitions
+ end
+ if extensions and extensions ~= "" then
+ if preamble then
+ preamble = preamble .. "\n" .. extensions
+ else
+ preamble = extensions
+ end
+ end
+ nofformats = nofformats + 1
+ local usedinstance = instance .. ":" .. nofformats
+ local mpx = mpxformats [usedinstance]
+ local mpp = mpxpreambles[instance] or ""
+ -- report_metapost("push instance %a (%S)",usedinstance,mpx)
+ if preamble then
+ preamble = prepareddata(preamble)
+ mpp = mpp .. "\n" .. preamble
+ mpxpreambles[instance] = mpp
+ end
+ if not mpx then
+ report_metapost("initializing instance %a using format %a and method %a",usedinstance,format,method)
+ mpx = metapost.checkformat(format,method)
+ mpxformats [usedinstance] = mpx
+ mpxextradata[mpx] = { }
+ if mpp ~= "" then
+ preamble = mpp
+ end
+ end
+ if preamble then
+ metapost.pushscriptrunner(mpx)
+ executempx(mpx,preamble)
+ metapost.popscriptrunner()
+ end
+ specification.mpx = mpx
+ return mpx
+end
+
+-- luatex.wrapup(function()
+-- for k, mpx in next, mpxformats do
+-- mpx:finish()
+-- end
+-- end)
+
+function metapost.popformat()
+ nofformats = nofformats - 1
+end
+
+function metapost.reset(mpx)
+ if not mpx then
+ -- nothing
+ elseif type(mpx) == "string" then
+ if mpxformats[mpx] then
+ local instance = mpxformats[mpx]
+ instance:finish()
+ mpxterminals[mpx] = nil
+ mpxextradata[mpx] = nil
+ mpxformats [mpx] = nil
+ end
+ else
+ for name, instance in next, mpxformats do
+ if instance == mpx then
+ mpx:finish()
+ mpxterminals[mpx] = nil
+ mpxextradata[mpx] = nil
+ mpxformats [mpx] = nil
+ break
+ end
+ end
+ end
+end
+
+local mp_tra = { }
+local mp_tag = 0
+
+-- key/values
+
+do
+
+ local stack, top = { }, nil
+
+ function metapost.setvariable(k,v)
+ if top then
+ top[k] = v
+ else
+ metapost.variables[k] = v
+ end
+ end
+
+ function metapost.pushvariable(k)
+ local t = { }
+ if top then
+ insert(stack,top)
+ top[k] = t
+ else
+ metapost.variables[k] = t
+ end
+ top = t
+ end
+
+ function metapost.popvariable()
+ top = remove(stack)
+ end
+
+ local stack = { }
+
+ function metapost.pushvariables()
+ insert(stack,metapost.variables)
+ metapost.variables = { }
+ end
+
+ function metapost.popvariables()
+ metapost.variables = remove(stack) or metapost.variables
+ end
+
+end
+
+
+if not metapost.process then
+
+ function metapost.process(specification)
+ metapost.run(specification)
+ end
+
+end
+
+-- run, process, convert and flush all work with a specification with the
+-- following (often optional) fields
+--
+-- mpx string or mp object
+-- data string or table of strings
+-- flusher table with flush methods
+-- askedfig string ("all" etc) or number
+-- incontext boolean
+-- plugmode boolean
+
+local function makebeginbanner(specification)
+ return formatters["%% begin graphic: n=%s\n\n"](metapost.n)
+end
+
+local function makeendbanner(specification)
+ return "\n% end graphic\n\n"
+end
+
+function metapost.run(specification)
+ local mpx = specification.mpx
+ local data = specification.data
+ local converted = false
+ local result = { }
+ local mpxdone = type(mpx) == "string"
+ if mpxdone then
+ mpx = metapost.pushformat { instance = mpx, format = mpx }
+ end
+ if mpx and data then
+ local tra = nil
+ starttiming(metapost) -- why not at the outer level ...
+ metapost.variables = { } -- todo also push / pop
+ metapost.pushscriptrunner(mpx)
+ if trace_graphics then
+ tra = mp_tra[mpx]
+ if not tra then
+ mp_tag = mp_tag + 1
+ local jobname = tex.jobname
+ tra = {
+ inp = io.open(formatters["%s-mplib-run-%03i.mp"] (jobname,mp_tag),"w"),
+ log = io.open(formatters["%s-mplib-run-%03i.log"](jobname,mp_tag),"w"),
+ }
+ mp_tra[mpx] = tra
+ end
+ local banner = makebeginbanner(specification)
+ tra.inp:write(banner)
+ tra.log:write(banner)
+ end
+ local function process(d,i)
+ if d then
+ if trace_graphics then
+ if i then
+ tra.inp:write(formatters["\n%% begin snippet %s\n"](i))
+ end
+ if type(d) == "table" then
+ for i=1,#d do
+ tra.inp:write(d[i])
+ end
+ else
+ tra.inp:write(d)
+ end
+ if i then
+ tra.inp:write(formatters["\n%% end snippet %s\n"](i))
+ end
+ end
+ starttiming(metapost.exectime)
+ result = executempx(mpx,d)
+ stoptiming(metapost.exectime)
+ if trace_graphics and result then
+ local str = result.log or result.error
+ if str and str ~= "" then
+ tra.log:write(str)
+ end
+ end
+ if not metapost.reporterror(result) then
+ if metapost.showlog then
+ -- make function and overload in lmtx
+ local str = result.term ~= "" and result.term or "no terminal output"
+ if not emptystring(str) then
+ metapost.lastlog = metapost.lastlog .. "\n" .. str
+ report_metapost("log: %s",str)
+ end
+ end
+ if result.fig then
+ converted = metapost.convert(specification,result)
+ end
+ end
+ elseif i then
+ report_metapost("error: invalid graphic component %s",i)
+ else
+ report_metapost("error: invalid graphic")
+ end
+ end
+
+-- local data = prepareddata(data)
+ if type(data) == "table" then
+ if trace_tracingall then
+ executempx(mpx,"tracingall;")
+ end
+ process(data)
+-- for i=1,#data do
+-- process(data[i],i)
+-- end
+ else
+ if trace_tracingall then
+ data = "tracingall;" .. data
+ end
+ process(data)
+ end
+ if trace_graphics then
+ local banner = makeendbanner(specification)
+ tra.inp:write(banner)
+ tra.log:write(banner)
+ end
+ stoptiming(metapost)
+ metapost.popscriptrunner()
+ end
+ if mpxdone then
+ metapost.popformat()
+ end
+ return converted, result
+end
+
+if not metapost.convert then
+
+ function metapost.convert()
+ report_metapost('warning: no converter set')
+ end
+
+end
+
+-- This will be redone as we no longer output svg of ps!
+
+-- function metapost.directrun(formatname,filename,outputformat,astable,mpdata)
+-- local fullname = file.addsuffix(filename,"mp")
+-- local data = mpdata or io.loaddata(fullname)
+-- if outputformat ~= "svg" then
+-- outputformat = "mps"
+-- end
+-- if not data then
+-- report_metapost("unknown file %a",filename)
+-- else
+-- local mpx = metapost.checkformat(formatname)
+-- if not mpx then
+-- report_metapost("unknown format %a",formatname)
+-- else
+-- report_metapost("processing %a",(mpdata and (filename or "data")) or fullname)
+-- local result = executempx(mpx,data)
+-- if not result then
+-- report_metapost("error: no result object returned")
+-- elseif result.status > 0 then
+-- report_metapost("error: %s",(result.term or "no-term") .. "\n" .. (result.error or "no-error"))
+-- else
+-- if metapost.showlog then
+-- metapost.lastlog = metapost.lastlog .. "\n" .. result.term
+-- report_metapost("info: %s",result.term or "no-term")
+-- end
+-- local figures = result.fig
+-- if figures then
+-- local sorted = table.sortedkeys(figures)
+-- if astable then
+-- local result = { }
+-- report_metapost("storing %s figures in table",#sorted)
+-- for k=1,#sorted do
+-- local v = sorted[k]
+-- if outputformat == "mps" then
+-- result[v] = figures[v]:postscript()
+-- else
+-- result[v] = figures[v]:svg() -- (3) for prologues
+-- end
+-- end
+-- return result
+-- else
+-- local basename = file.removesuffix(file.basename(filename))
+-- for k=1,#sorted do
+-- local v = sorted[k]
+-- local output
+-- if outputformat == "mps" then
+-- output = figures[v]:postscript()
+-- else
+-- output = figures[v]:svg() -- (3) for prologues
+-- end
+-- local outname = formatters["%s-%s.%s"](basename,v,outputformat)
+-- report_metapost("saving %s bytes in %a",#output,outname)
+-- io.savedata(outname,output)
+-- end
+-- return #sorted
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+
+function metapost.directrun(formatname,filename,outputformat,astable,mpdata)
+ report_metapost("producing postscript and svg is no longer supported")
+end
+
+do
+
+ local result = { }
+ local width = 0
+ local height = 0
+ local depth = 0
+ local bbox = { 0, 0, 0, 0 }
+
+ local flusher = {
+ startfigure = function(n,llx,lly,urx,ury)
+ result = { }
+ width = urx - llx
+ height = ury
+ depth = -lly
+ bbox = { llx, lly, urx, ury }
+ end,
+ flushfigure = function(t)
+ local r = #result
+ for i=1,#t do
+ r = r + 1
+ result[r] = t[i]
+ end
+ end,
+ stopfigure = function()
+ end,
+ }
+
+ -- make table variant:
+
+ function metapost.simple(instance,code,useextensions,dontwrap)
+ -- can we pickup the instance ?
+ local mpx = metapost.pushformat {
+ instance = instance or "simplefun",
+ format = "metafun", -- or: minifun
+ method = "double",
+ }
+-- if not code then
+-- code = ""
+-- end
+ metapost.process {
+ mpx = mpx,
+ flusher = flusher,
+ askedfig = 1,
+ useplugins = useextensions,
+ data = dontwrap and { code } or { "beginfig(1);", code, "endfig;" },
+ incontext = false,
+ }
+ metapost.popformat()
+ if result then
+ local stream = concat(result," ")
+ result = { } -- nil -- cleanup .. weird, we can have a dangling q
+ return stream, width, height, depth, bbox
+ else
+ return "", 0, 0, 0, { 0, 0, 0, 0 }
+ end
+ end
+
+end
+
+local getstatistics = mplib.getstatistics
+
+function metapost.getstatistics(memonly)
+ if memonly then
+ local n, m = 0, 0
+ for name, mpx in next, mpxformats do
+ n = n + 1
+ m = m + getstatistics(mpx).memory
+ end
+ return n, m
+ else
+ local t = { }
+ for name, mpx in next, mpxformats do
+ t[name] = getstatistics(mpx)
+ end
+ return t
+ end
+end
+
+local gethashentries = mplib.gethashentries
+
+function metapost.gethashentries(name,full)
+ if name then
+ local mpx = mpxformats[name] or mpxformats[name .. ":1"]
+ if mpx then
+ return gethashentries(mpx,full)
+ end
+ else
+ local t = { }
+ for name, mpx in next, mpxformats do
+ t[name] = gethashtokens(mpx,full)
+ end
+ return t
+ end
+end
+
+local gethashtokens = mplib.gethashtokens
+
+function metapost.getinstancenames()
+ return sortedkeys(mpxformats)
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-scn.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-scn.lmt
new file mode 100644
index 00000000000..e68abf5873c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-scn.lmt
@@ -0,0 +1,799 @@
+if not modules then modules = { } end modules ['mlib-scn'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- Very experimental, for Alan and me.
+
+-- for i = 1 upto 32000 : % 0.062
+-- ts := 5mm / 20;
+-- endfor ;
+--
+-- for i = 1 upto 32000 : % 0.219
+-- ts := (getparameter "axis" "sy") / 20;
+-- endfor ;
+--
+-- for i = 1 upto 32000 : % 0.266
+-- ts := (getparameterx "axis" "sy") / 20;
+-- endfor ;
+--
+-- pushparameters "axis";
+-- for i = 1 upto 32000 : % 0.250
+-- ts := (getparameterx "sy") / 20;
+-- endfor ;
+-- popparameters;
+
+local type, next, rawget, getmetatable, tonumber = type, next, rawget, getmetatable, tonumber
+local byte, gmatch = string.byte, string.gmatch
+local insert, remove = table.insert, table.remove
+
+local mplib = mplib
+local metapost = metapost
+
+local codes = metapost.codes
+local types = metapost.types
+
+local setmetatableindex = table.setmetatableindex
+
+local scanners = mp.scan
+local injectors = mp.inject
+
+local scannext = scanners.next
+local scanexpression = scanners.expression
+local scantoken = scanners.token
+local scansymbol = scanners.symbol
+local scanproperty = scanners.property
+local scannumeric = scanners.numeric
+local scannumber = scanners.number
+local scaninteger = scanners.integer
+local scanboolean = scanners.boolean
+local scanstring = scanners.string
+local scanpair = scanners.pair
+local scancolor = scanners.color
+local scancmykcolor = scanners.cmykcolor
+local scantransform = scanners.transform
+local scanpath = scanners.path
+local scanpen = scanners.pen
+
+local mpprint = mp.print
+local injectnumeric = injectors.numeric
+local injectstring = injectors.string
+local injectboolean = injectors.boolean
+local injectpair = injectors.pair
+local injecttriplet = injectors.color
+local injectquadruple = injectors.cmykcolor
+local injecttransform = injectors.transform
+local injectpath = injectors.path
+
+local report = logs.reporter("metapost")
+
+local semicolon_code = codes.semicolon
+local equals_code = codes.equals
+local comma_code = codes.comma
+local colon_code = codes.colon
+local leftbrace_code = codes.leftbrace
+local rightbrace_code = codes.rightbrace
+local leftbracket_code = codes.leftbracket
+local rightbracket_code = codes.rightbracket
+local leftdelimiter_code = codes.leftdelimiter
+local rightdelimiter_code = codes.rightdelimiter
+local numeric_code = codes.numeric
+local string_code = codes.string
+local capsule_code = codes.capsule
+local nullary_code = codes.nullary
+local tag_code = codes.tag
+local definedmacro_code = codes.definedmacro
+
+local typescanners = nil
+local tokenscanners = nil
+local scanset = nil
+local scanparameters = nil
+
+scanset = function() -- can be optimized, we now read twice
+ scantoken()
+ if scantoken(true) == rightbrace_code then
+ scantoken()
+ return { }
+ else
+ local l = { }
+ local i = 0
+ while true do
+ i = i + 1
+ local s = scansymbol(true)
+ if s == "{" then
+ l[i] = scanset()
+ elseif s == "[" then
+ local d = { }
+ scansymbol()
+ while true do
+ local s = scansymbol()
+ if s == "]" then
+ break;
+ elseif s == "," then
+ -- continue
+ else
+ local t = scantoken(true)
+ if t == equals_code or t == colon_code then
+ scantoken()
+ end
+ d[s] = tokenscanners[scantoken(true)]()
+ end
+ end
+ l[i] = d
+ else
+ local e = scanexpression(true)
+ l[i] = (typescanners[e] or scanexpression)()
+ end
+ if scantoken() == rightbrace_code then
+ break
+ else
+ -- whatever
+ end
+ end
+ return l
+ end
+end
+
+local function scan_pair () return scanpair (true) end
+local function scan_color () return scancolor (true) end
+local function scan_cmykcolor() return scancmykcolor(true) end
+local function scan_transform() return scantransform(true) end
+
+tokenscanners = {
+ [leftbrace_code] = scanset,
+ [numeric_code] = scannumeric,
+ [string_code] = scanstring,
+ [nullary_code] = scanboolean, -- todo
+}
+
+typescanners = {
+ [types.known] = scannumeric,
+ [types.numeric] = scannumeric,
+ [types.string] = scanstring,
+ [types.boolean] = scanboolean,
+ [types.pair] = scan_pair,
+ [types.color] = scan_color,
+ [types.cmykcolor] = scan_cmykcolor,
+ [types.transform] = scan_transform,
+ [types.path] = scanpath,
+ [types.pen] = scanpen,
+}
+
+table.setmetatableindex(tokenscanners,function()
+ local e = scanexpression(true)
+ return typescanners[e] or scanexpression
+end)
+
+-- a key like 'color' has code 'declare'
+
+local function scanparameters(fenced)
+ local data = { }
+ local close = "]"
+ if not fenced then
+ close = ";"
+ elseif scansymbol(true) == "[" then
+ scansymbol()
+ else
+ return data
+ end
+ while true do
+ -- local s = scansymbol()
+ local s = scansymbol(false,false)
+ if s == close then
+ break;
+ elseif s == "," then
+ -- continue
+ else
+ local t = scantoken(true)
+ if t == equals_code or t == colon_code then
+ -- optional equal or :
+ scantoken()
+ end
+ local kind = scantoken(true)
+ if kind == leftdelimiter_code or kind == tag_code or kind == capsule_code then
+ kind = scanexpression(true)
+ data[s] = (typescanners[kind] or scanexpression)()
+ elseif kind == leftbracket_code then
+ data[s] = get_parameters(true)
+ else
+ data[s] = tokenscanners[kind]()
+ end
+ end
+ end
+ return data
+end
+
+local namespaces = { }
+local presets = { }
+local passed = { }
+
+local function get_parameters(nested)
+ local data = { }
+ if nested or scansymbol(true) == "[" then
+ scansymbol()
+ else
+ return data
+ end
+ while true do
+ local s = scansymbol(false,false)
+ if s == "]" then
+ break;
+ elseif s == "," then
+ -- continue
+ else
+ local t = scantoken(true)
+ if t == equals_code or t == colon_code then
+ -- optional equal or :
+ scantoken()
+ end
+ local kind = scantoken(true)
+ if kind == leftdelimiter_code or kind == tag_code or kind == capsule_code then
+ kind = scanexpression(true)
+ data[s] = (typescanners[kind] or scanexpression)()
+ elseif kind == leftbracket_code then
+ data[s] = get_parameters(true)
+ else
+ data[s] = tokenscanners[kind]()
+ end
+ end
+ end
+ return data
+end
+
+local function getparameters()
+ local namespace = scanstring()
+ -- same as below
+ local parameters = get_parameters()
+ local presets = presets[namespace]
+ local passed = passed[namespace]
+ if passed then
+ if presets then
+ setmetatableindex(passed,presets)
+ end
+ setmetatableindex(parameters,passed)
+ elseif presets then
+ setmetatableindex(parameters,presets)
+ end
+ namespaces[namespace] = parameters
+ --
+end
+
+local function applyparameters()
+ local saved = namespaces
+ local namespace = scanstring()
+ local action = scanstring() -- before we scan the parameters
+ -- same as above
+ local parameters = get_parameters()
+ local presets = presets[namespace]
+ local passed = passed[namespace]
+ if passed then
+ if presets then
+ setmetatableindex(passed,presets)
+ end
+ setmetatableindex(parameters,passed)
+ elseif presets then
+ setmetatableindex(parameters,presets)
+ end
+ namespaces[namespace] = parameters
+ -- till here
+ mpprint(action)
+ namespaces = saved
+end
+
+local knownparameters = { }
+metapost.knownparameters = knownparameters
+
+local function presetparameters()
+ local namespace = scanstring()
+ local parent = nil
+ local t = scantoken(true)
+ if t == string_code then
+ parent = presets[scanstring()]
+ end
+ local p = get_parameters()
+ for k in next, p do
+ knownparameters[k] = true
+ end
+ if parent then
+ setmetatableindex(p,parent)
+ end
+ presets[namespace] = p
+end
+
+local function collectnames()
+ local l = { } -- can be reused but then we can't nest
+ local n = 0
+ while true do
+ local t = scantoken(true)
+ -- (1) not really needed
+ if t == numeric_code then
+ n = n + 1 l[n] = scaninteger(1)
+ elseif t == string_code then
+ n = n + 1 l[n] = scanstring(1)
+ elseif t == nullary_code then
+ n = n + 1 l[n] = scanboolean(1)
+ elseif t == leftbracket_code then
+ scantoken() -- leftbacket
+ n = n + 1 l[n] = scaninteger(1)
+ scantoken() -- rightbacket
+ elseif t == leftdelimiter_code or t == tag_code or t == capsule_code then
+ t = scanexpression(true)
+ n = n + 1 l[n] = (typescanners[t] or scanexpression)()
+ else
+ break
+ end
+ end
+ return l, n
+end
+
+local function get(v)
+ local t = type(v)
+ if t == "number" then
+ return injectnumeric(v)
+ elseif t == "boolean" then
+ return injectboolean(v)
+ elseif t == "string" then
+ return injectstring(v)
+ elseif t == "table" then
+ local n = #v
+ if type(v[1]) == "table" then
+ return injectpath(v)
+ elseif n == 2 then
+ return injectpair(v)
+ elseif n == 3 then
+ return injecttriplet(v)
+ elseif n == 4 then
+ return injectquadruple(v)
+ elseif n == 6 then
+ return injecttransform(v)
+ end
+ end
+ return injectnumeric(0)
+end
+
+local stack = { }
+
+local function pushparameters()
+ local l, n = collectnames()
+ insert(stack,namespaces)
+ for i=1,n do
+ local n = namespaces[l[i]]
+ if type(n) == "table" then
+ namespaces = n
+ else
+ break
+ end
+ end
+end
+
+local function popparameters()
+ local n = remove(stack)
+ if n then
+ namespaces = n
+ else
+ report("stack error")
+ end
+end
+
+-- todo:
+
+local function getparameter()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ if type(l) == "number" then
+ vl = v[1]
+ if vl == nil then
+ return injectnumeric(0)
+ end
+ else
+ return injectnumeric(0)
+ end
+ end
+ v = vl
+ end
+ if v == nil then
+ return injectnumeric(0)
+ else
+ return get(v)
+ end
+end
+
+local function hasparameter()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ local l = list[i]
+ local vl = rawget(v,l)
+ if vl == nil then
+ if type(l) == "number" then
+ vl = rawget(v,1)
+ if vl == nil then
+ return injectboolean(false)
+ end
+ else
+ return injectboolean(false)
+ end
+ end
+ v = vl
+ end
+ if v == nil then
+ return injectboolean(false)
+ else
+ return injectboolean(true)
+ end
+end
+
+local function hasoption()
+ local list, n = collectnames()
+ if n > 1 then
+ local v = namespaces
+ if n > 2 then
+ for i=1,n-1 do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ return injectboolean(false)
+ end
+ v = vl
+ end
+ else
+ v = v[list[1]]
+ end
+ if type(v) == "string" then
+ -- no caching .. slow anyway
+ local o = list[n]
+ if v == o then
+ return injectboolean(true)
+ end
+ for vv in gmatch(v,"[^%s,]+") do
+ for oo in gmatch(o,"[^%s,]+") do
+ if vv == oo then
+ return injectboolean(true)
+ end
+ end
+ end
+ end
+ end
+ return injectboolean(false)
+end
+
+local function getparameterdefault()
+ local list, n = collectnames()
+ local v = namespaces
+ if n == 1 then
+ local l = list[1]
+ local vl = v[l]
+ if vl == nil then
+ -- maybe backtrack
+ local top = stack[#stack]
+ if top then
+ vl = top[l]
+ end
+ end
+ if vl == nil then
+ return injectnumeric(0)
+ else
+ if type(vl) == "string" then
+ local td = type(list[n])
+ if td == "number" then
+ vl = tonumber(vl)
+ elseif td == "boolean" then
+ vl = vl == "true"
+ end
+ end
+ return get(vl)
+ end
+ else
+ for i=1,n-1 do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ if type(l) == "number" then
+ vl = v[1]
+ if vl == nil then
+ return get(list[n])
+ end
+ else
+ local last = list[n]
+ if last == "*" then
+ -- so, only when not pushed
+ local m = getmetatable(namespaces[list[1]])
+ if n then
+ m = m.__index -- can also be a _m_
+ end
+ if m then
+ local v = m
+ for i=2,n-1 do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ return injectnumeric(0)
+ end
+ v = vl
+ end
+ if v == nil then
+ return injectnumeric(0)
+ else
+ return get(v)
+ end
+ end
+ return injectnumeric(0)
+ else
+ return get(last)
+ end
+ end
+ end
+ v = vl
+ end
+ if v == nil then
+ return get(list[n])
+ else
+ if type(v) == "string" then
+ local td = type(list[n])
+ if td == "number" then
+ v = tonumber(v)
+ elseif td == "boolean" then
+ v = v == "true"
+ end
+ end
+ return get(v)
+ end
+ end
+end
+
+local function getparametercount()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ return injectnumeric(type(v) == "table" and #v or 0)
+end
+
+local function getmaxparametercount()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ local n = 0
+ if type(v) == "table" then
+ local v1 = v[1]
+ if type(v1) == "table" then
+ n = #v1
+ for i=2,#v do
+ local vi = v[i]
+ if type(vi) == "table" then
+ local vn = #vi
+ if vn > n then
+ n = vn
+ end
+ else
+ break;
+ end
+ end
+ end
+
+ end
+ return injectnumeric(n)
+end
+
+local validconnectors = {
+ [".."] = true,
+ ["..."] = true,
+ ["--"] = true,
+}
+
+local function getparameterpath()
+ local list, n = collectnames()
+ local close = list[n]
+ if type(close) == "boolean" then
+ n = n - 1
+ else
+ close = false
+ end
+ local connector = list[n]
+ if type(connector) == "string" and validconnectors[connector] then
+ n = n - 1
+ else
+ connector = "--"
+ end
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "table" then
+ return injectpath(v,connector,close)
+ else
+ return injectpair(0,0)
+ end
+end
+
+local function getparameterpen()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "table" then
+ return injectpath(v,"..",true)
+ else
+ return injectpair(0,0)
+ end
+end
+
+local function getparametertext()
+ local list, n = collectnames()
+ local strut = list[n]
+ if type(strut) == "boolean" then
+ n = n - 1
+ else
+ strut = false
+ end
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "string" then
+ return injectstring("\\strut " .. v)
+ else
+ return injectstring("")
+ end
+end
+
+-- local function getparameteroption()
+-- local list, n = collectnames()
+-- local last = list[n]
+-- if type(last) == "string" then
+-- n = n - 1
+-- else
+-- return false
+-- end
+-- local v = namespaces
+-- for i=1,n do
+-- v = v[list[i]]
+-- if not v then
+-- break
+-- end
+-- end
+-- if type(v) == "string" and v ~= "" then
+-- for s in gmatch(v,"[^ ,]+") do
+-- if s == last then
+-- return true
+-- end
+-- end
+-- end
+-- return false
+-- end
+
+function metapost.scanparameters()
+-- scantoken() -- we scan the semicolon
+ return get_parameters()
+end
+
+local registerscript = metapost.registerscript
+
+registerscript("getparameters", getparameters)
+registerscript("applyparameters", applyparameters)
+registerscript("presetparameters", presetparameters)
+registerscript("hasparameter", hasparameter)
+registerscript("hasoption", hasoption)
+registerscript("getparameter", getparameter)
+registerscript("getparameterdefault", getparameterdefault)
+registerscript("getparametercount", getparametercount)
+registerscript("getmaxparametercount",getmaxparametercount)
+registerscript("getparameterpath", getparameterpath)
+registerscript("getparameterpen", getparameterpen)
+registerscript("getparametertext", getparametertext)
+--------------("getparameteroption", getparameteroption)
+registerscript("pushparameters", pushparameters)
+registerscript("popparameters", popparameters)
+
+function metapost.getparameter(list)
+ local n = #list
+ local v = namespaces
+ for i=1,n do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ return
+ end
+ v = vl
+ end
+ return v
+end
+
+function metapost.getparameterset(namespace)
+ return namespace and namespaces[namespace] or namespaces
+end
+
+function metapost.setparameterset(namespace,t)
+ namespaces[namespace] = t
+end
+
+-- goodies
+
+registerscript("definecolor", function()
+ scantoken() -- we scan the semicolon
+ local s = get_parameters()
+ attributes.colors.defineprocesscolordirect(s)
+end)
+
+-- tex scanners
+
+local scanners = tokens.scanners
+local scanhash = scanners.hash
+local scanstring = scanners.string
+local scanvalue = scanners.value
+local scaninteger = scanners.integer
+local scanboolean = scanners.boolean
+local scanfloat = scanners.float
+local scandimension = scanners.dimension
+
+local definitions = { }
+
+local bpfactor = number.dimenfactors.bp
+local comma = byte(",")
+local close = byte("]")
+
+local scanrest = function() return scanvalue(comma,close) or "" end
+local scandimension = function() return scandimension() * bpfactor end
+
+local scanners = {
+ ["integer"] = scaninteger,
+ ["number"] = scanfloat,
+ ["numeric"] = scanfloat,
+ ["boolean"] = scanboolean,
+ ["string"] = scanrest,
+ ["dimension"] = scandimension,
+}
+
+interfaces.implement {
+ name = "lmt_parameters_define",
+ arguments = "string",
+ actions = function(namespace)
+ local d = scanhash()
+ for k, v in next, d do
+ d[k] = scanners[v] or scanrest
+ end
+ definitions[namespace] = d
+ end,
+}
+
+interfaces.implement {
+ name = "lmt_parameters_preset",
+ arguments = "string",
+ actions = function(namespace)
+ passed[namespace] = scanhash(definitions[namespace])
+ end,
+}
+
+interfaces.implement {
+ name = "lmt_parameters_reset",
+ arguments = "string",
+ actions = function(namespace)
+ passed[namespace] = nil
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mlib-svg.lmt b/Master/texmf-dist/tex/context/base/mkxl/mlib-svg.lmt
new file mode 100644
index 00000000000..dca2b6d191d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mlib-svg.lmt
@@ -0,0 +1,3333 @@
+if not modules then modules = { } end modules ['mlib-svg'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- Just a few notes:
+--
+-- There is no real need to boost performance here .. we can always make a fast
+-- variant when really needed. I will also do some of the todo's when I run into
+-- proper fonts. I need to optimize this a bit but will do that once I'm satisfied
+-- with the outcome and don't need more hooks and plugs. At some point I will
+-- optimize the MetaPost part because now we probably have more image wrapping
+-- than needed.
+--
+-- As usual with these standards, things like a path can be very compact while the
+-- rest is very verbose which defeats the point. This is a first attempt. There will
+-- be a converter to MP as well as directly to PDF. This module was made for one of
+-- the dangerous curves talks at the 2019 CTX meeting. I will do the font when I
+-- need it (not that hard).
+--
+-- The fact that in the more recent versions of SVG the older text related elements
+-- are depricated and not even supposed to be supported, combined with the fact that
+-- the text element assumes css styling, demonstrates that there is not so much as a
+-- standard. It basically means that whatever technology dominates at some point
+-- (probably combined with some libraries that at that point exist) determine what
+-- is standard. Anyway, it probably also means that these formats are not that
+-- suitable for long term archival purposes. So don't take the next implementation
+-- too serious. So in the end we now have (1) attributes for properties (which is
+-- nice and clean and what attributes are for, (2) a style attribute that needs to
+-- be parsed, (3) classes that map to styles and (4) element related styles, plus a
+-- kind of inheritance (given the limited number of elements sticking to only <g> as
+-- wrapper would have made much sense. Anyway, we need to deal with it. With all
+-- these style things going on, one can wonder where it will end. Basically svg
+-- became just a html element that way and less clean too. The same is true for
+-- tspan, which means that text itself is nested xml.
+--
+-- We can do a direct conversion to PDF but then we also loose the abstraction which
+-- in the future will be used, and for fonts we need to spawn out to TeX anyway, so
+-- the little overhead of calling MetaPost is okay I guess. Also, we want to
+-- overload labels, share fonts with the main document, etc. and are not aiming at a
+-- general purpose SVG converter. For going to PDF one can just use InkScape.
+--
+-- Written with Anne Clark on speakers as distraction.
+--
+-- Todo when I run into an example (but ony when needed and reasonable):
+--
+-- var(color,color)
+-- --color<decimal>
+-- currentColor : when i run into an example
+-- a bit more shading
+-- clip = [ auto | rect(llx,lly,urx,ury) ] (in svg)
+-- xlink url ... whatever
+-- masks
+-- opacity per group (i need to add that to metafun first, inefficient pdf but
+-- maybe filldraw can help here)
+--
+-- Maybe in metafun:
+--
+-- penciled n -> withpen pencircle scaled n
+-- applied (...) -> transformed bymatrix (...)
+-- withopacity n -> withtransparency (1,n)
+
+-- When testing mbo files:
+--
+-- empty paths
+-- missing control points
+-- funny fontnames like abcdefverdana etc
+-- paths representing glyphs but also with style specs
+-- all kind of attributes
+-- very weird and inefficient shading
+
+-- One can run into pretty crazy images, like lines that are fills being clipped
+-- to some width. That's the danger of hiding yourself behind an interface I guess.
+
+local rawget, rawset, type, tonumber, tostring, next, setmetatable = rawget, rawset, type, tonumber, tostring, next, setmetatable
+
+local P, S, R, C, Ct, Cs, Cc, Cp, Cg, Cf, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Cg, lpeg.Cf, lpeg.Carg
+
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local sqrt, abs = math.sqrt, math.abs
+local concat, setmetatableindex, sortedhash = table.concat, table.setmetatableindex, table.sortedhash
+local gmatch, gsub, find, match = string.gmatch, string.gsub, string.find, string.match
+local formatters, fullstrip = string.formatters, string.fullstrip
+local utfsplit, utfbyte = utf.split, utf.byte
+
+local xmlconvert, xmlcollected, xmlcount, xmlfirst, xmlroot = xml.convert, xml.collected, xml.count, xml.first, xml.root
+local xmltext, xmltextonly = xml.text, xml.textonly
+local css = xml.css or { } -- testing
+
+local function xmlinheritattributes(c,pa)
+ local at = c.at
+ local dt = c.dt
+ if at and dt then
+ if pa then
+ setmetatableindex(at,pa)
+ end
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "table" then
+ xmlinheritattributes(dti,at)
+ end
+ end
+ else
+ -- comment of so
+ end
+end
+
+xml.inheritattributes = xmlinheritattributes
+
+-- Maybe some day helpers will move to the metapost.svg namespace!
+
+metapost = metapost or { }
+local metapost = metapost
+local context = context
+
+local report = logs.reporter("metapost","svg")
+
+local trace = false trackers.register("metapost.svg", function(v) trace = v end)
+local trace_text = false trackers.register("metapost.svg.text", function(v) trace_text = v end)
+local trace_path = false trackers.register("metapost.svg.path", function(v) trace_path = v end)
+local trace_result = false trackers.register("metapost.svg.result", function(v) trace_result = v end)
+local trace_colors = false trackers.register("metapost.svg.colors", function(v) trace_colors = v end)
+local trace_fonts = false trackers.register("metapost.svg.fonts", function(v) trace_fonts = v end)
+
+-- This is just an experiment. Todo: reset hash etc. Also implement
+-- an option handler.
+
+local svghash = false do
+
+ local svglast = 0
+ local svglist = false
+
+ local function checkhash(t,k)
+ local n = svglast + 1
+ svglast = n
+ svglist[n] = k
+ t[k] = n
+ return n
+ end
+
+ function metapost.startsvghashing()
+ svglast = 0
+ svglist = { }
+ svghash = setmetatableindex(checkhash)
+ end
+
+ function metapost.stopsvghashing()
+ svglast = 0
+ svglist = false
+ svghash = false
+ end
+
+ interfaces.implement {
+ name = "svghashed",
+ arguments = "integer",
+ actions = function(n)
+ local t = svglist and svglist[n]
+ if t then
+ context(t)
+ end
+ end
+ }
+
+end
+
+-- We have quite some closures because otherwise we run into the local variable
+-- limitations. It doesn't always look pretty now, sorry. I'll clean up this mess
+-- some day (the usual nth iteration of code).
+--
+-- Most of the conversion is rather trivial code till I ran into a file with arcs. A
+-- bit of searching lead to the a2c javascript function but it has some puzzling
+-- thingies (like sin and cos definitions that look like leftovers and possible
+-- division by zero). Anyway, we can if needed optimize it a bit more. Here does it
+-- come from:
+
+-- http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+-- https://github.com/adobe-webplatform/Snap.svg/blob/b242f49e6798ac297a3dad0dfb03c0893e394464/src/path.js
+
+local a2c do
+
+ local pi, sin, cos, tan, asin, abs = math.pi, math.sin, math.cos, math.tan, math.asin, math.abs
+
+ local d120 = (pi * 120) / 180
+ local pi2 = 2 * pi
+
+ a2c = function(x1, y1, rx, ry, angle, large, sweep, x2, y2, f1, f2, cx, cy)
+
+ if (rx == 0 or ry == 0 ) or (x1 == x2 and y1 == y2) then
+ return { x1, y1, x2, y2, x2, y2 }
+ end
+
+ local recursive = f1
+ local rad = pi / 180 * angle
+ local res = nil
+ local cosrad = cos(-rad) -- local cosrad = cosd(angle)
+ local sinrad = sin(-rad) -- local sinrad = sind(angle)
+
+ if not recursive then
+
+ x1, y1 = x1 * cosrad - y1 * sinrad, x1 * sinrad + y1 * cosrad
+ x2, y2 = x2 * cosrad - y2 * sinrad, x2 * sinrad + y2 * cosrad
+
+ local x = (x1 - x2) / 2
+ local y = (y1 - y2) / 2
+ local xx = x * x
+ local yy = y * y
+ local h = xx / (rx * rx) + yy / (ry * ry)
+
+ if h > 1 then
+ h = sqrt(h)
+ rx = h * rx
+ ry = h * ry
+ end
+
+ local rx2 = rx * rx
+ local ry2 = ry * ry
+ local ry2xx = ry2 * xx
+ local rx2yy = rx2 * yy
+ local total = rx2yy + ry2xx -- otherwise overflow
+
+ local k = total == 0 and 0 or sqrt(abs((rx2 * ry2 - rx2yy - ry2xx) / total))
+
+ if large == sweep then
+ k = -k
+ end
+
+ cx = k * rx * y / ry + (x1 + x2) / 2
+ cy = k * -ry * x / rx + (y1 + y2) / 2
+
+ f1 = (y1 - cy) / ry -- otherwise crash on a tiny eps
+ f2 = (y2 - cy) / ry -- otherwise crash on a tiny eps
+
+ f1 = asin((f1 < -1.0 and -1.0) or (f1 > 1.0 and 1.0) or f1)
+ f2 = asin((f2 < -1.0 and -1.0) or (f2 > 1.0 and 1.0) or f2)
+
+ if x1 < cx then f1 = pi - f1 end
+ if x2 < cx then f2 = pi - f2 end
+
+ if f1 < 0 then f1 = pi2 + f1 end
+ if f2 < 0 then f2 = pi2 + f2 end
+
+ if sweep ~= 0 and f1 > f2 then f1 = f1 - pi2 end
+ if sweep == 0 and f2 > f1 then f2 = f2 - pi2 end
+
+ end
+
+ if abs(f2 - f1) > d120 then
+ local f2old = f2
+ local x2old = x2
+ local y2old = y2
+ f2 = f1 + d120 * ((sweep ~= 0 and f2 > f1) and 1 or -1)
+ x2 = cx + rx * cos(f2)
+ y2 = cy + ry * sin(f2)
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep, x2old, y2old, f2, f2old, cx, cy)
+ end
+
+ local c1 = cos(f1)
+ local s1 = sin(f1)
+ local c2 = cos(f2)
+ local s2 = sin(f2)
+
+ local t = tan((f2 - f1) / 4)
+ local hx = 4 * rx * t / 3
+ local hy = 4 * ry * t / 3
+
+ local r = { x1 - hx * s1, y1 + hy * c1, x2 + hx * s2, y2 - hy * c2, x2, y2, unpack(res or { }) }
+
+ if not recursive then -- we can also check for sin/cos being 0/1
+ cosrad = cos(rad)
+ sinrad = sin(rad)
+ -- cosrad = cosd(angle)
+ -- sinrad = sind(angle)
+ for i0=1,#r,2 do
+ local i1 = i0 + 1
+ local x = r[i0]
+ local y = r[i1]
+ r[i0] = x * cosrad - y * sinrad
+ r[i1] = x * sinrad + y * cosrad
+ end
+ end
+
+ return r
+ end
+
+end
+
+-- We share some patterns.
+
+local p_digit = lpegpatterns.digit
+local p_hexdigit = lpegpatterns.hexdigit
+local p_space = lpegpatterns.whitespace
+
+local factors = {
+ ["pt"] = 1.25,
+ ["mm"] = 3.543307,
+ ["cm"] = 35.43307,
+ ["px"] = 1,
+ ["pc"] = 15,
+ ["in"] = 90,
+ ["em"] = 12 * 1.25,
+ ["ex"] = 8 * 1.25,
+}
+
+metapost.svgfactors = factors
+
+local percentage_r = 1/100
+local percentage_x = percentage_r
+local percentage_y = percentage_r
+
+local asnumber, asnumber_r, asnumber_x, asnumber_y, asnumber_vx, asnumber_vy
+local asnumber_vx_t, asnumber_vy_t
+local p_number, p_separator, p_optseparator, p_numbers, p_fournumbers, p_path
+local p_number_n, p_number_x, p_number_vx, p_number_y, p_number_vy, p_number_r
+
+do
+
+ -- incredible: we can find .123.456 => 0.123 0.456 ...
+
+ local p_command_x = C(S("Hh"))
+ local p_command_y = C(S("Vv"))
+ local p_command_xy = C(S("CcLlMmQqSsTt"))
+ local p_command_a = C(S("Aa"))
+ local p_command = C(S("Zz"))
+
+ p_optseparator = S("\t\n\r ,")^0
+ p_separator = S("\t\n\r ,")^1
+ p_number = (S("+-")^0 * (p_digit^0 * P(".") * p_digit^1 + p_digit^1 * P(".") + p_digit^1))
+ * (P("e") * S("+-")^0 * p_digit^1)^-1
+
+ local function convert (n) n = tonumber(n) return n end
+ local function convert_r (n,u) n = tonumber(n) if u == true then return percentage_r * n elseif u then return u * n else return n end end
+ local function convert_x (n,u) n = tonumber(n) if u == true then return percentage_x * n elseif u then return u * n else return n end end
+ local function convert_y (n,u) n = tonumber(n) if u == true then return percentage_y * n elseif u then return u * n else return n end end
+ local function convert_vx(n,u) n = tonumber(n) if u == true then return percentage_x * n elseif u then return u * n else return n end end
+ local function convert_vy(n,u) n = - tonumber(n) if u == true then return percentage_y * n elseif u then return u * n else return n end end
+
+ local p_unit = (P("p") * S("txc") + P("e") * S("xm") + S("mc") * P("m") + P("in")) / factors
+ local p_percent = P("%") * Cc(true)
+
+ local c_number_n = C(p_number)
+ local c_number_u = C(p_number) * (p_unit + p_percent)^-1
+
+ p_number_n = c_number_n / convert
+ p_number_x = c_number_u / convert_x
+ p_number_vx = c_number_u / convert_vx
+ p_number_y = c_number_u / convert_y
+ p_number_vy = c_number_u / convert_vy
+ p_number_r = c_number_u / convert_r
+
+ asnumber = function(s) return s and lpegmatch(p_number, s) or 0 end
+ asnumber_r = function(s) return s and lpegmatch(p_number_r, s) or 0 end
+ asnumber_x = function(s) return s and lpegmatch(p_number_x, s) or 0 end
+ asnumber_y = function(s) return s and lpegmatch(p_number_y, s) or 0 end
+ asnumber_vx = function(s) return s and lpegmatch(p_number_vx,s) or 0 end
+ asnumber_vy = function(s) return s and lpegmatch(p_number_vy,s) or 0 end
+
+ local p_number_vx_t = Ct { (p_number_vx + p_separator)^1 }
+ local p_number_vy_t = Ct { (p_number_vy + p_separator)^1 }
+
+ local zerotable = { 0 }
+
+ asnumber_vx_t = function(s) return s and lpegmatch(p_number_vx_t,s) or zerotable end
+ asnumber_vy_t = function(s) return s and lpegmatch(p_number_vy_t,s) or zerotable end
+
+ local p_numbersep = p_number_n + p_separator
+ p_numbers = p_optseparator * P("(") * p_numbersep^0 * p_optseparator * P(")")
+ p_fournumbers = p_numbersep^4
+ p_path = Ct ( (
+ p_command_xy * (p_optseparator * p_number_vx *
+ p_optseparator * p_number_vy )^1
+ + p_command_x * (p_optseparator * p_number_vx )^1
+ + p_command_y * (p_optseparator * p_number_vy )^1
+ + p_command_a * (p_optseparator * p_number_vx *
+ p_optseparator * p_number_vy *
+ p_optseparator * p_number_r *
+ p_optseparator * p_number_n * -- flags
+ p_optseparator * p_number_n * -- flags
+ p_optseparator * p_number_vx *
+ p_optseparator * p_number_vy )^1
+ + p_command
+ + p_separator
+ )^1 )
+
+
+end
+
+-- We can actually use the svg color definitions from the tex end but maybe a user
+-- doesn't want those replace the normal definitions.
+--
+-- local hexhash = setmetatableindex(function(t,k) local v = lpegmatch(p_hexcolor, k) t[k] = v return v end) -- per file
+-- local hexhash3 = setmetatableindex(function(t,k) local v = lpegmatch(p_hexcolor3,k) t[k] = v return v end) -- per file
+--
+-- local function hexcolor (c) return hexhash [c] end -- directly do hexhash [c]
+-- local function hexcolor3(c) return hexhash3[c] end -- directly do hexhash3[c]
+
+local colormap = false
+
+local function prepared(t)
+ if type(t) == "table" then
+ local mapping = t.mapping or { }
+ local mapper = t.mapper
+ local colormap = setmetatableindex(mapping)
+ if mapper then
+ setmetatableindex(colormap,function(t,k)
+ local v = mapper(k)
+ t[k] = v or k
+ return v
+ end)
+ end
+ return colormap
+ else
+ return false
+ end
+end
+
+local colormaps = setmetatableindex(function(t,k)
+ local v = false
+ if type(k) == "string" then
+ v = prepared(table.load(k)) -- todo: same path as svg file
+ elseif type(k) == "table" then
+ v = prepared(k)
+ k = k.name or k
+ end
+ t[k] = v
+ return v
+end)
+
+function metapost.svgcolorremapper(colormap)
+ return colormaps[colormap]
+end
+
+-- todo: cache colors per image / remapper
+
+local colorcomponents, withcolor, thecolor, usedcolors do
+
+ local svgcolors = {
+ aliceblue = 0xF0F8FF, antiquewhite = 0xFAEBD7, aqua = 0x00FFFF, aquamarine = 0x7FFFD4,
+ azure = 0xF0FFFF, beige = 0xF5F5DC, bisque = 0xFFE4C4, black = 0x000000,
+ blanchedalmond = 0xFFEBCD, blue = 0x0000FF, blueviolet = 0x8A2BE2, brown = 0xA52A2A,
+ burlywood = 0xDEB887, cadetblue = 0x5F9EA0, hartreuse = 0x7FFF00, chocolate = 0xD2691E,
+ coral = 0xFF7F50, cornflowerblue = 0x6495ED, cornsilk = 0xFFF8DC, crimson = 0xDC143C,
+ cyan = 0x00FFFF, darkblue = 0x00008B, darkcyan = 0x008B8B, darkgoldenrod = 0xB8860B,
+ darkgray = 0xA9A9A9, darkgreen = 0x006400, darkgrey = 0xA9A9A9, darkkhaki = 0xBDB76B,
+ darkmagenta = 0x8B008B, darkolivegreen = 0x556B2F, darkorange = 0xFF8C00, darkorchid = 0x9932CC,
+ darkred = 0x8B0000, darksalmon = 0xE9967A, darkseagreen = 0x8FBC8F, darkslateblue = 0x483D8B,
+ darkslategray = 0x2F4F4F, darkslategrey = 0x2F4F4F, darkturquoise = 0x00CED1, darkviolet = 0x9400D3,
+ deeppink = 0xFF1493, deepskyblue = 0x00BFFF, dimgray = 0x696969, dimgrey = 0x696969,
+ dodgerblue = 0x1E90FF, firebrick = 0xB22222, floralwhite = 0xFFFAF0, forestgreen = 0x228B22,
+ fuchsia = 0xFF00FF, gainsboro = 0xDCDCDC, ghostwhite = 0xF8F8FF, gold = 0xFFD700,
+ goldenrod = 0xDAA520, gray = 0x808080, green = 0x008000, greenyellow = 0xADFF2F,
+ grey = 0x808080, honeydew = 0xF0FFF0, hotpink = 0xFF69B4, indianred = 0xCD5C5C,
+ indigo = 0x4B0082, ivory = 0xFFFFF0, khaki = 0xF0E68C, lavender = 0xE6E6FA,
+ lavenderblush = 0xFFF0F5, lawngreen = 0x7CFC00, lemonchiffon = 0xFFFACD, lightblue = 0xADD8E6,
+ lightcoral = 0xF08080, lightcyan = 0xE0FFFF, lightgoldenrodyellow = 0xFAFAD2, lightgray = 0xD3D3D3,
+ lightgreen = 0x90EE90, lightgrey = 0xD3D3D3, lightpink = 0xFFB6C1, lightsalmon = 0xFFA07A,
+ lightseagreen = 0x20B2AA, lightskyblue = 0x87CEFA, lightslategray = 0x778899, lightslategrey = 0x778899,
+ lightsteelblue = 0xB0C4DE, lightyellow = 0xFFFFE0, lime = 0x00FF00, limegreen = 0x32CD32,
+ linen = 0xFAF0E6, magenta = 0xFF00FF, maroon = 0x800000, mediumaquamarine = 0x66CDAA,
+ mediumblue = 0x0000CD, mediumorchid = 0xBA55D3, mediumpurple = 0x9370DB, mediumseagreen = 0x3CB371,
+ mediumslateblue = 0x7B68EE, mediumspringgreen = 0x00FA9A, mediumturquoise = 0x48D1CC, mediumvioletred = 0xC71585,
+ midnightblue = 0x191970, mintcream = 0xF5FFFA, mistyrose = 0xFFE4E1, moccasin = 0xFFE4B5,
+ navajowhite = 0xFFDEAD, navy = 0x000080, oldlace = 0xFDF5E6, olive = 0x808000,
+ olivedrab = 0x6B8E23, orange = 0xFFA500, orangered = 0xFF4500, orchid = 0xDA70D6,
+ palegoldenrod = 0xEEE8AA, palegreen = 0x98FB98, paleturquoise = 0xAFEEEE, palevioletred = 0xDB7093,
+ papayawhip = 0xFFEFD5, peachpuff = 0xFFDAB9, peru = 0xCD853F, pink = 0xFFC0CB,
+ plum = 0xDDA0DD, powderblue = 0xB0E0E6, purple = 0x800080, red = 0xFF0000,
+ rosybrown = 0xBC8F8F, royalblue = 0x4169E1, saddlebrown = 0x8B4513, salmon = 0xFA8072,
+ sandybrown = 0xF4A460, seagreen = 0x2E8B57, seashell = 0xFFF5EE, sienna = 0xA0522D,
+ silver = 0xC0C0C0, skyblue = 0x87CEEB, slateblue = 0x6A5ACD, slategray = 0x708090,
+ slategrey = 0x708090, snow = 0xFFFAFA, springgreen = 0x00FF7F, steelblue = 0x4682B4,
+ tan = 0xD2B48C, teal = 0x008080, thistle = 0xD8BFD8, tomato = 0xFF6347,
+ turquoise = 0x40E0D0, violet = 0xEE82EE, wheat = 0xF5DEB3, white = 0xFFFFFF,
+ whitesmoke = 0xF5F5F5, yellow = 0xFFFF00, yellowgreen = 0x9ACD32,
+ }
+
+ local f_rgb = formatters['withcolor svgcolor(%.3N,%.3N,%.3N)']
+ local f_cmyk = formatters['withcolor svgcmyk(%.3N,%.3N,%.3N,%.3N)']
+ local f_gray = formatters['withcolor svggray(%.3N)']
+ local f_rgba = formatters['withcolor svgcolor(%.3N,%.3N,%.3N) withtransparency (1,%.3N)']
+ local f_graya = formatters['withcolor svggray(%.3N) withtransparency (1,%.3N)']
+ local f_name = formatters['withcolor "%s"']
+ local f_svgrgb = formatters['svgcolor(%.3N,%.3N,%.3N)']
+ local f_svgcmyk = formatters['svgcmyk(%.3N,%.3N,%.3N,%.3N)']
+ local f_svggray = formatters['svggray(%.3N)']
+ local f_svgname = formatters['"%s"']
+
+ local extract = bit32.extract
+
+ local triplets = setmetatableindex(function(t,k)
+ -- we delay building all these strings
+ local v = svgcolors[k]
+ if v then
+ v = { extract(v,16,8)/255, extract(v,8,8)/255, extract(v,0,8)/255 }
+ else
+ v = false
+ end
+ t[k] = v
+ return v
+ end)
+
+ local p_fraction = C(p_number) * C("%")^-1 / function(a,b) return tonumber(a) / (b and 100 or 255) end
+ local p_angle = C(p_number) * P("deg")^0 / function(a) return tonumber(a) end
+ local p_percent = C(p_number) * P("%") / function(a) return tonumber(a) / 100 end
+ local p_absolute = C(p_number) / tonumber
+
+ local p_left = P("(")
+ local p_right = P(")")
+ local p_a = P("a")^-1
+ local p_h_a_color = p_left
+ * p_angle
+ * p_separator * p_percent
+ * p_separator * p_percent
+ * p_separator^0 * p_absolute^0
+ * p_right
+
+ local colors = attributes.colors
+ local colorvalues = colors.values
+ local colorindex = attributes.list[attributes.private('color')]
+ local hsvtorgb = colors.hsvtorgb
+ local hwbtorgb = colors.hwbtorgb
+ local forcedmodel = colors.forcedmodel
+
+ local p_splitcolor =
+ P("#") * C(p_hexdigit*p_hexdigit)^1 / function(r,g,b)
+ if not r then
+ return "gray", 0
+ elseif not (g and b) then
+ return "gray", tonumber(r or "0", 16) / 255 or 0
+ else
+ return "rgb",
+ tonumber(r or "0", 16) / 255 or 0,
+ tonumber(g or "0", 16) / 255 or 0,
+ tonumber(b or "0", 16) / 255 or 0
+ end
+ end
+ + P("rgb") * p_a
+ * p_left * (p_fraction + p_separator)^-3 * (p_absolute + p_separator)^0 * p_right / function(r,g,b,a)
+ return "rgb", r or 0, g or 0, b or 0, a or false
+ end
+ + P("cmyk")
+ * p_left * (p_absolute + p_separator)^0 * p_right / function(c,m,y,k)
+ return "cmyk", c or 0, m or 0, y or 0, k or 0
+ end
+ + P("hsl") * p_a
+ * p_h_a_color / function(h,s,l,a)
+ local r, g, b = hsvtorgb(h,s,l,a)
+ return "rgb", r or 0, g or 0, b or 0, a or false
+ end
+ + P("hwb") * p_a
+ * p_h_a_color / function(h,w,b,a)
+ local r, g, b = hwbtorgb(h,w,b)
+ return "rgb", r or 0, g or 0, b or 0, a or false
+ end
+
+ function metapost.svgsplitcolor(color)
+ if type(color) == "string" then
+ local what, s1, s2, s3, s4 = lpegmatch(p_splitcolor,color)
+ if not what then
+ local t = triplets[color]
+ if t then
+ what, s1, s2, s3 = "rgb", t[1], t[2], t[3]
+ end
+ end
+ return what, s1, s2, s3, s4
+ else
+ return "gray", 0, false
+ end
+ end
+
+ local function registeredcolor(name)
+ local color = colorindex[name]
+ if color then
+ local v = colorvalues[color]
+ local t = forcedmodel(v[1])
+ if t == 2 then
+ return "gray", v[2]
+ elseif t == 3 then
+ return "rgb", v[3], v[4], v[5]
+ elseif t == 4 then
+ return "cmyk", v[6], v[7], v[8], v[9]
+ else
+ --
+ end
+ end
+ end
+
+ -- we can have a fast check for #000000
+
+ local function validcolor(color)
+ if usedcolors then
+ usedcolors[color] = usedcolors[color] + 1
+ end
+ if colormap then
+ local c = colormap[color]
+ local t = type(c)
+ if t == "table" then
+ local what = t[1]
+ if what == "rgb" then
+ return
+ what,
+ tonumber(t[2]) or 0,
+ tonumber(t[3]) or 0,
+ tonumber(t[4]) or 0,
+ tonumber(t[4]) or false
+ elseif what == "cmyk" then
+ return
+ what,
+ tonumber(t[2]) or 0,
+ tonumber(t[3]) or 0,
+ tonumber(t[4]) or 0,
+ tonumber(t[5]) or 0
+ elseif what == "gray" then
+ return
+ what,
+ tonumber(t[2]) or 0,
+ tonumber(t[3]) or false
+ end
+ elseif t == "string" then
+ color = c
+ end
+ end
+ local what, s1, s2, s3, s4 = registeredcolor(color)
+ if what then
+ return what, s1, s2, s3, s4
+ end
+ what, s1, s2, s3, s4 = lpegmatch(p_splitcolor,color)
+ if not what then
+ local t = triplets[color]
+ if t then
+ s1, s3, s3 = t[1], t[2], t[3]
+ what = "rgb"
+ end
+ end
+ return what, s1, s2, s3, s4
+ end
+
+ colorcomponents = function(color)
+ local what, s1, s2, s3, s4 = validcolor(color)
+ return s1, s2, s3, s4 -- so 4 means cmyk
+ end
+
+ withcolor = function(color)
+ local what, s1, s2, s3, s4 = validcolor(color)
+ -- print(color,what, s1, s2, s3, s4)
+ if what == "rgb" then
+ if s4 then
+ if s1 == s2 and s1 == s3 then
+ return f_graya(s1,s4)
+ else
+ return f_rgba(s1,s2,s3,s4)
+ end
+ else
+ if s1 == s2 and s1 == s3 then
+ return f_gray(s1)
+ else
+ return f_rgb(s1,s2,s3)
+ end
+ end
+ elseif what == "cmyk" then
+ return f_cmyk(s1,s2,s3,s4)
+ elseif what == "gray" then
+ if s2 then
+ return f_graya(s1,s2)
+ else
+ return f_gray(s1)
+ end
+ end
+ return f_name(color)
+ end
+
+ thecolor = function(color)
+ local what, s1, s2, s3, s4 = validcolor(color)
+ if what == "rgb" then
+ if s4 then
+ if s1 == s2 and s1 == s3 then
+ return f_svggraya(s1,s4)
+ else
+ return f_svgrgba(s1,s2,s3,s4)
+ end
+ else
+ if s1 == s2 and s1 == s3 then
+ return f_svggray(s1)
+ else
+ return f_svgrgb(s1,s2,s3)
+ end
+ end
+ elseif what == "cmyk" then
+ return f_cmyk(s1,s2,s3,s4)
+ elseif what == "gray" then
+ if s2 then
+ return f_svggraya(s1,s2)
+ else
+ return f_svggray(s1)
+ end
+ end
+ return f_svgname(color)
+ end
+
+end
+
+-- actually we can loop faster because we can go to the last one
+
+local grabpath, grablist do
+
+ local f_moveto = formatters['(%N,%N)']
+ local f_curveto_z = formatters['controls(%N,%N)and(%N,%N)..(%N,%N)']
+ local f_curveto_n = formatters['..controls(%N,%N)and(%N,%N)..(%N,%N)']
+ local f_lineto_z = formatters['(%N,%N)']
+ local f_lineto_n = formatters['--(%N,%N)']
+
+ local m = { __index = function() return 0 end }
+
+ grabpath = function(str)
+ local p = lpegmatch(p_path,str) or { }
+ local np = #p
+ local all = { entries = np, closed = false, curve = false }
+ if np == 0 then
+ return all
+ end
+ setmetatable(p,m)
+ local t = { } -- no real saving here if we share
+ local n = 0
+ local a = 0
+ local i = 0
+ local last = "M"
+ local prev = last
+ local kind = "L"
+ local x, y = 0, 0
+ local x1, y1 = 0, 0
+ local x2, y2 = 0, 0
+ local rx, ry = 0, 0
+ local ar, al = 0, 0
+ local as, ac = 0, nil
+ local mx, my = 0, 0
+ while i < np do
+ i = i + 1
+ local pi = p[i]
+ if type(pi) ~= "number" then
+ last = pi
+ i = i + 1
+ pi = p[i]
+ end
+ -- most often
+ if last == "c" then
+ x1 = x + pi
+ i = i + 1 ; y1 = y + p[i]
+ i = i + 1 ; x2 = x + p[i]
+ i = i + 1 ; y2 = y + p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto curveto
+ elseif last == "l" then
+ x = x + pi
+ i = i + 1 ; y = y + p[i]
+ goto lineto
+ elseif last == "h" then
+ x = x + pi
+ goto lineto
+ elseif last == "v" then
+ y = y + pi
+ goto lineto
+ elseif last == "a" then
+ x1 = x
+ y1 = y
+ rx = pi
+ i = i + 1 ; ry = p[i]
+ i = i + 1 ; ar = p[i]
+ i = i + 1 ; al = p[i]
+ i = i + 1 ; as = p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto arc
+ elseif last == "s" then
+ if prev == "C" then
+ x1 = 2 * x - x2
+ y1 = 2 * y - y2
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = x + pi
+ i = i + 1 ; y2 = y + p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto curveto
+ elseif last == "m" then
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ x = x + pi
+ i = i + 1 ; y = y + p[i]
+ goto moveto
+ elseif last == "z" then
+ goto close
+ -- less frequent
+ elseif last == "C" then
+ x1 = pi
+ i = i + 1 ; y1 = p[i]
+ i = i + 1 ; x2 = p[i]
+ i = i + 1 ; y2 = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto curveto
+ elseif last == "L" then
+ x = pi
+ i = i + 1 ; y = p[i]
+ goto lineto
+ elseif last == "H" then
+ x = pi
+ goto lineto
+ elseif last == "V" then
+ y = pi
+ goto lineto
+ elseif last == "A" then
+ x1 = x
+ y1 = y
+ rx = pi
+ i = i + 1 ; ry = p[i]
+ i = i + 1 ; ar = p[i]
+ i = i + 1 ; al = p[i]
+ i = i + 1 ; as = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto arc
+ elseif last == "S" then
+ if prev == "C" then
+ x1 = 2 * x - x2
+ y1 = 2 * y - y2
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = pi
+ i = i + 1 ; y2 = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto curveto
+ elseif last == "M" then
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ x = pi ;
+ i = i + 1 ; y = p[i]
+ goto moveto
+ elseif last == "Z" then
+ goto close
+ -- very seldom
+ elseif last == "q" then
+ x1 = x + pi
+ i = i + 1 ; y1 = y + p[i]
+ i = i + 1 ; x2 = x + p[i]
+ i = i + 1 ; y2 = y + p[i]
+ goto quadratic
+ elseif last == "t" then
+ if prev == "C" then
+ x1 = 2 * x - x1
+ y1 = 2 * y - y1
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = x + pi
+ i = i + 1 ; y2 = y + p[i]
+ goto quadratic
+ elseif last == "Q" then
+ x1 = pi
+ i = i + 1 ; y1 = p[i]
+ i = i + 1 ; x2 = p[i]
+ i = i + 1 ; y2 = p[i]
+ goto quadratic
+ elseif last == "T" then
+ if prev == "C" then
+ x1 = 2 * x - x1
+ y1 = 2 * y - y1
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = pi
+ i = i + 1 ; y2 = p[i]
+ goto quadratic
+ else
+ goto continue
+ end
+ ::moveto::
+ n = n + 1 ; t[n] = f_moveto(x,y)
+ last = last == "M" and "L" or "l"
+ prev = "M"
+ mx = x
+ my = y
+ goto continue
+ ::lineto::
+ n = n + 1 ; t[n] = (n > 0 and f_lineto_n or f_lineto_z)(x,y)
+ prev = "L"
+ goto continue
+ ::curveto::
+ n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(x1,y1,x2,y2,x,y)
+ prev = "C"
+ goto continue
+ ::arc::
+ ac = a2c(x1,y1,rx,ry,ar,al,as,x,y)
+ for i=1,#ac,6 do
+ n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(
+ ac[i],ac[i+1],ac[i+2],ac[i+3],ac[i+4],ac[i+5]
+ )
+ end
+ prev = "A"
+ goto continue
+ ::quadratic::
+ n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(
+ x + 2/3 * (x1-x ), y + 2/3 * (y1-y ),
+ x2 + 2/3 * (x1-x2), y2 + 2/3 * (y1-y2),
+ x2, y2
+ )
+ x = x2
+ y = y2
+ prev = "C"
+ goto continue
+ ::close::
+ -- n = n + 1 ; t[n] = prev == "C" and "..cycle" or "--cycle"
+ n = n + 1 ; t[n] = "--cycle"
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ if i == np then
+ break
+ else
+ i = i - 1
+ end
+ kind = prev
+ prev = "Z"
+ -- this is kind of undocumented: a close also moves back
+ x = mx
+ y = my
+ ::continue::
+ end
+ if n > 0 then
+ a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
+ end
+ if prev == "Z" then
+ all.closed = true
+ end
+ all.curve = (kind == "C" or kind == "A")
+ return all, p
+ end
+
+ -- this is a bit tricky as what are points for a mark ... the next can be simplified
+ -- a lot
+
+ grablist = function(p)
+ local np = #p
+ if np == 0 then
+ return nil
+ end
+ local t = { }
+ local n = 0
+ local a = 0
+ local i = 0
+ local last = "M"
+ local prev = last
+ local kind = "L"
+ local x, y = 0, 0
+ local x1, y1 = 0, 0
+ local x2, y2 = 0, 0
+ local rx, ry = 0, 0
+ local ar, al = 0, 0
+ local as, ac = 0, nil
+ local mx, my = 0, 0
+ while i < np do
+ i = i + 1
+ local pi = p[i]
+ if type(pi) ~= "number" then
+ last = pi
+ i = i + 1
+ pi = p[i]
+ end
+ -- most often
+ if last == "c" then
+ x1 = x + pi
+ i = i + 1 ; y1 = y + p[i]
+ i = i + 1 ; x2 = x + p[i]
+ i = i + 1 ; y2 = y + p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto curveto
+ elseif last == "l" then
+ x = x + pi
+ i = i + 1 ; y = y + p[i]
+ goto lineto
+ elseif last == "h" then
+ x = x + pi
+ goto lineto
+ elseif last == "v" then
+ y = y + pi
+ goto lineto
+ elseif last == "a" then
+ x1 = x
+ y1 = y
+ rx = pi
+ i = i + 1 ; ry = p[i]
+ i = i + 1 ; ar = p[i]
+ i = i + 1 ; al = p[i]
+ i = i + 1 ; as = p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto arc
+ elseif last == "s" then
+ if prev == "C" then
+ x1 = 2 * x - x2
+ y1 = 2 * y - y2
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = x + pi
+ i = i + 1 ; y2 = y + p[i]
+ i = i + 1 ; x = x + p[i]
+ i = i + 1 ; y = y + p[i]
+ goto curveto
+ elseif last == "m" then
+ x = x + pi
+ i = i + 1 ; y = y + p[i]
+ goto moveto
+ elseif last == "z" then
+ goto close
+ -- less frequent
+ elseif last == "C" then
+ x1 = pi
+ i = i + 1 ; y1 = p[i]
+ i = i + 1 ; x2 = p[i]
+ i = i + 1 ; y2 = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto curveto
+ elseif last == "L" then
+ x = pi
+ i = i + 1 ; y = p[i]
+ goto lineto
+ elseif last == "H" then
+ x = pi
+ goto lineto
+ elseif last == "V" then
+ y = pi
+ goto lineto
+ elseif last == "A" then
+ x1 = x
+ y1 = y
+ rx = pi
+ i = i + 1 ; ry = p[i]
+ i = i + 1 ; ar = p[i]
+ i = i + 1 ; al = p[i]
+ i = i + 1 ; as = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto arc
+ elseif last == "S" then
+ if prev == "C" then
+ x1 = 2 * x - x2
+ y1 = 2 * y - y2
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = pi
+ i = i + 1 ; y2 = p[i]
+ i = i + 1 ; x = p[i]
+ i = i + 1 ; y = p[i]
+ goto curveto
+ elseif last == "M" then
+ x = pi ;
+ i = i + 1 ; y = p[i]
+ goto moveto
+ elseif last == "Z" then
+ goto close
+ -- very seldom
+ elseif last == "q" then
+ x1 = x + pi
+ i = i + 1 ; y1 = y + p[i]
+ i = i + 1 ; x2 = x + p[i]
+ i = i + 1 ; y2 = y + p[i]
+ goto quadratic
+ elseif last == "t" then
+ if prev == "C" then
+ x1 = 2 * x - x1
+ y1 = 2 * y - y1
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = x + pi
+ i = i + 1 ; y2 = y + p[i]
+ goto quadratic
+ elseif last == "Q" then
+ x1 = pi
+ i = i + 1 ; y1 = p[i]
+ i = i + 1 ; x2 = p[i]
+ i = i + 1 ; y2 = p[i]
+ goto quadratic
+ elseif last == "T" then
+ if prev == "C" then
+ x1 = 2 * x - x1
+ y1 = 2 * y - y1
+ else
+ x1 = x
+ y1 = y
+ end
+ x2 = pi
+ i = i + 1 ; y2 = p[i]
+ goto quadratic
+ else
+ goto continue
+ end
+ ::moveto::
+ n = n + 1 ; t[n] = x
+ n = n + 1 ; t[n] = y
+ last = last == "M" and "L" or "l"
+ prev = "M"
+ mx = x
+ my = y
+ goto continue
+ ::lineto::
+ n = n + 1 ; t[n] = x
+ n = n + 1 ; t[n] = y
+ prev = "L"
+ goto continue
+ ::curveto::
+ n = n + 1 ; t[n] = x
+ n = n + 1 ; t[n] = y
+ prev = "C"
+ goto continue
+ ::arc::
+ ac = a2c(x1,y1,rx,ry,ar,al,as,x,y)
+ for i=1,#ac,6 do
+ n = n + 1 ; t[n] = ac[i+4]
+ n = n + 1 ; t[n] = ac[i+5]
+ end
+ prev = "A"
+ goto continue
+ ::quadratic::
+ n = n + 1 ; t[n] = x2
+ n = n + 1 ; t[n] = y2
+ x = x2
+ y = y2
+ prev = "C"
+ goto continue
+ ::close::
+ n = n + 1 ; t[n] = mx
+ n = n + 1 ; t[n] = my
+ if i == np then
+ break
+ end
+ kind = prev
+ prev = "Z"
+ x = mx
+ y = my
+ ::continue::
+ end
+ return t
+ end
+
+end
+
+-- todo: viewbox helper
+
+local s_wrapped_start = "draw image ("
+local f_wrapped_stop = formatters[") shifted (0,%N) scaled %N ;"]
+
+local handletransform, handleviewbox do
+
+ local sind = math.sind
+
+ --todo: better lpeg
+
+ local f_rotatedaround = formatters[" rotatedaround((%N,%N),%N)"]
+ local f_rotated = formatters[" rotated(%N)"]
+ local f_shifted = formatters[" shifted(%N,%N)"]
+ local f_slanted_x = formatters[" xslanted(%N)"]
+ local f_slanted_y = formatters[" yslanted(%N)"]
+ local f_scaled = formatters[" scaled(%N)"]
+ local f_xyscaled = formatters[" xyscaled(%N,%N)"]
+ local f_matrix = formatters[" transformed bymatrix(%N,%N,%N,%N,%N,%N)"]
+
+ local s_transform_start = "draw image ( "
+ local f_transform_stop = formatters[")%s ;"]
+
+ local function rotate(r,x,y)
+ if x then
+ return r and f_rotatedaround(x,-(y or x),-r)
+ elseif r then
+ return f_rotated(-r)
+ else
+ return ""
+ end
+ end
+
+ local function translate(x,y)
+ if y then
+ return f_shifted(x,-y)
+ elseif x then
+ return f_shifted(x,0)
+ else
+ return ""
+ end
+ end
+
+ local function scale(x,y)
+ if y then
+ return f_xyscaled(x,y)
+ elseif x then
+ return f_scaled(x)
+ else
+ return ""
+ end
+ end
+
+ local function skewx(x)
+ if x then
+ return f_slanted_x(sind(-x))
+ else
+ return ""
+ end
+ end
+
+ local function skewy(y)
+ if y then
+ return f_slanted_y(sind(-y))
+ else
+ return ""
+ end
+ end
+
+ local function matrix(rx,sx,sy,ry,tx,ty)
+ return f_matrix(rx or 1, sx or 0, sy or 0, ry or 1, tx or 0, - (ty or 0))
+ end
+
+ -- How to deal with units here? Anyway, order seems to matter.
+
+ local p_transform = Cf ( Ct("") * (
+ lpegpatterns.whitespace^0 * Cg(
+ C("translate") * (p_numbers / translate) -- maybe xy
+ + C("scale") * (p_numbers / scale)
+ + C("rotate") * (p_numbers / rotate)
+ + C("matrix") * (p_numbers / matrix)
+ + C("skewX") * (p_numbers / skewx)
+ + C("skewY") * (p_numbers / skewy)
+ )
+ )^1, rawset)
+
+ handletransform = function(at)
+ local t = at.transform
+ if t then
+ local e = lpegmatch(p_transform,t)
+ if e then
+ e = concat({
+ e.rotate or "",
+ e.skewX or "",
+ e.skewY or "",
+ e.scale or "",
+ e.translate or "",
+ e.matrix or "",
+ }, " ")
+ return s_transform_start, f_transform_stop(e), t
+ end
+ end
+ end
+
+ handleviewbox = function(v)
+ if v then
+ local x, y, w, h = lpegmatch(p_fournumbers,v)
+ if h then
+ return x, y, w, h
+ end
+ end
+ end
+
+end
+
+local dashed do
+
+ -- actually commas are mandate but we're tolerant
+
+ local f_dashed_n = formatters[" dashed dashpattern (%s ) "]
+ local f_dashed_y = formatters[" dashed dashpattern (%s ) shifted (%N,0) "]
+
+ local p_number = p_optseparator/"" * p_number_r
+ local p_on = Cc(" on ") * p_number
+ local p_off = Cc(" off ") * p_number
+ local p_dashed = Cs((p_on * p_off^-1)^1)
+
+ dashed = function(s,o)
+ if not find(s,",") then
+ -- a bit of a hack:
+ s = s .. " " .. s
+ end
+ return (o and f_dashed_y or f_dashed_n)(lpegmatch(p_dashed,s),o)
+ end
+
+end
+
+do
+
+ local handlers = { }
+ local process = false
+ local root = false
+ local result = false
+ local r = false
+ local definitions = false
+ local classstyles = false
+ local tagstyles = false
+
+ local tags = {
+ ["a"] = true,
+ -- ["altgGlyph"] = true,
+ -- ["altgGlyphDef"] = true,
+ -- ["altgGlyphItem"] = true,
+ -- ["animate"] = true,
+ -- ["animateColor"] = true,
+ -- ["animateMotion"] = true,
+ -- ["animateTransform"] = true,
+ ["circle"] = true,
+ ["clipPath"] = true,
+ -- ["color-profile"] = true,
+ -- ["cursor"] = true,
+ ["defs"] = true,
+ -- ["desc"] = true,
+ ["ellipse"] = true,
+ -- ["filter"] = true,
+ -- ["font"] = true,
+ -- ["font-face"] = true,
+ -- ["font-face-format"] = true,
+ -- ["font-face-name"] = true,
+ -- ["font-face-src"] = true,
+ -- ["font-face-uri"] = true,
+ -- ["foreignObject"] = true,
+ ["g"] = true,
+ -- ["glyph"] = true,
+ -- ["glyphRef"] = true,
+ -- ["hkern"] = true,
+ ["image"] = true,
+ ["line"] = true,
+ ["linearGradient"] = true,
+ ["marker"] = true,
+ -- ["mask"] = true,
+ -- ["metadata"] = true,
+ -- ["missing-glyph"] = true,
+ -- ["mpath"] = true,
+ ["path"] = true,
+ -- ["pattern"] = true,
+ ["polygon"] = true,
+ ["polyline"] = true,
+ ["radialGradient"] = true,
+ ["rect"] = true,
+ -- ["script"] = true,
+ -- ["set"] = true,
+ ["stop"] = true,
+ ["style"] = true,
+ ["svg"] = true,
+ -- ["switch"] = true,
+ ["symbol"] = true,
+ ["text"] = true,
+ -- ["textPath"] = true,
+ -- ["title"] = true,
+ ["tspan"] = true,
+ ["use"] = true,
+ -- ["view"] = true,
+ -- ["vkern"] = true,
+ }
+
+ local pathtracer = {
+ ["stroke"] = "darkred",
+ ["stroke-opacity"] = ".5",
+ ["stroke-width"] = ".5",
+ ["fill"] = "darkgray",
+ ["fill-opacity"] = ".75",
+ }
+
+ local function handlechains(c)
+ if tags[c.tg] then
+ local at = c.at
+ local dt = c.dt
+ if at and dt then
+ -- at["inkscape:connector-curvature"] = nil -- cleare entry and might prevent table growth
+ local estyle = rawget(at,"style")
+ if estyle and estyle ~= "" then
+ for k, v in gmatch(estyle,"%s*([^:]+):%s*([^;]+);?") do
+ at[k] = v
+ end
+ end
+ local eclass = rawget(at,"class")
+ if eclass and eclass ~= "" then
+ for c in gmatch(eclass,"[^ ]+") do
+ local s = classstyles[c]
+ if s then
+ for k, v in next, s do
+ at[k] = v
+ end
+ end
+ end
+ end
+ local tstyle = tagstyles[tag]
+ if tstyle then
+ for k, v in next, tstyle do
+ at[k] = v
+ end
+ end
+ if trace_path and pathtracer then
+ for k, v in next, pathtracer do
+ at[k] = v
+ end
+ end
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "table" then
+ handlechains(dti)
+ end
+ end
+ end
+ end
+ end
+
+ local handlestyle do
+
+ -- It can also be CDATA but that is probably dealt with because we only
+ -- check for style entries and ignore the rest. But maybe we also need
+ -- to check a style at the outer level?
+
+ local p_key = C((R("az","AZ","09","__","--")^1))
+ local p_spec = P("{") * C((1-P("}"))^1) * P("}")
+ local p_valid = Carg(1) * P(".") * p_key + Carg(2) * p_key
+ local p_grab = ((p_valid * p_space^0 * p_spec / rawset) + p_space^1 + P(1))^1
+
+ local fontspecification = css.fontspecification
+
+ handlestyle = function(c)
+ local s = xmltext(c)
+ lpegmatch(p_grab,s,1,classstyles,tagstyles)
+ for k, v in next, classstyles do
+ local t = { }
+ for k, v in gmatch(v,"%s*([^:]+):%s*([^;]+);?") do
+ if k == "font" then
+ local s = fontspecification(v)
+ for k, v in next, s do
+ t["font-"..k] = v
+ end
+ else
+ t[k] = v
+ end
+ end
+ classstyles[k] = t
+ end
+ for k, v in next, tagstyles do
+ local t = { }
+ for k, v in gmatch(v,"%s*([^:]+):%s*([^;]+);?") do
+ if k == "font" then
+ local s = fontspecification(v)
+ for k, v in next, s do
+ t["font-"..k] = v
+ end
+ else
+ t[k] = v
+ end
+ end
+ tagstyles[k] = t
+ end
+ end
+
+ function handlers.style()
+ -- ignore
+ end
+
+ end
+
+ -- We can have root in definitions and then do a metatable lookup but use
+ -- is not used that often I guess.
+
+ local function locate(id)
+ local res = definitions[id]
+ if res then
+ return res
+ end
+ local ref = gsub(id,"^url%(#(.-)%)$","%1")
+ local ref = gsub(ref,"^#","")
+ -- we can make a fast id lookup
+ local res = xmlfirst(root,"**[@id='"..ref.."']")
+ if res then
+ definitions[id] = res
+ end
+ return res
+ end
+
+ -- also locate
+
+ local function handleclippath(at)
+ local clippath = at["clip-path"]
+
+ if not clippath then
+ return
+ end
+
+ local spec = definitions[clippath] or locate(clippath)
+
+ -- do we really need thsi crap
+ if not spec then
+ local index = match(clippath,"(%d+)")
+ if index then
+ spec = xmlfirst(root,"clipPath["..tostring(tonumber(index) or 0).."]")
+ end
+ end
+ -- so far for the crap
+
+ if not spec then
+ report("unknown clip %a",clippath)
+ return
+ elseif spec.tg ~= "clipPath" then
+ report("bad clip %a",clippath)
+ return
+ end
+
+ ::again::
+ for c in xmlcollected(spec,"/(path|use|g)") do
+ local tg = c.tg
+ if tg == "use" then
+ local ca = c.at
+ local id = ca["xlink:href"]
+ if id then
+ spec = locate(id)
+ if spec then
+ local sa = spec.at
+ setmetatableindex(sa,ca)
+ if spec.tg == "path" then
+ local d = sa.d
+ if d then
+ local p = grabpath(d)
+ p.evenodd = sa["clip-rule"] == "evenodd"
+ p.close = true
+ return p, clippath
+ else
+ return
+ end
+ else
+ goto again
+ end
+ end
+ end
+ -- break
+ elseif tg == "path" then
+ local ca = c.at
+ local d = ca.d
+ if d then
+ local p = grabpath(d)
+ p.evenodd = ca["clip-rule"] == "evenodd"
+ p.close = true
+ return p, clippath
+ else
+ return
+ end
+ else
+ -- inherit?
+ end
+ end
+ end
+
+ local s_shade_linear = ' withshademethod "linear" '
+ local s_shade_circular = ' withshademethod "circular" '
+ local f_shade_step = formatters['withshadestep ( withshadefraction %N withshadecolors(%s,%s) )']
+ local f_shade_one = formatters['withprescript "sh_center_a=%N %N"']
+ local f_shade_two = formatters['withprescript "sh_center_b=%N %N"']
+
+ local f_color = formatters['withcolor "%s"']
+ local f_opacity = formatters['withtransparency (1,%N)']
+ local f_pen = formatters['withpen pencircle scaled %N']
+
+ -- todo: gradient unfinished
+ -- todo: opacity but first we need groups in mp
+
+ local function gradient(id)
+ local spec = definitions[id] -- no locate !
+ if spec then
+ local kind = spec.tg
+ local shade = nil
+ local n = 1
+ local a = spec.at
+ if kind == "linearGradient" then
+ shade = { s_shade_linear }
+ --
+ local x1 = rawget(a,"x1")
+ local y1 = rawget(a,"y1")
+ local x2 = rawget(a,"x2")
+ local y2 = rawget(a,"y2")
+ if x1 and y1 then
+ n = n + 1 ; shade[n] = f_shade_one(asnumber_vx(x1),asnumber_vy(y1))
+ end
+ if x2 and y2 then
+ n = n + 1 ; shade[n] = f_shade_one(asnumber_vx(x2),asnumber_vy(y2))
+ end
+ --
+ elseif kind == "radialGradient" then
+ shade = { s_shade_circular }
+ --
+ local cx = rawget(a,"cx") -- x center
+ local cy = rawget(a,"cy") -- y center
+ local r = rawget(a,"r" ) -- radius
+ local fx = rawget(a,"fx") -- focal points
+ local fy = rawget(a,"fy") -- focal points
+ --
+ if cx and cy then
+ -- todo
+ end
+ if r then
+ -- todo
+ end
+ if fx and fy then
+ -- todo
+ end
+ else
+ report("unknown gradient %a",id)
+ return
+ end
+ -- local gu = a.gradientUnits
+ -- local gt = a.gradientTransform
+ -- local sm = a.spreadMethod
+ local colora, colorb
+ -- startcolor ?
+ for c in xmlcollected(spec,"/stop") do
+ local a = c.at
+ local offset = rawget(a,"offset")
+ local colorb = rawget(a,"stop-color")
+ local opacity = rawget(a,"stop-opacity")
+ if colorb then
+ colorb = thecolor(colorb)
+ end
+ if not colora then
+ colora = colorb
+ end
+ -- what if no percentage
+
+ local fraction = offset and asnumber_r(offset)
+ if not fraction then
+ -- offset = tonumber(offset)
+ -- for now
+ fraction = xmlcount(spec,"/stop")/100
+ end
+
+ if colora and colorb and color_a ~= "" and color_b ~= "" then
+ n = n + 1 ; shade[n] = f_shade_step(fraction,colora,colorb)
+ end
+
+ colora = colorb
+ end
+ return concat(shade," ")
+ end
+ end
+
+ local function drawproperties(stroke,at,opacity)
+ local p = at["stroke-width"]
+ if p then
+ p = f_pen(asnumber_r(p))
+ end
+ local d = at["stroke-dasharray"]
+ if d == "none" then
+ d = nil
+ elseif d then
+ local o = at["stroke-dashoffset"]
+ if o and o ~= "none" then
+ o = asnumber_r(o)
+ else
+ o = false
+ end
+ d = dashed(d,o)
+ end
+ local c = withcolor(stroke)
+ local o = at["stroke-opacity"] or (opacity and at["opacity"])
+ if o == "none" then
+ o = nil
+ elseif o then
+ o = asnumber_r(o)
+ if o and o ~= 1 then
+ o = f_opacity(o)
+ else
+ o = nil
+ end
+ end
+ return p, d, c, o
+ end
+
+ local s_opacity_start = "draw image ("
+ local f_opacity_stop = formatters["setgroup currentpicture to boundingbox currentpicture withtransparency (1,%N)) ;"]
+
+ local function sharedopacity(at)
+ local o = at["opacity"]
+ if o and o ~= "none" then
+ o = asnumber_r(o)
+ if o and o ~= 1 then
+ return s_opacity_start, f_opacity_stop(o)
+ end
+ end
+ end
+
+ local function fillproperties(fill,at,opacity)
+ local c = c ~= "none" and (gradient(fill) or withcolor(fill)) or nil
+ local o = at["fill-opacity"] or (opacity and at["opacity"])
+ if o and o ~= "none" then
+ o = asnumber_r(o)
+ if o == 1 then
+ return c
+ elseif o then
+ return c, f_opacity(o), o == 0
+ end
+ end
+ return c
+ end
+
+ -- todo: clip = [ auto | rect(llx,lly,urx,ury) ]
+
+ local s_offset_start = "draw image ( "
+ local f_offset_stop = formatters[") shifted (%N,%N) ;"]
+ local s_rotation_start = "draw image ( "
+ local f_rotation_stop = formatters[") rotatedaround((0,0),-angle((%N,%N))) ;"]
+ local f_rotation_angle = formatters[") rotatedaround((0,0),-%N) ;"]
+
+ local function offset(at)
+ local x = asnumber_vx(rawget(at,"x"))
+ local y = asnumber_vy(rawget(at,"y"))
+ if x ~= 0 or y ~= 0 then
+ return s_offset_start, f_offset_stop(x,y)
+ end
+ end
+
+ local s_viewport_start = "draw image ("
+ local s_viewport_stop = ") ;"
+ local f_viewport_shift = formatters["currentpicture := currentpicture shifted (%03N,%03N);"]
+ local f_viewport_scale = formatters["currentpicture := currentpicture xysized (%03N,%03N);"]
+ local f_viewport_clip = formatters["clip currentpicture to (unitsquare xyscaled (%03N,%03N));"]
+
+ local function viewport(x,y,w,h,noclip,scale)
+ r = r + 1 ; result[r] = s_viewport_start
+ return function()
+ local okay = w ~= 0 and h ~= 0
+ if okay and scale then
+ r = r + 1 ; result[r] = f_viewport_scale(w,h)
+ end
+ if x ~= 0 or y ~= 0 then
+ r = r + 1 ; result[r] = f_viewport_shift(-x,y)
+ end
+ if okay and not noclip then
+ r = r + 1 ; result[r] = f_viewport_clip(w,-h)
+ end
+
+ r = r + 1 ; result[r] = s_viewport_stop
+ end
+ end
+
+ -- maybe forget about defs and just always locate (and then backtrack
+ -- over <g> if needed)
+
+ function handlers.defs(c)
+ for c in xmlcollected(c,"/*") do
+ local a = c.at
+ if a then
+ local id = rawget(a,"id")
+ if id then
+ definitions["#" .. id ] = c
+ definitions["url(#" .. id .. ")"] = c
+ end
+ end
+ end
+ end
+
+ function handlers.symbol(c)
+ if uselevel == 0 then
+ local id = rawget(c.at,"id")
+ if id then
+ definitions["#" .. id ] = c
+ definitions["url(#" .. id .. ")"] = c
+ end
+ else
+ handlers.g(c)
+ end
+ end
+
+ local uselevel = 0
+ local bodyfontscale = 1
+
+ function handlers.use(c)
+ local at = c.at
+ local id = rawget(at,"href") or rawget(at,"xlink:href") -- better a rawget
+ local res = locate(id)
+ if res then
+ -- width height ?
+ uselevel = uselevel + 1
+ local boffset, eoffset = offset(at)
+ local btransform, etransform, transform = handletransform(at)
+
+ if boffset then
+ r = r + 1 result[r] = boffset
+ end
+
+ -- local clippath = at.clippath
+
+ if btransform then
+ r = r + 1 result[r] = btransform
+ end
+
+ local _transform = transform
+ local _clippath = clippath
+ at["transform"] = false
+ -- at["clip-path"] = false
+
+ process(res,"/*")
+
+ at["transform"] = _transform
+ -- at["clip-path"] = _clippath
+
+ if etransform then
+ r = r + 1 ; result[r] = etransform
+ end
+
+ if eoffset then
+ r = r + 1 result[r] = eoffset
+ end
+
+ uselevel = uselevel - 1
+ else
+ report("use: unknown definition %a",id)
+ end
+ end
+
+ local f_no_draw = formatters['nodraw (%s)']
+ local f_do_draw = formatters['draw (%s)']
+ local f_no_fill_c = formatters['nofill (%s..cycle)']
+ local f_do_fill_c = formatters['fill (%s..cycle)']
+ local f_eo_fill_c = formatters['eofill (%s..cycle)']
+ local f_no_fill_l = formatters['nofill (%s--cycle)']
+ local f_do_fill_l = formatters['fill (%s--cycle)']
+ local f_eo_fill_l = formatters['eofill (%s--cycle)']
+ local f_do_fill = f_do_fill_c
+ local f_eo_fill = f_eo_fill_c
+ local f_no_fill = f_no_fill_c
+-- local s_clip_start = 'draw image ('
+-- local f_clip_stop_c = formatters[') ; clip currentpicture to (%s..cycle) ;']
+-- local f_clip_stop_l = formatters[') ; clip currentpicture to (%s--cycle) ;']
+-- local f_clip_stop = f_clip_stop_c
+-- local f_eoclip_stop_c = formatters[') ; eoclip currentpicture to (%s..cycle) ;']
+-- local f_eoclip_stop_l = formatters[') ; eoclip currentpicture to (%s--cycle) ;']
+-- local f_eoclip_stop = f_eoclip_stop_c
+ local s_clip_start = 'save p ; picture p ; p := image ('
+ local f_clip_stop_c = formatters[') ; clip p to (%s..cycle) ; draw p ;']
+ local f_clip_stop_l = formatters[') ; clip p to (%s--cycle) ; draw p ;']
+ local f_clip_stop = f_clip_stop_c
+ local f_eoclip_stop_c = formatters[') ; eoclip p to (%s..cycle) ; draw p ;']
+ local f_eoclip_stop_l = formatters[') ; eoclip p to (%s--cycle) ; draw p ;']
+ local f_eoclip_stop = f_eoclip_stop_c
+
+ -- could be shared and then beginobject | endobject
+
+ local function flushobject(object,at,c,o)
+ local btransform, etransform = handletransform(at)
+ local cpath = handleclippath(at)
+
+ if cpath then
+ r = r + 1 ; result[r] = s_clip_start
+ end
+
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+
+ r = r + 1 ; result[r] = f_do_draw(object)
+
+ if c then
+ r = r + 1 ; result[r] = c
+ end
+
+ if o then
+ r = r + 1 ; result[r] = o
+ end
+
+ if etransform then
+ r = r + 1 ; result[r] = etransform
+ end
+
+ r = r + 1 ; result[r] = ";"
+
+ if cpath then
+ local f_done = cpath.evenodd
+ if cpath.curve then
+ f_done = f_done and f_eoclip_stop_c or f_clip_stop_c
+ else
+ f_done = f_done and f_eoclip_stop_l or f_clip_stop_l
+ end
+ r = r + 1 ; result[r] = f_done(cpath[1])
+ end
+ end
+
+ do
+
+ local flush
+
+ local f_linecap = formatters["interim linecap := %s ;"]
+ local f_linejoin = formatters["interim linejoin := %s ;"]
+ local f_miterlimit = formatters["interim miterlimit := %s ;"]
+
+ local s_begingroup = "begingroup;"
+ local s_endgroup = "endgroup;"
+
+ local linecaps = { butt = "butt", square = "squared", round = "rounded" }
+ local linejoins = { miter = "mitered", bevel = "beveled", round = "rounded" }
+
+ local function startlineproperties(at)
+ local cap = at["stroke-linecap"]
+ local join = at["stroke-linejoin"]
+ local limit = at["stroke-miterlimit"]
+ cap = cap and linecaps [cap]
+ join = join and linejoins[join]
+ limit = limit and asnumber_r(limit)
+ if cap or join or limit then
+ r = r + 1 ; result[r] = s_begingroup
+ if cap then
+ r = r + 1 ; result[r] = f_linecap(cap)
+ end
+ if join then
+ r = r + 1 ; result[r] = f_linejoin(join)
+ end
+ if limit then
+ r = r + 1 ; result[r] = f_miterlimit(limit)
+ end
+ return function()
+ at["stroke-linecap"] = false
+ at["stroke-linejoin"] = false
+ at["stroke-miterlimit"] = false
+ r = r + 1 ; result[r] = s_endgroup
+ at["stroke-linecap"] = cap
+ at["stroke-linejoin"] = join
+ at["stroke-miterlimit"] = limit
+ end
+ end
+ end
+
+ -- markers are a quite rediculous thing .. let's assume simple usage for now
+
+ function handlers.marker()
+ -- todo: is just a def too
+ end
+
+ -- kind of local svg ... so make a generic one
+ --
+ -- todo: combine more (offset+scale+rotation)
+
+ local function makemarker(where,c,x1,y1,x2,y2,x3,y3,parentat)
+ local at = c.at
+ local refx = rawget(at,"refX")
+ local refy = rawget(at,"refY")
+ local width = rawget(at,"markerWidth")
+ local height = rawget(at,"markerHeight")
+ local view = rawget(at,"viewBox")
+ local orient = rawget(at,"orient")
+ -- local ratio = rawget(at,"preserveAspectRatio")
+ local units = asnumber(at["markerUnits"] or parentat["stroke-width"]) or 1
+
+ local angx = 0
+ local angy = 0
+ local angle = 0
+
+ if where == "beg" then
+ if orient == "auto" then -- unchecked
+ -- no angle
+ angx = abs(x2 - x3)
+ angy = abs(y2 - y3)
+ elseif orient == "auto-start-reverse" then -- checked
+ -- points to start
+ angx = -abs(x2 - x3)
+ angy = -abs(y2 - y3)
+ elseif orient then -- unchecked
+ angle = asnumber_r(orient)
+ end
+ elseif where == "end" then
+ -- funny standard .. bug turned feature?
+ if orient == "auto" or orient == "auto-start-reverse" then
+ angx = abs(x1 - x2)
+ angy = abs(y1 - y2)
+ elseif orient then -- unchecked
+ angle = asnumber_r(orient)
+ end
+ elseif orient then -- unchecked
+ angle = asnumber_r(orient)
+ end
+ -- what wins: viewbox or w/h
+
+ refx = asnumber_x(refx)
+ refy = asnumber_y(refy)
+
+ width = (width and asnumber_x(width) or 3) * units
+ height = (height and asnumber_y(height) or 3) * units
+
+ local x = 0
+ local y = 0
+ local w = width
+ local h = height
+
+ -- kind of like the main svg
+
+ r = r + 1 ; result[r] = s_offset_start
+
+ local wrapupviewport
+-- todo : better viewbox code
+ local xpct, ypct, rpct
+ if view then
+ x, y, w, h = handleviewbox(view)
+ end
+
+ if width ~= 0 then
+ w = width
+ end
+ if height ~= 0 then
+ h = height
+ end
+
+ if h then
+ xpct = percentage_x
+ ypct = percentage_y
+ rpct = percentage_r
+ percentage_x = w / 100
+ percentage_y = h / 100
+ percentage_r = (sqrt(w^2 + h^2) / sqrt(2)) / 100
+ wrapupviewport = viewport(x,y,w,h,true,true) -- no clip
+ end
+
+ -- we can combine a lot here:
+
+ local hasref = refx ~= 0 or refy ~= 0
+ local hasrot = angx ~= 0 or angy ~= 0 or angle ~= 0
+
+ local btransform, etransform, transform = handletransform(at)
+
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+
+ if hasrot then
+ r = r + 1 ; result[r] = s_rotation_start
+ end
+
+ if hasref then
+ r = r + 1 ; result[r] = s_offset_start
+ end
+
+ local _transform = transform
+ at["transform"] = false
+
+ handlers.g(c)
+
+ at["transform"] = _transform
+
+ if hasref then
+ r = r + 1 ; result[r] = f_offset_stop(-refx,refy)
+ end
+
+ if hasrot then
+ if angle ~= 0 then
+ r = r + 1 ; result[r] = f_rotation_angle(angle)
+ else
+ r = r + 1 ; result[r] = f_rotation_stop(angx,angy)
+ end
+ end
+
+ if etransform then
+ r = r + 1 ; result[r] = etransform
+ end
+
+ if h then
+ percentage_x = xpct
+ percentage_y = ypct
+ percentage_r = rpct
+ if wrapupviewport then
+ wrapupviewport()
+ end
+ end
+ r = r + 1 ; result[r] = f_offset_stop(x2,y2)
+
+ end
+
+ -- do we need to metatable the attributes here?
+
+ local function addmarkers(list,begmarker,midmarker,endmarker,at)
+ local n = #list
+ if n > 3 then
+ if begmarker then
+ local m = locate(begmarker)
+ if m then
+ makemarker("beg",m,false,false,list[1],list[2],list[3],list[4],at)
+ end
+ end
+ if midmarker then
+ local m = locate(midmarker)
+ if m then
+ for i=3,n-2,2 do
+ makemarker("mid",m,list[i-2],list[i-1],list[i],list[i+1],list[i+2],list[i+3],at)
+ end
+ end
+ end
+ if endmarker then
+ local m = locate(endmarker)
+ if m then
+ makemarker("end",m,list[n-3],list[n-2],list[n-1],list[n],false,false,at)
+ end
+ end
+ else
+ -- no line
+ end
+ end
+
+ local function flush(shape,dofill,at,list,begmarker,midmarker,endmarker)
+
+ local fill = dofill and (at["fill"] or "black")
+ local stroke = at["stroke"] or "none"
+
+ local btransform, etransform = handletransform(at)
+ local cpath = handleclippath(at)
+
+ if cpath then
+ r = r + 1 ; result[r] = s_clip_start
+ end
+
+ local has_stroke = stroke and stroke ~= "none"
+ local has_fill = fill and fill ~= "none"
+
+ local bopacity, eopacity
+ if has_stroke and has_fill then
+ bopacity, eopacity = sharedopacity(at)
+ end
+
+ if bopacity then
+ r = r + 1 ; result[r] = bopacity
+ end
+
+ if has_fill then
+ local color, opacity = fillproperties(fill,at,not has_stroke)
+ local f_xx_fill = at["fill-rule"] == "evenodd" and f_eo_fill or f_do_fill
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+ r = r + 1 result[r] = f_xx_fill(shape)
+ if color then
+ r = r + 1 ; result[r] = color
+ end
+ if opacity then
+ r = r + 1 ; result[r] = opacity
+ end
+ r = r + 1 ; result[r] = etransform or ";"
+ end
+
+ if has_stroke then
+ local wrapup = startlineproperties(at)
+ local pen, dashing, color, opacity = drawproperties(stroke,at,not has_fill)
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+ r = r + 1 ; result[r] = f_do_draw(shape)
+ if pen then
+ r = r + 1 ; result[r] = pen
+ end
+ if dashing then
+ r = r + 1 ; result[r] = dashing
+ end
+ if color then
+ r = r + 1 ; result[r] = color
+ end
+ if opacity then
+ r = r + 1 ; result[r] = opacity
+ end
+ r = r + 1 ; result[r] = etransform or ";"
+ --
+ if list then
+ addmarkers(list,begmarker,midmarker,endmarker,at)
+ end
+ --
+ if wrapup then
+ wrapup()
+ end
+ end
+
+ if eopacity then
+ r = r + 1 ; result[r] = eopacity
+ end
+
+ if cpath then
+ r = r + 1 ; result[r] = (cpath.evenodd and f_eoclip_stop or f_clip_stop)(cpath[1])
+ end
+
+ end
+
+ local f_rectangle = formatters['unitsquare xyscaled (%N,%N) shifted (%N,%N)']
+ local f_rounded = formatters['roundedsquarexy(%N,%N,%N,%N) shifted (%N,%N)']
+ local f_line = formatters['((%N,%N)--(%N,%N))']
+ local f_ellipse = formatters['(fullcircle xyscaled (%N,%N) shifted (%N,%N))']
+ local f_circle = formatters['(fullcircle scaled %N shifted (%N,%N))']
+
+ function handlers.line(c)
+ local at = c.at
+ local x1 = rawget(at,"x1")
+ local y1 = rawget(at,"y1")
+ local x2 = rawget(at,"x2")
+ local y2 = rawget(at,"y2")
+
+ x1 = x1 and asnumber_vx(x1) or 0
+ y1 = y1 and asnumber_vy(y1) or 0
+ x2 = x2 and asnumber_vx(x2) or 0
+ y2 = y2 and asnumber_vy(y2) or 0
+
+ flush(f_line(x1,y1,x2,y2),false,at)
+ end
+
+ function handlers.rect(c)
+ local at = c.at
+ local width = rawget(at,"width")
+ local height = rawget(at,"height")
+ local x = rawget(at,"x")
+ local y = rawget(at,"y")
+ local rx = rawget(at,"rx")
+ local ry = rawget(at,"ry")
+
+ width = width and asnumber_x(width) or 0
+ height = height and asnumber_y(height) or 0
+ x = x and asnumber_vx(x) or 0
+ y = y and asnumber_vy(y) or 0
+
+ y = y - height
+
+ if rx then rx = asnumber(rx) end
+ if ry then ry = asnumber(ry) end
+
+ if rx or ry then
+ if not rx then rx = ry end
+ if not ry then ry = rx end
+ flush(f_rounded(width,height,rx,ry,x,y),true,at)
+ else
+ flush(f_rectangle(width,height,x,y),true,at)
+ end
+ end
+
+ function handlers.ellipse(c)
+ local at = c.at
+ local cx = rawget(at,"cx")
+ local cy = rawget(at,"cy")
+ local rx = rawget(at,"rx")
+ local ry = rawget(at,"ry")
+
+ cx = cx and asnumber_vx(cx) or 0
+ cy = cy and asnumber_vy(cy) or 0
+ rx = rx and asnumber_r (rx) or 0
+ ry = ry and asnumber_r (ry) or 0
+
+ flush(f_ellipse(2*rx,2*ry,cx,cy),true,at)
+ end
+
+ function handlers.circle(c)
+ local at = c.at
+ local cx = rawget(at,"cx")
+ local cy = rawget(at,"cy")
+ local r = rawget(at,"r")
+
+ cx = cx and asnumber_vx(cx) or 0
+ cy = cy and asnumber_vy(cy) or 0
+ r = r and asnumber_r (r) or 0
+
+ flush(f_circle(2*r,cx,cy),true,at)
+ end
+
+ local f_lineto_z = formatters['(%N,%N)']
+ local f_lineto_n = formatters['--(%N,%N)']
+
+ local p_pair = p_optseparator * p_number_vx * p_optseparator * p_number_vy
+ local p_open = Cc("(")
+ local p_close = Carg(1) * P(true) / function(s) return s end
+ local p_polyline = Cs(p_open * (p_pair / f_lineto_z) * (p_pair / f_lineto_n)^0 * p_close)
+ local p_polypair = Ct(p_pair^0)
+
+ local function poly(c,final)
+ local at = c.at
+ local points = rawget(at,"points")
+ if points then
+ local path = lpegmatch(p_polyline,points,1,final)
+ local list = nil
+ local begmarker = rawget(at,"marker-start")
+ local midmarker = rawget(at,"marker-mid")
+ local endmarker = rawget(at,"marker-end")
+ if begmarker or midmarker or endmarker then
+ list = lpegmatch(p_polypair,points)
+ end
+ flush(path,true,at,list,begmarker,midmarker,endmarker)
+ end
+ end
+
+ function handlers.polyline(c) poly(c, ")") end
+ function handlers.polygon (c) poly(c,"--cycle)") end
+
+ local s_image_start = "draw image ("
+ local s_image_stop = ") ;"
+
+ function handlers.path(c)
+ local at = c.at
+ local d = rawget(at,"d")
+ if d then
+ local shape, l = grabpath(d)
+ local fill = at["fill"] or "black"
+ local stroke = at["stroke"] or "none"
+ local n = #shape
+
+ local btransform, etransform = handletransform(at)
+ local cpath = handleclippath(at)
+ if cpath then
+ r = r + 1 ; result[r] = s_clip_start
+ end
+
+ -- todo: image (nicer for transform too)
+
+ if fill and fill ~= "none" then
+ local color, opacity = fillproperties(fill,at)
+ local f_xx_fill = at["fill-rule"] == "evenodd"
+ if shape.closed then
+ f_xx_fill = f_xx_fill and f_eo_fill or f_do_fill
+ elseif shape.curve then
+ f_xx_fill = f_xx_fill and f_eo_fill_c or f_do_fill_c
+ else
+ f_xx_fill = f_xx_fill and f_eo_fill_l or f_do_fill_l
+ end
+ if n == 1 then
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+ r = r + 1 result[r] = f_xx_fill(shape[1])
+ if color then
+ r = r + 1 ; result[r] = color
+ end
+ if opacity then
+ r = r + 1 ; result[r] = opacity
+ end
+ r = r + 1 ; result[r] = etransform or ";"
+ else
+ r = r + 1 ; result[r] = btransform or s_image_start
+ for i=1,n do
+ if i == n then
+ r = r + 1 ; result[r] = f_xx_fill(shape[i])
+ if color then
+ r = r + 1 ; result[r] = color
+ end
+ if opacity then
+ r = r + 1 ; result[r] = opacity
+ end
+ else
+ r = r + 1 ; result[r] = f_no_fill(shape[i])
+ end
+ r = r + 1 ; result[r] = ";"
+ end
+ r = r + 1 ; result[r] = etransform or s_image_stop
+ end
+ end
+
+ if stroke and stroke ~= "none" then
+ local begmarker = rawget(at,"marker-start")
+ local midmarker = rawget(at,"marker-mid")
+ local endmarker = rawget(at,"marker-end")
+ if begmarker or midmarker or endmarker then
+ list = grablist(l)
+ end
+ local wrapup = startlineproperties(at)
+ local pen, dashing, color, opacity = drawproperties(stroke,at)
+ if n == 1 and not list then
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+ r = r + 1 result[r] = f_do_draw(shape[1])
+ if pen then
+ r = r + 1 ; result[r] = pen
+ end
+ if dashing then
+ r = r + 1 ; result[r] = dashing
+ end
+ if color then
+ r = r + 1 ; result[r] = color
+ end
+ if opacity then
+ r = r + 1 ; result[r] = opacity
+ end
+ r = r + 1 result[r] = etransform or ";"
+ else
+ r = r + 1 result[r] = btransform or "draw image ("
+ for i=1,n do
+ r = r + 1 result[r] = f_do_draw(shape[i])
+ if pen then
+ r = r + 1 ; result[r] = pen
+ end
+ if dashing then
+ r = r + 1 ; result[r] = dashing
+ end
+ if color then
+ r = r + 1 ; result[r] = color
+ end
+ if opacity then
+ r = r + 1 ; result[r] = opacity
+ end
+ r = r + 1 ; result[r] = ";"
+ end
+ if list then
+ addmarkers(list,begmarker,midmarker,endmarker,at)
+ end
+ r = r + 1 ; result[r] = etransform or ") ;"
+ end
+ if wrapup then
+ wrapup()
+ end
+ end
+
+ if cpath then
+ r = r + 1 ; result[r] = f_clip_stop(cpath[1])
+ end
+
+ end
+ end
+
+ end
+
+ -- kind of special
+
+ do
+
+ -- some day:
+ --
+ -- specification = identifiers.jpg(data."string")
+ -- specification.data = data
+ -- inclusion takes from data
+ -- specification.data = false
+
+ local f_image = formatters[ [[figure("%s") xysized (%N,%N) shifted (%N,%N)]] ]
+
+ local nofimages = 0
+
+ function handlers.image(c)
+ local at = c.at
+ local im = rawget(at,"xlink:href")
+ if im then
+ local kind, data = match(im,"^data:image/([a-z]+);base64,(.*)$")
+ if kind == "png" then
+ -- ok
+ elseif kind == "jpeg" then
+ kind = "jpg"
+ else
+ kind = false
+ end
+ if kind and data then
+ local w = rawget(at,"width")
+ local h = rawget(at,"height")
+ local x = rawget(at,"x")
+ local y = rawget(at,"y")
+ w = w and asnumber_x(w)
+ h = h and asnumber_y(h)
+ x = x and asnumber_vx(x) or 0
+ y = y and asnumber_vy(y) or 0
+ nofimages = nofimages + 1
+ local name = "temp-svg-image-" .. nofimages .. "." .. kind
+ local data = mime.decode("base64")(data)
+ io.savedata(name,data)
+ if not w or not h then
+ local info = graphics.identifiers[kind](data,"string")
+ if info then
+ -- todo: keep aspect ratio attribute
+ local xsize = info.xsize
+ local ysize = info.ysize
+ if not w then
+ if not h then
+ w = xsize
+ h = ysize
+ else
+ w = (h / ysize) * xsize
+ end
+ else
+ h = (w / xsize) * ysize
+ end
+ end
+ end
+ -- safeguard:
+ if not w then w = h or 1 end
+ if not h then h = w or 1 end
+ luatex.registertempfile(name)
+ -- done:
+ flushobject(f_image(name,w,h,x,y - h),at)
+ else
+ -- nothing done
+ end
+ end
+ end
+
+ end
+
+ -- these transform: g a text svg symbol
+
+ do
+
+ function handlers.a(c)
+ process(c,"/*")
+ end
+
+ function handlers.g(c) -- much like flushobject so better split and share
+ local at = c.at
+
+ local btransform, etransform, transform = handletransform(at)
+ local cpath, clippath = handleclippath(at)
+
+ if cpath then
+ r = r + 1 ; result[r] = s_clip_start
+ end
+
+ if btransform then
+ r= r + 1 result[r] = btransform
+ end
+
+ local _transform = transform
+ local _clippath = clippath
+ at["transform"] = false
+ at["clip-path"] = false
+
+ process(c,"/*")
+
+ at["transform"] = _transform
+ at["clip-path"] = _clippath
+
+ if etransform then
+ r = r + 1 ; result[r] = etransform
+ end
+
+ if cpath then
+ local f_done = cpath.evenodd
+ if cpath.curve then
+ f_done = f_done and f_eoclip_stop_c or f_clip_stop_c
+ else
+ f_done = f_done and f_eoclip_stop_l or f_clip_stop_l
+ end
+ r = r + 1 ; result[r] = f_done(cpath[1])
+ end
+ end
+
+ -- this will never really work out
+ --
+ -- todo: register text in lua in mapping with id, then draw mapping unless overloaded
+ -- using lmt_svglabel with family,style,weight,size,id passed
+
+ -- nested tspans are messy: they can have displacements but in inkscape we also
+ -- see x and y (inner and outer element)
+
+ -- The size is a bit of an issue. I assume that the specified size relates to the
+ -- designsize but we want to be able to use other fonts.
+
+ do
+
+ local f_styled = formatters["\\svgstyled{%s}{%s}{%s}{%s}"]
+ local f_colored = formatters["\\svgcolored{%.3N}{%.3N}{%.3N}{"]
+-- local f_placed = formatters["\\svgplaced{%.3N}{%.3N}{}{"]
+local f_placed = formatters["\\svgplaced{%s}{%s}{}{"]
+ local f_poschar = formatters["\\svgposchar{%.3N}{%.3N}{%s}"]
+ local f_char = formatters["\\svgchar{%s}"]
+
+ local f_scaled = formatters["\\svgscaled{%N}{%s}{%s}{%s}"]
+ local f_normal = formatters["\\svgnormal{%s}{%s}{%s}"]
+ local f_hashed = formatters["\\svghashed{%s}"]
+
+ -- We move to the outer (x,y) and when we have an inner offset we
+ -- (need to) compensate for that outer offset.
+
+ -- local f_text_scaled_svg = formatters['(svgtext("%s") scaled %N shifted (%N,%N))']
+ -- local f_text_normal_svg = formatters['(svgtext("%s") shifted (%N,%N))']
+ -- local f_text_simple_svg = formatters['svgtext("%s")']
+
+ local anchors = {
+ ["start"] = "drt",
+ ["end"] = "dflt",
+ ["middle"] = "d",
+ }
+
+ local f_text_normal_svg = formatters['(textext.%s("%s") shifted (%N,%N))']
+ local f_text_simple_svg = formatters['textext.%s("%s")']
+
+ -- or just maptext
+
+ local f_mapped_normal_svg = formatters['(svgtext("%s") shifted (%N,%N))']
+ local f_mapped_simple_svg = formatters['svgtext("%s")']
+
+ local cssfamily = css.family
+ local cssstyle = css.style
+ local cssweight = css.weight
+ local csssize = css.size
+
+ local usedfonts = setmetatableindex(function(t,k)
+ local v = setmetatableindex("table")
+ t[k] = v
+ return v
+ end)
+
+ local p_texescape = lpegpatterns.texescape
+
+ -- For now as I need it for my (some 1500) test files.
+
+ local function checkedfamily(name)
+ if find(name,"^.-verdana.-$") then
+ name = "verdana"
+ end
+ return name
+ end
+
+ -- todo: only escape some chars and handle space
+
+ local defaultsize = 10
+
+ local function collect(t,c,x,y,size,scale,family,tx,ty)
+ local at = c.at
+ local ax = rawget(at,"x")
+ local ay = rawget(at,"y")
+ local dx = rawget(at,"dx")
+ local dy = rawget(at,"dy")
+ local v_fill = at["fill"]
+ local v_family = at["font-family"]
+ local v_style = at["font-style"]
+ local v_weight = at["font-weight"]
+ local v_size = at["font-size"]
+ --
+ ax = ax and asnumber_vx(ax) or x
+ ay = ay and asnumber_vy(ay) or y
+ --
+local d_x = dx and asnumber_vx(dx) or 0
+local d_y = dy and asnumber_vy(dy) or 0
+ --
+ if v_family then v_family = cssfamily(v_family) end
+ if v_style then v_style = cssstyle (v_style) end
+ if v_weight then v_weight = cssweight(v_weight) end
+ if v_size then v_size = csssize (v_size,factors) or tonumber(v_size) end
+ --
+ if not v_family then v_family = family end
+ if not v_weight then v_weight = "normal" end
+ if not v_style then v_style = "normal" end
+ --
+ if v_family then
+ v_family = fonts.names.cleanname(v_family)
+ v_family = checkedfamily(v_family)
+ end
+ --
+ usedfonts[v_family][v_weight][v_style] = true
+ --
+ ax = ax - x
+ ay = ay - y
+ --
+ local elayered = ax ~= 0 or ay ~= 0 or false
+-- local eplaced = dx ~= 0 or dy ~= 0 or false
+local eplaced = d_x ~= 0 or d_y ~= 0 or false
+ local usedsize, usedscaled
+
+ if elayered then
+ -- we're now at the outer level again so we need to scale
+ -- back to the outer level values
+ t[#t+1] = formatters["\\svgsetlayer{%0N}{%0N}{"](ax,-ay)
+ usedsize = v_size or defaultsize
+ usedscale = usedsize / defaultsize
+ else
+ -- we're nested so we can be scaled
+ usedsize = v_size or size
+ usedscale = (usedsize / defaultsize) / scale
+ end
+usedscale = (10 / bodyfontscale) * usedscale
+ --
+ if eplaced then
+if dx then
+ if not find(dx,"%a") then
+ dx = dx .. "bp"
+ elseif find(dx,"e") then
+ dx = formatters["%s\\dimexpr %s\\relax"](usedscale,dx)
+ end
+else
+ dx = "0pt"
+end
+if dy then
+ if not find(dy,"%a") then
+ dy = dy .. "bp"
+ elseif find(dy,"e") then
+ dy = formatters["%s\\dimexpr %s\\relax"](usedscale,dy)
+ end
+else
+ dy = "0pt"
+end
+ t[#t+1] = f_placed(dx,dy)
+ end
+ --
+-- if usedscale == 1 then
+-- t[#t+1] = f_normal( v_family,v_weight,v_style)
+-- else
+ t[#t+1] = f_scaled(usedscale,v_family,v_weight,v_style)
+-- end
+ t[#t+1] = "{"
+ --
+ if trace_fonts then
+ report("element : %s",c.tg)
+ report(" font family : %s",v_family)
+ report(" font weight : %s",v_weight)
+ report(" font style : %s",v_style)
+ report(" parent size : %s",size)
+ report(" parent scale : %s",scale)
+ report(" used size : %s",usedsize)
+ report(" used scale : %s",usedscale)
+ report(" layered : %l",elayered)
+ end
+ --
+ local ecolored = v_fill and v_fill ~= "" or false
+ if ecolored then
+ -- todo cmyk
+ local r, g, b = colorcomponents(v_fill)
+ if r and g and b then
+ t[#t+1] = f_colored(r,g,b)
+ else
+ ecolored = false
+ end
+ end
+ --
+ local dt = c.dt
+ local nt = #dt
+ for i=1,nt do
+ local di = dt[i]
+ if type(di) == "table" then
+ -- can be a tspan (should we pass dx too)
+ collect(t,di,x,y,usedsize,usedscale,v_family)
+ else
+ if i == 1 then
+ di = gsub(di,"^%s+","")
+ end
+ if i == nt then
+ di = gsub(di,"%s+$","")
+ end
+ local chars = utfsplit(di)
+ if svghash then
+ di = f_hashed(svghash[di])
+ elseif tx then
+ for i=1,#chars do
+ chars[i] = f_poschar(
+ (tx[i] or 0) - x,
+ (ty[i] or 0) - y,
+ utfbyte(chars[i])
+ )
+ end
+ di = "{" .. concat(chars) .. "}"
+ else
+ -- this needs to be texescaped ! and even quotes and newlines
+ -- or we could register it but that's a bit tricky as we nest
+ -- and don't know what we can expect here
+ -- di = lpegmatch(p_texescape,di) or di
+ for i=1,#chars do
+ chars[i] = f_char(utfbyte(chars[i]))
+ end
+ di = concat(chars)
+ end
+ t[#t+1] = di
+ end
+ end
+ --
+ if ecolored then
+ t[#t+1] = "}"
+ end
+ --
+ t[#t+1] = "}"
+ --
+ if eplaced then
+ t[#t+1] = "}"
+ end
+ if elayered then
+ t[#t+1] = "}"
+ end
+ --
+ return t
+ end
+
+ local s_startlayer = "\\svgstartlayer "
+ local s_stoplayer = "\\svgstoplayer "
+
+ function handlers.text(c)
+ local only = fullstrip(xmltextonly(c))
+ -- if metapost.processing() then
+ local at = c.at
+ local x = rawget(at,"x")
+ local y = rawget(at,"y")
+
+-- local dx = rawget(at,"dx")
+-- local dy = rawget(at,"dy")
+
+ local tx = asnumber_vx_t(x)
+ local ty = asnumber_vy_t(y)
+
+-- dx = dx and asnumber_vx(dx) or 0
+-- dy = dy and asnumber_vy(dy) or 0
+
+ x = tx[1] or 0 -- catch bad x/y spec
+ y = ty[1] or 0 -- catch bad x/y spec
+
+ local v_fill = at["fill"]
+ if not v_fill or v_fill == "none" then
+ v_fill = "black"
+ end
+ local color, opacity, invisible = fillproperties(v_fill,at)
+ local anchor = anchors[at["text-anchor"] or "start"] or "drt"
+ local r = metapost.remappedtext(only)
+-- x = x + dx
+-- y = y + dy
+ if r then
+ if x == 0 and y == 0 then
+ only = f_mapped_simple_svg(r.index)
+ else
+ only = f_mapped_normal_svg(r.index,x,y)
+ end
+ flushobject(only,at,color,opacity)
+ if trace_text then
+ report("text: %s",only)
+ end
+ elseif not invisible then -- can be an option
+ local scale = 1
+ local textid = 0
+ local result = { }
+ local nx = #tx
+ local ny = #ty
+ --
+ result[#result+1] = s_startlayer
+ if nx > 1 or ny > 1 then
+ concat(collect(result,c,x,y,defaultsize,1,"serif",tx,ty))
+ else
+ concat(collect(result,c,x,y,defaultsize,1,"serif"))
+ end
+ result[#result+1] = s_stoplayer
+ result = concat(result)
+ if x == 0 and y == 0 then
+ result = f_text_simple_svg(anchor,result)
+ else
+ result = f_text_normal_svg(anchor,result,x,y)
+ end
+ flushobject(result,at,color,opacity)
+ if trace_text then
+ report("text: %s",result)
+ end
+ elseif trace_text then
+ report("invisible text: %s",only)
+ end
+ -- elseif trace_text then
+ -- report("ignored text: %s",only)
+ -- end
+ end
+
+ function metapost.reportsvgfonts()
+ for family, weights in sortedhash(usedfonts) do
+ for weight, styles in sortedhash(weights) do
+ for style in sortedhash(styles) do
+ report("used font: %s-%s-%s",family,weight,style)
+ end
+ end
+ end
+ end
+
+ statistics.register("used svg fonts",function()
+ if next(usedfonts) then
+ -- also in log file
+ logs.startfilelogging(report,"used svg fonts")
+ local t = { }
+ for family, weights in sortedhash(usedfonts) do
+ for weight, styles in sortedhash(weights) do
+ for style in sortedhash(styles) do
+ report("%s-%s-%s",family,weight,style)
+ t[#t+1] = formatters["%s-%s-%s"](family,weight,style)
+ end
+ end
+ end
+ logs.stopfilelogging()
+ return concat(t," ")
+ end
+ end)
+
+ end
+
+ function handlers.svg(c,x,y,w,h,noclip,notransform,normalize)
+ local at = c.at
+
+ local wrapupviewport
+ local bhacked
+ local ehacked
+ local wd = w
+ -- local ex, em
+ local xpct, ypct, rpct
+
+ local btransform, etransform, transform = handletransform(at)
+
+bodyfontscale = tex.getdimen("bodyfontsize") / 65536
+
+ if trace then
+ report("view: %s, xpct %N, ypct %N","before",percentage_x,percentage_y)
+ end
+
+ local viewbox = at.viewBox
+
+ if viewbox then
+ x, y, w, h = handleviewbox(viewbox)
+ if trace then
+ report("viewbox: x %N, y %N, width %N, height %N",x,y,w,h)
+ end
+ end
+ if not w or not h or w == 0 or h == 0 then
+ noclip = true
+ end
+ if h then
+ --
+ -- em = factors["em"]
+ -- ex = factors["ex"]
+ -- factors["em"] = em
+ -- factors["ex"] = ex
+ --
+ xpct = percentage_x
+ ypct = percentage_y
+ rpct = percentage_r
+ percentage_x = w / 100
+ percentage_y = h / 100
+ percentage_r = (sqrt(w^2 + h^2) / sqrt(2)) / 100
+ if trace then
+ report("view: %s, xpct %N, ypct %N","inside",percentage_x,percentage_y)
+ end
+ wrapupviewport = viewport(x,y,w,h,noclip)
+ end
+ -- todo: combine transform and offset here
+
+ -- some fonts need this (bad transforms + viewbox)
+ if v and normalize and w and wd and w ~= wd and w > 0 and wd > 0 then
+ bhacked = s_wrapped_start
+ ehacked = f_wrapped_stop(y or 0,wd/w)
+ end
+ if btransform then
+ r = r + 1 ; result[r] = btransform
+ end
+ if bhacked then
+ r = r + 1 ; result[r] = bhacked
+ end
+ local boffset, eoffset = offset(at)
+ if boffset then
+ r = r + 1 result[r] = boffset
+ end
+
+ at["transform"] = false
+ at["viewBox"] = false
+
+ process(c,"/*")
+
+ at["transform"] = transform
+ at["viewBox"] = viewbox
+
+ if eoffset then
+ r = r + 1 result[r] = eoffset
+ end
+ if ehacked then
+ r = r + 1 ; result[r] = ehacked
+ end
+ if etransform then
+ r = r + 1 ; result[r] = etransform
+ end
+ if h then
+ --
+ -- factors["em"] = em
+ -- factors["ex"] = ex
+ --
+ percentage_x = xpct
+ percentage_y = ypct
+ percentage_r = rpct
+ if wrapupviewport then
+ wrapupviewport()
+ end
+ end
+ if trace then
+ report("view: %s, xpct %N, ypct %N","after",percentage_x,percentage_y)
+ end
+ end
+
+ end
+
+ process = function(x,p)
+ for c in xmlcollected(x,p) do
+ local tg = c.tg
+ local h = handlers[c.tg]
+ if h then
+ h(c)
+ end
+ end
+ end
+
+ -- For huge inefficient files there can be lots of garbage to collect so
+ -- maybe we should run the collector when a file is larger than say 50K.
+
+ function metapost.svgtomp(specification,pattern,notransform,normalize)
+ local mps = ""
+ local svg = specification.data
+ if type(svg) == "string" then
+ svg = xmlconvert(svg)
+ end
+ if svg then
+ local c = xmlfirst(svg,pattern or "/svg")
+ if c then
+ root = svg
+ result = { }
+ r = 0
+ definitions = { }
+ tagstyles = { }
+ classstyles = { }
+ colormap = specification.colormap
+ usedcolors = trace_colors and setmetatableindex("number") or false
+ for s in xmlcollected(c,"style") do -- can also be in a def, so let's play safe
+ handlestyle(c)
+ end
+ handlechains(c)
+ xmlinheritattributes(c) -- put this in handlechains
+ handlers.svg (
+ c,
+ specification.x,
+ specification.y,
+ specification.width,
+ specification.height,
+ specification.noclip,
+ notransform,
+ normalize,
+ specification.remap
+ )
+ if trace_result then
+ report("result graphic:\n %\n t",result)
+ end
+ if usedcolors and next(usedcolors) then
+ report("graphic %a uses colors: %s",specification.id or "unknown",table.sequenced(usedcolors))
+ end
+ mps = concat(result," ")
+ root = false
+ result = false
+ r = false
+ definitions = false
+ tagstyles = false
+ classstyles = false
+ colormap = false
+ else
+ report("missing svg root element")
+ end
+ else
+ report("bad svg blob")
+ end
+ return mps
+ end
+
+end
+
+-- These helpers might move to their own module .. some day ... also they will become
+-- a bit more efficient, because we now go to mp and back which is kind of redundant,
+-- but for now it will do.
+
+do
+
+ local bpfactor = number.dimenfactors.bp
+
+ function metapost.includesvgfile(filename,offset) -- offset in sp
+ if lfs.isfile(filename) then
+ context.startMPcode("doublefun")
+ context('draw lmt_svg [ filename = "%s", offset = %N ] ;',filename,(offset or 0)*bpfactor)
+ context.stopMPcode()
+ end
+ end
+
+ function metapost.includesvgbuffer(name,offset) -- offset in sp
+ context.startMPcode("doublefun")
+ context('draw lmt_svg [ buffer = "%s", offset = %N ] ;',name or "",(offset or 0)*bpfactor)
+ context.stopMPcode()
+ end
+
+ interfaces.implement {
+ name = "includesvgfile",
+ actions = metapost.includesvgfile,
+ arguments = { "string", "dimension" },
+ }
+
+ interfaces.implement {
+ name = "includesvgbuffer",
+ actions = metapost.includesvgbuffer,
+ arguments = { "string", "dimension" },
+ }
+
+ function metapost.showsvgpage(data)
+ local dd = data.data
+ if not dd then
+ local fn = data.filename
+ dd = fn and table.load(fn)
+ end
+ if type(dd) == "table" then
+ local comment = data.comment
+ local offset = data.pageoffset
+ local index = data.index
+ local first = math.max(index or 1,1)
+ local last = math.min(index or #dd,#dd)
+ for i=first,last do
+ local d = setmetatableindex( {
+ data = dd[i],
+ comment = comment and i or false,
+ pageoffset = offset or nil,
+ }, data)
+ metapost.showsvgpage(d)
+ end
+ elseif data.method == "code" then
+ context.startMPcode(doublefun)
+ context(metapost.svgtomp(data))
+ context.stopMPcode()
+ else
+ context.startMPpage { instance = "doublefun", offset = data.pageoffset or nil }
+ context(metapost.svgtomp(data))
+ local comment = data.comment
+ if comment then
+ context("draw boundingbox currentpicture withcolor .6red ;")
+ context('draw textext.bot("\\strut\\tttf %s") ysized (10pt) shifted center bottomboundary currentpicture ;',comment)
+ end
+ context.stopMPpage()
+ end
+ end
+
+ function metapost.typesvgpage(data)
+ local dd = data.data
+ if not dd then
+ local fn = data.filename
+ dd = fn and table.load(fn)
+ end
+ if type(dd) == "table" then
+ local index = data.index
+ if index and index > 0 and index <= #dd then
+ data = dd[index]
+ else
+ data = nil
+ end
+ end
+ if type(data) == "string" and data ~= "" then
+ buffers.assign("svgpage",data)
+ context.typebuffer ({ "svgpage" }, { option = "XML", strip = "yes" })
+ end
+ end
+
+ function metapost.svgtopdf(data,...)
+ local mps = metapost.svgtomp(data,...)
+ if mps then
+ -- todo: special instance, only basics needed
+ local pdf = metapost.simple("metafun",mps,true,false,"svg")
+ if pdf then
+ return pdf
+ else
+ -- message
+ end
+ else
+ -- message
+ end
+ end
+
+end
+
+do
+
+ local runner = sandbox.registerrunner {
+ name = "otfsvg2pdf",
+ program = "context",
+ template = "--batchmode --purgeall --runs=2 %filename%",
+ reporter = report_svg,
+ }
+
+ -- By using an independent pdf file instead of pdf streams we can use resources and still
+ -- cache. This is the old method updated. Maybe a future version will just do this runtime
+ -- but for now this is the most efficient method.
+
+ local decompress = gzip.decompress
+ local compress = gzip.compress
+
+ function metapost.svgshapestopdf(svgshapes,pdftarget,report_svg)
+ local texname = "temp-otf-svg-to-pdf.tex"
+ local pdfname = "temp-otf-svg-to-pdf.pdf"
+ local tucname = "temp-otf-svg-to-pdf.tuc"
+ local nofshapes = #svgshapes
+ local pdfpages = { filename = pdftarget }
+ local pdfpage = 0
+ local t = { }
+ local n = 0
+ --
+ os.remove(texname)
+ os.remove(pdfname)
+ os.remove(tucname)
+ --
+ if report_svg then
+ report_svg("processing %i svg containers",nofshapes)
+ statistics.starttiming(pdfpages)
+ end
+ --
+ -- can be option:
+ --
+ -- n = n + 1 ; t[n] = "\\nopdfcompression"
+ --
+ n = n + 1 ; t[n] = "\\starttext"
+ n = n + 1 ; t[n] = "\\setupMPpage[alternative=offset,instance=doublefun]"
+ --
+ for i=1,nofshapes do
+ local entry = svgshapes[i]
+ local data = entry.data
+ if decompress then
+ data = decompress(data) or data
+ end
+ local specification = {
+ data = xmlconvert(data),
+ x = 0,
+ y = 1000,
+ width = 1000,
+ height = 1000,
+ noclip = true,
+ }
+ for index=entry.first,entry.last do
+ if not pdfpages[index] then
+ pdfpage = pdfpage + 1
+ pdfpages[index] = pdfpage
+ local pattern = "/svg[@id='glyph" .. index .. "']"
+ n = n + 1 ; t[n] = "\\startMPpage"
+ n = n + 1 ; t[n] = metapost.svgtomp(specification,pattern,true,true) or ""
+ n = n + 1 ; t[n] = "\\stopMPpage"
+ end
+ end
+ end
+ n = n + 1 ; t[n] = "\\stoptext"
+ io.savedata(texname,concat(t,"\n"))
+ runner { filename = texname }
+ os.remove(pdftarget)
+ file.copy(pdfname,pdftarget)
+ if report_svg then
+ statistics.stoptiming(pdfpages)
+ report_svg("svg conversion time %s",statistics.elapsedseconds(pdfpages))
+ end
+ os.remove(texname)
+ os.remove(pdfname)
+ os.remove(tucname)
+ return pdfpages
+ end
+
+ function metapost.svgshapestomp(svgshapes,report_svg)
+ local nofshapes = #svgshapes
+ local mpshapes = { }
+ if report_svg then
+ report_svg("processing %i svg containers",nofshapes)
+ statistics.starttiming(mpshapes)
+ end
+ for i=1,nofshapes do
+ local entry = svgshapes[i]
+ local data = entry.data
+ if decompress then
+ data = decompress(data) or data
+ end
+ local specification = {
+ data = xmlconvert(data),
+ x = 0,
+ y = 1000,
+ width = 1000,
+ height = 1000,
+ noclip = true,
+ }
+ for index=entry.first,entry.last do
+ if not mpshapes[index] then
+ local pattern = "/svg[@id='glyph" .. index .. "']"
+ local mpcode = metapost.svgtomp(specification,pattern,true,true) or ""
+ if mpcode ~= "" and compress then
+ mpcode = compress(mpcode) or mpcode
+ end
+ mpshapes[index] = mpcode
+ end
+ end
+ end
+ if report_svg then
+ statistics.stoptiming(mpshapes)
+ report_svg("svg conversion time %s",statistics.elapsedseconds(mpshapes))
+ end
+ return mpshapes
+ end
+
+ function metapost.svgglyphtomp(fontname,unicode)
+ if fontname and unicode then
+ local id = fonts.definers.internal { name = fontname }
+ if id then
+ local tfmdata = fonts.hashes.identifiers[id]
+ if tfmdata then
+ local properties = tfmdata.properties
+ local svg = properties.svg
+ local hash = svg and svg.hash
+ local timestamp = svg and svg.timestamp
+ if hash then
+ local svgfile = containers.read(fonts.handlers.otf.svgcache,hash)
+ local svgshapes = svgfile and svgfile.svgshapes
+ if svgshapes then
+ if type(unicode) == "string" then
+ unicode = utfbyte(unicode)
+ end
+ local chardata = tfmdata.characters[unicode]
+ local index = chardata and chardata.index
+ if index then
+ for i=1,#svgshapes do
+ local entry = svgshapes[i]
+ if index >= entry.first and index <= entry.last then
+ local data = entry.data
+ if data then
+ local root = xml.convert(gzip.decompress(data) or data)
+ return metapost.svgtomp (
+ {
+ data = root,
+ x = 0,
+ y = 1000,
+ width = 1000,
+ height = 1000,
+ noclip = true,
+ },
+ "/svg[@id='glyph" .. index .. "']",
+ true,
+ true
+ )
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-aux.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mult-aux.mkxl
new file mode 100644
index 00000000000..40667e17d85
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-aux.mkxl
@@ -0,0 +1,1172 @@
+%D \module
+%D [ file=mult-aux,
+%D version=2010.08.2,
+%D title=\CONTEXT\ Multilingual Macros,
+%D subtitle=Helpers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D A generalization of \MKIV-like inheritance. Just something to play with
+%D (interface might change). The code here evolved in an email exchange between me
+%D and Wolgang Schuster.
+
+\writestatus{loading}{ConTeXt Multilingual Macros / Helpers}
+
+\registerctxluafile{mult-aux}{}
+
+\unprotect
+
+\immutable\edef\??empty{\Uchar25} \immutable\letvalue{\Uchar25}\empty % hex 19
+
+% \edef\s!parent{\Uchar29} % inlining  is ugly, a tiny bit faster, but neglectable on a run
+
+%D \starttyping
+%D \unprotect
+%D \def\????aa{@@@@aa}
+%D
+%D \installparameterhandler \????aa {whatever}
+%D \installsetuphandler \????aa {whatever}
+%D \installdefinehandler \????aa {whatever} \????aa % #3 == defaultroot
+%D \installfontandcolorhandler\????aa {whatever}
+%D
+%D % \installcommandhandler \????aa {whatever} \????aa
+%D \protect
+%D
+%D % \whateverparameter \c!test
+%D % \whateverparameterhash \c!test
+%D % \namedwhateverparameter \mycurrentwhatever \c!test
+%D % \usewhateverstyleandcolor \c!style \c!color
+%D % \everydefinewhatever (sets \currentwhatever)
+%D % \everypresetwhatever (can be used to reset parameters as we can redefine)
+%D % \everysetupwhatever (sets \currentwhatever)
+%D
+%D \starttext
+%D \definewhatever[first] \definewhatever[second][first]
+%D test: \def\currentwhatever{first} \whateverparameter{method} \par
+%D \setupwhatever [method=unset] test: \def\currentwhatever{first} \whateverparameter{method} \par
+%D \setupwhatever[first] [method=first] test: \def\currentwhatever{first} \whateverparameter{method} \par
+%D test: \def\currentwhatever{second} \whateverparameter{method} \par
+%D \setupwhatever[second][method=second] test: \def\currentwhatever{second} \whateverparameter{method} \par
+%D \stoptext
+%D \stoptyping
+
+% problem: every* could clash
+%
+% There can be less {} in the following definitions if we assume \??aa and \c!somecs
+%
+% todo: \def\detokenized...parameter#1{\detokenize\expandafter\expandafter\expandafter{\csname#1#2\endcsname}} % always root
+%
+% it might be more efficient to do this at the lua and
+%
+% watch the push/pop and predefinition of current .. this is needed for nested
+% definitions and overloaded defines using the predefined one
+
+% todo: add (relaxed) postsetup and postdefine hooks, just after the everys
+
+%D Start of experimental code: especially tables can have many assignments and
+%D although most time is spent in the typesetting anyway, we can squeeze out a
+%D little bit. Of course having 500 rows of 50 columns each with some setting does
+%D not happen that often. One should keep in mind that in the average document
+%D having some 500 assignments is no exception but there we're talking of
+%D neglectable runtime for them. Of course in the definitions below there is no real
+%D gain, only in the generated \setup* commands. Another situation with many
+%D assignments is \XML\ where we can pass attributes and normally don't do testing
+%D of them making sense.
+%
+% \testfeatureonce{100000}{\getparameters[bla][a=111,b=222,c=333]}% 1.669s
+% \testfeatureonce{100000}{\mult_interfaces_get_parameters{bla} [a=111,b=222,c=333]}% 1.529s
+% \testfeatureonce{100000}{\def\m_mult_interfaces_namespace{bla}\mult_interfaces_get_parameters_indeed[a=111,b=222,c=333]}% 1.466s
+
+% KEEP THIS:
+%
+% \let\m_mult_interfaces_namespace\empty
+%
+% \def\mult_interfaces_get_parameters#1[#2%
+% {\if\noexpand#2]%
+% \expandafter\gobbleoneargument
+% \else
+% \def\m_mult_interfaces_namespace{#1}%
+% \expandafter\mult_interfaces_get_parameters_indeed
+% \fi#2}
+%
+% \def\mult_interfaces_get_parameters_indeed#1]% namespace already set
+% {\mult_interfaces_get_parameters_item#1,],^^^^0004}
+%
+% \def\mult_interfaces_get_parameters_item#1,#2% #2 takes space before ,
+% {\if,#1,% dirty trick for testing #1=empty
+% \expandafter\mult_interfaces_get_parameters_item
+% \orelse\if]#1%
+% \expandafter\gobbleoneargument
+% \else
+% \mult_interfaces_get_parameters_assign#1==\empty^^^^0004%
+% % \expandafter\mult_interfaces_get_parameters_item % saves skipping when at end
+% \fi#2}
+%
+% \def\mult_interfaces_get_parameters_error#1#2% #3%
+% {\mult_interfaces_get_parameters_error_indeed{#1}{#2}%
+% \gobbleoneargument}
+%
+% \def\mult_interfaces_get_parameters_error_indeed#1#2%
+% {\showassignerror{#2}{\the\inputlineno\space(#1)}}
+%
+% \def\mult_interfaces_get_parameters_assign#1=#2=#3#0^^^^0004%
+% {\ifx\empty#1\empty
+% \expandafter\mult_interfaces_get_parameters_error
+% \orelse\ifx#3\empty
+% \expandafter\mult_interfaces_get_parameters_error
+% \else
+% \expandafter\mult_interfaces_adef % assignment def
+% \fi
+% \m_mult_interfaces_namespace{#1}{#2}%
+% \doubleexpandafter\mult_interfaces_get_parameters_item}
+%
+% \startinterface english
+%
+% % some 10% faster
+%
+% \let\mult_interfaces_get_parameters_error\undefined
+%
+% \def\mult_interfaces_get_parameters_error_one#0\csname#2#3\endcsname#0%
+% {\mult_interfaces_get_parameters_error_indeed{#2}{#3}\iftrue}
+%
+% \def\mult_interfaces_get_parameters_error_two#0\csname#2#3\endcsname#0%
+% {\mult_interfaces_get_parameters_error_indeed{#2}{#3}}
+%
+% \def\mult_interfaces_get_parameters_assign#1=#2=#3#0^^^^0004%
+% {\ifx\empty#1\empty
+% \mult_interfaces_get_parameters_error_one
+% \orelse\ifx#3\empty
+% \mult_interfaces_get_parameters_error_two
+% \else
+% \defcsname\m_mult_interfaces_namespace#1\endcsname{#2}%
+% \fi
+% \doubleexpandafter\mult_interfaces_get_parameters_item}
+%
+% \stopinterface
+%
+% \newif\ifassignment
+%
+% \def\mult_check_for_assignment_indeed#0=#2#0^^^^0004%
+% {\if#2^^^^0003\assignmentfalse\else\assignmenttrue\fi}
+%
+% \def\mult_check_for_assignment_indeed_begin_#0=#2#0^^^^0004%
+% {\if#2^^^^0003}
+%
+% \def\mult_check_for_assignment#1%
+% {\expandafter\mult_check_for_assignment_indeed\detokenize{#1}=^^^^0003^^^^0003^^^^0004}
+%
+% % End of experimental code.
+
+%D We keep the above as reference, but from now on experiment with the following
+%D implementation. In principle this one is faster, but because normally we don't
+%D have that many assignments it doesn't get noticed. For instance, the \LUAMETATEX\
+%D manual does less than 6000 assignments and the payoff starts around a tenfold of
+%D that. But \unknown\ it's less tracing, so that's another benefit.
+
+%D Watch out: we accept that a \[k=v,,k=v] will generate a case where a key can become
+%D a comma and trigger a warning but we intercept that elsewhere. The alternative is
+%D to skip to the comma first which takes more time.
+
+\let\m_mult_interfaces_namespace\empty
+
+\def\mult_interfaces_get_parameters#1[%
+ {\def\m_mult_interfaces_namespace{#1}%
+ \futureexpandis]\gobbleoneargument\mult_interfaces_get_parameters_indeed}
+
+\def\mult_interfaces_get_parameters_indeed#1]%
+ {\mult_interfaces_get_parameters_item#1\ignorearguments\ignorearguments}
+
+\def\mult_interfaces_get_parameters_item#*#1,%
+ {\ifarguments \else
+ \mult_interfaces_get_parameters_item_okay#1,\ignorearguments
+ \expandafter\mult_interfaces_get_parameters_item
+ \fi}
+
+\def\mult_interfaces_get_parameters_item_okay#1=#2,%
+ {\ifarguments
+ % done
+ \or
+ \mult_interfaces_get_parameters_item_error
+ \else
+ \mult_interfaces_adef\m_mult_interfaces_namespace{#1}{#2}%
+ \fi}
+
+\def\mult_interfaces_get_parameters_item_error#-#-#-#1#2%
+ {\if,#2\else\mult_interfaces_get_parameters_error_indeed{#1}{#2}\fi}
+
+\startinterface english
+
+ \def\mult_interfaces_get_parameters_item_okay#1=#2,%
+ {\ifarguments
+ % done
+ \or
+ \mult_interfaces_get_parameters_item_error
+ \else
+ \defcsname\m_mult_interfaces_namespace#1\endcsname{#2}%
+ \fi}
+
+ \def\mult_interfaces_get_parameters_item_error#-\defcsname#1#2\endcsname#-%
+ {\if,#2\else\mult_interfaces_get_parameters_error_indeed{#1}{#2}\fi}
+
+\stopinterface
+
+%D Used?
+
+\def\mult_check_for_assignment_indeed#-=#1#-^^^^0004%
+ {\if#1^^^^0003\assignmentfalse\else\assignmenttrue\fi}
+
+\def\mult_check_for_assignment_indeed_begin_#-=#1#-^^^^0004%
+ {\if#1^^^^0003}
+
+\def\mult_check_for_assignment#1%
+ {\expandafter\mult_check_for_assignment_indeed\detokenize{#1}=^^^^0003^^^^0003^^^^0004}
+
+%D Beware, zero arguments is an assignment!
+
+\def\mult_aux_no_assignment_indeed#1=#2]{\ifcase\ifarguments\plusone\or\zerocount\else\plusone\fi}
+\def\mult_aux_no_assignment#1]{\mult_aux_no_assignment_indeed#1\ignorearguments\relax}
+
+%D This can give wrong results when we pass e.g. \type{\c!format}, so either we need
+%D to use the \type {\k!} ones, but these are not defined in the english interface
+%D so from now on we assume that the low level ones are used with the symbolic names
+%D and that only the high level setup commands are used with language specific
+%D interfaces.
+
+% \startinterface english
+ \protected\def\mult_interfaces_let #1#2{\letcsname#1#2\endcsname}
+ \protected\def\mult_interfaces_lete#1#2{\letcsname#1#2\endcsname\empty}
+ \protected\def\mult_interfaces_def #1#2{\defcsname#1#2\endcsname}
+ \protected\def\mult_interfaces_edef#1#2{\edefcsname#1#2\endcsname}
+ \protected\def\mult_interfaces_gdef#1#2{\gdefcsname#1#2\endcsname}
+ \protected\def\mult_interfaces_xdef#1#2{\xdefcsname#1#2\endcsname}
+% \stopinterface
+
+%D Do, we only interface the assignment definition:
+
+\protected\def\mult_interfaces_adef#1#2{\defcsname#1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname}
+
+\startinterface english
+ \protected\def\mult_interfaces_adef#1#2{\defcsname#1#2\endcsname}
+\stopinterface
+
+% the commented detokenized variant that backtracks ... needs testing usage first
+%
+% \let\whatever\relax
+%
+% \definetest[oeps][bagger=\whatever]
+%
+% \def\currenttest{oeps} \edef\hans{\detokenizedtestparameter{bagger}}\meaning\hans\par
+% \def\currenttest{oeps} \edef\hans{\detokenizedtestparameter{reggab}}\meaning\hans\par
+%
+% slower: \def#3##1{\csname\ifcsname#1#2:##1\endcsname\expandafter\csstring\lastnamedcs\else\expandafter#5\csname#1#2:\s!parent\endcsname{##1}\fi\endcsname}%
+
+%D pre-expansion can be a bit faster but handly any effect on a normal run so let's
+%D go for saving some memory
+
+\def\mult_interfaces_detokenize{\expandafter\expandafter\expandafter\detokenize\expandafter\expandafter\expandafter}
+
+\protected\def\mult_interfaces_install_parameter_handler#1#2#3#4#5#6#7#8#9%
+ {\frozen\def#3##1{\csname\ifcsname#1#2:##1\endcsname#1#2:##1\else\expandafter#5\csname#1#2:\s!parent\endcsname{##1}\fi\endcsname}%
+ \frozen\def#4##1##2{\ifcsname##1:##2\endcsname##1:##2\else\expandafter#5\csname##1:\s!parent\endcsname{##2}\fi}%
+ \frozen\edef#5##1##2{\noexpand\ifrelax##1\??empty\noexpand\else\noexpand#4##1{##2}\noexpand\fi}% is {} needed around ##1 ?
+ \frozen\def#6##1##2{\csname\ifcsname#1##1:##2\endcsname#1##1:##2\else\expandafter#5\csname#1##1:\s!parent\endcsname{##2}\fi\endcsname}%
+ \frozen\def#7##1{\detokenize\expandafter\expandafter\expandafter{\csname#1#2:##1\endcsname}}% always root, no backtrack
+ \frozen\def#8##1{\begincsname#1#2:##1\endcsname}%
+ % TODO
+ \frozen\def#9##1##2{\expandafter\let\expandafter##1\csname\ifcsname#1#2:##2\endcsname#1#2:##2\else\expandafter#5\csname#1#2:\s!parent\endcsname{##2}\fi\endcsname}}
+
+\permanent\protected\def\installparameterhandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_parameter_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname #2parameter\endcsname
+ \expandafter\noexpand\csname do#2parameter\endcsname % or: #2_parameter
+ \expandafter\noexpand\csname do#2parentparameter\endcsname % or: #2_parent_parameter
+ \expandafter\noexpand\csname named#2parameter\endcsname
+ \expandafter\noexpand\csname detokenized#2parameter\endcsname
+ \expandafter\noexpand\csname direct#2parameter\endcsname
+ \expandafter\noexpand\csname letfrom#2parameter\endcsname}} % strict#2parameter is gone
+
+\protected\def\mult_interfaces_install_root_parameter_handler#1#2#3%
+ {\frozen\def#2##1{\detokenize\expandafter\expandafter\expandafter{\csname#1:##1\endcsname}}% always root
+ \frozen\def#3##1{\begincsname#1:##1\endcsname}}
+
+\permanent\protected\def\installrootparameterhandler#1#2%
+ {\normalexpanded
+ {\mult_interfaces_install_root_parameter_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname detokenizedroot#2parameter\endcsname
+ \expandafter\noexpand\csname root#2parameter\endcsname}}
+
+\protected\def\mult_interfaces_install_parameter_hash_handler#1#2#3#4#5#6#7#8#9%
+ {\frozen\def#3##1{#1#4{#1#2}{##1}:}% leading #1 was missing .. is this one used?
+ \frozen\def#4##1##2{\ifcsname##1:##2\endcsname##1\else\expandafter#5\csname##1:\s!parent\endcsname{##2}\fi}%
+ \frozen\def#5##1##2{\ifrelax##1\else#4##1{##2}\fi}%
+ \frozen\def#6{#1#2:}%
+ \frozen\def#7##1{#1##1:}%
+ \frozen\def#8{\ifempty#2\orelse\ifcsname#1#2:\s!parent\endcsname\else\letcsname#1#2:\s!parent\endcsname#1\fi}%
+ \frozen\protected\def#9##1{\edefcsname#1##1:\s!parent\endcsname{#1#2}}}
+
+\permanent\protected\def\installparameterhashhandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \immutable\letcsname#2namespace\endcsname#1%
+ \normalexpanded
+ {\mult_interfaces_install_parameter_hash_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname #2parameterhash\endcsname
+ \expandafter\noexpand\csname do#2parameterhash\endcsname % or : #2_parameter_hash
+ \expandafter\noexpand\csname do#2parentparameterhash\endcsname % or : #2_parent_parameter_hash
+ \expandafter\noexpand\csname current#2hash\endcsname
+ \expandafter\noexpand\csname named#2hash\endcsname
+ \expandafter\noexpand\csname check#2parent\endcsname
+ \expandafter\noexpand\csname chaintocurrent#2\endcsname}}
+
+%D In \MKIV\ we can probably use the english variant for all other languages too.
+
+% todo: inline the def/let
+
+% \startinterface english
+ \protected\def\mult_interfaces_install_parameter_set_handler#1#2#3#4#5#6%
+ {\frozen\protected\def#3##1{\defcsname#1#2:##1\endcsname}% ##1 {##2} (braces are mandate)
+ \frozen\protected\def#4##1{\edefcsname#1#2:##1\endcsname}% ##1 {##2} (braces are mandate)
+ \frozen\protected\def#5##1{\letcsname#1#2:##1\endcsname}% ##1 ##2
+ \frozen\protected\def#6##1{\letcsname#1#2:##1\endcsname\empty}}% ##1
+% \stopinterface
+
+\permanent\protected\def\installparametersethandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_parameter_set_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname set#2parameter\endcsname
+ \expandafter\noexpand\csname setexpanded#2parameter\endcsname
+ \expandafter\noexpand\csname let#2parameter\endcsname
+ \expandafter\noexpand\csname reset#2parameter\endcsname}}
+
+\mutable\let\dousecurrentstyleparameter\relax
+\mutable\let\dousecurrentcolorparameter\relax
+
+\mutable\let\currentstyleparameter\empty
+\mutable\let\currentcolorparameter\empty
+
+\protected\def\mult_interfaces_install_style_and_color_handler#1#2#3#4%
+ {\frozen\protected\def#2##1##2% style color
+ {\edef\currentstyleparameter{#1{##1}}% this name is public (can also set color e.g. in underline)
+ \ifempty\currentstyleparameter\else\dousecurrentstyleparameter\fi
+ \edef\currentcolorparameter{#1{##2}}% this name is public (so we do this after the style switch)
+ \ifempty\currentcolorparameter\else\dousecurrentcolorparameter\fi}%
+ \frozen\protected\def#3##1% style
+ {\edef\currentstyleparameter{#1{##1}}% this name is public
+ \ifempty\currentstyleparameter\else\dousecurrentstyleparameter\fi}%
+ \frozen\protected\def#4##1% color
+ {\edef\currentcolorparameter{#1{##1}}% this name is public
+ \ifempty\currentcolorparameter\else\dousecurrentcolorparameter\fi}}
+
+\permanent\protected\def\installstyleandcolorhandler#1#2%
+ {\normalexpanded
+ {\mult_interfaces_install_style_and_color_handler
+ \expandafter\noexpand\csname #2parameter\endcsname
+ \expandafter\noexpand\csname use#2styleandcolor\endcsname % maybe an alias use#2styleandcolorparameters
+ \expandafter\noexpand\csname use#2styleparameter\endcsname
+ \expandafter\noexpand\csname use#2colorparameter\endcsname}}
+
+\def\mult_check_for_parent#1#2#3#4%
+ {\ifcsname#1#4:\s!parent\endcsname\orelse\ifx#4\empty\else
+ \writestatus\m!system{error: invalid parent #4 for #3, #4 defined too (best check it)}%
+ \edefcsname#1#4:\s!parent\endcsname{#2}%
+ \fi}
+
+ \def\mult_interfaces_chain#1#2{\ifcsname#1#2:\s!chain\endcsname\lastnamedcs\space\fi}
+\permanent\def\getparentchain #1#2{\begincsname#1#2:\s!chain\endcsname}
+\permanent\def\getcurrentparentchain#1#2{\begincsname#1#2:\s!chain\endcsname}
+
+% we could have a \setcurrent... macro and then always make them a frozen
+% but it might have a little impact on performance ... something to leave
+% to when we're done with the transition (it's kind of massive)
+
+\protected\def\mult_interfaces_install_define_handler#1#2#3#4#5#6#7#8% why is \expanded still needed in clones
+ {\newtoks#5%
+ \newtoks#6%
+ \frozen\tolerant\protected\def#2[##1]##*[##2]##*[##3]% [child][parent][settings] | [child][settings] | [child][parent] | [child]
+ {\let#8#4%
+ % watch out: no \edef#4{##1} before the ifarguments because #1 can have macros
+ \ifarguments
+ \let#4\empty
+ \the#5% predefine
+ \let#7\empty
+ \letcsname#1#4:\s!chain\endcsname\empty
+ \edefcsname#1#4:\s!parent\endcsname{#3}%
+ \or
+ \edef#4{##1}%
+ \the#5% predefine
+ \let#7\empty
+ \edefcsname#1#4:\s!chain\endcsname{##1}%
+ \edefcsname#1#4:\s!parent\endcsname{#3}%
+ \or
+ \edef#4{##1}%
+ \the#5% predefine
+ \relax
+ \ifhastok={##2}%
+ \let#7\empty
+ \edefcsname#1#4:\s!chain\endcsname{##1}%
+ \edefcsname#1#4:\s!parent\endcsname{#3}%
+ \mult_interfaces_get_parameters{#1#4:}[##2]%
+ \else
+ \edef#7{##2}%
+ \ifempty#7%
+ \edefcsname#1#4:\s!chain\endcsname{##1}%
+ \edefcsname#1#4:\s!parent\endcsname{#3}%
+ \else
+ \mult_check_for_parent{#1}{#3}#4#7%
+ \edefcsname#1#4:\s!chain\endcsname{\mult_interfaces_chain#1{##2}##1}%
+ \edefcsname#1#4:\s!parent\endcsname{#1##2}%
+ \fi
+ \fi
+ \or
+ \edef#4{##1}%
+ \the#5% predefine
+ \edef#7{##2}%
+ \mult_check_for_parent{#1}{#3}#4#7%
+ \edefcsname#1#4:\s!chain\endcsname{\mult_interfaces_chain#1{##2}##1}%
+ \edefcsname#1#4:\s!parent\endcsname{#1##2}%
+ \mult_interfaces_get_parameters{#1#4:}[##3]%
+ \fi
+ \the#6%
+ \let#4#8}}
+
+\permanent\protected\def\installdefinehandler#1#2#3%
+ {\mutable\letcsname current#2\endcsname\empty
+ \mutable\letcsname current#2parent\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_define_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname define#2\endcsname
+ {\noexpand#3}% root
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname everypreset#2\endcsname
+ \expandafter\noexpand\csname everydefine#2\endcsname
+ \expandafter\noexpand\csname current#2parent\endcsname
+ \expandafter\noexpand\csname saved_defined_#2\endcsname}}
+
+\protected\def\mult_interfaces_install_setup_handler#1#2#3#4#5#6#7#8%
+ {\newtoks#4%
+ \newtoks#7%
+ \frozen\protected\def#5{\mult_interfaces_get_parameters{#1#3:}}% no every ! don't change it
+ \frozen\tolerant\protected\def#2[##1]##*[##2]% maybe helper
+ {\let#6#3%
+ \ifnum\lastarguments=\plustwo
+ \def#8####1% we will have a simple one as well
+ {\edef#3{####1}%
+ \mult_interfaces_get_parameters{#1#3:}[##2]%
+ \the#4}%
+ \processcommalist[##1]#8%
+ \else
+ \let#3\empty
+ \mult_interfaces_get_parameters{#1:}[##1]%
+ \the#4%
+ \fi
+ \let#3#6%
+ \the#7}}
+
+\permanent\protected\def\installsetuphandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_setup_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname setup#2\endcsname
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname everysetup#2\endcsname
+ \expandafter\noexpand\csname setupcurrent#2\endcsname
+ \expandafter\noexpand\csname saved_setup_current#2\endcsname
+ \expandafter\noexpand\csname everysetup#2root\endcsname
+ \expandafter\noexpand\csname nested_setup_current#2\endcsname}}
+
+\aliased\let\doingrootsetupnamed\plusone % \setuplayout[name][key=value]
+\aliased\let\doingrootsetuproot \plustwo % \setuplayout [key=value]
+\aliased\let\doingrootsetnamed \plusthree % \setuplayout[name]
+\aliased\let\doingrootsetroot \plusfour % \setuplayout
+
+\protected\def\mult_interfaces_install_switch_setup_handler_a#1#2#3%
+ {\frozen\protected\def#3{\mult_interfaces_get_parameters{#1#2:}}}
+
+\protected\def\mult_interfaces_install_switch_setup_handler_b#1#2#3#4#5#6#7#8#9%
+ {\newtoks#5%
+ \newconstant#2%
+ \newtoks#8%
+ \newtoks#9%
+ \frozen\tolerant\protected\def#4[##1]##*[##2]% maybe helper
+ {\ifarguments
+ % \setuplayout
+ \let#6#3% % previous becomes current
+ \let#3\empty % current becomes empty
+ #2\doingrootsetroot
+ \the#5%
+ \the#8% switchsetups
+ \or
+ \ifhastok={##1}%
+ % \setuplayout[key=value]
+ \let#7#3%
+ \let#6#3%
+ \let#3\empty
+ #2\doingrootsetuproot
+ \mult_interfaces_get_parameters{#1:}[##1]%
+ \the#5%
+ \the#8% switchsetups
+ \let#3#7%
+ \else
+ % \setuplayout[whatever]
+ \let#6#3% % previous becomes current
+ \edef#3{##1}% this will catch reset so one needs to test for it
+ \ifempty#3%
+ \let#7#6%
+ #2\doingrootsetuproot
+ \the#5%
+ \the#8% switchsetups
+ \let#3#7%
+ \else
+ #2\doingrootsetnamed
+ \the#5% % we can check for previous vs current
+ \the#8% switchsetups
+ \fi
+ \fi
+ \or
+ % \setuplayout[whatever][key=value]
+ \let#7#3%
+ \let#6#3%
+ \edef#3{##1}%
+ #2\doingrootsetupnamed
+ \mult_interfaces_get_parameters{#1#3:}[##2]%
+ \the#5%
+ \ifx#3#6\the#8\fi % only switchsetups if previous == current
+ \let#3#7%
+ \fi
+ #2\zerocount % mode is always zero at the end
+ \the#9}}
+
+\permanent\protected\def\installswitchsetuphandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \mutable\letcsname previous#2\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_switch_setup_handler_a
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname setupcurrent#2\endcsname
+ \mult_interfaces_install_switch_setup_handler_b
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname #2setupmode\endcsname
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname setup#2\endcsname
+ \expandafter\noexpand\csname everysetup#2\endcsname
+ \expandafter\noexpand\csname previous#2\endcsname
+ \expandafter\noexpand\csname saved_setup_current#2\endcsname
+ \expandafter\noexpand\csname everyswitch#2\endcsname
+ \expandafter\noexpand\csname everysetup#2root\endcsname}}
+
+\protected\def\mult_interfaces_install_auto_setup_handler#1#2#3#4#5#6#7#8%
+ {\newtoks#4%
+ \frozen\protected\def#5{\mult_interfaces_get_parameters{#1#3:}}%
+ \frozen\tolerant\protected\def#2[##1]##*[##2]##*[##3]%
+ {\let#7#3%
+ \ifarguments
+ \let#3\empty
+ \the#5%
+ \or
+ \let#3\empty
+ \mult_interfaces_get_parameters{#1:}[##1]%
+ \the#4%
+ \or
+ \def#8####1%
+ {\edef#3{####1}%
+ #6% checks parent and sets if needed
+ \mult_interfaces_get_parameters{#1#3:}[##2]%
+ \the#4}%
+ \processcommalist[##1]#8%
+ \or
+ \def#8####1%
+ {\edef#3{####1}%
+ \defcsname#1#3:\s!parent\endcsname{#1##2}%
+ \mult_interfaces_get_parameters{#1#3:}[##3]% always sets parent
+ \the#4}%
+ \processcommalist[##1]#8%
+ \fi
+ \let#3#7}}
+
+\permanent\protected\def\installautosetuphandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_auto_setup_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname setup#2\endcsname
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname everysetup#2\endcsname
+ \expandafter\noexpand\csname setupcurrent#2\endcsname
+ \expandafter\noexpand\csname check#2parent\endcsname
+ \expandafter\noexpand\csname saved_setup_current#2\endcsname
+ \expandafter\noexpand\csname nested_setup_current#2\endcsname}}
+
+\permanent\protected\def\installbasicparameterhandler#1#2%
+ {\installparameterhandler {#1}{#2}%
+ \installparameterhashhandler{#1}{#2}%
+ \installparametersethandler {#1}{#2}%
+ \installrootparameterhandler{#1}{#2}}
+
+\permanent\protected\def\installbasicautosetuphandler#1#2#3% \??self name \??parent (can be \??self)
+ {\installbasicparameterhandler{#1}{#2}%
+ \installautosetuphandler {#1}{#2}}
+
+\permanent\protected\def\installstylisticautosetuphandler#1#2#3% \??self name \??parent (can be \??self)
+ {\installbasicparameterhandler{#1}{#2}%
+ \installautosetuphandler {#1}{#2}%
+ \installstyleandcolorhandler {#1}{#2}}
+
+\permanent\protected\def\installcommandhandler#1#2#3% \??self name \??parent (can be \??self)
+ {\installbasicparameterhandler{#1}{#2}%
+ \installdefinehandler {#1}{#2}{#3}%
+ \installsetuphandler {#1}{#2}%
+ \installstyleandcolorhandler {#1}{#2}}
+
+\permanent\protected\def\installswitchcommandhandler#1#2#3% \??self name \??parent (can be \??self)
+ {\installbasicparameterhandler{#1}{#2}%
+ \installdefinehandler {#1}{#2}{#3}%
+ \installswitchsetuphandler {#1}{#2}%
+ \installstyleandcolorhandler {#1}{#2}}
+
+\permanent\protected\def\installautocommandhandler#1#2#3% automatically defined cloned setups
+ {\installbasicparameterhandler{#1}{#2}%
+ \installdefinehandler {#1}{#2}{#3}%
+ \installautosetuphandler {#1}{#2}%
+ \installstyleandcolorhandler {#1}{#2}}
+
+\permanent\protected\def\installsimplecommandhandler#1#2#3% no define (experiment) - use \check*parent when defining
+ {\installbasicparameterhandler{#1}{#2}%
+ \installsetuphandler {#1}{#2}%
+ \installstyleandcolorhandler {#1}{#2}}
+
+%D Many mechanisms have some kind of inheritance in place, and these are the
+%D speed||critical ones. Therefore there is no reason to stick to \type {\@@xxkey}
+%D for the sake of performance. For this reason we also provide a direct variant.
+%D This permits a more consistent treatment of namespaces. A \type
+%D {\whateverparameter} call is three times slower and a \type
+%D {\directwhateverparameter} call two times but for some 100K expansions we only
+%D loose some .1 second which is neglectable given the small amount of expansions in
+%D real runs.
+
+%D We don't need colons for such simple cases.
+
+\protected\def\mult_interfaces_install_direct_parameter_handler#1#2#3#4#5%
+ {\frozen\def#3##1{\begincsname#1##1\endcsname}%
+ \frozen\def#4##1{\detokenize\expandafter\expandafter\expandafter{\csname#1##1\endcsname}}%
+ \frozen\def#5##1{\begincsname#1##1\endcsname}}
+
+\permanent\protected\def\installdirectparameterhandler#1#2%
+ {\mutable\letcsname current#2\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_direct_parameter_handler
+ {\noexpand#1}%
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname #2parameter\endcsname
+ \expandafter\noexpand\csname detokenized#2parameter\endcsname
+ \expandafter\noexpand\csname direct#2parameter\endcsname}}
+
+\protected\def\mult_interfaces_install_direct_setup_handler#1#2#3#4%
+ {\newtoks#4%
+ \frozen\protected\def#2[##1]{\ifarguments\or\mult_interfaces_get_parameters#1[##1]\fi\the#4}%
+ \frozen\def#3{\mult_interfaces_get_parameters#1}}
+
+\permanent\protected\def\installdirectsetuphandler#1#2%
+ {\normalexpanded
+ {\mult_interfaces_install_direct_setup_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname setup#2\endcsname
+ \expandafter\noexpand\csname setupcurrent#2\endcsname % no \every (we use 'current' for consistency)
+ \expandafter\noexpand\csname everysetup#2\endcsname}}
+
+% \startinterface english
+ \protected\def\mult_interfaces_install_direct_parameter_set_handler#1#2#3#4#5%
+ {\frozen\protected\def#2##1{\defcsname#1##1\endcsname}%
+ \frozen\protected\def#3##1{\edefcsname#1##1\endcsname}%
+ \frozen\protected\def#4##1{\letcsname#1##1\endcsname}%
+ \frozen\protected\def#5##1{\letcsname#1##1\endcsname\empty}}%
+% \stopinterface
+
+\permanent\protected\def\installdirectparametersethandler#1#2%
+ {\normalexpanded
+ {\mult_interfaces_install_direct_parameter_set_handler
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname set#2parameter\endcsname
+ \expandafter\noexpand\csname setexpanded#2parameter\endcsname
+ \expandafter\noexpand\csname let#2parameter\endcsname
+ \expandafter\noexpand\csname reset#2parameter\endcsname}}
+
+\aliased\let\installdirectstyleandcolorhandler\installstyleandcolorhandler
+
+\permanent\protected\def\installdirectcommandhandler#1#2%
+ {\installdirectparameterhandler {#1}{#2}%
+ \installdirectsetuphandler {#1}{#2}%
+ \installdirectparametersethandler {#1}{#2}%
+ \installdirectstyleandcolorhandler{#1}{#2}}
+
+\permanent\protected\def\installsetuponlycommandhandler#1#2%
+ {\installdirectparameterhandler {#1}{#2}%
+ \installdirectsetuphandler {#1}{#2}%
+ \installdirectparametersethandler{#1}{#2}}
+
+% Experiment:
+
+% \installcorenamespace {one}
+% \installcorenamespace {two}
+%
+% \installcommandhandler \??one {one} \??one
+% \installcommandhandler \??two {two} \??two
+%
+% \defineone[test] \setupone[test][alpha=first]
+% \definetwo[test] \setuptwo[test][beta=second]
+%
+% \protect
+%
+% \def\currentone{test}
+% \def\currenttwo{test}
+%
+% \relateparameterhandlers {two} {test} {one} {test}
+%
+% yes:\oneparameter{alpha}\par
+% nop:\oneparameter{beta}\par
+% yes:\twoparameter{alpha}\par
+% yes:\twoparameter{beta}\par
+
+\permanent\protected\def\relateparameterhandlers#1#2#3#4% {from} {instance} {to} {instance}
+ {\immutable\edefcsname\csname#1namespace\endcsname#2:\s!parent\endcsname{\csname#3namespace\endcsname#4}}
+
+\permanent\protected\def\relateparameterhandlersbyns#1#2#3#4% {from} {instance} {to} {instance}
+ {\edefcsname#1#2:\s!parent\endcsname{#3#4}}
+
+%D Here is another experiment:
+
+\protected\def\mult_interfaces_install_action_handler#1#2#3%
+ {\frozen\tolerant\protected\defcsname#1\endcsname[##1]##*[##2]%
+ {\begingroup
+ \ifarguments
+ \let#2\empty
+ \or
+ %\ifcondition\expandafter\mult_check_for_assignment_indeed_begin_\detokenize{##1}=^^^^0003^^^^0003^^^^0004%
+ % \ifcondition\mult_aux_no_assignment_indeed##1\ignorearguments
+ % \edef#2{##1}%
+ % \else
+ % \let#2\empty
+ % #3[##1]%
+ % \fi
+ \ifhastok={##1}%
+ \let#2\empty
+ #3[##1]%
+ \else
+ \edef#2{##1}%
+ \fi
+ \or
+ \edef#2{##1}%
+ #3[##2]%
+ \fi
+ \directsetup{handler:action:#1}%
+ \endgroup}}
+
+\permanent\protected\def\installactionhandler#1%
+ {\mutable\letcsname current#1\endcsname\empty
+ \normalexpanded
+ {\mult_interfaces_install_action_handler
+ {#1}%
+ \expandafter\noexpand\csname current#1\endcsname
+ \expandafter\noexpand\csname setupcurrent#1\endcsname}}
+
+% First we had, in tune with the regular system variables:
+%
+% \starttyping
+% \protected\def\installnamespace#1{\setvalue{????#1}{@@@@#1}}
+% \stoptyping
+%
+% The following variant is nicer and in principle faster but that gets unnoticed
+% unless lots of expansion happens. Also, we can use long tags but the internal
+% expansion will be relatively small (and unlikely more than 4 characters). For
+% instance, \??xx used to expand to @@xx but now becomes for instance 123::. This
+% is one character more but in quite some cases we had : after such a tag in the
+% old situation. In the new situation we create more namespaces and don't need that
+% : any more, so we end up with on the average the same amount of tokens and
+% definitely less when we consider cases like \??xx:\c!align: which now is just
+% \??somealign and therefore has length 5 now (instead of 4+1+5+1=10).
+%
+% Eventualy we will have a verbose \blablanamespace and the difference between core
+% and regular can go ... after all, \xxxparameter can already clash between the two
+% prefix groups .. if users use this mechanism a lot they should use verbose names
+% anyway (the old two character names were mostly an optimization as they also
+% expanded to these characters).
+
+% todo: register namespaces at lua end for logging and reverse resolve
+% todo: move this to syst-ini so that we can use it real early
+
+\newcount\c_mult_interfaces_n_of_namespaces
+
+\def\v_interfaces_prefix_template
+ {\number\c_mult_interfaces_n_of_namespaces>}
+
+\permanent\protected\def\installnamespace#1% for modules and users
+ {\ifcsname ????#1\endcsname
+ \writestatus\m!system{duplicate user namespace '#1'}\wait
+ \else
+ \global\advance\c_mult_interfaces_n_of_namespaces\plusone
+ \immutable\edefcsname ????#1\endcsname{\v_interfaces_prefix_template}%
+ \fi}
+
+\permanent\protected\def\installcorenamespace#1%
+ {\ifcsname ??#1\endcsname
+ \writestatus\m!system{duplicate core namespace '#1'}\wait
+ \else
+ \global\advance\c_mult_interfaces_n_of_namespaces\plusone
+ \immutable\edefcsname ??#1\endcsname{\v_interfaces_prefix_template}%
+ \clf_registernamespace\c_mult_interfaces_n_of_namespaces{#1}%
+ \fi}
+
+\def\mult_interfaces_get_parameters_error_indeed#1#2%
+ {\clf_showassignerror{#1}{#2}\inputlineno}
+
+% We install two core namespaces here, as we want nice error messages. Maybe
+% we will reserve the first 9.
+
+\installcorenamespace{fontinstanceready}
+\installcorenamespace{fontinstancebasic}
+\installcorenamespace{fontinstanceclass}
+
+%D The next one is handy for local assignments.
+
+\installcorenamespace{dummy}
+
+\letvalue\??dummy\empty
+
+\permanent \def\dummyparameter #1{\begincsname\??dummy#1\endcsname}
+\permanent \def\directdummyparameter #1{\begincsname\??dummy#1\endcsname}
+\permanent\protected\def\setdummyparameter #1{\defcsname\??dummy#1\endcsname}
+\permanent\protected\def\setexpandeddummyparameter#1{\edefcsname\??dummy#1\endcsname}
+\permanent\protected\def\letdummyparameter #1{\letcsname\??dummy#1\endcsname}
+
+\edef\mult_interfaces_dummy{\??dummy} % nor immutable
+
+\permanent\protected\def\getdummyparameters[#1%
+ {\if\noexpand#1]%
+ \expandafter\gobbleoneargument
+ \else
+ \let\m_mult_interfaces_namespace\mult_interfaces_dummy
+ \expandafter\mult_interfaces_get_parameters_indeed
+ \fi#1}
+
+\mult_interfaces_install_style_and_color_handler
+ \directdummyparameter
+ \usedummystyleandcolor
+ \usedummystyleparameter
+ \usedummycolorparameter
+
+% Maybe a \definecorenamespace[name][directparameter,directsetup][parent] but we
+% don't gain much. Actually we might just inline all definitions.
+
+% \enabletrackers[interfaces.namespaces,context.flush]
+%
+% \definenamespace
+% [xy]
+% [type=module,
+% comment=test module,
+% version=1,
+% name=test,
+% style=yes,
+% command=yes,
+% setup=list,
+% set=yes,
+% parent=xy]
+%
+% \unprotect
+% \getparameters
+% [\????xy]
+% [text=]
+% \protect
+%
+% \definetest[one]
+%
+% \starttext
+%
+% “\testparameter{text}”
+%
+% \setuptest[text=foo]
+%
+% “\testparameter{text}”
+%
+% \setuptest[one][text=bar]
+%
+% “\testparameter{text}”
+%
+% \stoptext
+%
+% This is a user (module) command:
+
+\permanent\tolerant\protected\def\definenamespace[#1]#*[#2]% namespace settings
+ {\clf_definenamespace{#1}{#2}}
+
+\permanent\protected\def\listnamespaces
+ {\clf_listnamespaces}
+
+%D Helper:
+%D
+%D \starttyping
+%D \showparentchain{@@am}{left}
+%D \stoptyping
+
+\permanent\protected\def\showparentchain#1#2%
+ {\writestatus\m!system{chain: [ \mult_interfaces_show_parent_chain{#1#2}]}}
+
+\def\mult_interfaces_show_parent_chain#1%
+ {#1 => %
+ \ifcsname#1:\s!parent\endcsname
+ \expandafter\mult_interfaces_show_parent_chain\lastnamedcs
+ \fi}
+
+%D Another helper (needs to be applied):
+
+\permanent\protected\def\doifelsecommandhandler#1#2% namespace name
+ {\ifcsname#1#2:\s!parent\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifcommandhandlerelse\doifelsecommandhandler
+
+\permanent\protected\def\doifcommandhandler#1#2% namespace name
+ {\ifcsname#1#2:\s!parent\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotcommandhandler#1#2% namespace name
+ {\ifcsname#1#2:\s!parent\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+% another set of (fast) helpers (grep for usage):
+
+\permanent\def\expandnamespaceparameter#1#2#3% \??xx \getp \c!xx \v!yy
+ {\csname#1\ifcsname#1\expandafter\expandafter\expandafter\mult_aux_expand_namespace_parameter#2#3}
+
+\def\mult_aux_expand_namespace_parameter#1#2% \cs \v!yy
+ {#1\endcsname#1\else#2\fi\endcsname}
+
+\permanent\def\expandnamespacemacro#1#2#3% \??xx \some_edefed_cs \c!yy
+ {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname}
+
+\permanent\def\expandnamespacevalue#1#2% \??xx {...} \c!yy == optimized \expandcheckedcsname
+ {\csname#1\ifcsname#1\normalexpanded{\noexpand\syst_helpers_expand_checked_value{#2}}}
+
+\def\syst_helpers_expand_checked_value#1#2%
+ {#1\endcsname#1\else#2\fi\endcsname}
+
+%D Conventions:
+%D
+%D \starttyping
+%D \newcount \c_class_whatever
+%D \newconditional \c_class_whatever
+%D \newconstant \c_class_whatever
+%D \newdimen \d_class_whatever
+%D \newskip \s_class_whatever
+%D \newmuskip \s_class_whatever
+%D \newbox \b_class_whatever
+%D \newtoks \t_class_whatever
+%D
+%D \edef\p_class_whatever{\classparameter\c!whatever}
+%D \edef\m_class_whatever{whatever}
+%D \stoptyping
+
+% experiment: in principle this is faster but not that noticeable as we don't do that
+% many assignments and mechanism that do are also slow; the advantage is mostly nicer
+% in tracing
+
+\mutable\let\c_mult_set\relax
+
+\protected\def\mult_interfaces_install_definition_set#1#2#3#4#5#6#7%
+ {\newcount#3%
+ \let#6\empty
+ \protected\def#2%
+ {\expandafter\let\expandafter\c_mult_set\csname #1_t_#6\endcsname
+ \ifrelax\c_mult_set
+ \expandafter\newtoks\c_mult_set
+ \letcsname#1_t_#6\endcsname\c_mult_set
+ \fi}
+ \frozen\protected\def#4##1%
+ {\pushmacro#6%
+ \advance#3\plusone
+ \edef#6{##1}%
+ \unprotect}%
+ \frozen\protected\def#5%
+ {\protect
+ \advance#3\minusone
+ \popmacro#6}%
+ \frozen\protected\def#7##1%
+ {\edef#6{##1}%
+ #2%
+ \the\c_mult_set\relax}}
+
+\permanent\protected\def\installdefinitionset#1#2%
+ {\normalexpanded
+ {\mult_interfaces_install_definition_set
+ {\noexpand#1}% \??aa
+ \expandafter\noexpand\csname set_#2_toks\endcsname
+ \expandafter\noexpand\csname #2_nesting_depth\endcsname
+ \expandafter\noexpand\csname push#2\endcsname
+ \expandafter\noexpand\csname pop#2\endcsname
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname use#2\endcsname}}
+
+\protected\def\mult_interfaces_install_definition_set_member#1#2#3#4#5#6#7#8#9% no everysetups etc
+ {\let#5#2%
+ \frozen\protected\def#2%
+ {\ifcase#4\relax\expandafter#5\else\expandafter#6\fi}%
+ \tolerant\protected\def#6[##1]##*[##2]%
+ {\ifarguments\or
+ #3\toksapp\c_mult_set{#7[##1]}%
+ \or
+ #3\toksapp\c_mult_set{#8[##1][##2]}%
+ \fi}}
+
+\permanent\protected\def\installdefinitionsetmember#1#2#3#4%
+ {\normalexpanded
+ {\mult_interfaces_install_definition_set_member
+ {\noexpand#3}% \??aa
+ \expandafter\noexpand\csname setup#4\endcsname
+ \expandafter\noexpand\csname set_#2_toks\endcsname
+ \expandafter\noexpand\csname #2_nesting_depth\endcsname
+ \expandafter\noexpand\csname normal_setup_#4\endcsname
+ \expandafter\noexpand\csname delayed_setup_#4\endcsname
+ \expandafter\noexpand\csname setup#4_\s!single\endcsname
+ \expandafter\noexpand\csname setup#4_\s!double\endcsname}}
+
+%D Another experiment:
+
+\protected\def\mult_interfaces_install_parent_injector#1#2#3#4%
+ {\frozen\protected\def#4##1%
+ {\ifempty#3%
+ \defcsname#1#2:\s!parent\endcsname{#1##1}%
+ \fi}}
+
+\permanent\protected\def\installparentinjector#1#2%
+ {\normalexpanded{\mult_interfaces_install_parent_injector
+ {\noexpand#1}%
+ \expandafter\noexpand\csname current#2\endcsname
+ \expandafter\noexpand\csname current#2parent\endcsname
+ \expandafter\noexpand\csname inject#2parent\endcsname}}
+
+% The \LUA\ based variant is twice as fast as the above but as said, we don't use
+% this one that often. It's more about less tracing than speed here.
+
+\permanent\protected\def\installmacrostack#1%
+ {\ifdefined#1\else\mutable\let#1\empty\fi
+ \protected\gdefcsname push_macro_\csstring#1\endcsname{\localpushmacro#1}%
+ \protected\gdefcsname pop_macro_\csstring#1\endcsname{\localpopmacro #1}}
+
+\permanent\protected\def\installglobalmacrostack#1%
+ {\ifdefined#1\else\mutable\glet#1\empty\fi
+ \protected\gdefcsname push_macro_\csstring#1\endcsname{\globalpushmacro#1}%
+ \protected\gdefcsname pop_macro_\csstring#1\endcsname{\globalpopmacro #1}}
+
+% \showmacrostack can be used to see if there are different entries
+
+% \unprotect
+%
+% \installcorenamespace {test} \installcommandhandler \??test {test} \??test
+% \protected\def\TestMeA[#1]%
+% {\edef\currenttest{#1}
+% \edef\p_before{\testparameter\c!before}%
+% \ifempty\p_before \relax \else \relax \fi}
+% \protected\def\TestMeB[#1]%
+% {\edef\currenttest{#1}
+% \doifelsenothing{\testparameter\c!before}\relax\relax}
+% \protected\def\TestMeC[#1]%
+% {\edef\currenttest{#1}
+% \expandafter\expandafter\expandafter\ifempty\testparameter\c!before \relax \else \relax \fi}
+% \protected\def\TestMeD[#1]%
+% {\edef\currenttest{#1}
+% \doubleexpandafter\ifempty\testparameter\c!before \relax \else \relax \fi}
+%
+% \protect
+%
+% \starttext
+% \definetest[foo] \definetest[bar][foo] \setuptest[bar][before=indeed]
+% \testfeatureonce{100000}{\TestMeA[bar]} A:\elapsedtime \par % 0.502
+% \testfeatureonce{100000}{\TestMeB[bar]} B:\elapsedtime \par % 0.530
+% \testfeatureonce{100000}{\TestMeC[bar]} C:\elapsedtime \par % 0.487
+% \testfeatureonce{100000}{\TestMeD[bar]} D:\elapsedtime \par % 0.493
+% \stoptext
+
+% There is no real demand for this ... even if this is two to three times as fast we
+% only gain a few milliseconds:
+%
+% \starttyping
+% \protected\def\foo#1{[foo:#1]}
+%
+% \installcommalistprocessor {foo} \foo
+% \installcommalistprocessorcommand \processfoolist \foo
+%
+% \infofont
+%
+% \commalistprocessor{foo}[a,b,c,{x,y,z},d]\par
+% \processfoolist[a, b, c, {x,y,z}, d]\par
+% \processcommalist[{x,y,z}]\foo\blank
+%
+% \commalistprocessor{foo}[{x,y,z},a]\par
+% \commalistprocessor{foo}[{x,y,z}]\par
+% \processfoolist[{x,y,z},a]\par
+% \processfoolist[{x,y,z}]\par
+% \processcommalist[{x,y,z}]\foo\blank
+%
+% \protected\def\foo#1{}
+%
+% \testfeatureonce{400000}{\processfoolist [fixed,middle,bar]} \elapsedtime\quad
+%%\testfeatureonce{400000}{\commalistprocessor{foo}[fixed,middle,bar]} \elapsedtime\quad
+% \testfeatureonce{400000}{\processcommalist [fixed,middle,bar]\foo} \elapsedtime\quad
+% \stoptyping
+%
+% For instance the luatex manual only has some 3000 calls. But I keep this around as one
+% never knows when we might need it.
+
+\installcorenamespace{commalistprocessor}
+\installcorenamespace{commalistprocessorwrap}
+\installcorenamespace{commalistprocessorfirst}
+\installcorenamespace{commalistprocessornext}
+\installcorenamespace{commalistprocessoraction}
+
+\permanent\protected\def\installcommalistprocessor#1#2% 5 macro names overhead
+ {\protected\edefcsname\??commalistprocessor#1\endcsname[%
+ {\csname\??commalistprocessorwrap#1\endcsname\relax}% \relax preserves {}
+ \protected\edefcsname\??commalistprocessorwrap#1\endcsname##1]%
+ {\csname\??commalistprocessorfirst#1\endcsname##1,]}
+ \protected\edefcsname\??commalistprocessorfirst#1\endcsname\relax
+ {\csname\??commalistprocessornext#1\endcsname}%
+ \protected\edefcsname\??commalistprocessornext#1\endcsname
+ {\noexpand\futureexpandis]%
+ \noexpand\gobbleoneargument
+ \csname\??commalistprocessoraction#1\endcsname}
+ \protected\edefcsname\??commalistprocessoraction#1\endcsname##1,%
+ {\noexpand#2{##1}%
+ \csname\??commalistprocessornext#1\endcsname}}
+
+\permanent\protected\def\installcommalistprocessorcommand#1#2% \processor \action
+ {\edef\p_name{\csstring#2}%
+ \installcommalistprocessor\p_name{#2}%
+ \expandafter\let\expandafter#1\csname\??commalistprocessor\p_name\endcsname}
+
+\permanent\protected\def\commalistprocessor#1{\csname\??commalistprocessor#1\endcsname}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mult-def.mkxl
new file mode 100644
index 00000000000..13e22dadebf
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-def.mkxl
@@ -0,0 +1,32 @@
+%D \module
+%D [ file=mult-def,
+%D version=2008.10.22,
+%D title=\CONTEXT\ Multilingual Macros,
+%D subtitle=Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\installcorenamespace{multilingual}
+
+\immutable\setvalue{\??multilingual czech}{cs}
+\immutable\setvalue{\??multilingual german}{de}
+\immutable\setvalue{\??multilingual english}{en}
+\immutable\setvalue{\??multilingual french}{fr}
+\immutable\setvalue{\??multilingual italian}{it}
+\immutable\setvalue{\??multilingual dutch}{nl}
+\immutable\setvalue{\??multilingual persian}{pe}
+\immutable\setvalue{\??multilingual romanian}{ro}
+
+\permanent\def\userinterfacetag{\ifcsname\??multilingual\currentinterface\endcsname\lastnamedcs\else en\fi}
+\permanent\def\userresponsestag{\ifcsname\??multilingual\currentresponses\endcsname\lastnamedcs\else en\fi}
+
+\clf_setuserinterface{\userinterfacetag}{\userresponsestag}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-dim.mklx b/Master/texmf-dist/tex/context/base/mkxl/mult-dim.mklx
new file mode 100644
index 00000000000..0526b3775a1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-dim.mklx
@@ -0,0 +1,156 @@
+%D \module
+%D [ file=core-gen,
+%D version=1995.10.10,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=General,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / General}
+
+% we could write a proper parser now in lua
+
+\unprotect
+
+%D \macros
+%D {assigndimension,assignalfadimension}
+%D
+%D The following commands are used to process keyword based dimension setters.
+%D
+%D \starttyping
+%D \assigndimension
+%D {<value>|small|medium|big|-small|-medium|-big|none}
+%D {\dimension}
+%D {value small}
+%D {value medium}
+%D {value big}
+%D \stoptyping
+%D
+%D The given keyword determines the result.
+
+\installcorenamespace{dimensionnormal}
+
+\def\assign_dimension_direct#value#dimension#small#medium#big{#dimension=#value\relax}
+
+\immutable\setvalue{\??dimensionnormal \v!none }#value#dimension#small#medium#big{#dimension\zeropoint}
+\immutable\setvalue{\??dimensionnormal \empty }#value#dimension#small#medium#big{#dimension\zeropoint}
+\immutable\setvalue{\??dimensionnormal \v!small }#value#dimension#small#medium#big{#dimension=#small\relax}
+\immutable\setvalue{\??dimensionnormal \v!medium}#value#dimension#small#medium#big{#dimension=#medium\relax}
+\immutable\setvalue{\??dimensionnormal \v!big }#value#dimension#small#medium#big{#dimension=#big\relax}
+\immutable\setvalue{\??dimensionnormal-\v!small }#value#dimension#small#medium#big{#dimension=-#small\relax}
+\immutable\setvalue{\??dimensionnormal-\v!medium}#value#dimension#small#medium#big{#dimension=-#medium\relax}
+\immutable\setvalue{\??dimensionnormal-\v!big }#value#dimension#small#medium#big{#dimension=-#big\relax}
+\immutable\letvalue{\??dimensionnormal\s!unknown}\assign_dimension_direct
+
+\permanent\protected\def\assigndimension#value%
+ {\ifcsname\??dimensionnormal#value\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\assign_dimension_direct
+ \fi{#value}}
+
+%D The next variant assigns to a macro instead of a dimension.
+%D
+%D \starttyping
+%D \assignalfadimension
+%D {<value>|small|medium|big|none}
+%D {\macro}
+%D {value small}
+%D {value medium}
+%D {value big}
+%D \stoptyping
+%D
+%D This one is used for factors.
+
+\installcorenamespace{dimensionalfa}
+
+\def\assign_alpha_dimension_direct#value#macro#small#medium#big{\edef#macro{#value}}
+
+\immutable\setvalue{\??dimensionalfa\v!none }#value#macro#small#medium#big{\let #macro\!!zerocount}
+\immutable\setvalue{\??dimensionalfa\v!small }#value#macro#small#medium#big{\edef#macro{#small}}
+\immutable\setvalue{\??dimensionalfa\v!medium }#value#macro#small#medium#big{\edef#macro{#medium}}
+\immutable\setvalue{\??dimensionalfa\v!big }#value#macro#small#medium#big{\edef#macro{#big}}
+\immutable\letvalue{\??dimensionalfa\s!unknown}\assign_alpha_dimension_direct
+
+\permanent\protected\def\assignalfadimension#value%
+ {\ifcsname\??dimensionalfa#value\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\assign_alpha_dimension_direct
+ \fi
+ {#value}}
+
+%D \macros
+%D {assignvalue}
+%D
+%D A variant that does not assume dimenions ios the following:
+%D
+%D \starttyping
+%D \assignvalue
+%D {<value>|small|medium|big}
+%D {\macro}
+%D {value small}
+%D {value medium}
+%D {value big}
+%D \stoptyping
+
+\installcorenamespace{dimensionvalue}
+
+\def\assign_value_direct#value#macro#small#medium#big{\edef#macro{#value}}
+
+\immutable\setvalue{\??dimensionvalue\v!small }#value#macro#small#medium#big{\edef#macro{#small}}
+\immutable\setvalue{\??dimensionvalue\v!medium }#value#macro#small#medium#big{\edef#macro{#medium}}
+\immutable\setvalue{\??dimensionvalue\v!big }#value#macro#small#medium#big{\edef#macro{#big}}
+\immutable\letvalue{\??dimensionvalue\s!unknown}\assign_value_direct
+
+\permanent\protected\def\assignvalue#value%
+ {\ifcsname\??dimensionvalue#value\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\assign_value_direct
+ \fi{#value}}
+
+%D \macros
+%D {assignwidth}
+%D
+%D This one is used a few times.
+%D
+%D \starttyping
+%D \assignwidth
+%D {<value>|fit|broad}
+%D {\dimension}
+%D {text}
+%D {extra}
+%D \stoptyping
+
+\installcorenamespace{dimensionwidth}
+
+\newbox\b_assign_width
+
+\def\assign_width_direct#value#dimension#content#extra{#dimension=#value\relax}
+
+% line is like fit but can be used later as signal for ...
+
+\immutable\setvalue{\??dimensionwidth }#value#dimension#content#extra{\setbox\b_assign_width\hbox{#content}#dimension\wd\b_assign_width
+ \setbox\b_assign_width\emptybox}
+\immutable\setvalue{\??dimensionwidth\v!fit }#value#dimension#content#extra{\setbox\b_assign_width\hbox{#content}#dimension\wd\b_assign_width
+ \setbox\b_assign_width\emptybox}
+\immutable\setvalue{\??dimensionwidth\v!broad }#value#dimension#content#extra{\setbox\b_assign_width\hbox{#content}#dimension\dimexpr\wd\b_assign_width+#extra\relax
+ \setbox\b_assign_width\emptybox}
+\immutable\setvalue{\??dimensionwidth\v!line }#value#dimension#content#extra{\setbox\b_assign_width\hbox{#content}#dimension\wd\b_assign_width
+ \setbox\b_assign_width\emptybox}
+\immutable\letvalue{\??dimensionwidth\s!unknown}\assign_width_direct
+
+\permanent\protected\def\assignwidth#value%
+ {\ifcsname\??dimensionwidth#value\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\assign_width_direct
+ \fi{#value}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/mult-ini.lmt
new file mode 100644
index 00000000000..7c60d5b33af
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-ini.lmt
@@ -0,0 +1,389 @@
+if not modules then modules = { } end modules ['mult-ini'] = {
+ version = 1.001,
+ comment = "companion to mult-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, gmatch, match, find, sub = string.format, string.gmatch, string.match, string.find, string.sub
+local lpegmatch = lpeg.match
+local serialize, concat = table.serialize, table.concat
+local rawget, type, tonumber, next = rawget, type, tonumber, next
+
+local context = context
+local commands = commands
+local implement = interfaces.implement
+
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+local prtcatcodes = catcodes.numbers.prtcatcodes
+local vrbcatcodes = catcodes.numbers.vrbcatcodes
+local contextsprint = context.sprint
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+local report_interface = logs.reporter("interface","initialization")
+
+interfaces = interfaces or { }
+interfaces.constants = mark(interfaces.constants or { })
+interfaces.variables = mark(interfaces.variables or { })
+interfaces.elements = mark(interfaces.elements or { })
+interfaces.formats = mark(interfaces.formats or { })
+interfaces.translations = mark(interfaces.translations or { })
+interfaces.setupstrings = mark(interfaces.setupstrings or { })
+interfaces.corenamespaces = mark(interfaces.corenamespaces or { })
+interfaces.usednamespaces = mark(interfaces.usednamespaces or { })
+
+local registerstorage = storage.register
+local sharedstorage = storage.shared
+
+local constants = interfaces.constants
+local variables = interfaces.variables
+local elements = interfaces.elements
+local formats = interfaces.formats
+local translations = interfaces.translations
+local setupstrings = interfaces.setupstrings
+local corenamespaces = interfaces.corenamespaces
+local usednamespaces = interfaces.usednamespaces
+local reporters = { } -- just an optimization
+
+local setmacro = tokens.setters.macro
+
+registerstorage("interfaces/constants", constants, "interfaces.constants")
+registerstorage("interfaces/variables", variables, "interfaces.variables")
+registerstorage("interfaces/elements", elements, "interfaces.elements")
+registerstorage("interfaces/formats", formats, "interfaces.formats")
+registerstorage("interfaces/translations", translations, "interfaces.translations")
+registerstorage("interfaces/setupstrings", setupstrings, "interfaces.setupstrings")
+registerstorage("interfaces/corenamespaces", corenamespaces, "interfaces.corenamespaces")
+registerstorage("interfaces/usednamespaces", usednamespaces, "interfaces.usednamespaces")
+
+interfaces.interfaces = {
+ "cs", "de", "en", "fr", "it", "nl", "ro", "pe",
+}
+
+sharedstorage.currentinterface = sharedstorage.currentinterface or "en"
+sharedstorage.currentresponse = sharedstorage.currentresponse or "en"
+
+local currentinterface = sharedstorage.currentinterface
+local currentresponse = sharedstorage.currentresponse
+
+interfaces.currentinterface = currentinterface
+interfaces.currentresponse = currentresponse
+
+local complete = allocate()
+interfaces.complete = complete
+
+local function resolve(t,k) -- one access needed to get loaded (not stored!)
+ report_interface("loading interface definitions from 'mult-def.lua'")
+ complete = dofile(resolvers.findfile("mult-def.lua"))
+ report_interface("loading interface messages from 'mult-mes.lua'")
+ complete.messages = dofile(resolvers.findfile("mult-mes.lua"))
+ interfaces.complete = complete
+ return rawget(complete,k)
+end
+
+setmetatableindex(complete, resolve)
+
+local function valueiskey(t,k) -- will be helper
+ t[k] = k
+ return k
+end
+
+setmetatableindex(variables, valueiskey)
+setmetatableindex(constants, valueiskey)
+setmetatableindex(elements, valueiskey)
+setmetatableindex(formats, valueiskey)
+setmetatableindex(translations, valueiskey)
+setmetatableindex(setupstrings, valueiskey)
+
+function interfaces.registernamespace(n,namespace)
+ corenamespaces[n] = namespace
+ usednamespaces[namespace] = n
+end
+
+function interfaces.getnamespace(n)
+ return usednamespaces[n] .. ">"
+end
+
+if documentdata then
+
+ local prefix, getmacro
+
+ function documentdata.variable(name)
+ if not prefix then
+ prefix = usednamespaces.variables .. ">document:"
+ end
+ if not getmacro then
+ getmacro = tokens.getters.macro
+ end
+ return getmacro(prefix..name)
+ end
+
+end
+
+local function resolve(t,k)
+ local v = logs.reporter(k)
+ t[k] = v
+ return v
+end
+
+setmetatableindex(reporters,resolve)
+
+for category, _ in next, translations do
+ -- We pre-create reporters for already defined messages
+ -- because otherwise listing is incomplete and we want
+ -- to use that for checking so delaying makes not much
+ -- sense there.
+ local r = reporters[category]
+end
+
+-- adding messages
+
+local function add(target,tag,values)
+ local t = target[tag]
+ if not f then
+ target[tag] = values
+ else
+ for k, v in next, values do
+ if f[k] then
+ -- error
+ else
+ f[k] = v
+ end
+ end
+ end
+end
+
+function interfaces.settranslation(tag,values)
+ add(translations,tag,values)
+end
+
+function interfaces.setformat(tag,values)
+ add(formats,tag,values)
+end
+
+local function getsetupstring(tag)
+ return setupstrings[tag] or tag
+end
+
+interfaces.getsetupstring = getsetupstring
+
+-- the old method:
+
+local replacer = lpeg.replacer { { "--", "%%a" } }
+
+local function fulltag(category,tag)
+ return formatters["%s:%s"](category,lpegmatch(replacer,tag))
+end
+
+function interfaces.setmessages(category,str)
+ for tag, message in gmatch(str,"(%S+) *: *(.-) *[\n\r]") do
+ if tag == "title" then
+ translations[tag] = translations[tag] or tag
+ else
+ formats[fulltag(category,tag)] = lpegmatch(replacer,message)
+ end
+ end
+end
+
+function interfaces.setmessage(category,tag,message)
+ formats[fulltag(category,tag)] = lpegmatch(replacer,message)
+end
+
+function interfaces.getmessage(category,tag,default)
+ return formats[fulltag(category,tag)] or default or "unknown message"
+end
+
+function interfaces.doifelsemessage(category,tag)
+ return rawget(formats,fulltag(category,tag))
+end
+
+local splitter = lpeg.splitat(",")
+
+function interfaces.showmessage(category,tag,arguments)
+ local r = reporters[category]
+ local f = formats[fulltag(category,tag)]
+ local t = type(arguments)
+ if t == "string" and #arguments > 0 then
+ r(f,lpegmatch(splitter,arguments))
+ elseif t == "table" then
+ r(f,unpack(arguments))
+ elseif arguments then
+ r(f,arguments)
+ else
+ r(f)
+ end
+end
+
+-- till here
+
+function interfaces.setvariable(variable,given)
+ variables[given] = variable
+end
+
+function interfaces.setconstant(constant,given)
+ constants[given] = constant
+end
+
+function interfaces.setelement(element,given)
+ elements[given] = element
+end
+
+-- the real thing:
+
+logs.setmessenger(context.verbatim.ctxreport)
+
+interfaces.cachedsetups = interfaces.cachedsetups or { }
+interfaces.hashedsetups = interfaces.hashedsetups or { }
+
+local cachedsetups = interfaces.cachedsetups
+local hashedsetups = interfaces.hashedsetups
+
+storage.register("interfaces/cachedsetups", cachedsetups, "interfaces.cachedsetups")
+storage.register("interfaces/hashedsetups", hashedsetups, "interfaces.hashedsetups")
+
+function interfaces.cachesetup(t)
+ local hash = serialize(t)
+ local done = hashedsetups[hash]
+ if done then
+ return cachedsetups[done]
+ else
+ done = #cachedsetups + 1
+ cachedsetups[done] = t
+ hashedsetups[hash] = done
+ return t
+ end
+end
+
+function interfaces.interfacedcommand(name)
+ local command = complete.commands[name]
+ return command and command[currentinterface] or name
+end
+
+-- interface
+
+function interfaces.writestatus(category,message)
+ reporters[category](message) -- could also be a setmetatablecall
+end
+
+function interfaces.message(str)
+ texio.write(str) -- overloaded
+end
+
+implement { name = "registernamespace", actions = interfaces.registernamespace, arguments = { "integer", "string" } }
+implement { name = "setinterfaceconstant", actions = interfaces.setconstant, arguments = "2 strings" }
+implement { name = "setinterfacevariable", actions = interfaces.setvariable, arguments = "2 strings" }
+implement { name = "setinterfaceelement", actions = interfaces.setelement, arguments = "2 strings" }
+implement { name = "setinterfacemessage", actions = interfaces.setmessage, arguments = "3 strings" }
+implement { name = "setinterfacemessages", actions = interfaces.setmessages, arguments = "2 strings" }
+
+implement {
+ name = "showmessage",
+ public = true,
+ protected = true,
+ arguments = "3 arguments" ,
+ actions = interfaces.showmessage,
+}
+
+implement {
+ name = "doifelsemessage",
+ public = true,
+ protected = true,
+ arguments = "2 arguments",
+ actions = { interfaces.doifelsemessage, commands.doifelse },
+}
+
+implement {
+ name = "getmessage",
+ public = true,
+ protected = true,
+ arguments = "3 arguments",
+ actions = function(...)
+ setmacro("currentmessagetext", interfaces.getmessage(...))
+ end,
+}
+
+implement {
+ name = "writestatus",
+ overload = true,
+ public = true,
+ protected = true,
+ arguments = "2 arguments",
+ actions = interfaces.writestatus,
+}
+
+implement {
+ name = "message",
+ overload = true,
+ public = true,
+ protected = true,
+ arguments = "string",
+ actions = interfaces.message,
+}
+
+local function gss(s)
+ contextsprint(vrbcatcodes,getsetupstring(s))
+end
+
+implement { -- will be overloaded
+ name = "getsetupstring",
+ public = true,
+ -- protected = true,
+ arguments = "string",
+ actions = gss,
+}
+
+implement {
+ name = "rawsetupstring",
+ public = true,
+ arguments = "string",
+ actions = gss,
+}
+
+
+local function showassignerror(namespace,key,line)
+ -- if key and key ~= "" and key ~= "," then
+ local ns, instance = match(namespace,"^(%d+)[^%a]+(%a*)")
+ if ns then
+ namespace = corenamespaces[tonumber(ns)] or ns
+ end
+ -- injected in the stream for timing:
+ if instance and instance ~= "" then
+ context.writestatus("setup",formatters["error in line %a, namespace %a, instance %a, key %a"](line,namespace,instance,key))
+ else
+ context.writestatus("setup",formatters["error in line %a, namespace %a, key %a"](line,namespace,key))
+ end
+ -- end
+end
+
+implement {
+ name = "showassignerror",
+ actions = showassignerror,
+ arguments = { "string", "string", "integer" },
+}
+
+-- a simple helper
+
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local makesparse = function(t)
+ for k, v in next, t do
+ if not v or v == "" then
+ t[k] = nil
+ end
+ end
+ return t
+end
+
+function interfaces.checkedspecification(specification)
+ local kind = type(specification)
+ if kind == "table" then
+ return makesparse(specification)
+ elseif kind == "string" and specification ~= "" then
+ return makesparse(settings_to_hash(specification))
+ else
+ return { }
+ end
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mult-ini.mkxl
new file mode 100644
index 00000000000..17f63ef93b9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-ini.mkxl
@@ -0,0 +1,804 @@
+%D \module
+%D [ file=mult-ini,
+%D version=2008.10.22, % 1996.06.01,
+%D title=\CONTEXT\ Multilingual Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module is a stripped down version of \type {mult-ini.tex}, which we keep
+%D around as \type {mult-kep.tex} for sentimental reasons. There you will find some
+%D more historic information.
+
+\writestatus{loading}{ConTeXt Multilingual Macros / Initialization}
+
+\unprotect
+
+\registerctxluafile{mult-ini}{autosuffix}
+\registerctxluafile{mult-fmt}{initexonly}
+
+%D \macros
+%D [constanten,variabelen,commands]
+%D {v!,c!,k!,s!,e!,m!,l!,r!,f!,p!,x!,y!}
+%D
+%D In the system modules we introduced some prefixed constants, variables (both
+%D macros) and registers. Apart from a tremendous saving in terms of memory and a
+%D gain in speed we use from now on prefixes when possible for just another reason:
+%D consistency and multi||linguality. Systematically using prefixed macros enables
+%D us to implement a multi||lingual user interface. Redefining these next set of
+%D prefixes therefore can have desastrous results.
+%D
+%D \startlinecorrection
+%D \starttable[|c|c|c|]
+%D \HL
+%D \NC \bf prefix \NC \bf meaning \NC \bf application \NC\SR
+%D \HL
+%D \NC \type{\c!prefix!} \NC c! \NC constant (direct) \NC\FR
+%D \NC \type{\k!prefix!} \NC k! \NC constant (indirect) \NC\FR
+%D \NC \type{\e!prefix!} \NC e! \NC element \NC\MR
+%D \NC \type{\f!prefix!} \NC f! \NC file \NC\MR
+%D \NC \type{\m!prefix!} \NC m! \NC age \NC\MR
+%D \NC \type{\s!prefix!} \NC s! \NC system \NC\MR
+%D \NC \type{\v!prefix!} \NC v! \NC variable \NC\MR
+%D \HL
+%D \stoptable
+%D \stoplinecorrection
+%D
+%D In the single||lingual version we used \type {!}, \type {!!}, \type {!!!} and
+%D \type {!!!!}. In the meantime some of these are obsolete (we had some 12
+%D originally).
+
+\immutable\def\c!prefix!{c!}
+\immutable\def\k!prefix!{k!}
+\immutable\def\e!prefix!{e!}
+\immutable\def\f!prefix!{f!} % for the moment we keep this one
+\immutable\def\m!prefix!{m!}
+\immutable\def\s!prefix!{s!}
+\immutable\def\v!prefix!{v!}
+\immutable\def\t!prefix!{t!}
+
+%D \macros
+%D [constants,variables,commands]
+%D {@@,??}
+%D
+%D Variables generated by the system can be recognized on their prefix \type {@@}.
+%D They are composed of a command (class) specific tag, which can be recognized on
+%D \type {??}, and a system constant, which has the prefix \type {c!}. We'll se some
+%D more of this.
+
+\immutable\def\??prefix {??}
+\immutable\def\@@prefix {@@}
+
+%D Just to be complete we repeat some of the already defined system constants here.
+%D Maybe their prefix \type {\s!} now falls into place.
+
+\def\s!next {next} \def\s!default {default}
+\def\s!dummy {dummy} \def\s!unknown {unknown}
+
+\def\s!do {do} \def\s!dodo {dodo}
+
+\def\s!complex {complex} \def\s!start {start}
+\def\s!simple {simple} \def\s!stop {stop}
+
+\def\s!true {true}
+\def\s!false {false}
+
+%D The word \type {height} takes 6~token memory cells. The control sequence \type
+%D {\height} on the other hand uses only one. Knowing this, we can improve the
+%D performance of \TEX, both is terms of speed and memory usage, by using control
+%D sequences instead of the words written in full.
+%D
+%D Where in the \ASCII\ file the second lines takes nine extra characters, \TEX\
+%D saves us 13~tokens.
+%D
+%D \starttyping
+%D \hrule width 10pt height 2pt depth 1pt
+%D \hrule \s!width 10pt \s!height 2pt \s!depth 1pt
+%D \stoptyping
+%D
+%D One condition is that we have defined \type {\s!height}, \type {\s!width} and
+%D \type {\s!depth} as respectively \type {height}, \type {width} and \type {depth}.
+%D Using this scheme therefore only makes sense when a token sequence is used more
+%D than once. Savings like this should of course be implemented in english, just
+%D because \TEX\ is english.
+
+\def\s!width {width}
+\def\s!height {height}
+\def\s!depth {depth}
+\def\s!spread {spread}
+\def\s!plus {plus}
+\def\s!minus {minus}
+\def\s!to {to}
+
+\def\s!fil {fil}
+\def\s!fill {fill}
+\def\s!filll {filll}
+
+\def\s!attr {attr}
+\def\s!axis {axis}
+\def\s!both {both}
+\def\s!bottom {bottom}
+\def\s!left {left}
+\def\s!options {options}
+\def\s!orientation{orientation}
+\def\s!reverse {reverse}
+\def\s!right {right}
+\def\s!top {top}
+\def\s!xmove {xmove}
+\def\s!xoffset {xoffset}
+\def\s!ymove {ymove}
+\def\s!yoffset {yoffset}
+
+%D \macros
+
+%D {defineinterfaceconstant,
+%D defineinterfacevariable,
+%D defineinterfaceelement,
+%D definesystemvariable,
+%D definesystemconstant,
+%D definemessageconstant,
+%D definefileconstant}
+%D
+%D The first part of this module is dedicated to dealing with multi||lingual
+%D constants and variables. When \CONTEXT\ grew bigger and bigger in terms of bytes
+%D and used string space, we switched to predefined constants. At the cost of more
+%D hash table entries, the macros not only becase more compact, they became much
+%D faster too. Maybe an even bigger advantage was that mispelling could no longer
+%D lead to problems. Even a multi||lingual interface became possible.
+%D
+%D Constants |<|we'll introduce the concept of variables later on|>| are preceded by
+%D a type specific prefix, followed by a \type {!}. To force consistency, we provide
+%D a few commands for defining such constants.
+%D
+%D \starttyping
+%D \defineinterfaceconstant {name} {meaning}
+%D \defineinterfacevariable {name} {meaning}
+%D \defineinterfaceelement {name} {meaning}
+%D \stoptyping
+%D
+%D Which is the same as:
+%D
+%D \starttyping
+%D \def\c!name{meaning}
+%D \def\v!name{meaning}
+%D \def\e!name{meaning}
+%D \stoptyping
+
+%permanent\protected\def\defineinterfaceconstant#1#2{\immutable\defcsname\c!prefix!#1\endcsname{#2}}
+\permanent\protected\def\defineinterfacevariable#1#2{\immutable\defcsname\v!prefix!#1\endcsname{#2}}
+\permanent\protected\def\defineinterfaceelement #1#2{\immutable\defcsname\e!prefix!#1\endcsname{#2}}
+
+%D Next come some interface independant constants:
+%D
+%D \starttyping
+%D \definefileconstant {name} {meaning}
+%D \stoptyping
+
+\permanent\protected\def\definefileconstant#1#2{\immutable\defcsname\f!prefix!#1\endcsname{#2}}
+
+%D And finaly we have the one argument, space saving constants
+%D
+%D \starttyping
+%D \definesystemconstant {name}
+%D \definemessageconstant {name}
+%D \stoptyping
+
+\permanent\protected\def\definesystemconstant #1{\immutable\defcsname\s!prefix!#1\endcsname{#1}}
+\permanent\protected\def\definemessageconstant#1{\immutable\defcsname\m!prefix!#1\endcsname{#1}}
+
+%D For now here:
+
+\permanent\protected\def\definetagconstant #1{\immutable\defcsname\t!prefix!#1\endcsname{#1}}
+\permanent\protected\def\aliastagconstant #1#2{\aliased \letcsname\t!prefix!#1\expandafter\endcsname\csname\t!prefix!#2\endcsname}
+
+%D In a parameter driven system, some parameters are shared by more system
+%D components. In \CONTEXT\ we can distinguish parameters by a unique prefix. Such a
+%D prefix is defined with:
+%D
+%D \starttyping
+%D \definesystemvariable {name}
+%D \stoptyping
+
+\permanent\protected\def\definesystemvariable#1{\immutable\edefcsname\??prefix#1\endcsname{\@@prefix#1}}
+
+\definesystemvariable{ms}
+
+%D \macros
+%D {selectinterface,
+%D defaultinterface, currentinterface, currentresponses}
+%D
+%D With \type {\selectinterface} we specify the language we are going to use. The
+%D system asks for the language wanted, and defaults to \type {\currentinterface}
+%D when we just give \type {enter}. By default the message system uses the current
+%D interface language, but \type {\currentresponses} can specify another language
+%D too.
+%D
+%D Because we want to generate formats directly too, we do not ask for interface
+%D specifications when these are already defined (like in cont-nl.tex and alike).
+
+\ifdefined\defaultinterface
+
+ \permanent\protected\def\selectinterface
+ {\writestatus{interface}{defining \currentinterface\space interface}%
+ %writeline
+ \writestatus{interface}{using \currentresponses\space messages}%
+ %\writeline
+ \enforced\permanent\let\selectinterface\relax}
+
+\else
+
+ \immutable\def\defaultinterface{english}
+
+ \permanent\overloaded\protected\def\selectinterface
+ {\enforced\permanent\protected\def\selectinterface##1##2%
+ {\bgroup
+ \endlinechar\minusone
+ \global\read16 to ##1
+ \egroup
+ \doifnothing\currentinterface{\let##1=##2}%
+ \doifundefined{\s!prefix!##1}{\let##1=##2}}%
+ \selectinterface\currentinterface\defaultinterface
+ \writestatus{interface}{defining \currentinterface\space interface}%
+ %\writeline
+ \selectinterface\currentresponses\currentinterface
+ \writestatus{interface}{using \currentresponses\space messages}%
+ %\writeline
+ \enforced\permanent\let\selectinterface\relax}
+
+\fi
+
+\ifdefined\currentinterface \else \aliased\let\currentinterface\defaultinterface \fi
+\ifdefined\currentresponses \else \aliased\let\currentresponses\defaultinterface \fi
+
+%D \macros
+%D {startinterface}
+%D
+%D Sometimes we want to define things only for specific interface languages. This
+%D can be done by means of the selector:
+%D
+%D \starttyping
+%D \startinterface language
+%D
+%D language specific definitions & commands
+%D
+%D \stopinterface
+%D \stoptyping
+
+\permanent\protected\def\startinterface #1
+ {\doifnot{#1}{all}{\doifnotinset\currentinterface{#1}{\gobbleuntil\stopinterface}}}
+
+\aliased\let\stopinterface\relax
+
+%D \macros
+%D {startmessages,
+%D getmessage,
+%D showmessage,
+%D makemessage}
+%D
+%D A package as large as \CONTEXT\ can hardly function without a decent message
+%D mechanism. Due to its multi||lingual interface, the message subsystem has to be
+%D multi||lingual too. A major drawback of this feature is that we have to code
+%D messages. As a result, the source becomes less self documented. On the other
+%D hand, consistency will improve.
+%D
+%D Because the overhead in terms of entries in the (already exhausted) hash table
+%D has to be minimal, messages are packed in libraries. We can extract a message
+%D from such a library in three ways:
+%D
+%D \starttyping
+%D \getmessage {library} {tag}
+%D \showmessage {library} {tag} {data}
+%D \makemessage {library} {tag} {data}
+%D \stoptyping
+%D
+%D The first command gets the message \type {tag} from the \type {library}
+%D specified. The other commands take an extra argument: a list of items to be
+%D inserted in the message text. While \type {\showmessage} shows the message at the
+%D terminal, the other commands generate the message as text. Before we explain the
+%D \type {data} argument, we give an example of a library.
+%D
+%D \starttyping
+%D \startmessages english library: alfa
+%D title: something
+%D 1: first message
+%D 2: second (--) message --
+%D \stopmessages
+%D \stoptyping
+%D
+%D The first message is a simple one and can be shown with:
+%D
+%D \starttyping
+%D \showmessage {alfa} {1} {}
+%D \stoptyping
+%D
+%D The second message on the other hand needs some extra data:
+%D
+%D \starttyping
+%D \showmessage {alfa} {2} {and last,to you}
+%D \stoptyping
+%D
+%D This message is shown as:
+%D
+%D \starttyping
+%D something : second (and last) message to you
+%D \stoptyping
+%D
+%D As we can see, the title entry is shown with the message. The data fields are
+%D comma separated and are specified in the message text by \type {--}.
+%D
+%D It is not required to define all messages in a library at once. We can add
+%D messages to a library in the following way:
+%D
+%D \starttyping
+%D \startmessages english library: alfa
+%D 10: tenth message
+%D \stopmessages
+%D \stoptyping
+%D
+%D Because such definitions can take place in different modules, the system gives a
+%D warning when a tag occurs more than once. The first occurrence takes preference
+%D over later ones, so we had better use a save offset, as shown in the example. As
+%D we can see, the title field is specified only the first time!
+%D
+%D Because we want to check for duplicate tags, the macros are a bit more
+%D complicated than neccessary. The \NEWLINE\ token is used as message separator.
+%D
+%D For internal purposes one can use \type {\setmessagetext}, which puts the message
+%D text asked for in \type {\currentmessagetext}.
+%D
+%D These will become obsolete:
+
+\permanent\protected\def\startmessages #1 library: #2 %
+ {\begingroup
+ \ifcsname\m!prefix!#2\endcsname\else\immutable\setgvalue{\m!prefix!#2}{#2}\fi
+ \catcode\endoflineasciicode\othercatcode
+ \doifelseinset{#1}{\currentresponses,all}\mult_messages_start_yes\mult_messages_start_nop{#2}}
+
+\def\mult_messages_start_yes#1#2\stopmessages
+ {\clf_setinterfacemessages{#1}{#2}%
+ \endgroup}
+
+\def\mult_messages_start_nop#1#2\stopmessages
+ {\endgroup}
+
+\aliased\let\stopmessages\relax
+
+\mutable\let\currentmessagetext\empty
+
+\permanent\protected\def\setinterfacemessage#1#2#3%
+ {\ifcsname\m!prefix!#1\endcsname\else\immutable\gdefcsname\m!prefix!#1\endcsname{#1}\fi
+ \clf_setinterfacemessage{#1}{#2}{#3}}
+
+% \getmessage % {tag} {index} -> \currentmessagetext
+% \doifelsemessage % {tag} {index}
+% \showmessage % {tag} {index} {detail}
+% \writestatus % {tag} {string}
+% \message % {string}
+
+\aliased\let\doifmessageelse\doifelsemessage
+
+\permanent\protected\def\inlinemessage #1{\dontleavehmode{\tttf#1}}
+\permanent\protected\def\displaymessage#1{\blank\inlinemessage{#1}\blank}
+
+% \getsetupstring % {tag} defined at the lua end
+% \rawsetupstring % {tag} defined at the lua end
+
+%D For old times sake:
+
+\aliased\let\showwarning\showmessage
+
+%D \macros
+%D {dosetvalue,dosetevalue,dosetgvalue,dosetxvalue,docopyvalue,doresetvalue} % dogetvalue
+%D
+%D We already defined these auxiliary macros in the system modules. Starting with
+%D this module however, we have to take multi||linguality a bit more serious.
+%D
+%D In due time, when we exclusively use the parameter handler code, we can drop the
+%D backmapping (\type{\c!k...}) and make \type {\c!c...} similar to \type {\v!...}.
+%D In that case we can simply the following setters.
+
+\pushoverloadmode
+
+\permanent\protected\def\doletvalue #1#2{\letcsname #1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname}
+\permanent\protected\def\dosetvalue #1#2{\defcsname #1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname}
+\permanent\protected\def\dosetevalue #1#2{\edefcsname#1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname}
+\permanent\protected\def\dosetgvalue #1#2{\gdefcsname#1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname}
+\permanent\protected\def\dosetxvalue #1#2{\xdefcsname#1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname}
+\permanent\protected\def\doresetvalue #1#2{\letcsname #1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname\empty}
+\permanent\protected\def\doignorevalue#1#2#3{\letcsname #1\ifcsname\k!prefix!#2\endcsname\csname\k!prefix!#2\endcsname\else#2\fi\endcsname\empty}
+
+\permanent\protected\def\docopyvalue#1#2#3%
+ {\ifcsname\k!prefix!#3\endcsname
+ \defcsname#1\csname\k!prefix!#3\endcsname\expandafter\endcsname\expandafter
+ {\csname#2\csname\k!prefix!#3\endcsname\endcsname}%
+ \else
+ \defcsname#1#3\expandafter\endcsname\expandafter
+ {\csname#2#3\endcsname}%
+ \fi}
+
+\startinterface english
+
+ \permanent\protected\def\doletvalue #1#2{\letcsname #1#2\endcsname}
+ \permanent\protected\def\dosetvalue #1#2{\defcsname #1#2\endcsname}
+ \permanent\protected\def\dosetevalue #1#2{\edefcsname#1#2\endcsname}
+ \permanent\protected\def\dosetgvalue #1#2{\gdefcsname#1#2\endcsname}
+ \permanent\protected\def\dosetxvalue #1#2{\xdefcsname#1#2\endcsname}
+ \permanent\protected\def\doresetvalue #1#2{\letcsname #1#2\endcsname\empty}
+ \permanent\protected\def\doignorevalue#1#2#3{\letcsname #1#2\endcsname\empty}
+
+ \permanent\protected\def\docopyvalue#1#2#3%
+ {\defcsname#1#3\expandafter\endcsname\expandafter
+ {\csname#2#3\endcsname}}
+
+\stopinterface
+
+\popoverloadmode
+
+%D We can now redefine some messages that will be introduced in the multi||lingual
+%D system module.
+
+\permanent\protected\def\showassignerror #1#2{\showmessage\m!check1{#1,#2}}
+\permanent\protected\def\showargumenterror#1#2{\showmessage\m!check2{#1,#2}}
+\permanent\protected\def\showdefinederror #1#2{\showmessage\m!check3{#1,#2}}
+
+%D \CONTEXT\ is a parameter driven package. This means that users instruct the
+%D system by means of variables, values and keywords. These instructions take the
+%D form:
+%D
+%D \starttyping
+%D \setupsomething[some variable=some value, another one=a keyword]
+%D \stoptyping
+%D
+%D or by keyword only:
+%D
+%D \starttyping
+%D \dosomething[this way,that way,no way]
+%D \stoptyping
+%D
+%D Because the same variables can occur in more than one setup command, we have to
+%D be able to distinguish them. This is achieved by assigning them a unique prefix.
+%D
+%D Imagine a setup command for boxed text, that enables us to specify the height and
+%D width of the box. Behide the scenes the command
+%D
+%D \starttyping
+%D \setupbox [width=12cm, height=3cm]
+%D \stoptyping
+%D
+%D results in something like
+%D
+%D \starttyping
+%D \<box><width> {12cm}
+%D \<box><height> {3cm}
+%D \stoptyping
+%D
+%D while a similar command for specifying the page dimensions
+%D of an \cap {A4} page results in:
+%D
+%D \starttyping
+%D \<page><width> {21.0cm}
+%D \<page><height> {27.9cm}
+%D \stoptyping
+%D
+%D The prefixes \type {<box>} and \type {<page>} are hidden from users and can
+%D therefore be language independant. Variables on the other hand, differ for each
+%D language:
+%D
+%D \starttyping
+%D \<box><color> {<blue>}
+%D \<box><kleur> {<blauw>}
+%D \<box><couleur> {<blue>}
+%D \stoptyping
+%D
+%D In this example we can see that the assigned values or keywords are language
+%D dependant too. This will be a complication when defining multi||lingual setup
+%D files.
+%D
+%D A third phenomena is that variables and values can have a similar meaning.
+%D
+%D \starttyping
+%D \<pagenumber><location> {<left>}
+%D \<skip><left> {12cm}
+%D \stoptyping
+%D
+%D A (minor) complication is that where in english we use \type {<left>}, in dutch
+%D we find both \type {<links>} and \type {<linker>}. This means that when we use
+%D some sort of translation table, we have to distinguish between the variables at
+%D the left side and the fixed values at the right.
+%D
+%D The same goes for commands that are composed of different user supplied and/or
+%D language specific elements. In english we can use:
+%D
+%D \starttyping
+%D \<empty><figure>
+%D \<empty><intermezzo>
+%D \stoptyping
+%D
+%D But in dutch we have the following:
+%D
+%D \starttyping
+%D \<lege><figuur>
+%D \<leeg><intermezzo>
+%D \stoptyping
+%D
+%D These subtle differences automatically lead to a solution where variables,
+%D values, elements and other components have a similar logical name (used in
+%D macro's) but a different meaning (supplied by the user).
+%D
+%D Our solution is one in which the whole system is programmed in terms of
+%D identifiers with language specific meanings. In such an implementation, each
+%D fixed variable is available as:
+%D
+%D \starttyping
+%D \<prefix><variable>
+%D \stoptyping
+%D
+%D This means that for instance:
+%D
+%D \starttyping
+%D \setupbox[width=12cm]
+%D \stoptyping
+%D
+%D expands to something like:
+%D
+%D \starttyping
+%D \def\boxwidth{12cm}
+%D \stoptyping
+%D
+%D because we don't want to recode the source, a setup command in another language
+%D has to expand to this variable, so:
+%D
+%D \starttyping
+%D \setupblock[width=12cm]
+%D \stoptyping
+%D
+%D has to result in the definition of \type {\boxwidth} too. This method enables us
+%D to build compact, fast and readable code.
+%D
+%D An alternative method, which we considered using, uses a more indirect way. In
+%D this case, both calls generate a different variable:
+%D
+%D \starttyping
+%D \def\boxwidth {12cm}
+%D \def\boxbreedte {12cm}
+%D \stoptyping
+%D
+%D And because we don't want to recode those megabytes of already developed code,
+%D this variable has to be called with something like:
+%D
+%D \starttyping
+%D \valueof\box\width
+%D \stoptyping
+%D
+%D where \type {\valueof} takes care of the translation of \type {width} or \type
+%D {breedte} to \type {width} and combining this with \type {box} to \type
+%D {\boxwidth}.
+%D
+%D One advantage of this other scheme is that, within certain limits, we can
+%D implement an interface that can be switched to another language at will, while
+%D the current approach fixes the interface at startup. There are, by the way, other
+%D reasons too for not choosing this scheme. Switching user generated commands is
+%D for instance impossible and a dual interface would therefore give a strange mix
+%D of languages.
+%D
+%D Now let's work out the first scheme. Although the left hand of the assignment is
+%D a variable from the users point of view, it is a constant in terms of the system.
+%D Both \type {width} and \type {breedte} expand to \type {width} because in the
+%D source we only encounter \type {width}. Such system constants are presented as
+%D
+%D \starttyping
+%D \c!width
+%D \stoptyping
+%D
+%D This constant is always equivalent to \type {width}. As we can see, we use \type
+%D {c!} to mark this one as constant. Its dutch counterpart is:
+%D
+%D \starttyping
+%D breedte
+%D \stoptyping
+%D
+%D When we interpret a setup command each variable is translated to it's \type{c!}
+%D counterpart. This means that \type {breedte} and \type{width} expand to \type
+%D {breedte} and \type {\c!width} which both expand to \type {width}. That way user
+%D variables become system constants.
+%D
+%D The interpretation is done by means of a general setup command \type
+%D {\getparameters} that we introduced in the system module. Let us define some
+%D simple setup command:
+%D
+%D \starttyping
+%D \protected\def\setupbox[#1]%
+%D {\getparameters[\??bx][#1]}
+%D \stoptyping
+%D
+%D This command can be used as:
+%D
+%D \starttyping
+%D \setupbox [width=3cm, height=1cm]
+%D \stoptyping
+%D
+%D Afterwards we have two variables \type {\@@bxwidth} and \type {\@@bxheight} which
+%D have the values \type {3cm} and \type {1cm} assigned. These variables are a
+%D combinatiom of the setup prefix \type {\??bx}, which expands to \type {@@bx} and
+%D the translated user supplied variables \type {width} and \type {height} or \type
+%D {breedte} and \type {hoogte}, depending on the actual language. In dutch we just
+%D say:
+%D
+%D \starttyping
+%D \stelblokin [breedte=3cm,hoogte=1cm]
+%D \stoptyping
+%D
+%D and get ourselves \type {\@@bxwidth} and \type {\@@bxheight} too. In the source
+%D of \CONTEXT, we can recognize constants and variables on their leading \type
+%D {c!}, \type {v!} etc., prefixes on \type {??} and composed variables on \type
+%D {@@}.
+%D
+%D We already saw that user supplied keywords need some special treatment too. This
+%D time we don't translate the keyword, but instead use in the source a variable
+%D which meaning depends on the interface language.
+%D
+%D \starttyping
+%D \v!left
+%D \stoptyping
+%D
+%D Which can be used in macro's like:
+%D
+%D \starttyping
+%D \processaction
+%D [\@@bxlocation]
+%D [ \v!left=>\dosomethingontheleft,
+%D \v!middle=>\dosomthinginthemiddle,
+%D \v!right=>\dosomethingontheright]
+%D \stoptyping
+%D
+%D Because variables like \type {\@@bxlocation} can have a lot of meanings,
+%D including tricky expandable tokens, we cannot translate this meaning when we
+%D compare. This means that \type {\@@bxlocation} can be \type {left} of \type
+%D {links} of whatever meaning suits the language. But because \type {\v!left} also
+%D has a meaning that suits the language, we are able to compare.
+%D
+%D Although we know it sounds confusing we want to state two important
+%D characteristics of the interface as described:
+%D
+%D \startnarrower \em
+%D user variables become system constants
+%D \stopnarrower
+%D
+%D and
+%D
+%D \startnarrower \em
+%D user constants (keywords) become system variables
+%D \stopnarrower
+%D
+%D The \type {\c!internal} is a left over from the time that the user interface
+%D documents were not using a specification alongside a keyword specification but
+%D used a shared file in which case we need to go in both directions.
+
+% temporary mkiv hack (we can best just store the whole table in memory)
+
+\permanent\overloaded\protected\def\setinterfaceconstant#1#2%
+ {\clf_setinterfaceconstant{#1}{#2}%
+ \immutable\defcsname\c!prefix!#1\endcsname{#1}}
+
+\permanent\overloaded\protected\def\setinterfacevariable#1#2%
+ {\clf_setinterfacevariable{#1}{#2}%
+ \immutable\defcsname\v!prefix!#1\endcsname{#2}}
+
+%D \macros
+%D {defineinterfaceconstant}
+%D
+%D Next we redefine a previously defined macro to take care of interface translation
+%D too. It's a bit redundant, because in these situations we could use the
+%D c||version, but for documentation purposes the x||alternative comes in handy.
+
+\permanent\overloaded\protected\def\defineinterfaceconstant#1#2%
+ {\immutable\defcsname\c!prefix!#1\endcsname{#2}}
+
+%D \macros
+%D {startelements}
+%D
+%D Due to the object oriented nature of \CONTEXT, we also need to define the
+%D elements that are used to build commands.
+%D
+%D Such elements sometimes are the same in different languages, but mostly they
+%D differ. Things can get even confusing when we look at for instance the setup
+%D commands. In english we say \type{\setup<something>}, but in dutch we have: \type
+%D {\stel<iets>in}. Such split elements are no problem, because we just define two
+%D elements. When no second part is needed, we use a \type {-}:
+
+\permanent\overloaded\protected\def\setinterfaceelement#1#2%
+ {\clf_setinterfaceelement{#1}{#2}%
+ \ifcsname\e!prefix!#1\endcsname
+ \doifnotvalue{\e!prefix!#1}{#2}{\enforced\immutable\setvalue{\e!prefix!#1}{#2}}%
+ \else
+ \enforced\immutable\setvalue{\e!prefix!#1}{#2}%
+ \fi}
+
+\permanent\protected\def\setinterfacecommand#1#2% \frozen ? \permanent ?
+ {\doifnot{#1}{#2}% todo: let when already defined
+ {\defcsname#2\expandafter\endcsname\expandafter{\csname#1\endcsname}}}
+
+%D We just ignore these:
+
+\permanent\protected\def\startvariables{\gobbleuntil\stopvariables} \aliased\let\stopvariables\relax
+\permanent\protected\def\startconstants{\gobbleuntil\stopconstants} \aliased\let\stopconstants\relax
+\permanent\protected\def\startelements {\gobbleuntil\stopelements } \aliased\let\stopelements \relax
+\permanent\protected\def\startcommands {\gobbleuntil\stopcommands } \aliased\let\stopcommands \relax
+
+%D For at the \LUA\ end (experiment):
+
+% \def\ui_c#1#2{\immutable\gdefcsname\c!prefix!#1\endcsname{#1}%
+% \immutable\gdefcsname\k!prefix!#2\endcsname{#1}} % backmapping from non english
+% \def\ui_s #1{\immutable\gdefcsname\c!prefix!#1\endcsname{#1}%
+% \immutable\gdefcsname\k!prefix!#1\endcsname{#1}} % backmapping from non english
+% \def\ui_v#1#2{\immutable\gdefcsname\v!prefix!#1\endcsname{#2}}
+% \def\ui_e#1#2{\immutable\gdefcsname\e!prefix!#1\endcsname{#2}}
+% \def\ui_a#1#2{\frozen\protected\def#2{#1}} % will go away
+%
+% \startinterface english
+%
+% \def\ui_c#1#2{\immutable\gdefcsname\c!prefix!#1\endcsname{#1}}
+% \def\ui_s #1{\immutable\gdefcsname\c!prefix!#1\endcsname{#1}}
+%
+% \stopinterface
+
+%D So much for the basic multi||lingual interface commands. The macro's can be
+%D enhanced with more testing facilities, but for the moment they suffice.
+
+% use \Uchar
+
+\ifdefined\zwnj \else \immutable\edef\zwnj{\directlua{utf.char(\number"200C)}} \fi % needed for cont-pe % maybe to char-utf.mkiv
+\ifdefined\zwj \else \immutable\edef\zwj {\directlua{utf.char(\number"200D)}} \fi % needed for cont-pe % maybe to char-utf.mkiv
+
+%D \macros
+%D {contextversion, contextversionnumber, contextversionno,
+%D contextbanner, showcontextbanner, formatversion}
+%D
+%D Out of convenience we define the banners here. This might move to the \LUA\ end.
+
+\pushoverloadmode
+
+\permanent\overloaded\def\contextbanner
+ {ConTeXt \space
+ ver: \contextversion \space \contextmark \space \space
+ fmt: \formatversion \space \space
+ int: \currentinterface/\currentresponses}
+
+\permanent\overloaded\protected\def\showcontextbanner
+ {\writestatus\m!system\empty
+ \writestatus\m!system\contextbanner
+ \writestatus\m!system\empty}
+
+\immutable\overloaded\edef\formatversion
+ {\the\normalyear.\the\normalmonth.\the\normalday}
+
+\newcount\contextversionno
+
+\ifdefined\contextversion \else
+ \immutable\edef\contextversion{\the\normalyear.\the\normalmonth.\the\normalday\space 00:00}
+\fi
+
+\permanent\overloaded\protected\def \contextversionnumber #1.#2.#3 #4:#5\relax{#1#2#3}
+ \contextversionno \expandafter\contextversionnumber\contextversion\relax
+\immutable\overloaded \edef\contextversionnumber {\the\contextversionno\space\contextmark}
+
+\popoverloadmode
+
+%D \macros
+%D {everydump}
+%D
+%D This one is only used when we generate the format.
+
+% \ifdefined\everydump \else
+% \newtoks\everydump
+% \def\dump{\the\everydump\global\everydump\emptytoks\glet\dump\relax\normaldump}
+% \fi
+
+% \appendtoks \showcontextbanner \to \everydump
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-prm.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mult-prm.mkxl
new file mode 100644
index 00000000000..2ec763c3943
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-prm.mkxl
@@ -0,0 +1,117 @@
+%D \module
+%D [ file=mult-prm,
+%D version=2011.09.18, % actually older
+%D title=\CONTEXT\ Multilingual Macros,
+%D subtitle=Primitives,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is therefore
+%C copyrighted by \PRAGMA. See mreadme.pdf for details.
+
+%D This file is only a helper for generating files that can be used in an
+%D editor for syntax highlighting.
+
+% local all = table.load("mult-prm.lua")
+% local tex = table.tohash(all.tex)
+% for k, v in next, all do
+% if k ~= "tex" then
+% local h = table.tohash(v)
+% for k, v in next, h do
+% tex[k] = nil
+% end
+% all[k] = table.sortedkeys(h)
+% end
+% end
+% all.tex = table.sortedkeys(tex)
+% print((string.gsub(table.serialize(all,true),' "','\n "')))
+
+\startluacode
+
+ context.starttext()
+
+ local missing = {
+ tex = {
+ -- todo: differenced between luatex and luametatex
+ },
+ etex = {
+ -- todo: differenced between luatex and luametatex
+ },
+ luatex = {
+ -- todo: differenced between luatex and luametatex
+ },
+ pdftex = { -- maybe not complete
+ "ifpdfabsdim", "ifpdfabsnum", "ifpdfprimitive", "pdfadjustspacing",
+ "pdfannot", "pdfcatalog", "pdfcolorstack", "pdfcolorstackinit",
+ "pdfcompresslevel", "pdfcopyfont", "pdfcreationdate",
+ "pdfdecimaldigits", "pdfdest", "pdfdestmargin", "pdfdraftmode",
+ "pdfeachlinedepth", "pdfeachlineheight", "pdfendlink",
+ "pdfendthread", "pdffirstlineheight", "pdffontattr", "pdffontexpand",
+ "pdffontname", "pdffontobjnum", "pdffontsize", "pdfgamma",
+ "pdfgentounicode", "pdfglyphtounicode", "pdfhorigin",
+ "pdfignoreddimen", "pdfignoreunknownimages", "pdfimageaddfilename",
+ "pdfimageapplygamma", "pdfimagegamma", "pdfimagehicolor",
+ "pdfimageresolution", "pdfincludechars", "pdfinclusioncopyfonts",
+ "pdfinclusionerrorlevel", "pdfinfo", "pdfinfoomitdate",
+ "pdfinsertht", "pdflastannot", "pdflastlinedepth", "pdflastlink",
+ "pdflastobj", "pdflastxform", "pdflastximage", "pdflastximagepages",
+ "pdflastxpos", "pdflastypos", "pdflinkmargin", "pdfliteral",
+ "pdfmapfile", "pdfmapline", "pdfmajorversion", "pdfminorversion", "pdfnames",
+ "pdfnoligatures", "pdfnormaldeviate", "pdfobj", "pdfrecompress",
+ "pdfobjcompresslevel", "pdfoutline", "pdfoutput", "pdfpageattr",
+ "pdfpagebox", "pdfpageheight", "pdfpageref", "pdfpageresources",
+ "pdfpagesattr", "pdfpagewidth", "pdfpkfixeddpi", "pdfpkmode",
+ "pdfpkresolution", "pdfprimitive", "pdfprotrudechars", "pdfpxdimen",
+ "pdfrandomseed", "pdfrefobj", "pdfrefxform", "pdfrefximage",
+ "pdfreplacefont", "pdfrestore", "pdfretval", "pdfsave", "pdfsavepos",
+ "pdfsetmatrix", "pdfsetrandomseed", "pdfstartlink", "pdfstartthread",
+ "pdfsuppressoptionalinfo", "pdfsuppressptexinfo", "pdftexbanner",
+ "pdftexrevision", "pdftexversion", "pdfthread", "pdfthreadmargin",
+ "pdftracingfonts", "pdftrailer", "pdftrailerid", "pdfuniformdeviate",
+ "pdfuniqueresname", "pdfvorigin", "pdfxform", "pdfxformattr",
+ "pdfxformmargin", "pdfxformname", "pdfxformresources", "pdfximage",
+ "pdfomitcidset", "pdfomitcharset",
+ },
+ aleph = { -- we don't bother
+ "Alephminorversion", "Alephrevision", "Alephversion",
+ },
+ omega = { -- we don't bother
+ "Omegaminorversion", "Omegarevision", "Omegaversion",
+ },
+ xetex = { -- we don't bother
+ "XeTeXversion",
+ },
+ -- plain = {
+ -- "TeX",
+ -- "bgroup", "egroup", "endgraf", "space", "empty", "null",
+ -- "newcount", "newdimen", "newskip", "newmuskip", "newbox", "newtoks", "newhelp", "newread", "newwrite", "newfam", "newlanguage", "newinsert", "newif",
+ -- "maxdimen", "magstephalf", "magstep",
+ -- "frenchspacing", "nonfrenchspacing", "normalbaselines", "obeylines", "obeyspaces", "raggedright", "ttraggedright",
+ -- "thinspace", "negthinspace", "enspace", "enskip", "quad", "qquad",
+ -- "smallskip", "medskip", "bigskip", "removelastskip", "topglue", "vglue", "hglue",
+ -- "break", "nobreak", "allowbreak", "filbreak", "goodbreak", "smallbreak", "medbreak", "bigbreak",
+ -- "line", "leftline", "rightline", "centerline", "rlap", "llap", "underbar", "strutbox", "strut",
+ -- "cases", "matrix", "pmatrix", "bordermatrix", "eqalign", "displaylines", "eqalignno", "leqalignno",
+ -- "pageno", "folio", "tracingall", "showhyphens", "fmtname", "fmtversion",
+ -- "hphantom", "vphantom", "phantom", "smash",
+ -- },
+ }
+
+ local primitives = {
+ tex = table.sorted( table.merged( missing.tex , tex.extraprimitives("core","tex") ) ),
+ etex = table.sorted( table.merged( missing.etex , tex.extraprimitives("etex") ) ),
+ pdftex = table.sorted( table.merged( missing.pdftex, { } ) ),
+ luatex = table.sorted( table.merged( missing.luatex, tex.extraprimitives("luatex") ) ),
+ aleph = table.sorted( table.merged( missing.aleph , { } ) ),
+ omega = table.sorted( table.merged( missing.omega , { } ) ),
+ xetex = table.sorted( table.merged( missing.xetex , { } ) ),
+ }
+
+ -- table.remove(primitives.tex,1) -- get rid of \-
+
+ io.savedata("mult-prm.lua",table.serialize(primitives,true,{ reduce = true, inline = false }))
+
+ context.stoptext()
+
+\stopluacode
diff --git a/Master/texmf-dist/tex/context/base/mkxl/mult-sys.mkxl b/Master/texmf-dist/tex/context/base/mkxl/mult-sys.mkxl
new file mode 100644
index 00000000000..3e1d32a916d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/mult-sys.mkxl
@@ -0,0 +1,607 @@
+%D \module
+%D [ file=mult-sys,
+%D version=1996.06.01,
+%D title=\CONTEXT\ Multilingual Macros,
+%D subtitle=System,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D In boring module we define a lot of obscure but useful system constants.
+%D By doing so we save lots of memory while at the same time we prevent
+%D ourself making typing errors.
+
+\writestatus{loading}{ConTeXt Multilingual Macros / System}
+
+\unprotect
+
+%D This file is mostly the same as the \MKII\ variant but we keep extending
+%D \MKIV, so it was bout time to have a dedicated variant.
+%D
+%D The constants are grouped in such a way that there is a minimal change of
+%D conflicts.
+%D
+%D \starttyping
+%D \definesystemconstants {word}
+%D \definemessageconstant {word}
+%D \stoptyping
+%D
+%D This commands generate \type{\s!word} and \type{\m!word}.
+
+\definesystemconstant {hans}
+\definesystemconstant {taco}
+\definesystemconstant {wolfgang}
+
+%D First we define some system constants used for both the multi||lingual
+%D interface and multi||linguag typesetting.
+
+% definesystemconstant {slovene}
+% definesystemconstant {cz}
+% definesystemconstant {vn}
+
+\definesystemconstant {afrikaans} \definesystemconstant {af}
+\definesystemconstant {ancientgreek} \definesystemconstant {agr}
+\definesystemconstant {ancientlatin} \definesystemconstant {ala}
+\definesystemconstant {arabic} \definesystemconstant {ar}
+\definesystemconstant {belarussian} \definesystemconstant {be}
+\definesystemconstant {bokmal} \definesystemconstant {nb}
+\definesystemconstant {catalan} \definesystemconstant {ca}
+\definesystemconstant {chinese} \definesystemconstant {cn}
+\definesystemconstant {croatian} \definesystemconstant {hr}
+\definesystemconstant {czech} \definesystemconstant {cs}
+\definesystemconstant {danish} \definesystemconstant {da}
+\definesystemconstant {dutch} \definesystemconstant {nl}
+\definesystemconstant {english} \definesystemconstant {en}
+\definesystemconstant {estonian} \definesystemconstant {et}
+\definesystemconstant {esperanto} % \definesystemconstant {eo}
+\definesystemconstant {farsi} \definesystemconstant {fa} % just persian
+\definesystemconstant {finnish} \definesystemconstant {fi}
+\definesystemconstant {french} \definesystemconstant {fr}
+\definesystemconstant {gbenglish} \definesystemconstant {gb}
+\definesystemconstant {german} \definesystemconstant {de}
+\definesystemconstant {greek} \definesystemconstant {gr}
+\definesystemconstant {hebrew} \definesystemconstant {he}
+\definesystemconstant {yiddish} \definesystemconstant {yi}
+\definesystemconstant {hungarian} \definesystemconstant {hu}
+\definesystemconstant {italian} \definesystemconstant {it}
+\definesystemconstant {japanese} \definesystemconstant {ja}
+\definesystemconstant {korean} \definesystemconstant {kr}
+\definesystemconstant {latin} \definesystemconstant {la}
+\definesystemconstant {lithuanian} \definesystemconstant {lt}
+\definesystemconstant {malayalam} \definesystemconstant {ml}
+\definesystemconstant {macedonian} \definesystemconstant {mk}
+\definesystemconstant {norwegian} \definesystemconstant {no}
+\definesystemconstant {nynorsk} \definesystemconstant {nn}
+\definesystemconstant {persian} \definesystemconstant {pe}
+\definesystemconstant {polish} \definesystemconstant {pl}
+\definesystemconstant {portuguese} \definesystemconstant {pt}
+\definesystemconstant {romanian} \definesystemconstant {ro}
+\definesystemconstant {russian} \definesystemconstant {ru}
+\definesystemconstant {slovak} \definesystemconstant {sk}
+\definesystemconstant {slovenian} \definesystemconstant {sl}
+\definesystemconstant {serbian} \definesystemconstant {sr}
+\definesystemconstant {spanish} \definesystemconstant {es}
+\definesystemconstant {swedish} \definesystemconstant {sv}
+\definesystemconstant {thai} \definesystemconstant {th} % mojca mentioned it at BT2013 but we need more info
+\definesystemconstant {turkish} \definesystemconstant {tr}
+\definesystemconstant {turkmen} \definesystemconstant {tk}
+\definesystemconstant {ukenglish} \definesystemconstant {uk}
+\definesystemconstant {ukrainian} \definesystemconstant {ua}
+\definesystemconstant {usenglish} \definesystemconstant {us}
+\definesystemconstant {vietnamese} \definesystemconstant {vi}
+
+%D Left|-|overs:
+
+\defineinterfaceconstant {HL} {HL}
+\defineinterfaceconstant {VL} {VL}
+\defineinterfaceconstant {NL} {NL}
+
+%D For proper \UNICODE\ support we need a few font related constants.
+
+\definesystemconstant {action}
+\definesystemconstant {all}
+\definesystemconstant {ampersand}
+\definesystemconstant {ascii}
+\definesystemconstant {attribute}
+\definesystemconstant {attr}
+\definesystemconstant {author}
+\definesystemconstant {auto}
+\definesystemconstant {axis}
+\definesystemconstant {exact}
+\definesystemconstant {a}
+\definesystemconstant {bf}
+\definesystemconstant {big}
+\definesystemconstant {binary}
+\definesystemconstant {bi}
+\definesystemconstant {blackboard}
+\definesystemconstant {black}
+\definesystemconstant {BoldItalic}
+\definesystemconstant {BoldSlanted}
+\definesystemconstant {Bold}
+\definesystemconstant {bold}
+\definesystemconstant {both}
+\definesystemconstant {bottomfloat}
+\definesystemconstant {bottom}
+\definesystemconstant {box}
+\definesystemconstant {bp}
+\definesystemconstant {bs}
+\definesystemconstant {b}
+\definesystemconstant {calligraphy}
+\definesystemconstant {Calligraphy}
+\definesystemconstant {Caps}
+\definesystemconstant {casual}
+\definesystemconstant {Casual}
+\definesystemconstant {catcodes}
+\definesystemconstant {catcodetable}
+\definesystemconstant {cc}
+\definesystemconstant {cg}
+\definesystemconstant {chain}
+\definesystemconstant {check}
+\definesystemconstant {child}
+\definesystemconstant {cite}
+\definesystemconstant {class}
+\definesystemconstant {clone}
+\definesystemconstant {cmyk}
+\definesystemconstant {cm}
+\definesystemconstant {code}
+\definesystemconstant {collapsing}
+\definesystemconstant {command}
+\definesystemconstant {compare}
+\definesystemconstant {complex}
+\definesystemconstant {container}
+\definesystemconstant {counter}
+\definesystemconstant {cramped}
+\definesystemconstant {current}
+\definesystemconstant {c}
+\definesystemconstant {data}
+\definesystemconstant {dd}
+\definesystemconstant {decimal}
+\definesystemconstant {DefaultFont}
+\definesystemconstant {default}
+\definesystemconstant {depth}
+\definesystemconstant {designsize}
+\definesystemconstant {direction}
+\definesystemconstant {display}
+\definesystemconstant {document}
+\definesystemconstant {dodo}
+\definesystemconstant {done}
+\definesystemconstant {double}
+\definesystemconstant {do}
+\definesystemconstant {dtp}
+\definesystemconstant {dummy}
+\definesystemconstant {d}
+\definesystemconstant {edge}
+\definesystemconstant {effect}
+\definesystemconstant {empty}
+\definesystemconstant {em}
+\definesystemconstant {environment}
+\definesystemconstant {extensions}
+\definesystemconstant {external}
+\definesystemconstant {ex}
+\definesystemconstant {e}
+\definesystemconstant {fallbacks}
+\definesystemconstant {fallback}
+\definesystemconstant {false}
+\definesystemconstant {fax}
+\definesystemconstant {features}
+\definesystemconstant {file}
+\definesystemconstant {filll}
+\definesystemconstant {fill}
+\definesystemconstant {fil}
+\definesystemconstant {first}
+\definesystemconstant {float}
+\definesystemconstant {font}
+\definesystemconstant {forget}
+\definesystemconstant {format}
+\definesystemconstant {fraktur}
+\definesystemconstant {full}
+\definesystemconstant {f}
+\definesystemconstant {global}
+\definesystemconstant {goodies}
+\definesystemconstant {gray}
+\definesystemconstant {handler}
+\definesystemconstant {handwriting}
+\definesystemconstant {Handwriting}
+\definesystemconstant {hascaption}
+\definesystemconstant {haslevel}
+\definesystemconstant {hasnumber}
+\definesystemconstant {hasparent}
+\definesystemconstant {hassuffix}
+\definesystemconstant {hastitle}
+\definesystemconstant {head}
+\definesystemconstant {height}
+\definesystemconstant {hw}
+\definesystemconstant {hyphenmin}
+\definesystemconstant {indeed}
+\definesystemconstant {initializations}
+\definesystemconstant {insert} % maybe insertclass
+\definesystemconstant {integral}
+\definesystemconstant {internal}
+\definesystemconstant {in}
+\definesystemconstant {italics}
+\definesystemconstant {Italic}
+\definesystemconstant {italic}
+%definesystemconstant {it}
+\definesystemconstant {itemgroupcolumns}
+\definesystemconstant {kernpairs}
+\definesystemconstant {language}
+\definesystemconstant {lcgreek}
+\definesystemconstant {lefthyphenchar}
+\definesystemconstant {lefthyphenmin}
+\definesystemconstant {leftpage}
+\definesystemconstant {left}
+\definesystemconstant {level}
+\definesystemconstant {ligatures}
+\definesystemconstant {line}
+\definesystemconstant {link}
+\definesystemconstant {list}
+\definesystemconstant {local}
+\definesystemconstant {lua}
+\definesystemconstant {mapping}
+\definesystemconstant {map}
+\definesystemconstant {margintext}
+\definesystemconstant {margin}
+\definesystemconstant {marker}
+\definesystemconstant {MathBold} % not used
+\definesystemconstant {MathRomanBold}
+\definesystemconstant {MathRoman}
+\definesystemconstant {math}
+\definesystemconstant {Math} % not used
+\definesystemconstant {mblr}
+\definesystemconstant {mbrl}
+\definesystemconstant {mb}
+\definesystemconstant {middle}
+\definesystemconstant {minus}
+\definesystemconstant {mixedcolumn}
+\definesystemconstant {mi} % maybe some day a special default vector
+\definesystemconstant {mkii}
+\definesystemconstant {mkiv}
+\definesystemconstant {mm}
+\definesystemconstant {mnem} % kind of generic short tag
+\definesystemconstant {mode}
+\definesystemconstant {MonoBoldItalic}
+\definesystemconstant {MonoBoldSlanted}
+\definesystemconstant {MonoBold}
+\definesystemconstant {MonoCaps}
+\definesystemconstant {MonoItalic}
+\definesystemconstant {MonoSlanted}
+\definesystemconstant {Mono}
+\definesystemconstant {mono}
+\definesystemconstant {mrlr}
+\definesystemconstant {mrrl}
+\definesystemconstant {mr}
+\definesystemconstant {multi}
+\definesystemconstant {name}
+\definesystemconstant {next}
+\definesystemconstant {nocite}
+\definesystemconstant {nodepth}
+\definesystemconstant {noheight}
+\definesystemconstant {nomath}
+\definesystemconstant {none}
+\definesystemconstant {normal}
+\definesystemconstant {Normal}
+\definesystemconstant {nowidth}
+\definesystemconstant {numbering}
+\definesystemconstant {number}
+\definesystemconstant {options}
+\definesystemconstant {ord}
+\definesystemconstant {orientation}
+\definesystemconstant {otr}
+\definesystemconstant {pagefloat}
+\definesystemconstant {page}
+\definesystemconstant {paragraph}
+\definesystemconstant {parent}
+\definesystemconstant {patterns}
+\definesystemconstant {pc}
+\definesystemconstant {pickup}
+\definesystemconstant {plural}
+\definesystemconstant {plus}
+%definesystemconstant {pt}
+\definesystemconstant {realpage}
+\definesystemconstant {register}
+\definesystemconstant {Regular}
+\definesystemconstant {regular}
+\definesystemconstant {rel}
+\definesystemconstant {reset}
+\definesystemconstant {reverse}
+\definesystemconstant {rgb}
+\definesystemconstant {righthyphenchar}
+\definesystemconstant {righthyphenmin}
+\definesystemconstant {rightpage}
+\definesystemconstant {right}
+\definesystemconstant {rm}
+\definesystemconstant {rname}
+\definesystemconstant {rscale}
+\definesystemconstant {SansBoldItalic}
+\definesystemconstant {SansBoldSlanted}
+\definesystemconstant {SansBold}
+\definesystemconstant {SansCaps}
+\definesystemconstant {SansItalic}
+\definesystemconstant {SansSlanted}
+\definesystemconstant {Sans}
+\definesystemconstant {sans}
+\definesystemconstant {scriptscript}
+\definesystemconstant {script}
+\definesystemconstant {sc}
+\definesystemconstant {second}
+\definesystemconstant {section}
+\definesystemconstant {SerifBoldItalic}
+\definesystemconstant {SerifBoldSlanted}
+\definesystemconstant {SerifBold}
+\definesystemconstant {SerifCaps}
+\definesystemconstant {SerifItalic}
+\definesystemconstant {SerifSlanted}
+\definesystemconstant {Serif}
+\definesystemconstant {serif}
+\definesystemconstant {setups}
+\definesystemconstant {setup}
+\definesystemconstant {set}
+\definesystemconstant {themaintextcolor}
+\definesystemconstant {simple}
+\definesystemconstant {Simple}
+\definesystemconstant {single}
+\definesystemconstant {singular}
+\definesystemconstant {size}
+\definesystemconstant {slanted}
+\definesystemconstant {Slanted}
+%definesystemconstant {sl}
+\definesystemconstant {smallcaps}
+\definesystemconstant {small}
+\definesystemconstant {somewhere}
+\definesystemconstant {special}
+\definesystemconstant {spec}
+\definesystemconstant {spot}
+\definesystemconstant {spread}
+\definesystemconstant {sp}
+\definesystemconstant {ss}
+\definesystemconstant {start}
+\definesystemconstant {stop}
+\definesystemconstant {subpage}
+\definesystemconstant {subtype}
+\definesystemconstant {sub}
+\definesystemconstant {Support}
+\definesystemconstant {sygreek}
+\definesystemconstant {symbol}
+\definesystemconstant {system} % not yet interfaces messages
+\definesystemconstant {text}
+\definesystemconstant {tex}
+\definesystemconstant {tf}
+\definesystemconstant {third}
+\definesystemconstant {topfloat}
+\definesystemconstant {top}
+\definesystemconstant {to}
+\definesystemconstant {traditional}
+\definesystemconstant {triple}
+\definesystemconstant {true}
+\definesystemconstant {tt}
+\definesystemconstant {Type}
+\definesystemconstant {ucgreek}
+\definesystemconstant {uncramped}
+\definesystemconstant {unexpanded}
+\definesystemconstant {Unicode}
+\definesystemconstant {unknown}
+\definesystemconstant {userdata}
+\definesystemconstant {userpage}
+\definesystemconstant {user}
+\definesystemconstant {vfrac}
+\definesystemconstant {white}
+\definesystemconstant {width}
+\definesystemconstant {xml}
+\definesystemconstant {xmove}
+\definesystemconstant {xoffset}
+\definesystemconstant {xx}
+\definesystemconstant {x}
+\definesystemconstant {ymove}
+\definesystemconstant {yoffset}
+
+%D As the name of their define command states, the next set of constants is used in
+%D the message macro's.
+
+\definemessageconstant {backend}
+\definemessageconstant {check}
+\definemessageconstant {chemicals}
+\definemessageconstant {colors}
+\definemessageconstant {columns}
+\definemessageconstant {fields}
+\definemessageconstant {figures}
+\definemessageconstant {files}
+\definemessageconstant {floatblocks}
+\definemessageconstant {fonts}
+\definemessageconstant {handlings}
+\definemessageconstant {interactions}
+\definemessageconstant {javascripts}
+\definemessageconstant {languages}
+\definemessageconstant {layouts}
+\definemessageconstant {lua}
+\definemessageconstant {metapost}
+\definemessageconstant {publications}
+\definemessageconstant {references}
+\definemessageconstant {regimes}
+\definemessageconstant {structures}
+\definemessageconstant {symbols}
+\definemessageconstant {system}
+\definemessageconstant {textblocks}
+\definemessageconstant {verbatims}
+\definemessageconstant {versions}
+
+%D When we use numbers and dimensions the same applies as with the keywords like
+%D \type {width} and \type {plus} mentioned earlier.
+
+\immutable\def\!!ten {10}
+\immutable\def\!!twelve {12}
+\immutable\def\!!hundred {100}
+\immutable\def\!!thousand {1000}
+\immutable\def\!!tenthousand {10000}
+\immutable\def\!!maxcard {65536}
+\immutable\def\!!medcard {32768}
+
+\immutable\def\!!zeropoint {0pt}
+\immutable\def\!!onepoint {1pt}
+\immutable\def\!!twopoint {2pt}
+\immutable\def\!!threepoint {3pt}
+\immutable\def\!!fourpoint {4pt}
+\immutable\def\!!fivepoint {5pt}
+\immutable\def\!!sixpoint {6pt}
+\immutable\def\!!sevenpoint {7pt}
+\immutable\def\!!eightpoint {8pt}
+\immutable\def\!!ninepoint {9pt}
+\immutable\def\!!tenpoint {10pt}
+\immutable\def\!!elevenpoint {11pt}
+\immutable\def\!!twelvepoint {12pt}
+\immutable\def\!!fourteenpointfour {14.4pt}
+
+\immutable\def\!!plustoken {+} % todo: \??plustoken
+\immutable\def\!!minustoken {-} % todo: \??minustoken
+
+\immutable\def\__unknown__ {\string\\//} % unlikely value
+
+%D Variables are composed of a command specific tag and a user supplied variable
+%D (system constant). The tag \type {du} for instance is available as \type {\??du}
+%D and expands to \type {@@du} in composed variables.
+
+\definesystemvariable {du} % dummy, will stay
+
+% bibl:
+
+\definesystemvariable {pv} % PublicationVariable
+\definesystemvariable {pb} % PuBlication
+
+% needs checking (namespaces now)
+
+\definesystemvariable {fw} % simpleFonts by Wolfgang
+
+% old pragma
+
+\definesystemvariable {kt} % KonTakten
+\definesystemvariable {kw} % KontaktWaarde
+\definesystemvariable {st} % STickers
+\definesystemvariable {km} % KenMerk
+
+% mkii
+
+\definesystemvariable {xf} % XML File (xtag)
+\definesystemvariable {xp} % XML Processing (xtag, so still needed)
+\definesystemvariable {fo} % xml FO (xtag)
+\definesystemvariable {fx} % FoXet
+
+% mkiv
+
+%D Next we define some language independant one letter variables and keywords. We can
+%D actually make these system variables.
+
+\defineinterfaceconstant {x} {x}
+\defineinterfaceconstant {y} {y}
+\defineinterfaceconstant {w} {w}
+\defineinterfaceconstant {h} {h}
+\defineinterfaceconstant {s} {s}
+\defineinterfaceconstant {t} {t}
+
+\defineinterfaceconstant {a} {a} % can go
+\defineinterfaceconstant {b} {b} % can go
+\defineinterfaceconstant {c} {c} % can go
+\defineinterfaceconstant {d} {d} % can go
+\defineinterfaceconstant {e} {e} % can go
+\defineinterfaceconstant {f} {f} % can go
+
+%defineinterfaceconstant {s} {s}
+\defineinterfaceconstant {r} {r}
+\defineinterfaceconstant {g} {g}
+%defineinterfaceconstant {b} {b}
+%defineinterfaceconstant {c} {c}
+\defineinterfaceconstant {m} {m}
+%defineinterfaceconstant {y} {y}
+\defineinterfaceconstant {k} {k}
+%defineinterfaceconstant {a} {a} % alternative
+%defineinterfaceconstant {t} {t} % transparency
+\defineinterfaceconstant {p} {p} % percentage
+%defineinterfaceconstant {h} {h}
+%defineinterfaceconstant {s} {s}
+\defineinterfaceconstant {v} {v}
+
+\defineinterfaceconstant {rgb} {rgb}
+\defineinterfaceconstant {cmyk} {cmyk}
+\defineinterfaceconstant {mp} {mp}
+
+\defineinterfacevariable {rgb} {rgb}
+\defineinterfacevariable {cmyk} {cmyk}
+\defineinterfacevariable {mp} {mp}
+
+\defineinterfacevariable {s} {s}
+
+\defineinterfacevariable {a} {a}
+\defineinterfacevariable {b} {b}
+\defineinterfacevariable {c} {c}
+\defineinterfacevariable {d} {d}
+
+%D Special purpose variables:
+
+\immutable\def\v!oddeven#1{\ifodd#1\v!odd\else\v!even\fi}
+
+%D The names of files and their extensions are fixed. \CONTEXT\ uses as less
+%D files as possible. Utility files can be recognized by the first two
+%D characters of the extension: \type {tu}.
+
+%definefileconstant {utilityfilename} {texutil}
+%definefileconstant {blockextension} {tub}
+%definefileconstant {figureextension} {tuf}
+%definefileconstant {inputextension} {tui}
+%definefileconstant {outputextension} {tuo} % tup for previous run
+%definefileconstant {optionextension} {top}
+%definefileconstant {temporaryextension} {tmp}
+%definefileconstant {patternsextension} {pat}
+%definefileconstant {hyphensextension} {hyp}
+%definefileconstant {fontmapextension} {map}
+\definefileconstant {bibextension} {bbl}
+
+%D These files are loaded at start||up. They may contain system specific setups (or
+%D calls to other files), old macro's, to garantee compatibility and new macro's noy
+%D yet present in the format.
+
+\definefileconstant {sysfilename} {cont-sys.mkxl}
+\definefileconstant {newfilename} {cont-new.mkxl}
+\definefileconstant {locfilename} {cont-loc.mkxl}
+\definefileconstant {expfilename} {cont-exp.mkxl}
+\definefileconstant {fntfilename} {cont-fnt.mkxl} % not yet used
+\definefileconstant {gdsfilename} {cont-fnt.lfg} % not yet used
+
+%D The setup files for the language, font, color and special subsystems have a common
+%D prefix. This means that we have at most three characters for unique filenames.
+
+\definefileconstant {colo_run} {s-colors-show}
+\definefileconstant {font_run} {s-fonts-show}
+\definefileconstant {page_run} {s-layout-show}
+\definefileconstant {symb_run} {s-symbols-show}
+\definefileconstant {publ_tra} {s-publications-show}
+
+%D For figure inclusion we need(ed):
+
+%defineinterfaceconstant {tif} {tif}
+%defineinterfaceconstant {eps} {eps}
+%defineinterfaceconstant {mps} {mps}
+%defineinterfaceconstant {jpg} {jpg}
+%defineinterfaceconstant {pdf} {pdf}
+%defineinterfaceconstant {png} {png}
+%defineinterfaceconstant {avi} {avi}
+%defineinterfaceconstant {mov} {mov}
+%defineinterfaceconstant {svg} {svg}
+\defineinterfaceconstant {tex} {tex} % Also gone?
+%defineinterfaceconstant {tmp} {tmp}
+%defineinterfaceconstant {cld} {cld}
+
+%D A careful reader will have noticed that in the module \type {mult-ini} we defined
+%D \type {\selectinterface}. We were not yet able to actually select an interface,
+%D because we still had to define the constants and variables. Now we've done so,
+%D selection is permitted.
+
+\selectinterface
+
+%D Ok, here are some more, because we've got ourselves some extensions to \CONTEXT.
+
+\definemessageconstant {addresses}
+\definemessageconstant {documents}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-acc.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-acc.lmt
new file mode 100644
index 00000000000..2c3302a3ea7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-acc.lmt
@@ -0,0 +1,112 @@
+if not modules then modules = { } end modules ['node-acc'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- see mkiv lua module for some experimental unused code
+
+local nodes, node = nodes, node
+
+local tasks = nodes.tasks
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getattr = nuts.getattr
+local getlist = nuts.getlist
+local getchar = nuts.getchar
+local getnext = nuts.getnext
+
+local setattr = nuts.setattr
+local setlink = nuts.setlink
+local setchar = nuts.setchar
+local setsubtype = nuts.setsubtype
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+
+local nextglyph = nuts.traversers.glyph
+local nextnode = nuts.traversers.node
+
+local copy_node = nuts.copy
+local insert_after = nuts.insert_after
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+
+local glue_code = nodecodes.glue
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local userskip_code = gluecodes.user
+local spaceskip_code = gluecodes.spaceskip
+local xspaceskip_code = gluecodes.xspaceskip
+
+local a_characters = attributes.private("characters")
+
+local nofreplaced = 0
+
+-- todo: nbsp etc
+-- todo: collapse kerns (not needed, backend does this)
+-- todo: maybe cache as we now create many nodes
+-- todo: check for subtype related to spacing (13/14 but most seems to be user anyway)
+
+local trace = false trackers.register("backend.spaces", function(v) trace = v end)
+local slot = nil
+
+local function injectspaces(head)
+ -- This can become two fast loops or we just move this to the backend where we can
+ -- also check for spaces (it actually is rather old code that relates to tagging
+ -- and so, which was implemented rather early in the mkiv saga).
+ local p, p_id
+ local n = head
+ while n do
+ local id = getid(n)
+ if id == glue_code then
+ if p and getid(p) == glyph_code then
+ local s = getsubtype(n)
+ if s == spaceskip_code or s == xspaceskip_code then
+ local g = copy_node(p)
+ local a = getattr(n,a_characters)
+ setchar(g,slot)
+ setlink(p,g,n)
+ setwidth(n,getwidth(n) - getwidth(g))
+ if a then
+ setattr(g,a_characters,a)
+ end
+ setattr(n,a_characters,0)
+ nofreplaced = nofreplaced + 1
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ injectspaces(getlist(n),slot)
+ end
+ p_id = id
+ p = n
+ n = getnext(n)
+ end
+ return head
+end
+
+nodes.handlers.accessibility = function(head)
+ if trace then
+ if not slot then
+ slot = fonts.helpers.privateslot("visualspace")
+ end
+ else
+ slot = 32
+ end
+ return injectspaces(head,slot)
+end
+
+statistics.register("inserted spaces in output",function()
+ if nofreplaced > 0 then
+ return nofreplaced
+ end
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-aux.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-aux.lmt
new file mode 100644
index 00000000000..d952ec41b51
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-aux.lmt
@@ -0,0 +1,396 @@
+if not modules then modules = { } end modules ['node-aux'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: n1 .. n2 : __concat metatable
+
+local type, tostring = type, tostring
+
+local nodes = nodes
+local context = context
+
+local utfvalues = utf.values
+
+local nodecodes = nodes.nodecodes
+
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local attributelist_code = nodecodes.attributelist -- temporary
+local par_code = nodecodes.par
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+local tonode = nuts.tonode
+local vianuts = nuts.vianuts
+
+local getbox = nuts.getbox
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local getattr = nuts.getattr
+local getboth = nuts.getboth
+local getprev = nuts.getprev
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+local getboxglue = nuts.getboxglue
+local setboxglue = nuts.setboxglue
+
+local setfield = nuts.setfield
+local setattr = nuts.setattr
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local setattrlist = nuts.setattrlist
+
+local traversers = nuts.traversers
+local nextnode = traversers.node
+local nextglyph = traversers.glyph
+
+local flush_node = nuts.flush
+local flush_list = nuts.flush_list
+local hpack_nodes = nuts.hpack
+local unset_attribute = nuts.unset_attribute
+local first_glyph = nuts.first_glyph
+local copy_node = nuts.copy
+local find_tail = nuts.tail
+local getbox = nuts.getbox
+local count = nuts.count
+local isglyph = nuts.isglyph
+
+local nodepool = nuts.pool
+local new_glue = nodepool.glue
+local new_glyph = nodepool.glyph
+
+local unsetvalue = attributes.unsetvalue
+
+local current_font = font.current
+
+local texsetbox = tex.setbox
+
+local report_error = logs.reporter("node-aux:error")
+
+local function takebox(id)
+ local box = getbox(id)
+ if box then
+ local list = getlist(box)
+ setlist(box,nil)
+ local copy = copy_node(box)
+ if list then
+ setlist(copy,list)
+ end
+ texsetbox(id,false)
+ return copy
+ end
+end
+
+function nodes.takebox(id)
+ local b = takebox(id)
+ if b then
+ return tonode(b)
+ end
+end
+
+local splitbox = tex.splitbox
+nodes.splitbox = splitbox
+
+function nuts.splitbox(id,height)
+ return tonut(splitbox(id,height))
+end
+
+-- function nodes.takelist(n)
+-- -- when we need it
+-- end
+
+function nuts.takelist(n)
+ local l = getlist(n)
+ setlist(n)
+ flush_node(n)
+ return l
+end
+
+nuts.takebox = takebox
+tex.takebox = nodes.takebox -- sometimes more clear
+
+-- so far
+
+local function repackhlist(list,...)
+ local temp, b = hpack_nodes(list,...)
+ list = getlist(temp)
+ setlist(temp)
+ flush_node(temp)
+ return list, b
+end
+
+nuts.repackhlist = repackhlist
+
+function nodes.repackhlist(list,...)
+ local list, b = repackhlist(tonut(list),...)
+ return tonode(list), b
+end
+
+local function set_attributes(head,attr,value)
+ for n, id in nextnode, head do
+ setattr(n,attr,value)
+ if id == hlist_node or id == vlist_node then
+ set_attributes(getlist(n),attr,value)
+ end
+ end
+end
+
+local function set_unset_attributes(head,attr,value)
+ for n, id in nextnode, head do
+ if not getattr(n,attr) then
+ setattr(n,attr,value)
+ end
+ if id == hlist_code or id == vlist_code then
+ set_unset_attributes(getlist(n),attr,value)
+ end
+ end
+end
+
+local function unset_attributes(head,attr)
+ for n, id in nextnode, head do
+ setattr(n,attr,unsetvalue)
+ if id == hlist_code or id == vlist_code then
+ unset_attributes(getlist(n),attr)
+ end
+ end
+end
+
+-- for old times sake
+
+nuts.setattribute = nuts.setattr nodes.setattribute = nodes.setattr
+nuts.getattribute = nuts.getattr nodes.getattribute = nodes.getattr
+nuts.unsetattribute = nuts.unset_attribute nodes.unsetattribute = nodes.unset_attribute
+nuts.has_attribute = nuts.has_attribute nodes.has_attribute = nodes.has_attribute
+nuts.firstglyph = nuts.first_glyph nodes.firstglyph = nodes.first_glyph
+
+nuts.setattributes = set_attributes nodes.setattributes = vianuts(set_attributes)
+nuts.setunsetattributes = set_unset_attributes nodes.setunsetattributes = vianuts(set_unset_attributes)
+nuts.unsetattributes = unset_attributes nodes.unsetattributes = vianuts(unset_attributes)
+
+function nuts.firstcharacter(n,untagged) -- tagged == subtype > 255
+ if untagged then
+ return first_glyph(n)
+ else
+ for g in nextglyph ,n do
+ return g
+ end
+ end
+end
+
+local function firstcharinbox(n)
+ local l = getlist(getbox(n))
+ if l then
+ for g, c in nextglyph, l do
+ return c
+ end
+ end
+ return 0
+end
+
+nuts .firstcharinbox = firstcharinbox
+nodes.firstcharinbox = firstcharinbox -- hm, ok ?
+nodes.firstcharacter = vianuts(firstcharacter)
+
+interfaces.implement {
+ name = "buildtextaccent",
+ arguments = "integer",
+ actions = function(n) -- Is this crap really used? Or was it an experiment?
+ local char = firstcharinbox(n)
+ if char > 0 then
+ -- context.accent(false,char)
+ context([[\accent%s\relax]],char)
+ end
+ end
+}
+
+-- this depends on fonts, so we have a funny dependency ... will be
+-- sorted out .. we could make tonodes a plugin into this
+
+local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob-ini
+ if not str or str == "" then
+ return
+ end
+ local head, tail, space, fnt, template = nil, nil, nil, nil, nil
+ if not fnt then
+ fnt = current_font()
+ elseif type(fnt) ~= "number" and getid(fnt) == glyph_code then -- so it has to be a real node
+ fnt, template = nil, tonut(fnt)
+ end
+ for s in utfvalues(str) do
+ local n
+ if s == 32 then
+ if space then
+ n = copy_node(space)
+ elseif fonts then -- depedency
+ local parameters = fonts.hashes.identifiers[fnt].parameters
+ space = new_glue(parameters.space,parameters.space_stretch,parameters.space_shrink)
+ n = space
+ end
+ elseif template then
+ n = copy_node(template)
+ setvalue(n,"char",s)
+ else
+ n = new_glyph(fnt,s)
+ end
+ if attr then -- normally false when template
+ setattrlist(n,attr)
+ end
+ if head then
+ setlink(tail,n)
+ else
+ head = n
+ end
+ tail = n
+ end
+ return head, tail
+end
+
+nuts.tonodes = tonodes
+
+nodes.tonodes = function(str,fnt,attr)
+ local head, tail = tonodes(str,fnt,attr)
+ return tonode(head), tonode(tail)
+end
+
+local function link(list,currentfont,currentattr,head,tail) -- an oldie, might be replaced
+ for i=1,#list do
+ local n = list[i]
+ if n then
+ local tn = type(n)
+ if tn == "string" then
+ if #tn > 0 then
+ if not currentfont then
+ currentfont = current_font()
+ end
+ local h, t = tonodes(n,currentfont,currentattr)
+ if not h then
+ -- skip
+ elseif not head then
+ head, tail = h, t
+ else
+ setnext(tail,h)
+ setprev(h,t)
+ tail = t
+ end
+ end
+ elseif tn == "table" then
+ if #tn > 0 then
+ if not currentfont then
+ currentfont = current_font()
+ end
+ head, tail = link(n,currentfont,currentattr,head,tail)
+ end
+ elseif not head then
+ head = n
+ tail = find_tail(n)
+ elseif getid(n) == attributelist_code then
+ -- weird case
+ report_error("weird node type in list at index %s:",i)
+ for i=1,#list do
+ local l = list[i]
+ report_error("%3i: %s %S",i,getid(l) == attributelist_code and "!" or ">",l)
+ end
+ os.exit()
+ else
+ setlink(tail,n)
+ if getnext(n) then
+ tail = find_tail(n)
+ else
+ tail = n
+ end
+ end
+ else
+ -- permitting nil is convenient
+ end
+ end
+ return head, tail
+end
+
+nuts.link = link
+
+nodes.link = function(list,currentfont,currentattr,head,tail)
+ local head, tail = link(list,currentfont,currentattr,tonut(head),tonut(tail))
+ return tonode(head), tonode(tail)
+end
+
+local function locate(start,wantedid,wantedsubtype)
+ for n, id, subtype in nextnode, start do
+ if id == wantedid then
+ if not wantedsubtype or subtype == wantedsubtype then
+ return n
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local found = locate(getlist(n),wantedid,wantedsubtype)
+ if found then
+ return found
+ end
+ end
+ end
+end
+
+nuts.locate = locate
+
+function nodes.locate(start,wantedid,wantedsubtype)
+ local found = locate(tonut(start),wantedid,wantedsubtype)
+ return found and tonode(found)
+end
+
+local function rehpack(n,width)
+ local head = getlist(n)
+ local size = width or getwidth(n)
+ local temp = hpack_nodes(head,size,"exactly")
+ setwidth(n,size)
+ local set, order, sign = getboxglue(temp)
+ setboxglue(n,set,order,sign)
+ setlist(temp)
+ flush_node(temp)
+ return n
+end
+
+nuts.rehpack = rehpack
+
+function nodes.rehpack(n,...)
+ rehpack(tonut(n),...)
+end
+
+do
+
+ local parcodes = nodes.parcodes
+ local hmodepar_code = parcodes.hmode_par
+ local vmodepar_code = parcodes.vmode_par
+
+ local getnest = tex.getnest
+ local getsubtype = nuts.getsubtype
+
+ function nuts.setparproperty(action,...)
+ local tail = tonut(getnest().tail)
+ while tail do
+ if getid(tail) == par_code then
+ local s = getsubtype(tail)
+ if s == hmodepar_code or s == vmodepar_code then
+ return action(tail,...)
+ else
+ -- something is wrong here
+ end
+ end
+ tail = getprev(tail)
+ end
+ end
+
+ local getsubtype = nodes.getsubtype
+
+ function nodes.start_of_par(n)
+ local s = getsubtype(n)
+ return s == hmodepar_code or s == vmodepar_code
+ end
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-bck.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-bck.lmt
new file mode 100644
index 00000000000..9dff3ac40cb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-bck.lmt
@@ -0,0 +1,294 @@
+if not modules then modules = { } end modules ['node-bck'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to node-bck.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- beware, this one takes quite some runtime, so we need a status flag
+-- maybe some page related state
+
+-- todo: done (or just get rid of done altogether) ... saves no purpose
+-- any longer
+
+local attributes, nodes, node = attributes, nodes, node
+
+local enableaction = nodes.tasks.enableaction
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local alignmentlist_code = listcodes.alignment
+local celllist_code = listcodes.cell
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getlist = nuts.getlist
+local getattr = nuts.getattr
+local getsubtype = nuts.getsubtype
+local getwhd = nuts.getwhd
+local getwidth = nuts.getwidth
+local getprop = nuts.getprop
+
+local setattr = nuts.setattr
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+local setattributelist = nuts.setattributelist
+local setprop = nuts.setprop
+
+local takebox = nuts.takebox
+local findtail = nuts.tail
+
+local nextnode = nuts.traversers.node
+local nexthlist = nuts.traversers.hlist
+local nextlist = nuts.traversers.list
+
+local flush_node_list = nuts.flush_list
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+local new_hlist = nodepool.hlist
+
+local privateattributes = attributes.private
+local unsetvalue = attributes.unsetvalue
+
+local linefillers = nodes.linefillers
+
+local a_background = privateattributes("background")
+local a_alignbackground = privateattributes("alignbackground")
+local a_linefiller = privateattributes("linefiller")
+local a_ruled = privateattributes("ruled")
+
+local trace_alignment = false
+local report_alignment = logs.reporter("backgrounds","alignment")
+
+trackers.register("backgrounds.alignments",function(v) trace_alignment = v end)
+
+-- We can't use listbuilders with where=alignment because at that stage we have
+-- unset boxes. Also, post_linebreak is unsuitable for nested processing as we
+-- get the same stuff many times (wrapped again and again).
+--
+-- After many experiments with different callbacks the shipout is still the best
+-- place but then we need to store some settings longer or save them with the node.
+-- For color only we can get away with it with an extra attribute flagging a row
+-- but for more complex stuff we can better do as we do here now.
+
+local overshoot = math.floor(65781/5) -- could be an option per table (just also store it)
+
+local function colored_a(current,list,template,id)
+ local width, height, depth = getwhd(current)
+ local total = height + depth
+ if width > 0 and total > 0 then
+ local rule = nil
+ --
+ local a = getattr(template,a_linefiller)
+ if a then
+ local d = linefillers.data[a%1000]
+ if d then
+ rule = linefillers.filler(template,d,width,height,depth)
+ end
+ end
+ --
+ if not rule then
+ rule = new_rule(width,height,depth)
+ setattributelist(rule,template)
+ end
+ local back = new_kern(-((id == vlist_code and total) or width))
+ return setlink(rule,back,list)
+ end
+end
+
+local function colored_b(current,list,template,id,indent)
+ local width, height, depth = getwhd(current)
+ local total = height + depth
+ if width > 0 and total > 0 then
+ local fore = (indent ~= 0) and new_kern(indent)
+ local rule = nil
+ --
+ local a = getattr(template,a_linefiller)
+ if a then
+ local d = linefillers.data[a%1000]
+ if d then
+ rule = linefillers.filler(template,d,width-indent,height,depth)
+ end
+ end
+ --
+ if not rule then
+ rule = new_rule(width-indent,height+overshoot,depth+overshoot)
+ setattributelist(rule,template)
+ end
+ if overshoot == 0 then
+ local back = new_kern(-((id == vlist_code and total) or width))
+ return setlink(fore,rule,back,list)
+ else
+ rule = new_hlist(rule)
+ return setlink(fore,rule,list)
+ end
+ end
+end
+
+local templates = { }
+local currentrow = 0
+local enabled = false
+local alignments = false
+
+local function add_alignbackgrounds(head,list)
+ for current, id, subtype, list in nextlist, list do
+ if list and id == hlist_code and subtype == celllist_code then
+ for template in nexthlist, list do
+ local background = getattr(template,a_alignbackground)
+ if background then
+ local list = colored_a(current,list,template)
+ if list then
+ setlist(current,list)
+ end
+ setattr(template,a_alignbackground,unsetvalue) -- or property
+ end
+ break
+ end
+ end
+ end
+ local template = getprop(head,"alignmentchecked")
+ if template then
+ list = colored_b(head,list,template[1],hlist_code,template[2])
+ flush_node_list(template)
+ templates[currentrow] = false
+ return list
+ end
+end
+
+local function add_backgrounds(head,id,list)
+ if list then
+ for current, id, subtype, list in nextlist, list do
+ if list then
+ if alignments and subtype == alignmentlist_code then
+ local l = add_alignbackgrounds(current,list)
+ if l then
+ list = l
+ setlist(current,list)
+ end
+ end
+ local l = add_backgrounds(current,id,list)
+ if l then
+ list = l
+ setlist(current,l)
+ end
+ end
+ end
+ end
+ if id == hlist_code or id == vlist_code then
+ local background = getattr(head,a_background)
+ if background then
+ list = colored_a(head,list,head,id)
+ -- not needed
+ setattr(head,a_background,unsetvalue) -- or property -- todo
+ return list
+ end
+ end
+end
+
+function nodes.handlers.backgrounds(head)
+ add_backgrounds(head,getid(head),getlist(head))
+ return head
+end
+
+function nodes.handlers.backgroundspage(head,where)
+ if head and where == "alignment" then
+ for n in nexthlist, head do
+ local p = getprop(n,"alignmentchecked")
+ if not p and getsubtype(n) == alignmentlist_code then
+ currentrow = currentrow + 1
+ local template = templates[currentrow]
+ if trace_alignment then
+ report_alignment("%03i %s %s",currentrow,"page",template and "+" or "-")
+ end
+ setprop(n,"alignmentchecked",template)
+ end
+ end
+ end
+ return head
+end
+
+function nodes.handlers.backgroundsvbox(head,where)
+ if head and where == "vbox" then
+ local list = getlist(head)
+ if list then
+ for n in nexthlist, list do
+ local p = getprop(n,"alignmentchecked")
+ if not p and getsubtype(n) == alignmentlist_code then
+ currentrow = currentrow + 1
+ local template = templates[currentrow]
+ if trace_alignment then
+ report_alignment("%03i %s %s",currentrow,"vbox",template and "+" or "-")
+ end
+ setprop(n,"alignmentchecked",template)
+ end
+ end
+ end
+ end
+ return head
+end
+
+-- interfaces.implement {
+-- name = "enablebackgroundboxes",
+-- onlyonce = true,
+-- actions = enableaction,
+-- arguments = { "'shipouts'", "'nodes.handlers.backgrounds'" }
+-- }
+--
+-- doing it in the shipout works as well but this is nicer
+
+local function enable(alignmentstoo)
+ if not enabled then
+ enabled = true
+ enableaction("shipouts","nodes.handlers.backgrounds")
+ end
+ if not alignments and alignmentstoo then
+ alignments = true
+ enableaction("vboxbuilders","nodes.handlers.backgroundsvbox")
+ enableaction("mvlbuilders", "nodes.handlers.backgroundspage")
+ end
+end
+
+interfaces.implement {
+ name = "enablebackgroundboxes",
+ onlyonce = true,
+ actions = enable,
+}
+
+interfaces.implement {
+ name = "enablebackgroundalign",
+ onlyonce = true,
+ actions = function()
+ enable(true)
+ end,
+}
+
+interfaces.implement {
+ name = "setbackgroundrowdata",
+ arguments = { "integer", "integer", "dimension" },
+ actions = function(row,box,indent)
+ row = row -1 -- better here than in tex
+ if box == 0 then
+ templates[row] = false
+ else
+ templates[row] = { takebox(box), indent }
+ end
+ end,
+}
+
+interfaces.implement {
+ name = "resetbackgroundrowdata",
+ actions = function()
+ currentrow = 0
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-bck.mkxl b/Master/texmf-dist/tex/context/base/mkxl/node-bck.mkxl
new file mode 100644
index 00000000000..b456313df3b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-bck.mkxl
@@ -0,0 +1,80 @@
+%D \module
+%D [ file=node-bck,
+%D version=2009.06.08,
+%D title=\CONTEXT\ Node Macros,
+%D subtitle=Backgrounds,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Support / Backgrounds}
+
+%D This is first attempt to replacing backgrounds in a few tables
+%D mechanisms. When used more frequently, we can store the color
+%D spec in the attribute.
+
+\unprotect
+
+\registerctxluafile{node-bck}{autosuffix,optimize}
+
+% \backgroundvbox[green] {\input tufte } \par
+% \backgroundvbox[blue] {\input ward } \par
+% \backgroundvbox[red] {\input knuth } \par
+% \backgroundhbox[yellow]{\rotate[rotation=45]{hello world}} \par
+
+\permanent\def\colorattr#1%
+ {\ifcsname\??colorattribute\currentcolorprefix#1\endcsname
+ \node_backgrounds_thecolorattr{\currentcolorprefix#1}%
+ \orelse\ifcsname\??colorattribute#1\endcsname
+ \node_backgrounds_thecolorattr{#1}%
+ \fi}
+
+\def\node_backgrounds_thecolorattr#1%
+ {attr \colormodelattribute \c_attr_colormodel
+ attr \colorattribute \lastnamedcs
+ attr \transparencyattribute \rawtransparencyattribute{#1} } % can be optimized
+
+\permanent\def\thecolorattr#1%
+ {attr \colormodelattribute \c_attr_colormodel
+ attr \colorattribute \csname\??colorattribute#1\endcsname
+ attr \transparencyattribute \rawtransparencyattribute{#1} } % can be optimized
+
+\permanent\def\backgroundcolorattr#1%
+ {\ifcsname\??colorattribute\currentcolorprefix#1\endcsname
+ \node_backgrounds_thebackgroundcolorattr{\currentcolorprefix#1}%
+ \orelse\ifcsname\??colorattribute#1\endcsname
+ \node_backgrounds_thebackgroundcolorattr{#1}%
+ \fi}
+
+\permanent\def\thebackgroundcolorattr#1%
+ {attr \backgroundattribute \plusone
+ attr \colormodelattribute \c_attr_colormodel
+ attr \colorattribute \csname\??colorattribute#1\endcsname
+ attr \transparencyattribute \rawtransparencyattribute{#1} } % can be optimized
+
+\def\node_backgrounds_thebackgroundcolorattr#1%
+ {attr \backgroundattribute \plusone
+ attr \colormodelattribute \c_attr_colormodel
+ attr \colorattribute \lastnamedcs
+ attr \transparencyattribute \rawtransparencyattribute{#1} } % can be optimized
+
+\permanent\def\thealignbackgroundcolorattr#1%
+ {attr \alignbackgroundattribute \plusone
+ attr \colormodelattribute \c_attr_colormodel
+ attr \colorattribute \csname\??colorattribute#1\endcsname
+ attr \transparencyattribute \rawtransparencyattribute{#1} } % can be optimized
+
+\permanent\protected\def\backgroundhbox{\node_backgrounds_boxes_add\hbox}
+\permanent\protected\def\backgroundvbox{\node_backgrounds_boxes_add\vbox}
+\permanent\protected\def\backgroundvtop{\node_backgrounds_boxes_add\vtop}
+\permanent\protected\def\backgroundline{\dontleavehmode\node_backgrounds_boxes_add\hbox}
+
+\def\node_backgrounds_boxes_add#1[#2]%
+ {\clf_enablebackgroundboxes
+ #1\backgroundcolorattr{#2}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-cmp.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-cmp.lmt
new file mode 100644
index 00000000000..21fd700f093
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-cmp.lmt
@@ -0,0 +1,237 @@
+if not modules then modules = { } end modules ['node-cmp'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is a LuaTeX compatibility module. The code below is no longer
+-- present in node-met or node-aux. These functions are rarely used.
+
+if node.count then
+ return
+end
+
+local type = type
+
+local node = node
+local direct = node.direct
+local todirect = direct.tovaliddirect
+local tonode = direct.tonode
+
+local count = direct.count
+local length = direct.length
+local slide = direct.slide
+
+function node.count(id,first,last)
+ return count(id,first and todirect(first), last and todirect(last) or nil)
+end
+
+function node.length(first,last)
+ return length(first and todirect(first), last and todirect(last) or nil)
+end
+
+function node.slide(n)
+ if n then
+ n = slide(todirect(n))
+ if n then
+ return tonode(n)
+ end
+ end
+ return nil
+end
+
+local hyphenating = direct.hyphenating
+local ligaturing = direct.ligaturing
+local kerning = direct.kerning
+
+-- kind of inconsistent
+
+function node.hyphenating(first,last)
+ if first then
+ local h, t = hyphenating(todirect(first), last and todirect(last) or nil)
+ return h and tonode(h) or nil, t and tonode(t) or nil, true
+ else
+ return nil, false
+ end
+end
+
+function node.ligaturing(first,last)
+ if first then
+ local h, t = ligaturing(todirect(first), last and todirect(last) or nil)
+ return h and tonode(h) or nil, t and tonode(t) or nil, true
+ else
+ return nil, false
+ end
+end
+
+function node.kerning(first,last)
+ if first then
+ local h, t = kerning(todirect(first), last and todirect(last) or nil)
+ return h and tonode(h) or nil, t and tonode(t) or nil, true
+ else
+ return nil, false
+ end
+ end
+
+local protect_glyph = direct.protect_glyph
+local unprotect_glyph = direct.unprotect_glyph
+local protect_glyphs = direct.protect_glyphs
+local unprotect_glyphs = direct.unprotect_glyphs
+
+function node.protect_glyphs(first,last)
+ protect_glyphs(todirect(first), last and todirect(last) or nil)
+end
+
+function node.unprotect_glyphs(first,last)
+ unprotect_glyphs(todirect(first), last and todirect(last) or nil)
+end
+
+function node.protect_glyph(first)
+ protect_glyph(todirect(first))
+end
+
+function node.unprotect_glyph(first)
+ unprotect_glyph(todirect(first))
+end
+
+local flatten_discretionaries = direct.flatten_discretionaries
+local check_discretionaries = direct.check_discretionaries
+local check_discretionary = direct.check_discretionary
+
+function node.flatten_discretionaries(first)
+ local h, count = flatten_discretionaries(todirect(first))
+ return tonode(h), count
+end
+
+function node.check_discretionaries(n)
+ check_discretionaries(todirect(n))
+end
+
+function node.check_discretionary(n)
+ check_discretionary(todirect(n))
+end
+
+local hpack = direct.hpack
+local vpack = direct.vpack
+local list_to_hlist = direct.mlist_to_hlist
+
+function node.hpack(head,...)
+ local h, badness = hpack(head and todirect(head) or nil,...)
+ return tonode(h), badness
+end
+
+function node.vpack(head,...)
+ local h, badness = vpack(head and todirect(head) or nil,...)
+ return tonode(h), badness
+end
+
+function node.mlist_to_hlist(head,...)
+ return tonode(mlist_to_hlist(head and todirect(head) or nil,...))
+end
+
+local end_of_math = direct.end_of_math
+local find_attribute = direct.find_attribute
+local first_glyph = direct.first_glyph
+
+function node.end_of_math(n)
+ if n then
+ n = end_of_math(todirect(n))
+ if n then
+ return tonode(n)
+ end
+ end
+ return nil
+end
+
+function node.find_attribute(n,a)
+ if n then
+ local v, n = find_attribute(todirect(n),a)
+ if n then
+ return v, tonode(n)
+ end
+ end
+ return nil
+end
+
+function node.first_glyph(first,last)
+ local n = first_glyph(todirect(first), last and todirect(last) or nil)
+ return n and tonode(n) or nil
+end
+
+local dimensions = direct.dimensions
+local rangedimensions = direct.rangedimensions
+local effective_glue = direct.effective_glue
+
+function node.dimensions(a,b,c,d,e)
+ if type(a) == "userdata" then
+ a = todirect(a)
+ if type(b) == "userdata" then
+ b = todirect(b)
+ end
+ return dimensions(a,b)
+ else
+ d = todirect(d)
+ if type(e) == "userdata" then
+ e = todirect(e)
+ end
+ return dimensions(a,b,c,d,e)
+ end
+ return 0, 0, 0
+end
+
+function node.rangedimensions(parent,first,last)
+ return rangedimenensions(todirect(parent),todirect(first),last and todirect(last))
+end
+
+function node.effective_glue(list,parent)
+ return effective_glue(list and todirect(list) or nil,parent and todirect(parent) or nil)
+end
+
+local uses_font = direct.uses_font
+local has_glyph = direct.has_glyph
+local protrusion_skippable = direct.protrusion_skippable
+local make_extensible = direct.make_extensible
+
+function node.uses_font(n,f)
+ return uses_font(todirect(n),f)
+end
+
+function node.has_glyph(n)
+ return has_glyph(todirect(n))
+end
+
+function node.protrusion_skippable(n)
+ return protrusion_skippable(todirect(n))
+end
+
+function node.make_extensible(...)
+ local n = make_extensible(...)
+ return n and tonode(n) or nil
+end
+
+local last_node = direct.last_node
+
+function node.last_node()
+ local n = last_node()
+ return n and tonode(n) or nil
+end
+
+local is_zero_glue = direct.is_zero_glue
+local getglue = direct.getglue
+local setglue = direct.setglue
+
+function node.is_zero_glue(n)
+ return is_zero_glue(todirect(n))
+end
+
+function node.get_glue(n)
+ return get_glue(todirect(n))
+end
+
+function node.set_glue(n)
+ return set_glue(todirect(n))
+end
+
+node.family_font = tex.getfontoffamily
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-ext.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-ext.lmt
new file mode 100644
index 00000000000..2f909cd3d1a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-ext.lmt
@@ -0,0 +1,650 @@
+if not modules then modules = { } end modules ['back-out'] = {
+ version = 1.001,
+ comment = "companion to back-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, unpack, rawset = type, unpack, type
+local loadstring = loadstring
+local sind, cosd, abs = math.sind, math.cosd, math.abs
+local insert, remove = table.insert, table.remove
+
+local context = context
+local implement = interfaces.implement
+
+local allocate = utilities.storage.allocate
+
+local formatters = string.formatters
+
+local get = token.get_index -- getters
+local scanners = tokens.scanners
+local scaninteger = scanners.integer
+local scanstring = scanners.string
+local scankeyword = scanners.keyword
+local scantokenlist = scanners.tokenlist
+local scannumber = scanners.number
+
+local nuts = nodes.nuts
+local tonode = nuts.tonode
+local copynut = nuts.copy
+local nutpool = nuts.pool
+local nodepool = nodes.pool
+
+local nodeproperties = nodes.properties.data
+
+local register = nutpool.register
+local newnut = nuts.new
+local tonode = nodes.tonode
+
+-- Whatsits are a generic node type with very few fields. We use them to implement
+-- whatever we like. Some are good old tex whatsits but many are specific to the
+-- macro package. They are part of the backend.
+
+backends = backends or { }
+local backends = backends
+
+local whatsit_code = nodes.nodecodes.whatsit
+local whatsitcodes = allocate { }
+nodes.whatsitcodes = whatsitcodes
+local lastwhatsit = 0
+
+nodes.subtypes.whatsit = whatsitcodes
+
+local function registerwhatsit(name)
+ lastwhatsit = lastwhatsit + 1
+ whatsitcodes[lastwhatsit] = name
+ whatsitcodes[name] = lastwhatsit
+ return lastwhatsit
+end
+
+local function registerwhatsitnode(name)
+ return register(newnut(whatsit_code,registerwhatsit(name)))
+end
+
+-- We only have a subset of literals. In fact, we try to avoid literals.
+
+do
+
+ local literalvalues = allocate { }
+ nodes.literalvalues = literalvalues
+ local lastliteral = 0
+ local literalnode = registerwhatsitnode("literal")
+
+ local function registerliteral(name,alias)
+ lastliteral = lastliteral + 1
+ literalvalues[lastliteral] = name
+ literalvalues[name] = lastliteral
+ if alias then
+ literalvalues[alias] = lastliteral
+ end
+ return lastliteral
+ end
+
+ local originliteral_code = registerliteral("origin")
+ local pageliteral_code = registerliteral("page")
+ local directliteral_code = registerliteral("always","direct")
+ local rawliteral_code = registerliteral("raw")
+ local textliteral_code = registerliteral("text") -- not to be used
+ local fontliteral_code = registerliteral("font") -- not to be used
+
+ function nutpool.originliteral(str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = originliteral_code } return t end
+ function nutpool.pageliteral (str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = pageliteral_code } return t end
+ function nutpool.directliteral(str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = directliteral_code } return t end
+ function nutpool.rawliteral (str) local t = copynut(literalnode) nodeproperties[t] = { data = str, mode = rawliteral_code } return t end
+
+ local pdfliterals = {
+ [originliteral_code] = originliteral_code, [literalvalues[originliteral_code]] = originliteral_code,
+ [pageliteral_code] = pageliteral_code, [literalvalues[pageliteral_code]] = pageliteral_code,
+ [directliteral_code] = directliteral_code, [literalvalues[directliteral_code]] = directliteral_code,
+ [rawliteral_code] = rawliteral_code, [literalvalues[rawliteral_code]] = rawliteral_code,
+ }
+
+ function nutpool.literal(mode,str)
+ local t = copynut(literalnode)
+ if str then
+ nodeproperties[t] = { data = str, mode = pdfliterals[mode] or pageliteral_code }
+ else
+ nodeproperties[t] = { data = mode, mode = pageliteral_code }
+ end
+ return t
+ end
+
+end
+
+-- The latelua node is just another whatsit and we handle the \LUA\ code with
+-- other \LUA\ code, contrary to \LUATEX\ where it's a native node.
+
+do
+
+ local getdata = nuts.getdata
+ local serialize = token.serialize
+
+ local lateluanode = registerwhatsitnode("latelua")
+ local noflatelua = 0
+
+ function nutpool.latelua(code)
+ local n = copynut(lateluanode)
+ nodeproperties[n] = { data = code }
+ return n
+ end
+
+ function backends.latelua(current,pos_h,pos_v) -- todo: pass pos_h and pos_v (more efficient in lmtx)
+ local p = nodeproperties[current]
+ if p then
+ data = p.data
+ else
+ data = getdata(current)
+ end
+ noflatelua = noflatelua + 1
+ local kind = type(data)
+ if kind == "table" then
+ data.action(data.specification or data)
+ elseif kind == "function" then
+ data()
+ else
+ if kind ~= "string" then
+ data = serialize(data)
+ end
+ if #data ~= "" then
+ local code = loadstring(data)
+ if code then
+ code()
+ end
+ end
+ end
+ end
+
+ function backends.getcallbackstate()
+ return { count = noflatelua }
+ end
+
+ implement {
+ name = "latelua",
+ public = true,
+ protected = true,
+ actions = function()
+ local node = copynut(lateluanode)
+ local name = "latelua"
+ if scankeyword("name") then
+ name = scanstring()
+ end
+ local data = scantokenlist()
+ nodeproperties[node] = { name = name, data = data }
+ return context(tonode(node))
+ end,
+ }
+
+end
+
+-- This can be anything. Dealing with these nodes is done via properties, as with
+-- the other whatsits.
+
+do
+
+ local usernode = registerwhatsitnode("userdefined")
+
+ local userids = allocate()
+ local lastid = 0
+
+ setmetatable(userids, {
+ __index = function(t,k)
+ if type(k) == "string" then
+ lastid = lastid + 1
+ rawset(userids,lastid,k)
+ rawset(userids,k,lastid)
+ return lastid
+ else
+ rawset(userids,k,k)
+ return k
+ end
+ end,
+ __call = function(t,k)
+ return t[k]
+ end
+ } )
+
+ function nutpool.userdefined(id,data)
+ local n = copynut(usernode)
+ nodeproperties[n] = { id = id, data = data }
+ return n
+ end
+
+ nutpool .usernode = nutpool.userdefined
+ nutpool .userids = userids
+ nodepool.userids = userids
+
+end
+
+-- This one is only used by generic packages.
+
+do
+
+ local saveposnode = registerwhatsitnode("savepos")
+
+ function nutpool.savepos()
+ return copynut(saveposnode)
+ end
+
+end
+
+do
+
+ local savenode = registerwhatsitnode("save")
+ local restorenode = registerwhatsitnode("restore")
+ local setmatrixnode = registerwhatsitnode("setmatrix")
+
+ local stack = { }
+ local restore = true -- false
+
+ -- these are old school nodes
+
+ function nutpool.save()
+ return copynut(savenode)
+ end
+
+ function nutpool.restore()
+ return copynut(restorenode)
+ end
+
+ function nutpool.setmatrix(rx,sx,sy,ry,tx,ty)
+ local t = copynut(setmatrixnode)
+ nodeproperties[t] = { matrix = { rx, sx, sy, ry, tx, ty } }
+ return t
+ end
+
+ -- common
+
+ local function stopcommon(n)
+ local top = remove(stack)
+ if top == false then
+ return -- not wrapped
+ elseif top == true then
+ return copynut(n)
+ elseif top then
+ local t = copynut(n)
+ nodeproperties[t] = { matrix = { unpack(top) } }
+ return t
+ else
+ return -- nesting error
+ end
+ end
+
+ -- matrix
+
+ local startmatrixnode = registerwhatsitnode("startmatrix")
+ local stopmatrixnode = registerwhatsitnode("stopmatrix")
+
+ local function startmatrix(rx, sx, sy, ry)
+ if rx == 1 and sx == 0 and sy == 0 and ry == 1 then
+ insert(stack,false)
+ else
+ local t = copynut(startmatrixnode)
+ nodeproperties[t] = { matrix = { rx, sx, sy, ry } }
+ insert(stack,store and { -rx, -sx, -sy, -ry } or true)
+ return t
+ end
+ end
+
+ local function stopmatrix()
+ return stopcommon(stopmatrixnode)
+ end
+
+ implement {
+ name = "startmatrix",
+ actions = function()
+ local rx, sx, sy, ry = 1, 0, 0, 1
+ while true do
+ if scankeyword("rx") then rx = scannumber()
+ elseif scankeyword("ry") then ry = scannumber()
+ elseif scankeyword("sx") then sx = scannumber()
+ elseif scankeyword("sy") then sy = scannumber()
+ else break end
+ end
+ local t = startmatrix(rx,sx,sy,ry)
+ if t then
+ context(tonode(t))
+ end
+ end,
+ }
+
+ implement {
+ name = "stopmatrix",
+ actions = function()
+ local t = stopmatrix(n)
+ if t then
+ context(tonode(t))
+ end
+ end,
+ }
+
+ -- scale
+
+ local startscalingnode = registerwhatsitnode("startscaling")
+ local stopscalingnode = registerwhatsitnode("stopscaling")
+
+ local function startscaling(rx, ry) -- at the tex end we use sx and sy instead of rx and ry
+ if rx == 1 and ry == 1 then
+ insert(stack,false)
+ else
+ if rx == 0 then
+ rx = 0.0001
+ end
+ if ry == 0 then
+ ry = 0.0001
+ end
+ local t = copynut(startscalingnode)
+ nodeproperties[t] = { matrix = { rx, 0, 0, ry } }
+ insert(stack,restore and { 1/rx, 0, 0, 1/ry } or true)
+ return t
+ end
+ end
+
+ local function stopscaling() -- at the tex end we use sx and sy instead of rx and ry
+ return stopcommon(stopscalingnode)
+ end
+
+ implement {
+ name = "startscaling",
+ actions = function()
+ local rx, ry = 1, 1
+ while true do
+ if scankeyword("rx") then
+ rx = scannumber()
+ elseif scankeyword("ry") then
+ ry = scannumber()
+ else
+ break
+ end
+ end
+ local t = startscaling(rx,ry)
+ if t then
+ context(tonode(t))
+ end
+ end,
+ }
+
+ implement {
+ name = "stopscaling",
+ actions = function()
+ local t = stopscaling(n)
+ if t then
+ context(tonode(t))
+ end
+ end,
+ }
+
+ -- rotate
+
+ local startrotationnode = registerwhatsitnode("startrotation")
+ local stoprotationnode = registerwhatsitnode("stoprotation")
+
+ local function startrotation(a)
+ if a == 0 then
+ insert(stack,false)
+ else
+ local s, c = sind(a), cosd(a)
+ if abs(s) < 0.000001 then
+ s = 0 -- otherwise funny -0.00000
+ end
+ if abs(c) < 0.000001 then
+ c = 0 -- otherwise funny -0.00000
+ end
+ local t = copynut(startrotationnode)
+ nodeproperties[t] = { matrix = { c, s, -s, c } }
+ insert(stack,restore and { c, -s, s, c } or true)
+ return t
+ end
+ end
+
+ local function stoprotation()
+ return stopcommon(stoprotationnode)
+ end
+
+ nutpool.startrotation = startrotation
+
+ implement {
+ name = "startrotation",
+ actions = function()
+ local n = scannumber()
+ local t = startrotation(n)
+ if t then
+ context(tonode(t))
+ end
+ end,
+ }
+
+ implement {
+ name = "stoprotation",
+ actions = function()
+ local t = stoprotation()
+ if t then
+ context(tonode(t))
+ end
+ end,
+ }
+
+ -- mirror
+
+ local startmirroringnode = registerwhatsitnode("startmirroring")
+ local stopmirroringnode = registerwhatsitnode("stopmirroring")
+
+ local function startmirroring()
+ local t = copynut(startmirroringnode)
+ nodeproperties[t] = { matrix = { -1, 0, 0, 1 } }
+ return t
+ end
+
+ local function stopmirroring()
+ local t = copynut(stopmirroringnode)
+ nodeproperties[t] = { matrix = { -1, 0, 0, 1 } }
+ return t
+ end
+
+ implement {
+ name = "startmirroring",
+ actions = function()
+ context(tonode(startmirroring()))
+ end,
+ }
+
+ implement {
+ name = "stopmirroring",
+ actions = function()
+ context(tonode(stopmirroring()))
+ end,
+ }
+
+ -- clip
+
+ local startclippingnode = registerwhatsitnode("startclipping")
+ local stopclippingnode = registerwhatsitnode("stopclipping")
+
+ local function startclipping(path)
+ local t = copynut(startclippingnode)
+ nodeproperties[t] = { path = path }
+ return t
+ end
+
+ local function stopclipping()
+ return copynut(stopclippingnode)
+ end
+
+ implement {
+ name = "startclipping",
+ actions = function()
+ context(tonode(startclipping(scanstring())))
+ end
+ }
+
+ implement {
+ name = "stopclipping",
+ actions = function()
+ context(tonode(stopclipping()))
+ end,
+ }
+
+end
+
+-- The (delayed and immediate) write operations are emulated in \LUA\ and presented
+-- as primitives at the \TEX\ end.
+
+do
+
+ local logwriter = logs.writer
+ local openfile = io.open
+ local flushio = io.flush
+ local serialize = token.serialize
+
+ local opennode = registerwhatsitnode("open")
+ local writenode = registerwhatsitnode("write")
+ local closenode = registerwhatsitnode("close")
+ local channels = { }
+ local immediate_code = tex.flagcodes.immediate
+
+ function backends.openout(n)
+ local p = nodeproperties[n]
+ if p then
+ local handle = openfile(p.filename,"wb") or false
+ if handle then
+ channels[p.channel] = handle
+ else
+ -- error
+ end
+ end
+ end
+
+ function backends.writeout(n)
+ local p = nodeproperties[n]
+ if p then
+ local handle = channels[p.channel]
+ local content = serialize(p.data)
+ if handle then
+ handle:write(content,"\n")
+ else
+ logwriter(content,"\n")
+ end
+ end
+ end
+
+ function backends.closeout(n)
+ local p = nodeproperties[n]
+ if p then
+ local channel = p.channel
+ local handle = channels[channel]
+ if handle then
+ handle:close()
+ channels[channel] = false
+ flushio()
+ else
+ -- error
+ end
+ end
+ end
+
+ local function immediately(prefix)
+ return prefix and (prefix & immediate_code) ~= 0
+ end
+
+ implement {
+ name = "openout",
+ public = true,
+ usage = "value",
+ actions = function(prefix)
+ local channel = scaninteger()
+ scankeyword("=") -- hack
+ local filename = scanstring()
+ if not immediately(prefix) then
+ local n = copynut(opennode)
+ nodeproperties[n] = { channel = channel, filename = filename } -- action = "open"
+ return context(tonode(n))
+ elseif not channels[channel] then
+ local handle = openfile(filename,"wb") or false
+ if handle then
+ channels[channel] = handle
+ else
+ -- error
+ end
+ end
+ end,
+ }
+
+ implement {
+ name = "write",
+ public = true,
+ usage = "value",
+ actions = function(prefix)
+ local channel = scaninteger()
+ if not immediately(prefix) then
+ local t = scantokenlist()
+ local n = copynut(writenode)
+ nodeproperties[n] = { channel = channel, data = t } -- action = "write"
+ return context(tonode(n))
+ else
+ local content = scanstring()
+ local handle = channels[channel]
+ if handle then
+ handle:write(content,"\n")
+ else
+ logwriter(content,"\n")
+ end
+ end
+ end,
+ }
+
+ implement {
+ name = "closeout",
+ public = true,
+ usage = "value",
+ actions = function(prefix)
+ local channel = scaninteger()
+ if not immediately(prefix) then
+ local n = copynut(closenode)
+ nodeproperties[n] = { channel = channel } -- action = "close"
+ return context(tonode(n))
+ else
+ local handle = channels[channel]
+ if handle then
+ handle:close()
+ channels[channel] = false
+ flushio()
+ else
+ -- error
+ end
+ end
+ end,
+ }
+
+ -- why ...
+
+ local open_command = get(token.create("openout"))
+ local write_command = get(token.create("write"))
+ local close_command = get(token.create("closeout"))
+
+end
+
+-- states: these get injected independent of grouping
+
+do
+
+ local setstatenode = registerwhatsitnode("setstate")
+
+ function nutpool.setstate(data)
+ local n = copynut(setstatenode)
+ nodeproperties[n] = { data = data }
+ return n
+ end
+
+end
+
+-- We don't support specials so we make them dummies.
+
+do
+ implement {
+ name = "special",
+ actions = scanstring,
+ public = true,
+ protected = true,
+ }
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-fin.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-fin.lmt
new file mode 100644
index 00000000000..2ca7491a1b2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-fin.lmt
@@ -0,0 +1,939 @@
+if not modules then modules = { } end modules ['node-fin'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to node-fin.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- this module is being reconstructed
+-- local functions, only slightly slower
+--
+-- leaders are also triggers ... see colo-ext for an example (negate a box)
+
+local next, type, format = next, type, string.format
+local setmetatableindex = table.setmetatableindex
+
+local attributes, nodes, node = attributes, nodes, node
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getlist = nuts.getlist
+local getleader = nuts.getleader
+local getattr = nuts.getattr
+local getattrs = nuts.getattrs
+local getwidth = nuts.getwidth
+local getwhd = nuts.getwhd
+local getorientation = nuts.getorientation
+local has_dimensions = nuts.has_dimensions
+local getbox = nuts.getbox
+
+local setlist = nuts.setlist
+local setleader = nuts.setleader
+
+local copy_node = nuts.copy
+local find_tail = nuts.tail
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+
+local nextnode = nuts.traversers.node
+local nextcontent = nuts.traversers.content
+
+local nodecodes = nodes.nodecodes
+local rulecodes = nodes.rulecodes
+
+local boxrule_code = rulecodes.box
+local imagerule_code = rulecodes.image
+local emptyrule_code = rulecodes.empty
+
+local container_code = nodes.listcodes.container
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local rule_code = nodecodes.rule
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local texlists = tex.lists
+local texgetnest = tex.getnest
+
+local states = attributes.states
+local numbers = attributes.numbers
+local a_trigger = attributes.private('trigger')
+local triggering = false
+
+local implement = interfaces.implement
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local loadstripped = utilities.lua.loadstripped
+local unsetvalue = attributes.unsetvalue
+
+-- these two will be like trackers
+
+function states.enabletriggering () triggering = true end
+function states.disabletriggering() triggering = false end
+
+implement { name = "enablestatetriggering", actions = states.enabletriggering }
+implement { name = "disablestatetriggering", actions = states.disabletriggering }
+
+nodes.plugindata = nil
+
+-- inheritance: -0x7FFFFFFF -- we can best use nil and skip !
+
+local template = [[
+local plugin = nodes.plugindata
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local namespace = plugin.namespace
+local attribute = namespace.attribute or attributes.numbers[plugin.name]
+local processor = plugin.processor
+local initializer = plugin.initializer
+local resolver = plugin.resolver
+local finalizer = plugin.finalizer
+local flusher = plugin.flusher
+if not processor then
+ return function(head)
+ return head
+ end
+elseif initializer or finalizer or resolver then
+ return function(head)
+ starttiming(attributes)
+ local used, inheritance
+ if resolver then
+ inheritance = resolver()
+ end
+ if initializer then
+ initializer(namespace,attribute,head)
+ end
+ head = processor(namespace,attribute,head,inheritance)
+ if finalizer then
+ head, used = finalizer(namespace,attribute,head)
+ if used and flusher then
+ head = flusher(namespace,attribute,head,used)
+ end
+ end
+ stoptiming(attributes)
+ return head
+ end
+else
+ return function(head)
+ starttiming(attributes)
+ head = processor(namespace,attribute,head)
+ stoptiming(attributes)
+ return head
+ end
+end
+nodes.plugindata = nil
+]]
+
+function nodes.installattributehandler(plugin)
+ nodes.plugindata = plugin
+ return loadstripped(template)()
+end
+
+-- the injectors
+
+local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger
+local current, current_selector = 0, 0 -- nb, stack has a local current !
+local nsbegin, nsend, nsreset
+
+function states.initialize(namespace,attribute,head)
+ nsdata = namespace.data
+ nsnone = namespace.none
+ nsforced = namespace.forced
+ nsselector = namespace.selector
+ nslistwise = namespace.listwise
+ nstrigger = triggering and namespace.triggering and a_trigger
+ current = 0
+ current_selector = 0
+ nsstep = namespace.resolve_step
+ if nsstep then
+ nsreset = namespace.resolve_reset
+ nsbegin = namespace.resolve_begin
+ nsend = namespace.resolve_end
+ nspush = namespace.push
+ nspop = namespace.pop
+ end
+end
+
+function states.finalize(namespace,attribute,head) -- is this one ok?
+ if current > 0 and nsnone then
+ local id = getid(head)
+ if id == hlist_code or id == vlist_code then
+ local content = getlist(head)
+ if content then
+-- local list = insert_node_before(content,content,copy_node(nsnone)) -- two return values
+ local list = insert_node_after(content,find_tail(content),copy_node(nsnone)) -- two return values
+ if list ~= content then
+ setlist(head,list)
+ end
+ end
+ else
+-- head = insert_node_before(head,head,copy_node(nsnone))
+ head = insert_node_after(find_tail(head),head,copy_node(nsnone))
+ end
+ return head, true
+ end
+ return head, false
+end
+
+-- we need to deal with literals too (reset as well as oval)
+
+local function process(attribute,head,inheritance,default) -- one attribute
+ local check = false
+ local leader = nil
+ for stack, id, subtype, content in nextcontent, head do
+ if id == glyph_code or id == disc_code then
+ check = true
+ elseif id == glue_code then
+ check = true
+ leader = content
+ elseif id == hlist_code or id == vlist_code then
+ -- tricky checking
+ local outer
+ if subtype == container_code then
+ check = true
+current = 0
+ end
+ if getorientation(stack) then
+ outer = getattr(stack,attribute)
+ if outer then
+ if default and outer == inheritance then
+ if current ~= default then
+ head = insert_node_before(head,stack,copy_node(nsdata[default]))
+ current = default
+ end
+ elseif current ~= outer then
+ head = insert_node_before(head,stack,copy_node(nsdata[c]))
+ current = outer
+ end
+ elseif default and inheritance then
+ if current ~= default then
+ head = insert_node_before(head,stack,copy_node(nsdata[default]))
+ current = default
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current = 0
+ end
+ end
+ -- begin nested --
+ local list
+ if nstrigger and getattr(stack,nstrigger) then
+ if not outer then
+ outer = getattr(stack,attribute)
+ end
+ if outer ~= inheritance then
+ list = process(attribute,content,inheritance,outer)
+ else
+ list = process(attribute,content,inheritance,default)
+ end
+ else
+ list = process(attribute,content,inheritance,default)
+ end
+ if content ~= list then
+ setlist(stack,list)
+ end
+ -- end nested --
+ elseif id == rule_code then
+ check = has_dimensions(stack)
+ end
+ -- much faster this way than using a check() and nested() function
+ if check then
+ local c = getattr(stack,attribute)
+ if c then
+ if default and c == inheritance then
+ if current ~= default then
+ head = insert_node_before(head,stack,copy_node(nsdata[default]))
+ current = default
+ end
+ elseif current ~= c then
+ head = insert_node_before(head,stack,copy_node(nsdata[c]))
+ current = c
+ end
+ if leader then
+ local savedcurrent = current
+ local ci = getid(leader)
+ if ci == hlist_code or ci == vlist_code then
+ -- else we reset inside a box unneeded, okay, the downside is
+ -- that we trigger color in each repeated box, so there is room
+ -- for improvement here
+ current = 0
+ end
+ -- begin nested --
+ local list
+ if nstrigger and getattr(stack,nstrigger) then
+ local outer = getattr(stack,attribute)
+ if outer ~= inheritance then
+ list = process(attribute,leader,inheritance,outer)
+ else
+ list = process(attribute,leader,inheritance,default)
+ end
+ else
+ list = process(attribute,leader,inheritance,default)
+ end
+ if leader ~= list then
+ setleader(stack,list)
+ end
+ -- end nested --
+ current = savedcurrent
+ leader = false
+ end
+ elseif default and inheritance then
+ if current ~= default then
+ head = insert_node_before(head,stack,copy_node(nsdata[default]))
+ current = default
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current = 0
+ end
+ check = false
+ end
+ end
+ return head
+end
+
+states.process = function(namespace,attribute,head,default)
+ return process(attribute,head,default)
+end
+
+local function simple(attribute,head)
+ local check = false
+ local leader = nil
+ for stack, id, subtype, content in nextcontent, head do
+ if id == glyph_code or id == disc_code then
+ check = true
+ elseif id == glue_code then
+ check = true
+ leader = content
+ elseif id == hlist_code or id == vlist_code then
+ if subtype == container_code then
+ check = true
+current = 0
+ end
+ if getorientation(stack) then
+ local outer = getattr(stack,attribute)
+ if outer then
+ if current ~= outer then
+ if current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ end
+ head = insert_node_before(head,stack,copy_node(nsdata[c]))
+ current = outer
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current = 0
+ end
+ end
+ local list = simple(attribute,content)
+ if content ~= list then
+ setlist(stack,list)
+ end
+ elseif id == rule_code then
+ check = has_dimensions(stack)
+ end
+ if check then
+ local c = getattr(stack,attribute)
+ if c then
+ if current ~= c then
+ if current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ end
+ head = insert_node_before(head,stack,copy_node(nsdata[c]))
+ current = c
+ end
+ if leader then
+ local savedcurrent = current
+ local ci = getid(leader)
+ if ci == hlist_code or ci == vlist_code then
+ current = 0
+ end
+ local list = simple(attribute,leader)
+ if leader ~= list then
+ setleader(stack,list)
+ end
+ current = savedcurrent
+ leader = false
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current = 0
+ end
+ check = false
+ end
+ end
+ return head
+end
+
+states.simple = function(namespace,attribute,head,default)
+ return simple(attribute,head,default)
+end
+
+-- we can force a selector, e.g. document wide color spaces, saves a little
+-- watch out, we need to check both the selector state (like colorspace) and
+-- the main state (like color), otherwise we get into troubles when a selector
+-- state changes while the main state stays the same (like two glyphs following
+-- each other with the same color but different color spaces e.g. \showcolor)
+
+-- local function selective(attribute,head,inheritance,default) -- two attributes
+-- local check = false
+-- local leader = nil
+-- for stack, id, subtype, content in nextcontent, head do
+-- if id == glyph_code or id == disc_code then
+-- check = true
+-- elseif id == glue_code then
+-- check = true
+-- leader = content -- getleader(stack)
+-- elseif id == hlist_code or id == vlist_code then
+-- -- tricky checking
+-- if subtype == container_code then
+-- check = true
+-- current = 0
+-- end
+-- local outer
+-- if getorientation(stack) then
+-- outer = getattr(stack,attribute)
+-- if outer then
+-- if default and outer == inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector]))
+-- current = default
+-- end
+-- else
+-- local s = getattr(stack,nsselector)
+-- -- local s = nsforced or getattr(stack,nsselector)
+-- if current ~= outer or current_selector ~= s then
+-- local data = nsdata[outer]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+-- current = outer
+-- current_selector = s
+-- end
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector]))
+-- current = default
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current, current_selector = 0, 0
+-- end
+-- end
+-- -- begin nested
+-- local list
+-- if nstrigger and getattr(stack,nstrigger) then
+-- if not outer then
+-- outer = getattr(stack,attribute)
+-- end
+-- if outer ~= inheritance then
+-- list = selective(attribute,content,inheritance,outer)
+-- else
+-- list = selective(attribute,content,inheritance,default)
+-- end
+-- else
+-- list = selective(attribute,content,inheritance,default)
+-- end
+-- if content ~= list then
+-- setlist(stack,list)
+-- end
+-- -- end nested
+-- elseif id == rule_code then
+-- if subtype == boxrule_code or subtype == imagerule_code or subtype == emptyrule_code then
+-- -- so no redundant color stuff (only here, layers for instance should obey)
+-- check = false
+-- else
+-- check = has_dimensions(stack)
+-- end
+-- end
+-- if check then
+-- local c = getattr(stack,attribute)
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector]))
+-- current = default
+-- end
+-- else
+-- local s = getattr(stack,nsselector)
+-- -- local s = nsforced or getattr(stack,nsselector)
+-- if current ~= c or current_selector ~= s then
+-- local data = nsdata[c]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+-- current = c
+-- current_selector = s
+-- end
+-- end
+-- if leader then
+-- -- begin nested
+-- local list
+-- if nstrigger and getattr(stack,nstrigger) then
+-- local outer = getattr(stack,attribute)
+-- if outer ~= inheritance then
+-- list = selective(attribute,leader,inheritance,outer)
+-- else
+-- list = selective(attribute,leader,inheritance,default)
+-- end
+-- else
+-- list = selective(attribute,leader,inheritance,default)
+-- end
+-- if leader ~= list then
+-- setleader(stack,list)
+-- end
+-- -- end nested
+-- leader = false
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector]))
+-- current = default
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current, current_selector = 0, 0
+-- end
+-- check = false
+-- end
+-- end
+-- return head
+-- end
+
+local function selective(attribute,head,inheritance,default) -- two attributes
+ local check = false
+ local leader = nil
+ for stack, id, subtype, content in nextcontent, head do
+ if id == glyph_code or id == disc_code then
+ check = true
+ elseif id == glue_code then
+ check = true
+ leader = content -- getleader(stack)
+ elseif id == hlist_code or id == vlist_code then
+ -- tricky checking
+ if subtype == container_code then
+ check = true
+current = 0
+ end
+ local outer, s
+ if getorientation(stack) then
+ outer, s = getattrs(stack,attribute,nsselector)
+ if outer then
+ if default and outer == inheritance then
+ if current ~= default then
+ local data = nsdata[default]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or nsselector]))
+ current = default
+ end
+ elseif current ~= outer or current_selector ~= s then
+ local data = nsdata[outer]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+ current = outer
+ current_selector = s
+ end
+ elseif default and inheritance then
+ if current ~= default then
+ local data = nsdata[default]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+ current = default
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current, current_selector = 0, 0
+ end
+ end
+ -- begin nested
+ local list
+ if nstrigger and getattr(stack,nstrigger) then
+ if not outer then
+ outer = getattr(stack,attribute)
+ end
+ if outer ~= inheritance then
+ list = selective(attribute,content,inheritance,outer)
+ else
+ list = selective(attribute,content,inheritance,default)
+ end
+ else
+ list = selective(attribute,content,inheritance,default)
+ end
+ if content ~= list then
+ setlist(stack,list)
+ end
+ -- end nested
+ elseif id == rule_code then
+ if subtype == boxrule_code or subtype == imagerule_code or subtype == emptyrule_code then
+ -- so no redundant color stuff (only here, layers for instance should obey)
+ check = false
+ else
+ check = has_dimensions(stack)
+ end
+ end
+ if check then
+ local c, s = getattrs(stack,attribute,nsselector)
+ if c then
+ if default and c == inheritance then
+ if current ~= default then
+ local data = nsdata[default]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+ current = default
+ end
+ elseif current ~= c or current_selector ~= s then
+ local data = nsdata[c]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+ current = c
+ current_selector = s
+ end
+ if leader then
+ -- begin nested
+ local list
+ if nstrigger and getattr(stack,nstrigger) then
+ local outer = getattr(stack,attribute)
+ if outer ~= inheritance then
+ list = selective(attribute,leader,inheritance,outer)
+ else
+ list = selective(attribute,leader,inheritance,default)
+ end
+ else
+ list = selective(attribute,leader,inheritance,default)
+ end
+ if leader ~= list then
+ setleader(stack,list)
+ end
+ -- end nested
+ leader = false
+ end
+ elseif default and inheritance then
+ if current ~= default then
+ local data = nsdata[default]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector]))
+ current = default
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current, current_selector = 0, 0
+ end
+ check = false
+ end
+ end
+ return head
+end
+
+states.selective = function(namespace,attribute,head,default)
+ return selective(attribute,head,default)
+end
+
+-- Ideally the next one should be merged with the previous but keeping it separate is
+-- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers
+-- (as used in the stepper). In the stepper we cannot use the box branch as it involves
+-- paragraph lines and then gets mixed up. A messy business (esp since we want to be
+-- efficient).
+--
+-- Todo: make a better stacker. Keep track (in attribute) about nesting level. Not
+-- entirely trivial and a generic solution is nicer (compares to the exporter).
+
+local function stacked(attribute,head,default) -- no triggering, no inheritance, but list-wise
+ local stack = head
+ local current = default or 0
+ local depth = 0
+ local check = false
+ local leader = false
+ while stack do
+ local id = getid(stack)
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = getleader(stack)
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = getlist(stack)
+ if content then
+ -- the problem is that broken lines gets the attribute which can be a later one
+ local list
+ if subtype == container_code then
+ check = true
+current = 0
+ end
+ if nslistwise then
+ local a = getattr(stack,attribute)
+ if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
+ local p = current
+ current = a
+ head = insert_node_before(head,stack,copy_node(nsdata[a]))
+ list = stacked(attribute,content,current) -- two return values
+ head, stack = insert_node_after(head,stack,copy_node(nsnone))
+ current = p
+ else
+ list = stacked(attribute,content,current)
+ end
+ else
+ list = stacked(attribute,content,current)
+ end
+ if content ~= list then
+ setlist(stack,list) -- only if ok
+ end
+ end
+ elseif id == rule_code then
+ check = has_dimensions(stack)
+ end
+ if check then
+ local a = getattr(stack,attribute)
+ if a then
+ if current ~= a then
+ head = insert_node_before(head,stack,copy_node(nsdata[a]))
+ depth = depth + 1
+ current = a
+ end
+ if leader then
+ local content = getlist(leader)
+ if content then
+ local list = stacked(attribute,content,current)
+ if leader ~= list then
+ setleader(stack,list) -- only if ok
+ end
+ end
+ leader = false
+ end
+ elseif default > 0 then
+ --
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ depth = depth - 1
+ current = 0
+ end
+ check = false
+ end
+ stack = getnext(stack)
+ end
+ while depth > 0 do
+ head = insert_node_after(head,stack,copy_node(nsnone))
+ depth = depth - 1
+ end
+ return head
+end
+
+states.stacked = function(namespace,attribute,head,default)
+ return stacked(attribute,head,default)
+end
+
+-- experimental
+
+-- local function stacker(attribute,head,default) -- no triggering, no inheritance, but list-wise
+--
+-- -- nsbegin()
+-- local stacked = false
+--
+-- local current = head
+-- local previous = head
+-- local attrib = default or unsetvalue
+-- local check = false
+-- local leader = false
+--
+-- while current do
+-- local id = getid(current)
+-- if id == glyph_code then
+-- check = true
+-- elseif id == glue_code then
+-- leader = getleader(current)
+-- if leader then
+-- check = true
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = getlist(current)
+-- if content then
+-- local list
+-- if subtype == container_code then
+-- check = true
+-- current = 0
+-- end
+-- if nslistwise then
+-- local a = getattr(current,attribute)
+-- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
+-- head = insert_node_before(head,current,copy_node(nsdata[a]))
+-- list = stacker(attribute,content,a)
+-- if list ~= content then
+-- setlist(current,list)
+-- end
+-- head, current = insert_node_after(head,current,copy_node(nsnone))
+-- else
+-- list = stacker(attribute,content,attrib)
+-- if list ~= content then
+-- setlist(current,list)
+-- end
+-- end
+-- else
+-- list = stacker(attribute,content,default)
+-- if list ~= content then
+-- setlist(current,list)
+-- end
+-- end
+-- end
+-- elseif id == rule_code then
+-- check = has_dimensions(current)
+-- end
+--
+-- if check then
+-- local a = getattr(current,attribute) or unsetvalue
+-- if a ~= attrib then
+-- if not stacked then
+-- stacked = true
+-- nsbegin()
+-- end
+-- local n = nsstep(a)
+-- if n then
+-- head = insert_node_before(head,current,n) -- a
+-- end
+-- attrib = a
+-- if leader then
+-- -- tricky as a leader has to be a list so we cannot inject before
+-- -- local list = stacker(attribute,leader,attrib)
+-- -- leader = false
+--
+-- local content = getlist(leader)
+-- if content then
+-- local list = stacker(attribute,leader,attrib)
+-- if leader ~= list then
+-- setleader(current,list)
+-- end
+-- end
+--
+-- leader = false
+-- end
+-- end
+-- check = false
+-- end
+--
+-- previous = current
+-- current = getnext(current)
+-- end
+--
+-- if stacked then
+-- local n = nsend()
+-- while n do
+-- head = insert_node_after(head,previous,n)
+-- n = nsend()
+-- end
+-- end
+--
+-- return head
+-- end
+
+local function stacker(attribute,head,default) -- no triggering, no inheritance, but list-wise
+
+ -- nsbegin()
+ local stacked = false
+
+ local current = head
+ local previous = head
+ local attrib = default or unsetvalue
+ local check = false
+ local leader = false
+
+ for current, id, subtype, content in nextcontent, head do
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = content
+ check = true
+ elseif id == hlist_code or id == vlist_code then
+ local list
+ if subtype == container_code then
+ check = true
+-- attrib = default or unsetvalue -- or just umsetvalue
+ end
+ if nslistwise then
+ local a = getattr(current,attribute)
+ if a and attrib ~= a and nslistwise[a] then -- viewerlayer
+ head = insert_node_before(head,current,copy_node(nsdata[a]))
+ list = stacker(attribute,content,a)
+ if list ~= content then
+ setlist(current,list)
+ end
+ head, current = insert_node_after(head,current,copy_node(nsnone))
+ else
+ list = stacker(attribute,content,attrib)
+ if list ~= content then
+ setlist(current,list)
+ end
+ end
+ else
+ list = stacker(attribute,content,default)
+ if list ~= content then
+ setlist(current,list)
+ end
+ end
+ elseif id == rule_code then
+ if subtype == boxrule_code or subtype == imagerule_code or subtype == emptyrule_code then
+ -- so no redundant color stuff (only here, layers for instance should obey)
+ check = false
+ else
+ check = has_dimensions(current)
+ end
+ end
+
+ if check then
+ local a = getattr(current,attribute) or unsetvalue
+ if a ~= attrib then
+ if not stacked then
+ stacked = true
+ nsbegin()
+ end
+ local n = nsstep(a)
+ if n then
+ head = insert_node_before(head,current,n) -- a
+ end
+ attrib = a
+ if leader then
+ -- tricky as a leader has to be a list so we cannot inject before
+ -- local list = stacker(attribute,leader,attrib)
+ -- leader = false
+
+ local content = getlist(leader)
+ if content then
+ local list = stacker(attribute,leader,attrib)
+ if leader ~= list then
+ setleader(current,list)
+ end
+ end
+
+ leader = false
+ end
+ end
+ check = false
+ end
+
+ previous = current
+ end
+
+ if stacked then
+ local n = nsend()
+ while n do
+ head = insert_node_after(head,previous,n)
+ n = nsend()
+ end
+ end
+
+ return head
+end
+
+states.stacker = function(namespace,attribute,head,default)
+ local head = stacker(attribute,head,default)
+ nsreset()
+ return head
+end
+
+-- -- --
+
+statistics.register("attribute processing time", function()
+ return statistics.elapsedseconds(attributes,"front- and backend")
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-fin.mkxl b/Master/texmf-dist/tex/context/base/mkxl/node-fin.mkxl
new file mode 100644
index 00000000000..6840c85ab53
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-fin.mkxl
@@ -0,0 +1,78 @@
+%D \module
+%D [ file=attr-ini,
+%D version=2007.06.06, % probably a bit older
+%D title=\CONTEXT\ Node Macros,
+%D subtitle=Finalizing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Support / Finalizing}
+
+\unprotect
+
+\registerctxluafile{node-shp}{autosuffix,optimize}
+\registerctxluafile{node-fin}{autosuffix,optimize}
+
+\permanent\protected\def\finalizeobjectbox #1{\clf_finalizebox#1\relax}
+\permanent\protected\def\finalizeshipoutbox#1{\clf_finalizebox#1\relax}
+
+% Experimental (for Aditya), not in mkxl, if we need it, it has to be more
+% clever.
+
+% \permanent\protected\def\cleanupbox#1{\clf_cleanupbox#1\relax}
+
+% Tricky stuff: this might become obsolete. It is about time to check this out
+% now (after more than a decade)! The few times that we need this we can better
+% copy some of the sensitive attributes (mostly color).
+
+\definesystemattribute[trigger][public]
+
+\newcount\c_syst_attr_trigger
+
+\permanent\edef\startinheritattributes{\c_attr_trigger\plusone}
+\permanent\edef\stopinheritattributes {\c_attr_trigger\attributeunsetvalue}
+
+\permanent\def\syst_attr_trigger_copy_yes{\afterassignment\syst_attr_trigger_copy_indeed\c_syst_attr_trigger}
+\permanent\def\syst_attr_trigger_dump_yes{\afterassignment\syst_attr_trigger_dump_indeed\c_syst_attr_trigger}
+
+\def\syst_attr_trigger_copy_indeed
+ {\ifvbox\c_syst_attr_trigger
+ \vpack attr \triggerattribute \plusone {\unvcopy\c_syst_attr_trigger}%
+ \else
+ \hpack attr \triggerattribute \plusone {\unhcopy\c_syst_attr_trigger}%
+ \fi}
+
+\def\syst_attr_trigger_dump_indeed
+ {\ifvbox\c_syst_attr_trigger
+ \vpack attr \triggerattribute \plusone {\unvbox\c_syst_attr_trigger}%
+ \else
+ \hpack attr \triggerattribute \plusone {\unhbox\c_syst_attr_trigger}%
+ \fi}
+
+\permanent\protected\def\enableattributeinheritance
+ {\clf_enablestatetriggering
+ \enforced\let\attributedcopy\syst_attr_trigger_copy_yes
+ \enforced\let\attributedbox \syst_attr_trigger_dump_yes}
+
+\permanent\protected\def\disableattributeinheritance
+ {\clf_disablestatetriggering
+ \enforced\let\attributedcopy\copy
+ \enforced\let\attributedbox \box}
+
+\disableattributeinheritance
+
+\installtexdirective
+ {attributes.inheritance}
+ {\enableattributeinheritance}
+ {\disableattributeinheritance}
+
+% \appendtoks
+% \enableattributeinheritance % might become default
+% \to\everyjob
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-fnt.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-fnt.lmt
new file mode 100644
index 00000000000..c96bdde3d1e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-fnt.lmt
@@ -0,0 +1,550 @@
+if not modules then modules = { } end modules ['node-fnt'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+if not context then os.exit() end -- generic function in node-dum
+
+local next, type = next, type
+local concat, keys = table.concat, table.keys
+
+local nodes, node, fonts = nodes, node, fonts
+
+local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end)
+local trace_fontrun = false trackers.register("nodes.fontrun", function(v) trace_fontrun = v end)
+local trace_variants = false trackers.register("nodes.variants", function(v) trace_variants = v end)
+
+-- bad namespace for directives
+
+local force_discrun = true directives.register("nodes.discrun", function(v) force_discrun = v end)
+local force_boundaryrun = true directives.register("nodes.boundaryrun", function(v) force_boundaryrun = v end)
+local force_basepass = true directives.register("nodes.basepass", function(v) force_basepass = v end)
+local keep_redundant = false directives.register("nodes.keepredundant",function(v) keep_redundant = v end)
+
+local report_fonts = logs.reporter("fonts","processing")
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontvariants = fonthashes.variants
+local fontmodes = fonthashes.modes
+
+local otf = fonts.handlers.otf
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local nodecodes = nodes.nodecodes
+local boundarycodes = nodes.boundarycodes
+
+local handlers = nodes.handlers
+
+local nuts = nodes.nuts
+
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getreplace = nuts.getreplace
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getdata = nuts.getdata
+local getglyphdata = nuts.getglyphdata
+
+local setchar = nuts.setchar
+local setlink = nuts.setlink
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+
+local isglyph = nuts.isglyph -- unchecked
+local ischar = nuts.ischar -- checked
+
+local nextboundary = nuts.traversers.boundary
+local nextdisc = nuts.traversers.disc
+local nextchar = nuts.traversers.char
+
+local flush_node = nuts.flush
+
+local disc_code = nodecodes.disc
+local boundary_code = nodecodes.boundary
+
+local wordboundary_code = boundarycodes.word
+
+local protect_glyphs = nuts.protect_glyphs
+local unprotect_glyphs = nuts.unprotect_glyphs
+
+local setmetatableindex = table.setmetatableindex
+
+-- some tests with using an array of dynamics[id] and processes[id] demonstrated
+-- that there was nothing to gain (unless we also optimize other parts)
+--
+-- maybe getting rid of the intermediate shared can save some time
+
+local run = 0
+
+local setfontdynamics = { }
+local fontprocesses = { }
+
+setmetatableindex(setfontdynamics, function(t,font)
+ local tfmdata = fontdata[font]
+ local shared = tfmdata.shared
+ local f = shared and shared.dynamics and otf.setdynamics or false
+ if f then
+ local v = { }
+ t[font] = v
+ setmetatableindex(v,function(t,k)
+ local v = f(font,k)
+ t[k] = v
+ return v
+ end)
+ return v
+ else
+ t[font] = false
+ return false
+ end
+end)
+
+setmetatableindex(fontprocesses, function(t,font)
+ local tfmdata = fontdata[font]
+ local shared = tfmdata.shared -- we need to check shared, only when same features
+ local processes = shared and shared.processes
+ if processes and #processes > 0 then
+ t[font] = processes
+ return processes
+ else
+ t[font] = false
+ return false
+ end
+end)
+
+fonts.hashes.setdynamics = setfontdynamics
+fonts.hashes.processes = fontprocesses
+
+-- if we forget about basemode we don't need to test too much here and we can consider running
+-- over sub-ranges .. this involves a bit more initializations but who cares .. in that case we
+-- also need to use the stop criterium (we already use head too) ... we cannot use traverse
+-- then, so i'll test it on some local clone first ... the only pitfall is changed directions
+-- inside a run which means that we need to keep track of this which in turn complicates matters
+-- in a way i don't like
+
+-- we need to deal with the basemode fonts here and can only run over ranges as we otherwise get
+-- luatex craches due to all kind of asserts in the disc/lig builder
+
+-- there is no gain in merging used (dynamic 0) and dynamics apart from a bit less code
+
+local ligaturing = nuts.ligaturing
+local kerning = nuts.kerning
+
+local function start_trace(head)
+ run = run + 1
+ report_fonts()
+ report_fonts("checking node list, run %s",run)
+ report_fonts()
+ local n = head
+ while n do
+ local char, id = isglyph(n)
+ if char then
+ local font = id
+ local dynamic = getglyphdata(n) or 0
+ report_fonts("font %03i, dynamic %03i, glyph %C",font,dynamic,char)
+ elseif id == disc_code then
+ report_fonts("[disc] %s",nodes.listtoutf(n,true,false,n))
+ elseif id == boundary_code then
+ report_fonts("[boundary] %i:%i",getsubtype(n),getdata(n))
+ else
+ report_fonts("[%s]",nodecodes[id])
+ end
+ n = getnext(n)
+ end
+end
+
+local function stop_trace(u,usedfonts,d,dynamicfonts,b,basefonts,r,redundant)
+ report_fonts()
+ report_fonts("statics : %s",u > 0 and concat(keys(usedfonts)," ") or "none")
+ report_fonts("dynamics: %s",d > 0 and concat(keys(dynamicfonts)," ") or "none")
+ report_fonts("built-in: %s",b > 0 and b or "none")
+ report_fonts("removed : %s",r > 0 and r or "none")
+ report_fonts()
+end
+
+do
+
+ local usedfonts
+ local dynamicfonts
+ local basefonts -- could be reused
+ local basefont
+ local prevfont
+ local prevdynamic
+ local variants
+ local redundant -- could be reused
+ local firstnone
+ local lastfont
+ local lastproc
+ local lastnone
+
+ local d, u, b, r
+
+ local function protectnone()
+ protect_glyphs(firstnone,lastnone)
+ firstnone = nil
+ end
+
+ local function setnone(n)
+ if firstnone then
+ protectnone()
+ end
+ if basefont then
+ basefont[2] = getprev(n)
+ basefont = false
+ end
+ if not firstnone then
+ firstnone = n
+ end
+ lastnone = n
+ end
+
+ local function setbase(n)
+ if firstnone then
+ protectnone()
+ end
+ if force_basepass then
+ if basefont then
+ basefont[2] = getprev(n)
+ end
+ b = b + 1
+ basefont = { n, false }
+ basefonts[b] = basefont
+ end
+ end
+
+ local function setnode(n,font,dynamic) -- we could use prevfont and prevdynamic when we set then first
+ if firstnone then
+ protectnone()
+ end
+ if basefont then
+ basefont[2] = getprev(n)
+ basefont = false
+ end
+ if dynamic > 0 then
+ local used = dynamicfonts[font]
+ if not used then
+ used = { }
+ dynamicfonts[font] = used
+ end
+ if not used[dynamic] then
+ local fd = setfontdynamics[font]
+ if fd then
+ used[dynamic] = fd[dynamic]
+ d = d + 1
+ end
+ end
+ else
+ local used = usedfonts[font]
+ if not used then
+ lastfont = font
+ lastproc = fontprocesses[font]
+ if lastproc then
+ usedfonts[font] = lastproc
+ u = u + 1
+ end
+ end
+ end
+ end
+
+ function handlers.characters(head,groupcode,size,packtype,direction)
+ -- either next or not, but definitely no already processed list
+ starttiming(nodes)
+
+ usedfonts = { }
+ dynamicfonts = { }
+ basefonts = { }
+ basefont = nil
+ prevfont = nil
+ prevdynamic = 0
+ variants = nil
+ redundant = nil
+ firstnone = nil
+ lastfont = nil
+ lastproc = nil
+ lastnone = nil
+
+ local fontmode = nil -- base none or other
+
+ d, u, b, r = 0, 0, 0, 0
+
+ if trace_fontrun then
+ start_trace(head)
+ end
+
+ -- There is no gain in checking for a single glyph and then having a fast path. On the
+ -- metafun manual (with some 2500 single char lists) the difference is just noise.
+
+ for n, char, font, dynamic in nextchar, head do
+
+ if font ~= prevfont then
+ prevfont = font
+ fontmode = fontmodes[font]
+ if fontmode == "none" then
+ prevdynamic = 0
+ variants = false
+ setnone(n)
+ elseif fontmode == "base" then
+ prevdynamic = 0
+ variants = false
+ setbase(n)
+ else
+ -- local dynamic = getglyphdata(n) or 0 -- zero dynamic is reserved for fonts in context
+ prevdynamic = dynamic
+ variants = fontvariants[font]
+ setnode(n,font,dynamic)
+ end
+ elseif fontmode == "node" then
+ local dynamic = getglyphdata(n) or 0 -- zero dynamic is reserved for fonts in context
+ if dynamic ~= prevdynamic then
+ prevdynamic = dynamic
+ variants = fontvariants[font]
+ setnode(n,font,dynamic)
+ end
+ elseif firstnone then
+ lastnone = n
+ end
+
+ if variants then
+ if (char >= 0xFE00 and char <= 0xFE0F) or (char >= 0xE0100 and char <= 0xE01EF) then
+ -- if variants and char >= 0xFE00 then
+ -- if char < 0xFE0F or (char >= 0xE0100 and char <= 0xE01EF) then
+ local hash = variants[char]
+ if hash then
+ local p = getprev(n)
+ if p then
+ local char = ischar(p) -- checked
+ local variant = hash[char]
+ if variant then
+ if trace_variants then
+ report_fonts("replacing %C by %C",char,variant)
+ end
+ setchar(p,variant)
+ if redundant then
+ r = r + 1
+ redundant[r] = n
+ else
+ r = 1
+ redundant = { n }
+ end
+ end
+ end
+ elseif keep_redundant then
+ -- go on, can be used for tracing
+ elseif redundant then
+ r = r + 1
+ redundant[r] = n
+ else
+ r = 1
+ redundant = { n }
+ end
+ end
+ end
+
+ end
+
+ if firstnone then
+ protectnone()
+ end
+
+ if force_boundaryrun then
+
+ -- we can inject wordboundaries and then let the hyphenator do its work
+ -- but we need to get rid of those nodes in order to build ligatures
+ -- and kern (a rather context thing)
+
+ for b, subtype in nextboundary, head do
+ if subtype == wordboundary_code then
+ if redundant then
+ r = r + 1
+ redundant[r] = b
+ else
+ r = 1
+ redundant = { b }
+ end
+ end
+ end
+
+ end
+
+ if redundant then
+ for i=1,r do
+ local r = redundant[i]
+ local p, n = getboth(r)
+ if r == head then
+ head = n
+ setprev(n)
+ else
+ setlink(p,n)
+ end
+ if b > 0 then
+ for i=1,b do
+ local bi = basefonts[i]
+ local b1 = bi[1]
+ local b2 = bi[2]
+ if b1 == b2 then
+ if b1 == r then
+ bi[1] = false
+ bi[2] = false
+ end
+ elseif b1 == r then
+ bi[1] = n
+ elseif b2 == r then
+ bi[2] = p
+ end
+ end
+ end
+ flush_node(r)
+ end
+ end
+
+ if force_discrun then
+ -- basefont is not supported in disc only runs ... it would mean a lot of
+ -- ranges .. we could try to run basemode as a separate processor run but not
+ -- for now (we can consider it when the new node code is tested
+ for disc in nextdisc, head do
+ -- doing only replace is good enough because pre and post are normally used
+ -- for hyphens and these come from fonts that part of the hyphenated word
+ local r = getreplace(disc)
+ if r then
+ local prevfont = nil
+ local prevdynamic = nil
+ local none = false
+ firstnone = nil
+ basefont = nil
+ for n, char, font, dynamic in nextchar, r do
+ -- local dynamic = getglyphdata(n) or 0 -- zero dynamic is reserved for fonts in context
+ if font ~= prevfont or dynamic ~= prevdynamic then
+ prevfont = font
+ prevdynamic = dynamic
+ local fontmode = fontmodes[font]
+ if fontmode == "none" then
+ setnone(n)
+ elseif fontmode == "base" then
+ -- so the replace gets an extra treatment ... so be it
+ setbase(n)
+ else
+ setnode(n,font,dynamic)
+ end
+ elseif firstnone then
+ -- lastnone = n
+ lastnone = nil
+ end
+ -- we assume one font for now (and if there are more and we get into issues then
+ -- we can always remove the break)
+ break
+ end
+ if firstnone then
+ protectnone()
+ end
+ end
+ end
+
+ end
+
+ if trace_fontrun then
+ stop_trace(u,usedfonts,d,dynamicfonts,b,basefonts,r,redundant)
+ end
+
+ -- in context we always have at least 2 processors
+ if u == 0 then
+ -- skip
+ elseif u == 1 then
+ for i=1,#lastproc do
+ head = lastproc[i](head,lastfont,0,direction)
+ end
+ else
+ for font, processors in next, usedfonts do -- unordered
+ for i=1,#processors do
+ head = processors[i](head,font,0,direction,u) -- u triggers disc optimizer
+ end
+ end
+ end
+ if d == 0 then
+ -- skip
+ elseif d == 1 then
+ local font, dynamics = next(dynamicfonts)
+ for dynamic, processors in next, dynamics do -- unordered, dynamic can switch in between
+ for i=1,#processors do
+ head = processors[i](head,font,dynamic,direction)
+ end
+ end
+ else
+ for font, dynamics in next, dynamicfonts do
+ for dynamic, processors in next, dynamics do -- unordered, dynamic can switch in between
+ for i=1,#processors do
+ head = processors[i](head,font,dynamic,direction,d) -- d triggers disc optimizer
+ end
+ end
+ end
+ end
+ if b == 0 then
+ -- skip
+ elseif b == 1 then
+ -- only one font
+ local range = basefonts[1]
+ local start = range[1]
+ local stop = range[2]
+ if (start or stop) and (start ~= stop) then
+ local front = head == start
+ if stop then
+ start = ligaturing(start,stop)
+ start = kerning(start,stop)
+ elseif start then -- safeguard
+ start = ligaturing(start)
+ start = kerning(start)
+ end
+ if front and head ~= start then
+ head = start
+ end
+ end
+ else
+ -- multiple fonts
+ for i=1,b do
+ local range = basefonts[i]
+ local start = range[1]
+ local stop = range[2]
+ if start then -- and start ~= stop but that seldom happens
+ local front = head == start
+ local prev = getprev(start)
+ local next = getnext(stop)
+ if stop then
+ start, stop = ligaturing(start,stop)
+ start, stop = kerning(start,stop)
+ else
+ start = ligaturing(start)
+ start = kerning(start)
+ end
+ -- is done automatically
+ if prev then
+ setlink(prev,start)
+ end
+ if next then
+ setlink(stop,next)
+ end
+ -- till here
+ if front and head ~= start then
+ head = start
+ end
+ end
+ end
+ end
+
+ stoptiming(nodes)
+
+ if trace_characters then
+ nodes.report(head)
+ end
+
+ return head
+ end
+
+end
+
+handlers.protectglyphs = protect_glyphs
+handlers.unprotectglyphs = unprotect_glyphs
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-gcm.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-gcm.lmt
new file mode 100644
index 00000000000..7f2a7bded8a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-gcm.lmt
@@ -0,0 +1,114 @@
+if not modules then modules = { } end modules ['node-gmc'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, tostring = type, tostring
+
+local nodes = nodes
+local ligature_code = nodes.glyphcodes.ligature
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getsubtype = nuts.getsubtype
+local copy_node = nuts.copy
+local flush_list = nuts.flush_list
+local count = nuts.count
+local isglyph = nuts.isglyph
+local getprop = nuts.getprop
+local setprop = nuts.setprop
+
+local fastcopy = table.fastcopy
+
+local report_error = logs.reporter("node-aux:error")
+
+local function set_components(base,list)
+ local t = { }
+ local n = 0
+ local l = list
+ while list do
+ local char = isglyph(list)
+ if char then
+ n = n + 1
+ t[n] = char
+ end
+ list = getnext(list)
+ end
+ -- not yet as we need them
+ --
+ -- flush_list(l)
+ --
+ setprop(base,"components",n > 0 and t or false)
+end
+
+local function get_components(base)
+ return getprop(base,"components")
+end
+
+local function copy_no_components(base)
+ local copy = copy_node(base)
+ setprop(copy,"components",false) -- no metatable lookup!
+ return copy
+end
+
+local function copy_only_glyphs(base)
+ local t = getprop(base,"components") -- also metatable
+ if t then
+ return fastcopy(t)
+ end
+end
+
+local function do_count(t,marks)
+ local n = 0
+ if t then
+ for i=1,#t do
+ local c = t[i]
+ if type(c) == "table" then
+ n = n + do_count(t,marks)
+ elseif not marks[c] then
+ n = n + 1
+ else
+ --marks don't count
+ end
+ end
+ end
+ return n
+end
+
+-- start is a mark and we need to keep that one
+
+local done = false
+
+local function count_components(base,marks)
+ local char = isglyph(base)
+ if char then
+ if getsubtype(base) == ligature_code then
+ if not done then
+ logs.report("fonts","!")
+ logs.report("fonts","! check count_components with mkiv !")
+ logs.report("fonts","!")
+ done = true
+ end
+ local t = getprop(base,"components")
+ if t then
+ return do_count(t,marks)
+ end
+ elseif not marks[char] then
+ return 1
+ end
+ end
+ return 0
+end
+
+nuts.set_components = set_components
+nuts.get_components = get_components
+nuts.copy_only_glyphs = copy_only_glyphs
+nuts.copy_no_components = copy_no_components
+nuts.count_components = count_components
+nuts.flush_components = flush_list
+
+nuts.setcomponents = function() report_error("unsupported: %a","setcomponents") end
+nuts.getcomponents = function() report_error("unsupported: %a","getcomponents") end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-ini.lmt
new file mode 100644
index 00000000000..4ba4b4000a1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-ini.lmt
@@ -0,0 +1,200 @@
+if not modules then modules = { } end modules ['node-ini'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>Most of the code that had accumulated here is now separated in modules.</p>
+--ldx]]--
+
+local next, type, tostring = next, type, tostring
+local gsub = string.gsub
+local concat, remove = table.concat, table.remove
+local sortedhash, sortedkeys, swapped = table.sortedhash, table.sortedkeys, table.swapped
+
+--[[ldx--
+<p>Access to nodes is what gives <l n='luatex'/> its power. Here we implement a
+few helper functions. These functions are rather optimized.</p>
+--ldx]]--
+
+nodes = nodes or { }
+local nodes = nodes
+nodes.handlers = nodes.handlers or { }
+
+local mark = utilities.storage.mark
+local allocate = utilities.storage.allocate
+local formatcolumns = utilities.formatters.formatcolumns
+
+local getsubtypes = node.subtypes
+local getvalues = node.values
+
+tex.magicconstants = { -- we use tex.constants for something else
+ running = -1073741824, -- null_flag
+ maxdimen = 1073741823, -- max_dimen
+ trueinch = 4736286,
+}
+
+local listcodes = mark(getsubtypes("list"))
+local rulecodes = mark(getsubtypes("rule"))
+local dircodes = mark(getsubtypes("dir"))
+local glyphcodes = mark(getsubtypes("glyph"))
+local disccodes = mark(getsubtypes("disc"))
+local gluecodes = mark(getsubtypes("glue"))
+local boundarycodes = mark(getsubtypes("boundary"))
+local penaltycodes = mark(getsubtypes("penalty"))
+local kerncodes = mark(getsubtypes("kern"))
+local mathcodes = mark(getsubtypes("math"))
+local noadcodes = mark(getsubtypes("noad"))
+local radicalcodes = mark(getsubtypes("radical"))
+local accentcodes = mark(getsubtypes("accent"))
+local fencecodes = mark(getsubtypes("fence"))
+----- fractioncodes = mark(getsubtypes("fraction"))
+local parcodes = mark(getsubtypes("par"))
+
+local fillvalues = mark(getvalues("fill"))
+local dirvalues = mark(getvalues("dir"))
+
+local function simplified(t)
+ local r = { }
+ for k, v in next, t do
+ r[k] = gsub(v,"_","")
+ end
+ return r
+end
+
+local noadoptions = allocate {
+ set = 0x08,
+ unused_1 = 0x00 + 0x08,
+ unused_2 = 0x01 + 0x08,
+ axis = 0x02 + 0x08,
+ no_axis = 0x04 + 0x08,
+ exact = 0x10 + 0x08,
+ left = 0x11 + 0x08,
+ middle = 0x12 + 0x08,
+ right = 0x14 + 0x08,
+}
+
+local nodecodes = simplified(node.types())
+
+gluecodes = allocate(swapped(gluecodes,gluecodes))
+dircodes = allocate(swapped(dircodes,dircodes))
+boundarycodes = allocate(swapped(boundarycodes,boundarycodes))
+noadcodes = allocate(swapped(noadcodes,noadcodes))
+radicalcodes = allocate(swapped(radicalcodes,radicalcodes))
+nodecodes = allocate(swapped(nodecodes,nodecodes))
+listcodes = allocate(swapped(listcodes,listcodes))
+glyphcodes = allocate(swapped(glyphcodes,glyphcodes))
+kerncodes = allocate(swapped(kerncodes,kerncodes))
+penaltycodes = allocate(swapped(penaltycodes,penaltycodes))
+mathcodes = allocate(swapped(mathcodes,mathcodes))
+disccodes = allocate(swapped(disccodes,disccodes))
+accentcodes = allocate(swapped(accentcodes,accentcodes))
+fencecodes = allocate(swapped(fencecodes,fencecodes))
+parcodes = allocate(swapped(parcodes,parcodes))
+rulecodes = allocate(swapped(rulecodes,rulecodes))
+noadoptions = allocate(swapped(noadoptions,noadoptions))
+
+dirvalues = allocate(swapped(dirvalues,dirvalues))
+fillvalues = allocate(swapped(fillvalues,fillvalues))
+
+nodes.gluecodes = gluecodes
+nodes.dircodes = dircodes
+nodes.boundarycodes = boundarycodes
+nodes.noadcodes = noadcodes
+nodes.listcodes = listcodes
+nodes.glyphcodes = glyphcodes
+nodes.kerncodes = kerncodes
+nodes.penaltycodes = penaltycodes
+nodes.mathcodes = mathcodes
+nodes.disccodes = disccodes
+nodes.accentcodes = accentcodes
+nodes.radicalcodes = radicalcodes
+nodes.fencecodes = fencecodes
+nodes.parcodes = parcodes
+nodes.rulecodes = rulecodes
+
+nodes.noadoptions = noadoptions
+nodes.fillvalues = fillvalues
+nodes.fillcodes = fillvalues -- for now
+nodes.dirvalues = dirvalues
+
+nodes.nodecodes = nodecodes
+
+local subtypes = allocate {
+ glue = gluecodes,
+ dir = dircodes,
+ boundary = boundarycodes,
+ noad = noadcodes,
+ glyph = glyphcodes,
+ kern = kerncodes,
+ penalty = penaltycodes,
+ math = mathcodes,
+ disc = disccodes,
+ accent = accentcodes,
+ radical = radicalcodes,
+ fence = fencecodes,
+ par = parcodes,
+ rule = rulecodes,
+
+ vlist = listcodes,
+ hlist = listcodes,
+
+ -- list = listcodes,
+
+ -- parameter = parametercodes,
+ -- user = usercodes,
+}
+
+for k, v in table.sortedhash(subtypes) do
+ local i = nodecodes[k]
+ if i and not subtypes[i] then
+ subtypes[i] = v
+ end
+end
+
+nodes.subtypes = subtypes
+
+-- a few more friendly aliases:
+
+nodes.skipcodes = gluecodes
+nodes.directioncodes = dircodes
+nodes.discretionarycodes = disccodes
+
+nodes.directionvalues = dirvalues
+
+glyphcodes.glyph = glyphcodes.character
+
+gluecodes.parfillrightskip = gluecodes.parfillrightskip or gluecodes.parfillskip
+gluecodes.parfillskip = gluecodes.parfillskip or gluecodes.parfillrightskip
+
+listcodes.row = listcodes.alignment
+listcodes.column = listcodes.alignment
+
+kerncodes.kerning = kerncodes.fontkern
+kerncodes.italiccorrection = kerncodes.italiccorrection or 1 -- new
+
+nodes.noadoptions = {
+ set = 0x08,
+ unused_1 = 0x00 + 0x08,
+ unused_2 = 0x01 + 0x08,
+ axis = 0x02 + 0x08,
+ no_axis = 0x04 + 0x08,
+ exact = 0x10 + 0x08,
+ left = 0x11 + 0x08,
+ middle = 0x12 + 0x08,
+ right = 0x14 + 0x08,
+}
+
+-- We use the real node code numbers.
+
+local texsetintegervalue = tex.setintegervalue
+
+for i=0,nodecodes.glyph do
+ texsetintegervalue(nodecodes[i] .. "nodecode",i,"immutable")
+end
+for i=0,#gluecodes do
+ texsetintegervalue(gluecodes[i] .. "subtypecode",i,"immutable")
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/node-ini.mkxl
new file mode 100644
index 00000000000..b3f17e04159
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-ini.mkxl
@@ -0,0 +1,56 @@
+%D \module
+%D [ file=node-ini,
+%D version=2006.08.20,
+%D title=\CONTEXT\ Node Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Support / Initialization}
+
+\unprotect
+
+\newcount\filterstate \filterstate\plusone % hm, public
+
+\registerctxluafile{node-cmp}{autosuffix}
+\registerctxluafile{node-ini}{autosuffix}
+\registerctxluafile{node-met}{autosuffix}
+\registerctxluafile{node-nut}{autosuffix}
+\registerctxluafile{node-res}{autosuffix}
+\registerctxluafile{node-ext}{autosuffix}
+%registerctxluafile{node-ppt}{} % experimental, not used so probably useless
+\registerctxluafile{node-aux}{autosuffix}
+\registerctxluafile{node-gcm}{autosuffix}
+\registerctxluafile{node-tst}{}
+\registerctxluafile{node-tra}{autosuffix}
+\registerctxluafile{node-snp}{autosuffix}
+\registerctxluafile{node-tsk}{}
+\registerctxluafile{node-tex}{autosuffix}
+\registerctxluafile{node-dir}{} % experimental, not yet (and maybe never) used
+\registerctxluafile{node-pro}{autosuffix}
+\registerctxluafile{node-ser}{autosuffix}
+\registerctxluafile{node-acc}{autosuffix} % experimental
+%registerctxluafile{node-prp}{} % makes no sense (yet)
+\registerctxluafile{node-scn}{autosuffix}
+\registerctxluafile{node-syn}{autosuffix}
+\registerctxluafile{node-par}{autosuffix}
+
+%D This might go away (needs checking anyway, very old code):
+
+\newcount\c_node_tracers_show_box % box number
+
+\permanent\protected\def\shownextnodes{\afterassignment\node_tracers_show_next\c_node_tracers_show_box}
+\permanent\protected\def\showflatnodes{\afterassignment\node_tracers_show_flat\c_node_tracers_show_box}
+
+\def\node_tracers_show_next{\ctxlua{texio.write_nl('log',nodes.serializebox(\number\c_node_tracers_show_box,false,true))}}
+\def\node_tracers_show_flat{\ctxlua{texio.write_nl('log',nodes.serializebox(\number\c_node_tracers_show_box,true, true))}}
+
+\permanent\protected\def\visualizenextnodes{\dowithnextbox{\ctxlua{nodes.visualizebox(\number\nextbox,false,true)}}}
+\permanent\protected\def\visualizeflatnodes{\dowithnextbox{\ctxlua{nodes.visualizebox(\number\nextbox,true,true)}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-met.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-met.lmt
new file mode 100644
index 00000000000..a3f02d7093a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-met.lmt
@@ -0,0 +1,633 @@
+if not modules then modules = { } end modules ['node-MET'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is an experimental module. Don't use nuts for generic code, at least not till
+-- the regular code is proven stable. No support otherwise.
+
+-- luatex: todo: copylist should return h, t
+-- todo: see if using insert_before and insert_after makes sense here
+
+-- This file is a side effect of the \LUATEX\ speed optimization project of Luigi
+-- Scarso and me. As \CONTEXT\ spends over half its time in \LUA, we though that
+-- using \LUAJIT\ could improve performance. We've published some of our experiences
+-- elsewhere, but to summarize: \LUAJITTEX\ benefits a lot from the faster virtual
+-- machine, but when jit is turned of we loose some again. We experimented with
+-- ffi (without messing up the \CONTEXT\ code too much) but there we also lost more
+-- than we gained (mostly due to lack of compatible userdata support: it's all or
+-- nothing). This made us decide to look into the \LUA||\TEX\ interfacing and by
+-- profiling and careful looking at the (of course then still beta source code) we
+-- could come up with some improvements. The first showed up in 0.75 and we've more
+-- on the agenda for 0.80. Although some interfaces could be sped up significantly
+-- in practice we're only talking of 5||10\% on a \CONTEXT\ run and maybe more when
+-- complex and extensive node list manipulations happens (we're talking of hundreds
+-- of millions cross boundary calls then for documents of hundreds pages). One of the
+-- changes in the \CONTEXT\ code base is that we went from indexed access to nodes to
+-- function calls (in principle faster weren't it that the accessors need to do more
+-- checking which makes them slower) and from there to optimizing these calls as well
+-- as providing fast variants for well defined situations. At first optimizations were
+-- put in a separate \type {node.fast} table although some improvements could be
+-- ported to the main node functions. Because we got the feeling that more gain was
+-- possible (especially when using more complex fonts and \MKIV\ functionality) we
+-- eventually abandoned this approach and dropped the \type {fast} table in favour of
+-- another hack. In the process we had done lots of profiling and testing so we knew
+-- where time was wasted,
+--
+-- As lots of testing and experimenting was part of this project, I could not have
+-- done without stacks of new \CD s and \DVD s. This time Porcupine Tree, No-Man
+-- and Archive were came to rescue.
+--
+-- It all started with testing performance of:
+--
+-- node.getfield = metatable.__index
+-- node.setfield = metatable.__newindex
+
+local type, select = type, select
+local setmetatableindex = table.setmetatableindex
+
+-- First we get the metatable of a node:
+
+local metatable = nil
+
+do
+ local glyph = node.new("glyph",0)
+ metatable = getmetatable(glyph)
+ node.free(glyph)
+end
+
+-- statistics.tracefunction(node, "node", "getfield","setfield")
+-- statistics.tracefunction(node.direct,"node.direct","getfield","setfield")
+
+-- We start with some helpers and provide all relevant basic functions in the
+-- node namespace as well.
+
+nodes = nodes or { }
+local nodes = nodes
+
+local nodecodes = nodes.nodecodes
+
+nodes.tostring = node.tostring or tostring
+nodes.copy = node.copy
+nodes.copy_node = node.copy
+nodes.copy_list = node.copy_list
+nodes.delete = node.delete
+nodes.dimensions = node.dimensions
+nodes.rangedimensions = node.rangedimensions
+nodes.end_of_math = node.end_of_math
+nodes.flush = node.flush_node
+nodes.flush_node = node.flush_node
+nodes.flush_list = node.flush_list
+nodes.free = node.free
+nodes.insert_after = node.insert_after
+nodes.insert_before = node.insert_before
+nodes.hpack = node.hpack
+nodes.new = node.new
+nodes.tail = node.tail
+nodes.traverse = node.traverse
+nodes.traverse_id = node.traverse_id
+nodes.traverse_char = node.traverse_char
+nodes.traverse_glyph = node.traverse_glyph
+nodes.traverse_list = node.traverse_list
+nodes.slide = node.slide
+nodes.vpack = node.vpack
+nodes.fields = node.fields
+nodes.is_node = node.is_node
+nodes.setglue = node.setglue
+nodes.uses_font = node.uses_font
+
+nodes.first_glyph = node.first_glyph
+nodes.has_glyph = node.has_glyph or node.first_glyph
+
+nodes.current_attributes = node.current_attributes or node.current_attr
+nodes.has_field = node.has_field
+nodes.last_node = node.last_node
+nodes.usedlist = node.usedlist
+nodes.protrusion_skippable = node.protrusion_skippable
+nodes.check_discretionaries = node.check_discretionaries
+nodes.write = node.write
+nodes.flatten_discretionaries = node.flatten_discretionaries
+
+nodes.count = node.count
+nodes.length = node.length
+
+nodes.has_attribute = node.has_attribute
+nodes.set_attribute = node.set_attribute
+nodes.find_attribute = node.find_attribute
+nodes.unset_attribute = node.unset_attribute
+
+nodes.protect_glyph = node.protect_glyph
+nodes.protect_glyphs = node.protect_glyphs
+nodes.unprotect_glyph = node.unprotect_glyph
+nodes.unprotect_glyphs = node.unprotect_glyphs
+nodes.kerning = node.kerning
+nodes.ligaturing = node.ligaturing
+nodes.hyphenating = node.hyphenating
+nodes.mlist_to_hlist = node.mlist_to_hlist
+
+nodes.effective_glue = node.effective_glue
+nodes.getglue = node.getglue
+nodes.setglue = node.setglue
+nodes.is_zero_glue = node.is_zero_glue
+
+nodes.tonode = function(n) return n end
+nodes.tonut = function(n) return n end
+
+-- These are never used in \CONTEXT, only as a gimmick in node operators
+-- so we keep them around.
+--
+-- Fro nwo I keep them in \LMTX\ but they will go away!
+
+local n_getfield = node.getfield
+local n_getattr = node.get_attribute
+
+local n_setfield = node.setfield
+local n_setattr = n_setfield
+
+nodes.getfield = n_getfield
+nodes.setfield = n_setfield
+nodes.getattr = n_getattr
+nodes.setattr = n_setattr
+nodes.takeattr = nodes.unset_attribute
+
+local function n_getid (n) return n_getfield(n,"id") end
+local function n_getsubtype(n) return n_getfield(n,"subtype") end
+
+nodes.getid = n_getid
+nodes.getsubtype = n_getsubtype
+
+local function n_getchar(n) return n_getfield(n,"char") end
+local function n_setchar(n,c) return n_setfield(n,"char",c) end
+local function n_getfont(n) return n_getfield(n,"font") end
+local function n_setfont(n,f) return n_setfield(n,"font",f) end
+
+nodes.getchar = n_getchar
+nodes.setchar = n_setchar
+nodes.getfont = n_getfont
+nodes.setfont = n_setfont
+
+local function n_getlist (n) return n_getfield(n,"list") end
+local function n_setlist (n,l) return n_setfield(n,"list",l) end
+local function n_getleader(n) return n_getfield(n,"leader") end
+local function n_setleader(n,l) return n_setfield(n,"leader",l) end
+
+nodes.getlist = n_getlist
+nodes.setlist = n_setlist
+nodes.getleader = n_getleader
+nodes.setleader = n_setleader
+
+local function n_getnext(n) return n_getfield(n,"next") end
+local function n_setnext(n,nn) return n_setfield(n,"next",nn) end
+local function n_getprev(n) return n_getfield(n,"prev") end
+local function n_setprev(n,pp) return n_setfield(n,"prev",pp) end
+local function n_getboth(n) return n_getfield(n,"prev"), n_getfield(n,"next") end
+local function n_setboth(n,pp,nn) return n_setfield(n,"prev",pp), n_setfield(n,"next",nn) end
+
+nodes.getnext = n_getnext
+nodes.setnext = n_setnext
+nodes.getprev = n_getprev
+nodes.setprev = n_setprev
+nodes.getboth = n_getboth
+nodes.setboth = n_setboth
+
+local function n_setlink(...)
+ -- not that fast but not used often anyway
+ local h = nil
+ for i=1,select("#",...) do
+ local n = select(i,...)
+ if not n then
+ -- go on
+ elseif h then
+ n_setfield(h,"next",n)
+ n_setfield(n,"prev",h)
+ else
+ h = n
+ end
+ end
+ return h
+end
+
+nodes.setlink = n_setlink
+
+nodes.getbox = node.getbox or tex.getbox
+nodes.setbox = node.setbox or tex.setbox
+
+local n_flush_node = nodes.flush
+local n_copy_node = nodes.copy
+local n_copy_list = nodes.copy_list
+local n_find_tail = nodes.tail
+local n_insert_after = nodes.insert_after
+local n_insert_before = nodes.insert_before
+local n_slide = nodes.slide
+
+local n_remove_node = node.remove -- not yet nodes.remove
+
+local function remove(head,current,free_too)
+ local t = current
+ head, current = n_remove_node(head,current)
+ if not t then
+ -- forget about it
+ elseif free_too then
+ n_flush_node(t)
+ t = nil
+ else
+ n_setboth(t)
+ end
+ return head, current, t
+end
+
+nodes.remove = remove
+
+function nodes.delete(head,current)
+ return remove(head,current,true)
+end
+
+-- local h, c = nodes.replace(head,current,new)
+-- local c = nodes.replace(false,current,new)
+-- local c = nodes.replace(current,new)
+--
+-- todo: check for new.next and find tail
+
+function nodes.replace(head,current,new) -- no head returned if false
+ if not new then
+ head, current, new = false, head, current
+-- current, new = head, current
+ end
+ local prev = n_getprev(current)
+ local next = n_getnext(current)
+ if next then
+ n_setlink(new,next)
+ end
+ if prev then
+ n_setlink(prev,new)
+ end
+ if head then
+ if head == current then
+ head = new
+ end
+ n_flush_node(current)
+ return head, new
+ else
+ n_flush_node(current)
+ return new
+ end
+end
+
+-- nodes.countall : see node-nut.lua
+
+function nodes.append(head,current,...)
+ for i=1,select("#",...) do
+ head, current = n_insert_after(head,current,(select(i,...)))
+ end
+ return head, current
+end
+
+function nodes.prepend(head,current,...)
+ for i=1,select("#",...) do
+ head, current = n_insert_before(head,current,(select(i,...)))
+ end
+ return head, current
+end
+
+function nodes.linked(...)
+ local head, last
+ for i=1,select("#",...) do
+ local next = select(i,...)
+ if next then
+ if head then
+ n_setlink(last,next)
+ else
+ head = next
+ end
+ last = n_find_tail(next) -- we could skip the last one
+ end
+ end
+ return head
+end
+
+function nodes.concat(list) -- consider tail instead of slide
+ local head, tail
+ for i=1,#list do
+ local li = list[i]
+ if li then
+ if head then
+ n_setlink(tail,li)
+ else
+ head = li
+ end
+ tail = n_slide(li)
+ end
+ end
+ return head, tail
+end
+
+function nodes.reference(n)
+ return n and tonut(n) or "<none>"
+end
+
+-- Here starts an experiment with metatables. Of course this only works with nodes
+-- wrapped in userdata with a metatable.
+--
+-- Nodes are kind of special in the sense that you need to keep an eye on creation
+-- and destruction. This is quite natural if you consider that changing the content
+-- of a node would also change any copy (or alias). As there are too many pitfalls
+-- we don't have this kind of support built in \LUATEX, which means that macro
+-- packages are free to provide their own. One can even use local variants.
+--
+-- n1 .. n2 : append nodes, no copies
+-- n1 * 5 : append 4 copies of nodes
+-- 5 + n1 : strip first 5 nodes
+-- n1 - 5 : strip last 5 nodes
+-- n1 + n2 : inject n2 after first of n1
+-- n1 - n2 : inject n2 before last of n1
+-- n1^2 : two copies of nodes (keep orginal)
+-- - n1 : reverse nodes
+-- n1/f : apply function to nodes
+
+-- local s = nodes.typesetters.tonodes
+--
+-- local function w(lst)
+-- context.dontleavehmode()
+-- context(lst)
+-- context.par()
+-- end
+--
+-- local n1 = s("a")
+-- local n2 = s("b")
+-- local n3 = s("c")
+-- local n4 = s("d")
+-- local n5 = s("e")
+-- local n6 = s("f")
+-- local n7 = s("g")
+--
+-- local n0 = n1 .. (n2 * 10).. n3 .. (5 * n4) .. n5 .. ( 5 * n6 ) .. n7 / function(n) n.char = string.byte("!") return n end
+--
+-- w(#n0)
+--
+-- w(n0)
+--
+-- local n1 = s("a") * 10
+-- local n2 = s("b") * 10
+--
+-- local n0 = ((5 + n1) .. (n2 - 5) )
+-- local n0 = - n0
+--
+-- local n0 = nil .. n0^3 .. nil
+--
+-- w(n0)
+--
+-- w ( s("a") + s("b") ) w ( s("a") + 4*s("b") ) w ( 4*s("a") + s("b") ) w ( 4*s("a") + 4*s("b") )
+-- w ( s("a") - s("b") ) w ( s("a") - 4*s("b") ) w ( 4*s("a") - s("b") ) w ( 4*s("a") - 4*s("b") )
+
+local n_remove_node = nodes.remove
+
+metatable.__concat = function(n1,n2) -- todo: accept nut on one end
+ if not n1 then
+ return n2
+ elseif not n2 then
+ return n1
+ elseif n1 == n2 then
+ -- or abort
+ return n2 -- or n2 * 2
+ else
+ local tail = n_find_tail(n1)
+ n_setlink(tail,n2)
+ return n1
+ end
+end
+
+metatable.__mul = function(n,multiplier)
+ if type(multiplier) ~= "number" then
+ n, multiplier = multiplier, n
+ end
+ if multiplier <= 1 then
+ return n
+ elseif n_getnext(n) then
+ local head
+ for i=2,multiplier do
+ local h = n_copy_list(n)
+ if head then
+ local t = n_find_tail(h)
+ n_setlink(t,head)
+ end
+ head = h
+ end
+ local t = n_find_tail(n)
+ n_setlink(t,head)
+ else
+ local head
+ for i=2,multiplier do
+ local c = n_copy_node(n)
+ if head then
+ n_setlink(c,head)
+ end
+ head = c
+ end
+ n_setlink(n,head)
+ end
+ return n
+end
+
+metatable.__sub = function(first,second)
+ if type(second) == "number" then
+ local tail = n_find_tail(first)
+ for i=1,second do
+ local prev = n_getprev(tail)
+ n_flush_node(tail) -- can become flushlist/flushnode
+ if prev then
+ tail = prev
+ else
+ return nil
+ end
+ end
+ if tail then
+ n_setnext(tail)
+ return first
+ else
+ return nil
+ end
+ else
+ -- aaaaa - bbb => aaaabbba
+ local firsttail = n_find_tail(first)
+ local prev = n_getprev(firsttail)
+ if prev then
+ local secondtail = n_find_tail(second)
+ n_setlink(secondtail,firsttail)
+ n_setlink(prev,second)
+ return first
+ else
+ local secondtail = n_find_tail(second)
+ n_setlink(secondtail,first)
+ return second
+ end
+ end
+end
+
+metatable.__add = function(first,second)
+ if type(first) == "number" then
+ local head = second
+ for i=1,first do
+ local second = n_getnext(head)
+ n_flush_node(head) -- can become flushlist/flushnode
+ if second then
+ head = second
+ else
+ return nil
+ end
+ end
+ if head then
+ n_setprev(head)
+ return head
+ else
+ return nil
+ end
+ else
+ -- aaaaa + bbb => abbbaaaa
+ local next = n_getnext(first)
+ if next then
+ local secondtail = n_find_tail(second)
+ n_setlink(first,second)
+ n_setlink(secondtail,next)
+ else
+ n_setlink(first,second)
+ end
+ return first
+ end
+end
+
+metatable.__len = function(current)
+ local length = 0
+ while current do
+ current = n_getnext(current)
+ length = length + 1
+ end
+ return length
+end
+
+metatable.__div = function(list,action)
+ return action(list) or list -- always a value
+end
+
+metatable.__pow = function(n,multiplier)
+ local tail = n
+ local head = nil
+ if n_getnext(n) then
+ if multiplier == 1 then
+ head = n_copy_list(n)
+ else
+ for i=1,multiplier do
+ local h = n_copy_list(n)
+ if head then
+ local t = n_find_tail(h)
+ n_setlink(t,head)
+ end
+ head = h
+ end
+ end
+ else
+ if multiplier == 1 then
+ head = n_copy_node(n)
+ else
+ for i=2,multiplier do
+ local c = n_copy_node(n)
+ if head then
+ n_setlink(head,c)
+ end
+ head = c
+ end
+ end
+ end
+ -- todo: tracing
+ return head
+end
+
+metatable.__unm = function(head)
+ local last = head
+ local first = head
+ local current = n_getnext(head)
+ while current do
+ local next = n_getnext(current)
+ n_setlink(current,first)
+ first = current
+ current = next
+ end
+ n_setprev(first)
+ n_setnext(last)
+ return first
+end
+
+-- see node-nut.lua for more info on going nuts
+
+-- if not gonuts then
+--
+-- local nuts = { }
+-- nodes.nuts = nuts
+--
+-- local function dummy(f) return f end
+--
+-- nodes.vianuts = dummy
+-- nodes.vianodes = dummy
+--
+-- for k, v in next, nodes do
+-- if type(v) == "function" then
+-- nuts[k] = v
+-- end
+-- end
+--
+-- end
+
+-- also handy
+
+local tonode = nodes.tonode
+local whatsit_code = nodecodes.whatsit
+local getfields = node.fields
+local sort = table.sort
+local whatsitkeys = { }
+local keys = { whatsit = whatsitkeys }
+local messyhack = table.tohash { -- temporary solution
+ nodecodes.attributelist,
+ nodecodes.attribute,
+ nodecodes.action, -- hm
+}
+
+setmetatableindex(keys,function(t,k)
+ local v = (k == "attributelist" or k == nodecodes.attributelist) and { } or getfields(k)
+ if messyhack[k] then
+ for i=1,#v do
+ if v[i] == "subtype" then
+ remove(v,i)
+ break
+ end
+ end
+ end
+ if v[ 0] then v[#v+1] = "next" v[ 0] = nil end
+ if v[-1] then v[#v+1] = "prev" v[-1] = nil end
+ sort(v)
+ t[k] = v
+ return v
+end)
+
+setmetatableindex(whatsitkeys,function(t,k)
+ local v = getfields(whatsit_code,k)
+ if v[ 0] then v[#v+1] = "next" v[ 0] = nil end
+ if v[-1] then v[#v+1] = "prev" v[-1] = nil end
+ sort(v)
+ t[k] = v
+ return v
+end)
+
+local function nodefields(n)
+ n = tonode(n)
+ local id = n.id
+ if id == whatsit_code then
+ return whatsitkeys[n.subtype]
+ else
+ return keys[id]
+ end
+end
+
+nodes.keys = keys -- [id][subtype]
+nodes.fields = nodefields -- (n)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-mig.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-mig.lmt
new file mode 100644
index 00000000000..c1991350935
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-mig.lmt
@@ -0,0 +1,134 @@
+if not modules then modules = { } end modules ['node-mig'] = {
+ version = 1.001,
+ comment = "companion to node-mig.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: insert_after
+
+local format = string.format
+
+local trace_migrations = false trackers.register("nodes.migrations", function(v) trace_migrations = v end)
+
+local report_nodes = logs.reporter("nodes","migrations")
+
+local attributes = attributes
+local nodes = nodes
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getlist = nuts.getlist
+local getpost = nuts.getpost
+local getprop = nuts.getprop
+
+local setprop = nuts.setprop
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+local setpost = nuts.setpost
+
+local count = nuts.count
+local migrate = nuts.migrate
+
+local nodecodes = nodes.nodecodes
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local insert_code = nodecodes.ins
+local mark_code = nodecodes.mark
+
+local a_migrated = attributes.private("migrated")
+
+local migrate_inserts = false
+local migrate_marks = false
+local t_inserts = 0
+local t_marks = 0
+local t_sweeps = 0
+
+local trialtypesetting = context.trialtypesetting
+
+function nodes.handlers.migrate(head,where)
+ if head and not trialtypesetting() and where == "alignment" then
+ if trace_migrations then
+ report_nodes("migration sweep %a",where)
+ end
+ local current = head
+ while current do
+ local id = getid(current)
+ if (id == vlist_code or id == hlist_code or id == insert_code) and not getprop(current,"migrated") then
+ setprop(current,"migrated",true)
+ local list = getlist(current)
+ if list then
+ t_sweeps = t_sweeps + 1
+ local h, first, last = migrate(list,migrate_inserts,migrate_marks)
+ if first then
+ if trace_migrations then
+ local ni = count(insert_code,first)
+ local nm = count(mark_code,first)
+ t_inserts = t_inserts + ni
+ t_marks = t_marks + nm
+ report_nodes("sweep %a, container %a, %s inserts and %s marks migrated outwards during %a",
+ t_sweeps,nodecodes[id],ni,nm,where)
+
+ end
+ local p, t = getpost(current)
+ if p then
+ setlink(t,first)
+ else
+ setpost(current,first)
+ end
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ end
+ return head
+end
+
+statistics.register("node migrations", function()
+ if trace_migrations and t_sweeps > 0 then
+ return format("%s sweeps, %s inserts moved, %s marks moved",t_sweeps,t_inserts,t_marks)
+ end
+end)
+
+-- Since we started with mkiv we had it as experiment but it is about time
+-- to have a more formal interface .. it's still optional due to possible
+-- side effects.
+
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+local texsetcount = tex.setcount
+
+local migrations = { }
+nodes.migrations = migrations
+local enabled = false
+
+local function check()
+ if migrate_marks or migrate_inserts then
+ if not enabled then
+ enableaction("mvlbuilders", "nodes.handlers.migrate")
+ enabled = true
+ texsetcount("automigrationmode",3)
+ end
+ else
+ if enabled then
+ disableaction("mvlbuilders", "nodes.handlers.migrate")
+ enabled = false
+ texsetcount("automigrationmode",0)
+ end
+ end
+end
+
+function migrations.setmarks(v)
+ migrate_marks = v
+ check()
+end
+
+function migrations.setinserts(v)
+ migrate_inserts = v
+ check()
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-mig.mkxl b/Master/texmf-dist/tex/context/base/mkxl/node-mig.mkxl
new file mode 100644
index 00000000000..420140e6fd9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-mig.mkxl
@@ -0,0 +1,44 @@
+%D \module
+%D [ file=node-mig,
+%D version=2010.01.19,
+%D title=\CONTEXT\ Node Macros,
+%D subtitle=Inserts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Support / Migrations}
+
+%D \starttyping
+%D \automigrateinserts
+%D
+%D \hbox{\vbox{x\footnote{1}x\footnote{2}x\footnote{3}x}}
+%D \vbox{x\footnote{4}x}
+%D \hbox{\vbox{x\footnote{5}x}}
+%D
+%D \hbox{x}
+%D \hbox{x\footnote{6}x}
+%D \hbox{x}
+%D
+%D \starttabulate
+%D \NC test \NC test \footnote{test} \input tufte \NC \NR
+%D \NC test \NC test \footnote{test} \input tufte \NC \NR
+%D \NC test \NC test \footnote{test} \input tufte \NC \NR
+%D \NC test \NC test \footnote{test} \input tufte \NC \NR
+%D \NC test \NC test \footnote{test} \input tufte \NC \NR
+%D \NC test \NC test \footnote{test} \input tufte \NC \NR
+%D \stoptabulate
+%D \stoptyping
+
+\unprotect
+
+\registerctxluafile{node-mig}{autosuffix}
+
+\aliased\let\automigrateinserts\donothing
+\aliased\let\automigratemarks \donothing
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-nut.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-nut.lmt
new file mode 100644
index 00000000000..7ef087178cd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-nut.lmt
@@ -0,0 +1,601 @@
+if not modules then modules = { } end modules ['node-nut'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Some comments anbout going nuts can be found in the mkiv file with the
+-- suffix "lua", including some timing and musings. We copy the direct table
+-- (1) because that's what we did while things evolved and (2) because we
+-- also add some more helpers.
+
+local type, rawget = type, rawget
+
+local nodes = nodes
+local direct = node.direct
+
+local fastcopy = table.fastcopy
+
+local nodecodes = nodes.nodecodes
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glyph_code = nodecodes.glyph
+
+local tonode = direct.tonode
+local tonut = direct.todirect
+
+local is_node = direct.is_node
+local is_nut = direct.is_direct
+
+local d_remove_node = direct.remove
+local d_flush_node = direct.flush_node
+local d_getnext = direct.getnext
+local d_getprev = direct.getprev
+local d_getid = direct.getid
+local d_getlist = direct.getlist
+local d_find_tail = direct.tail
+local d_insert_after = direct.insert_after
+local d_insert_before = direct.insert_before
+local d_slide = direct.slide
+local d_traverse = direct.traverse
+local d_setlink = direct.setlink
+local d_getboth = direct.getboth
+
+local nuts = {
+ check_discretionaries = direct.check_discretionaries,
+ copy = direct.copy,
+ copy_list = direct.copy_list,
+ copy_node = direct.copy,
+ copy_only = direct.copy_only or direct.copy,
+ count = direct.count,
+ current_attributes = direct.current_attributes,
+ delete = direct.delete,
+ dimensions = direct.dimensions,
+ naturalhsize = direct.naturalhsize,
+ naturalwidth = direct.naturalwidth,
+ effective_glue = direct.effective_glue,
+ end_of_math = direct.end_of_math,
+ exchange = direct.exchange,
+ find_attribute = direct.find_attribute,
+ first_glyph = direct.first_glyph,
+ flatten_discretionaries = direct.flatten_discretionaries,
+ flush = d_flush_node,
+ flush_components = direct.flush_components,
+ flush_list = direct.flush_list,
+ flush_node = direct.flush_node,
+ free = direct.free,
+ getsynctexfields = direct.get_synctex_fields,
+ getattr = direct.get_attribute,
+ getattrs = direct.get_attributes,
+ getattributelist = direct.getattributelist,
+ getattrlist = direct.getattributelist,
+ getboth = d_getboth,
+ getbox = direct.getbox,
+ getboxglue = direct.getglue,
+ getchar = direct.getchar,
+ getcomponents = direct.getcomponents,
+ getdata = direct.getdata,
+ getdepth = direct.getdepth,
+ getdir = direct.getdir,
+ getdirection = direct.getdirection,
+ getdisc = direct.getdisc,
+ getdiscretionary = direct.getdisc,
+ getexpansion = direct.getexpansion,
+ getfam = direct.getfam,
+ getfield = direct.getfield,
+ getfont = direct.getfont,
+ getglue = direct.getglue,
+ getglyphdata = direct.getglyphdata,
+ getheight = direct.getheight,
+ getid = d_getid,
+ getkern = direct.getkern,
+ getlang = direct.getlanguage,-- will become obsolete
+ getlanguage = direct.getlanguage,
+ getleader = direct.getleader,
+ getlist = d_getlist,
+ getnext = d_getnext,
+ getnormalizedline = direct.getnormalizedline,
+ getnucleus = direct.getnucleus,
+ getoffsets = direct.getoffsets,
+ -- getxyoffsets = direct.getxyoffsets,
+ getscale = direct.getscale,
+ getscales = direct.getscales,
+ getxscale = direct.getxscale,
+ getyscale = direct.getyscale,
+ xscaled = direct.xscaled,
+ yscaled = direct.yscaled,
+ getxyscales = direct.getxyscales,
+ getorientation = direct.getorientation,
+ getoptions = direct.getoptions,
+ getpenalty = direct.getpenalty,
+ getpost = direct.getpost,
+ getpre = direct.getpre,
+ getprev = d_getprev,
+ getreplace = direct.getreplace,
+ getruledata = direct.getdata, -- obsolete when we have the split
+ getscript = direct.setscript,
+ getshift = direct.getshift,
+ getstate = direct.getstate,
+ getsub = direct.getsub,
+ getsubpre = direct.getsubpre,
+ getsubtype = direct.getsubtype,
+ getsup = direct.getsup,
+ getsuppre = direct.getsuppre,
+ getsurround = direct.getkern,
+ gettotal = direct.gettotal,
+ getvalue = direct.getdata, -- obsolete
+ getwhd = direct.getwhd,
+ getwidth = direct.getwidth,
+ has_attribute = direct.has_attribute,
+ has_dimensions = direct.has_dimensions,
+ has_field = direct.has_field,
+ has_glyph = direct.has_glyph or direct.first_glyph,
+ hpack = direct.hpack,
+ hyphenating = direct.hyphenating,
+ insert_after = d_insert_after,
+ insert_before = d_insert_before,
+ is_direct = is_direct,
+ is_node = is_node,
+ is_nut = direct.is_direct,
+ is_zero_glue = direct.is_zero_glue,
+ ischar = direct.is_char,
+ isprevchar = direct.is_prev_char,
+ isnextchar = direct.is_next_char,
+ isprevglyph = direct.is_prev_glyph,
+ isnextglyph = direct.is_next_glyph,
+ isglyph = direct.is_glyph,
+ kerning = direct.kerning,
+ last_node = direct.last_node,
+ length = direct.length,
+ ligaturing = direct.ligaturing,
+ migrate = direct.migrate,
+ mlist_to_hlist = direct.mlist_to_hlist,
+ new = direct.new,
+ protect_glyph = direct.protect_glyph,
+ protect_glyphs = direct.protect_glyphs,
+ protrusion_skippable = direct.protrusion_skippable,
+ rangedimensions = direct.rangedimensions,
+ getglyphdimensions = direct.getglyphdimensions,
+ getkerndimension = direct.getkerndimension,
+ remove = d_remove_node,
+ reverse = direct.reverse,
+ set_attribute = direct.set_attribute,
+ addmargins = direct.addmargins,
+ addxymargins = direct.addxymargins,
+ setattr = direct.set_attribute,
+ setattrs = direct.set_attributes,
+ setattributelist = direct.setattributelist,
+ setattrlist = direct.setattributelist,
+ setboth = direct.setboth,
+ setbox = direct.setbox,
+ setboxglue = direct.setglue,
+ setchar = direct.setchar,
+ setcomponents = direct.setcomponents,
+ setdata = direct.setdata,
+ setdepth = direct.setdepth,
+ setdir = direct.setdir,
+ setdirection = direct.setdirection,
+ setdisc = direct.setdisc,
+ setdiscretionary = direct.setdisc,
+ setexpansion = direct.setexpansion,
+ setfam = direct.setfam,
+ setfield = direct.setfield,
+ setfont = direct.setfont,
+ setglue = direct.setglue,
+ setglyphdata = direct.setglyphdata,
+ setheight = direct.setheight,
+ setkern = direct.setkern,
+ setlang = direct.setlanguage,
+ setlanguage = direct.setlanguage,
+ setleader = direct.setleader,
+ setlink = d_setlink,
+ setlist = direct.setlist,
+ setnext = direct.setnext,
+ setnucleus = direct.setnucleus,
+ setscale = direct.setscale or direct.setscales,
+ setscales = direct.setscales,
+ setoffsets = direct.setoffsets,
+ addxoffset = direct.addxoffset,
+ addyoffset = direct.addyoffset,
+ setorientation = direct.setorientation,
+ setoptions = direct.setoptions,
+ setpenalty = direct.setpenalty,
+ setpost = direct.setpost,
+ setpre = direct.setpre,
+ setprev = direct.setprev,
+ setreplace = direct.setreplace,
+ setruledata = direct.setdata, -- obsolete when we have the split
+ setscript = direct.getscript,
+ setshift = direct.setshift,
+ setsplit = direct.setsplit,
+ setstate = direct.setstate,
+ setsub = direct.setsub,
+ setsubpre = direct.setsubpre,
+ setsubtype = direct.setsubtype,
+ setsup = direct.setsup,
+ setsuppre = direct.setsuppre,
+ setsurround = direct.setkern,
+ setsynctexfields = direct.set_synctex_fields,
+ setvalue = direct.setdata, -- obsolete
+ setwhd = direct.setwhd,
+ setwidth = direct.setwidth,
+ slide = d_slide,
+ start_of_par = direct.start_of_par,
+ tail = d_find_tail,
+ takeattr = direct.unset_attribute, -- ?
+ tonode = tonode,
+ tonut = tonut,
+ tostring = direct.tostring,
+ traverse = d_traverse,
+ traverse_char = direct.traverse_char,
+ traverse_glyph = direct.traverse_glyph,
+ traverse_id = direct.traverse_id,
+ traverse_list = direct.traverse_list,
+ traverse_content = direct.traverse_content,
+ unprotect_glyph = direct.unprotect_glyph,
+ unprotect_glyphs = direct.unprotect_glyphs,
+ unset_attribute = direct.unset_attribute,
+ unset_attributes = direct.unset_attributes,
+ usedlist = direct.usedlist,
+ uses_font = direct.uses_font,
+ vpack = direct.vpack,
+ write = direct.write,
+ append = direct.append,
+ has_glyph_option = direct.has_glyph_option,
+ show = direct.show,
+}
+
+nodes.nuts = nuts
+
+nodes.is_node = is_node
+nodes.is_direct = is_nut
+nodes.is_nut = is_nut
+
+nodes.tonode = tonode
+nodes.tonut = tonut
+
+function nuts.delete(head,current)
+ return d_remove_node(head,current,true)
+end
+
+function nuts.replace(head,current,new) -- no head returned if false
+ if not new then
+ head, current, new = false, head, current
+ end
+ local prev, next = d_getboth(current)
+ if prev or next then
+ d_setlink(prev,new,next)
+ end
+ if head then
+ if head == current then
+ head = new
+ end
+ d_flush_node(current)
+ return head, new
+ else
+ d_flush_node(current)
+ return new
+ end
+end
+
+local function countall(stack,flat)
+ local n = 0
+ while stack do
+ local id = d_getid(stack)
+ if not flat and id == hlist_code or id == vlist_code then
+ local list = d_getlist(stack)
+ if list then
+ n = n + 1 + countall(list) -- self counts too
+ else
+ n = n + 1
+ end
+ else
+ n = n + 1
+ end
+ stack = d_getnext(stack)
+ end
+ return n
+end
+
+nuts.countall = countall
+
+function nodes.countall(stack,flat)
+ return countall(tonut(stack),flat)
+end
+
+function nuts.append(head,current,...)
+ for i=1,select("#",...) do
+ head, current = d_insert_after(head,current,(select(i,...)))
+ end
+ return head, current
+end
+
+function nuts.prepend(head,current,...)
+ for i=1,select("#",...) do
+ head, current = d_insert_before(head,current,(select(i,...)))
+ end
+ return head, current
+end
+
+function nuts.linked(...) -- slides !
+ local head, last
+ for i=1,select("#",...) do
+ local next = select(i,...)
+ if next then
+ if head then
+ d_setlink(last,next)
+ else
+ head = next
+ end
+ last = d_find_tail(next) -- we could skip the last one
+ end
+ end
+ return head
+end
+
+function nuts.concat(list) -- consider tail instead of slide
+ local head, tail
+ for i=1,#list do
+ local li = list[i]
+ if li then
+ if head then
+ d_setlink(tail,li)
+ else
+ head = li
+ end
+ tail = d_slide(li)
+ end
+ end
+ return head, tail
+end
+
+function nuts.reference(n)
+ return n or "<none>"
+end
+
+function nuts.vianuts (f) return function(n,...) return tonode(f(tonut (n),...)) end end
+function nuts.vianodes(f) return function(n,...) return tonut (f(tonode(n),...)) end end
+
+nodes.vianuts = nuts.vianuts
+nodes.vianodes = nuts.vianodes
+
+function nodes.insert_list_after(h,c,n)
+ local t = n_tail(n)
+ if c then
+ local cn = n_getnext(c)
+ if cn then
+ -- no setboth here yet
+ n_setfield(t,"next",cn)
+ n_setfield(cn,"prev",t)
+ else
+ n_setfield(t,"next",nil)
+ end
+ n_setfield(c,"next",n)
+ n_setfield(n,"prev",c)
+ return h, n
+ end
+ return n, t
+end
+
+function nuts.insert_list_after(h,c,n)
+ local t = d_tail(n)
+ if c then
+ local cn = d_getnext(c)
+ if cn then
+ d_setlink(t,cn)
+ else
+ d_setnext(t)
+ end
+ d_setlink(c,n)
+ return h, n
+ end
+ return n, t
+end
+
+-- test code only
+
+-- collectranges and mix
+
+local report = logs.reporter("sliding")
+
+local function message(detail,head,current,previous)
+ report("error: %s, current: %s:%s, previous: %s:%s, list: %s, text: %s",
+ detail,
+ nodecodes[d_getid(current)],
+ current,
+ nodecodes[d_getid(previous)],
+ previous,
+ nodes.idstostring(head),
+ nodes.listtoutf(head)
+ )
+ utilities.debugger.showtraceback(report)
+end
+
+local function warn()
+ report()
+ report("warning: the slide tracer is enabled")
+ report()
+ warn = false
+end
+
+local function tracedslide(head)
+ if head then
+ if warn then
+ warn()
+ end
+ local next = d_getnext(head)
+ if next then
+ local prev = head
+ for n in d_traverse(next) do
+ local p = d_getprev(n)
+ if not p then
+ message("unset",head,n,prev)
+ -- break
+ elseif p ~= prev then
+ message("wrong",head,n,prev)
+ -- break
+ end
+ prev = n
+ end
+ end
+ return d_slide(head)
+ end
+end
+
+local function nestedtracedslide(head,level) -- no sliding !
+ if head then
+ if warn then
+ warn()
+ end
+ local id = d_getid(head)
+ local next = d_getnext(head)
+ if next then
+ report("%whead:%s",level or 0,nodecodes[id])
+ local prev = head
+ for n in d_traverse(next) do
+ local p = d_getprev(n)
+ if not p then
+ message("unset",head,n,prev)
+ -- break
+ elseif p ~= prev then
+ message("wrong",head,n,prev)
+ -- break
+ end
+ prev = n
+ local id = d_getid(n)
+ if id == hlist_code or id == vlist_code then
+ nestedtracedslide(d_getlist(n),(level or 0) + 1)
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ report("%wlist:%s",level or 0,nodecodes[id])
+ nestedtracedslide(d_getlist(head),(level or 0) + 1)
+ end
+ -- return d_slide(head)
+ end
+end
+
+local function untracedslide(head)
+ if head then
+ if warn then
+ warn()
+ end
+ local next = d_getnext(head)
+ if next then
+ local prev = head
+ for n in d_traverse(next) do
+ local p = d_getprev(n)
+ if not p then
+ return "unset", d_getid(n)
+ elseif p ~= prev then
+ return "wrong", d_getid(n)
+ end
+ prev = n
+ end
+ end
+ return d_slide(head)
+ end
+end
+
+nuts.tracedslide = tracedslide
+nuts.untracedslide = untracedslide
+nuts.nestedtracedslide = nestedtracedslide
+
+-- this might move
+
+local propertydata = direct.get_properties_table(true)
+
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+
+nodes.properties = {
+ data = propertydata,
+}
+
+-- experimental code with respect to copying attributes has been removed
+-- as it doesn't pay of (most attributes are only accessed once anyway)
+
+function nuts.getprop(n,k)
+ local p = propertydata[n]
+ if p then
+ if k then
+ return p[k]
+ else
+ return p
+ end
+ end
+end
+
+function nuts.rawprop(n,k)
+ local p = rawget(propertydata,n)
+ if p then
+ if k then
+ return p[k]
+ else
+ return p
+ end
+ end
+end
+
+function nuts.setprop(n,k,v)
+ local p = propertydata[n]
+ if p then
+ p[k] = v
+ else
+ propertydata[n] = { [k] = v }
+ end
+end
+
+function nuts.theprop(n)
+ local p = propertydata[n]
+ if not p then
+ p = { }
+ propertydata[n] = p
+ end
+ return p
+end
+
+
+function nuts.isdone(n,k)
+ local p = propertydata[n]
+ if not p then
+ propertydata[n] = { [k] = true }
+ return false
+ end
+ local v = p[k]
+ if v == nil then
+ propertydata[n] = { [k] = true }
+ return false
+ end
+ return v
+end
+
+function nuts.copy_properties(source,target,what)
+ local newprops = propertydata[source]
+ if not newprops then
+ -- nothing to copy
+ return
+ end
+ if what then
+ -- copy one category
+ newprops = rawget(source,what)
+ if newprops then
+ newprops = fastcopy(newprops)
+ local p = rawget(propertydata,target)
+ if p then
+ p[what] = newprops
+ else
+ propertydata[target] = {
+ [what] = newprops,
+ }
+ end
+ end
+ else
+ -- copy all properties
+ newprops = fastcopy(newprops)
+ propertydata[target] = newprops
+ end
+ return newprops -- for checking
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-par.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-par.lmt
new file mode 100644
index 00000000000..125c1b687c1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-par.lmt
@@ -0,0 +1,101 @@
+if not modules then modules = { } end modules ['node-par'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local sequencers = utilities.sequencers
+
+-- This is called a lot! I'm a bit reluctant with this one because it is
+-- sensitive for order. In many other callbacks ther eis no action at the
+-- tex end but here ... Anyway, it has been around for a while now (2019)
+-- and so far I had no need for extensive usage so we're okay.
+
+do
+
+ local actions = nodes.tasks.actions("everypar")
+
+ local function everypar(head)
+ starttiming(builders)
+ head = actions(head)
+ stoptiming(builders)
+ return head
+ end
+
+ callbacks.register("insert_par",everypar,"after paragraph start")
+
+end
+
+-- Originally this one was meant to deal with the indentation (like turn a box into
+-- a skip or prevent it) but that never really was used. The return value still
+-- detemines if an indentation box or skip is injected. Will I change that?
+
+do
+
+ local actions = sequencers.new {
+ name = "paragraph",
+ arguments = "mode,indented,context",
+ returnvalues = "indented",
+ results = "indented",
+ }
+
+ sequencers.appendgroup(actions,"before") -- user
+ sequencers.appendgroup(actions,"system") -- private
+ sequencers.appendgroup(actions,"after" ) -- user
+
+ local function paragraph(mode,indented,context) -- context used to be the cmd code
+ local runner = actions.runner
+ if runner then
+ starttiming(builders)
+ indented = runner(mode,indented,context)
+ stoptiming(builders)
+ end
+ return indented
+ end
+
+ callbacks.register("begin_paragraph",paragraph,"before paragraph start")
+
+end
+
+-- This one is a playground for some old metafun gimmicks that I want to improve
+-- while I'm updating the manual to lmtx. but it might also be useful for other
+-- purposes. It fits in the category obscure and probably takes while to stabelize
+-- (if it stays at all). Again, this is one that has the danger of interference,
+-- so when it finally got an action handler it only got a system one.
+
+do
+
+ local actions = sequencers.new {
+ name = "paragraphcontext",
+ arguments = "context",
+ returnvalues = "ignore",
+ results = "ignore",
+ }
+
+ ----------.appendgroup(actions,"before") -- user
+ sequencers.appendgroup(actions,"system") -- private
+ ----------.appendgroup(actions,"after" ) -- user
+
+ local function parcontext(parcontext)
+ local runner = actions.runner
+ if runner then
+ starttiming(builders)
+ local ignore = runner(parcontext)
+ stoptiming(builders)
+ return ignore
+ end
+ end
+
+ callbacks.register("paragraph_context",parcontext,"when the context is dealt with")
+
+end
+
+-- This means that we now have most callbacks in use, even the ones that I'm not sure
+-- about. It also means that with the above enabled we might have performance now at
+-- its worst. I can optimize this a little but it's not worth the effort (and added
+-- complication).
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-pro.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-pro.lmt
new file mode 100644
index 00000000000..e736f2b76b6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-pro.lmt
@@ -0,0 +1,219 @@
+if not modules then modules = { } end modules ['node-pro'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local trace_callbacks = false trackers .register("nodes.callbacks", function(v) trace_callbacks = v end)
+local force_processors = false directives.register("nodes.processors.force", function(v) force_processors = v end)
+
+local report_nodes = logs.reporter("nodes","processors")
+
+local nodes = nodes
+local tasks = nodes.tasks
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+
+nodes.processors = nodes.processors or { }
+local processors = nodes.processors
+
+-- vbox: grouptype: vbox vtop output split_off split_keep | box_type: exactly|aditional
+-- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode) | box_type: exactly|aditional
+
+local actions = tasks.actions("processors")
+
+do
+
+ local isglyph = nuts.isglyph
+ local getnext = nuts.getnext
+
+ local utfchar = utf.char
+ local concat = table.concat
+
+ local n = 0
+
+ local function reconstruct(head) -- we probably have a better one
+ local t, n, h = { }, 0, head
+ while h do
+ n = n + 1
+ local char, id = isglyph(h)
+ if char then -- todo: disc etc
+ t[n] = utfchar(char)
+ else
+ t[n] = "[]"
+ end
+ h = getnext(h)
+ end
+ return concat(t)
+ end
+
+ function processors.tracer(what,head,groupcode,before,after,show)
+ if not groupcode then
+ groupcode = "unknown"
+ elseif groupcode == "" then
+ groupcode = "mvl"
+ end
+ n = n + 1
+ if show then
+ report_nodes("%s: location %a, group %a, # before %a, # after %s, stream: %s",what,n,groupcode,before,after,reconstruct(head))
+ else
+ report_nodes("%s: location %a, group %a, # before %a, # after %s",what,n,groupcode,before,after)
+ end
+ end
+
+end
+
+processors.enabled = true -- this will become a proper state (like trackers)
+
+do
+
+ -- local has_glyph = nodes.has_glyph
+ local count_nodes = nodes.countall
+
+ local texget = tex.get
+
+ local tracer = processors.tracer
+
+ -- We've set \hlistcallbackmode=1 so glyph checking happens at the other end!
+
+ local function pre_linebreak_filter(head,groupcode)
+ -- local found = force_processors or has_glyph(head)
+ -- if found then
+ if trace_callbacks then
+ local before = count_nodes(head,true)
+ head = actions(head,groupcode)
+ local after = count_nodes(head,true)
+ tracer("pre_linebreak",head,groupcode,before,after,true)
+ else
+ head = actions(head,groupcode)
+ end
+ -- elseif trace_callbacks then
+ -- local n = count_nodes(head,false)
+ -- tracer("pre_linebreak",head,groupcode,n,n)
+ -- end
+ return head
+ end
+
+ local function hpack_filter(head,groupcode,size,packtype,direction,attributes)
+ -- local found = force_processors or has_glyph(head)
+ -- if found then
+ --
+ -- yes or no or maybe an option
+ --
+ if not direction then
+ direction = texget("textdir")
+ end
+ --
+ if trace_callbacks then
+ local before = count_nodes(head,true)
+ head = actions(head,groupcode,size,packtype,direction,attributes)
+ local after = count_nodes(head,true)
+ tracer("hpack",head,groupcode,before,after,true)
+ else
+ head = actions(head,groupcode,size,packtype,direction,attributes)
+ end
+ -- elseif trace_callbacks then
+ -- local n = count_nodes(head,false)
+ -- tracer("hpack",head,groupcode,n,n)
+ -- end
+ return head
+ end
+
+ processors.pre_linebreak_filter = pre_linebreak_filter
+ processors.hpack_filter = hpack_filter
+
+ do
+
+ local hpack = nodes.hpack
+
+ function nodes.fullhpack(head,...)
+ return hpack((hpack_filter(head)),...)
+ end
+
+ end
+
+ do
+
+ local hpack = nuts.hpack
+
+ function nuts.fullhpack(head,...)
+ return hpack(tonut(hpack_filter(tonode(head))),...)
+ end
+
+ end
+
+ callbacks.register('pre_linebreak_filter', pre_linebreak_filter, "horizontal manipulations (before par break)")
+ callbacks.register('hpack_filter' , hpack_filter, "horizontal manipulations (before hbox creation)")
+
+end
+
+do
+ -- Beware, these are packaged boxes so no first_glyph test needed. Maybe some day I'll add a hash
+ -- with valid groupcodes. Watch out, much can pass twice, for instance vadjust passes two times,
+
+ local actions = tasks.actions("finalizers") -- head, where
+ local count_nodes = nodes.countall
+
+ local tracer = processors.tracer
+
+ local function post_linebreak_filter(head,groupcode)
+ if trace_callbacks then
+ local before = count_nodes(head,true)
+ head = actions(head,groupcode)
+ local after = count_nodes(head,true)
+ tracer("post_linebreak",head,groupcode,before,after,true)
+ else
+ head = actions(head,groupcode)
+ end
+ return head
+ end
+
+ processors.post_linebreak_filter = post_linebreak_filter
+
+ callbacks.register("post_linebreak_filter", post_linebreak_filter,"horizontal manipulations (after par break)")
+
+end
+
+do
+
+ ----- texnest = tex.nest
+ local getnest = tex.getnest
+
+ local getlist = nuts.getlist
+ local setlist = nuts.setlist
+ local getsubtype = nuts.getsubtype
+
+ local linelist_code = nodes.listcodes.line
+
+ local actions = tasks.actions("contributers")
+
+ function processors.contribute_filter(groupcode)
+ if groupcode == "box" then -- "pre_box"
+ local whatever = getnest()
+ if whatever then
+ local line = whatever.tail
+ if line then
+ line = tonut(line)
+ if getsubtype(line) == linelist_code then
+ local head = getlist(line)
+ if head then
+ local result = actions(head,groupcode,line)
+ if result and result ~= head then
+ setlist(line,result)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ callbacks.register("contribute_filter", processors.contribute_filter,"things done with lines")
+
+end
+
+statistics.register("h-node processing time", function()
+ return statistics.elapsedseconds(nodes,"including kernel") -- hm, ok here?
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-ref.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-ref.lmt
new file mode 100644
index 00000000000..c02a37dd107
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-ref.lmt
@@ -0,0 +1,831 @@
+if not modules then modules = { } end modules ['node-ref'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to node-ref.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We supported pdf right from the start and in mkii this has resulted in
+-- extensive control over the links. Nowadays pdftex provides a lot more
+-- control over margins but as mkii supports multiple backends we stuck to
+-- our own mechanisms. In mkiv again we implement our own handling. Eventually
+-- we will even disable the pdf primitives.
+
+-- helper, will end up in luatex
+
+-- is grouplevel still used?
+
+local tonumber = tonumber
+local concat = table.concat
+
+local attributes, nodes, node = attributes, nodes, node
+
+local allocate = utilities.storage.allocate, utilities.storage.mark
+local mark = utilities.storage.allocate, utilities.storage.mark
+
+local nodeinjections = backends.nodeinjections
+local codeinjections = backends.codeinjections
+
+local cleanupreferences = false
+local cleanupdestinations = true
+
+local transparencies = attributes.transparencies
+local colors = attributes.colors
+local references = structures.references
+local enableaction = nodes.tasks.enableaction
+
+local trace_references = false trackers.register("nodes.references", function(v) trace_references = v end)
+local trace_destinations = false trackers.register("nodes.destinations", function(v) trace_destinations = v end)
+local trace_areas = false trackers.register("nodes.areas", function(v) trace_areas = v end)
+local show_references = false trackers.register("nodes.references.show", function(v) show_references = tonumber(v) or (v and 2.25 or false) end)
+local show_destinations = false trackers.register("nodes.destinations.show", function(v) show_destinations = tonumber(v) or (v and 2.00 or false) end)
+
+local report_reference = logs.reporter("backend","references")
+local report_destination = logs.reporter("backend","destinations")
+local report_area = logs.reporter("backend","areas")
+
+local texsetcount = tex.setcount
+----- texsetattribute = tex.setattribute
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+local tonode = nuts.tonode
+local tonut = nuts.tonut
+
+local setlink = nuts.setlink
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+local getheight = nuts.getheight
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getsubtype = nuts.getsubtype
+local getwhd = nuts.getwhd
+local getdirection = nuts.getdirection
+local setshift = nuts.setshift
+local getboxglue = nuts.getboxglue
+
+local hpack_list = nuts.hpack
+local vpack_list = nuts.vpack
+local getdimensions = nuts.dimensions
+local getrangedimensions = nuts.rangedimensions
+local traverse = nuts.traverse
+local find_node_tail = nuts.tail
+local start_of_par = nuts.start_of_par
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+local listcodes = nodes.listcodes
+
+local dirvalues = nodes.dirvalues
+local lefttoright_code = dirvalues.lefttoright
+local righttoleft_code = dirvalues.righttoleft
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local glyph_code = nodecodes.glyph
+local rule_code = nodecodes.rule
+local dir_code = nodecodes.dir
+local par_code = nodecodes.par
+
+local leftskip_code = gluecodes.leftskip
+local rightskip_code = gluecodes.rightskip
+local parfillleftskip_code = gluecodes.parfillleftskip
+local parfillskip_code = gluecodes.parfillskip
+
+----- linelist_code = listcodes.line
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+local new_hlist = nodepool.hlist
+
+local flush_node = nuts.flush
+
+local tosequence = nodes.tosequence
+
+local implement = interfaces.implement
+
+-- Normally a (destination) area is a box or a simple stretch if nodes but when it is
+-- a paragraph we have a problem: we cannot calculate the height well. This happens
+-- with footnotes or content broken across a page.
+
+-- skip is somewhat messy
+-- when we have a line line we can look at the next line and make a shape
+-- see dimensions: this is tricky with split off boxes like inserts
+-- where we can end up with a first and last spanning lines so maybe
+-- we need to do vlists differently
+-- no need for dir here if we inject in the right spot as then we can
+-- pick up the dir elsewhere (in lmtx)
+
+local inject_areas do
+
+ local function hlist_dimensions(start,stop,parent)
+ local last = stop and getnext(stop)
+ if parent then
+ return getrangedimensions(parent,start,last)
+ else
+ return getdimensions(start,last)
+ end
+ end
+
+ local function vlist_dimensions(start,stop) -- also needs the stretch and so
+ local temp
+ if stop then
+ temp = getnext(stop)
+ setnext(stop,nil)
+ end
+ local v = vpack_list(start) -- use helper but happens seldom anyway so ...
+ local w, h, d = getwhd(v)
+ setlist(v) -- not needed
+ flush_node(v)
+ if temp then
+ setnext(stop,temp)
+ end
+ return w, h, d
+ end
+
+ -- not ok when vlist at mvl level
+
+ local function dimensions(parent,start,stop) -- in principle we could move some to the caller
+ if start == stop then
+ local id = getid(start)
+ if id == hlist_code or id == vlist_code or id == rule_code or id == glyph_code then
+ local sw, sh, sd = getwhd(start)
+ local pw, ph, pd = getwhd(parent)
+ local ht = sh == 0 and ph or sh -- changed
+ local dp = sd == 0 and pd or sd -- changed
+ if trace_areas then
+ report_area("dimensions taken of %a (%p,%p,%p) with parent (%p,%p,%p) -> (%p,%p,%p)",
+ nodecodes[id],sw,sh,sd,pw,ph,pd,sw,ht,dp)
+ end
+ return sw, ht, dp
+ else
+ if trace_areas then
+ report_area("dimensions calculated of %a",nodecodes[id])
+ end
+ return hlist_dimensions(start,stop) -- one node only so simple
+ end
+ elseif parent then
+ -- todo: if no prev and no next and parent
+ -- todo: we need a a list_dimensions for a vlist
+ if getid(parent) == vlist_code then
+ --
+ -- we can as well calculate here because we only have kerns and glue
+ --
+ local last = stop and getnext(stop)
+ local first = nil
+ local last = nil
+ local current = start
+ local noflines = 0
+ while current do -- can be loop
+ local id = getid(current)
+ if id == hlist_code or id == vlist_code or id == rule_code then
+ if noflines == 0 then
+ first = current
+ noflines = 1
+ else
+ noflines = noflines + 1
+ end
+ last = current
+ end
+ if current == stop then
+ break
+ else
+ current = getnext(current)
+ end
+ end
+ if noflines > 1 then
+ if trace_areas then
+ report_area("dimensions taken of vlist")
+ end
+ local w, h, d = vlist_dimensions(first,last,parent)
+ local ht = getheight(first)
+ return w, ht, d + h - ht, first
+ elseif first then
+ if trace_areas then
+ report_area("dimensions taken of first line in vlist")
+ end
+ local w, h, d = getwhd(first)
+ return w, h, d, first
+ else
+ if trace_areas then
+ report_area("dimensions taken of vlist (probably wrong)")
+ end
+ return hlist_dimensions(start,stop,parent)
+ end
+ else
+ if trace_areas then
+ report_area("dimensions taken of range starting with %a using parent",nodecodes[getid(start)])
+ end
+ return hlist_dimensions(start,stop,parent)
+ end
+ else
+ if trace_areas then
+ report_area("dimensions taken of range starting with %a",nodecodes[getid(start)])
+ end
+ return hlist_dimensions(start,stop)
+ end
+ end
+
+ -- Setting these once and not passign them each nested call is faster and this injector is
+ -- actually one of the more costly calls in a run (when we have lots of references to
+ -- check) and it's also a bti less code.
+
+ local attribute, make, stack, done
+
+ local function inject_range(head,first,last,reference,parent,pardir,txtdir)
+ local width, height, depth, line = dimensions(parent,first,last)
+ if txtdir == righttoleft_code then
+ width = - width
+ elseif txtdir == lefttoright_code then
+ -- go on
+ elseif pardir == righttoleft_code then
+ width = - width
+ end
+ local result, resolved = make(width,height,depth,reference)
+ if result and resolved then
+ if line then
+ -- special case, we only treat the first line in a vlist
+ local l = getlist(line)
+ if trace_areas then
+ report_area("%s: %i : %s %s %s => w=%p, h=%p, d=%p","line",
+ reference,pardir or "?",txtdir or "?",
+ tosequence(l,nil,true),width,height,depth)
+ end
+ setlist(line,result)
+ setlink(result,l)
+ return head, last
+ elseif head == first then
+ if trace_areas then
+ report_area("%s: %i : %s %s %s => w=%p, h=%p, d=%p","head",
+ reference,pardir or "?",txtdir or "?",
+ tosequence(first,last,true),width,height,depth)
+ end
+ setlink(result,first)
+ return result, last
+ else
+ if trace_areas then
+ report_area("%s: %i : %s %s %s => w=%p, h=%p, d=%p","middle",
+ reference,pardir or "?",txtdir or "?",
+ tosequence(first,last,true),width,height,depth)
+ end
+ if first == last and getid(parent) == vlist_code and getid(first) == hlist_code then
+ if trace_areas then
+ -- think of a button without \dontleavehmode in the mvl
+ report_area("compensating for link in vlist")
+ end
+ setlink(result,getlist(first))
+ setlist(first,result)
+ else
+ setlink(getprev(first),result,first)
+ end
+ return head, last
+ end
+ else
+ return head, last
+ end
+ end
+
+ -- Somehow this one no longer got used. See node-ref.lua for the implementation.
+ --
+ -- local function inject_list(id,current,reference,pardir,txtdir)
+ -- end
+
+ local function inject(head,skip,parent,pardir,txtdir)
+ local first, last, firstdir, reference
+ local current = head
+ while current do
+ local id = getid(current)
+ if id == hlist_code or id == vlist_code then
+ local r = getattr(current,attribute)
+ -- test \goto{test}[page(2)] test \gotobox{test}[page(2)]
+ -- test \goto{\TeX}[page(2)] test \gotobox{\hbox {x} \hbox {x}}[page(2)]
+ if r then
+ if not reference then
+ reference = r
+ first = current
+ last = current
+ firstdir = txtdir
+ elseif r == reference then
+ -- same link
+ last = current
+ elseif (done[reference] or 0) == 0 then
+ if not skip or r > skip then -- maybe no > test
+ head, current = inject_range(head,first,last,reference,parent,pardir,firstdir)
+ reference = nil
+ first = nil
+ last = nil
+ firstdir = nil
+ end
+ else
+ reference = r
+ first = current
+ last = current
+ firstdir = txtdir
+ end
+ done[r] = (done[r] or 0) + 1
+ end
+ local list = getlist(current)
+ if list then
+ local h
+ h, pardir, txtdir = inject(list,r or skip or 0,current,pardir,txtdir)
+ if h ~= current then
+ setlist(current,h)
+ end
+ end
+ if r then
+ done[r] = done[r] - 1
+ end
+ goto NEXT
+ elseif id == dir_code then
+ local direction, pop = getdirection(current)
+ txtdir = not pop and direction -- we might need a stack
+ goto NEXT
+ elseif id == par_code then
+ if start_of_par(current) then
+ pardir = getdirection(current)
+ end
+ goto NEXT
+ elseif id == glue_code then
+ local subtype = getsubtype(current)
+ if subtype == leftskip_code or subtype == parfillleftskip_code then
+ goto NEXT
+ elseif subtype == rightskip_code or subtype == parfillskip_code then
+ if reference and (done[reference] or 0) == 0 then
+ head, current = inject_range(head,first,last,reference,parent,pardir,firstdir)
+ reference = nil
+ first = nil
+ last = nil
+ firstdir = nil
+ end
+ goto NEXT
+ end
+ end
+ do
+ local r = getattr(current,attribute)
+ if not r then
+ -- just go on, can be kerns
+ elseif not reference then
+ reference = r
+ first = current
+ last = current
+ firstdir = txtdir
+ elseif r == reference then
+ last = current
+ elseif (done[reference] or 0) == 0 then -- or (id == glue_code and getsubtype(current) == right_skip_code) then
+ if not skip or r > skip then -- maybe no > test
+ head, current = inject_range(head,first,last,reference,parent,pardir,firstdir)
+ reference = nil
+ first = nil
+ last = nil
+ firstdir = nil
+ else
+-- print("!!!!!!!!")
+ end
+ else
+ -- todo: safeguard when we have a glyph or dics or ... so we might as well
+ -- then scan for all possibel content nodes
+ reference = r
+ first = current
+ last = current
+ firstdir = txtdir
+ end
+ end
+ ::NEXT::
+ current = getnext(current)
+ end
+ if reference and (done[reference] or 0) == 0 then
+ head = inject_range(head,first,last,reference,parent,pardir,firstdir)
+ end
+ return head, pardir, txtdir
+ end
+
+ inject_areas = function(head,a,m,s,d)
+ attribute = a
+ make = m
+ stack = s
+ done = d
+ return (inject(head))
+ end
+
+end
+
+-- tracing: todo: use predefined colors
+
+local colorize, justadd do
+
+ local u_transparency = nil
+ local u_colors = { }
+ local force_gray = true
+
+ local register_color = colors.register
+
+ local setcoloring = nuts.colors.set
+
+ local function addstring(what,str,shift) --todo make a pluggable helper (in font-ctx)
+ if str then
+ local typesetters = nuts.typesetters
+ if typesetters then
+ local hashes = fonts.hashes
+ local infofont = fonts.infofont()
+ local emwidth = hashes.emwidths [infofont]
+ local exheight = hashes.exheights[infofont]
+ if what == "reference" then
+ str = str .. " "
+ shift = - (shift or 2.25) * exheight
+ else
+ str = str .. " "
+ shift = (shift or 2) * exheight
+ end
+ local text = typesetters.tohpack(str,infofont)
+ local rule = new_rule(emwidth/5,4*exheight,3*exheight)
+ setshift(text,shift)
+ return hpack_list(setlink(text,rule))
+ end
+ end
+ end
+
+ colorize = function(width,height,depth,n,reference,what,sr,offset)
+ if force_gray then
+ n = 0
+ end
+ u_transparency = u_transparency or transparencies.register(nil,2,.65)
+ local ucolor = u_colors[n]
+ if not ucolor then
+ if n == 1 then
+ u_color = register_color(nil,'rgb',.75,0,0)
+ elseif n == 2 then
+ u_color = register_color(nil,'rgb',0,.75,0)
+ elseif n == 3 then
+ u_color = register_color(nil,'rgb',0,0,.75)
+ else
+ n = 0
+ u_color = register_color(nil,'gray',.5)
+ end
+ u_colors[n] = u_color
+ end
+ if width == 0 then
+ -- probably a strut as placeholder
+ report_area("%s %s has no %s dimensions, width %p, height %p, depth %p",what,reference,"horizontal",width,height,depth)
+ width = 65536
+ end
+ if height + depth <= 0 then
+ report_area("%s %s has no %s dimensions, width %p, height %p, depth %p",what,reference,"vertical",width,height,depth)
+ height = 65536/2
+ depth = height
+ end
+ local rule = setcoloring(new_rule(width,height,depth),1,u_color,u_transparency) -- gray color model
+ if width < 0 then
+ local kern = new_kern(width)
+ setwidth(rule,-width)
+ setnext(kern,rule)
+ setprev(rule,kern)
+ return kern
+ elseif sr and sr ~= "" then
+ local text = addstring(what,sr,shift)
+ if text then
+ local kern = new_kern(-getwidth(text))
+ setlink(kern,text,rule)
+ return kern
+ end
+ end
+ return rule
+ end
+
+ justadd = function(what,sr,shift,current) -- needs testing
+ if sr and sr ~= "" then
+ local text = addstring(what,sr,shift)
+ if text then
+ local kern = new_kern(-getwidth(text))
+ setlink(kern,text,current)
+ return new_hlist(kern)
+ end
+ end
+ end
+
+end
+
+local nofreferences = 0
+
+do
+
+ local stack = { }
+ local done = { }
+ local attribute = attributes.private('reference')
+ local topofstack = 0
+
+ nodes.references = {
+ attribute = attribute,
+ stack = stack,
+ done = done,
+ }
+
+ -- todo: get rid of n (n is just a number, can be used for tracing, obsolete)
+
+ local function setreference(h,d,r) -- h and d can be nil
+ topofstack = topofstack + 1
+ -- the preroll permits us to determine samepage (but delayed also has some advantages)
+ -- so some part of the backend work is already done here
+ stack[topofstack] = { r, h or false, d or false, codeinjections.prerollreference(r) }
+ -- texsetattribute(attribute,topofstack) -- todo -> at tex end
+ texsetcount("lastreferenceattribute",topofstack)
+ end
+
+ function references.get(n) -- not public so functionality can change
+ local sn = stack[n]
+ return sn and sn[1]
+ end
+
+ local function makereference(width,height,depth,reference) -- height and depth are of parent
+ local sr = stack[reference]
+ if sr then
+ if trace_references then
+ report_reference("resolving attribute %a",reference)
+ end
+ local resolved = sr[1]
+ local ht = sr[2]
+ local dp = sr[3]
+ local set = sr[4]
+ local n = sr[5]
+ -- logs.report("temp","child: ht=%p dp=%p, parent: ht=%p dp=%p",ht,dp,height,depth)
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ -- logs.report("temp","used: ht=%p dp=%p",height,depth)
+ local annot = nodeinjections.reference(width,height,depth,set,resolved.mesh)
+ if annot then
+ annot = tonut(annot) -- todo
+ nofreferences = nofreferences + 1
+ local result, current, texts
+ if show_references then
+ local d = resolved
+ if d then
+ local r = d.reference
+ local p = d.prefix
+ if r then
+ if p then
+ texts = p .. "|" .. r
+ else
+ texts = r
+ end
+ else
+ -- t[#t+1] = d.internal or "?"
+ end
+ end
+ end
+ if trace_references then
+ local step = 65536
+ result = new_hlist(colorize(width,height-step,depth-step,2,reference,"reference",texts,show_references)) -- step subtracted so that we can see seperate links
+ current = result
+ elseif texts then
+ texts = justadd("reference",texts,show_references,current)
+ if texts then
+ current = texts
+ end
+ end
+ if current then
+ setlink(current,annot)
+ else
+ result = annot
+ end
+ references.registerpage(n)
+ result = new_hlist(result)
+ if cleanupreferences then stack[reference] = nil end
+ return result, resolved
+ elseif trace_references then
+ report_reference("unable to resolve annotation %a",reference)
+ end
+ elseif trace_references then
+ report_reference("unable to resolve attribute %a",reference)
+ end
+ end
+
+ function nodes.references.handler(head)
+ if head and topofstack > 0 then
+ return inject_areas(head,attribute,makereference,stack,done)
+ else
+ return head
+ end
+ end
+
+ function references.inject(prefix,reference,specification) -- todo: use currentreference is possible
+ local set, bug = references.identify(prefix,reference)
+ if bug or #set == 0 then
+ -- unknown ref, just don't set it and issue an error
+ else
+ set.highlight = specification.highlight
+ set.newwindow = specification.newwindow
+ set.layer = specification.layer
+ setreference(specification.height,specification.depth,set) -- sets attribute / todo: for set[*].error
+ end
+ end
+
+ -- function references.injectinternal(internal,specification)
+ -- references.inject("","internal("..internal..")",specification)
+ -- if bug or #set == 0 then
+ -- -- unknown ref, just don't set it and issue an error
+ -- else
+ -- -- nil prefix when ""
+ -- -- check
+ -- set.highlight = specification.highlight
+ -- set.newwindow = specification.newwindow
+ -- set.layer = specification.layer
+ -- setreference(specification.height,specification.depth,set) -- sets attribute / todo: for set[*].error
+ -- end
+ -- end
+
+ function references.injectcurrentset(h,d) -- used inside doifelse
+ local currentset = references.currentset
+ if currentset then
+ setreference(h,d,currentset) -- sets attribute / todo: for set[*].error
+ end
+ end
+
+end
+
+local nofdestinations = 0
+
+do
+
+ local stack = { }
+ local done = { }
+ local attribute = attributes.private('destination')
+ local topofstack = 0
+
+ nodes.destinations = {
+ attribute = attribute,
+ stack = stack,
+ done = done,
+ }
+
+ local function setdestination(n,h,d,name,view) -- n = grouplevel, name == table
+ topofstack = topofstack + 1
+ stack[topofstack] = { n, h, d, name, view }
+ return topofstack
+ end
+
+ local function makedestination(width,height,depth,reference)
+ local sr = stack[reference]
+ if sr then
+ if trace_destinations then
+ report_destination("resolving attribute %a",reference)
+ end
+ local resolved = sr[1]
+ local ht = sr[2]
+ local dp = sr[3]
+ local name = sr[4]
+ local view = sr[5]
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ local result, current, texts
+ if show_destinations then
+ if name and #name > 0 then
+ local t = { }
+ for i=1,#name do
+ local s = name[i]
+ if type(s) == "number" then
+ local d = references.internals[s]
+ if d then
+ d = d.references
+ local r = d.reference
+ local p = d.usedprefix
+ if r then
+ if p then
+ t[#t+1] = p .. "|" .. r
+ else
+ t[#t+1] = r
+ end
+ else
+ -- t[#t+1] = d.internal or "?"
+ end
+ end
+ else
+ -- in fact we have a prefix:name here
+ end
+ end
+ if #t > 0 then
+ texts = concat(t," & ")
+ end
+ end
+ end
+ if trace_destinations then
+ local step = 0
+ if width == 0 then
+ step = 4*65536
+ width, height, depth = 5*step, 5*step, 0
+ end
+ local rule = new_hlist(colorize(width,height,depth,3,reference,"destination",texts,show_destinations))
+ if not result then
+ result, current = rule, rule
+ else
+ setlink(current,rule)
+ current = rule
+ end
+ width, height = width - step, height - step
+ elseif texts then
+ texts = justadd("destination",texts,show_destinations,current)
+ if texts then
+ current = texts
+ end
+ end
+ nofdestinations = nofdestinations + 1
+ local annot = nodeinjections.destination(width,height,depth,name,view)
+ if annot then
+ annot = tonut(annot) -- obsolete soon
+ if result then
+ setlink(current,annot)
+ else
+ result = annot
+ end
+ current = find_node_tail(annot)
+ end
+ if result then
+ result = new_hlist(result)
+ end
+ if cleanupdestinations then stack[reference] = nil end
+ return result, resolved
+ elseif trace_destinations then
+ report_destination("unable to resolve attribute %a",reference)
+ end
+ end
+
+ function nodes.destinations.handler(head)
+ if head and topofstack > 0 then
+ return inject_areas(head,attribute,makedestination,stack,done)
+ else
+ return head
+ end
+ end
+
+ -- will move
+
+ function references.mark(reference,h,d,view)
+ return setdestination(tex.currentgrouplevel,h,d,reference,view)
+ end
+
+end
+
+implement {
+ name = "injectreference",
+ actions = references.inject,
+ arguments = {
+ "string",
+ "string",
+ {
+ { "highlight", "boolean" },
+ { "newwindow", "boolean" },
+ { "layer" },
+ { "height", "dimen" },
+ { "depth", "dimen" },
+ { "view" },
+ }
+ }
+}
+
+-- implement {
+-- name = "injectinternalreference",
+-- actions = references.injectinternal,
+-- arguments = {
+-- "integer",
+-- {
+-- { "highlight", "boolean" },
+-- { "newwindow", "boolean" },
+-- { "layer" },
+-- { "height", "dimen" },
+-- { "depth", "dimen" },
+-- { "view" },
+-- }
+-- }
+-- }
+
+implement {
+ name = "injectcurrentreference",
+ actions = references.injectcurrentset,
+}
+
+implement {
+ name = "injectcurrentreferencehtdp",
+ actions = references.injectcurrentset,
+ arguments = { "dimen", "dimen" },
+}
+
+statistics.register("interactive elements", function()
+ if nofreferences > 0 or nofdestinations > 0 then
+ return string.format("%s references, %s destinations",nofreferences,nofdestinations)
+ else
+ return nil
+ end
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-res.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-res.lmt
new file mode 100644
index 00000000000..fbb1d1fe235
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-res.lmt
@@ -0,0 +1,563 @@
+if not modules then modules = { } end modules ['node-res'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next, rawset = type, next, rawset
+local gmatch, format = string.gmatch, string.format
+
+--[[ldx--
+<p>The next function is not that much needed but in <l n='context'/> we use
+for debugging <l n='luatex'/> node management.</p>
+--ldx]]--
+
+local nodes, node = nodes, node
+
+local report_nodes = logs.reporter("nodes","housekeeping")
+
+nodes.pool = nodes.pool or { }
+local nodepool = nodes.pool
+
+local gluecodes = nodes.gluecodes
+local kerncodes = nodes.kerncodes
+local rulecodes = nodes.rulecodes
+local nodecodes = nodes.nodecodes
+local boundarycodes = nodes.boundarycodes
+local usercodes = nodes.usercodes
+
+local nodeproperties = nodes.properties.data
+
+local glyph_code = nodecodes.glyph
+local rule_code = nodecodes.rule
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local gluespec_code = nodecodes.gluespec
+
+local currentfont = font.current
+local texgetcount = tex.getcount
+
+local allocate = utilities.storage.allocate
+
+local reserved = { }
+local nofreserved = 0
+
+-- nuts overload
+
+local nuts = nodes.nuts
+local nutpool = { }
+nuts.pool = nutpool
+
+local tonut = nuts.tonut
+local tonode = nuts.tonode
+
+local getbox = nuts.getbox
+local getid = nuts.getid
+local getlist = nuts.getlist
+local getglue = nuts.getglue
+
+local setfield = nuts.setfield
+local setchar = nuts.setchar
+local setlist = nuts.setlist
+local setwhd = nuts.setwhd
+local setglue = nuts.setglue
+local setdisc = nuts.setdisc
+local setfont = nuts.setfont
+local setkern = nuts.setkern
+local setpenalty = nuts.setpenalty
+local setdir = nuts.setdir
+local setdirection = nuts.setdirection
+local setshift = nuts.setshift
+local setwidth = nuts.setwidth
+local setsubtype = nuts.setsubtype
+local setleader = nuts.setleader
+
+local setdata = nuts.setdata
+local setruledata = nuts.setruledata
+local setvalue = nuts.setvalue
+
+local copy_nut = nuts.copy_only or nuts.copy
+local new_nut = nuts.new
+local flush_nut = nuts.flush
+
+-- at some point we could have a dual set (the overhead of tonut is not much larger than
+-- metatable associations at the lua/c end esp if we also take assignments into account
+
+-- table.setmetatableindex(nodepool,function(t,k,v)
+-- -- report_nodes("defining nodepool[%s] instance",k)
+-- local f = nutpool[k]
+-- local v = function(...)
+-- return tonode(f(...))
+-- end
+-- t[k] = v
+-- return v
+-- end)
+--
+-- -- we delay one step because that permits us a forward reference
+-- -- e.g. in pdfsetmatrix
+
+table.setmetatableindex(nodepool,function(t,k,v)
+ -- report_nodes("defining nodepool[%s] instance",k)
+ local v = function(...)
+ local f = nutpool[k]
+ local v = function(...)
+ return tonode(f(...))
+ end
+ t[k] = v
+ return v(...)
+ end
+ t[k] = v
+ return v
+end)
+
+local function register_nut(n)
+ nofreserved = nofreserved + 1
+ reserved[nofreserved] = n
+ return n
+end
+
+local function register_node(n)
+ nofreserved = nofreserved + 1
+ if type(n) == "number" then -- isnut(n)
+ reserved[nofreserved] = n
+ else
+ reserved[nofreserved] = tonut(n)
+ end
+ return n
+end
+
+nodepool.register = register_node
+nutpool.register = register_node -- could be register_nut
+
+-- so far
+
+local disc = register_nut(new_nut(nodecodes.disc))
+local kern = register_nut(new_nut(kern_code,kerncodes.userkern))
+local fontkern = register_nut(new_nut(kern_code,kerncodes.fontkern))
+local italickern = register_nut(new_nut(kern_code,kerncodes.italiccorrection))
+local penalty = register_nut(new_nut(nodecodes.penalty))
+local glue = register_nut(new_nut(glue_code))
+local gluespec = register_nut(new_nut(gluespec_code))
+local glyph = register_nut(new_nut(glyph_code,0))
+
+local textdir = register_nut(new_nut(nodecodes.dir))
+
+local left_margin_kern = register_nut(new_nut(kern_code,kerncodes.leftmargincode))
+local right_margin_kern = register_nut(new_nut(kern_code,kerncodes.rightmargincode))
+
+local lineskip = register_nut(new_nut(glue_code,gluecodes.lineskip))
+local baselineskip = register_nut(new_nut(glue_code,gluecodes.baselineskip))
+local leftskip = register_nut(new_nut(glue_code,gluecodes.leftskip))
+local rightskip = register_nut(new_nut(glue_code,gluecodes.rightskip))
+local lefthangskip = register_nut(new_nut(glue_code,gluecodes.lefthangskip))
+local righthangskip = register_nut(new_nut(glue_code,gluecodes.righthangskip))
+local indentskip = register_nut(new_nut(glue_code,gluecodes.indentskip))
+local correctionskip = register_nut(new_nut(glue_code,gluecodes.correctionskip))
+
+local temp = register_nut(new_nut(nodecodes.temp,0))
+
+local noad = register_nut(new_nut(nodecodes.noad))
+local delimiter = register_nut(new_nut(nodecodes.delimiter))
+local fence = register_nut(new_nut(nodecodes.fence))
+local submlist = register_nut(new_nut(nodecodes.submlist))
+local accent = register_nut(new_nut(nodecodes.accent))
+local radical = register_nut(new_nut(nodecodes.radical))
+local fraction = register_nut(new_nut(nodecodes.fraction))
+local subbox = register_nut(new_nut(nodecodes.subbox))
+local mathchar = register_nut(new_nut(nodecodes.mathchar))
+local mathtextchar = register_nut(new_nut(nodecodes.mathtextchar))
+local choice = register_nut(new_nut(nodecodes.choice))
+
+local boundary = register_nut(new_nut(nodecodes.boundary,boundarycodes.user))
+local wordboundary = register_nut(new_nut(nodecodes.boundary,boundarycodes.word))
+
+local cleader = register_nut(copy_nut(glue)) setsubtype(cleader,gluecodes.cleaders) setglue(cleader,0,65536,0,2,0)
+
+-- the dir field needs to be set otherwise crash:
+
+local lefttoright_code = nodes.dirvalues.lefttoright
+
+local rule = register_nut(new_nut(rule_code)) -- setdirection(rule, lefttoright_code)
+local emptyrule = register_nut(new_nut(rule_code,rulecodes.empty)) -- setdirection(rule, lefttoright_code)
+local userrule = register_nut(new_nut(rule_code,rulecodes.user)) -- setdirection(rule, lefttoright_code)
+local outlinerule = register_nut(new_nut(rule_code,rulecodes.outline)) -- setdirection(rule, lefttoright_code)
+local hlist = register_nut(new_nut(nodecodes.hlist)) setdirection(hlist,lefttoright_code)
+local vlist = register_nut(new_nut(nodecodes.vlist)) setdirection(vlist,lefttoright_code)
+
+function nutpool.glyph(fnt,chr)
+ local n = copy_nut(glyph)
+ if fnt then
+ setfont(n,fnt == true and currentfont() or fnt,chr)
+ elseif chr then
+ setchar(n,chr)
+ end
+ return n
+end
+
+function nutpool.penalty(p)
+ local n = copy_nut(penalty)
+ if p and p ~= 0 then
+ setpenalty(n,p)
+ end
+ return n
+end
+
+function nutpool.kern(k)
+ local n = copy_nut(kern)
+ if k and k ~= 0 then
+ setkern(n,k)
+ end
+ return n
+end
+
+function nutpool.boundary(v)
+ local n = copy_nut(boundary)
+ if v and v ~= 0 then
+ setvalue(n,v)
+ end
+ return n
+end
+
+function nutpool.wordboundary(v)
+ local n = copy_nut(wordboundary)
+ if v and v ~= 0 then
+ setvalue(n,v)
+ end
+ return n
+end
+
+function nutpool.fontkern(k)
+ local n = copy_nut(fontkern)
+ if k and k ~= 0 then
+ setkern(n,k)
+ end
+ return n
+end
+
+function nutpool.italickern(k)
+ local n = copy_nut(italickern)
+ if k and k ~= 0 then
+ setkern(n,k)
+ end
+ return n
+end
+
+function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order)
+ local n = copy_nut(gluespec)
+ if width or stretch or shrink or stretch_order or shrink_order then
+ setglue(n,width,stretch,shrink,stretch_order,shrink_order)
+ end
+ return n
+end
+
+local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order)
+ -- maybe setglue
+ local n = copy_nut(skip)
+ if width or stretch or shrink or stretch_order or shrink_order then
+ setglue(n,width,stretch,shrink,stretch_order,shrink_order)
+ end
+ return n
+end
+
+function nutpool.stretch(a,b)
+ -- width stretch shrink stretch_order shrink_order
+ local n = copy_nut(glue)
+ if not b then
+ a, b = 1, a or 1
+ end
+ setglue(n,0,a,0,b,0)
+ return n
+end
+
+function nutpool.shrink(a,b)
+ local n = copy_nut(glue)
+ if not b then
+ a, b = 1, a or 1
+ end
+ setglue(n,0,0,a,0,0,b)
+ return n
+end
+
+function nutpool.glue(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(glue,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.negatedglue(glue)
+ local n = copy_nut(glue)
+ local width, stretch, shrink = getglue(n)
+ setglue(n,-width,-stretch,-shrink)
+ return n
+end
+
+function nutpool.leftskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.rightskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.lefthangskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(lefthangskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.righthangskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(righthangskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.indentskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(indentskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.lineskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function nutpool.baselineskip(width,stretch,shrink)
+ return someskip(baselineskip,width,stretch,shrink)
+end
+
+function nutpool.disc(pre,post,replace)
+ local d = copy_nut(disc)
+ if pre or post or replace then
+ setdisc(d,pre,post,replace)
+ end
+ return d
+end
+
+function nutpool.direction(dir,swap)
+ local t = copy_nut(textdir)
+ if not dir then
+ -- just a l2r start node
+ elseif swap then
+ setdirection(t,dir,true)
+ else
+ setdirection(t,dir,false)
+ end
+ return t
+end
+
+function nutpool.rule(width,height,depth) -- w/h/d == nil will let them adapt
+ local n = copy_nut(rule)
+ if width or height or depth then
+ setwhd(n,width,height,depth)
+ end
+ return n
+end
+
+function nutpool.emptyrule(width,height,depth) -- w/h/d == nil will let them adapt
+ local n = copy_nut(emptyrule)
+ if width or height or depth then
+ setwhd(n,width,height,depth)
+ end
+ return n
+end
+
+function nutpool.userrule(width,height,depth) -- w/h/d == nil will let them adapt
+ local n = copy_nut(userrule)
+ if width or height or depth then
+ setwhd(n,width,height,depth)
+ end
+ return n
+end
+
+function nutpool.outlinerule(width,height,depth,line) -- w/h/d == nil will let them adapt
+ local n = copy_nut(outlinerule)
+ if width or height or depth then
+ setwhd(n,width,height,depth)
+ end
+ if line then
+ setruledata(n,line)
+ end
+ return n
+end
+
+function nutpool.leader(width,list)
+ local n = copy_nut(cleader)
+ if width then
+ setwidth(n,width)
+ end
+ if list then
+ setleader(n,list)
+ end
+ return n
+end
+
+function nutpool.leftmarginkern(glyph,width)
+ local n = copy_nut(left_margin_kern)
+ if not glyph then
+ report_nodes("invalid pointer to left margin glyph node")
+ elseif getid(glyph) ~= glyph_code then
+ report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left")
+ else
+ setfield(n,"glyph",glyph)
+ end
+ if width and width ~= 0 then
+ setwidth(n,width)
+ end
+ return n
+end
+
+function nutpool.rightmarginkern(glyph,width)
+ local n = copy_nut(right_margin_kern)
+ if not glyph then
+ report_nodes("invalid pointer to right margin glyph node")
+ elseif getid(glyph) ~= glyph_code then
+ report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right")
+ else
+ setfield(n,"glyph",glyph)
+ end
+ if width and width ~= 0 then
+ setwidth(n,width)
+ end
+ return n
+end
+
+function nutpool.temp()
+ return copy_nut(temp)
+end
+
+function nutpool.noad() return copy_nut(noad) end
+function nutpool.delimiter() return copy_nut(delimiter) end nutpool.delim = nutpool.delimiter
+function nutpool.fence() return copy_nut(fence) end
+function nutpool.submlist() return copy_nut(submlist) end
+function nutpool.noad() return copy_nut(noad) end
+function nutpool.fence() return copy_nut(fence) end
+function nutpool.accent() return copy_nut(accent) end
+function nutpool.radical() return copy_nut(radical) end
+function nutpool.fraction() return copy_nut(fraction) end
+function nutpool.subbox() return copy_nut(subbox) end
+function nutpool.mathchar() return copy_nut(mathchar) end
+function nutpool.mathtextchar() return copy_nut(mathtextchar) end
+function nutpool.choice() return copy_nut(choice) end
+
+local function new_hlist(list,width,height,depth,shift)
+ local n = copy_nut(hlist)
+ if list then
+ setlist(n,list)
+ end
+ if width or height or depth then
+ setwhd(n,width,height,depth)
+ end
+ if shift and shift ~= 0 then
+ setshift(n,shift)
+ end
+ return n
+end
+
+local function new_vlist(list,width,height,depth,shift)
+ local n = copy_nut(vlist)
+ if list then
+ setlist(n,list)
+ end
+ if width or height or depth then
+ setwhd(n,width,height,depth)
+ end
+ if shift and shift ~= 0 then
+ setshift(n,shift)
+ end
+ return n
+end
+
+nutpool.hlist = new_hlist
+nutpool.vlist = new_vlist
+
+function nodepool.hlist(list,width,height,depth,shift)
+ return tonode(new_hlist(list and tonut(list),width,height,depth,shift))
+end
+
+function nodepool.vlist(list,width,height,depth,shift)
+ return tonode(new_vlist(list and tonut(list),width,height,depth,shift))
+end
+
+function nutpool.usernode(id,data)
+ local n = copy_nut(user_node)
+ nodeproperties[n] = {
+ id = id,
+ data = data,
+ }
+ return n
+end
+
+-- housekeeping
+
+local function cleanup(nofboxes) -- todo
+ local tracers = nodes.tracers
+ if tracers and tracers.steppers then -- to be resolved
+ tracers.steppers.reset() -- todo: make a registration subsystem
+ end
+ local nl = 0
+ local nr = nofreserved
+ for i=1,nofreserved do
+ local ri = reserved[i]
+ flush_nut(reserved[i])
+ end
+ if nofboxes then
+ for i=0,nofboxes do
+ local l = getbox(i)
+ if l then
+ flush_nut(l) -- also list ?
+ nl = nl + 1
+ end
+ end
+ end
+ reserved = { }
+ nofreserved = 0
+ return nr, nl, nofboxes -- can be nil
+end
+
+local usage = node.inuse
+local stock = node.instock
+
+nutpool .cleanup = cleanup
+nodepool.cleanup = cleanup
+
+nutpool .usage = usage
+nodepool.usage = usage
+
+nutpool .stock = stock
+nodepool.stock = stock
+
+-- end
+
+statistics.register("cleaned up reserved nodes", function()
+ return format("%s nodes, %s lists of %s", cleanup(texgetcount("c_syst_last_allocated_box")))
+end) -- \topofboxstack
+
+statistics.register("node memory usage", function() -- comes after cleanup !
+ local used = usage()
+ if next(used) then
+ local t, n = { }, 0
+ for k, v in table.sortedhash(used) do
+ n = n + 1 ; t[n] = format("%s %s",v,k)
+ end
+ return table.concat(t,", ")
+ end
+end)
+
+lua.registerfinalizer(cleanup, "cleanup reserved nodes")
+
+do
+
+ local glyph = glyph
+ local traverse_id = nuts.traverse_id
+
+ local traversers = table.setmetatableindex(function(t,k)
+ local v = traverse_id(type(k) == "number" and k or nodecodes[k],glyph)
+ t[k] = v
+ return v
+ end)
+
+ -- these are special:
+
+ traversers.node = nuts.traverse (glyph)
+ traversers.char = nuts.traverse_char (glyph)
+ traversers.glyph = nuts.traverse_glyph (glyph)
+ traversers.list = nuts.traverse_list (glyph)
+ traversers.content = nuts.traverse_content(glyph)
+
+ nuts.traversers = traversers
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-rul.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-rul.lmt
new file mode 100644
index 00000000000..a95c5272d36
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-rul.lmt
@@ -0,0 +1,725 @@
+if not modules then modules = { } end modules ['node-rul'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to node-rul.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this will go to an auxiliary module
+-- beware: rules now have a dir field
+--
+-- todo: make robust for layers ... order matters
+
+-- todo: collect successive bit and pieces and combine them
+--
+-- path s ; s := shaped(p) ; % p[] has rectangles
+-- fill s withcolor .5white ;
+-- draw boundingbox s withcolor yellow;
+
+local tonumber = tonumber
+
+local context = context
+local attributes = attributes
+local nodes = nodes
+local properties = nodes.properties.data
+
+local enableaction = nodes.tasks.enableaction
+
+local nuts = nodes.nuts
+local tonode = nuts.tonode
+local tonut = nuts.tonut
+
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local setlink = nuts.setlink
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getdirection = nuts.getdirection
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local setattrs = nuts.setattrs
+local getfont = nuts.getfont
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local setwhd = nuts.setwhd
+local setattrlist = nuts.setattrlist
+local setshift = nuts.setshift
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+local setoffsets = nuts.setoffsets
+local setfield = nuts.setfield
+local getruledata = nuts.getruledata
+
+local isglyph = nuts.isglyph
+
+local flushlist = nuts.flush_list
+local effective_glue = nuts.effective_glue
+local insert_node_after = nuts.insert_after
+local insert_node_before = nuts.insert_before
+local find_tail = nuts.tail
+local setglue = nuts.setglue
+local getrangedimensions = nuts.rangedimensions
+local hpack_nodes = nuts.hpack
+local copy_list = nuts.copy_list
+
+local nextlist = nuts.traversers.list
+local nextglue = nuts.traversers.glue
+
+local nodecodes = nodes.nodecodes
+local rulecodes = nodes.rulecodes
+local gluecodes = nodes.gluecodes
+local listcodes = nodes.listcodes
+
+local glyph_code = nodecodes.glyph
+local par_code = nodecodes.par
+local dir_code = nodecodes.dir
+local glue_code = nodecodes.glue
+local hlist_code = nodecodes.hlist
+
+local indentlist_code = listcodes.indent
+local linelist_code = listcodes.line
+
+local leftskip_code = gluecodes.leftskip
+local rightskip_code = gluecodes.rightskip
+local parfillleftskip_code = gluecodes.parfillleftskip
+local parfillrightskip_code = gluecodes.parfillrightskip
+local indentskip_code = gluecodes.indentskip
+
+local nodepool = nuts.pool
+
+local new_rule = nodepool.rule
+local new_userrule = nodepool.userrule
+local new_kern = nodepool.kern
+local new_leader = nodepool.leader
+
+local n_tostring = nodes.idstostring
+local n_tosequence = nodes.tosequence
+
+local variables = interfaces.variables
+local implement = interfaces.implement
+
+local privateattributes = attributes.private
+
+local a_ruled = privateattributes('ruled')
+local a_runningtext = privateattributes('runningtext')
+local a_color = privateattributes('color')
+local a_transparency = privateattributes('transparency')
+local a_colormodel = privateattributes('colormodel')
+local a_linefiller = privateattributes("linefiller")
+local a_viewerlayer = privateattributes("viewerlayer")
+
+local v_both = variables.both
+local v_left = variables.left
+local v_right = variables.right
+local v_local = variables["local"]
+local v_yes = variables.yes
+local v_foreground = variables.foreground
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontresources = fonthashes.resources
+
+local dimenfactor = fonts.helpers.dimenfactor
+local splitdimen = number.splitdimen
+local setmetatableindex = table.setmetatableindex
+
+local magicconstants = tex.magicconstants
+local running = magicconstants.running
+
+local striprange = nuts.striprange
+local processwords = nuts.processwords
+
+local setcoloring = nuts.colors.set
+
+local rules = nodes.rules or { }
+nodes.rules = rules
+rules.data = rules.data or { }
+
+local nutrules = nuts.rules or { }
+nuts.rules = nutrules -- not that many
+
+storage.register("nodes/rules/data", rules.data, "nodes.rules.data")
+
+local data = rules.data
+
+-- we implement user rules here as it takes less code this way
+
+local function usernutrule(t,noattributes)
+ local r = new_userrule(t.width or 0,t.height or 0,t.depth or 0)
+ if noattributes == false or noattributes == nil then
+ -- avoid fuzzy ones
+ else
+ setattrlist(r,true)
+ end
+ properties[r] = t
+ return r
+end
+
+nutrules.userrule = usernutrule
+
+local function userrule(t,noattributes)
+ return tonode(usernutrule(t,noattributes))
+end
+
+rules.userrule = userrule
+local ruleactions = { }
+
+rules .ruleactions = ruleactions
+nutrules.ruleactions = ruleactions -- convenient
+
+local function mathaction(n,h,v,what)
+ local font = getruledata(n)
+ local actions = fontresources[font].mathruleactions
+ if actions then
+ local action = actions[what]
+ if action then
+ action(n,h,v,font)
+ end
+ end
+end
+
+local function mathradical(n,h,v)
+ mathaction(n,h,v,"radicalaction")
+end
+
+local function mathrule(n,h,v)
+ mathaction(n,h,v,"hruleaction")
+end
+
+local x
+
+local function useraction(n,h,v)
+ local p = properties[n]
+ if p then
+ local i = p.type or "draw"
+ local a = ruleactions[i]
+ if a then
+ a(p,h,v,i,n)
+ end
+ end
+end
+
+local subtypeactions = {
+ [rulecodes.user] = useraction,
+ [rulecodes.over] = mathrule,
+ [rulecodes.under] = mathrule,
+ [rulecodes.fraction] = mathrule,
+ [rulecodes.radical] = mathradical,
+}
+
+function rules.process(n,h,v)
+ local n = tonut(n) -- already a nut
+ local s = getsubtype(n)
+ local a = subtypeactions[s]
+ if a then
+ a(n,h,v)
+ end
+end
+
+--
+
+local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end)
+local report_ruled = logs.reporter("nodes","rules")
+
+function rules.define(settings)
+ local nofdata = #data + 1
+ data[nofdata] = settings
+ local text = settings.text
+ if text then
+ local b = nuts.takebox(text)
+ if b then
+ nodepool.register(b)
+ settings.text = getlist(b)
+ else
+ settings.text = nil
+ end
+ end
+ return nofdata
+end
+
+local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose
+ local font = nil
+ local char, id = isglyph(f)
+ if char then
+ font = id
+ elseif id == hlist_code then
+ font = getattr(f,a_runningtext)
+ end
+ if not font then
+ -- saveguard ... we need to deal with rules and so (math)
+ return head
+ end
+ local r, m
+ if strip then
+ if trace_ruled then
+ local before = n_tosequence(f,l,true)
+ f, l = striprange(f,l)
+ local after = n_tosequence(f,l,true)
+ report_ruled("range stripper, before %a, after %a",before,after)
+ else
+ f, l = striprange(f,l)
+ end
+ end
+ if not f then
+ return head
+ end
+ local wd, ht, dp = getrangedimensions(parent,f,getnext(l))
+ local method = d.method
+ local empty = d.empty == v_yes
+ local offset = d.offset
+ local dy = d.dy
+ local order = d.order
+ local max = d.max
+ local mp = d.mp
+ local rulethickness = d.rulethickness
+ local unit = d.unit
+ local ma = d.ma
+ local ca = d.ca
+ local ta = d.ta
+ local colorspace = ma > 0 and ma or getattr(f,a_colormodel) or 1
+ local color = ca > 0 and ca or getattr(f,a_color)
+ local transparency = ta > 0 and ta or getattr(f,a_transparency)
+ local foreground = order == v_foreground
+ local layer = getattr(f,a_viewerlayer)
+ local e = dimenfactor(unit,font) -- what if no glyph node
+ local rt = tonumber(rulethickness)
+ if rt then
+ rulethickness = e * rulethickness / 2
+ else
+ local n, u = splitdimen(rulethickness)
+ if n and u then -- we need to intercept ex and em and % and ...
+ rulethickness = n * dimenfactor(u,fontdata[font]) / 2
+ else
+ rulethickness = 1/5
+ end
+ end
+ --
+ if level > max then
+ level = max
+ end
+ if method == 0 then -- center
+ offset = 2*offset
+ m = (offset+(level-1)*dy)*e/2 + rulethickness/2
+ else
+ m = 0
+ end
+
+ local function inject(r,wd,ht,dp)
+ if layer then
+ setattr(r,a_viewerlayer,layer)
+ end
+ if empty then
+ head = insert_node_before(head,f,r)
+ setlink(r,getnext(l))
+ setprev(f)
+ setnext(l)
+ flushlist(f)
+ else
+ local k = new_kern(-wd)
+ if foreground then
+ insert_node_after(head,l,k)
+ insert_node_after(head,k,r)
+ l = r
+ else
+ head = insert_node_before(head,f,r)
+ insert_node_after(head,r,k)
+ end
+ end
+ if trace_ruled then
+ report_ruled("level %a, width %p, height %p, depth %p, nodes %a, text %a",
+ level,wd,ht,dp,n_tostring(f,l),n_tosequence(f,l,true))
+ end
+ end
+
+ if mp and mp ~= "" then
+ local r = usernutrule {
+ width = wd,
+ height = ht,
+ depth = dp,
+ type = "mp",
+ factor = e,
+ offset = offset,
+ line = rulethickness,
+ data = mp,
+ ma = colorspace,
+ ca = color,
+ ta = transparency,
+ }
+ inject(r,wd,ht,dp)
+ else
+ local tx = d.text
+ if tx then
+ local l = copy_list(tx)
+ if d["repeat"] == v_yes then
+ l = new_leader(wd,l)
+ setattrlist(l,tx)
+ end
+ l = hpack_nodes(l,wd,"exactly")
+ inject(l,wd,ht,dp)
+ else
+ for i=1,level do
+ local hd = (offset+(i-1)*dy)*e - m
+ local ht = hd + rulethickness
+ local dp = -hd + rulethickness
+ inject(setcoloring(new_rule(wd,ht,dp),colorspace,color,transparency),wd,ht,dp)
+ end
+ end
+ end
+ return head
+end
+
+rules.handler = function(head)
+ return processwords(a_ruled,data,flush_ruled,head)
+end
+
+function rules.enable()
+ enableaction("shipouts","nodes.rules.handler")
+end
+
+local trace_shifted = false trackers.register("nodes.shifting", function(v) trace_shifted = v end)
+
+local report_shifted = logs.reporter("nodes","shifting")
+
+local a_shifted = attributes.private('shifted')
+
+local shifts = nodes.shifts or { }
+nodes.shifts = shifts
+shifts.data = shifts.data or { }
+
+storage.register("nodes/shifts/data", shifts.data, "nodes.shifts.data")
+
+local data = shifts.data
+
+function shifts.define(settings)
+ local nofdata = #data + 1
+ data[nofdata] = settings
+ return nofdata
+end
+
+local function flush_shifted(head,first,last,data,level,parent,strip) -- not that fast but acceptable for this purpose
+ if true then
+ first, last = striprange(first,last)
+ end
+ local prev = getprev(first)
+ local next = getnext(last)
+ setprev(first)
+ setnext(last)
+ local width, height, depth = getrangedimensions(parent,first,next)
+ local list = hpack_nodes(first,width,"exactly") -- we can use a simple pack
+ if first == head then
+ head = list
+ end
+ if prev then
+ setlink(prev,list)
+ end
+ if next then
+ setlink(list,next)
+ end
+ local raise = data.dy * dimenfactor(data.unit,fontdata[getfont(first)])
+ setshift(list,raise)
+ setwhd(list,width,height,depth)
+ if trace_shifted then
+ report_shifted("width %p, nodes %a, text %a",width,n_tostring(first,last),n_tosequence(first,last,true))
+ end
+ return head
+end
+
+shifts.handler = function(head)
+ return processwords(a_shifted,data,flush_shifted,head)
+end
+
+function shifts.enable()
+ enableaction("shipouts","nodes.shifts.handler")
+end
+
+-- linefillers
+
+local linefillers = nodes.linefillers or { }
+nodes.linefillers = linefillers
+linefillers.data = linefillers.data or { }
+
+storage.register("nodes/linefillers/data", linefillers.data, "nodes.linefillers.data")
+
+local data = linefillers.data
+
+function linefillers.define(settings)
+ local nofdata = #data + 1
+ data[nofdata] = settings
+ return nofdata
+end
+
+local function linefiller(current,data,width,location)
+ local height = data.height
+ local depth = data.depth
+ local mp = data.mp
+ local ma = data.ma
+ local ca = data.ca
+ local ta = data.ta
+ if mp and mp ~= "" then
+ return usernutrule {
+ width = width,
+ height = height,
+ depth = depth,
+ type = "mp",
+ line = data.rulethickness,
+ data = mp,
+ ma = ma,
+ ca = ca,
+ ta = ta,
+ option = location,
+ direction = getdirection(current),
+ }
+ else
+ return setcoloring(new_rule(width,height,depth),ma,ca,ta)
+ end
+end
+
+function linefillers.filler(current,data,width,height,depth)
+ if width and width > 0 then
+ local height = height or data.height or 0
+ local depth = depth or data.depth or 0
+ if (height + depth) ~= 0 then
+ local mp = data.mp
+ local ma = data.ma
+ local ca = data.ca
+ local ta = data.ta
+ if mp and mp ~= "" then
+ return usernutrule {
+ width = width,
+ height = height,
+ depth = depth,
+ type = "mp",
+ line = data.rulethickness,
+ data = mp,
+ ma = ma,
+ ca = ca,
+ ta = ta,
+ option = location,
+ direction = getdirection(current),
+ }
+ else
+ return setcoloring(new_rule(width,height,depth),ma,ca,ta)
+ end
+ end
+ end
+end
+
+local function getskips(list) -- this could be a helper
+ local ls = nil
+ local rs = nil
+ local is = nil
+ local pl = nil
+ local pr = nil
+ local ok = false
+ for n, subtype in nextglue, list do
+ if subtype == rightskip_code then
+ rs = n
+ elseif subtype == parfillrightskip_code then
+ pr = n
+ elseif subtype == leftskip_code then
+ ls = n
+ elseif subtype == indentskip_code then
+ is = n
+ elseif subtype == parfillleftskip_code then
+ pl = n
+ end
+ end
+ return is, ls, pl, pr, rs
+end
+
+function linefillers.handler(head)
+ -- we have a normalized line ..
+ for current, id, subtype, list in nextlist, head do
+ if subtype == linelist_code and list then
+ local a = getattr(current,a_linefiller)
+ if a then
+ local class = a % 1000
+ local data = data[class]
+ if data then
+ local location = data.location
+ local scope = data.scope
+ local distance = data.distance
+ local threshold = data.threshold
+ local leftlocal = false
+ local rightlocal = false
+ --
+ if scope == v_right then
+ leftlocal = true
+ elseif scope == v_left then
+ rightlocal = true
+ elseif scope == v_local then
+ leftlocal = true
+ rightlocal = true
+ end
+ --
+ local is, ls, pl, pr, rs = getskips(list)
+ if ls and rs then
+ if location == v_left or location == v_both then
+ local indentation = is and getwidth(is) or 0
+ local leftfixed = ls and getwidth(ls) or 0
+ local lefttotal = ls and effective_glue(ls,current) or 0
+ local width = lefttotal - (leftlocal and leftfixed or 0) + indentation - distance
+ if width > threshold then
+ if is then
+ setwidth(is,0)
+ end
+ setglue(ls,leftlocal and getwidth(ls) or nil)
+ if distance > 0 then
+ insert_node_after(list,ls,new_kern(distance))
+ end
+ insert_node_after(list,ls,linefiller(current,data,width,"left"))
+ end
+ end
+ --
+ if location == v_right or location == v_both then
+ local rightfixed = rs and getwidth(rs) or 0
+ local righttotal = rs and effective_glue(rs,current) or 0
+ local parfixed = pr and getwidth(pr) or 0
+ local partotal = pr and effective_glue(pr,current) or 0
+ local width = righttotal - (rightlocal and rightfixed or 0) + partotal - distance
+ if width > threshold then
+ if pr then
+ setglue(pr)
+ end
+ setglue(rs,rightlocal and getwidth(rs) or nil)
+ if distance > 0 then
+ insert_node_before(list,rs,new_kern(distance))
+ end
+ insert_node_before(list,rs,linefiller(current,data,width,"right"))
+ end
+ end
+ else
+ -- error, not a properly normalized line
+ end
+ end
+ end
+ end
+ end
+ return head
+end
+
+local enable = false
+
+function linefillers.enable()
+ if not enable then
+ -- we could now nil it
+ enableaction("finalizers","nodes.linefillers.handler")
+ enable = true
+ end
+end
+
+-- interface
+
+implement {
+ name = "definerule",
+ actions = { rules.define, context },
+ arguments = {
+ {
+ { "continue" },
+ { "unit" },
+ { "order" },
+ { "method", "integer" },
+ { "offset", "number" },
+ { "rulethickness" },
+ { "dy", "number" },
+ { "max", "number" },
+ { "ma", "integer" },
+ { "ca", "integer" },
+ { "ta", "integer" },
+ { "mp" },
+ { "empty" },
+ { "text", "integer" },
+ { "repeat" },
+ }
+ }
+}
+
+implement {
+ name = "enablerules",
+ onlyonce = true,
+ actions = rules.enable
+}
+
+implement {
+ name = "defineshift",
+ actions = { shifts.define, context },
+ arguments = {
+ {
+ { "continue" },
+ { "unit" },
+ { "method", "integer" },
+ { "dy", "number" },
+ }
+ }
+}
+
+implement {
+ name = "enableshifts",
+ onlyonce = true,
+ actions = shifts.enable
+}
+
+implement {
+ name = "definelinefiller",
+ actions = { linefillers.define, context },
+ arguments = {
+ {
+ { "method", "integer" },
+ { "location", "string" },
+ { "scope", "string" },
+ { "mp", "string" },
+ { "ma", "integer" },
+ { "ca", "integer" },
+ { "ta", "integer" },
+ { "depth", "dimension" },
+ { "height", "dimension" },
+ { "distance", "dimension" },
+ { "threshold", "dimension" },
+ { "rulethickness", "dimension" },
+ }
+ }
+}
+
+implement {
+ name = "enablelinefillers",
+ onlyonce = true,
+ actions = linefillers.enable
+}
+
+-- We add a bonus feature here (experiment):
+
+interfaces.implement {
+ name = "autorule",
+ arguments = {
+ {
+ { "width", "dimension" },
+ { "height", "dimension" },
+ { "depth", "dimension" },
+ { "xoffset", "dimension" },
+ { "yoffset", "dimension" },
+ { "left", "dimension" },
+ { "right", "dimension" },
+ },
+ },
+ actions = function(t)
+ local n = new_rule(
+ t.width or running,
+ t.height or running,
+ t.depth or running
+ )
+ setattrlist(n,true)
+ setoffsets(n,t.xoffset,t.yoffset) -- ,t.left, t.right
+ local l = t.left
+ local r = t.right
+ if l then
+ setfield(n,"left",l)
+ end
+ if r then
+ setfield(n,"right",r)
+ end
+ context(tonode(n))
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-rul.mkxl b/Master/texmf-dist/tex/context/base/mkxl/node-rul.mkxl
new file mode 100644
index 00000000000..2b55561d85d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-rul.mkxl
@@ -0,0 +1,610 @@
+%D \module
+%D [ file=node-rul,
+%D version=2009.11.03, % 1995.10.10,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Bars,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: ex and and em traveling with attribute
+% todo: this will move to typo-rul + ctxcommands
+
+\writestatus{loading}{ConTeXt Core Macros / Bars}
+
+%D \macros
+%D {underbar,underbars,
+%D overbar,overbars,
+%D overstrike,overstrikes,
+%D setupbar}
+%D
+%D In the rare case that we need undelined words, for instance because all font
+%D alternatives are already in use, one can use \type {\underbar} and \type
+%D {\overstrike} and their plural forms.
+%D
+%D \startbuffer
+%D \underbars {drawing \underbar{bars} under words is a typewriter leftover}
+%D \overstrikes {striking words makes them \overstrike {unreadable} but
+%D sometimes even \overbar {top lines} come into view.}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D The formal definitions are:
+%D
+%D \showsetup{underbar}
+%D \showsetup{underbars}
+%D \showsetup{overbar}
+%D \showsetup{overbars}
+%D \showsetup{overstrike}
+%D \showsetup{overstrikes}
+%D
+%D \showsetup{setupbars}
+%D
+%D Units can be any normal \TEX\ unit:
+%D
+%D \startbuffer
+%D \setupbars[unit=mm,rulethickness=1] bar\startbar[underbar]foo\stopbar bar\blank
+%D \setupbars[unit=ex,rulethickness=1] bar\startbar[underbar]foo\stopbar bar\blank
+%D \setupbars[unit=pt,rulethickness=1] bar\startbar[underbar]foo\stopbar bar\blank
+%D \setupbars[unit=pt,rulethickness=10pt] bar\startbar[underbar]foo\stopbar bar
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+%D
+%D As with many early usage of \LUA\ in \MKIV\ this mechanism explores a way
+%D to deal with local settings at the \TEX\ end and remembering parameters
+%D at the \LUA\ end. We might do things differently now, but as settings normally
+%D don't change that often, we're not in a hurry to do that now. The problem at
+%D the \LUA\ end is that we don't know when to clean up.
+
+\unprotect
+
+%definesystemattribute[ruled]
+%definesystemattribute[shifted]
+
+\registerctxluafile{node-rul}{autosuffix,optimize}
+
+\installcorenamespace{bar}
+\installcorenamespace{barindex}
+\installcorenamespace{barattribute}
+\installcorenamespace{barstack}
+
+\installcommandhandler \??bar {bar} \??bar
+
+\newtoks\t_node_rules_checklist
+
+\mutable\let\c_node_rules_index\relax % temporary synonym (can be shared)
+\mutable\let\p_node_rules_color\empty
+
+\aliased\let\setupbars\setupbar
+
+\appendtoks
+ \ifempty\currentbar
+ \the\t_node_rules_checklist
+ \else
+ \node_rules_define
+ \fi
+\to \everysetupbar
+
+\appendtoks
+ \ifcsname\??barindex\currentbar\endcsname
+ \lastnamedcs\zerocount
+ \else
+ \expandafter\newcount\csname\??barindex\currentbar\endcsname
+ \fi
+ % \normalexpanded{\t_node_rules_checklist{\node_rules_redefine{\currentbar}\the\t_node_rules_checklist}}%
+ \normalexpanded{\t_node_rules_checklist{\the\t_node_rules_checklist\relax\node_rules_redefine{\currentbar}}}%
+ % \etoksapp\t_node_rules_checklist{\node_rules_redefine{\currentbar}}%
+ \node_rules_define
+ \frozen\instance\setuevalue\currentbar{\node_rules_direct{\currentbar}}%
+\to \everydefinebar
+
+\newbox\b_node_rules
+
+\protected\def\node_rules_define
+ {\edef\p_node_rules_color{\barparameter\c!color}%
+ \edef\p_node_text{\barparameter\c!text}%
+ \ifempty\p_node_text\else
+ \setbox\b_node_rules\hbox{\p_node_text}%
+ \fi
+ \expandafter\integerdef\csname\??barattribute\currentbar\endcsname
+ \clf_definerule
+ continue {\barparameter\c!continue}%
+ unit {\barparameter\c!unit}%
+ order {\barparameter\c!order}%
+ rulethickness {\barparameter\c!rulethickness}%
+ method \barparameter\c!method
+ max \barparameter\c!max\space % number
+ mp {\includeMPgraphic{\barparameter\c!mp}}
+ ma \c_attr_colormodel
+ ca \rawcolorattribute\p_node_rules_color
+ ta \rawtransparencyattribute\p_node_rules_color
+ offset \barparameter\c!offset\space % number
+ dy \barparameter\c!dy\space % number
+ empty {\barparameter\c!empty}%
+ \ifempty\p_node_text\else
+ % not that useful and efficient, more for testing something
+ text \b_node_rules
+ repeat {\barparameter\c!repeat}%
+ \fi
+ \relax
+ \relax}
+
+\protected\def\node_rules_redefine#1%
+ {\def\currentbar{#1}\node_rules_define}
+
+\protected\def\node_rules_direct#1%
+ {\groupedcommand
+ {\node_rules_set{#1}\barparameter\c!left}%
+ {\relax\barparameter\c!right}}
+
+\permanent\protected\def\inlinebar[#1]%
+ {\node_rules_direct{#1}}
+
+% \protected\def\inlinecurrentbar
+% {\node_rules_direct{\currentbar}}
+
+% store in properties
+
+\permanent\protected\def\node_rules_set#1% maybe reverse the 1000 (also maybe use more attributes instead of settings)
+ {\edef\currentbar{#1}%
+ \usebarstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ % maybe: \usebarstyleandcolor\c!textgroundstyle\c!textcolor
+ % todo: move this to lua .. we callout anyway
+ \expandafter\let\expandafter\c_node_rules_index\csname\??barindex#1\endcsname
+ \advance\c_node_rules_index\plusone
+ \clf_enablerules % will be relaxed
+ \c_attr_ruled\numexpr
+ \plusthousand*\c_node_rules_index
+ % optimizing this one needs testing
+ +\csname\??barattribute#1\ifcsname\??bar#1:\number\c_node_rules_index\s!parent\endcsname:\number\c_node_rules_index\fi\endcsname
+ \relax}
+
+\permanent\protected\def\resetbar
+ {\c_attr_ruled\attributeunsetvalue}
+
+\permanent\protected\def\nobar
+ {\groupedcommand
+ {\resetbar\barparameter\c!left}%
+ {\relax\barparameter\c!right}}
+
+\permanent\protected\def\startbar[#1]%
+ {\begingroup
+ \node_rules_set{#1}%
+ \ignorespaces
+ \barparameter\c!left}
+
+\permanent\protected\def\stopbar
+ {\removeunwantedspaces
+ \barparameter\c!right
+ \endgroup}
+
+\permanent\protected\def\setbar[#1]%
+ {\node_rules_set{#1}}
+
+\aliased\let\directsetbar\node_rules_set
+
+% ungrouped
+
+\newcount\c_node_rules_nesting % todo: same as colors
+
+\permanent\protected\def\pushbar[#1]%
+ {\global\advance\c_node_rules_nesting\plusone
+ \edefcsname\??barstack\number\c_node_rules_nesting\endcsname{\c_attr_ruled\the\c_attr_ruled}%
+ \node_rules_set{#1}}
+
+\permanent\protected\def\popbar
+ {\csname\??barstack\number\c_node_rules_nesting\endcsname
+ \global\advance\c_node_rules_nesting\minusone}
+
+\setupbars
+ [\c!method=0, % new: 0=center nested, 1=stack nested
+ \c!continue=\v!no,
+ \c!empty=, % new: yes = hide text
+ \c!offset=0, % upwards, replaces: topoffset bottomoffset
+ \c!dy=0,
+ \c!max=3,
+ \c!style=,
+ \c!rulethickness=.1,
+ \c!order=\v!foreground,
+ \c!unit=ex, % so now we are relative
+ \c!color=] % replaces: rulecolor
+
+% \definebar[touchbar] [\c!method=0,\c!dy=-0.4,\c!offset=-0.0]
+% \definebar[touchbars] [touchbar] [\c!continue=\v!yes]
+
+\pushoverloadmode
+
+\aliased\let\normalmathoverbar \overbar
+\aliased\let\normalmathunderbar \underbar
+%aliased\let\normalmathoverstrike \overstrike
+%aliased\let\normalmathunderstrike\understrike
+
+\definebar[\v!overbar] [\c!method=1,\c!dy=0.4,\c!offset=1.8,\c!continue=\v!yes]
+\definebar[\v!underbar] [\c!method=1,\c!dy=-0.4,\c!offset=-0.3,\c!continue=\v!yes]
+\definebar[\v!overstrike][\c!method=0,\c!dy=0.4,\c!offset=0.5,\c!continue=\v!yes]
+
+\definebar
+ [\v!understrike]
+ [\c!method=0,
+ \c!offset=1.375,
+ \c!rulethickness=2.5,
+ \c!continue=\v!yes,
+ \c!order=\v!background,
+ \c!color=lightgray]
+
+\definebar[\v!overbars] [\v!overbar] [\c!continue=\v!no]
+\definebar[\v!underbars] [\v!underbar] [\c!continue=\v!no]
+\definebar[\v!overstrikes] [\v!overstrike] [\c!continue=\v!no]
+\definebar[\v!understrikes][\v!understrike][\c!continue=\v!no]
+
+\definebar
+ [\v!hiddenbar]
+ [\v!underbar]
+ [\c!continue=\v!yes,
+ \c!empty=\v!yes,
+ \c!left=\zwj,
+ \c!right=\zwj]
+
+% \setupbar[\v!overstrike][continue=all]
+
+% we want these always so ...
+
+\ifdefined\normalmathunderbar
+ \enforced\permanent\expandafter\let\expandafter\normaltextunderbar\csname\v!underbar\endcsname
+ \enforced\permanent\protected\def\underbar{\mathortext\normalmathunderbar\normaltextunderbar}
+\else
+ \enforced\permanent\expandafter\let\expandafter\underbar\csname\v!underbar\endcsname
+\fi
+
+\ifdefined\normalmathoverbar
+ \enforced\permanent\expandafter\let\expandafter\normaltextoverbar\csname\v!overbar\endcsname
+ \enforced\permanent\protected\def\overbar{\mathortext\normalmathoverbar\normaltextoverbar}
+\else
+ \enforced\permanent\expandafter\let\expandafter\overbar\csname\v!overbar\endcsname
+\fi
+
+% \ifdefined\normalmathunderstrike
+% \enforced\permanent\expandafter\let\expandafter\normaltextunderstrike\csname\v!understrike\endcsname
+% \enforced\permanent\protected\def\understrike{\mathortext\normalmathunderstrike\normaltextunderstrike}
+% \else
+% \enforced\permanent\expandafter\let\expandafter\understrike\csname\v!understrike\endcsname
+% \fi
+%
+% \ifdefined\normalmathoverstrike
+% \enforced\permanent\expandafter\let\expandafter\normaltextoverstrike\csname\v!overstrike\endcsname
+% \enforced\permanent\protected\def\overstrike{\mathortext\normalmathoverstrike \normaltextoverstrike}
+% \else
+% \enforced\permanent\expandafter\let\expandafter\overstrike\csname\v!overstrike\endcsname
+% \fi
+
+\enforced\permanent\expandafter\let\expandafter\overstrikes\csname\v!overstrikes\endcsname
+\enforced\permanent\expandafter\let\expandafter\underbars \csname\v!underbars \endcsname
+\enforced\permanent\expandafter\let\expandafter\overbars \csname\v!overbars \endcsname
+
+\enforced\permanent\protected\def\setupunderbar[#1]% too incompatible for the moment
+ {}
+
+\popoverloadmode
+
+%D An experimental new feature:
+%D
+%D \startbuffer
+%D test {\red\underrandoms{test me}} and \underrandom{test} or \underrandom{grep} \blank
+%D test {\red\underdashes {test me}} and \underdash {test} or \underdash {grep} \blank
+%D test {\red\underdots {test me}} and \underdot {test} or \underdot {grep} \blank
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\startuseMPgraphic{rules:under:random}
+ draw
+ ((0,RuleDepth) ... (RuleWidth,RuleDepth)) randomized (4*RuleThickness)
+ shifted (0,RuleFactor*RuleOffset)
+ withpen pencircle scaled RuleThickness
+ withcolor RuleColor ;
+ setbounds currentpicture to unitsquare xysized(RuleWidth,RuleHeight) ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{rules:under:dash}
+ draw
+ ((0,RuleDepth) -- (RuleWidth,RuleDepth))
+ shifted (0,RuleFactor*RuleOffset)
+ dashed dashpattern(on RuleFactor/2 off RuleFactor/2)
+ withpen pencircle scaled RuleThickness
+ withcolor RuleColor ;
+ setbounds currentpicture to unitsquare xysized(RuleWidth,RuleHeight) ;
+\stopuseMPgraphic
+
+\startuseMPgraphic{rules:under:dots}
+ path p ; p := (0,RuleDepth) -- (RuleWidth,RuleDepth) ;
+ numeric l ; l := arclength(p);
+ l := l mod RuleThickness/4;
+ draw
+ p
+ shifted (l,RuleFactor*RuleOffset)
+ dashed dashpattern(off 2RuleThickness+l on 0 off 2RuleThickness)
+ withpen pencircle scaled 2RuleThickness
+ withcolor RuleColor ;
+ setbounds currentpicture to unitsquare xysized(RuleWidth,RuleHeight) ;
+\stopuseMPgraphic
+
+\definebar
+ [undergraphic]
+ [\c!mp=rules:under:dash,
+ \c!offset=-.2,
+ \c!order=\v!background]
+
+\definebar[underrandom] [undergraphic][\c!mp=rules:under:random]
+\definebar[underrandoms][underrandom] [\c!continue=\v!yes]
+
+\definebar[underdash] [undergraphic][\c!mp=rules:under:dash]
+\definebar[underdashes] [underdash] [\c!continue=\v!yes]
+
+\definebar[underdot] [undergraphic][\c!mp=rules:under:dots]
+\definebar[underdots] [underdot] [\c!continue=\v!yes]
+
+%D This will move: (a bit duplicated)
+
+\installcorenamespace{shift}
+\installcorenamespace{shiftindex}
+\installcorenamespace{shiftattribute}
+
+\installcommandhandler \??shift {shift} \??shift
+
+\newtoks\t_node_shifts_checklist
+
+\mutable\let\c_node_shifts_index\relax % temporary synonym (can be shared)
+
+\let\setupshifts\setupshift
+
+\appendtoks
+ \ifempty\currentshift
+ \the\t_node_shifts_checklist
+ \else
+ \node_shifts_define
+ \fi
+\to \everysetupshift
+
+\appendtoks
+ \ifcsname\??shiftindex\currentshift\endcsname
+ \lastnamedcs\zerocount
+ \else
+ \expandafter\newcount\csname\??shiftindex\currentshift\endcsname
+ \fi
+ \normalexpanded{\t_node_shifts_checklist{\the\t_node_shifts_checklist\node_shifts_redefine{\currentshift}}}% order ?
+ \node_shifts_define
+ \frozen\instance\setuevalue\currentshift{\node_shifts_direct{\currentshift}}%
+\to \everydefineshift
+
+\protected\def\node_shifts_define
+ {\setevalue{\??shiftattribute\currentshift}{\number
+ \clf_defineshift
+ continue {\shiftparameter\c!continue}%
+ unit {\shiftparameter\c!unit}%
+ method \shiftparameter\c!method
+ dy \shiftparameter\c!dy % number
+ \relax}}
+
+\protected\def\node_shifts_redefine#1%
+ {\def\currentshift{#1}\node_shifts_define}
+
+\protected\def\node_shifts_set#1% todo: check parent ! todo: move attr etc to lua
+ {\def\currentshift{#1}%
+ \expandafter\let\expandafter\c_node_shifts_index\csname\??shiftindex#1\endcsname
+ \advance\c_node_shifts_index\plusone
+ \clf_enableshifts % will be relaxed
+ \c_attr_shifted\numexpr
+ \plusthousand*\c_node_shifts_index
+ +\csname\??shiftattribute#1\ifcsname\??shift#1:\number\c_node_shifts_index\s!parent\endcsname:\number\c_node_shifts_index\fi\endcsname
+ \relax
+ \useshiftstyleandcolor\c!style\c!color
+ \dosetupisolatedalign{\shiftparameter\c!align}} % weird feature that i probably needed once
+
+\permanent\protected\def\startshift[#1]%
+ {\begingroup
+ \node_shifts_set{#1}%
+ \ignorespaces}
+
+\permanent\protected\def\stopshift
+ {\removeunwantedspaces
+ \endgroup}
+
+\protected\def\node_shifts_direct#1%
+ {\groupedcommand
+ {\begingroup\dostartisolation\begingroup\node_shifts_set{#1}\ignorespaces}
+ {\removeunwantedspaces\endgroup\dostopisolation\endgroup}}
+
+\setupshifts
+ [\c!method=0,
+ \c!continue=\v!no,
+ \c!dy=0,
+ \c!unit=ex,
+ \c!align=,
+ \c!style=,
+ \c!color=]
+
+\defineshift [\v!shiftup] [\c!method=0,\c!dy=-1,\c!unit=ex,\c!continue=\v!yes,\c!style=\txx,\c!color=]
+\defineshift [\v!shiftdown] [\c!method=1,\c!dy=.3,\c!unit=ex,\c!continue=\v!yes,\c!style=\txx,\c!color=]
+
+% we want these always so ...
+
+\expandafter\let\expandafter\shiftup \csname\v!shiftup \endcsname
+\expandafter\let\expandafter\shiftdown \csname\v!shiftdown \endcsname
+
+% This is a weird helper.. these might go away:
+
+\permanent\protected\def\dostartisolation{\signalcharacter}
+\permanent\protected\def\dostopisolation {\signalcharacter}
+\permanent\protected\def\doisolator {\signalcharacter}
+
+\permanent\protected\def\dosetupisolatedalign#1%
+ {\doisolator
+ \setupalign[#1]\relax}
+
+\permanent\protected\def\doisolatedgroupedalign#1#2%
+ {\groupedcommand
+ {\begingroup\dostartisolation\begingroup#1}
+ {#2\endgroup\dostopisolation\endgroup}}
+
+%D More rules.
+
+% The following code rocks and was written with the Toto Live in Poland bluray
+% in loop mode on my 5.1 surround development setup (the Toto lineup with Simon
+% Phillips on drums). The Amsterdam concert is equally energizing.
+
+\installcorenamespace{linefiller}
+\installcorenamespace{linefillerindex}
+\installcorenamespace{linefillerattribute}
+
+\installcommandhandler \??linefiller {linefiller} \??linefiller
+
+\definesystemattribute[linefiller][public]
+
+\newtoks\t_node_linefiller_checklist
+
+\mutable\let\c_node_linefiller_index\relax % temporary synonym (can be shared)
+
+\aliased\let\setuplinefillers\setuplinefiller
+
+\appendtoks
+ \ifempty\currentlinefiller
+ \the\t_node_linefiller_checklist
+ \else
+ \node_linefiller_define
+ \fi
+\to \everysetuplinefiller
+
+\appendtoks
+ \ifcsname\??linefillerindex\currentlinefiller\endcsname
+ \lastnamedcs\zerocount
+ \else
+ \expandafter\newcount\csname\??linefillerindex\currentlinefiller\endcsname
+ \fi
+ \etoksapp\t_node_linefiller_checklist{\t_node_linefiller_checklist\node_linefiller_redefine{\currentlinefiller}}%
+ \node_linefiller_define
+\to \everydefinelinefiller
+
+\protected\def\node_linefiller_define
+ {\edef\p_node_rules_color{\linefillerparameter\c!color}%
+ \expandafter\integerdef\csname\??linefillerattribute\currentlinefiller\endcsname
+ \clf_definelinefiller
+ %method \linefillerparameter\c!method
+ location {\linefillerparameter\c!location}%
+ scope {\linefillerparameter\c!scope}%
+ mp {\includeMPgraphic{\linefillerparameter\c!mp}}%
+ ma \c_attr_colormodel
+ ca \rawcolorattribute\p_node_rules_color
+ ta \rawtransparencyattribute\p_node_rules_color
+ height \dimexpr\linefillerparameter\c!height\relax
+ depth \dimexpr\linefillerparameter\c!depth\relax
+ distance \dimexpr\linefillerparameter\c!distance\relax
+ threshold \dimexpr\linefillerparameter\c!threshold\relax
+ rulethickness \dimexpr\linefillerparameter\c!rulethickness\relax
+ \relax
+ \relax}
+
+\protected\def\node_linefiller_redefine#1%
+ {\def\currentlinefiller{#1}\node_linefiller_define}
+
+\protected\def\node_linefiller_set#1% todo: check parent ! todo: move attr etc to lua
+ {\def\currentlinefiller{#1}%
+ \expandafter\let\expandafter\c_node_linefiller_index\csname\??linefillerindex#1\endcsname
+ \advance\c_node_linefiller_index\plusone
+ \clf_enablelinefillers
+ \c_attr_linefiller\numexpr
+ \plusthousand*\c_node_linefiller_index
+ +\csname\??linefillerattribute#1\ifcsname\??linefiller#1:\number\c_node_linefiller_index\s!parent\endcsname:\number\c_node_linefiller_index\fi\endcsname
+ \relax}
+
+\permanent\tolerant\protected\def\startlinefiller[#1]#*[#2]%
+ {\begingroup
+ \par
+ \def\currentlinefiller{#1}%
+ \ifparameter#2\or
+ % we need to update settings
+ \setuplinefiller[#1][#2]% no \setupcurrentlinefiller as we need to update settings
+ \fi
+ \node_linefiller_set{#1}%
+ \linefillerparameter\c!before
+ \usealignparameter\linefillerparameter
+ \uselinefillerstyleandcolor\c!textstyle\c!textcolor} % bars have foregroundcolor
+
+\permanent\protected\def\stoplinefiller
+ {\par
+ \linefillerparameter\c!after
+ \endgroup}
+
+\permanent\protected\def\setlinefiller[#1]%
+ {\node_linefiller_set{#1}}
+
+\setuplinefillers
+ [%c!method=0,
+ %c!mp=,
+ \c!location=\v!both,
+ \c!scope=\v!local,
+ \c!distance=\zeropoint,
+ \c!threshold=\zeropoint,
+ \c!rulethickness=\linewidth,
+ \c!height=\linewidth,
+ \c!depth=\zeropoint,
+ %\c!textcolor=,
+ %\c!textstyle=,
+ %\c!align=,
+ %\c!before=,
+ %\c!after=,
+ \c!color=]
+
+\definelinefiller
+ [filler]
+ [\c!height=.75\exheight,
+ %\c!mp=rules:filler:demo,
+ %\c!threshold=.25\emwidth,
+ \c!distance=.25\emwidth,
+ \c!rulethickness=.25\exheight]
+
+%D Bonus:
+%D
+%D \starttyping
+%D \startuseMPgraphic{foo}
+%D fill unitsquare
+%D xyscaled (RuleWidth,RuleHeight+RuleDepth) enlarged (ExHeight/4,ExHeight/8)
+%D shifted (-ExHeight/8,ExHeight/16)
+%D withcolor RuleColor ;
+%D \stopuseMPgraphic
+%D
+%D \definelinefiller[foo][mp=foo,color=darkred]
+%D
+%D \linefillerhbox[foo]{OEPS}
+%D \stoptyping
+
+\protected\def\node_backgrounds_filler_box#1#2[#3]%
+ {\bgroup
+ \clf_enablebackgroundboxes
+ \dowithnextbox{%
+ \node_linefiller_set{#3}% already sets the attribute
+ #1%
+ attr \backgroundattribute \plusone
+ % attr \linefillerattribute \the\c_attr_linefiller
+ {\box\nextbox}%
+ \egroup}%
+ #2}
+
+\permanent\protected\def\linefillerhbox{\node_backgrounds_filler_box\hpack\hbox}
+\permanent\protected\def\linefillervbox{\node_backgrounds_filler_box\vpack\vbox}
+\permanent\protected\def\linefillervtop{\node_backgrounds_filler_box\tpack\vtop}
+
+%D Bonus:
+
+\permanent\protected\def\autorule{\clf_autorule} % todo: public implementor
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-scn.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-scn.lmt
new file mode 100644
index 00000000000..0ec1ba387c9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-scn.lmt
@@ -0,0 +1,314 @@
+if not modules then modules = { } end modules ['node-scn'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local floor = math.floor
+
+local attributes = attributes
+local nodes = nodes
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getattr = nuts.getattr
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+
+local end_of_math = nuts.end_of_math
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local rule_code = nodecodes.rule
+local boundary_code = nodecodes.boundary
+local dir_code = nodecodes.dir
+local math_code = nodecodes.math
+local glue_code = nodecodes.glue
+local penalty_code = nodecodes.penalty
+local kern_code = nodecodes.kern
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local userskip_code = gluecodes.userskip
+local spaceskip_code = gluecodes.spaceskip
+local xspaceskip_code = gluecodes.xspaceskip
+local leaders_code = gluecodes.leaders
+
+local fontkern_code = kerncodes.fontkern
+
+local variables = interfaces.variables
+
+local privateattributes = attributes.private
+
+local a_runningtext = privateattributes('runningtext')
+
+local v_yes = variables.yes
+local v_all = variables.all
+
+local function striprange(first,last) -- todo: dir
+ if first and last then -- just to be sure
+ if first == last then
+ return first, last
+ end
+ while first and first ~= last do
+ local id = getid(first)
+ if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code
+ break
+ else
+ first = getnext(first)
+ end
+ end
+ if not first then
+ return nil, nil
+ elseif first == last then
+ return first, last
+ end
+ while last and last ~= first do
+ local id = getid(last)
+ if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code
+ break
+ else
+ local prev = getprev(last) -- luatex < 0.70 has italic correction kern not prev'd
+ if prev then
+ last = prev
+ else
+ break
+ end
+ end
+ end
+ if not last then
+ return nil, nil
+ end
+ end
+ return first, last
+end
+
+nuts.striprange = striprange
+
+-- todo: order and maybe other dimensions
+
+-- we can use this one elsewhere too
+--
+-- todo: functions: word, sentence
+--
+-- glyph rule unset whatsit glue margin_kern kern math disc
+
+-- we assume {glyphruns} and no funny extra kerning, ok, maybe we need
+-- a dummy character as start and end; anyway we only collect glyphs
+--
+-- this one needs to take layers into account (i.e. we need a list of
+-- critical attributes)
+
+-- omkeren class en level -> scheelt functie call in analyze
+
+-- todo: switching inside math
+
+-- handlers
+
+local function processwords(attribute,data,flush,head,parent,skip) -- we have hlistdir and local dir
+ local n = head
+ if n then
+ local f, l, a, d, i, class
+ local continue, leaders, done, strip, level = false, false, false, true, -1
+ while n do
+ local id = getid(n)
+ if id == glyph_code or id == rule_code or (id == hlist_code and getattr(n,a_runningtext)) then
+ local aa = getattr(n,attribute)
+ if aa and aa ~= skip then
+ if aa == a then
+ if not f then -- ?
+ f = n
+ end
+ l = n
+ else
+ -- possible extensions: when in same class then keep spanning
+ local newlevel, newclass = floor(aa/1000), aa%1000 -- will be configurable
+ -- strip = not continue or level == 1 -- 0
+ if f then
+ if class == newclass then -- and newlevel > level then
+ head, done = flush(head,f,l,d,level,parent,false), true
+ else
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ end
+ end
+ f, l, a = n, n, aa
+ level, class = newlevel, newclass
+ d = data[class]
+ if d then
+ local c = d.continue
+ leaders = c == v_all
+ continue = leaders or c == v_yes
+ else
+ continue = true
+ end
+ end
+ else
+ if f then
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ end
+ f, l, a = nil, nil, nil
+ end
+ if id == hlist_code then
+ local list = getlist(n)
+ if list then
+ setlist(n,(processwords(attribute,data,flush,list,n,aa))) -- watch ()
+ end
+ end
+ elseif id == disc_code or id == boundary_code then
+ if f then
+ l = n
+ end
+ elseif id == kern_code and getsubtype(n) == fontkern_code then
+ if f then
+ l = n
+ end
+ elseif id == math_code then
+ -- otherwise not consistent: a $b$ c vs a $b+c$ d etc
+ -- we need a special (optional) go over math variant
+ if f then
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ f, l, a = nil, nil, nil
+ end
+ elseif id == hlist_code or id == vlist_code then
+ if f then
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ f, l, a = nil, nil, nil
+ end
+ local list = getlist(n)
+ if list then
+ setlist(n,(processwords(attribute,data,flush,list,n,skip))) -- watch ()
+ end
+ elseif id == dir_code then -- only changes in dir, we assume proper boundaries
+ if f then
+ l = n
+ end
+ elseif f then
+ if continue then
+ if id == penalty_code then
+ l = n
+ -- elseif id == kern_code then
+ -- l = n
+ elseif id == glue_code then
+ -- catch \underbar{a} \underbar{a} (subtype test is needed)
+ local subtype = getsubtype(n)
+ if getattr(n,attribute) and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code or (leaders and subtype >= leaders_code)) then
+ l = n
+ else
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ f, l, a = nil, nil, nil
+ end
+ end
+ else
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ f, l, a = nil, nil, nil
+ end
+ end
+ n = getnext(n)
+ end
+ if f then
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ end
+ return head, true -- todo: done
+ else
+ return head, false
+ end
+end
+
+nuts.processwords = function(attribute,data,flush,head,parent) -- we have hlistdir and local dir
+ return processwords(attribute,data,flush,head,parent)
+end
+
+-- works on lines !
+-- todo: stack because skip can change when nested
+
+local function processranges(attribute,flush,head,parent,depth,skip)
+ local n = head
+ if n then
+ local f, l, a
+ local done = false
+ while n do
+ local id = getid(n)
+ if id == glyph_code or id == rule_code then
+ local aa = getattr(n,attribute)
+-- if aa and (not skip or aa ~= skip) then
+ if aa then
+ if aa == a then
+ if not f then
+ f = n
+ end
+ l = n
+ else
+ if f then
+ head, done = flush(head,f,l,a,parent,depth), true
+ end
+ f, l, a = n, n, aa
+ end
+ else
+ if f then
+ head, done = flush(head,f,l,a,parent,depth), true
+ end
+ f, l, a = nil, nil, nil
+ end
+ elseif id == disc_code or id == boundary_code then
+ if f then
+ l = n
+ else
+ -- weird
+ end
+ elseif id == kern_code and getsubtype(n) == fontkern_code then
+ if f then
+ l = n
+ end
+ -- elseif id == penalty_code then
+ elseif id == glue_code then
+ -- todo: leaders
+ elseif id == hlist_code or id == vlist_code then
+ local aa = getattr(n,attribute)
+-- if aa and (not skip or aa ~= skip) then
+ if aa then
+ if aa == a then
+ if not f then
+ f = n
+ end
+ l = n
+ else
+ if f then
+ head, done = flush(head,f,l,a,parent,depth), true
+ end
+ f, l, a = n, n, aa
+ end
+ else
+ if f then
+ head, done = flush(head,f,l,a,parent,depth), true
+ end
+ f, l, a = nil, nil, nil
+ end
+ local list = getlist(n)
+ if list then
+ setlist(n,(processranges(attribute,flush,list,n,depth+1,aa)))
+ end
+ end
+ n = getnext(n)
+ end
+ if f then
+ head, done = flush(head,f,l,a,parent,depth), true
+ end
+ return head, done
+ else
+ return head, false
+ end
+end
+
+nuts.processranges = function(attribute,flush,head,parent) -- we have hlistdir and local dir
+ return processranges(attribute,flush,head,parent,0)
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-ser.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-ser.lmt
new file mode 100644
index 00000000000..8fcbb31e473
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-ser.lmt
@@ -0,0 +1,284 @@
+if not modules then modules = { } end modules ['node-ser'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, tostring = type, tostring
+local concat, tohash, sortedkeys, sortedhash, printtable, serialize = table.concat, table.tohash, table.sortedkeys, table.sortedhash, table.print, table.serialize
+local formatters, format, rep = string.formatters, string.format, string.rep
+
+local allocate = utilities.storage.allocate
+
+local context = context
+local nodes = nodes
+local node = node
+
+local getfields = node.fields
+
+local is_node = nodes.is_node
+
+local nodecodes = nodes.nodecodes
+local subtypes = nodes.subtypes
+
+local tonode = nodes.tonode
+local tonut = nodes.tonut
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local f_char = formatters["%U"]
+local f_attr = formatters["<attribute : %i>"]
+local f_recurse = formatters["<recursive : %i>"]
+
+-- flat : don't use next, but indexes
+-- verbose : also add type
+
+local canbezero = {
+ integer = true,
+ dimension = true,
+ number = true,
+}
+
+local canbelist = {
+ attribute = "<attribute>",
+ node = "<node>",
+ token = "<token>",
+ attribute_list = "<attributes>",
+ node_list = "<nodes>",
+ token_list = "<tokens>",
+}
+
+local canbeignored = {
+ prev = "<node>"
+}
+
+local canbechar = {
+ char = true,
+ small_char = true,
+ large_char = true,
+}
+
+local fieldtypes = table.setmetatableindex(function(t,k)
+ local v = getfields(k,true) or false
+ t[k] = v
+ return v
+end)
+
+nodes.fieldtypes = fieldtypes
+
+local function astable(n)
+ n = tonode(n)
+ if n then
+ local fields = fieldtypes[n.id]
+ if fields then
+ local result = { }
+ for field, fieldtype in sortedhash(fields) do
+ local value = n[field]
+ if value then
+ if canbeignored[field] then
+ value = nil
+ elseif canbezero[fieldtype] and value == 0 then
+ value = nil
+ elseif canbelist[fieldtype] then
+ value = canbelist[fieldtype]
+ end
+ if value then
+ result[field] = value
+ end
+ end
+ end
+ local id = n.id
+ if id then
+ id = nodecodes[id]
+ result.id = id
+ local subtype = n.subtype
+ if subtype then
+ local subtypes = subtypes[id]
+ if subtypes then
+ result.subtype = subtypes[subtype]
+ end
+ end
+ end
+ return result
+ end
+ end
+end
+
+nodes.astable = astable
+
+setinspector("node",function(v) if is_node(v) then printtable(astable(v),tostring(v)) return true end end)
+
+local function to_table(n,flat,verbose,noattributes,done)
+ local d = tonut(n)
+ if done[d] then
+ return f_recurse(d)
+ else
+ done[d] = true
+ local fields = fieldtypes[n.id]
+ if fields then
+ local result = { }
+ for field, fieldtype in sortedhash(fields) do
+ local value = n[field]
+ if value then
+ if fieldtype == "attribute_list" or fieldtype == "attribute" then
+ if noattributes then
+ result[value] = canbeignored[value]
+ else
+ result[value] = to_table(value,flat,verbose,noattributes,done)
+ end
+ elseif canbeignored[field] then
+ result[value] = canbeignored[value]
+ elseif not verbose and canbezero[fieldtype] and value == 0 then
+ value = nil
+ elseif canbelist[fieldtype] then
+ if flat then
+ result[value] = canbelist[value]
+ else
+ result[value] = to_table(value,flat,verbose,noattributes,done)
+ end
+ end
+ if value then
+ result[field] = value
+ end
+ end
+ end
+ if verbose then
+ local id = result.id
+ if id then
+ id = nodecodes[id]
+ result.id = id
+ local subtype = result.subtype
+ if subtype then
+ local subtypes = subtypes[id]
+ if subtypes then
+ result.subtype = subtypes[subtype]
+ end
+ end
+ end
+ for k, v in next, canbechar do
+ local v = result[k]
+ if v then
+ result[k] = f_char(v)
+ end
+ end
+ end
+ return result
+ end
+ end
+end
+
+local function totable(n,flat,verbose,noattributes) -- nicest: n,true,true,true
+ if n then
+ local d = { }
+ if flat then
+ local t, tn = { }, 0
+ while n do
+ tn = tn + 1
+ local nt = to_table(n,flat,verbose,noattributes,d)
+ t[tn] = nt
+ nt.next = nil
+ nt.prev = nil
+ n = n.next
+ end
+ done = nil
+ return t
+ else
+ local t = to_table(n,flat,verbose,noattributes,d)
+ local n = n.next
+ if n then
+ t.next = totable(n,flat,verbose,noattributes,d)
+ end
+ return t
+ end
+ else
+ return { }
+ end
+end
+
+nodes.totable = function(n,...) return totable(tonode(n),...) end
+nodes.totree = function(n) return totable(tonode(n),true,true,true) end -- no attributes, todo: attributes in k,v list
+
+local function key(k)
+ return ((type(k) == "number") and "["..k.."]") or k
+end
+
+function nodes.serialize(root,flat,verbose,noattributes,name)
+ return serialize(totable(tonode(root),flat,verbose,noattributes),name)
+end
+
+function nodes.serializebox(n,flat,verbose,noattributes,name)
+ return serialize(totable(tex.box[n],flat,verbose,noattributes),name)
+end
+
+function nodes.visualizebox(n,flat,verbose,noattributes,name)
+ context.tocontext(totable(tex.box[n],flat,verbose,noattributes),name)
+end
+
+function nodes.list(head,n) -- name might change to nodes.type -- to be checked .. will move to module anyway
+ head = tonode(head)
+ if not n then
+ context.starttyping(true)
+ end
+ while head do
+ local id = head.id
+ context(rep(" ",n or 0) .. tostring(head) .. "\n")
+ if id == hlist_code or id == vlist_code then
+ nodes.list(head.list,(n or 0)+1)
+ end
+ head = head.next
+ end
+ if not n then
+ context.stoptyping(true)
+ end
+end
+
+function nodes.print(head,n)
+ head = tonode(head)
+ while head do
+ local id = head.id
+ logs.writer(string.formatters["%w%S"],n or 0,head)
+ if id == hlist_code or id == vlist_code then
+ nodes.print(head.list,(n or 0)+1)
+ end
+ head = head.next
+ end
+end
+
+-- quick hack, nicer is to have a proper expand per node type already prepared
+
+local function apply(n,action)
+ while n do
+ action(n)
+ local id = n.id
+ if id == hlist_code or id == vlist_code then
+ apply(n.list,action)
+ end
+ n = n.next
+ end
+end
+
+nodes.apply = apply
+
+local nuts = nodes.nuts
+local getid = nuts.getid
+local getlist = nuts.getlist
+local getnext = nuts.getnext
+
+local function apply(n,action)
+ while n do
+ action(n)
+ local id = getid(n)
+ if id == hlist_code or id == vlist_code then
+ local list = getlist(n,action)
+ if list then
+ apply(list,action)
+ end
+ end
+ n = getnext(n)
+ end
+end
+
+nuts.apply = apply
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-shp.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-shp.lmt
new file mode 100644
index 00000000000..64508ecc746
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-shp.lmt
@@ -0,0 +1,115 @@
+if not modules then modules = { } end modules ['node-shp'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, type = next, type
+local format = string.format
+local setmetatableindex = table.setmetatableindex
+
+local nodes = nodes
+local tasks = nodes.tasks
+local handlers = nodes.handlers
+
+local nodecodes = nodes.nodecodes
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local nextnode = nuts.traversers.node
+local getlist = nuts.getlist
+local getbox = nuts.getbox
+
+local implement = interfaces.implement
+
+local actions = tasks.actions("shipouts")
+
+function handlers.finalizebox(box)
+ actions(getbox(box)) -- nut
+end
+
+handlers.cleanuppage = nuts.flatten_discretionaries
+
+-- interface
+
+implement {
+ name = "finalizebox",
+ arguments = "integer",
+ actions = handlers.finalizebox,
+}
+
+-- just in case we want to optimize lookups:
+
+local frequencies = { }
+
+nodes.tracers.frequencies = frequencies
+
+local data = { }
+local done = false
+
+setmetatableindex(data,function(t,k)
+ local v = { }
+ setmetatableindex(v,function(t,k)
+ local v = { }
+ t[k] = v
+ setmetatableindex(v,function(t,k)
+ t[k] = 0
+ return 0
+ end)
+ return v
+ end)
+ t[k] = v
+ return v
+end)
+
+local function count(head,data,subcategory)
+ -- no components, pre, post, replace .. can maybe an option .. but
+ -- we use this for optimization so it makes sense to look the the
+ -- main node only
+ for n, id in nextnode, tonut(head) do
+ local dn = data[nodecodes[id]] -- we could use id and then later convert to nodecodes
+ dn[subcategory] = dn[subcategory] + 1
+ if id == hlist_code or id == vlist_code then
+ count(getlist(n),data,subcategory)
+ end
+ end
+end
+
+local function register(category,subcategory)
+ return function(head)
+ done = true
+ count(head,data[category],subcategory)
+ return head, false
+ end
+end
+
+frequencies.register = register
+frequencies.filename = nil
+
+trackers.register("nodes.frequencies",function(v)
+ if type(v) == "string" then
+ frequencies.filename = v
+ end
+ handlers.frequencies_shipouts_before = register("shipouts", "begin")
+ handlers.frequencies_shipouts_after = register("shipouts", "end")
+ handlers.frequencies_processors_before = register("processors", "begin")
+ handlers.frequencies_processors_after = register("processors", "end")
+ tasks.prependaction("shipouts", "before", "nodes.handlers.frequencies_shipouts_before")
+ tasks.appendaction ("shipouts", "after", "nodes.handlers.frequencies_shipouts_after")
+ tasks.prependaction("processors", "before", "nodes.handlers.frequencies_processors_before")
+ tasks.appendaction ("processors", "after", "nodes.handlers.frequencies_processors_after")
+end)
+
+statistics.register("node frequencies", function()
+ if done then
+ local filename = frequencies.filename or (tex.jobname .. "-frequencies.lua")
+ io.savedata(filename,table.serialize(data,true))
+ return format("saved in %q",filename)
+ end
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-snp.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-snp.lmt
new file mode 100644
index 00000000000..8c0237a5c7d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-snp.lmt
@@ -0,0 +1,69 @@
+if not modules then modules = { } end modules ['node-snp'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+if not nodes then
+ nodes = { } -- also loaded in mtx-timing
+end
+
+local snapshots = { }
+nodes.snapshots = snapshots
+
+local status = status
+
+local nodeusage = nodes.pool and nodes.pool.usage
+local nodestock = nodes.pool and nodes.pool.stock
+local clock = os.gettimeofday or os.clock -- should go in environment
+local lasttime = clock()
+local samples = { }
+
+function snapshots.takesample(comment)
+ if nodeusage then
+ local c = clock()
+ samples[#samples+1] = {
+ nodes = nodeusage(),
+ stock = nodestock(),
+ texcallbacks = status.getcallbackstate(),
+ mpcallbacks = mplib.getcallbackstate(),
+ backendcallbacks = backends.getcallbackstate(),
+ luavariables = status.getluastate(),
+ texvariables = status.gettexstate(),
+ comment = comment,
+ variables = {
+ lasttime = c,
+ elapsed = c - lasttime,
+ },
+ memories = {
+ pool = status.getpoolstate(),
+ hash = status.gethashstate(),
+ lookup = status.getlookupstate(),
+ node = status.getnodestate(),
+ token = status.gettokenstate(),
+ buffer = status.getbufferstate(),
+ input = status.getinputstate(),
+ file = status.getfilestate(),
+ nest = status.getneststate(),
+ parameter = status.getparameterstate(),
+ save = status.getsavestate(),
+ expand = status.getexpandstate(),
+ read = status.getreadstate(),
+ font = status.getfontstate(),
+ language = status.getlanguagestate(),
+ mark = status.getmarkstate(),
+ sparse = status.getsparsestate(),
+ },
+ }
+ end
+end
+
+function snapshots.getsamples()
+ return samples -- one return value !
+end
+
+function snapshots.resetsamples()
+ samples = { }
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-syn.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-syn.lmt
new file mode 100644
index 00000000000..ce5f0d2ddf4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-syn.lmt
@@ -0,0 +1,782 @@
+if not modules then modules = { } end modules ['node-syn'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Because we have these fields in some node that are used by synctex, and because
+-- some users seem to like that feature, I decided to implement a variant that might
+-- work out better for ConTeXt. This is experimental code. I don't use it myself so
+-- it will take a while to mature. There will be some helpers that one can use in
+-- more complex situations like included xml files. Currently (somewhere else) we
+-- take care of valid files, that is: we prohibit access to files in the tree
+-- because we don't want users to mess up styles.
+--
+-- It is unclear how the output gets interpreted but by reverse engineering (and
+-- stripping) the file generated by generic synctex, I got there eventually. For
+-- instance, we only need to be able to go back to a place where text is entered,
+-- but still we need all that redundant box wrapping. Anyway, I was able to get a
+-- minimal output and cross my fingers that the parser used in editors is not
+-- changed in fundamental ways.
+--
+-- I only tested SumatraPDF with SciTE, for which one needs to configure in the
+-- viewer:
+--
+-- InverseSearchCmdLine = c:\data\system\scite\wscite\scite.exe "%f" "-goto:%l" $
+--
+-- In fact, a way more powerful implementation would have been not to add a library
+-- to a viewer, but letthe viewer call an external program:
+--
+-- InverseSearchCmdLine = mtxrun.exe --script synctex --edit --name="%f" --line="%l" $
+--
+-- which would (re)launch the editor in the right spot. That way we can really
+-- tune well to the macro package used and also avoid the fuzzy heuristics of
+-- the library.
+--
+-- Unfortunately syntex always removes the files at the end and not at the start
+-- (this happens in synctexterminate) so we need to work around that by using an
+-- intermediate file. This is no big deal in context (which has a runner) but
+-- definitely not nice.
+--
+-- The visualizer code is only needed for testing so we don't use fancy colors or
+-- provide more detail. After all we're only interested in rendered source text
+-- anyway. We try to play safe which sometimes means that we'd better no go
+-- somewhere than go someplace wrong.
+--
+-- A previous version had a mode for exporting boxes and such but I removed that
+-- as it made no sense. Also, collecting output in a table was not faster than
+-- directly piping to the file, probably because the amount is not that large. We
+-- keep some left-overs commented.
+--
+-- A significate reduction in file size can be realized when reusing the same
+-- values. Actually that triggered the current approach in ConTeXt. In the latest
+-- synctex parser vertical positions can be repeated by an "=" sign but for some
+-- reason only for that field. It's probably trivial to do that for all of "w h d v
+-- h" but it currently not the case so I'll delay that till all are supported. (We
+-- could benefit a lot from such a repetition scheme but not much from a "v" alone
+-- which -alas- indicates that synctex is still mostly a latex targeted story.)
+--
+-- It's kind of hard to fight the parser because it really wants to go to some file
+-- but maybe some day I can figure it out. Some untagged text (in the pdf) somehow
+-- gets seen as part of the last box. Anonymous content is simply not part of the
+-- concept. Using a dummy name doesn't help either as the editor gets a signal to
+-- open that dummy. Even an empty filename doesn't work.
+--
+-- We output really simple and compact code, like:
+--
+-- SyncTeX Version:1
+-- Input:1:e:/tmp/oeps.tex
+-- Input:2:c:/data/develop/context/sources/klein.tex
+-- Output:pdf
+-- Magnification:1000
+-- Unit:1
+-- X Offset:0
+-- Y Offset:0
+-- Content:
+-- !160
+-- {1
+-- h0,0:0,0,0,0,0
+-- v0,0:0,55380990:39158276,55380990,0
+-- h2,1:4661756,9176901:27969941,655360,327680
+-- h2,2:4661756,10125967:26048041,655360,327680
+-- h2,3:30962888,10125967:1668809,655360,327680
+-- h2,3:4661756,11075033:23142527,655360,327680
+-- h2,4:28046650,11075033:4585047,655360,327680
+-- h2,4:4661756,12024099:22913954,655360,327680
+-- h2,5:27908377,12024099:4723320,655360,327680
+-- h2,5:4661756,12973165:22918783,655360,327680
+-- h2,6:27884864,12973165:4746833,655360,327680
+-- h2,6:4661756,13922231:18320732,655360,327680
+-- ]
+-- !533
+-- }1
+-- Input:3:c:/data/develop/context/sources/ward.tex
+-- !57
+-- {2
+-- h0,0:0,0,0,0,0
+-- v0,0:0,55380990:39158276,55380990,0
+-- h3,1:4661756,9176901:18813145,655360,327680
+-- h3,2:23713999,9176901:8917698,655360,327680
+-- h3,2:4661756,10125967:10512978,655360,327680
+-- h3,3:15457206,10125967:17174491,655360,327680
+-- h3,3:4661756,11075033:3571223,655360,327680
+-- h3,4:8459505,11075033:19885281,655360,327680
+-- h3,5:28571312,11075033:4060385,655360,327680
+-- h3,5:4661756,12024099:15344870,655360,327680
+-- ]
+-- !441
+-- }2
+-- !8
+-- Postamble:
+-- Count:22
+-- !23
+-- Post scriptum:
+--
+-- But for some reason, when the pdf file has some extra content (like page numbers)
+-- the main document is consulted. Bah. It would be nice to have a mode for *only*
+-- looking at marked areas. It somehow works not but maybe depends on the parser.
+--
+-- Supporting reuseable objects makes not much sense as these are often graphics or
+-- ornamental. They can not have hyperlinks etc (at least not without some hackery
+-- which I'm not willing to do) so basically they are sort of useless for text.
+--
+-- Some generic (more clever code) has been removed as I don't see things change
+-- that much.
+
+local type, rawset = type, rawset
+local concat = table.concat
+local formatters = string.formatters
+local replacesuffix, suffixonly, nameonly = file.replacesuffix, file.suffix, file.nameonly
+local openfile, renamefile, removefile = io.open, os.rename, os.remove
+
+local report_system = logs.reporter("system")
+
+local tex = tex
+local texget = tex.get
+
+local nuts = nodes.nuts
+
+local getid = nuts.getid
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getnext = nuts.getnext
+local getwhd = nuts.getwhd
+local getwidth = nuts.getwidth
+local getsubtype = nuts.getsubtype
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local penalty_code = nodecodes.penalty
+local kern_code = nodecodes.kern
+----- rule_code = nodecodes.rule
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local dir_code = nodecodes.dir
+local fontkern_code = kerncodes.fontkern
+
+local cancel_code = nodes.dircodes.cancel
+
+local insert_before = nuts.insert_before
+local insert_after = nuts.insert_after
+
+local nodepool = nuts.pool
+local new_latelua = nodepool.latelua
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+
+local getdimensions = nuts.dimensions
+local getrangedimensions = nuts.rangedimensions
+
+local getsynctexfields = nuts.getsynctexfields or nuts.get_synctex_fields
+local forcesynctextag = tex.forcesynctextag or tex.force_synctex_tag
+local forcesynctexline = tex.forcesynctexline or tex.force_synctex_line
+local getsynctexline = tex.getsynctexline or tex.get_synctex_line
+local setsynctexmode = tex.setsynctexmode or tex.set_synctex_mode
+
+local foundintree = resolvers.foundintree
+
+local getpagedimensions = layouts.getpagedimensions
+
+local eol = "\010"
+
+----- f_glue = formatters["g%i,%i:%i,%i\010"]
+----- f_glyph = formatters["x%i,%i:%i,%i\010"]
+----- f_kern = formatters["k%i,%i:%i,%i:%i\010"]
+----- f_rule = formatters["r%i,%i:%i,%i:%i,%i,%i\010"]
+----- f_form = formatters["f%i,%i,%i\010"]
+local z_hlist = "[0,0:0,0:0,0,0\010"
+local z_vlist = "(0,0:0,0:0,0,0\010"
+----- z_xform = "<0,0:0,0,0\010" -- or so
+local s_hlist = "]\010"
+local s_vlist = ")\010"
+----- s_xform = ">\010"
+local f_hlist_1 = formatters["h%i,%i:%i,%i:%i,%i,%i\010"]
+local f_hlist_2 = formatters["h%i,%i:%i,%s:%i,%i,%i\010"]
+local f_vlist_1 = formatters["v%i,%i:%i,%i:%i,%i,%i\010"]
+local f_vlist_2 = formatters["v%i,%i:%i,%s:%i,%i,%i\010"]
+
+local synctex = luatex.synctex or { }
+luatex.synctex = synctex
+
+local getpos ; getpos = function() getpos = job.positions.getpos return getpos() end
+
+-- status stuff
+
+local enabled = false
+local paused = 0
+local used = false
+local never = false
+
+-- the file name stuff
+
+local noftags = 0
+local stnums = { }
+local nofblocked = 0
+local blockedfilenames = { }
+local blockedsuffixes = {
+ mkii = true,
+ mkiv = true,
+ mkvi = true,
+ mkxl = true,
+ mklx = true,
+ mkix = true,
+ mkxi = true,
+ -- lfg = true,
+}
+
+local sttags = table.setmetatableindex(function(t,name)
+ if blockedsuffixes[suffixonly(name)] then
+ -- Just so that I don't get the ones on my development tree.
+ nofblocked = nofblocked + 1
+ return 0
+ elseif blockedfilenames[nameonly(name)] then
+ -- So we can block specific files.
+ nofblocked = nofblocked + 1
+ return 0
+ elseif foundintree(name) then
+ -- One shouldn't edit styles etc this way.
+ nofblocked = nofblocked + 1
+ return 0
+ else
+ noftags = noftags + 1
+ t[name] = noftags
+ stnums[noftags] = name
+ return noftags
+ end
+end)
+
+function synctex.blockfilename(name)
+ blockedfilenames[nameonly(name)] = name
+end
+
+function synctex.setfilename(name,line)
+ if paused == 0 and name then
+ forcesynctextag(sttags[name])
+ if line then
+ forcesynctexline(line)
+ end
+ end
+end
+
+function synctex.resetfilename()
+ if paused == 0 then
+ forcesynctextag(0)
+ forcesynctexline(0)
+ end
+end
+
+do
+
+ local nesting = 0
+ local ignored = false
+
+ function synctex.pushline()
+ nesting = nesting + 1
+ if nesting == 1 then
+ local l = getsynctexline()
+ ignored = l and l > 0
+ if not ignored then
+ forcesynctexline(texget("inputlineno"))
+ end
+ end
+ end
+
+ function synctex.popline()
+ if nesting == 1 then
+ if not ignored then
+ forcesynctexline()
+ ignored = false
+ end
+ end
+ nesting = nesting - 1
+ end
+
+end
+
+-- the node stuff
+
+local filehandle = nil
+local nofsheets = 0
+local nofobjects = 0
+local last = 0
+local filesdone = 0
+local tmpfile = false
+local logfile = false
+
+local function writeanchor()
+ local size = filehandle:seek("end")
+ filehandle:write("!",size-last,eol)
+ last = size
+end
+
+local function writefiles()
+ local total = #stnums
+ if filesdone < total then
+ for i=filesdone+1,total do
+ filehandle:write("Input:",i,":",stnums[i],eol)
+ end
+ filesdone = total
+ end
+end
+
+local function makenames()
+ logfile = replacesuffix(tex.jobname,"synctex")
+ tmpfile = replacesuffix(logfile,"syncctx")
+end
+
+local function flushpreamble()
+ makenames()
+ filehandle = openfile(tmpfile,"wb")
+ if filehandle then
+ filehandle:write("SyncTeX Version:1",eol)
+ writefiles()
+ filehandle:write("Output:pdf",eol)
+ filehandle:write("Magnification:1000",eol)
+ filehandle:write("Unit:1",eol)
+ filehandle:write("X Offset:0",eol)
+ filehandle:write("Y Offset:0",eol)
+ filehandle:write("Content:",eol)
+ flushpreamble = function()
+ writefiles()
+ return filehandle
+ end
+ else
+ enabled = false
+ end
+ return filehandle
+end
+
+function synctex.wrapup()
+ if tmpfile then
+ renamefile(tmpfile,logfile)
+ tmpfile = nil
+ end
+end
+
+local function flushpostamble()
+ if not filehandle then
+ return
+ end
+ writeanchor()
+ filehandle:write("Postamble:",eol)
+ filehandle:write("Count:",nofobjects,eol)
+ writeanchor()
+ filehandle:write("Post scriptum:",eol)
+ filehandle:close()
+ enabled = false
+end
+
+local x_hlist do
+
+ local function doaction_1(t,l,w,h,d)
+ local pagewidth, pageheight = getpagedimensions()
+ local x, y = getpos()
+ filehandle:write(f_hlist_1(t,l,x,pageheight-y,w,h,d))
+ nofobjects = nofobjects + 1
+ end
+
+ local lasty = false
+
+ local function doaction_2(t,l,w,h,d)
+ local pagewidth, pageheight = getpagedimensions()
+ local x, y = getpos()
+ y = pageheight - y
+ filehandle:write(f_hlist_2(t,l,x,y == lasty and "=" or y,w,h,d))
+ lasty = y
+ nofobjects = nofobjects + 1
+ end
+
+ local doaction = doaction_1
+
+ x_hlist = function(head,current,t,l,w,h,d)
+ if filehandle then
+ return insert_before(head,current,new_latelua(function() doaction(t,l,w,h,d) end))
+ else
+ return head
+ end
+ end
+
+ directives.register("system.synctex.compression", function(v)
+ doaction = tonumber(v) == 2 and doaction_2 or doaction_1
+ end)
+
+end
+
+-- color is already handled so no colors
+
+local collect = nil
+local fulltrace = false
+local trace = false
+local height = 10 * 65536
+local depth = 5 * 65536
+local traceheight = 32768
+local tracedepth = 32768
+
+trackers.register("system.synctex.visualize", function(v)
+ trace = v
+ fulltrace = v == "real"
+end)
+
+local function inject(head,first,last,tag,line)
+ local w, h, d = getdimensions(first,getnext(last))
+ if h < height then
+ h = height
+ end
+ if d < depth then
+ d = depth
+ end
+ if trace then
+ head = insert_before(head,first,new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth))
+ head = insert_before(head,first,new_kern(-w))
+ end
+ head = x_hlist(head,first,tag,line,w,h,d)
+ return head
+end
+
+local function collect_min(head)
+ local current = head
+ while current do
+ local id = getid(current)
+ if id == glyph_code then
+ local first = current
+ local last = current
+ local tag = 0
+ local line = 0
+ while true do
+ if id == glyph_code then
+ local tc, lc = getsynctexfields(current)
+ if tc and tc > 0 then
+ tag = tc
+ line = lc
+ end
+ last = current
+ elseif id == disc_code or (id == kern_code and getsubtype(current) == fontkern_code) then
+ last = current
+ else
+ if tag > 0 then
+ head = inject(head,first,last,tag,line)
+ end
+ break
+ end
+ current = getnext(current)
+ if current then
+ id = getid(current)
+ else
+ if tag > 0 then
+ head = inject(head,first,last,tag,line)
+ end
+ return head
+ end
+ end
+ end
+ -- pick up (as id can have changed)
+ if id == hlist_code or id == vlist_code then
+ local list = getlist(current)
+ if list then
+ local l = collect(list)
+ if l ~= list then
+ setlist(current,l)
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ return head
+end
+
+local function inject(parent,head,first,last,tag,line)
+ local w, h, d = getrangedimensions(parent,first,getnext(last))
+ if h < height then
+ h = height
+ end
+ if d < depth then
+ d = depth
+ end
+ if trace then
+ head = insert_before(head,first,new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth))
+ head = insert_before(head,first,new_kern(-w))
+ end
+ head = x_hlist(head,first,tag,line,w,h,d)
+ return head
+end
+
+local function collect_max(head,parent)
+ local current = head
+ while current do
+ local id = getid(current)
+ if id == glyph_code then
+ local first = current
+ local last = current
+ local tag = 0
+ local line = 0
+ while true do
+ if id == glyph_code then
+ local tc, lc = getsynctexfields(current)
+ if tc and tc > 0 then
+ if tag > 0 and (tag ~= tc or line ~= lc) then
+ head = inject(parent,head,first,last,tag,line)
+ first = current
+ end
+ tag = tc
+ line = lc
+ last = current
+ else
+ if tag > 0 then
+ head = inject(parent,head,first,last,tag,line)
+ tag = 0
+ end
+ first = nil
+ last = nil
+ end
+ elseif id == disc_code then
+ if not first then
+ first = current
+ end
+ last = current
+ elseif id == kern_code and getsubtype(current) == fontkern_code then
+ if first then
+ last = current
+ end
+ elseif id == glue_code then
+ if tag > 0 then
+ local tc, lc = getsynctexfields(current)
+ if tc and tc > 0 then
+ if tag ~= tc or line ~= lc then
+ head = inject(parent,head,first,last,tag,line)
+ tag = 0
+ break
+ end
+ else
+ head = inject(parent,head,first,last,tag,line)
+ tag = 0
+ break
+ end
+ else
+ tag = 0
+ break
+ end
+ id = nil -- so no test later on
+ elseif id == penalty_code then
+ -- go on (and be nice for math)
+ else
+ if tag > 0 then
+ head = inject(parent,head,first,last,tag,line)
+ tag = 0
+ end
+ break
+ end
+ current = getnext(current)
+ if current then
+ id = getid(current)
+ else
+ if tag > 0 then
+ head = inject(parent,head,first,last,tag,line)
+ end
+ return head
+ end
+ end
+ end
+ -- pick up (as id can have changed)
+ if id == hlist_code or id == vlist_code then
+ local list = getlist(current)
+ if list then
+ local l = collect(list,current)
+ if l and l ~= list then
+ setlist(current,l)
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ return head
+end
+
+collect = collect_max
+
+function synctex.collect(head,where)
+ if enabled and where ~= "object" then
+ return collect(head,head)
+ else
+ return head
+ end
+end
+
+-- also no solution for bad first file resolving in sumatra
+
+function synctex.start()
+ if enabled then
+ nofsheets = nofsheets + 1 -- could be realpageno
+ if flushpreamble() then
+ writeanchor()
+ filehandle:write("{",nofsheets,eol)
+ -- this seems to work:
+ local pagewidth, pageheight = getpagedimensions()
+ filehandle:write(z_hlist)
+ filehandle:write(f_vlist_1(0,0,0,pageheight,pagewidth,pageheight,0))
+ end
+ end
+end
+
+function synctex.stop()
+ if enabled then
+ -- filehandle:write(s_vlist,s_hlist)
+ filehandle:write(s_hlist)
+ writeanchor()
+ filehandle:write("}",nofsheets,eol)
+ nofobjects = nofobjects + 2
+ end
+end
+
+local enablers = { }
+local disablers = { }
+
+function synctex.registerenabler(f)
+ enablers[#enablers+1] = f
+end
+
+function synctex.registerdisabler(f)
+ disablers[#disablers+1] = f
+end
+
+function synctex.enable()
+ if not never and not enabled then
+ enabled = true
+ setsynctexmode(3) -- we want details
+ if not used then
+ nodes.tasks.enableaction("shipouts","luatex.synctex.collect")
+ report_system("synctex functionality is enabled, expect 5-10 pct runtime overhead!")
+ used = true
+ end
+ for i=1,#enablers do
+ enablers[i](true)
+ end
+ end
+end
+
+function synctex.disable()
+ if enabled then
+ setsynctexmode(0)
+ report_system("synctex functionality is disabled!")
+ enabled = false
+ for i=1,#disablers do
+ disablers[i](false)
+ end
+ end
+end
+
+function synctex.finish()
+ if enabled then
+ flushpostamble()
+ else
+ makenames()
+ removefile(logfile)
+ removefile(tmpfile)
+ end
+end
+
+local filename = nil
+
+function synctex.pause()
+ paused = paused + 1
+ if enabled and paused == 1 then
+ setsynctexmode(0)
+ end
+end
+
+function synctex.resume()
+ if enabled and paused == 1 then
+ setsynctexmode(3)
+ end
+ paused = paused - 1
+end
+
+-- not the best place
+
+luatex.registerstopactions(synctex.finish)
+
+statistics.register("synctex tracing",function()
+ if used then
+ return string.format("%i referenced files, %i files ignored, %i objects flushed, logfile: %s",
+ noftags,nofblocked,nofobjects,logfile)
+ end
+end)
+
+local implement = interfaces.implement
+local variables = interfaces.variables
+
+function synctex.setup(t)
+ if t.state == variables.never then
+ synctex.disable() -- just in case
+ never = true
+ return
+ end
+ if t.method == variables.max then
+ collect = collect_max
+ else
+ collect = collect_min
+ end
+ if t.state == variables.start then
+ synctex.enable()
+ else
+ synctex.disable()
+ end
+end
+
+implement {
+ name = "synctexblockfilename",
+ arguments = "string",
+ actions = synctex.blockfilename,
+}
+
+implement {
+ name = "synctexsetfilename",
+ arguments = "string",
+ actions = synctex.setfilename,
+}
+
+implement {
+ name = "synctexresetfilename",
+ actions = synctex.resetfilename,
+}
+
+implement {
+ name = "setupsynctex",
+ actions = synctex.setup,
+ arguments = {
+ {
+ { "state" },
+ { "method" },
+ },
+ },
+}
+
+implement {
+ name = "synctexpause",
+ actions = synctex.pause,
+}
+
+implement {
+ name = "synctexresume",
+ actions = synctex.resume,
+}
+
+implement {
+ name = "synctexpushline",
+ actions = synctex.pushline,
+}
+
+implement {
+ name = "synctexpopline",
+ actions = synctex.popline,
+}
+
+implement {
+ name = "synctexdisable",
+ actions = synctex.disable,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-tex.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-tex.lmt
new file mode 100644
index 00000000000..aae084740dc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-tex.lmt
@@ -0,0 +1,39 @@
+if not modules then modules = { } end modules ['node-tex'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+builders = builders or { }
+local kernel = builders.kernel or { }
+builders.kernel = kernel
+
+local nuts = nodes.nuts
+
+local hyphenate = language.hyphenate
+
+local hyphenating = nuts.hyphenating
+local ligaturing = nuts.ligaturing
+local kerning = nuts.kerning
+
+function kernel.hyphenation(head)
+ return (hyphenate(head)) -- nodes !
+end
+
+function kernel.hyphenating(head)
+ return (hyphenating(head))
+end
+
+function kernel.ligaturing(head)
+ return (ligaturing(head))
+end
+
+function kernel.kerning(head)
+ return (kerning(head))
+end
+
+callbacks.register('hyphenate' , false, "normal hyphenation routine, called elsewhere")
+callbacks.register('ligaturing', false, "normal ligaturing routine, called elsewhere")
+callbacks.register('kerning' , false, "normal kerning routine, called elsewhere")
diff --git a/Master/texmf-dist/tex/context/base/mkxl/node-tra.lmt b/Master/texmf-dist/tex/context/base/mkxl/node-tra.lmt
new file mode 100644
index 00000000000..6060522bec4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/node-tra.lmt
@@ -0,0 +1,739 @@
+if not modules then modules = { } end modules ['node-tra'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>This is rather experimental. We need more control and some of this
+might become a runtime module instead. This module will be cleaned up!</p>
+--ldx]]--
+
+local next = next
+local utfchar = utf.char
+local format, match, gmatch, concat, rep = string.format, string.match, string.gmatch, table.concat, string.rep
+local lpegmatch = lpeg.match
+local clock = os.gettimeofday or os.clock -- should go in environment
+
+local report_nodes = logs.reporter("nodes","tracing")
+
+local nodes, node, context = nodes, node, context
+
+local texgetattribute = tex.getattribute
+
+local tracers = nodes.tracers or { }
+nodes.tracers = tracers
+
+local tasks = nodes.tasks or { }
+nodes.tasks = tasks
+
+local handlers = nodes.handlers or {}
+nodes.handlers = handlers
+
+local injections = nodes.injections or { }
+nodes.injections = injections
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+local tonode = nuts.tonode
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local getdisc = nuts.getdisc
+local setattr = nuts.setattr
+local setattrs = nuts.setattrs
+local getglue = nuts.getglue
+local isglyph = nuts.isglyph
+local getdirection = nuts.getdirection
+local getwidth = nuts.getwidth
+
+local flush_list = nuts.flush_list
+local count_nodes = nuts.countall
+local used_nodes = nuts.usedlist
+
+local nextnode = nuts.traversers.node
+local nextglyph = nuts.traversers.glyph
+
+local shownodes = nuts.show
+
+local d_tostring = nuts.tostring
+
+local nutpool = nuts.pool
+local new_rule = nutpool.rule
+
+local nodecodes = nodes.nodecodes
+local whatsitcodes = nodes.whatsitcodes
+local fillcodes = nodes.fillcodes
+
+local subtypes = nodes.subtypes
+
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local kern_code = nodecodes.kern
+local rule_code = nodecodes.rule
+local dir_code = nodecodes.dir
+local par_code = nodecodes.par
+local whatsit_code = nodecodes.whatsit
+
+local dimenfactors = number.dimenfactors
+local formatters = string.formatters
+
+local start_of_par = nuts.start_of_par
+
+-- this will be reorganized:
+
+function nodes.showlist(head, message)
+ if message then
+ report_nodes(message)
+ end
+ for n in nextnode, tonut(head) do
+ report_nodes(d_tostring(n))
+ end
+end
+
+function nodes.handlers.checkglyphs(head,message)
+ local h = tonut(head) -- tonut needed?
+ local t = { }
+ local n = 0
+ local f = formatters["%U:%s"]
+ for g, char, font in nextglyph, h do
+ n = n + 1
+ t[n] = f(char,getsubtype(g))
+ end
+ if n == 0 then
+ -- nothing to report
+ elseif message and message ~= "" then
+ report_nodes("%s, %s glyphs: % t",message,n,t)
+ else
+ report_nodes("%s glyphs: % t",n,t)
+ end
+ return false
+end
+
+local fontcharacters -- = fonts.hashes.descriptions
+
+local function tosequence(start,stop,compact)
+ if start then
+ if not fontcharacters then
+ fontcharacters = fonts.hashes.descriptions
+ if not fontcharacters then
+ return "[no char data]"
+ end
+ end
+ local f_sequence = formatters["U+%04X:%s"]
+ local f_subrange = formatters["[[ %s ][ %s ][ %s ]]"]
+ start = tonut(start)
+ stop = stop and tonut(stop)
+ local t = { }
+ local n = 0
+ while start do
+ local c, id = isglyph(start)
+ if c then
+ local u = fontcharacters[id][c] -- id == font id
+ u = u and u.unicode or c
+ if type(u) == "table" then
+ local tt = { }
+ for i=1,#u do
+ local c = u[i]
+ tt[i] = compact and utfchar(c) or f_sequence(c,utfchar(c))
+ end
+ n = n + 1 ; t[n] = "(" .. concat(tt," ") .. ")"
+ else
+ n = n + 1 ; t[n] = compact and utfchar(c) or f_sequence(c,utfchar(c))
+ end
+ elseif id == disc_code then
+ local pre, post, replace = getdisc(start)
+ t[#t+1] = f_subrange(pre and tosequence(pre),post and tosequence(post),replace and tosequence(replace))
+ elseif id == rule_code then
+ n = n + 1 ; t[n] = compact and "|" or nodecodes[id] or "?"
+ elseif id == dir_code then
+ local d, p = getdirection(start)
+ n = n + 1 ; t[n] = "[<" .. (p and "-" or "+") .. d .. ">]" -- todo l2r etc
+ elseif id == par_code and start_of_par(current) then
+ n = n + 1 ; t[n] = "[<" .. getdirection(start) .. ">]" -- todo l2r etc
+ elseif compact then
+ n = n + 1 ; t[n] = "[]"
+ else
+ n = n + 1 ; t[n] = nodecodes[id]
+ end
+ if start == stop then
+ break
+ else
+ start = getnext(start)
+ end
+ end
+ if compact then
+ return concat(t)
+ else
+ return concat(t," ")
+ end
+ else
+ return "[empty]"
+ end
+end
+
+nodes.tosequence = tosequence
+nuts .tosequence = tosequence
+
+function nodes.report(t)
+ report_nodes("output %a, %s nodes",tex.getoutputactive(),count_nodes(t))
+end
+
+function nodes.packlist(head)
+ local t = { }
+ for n in nextnode, tonut(head) do
+ t[#t+1] = d_tostring(n)
+ end
+ return t
+end
+
+function nodes.idstostring(head,tail)
+ head = tonut(head)
+ tail = tail and tonut(tail)
+ local t = { }
+ local last_id = nil
+ local last_n = 0
+ local f_two = formatters["[%s*%s]"]
+ local f_one = formatters["[%s]"]
+ for n, id, subtype in nextnode, head do
+ if id == whatsit_code then
+ id = whatsitcodes[subtype]
+ else
+ id = nodecodes[id]
+ end
+ if not last_id then
+ last_id = id
+ last_n = 1
+ elseif last_id == id then
+ last_n = last_n + 1
+ else
+ if last_n > 1 then
+ t[#t+1] = f_two(last_n,last_id)
+ else
+ t[#t+1] = f_one(last_id)
+ end
+ last_id = id
+ last_n = 1
+ end
+ if n == tail then
+ break
+ end
+ end
+ if not last_id then
+ t[#t+1] = "no nodes"
+ else
+ if last_n > 1 then
+ t[#t+1] = f_two(last_n,last_id)
+ else
+ t[#t+1] = f_one(last_id)
+ end
+ end
+ return concat(t," ")
+end
+
+function nodes.idsandsubtypes(head)
+ local h = tonut(head)
+ local t = { }
+ local f = formatters["%s:%s"]
+ for n, id, subtype in nextnode, h do
+ local c = nodecodes[id]
+ if subtype then
+ t[#t+1] = f(c,subtypes[id][subtype])
+ else
+ t[#t+1] = c
+ end
+ end
+ return concat(t, " ")
+end
+
+-- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks
+-- head = tonut(head)
+-- tail = tonut(tail)
+-- local n = head
+-- while n.next do
+-- n = n.next
+-- end
+-- local t, last_id, last_n = { }, nil, 0
+-- while n do
+-- local id = n.id
+-- if not last_id then
+-- last_id, last_n = id, 1
+-- elseif last_id == id then
+-- last_n = last_n + 1
+-- else
+-- if last_n > 1 then
+-- t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?")
+-- else
+-- t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?")
+-- end
+-- last_id, last_n = id, 1
+-- end
+-- if n == head then
+-- break
+-- end
+-- n = getprev(n)
+-- end
+-- if not last_id then
+-- t[#t+1] = "no nodes"
+-- elseif last_n > 1 then
+-- t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?")
+-- else
+-- t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?")
+-- end
+-- return table.concat(table.reversed(t)," ")
+-- end
+
+local function showsimplelist(h,depth,n)
+ h = h and tonut(h)
+ while h do
+ report_nodes("% w%s",n,d_tostring(h))
+ if not depth or n < depth then
+ local id = getid(h)
+ if id == hlist_code or id == vlist_code then
+ showsimplelist(getlist(h),depth,n+1)
+ end
+ end
+ h = getnext(h)
+ end
+end
+
+nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end
+
+local function listtoutf(h,joiner,textonly,last,nodisc)
+ local w = { }
+ local n = 0
+ local g = formatters["<%i>"]
+ local d = formatters["[%s|%s|%s]"]
+ while h do
+ local c, id = isglyph(h)
+ if c then
+ n = n + 1 ; w[n] = c >= 0 and utfchar(c) or g(c)
+ if joiner then
+ n = n + 1 ; w[n] = joiner
+ end
+ elseif id == disc_code then
+ local pre, pos, rep = getdisc(h)
+ if not nodisc then
+ n = n + 1 ; w[n] = d(
+ pre and listtoutf(pre,joiner,textonly) or "",
+ pos and listtoutf(pos,joiner,textonly) or "",
+ rep and listtoutf(rep,joiner,textonly) or ""
+ )
+ elseif rep then
+ n = n + 1 ; w[n] = listtoutf(rep,joiner,textonly) or ""
+ end
+ if joiner then
+ n = n + 1 ; w[n] = joiner
+ end
+ elseif textonly then
+ if id == glue_code then
+ if getwidth(h) > 0 then
+ n = n + 1 ; w[n] = " "
+ end
+ elseif id == hlist_code or id == vlist_code then
+ n = n + 1 ; w[n] = "["
+ n = n + 1 ; w[n] = listtoutf(getlist(h),joiner,textonly,last,nodisc)
+ n = n + 1 ; w[n] = "]"
+ end
+ else
+ n = n + 1 ; w[n] = "[-]"
+ end
+ if h == last then
+ break
+ else
+ h = getnext(h)
+ end
+ end
+ return concat(w,"",1,(w[n] == joiner) and (n-1) or n)
+end
+
+function nodes.listtoutf(h,joiner,textonly,last,nodisc)
+ if h then
+ local joiner = joiner == true and utfchar(0x200C) or joiner -- zwnj
+ return listtoutf(tonut(h),joiner,textonly,last and tonut(last),nodisc)
+ else
+ return ""
+ end
+end
+
+local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" }
+
+local function showboxes(n,symbol,depth)
+ depth = depth or 0
+ symbol = symbol or "."
+ for n, id, subtype in nextnode, tonut(n) do
+ if id == hlist_code or id == vlist_code then
+ report_nodes(rep(symbol,depth) .. what[subtype] or subtype)
+ showboxes(getlist(n),symbol,depth+1)
+ end
+ end
+end
+
+nodes.showboxes = showboxes
+
+local ptfactor = dimenfactors.pt
+local bpfactor = dimenfactors.bp
+local stripper = lpeg.patterns.stripzeros
+
+local f_f_f = formatters["%0.5Fpt plus %0.5F%s minus %0.5F%s"]
+local f_f_m = formatters["%0.5Fpt plus %0.5F%s minus %0.5Fpt"]
+local f_p_f = formatters["%0.5Fpt plus %0.5Fpt minus %0.5F%s"]
+local f_p_m = formatters["%0.5Fpt plus %0.5Fpt minus %0.5Fpt"]
+local f_f_z = formatters["%0.5Fpt plus %0.5F%s"]
+local f_p_z = formatters["%0.5Fpt plus %0.5Fpt"]
+local f_z_f = formatters["%0.5Fpt minus %0.5F%s"]
+local f_z_m = formatters["%0.5Fpt minus %0.5Fpt"]
+local f_z_z = formatters["%0.5Fpt"]
+
+local tonut = nodes.tonut
+
+local function nodetodimen(n)
+ n = tonut(n)
+ local id = getid(n)
+ if id == kern_code then
+ local width = getwidth(n)
+ if width == 0 then
+ return "0pt"
+ else
+ return f_z_z(width)
+ end
+ elseif id ~= glue_code then
+ return "0pt"
+ end
+ local width, stretch, shrink, stretch_order, shrink_order = getglue(n)
+ stretch = stretch / 65536
+ shrink = shrink / 65536
+ width = width / 65536
+ if stretch_order ~= 0 then
+ if shrink_order ~= 0 then
+ return f_f_f(width,stretch,fillcodes[stretch_order],shrink,fillcodes[shrink_order])
+ elseif shrink ~= 0 then
+ return f_f_m(width,stretch,fillcodes[stretch_order],shrink)
+ else
+ return f_f_z(width,stretch,fillcodes[stretch_order])
+ end
+ elseif shrink_order ~= 0 then
+ if stretch ~= 0 then
+ return f_p_f(width,stretch,shrink,fillcodes[shrink_order])
+ else
+ return f_z_f(width,shrink,fillcodes[shrink_order])
+ end
+ elseif stretch ~= 0 then
+ if shrink ~= 0 then
+ return f_p_m(width,stretch,shrink)
+ else
+ return f_p_z(width,stretch)
+ end
+ elseif shrink ~= 0 then
+ return f_z_m(width,shrink)
+ elseif width == 0 then
+ return "0pt"
+ else
+ return f_z_z(width)
+ end
+end
+
+
+-- number.todimen(123)
+-- number.todimen(123,"cm")
+-- number.todimen(123,false,"%F))
+
+local f_pt = formatters["%p"]
+local f_un = formatters["%F%s"]
+
+dimenfactors[""] = dimenfactors.pt
+
+local function numbertodimen(d,unit,fmt)
+ if not d or d == 0 then
+ if fmt then
+ return formatters[fmt](0,unit or "pt")
+ elseif unit then
+ return 0 .. unit
+ else
+ return "0pt"
+ end
+ elseif fmt then
+ if not unit then
+ unit = "pt"
+ end
+ return formatters[fmt](d*dimenfactors[unit],unit)
+ elseif not unit or unit == "pt" then
+ return f_pt(d)
+ else
+ return f_un(d*dimenfactors[unit],unit)
+ end
+end
+
+number.todimen = numbertodimen
+nodes .todimen = nodetodimen
+
+function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end
+function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end
+function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end
+function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end
+function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end
+function number.toscaledpoints(n) return n .. "sp" end
+function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end
+function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end
+function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end
+function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end
+-------- number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end
+-------- number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end
+
+function nodes.topoints (n,fmt) return nodetodimen(n,"pt",fmt) end
+function nodes.toinches (n,fmt) return nodetodimen(n,"in",fmt) end
+function nodes.tocentimeters (n,fmt) return nodetodimen(n,"cm",fmt) end
+function nodes.tomillimeters (n,fmt) return nodetodimen(n,"mm",fmt) end
+function nodes.toscaledpoints(n,fmt) return nodetodimen(n,"sp",fmt) end
+function nodes.toscaledpoints(n) return n .. "sp" end
+function nodes.tobasepoints (n,fmt) return nodetodimen(n,"bp",fmt) end
+function nodes.topicas (n,fmt) return nodetodimen(n "pc",fmt) end
+function nodes.todidots (n,fmt) return nodetodimen(n,"dd",fmt) end
+function nodes.tociceros (n,fmt) return nodetodimen(n,"cc",fmt) end
+-------- nodes.tonewdidots (n,fmt) return nodetodimen(n,"nd",fmt) end
+-------- nodes.tonewciceros (n,fmt) return nodetodimen(n,"nc",fmt) end
+
+-- stop redefinition
+
+local points = function(n)
+ if not n or n == 0 then
+ return "0pt"
+ elseif type(n) == "number" then
+ return lpegmatch(stripper,format("%.5fpt",n*ptfactor)) -- faster than formatter
+ else
+ return numbertodimen(n,"pt") -- also deals with nodes
+ end
+end
+
+local basepoints = function(n)
+ if not n or n == 0 then
+ return "0bp"
+ elseif type(n) == "number" then
+ return lpegmatch(stripper,format("%.5fbp",n*bpfactor)) -- faster than formatter
+ else
+ return numbertodimen(n,"bp") -- also deals with nodes
+ end
+end
+
+local pts = function(n)
+ if not n or n == 0 then
+ return "0pt"
+ elseif type(n) == "number" then
+ return format("%.5fpt",n*ptfactor) -- faster than formatter
+ else
+ return numbertodimen(n,"pt") -- also deals with nodes
+ end
+end
+
+local nopts = function(n)
+ if not n or n == 0 then
+ return "0"
+ else
+ return format("%.5f",n*ptfactor) -- faster than formatter
+ end
+end
+
+number.points = points
+number.basepoints = basepoints
+number.pts = pts
+number.nopts = nopts
+
+nodes.points = function(n) return numbertodimen(n,"pt") end
+nodes.basepoints = function(n) return numbertodimen(n,"bp") end
+nodes.pts = function(n) return numbertodimen(n,"pt") end
+nodes.nopts = function(n) return format("%.5f",n*ptfactor) end
+
+local colors = { }
+tracers.colors = colors
+
+local unsetvalue = attributes.unsetvalue
+
+local a_color = attributes.private('color')
+local a_colormodel = attributes.private('colormodel')
+local m_color = attributes.list[a_color] or { }
+
+function colors.set(n,c,s,t) -- also unsets !
+ local mc = m_color[c]
+ local nn = tonut(n)
+ if mc then
+ local mm = s or texgetattribute(a_colormodel)
+ if mm <= 0 then
+ mm = 1
+ end
+ -- only here t is dealt with
+ if t then
+ setattrs(nn,a_colormodel,mm,a_color,mc,a_transparency,m_transparency[t] or unsetvalue)
+ else
+ setattrs(nn,a_colormodel,mm,a_color,mc)
+ end
+ else
+ setattr(nn,a_color,unsetvalue)
+ end
+ return n
+end
+
+function colors.setlist(n,c,s,t)
+ local nn = tonut(n)
+ local mc = m_color[c] or unsetvalue
+ local mm = s or texgetattribute(a_colormodel)
+ if mm <= 0 then
+ mm = 1
+ end
+ if t then
+ t = m_transparency[t] or unsetvalue
+ while nn do
+ setattrs(nn,a_colormodel,mm,a_color,mc,a_transparency,t)
+ nn = getnext(nn)
+ end
+ else
+ while nn do
+ setattrs(nn,a_colormodel,mm,a_color,mc)
+ nn = getnext(nn)
+ end
+ end
+ return n
+end
+
+function colors.reset(n)
+ setattr(tonut(n),a_color,unsetvalue)
+ return n
+end
+
+-- maybe
+
+local transparencies = { }
+tracers.transparencies = transparencies
+
+local a_transparency = attributes.private('transparency')
+local m_transparency = attributes.list[a_transparency] or { }
+
+function transparencies.set(n,t)
+ setattr(tonut(n),a_transparency,m_transparency[t] or unsetvalue)
+ return n
+end
+
+function transparencies.setlist(n,c,s)
+ local nn = tonut(n)
+ local mt = m_transparency[c] or unsetvalue
+ while nn do
+ setattr(nn,a_transparency,mt)
+ nn = getnext(nn)
+ end
+ return n
+end
+
+function transparencies.reset(n)
+ setattr(n,a_transparency,unsetvalue)
+ return n
+end
+
+-- for the moment here
+
+local visualizers = nodes.visualizers or { }
+nodes.visualizers = visualizers
+
+function visualizers.handler(head)
+ return head, false
+end
+
+-- we could cache attribute lists and set attr (copy will increment count) .. todo ..
+-- although tracers are used seldom
+
+local function setproperties(n,c,s)
+ local nn = tonut(n)
+ local mm = texgetattribute(a_colormodel)
+ setattr(nn,a_colormodel,mm > 0 and mm or 1)
+ setattr(nn,a_color,m_color[c])
+ setattr(nn,a_transparency,m_transparency[c])
+ return n
+end
+
+tracers.setproperties = setproperties
+
+-- setting attrlist entries instead of attr for successive entries doesn't
+-- speed up much (this function is only used in tracers anyway)
+
+function tracers.setlist(n,c,s)
+ local nn = tonut(n)
+ local mc = m_color[c]
+ local mt = m_transparency[c]
+ local mm = texgetattribute(a_colormodel)
+ if mm <= 0 then
+ mm = 1
+ end
+ while nn do
+ setattr(nn,a_colormodel,mm)
+ setattr(nn,a_color,mc)
+ setattr(nn,a_transparency,mt)
+ nn = getnext(nn)
+ end
+ return n
+end
+
+function tracers.resetproperties(n)
+ local nn = tonut(n)
+ setattr(nn,a_color,unsetvalue)
+ setattr(nn,a_transparency,unsetvalue)
+ return n
+end
+
+-- this one returns a nut
+
+local nodestracerpool = { }
+local nutstracerpool = { }
+
+tracers.pool = {
+ nodes = nodestracerpool,
+ nuts = nutstracerpool,
+}
+
+table.setmetatableindex(nodestracerpool,function(t,k,v)
+ local f = nutstracerpool[k]
+ local v = function(...)
+ return tonode(f(...))
+ end
+ t[k] = v
+ return v
+end)
+
+function nutstracerpool.rule(w,h,d,c,s)
+ return setproperties(new_rule(w,h,d),c,s)
+end
+
+tracers.rule = nodestracerpool.rule -- for a while
+
+-- local function show(head,n,message)
+-- print("START",message or "")
+-- local i = 0
+-- for current in traverse(head) do
+-- local prev = getprev(current)
+-- local next = getnext(current)
+-- i = i + 1
+-- print(i, prev and nodecodes[getid(prev)],nodecodes[getid(current)],next and nodecodes[getid(next)])
+-- if i == n then
+-- break
+-- end
+-- end
+-- print("STOP", message or "")
+-- end
+
+function nodes.handlers.show(n)
+ if n then
+ shownodes(tonut(n))
+ else
+ -- maybe a message
+ end
+ return n
+end
+
+trackers.register("fonts.result.show", function(v)
+ nodes.tasks.setaction("processors","nodes.handlers.show",v)
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/norm-ctx.mkxl b/Master/texmf-dist/tex/context/base/mkxl/norm-ctx.mkxl
new file mode 100644
index 00000000000..5cee04935b6
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/norm-ctx.mkxl
@@ -0,0 +1,87 @@
+%D \module
+%D [ file=norm-ctx,
+%D version=2009.03.19,
+%D title=\CONTEXT\ Normal Macros,
+%D subtitle=\CONTEXT,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D A few more might end up here (like the weird ones in syst-ini).
+
+\let\normalreqno\normaleqno
+
+% more friendly in setups:
+
+\aliased\let\ordordspacing \Umathordordspacing
+\aliased\let\ordopspacing \Umathordopspacing
+\aliased\let\ordbinspacing \Umathordbinspacing
+\aliased\let\ordrelspacing \Umathordrelspacing
+\aliased\let\ordopenspacing \Umathordopenspacing
+\aliased\let\ordclosespacing \Umathordclosespacing
+\aliased\let\ordpunctspacing \Umathordpunctspacing
+\aliased\let\ordinnerspacing \Umathordinnerspacing
+\aliased\let\opordspacing \Umathopordspacing
+\aliased\let\opopspacing \Umathopopspacing
+\aliased\let\opbinspacing \Umathopbinspacing
+\aliased\let\oprelspacing \Umathoprelspacing
+\aliased\let\opopenspacing \Umathopopenspacing
+\aliased\let\opclosespacing \Umathopclosespacing
+\aliased\let\oppunctspacing \Umathoppunctspacing
+\aliased\let\opinnerspacing \Umathopinnerspacing
+\aliased\let\binordspacing \Umathbinordspacing
+\aliased\let\binopspacing \Umathbinopspacing
+\aliased\let\binbinspacing \Umathbinbinspacing
+\aliased\let\binrelspacing \Umathbinrelspacing
+\aliased\let\binopenspacing \Umathbinopenspacing
+\aliased\let\binclosespacing \Umathbinclosespacing
+\aliased\let\binpunctspacing \Umathbinpunctspacing
+\aliased\let\bininnerspacing \Umathbininnerspacing
+\aliased\let\relordspacing \Umathrelordspacing
+\aliased\let\relopspacing \Umathrelopspacing
+\aliased\let\relbinspacing \Umathrelbinspacing
+\aliased\let\relrelspacing \Umathrelrelspacing
+\aliased\let\relopenspacing \Umathrelopenspacing
+\aliased\let\relclosespacing \Umathrelclosespacing
+\aliased\let\relpunctspacing \Umathrelpunctspacing
+\aliased\let\relinnerspacing \Umathrelinnerspacing
+\aliased\let\openordspacing \Umathopenordspacing
+\aliased\let\openopspacing \Umathopenopspacing
+\aliased\let\openbinspacing \Umathopenbinspacing
+\aliased\let\openrelspacing \Umathopenrelspacing
+\aliased\let\openopenspacing \Umathopenopenspacing
+\aliased\let\openclosespacing \Umathopenclosespacing
+\aliased\let\openpunctspacing \Umathopenpunctspacing
+\aliased\let\openinnerspacing \Umathopeninnerspacing
+\aliased\let\closeordspacing \Umathcloseordspacing
+\aliased\let\closeopspacing \Umathcloseopspacing
+\aliased\let\closebinspacing \Umathclosebinspacing
+\aliased\let\closerelspacing \Umathcloserelspacing
+\aliased\let\closeopenspacing \Umathcloseopenspacing
+\aliased\let\closeclosespacing\Umathcloseclosespacing
+\aliased\let\closepunctspacing\Umathclosepunctspacing
+\aliased\let\closeinnerspacing\Umathcloseinnerspacing
+\aliased\let\punctordspacing \Umathpunctordspacing
+\aliased\let\punctopspacing \Umathpunctopspacing
+\aliased\let\punctbinspacing \Umathpunctbinspacing
+\aliased\let\punctrelspacing \Umathpunctrelspacing
+\aliased\let\punctopenspacing \Umathpunctopenspacing
+\aliased\let\punctclosespacing\Umathpunctclosespacing
+\aliased\let\punctpunctspacing\Umathpunctpunctspacing
+\aliased\let\punctinnerspacing\Umathpunctinnerspacing
+\aliased\let\innerordspacing \Umathinnerordspacing
+\aliased\let\inneropspacing \Umathinneropspacing
+\aliased\let\innerbinspacing \Umathinnerbinspacing
+\aliased\let\innerrelspacing \Umathinnerrelspacing
+\aliased\let\inneropenspacing \Umathinneropenspacing
+\aliased\let\innerclosespacing\Umathinnerclosespacing
+\aliased\let\innerpunctspacing\Umathinnerpunctspacing
+\aliased\let\innerinnerspacing\Umathinnerinnerspacing
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-bar.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-bar.mkxl
new file mode 100644
index 00000000000..c6a11153165
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-bar.mkxl
@@ -0,0 +1,94 @@
+%D \module
+%D [ file=pack-bar,
+%D version=2009.06.26,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Bars,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Bars}
+
+
+\unprotect
+
+%D This code has been moved from scrn-int to here (was some old experimental code).
+%D It could be in scrn-bar but it's static. In the meantime the interface has been
+%D adapted to a key|/|value one.
+%D
+%D \startbuffer
+%D \dorecurse{10}{
+%D \ruledhbox{\horizontalpositionbar[n=#1,min=1,max=10,text=!,color=red]}
+%D \par
+%D }
+%D \stopbuffer
+%D
+%D \typebuffer \stoplinecorrection \getbuffer \stoplinecorrection
+%D
+%D \startbuffer
+%D \dorecurse{10}{
+%D \ruledhbox{\horizontalgrowingbar[n=#1,min=1,max=10,text=!,color=red]}
+%D \par
+%D }
+%D \stopbuffer
+%D
+%D \typebuffer \stoplinecorrection \getbuffer \stoplinecorrection
+
+\installcorenamespace{positionbar}
+
+\installsimplecommandhandler \??positionbar {positionbar} \??positionbar
+
+\setuppositionbar
+ [\c!min=\plusone,
+ \c!max=\plusone,
+ \c!n=\plusone
+ \c!text=?,
+ \c!width=\emwidth,
+ \c!height=\strutheight,
+ \c!depth=\strutdepth]
+
+\permanent\protected\def\horizontalpositionbar[#1]%
+ {\hbox to \hsize
+ {\setuppositionbar[#1]%
+ \usepositionbarstyleandcolor\c!style\c!color
+ \hskip\zeropoint\s!plus \positionbarparameter\c!n \s!fill
+ \hskip\zeropoint\s!plus-\positionbarparameter\c!min\s!fill
+ \positionbarparameter\c!text\relax
+ \hskip\zeropoint\s!plus \positionbarparameter\c!max\s!fill
+ \hskip\zeropoint\s!plus-\positionbarparameter\c!n \s!fill}}
+
+\permanent\protected\def\verticalpositionbar[#1]%
+ {\vbox to \vsize
+ {\hsize\positionbarparameter\c!width
+ \setuppositionbar[#1]%
+ \usepositionbarstyleandcolor\c!style\c!color
+ \vskip\zeropoint\s!plus \positionbarparameter\c!n \s!fill
+ \vskip\zeropoint\s!plus-\positionbarparameter\c!min\s!fill
+ \positionbarparameter\c!text\relax
+ \vskip\zeropoint\s!plus \positionbarparameter\c!max\s!fill
+ \vskip\zeropoint\s!plus-\positionbarparameter\c!n \s!fill}}
+
+\permanent\protected\def\horizontalgrowingbar[#1]%
+ {\hpack to \hsize
+ {\setuppositionbar[#1]%
+ \usepositionbarstyleandcolor\c!style\c!color
+ \vrule\s!width\zeropoint\s!height\positionbarparameter\c!height\s!depth\positionbarparameter\c!depth
+ \leaders\vrule\hskip\zeropoint\s!plus\numexpr\positionbarparameter\c!n-\positionbarparameter\c!min+\plusone\relax\s!fill
+ \hskip\zeropoint\s!plus \positionbarparameter\c!max\s!fill
+ \hskip\zeropoint\s!plus-\positionbarparameter\c!n \s!fill}}
+
+\permanent\protected\def\verticalgrowingbar[#1]%
+ {\vpack to \vsize
+ {\hsize\positionbarparameter\c!width
+ \setuppositionbar[#1]%
+ \usepositionbarstyleandcolor\c!style\c!color
+ \hrule\s!width\positionbarparameter\c!width\s!height\zeropoint\s!depth\zeropoint
+ \leaders\hrule\vskip\zeropoint\s!plus\numexpr\positionbarparameter\c!n-\positionbarparameter\c!min+\plusone\relax\s!fill
+ \vskip\zeropoint\s!plus \positionbarparameter\c!max\s!fill
+ \vskip\zeropoint\s!plus-\positionbarparameter\c!n \s!fill}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-bck.mklx b/Master/texmf-dist/tex/context/base/mkxl/pack-bck.mklx
new file mode 100644
index 00000000000..3c15beb5f4f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-bck.mklx
@@ -0,0 +1,245 @@
+%D \module
+%D [ file=pack-bck, % moved from pack-rul,
+%D version=20111115, % 1998.10.16,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Simple Backgrounds,
+%D author=Hans Hagen \& Wolfgang Schuster,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Simple Backgrounds}
+
+%D The code here is already pretty old and is used for simple backgrounds. As it is
+%D still used WS adapted the code to the new command handler methods so that it gets
+%D a second life. So it made sense to mkvi as well. Some more code is moved here
+%D too.
+
+%D \macros
+%D {setupbackground,startbackground,background}
+%D
+%D The section deals with backgrounds in the running text. This means that texts is
+%D to be collected and split over pages. To show what can be done, we provide this
+%D part of the documentation with some gray background and a red frame. Both the
+%D background and frame can have all characteristics of \type {\framed}.
+%D
+%D \starttyping
+%D \setupbackground
+%D [backgroundoffset=4pt,
+%D background=color,
+%D frame=on,
+%D framecolor=red,
+%D leftoffset=2pt]
+%D \stoptyping
+%D
+%D The implementation is not that sophisticated, but suffices. The main problem with
+%D this kind of functionality is to get the spacing all right.
+%D
+%D Specifying the background is more or less the same as
+%D specifying a framed box.
+%D
+%D \showsetup{setupbackground}
+%D
+%D {\em This mechanism is sort of obsolete and textbackgrounds should be used
+%D instead.}
+
+\unprotect
+
+\installcorenamespace {background}
+
+\installframedcommandhandler \??background {background} \??background
+
+% The mode is not that public an dmostlu an initial mode:
+%
+% 0 : no split
+% 1 : no split, honoring leftskip (messy)
+% 2 : split
+% 3 : split, honoring leftskip (messy)
+%
+% While processing the modes will change from 3->1 or 2->0. Don't depend on the
+% mode being public currently as it could become a key.
+
+\setnewconstant\backgroundsplitmode\plusthree
+
+\appendtoks
+ \frozen\setuevalue{\e!start\currentbackground}{\pack_backgrounds_start [\currentbackground]}%
+ \frozen\setuevalue{\e!stop \currentbackground}{\pack_backgrounds_stop }%
+ \frozen\setuevalue {\currentbackground}{\pack_backgrounds_direct[\currentbackground]}%
+\to \everydefinebackground
+
+\protected\def\pack_backgrounds_start[#tag]%
+ {\endgraf
+ \begingroup
+ \def\currentbackground{#tag}%
+ \doifelse{\backgroundparameter\c!state}\v!start
+ \pack_backgrounds_start_indeed
+ \pack_backgrounds_start_ignore}
+
+\tolerant\def\pack_backgrounds_start_indeed[#settings]%
+ {\setupcurrentbackground[#settings,\c!state=\v!start,\c!offset=\v!overlay]%
+ \let\pack_backgrounds_stop\pack_backgrounds_stop_indeed
+ \setbox\scratchboxone\vbox\bgroup
+ \strut
+ \vskip-2\lineheight
+ \strut
+ \blank[\v!disable]
+ \leftskip \backgroundparameter\c!leftoffset
+ \rightskip\backgroundparameter\c!rightoffset}
+
+\tolerant\def\pack_backgrounds_start_ignore[#settings]%
+ {\let\pack_backgrounds_stop\pack_backgrounds_stop_ignore}
+
+\let\pack_backgrounds_stop\relax
+
+\protected\def\pack_backgrounds_stop_indeed % we shoul duse the fast background variant of framed
+ {\endgraf
+ \removelastskip
+ \strut
+ \vskip-2\lineheight
+ \strut
+ % safeguard added
+ \ifdim\dimexpr\pagetotal+2\lineheight\relax>\pagegoal
+ \page
+ \fi
+ % till here
+ \egroup
+ \scratchdistance\leftskip % new **
+ \forgetall
+ \ifinsidefloat
+ \backgroundsplitmode\zerocount
+ \fi
+ \ifcase\backgroundsplitmode
+ \inheritedbackgroundframed{\box\scratchboxone}%
+ \or
+ \hskip\scratchdistance
+ \inheritedbackgroundframed{\box\scratchboxone}%
+ \else
+ \splitmaxdepth\boxmaxdepth
+ \splittopskip\topskip
+ \doloop
+ {\ifzeropt\pagetotal % empty page
+ \scratchdimen\textheight
+ \backgroundsplitmode\plusone % split to max height
+ \else
+ \setbox\scratchdimentwo\vbox{\backgroundparameter\c!before}%
+ \scratchdimen\dimexpr\pagegoal-\ht\scratchdimentwo-\pagetotal\relax
+ \backgroundsplitmode\plustwo % split to partial height
+ \fi
+ \ifdim\scratchdimen<\zeropoint
+ \scratchdimen\pagegoal
+ \fi
+ \advance\scratchdimen\dimexpr
+ -\backgroundparameter\c!topoffset
+ -\backgroundparameter\c!bottomoffset
+ \relax
+ \ifdim\scratchdimen>2\lineheight\relax % reasonable, will be configurable
+ \ifdim\ht\scratchboxone>\scratchdimen % larger than page
+ \setbox\scratchdimentwo\vsplit\scratchboxone to \scratchdimen
+ \else
+ \setbox\scratchdimentwo\box\scratchboxone
+ \backgroundsplitmode\zerocount % no split
+ \fi
+ \setbox\scratchdimentwo\vbox \ifcase\backgroundsplitmode\or to \textheight \fi % max split
+ {\vskip\backgroundparameter\c!topoffset
+ \unvcopy\scratchdimentwo
+ \prevdepth\dp\scratchdimentwo
+ \obeydepth
+ \vskip\backgroundparameter\c!bottomoffset
+ \vfill}
+ \backgroundparameter\c!before
+ \ifcase\backgroundsplitmode\or\or % partial split
+ \ifdim\pagegoal<\maxdimen
+ \pagegoal=1.2\pagegoal % be a bit more tolerant
+ \fi
+ \fi
+ \startlinecorrection
+ \ifnum\backgroundsplitmode=\plusthree
+ \hskip\scratchdistance % new **
+ \fi
+ \inheritedbackgroundframed{\box\scratchdimentwo}%
+ \stoplinecorrection
+ \ifcase\backgroundsplitmode % no split
+ \backgroundparameter\c!after
+ \else % some split
+ \page_otr_fill_and_eject_page % no \page !
+ \fi
+ \else
+ \page
+ \fi
+ \ifdim\ht\scratchboxone>\zeropoint \else
+ \exitloop
+ \fi}%
+ \fi
+ \endgroup
+ \endgraf}
+
+\protected\def\pack_backgrounds_stop_ignore
+ {\endgroup
+ \endgraf}
+
+\protected\def\pack_backgrounds_direct[#tag]%
+ {\begingroup
+ \def\currentbackground{#tag}%
+ \doifelse{\backgroundparameter\c!state}\c!start
+ \pack_backgrounds_direct_indeed
+ \pack_backgrounds_direct_ignore}
+
+\def\pack_backgrounds_direct_indeed
+ {\dowithnextboxcs\pack_backgrounds_direct_finish\vbox}
+
+\def\pack_backgrounds_direct_finish
+ {\setupcurrentbackground[\c!offset=\v!overlay]%
+ \inheritedbackgroundframed{\flushnextbox}%
+ \endgroup}
+
+\def\pack_backgrounds_direct_ignore
+ {\endgroup}
+
+\definebackground[\v!background]
+
+\ifdefined\startbackground \else
+ \permanent\expandafter\let\expandafter\startbackground\csname\e!start\v!background\endcsname
+ \permanent\expandafter\let\expandafter\stopbackground \csname\e!stop \v!background\endcsname
+ \permanent\expandafter\let\expandafter\background \csname \v!background\endcsname
+\fi
+
+\setupbackground
+ [\c!leftoffset=.5\bodyfontsize,
+ \c!rightoffset=\backgroundparameter\c!leftoffset,
+ \c!topoffset=\zeropoint,
+ \c!bottomoffset=\backgroundparameter\c!topoffset,
+ \c!state=\v!start,
+ \c!radius=.5\bodyfontsize,
+ \c!corner=\v!rectangular,
+ \c!frame=\v!off,
+ \c!depth=\zeropoint,
+ % \c!color=,
+ \c!background=\v!color,
+ \c!backgroundcolor=lightgray,
+ \c!before=,
+ \c!after=]
+
+%D \startbuffer
+%D \setupbackground[state=start,frame=on]
+%D
+%D \startbackground
+%D \input ward
+%D \stopbackground
+%D
+%D \background{\input ward\par}
+%D
+%D \setupbackground[state=stop]
+%D
+%D \startbackground
+%D \input ward
+%D \stopbackground
+%D
+%D \background{\input ward\par}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-box.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-box.mkxl
new file mode 100644
index 00000000000..77b0bbc69c8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-box.mkxl
@@ -0,0 +1,1121 @@
+%D \module
+%D [ file=pack-box,
+%D version=2002.04.12,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Boxes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Boxes}
+
+%D This module contains all kind of macros for moving content around. Many
+%D macros here come from other modules, but depencies made it more clear
+%D to isolate them. We invite users to document the macros. They can be
+%D handy shortcuts for otherwise complex tasks.
+
+\unprotect
+
+%D We need to set the size, else we get dimensions depending on the content,
+%D which in itself is ok, but can lead to loops due to rounding errors (happened
+%D in demo-obv).
+
+% \definelayer[\v!text-2][\c!position=\v!yes,\c!region=,\c!width=\d_overlay_width,\c!height=\d_overlay_height]
+% \definelayer[\v!text-1][\c!position=\v!yes,\c!region=,\c!width=\d_overlay_width,\c!height=\d_overlay_height]
+% \definelayer[\v!text+1][\c!position=\v!yes,\c!region=,\c!width=\d_overlay_width,\c!height=\d_overlay_height]
+% \definelayer[\v!text+2][\c!position=\v!yes,\c!region=,\c!width=\d_overlay_width,\c!height=\d_overlay_height]
+
+\definelayer[\v!text-2][\c!position=\v!yes,\c!region=,\c!width=\textwidth,\c!height=\textheight]
+\definelayer[\v!text-1][\c!position=\v!yes,\c!region=,\c!width=\textwidth,\c!height=\textheight]
+\definelayer[\v!text+1][\c!position=\v!yes,\c!region=,\c!width=\textwidth,\c!height=\textheight]
+\definelayer[\v!text+2][\c!position=\v!yes,\c!region=,\c!width=\textwidth,\c!height=\textheight]
+
+\permanent\protected\def\internaltextoverlay#1% will become more generic and installable
+ {\startoverlay % i.e. probably an overlay by itself
+ {\positionregionoverlay\textanchor{\v!text#1}}% see later
+ {\composedlayer {\v!text#1}}%
+ \stopoverlay}
+
+\defineoverlay[\v!text-2][\internaltextoverlay{-2}]
+\defineoverlay[\v!text-1][\internaltextoverlay{-1}]
+\defineoverlay[\v!text+1][\internaltextoverlay{+1}]
+\defineoverlay[\v!text+2][\internaltextoverlay{+2}]
+
+\installcorenamespace {anchor}
+
+% produces a box too
+%
+% \anchor[text-1][preset=lefttop][framed settings]{HELLO WORLD}
+
+\mutable\let\currentanchor\empty
+
+\permanent\tolerant\protected\def\defineanchor[#1]#*[#2]#*[#3]#*[#4]% name targetlayer layersetting framedsetting
+ {\setvalue{\??anchor#1}{\pack_anchors_process_defined{#2}{#3}{#4}}}
+
+\let\pack_anchors_process_defined_indeed\gobbletwooptionals
+
+\def\pack_anchors_process_defined#1#2#3% brrr
+ {\tolerant\def\pack_anchors_process_defined_indeed[##1]##*[##2]%
+ {\ifarguments
+ \def\next{\pack_anchors_process_indeed{#1}{#2}{#3}}%
+ \or
+ \def\next{\pack_anchors_process_indeed{#1}{#2,##1}{#2,##1}}%
+ \or
+ \def\next{\pack_anchors_process_indeed{#1}{#2,##1}{#3,##2}}%
+ \fi
+ \next}% picks up a box
+ \pack_anchors_process_defined_indeed}
+
+\permanent\protected\def\anchor[#1]%
+ {\begingroup
+ \edef\currentanchor{#1}%
+ \ifcsname\??anchor\currentanchor\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\pack_anchor_notdefined
+ \fi}
+
+\tolerant\def\pack_anchor_notdefined[#1]#*[#2]%
+ {\ifarguments
+ \expandafter\gobbletwoarguments
+ \or
+ \expandafter\pack_anchor_notdefined_one
+ \else
+ \expandafter\pack_anchor_notdefined_two
+ \fi{#1}{#2}}
+
+\def\pack_anchor_notdefined_one #1#2{\dowithnextbox{\pack_anchors_process_finish\currentanchor{#1}{#1}}\vbox}
+\def\pack_anchor_notdefined_two #1#2{\dowithnextbox{\pack_anchors_process_finish\currentanchor{#1}{#2}}\vbox}
+\def\pack_anchors_process_indeed#1#2#3{\dowithnextbox{\pack_anchors_process_finish {#1}{#2}{#3}}\vbox}
+
+\newbox \b_pack_anchors
+\newdimen\d_pack_anchors_width
+\newdimen\d_pack_anchors_height
+\newdimen\d_pack_anchors_depth
+
+% \definelayer[anchor]
+
+\newcount\c_pack_anchors_n
+\newtoks \t_pack_anchors_flush
+
+\protected\def\pack_anchors_register#1#2%
+ {\global\advance\c_pack_anchors_n\plusone
+ \pagereference[\v!layer:\v!anchor:\number\c_pack_anchors_n]%
+ \putboxincache\v!anchor{\number\c_pack_anchors_n}\b_pack_anchors
+% \xtoksapp\t_pack_anchors_flush{\pack_anchors_flush{\number\c_pack_anchors_n}{#1}{#2}}%
+ \doglobal\appendetoks
+ \pack_anchors_flush{\number\c_pack_anchors_n}{#1}{#2}%
+ \to \t_pack_anchors_flush
+ \glet\pack_anchors_flush_all\pack_anchors_flush_all_indeed}
+
+\protected\def\pack_anchors_flush#1#2#3%
+ {\doifelseboxincache\v!anchor{#1}
+ {\doifelsereferencefound{\v!layer:\v!anchor:#1}
+ {\ifnum\currentreferencerealpage=\realpageno\relax
+ \setlayer[#2][#3,\c!position=\v!no]{\directboxfromcache\v!anchor{#1}}%
+ \else
+ \donetrue
+ \fi
+ }\donetrue}%
+ \donetrue}%
+
+\protected\def\pack_anchors_flush_all_indeed
+ {\donefalse
+ \the\t_pack_anchors_flush
+ \ifdone\else
+ \global\t_pack_anchors_flush\emptytoks
+ \glet\pack_anchors_flush_all\relax
+ \fi}
+
+\let\pack_anchors_flush_all\relax
+
+\appendtoks
+ \pack_anchors_flush_all
+\to \everybeforepagebody
+
+\def\pack_anchors_process_finish#1#2#3% brrr: we need to apply offset only once .. a bit messy
+ {\checkpositionoverlays
+ \setbox\b_pack_anchors\box\nextbox
+ \framed % could be a predefined framed but used seldom
+ [\c!offset=\v!overlay,\c!frame=\v!off,#3]
+ {\pack_anchors_register{#1}{#2}%
+ \novrule % hm, not needed as we frame the size (but kind of default)
+ \s!width \wd\b_pack_anchors
+ \s!height\ht\b_pack_anchors
+ \s!depth \dp\b_pack_anchors}%
+ \endgroup}
+
+% \setlayeranchored[text-1][preset=lefttop]{HELLO WORLD} produces a simple (empty) hbox
+% synchronizes per page
+
+\permanent\tolerant\protected\def\setlayeranchored[#1]#*[#2]%
+ {\begingroup
+ \ifarguments
+ \dowithnextbox
+ {\endgroup}%
+ \else
+ \dowithnextbox
+ {\checkpositionoverlays
+ \setbox\b_pack_anchors\box\nextbox
+ \dontleavehmode\hpack{\pack_anchors_register{#1}{#2}}%
+ \endgroup}%
+ \fi
+ \hbox}
+
+% collectors
+
+\installcorenamespace{collectorbox}
+\installcorenamespace{collector}
+
+\installcommandhandler \??collector {collector} \??collector
+
+\setupcollector
+ [\c!state=\v!start,
+ \c!x=\zeropoint,
+ \c!y=\zeropoint,
+ \c!offset=\zeropoint,
+ \c!rotation=, % geen 0 !
+ \c!hoffset=\zeropoint,
+ \c!voffset=\zeropoint,
+ \c!location=rb,
+ \c!corner=]
+
+\appendtoks
+ \ifcsname\??collectorbox\currentcollector\endcsname \else
+ \expandafter\newbox\csname\??collectorbox\currentcollector\endcsname
+ \fi
+\to \everydefinecollector
+
+\permanent\protected\def\resetcollector[#1]%
+ {\ifcsname\??collectorbox#1\endcsname
+ \global\setbox\lastnamedcs\emptybox
+ \fi}
+
+\newconditional\c_pack_boxes_collector_valid_box
+
+\mutable\let\b_pack_boxes_collector\scratchbox
+
+\def\pack_boxes_collector_check_box#1%
+ {\edef\currentcollector{#1}%
+ \ifcsname\??collectorbox\currentcollector\endcsname
+ \settrue\c_pack_boxes_collector_valid_box
+ \expandafter\let\expandafter\b_pack_boxes_collector\lastnamedcs
+ \else
+ \setfalse\c_pack_boxes_collector_valid_box
+ \writestatus{collector}{unknown collector \currentcollector}%
+ \fi}
+
+\permanent\tolerant\protected\def\setcollector[#1]#*[#2]% todo: keep reference point
+ {\bgroup
+ \pack_boxes_collector_check_box{#1}%
+ \ifconditional\c_pack_boxes_collector_valid_box
+ \setupcurrentcollector[#2]%
+ \expandafter\pack_boxes_collector_yes
+ \else
+ \expandafter\pack_boxes_collector_nop
+ \fi}
+
+\def\pack_boxes_collector_yes
+ {\forgetall
+ \dontcomplain
+ \dowithnextboxcs\pack_boxes_collector_finish\hbox}
+
+\let\pack_boxes_collector_nop\egroup
+
+\installcorenamespace{collectorcorners}
+
+\setvalue{\??collectorcorners\v!middle}%
+ {\ifdim\d_pack_layers_x_size>\zeropoint
+ \advance\d_pack_layers_x_position.5\d_pack_layers_x_size
+ \fi
+ \ifdim\d_pack_layers_y_size>\zeropoint
+ \advance\d_pack_layers_y_position.5\d_pack_layers_y_size
+ \fi}
+
+\setvalue{\??collectorcorners\v!bottom}%
+ {\ifdim\d_pack_layers_y_size>\zeropoint
+ \advance\d_pack_layers_y_position-\d_pack_layers_y_size
+ \d_pack_layers_y_position-\d_pack_layers_y_position
+ \fi}
+
+\setvalue{\??collectorcorners\v!right}%
+ {\ifdim\d_pack_layers_x_size>\zeropoint
+ \advance\d_pack_layers_x_position-\d_pack_layers_x_size
+ \d_pack_layers_x_position-\d_pack_layers_x_position
+ \fi}
+
+\def\pack_boxes_collector_check_corner#1%
+ {\ifcsname\??collectorcorners#1\endcsname
+ \lastnamedcs
+ \fi}
+
+\def\pack_boxes_collector_finish
+ {\edef\p_collector_rotation{\collectorparameter\c!rotation}%
+ \edef\p_collector_corner {\collectorparameter\c!corner}%
+ \ifempty\p_collector_rotation \else
+ \setbox\nextbox\hpack
+ {\rotate
+ [\c!location=\v!high,
+ \c!rotation=\p_collector_rotation]
+ {\box\nextbox}}%
+ \fi
+ \d_pack_layers_x_size\wd\b_pack_boxes_collector
+ \d_pack_layers_y_size\htdp\b_pack_boxes_collector
+ \d_pack_layers_x_position\dimexpr\collectorparameter\c!x+\collectorparameter\c!hoffset\relax
+ \d_pack_layers_y_position\dimexpr\collectorparameter\c!y+\collectorparameter\c!voffset\relax
+ \rawprocesscommacommand[\p_collector_corner]\pack_boxes_collector_check_corner
+ \setbox\nextbox\hpack
+ {\alignedbox[\collectorparameter\c!location]\vpack{\box\nextbox}}%
+ \boxmaxdepth\zeropoint % really needed, nice example
+ \global\advance\boxhdisplacement\d_pack_layers_x_position
+ \ifdim\boxhdisplacement<\zeropoint
+ \global\setbox\b_pack_boxes_collector\hpack
+ {\kern-\boxhdisplacement
+ \box\b_pack_boxes_collector}%
+ \fi
+ \global\advance\boxvdisplacement\d_pack_layers_y_position
+ \ifdim\boxvdisplacement<\zeropoint
+ \global\setbox\b_pack_boxes_collector\hpack
+ {\lower-\boxvdisplacement
+ \box\b_pack_boxes_collector}%
+ \fi
+ \d_pack_layers_x_size\wd\b_pack_boxes_collector
+ \d_pack_layers_y_size\htdp\b_pack_boxes_collector
+ \global\setbox\b_pack_boxes_collector\hpack
+ {\box\b_pack_boxes_collector
+ \kern\dimexpr
+ -\d_pack_layers_x_size
+ +\d_pack_layers_x_position
+ \ifdim\boxhdisplacement<\zeropoint
+ -\boxhdisplacement
+ \fi
+ \relax
+ \lower\d_pack_layers_y_position\hpack
+ {\ifdim\boxvdisplacement<\zeropoint
+ \lower-\boxvdisplacement
+ \fi
+ \box\nextbox}}%
+ % combine height and depth into depth only (later flushed as height)
+ \global\setbox\b_pack_boxes_collector\hpack
+ {\lower\ht\b_pack_boxes_collector\box\b_pack_boxes_collector}%
+ % just to be sure
+ \ifdim\wd\b_pack_boxes_collector<\d_pack_layers_x_size
+ \wd\b_pack_boxes_collector\d_pack_layers_x_size
+ \fi
+ \egroup}
+
+\permanent\protected\def\flushcollector[#1]%
+ {\bgroup
+ \pack_boxes_collector_check_box{#1}%
+ \ifconditional\c_pack_boxes_collector_valid_box
+ \edef\p_collector_state{\collectorparameter\c!state}%
+ \ifx\p_collector_state\v!stop \else
+ \vpack{\hpack{\raise
+ \dp\b_pack_boxes_collector
+ \ifx\p_collector_state\v!repeat\copy\else\box\fi\b_pack_boxes_collector}}%
+ \fi
+ \fi
+ \egroup}
+
+\permanent\protected\def\composedcollector#1% no [], handy as argument
+ {\flushcollector[#1]}
+
+\permanent\tolerant\protected\def\adaptcollector[#1]#*[#2]% % a typical case where \global\wd looks better in the code
+ {\begingroup
+ \pack_boxes_collector_check_box{#1}%
+ \ifconditional\c_pack_boxes_collector_valid_box
+ \letcollectorparameter\c!hoffset\zeropoint
+ \letcollectorparameter\c!voffset\zeropoint
+ \setupcurrentcollector[#2]%
+ \global\wd\b_pack_boxes_collector\dimexpr\wd\b_pack_boxes_collector+\collectorparameter\c!hoffset\relax
+ \global\ht\b_pack_boxes_collector\dimexpr\ht\b_pack_boxes_collector+\collectorparameter\c!voffset\relax
+ \fi
+ \endgroup}
+
+%\definecollector[test]
+%\setcollector[test]
+% [location=rb]
+% {\externalfigure[koe][frame=on,width=3cm]}
+%\setcollector[test]
+% [corner={right,bottom},location={left,top}]
+% {\framed{gans}}
+%\composedcollector{test}
+
+\definecollector
+ [caption]
+
+\permanent\tolerant\protected\def\collectedtext[#1]#*[#2]% for captions
+ {\pack_boxes_collector_text[#1][#2]}
+
+\def\pack_boxes_collector_text[#1][#2]#3% #3 is mandate so no tolerant here, so still two step
+ {\bgroup
+ \dowithnextbox
+ {\setcollector
+ [caption]
+ {\box\nextbox}%
+ \setcollector
+ [caption][#1]%
+ {\letdummyparameter\c!style\empty
+ \letdummyparameter\c!color\empty
+ \getdummyparameters[#2]%
+ \dousestyleparameter{\directdummyparameter\c!style}%
+ \setupinterlinespace
+ \normalexpanded{\framed[\c!foregroundcolor=\directdummyparameter\c!color,\c!foregroundstyle=\directdummyparameter\c!style},\c!frame=\v!overlay,#2]{#3}}%
+ \composedcollector{caption}%
+ \egroup}%
+ \hbox}
+
+% \collectedtext
+% [corner={right,bottom},location={left,top}]
+% [background=color,backgroundcolor=white,offset=0pt]
+% {gans}
+% {\externalfigure[koe][width=3cm]}
+%
+% \collectedtext
+% [rotation=90,corner={right,bottom},location={right,top}]
+% [frame=on,offset=0pt]
+% {gans}
+% {\externalfigure[koe][width=3cm]}
+%
+% \collectedtext
+% [rotation=90,corner={left,bottom},location={left,top}]
+% [frame=on,offset=0pt]
+% {gans}
+% {\externalfigure[koe][width=3cm]}
+
+\installcorenamespace {layeredtext}
+\installcorenamespace {layeredtextlayer}
+\installcorenamespace {layeredtextframed}
+
+\installsimpleframedcommandhandler \??layeredtext {layeredtext} \??layeredtext
+
+\newdimen\d_pack_layeredtexts_width
+\newdimen\d_pack_layeredtexts_height
+
+\definelayer % private
+ [\??layeredtextlayer]
+
+\setuplayer % private
+ [\??layeredtextlayer]
+ [\c!width=\d_pack_layeredtexts_width,
+ \c!height=\d_pack_layeredtexts_height]
+
+\defineframed % private
+ [\??layeredtextframed]
+ [\c!offset=\v!overlay,
+ \c!frame=\v!off,
+ \c!background={\v!foreground,\??layeredtextlayer},
+ \c!width=\d_pack_layeredtexts_width,
+ \c!height=\d_pack_layeredtexts_height]
+
+\setuplayeredtext % public (the attached data, usually small stuff, not the main thing)
+ [\c!frame=\v!overlay,
+ \c!color=,
+ \c!style=,
+ \c!foregroundcolor=\layeredtextparameter\c!color,
+ \c!foregroundstyle=\layeredtextparameter\c!style]
+
+\permanent\tolerant\protected\def\placelayeredtext[#1]#*[#2]#*[#3]#4% tag layersettings content(framed)settings content
+ {\bgroup
+ \edef\currentlayeredtext{#1}%
+ \checklayeredtextparent % bonus
+ \setupcurrentlayeredtext[#3]%
+ \dowithnextbox
+ {\d_pack_layeredtexts_width \wd\nextbox
+ \d_pack_layeredtexts_height\ht\nextbox
+ \begingroup % preserve \nextbox
+ \setlayer
+ [\??layeredtextlayer]%
+ [#2]%
+ {\setfalse\fontattributeisset
+ \uselayeredtextstyleparameter\c!style
+ \ifconditional\fontattributeisset
+ \setupinterlinespace
+ \fi
+ \inheritedlayeredtextframed{#4}}%
+ \endgroup
+ \placeframed[\??layeredtextframed]{\flushnextbox}%
+ \egroup}%
+ \hbox}
+
+\permanent\protected\def\layeredtext
+ {\placelayeredtext[]}
+
+% \layeredtext
+% [corner={right,bottom},location={left,top}]
+% [background=color,backgroundcolor=white,offset=0pt]
+% {gans}
+% {\externalfigure[koe][width=3cm]}
+%
+% \layeredtext
+% [rotation=90,corner={right,bottom},location={right,top}]
+% [frame=on,offset=0pt]
+% {gans}
+% {\externalfigure[koe][width=3cm]}
+%
+% \layeredtext
+% [rotation=90,corner={left,bottom},location={left,top}]
+% [frame=on,offset=0pt]
+% {gans}
+% {\externalfigure[koe][width=3cm]}
+
+\permanent\tolerant\protected\def\ornamenttext[#1]#*[#2]% takes another argument
+ {\bgroup
+ \doifelseassignment{#1}
+ {\letdummyparameter\c!alternative\v!a
+ \getdummyparameters[#1]%
+ \doifelse{\directdummyparameter\c!alternative}\v!a
+ {\egroup\collectedtext}%
+ {\egroup\layeredtext }%
+ [#1][#2]}%
+ {\egroup\csname#1\endcsname}}
+
+\permanent\tolerant\protected\def\defineornament[#1]#*[#2]#*[#3]%
+ {\protected\frozen\instance\defcsname#1\endcsname{\ornamenttext[#2][#3]}}
+
+% \defineornament
+% [affiliation]
+% [rotation=90,corner={right,bottom},location={right,top},
+% hoffset=-.25ex]
+% [frame=on,background=color,backgroundcolor=red,offset=0pt]
+%
+% \ruledhbox{\affiliation{gans}{\externalfigure[koe][width=3cm]}}
+%
+% \defineornament
+% [affiliation]
+% [rotation=90,corner={right,bottom},location={right,top},
+% hoffset=-.25ex,alternative=b]
+% [frame=on,background=color,backgroundcolor=red,offset=0pt]
+%
+% \ruledhbox{\affiliation{gans}{\externalfigure[koe][width=3cm]}}
+%
+% \defineornament
+% [affiliation]
+% [rotation=90,corner={right,bottom},location={left,top},
+% hoffset=.25ex,voffset=.25ex,alternative=a]
+% [background=color,style=\ss\tfxx,backgroundcolor=white,offset=0pt]
+%
+% \affiliation{photo}{\externalfigure[molen][width=3cm]}
+%
+% \defineornament
+% [affiliation]
+% [rotation=90,corner={right,bottom},location={left,top},
+% hoffset=.25ex,voffset=.25ex,alternative=b]
+% [background=color,style=\ss\tfxx,backgroundcolor=white,offset=0pt]
+%
+% \affiliation{drawing}{\externalfigure[hakker][width=3cm]}
+
+\newcount\c_pack_boxes_bleeds
+
+\installcorenamespace {bleeding}
+
+\installdirectcommandhandler \??bleeding {bleeding} % \??bleeding
+
+\setupbleeding
+ [\c!location=l,
+ \c!stretch=\v!yes,
+ \c!width=3cm,
+ \c!height=3cm,
+ \c!offset=2mm,
+ \c!page=\v!no,
+ \c!voffset=\scratchoffset, % is set to \bleedingparameter\c!offset
+ \c!hoffset=\scratchoffset] % which often saves one resolve
+
+\permanent\def\bleedwidth {\the\hsize} % these are global !
+\permanent\def\bleedheight{\the\vsize} % these are global !
+
+\newconditional\c_pack_boxes_l
+\newconditional\c_pack_boxes_r
+\newconditional\c_pack_boxes_t
+\newconditional\c_pack_boxes_b
+
+\installcorenamespace{bleedinglocation}
+
+\setvalue{\??bleedinglocation t}{\settrue\c_pack_boxes_t\scratchhoffset\zeropoint}
+\setvalue{\??bleedinglocation b}{\settrue\c_pack_boxes_b\scratchhoffset\zeropoint}
+\setvalue{\??bleedinglocation l}{\settrue\c_pack_boxes_l\scratchvoffset\zeropoint}
+\setvalue{\??bleedinglocation r}{\settrue\c_pack_boxes_r\scratchvoffset\zeropoint}
+\setvalue{\??bleedinglocation bl}{\settrue\c_pack_boxes_l\settrue\c_pack_boxes_b}
+\setvalue{\??bleedinglocation lb}{\settrue\c_pack_boxes_l\settrue\c_pack_boxes_b}
+\setvalue{\??bleedinglocation br}{\settrue\c_pack_boxes_r\settrue\c_pack_boxes_b}
+\setvalue{\??bleedinglocation rb}{\settrue\c_pack_boxes_r\settrue\c_pack_boxes_b}
+\setvalue{\??bleedinglocation tl}{\settrue\c_pack_boxes_l\settrue\c_pack_boxes_t}
+\setvalue{\??bleedinglocation lt}{\settrue\c_pack_boxes_l\settrue\c_pack_boxes_t}
+\setvalue{\??bleedinglocation tr}{\settrue\c_pack_boxes_r\settrue\c_pack_boxes_t}
+\setvalue{\??bleedinglocation rt}{\settrue\c_pack_boxes_r\settrue\c_pack_boxes_t}
+
+\mutable\let\currentbleedposition\empty
+\mutable\let\currentpageposition \empty
+
+\permanent\tolerant\protected\def\bleed[#1]%
+ {\hbox\bgroup
+ \global\advance\c_pack_boxes_bleeds\plusone
+ %
+ \enforced\permanent\xdef\bleedwidth {\the\hsize}%
+ \enforced\permanent\xdef\bleedheight{\the\vsize}%
+ %
+ \edef\currentbleedposition{bleed:\number\c_pack_boxes_bleeds}%
+ \edef\currentpageposition {page:0}% todo: per page
+ %
+ \setupcurrentbleeding[#1]%
+ %
+ \scratchwidth \bleedingparameter\c!width
+ \scratchheight \bleedingparameter\c!height
+ \scratchoffset \bleedingparameter\c!offset
+ \scratchhoffset\bleedingparameter\c!hoffset
+ \scratchvoffset\bleedingparameter\c!voffset
+ %
+ \setfalse\c_pack_boxes_l % left
+ \setfalse\c_pack_boxes_r % right
+ \setfalse\c_pack_boxes_t % top
+ \setfalse\c_pack_boxes_b % bottom
+ %
+ \csname\??bleedinglocation\bleedingparameter\c!location\endcsname
+ %
+ \doifelse{\bleedingparameter\c!stretch}\v!yes\donetrue\donefalse
+ %
+ \enforced\permanent\xdef\bleedwidth{\the\dimexpr
+ \ifdone
+ \ifconditional\c_pack_boxes_l
+ \scratchwidth+\MPx\currentbleedposition-\MPx\currentpageposition
+ \orelse\ifconditional\c_pack_boxes_r
+ \paperwidth -\MPx\currentbleedposition+\MPx\currentpageposition % not checked
+ \else
+ \scratchwidth
+ \fi
+ \else
+ \scratchwidth
+ \fi+\scratchhoffset
+ \relax}%
+ \enforced\permanent\xdef\bleedheight{\the\dimexpr
+ \ifdone
+ \ifconditional\c_pack_boxes_t
+ \paperheight -\MPy\currentbleedposition+\MPy\currentpageposition % not checked
+ \orelse\ifconditional\c_pack_boxes_b
+ \scratchheight+\MPy\currentbleedposition-\MPy\currentpageposition + \MPh\currentbleedposition % not checked (\MPh added)
+ \else
+ \scratchheight
+ \fi
+ \else
+ \scratchheight
+ \fi+\scratchvoffset
+ \relax}%
+ \dowithnextboxcontentcs\pack_boxes_bleed_settings\pack_boxes_bleed_finish\hbox}
+
+\def\pack_boxes_bleed_settings
+ {\hsize\bleedwidth
+ \vsize\bleedheight}
+
+\def\pack_boxes_bleed_finish
+ {\doif{\bleedingparameter\c!page}\v!yes
+ {\setbox\nextbox\topskippedbox{\box\nextbox}}%
+ \setbox\nextbox\hpack to \scratchwidth
+ {\ifconditional\c_pack_boxes_l\hss\fi
+ \box\nextbox
+ \ifconditional\c_pack_boxes_r\hss\fi}%
+ \ifconditional\c_pack_boxes_b
+ \setbox\nextbox\hpack
+ {\lower\bleedheight\hpack{\raise\scratchheight\box\nextbox}}%
+ \fi
+ \wd\nextbox\scratchwidth
+ \ht\nextbox\scratchheight
+ \dp\nextbox\zeropoint
+ \ifdone
+ \hpos\currentbleedposition{\box\nextbox}%
+ \else
+ \box\nextbox
+ \fi
+ \egroup}
+
+\defineexternalfigure
+ [bleed] % should be \v!bleed
+ [\c!width=\bleedwidth,
+ \c!height=\bleedheight]
+
+% \placefigure[left]{none}
+% {\bleed[width=5cm,height=3cm,location=lt]{\externalfigure[koe][bleed]}}
+%
+% \input tufte
+%
+% \placefigure[left]{none}
+% {\bleed[width=5cm,height=3cm,location=l]{\externalfigure[koe][bleed]}}
+%
+% \input tufte
+%
+% \placefigure[right]{none}
+% {\bleed[width=5cm,height=3cm,location=r]{\externalfigure[koe][bleed]}}
+%
+% \input tufte
+%
+% \placefigure
+% [top,none]
+% {} % no caption
+% {\bleed
+% [hoffset=-\backspace,
+% voffset=3mm,
+% width=0cm,
+% height=6\lineheight,
+% page=yes, % correct for topskip
+% location=lt]
+% {\externalfigure[koe][bleed][frame=on]}}
+
+% \setlayerframed[layer id][layer settings][framed setting]{data}
+% \setlayerframed[layer id][combined settings]{data}
+
+% tricky: offsets apply to both the layer and the framed; it makes sense to
+% only apply the offset to ...
+
+\permanent\tolerant\protected\def\setlayerframed[#1]#*[#2]#*[#3]
+ {\ifarguments
+ % error
+ \or
+ \expandafter\pack_layers_set_framed_s
+ \or
+ \expandafter\pack_layers_set_framed_d
+ \or
+ \expandafter\pack_layers_set_framed_t
+ \fi[#1][#2][#3]}
+
+\def\pack_layers_set_framed_s[#1][#2][#3]%
+ {\setlayer[#1][\c!width=\wd\nextbox,\c!height=\ht\nextbox,\c!offset=\zeropoint]%
+ \normalframedwithsettings[\c!location=\v!normal]} % different kind of location
+
+\def\pack_layers_set_framed_d[#1][#2][#3]%
+ {\setlayer[#1][\c!width=\wd\nextbox,\c!height=\ht\nextbox,#2,\c!offset=\zeropoint]%
+ \normalframedwithsettings[\c!location=\v!normal,#2]} % different kind of location
+
+\def\pack_layers_set_framed_t[#1][#2][#3]%
+ {\setlayer[#1][#2]%
+ \normalframedwithsettings[#3]}
+
+\permanent\tolerant\protected\def\setlayertext[#1]#*[#2]#*[#3]%
+ {\bgroup
+ \letdummyparameter\c!align\empty
+ \letdummyparameter\c!width\hsize
+ \letdummyparameter\c!color\empty
+ \letdummyparameter\c!style\empty
+ \getdummyparameters[#3]%
+ \dowithnextboxcontent
+ {\forgetall
+ \hsize\directdummyparameter\c!width
+ \usealignparameter\directdummyparameter
+ \dousestyleparameter{\directdummyparameter\c!style}}
+ {\setlayer[#1][#2]{\strut\dousecolorparameter{\directdummyparameter\c!color}\flushnextbox}% maybe expand the color
+ \egroup}%
+ \vtop}
+
+% \setupbackgrounds
+% [page]
+% [background=pagefigures]
+%
+% \definelayer
+% [pagefigures]
+% [x=-2mm,
+% y=-2mm,
+% width=\paperwidth,
+% height=\paperheight]
+%
+% \definelayerpreset [lefttop] [corner={left,top},location={right,bottom}]
+% \definelayerpreset [righttop] [corner={right,top},location={left,bottom}]
+% \definelayerpreset [leftbottom] [corner={left,bottom},location={right,top}]
+% \definelayerpreset [rightbottom] [corner={right,bottom},location={left,top}]
+% \definelayerpreset [middle] [corner=middle,location=middle]
+%
+% \setlayer[pagefigures][preset=lefttop]
+% \setlayer[pagefigures][preset=righttop]
+% \setlayer[pagefigures][preset=leftbottom]
+% \setlayer[pagefigures][preset=rightbottom]
+
+\definelayerpreset
+ [\v!left\v!top]
+ [\c!corner={\v!left,\v!top},\c!location={\v!right,\v!bottom}]
+
+\definelayerpreset
+ [\v!right\v!top]
+ [\c!corner={\v!right,\v!top},\c!location={\v!left,\v!bottom}]
+
+\definelayerpreset
+ [\v!left\v!bottom]
+ [\c!corner={\v!left,\v!bottom},\c!location={\v!right,\v!top}]
+
+\definelayerpreset
+ [\v!right\v!bottom]
+ [\c!corner={\v!right,\v!bottom},\c!location={\v!left,\v!top}]
+
+\definelayerpreset
+ [\v!middle]
+ [\c!corner=\v!middle,\c!location=\v!middle]
+
+\definelayerpreset
+ [\v!middle\v!top]
+ [\c!location=\v!bottom,\c!corner=\v!top,\c!dx=.5\layerwidth]
+
+\definelayerpreset
+ [\v!middle\v!bottom]
+ [\c!location=\v!top,\c!corner=\v!bottom,\c!dx=.5\layerwidth]
+
+\definelayerpreset
+ [\v!middle\v!left]
+ [\c!location=\v!right,\c!corner=\v!left,\c!dy=.5\layerheight]
+
+\definelayerpreset
+ [\v!middle\v!right]
+ [\c!location=\v!left,\c!corner=\v!right,\c!dy=.5\layerheight]
+
+% left and right hanging:
+
+\definelayerpreset
+ [\v!left\v!top\v!left]
+ [\c!location={\v!left,\v!bottom},\c!corner={\v!left,\v!top}]
+
+\definelayerpreset
+ [\v!right\v!top\v!right]
+ [\c!location={\v!right,\v!bottom},\c!corner={\v!right,\v!top}]
+
+\installcorenamespace{alignedboxes}
+
+\permanent\tolerant\protected\def\alignedbox[#1]{\bgroup\serializecommalist[#1]\dowithnextboxcs\pack_boxes_aligned_finish}
+\permanent\tolerant\protected\def\aligned [#1]{\bgroup\serializecommalist[#1]\dowithnextboxcs\pack_boxes_aligned_finish\hbox}
+
+\def\pack_boxes_aligned_finish
+ {\ifcsname\??alignedboxes\serializedcommalist\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\middlebox
+ \fi{\flushnextbox}%
+ \egroup}
+
+\letcsname\??alignedboxes \endcsname\middlebox
+\letcsname\??alignedboxes\v!middle \endcsname\middlebox
+\letcsname\??alignedboxes\v!middle\v!middle\endcsname\middlebox
+\letcsname\??alignedboxes\v!left \endcsname\leftbox
+\letcsname\??alignedboxes\v!left \v!top \endcsname\lefttopbox
+\letcsname\??alignedboxes\v!left \v!bottom\endcsname\leftbottombox
+\letcsname\??alignedboxes\v!right \endcsname\rightbox
+\letcsname\??alignedboxes\v!right \v!top \endcsname\righttopbox
+\letcsname\??alignedboxes\v!right \v!bottom\endcsname\rightbottombox
+\letcsname\??alignedboxes\v!bottom \endcsname\bottombox
+\letcsname\??alignedboxes\v!bottom\v!left \endcsname\bottomleftbox
+\letcsname\??alignedboxes\v!bottom\v!right \endcsname\bottomrightbox
+\letcsname\??alignedboxes\v!top \endcsname\topbox
+\letcsname\??alignedboxes\v!top \v!left \endcsname\topleftbox
+\letcsname\??alignedboxes\v!top \v!right \endcsname\toprightbox
+\letcsname\??alignedboxes\v!line \endcsname\baselinemiddlebox % \v!grid is taken
+\letcsname\??alignedboxes\v!line \v!left \endcsname\baselineleftbox
+\letcsname\??alignedboxes\v!line \v!middle\endcsname\baselinemiddlebox
+\letcsname\??alignedboxes\v!line \v!right \endcsname\baselinerightbox
+\letcsname\??alignedboxes c\endcsname\middlebox
+\letcsname\??alignedboxes l\endcsname\leftbox
+\letcsname\??alignedboxes r\endcsname\rightbox
+\letcsname\??alignedboxes b\endcsname\bottombox
+\letcsname\??alignedboxes t\endcsname\topbox
+\letcsname\??alignedboxes lt\endcsname\lefttopbox
+\letcsname\??alignedboxes lb\endcsname\leftbottombox
+\letcsname\??alignedboxes rt\endcsname\righttopbox
+\letcsname\??alignedboxes rb\endcsname\rightbottombox
+\letcsname\??alignedboxes tl\endcsname\topleftbox
+\letcsname\??alignedboxes bl\endcsname\bottomleftbox
+\letcsname\??alignedboxes tr\endcsname\toprightbox
+\letcsname\??alignedboxes br\endcsname\bottomrightbox
+\letcsname\??alignedboxes m\endcsname\middlebox
+\letcsname\??alignedboxes g\endcsname\baselinemiddlebox
+\letcsname\??alignedboxes gl\endcsname\baselineleftbox
+\letcsname\??alignedboxes gc\endcsname\baselinemiddlebox
+\letcsname\??alignedboxes gr\endcsname\baselinerightbox
+
+% left/right/top/bottomoffset -> dimensions change
+% x/y | method=fixed -> dimensions don't change
+
+\installcorenamespace{offsetbox}
+
+\installautocommandhandler \??offsetbox {offsetbox} \??offsetbox
+
+\setupoffsetbox
+ [\c!x=\zeropoint,
+ \c!y=\zeropoint,
+ \c!width=\wd\nextbox,
+ \c!height=\ht\nextbox,
+ \c!depth=\dp\nextbox,
+ \c!location=,
+ \c!leftoffset=\zeropoint,
+ \c!rightoffset=\zeropoint,
+ \c!topoffset=\zeropoint,
+ \c!bottomoffset=\zeropoint,
+ \c!method=]
+
+\aliased\let\setupoffset\setupoffsetbox
+
+\permanent\tolerant\protected\def\offsetbox[#1]%
+ {\bgroup
+ \pack_boxes_offset_check[#1]%
+ \dowithnextboxcs\pack_boxes_offsetfinish}
+
+\permanent\tolerant\protected\def\offset[#1]%
+ {\bgroup
+ \pack_boxes_offset_check[#1]%
+ \dowithnextboxcs\pack_boxes_offsetfinish\hbox}
+
+\newcount\c_pack_boxes_offset_level
+
+\def\pack_boxes_offset_check
+ {\advance\c_pack_boxes_offset_level\plusone
+ \edef\currentoffsetbox{\the\c_pack_boxes_offset_level}%
+ \checkoffsetboxparent
+ \setupcurrentoffsetbox}
+
+\def\pack_boxes_offsetfinish
+ {\donefalse
+ \scratchxposition \offsetboxparameter\c!x
+ \scratchyposition \offsetboxparameter\c!y
+ \scratchleftoffset \offsetboxparameter\c!leftoffset
+ \scratchrightoffset \offsetboxparameter\c!rightoffset
+ \scratchtopoffset \offsetboxparameter\c!topoffset
+ \scratchbottomoffset\offsetboxparameter\c!bottomoffset
+ \relax % really needed
+ \ifzeropt\scratchleftoffset \else\donetrue\fi
+ \ifzeropt\scratchrightoffset \else\donetrue\fi
+ \ifzeropt\scratchtopoffset \else\donetrue\fi
+ \ifzeropt\scratchbottomoffset\else\donetrue\fi
+ \ifdone
+ \edef\p_method{\offsetboxparameter\c!method}%
+ \ifx\p_method\v!fixed % new
+ \ifzeropt\scratchleftoffset
+ \ifzeropt\scratchrightoffset \else
+ \scratchxposition-\scratchrightoffset
+ \scratchrightoffset\zeropoint
+ \fi
+ \else
+ \scratchxposition\scratchleftoffset
+ \scratchleftoffset\zeropoint
+ \fi
+ \ifzeropt\scratchtopoffset
+ \ifzeropt\scratchbottomoffset \else
+ \scratchyposition-\scratchbottomoffset
+ \scratchbottomoffset\zeropoint
+ \fi
+ \else
+ \scratchyposition\scratchtopoffset
+ \scratchtopoffset\zeropoint
+ \fi
+ \donefalse
+ \fi
+ \fi
+ \ifdone
+ \setbox\nextbox\vpack
+ {\forgetall % already done
+ \offinterlineskip
+ \kern\scratchtopoffset
+ \hpack
+ {\kern\scratchleftoffset
+ \box\nextbox
+ \kern\scratchrightoffset}%
+ \kern\scratchbottomoffset}%
+ \ht\nextbox\htdp\nextbox
+ \dp\nextbox\zeropoint
+ \fi
+ \scratchwidth \offsetboxparameter\c!width
+ \scratchheight\offsetboxparameter\c!height
+ \scratchdepth \offsetboxparameter\c!depth
+ \edef\p_location{\offsetboxparameter\c!location}%
+ \setbox\nextbox\hpack
+ {\kern\scratchxposition
+ \lower\scratchyposition\hpack
+ {\ifempty\p_location
+ \box\nextbox
+ \else
+ \alignedbox[\p_location]\hpack{\box\nextbox}%
+ \fi}}%
+ \wd\nextbox\scratchwidth
+ \ht\nextbox\scratchheight
+ \dp\nextbox\scratchdepth
+ \box\nextbox
+ \egroup}
+
+%D \starttyping
+%D \framed[offset=overlay]{\offset[leftoffset=1cm]
+%D {\framed[offset=overlay]{\offset[rightoffset=1cm]
+%D {\externalfigure[koe][width=1cm]}}}}
+%D
+%D \blank
+%D
+%D \framed[offset=overlay]{\offset[leftoffset=1cm] {\externalfigure[koe][width=5cm]}} \blank
+%D \framed[offset=overlay]{\offset[rightoffset=1cm] {\externalfigure[koe][width=5cm]}} \blank
+%D \framed[offset=overlay]{\offset[topoffset=1cm] {\externalfigure[koe][width=5cm]}} \blank
+%D \framed[offset=overlay]{\offset[bottomoffset=1cm]{\externalfigure[koe][width=5cm]}} \blank
+%D \stoptyping
+
+% \useMPlibrary[pre] \setupbackgrounds[page][background=pagegrid]
+%
+% \placefigure[left,none]{}{\offset[leftoffset=1cm] {\externalfigure[cow][width=3cm]}} \input tufte
+% \placefigure[left,none]{}{\offset[rightoffset=1cm] {\externalfigure[cow][width=3cm]}} \input tufte
+% \placefigure[left,none]{}{\offset[topoffset=1cm] {\externalfigure[cow][width=3cm]}} \input tufte
+% \placefigure[left,none]{}{\offset[bottomoffset=1cm]{\externalfigure[cow][width=3cm]}} \input tufte
+
+% Some old code:
+%
+% \ltabbed{\romannumerals{3}}{\romannumerals{1}} test \endgraf
+% \ltabbed{\romannumerals{3}}{\romannumerals{2}} test \endgraf
+% \ltabbed{\romannumerals{3}}{\romannumerals{3}} test \endgraf
+%
+% \rtabbed{\romannumerals{3}}{\romannumerals{1}} test \endgraf
+% \rtabbed{\romannumerals{3}}{\romannumerals{2}} test \endgraf
+% \rtabbed{\romannumerals{3}}{\romannumerals{3}} test \endgraf
+%
+% \ctabbed{\romannumerals{3}}{\romannumerals{1}} test \endgraf
+% \ctabbed{\romannumerals{3}}{\romannumerals{2}} test \endgraf
+% \ctabbed{\romannumerals{3}}{\romannumerals{3}} test \endgraf
+
+\def\pack_boxes_tabbed#1#2#3#4%
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hpack{#3}%
+ \hpack to \wd\scratchbox{#1#4#2}%
+ \endgroup}
+
+\permanent\protected\def\ltabbed{\pack_boxes_tabbed\relax\hss}
+\permanent\protected\def\rtabbed{\pack_boxes_tabbed\hss \relax}
+\permanent\protected\def\ctabbed{\pack_boxes_tabbed\hss \hss}
+
+\aliased\let\mtabbed\ctabbed
+
+% to be documented
+
+\permanent\protected\def\phantombox[#1]% == \framed[\c!empty=\v!yes,\c!offset=\v!overlay,#1]{}
+ {\hpack\bgroup
+ \letdummyparameter\c!width \zeropoint
+ \letdummyparameter\c!height\zeropoint
+ \letdummyparameter\c!depth \zeropoint
+ \getdummyparameters[#1]%
+ \setbox\scratchbox\emptyhbox
+ \wd\scratchbox\directdummyparameter\c!width
+ \ht\scratchbox\directdummyparameter\c!height
+ \dp\scratchbox\directdummyparameter\c!depth
+ \box\scratchbox
+ \egroup}
+
+% \backgroundimage{1}{\hsize}{\vsize}{\externalfigure[cow][\c!width=3cm]}
+
+% \framed[offset=overlay,width=6cm,height=3cm]{\backgroundimage {1}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=6cm,height=3cm]{\backgroundimage {2}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=6cm,height=3cm]{\backgroundimage {3}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+
+% \framed[offset=overlay,width=4cm,height=2cm]{\backgroundimagefill{1}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=4cm,height=2cm]{\backgroundimagefill{2}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=4cm,height=2cm]{\backgroundimagefill{3}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=2cm,height=4cm]{\backgroundimagefill{1}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=2cm,height=4cm]{\backgroundimagefill{2}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+% \framed[offset=overlay,width=2cm,height=4cm]{\backgroundimagefill{3}{\hsize}{\vsize}{\externalfigure[cow][width=1cm]}}
+%
+% \framed[offset=overlay,width=4cm,height=2cm]{\backgroundimagefill{1}{\hsize}{\vsize}{\externalfigure[mill]}}
+% \framed[offset=overlay,width=4cm,height=2cm]{\backgroundimagefill{2}{\hsize}{\vsize}{\externalfigure[mill]}}
+% \framed[offset=overlay,width=4cm,height=2cm]{\backgroundimagefill{3}{\hsize}{\vsize}{\externalfigure[mill]}}
+% \framed[offset=overlay,width=2cm,height=4cm]{\backgroundimagefill{1}{\hsize}{\vsize}{\externalfigure[mill]}}
+% \framed[offset=overlay,width=2cm,height=4cm]{\backgroundimagefill{2}{\hsize}{\vsize}{\externalfigure[mill]}}
+% \framed[offset=overlay,width=2cm,height=4cm]{\backgroundimagefill{3}{\hsize}{\vsize}{\externalfigure[mill]}}
+
+\permanent\protected\def\backgroundimage#1#2#3% repeat hsize vsize
+ {\bgroup
+ \forgetall
+ \scratchcounter#1\relax
+ \scratchwidth #2\relax
+ \scratchheight #3\relax
+ \dowithnextboxcs\pack_boxes_background_image\hbox}
+
+\def\pack_boxes_background_image
+ {\offinterlineskip
+ \ifcase\scratchcounter
+ % just one
+ \else
+ \scratchdimen\scratchwidth \divide\scratchdimen\wd\nextbox\scratchnx\scratchdimen\advance\scratchnx\plusone\relax
+ \scratchdimen\scratchheight\divide\scratchdimen\ht\nextbox\scratchny\scratchdimen\advance\scratchny\plusone\relax
+ % to be considered: methods
+ \ifcase\scratchcounter
+ \or % x and y
+ \setbox\nextbox\hpack{\dorecurse\scratchnx{\copy\nextbox}}%
+ \setbox\nextbox\vpack{\dorecurse\scratchny{\copy\nextbox\endgraf}}%
+ \or % x
+ \setbox\nextbox\hpack{\dorecurse\scratchnx{\copy\nextbox}}%
+ \or % y
+ \setbox\nextbox\vpack{\dorecurse\scratchny{\copy\nextbox\endgraf}}%
+ \fi
+ \fi
+ \ifdim\wd\nextbox>\scratchwidth
+ \setbox\nextbox\hpack to \scratchwidth{\hss\box\nextbox\hss}%
+ \setbox\nextbox\hpack{\normalexpanded{\clip[\c!width=\the\scratchwidth,\c!height=\the\ht\nextbox]{\box\nextbox}}}%
+ \fi
+ \ifdim\ht\nextbox>\scratchheight
+ \setbox\nextbox\vpack to \scratchheight{\vss\box\nextbox\vss}%
+ \setbox\nextbox\hpack{\normalexpanded{\clip[\c!width=\the\wd\nextbox,\c!height=\the\scratchheight]{\box\nextbox}}}%
+ \fi
+ \box\nextbox
+ \egroup}
+
+\permanent\protected\def\backgroundimagefill#1#2#3% repeat hsize vsize
+ {\bgroup
+ \forgetall
+ \scratchcounter#1\relax
+ \scratchwidth #2\relax
+ \scratchheight #3\relax
+ \dowithnextboxcs\pack_boxes_background_image_fill\hbox}
+
+\def\pack_boxes_background_image_fill
+ {\offinterlineskip
+ \setbox\nextbox\hpack\bgroup
+ \ifdim\scratchwidth>\scratchheight
+ \scale[\c!width=\the\scratchwidth]{\box\nextbox}%
+ \else
+ \scale[\c!height=\the\scratchheight]{\box\nextbox}%
+ \fi
+ \egroup
+ \ifdim\wd\nextbox>\scratchwidth
+ \setbox\nextbox\hpack to \scratchwidth
+ {\ifcase\scratchcounter
+ \hss\box\nextbox\hss
+ \or
+ \box\nextbox\hss
+ \or
+ \hss\box\nextbox
+ \else
+ \hss\box\nextbox\hss
+ \fi}%
+ \setbox\nextbox\hpack
+ {\normalexpanded{\clip[\c!width=\the\scratchwidth,\c!height=\the\ht\nextbox]{\box\nextbox}}}%
+ \fi
+ \ifdim\ht\nextbox>\scratchheight
+ \setbox\nextbox\vpack to \scratchheight
+ {\ifcase\scratchcounter
+ \vss\box\nextbox\vss
+ \or
+ \box\nextbox\vss
+ \or
+ \vss\box\nextbox
+ \else
+ \vss\box\nextbox\vss
+ \fi}%
+ \setbox\nextbox\hpack
+ {\normalexpanded{\clip[\c!width=\the\wd\nextbox,\c!height=\the\scratchheight]{\box\nextbox}}}%
+ \fi
+ \box\nextbox
+ \egroup}
+
+\permanent\protected\def\overlayimage#1%
+ {\vpack to \overlayheight\bgroup
+ \vss
+ \hpack to \overlaywidth\bgroup
+ \hss
+ \externalfigure[#1][\c!factor=\v!min,\c!equalwidth=\overlaywidth,\c!equalheight=\overlayheight]%
+ \hss
+ \egroup
+ \vss
+ \egroup}
+
+\permanent\protected\def\clippedoverlayimage#1%
+ {\clip
+ [\c!width=\overlaywidth,\c!height=\overlayheight]
+ {\overlayimage{#1}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-com.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-com.mkxl
new file mode 100644
index 00000000000..44cfc9a4fa2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-com.mkxl
@@ -0,0 +1,990 @@
+%D \module
+%D [ file=pack-com, % used to be in core-mis,
+%D version=20120111,
+%D title=\CONTEXT\ Packing Macros,
+%D subtitle=Combinations,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Combinations}
+
+\unprotect
+
+% \startfloatcombination will be redone ... we can decouple the floatcontent
+% and caption and pass them to combinations so that we get better fit when the
+% caption is wider than the float, testcase:
+%
+% \startfloatcombination [2*2]
+% \placefigure[local]{alpha}{\externalfigure[cow.pdf][width=1cm]}%
+% \placefigure[local]{beta} {\externalfigure[cow.pdf][width=2cm]}%
+% \placefigure[local]{gamma}{\externalfigure[cow.pdf][width=3cm]}
+% \placefigure[local]{delta}{\externalfigure[cow.pdf][width=4cm]}
+% \stopfloatcombination
+
+%D We could of course map combinations onto one of the table mechanisms but as it
+%D has served us well for ages we keep this one. The code has been cleaned up a bit
+%D and mkiv'd.
+%D
+%D Okay ... I might luafy this one eventually.
+
+% \startcombination {alpha} {a} {beta} {b} \stopcombination
+% \startcombination[2*1] {alpha} {a} {beta} {b} \stopcombination
+% \startcombination[1*2] {alpha} {a} {beta} {b} \stopcombination
+% \startcombination[2] {alpha} {a} {beta} {b} \stopcombination
+% \startcombination[2] \combination {alpha} {a} \combination{beta} {b} \stopcombination
+
+%D We do support some structure but the order matters and currently it's only window
+%D dressing:
+
+%D \starttyping
+%D \let\startcontent\bgroup
+%D \let\stopcontent \egroup
+%D \let\startcaption\bgroup
+%D \let\stopcaption \egroup
+%D \stoptyping
+%D
+%D Of course we should have started with more structure as it would simply the code.
+%D
+%D \starttyping
+%D \startcombination
+%D \startcontent
+%D \externalfigure[cow]
+%D \stopcontent
+%D \startcaption
+%D Some cow.
+%D \stopcaption
+%D \startcontent
+%D \externalfigure[cow]
+%D \stopcontent
+%D \startcaption
+%D The same cow.
+%D \stopcaption
+%D \stopcombination
+%D \stoptyping
+
+\ifdefined\dotagcombination \else \aliased\let\dotagcombination\relax \fi
+
+\newsystemmode{combination}
+
+\appendtoks
+ \globalresetsystemmode{combination}%
+\to \everyinsidefloat
+
+\newcount\c_pack_combinations_nesting % local
+
+\newcount\c_pack_combinations_x % global
+\newcount\c_pack_combinations_y % global
+\newcount\c_pack_combinations_max % global
+
+\newdimen\d_pack_combinations_ht % global
+
+\newbox \b_pack_combinations_captions % global % can go
+\newbox \b_pack_combinations_temp % global % can go
+\newbox \b_pack_combinations_content % local
+\newbox \b_pack_combinations_caption % local
+
+\installcorenamespace{combination}
+
+\installcommandhandler \??combination {combination} \??combination
+
+\initializeboxstack{\??combination captions}
+\initializeboxstack{\??combination temp}
+
+\newcount\c_pack_combinations_x_saved
+\newcount\c_pack_combinations_y_saved
+\newcount\c_pack_combinations_max_saved
+
+\newdimen\d_pack_combinations_ht_saved
+
+\newbox \b_pack_combinations_captions_saved
+\newbox \b_pack_combinations_temp_saved
+\newbox \b_pack_combinations_content_saved
+\newbox \b_pack_combinations_caption_saved
+
+\setfalse\c_strc_constructions_define_commands
+
+\def\pack_combinations_push
+ {\advance\c_pack_combinations_nesting\plusone
+ \ifnum\c_pack_combinations_nesting>\plusone
+ \c_pack_combinations_x_saved \c_pack_combinations_x
+ \c_pack_combinations_y_saved \c_pack_combinations_y
+ \c_pack_combinations_max_saved\c_pack_combinations_max
+ \d_pack_combinations_ht_saved \d_pack_combinations_ht
+ \setbox\b_pack_combinations_captions_saved\box\b_pack_combinations_captions
+ \setbox\b_pack_combinations_temp_saved \box\b_pack_combinations_temp
+ \setbox\b_pack_combinations_content_saved \box\b_pack_combinations_content
+ \setbox\b_pack_combinations_caption_saved \box\b_pack_combinations_caption
+ \else
+ \globalsetsystemmode{combination}% why global
+ \fi}
+
+\def\pack_combinations_pop
+ {\ifnum\c_pack_combinations_nesting>\plusone
+ \global\c_pack_combinations_x \c_pack_combinations_x_saved
+ \global\c_pack_combinations_y \c_pack_combinations_y_saved
+ \global\c_pack_combinations_max\c_pack_combinations_max_saved
+ \global\d_pack_combinations_ht \d_pack_combinations_ht_saved
+ \global\setbox\b_pack_combinations_captions\box\b_pack_combinations_captions_saved
+ \global\setbox\b_pack_combinations_temp \box\b_pack_combinations_temp_saved
+ \setbox\b_pack_combinations_content \box\b_pack_combinations_content_saved
+ \setbox\b_pack_combinations_caption \box\b_pack_combinations_caption_saved
+ \else
+ \globalresetsystemmode{combination}% why global
+ \fi
+ \advance\c_pack_combinations_nesting\minusone}
+
+\definelabel
+ [\v!combination] % handy for configuring
+ [\c!numberconversion=\v!character,
+ \c!text=]
+
+\settrue\c_strc_constructions_define_commands
+
+\setupcombination
+ [\c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!distance=\emwidth,
+ \c!location=\v!bottom, % can be something {top,left}
+ \c!before=\blank,
+ \c!after=,
+ \c!inbetween={\blank[\v!medium]},
+ %\c!style=,
+ %\c!color=,
+ \c!nx=2, % new
+ \c!ny=1, % new
+ \c!align=\v!middle]
+
+\let\setupcombinations\setupcombination % for the moment (we might distinguish)
+
+\installcorenamespace{combinationlocation}
+\installcorenamespace{combinationalternative}
+
+\appendtoks
+ \setfalse\c_strc_constructions_define_commands
+ \normalexpanded
+ {\definelabel
+ [\v!combination:\currentcombination]%
+ [\v!combination\ifempty\currentcombinationparent\else:\currentcombinationparent\fi]}%
+ [\s!counter=\currentcombination,\c!levels=1]%
+ \settrue\c_strc_constructions_define_commands
+\to \everydefinecombination
+
+\setvalue{\??combinationlocation\v!left }{\let\m_pack_combinations_leftfiller\relax}
+\setvalue{\??combinationlocation\v!right }{\let\m_pack_combinations_rightfiller\relax}
+\setvalue{\??combinationlocation\v!top }{\let\m_pack_combinations_valigner\depthonlybox}
+\setvalue{\??combinationlocation\v!middle}{\let\m_pack_combinations_valigner\halfwaybox}
+
+\def\pack_combinations_location_reset
+ {\let\m_pack_combinations_rightfiller\hfil
+ \let\m_pack_combinations_leftfiller \hfil
+ \let\m_pack_combinations_valigner \firstofoneargument}
+
+\pack_combinations_location_reset
+
+\def\pack_combinations_location_step#1%
+ {\csname\??combinationlocation#1\endcsname}
+
+% formally ok:
+%
+% \protected\def\stopcombination
+% {\egroup
+% \egroup}
+%
+% more robust:
+%
+% \protected\def\stopcombination
+% {{}{}{}{}{}{}{}{}% catches (at most 4) missing entries
+% \egroup
+% \egroup}
+%
+% even better:
+%
+% \protected\def\stopcombination
+% {\bgroup
+% \scratchtoks{{}}%
+% \dorecurse\c_pack_combinations_y
+% {\toksapp{{}{}}}%
+% \expandafter\egroup\the\scratchtoks
+% \egroup
+% \dostoptagged
+% \egroup}
+%
+% faster
+
+\ifdefined\startcontent \else \aliased\let\startcontent\relax \fi
+\ifdefined\stopcontent \else \aliased\let\stopcontent \relax \fi
+\ifdefined\startcaption \else \aliased\let\startcaption\relax \fi
+\ifdefined\stopcaption \else \aliased\let\stopcaption \relax \fi
+
+\protected\def\pack_common_content_start{\bgroup\ignorespaces}
+\protected\def\pack_common_content_stop {\removeunwantedspaces\egroup}
+\protected\def\pack_common_caption_start{\bgroup\ignorespaces}
+\protected\def\pack_common_caption_stop {\removeunwantedspaces\egroup}
+
+\newtoks\everycombination
+
+\aliased\let\combination\empty
+
+\let\p_nx_ny\empty
+
+\permanent\protected\def\stopcombination
+ {\bgroup\normalexpanded{\egroup{}\ntimes{{}{}}\c_pack_combinations_y}% brr
+ \dostoptagged
+ \egroup
+ \pack_combinations_pop
+ \egroup}
+
+\permanent\tolerant\protected\def\startcombination[#1]#*[#2]% can be simplified
+ {\bgroup % so we can grab a group
+ \pack_combinations_push
+ \edef\currentcombination{#1}%
+ \edef\p_nx_ny{#2}%
+ %
+ \ifempty\p_nx_ny
+ \ifcondition\validassignment{#1}%
+ \let\currentcombination\empty
+ \setupcurrentcombination[#1]%
+ \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
+ \else
+ \doifelseinstring{*}\currentcombination
+ {\edef\p_nx_ny{\currentcombination*\plusone*}%
+ \let\currentcombination\empty}
+ {\doifelsenumber\currentcombination
+ {\edef\p_nx_ny{\currentcombination*\plusone*}%
+ \let\currentcombination\empty}
+ {\edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}}}%
+ \fi
+ \else
+ \ifcondition\validassignment{#2}%
+ \setupcurrentcombination[#2]%
+ \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
+ \else
+ \edef\p_nx_ny{\p_nx_ny*\plusone*}%
+ \fi
+ \fi
+ %
+% test first:
+%
+% \ifempty\p_nx_ny
+% \ifhastok={#1}%
+% \let\currentcombination\empty
+% \setupcurrentcombination[#1]%
+% \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
+% \orelse\ifhastok*{\currentcombination}%
+% \edef\p_nx_ny{\currentcombination*\plusone*}%
+% \let\currentcombination\empty
+% \orelse\ifchknum\currentcombination\or
+% \edef\p_nx_ny{\currentcombination*\plusone*}%
+% \let\currentcombination\empty
+% \else
+% \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
+% \fi
+% \orelse\ifhastok={#2}%
+% \setupcurrentcombination[#2]%
+% \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
+% \else
+% \edef\p_nx_ny{\p_nx_ny*\plusone*}%
+% \fi
+ %
+ \forgetall
+ %
+ \the\everycombination
+ %
+ \enforced\let\startcontent\pack_common_content_start
+ \enforced\let\stopcontent \pack_common_content_stop
+ \enforced\let\startcaption\pack_common_caption_start
+ \enforced\let\stopcaption \pack_common_caption_stop
+ %
+ \edef\p_height {\combinationparameter\c!height}%
+ \edef\p_width {\combinationparameter\c!width}%
+ \edef\p_location{\combinationparameter\c!location}%
+ \edef\p_distance{\combinationparameter\c!distance}%
+ %
+ \pack_combinations_location_reset
+ \rawprocesscommacommand[\p_location]\pack_combinations_location_step
+ %
+ \dostarttaggedchained\t!combination\currentcombination\??combination
+ \vbox \ifx\p_height\v!fit\else to \p_height \fi \bgroup
+ \enforced\let\combination\empty % permits \combination{}{} handy for cld
+ \normalexpanded{\pack_combinations_start_indeed[\p_nx_ny]}}
+
+\let\pack_combinations_check_x_y\relax
+
+\protected\def\pack_combinations_start_indeed[#1*#2*#3]%
+ {\global\c_pack_combinations_x#1\relax
+ \global\c_pack_combinations_y#2\relax
+ \setexpandedcombinationparameter\c!nx{\the\c_pack_combinations_x}% in case we access it
+ \setexpandedcombinationparameter\c!ny{\the\c_pack_combinations_y}% in case we access it
+ \pack_combinations_check_x_y
+ \dotagcombination
+ \global\setbox\b_pack_combinations_captions\emptybox
+ \global\c_pack_combinations_max\c_pack_combinations_x
+ \multiply\c_pack_combinations_y\c_pack_combinations_x
+ \tabskip\zeropoint
+ \halign \ifx\p_width\v!fit\else to \p_width \fi \bgroup % repetitive preamble
+ \aligntab
+ \m_pack_combinations_leftfiller
+ \alignmark\alignmark
+ \m_pack_combinations_rightfiller
+ \aligntab
+ \tabskip\zeropoint \s!plus 1fill % \fillskip
+ \alignmark\alignmark
+ \cr
+ \pack_combinations_pickup}
+
+%D I've first considered using a constructor directly but it's more overhead and
+%D some settings conflict with already used combination settings so instead we plug
+%D in labels. This also permits extensions later on.
+
+\appendtoks
+ \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
+\to \everydefinecombination
+
+\def\pack_combinations_pickup
+ {\dostarttagged\t!combinationpair\empty % better make this text
+ \dostarttagged\t!combinationcontent\empty
+ \expandafterpars\pack_combinations_pickup_content_indeed}
+
+\def\pack_combinations_pickup_content_indeed
+ {\dowithnextboxcs\pack_combinations_pickup_content\hbox}
+
+\def\pack_combinations_pickup_content % we want to add struts but still ignore an empty box
+ {\dostoptagged
+ \setbox\b_pack_combinations_content\box\nextbox
+ \dostarttagged\t!combinationcaption\empty
+ \expandnamespacemacro\??combinationalternative\p_pack_combinations_alternative\v!text}
+
+\setvalue{\??combinationalternative\v!text}%
+ {\expandafterpars\pack_combinations_alternative_text_indeed}
+
+\setvalue{\??combinationalternative\v!label}%
+ {\expandafterpars\pack_combinations_alternative_label_indeed}
+
+\def\pack_combinations_alternative_text_indeed
+ {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
+ \afterassignment\pack_combinations_caption_first
+ \let\nexttoken=}
+
+\def\pack_combinations_alternative_label_indeed
+ {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
+ \hsize\wd\b_pack_combinations_content
+ \usealignparameter\combinationparameter
+ \usecombinationstyleandcolor\c!style\c!color
+ \begstrut
+ \normalexpanded{\strc_labels_command[\v!combination\ifempty\currentcombination\else:\currentcombination\fi]}%
+ \endstrut
+ \egroup}
+
+\appendtoks
+ \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
+ \ifx\p_pack_combinations_alternative\v!label
+ \edef\p_continue{\combinationparameter\c!continue}%
+ \ifx\p_continue\v!yes \else
+ \normalexpanded{\strc_labels_reset{\v!combination\ifempty\currentcombination\else:\currentcombination\fi}{1}}%
+ \fi
+ \fi
+\to \everycombination
+
+\def\pack_combinations_pickup_caption
+ {\dostoptagged
+ \dostoptagged
+ \setbox\b_pack_combinations_caption\box\nextbox
+ \pack_combinations_pickup_package_pair}
+
+\def\pack_combinations_caption_first
+ {\futurelet\nexttoken\pack_combinations_caption_second}
+
+\def\pack_combinations_caption_second
+ {\ifx\nexttoken\egroup
+ % the caption is empty
+ \orelse\ifx\nexttoken\stopcaption
+ % the caption is empty (new per 2014-05-24)
+ \else
+ % todo: \p_pack_combinations_alternative\v!none: no style, strut etc
+ \hsize\wd\b_pack_combinations_content
+ \usealignparameter\combinationparameter
+ \usecombinationstyleandcolor\c!style\c!color
+ \bgroup
+ \aftergroup\endstrut
+ \aftergroup\egroup
+ \begstrut
+ \fi}
+
+\def\pack_combinations_pickup_package_pair % we need to store the caption row
+ {\vbox
+ {\forgetall
+ \m_pack_combinations_valigner{\box\b_pack_combinations_content}%
+ % we need to save the caption for a next alignment line
+ \pack_combinations_save_caption}%
+ \ifnum\c_pack_combinations_y>\plusone
+ \global\advance\c_pack_combinations_y\minusone
+ \global\advance\c_pack_combinations_x\minusone
+ \ifcase\c_pack_combinations_x
+ \doubleexpandafter\pack_combinations_pickup_package_pair_a
+ \else
+ \doubleexpandafter\pack_combinations_pickup_package_pair_b
+ \fi
+ \else
+ \singleexpandafter\pack_combinations_pickup_package_pair_c
+ \fi}
+
+\def\pack_combinations_pickup_package_pair_a
+ {\cr
+ \pack_combinations_flush_captions
+ \noalign
+ {\forgetall
+ \global\setbox\b_pack_combinations_captions\emptybox
+ \nointerlineskip
+ \combinationparameter\c!after
+ \combinationparameter\c!before
+ \vss
+ \nointerlineskip}%
+ \global\c_pack_combinations_x\c_pack_combinations_max
+ \pack_combinations_pickup}
+
+\def\pack_combinations_pickup_package_pair_b
+ {\aligntab
+ \aligntab
+ \aligntab
+ \kern\p_distance
+ \aligntab
+ \pack_combinations_pickup}
+
+\def\pack_combinations_pickup_package_pair_c
+ {\cr
+ \pack_combinations_flush_captions
+ \egroup}
+
+\installcorenamespace{combinationcaption}
+
+\def\pack_combinations_save_caption
+ {\ifdim\htdp\b_pack_combinations_caption>\d_pack_combinations_ht
+ \global\d_pack_combinations_ht\htdp\b_pack_combinations_caption
+ \fi
+ \savebox{\??combinationcaption:\number\c_pack_combinations_nesting}{\number\c_pack_combinations_x}{\box\b_pack_combinations_caption}}
+
+\let\pack_combinations_flush_captions_indeed\relax
+
+\def\pack_combinations_flush_captions
+ {\noalign
+ {\ifdim\d_pack_combinations_ht>\zeropoint
+ \nointerlineskip % indeed
+ \combinationparameter\c!inbetween
+ \global\c_pack_combinations_x\c_pack_combinations_max
+ \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_yes
+ \else
+ \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_nop
+ \fi}%
+ \pack_combinations_flush_captions_indeed
+ \crcr}
+
+\def\pack_combinations_flush_captions_yes
+ {\vpack to \d_pack_combinations_ht\bgroup
+ \foundbox{\??combinationcaption:\number\c_pack_combinations_nesting}{\number\c_pack_combinations_x}%
+ \vss
+ \egroup
+ \global\advance\c_pack_combinations_x\minusone
+ \ifnum\c_pack_combinations_x>\zerocount % \c_pack_combinations_max
+ \expandafter\pack_combinations_flush_captions_yes_followup
+ \else
+ \global\d_pack_combinations_ht\zeropoint
+ \initializeboxstack{\??combinationcaption:\number-\c_pack_combinations_nesting}%
+ \fi}
+
+\let\pack_combinations_flush_captions_nop\donothing
+
+\def\pack_combinations_flush_captions_yes_followup
+ {\aligntab
+ \aligntab
+ \aligntab
+ \aligntab
+ \pack_combinations_flush_captions_indeed}
+
+%D \macros
+%D {startfloatcombination}
+%D
+%D \setupexternalfigures[directory={../sample}]
+%D \startbuffer
+%D \placefigure
+%D [left,none]
+%D {}
+%D {\startfloatcombination[2*2]
+%D \placefigure{alpha}{\externalfigure[cow.pdf][width=1cm]}
+%D \placefigure{beta} {\externalfigure[cow.pdf][width=2cm]}
+%D \placefigure{gamma}{\externalfigure[cow.pdf][width=3cm]}
+%D \placefigure{delta}{\externalfigure[cow.pdf][width=4cm]}
+%D \stopfloatcombination}
+%D
+%D \input tufte
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\protected\def\pack_combinations_float_hack_a#1%
+ {\strc_floats_build_box_separate_split{#1}%
+ \box\b_strc_floats_separate_content}
+
+\protected\def\pack_combinations_float_hack_b#1%
+ {\box\b_strc_floats_separate_caption}
+
+\permanent\tolerant\protected\def\startfloatcombination[#1]#*[#2]%
+ {\ifinsidefloat\else\dontleavehmode\fi % tricky, floatcombinations fail to align well otherwise
+ \vbox\bgroup
+ \strc_floats_build_box_separate_set
+ %\insidecolumnstrue % trick, forces no centering, todo: proper switch/feature
+ \postcenterfloatmethod\zerocount
+ \forcelocalfloats
+ \enforced\permanent\protected\def\stopfloatcombination{\pack_combinations_stop_float{#1}}}
+
+\aliased\let\stopfloatcombination\relax
+
+\def\pack_combinations_float_check_x_y
+ {\ifnum\numexpr\c_pack_combinations_x*\c_pack_combinations_y\relax<\noflocalfloats\relax
+ \global\c_pack_combinations_x\noflocalfloats
+ \global\c_pack_combinations_y\plusone
+ \fi
+ \let\pack_combinations_check_x_y\relax}%
+
+\def\pack_combinations_stop_float#1%
+ {\scratchtoks\emptytoks
+ \dorecurse\noflocalfloats
+ {\appendetoks
+ {\pack_combinations_float_hack_a{\recurselevel}}%
+ {\pack_combinations_float_hack_b{\recurselevel}}%
+ \to\scratchtoks}% brrr
+ \let\pack_combinations_check_x_y\pack_combinations_float_check_x_y
+ \expanded{\startcombination[#1]\the\scratchtoks}\stopcombination
+ \resetlocalfloats
+ \egroup}
+
+%D \macros
+%D {definepairedbox, setuppairedbox, placepairedbox}
+%D
+%D Paired boxes, formally called legends, but from now on a legend is just an
+%D instance, are primarily meant for typesetting some text alongside an
+%D illustration. Although there is quite some variation possible, the functionality
+%D is kept simple, if only because in most cases such pairs are typeset sober.
+%D
+%D The location specification accepts a pair, where the first keyword specifies the
+%D arrangement, and the second one the alignment. The first key of the location pair
+%D is one of \type {left}, \type {right}, \type {top} or \type {bottom}, while the
+%D second key can also be \type {middle}.
+%D
+%D The first box is just collected in an horizontal box, but the second one is a
+%D vertical box that gets passed the bodyfont and alignment settings.
+%D
+%D In many cases the table builders can be used instead, but as this mechanism is a
+%D traditional \CONTEXT\ one we keep it around.
+
+%D \macros
+%D {setuplegend, placelegend}
+%D
+%D It makes sense to typeset a legend to a figure in \TEX\ and not in a drawing
+%D package. The macro \type {\placelegend} combines a figure (or something else) and
+%D its legend. This command is just a paired box.
+%D
+%D The legend is placed according to \type {location}, being \type {bottom} or \type
+%D {right}. The macro macro is used as follows.
+%D
+%D \starttyping
+%D \placefigure
+%D {whow}
+%D {\placelegend
+%D {\externalfigure[cow]}
+%D {\starttabulate
+%D \NC 1 \NC head \NC \NR
+%D \NC 2 \NC legs \NC \NR
+%D \NC 3 \NC tail \NC \NR
+%D \stoptabulate}}
+%D
+%D \placefigure
+%D {whow}
+%D {\placelegend
+%D {\externalfigure[cow]}
+%D {\starttabulate[|l|l|l|l|]
+%D \NC 1 \NC head \NC 3 \NC tail \NC \NR
+%D \NC 2 \NC legs \NC \NC \NC \NR
+%D \stoptabulate}}
+%D
+%D \placefigure
+%D {whow}
+%D {\placelegend[n=2]
+%D {\externalfigure[cow]}
+%D {\starttabulate
+%D \NC 1 \NC head \NC \NR
+%D \NC 2 \NC legs \NC \NR
+%D \NC 3 \NC tail \NC \NR
+%D \stoptabulate}}
+%D
+%D \placefigure
+%D {whow}
+%D {\placelegend[n=2]
+%D {\externalfigure[cow]}
+%D {head \par legs \par tail}}
+%D
+%D \placefigure
+%D {whow}
+%D {\placelegend[n=2]
+%D {\externalfigure[cow]}
+%D {\startitemize[packed]
+%D \item head \item legs \item tail \item belly \item horns
+%D \stopitemize}}
+%D
+%D \placefigure
+%D {whow}
+%D {\placelegend[n=2,width=.8\hsize]
+%D {\externalfigure[cow]}
+%D {\startitemize[packed]
+%D \item head \item legs \item tail \item belly \item horns
+%D \stopitemize}}
+%D
+%D \def\MytTestTwo#1#2%
+%D {\placefigure
+%D {whow}
+%D {\placelegend[location={#1,#2}]
+%D {\externalfigure[cow]}
+%D {\starttabulate
+%D \NC 1 \NC head \NC \NR
+%D \NC 2 \NC legs \NC \NR
+%D \NC 3 \NC tail \NC \NR
+%D \stoptabulate}}}
+%D
+%D \def\MytTestOne#1{\processcommalist[left,right,top,bottom]{\MytTestTwo{#1}}}
+%D
+%D \processcommalist[left,right,top,bottom,middle]\MytTestOne
+%D \stoptyping
+%D
+%D More structure is also possible (the order matters!):
+%D
+%D \starttyping
+%D \startplacefigure[title=whow]
+%D \startplacelegend[location={bottom,middle},color=red]
+%D \startcontent
+%D \externalfigure[cow]
+%D \stopcontent
+%D \startcaption
+%D \starttabulate[|l|l|]
+%D \NC 1 \NC head \NC \NR
+%D \NC 2 \NC legs \NC \NR
+%D \NC 3 \NC tail \NC \NR
+%D \stoptabulate
+%D \stopcaption
+%D \stopplacelegend
+%D \stopplacefigure
+%D \stoptyping
+
+% todo: natural size
+
+\newsystemmode{pairedbox}
+
+\appendtoks
+ \globalresetsystemmode{pairedbox}%
+\to \everyinsidefloat
+
+\installcorenamespace {pairedbox}
+
+\installcommandhandler \??pairedbox {pairedbox} \??pairedbox
+
+\setuppairedbox
+ [\c!n=1,
+ \c!distance=\bodyfontsize,
+ %\c!before=,
+ %\c!after=,
+ %\c!color=,
+ %\c!style=,
+ \c!inbetween={\blank[\v!medium]},
+ \c!width=\hsize,
+ \c!height=\vsize,
+ \c!maxwidth=\textwidth, % \makeupwidth,
+ \c!maxheight=\textheight, % \makeupheight,
+ %\c!bodyfont=,
+ %\c!align=,
+ \c!location=\v!bottom]
+
+% watch the hsize/vsize tricks
+
+\newbox \b_pack_pairedboxes_first
+\newbox \b_pack_pairedboxes_second
+\newdimen\s_pack_pairedboxes_size
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!setup\currentpairedbox\e!endsetup}{\setuppairedbox [\currentpairedbox]}%
+ \frozen\instance\setuevalue{\e!place\currentpairedbox }{\placepairedbox [\currentpairedbox]}% one argument is mandate anyway
+ \frozen\instance\setuevalue{\e!start\e!place\currentpairedbox }{\startplacepairedbox[\currentpairedbox]}% one argument is mandate anyway
+ \frozen\instance\setuevalue{\e!stop\e!place \currentpairedbox }{\stopplacepairedbox }%
+\to \everydefinepairedbox
+
+\permanent\tolerant\protected\def\startplacepairedbox[#1]#*[#2]%
+ {\bgroup
+ \edef\currentpairedbox{#1}%
+ \setupcurrentpairedbox[#2]%
+ \pairedboxparameter\c!before
+ \bgroup
+ \edef\p_location{\pairedboxparameter\c!location}%
+ \edef\p_n {\pairedboxparameter\c!n}%
+ %
+ \enforced\let\startcontent\pack_common_content_start
+ \enforced\let\stopcontent \pack_common_content_stop
+ \enforced\let\startcaption\pack_common_caption_start
+ \enforced\let\stopcaption \pack_common_caption_stop
+ %
+ \globalsetsystemmode{pairedbox}%
+ \pack_pairedboxes_before
+ \expandafterpars\pack_pairedboxes_first_pickup}
+
+\permanent\protected\def\stopplacepairedbox{} % we just pick up two boxes
+
+\aliased\let\placepairedbox\startplacepairedbox % we just pick up two boxes
+
+\def\pack_pairedboxes_first_pickup
+ {\dowithnextboxcs\pack_pairedboxes_first\hbox
+ \bgroup
+ \let\next=}
+
+\def\pack_pairedboxes_first
+ {\pack_pairedboxes_between
+ \expandafterpars\pack_pairedboxes_second_pickup}
+
+\def\pack_pairedboxes_second_pickup
+ {\dowithnextboxcs\pack_pairedboxes_second\vbox
+ \bgroup
+ \pack_pairedboxes_inside_second
+ \let\next=}
+
+\def\pack_pairedboxes_second
+ {\pack_pairedboxes_after
+ \egroup
+ \pairedboxparameter\c!after
+ \egroup}
+
+\newconditional\c_pack_pairedboxes_horizontal \settrue\c_pack_pairedboxes_horizontal
+
+\installcorenamespace{pairedboxnature}
+\installcorenamespace{pairedboxalign}
+
+\let\pack_pairedboxes_flush \relax
+\let\pack_pairedboxes_fill_top \relax
+\let\pack_pairedboxes_fill_bottom\relax
+
+\defcsname\??pairedboxnature\v!left\endcsname
+ {\settrue\c_pack_pairedboxes_horizontal
+ \let\pack_pairedboxes_flush\pack_pairedboxes_flush_left}
+
+\defcsname\??pairedboxnature\v!right\endcsname
+ {\settrue\c_pack_pairedboxes_horizontal
+ \let\pack_pairedboxes_flush\pack_pairedboxes_flush_right}
+
+\defcsname\??pairedboxnature\v!top\endcsname
+ {\setfalse\c_pack_pairedboxes_horizontal
+ \let\pack_pairedboxes_fill_top\relax
+ \let\pack_pairedboxes_fill_bottom\vss
+ \let\pack_pairedboxes_flush\pack_pairedboxes_flush_top}
+
+\defcsname\??pairedboxnature\v!bottom\endcsname
+ {\setfalse\c_pack_pairedboxes_horizontal
+ \let\pack_pairedboxes_fill_top\vss
+ \let\pack_pairedboxes_fill_bottom\relax
+ \let\pack_pairedboxes_flush\pack_pairedboxes_flush_bottom}
+
+\def\pack_pairedboxes_flush_left
+ {\box\b_pack_pairedboxes_second
+ \kern\pairedboxparameter\c!distance
+ \box\b_pack_pairedboxes_first}
+
+\def\pack_pairedboxes_flush_right
+ {\box\b_pack_pairedboxes_first
+ \kern\pairedboxparameter\c!distance
+ \box\b_pack_pairedboxes_second}
+
+\def\pack_pairedboxes_flush_top
+ {\box\b_pack_pairedboxes_second
+ \endgraf
+ \nointerlineskip
+ \pairedboxparameter\c!inbetween
+ \box\b_pack_pairedboxes_first}
+
+\def\pack_pairedboxes_flush_bottom
+ {\box\b_pack_pairedboxes_first
+ \endgraf
+ \nointerlineskip
+ \pairedboxparameter\c!inbetween
+ \box\b_pack_pairedboxes_second}
+
+\let\pack_pairedboxes_align_l\relax
+\let\pack_pairedboxes_align_r\relax
+\let\pack_pairedboxes_align_t\relax
+\let\pack_pairedboxes_align_b\relax
+
+\defcsname\??pairedboxalign\v!left\endcsname % 0
+ {\let\pack_pairedboxes_align_l\relax
+ \let\pack_pairedboxes_align_r\hss
+ \let\pack_pairedboxes_align_t\relax
+ \let\pack_pairedboxes_align_b\relax}
+
+\defcsname\??pairedboxalign\v!right\endcsname % 1
+ {\let\pack_pairedboxes_align_l\hss
+ \let\pack_pairedboxes_align_r\relax
+ \let\pack_pairedboxes_align_t\relax
+ \let\pack_pairedboxes_align_b\relax}
+
+\defcsname\??pairedboxalign\v!high\endcsname % 2
+ {\let\pack_pairedboxes_align_l\relax
+ \let\pack_pairedboxes_align_r\relax
+ \let\pack_pairedboxes_align_t\relax
+ \let\pack_pairedboxes_align_b\vss}
+
+\defcsname\??pairedboxalign\v!low\endcsname % 3
+ {\let\pack_pairedboxes_align_l\relax
+ \let\pack_pairedboxes_align_r\relax
+ \let\pack_pairedboxes_align_t\vss
+ \let\pack_pairedboxes_align_b\relax}
+
+\defcsname\??pairedboxalign\v!middle\endcsname % 4
+ {\let\pack_pairedboxes_align_l\hss
+ \let\pack_pairedboxes_align_r\hss
+ \let\pack_pairedboxes_align_t\vss
+ \let\pack_pairedboxes_align_b\vss}
+
+\defcsname\??pairedboxalign\v!bottom\endcsname{\csname\??pairedboxalign\v!low \endcsname}
+\defcsname\??pairedboxalign \v!top\endcsname{\csname\??pairedboxalign\v!high\endcsname}
+
+\def\pack_pairedbox_valign#1{\setbox#1\vpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_t\box#1\pack_pairedboxes_align_b}}
+\def\pack_pairedbox_halign#1{\setbox#1\hpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_l\box#1\pack_pairedboxes_align_r}}
+
+\def\pack_pairedboxes_before
+ {\ifempty\p_location
+ \csname\??pairedboxnature\v!left \endcsname
+ \csname\??pairedboxalign \v!middle\endcsname
+ \else
+ \getfromcommacommand[\p_location][1]%
+ \csname\??pairedboxnature
+ \ifcsname\??pairedboxnature\commalistelement\endcsname\commalistelement\else\v!left\fi
+ \endcsname
+ \getfromcommacommand[\p_location][2]%
+ \csname\??pairedboxalign
+ \ifcsname\??pairedboxalign\commalistelement\endcsname\commalistelement\else\v!middle\fi
+ \endcsname
+ \fi}
+
+\def\pack_pairedboxes_between
+ {\usebodyfontparameter\pairedboxparameter
+ \setbox\b_pack_pairedboxes_first\box\nextbox
+ \ifconditional\c_pack_pairedboxes_horizontal
+ \pack_pairedboxes_between_horizontal
+ \else
+ \pack_pairedboxes_between_vertical
+ \fi
+ \ifnum\p_n>\plusone
+ \setrigidcolumnhsize\hsize{\pairedboxparameter\c!distance}\p_n
+ \fi}
+
+\def\pack_pairedboxes_between_horizontal
+ {\scratchdistance\pairedboxparameter\c!distance
+ \scratchwidth\pairedboxparameter\c!maxwidth\relax
+ \setlocalhsize
+ \hsize\dimexpr\availablehsize-\wd\b_pack_pairedboxes_first-\scratchdistance\relax
+ \hsize\pairedboxparameter\c!width\relax % can be \hsize
+ \scratchdimen\dimexpr\wd\b_pack_pairedboxes_first+\scratchdistance\relax
+ \ifdim\dimexpr\hsize+\scratchdimen\relax>\scratchwidth
+ \hsize\dimexpr\scratchwidth-\scratchdimen\relax
+ \fi}
+
+\def\pack_pairedboxes_between_vertical
+ {\scratchwidth\pairedboxparameter\c!maxwidth\relax
+ \hsize\wd\b_pack_pairedboxes_first
+ \hsize\pairedboxparameter\c!width\relax % can be \hsize
+ \ifdim\hsize>\scratchwidth\relax
+ \hsize\scratchwidth
+ \fi}
+
+\def\pack_pairedboxes_after
+ {\setbox\b_pack_pairedboxes_second\vpack
+ {\ifnum\p_n>\plusone
+ \rigidcolumnbalance\nextbox
+ \else
+ \box\nextbox
+ \fi}%
+ \ifconditional\c_pack_pairedboxes_horizontal
+ \pack_pairedboxes_pack_horizontal
+ \else
+ \pack_pairedboxes_pack_vertical
+ \fi}
+
+\def\pack_pairedboxes_pack_horizontal
+ {\dontleavehmode\hbox\bgroup
+ \forgetall
+ \s_pack_pairedboxes_size\ht
+ \ifdim\ht\b_pack_pairedboxes_first>\ht\b_pack_pairedboxes_second
+ \b_pack_pairedboxes_first
+ \else
+ \b_pack_pairedboxes_second
+ \fi
+ \vsize\s_pack_pairedboxes_size
+ \ifdim\s_pack_pairedboxes_size<\pairedboxparameter\c!height\relax % can be \vsize
+ \s_pack_pairedboxes_size\pairedboxparameter\c!height
+ \fi
+ \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax
+ \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight
+ \fi
+ \pack_pairedbox_valign\b_pack_pairedboxes_first
+ \pack_pairedbox_valign\b_pack_pairedboxes_second
+ \pack_pairedboxes_flush
+ \egroup}
+
+\def\pack_pairedboxes_pack_vertical
+ {\dontleavehmode\vpack\bgroup
+ \forgetall
+ \s_pack_pairedboxes_size\wd
+ \ifdim\wd\b_pack_pairedboxes_first>\wd\b_pack_pairedboxes_second
+ \b_pack_pairedboxes_first
+ \else
+ \b_pack_pairedboxes_second
+ \fi
+ \pack_pairedbox_halign\b_pack_pairedboxes_first
+ \pack_pairedbox_halign\b_pack_pairedboxes_second
+ \s_pack_pairedboxes_size\ht\b_pack_pairedboxes_second
+ \vsize\s_pack_pairedboxes_size
+ \ifdim\ht\b_pack_pairedboxes_second<\pairedboxparameter\c!height\relax % can be \vsize
+ \s_pack_pairedboxes_size\pairedboxparameter\c!height\relax % \relax needed
+ \fi
+ \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax % todo: totale hoogte
+ \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight\relax % \relax needed
+ \fi
+ \ifdim\s_pack_pairedboxes_size>\ht\b_pack_pairedboxes_second
+ \setbox\b_pack_pairedboxes_second\vpack to \s_pack_pairedboxes_size
+ {\pack_pairedboxes_fill_top
+ \box\b_pack_pairedboxes_second
+ \pack_pairedboxes_fill_bottom}% \kern\zeropoint
+ \fi
+ \pack_pairedboxes_flush
+ \egroup}
+
+\def\pack_pairedboxes_inside_second
+ {\forgetall
+ \setupalign[\pairedboxparameter\c!align]%
+ \usepairedboxstyleandcolor\c!style\c!color
+ \tolerantTABLEbreaktrue % hm.
+ \blank[\v!disable]% use fast one
+ \everypar{\begstrut}} % also flushers here (see bTABLE)
+
+\definepairedbox[\v!legend]
+
+\permanent\protected\def\placeontopofeachother{\bgroup\dowithnextboxcs\pack_topofeachother_one\hbox}
+\permanent\protected\def\placesidebyside {\bgroup\dowithnextboxcs\pack_sidebyside_one \hbox}
+
+\def\pack_topofeachother_one{\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_topofeachother_two\hbox}
+\def\pack_sidebyside_one {\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_sidebyside_two \hbox}
+
+\def\pack_topofeachother_two{\setbox\scratchboxtwo\box\nextbox
+ \halign{\hss\alignmark\alignmark\hss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
+ \egroup\egroup}
+\def\pack_sidebyside_two {\setbox\scratchboxtwo\box\nextbox
+ \valign{\vss\alignmark\alignmark\vss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
+ \egroup\egroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-cut.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-cut.mkxl
new file mode 100644
index 00000000000..0cd06ed43e8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-cut.mkxl
@@ -0,0 +1,164 @@
+%D \module
+%D [ file=pack-cut, % comes from core-vis/trac-vis
+%D version=1996.06.01,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Cut boxes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D \macros
+%D {makecutbox, cuthbox, cutvbox, cutvtop}
+%D
+%D Although mainly used for marking the page, these macros can also serve local use.
+%D
+%D \startbuffer
+%D \setbox0=\vbox{a real \crlf vertical box} \makecutbox0
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This marked \type{\vbox} shows up as:
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D The alternative macros are used as:
+%D
+%D \startbuffer
+%D \cuthbox{a made cut box}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This is typeset as:
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D By setting the next macros one can influence the length of the marks as well as
+%D the horizontal and vertical divisions.
+
+\newdimen\d_pack_cutmarks_width
+\newdimen\d_pack_cutmarks_height
+\newdimen\d_pack_cutmarks_depth
+
+\newcount\horizontalcutmarks \horizontalcutmarks \plustwo
+\newcount\verticalcutmarks \verticalcutmarks \plustwo
+\newcount\cutmarkoffset \cutmarkoffset \plusone
+
+\mutable\let\cutmarksymbol \relax
+\mutable\let\cutmarktoptext \empty
+\mutable\let\cutmarkbottomtext \empty
+\mutable\let\cutmarkhoffset \empty
+\mutable\let\cutmarkvoffset \empty
+\mutable\def\cutmarklength {2\bodyfontsize}
+\mutable\def\cutmarkrulethickness{\onepoint}
+
+\permanent\protected\def\horizontalcuts
+ {\hpack to \d_pack_cutmarks_width
+ {\dorecurse\horizontalcutmarks{\vrule\s!width\cutmarkrulethickness\s!height\cutmarklength\hfill}%
+ \unskip}}
+
+\permanent\protected\def\verticalcuts
+ {\vpack to \dimexpr\d_pack_cutmarks_height+\d_pack_cutmarks_depth\relax
+ {\hsize\cutmarklength
+ \dorecurse\verticalcutmarks{\vrule\s!height\cutmarkrulethickness\s!width\hsize\vfill}%
+ \unskip}}
+
+\permanent\protected\def\baselinecuts
+ {\ifdim\d_pack_cutmarks_depth>\zeropoint
+ \vpack to \dimexpr\d_pack_cutmarks_height+\d_pack_cutmarks_depth\relax
+ {\hsize\dimexpr\cutmarklength/2\relax
+ \vskip\zeropoint\s!plus\d_pack_cutmarks_height
+ \vrule\s!height\cutmarkrulethickness\s!width\hsize
+ \vskip\zeropoint\s!plus\d_pack_cutmarks_depth}%
+ \fi}
+
+\permanent\protected\def\cutmarksymbols#1%
+ {\hpack to \d_pack_cutmarks_width
+ {\setbox\scratchbox\hbox to \cutmarklength
+ {\hss\infofont\cutmarksymbol\hss}%
+ \hss
+ \vpack to \cutmarklength
+ {\scratchdimen\dimexpr\cutmarklength/2\relax
+ \scratchskip \ifempty\cutmarkhoffset\cutmarkoffset\scratchdimen\else\cutmarkhoffset\fi
+ \vss
+ \hbox to \d_pack_cutmarks_width
+ {\llap{\copy\scratchbox\hskip\scratchskip}%
+ \hskip\scratchdimen\hss
+ \infofont#1%
+ \hss\hskip\scratchdimen
+ \rlap{\hskip\scratchskip\copy\scratchbox}}%
+ \vss}%
+ \hss}}
+
+\permanent\protected\def\makecutbox#1%
+ {\bgroup
+ \d_pack_cutmarks_height\ht#1%
+ \d_pack_cutmarks_depth \dp#1%
+ \d_pack_cutmarks_width \wd#1%
+ \setbox#1\hpack
+ {\dontcomplain
+ \forgetall
+ \boxmaxdepth\maxdimen
+ \offinterlineskip
+ \scratchdimen\dimexpr\cutmarklength/2\relax
+ \hsize\d_pack_cutmarks_width
+ \setbox\scratchbox\vpack
+ {\setbox\scratchbox\hpack{\horizontalcuts}%
+ \scratchskip\ifempty\cutmarkvoffset\cutmarkoffset\scratchdimen\else\cutmarkvoffset\fi
+ \tlap{\copy\scratchbox\vskip\scratchskip}%
+ \hpack to \d_pack_cutmarks_width
+ {\scratchskip\ifempty\cutmarkhoffset\cutmarkoffset\scratchdimen\else\cutmarkhoffset\fi
+ \setbox\scratchbox\hpack{\verticalcuts}%
+ \llap{\copy\scratchbox\hskip\scratchskip}%
+ \ifdim\d_pack_cutmarks_depth=\zeropoint
+ \hfill
+ \else
+ \bgroup
+ \setbox\scratchbox\hpack{\baselinecuts}%
+ \llap{\copy\scratchbox\hskip\scratchskip}%
+ \hfill
+ \rlap{\hskip\scratchskip\copy\scratchbox}%
+ \egroup
+ \fi
+ \rlap{\hskip\scratchskip\copy\scratchbox}}%
+ \blap{\vskip\scratchskip\copy\scratchbox}}%
+ \ht\scratchbox\d_pack_cutmarks_height
+ \dp\scratchbox\d_pack_cutmarks_depth
+ \wd\scratchbox\zeropoint
+ \startcolor[\defaulttextcolor]%
+ \box\scratchbox
+ \ifrelax\cutmarksymbol \else
+ \setbox\scratchbox\vpack
+ {\scratchskip\ifempty\cutmarkvoffset\cutmarkoffset\scratchdimen\else\cutmarkvoffset\fi
+ \vskip-\dimexpr\scratchskip+\cutmarklength\relax
+ \hpack{\cutmarksymbols\cutmarktoptext}%
+ \vskip\dimexpr\scratchskip+\d_pack_cutmarks_height+\d_pack_cutmarks_depth+\scratchskip\relax
+ \hpack{\cutmarksymbols\cutmarkbottomtext}}%
+ \ht\scratchbox\d_pack_cutmarks_height
+ \dp\scratchbox\d_pack_cutmarks_depth
+ \wd\scratchbox\zeropoint
+ \box\scratchbox
+ \fi
+ \stopcolor
+ \box#1}%
+ \wd#1\d_pack_cutmarks_width
+ \ht#1\d_pack_cutmarks_height
+ \dp#1\d_pack_cutmarks_depth
+ \egroup}
+
+\permanent\protected\def\cuthbox{\hpack\bgroup\dowithnextbox{\makecutbox\nextbox\flushnextbox\egroup}\hbox}
+\permanent\protected\def\cutvbox{\vpack\bgroup\dowithnextbox{\makecutbox\nextbox\flushnextbox\egroup}\vbox}
+\permanent\protected\def\cutvtop{\tpack\bgroup\dowithnextbox{\makecutbox\nextbox\flushnextbox\egroup}\vtop}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-fen.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-fen.mkxl
new file mode 100644
index 00000000000..fc9817f92f3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-fen.mkxl
@@ -0,0 +1,81 @@
+%D \module
+%D [ file=pack-fen,
+%D version=2011.07.21,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle= Fences for Ruled Content,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Fences for Ruled Content}
+
+%D \startbuffer
+%D \useMPlibrary[meta-imp-hooks.mkiv]
+%D
+%D \framed[frame=off,bottomframe=righthook]{Bottom right hook}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+
+\unprotect
+
+\permanent\protected\def\installleftframerenderer#1#2%
+ {\defcsname\??framedleft\v!on#1\endcsname{\whateverleftframe{#2}}%
+ \letcsname\??framedleft\v!off#1\expandafter\endcsname\csname\??framedleft\v!on#1\endcsname}
+
+\permanent\protected\def\installrightframerenderer#1#2%
+ {\defcsname\??framedright\v!on#1\endcsname{\whateverrightframe{#2}}%
+ \letcsname\??framedright\v!off#1\expandafter\endcsname\csname\??framedright\v!on#1\endcsname}
+
+\permanent\protected\def\installtopframerenderer#1#2%
+ {\defcsname\??framedtop\v!on#1\endcsname{\whatevertopframe{#2}}%
+ \letcsname\??framedtop\v!off#1\expandafter\endcsname\csname\??framedtop\v!on#1\endcsname}
+
+\permanent\protected\def\installbottomframerenderer#1#2%
+ {\defcsname\??framedbottom\v!on#1\endcsname{\whateverbottomframe{#2}}%
+ \letcsname\??framedbottom\v!off#1\expandafter\endcsname\csname\??framedbottom\v!on#1\endcsname}
+
+\newbox\b_framed_rendered
+
+\permanent\protected\def\whateverleftframe#1%
+ {\pack_framed_overlay_initialize_indeed
+ \setbox\b_framed_rendered\vpack to \d_overlay_height{\vss#1\vss}%
+ \setbox\b_framed_rendered\hpack to \zeropoint{\box\b_framed_rendered\hss}%
+ \ht\b_framed_rendered\zeropoint
+ \dp\b_framed_rendered\zeropoint
+ \box\b_framed_rendered}
+
+\permanent\protected\def\whateverrightframe#1%
+ {\pack_framed_overlay_initialize_indeed
+ \setbox\b_framed_rendered\vpack to \d_overlay_height{\vss#1\vss}%
+ \setbox\b_framed_rendered\hpack to \zeropoint{\hss\box\b_framed_rendered}%
+ \ht\b_framed_rendered\zeropoint
+ \dp\b_framed_rendered\zeropoint
+ \box\b_framed_rendered}
+
+\permanent\protected\def\whatevertopframe#1%
+ {\pack_framed_overlay_initialize_indeed
+ \setbox\b_framed_rendered\hpack to \d_overlay_width{\hss#1\hss}%
+ \setbox\b_framed_rendered\vpack to \zeropoint{\box\b_framed_rendered\vss}%
+ \ht\b_framed_rendered\zeropoint
+ \dp\b_framed_rendered\zeropoint
+ \box\b_framed_rendered
+ \nointerlineskip}
+
+\permanent\protected\def\whateverbottomframe#1%
+ {\pack_framed_overlay_initialize_indeed
+ \setbox\b_framed_rendered\hpack to \d_overlay_width{\hss#1\hss}%
+ \setbox\b_framed_rendered\vpack to \zeropoint{\vss\box\b_framed_rendered}%
+ \ht\b_framed_rendered\zeropoint
+ \dp\b_framed_rendered\zeropoint
+ \box\b_framed_rendered}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-lyr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-lyr.mkxl
new file mode 100644
index 00000000000..3c0352bf3d1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-lyr.mkxl
@@ -0,0 +1,779 @@
+%D \module
+%D [ file=pack-lyr,
+%D version=2000.10.20,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Layers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Layers}
+
+% todo : first / last / next / +... => page key
+% test on left/right box when no doublesided option given
+% use \ifcsname instead of doifvalue
+
+\unprotect
+
+% When being backgrounds layers get the background offset displacement. Should be
+% an option, on by default (compatibility).
+
+%D The layering mechanism implemented here is independent of the output routine, but
+%D future extensions may depend on a more close cooperation.
+%D
+%D First we overload a macro from \type {pack-rul}. From now on we accept a
+%D (optional) argument: the specific layer it will go in. This means that we can
+%D move an overlay from one background to the other using the dimensions of the
+%D parent.
+
+\ifdefined\defineoverlay \else \message{loaded to early} \wait \fi
+
+\pushoverloadmode
+
+\permanent\overloaded\tolerant\protected\def\defineoverlay[#1]#*[#2]#*[#3]% overlay [layer] content
+ {\ifnum\lastarguments=\plusthree
+ %\writestatus{BEWARE}{This (overlay definition) has changed!}% temp
+ \def\pack_framed_define_overlay_indeed##1{\defcsname\??overlay##1\endcsname{\setlayer[#2]{\executedefinedoverlay{##1}{#3}}}}%
+ \else
+ \def\pack_framed_define_overlay_indeed##1{\defcsname\??overlay##1\endcsname{\executedefinedoverlay{##1}{#2}}}%
+ \fi
+ \processcommalist[#1]\pack_framed_define_overlay_indeed}
+
+\popoverloadmode
+
+%D We use the command handler code. The previous, more direct parameter handling was
+%D 25\% faster when no parameters were passed when adding content to a layer.
+%D However, when we pass for instance a preset, the new methos is some 10\% faster
+%D and it happens that in most cases we do pass some parameters. It would be
+%D interesting to see if we can push the preset in between the regular chain but it
+%D could also lead to unwanted side effects when nesting layer placement.
+
+\installcorenamespace{layer}
+\installcorenamespace{layerbox}
+\installcorenamespace{layerpreset}
+\installcorenamespace{layerposition} % brr, unreadable
+
+%D \macros
+%D {definelayer,setuplayer}
+%D
+%D Each layer gets its own (global) box. This also means that the data that goes
+%D into a layer, is typeset immediately. Each layer automatically gets an associated
+%D overlay, which can be used in any background assignment.
+%D
+%D After a layer is defined, you can change its characteristics.
+
+\installcommandhandler \??layer {layer} \??layer
+
+\setuplayer
+ [\c!state=\v!start,
+ %\c!doublesided=,
+ %\c!preset=,
+ %\c!option=,
+ %\c!corner=,
+ %\c!page=,
+ %\c!rotation=, % geen 0 !
+ \c!direction=\v!normal,
+ \c!position=\v!no,
+ \c!method=\v!overlay,
+ \c!x=\zeropoint,
+ \c!y=\zeropoint,
+ \c!line=0,
+ \c!column=0,
+ \c!width=\wd\nextbox, % don't change this globally
+ \c!height=\ht\nextbox, % don't change this globally
+ \c!offset=\zeropoint,
+ \c!hoffset=\zeropoint,
+ \c!voffset=\zeropoint,
+ \c!dx=\zeropoint,
+ \c!dy=\zeropoint,
+ \c!location=rb,
+ \c!sx=1,
+ \c!sy=1,
+ \c!region=\layeranchor]
+
+\permanent\def\layeranchor{\currentlayer:\the\realpageno}
+
+\let\p_pack_layers_doublesided\empty
+\let\p_pack_layers_state \empty
+\let\p_pack_layers_option \empty
+\let\p_pack_layers_method \empty
+\let\p_pack_layers_preset \empty
+\let\p_pack_layers_rotation \empty
+\let\p_pack_layers_position \empty
+\let\p_pack_layers_hoffset \empty
+\let\p_pack_layers_voffset \empty
+\let\p_pack_layers_offset \empty
+\let\p_pack_layers_dx \empty
+\let\p_pack_layers_dy \empty
+\let\p_pack_layers_sx \empty
+\let\p_pack_layers_sy \empty
+\let\p_pack_layers_x \empty
+\let\p_pack_layers_y \empty
+\let\p_pack_layers_corner \empty
+\let\p_pack_layers_location \empty
+\let\p_pack_layers_line \empty
+\let\p_pack_layers_column \empty
+\let\p_pack_layers_width \empty
+\let\p_pack_layers_height \empty
+\let\p_pack_layers_direction \empty
+\let\p_pack_layers_region \empty
+
+\let\m_pack_layers_page \empty
+\let\m_pack_layers_target \empty
+\let\m_pack_layers_region \empty
+\let\m_pack_layers_anchor \empty
+
+\newconditional\c_pack_layers_repeated
+\newconditional\c_pack_layers_trace
+\newcount \c_pack_layers_current_data
+
+\newbox\b_layers \pushoverloadmode \mutable\let\b_layers\b_layers \popoverloadmode
+
+\newdimen\d_pack_layers_x_size
+\newdimen\d_pack_layers_y_size
+\newdimen\d_pack_layers_x_offset
+\newdimen\d_pack_layers_y_offset
+\newdimen\d_pack_layers_x_position
+\newdimen\d_pack_layers_y_position
+
+\newdimen\layerwidth
+\newdimen\layerheight
+
+\mutable\let\lastlayerxpos\!!zeropoint
+\mutable\let\lastlayerypos\!!zeropoint
+\mutable\let\lastlayerwd \!!zeropoint
+\mutable\let\lastlayerht \!!zeropoint
+\mutable\let\lastlayerdp \!!zeropoint
+
+\appendtoks
+ \edef\p_pack_layers_doublesided{\layerparameter\c!doublesided}%
+ \ifx\p_pack_layers_doublesided\v!yes
+ \relateparameterhandlers{layer}{\v!left \currentlayer}{layer}\currentlayer % permits left*
+ \relateparameterhandlers{layer}{\v!right\currentlayer}{layer}\currentlayer % permits right*
+ \pack_layers_preset_box{\v!left \currentlayer}%
+ \pack_layers_preset_box{\v!right\currentlayer}%
+ \fi
+ \pack_layers_preset_box\currentlayer
+ \normalexpanded{\defineoverlay[\currentlayer][\noexpand\composedlayer{\currentlayer}]}%
+\to \everydefinelayer
+
+\def\pack_layers_preset_box#1%
+ {\ifcsname\??layerbox#1\endcsname
+ \resetlayer[#1]%
+ \else
+ \expandafter\newbox\csname\??layerbox#1\endcsname
+ \fi}
+
+%D \macros
+%D {resetlayer}
+%D
+%D This macro hardly needs an explanation (but is seldom needed anyway).
+
+\def\pack_layers_reset_box#1%
+ {\ifcsname\??layerbox#1\endcsname
+ %\global\setbox\csname\??layerbox#1\endcsname\emptybox
+ \global\setbox\lastnamedcs\emptybox
+ \fi}
+
+\permanent\protected\def\resetlayer[#1]%
+ {\pack_layers_reset_box{#1}%
+ \pack_layers_reset_box{\v!left #1}%
+ \pack_layers_reset_box{\v!right#1}%
+ \pack_layers_reset_box{#1:\the\realpageno}}
+
+%D \macros
+%D {setlayer}
+%D
+%D Data is moved into a layer with the following macro. When \type {position} is
+%D set, relative positioning is used, with the current point as reference point.
+%D Otherwise the topleft corner is used as reference point.
+%D
+%D \starttyping
+%D \setlayer [identifier] [optional parameters] {data}
+%D \stoptyping
+
+\mutable\let\currentlayerwidth \empty
+\mutable\let\currentlayerheight\empty
+
+\permanent\tolerant\protected\def\setcurrentlayerdimensions[#1]#*[#2]% name left|right
+ {\edef\currentlayerwidth {\thelayerwidth {#2#1}}%
+ \edef\currentlayerheight{\thelayerheight{#2#1}}}
+
+\permanent\def\thelayerwidth #1{\the\ifcsname\??layerbox#1\endcsname\wd\lastnamedcs\else\zeropoint\fi}
+\permanent\def\thelayerheight#1{\the\ifcsname\??layerbox#1\endcsname\ht\lastnamedcs\else\zeropoint\fi}
+
+\permanent\tolerant\protected\def\setlayer[#1]#*[#2]#*[#3]% #4 == box do \fi is ok
+ {\bgroup
+ \nofarguments\lastarguments
+ \checkpositionoverlays % otherwise funny regions
+ \edef\currentlayer{#1}%
+ \edef\p_pack_layers_state{\layerparameter\c!state}%
+ \ifx\p_pack_layers_state\v!stop
+ \dowithnextboxcs\egroup\hbox % no pack ?
+ \orelse\ifnum\nofarguments=\plusthree
+ \edef\m_pack_layers_target{#2}%
+ \pack_layers_set_indeed{#3}%
+ \orelse\ifhastok={#2}%
+ \let\m_pack_layers_target\empty
+ \pack_layers_set_indeed{#2}%
+ \else
+ \edef\m_pack_layers_target{#2}%
+ \pack_layers_set_indeed\empty
+ \fi}
+
+\def\pack_layers_set_indeed#1%
+ {\page_backgrounds_recalculate % brrr
+ \global\advance\c_pack_layers_current_data\plusone
+ \forgetall
+ \dontcomplain
+ \edef\p_pack_layers_option{\layerparameter\c!option}%
+ \ifx\p_pack_layers_option\v!test
+ \settrue\c_pack_layers_trace
+ \traceboxplacementtrue
+ \fi
+ \dowithnextbox{\pack_layers_set_finish{#1}}\hbox}
+
+\def\pack_layers_set_finish#1%
+ {\ifcsname\??layerbox\currentlayer\endcsname % can move up
+ \ifx\m_pack_layers_target\v!even
+ \ifodd\realpageno
+ % discard nextbox
+ \else
+ \let\m_pack_layers_target\v!left
+ \pack_layers_set_content{#1}%
+ \fi
+ \orelse\ifx\m_pack_layers_target\v!odd
+ \ifodd\realpageno
+ \let\m_pack_layers_target\v!right
+ \pack_layers_set_content{#1}%
+ \else
+ % discard nextbox
+ \fi
+ \else
+ \pack_layers_set_content{#1}%
+ \fi
+ \else
+ \writestatus{layer}{unknown layer \currentlayer}%
+ \fi
+ \egroup}
+
+% todo: left/right
+% todo: get position data in one go
+
+\def\pack_layers_set_last_position_yes % target: left|right
+ {% this will become one call
+ \edef\m_pack_layers_anchor{\??layerposition\the\c_pack_layers_current_data}%
+ \edef\m_pack_layers_page {\MPp\m_pack_layers_anchor}%
+ %edef\m_pack_layers_region{\MPr\m_pack_layers_anchor}% wrong one
+ \edef\m_pack_layers_region{\layerparameter\c!region}%
+ \d_pack_layers_x_position \dimexpr-\MPx\m_pack_layers_region+\MPx\m_pack_layers_anchor\relax
+ \d_pack_layers_y_position \dimexpr \MPy\m_pack_layers_region-\MPy\m_pack_layers_anchor+\MPh\m_pack_layers_region\relax
+ \xdef\lastlayerxpos{\the\d_pack_layers_x_position}%
+ \xdef\lastlayerypos{\the\d_pack_layers_y_position}%
+ % \writestatus{layering}{region: \m_pack_layers_region=>\MPxywhd\m_pack_layers_region}%
+ % \writestatus {}{anchor: \m_pack_layers_anchor=>\MPxywhd\m_pack_layers_anchor}%
+ % \writestatus {}{offset: \c!dx,\c!dy =>\lastlayerxpos,\lastlayerypos}%
+ \global\letlayerparameter\c!state\v!start % needed ?
+ \setbox\b_layers\vpack to \d_pack_layers_y_size
+ {\hpack to \d_pack_layers_x_size
+ {\xypos\m_pack_layers_anchor\hss}%
+ \vss}}
+
+\def\pack_layers_set_last_position_nop
+ {\setbox\b_layers\emptybox
+ \d_pack_layers_x_position\p_pack_layers_sx\dimexpr\p_pack_layers_x\relax
+ \d_pack_layers_y_position\p_pack_layers_sy\dimexpr\p_pack_layers_y\relax
+ \glet\lastlayerxpos\!!zeropoint
+ \glet\lastlayerypos\!!zeropoint
+ \doifinset\v!bottom\p_pack_layers_corner\pack_layers_set_bottom_positions
+ \doifinset\v!right \p_pack_layers_corner\pack_layers_set_right_positions
+ \doifinset\v!middle\p_pack_layers_corner\pack_layers_set_middle_positions
+ \edef\m_pack_layers_page{\layerparameter\c!page}}
+
+\permanent\tolerant\protected\def\definelayerpreset[#1]#*[#2]%
+ {\ifarguments\or
+ \letcsname\??layerpreset#1\endcsname\empty
+ \orelse\ifhastok={#2}%
+ \defcsname\??layerpreset#1\endcsname{\setupcurrentlayer[#2]}%
+ \else
+ \defcsname\??layerpreset#1\endcsname{\csname\??layerpreset#2\endcsname}%
+ \fi}
+
+\mutable\integerdef\b_layers_page\zerocount
+
+\def\pack_layers_set_content#1%
+ {\layerwidth \layerparameter\c!width % global (local later)
+ \layerheight\layerparameter\c!height % global (local later)
+ \d_pack_layers_x_size\layerwidth
+ \d_pack_layers_y_size\layerheight
+ %
+ \setupcurrentlayer[#1]% preroll
+ %
+ \edef\p_pack_layers_preset {\layerparameter\c!preset }%
+ %
+ \ifcsname\??layerpreset\p_pack_layers_preset\endcsname
+ \lastnamedcs
+ \setupcurrentlayer[#1]% postroll
+ \fi
+ %
+ \edef\p_pack_layers_rotation {\layerparameter\c!rotation }%
+ \edef\p_pack_layers_position {\layerparameter\c!position }%
+ \edef\p_pack_layers_hoffset {\layerparameter\c!hoffset }%
+ \edef\p_pack_layers_voffset {\layerparameter\c!voffset }%
+ \edef\p_pack_layers_offset {\layerparameter\c!offset }%
+ \edef\p_pack_layers_dx {\layerparameter\c!dx }%
+ \edef\p_pack_layers_dy {\layerparameter\c!dy }%
+ \edef\p_pack_layers_sx {\layerparameter\c!sx }%
+ \edef\p_pack_layers_sy {\layerparameter\c!sy }%
+ \edef\p_pack_layers_x {\layerparameter\c!x }%
+ \edef\p_pack_layers_y {\layerparameter\c!y }%
+ \edef\p_pack_layers_corner {\layerparameter\c!corner }%
+ \edef\p_pack_layers_location {\layerparameter\c!location }%
+ \edef\p_pack_layers_line {\layerparameter\c!line }%
+ \edef\p_pack_layers_column {\layerparameter\c!column }%
+ \edef\p_pack_layers_width {\layerparameter\c!width }% local ones
+ \edef\p_pack_layers_height {\layerparameter\c!height }% local ones
+ \edef\p_pack_layers_direction{\layerparameter\c!direction}%
+ %
+ \ifx\p_pack_layers_position\v!overlay
+ \let\p_pack_layers_width \zeropoint
+ \let\p_pack_layers_height \zeropoint
+ \let\p_pack_layers_position\v!yes
+ \fi
+ \ifempty\p_pack_layers_rotation \else
+ % use direct call
+ \setbox\nextbox\hpack
+ {\rotate[\c!location=\v!high,\c!rotation=\layerparameter\c!rotation]{\box\nextbox}}%
+ \fi
+ \d_pack_layers_x_offset\p_pack_layers_sx\dimexpr
+ \ifx\p_pack_layers_hoffset\v!max\d_pack_layers_x_size\else\p_pack_layers_hoffset\fi+\p_pack_layers_offset+\p_pack_layers_dx
+ \relax
+ \d_pack_layers_y_offset\p_pack_layers_sy\dimexpr
+ \ifx\p_pack_layers_voffset\v!max\d_pack_layers_y_size\else\p_pack_layers_voffset\fi+\p_pack_layers_offset+\p_pack_layers_dy
+ \relax
+ \ifx\p_pack_layers_position\v!yes
+ \pack_layers_set_last_position_yes
+ \else
+ \pack_layers_set_last_position_nop
+ \fi
+ %
+ \ifempty\m_pack_layers_page \else % is expanded
+ \edef\m_pack_layers_page{:\m_pack_layers_page}%
+ \ifcsname\??layerbox\m_pack_layers_target\currentlayer\m_pack_layers_page\endcsname \else
+ \expandafter\newbox\csname\??layerbox\m_pack_layers_target\currentlayer\m_pack_layers_page\endcsname
+ \fi
+ \fi
+ \integerdef\b_layers_page\csname\??layerbox\m_pack_layers_target\currentlayer\m_pack_layers_page\endcsname
+ \ifvoid\b_layers_page
+ \gsetboxllx\b_layers_page\zeropoint
+ \gsetboxlly\b_layers_page\zeropoint
+ \fi
+ \global\setbox\b_layers_page\vpack %to \layerparameter\c!height % new, otherwise no negative y possible
+ {\offinterlineskip
+ \ifvoid\b_layers_page
+ \scratchwidth \zeropoint
+ \scratchheight\zeropoint
+ \else
+ \scratchwidth \wd\b_layers_page
+ \scratchheight\ht\b_layers_page
+ \ht\b_layers_page\zeropoint
+ \dp\b_layers_page\zeropoint
+ \wd\b_layers_page\zeropoint
+ \ifx\p_pack_layers_direction\v!reverse\else
+ \box\b_layers_page
+ \fi
+ \fi
+ % don't move
+ \xdef\lastlayerwd{\the\wd\nextbox}%
+ \xdef\lastlayerht{\the\ht\nextbox}% % not entirely ok when grid !
+ \xdef\lastlayerdp{\the\dp\nextbox}% % not entirely ok when grid !
+ % this code
+ \ifx\p_pack_layers_location\v!grid
+ \ht\nextbox\strutheight
+ \dp\nextbox\strutdepth
+ \else
+ \setbox\nextbox\hpack
+ {\alignedbox[\p_pack_layers_location]\vpack{\box\nextbox}}%
+ \fi
+ \ifnum\p_pack_layers_line=\zerocount\else % no \ifcase, can be negative
+ \advance\d_pack_layers_y_position\dimexpr\p_pack_layers_line\lineheight+\topskip-\lineheight-\ht\nextbox\relax
+ \fi
+ \ifnum\p_pack_layers_column=\zerocount\else % no \ifcase, can be negative
+ \advance\d_pack_layers_x_position\layoutcolumnoffset\p_pack_layers_column\relax
+ \fi
+ \ifx\p_pack_layers_location\v!grid
+ \setbox\nextbox\hpack
+ {\alignedbox[rb]\vpack{\box\nextbox}}%
+ \fi
+ % ll registration
+ \scratchdimen\dimexpr\d_pack_layers_x_position+\d_pack_layers_x_offset\relax
+ \ifdim\scratchdimen<\getboxllx\b_layers_page
+ \gsetboxllx\b_layers_page\scratchdimen
+ \fi
+ \advance\scratchdimen\wd\nextbox
+ \wd\nextbox\ifdim\scratchdimen>\scratchwidth \scratchdimen \else \scratchwidth \fi
+ \scratchdimen\dimexpr\d_pack_layers_y_position+\d_pack_layers_y_offset\relax
+ \ifdim\scratchdimen<\getboxlly\b_layers_page
+ \gsetboxlly\b_layers_page\scratchdimen
+ \fi
+ % ll compensation
+ \advance\scratchdimen\dimexpr\ht\nextbox+\dp\nextbox\relax
+ \ht\nextbox\ifdim\scratchdimen>\scratchheight \scratchdimen \else \scratchheight \fi
+ \dp\nextbox\zeropoint
+ % placement
+ \hsize\p_pack_layers_width
+ \vpack to \p_pack_layers_height \bgroup
+ \smashbox\nextbox
+ \vskip\dimexpr\d_pack_layers_y_position+\d_pack_layers_y_offset\relax
+ \hskip\dimexpr\d_pack_layers_x_position+\d_pack_layers_x_offset\relax
+ % or maybe instead of the \vskip
+ % \raise-\dimexpr\d_pack_layers_y_position+\d_pack_layers_y_offset\relax
+ \box\nextbox
+ \ifvoid\b_layers_page
+ % already flushed
+ \else
+ % the reverse case % check !
+ \vskip-\dimexpr\d_pack_layers_y_position+\d_pack_layers_y_offset\relax
+ \box\b_layers_page
+ \fi
+ \egroup}%
+ % when position is true, the layerbox holds the compensation and needs
+ % to be placed; never change this !
+ \ifvoid\b_layers \else
+ \box\b_layers
+ \fi}
+
+\def\pack_layers_set_bottom_positions
+ {\ifnum\p_pack_layers_line=\zerocount\else % can be < 0
+ \edef\p_pack_layers_line{\the\numexpr-\p_pack_layers_line+\layoutlines+\plusone\relax}% use counter instead ?
+ \fi
+ \ifdim\d_pack_layers_y_size>\zeropoint
+ \advance\d_pack_layers_y_position-\d_pack_layers_y_size
+ \d_pack_layers_y_position-\d_pack_layers_y_position
+ \d_pack_layers_y_offset-\d_pack_layers_y_offset
+ \fi}
+
+\def\pack_layers_set_right_positions
+ {\ifnum\p_pack_layers_column=\zerocount\else % can be < 0
+ \edef\p_pack_layers_column{\the\numexpr-\layerparameter\c!column+\layoutcolumns+\plusone\relax}% use counter instead ?
+ \fi
+ \ifdim\d_pack_layers_x_size>\zeropoint
+ \advance\d_pack_layers_x_position-\d_pack_layers_x_size
+ \d_pack_layers_x_position-\d_pack_layers_x_position
+ \d_pack_layers_x_offset-\d_pack_layers_x_offset
+ \fi}
+
+\def\pack_layers_set_middle_positions
+ {\ifdim\d_pack_layers_x_size>\zeropoint \advance\d_pack_layers_x_position.5\d_pack_layers_x_size \fi
+ \ifdim\d_pack_layers_y_size>\zeropoint \advance\d_pack_layers_y_position.5\d_pack_layers_y_size \fi}
+
+%D Given the task to be accomplished, the previous macro is not even that
+%D complicated. It mainly comes down to skipping to the right place and placing a
+%D box on top of or below the existing content. In the case of position tracking,
+%D another reference point is chosen.
+
+%D \macros
+%D {doifelselayerdata}
+
+\permanent\def\doifelselayerdata#1%
+ {\ifcsname\??layerbox#1\endcsname
+ %\ifvoid\csname\??layerbox#1\endcsname
+ \ifvoid\lastnamedcs
+ \doubleexpandafter\secondoftwoarguments
+ \else
+ \doubleexpandafter\firstoftwoarguments
+ \fi
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiflayerdataelse\doifelselayerdata
+
+%D \macros
+%D {flushlayer}
+%D
+%D When we flush a layer, we flush both the main one and the page dependent one
+%D (when defined). This feature is more efficient in \ETEX\ since there testing for
+%D an undefined macro does not takes hash space.
+
+% todo: setups before flush, handy hook
+
+\permanent\protected\def\flushlayer[#1]% quite core, so optimized (todo: check for void)
+ {\begingroup
+ \forgetall
+ \edef\currentlayer{#1}%
+ \edef\p_pack_layers_state{\layerparameter\c!state}%
+ \ifx\p_pack_layers_state\v!stop
+ % nothing
+ \orelse\ifx\p_pack_layers_state\v!next
+ \global\letlayerparameter\c!state\v!start % dangerous, stack-built-up
+ \orelse\ifx\p_pack_layers_state\v!continue
+ \global\letlayerparameter\c!state\v!repeat % dangerous, stack-built-up
+ \else
+ \edef\p_pack_layers_doublesided{\layerparameter\c!doublesided}%
+ \ifx\p_pack_layers_doublesided\v!yes
+ \ifcsname\??layerbox#1\endcsname
+ % we can make a dedicated one for this
+ \doifbothsidesoverruled
+ {\pack_layers_flush_double\v!left }%
+ {\pack_layers_flush_double\v!right}%
+ {\pack_layers_flush_double\v!left }%
+ \else
+ \pack_layers_flush_single
+ \fi
+ \else
+ \pack_layers_flush_single
+ \fi
+ \fi
+ \endgroup}
+
+% optimized
+
+\mutable\integerdef\b_layer_one\zerocount
+\mutable\integerdef\b_layer_two\zerocount
+
+\def\pack_layers_flush_single
+ {\ifcsname\??layerbox\currentlayer\endcsname
+ \ifvoid\lastnamedcs
+ \ifcsname\??layerbox\currentlayer:\the\realpageno\endcsname
+ \ifvoid\lastnamedcs\else
+ \chardef\b_layer_two\lastnamedcs
+ \pack_layers_flush_indeed\zerocount{\currentlayer:\the\realpageno}\b_layer_two
+ \fi
+ \fi
+ \else
+ \chardef\b_layer_one\lastnamedcs % will be \integerdef
+ \ifcsname\??layerbox\currentlayer:\the\realpageno\endcsname
+ \ifvoid\lastnamedcs\else
+ \chardef\b_layer_two\lastnamedcs % will be \integerdef
+ \startoverlay
+ {\pack_layers_flush_indeed\plusone \currentlayer \b_layer_one}%
+ {\pack_layers_flush_indeed\zerocount{\currentlayer:\the\realpageno}\b_layer_two}%
+ \stopoverlay
+ \fi
+ \else
+ \pack_layers_flush_indeed\plusone\currentlayer\b_layer_one
+ \fi
+ \fi
+ \orelse\ifcsname\??layerbox\currentlayer:\the\realpageno\endcsname
+ \ifvoid\lastnamedcs
+ % nothing
+ \else
+ \chardef\b_layer_two\lastnamedcs
+ \pack_layers_flush_indeed\zerocount{\currentlayer:\the\realpageno}\b_layer_two
+ \fi
+ \fi}
+
+% less optimized:
+
+\def\pack_layers_flush_double#1%
+ {\startoverlay
+ {\ifcsname\??layerbox\currentlayer\endcsname
+ \ifvoid\lastnamedcs\else \chardef\b_layer_two\lastnamedcs
+ \pack_layers_flush_indeed\plusone\currentlayer\b_layer_two
+ \fi
+ \fi}%
+ {\ifcsname\??layerbox\currentlayer:\the\realpageno\endcsname
+ \ifvoid\lastnamedcs\else \chardef\b_layer_two\lastnamedcs
+ \pack_layers_flush_indeed\zerocount{\currentlayer:\the\realpageno}\b_layer_two
+ \fi
+ \fi}%
+ {\ifcsname\??layerbox#1\currentlayer\endcsname
+ \ifvoid\lastnamedcs\else \chardef\b_layer_two\lastnamedcs
+ \pack_layers_flush_indeed\plusone{#1\currentlayer}\b_layer_two
+ \fi
+ \fi}%
+ {\ifcsname\??layerbox#1\currentlayer:\the\realpageno\endcsname
+ \ifvoid\lastnamedcs\else \chardef\b_layer_two\lastnamedcs
+ \pack_layers_flush_indeed\zerocount{#1\currentlayer:\the\realpageno}\b_layer_two
+ \fi
+ \fi}%
+ \stopoverlay}
+
+\let\pack_layers_top_fill \relax
+\let\pack_layers_bottom_fill\vss
+
+\def\pack_layers_flush_indeed#1#2#3%
+ {\begingroup % already grouped
+ \offinterlineskip
+ \edef\p_pack_layers_preset{\layerparameter\c!preset}%
+ \ifcsname\??layerpreset\p_pack_layers_preset\endcsname
+ \lastnamedcs
+ \fi
+ \edef\p_pack_layers_method{\layerparameter\c!method}%
+ \edef\p_pack_layers_option{\layerparameter\c!option}%
+ \ifx\p_pack_layers_option\v!test
+ \settrue\c_pack_layers_trace
+ \traceboxplacementtrue
+ \fi
+ \ifcase#1\relax
+ \setfalse\c_pack_layers_repeated
+ \else
+ \edef\p_pack_layers_position{\layerparameter\c!position}%
+ \ifx\p_pack_layers_position\v!yes
+ \setfalse\c_pack_layers_repeated
+ \else
+ \edef\p_pack_layers_repeat{\layerparameter\c!repeat}%
+ \ifx\p_pack_layers_repeat\v!yes
+ \settrue\c_pack_layers_repeated
+ \orelse\ifx\p_pack_layers_state\v!repeat
+ \settrue\c_pack_layers_repeated
+ \else
+ \setfalse\c_pack_layers_repeated
+ \fi
+ \fi
+ \fi
+ %chardef\b_layers\csname\??layerbox#2\endcsname % trick
+ \let\b_layers#3%
+ % we need to copy in order to retain the negative offsets for a next
+ % stage of additions, i.e. llx/lly accumulate in repeat mode and the
+ % compensation may differ each flush depending on added content
+ \setbox\nextbox
+ \ifx\p_pack_layers_method\v!fit
+ \pack_layers_positioned_box_yes
+ \else
+ \pack_layers_positioned_box_nop
+ \fi
+ % todo: method=offset => overlayoffset right/down (handy for backgrounds with offset)
+ \doifelseoverlay{#2}%
+ {\setlayoutcomponentattribute{\v!layer:#2}}%
+ \resetlayoutcomponentattribute
+ % we have conflicting demands: some mechanisms want ll anchoring .. I need to figure this out
+ % and maybe we will have 'origin=bottom' or so
+ \setbox\nextbox
+ \ifx\p_pack_layers_option\v!test \ruledvbox \else \vpack \fi \ifx\p_pack_layers_method\v!overlay to \d_overlay_height \fi \layoutcomponentboxattribute
+ {\pack_layers_top_fill
+%
+% \hpack \ifx\p_pack_layers_method\v!overlay to \d_overlay_width \fi
+% {\box\nextbox
+% \hss}%
+%
+ \ifx\p_pack_layers_method\v!overlay \wd\nextbox\d_overlay_width\fi
+ \box\nextbox
+%
+ \pack_layers_bottom_fill}%
+ % \edef\currentlayer{#2}% :\the\realpageno}% local .. check \anchor
+ % \edef\p_pack_layers_position{\layerparameter\c!position}% local
+ \ifx\p_pack_layers_position\v!yes
+ \edef\p_pack_layers_region{\layerparameter\c!region}%
+ \ifempty\p_pack_layers_region\else
+ \anch_mark_tagged_box\nextbox\p_pack_layers_region % was \layeranchor
+ \fi
+ \fi
+ \box\nextbox
+ %
+ \ifconditional\c_pack_layers_repeated\else
+ \gsetboxllx\b_layers\zeropoint
+ \gsetboxlly\b_layers\zeropoint
+ \fi
+ \endgroup}
+
+\def\pack_layers_positioned_box_yes
+ {\vpack
+ {\vskip-\getboxlly\b_layers
+ \hskip-\getboxllx\b_layers
+ \hsize-\dimexpr\getboxllx\b_layers-\wd\b_layers\relax
+ \ifconditional\c_pack_layers_repeated\copy\else\box\fi\b_layers}}
+
+\def\pack_layers_positioned_box_nop
+ {\ifconditional\c_pack_layers_repeated\copy\else\box\fi\b_layers}
+
+% \definelayer[test][method=fit] \setupcolors[state=start,option=test]
+%
+% \framed[framecolor=red,offset=overlay]{\setlayer[test]{aa}\setlayer[test][x=10pt]{g}\flushlayer[test]}
+% \framed[framecolor=red,offset=overlay]{\setlayer[test]{aa}\setlayer[test][x=-10pt]{bb}\flushlayer[test]}
+% \framed[framecolor=red,offset=overlay]{\setlayer[test][x=-20pt]{cccccc}\flushlayer[test]}
+% \framed[framecolor=red,offset=overlay]{\setlayer[test]{dd}\setlayer[test][x=-20pt,y=-3pt]{eeeeee}\flushlayer[test]}
+
+%D \macros
+%D {composedlayer,placelayer,tightlayer}
+%D
+%D This is a handy shortcut, which saves a couple of braces when we use it as
+%D parameter. This name also suits better to other layering commands.
+
+\permanent\protected\def\composedlayer#1{\flushlayer[#1]}
+
+\permanent\protected\def\tightlayer[#1]%
+ {\hpack
+ {\def\currentlayer{#1}% todo: left/right
+ \setbox\nextbox\emptybox
+ \d_overlay_width \layerparameter\c!width
+ \d_overlay_height\layerparameter\c!height
+ \composedlayer{#1}}}
+
+\aliased\let\placelayer\flushlayer
+
+%D \macros
+%D {setMPlayer}
+%D
+%D The following layer macro uses the positions that are registered by \METAPOST.
+%D
+%D \starttyping
+%D \definelayer[test]
+%D
+%D \setMPlayer [test] [somepos-1] {Whatever we want here!}
+%D \setMPlayer [test] [somepos-2] {Whatever we need there!}
+%D \setMPlayer [test] [somepos-3] {\externalfigure[cow.mps][width=2cm]}
+%D
+%D \startuseMPgraphic{oeps}
+%D draw fullcircle scaled 10cm withcolor red ;
+%D register ("somepos-1",2cm,3cm,center currentpicture) ;
+%D register ("somepos-2",8cm,5cm,(-1cm,-2cm)) ;
+%D register ("somepos-3",0cm,0cm,(-2cm,2cm)) ;
+%D \stopuseMPgraphic
+%D
+%D \getMPlayer[test]{\useMPgraphic{oeps}}
+%D \stoptyping
+%D
+%D The last line is equivalent to
+%D
+%D \starttyping
+%D \framed
+%D [background={foreground,test},offset=overlay]
+%D {\useMPgraphic{oeps}}
+%D \stoptyping
+
+\mutable\def\MPlayerwidth {\hsize}
+\mutable\def\MPlayerheight{\vsize}
+
+\permanent\tolerant\protected\def\setMPlayer[#1]#*[#2]#*[#3]%
+ {\edef\MPlayerwidth {\MPw{#2}}%
+ \edef\MPlayerheight{\MPh{#2}}%
+ \setlayer[#1][\c!x=\MPx{#2},\c!y=\MPy{#2},\c!position=\v!no,#3]}
+
+\permanent\tolerant\protected\def\getMPlayer[#1]#*[#2]%
+ {\framed[\c!background={\v!foreground,#1},\c!frame=\v!off,\c!offset=\v!overlay,#2]} % takes argument
+
+%D Watch out, a redefinition:
+
+\ifdefined\settextpagecontent \else
+ \writestatus\m!system{error in page-lyr.tex} \wait
+\fi
+
+\definelayer
+ [OTRTEXT]
+
+\setuplayer
+ [OTRTEXT]
+ [\c!width=\innermakeupwidth,
+ \c!height=\textheight]
+
+\aliased\let\normalsettextpagecontent\settextpagecontent % will be overloaded in page-spr
+
+\overloaded\aliased\let\normalsettextpagecontent\settextpagecontent % will be overloaded in page-spr
+
+\permanent\overloaded\protected\def\settextpagecontent#1#2#3% #2 and #3 will disappear
+ {\doifelselayerdata{OTRTEXT}
+ {\setbox#1\hpack to \makeupwidth
+ {\startoverlay
+ {\tightlayer[OTRTEXT]} % first, otherwise problems with toc
+ {\normalsettextpagecontent{#1}{#2}{#3}\box#1}
+ \stopoverlay}%
+ \dp#1\zeropoint}%
+ {\normalsettextpagecontent{#1}{#2}{#3}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-mis.mklx b/Master/texmf-dist/tex/context/base/mkxl/pack-mis.mklx
new file mode 100644
index 00000000000..5c63f533c6b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-mis.mklx
@@ -0,0 +1,112 @@
+%D \module
+%D [ file=pack-mis, % moved from e.g. core-mis
+%D version=1998.01.29,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Miscelaneous,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packing Macros / Misc Commands}
+
+\unprotect
+
+% a quite old mechanism already (but inheritance added)
+%
+% \defineplacement[name][settings]
+% \setupplacement [name][settings]
+% \placement [name][settings]
+% \place<name> [settings]
+
+\installcorenamespace{placement}
+
+\installcommandhandler \??placement {placement} \??placement
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!place\currentplacement}{\pack_placement{\currentplacement}}%
+\to \everydefineplacement
+
+\setupplacement
+ [\c!left=\hss,
+ \c!right=\hss,
+ \c!linecorrection=\v!off,
+ \c!depthcorrection=\v!off,
+ \c!grid=\v!middle,
+ %\c!before=,
+ %\c!after=,
+ \c!margin=\v!standard]
+
+\permanent\protected\def\placement[#tag]%
+ {\pack_placement{#tag}}
+
+\protected\def\pack_placement#tag%
+ {\bgroup
+ \edef\currentplacement{#tag}%
+ \doifelsenextoptionalcs\pack_placement_yes\pack_placement_nop}
+
+\def\pack_placement_yes[#settings]%
+ {\setupcurrentplacement[#settings]%
+ \pack_placement_nop}
+
+\def\pack_placement_nop
+ {\dowithnextboxcontentcs\forgetall\pack_placement_flush\vbox}
+
+\def\pack_placement_flush
+ {\setlocalhsize
+ \placementparameter\c!before
+ \begingroup
+ \disableparpositions
+ \setbox\nextbox\hbox to \localhsize
+ {\placementparameter\c!left
+ \flushnextbox
+ \placementparameter\c!right}%
+ %\ifinsidefloat \else
+ % \page_backgrounds_add_local_to_box\nextbox
+ %\fi
+\flushnotes % new per 2014-05-29 : todo: move them up in the mvl
+ \ifgridsnapping
+ \pack_placement_flush_grid_yes
+ \else
+ \pack_placement_flush_grid_nop
+ \fi
+ \endgroup
+ \placementparameter\c!after
+ \egroup}
+
+\def\pack_placement_flush_grid_yes
+ {\edef\p_grid{\placementparameter\c!grid}%
+ \ifempty\p_grid
+ \let\p_grid\v!middle
+ \fi
+ \pack_placement_flush_grid_noindent
+ \snaptogrid[\p_grid]\hbox{\box\nextbox}}
+
+\def\pack_placement_flush_grid_nop
+ {\edef\p_linecorrection {\placementparameter\c!linecorrection}%
+ \edef\p_depthcorrection{\placementparameter\c!depthcorrection}%
+ \ifx\p_linecorrection\v!on
+ \startbaselinecorrection
+ \fi
+ \pack_placement_flush_grid_noindent
+ \box\nextbox
+ \ifx\p_depthcorrection\v!on
+ \baselinecorrection
+ \fi
+ \ifx\p_linecorrection\v!on
+ \stopbaselinecorrection
+ \fi}
+
+\def\pack_placement_flush_grid_noindent
+ {\edef\p_margin{\placementparameter\c!margin}%
+ \ifx\p_margin\v!standard
+ \noindent
+ \orelse\ifx\p_margin\v!yes
+ \noindent
+ \fi}
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-mrl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-mrl.mkxl
new file mode 100644
index 00000000000..8bbeeb7985e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-mrl.mkxl
@@ -0,0 +1,845 @@
+%D \module
+%D [ file=pack-mrl, % was pack-rul/core-rul,
+%D version=1998.10.16,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=More Rules,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / More Rules}
+
+%D The (re)implementation of margin rules has been moved elsewhere.
+
+\unprotect
+
+%D \macros
+%D {setupblackrules,blackrule}
+%D
+%D The graphic capabilities of \TEX\ do not go beyond simple filled rules, except of
+%D course when using specials or, in \MKIV, manipulate node lists. Let's start with
+%D a warning: using this commands is far more slower than using the \TEX\ primitives
+%D \type {\hrule} and \type {\vrule}, but they save us some tokens. The
+%D characteristics of these rule drawing command can be set by:
+%D
+%D \showsetup{setupblackrules}
+%D
+%D The simple command draws only one rule. Its optional argument can be used to
+%D specify the dimensions. By setting the width, height or depth to \type {max}, one
+%D gets the natural dimensions.
+%D
+%D \showsetup{blackrule}
+
+\installcorenamespace{blackrules}
+
+\installsimplecommandhandler \??blackrules {blackrules} \??blackrules
+
+\permanent\protected\def\blackrule
+ {\hpack\bgroup
+ \doifelsenextoptionalcs\pack_black_rule_pickup\pack_black_rule_indeed}
+
+\def\pack_black_rule_pickup[#1]%
+ {\setupcurrentblackrules[#1]%
+ \pack_black_rule_indeed}
+
+\def\pack_black_rule_indeed
+ {\edef\p_width {\directblackrulesparameter\c!width }%
+ \edef\p_height{\directblackrulesparameter\c!height}%
+ \edef\p_depth {\directblackrulesparameter\c!depth }%
+ \ifx\p_height\v!max
+ \setstrut
+ \orelse\ifx\p_depth\v!max
+ \setstrut
+ \fi
+ \useblackrulesstyleandcolor\c!style\c!color
+ \ifcsname\??blackruletype\directblackrulesparameter\c!type\endcsname
+ \lastnamedcs
+ \else
+ \vrule
+ \fi
+ \ifx\p_width \v!max\s!width \emwidth\orelse\ifempty\p_width \else\s!width \p_width \fi
+ \ifx\p_height\v!max\s!height\strutht\orelse\ifempty\p_height\else\s!height\p_height\fi
+ \ifx\p_depth \v!max\s!depth \strutdp\orelse\ifempty\p_depth \else\s!depth \p_depth \fi
+ \egroup}
+
+\setupblackrules
+ [\c!width=\emwidth,
+ \c!height=\exheight,
+ \c!depth=\zeropoint,
+ \c!color=]
+
+%D \macros
+%D {blackrules}
+%D
+%D One can call for a sequence of black rules, if needed equally spaced over the
+%D given width.
+%D
+%D \showsetup{blackrules}
+%D
+%D The two alternative calls are therefore:
+%D
+%D \startbuffer
+%D Tell me, is this according to the \blackrules[n=6]?
+%D These \blackrules[alternativevariant=b,n=10,distance=.2em,width=4cm] are quite clear.
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D or:
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D We could of course have implemented this macro using \type {\leaders}, but this
+%D would probably have taken more tokens.
+
+\permanent\tolerant\protected\def\blackrules[#1]% probably never used
+ {\hpack\bgroup
+ \ifarguments\or\setupcurrentblackrules[#1]\fi
+ \scratchwidth \directblackrulesparameter\c!width
+ \scratchheight \directblackrulesparameter\c!height
+ \scratchdepth \directblackrulesparameter\c!depth
+ \scratchdistance\directblackrulesparameter\c!distance
+ \scratchcounter \directblackrulesparameter\c!n
+ \edef\p_alternative{\blackrulesparameter\c!alternative}%
+ \ifx\p_alternative\c!b % why not just check distance
+ \ifnum\scratchcounter=\plusone
+ \scratchdistance\zeropoint
+ \else
+ \scratchwidth\dimexpr(\scratchwidth-\scratchcounter\scratchdistance+\scratchdistance)/\scratchcounter\relax
+ \fi
+ \fi
+ \useblackrulesstyleandcolor\c!style\c!color
+ % a typical case of where we can use a simple loop or even a leaders
+ \dorecurse\scratchcounter\pack_black_rules_step
+ \unskip
+ \egroup}
+
+\def\pack_black_rules_step
+ {\ifcsname\??blackruletype\directblackrulesparameter\c!type\endcsname
+ \lastnamedcs
+ \else
+ \vrule
+ \fi
+ \s!width \scratchwidth
+ \s!height\scratchheight
+ \s!depth \scratchdepth
+ \relax
+ \ifzeropt\scratchdistance\else
+ \hskip\scratchdistance
+ \fi}
+
+\installcorenamespace{blackruletype}
+
+\setvalue{\??blackruletype mp}%
+ {\frule
+ type {mp}%
+ data {\includeMPgraphic{\directblackrulesparameter\c!mp}}
+ line \dimexpr\directblackrulesparameter\c!rulethickness\relax
+ }
+
+\letvalue{\??blackruletype\s!no }\novrule
+%letvalue{\??blackruletype\s!yes}\vrule
+
+\setupblackrules
+ [\c!n=3,
+ \c!rulethickness=\linewidth,
+ \c!alternative=\c!a,
+ \c!distance=.25\emwidth,
+ \c!color=]
+
+%D \macros
+%D {vl, hl}
+%D
+%D The command \type {\vl} draws a vertical rule \vl\ with strut dimensions,
+%D multiplied with the factor specified in the optional argument. The height and
+%D depth are clipped \vl [3] to the baselinedistance. Its horizontal counterpart
+%D \type {\hl} draws a horizontal rule \hl\ with a width of 1em, multiplied with the
+%D optional factor. The horizontal rule is drawn on top of the baseline.
+%D
+%D \showsetup{vl}
+%D \showsetup{hl}
+
+\protected\def\pack_rule_vl_indeed#1#2#3%
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox
+ {\vrule
+ \s!width #1\linewidth
+ \s!height#2\strutht
+ \s!depth #3\strutdp}%
+ \dp\scratchbox\strutdp
+ \ht\scratchbox\strutht
+ \box\scratchbox
+ \endgroup}
+
+\permanent\tolerant\protected\def\vl[#1]%
+ {\pack_rule_vl_indeed{#1}{#1}{#1}}
+
+\permanent\tolerant\protected\def\hl[#1]%
+ {\dontleavehmode
+ \hbox
+ {\vrule
+ \s!width #1\emwidth
+ \s!height\linewidth
+ \s!depth \zeropoint}}
+
+% \let\dovlwdhtdp\pack_rule_vl_indeed % used elsewhere .. not any more
+
+%D \macros
+%D {hairline, thinrule, thinrules, setupthinrules}
+%D
+%D Drawing thin lines can of course easily be accomplished by the \TEX\
+%D primitives \type{\hrule} and \type{\vrule}. The next few macros however
+%D free us from some specifications.
+%D
+%D \startbuffer
+%D some text
+%D
+%D \hairline
+%D
+%D some more text
+%D
+%D \thinrule
+%D
+%D more and more text
+%D
+%D hi \thinrule\ there
+%D
+%D and then the final text
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D becomes
+%D
+%D \startexample
+%D \getbuffer
+%D \stopexample
+%D
+%D So we've got
+%D
+%D \showsetup{hairline}
+%D \showsetup{thinrule}
+%D
+%D Both can be set up with:
+%D
+%D \showsetup{setupthinrules}
+%D
+%D We also have
+%D
+%D \showsetup{thinrules}
+%D
+%D which looks like: \thinrules[n=2]
+
+\installcorenamespace{thinrules}
+\installcorenamespace{thinrulealternatives}
+
+\installdirectcommandhandler \??thinrules {thinrules}
+
+\setupthinrules
+ [\c!interlinespace=\v!small,
+ \c!n=3,
+ \c!before=,
+ \c!inbetween={\blank[\v!white]},
+ \c!after=,
+ \c!color=,
+ \c!height=.5\linewidth,
+ \c!depth=.5\linewidth,
+ \c!frame=\v!on, % compatible with textbackgrounds
+ \c!alternative=\v!b,
+ \c!backgroundcolor=,
+ \c!background=,
+ \c!rulethickness=\linewidth]
+
+\letvalue{\??thinrulealternatives\v!a }\zerocount
+\letvalue{\??thinrulealternatives\v!b }\plusone
+\letvalue{\??thinrulealternatives\v!c }\plustwo
+\letvalue{\??thinrulealternatives\v!none}\zerocount
+
+\newconstant\c_pack_thinrules_type
+
+\permanent\protected\def\thinrule
+ {\strut
+ \bgroup
+ \edef\p_height {\directthinrulesparameter\c!height}%
+ \edef\p_depth {\directthinrulesparameter\c!depth}%
+ \edef\p_background{\directthinrulesparameter\c!background}%
+ \edef\p_frame {\directthinrulesparameter\c!frame}%
+ \linewidth\dimexpr\directthinrulesparameter\c!rulethickness/\plustwo\relax
+ \ifzeropt\linewidth
+ \c_pack_thinrules_type\zerocount
+ \orelse\ifx\p_frame\v!on
+ \c_pack_thinrules_type\expandnamespaceparameter\??thinrulealternatives\directthinrulesparameter\c!alternative\v!b\relax
+ \else
+ \c_pack_thinrules_type\zerocount
+ \fi
+ \ifnum\c_pack_thinrules_type=\plusone
+ \ifx\p_height\v!max
+ \scratchheight\strutht
+ \else
+ \setdimensionwithunit\scratchheight\p_height\strutht
+ \fi
+ \ifx\p_depth\v!max
+ \scratchdepth\strutdp
+ \else
+ \setdimensionwithunit\scratchdepth\p_depth\strutdp
+ \fi
+ \else
+ \scratchheight\strutht
+ \scratchdepth \strutdp
+ \fi
+ \ifx\p_background\v!color
+ \startcolor[\directthinrulesparameter\c!backgroundcolor]%
+ \ifnum\c_pack_thinrules_type=\plustwo % prevent overshoot due to rounding
+ \leaders
+ \hrule
+ \s!height\dimexpr\scratchheight-\linewidth\relax
+ \s!depth \dimexpr\scratchdepth -\linewidth\relax
+ \hfill
+ \else
+ \leaders
+ \hrule
+ \s!height\scratchheight
+ \s!depth \scratchdepth
+ \hfill
+ \fi
+ \stopcolor
+ \ifcase\c_pack_thinrules_type
+ % no rule
+ \or
+ \startcolor[\directthinrulesparameter\c!color]%
+ \hfillneg
+ \leaders
+ \hrule
+ \s!height\linewidth
+ \s!depth \linewidth
+ \hfill
+ \stopcolor
+ \or
+ \startcolor[\directthinrulesparameter\c!color]%
+ \hfillneg
+ \leaders
+ \hrule
+ \s!height\dimexpr-\scratchdepth+\linewidth\relax
+ \s!depth \scratchdepth
+ \hfill
+ \hfillneg
+ \leaders
+ \hrule
+ \s!height\scratchheight
+ \s!depth \dimexpr-\scratchheight+\linewidth\relax
+ \hfill
+ \stopcolor
+ \fi
+ \else
+ \ifcase\c_pack_thinrules_type
+ % no rule
+ \else
+ \startcolor[\directthinrulesparameter\c!color]%
+ \leaders
+ \hrule
+ \s!height\scratchheight
+ \s!depth \scratchdepth
+ \hfill
+ \stopcolor
+ \fi
+ \fi
+ \strut
+ \carryoverpar\egroup}
+
+\permanent\protected\def\hairline % can be overloaded
+ {\endgraf
+ \thinrule
+ \endgraf}
+
+\permanent\tolerant\protected\def\thinrules[#1]%
+ {\bgroup
+ \setupcurrentthinrules[#1]%
+ \scratchcounter\directthinrulesparameter\c!n\relax
+ \ifcase\scratchcounter
+ % nothing, not even before/after
+ \let\p_after\relax
+ \else
+ \assignvalue{\directthinrulesparameter\c!interlinespace}\m_pack_thinrules_interlinespace{1.0}{1.5}{2.0}%
+ \spacing\m_pack_thinrules_interlinespace
+ \edef\p_after {\directthinrulesparameter\c!after}%
+ \edef\p_inbetween{\directthinrulesparameter\c!inbetween}%
+ \directthinrulesparameter\c!before
+ \ifcase\scratchcounter\or
+ \thinrule
+ \else
+ \dorecurse\scratchcounter
+ {\ifnum\recurselevel=\scratchcounter \directvspacing\v!samepage \else % \penalty500
+ \ifnum\recurselevel=\plustwo \directvspacing\v!samepage \fi\fi % \penalty500
+ \thinrule
+ \ifnum\recurselevel<\scratchcounter\relax
+ % test needed, else messed up whitespace
+ \ifempty\p_inbetween
+ \softbreak % \ifhmode \hskip \parfillskip \break \fi
+ \else
+ \endgraf
+ \nowhitespace
+ \p_inbetween
+ \fi
+ \fi}%
+ \fi
+ \fi
+ \ifempty\p_after
+ \carryoverpar\egroup
+ \else
+ \p_after\egroup
+ \fi{}}
+
+%D A couple of examples are given below.
+%D
+%D \startbuffer
+%D \setupthinrules[n=3,inbetween=,color=gray]
+%D
+%D test test \thinrules\ test test \par
+%D test test \thinrules [color=green] test test \par
+%D test test \thinrules [height=max, depth=max] test test \par
+%D
+%D \setupthinrules[height=.9,depth=.9]
+%D
+%D test test \thinrules\ test test \par
+%D test test \thinrules [alternativevariant=b] test test \par
+%D test test \thinrules [alternativevariant=c] test test \par
+%D test test \thinrules [alternativevariant=c,inbetween=\vskip2ex] test test \par
+%D \stopbuffer
+%D
+%D \typebuffer {\getbuffer}
+%D
+%D There are a couple of alternative ways to visualize rules using backgrounds. At
+%D first sight these may look strange, but they make sense in educational settings.
+%D The alternatives are more or less compatible with the more advanced \METAPOST\
+%D based implementation.
+%D
+%D \startbuffer[a]
+%D \setupthinrules
+%D [n=2,
+%D backgroundcolor=gray ,
+%D rulethickness=1pt,
+%D colorkleur=donkerblauw,
+%D after=\blank,
+%D before=\blank]
+%D \stopbuffer
+%D
+%D \typebuffer[a]
+%D
+%D \startbuffer[b]
+%D \thinrules[alternativevariant=a]
+%D \thinrules[alternativevariant=b]
+%D \thinrules[alternativevariant=c]
+%D \stopbuffer
+%D
+%D \typebuffer[b] \getbuffer[a,b]
+%D
+%D \startbuffer[b]
+%D \thinrules[alternativevariant=a,background=color]
+%D \thinrules[alternativevariant=b,background=color]
+%D \thinrules[alternativevariant=c,background=color]
+%D \stopbuffer
+%D
+%D \typebuffer[b] \getbuffer[a,b]
+%D
+%D \startbuffer[b]
+%D \thinrules[alternativevariant=a,height=.8,depth=.8,background=color]
+%D \thinrules[alternativevariant=b,height=.8,depth=.8,background=color]
+%D \thinrules[alternativevariant=c,height=.8,depth=.8,background=color]
+%D \stopbuffer
+%D
+%D \typebuffer[b] \getbuffer[a,b]
+
+%D \macros
+%D {textrule, starttextrule, setuptextrules}
+%D
+%D Putting rules before and after a paragraph is very space sensitive, but the
+%D next command handles that quite well. It comes in two disguises:
+%D
+%D \startbuffer
+%D \textrule[top]{fragments}
+%D \input reich
+%D \textrule
+%D \stopbuffer
+%D
+%D \start \typebuffer \getbuffer \stop
+%D
+%D \startbuffer
+%D \setuptextrules
+%D [width=90pt,distance=12pt,rulecolor=blue,
+%D bodyfont=small,style=\sc,color=red]
+%D
+%D \starttextrule{Ship Building Tools}
+%D \nl \setuptolerance[tolerant] \input materie
+%D \stoptextrule
+%D \stopbuffer
+%D
+%D \bgroup \typebuffer \getbuffer \egroup
+%D
+%D \startbuffer
+%D \setuptextrules
+%D [location=inmargin,
+%D bodyfont=small,style=slantedbold]
+%D
+%D \starttextrule{wonderful}
+%D \input tufte
+%D \stoptextrule
+%D \stopbuffer
+%D
+%D \bgroup \typebuffer \getbuffer \egroup
+%D
+%D The formal definition of these commands is:
+%D
+%D \showsetup{textrule}
+%D \showsetup{starttextrule}
+%D \showsetup{setuptextrules}
+%D
+%D The implementation looks a bit complicated due to the optional arguments.
+
+\installcorenamespace{textrules}
+\installcorenamespace{textrulealternatives}
+
+\installdirectcommandhandler \??textrules {textrules}
+
+\setuptextrules
+ [\c!location=\v!left,
+ \c!before=\blank,
+ \c!after=\blank,
+ \c!inbetween=,
+ \c!width=2\emwidth,
+ \c!style=\v!bold,
+ \c!color=,
+ \c!rulecolor=,
+ \c!bodyfont=,
+ \c!depthcorrection=\v!on,
+ \c!rulethickness=\linewidth,
+ \c!distance=.5\emwidth]
+
+% can be done nicer with futureexpandis
+
+\permanent\tolerant\protected\def\textrule[#1]%
+ {\ifarguments
+ \expandafter\pack_textrule_nop
+ \else
+ \expandafter\pack_textrule_yes
+ \fi[#1]}
+
+\def\pack_textrule_yes[#1]%
+ {\expandnamespacevalue\??textrulealternatives{#1}\v!bottom}
+
+% \tolerant\def\pack_textrule_nop[#1]#:#=%
+% {\ifparameter#2\or
+% \expandafter\pack_textrule_nop_indeed_yes
+% \else
+% \expandafter\pack_textrule_nop_indeed_nop
+% \fi[#1]{#2}}
+
+\def\pack_textrule_nop[#1]%
+ {\doifelsenextbgroupcs\pack_textrule_nop_indeed_yes\pack_textrule_nop_indeed_nop}
+
+\def\pack_textrule_nop_indeed_yes{\csname\??textrulealternatives\v!top \endcsname}
+\def\pack_textrule_nop_indeed_nop{\csname\??textrulealternatives\v!bottom\endcsname\empty}
+
+%D\startbuffer
+%D\showstruts
+%D
+%D\setupwhitespace[none]
+%D
+%D\textrule[top]{test} xxxxx\smash{\strut} \textrule[bottom]{test}
+%D\textrule[top]{test} xxxxx\strut \textrule[bottom]{test}
+%D
+%D\setupwhitespace[big]
+%D
+%D\textrule[top]{test} xxxxx\smash{\strut} \textrule[bottom]{test}
+%D\textrule[top]{test} xxxxx\strut \textrule[bottom]{test}
+%D\stoptyping
+%D
+%D \typebuffer \start \getbuffer \stop
+
+\setvalue{\??textrulealternatives\v!top}#1%
+ {\page[\v!preference] % interferes
+ \directtextrulesparameter\c!before\relax
+ \blank[\v!samepage,\v!nowhite]%
+ \pack_textrule_with_text_yes{#1}%
+ \blank[\v!samepage,\v!nowhite]%
+ \directtextrulesparameter\c!inbetween\relax
+ \endgraf}
+
+\setvalue{\??textrulealternatives\v!bottom}#1%
+ {\blank[\v!samepage,\v!nowhite]%
+ \pack_textrule_following{#1}%
+ \blank[\v!samepage,\v!nowhite]%
+ \directtextrulesparameter\c!after\relax
+ \page[\v!preference]}
+
+\setvalue{\??textrulealternatives\v!middle}#1%
+ {\blank[\v!samepage,\v!nowhite]%
+ \directtextrulesparameter\c!inbetween\relax
+ \pack_textrule_following{#1}%
+ \blank[\v!samepage,\v!nowhite]%
+ \directtextrulesparameter\c!inbetween\relax
+ \page[\v!preference]}
+
+\def\pack_textrule_with_text_yes#1%
+ {\noindent % this will force side floats to be calculated
+ \bgroup
+ \setbox\scratchbox\hpack to \availablehsize
+ {\scratchwidth \directtextrulesparameter\c!rulethickness\relax
+ \scratchheight\dimexpr .5\exheight+.5\scratchwidth\relax
+ \scratchdepth \dimexpr-.5\exheight+.5\scratchwidth\relax
+ \doifsomething{#1}
+ {\doifelse{\directtextrulesparameter\c!location}\v!inmargin
+ {\llap
+ {\usetextrulesstyleandcolor\c!style\c!color
+ #1%
+ \hskip\leftmargindistance}}
+ {\color[\directtextrulesparameter\c!rulecolor]
+ {\vrule
+ \s!height\scratchheight
+ \s!depth \scratchdepth
+ \s!width \directtextrulesparameter\c!width}%
+ \hbox spread 2\dimexpr\directtextrulesparameter\c!distance\relax
+ {\hss
+ \usetextrulesstyleandcolor\c!style\c!color
+ \strut#1%
+ \hss}}}%
+ \color[\directtextrulesparameter\c!rulecolor]
+ {\leaders\hrule
+ \s!height\scratchheight
+ \s!depth \scratchdepth
+ \hfill}}%
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox
+ %\carryoverpar
+ \egroup}
+
+\def\pack_textrule_with_text_nop#1%
+ {\ifhmode
+ \endgraf
+ \fi
+ \doifelse{\directtextrulesparameter\c!depthcorrection}\v!on
+ \pack_textrule_correct_depth_yes
+ \pack_textrule_correct_depth_nop
+ \nointerlineskip
+ \noindent\naturalvpack % was \dontleavehmode
+ {\color[\directtextrulesparameter\c!rulecolor]
+ {\hrule
+ \s!depth \directtextrulesparameter\c!rulethickness
+ \s!height\zeropoint
+ \s!width \availablehsize}}}
+
+\def\pack_textrule_correct_depth_yes
+ {\vskip\dimexpr
+ \strutdp +.5\exheight
+ \ifdim\prevdepth>\strutdp\else
+ \ifdim\prevdepth>\zeropoint
+ -\prevdepth
+ \fi
+ \fi
+ \relax
+ \relax}
+
+\def\pack_textrule_correct_depth_nop
+ {\vskip\dimexpr
+ \strutdp +.5\exheight
+ \relax
+ \relax}
+
+\def\pack_textrule_following#1%
+ {\doifelsenothing{#1}
+ \pack_textrule_with_text_nop
+ \pack_textrule_with_text_yes
+ {#1}%
+ \ifvmode
+ \prevdepth\zeropoint
+ \fi}
+
+%D The grouped commands also supports bodyfont switching:
+
+\permanent\protected\def\starttextrule#1%
+ {\bgroup
+ \def\pack_textrule_nop_indeed_nop{\csname\??textrulealternatives\v!middle\endcsname}%
+ %\let\pack_textrule_nop_indeed_yes\pack_textrule_nop_indeed_nop % needs checking
+ \csname\??textrulealternatives\v!top\endcsname{#1}%
+ \bgroup
+ \usebodyfontparameter\directtextrulesparameter}
+
+\permanent\protected\def\stoptextrule
+ {\par
+ \egroup
+ \csname\??textrulealternatives\v!bottom\endcsname\empty
+ \egroup}
+
+%D \macros
+%D {fillinrules, setupfillinrules}
+%D
+%D The next few commands do not really deserve a place in a core module, because
+%D they deal with specific typography. Nevertheless I decided to make them part of
+%D the core, because they permit us to make questionaires. Let's start with some
+%D examples.
+%D
+%D \fillinrules[n=2,width=fit]{first}
+%D \fillinrules[n=2,width=broad]{first}
+%D \fillinrules[n=2,width=3cm]{first}
+%D \fillinrules[n=2,width=3cm,distance=.5em,separator=:]{first}
+%D \fillinrules[n=2]{first}{last}
+%D \fillintext{first}{last} \input reich \par
+%D
+%D The main command is \type{\fillinrules}. This command takes one and an optional
+%D second argument and sets a paragraph with empty visualized lines.
+%D
+%D \showsetup{fillinrules}
+%D \showsetup{setupfillinrules}
+
+\installcorenamespace{fillinrules}
+
+\installdirectcommandhandler \??fillinrules {fillinrules}
+
+\setupfillinrules
+ [\c!width=\v!broad,
+ \c!distance=\emwidth,
+ \c!before=\blank,
+ \c!after=\blank,
+ \c!n=\plusone,
+ \c!interlinespace=\v!small,
+ \c!separator=,
+ \c!style=,
+ \c!color=]
+
+\def\pack_fillinrules_rule_indeed{\thinrules}
+
+\permanent\tolerant\protected\def\fillinrules[#1]#:#=#*#=%
+ {\endgraf
+ \begingroup
+ \setupcurrentfillinrules[#1]%
+ \directfillinrulesparameter\c!before
+ \setupcurrentthinrules
+ [\c!n=\directfillinrulesparameter\c!n,
+ \c!interlinespace=\directfillinrulesparameter\c!interlinespace,
+ \c!before=,
+ \c!after=]%
+ \scratchdistance\directfillinrulesparameter\c!distance\relax
+ \noindent
+ \iftok{#2}\emptytoks\else
+ \edef\p_width{\directfillinrulesparameter\c!width}%
+ \ifx\p_width\v!fit
+ \scratchdistance\zeropoint
+ \hbox
+ \orelse\ifx\p_width\v!broad
+ \hbox
+ \else
+ \hbox to \directfillinrulesparameter\c!width
+ \fi
+ \bgroup
+ \usefillinrulesstyleandcolor\c!style\c!color
+ \strut
+ #2%
+ \hfill\directfillinrulesparameter\c!separator
+ \hskip\scratchdistance
+ \egroup
+ \fi
+ \setupwhitespace[\v!big]%
+ \ignorespaces
+ \pack_fillinrules_rule_indeed
+ \iftok{#3}\emptytoks\else
+ \kern\scratchdistance
+ \usefillinrulesstyleandcolor\c!style\c!color
+ #3%
+ \strut
+ \fi
+ \endgraf
+ \directfillinrulesparameter\c!after
+ \endgroup}
+
+%D \macros
+%D {fillintext}
+%D
+%D To provide compatible layouts when texts and lines are mixed, one can typeset
+%D a paragraph by using the command \type {\fillintext}.
+%D
+%D \showsetup{fillintext}
+
+\permanent\tolerant\protected\def\fillintext[#1]#:#=#*#=%
+ {\endgraf
+ \begingroup
+ \def\pack_fillinrules_rule_indeed{\unhbox\nextbox\unskip}% hm, needs checking
+ \dowithnextbox{\fillinrules[#1]{#2}{\hfill#3}}%
+ \hbox\bgroup\enforced\let\par\egroup\ignorespaces} % bah, i will drop this compatibility hack
+
+%D \macros
+%D {fillinline, setupfillinlines}
+%D
+%D Another member of the family takes care of putting a (often small) rule after
+%D a piece of text, like
+%D
+%D \startbuffer
+%D \fillinline \input reich \par
+%D \fillinline[margin=0cm] \input reich \par
+%D \stopbuffer
+%D
+%D \startexample
+%D \getbuffer
+%D \stopexample
+%D
+%D which was typeset by saying:
+%D
+%D \typebuffer
+%D
+%D The two commands that take care of this are:
+%D
+%D \showsetup{fillinline}
+%D \showsetup{setupfillinlines}
+%D
+%D Contrary to older implementations we now use the wrapper.
+
+\installcorenamespace{fillinlines}
+
+\installdirectcommandhandler \??fillinlines {fillinlines}
+
+\setupfillinlines
+ [\c!width=8\emwidth, % was 3cm
+ \c!margin=\directfillinlinesparameter\c!width,
+ \c!rulethickness=\linewidth,
+ \c!color=,
+ \c!distance=\emwidth,
+ \c!before=\blank,
+ \c!after=\blank]
+
+\protected\def\pack_fillinline_before#1%
+ {}
+
+\protected\def\pack_fillinline_after#1%
+ {\begingroup
+ \setupcurrentfillinlines[#1]%
+ \ifhmode\unskip\hfill\fi
+ \scratchdistance\directfillinlinesparameter\c!distance
+ \scratchheight\dimexpr\directfillinlinesparameter\c!rulethickness/\plustwo\relax
+ \scratchwidth \dimexpr\directfillinlinesparameter\c!width-\scratchdistance\relax
+ \ifdim\scratchwidth>\directfillinlinesparameter\c!margin\else
+ \expandafter\rlap
+ \fi
+ {\kern\scratchdistance
+ \blackrule
+ [\c!color=\directfillinlinesparameter\c!color,
+ \c!width=\scratchwidth,
+ \c!height=\scratchheight,
+ \c!depth=\scratchheight]}%
+ \frozen\advance\rightskip \directfillinlinesparameter\c!margin\relax
+ \frozen\parfillskip\zeropoint\relax
+ \endgroup}
+
+\permanent\tolerant\protected\def\fillinline[#1]%
+ {\registerparwrapper
+ {fillinline}%
+ {\pack_fillinline_before{#1}}%
+ {\pack_fillinline_after {#1}%
+ \unregisterparwrapper{fillinline}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-obj.lmt b/Master/texmf-dist/tex/context/base/mkxl/pack-obj.lmt
new file mode 100644
index 00000000000..f021339b472
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-obj.lmt
@@ -0,0 +1,305 @@
+if not modules then modules = { } end modules ['pack-obj'] = {
+ version = 1.001,
+ comment = "companion to pack-obj.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>We save object references in the main utility table. jobobjects are
+reusable components.</p>
+--ldx]]--
+
+local context = context
+local codeinjections = backends.codeinjections
+local ctx_doifelse = commands.doifelse
+
+local report = logs.reporter("objects")
+local trace = false trackers.register("objects",function(v) trace = v end)
+
+local nuts = nodes.nuts
+
+local setlink = nuts.setlink
+local getlist = nuts.getlist
+local setbox = nuts.setbox
+
+local new_latelua = nuts.pool.latelua
+
+local settexdimen = tokens.setters.dimen
+
+local getcount = tex.getcount
+
+local implement = interfaces.implement
+local setmacro = interfaces.setmacro
+
+local allocate = utilities.storage.allocate
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local jobobjects = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.objects = jobobjects
+
+local function initializer()
+ collected = jobobjects.collected
+ tobesaved = jobobjects.tobesaved
+end
+
+job.register('job.objects.collected', tobesaved, initializer, nil)
+
+local function saveobject(tag,number,page)
+ local data = { number, page }
+ tobesaved[tag] = data
+ collected[tag] = data
+end
+
+local function saveobjectspec(specification)
+ local tag = specification.tag
+ local data = { specification.number, specification.page }
+ tobesaved[tag] = data
+ collected[tag] = data
+end
+
+local function setobject(tag,number,page)
+ collected[tag] = { number, page }
+end
+
+local function getobject(tag)
+ return collected[tag] or tobesaved[tag]
+end
+
+local function getobjectnumber(tag,default)
+ local o = collected[tag] or tobesaved[tag]
+ return o and o[1] or default
+end
+
+local function getobjectpage(tag,default)
+ local o = collected[tag] or tobesaved[tag]
+ return o and o[2] or default
+end
+
+jobobjects.save = saveobject
+jobobjects.set = setobject
+jobobjects.get = getobject
+jobobjects.number = getobjectnumber
+jobobjects.page = getobjectpage
+
+-- implement {
+-- name = "saveobject",
+-- actions = saveobject
+-- }
+--
+-- implement {
+-- name = "setobject",
+-- actions = setobject,
+-- arguments = { "string", "integer", "integer" }
+-- }
+--
+-- implement {
+-- name = "objectnumber",
+-- actions = { getobjectnumber, context },
+-- arguments = "2 strings",
+-- }
+--
+-- implement {
+-- name = "objectpage",
+-- actions = { getobjectpage, context },
+-- arguments = "2 strings",
+-- }
+--
+-- implement {
+-- name = "doifelseobjectreferencefound",
+-- actions = { getobject, commands.doifelse },
+-- arguments = "string"
+-- }
+
+-- if false then
+-- -- we can flush the inline ref ourselves now if we want
+-- local flush = new_latelua("pdf.flushxform("..index..")")
+-- flush.next = list
+-- next.prev = flush
+-- end
+
+local data = table.setmetatableindex("table")
+
+objects = {
+ data = data,
+ n = 0,
+}
+
+local objects = objects
+
+function objects.register(ns,id,b,referenced,offset,mode)
+ local n = objects.n + 1
+ objects.n = n
+ nodes.handlers.finalizebox(b)
+ if mode == 0 then
+ -- tex
+ data[ns][id] = {
+ codeinjections.registerboxresource(b), -- a box number
+ offset,
+ referenced or false,
+ mode,
+ }
+ else
+ -- box (backend)
+ data[ns][id] = {
+ codeinjections.registerboxresource(b,offset), -- a box number
+ false,
+ referenced,
+ mode,
+ }
+ end
+ if trace then
+ report("registering object %a (n=%i)",id,n)
+ end
+end
+
+function objects.restore(ns,id) -- why not just pass a box number here too (ok, we also set offset)
+ local d = data[ns][id]
+ if d then
+ local index = d[1]
+ local offset = d[2]
+ local status = d[3]
+ local mode = d[4]
+ local hbox = codeinjections.restoreboxresource(index) -- a nut !
+ if status then
+ local list = getlist(hbox)
+ local page = new_latelua {
+ action = saveobjectspec,
+ tag = ns .. "::" .. id,
+ number = index,
+ page = getcount("realpageno"),
+ }
+ -- list is a rule
+ setlink(list,page)
+ end
+ setbox("objectbox",hbox)
+ settexdimen("objectoff",offset or 0)
+ else
+ setbox("objectbox",nil)
+ settexdimen("objectoff",0) -- for good old times
+ end
+ if trace then
+ report("restoring object %a",id)
+ end
+end
+
+function objects.dimensions(index)
+ local d = data[ns][id]
+ if d then
+ return codeinjections.boxresourcedimensions(d[1])
+ else
+ return 0, 0, 0, 0
+ end
+end
+
+function objects.reference(ns,id)
+ local d = data[ns][id]
+ if d then
+ return d[1]
+ else
+ return getobjectnumber(ns .."::" .. id,0)
+ end
+end
+
+function objects.page(ns,id)
+ return getobjectpage(ns .."::" .. id,getcount("realpageno"))
+end
+
+function objects.found(ns,id)
+ return data[ns][id]
+end
+
+implement {
+ name = "registerreferencedobject",
+ arguments = { "string", "string", "integer", true, "dimension", "integer" },
+ actions = objects.register,
+}
+
+implement {
+ name = "registerobject",
+ arguments = { "string", "string", "integer", false, "dimension", "integer" },
+ actions = objects.register,
+}
+
+implement {
+ name = "restoreobject",
+ arguments = "2 strings",
+ actions = objects.restore,
+}
+
+implement {
+ name = "doifelseobjectfound",
+ arguments = "2 arguments",
+ public = true,
+ protected = true,
+ actions = function(ns,id)
+ ctx_doifelse(data[ns][id])
+ -- ctx_doifelse(objects.reference(ns,id))
+ end,
+}
+
+implement {
+ name = "doifelseobjectreferencefound",
+ arguments = "2 arguments",
+ public = true,
+ protected = true,
+ actions = function(ns,id)
+ -- ctx_doifelse(data[ns][id])
+ ctx_doifelse(objects.reference(ns,id))
+ end,
+}
+
+implement {
+ name = "getobjectreference",
+ arguments = { "argument", "argument", "csname" },
+ public = true,
+ protected = true,
+ actions = function(ns,id,target)
+ setmacro(target,objects.reference(ns,id),"global")
+ end
+}
+
+implement {
+ name = "getobjectreferencepage",
+ arguments = { "argument", "argument", "csname" },
+ public = true,
+ protected = true,
+ actions = function(ns,id,target)
+ setmacro(target,objects.page(ns,id),"global")
+ end
+}
+
+implement {
+ name = "getobjectdimensions",
+ arguments = "2 arguments",
+ public = true,
+ protected = true,
+ actions = function(ns,id)
+ local object = data[ns][id]
+ local w, h, d, o = 0, 0, 0, 0
+ if object then
+ w, h, d, o = codeinjections.boxresourcedimensions(object[1])
+ end
+ settexdimen("objectwd",w or 0)
+ settexdimen("objectht",h or 0)
+ settexdimen("objectdp",d or 0)
+ settexdimen("objectoff",o or #objects > 2 and object[2] or 0)
+ end
+}
+
+-- for the moment here:
+
+implement {
+ name = "registerbackendsymbol",
+ arguments = { "string", "integer" },
+ actions = function(...)
+ codeinjections.registersymbol(...)
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-obj.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-obj.mkxl
new file mode 100644
index 00000000000..04fc90a5853
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-obj.mkxl
@@ -0,0 +1,168 @@
+%D \module
+%D [ file=pack-obj,
+%D version=1998.01.15,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Objects,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Objects}
+
+\unprotect
+
+\registerctxluafile{pack-obj}{autosuffix}
+
+%D \macros
+%D {setobject,getobject,ifinobject}
+%D
+%D Boxes can be considered reuable objects. Traditionally once passed to the \DVI\
+%D file, such objects cannot be reused. In \PDF\ however, reusing is possible and
+%D sometimes even a necessity. Therefore, \CONTEXT\ supports reusable objects and
+%D \LUATEX\ has native support for so called box resources.
+%D
+%D The first application of objects in \CONTEXT\ concerned \METAPOST\ graphics and
+%D fill||in form fields. Reusing resources can save lots of bytes and sometimes also
+%D runtime.
+%D
+%D \starttyping
+%D \setobject{class}{name}\somebox{}
+%D \getobject{class}{name}
+%D \stoptyping
+%D
+%D Here \type {\somebox} can be whatever box specification suits \TEX. Although the
+%D implementation in \MKIV\ is somewhat different the principles have not changed.
+
+\installcorenamespace {objects}
+
+\newif \ifinobject
+\newbox \objectbox
+
+\mutable\def\objectoffset{1cm}
+
+\newdimen\objectoff \permanent\def\objectmargin{\the\objectoff}
+\newdimen\objectwd \permanent\def\objectwidth {\the\objectwd}
+\newdimen\objectht \permanent\def\objectheight{\the\objectht}
+\newdimen\objectdp \permanent\def\objectdepth {\the\objectdp}
+
+%D If I have time I will use the unreferenced variant for e.g. mp reuse. This can be
+%D rewritten in \LUA\ anyway.
+
+\permanent\protected\def\setreferencedobject #1#2{\begingroup\synctexpause\objectoff\objectoffset\inobjecttrue\dowithnextbox{\pack_objects_set_yes{#1}{#2}}}
+\permanent\protected\def\settightreferencedobject #1#2{\begingroup\synctexpause\objectoff\zeropoint \inobjecttrue\dowithnextbox{\pack_objects_set_yes{#1}{#2}}}
+\permanent\protected\def\setunreferencedobject #1#2{\begingroup\synctexpause\objectoff\objectoffset\inobjecttrue\dowithnextbox{\pack_objects_set_nop{#1}{#2}}}
+\permanent\protected\def\settightunreferencedobject#1#2{\begingroup\synctexpause\objectoff\zeropoint \inobjecttrue\dowithnextbox{\pack_objects_set_nop{#1}{#2}}}
+
+\aliased\let\setobject \setreferencedobject
+\aliased\let\settightobject\settightreferencedobject
+
+%D We can get subtle differences in boundingboxes but both methods work ok.
+
+\newconstant\c_pack_objects_offset_mode % 0=tex 1=box
+
+\protected\def\pack_objects_set_yes#1#2%
+ {\ifcase\c_pack_objects_offset_mode
+ \ifzeropt\objectoff
+ \pack_objects_package_nop\nextbox
+ \else
+ \pack_objects_package_yes\nextbox
+ \fi
+ \else
+ \pack_objects_package_nop\nextbox
+ \fi
+ \clf_registerreferencedobject{#1}{#2}\objectbox\objectoff\c_pack_objects_offset_mode
+ \synctexresume
+ \endgroup}
+
+\protected\def\pack_objects_set_nop#1#2%
+ {\ifcase\c_pack_objects_offset_mode
+ \ifzeropt\objectoff
+ \pack_objects_package_nop\nextbox
+ \else
+ \pack_objects_package_yes\nextbox
+ \fi
+ \else
+ \pack_objects_package_nop\nextbox
+ \fi
+ \clf_registerobject{#1}{#2}\objectbox\objectoff\c_pack_objects_offset_mode
+ \synctexresume
+ \endgroup}
+
+\def\pack_objects_package_nop#1% we pack because otherwise \ruledhbox is still tight
+ {\setbox\objectbox\hpack{\box#1}}
+
+\def\pack_objects_package_yes#1%
+ {\objectwd\dimexpr\wd#1+2\objectoff\relax
+ \objectht\dimexpr\ht#1+ \objectoff\relax
+ \objectdp\dimexpr\dp#1+ \objectoff\relax
+ \setbox\objectbox\hpack
+ {\hskip\objectoff
+ \box#1}%
+ \wd\objectbox\objectwd
+ \ht\objectbox\objectht
+ \dp\objectbox\objectdp}
+
+\permanent\protected\def\getobject#1#2%
+ {\begingroup
+ \synctexpause
+ \clf_restoreobject{#1}{#2}%
+ \ifzeropt\objectoff\else
+ \objectoff-\objectoff
+ \pack_objects_package_yes\objectbox
+ \fi
+ \box\objectbox
+ \synctexresume
+ \endgroup}
+
+%D If needed one can ask for the dimensions of an object with:
+%D
+%D \starttyping
+%D \getobjectdimensions{class}{name}
+%D \stoptyping
+%D
+%D The results are reported in \type {\objectwidth}, \type {\objectheight} and \type
+%D {\objectdepth} as well as \type {\objectoffset}.
+
+% \getobjectdimensions #1#2 % defined at the lua end
+
+%D \macros
+%D {doifobjectfoundelse,doifobjectreferencefoundelse}
+%D
+%D To prevent redundant definition of objects, one can use the next tests:
+%D
+%D \starttyping
+%D \doifobjectfoundelse{class}{object}{do then}{do else}
+%D \doifobjectreferencefoundelse{class}{object}{do then}{do else}
+%D \stoptyping
+
+\immutable\def\defaultobjectreference{0}
+\immutable\def\defaultobjectpage {\realfolio}
+
+% \getobjectreference {#1}{#2\csname % define at the lua end
+% \getobjectreferencepage {#1}{#2\csname % define at the lua end
+% \doifelseobjectfound {#1}{#2} % defined atthe lua end
+% \doifelseobjectreferencefound {#1}{#2} % defined atthe lua end
+
+\aliased\let\dogetobjectreference \getobjectreference % for now, will go away
+\aliased\let\dogetobjectreferencepage\getobjectreferencepage % for now, will go away
+
+\aliased\let\doifobjectfoundelse \doifelseobjectfound
+\aliased\let\doifobjectreferencefoundelse\doifelseobjectreferencefound
+
+%D For the moment here:
+
+\mutable\let\lastpredefinedsymbol\empty % some day we can do more at the lua end
+
+\permanent\protected\def\predefinesymbol[#1]%
+ {\begingroup
+ \xdef\lastpredefinedsymbol{#1}%
+ \settightobject{SYM}{#1}\hbox{\symbol[#1]}% to be checked ... maybe only fitting
+ \getobjectreference{SYM}{#1}\m_back_object_reference
+ \clf_registerbackendsymbol{#1}\m_back_object_reference\relax
+ \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-ori.lmt b/Master/texmf-dist/tex/context/base/mkxl/pack-ori.lmt
new file mode 100644
index 00000000000..a786b19ef13
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-ori.lmt
@@ -0,0 +1,70 @@
+if not modules then modules = { } end modules ['pack-ori'] = {
+ version = 1.001,
+ comment = "companion to supp-box.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local context = context
+local implement = interfaces.implement
+local variables = interfaces.variables
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+local orientation = {
+ [variables.up] = 0x000,
+ [variables.left] = 0x001,
+ [variables.down] = 0x002,
+ [variables.right] = 0x003,
+ [variables.top] = 0x004,
+ [variables.bottom] = 0x005,
+}
+local vertical = {
+ [variables.line] = 0x000,
+ [variables.top] = 0x010,
+ [variables.bottom] = 0x020,
+ [variables.middle] = 0x030,
+}
+local horizontal = {
+ [variables.middle] = 0x000,
+ [variables.flushleft] = 0x100,
+ [variables.flushright] = 0x200,
+ [variables.left] = 0x300,
+ [variables.right] = 0x400,
+}
+
+implement {
+ name = "toorientation",
+ public = true,
+ arguments = {
+ {
+ { "horizontal", "string" },
+ { "vertical", "string" },
+ { "orientation", "string" },
+ }
+ },
+ actions = function(t)
+ local n = 0
+ local m = t.horizontal if m then n = n | (horizontal [m] or 0) end
+ local m = t.vertical if m then n = n | (vertical [m] or 0) end
+ local m = t.orientation if m then n = n | (orientation[m] or 0) end
+ -- logs.report("orientation","0x%03X : %s",n,table.sequenced(t))
+ context(n)
+ end,
+}
+
+implement {
+ name = "stringtoorientation",
+ public = true,
+ arguments = "string",
+ actions = function(s)
+ local n = 0
+ local t = settings_to_array(s)
+ local m = t[1] if m then n = n | (horizontal [m] or 0) end
+ local m = t[2] if m then n = n | (vertical [m] or 0) end
+ local m = t[3] if m then n = n | (orientation[m] or 0) end
+ -- logs.report("orientation","0x%03X : %s",n,s)
+ context(n)
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-ori.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-ori.mkxl
new file mode 100644
index 00000000000..4365db3ac66
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-ori.mkxl
@@ -0,0 +1,97 @@
+%D \module
+%D [ file=pack-ori, % was experimental
+%D version=2019.07.30,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Anchoring,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Anchoring}
+
+\registerctxluafile{pack-ori}{autosuffix}
+
+\unprotect
+
+%D Possible options are:
+%D
+%D \starttabulate[|T|T|]
+%D \NC orientation \NC up left down right top bottom
+%D \NC vertical \NC line top bottom middle
+%D \NC horizontal \NC middle flushleft flushright left right
+%D \stoptabulate
+
+\installcorenamespace{orientation}
+\installcorenamespace{orientations}
+
+\installcommandhandler \??orientation {orientation} \??orientation
+
+\setuporientation
+ [\c!orientation=\v!normal,
+ \c!horizontal=\v!normal,
+ \c!vertical=\v!normal]
+
+\appendtoks
+ \expandafter\chardef\csname\??orientations\currentorientation\endcsname\toorientation
+ orientation {\orientationparameter\c!orientation}
+ horizontal {\orientationparameter\c!horizontal}
+ vertical {\orientationparameter\c!vertical}
+ \relax\relax
+\to \everydefineorientation
+
+\defineorientation[\v!normal]
+
+\defineorientation[\v!up] [\c!orientation=\v!up]
+\defineorientation[\v!down] [\c!orientation=\v!down]
+\defineorientation[\v!left] [\c!orientation=\v!left]
+\defineorientation[\v!right] [\c!orientation=\v!right]
+\defineorientation[\v!top] [\c!orientation=\v!top]
+\defineorientation[\v!bottom][\c!orientation=\v!bottom]
+
+%D There will be some more helpers here.
+
+\permanent\def\theorientation#1%
+ {\numexpr
+ \ifcsname\??orientations#1\endcsname
+ \lastnamedcs
+ \else
+ \zerocount
+ \fi
+ \relax}
+
+\permanent\def\autoorientation#1%
+ {\numexpr
+ \ifcsname\??orientations#1\endcsname
+ \lastnamedcs
+ \else
+ \auto_orientation{#1}%
+ \fi
+ \relax}
+
+%D The \type {\immediateassignment} and \type {\immediateassigned} primitives are
+%D kind of obsolete and might be dropped (if not already) from \LUAMETATEX. Local
+%D scanning is, although it is a bit slower, more general. We didn't really need and
+%D use these primitives in \CONTEXT\ anyway, apart from some playing around with
+%D some full expansion, which made no sense in the end so it was dropped. When
+%D things get that hairy it's often a good reason to look into \LUA\ variants.
+
+\def\auto_orientation#1%
+ %{\immediateassignment\global\expandafter\chardef\csname\??orientations#1\endcsname\stringtoorientation{#1}% % quite fast
+ {\beginlocalcontrol\global\expandafter\chardef\csname\??orientations#1\endcsname\stringtoorientation{#1}\endlocalcontrol % good enough
+ %{\localcontrolled{\global\expandafter\chardef\csname\??orientations#1\endcsname\stringtoorientation{#1}}% % a bit slower
+ \csname\??orientations#1\endcsname}
+
+\permanent\protected\def\doifelseorientation#1%
+ {\ifcsname\??orientations#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+% \def\iforientationcsname#1{\ifcsname\??orientations}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-pos.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-pos.mkxl
new file mode 100644
index 00000000000..aee22081fdd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-pos.mkxl
@@ -0,0 +1,160 @@
+%D \module
+%D [ file=pack-pos,
+%D version=2010.11.17 % real old code, updated a bit
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Positioning,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Positioning}
+
+\unprotect
+
+% An old but still usefull mechanism:
+%
+% \ruledvbox{\startpositioning
+% \position(1,1){test}
+% \position[xstep=relative](1,1){test}
+% \position[ystep=relative](3,-1){test}
+% \position(10,10){test}
+% \stoppositioning}
+%
+% watch out: rather global
+
+\installcorenamespace {positioning}
+
+\installcommandhandler \??positioning {positioning} \??positioning
+
+\setuppositioning
+ [\c!state=\v!start,
+ \c!unit=\s!cm,
+ \c!factor=\plusone,
+ \c!scale =\plusone,
+ \c!xfactor=\positioningparameter\c!factor,
+ \c!yfactor=\positioningparameter\c!factor,
+ \c!xscale=\positioningparameter\c!scale,
+ \c!yscale=\positioningparameter\c!scale,
+ \c!xstep=\v!absolute,
+ \c!ystep=\v!absolute,
+ \c!xoffset=\zeropoint,
+ \c!yoffset=\zeropoint]
+
+\newdimen\d_pack_positioning_x_position
+\newdimen\d_pack_positioning_y_position
+\newdimen\d_pack_positioning_x_dimension
+\newdimen\d_pack_positioning_y_dimension
+\newdimen\d_pack_positioning_x_offset
+\newdimen\d_pack_positioning_y_offset
+
+\newbox\b_pack_positioning
+
+\permanent\tolerant\protected\def\startpositioning[#1]#*[#2]%
+ {\bgroup
+ \ifparameters
+ \let\currentpositioning\empty
+ \or
+ \ifhastok={#1}%
+ \let\currentpositioning\empty
+ \setupcurrentpositioning[#1]%
+ \else
+ \edef\currentpositioning{#1}%
+ \fi
+ \or
+ \edef\currentpositioning{#1}%
+ \setupcurrentpositioning[#2]%
+ \fi
+ \d_pack_positioning_x_position \zeropoint
+ \d_pack_positioning_y_position \zeropoint
+ \d_pack_positioning_x_dimension\zeropoint
+ \d_pack_positioning_y_dimension\zeropoint
+ \d_pack_positioning_x_offset \zeropoint
+ \d_pack_positioning_y_offset \zeropoint
+ \hfuzz\paperwidth
+ \vfuzz\paperheight
+ \setbox\b_pack_positioning\hbox\bgroup
+ \ignorespaces}
+
+\permanent\protected\def\stoppositioning
+ {\removeunwantedspaces
+ \doifnot{\positioningparameter\c!offset}\v!yes
+ {\global\d_pack_positioning_x_offset\zeropoint
+ \global\d_pack_positioning_y_offset\zeropoint}%
+ \global\advance\d_pack_positioning_x_dimension\d_pack_positioning_x_offset
+ \global\advance\d_pack_positioning_y_dimension\d_pack_positioning_y_offset
+ \egroup
+ \vbox to \d_pack_positioning_y_dimension
+ {\vskip\d_pack_positioning_y_offset
+ \hbox to \d_pack_positioning_x_dimension
+ {\hskip\d_pack_positioning_x_offset
+ \box\b_pack_positioning
+ \hfill}
+ \vfill}%
+ \egroup}
+
+\protected\def\pack_positioning_calculate#1#2#3#4#5#6#7#8#9%
+ {\setdimensionwithunit\scratchdimen{#1}{\positioningparameter\c!unit}%
+ \scratchdimen\positioningparameter#8\scratchdimen
+ \scratchdimen\positioningparameter#9\scratchdimen
+ \advance\scratchdimen\positioningparameter#4\relax
+ % == \scratchdimen\dimexpr#8\dimexpr#9\scratchdimen\relax+#4\relax
+ \doif{\positioningparameter#2}\v!relative
+ {\advance\scratchdimen#3%
+ \letpositioningparameter#4\zeropoint}%
+ #3\scratchdimen
+ \doifnot{\positioningparameter\c!state}\v!overlay
+ {\scratchdimen\dimexpr#5\nextbox+#3\relax
+ \ifdim #3<-#7\relax \global#7-#3\relax \fi
+ \ifdim\scratchdimen> #6\relax \global#6\scratchdimen\fi}}
+
+\permanent\tolerant\protected\def\position[#1]#:(#2,#3)%
+ {\bgroup
+ \ifparameter#1\or
+ \setupcurrentpositioning[#1]%
+ \fi
+ \removeunwantedspaces
+ \dowithnextbox{\pack_positioning_position_indeed{#2}{#3}}\hbox}
+
+\def\pack_positioning_position_indeed#1#2%
+ {\dontcomplain
+ \pack_positioning_calculate
+ {#1}%
+ \c!xstep
+ \d_pack_positioning_x_position
+ \c!xoffset
+ \wd
+ \d_pack_positioning_x_dimension
+ \d_pack_positioning_x_offset
+ \c!xscale
+ \c!xfactor
+ \pack_positioning_calculate
+ {#2}%
+ \c!ystep
+ \d_pack_positioning_y_position
+ \c!yoffset
+ \htdp
+ \d_pack_positioning_y_dimension
+ \d_pack_positioning_y_offset
+ \c!yscale
+ \c!yfactor
+ \vbox to \zeropoint
+ {\offinterlineskip % else we get an empty line
+ \vskip\d_pack_positioning_y_position
+ \hbox to \zeropoint
+ {\hskip\d_pack_positioning_x_position
+ \box\nextbox
+ \hss}
+ \vss}%
+ \normalexpanded
+ {\egroup
+ \d_pack_positioning_x_position\the\d_pack_positioning_x_position
+ \d_pack_positioning_y_position\the\d_pack_positioning_y_position
+ \setexpandedpositioningparameter\c!xoffset{\the\dimexpr\positioningparameter\c!xoffset}%
+ \setexpandedpositioningparameter\c!yoffset{\the\dimexpr\positioningparameter\c!yoffset}}%
+ \ignorespaces}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-rul.lmt b/Master/texmf-dist/tex/context/base/mkxl/pack-rul.lmt
new file mode 100644
index 00000000000..d99f0a78e2c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-rul.lmt
@@ -0,0 +1,258 @@
+if not modules then modules = { } end modules ['pack-rul'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to pack-rul.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>An explanation is given in the history document <t>mk</t>.</p>
+--ldx]]--
+
+-- we need to be careful with display math as it uses shifts
+
+-- \framed[align={lohi,middle}]{$x$}
+-- \framed[align={lohi,middle}]{$ $}
+-- \framed[align={lohi,middle}]{\hbox{ }}
+-- \framed[align={lohi,middle}]{\hbox{}}
+-- \framed[align={lohi,middle}]{$\hskip2pt$}
+
+local type = type
+
+local context = context
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local boxlist_code = listcodes.box
+local linelist_code = listcodes.line
+local equationlist_code = listcodes.equation
+
+local texsetdimen = tex.setdimen
+local texsetcount = tex.setcount
+
+local implement = interfaces.implement
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getwhd = nuts.getwhd
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getbox = nuts.getbox
+local getdirection = nuts.getdirection
+local setshift = nuts.setshift
+local setwidth = nuts.setwidth
+local getwidth = nuts.getwidth
+local setboxglue = nuts.setboxglue
+local getboxglue = nuts.getboxglue
+
+local hpack = nuts.hpack
+local getdimensions = nuts.dimensions
+local naturalhsize = nuts.naturalhsize
+local flush_node = nuts.flush
+
+local traversers = nuts.traversers
+local nexthlist = traversers.hlist
+local nextvlist = traversers.vlist
+local nextlist = traversers.list
+
+local checkformath = false
+
+directives.register("framed.checkmath",function(v) checkformath = v end) -- experiment
+
+-- beware: dir nodes and pseudostruts can end up on lines of their own
+
+local function doreshapeframedbox(n)
+ local box = getbox(n)
+ local noflines = 0
+ local nofnonzero = 0
+ local firstheight = nil
+ local lastdepth = nil
+ local lastlinelength = 0
+ local minwidth = 0
+ local maxwidth = 0
+ local totalwidth = 0
+ local averagewidth = 0
+ local boxwidth = getwidth(box)
+ -- if boxwidth ~= 0 and getsubtype(box) == vlist_code then
+ if boxwidth ~= 0 then
+ local list = getlist(box)
+ if list then
+ local hdone = false
+ for n, id, subtype, list in nextlist, list do -- no dir etc needed
+ local width, height, depth = getwhd(n)
+ if not firstheight then
+ firstheight = height
+ end
+ lastdepth = depth
+ noflines = noflines + 1
+ if list then
+ if id == hlist_code then
+ if subtype == boxlist_code or subtype == linelist_code then
+ -- the fast one also returns compensation (plus node)
+ local w, c, n = naturalhsize(list)
+ if n then
+ -- can become an option in framed: strict
+ w = w - c
+ else
+ -- print("no compensation found")
+ end
+ lastlinelength = w
+ else
+ lastlinelength = width
+ end
+ hdone = true
+ else
+ lastlinelength = width
+ -- vdone = true
+ end
+ if lastlinelength > maxwidth then
+ maxwidth = lastlinelength
+ end
+ if lastlinelength < minwidth or minwidth == 0 then
+ minwidth = lastlinelength
+ end
+ if lastlinelength > 0 then
+ nofnonzero = nofnonzero + 1
+ end
+ totalwidth = totalwidth + lastlinelength
+ end
+ end
+ if not firstheight then
+ -- done)
+ elseif maxwidth ~= 0 then
+ if hdone then
+ for h, id, subtype, list in nextlist, list do
+ if list and id == hlist_code then
+ -- called a lot so maybe a simple case is needed
+ if subtype == boxlist_code or subtype == linelist_code then
+ -- getdirection is irrelevant here so it will go
+ -- somehow a parfillskip also can get influenced
+ local p = hpack(list,maxwidth,'exactly',getdirection(h)) -- multiple return value
+ local set, order, sign = getboxglue(p)
+ setboxglue(h,set,order,sign)
+ setlist(p)
+ flush_node(p)
+ elseif checkformath and subtype == equationlist_code then
+ -- display formulas use a shift
+ if nofnonzero == 1 then
+ setshift(h,0)
+ end
+ end
+ setwidth(h,maxwidth)
+ end
+ end
+ end
+ -- if vdone then
+ -- for v in nextvlist, list do
+ -- local width = getwidth(n)
+ -- if width > maxwidth then
+ -- setwidth(v,maxwidth)
+ -- end
+ -- end
+ -- end
+ setwidth(box,maxwidth)
+ averagewidth = noflines > 0 and totalwidth/noflines or 0
+ else -- e.g. empty math {$ $} or \hbox{} or ...
+ setwidth(box,0)
+ end
+ end
+ end
+ texsetcount("global","framednoflines",noflines)
+ texsetdimen("global","framedfirstheight",firstheight or 0) -- also signal
+ texsetdimen("global","framedlastdepth",lastdepth or 0)
+ texsetdimen("global","framedminwidth",minwidth)
+ texsetdimen("global","framedmaxwidth",maxwidth)
+ texsetdimen("global","framedaveragewidth",averagewidth)
+end
+
+local function doanalyzeframedbox(n) -- traverse_list
+ local box = getbox(n)
+ local noflines = 0
+ local firstheight = nil
+ local lastdepth = nil
+ if getwidth(box) ~= 0 then
+ local list = getlist(box)
+ if list then
+ for n in nexthlist, list do
+ local width, height, depth = getwhd(n)
+ if not firstheight then
+ firstheight = height
+ end
+ lastdepth = depth
+ noflines = noflines + 1
+ end
+ for n in nextvlist, list do
+ local width, height, depth = getwhd(n)
+ if not firstheight then
+ firstheight = height
+ end
+ lastdepth = depth
+ noflines = noflines + 1
+ end
+ end
+ end
+ texsetcount("global","framednoflines",noflines)
+ texsetdimen("global","framedfirstheight",firstheight or 0)
+ texsetdimen("global","framedlastdepth",lastdepth or 0)
+end
+
+implement { name = "doreshapeframedbox", actions = doreshapeframedbox, arguments = "integer" }
+implement { name = "doanalyzeframedbox", actions = doanalyzeframedbox, arguments = "integer" }
+
+local function maxboxwidth(box)
+ local boxwidth = getwidth(box)
+ if boxwidth == 0 then
+ return 0
+ end
+ local list = getlist(box)
+ if not list then
+ return 0
+ end
+ if getid(box) == hlist_code then
+ return boxwidth
+ end
+ local lastlinelength = 0
+ local maxwidth = 0
+ for n, subtype in nexthlist, list do -- no dir etc needed
+ local l = getlist(n)
+ if l then
+ if subtype == boxlist_code or subtype == linelist_code then
+ lastlinelength = getdimensions(l)
+ else
+ lastlinelength = getwidth(n)
+ end
+ if lastlinelength > maxwidth then
+ maxwidth = lastlinelength
+ end
+ end
+ end
+ for n, subtype in nextvlist, list do -- no dir etc needed
+ local l = getlist(n)
+ if l then
+ lastlinelength = getwidth(n)
+ if lastlinelength > maxwidth then
+ maxwidth = lastlinelength
+ end
+ end
+ end
+ return maxwidth
+end
+
+nodes.maxboxwidth = maxboxwidth
+
+implement {
+ name = "themaxboxwidth",
+ public = true,
+ actions = function(n) context("%rsp",maxboxwidth(getbox(n))) end, -- r = rounded
+ arguments = "integer"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/pack-rul.mkxl b/Master/texmf-dist/tex/context/base/mkxl/pack-rul.mkxl
new file mode 100644
index 00000000000..b6a22d89492
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/pack-rul.mkxl
@@ -0,0 +1,3017 @@
+%D \module
+%D [ file=pack-rul, % was core-rul,
+%D version=1998.10.16,
+%D title=\CONTEXT\ Packaging Macros,
+%D subtitle=Ruled Content,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Packaging Macros / Ruled Content}
+
+%D The code here is expanded lots of time as framed is used in many places. This is
+%D why the code here is (and gets) optimized as much as possible. Also, by avoiding
+%D packaging and expansion we also keep tracing reasonable. For instance, multiple
+%D stacked backgrounds can slow down a run if not optimized this way.
+
+\registerctxluafile{pack-rul}{autosuffix,optimize}
+
+\unprotect
+
+% \definesystemvariable {ol} % OmLijnd -> check scrn-fld too
+
+%D \macros
+%D {linewidth, setuplinewidth}
+%D
+%D This module deals with rules (lines) in several ways. First we introduce two
+%D macros that can be used to set some common characteristics.
+%D
+%D \showsetup{setuplinewidth}
+%D
+%D The linewidth is available in \type{\linewidth}. The preset value of .4pt equals
+%D the default hard coded \TEX\ rule width.
+
+\newdimen\linewidth
+
+\permanent\tolerant\protected\def\setuplinewidth[#1]%
+ {\assigndimension{#1}\linewidth{.2\points}{.4\points}{.6\points}}
+
+%D The parameter handler:
+
+\installcorenamespace{framed}
+\installcorenamespace{framedtop}
+\installcorenamespace{framedbottom}
+\installcorenamespace{framedleft}
+\installcorenamespace{framedright}
+
+\installcorenamespace{regularframed}
+\installcorenamespace{simplifiedframed}
+
+\installcommandhandler \??framed {framed} \??framed
+
+\aliased\let\pack_framed_framedparameter \framedparameter
+\aliased\let\pack_framed_framedparameterhash\framedparameterhash
+\aliased\let\pack_framed_setupcurrentframed \setupcurrentframed
+
+\def\pack_framed_initialize
+ {\enforced\let\framedparameter \pack_framed_framedparameter
+ \enforced\let\framedparameterhash\pack_framed_framedparameterhash
+ \enforced\let\setupcurrentframed \pack_framed_setupcurrentframed
+ \inframedtrue}
+
+%D A helper:
+
+\permanent\def\frameddimension#1{\the\dimexpr\framedparameter{#1}\relax}
+
+%D Inheritance:
+
+\permanent\protected\def\installinheritedframed#1%
+ {\normalexpanded{\mult_interfaces_install_inherited_framed
+ \expandafter\noexpand\csname current#1\endcsname
+ \expandafter\noexpand\csname #1parameter\endcsname
+ \expandafter\noexpand\csname #1parameterhash\endcsname
+ \expandafter\noexpand\csname do#1parameter\endcsname
+ \expandafter\noexpand\csname do#1parentparameter\endcsname
+ \expandafter\noexpand\csname do#1rootparameter\endcsname
+ \expandafter\noexpand\csname setupcurrent#1\endcsname
+ \expandafter\noexpand\csname inherited#1framed\endcsname
+ \expandafter\noexpand\csname inherited#1framedbox\endcsname}} % new
+
+\protected\def\mult_interfaces_install_inherited_framed#1#2#3#4#5#6#7#8#9%
+ {\enforced\frozen\def#5##1##2{\ifrelax##1#6{##2}\else#4{##1}{##2}\fi}%
+ \enforced\frozen\def#6##1{\ifcsname\??framed:##1\endcsname\??framed:##1\else\??empty\fi}% root
+ \frozen\instance\protected\def#8%
+ {\bgroup
+ \bgroup
+ \inframedtrue
+ \enforced\let\currentframed #1%
+ \enforced\let\framedparameter #2%
+ \enforced\let\framedparameterhash#3%
+ \enforced\let\setupcurrentframed #7%
+ \pack_framed_process_indeed}%
+ \frozen\instance\protected\def#9%
+ {\bgroup
+ \inframedtrue
+ \enforced\let\currentframed #1%
+ \enforced\let\framedparameter #2%
+ \enforced\let\framedparameterhash#3%
+ \enforced\let\setupcurrentframed #7%
+ \pack_framed_process_box_indeed}}
+
+\permanent\protected\def\installframedcommandhandler#1#2#3%
+ {\installcommandhandler{#1}{#2}{#3}%
+ \installinheritedframed{#2}}
+
+\permanent\protected\def\installframedautocommandhandler#1#2#3%
+ {\installautocommandhandler{#1}{#2}{#3}%
+ \installinheritedframed{#2}}
+
+\permanent\protected\def\installsimpleframedcommandhandler#1#2#3%
+ {\installsimplecommandhandler{#1}{#2}{#3}%
+ \installinheritedframed{#2}}
+
+% for regular framed
+
+\setupframed
+ [\c!width=\v!fit,
+ \c!height=\v!broad,
+ %\c!minheight=\zeropoint,
+ %\c!lines=,
+ \c!offset=.25\exheight, % \defaultframeoffset
+ \c!empty=\v!no,
+ \c!frame=\v!on,
+ %\c!topframe=,
+ %\c!bottomframe=,
+ %\c!leftframe=,
+ %\c!rightframe=,
+ \c!radius=.5\bodyfontsize,
+ \c!rulethickness=\linewidth,
+ \c!corner=\v!rectangular,
+ \c!depth=\zeropoint,
+ %\c!foregroundcolor=,
+ %\c!foregroundstyle=,
+ %\c!background=,
+ %\c!backgroundcolor=,
+ \c!backgroundoffset=\zeropoint,
+ %\c!framecolor=,
+ \c!frameoffset=\zeropoint,
+ \c!backgroundcorner=\framedparameter\c!corner, % use \p_ here
+ \c!backgroundradius=\framedparameter\c!radius,
+ \c!backgrounddepth=\framedparameter\c!depth,
+ \c!framecorner=\framedparameter\c!corner,
+ \c!frameradius=\framedparameter\c!radius,
+ \c!framedepth=\framedparameter\c!depth,
+ %\c!component=,
+ %\c!region=,
+ %\c!align=,
+ \c!bottom=\vss,
+ %\c!top=,
+ \c!strut=\v!yes,
+ \c!autostrut=\v!yes,
+ \c!location=\v!normal,
+ %\c!orientation=,
+ %\c!anchoring=,
+ \c!autowidth=\v!yes,
+ %\c!setups=,
+ \c!loffset=\zeropoint,
+ \c!roffset=\zeropoint,
+ \c!toffset=\zeropoint,
+ \c!boffset=\zeropoint]
+
+%D For backgrounds and such:
+
+\defineframed
+ [\??simplifiedframed]
+ [\c!frame=\v!off,
+ \c!depth=\zeropoint,
+ \c!offset=\v!overlay,
+ \c!component=,
+ \c!region=,
+ \c!radius=.5\bodyfontsize,
+ \c!rulethickness=\linewidth,
+ \c!corner=\v!rectangular,
+ \c!backgroundoffset=\zeropoint,
+ \c!frameoffset=\zeropoint,
+ \c!backgroundcorner=\framedparameter\c!corner, % use \p_ here
+ \c!backgroundradius=\framedparameter\c!radius,
+ \c!backgrounddepth=\framedparameter\c!depth,
+ \c!framecorner=\framedparameter\c!corner,
+ \c!frameradius=\framedparameter\c!radius,
+ \c!framedepth=\framedparameter\c!depth,
+ \c!location=\v!normal,
+ \c!loffset=\zeropoint,
+ \c!roffset=\zeropoint,
+ \c!toffset=\zeropoint,
+ \c!boffset=\zeropoint]
+
+\permanent\protected\def\definesimplifiedframed[#1]% no settings
+ {\defineframed[#1][\??simplifiedframed]%
+ \enforced\letcsname#1\endcsname\undefined}
+
+\letcsname\??simplifiedframed\endcsname\undefined
+
+%D We will communicate through module specific variables, current framed
+%D parameters and some reserved dimension registers.
+
+\newdimen\d_framed_target_wd
+\newdimen\d_framed_target_ht
+\newdimen\d_framed_target_dp
+\newdimen\d_framed_linewidth \aliased\let\ruledlinewidth\d_framed_linewidth % needed at lua end
+
+\let\p_framed_frame \empty % \framedparameter\c!frame
+\let\p_framed_backgroundoffset\empty
+\let\p_framed_foregroundstyle \empty
+\let\p_framed_autostrut \empty
+\let\p_framed_location \empty
+\let\p_framed_orientation \empty
+\let\p_framed_anchoring \empty
+\let\p_framed_autowidth \empty
+\let\p_framed_franalyze \empty
+\let\p_framed_backgroundcorner\empty
+\let\p_framed_backgroundradius\empty
+\let\p_framed_framecorner \empty
+\let\p_framed_frameradius \empty
+\let\p_framed_lines \empty
+\let\p_framed_empty \empty
+\let\p_framed_backgroundcolor \empty
+\let\p_framed_framecolor \empty
+\let\p_framed_component \empty
+\let\p_framed_background \empty
+\let\p_framed_rulethickness \empty
+\let\p_framed_foregroundcolor \empty
+\let\p_framed_setups \empty
+
+%D We don't have to stick to a \TEX\ drawn rule, but also can use rounded
+%D or even fancier shapes, as we will see later on.
+
+\def\pack_framed_filled_box
+ {\edef\p_framed_backgroundcorner{\framedparameter\c!backgroundcorner}%
+ \ifx\p_framed_backgroundcorner\v!rectangular
+ \pack_framed_filled_box_normal
+ \else
+ \pack_framed_filled_box_radius
+ \fi}
+
+\def\pack_framed_filled_box_normal
+ {\vrule
+ \s!width \d_framed_target_wd
+ \s!height\d_framed_target_ht
+ \s!depth \d_framed_target_dp
+ \relax}
+
+\def\pack_framed_filled_box_radius
+ {\edef\p_framed_backgroundradius{\framedparameter\c!backgroundradius}%
+ \ifzeropt\dimexpr\p_framed_backgroundradius\relax % just in case of .x\bodyfontsize
+ \pack_framed_filled_box_normal
+ \else
+ \pack_framed_filled_box_round
+ \fi}
+
+\def\pack_framed_filled_box_round
+ {\raise\d_framed_target_dp\hpack{\frule
+ type fill
+ width \d_framed_target_wd
+ height \d_framed_target_ht
+ depth \d_framed_target_dp
+ line \d_framed_linewidth
+ radius \p_framed_backgroundradius\space
+ corner {\p_framed_backgroundcorner}
+ \relax}}
+
+\def\pack_framed_stroked_box
+ {\edef\p_framed_framecorner{\framedparameter\c!framecorner}%
+ \ifx\p_framed_framecorner\v!rectangular
+ \pack_framed_stroked_box_normal
+ \else
+ \pack_framed_stroked_box_radius
+ \fi}
+
+\def\pack_framed_stroked_box_radius
+ {\edef\p_framed_frameradius{\framedparameter\c!frameradius}%
+ \ifzeropt\dimexpr\p_framed_frameradius\relax % just in case of .x\bodyfontsize
+ \pack_framed_stroked_box_normal
+ \orelse\ifx\p_framed_frame\v!on
+ \pack_framed_stroked_box_round
+ \fi}
+
+% \pack_framed_stroked_box_normal % later
+
+\def\pack_framed_stroked_box_round
+ {\raise\d_framed_target_dp\hpack{\frule
+ width \d_framed_target_wd
+ height \d_framed_target_ht
+ depth \d_framed_target_dp
+ line \d_framed_linewidth
+ radius \p_framed_frameradius\space
+ corner {\p_framed_framecorner}
+ \relax}}
+
+% a lot of weird corners
+%
+% \startTEXpage
+% \dontleavehmode\framed
+% [corner=0,frame=on,framecolor=green,
+% background=color,backgroundcolor=yellow]{\tttf TEST \twodigits\recurselevel}%
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse {1} {4}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green,
+% background=color,backgroundcolor=yellow]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse {5} {8}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green,
+% background=color,backgroundcolor=yellow]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse {1} {4}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse {5} {8}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse {9}{12}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse{13}{16}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse{17}{20}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse{21}{24}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \vskip1em
+% \dontleavehmode\dostepwiserecurse{25}{28}{1}{\framed
+% [corner=\recurselevel,frame=on,framecolor=green]{\tttf TEST \twodigits\recurselevel}%
+% \quad}
+% \stopTEXpage
+
+%D It won't be a surprise that we not only provide gray boxes, but also colored
+%D ones. Here it is:
+
+\def\pack_framed_background_box_color
+ {\edef\p_framed_backgroundcolor{\framedparameter\c!backgroundcolor}%
+ \ifempty\p_framed_backgroundcolor \else
+ \doifcolor\p_framed_backgroundcolor\pack_framed_background_box_color_indeed
+ \fi}
+
+\def\pack_framed_background_box_color_indeed
+ {\hpack{\dousecolorparameter\p_framed_backgroundcolor\pack_framed_filled_box}}
+
+%D \macros
+%D {defineoverlay, doifoverlayelse, overlayoffset,
+%D overlaywidth, overlayheight, overlaydepth,
+%D overlaycolor, overlaylinecolor, overlaylinewidth}
+%D
+%D Before we define the macro that actually takes card of the backgrounds, we
+%D introduce overlays. An overlay is something that contrary to its name lays {\em
+%D under} the text. An example of an overlay definition is:
+%D
+%D \startbuffer[tmp-1]
+%D \defineoverlay
+%D [fancy]
+%D [{\externalfigure
+%D [mp-cont.502]
+%D [width=\overlaywidth,
+%D height=\overlayheight]}]
+%D \stopbuffer
+%D
+%D \typebuffer[tmp-1]
+%D
+%D That for instance can be uses in:
+%D
+%D \startbuffer[tmp-2]
+%D \framed[backgroundachtergrond=fancy]{How Fancy!}
+%D \framed[backgroundachtergrond=fancy,frame=off]{Even More Fancy!}
+%D \stopbuffer
+%D
+%D and looks like:
+%D
+%D \startlinecorrection
+%D \vbox{\baselineskip24pt\getbuffer[tmp-1]\getbuffer[tmp-2]}
+%D \stoplinecorrection
+%D
+%D The formal definition is:
+%D
+%D \showsetup{defineoverlay}
+%D
+%D This macro's definition is a bit obscure, due the many non||used arguments and
+%D the two step call that enable the setting of the width, height and depth
+%D variables. Multiple backgrounds are possible and are specified as:
+%D
+%D \starttyping
+%D \framed[background={one,two,three}]{Three backgrounds!}
+%D \stoptyping
+%D
+%D Most drawing packages only know width and height. Therefore the dimensions have a
+%D slightly different meaning here:
+%D
+%D \startitemize[packed]
+%D \item \type{\overlaywidth }: width of the overlay
+%D \item \type{\overlayheight}: height plus depth of the overlay
+%D \item \type{\overlaydepth }: depth of the overlay
+%D \stopitemize
+%D
+%D The resulting box is lowered to the right depth.
+
+\newdimen\d_overlay_width
+\newdimen\d_overlay_height
+\newdimen\d_overlay_depth
+\newdimen\d_overlay_offset
+\newdimen\d_overlay_linewidth
+
+\let\m_overlay_region\empty
+
+% expandable ... in a future version the space will go (in mp one can use Overlay*)
+
+\mutable \def\overlaywidth {\the\d_overlay_width \space} % We preset the variables
+\mutable \def\overlayheight {\the\d_overlay_height \space} % to some reasonable default
+\mutable \def\overlaydepth {\the\d_overlay_depth \space} % values.
+\mutable \def\overlayoffset {\the\d_overlay_offset \space} % of the frame can be (are)
+\mutable \def\overlaylinewidth {\the\d_overlay_linewidth\space} % set somewhere else.
+\mutable \def\overlayregion {\m_overlay_region}
+
+\mutable \def\overlayradius {\framedparameter\c!frameradius}
+\mutable \def\overlaycolor {\framedparameter\c!backgroundcolor}
+\mutable \def\overlaylinecolor {\framedparameter\c!framecolor}
+%mutable \edef\overlaycorner {\framedparameter\c!backgroundcorner}
+%mutable \edef\overlayradius {\framedparameter\c!backgroundradius}
+
+\permanent\def\usedoverlaywidth {\dimexpr\d_overlay_width \relax}
+\permanent\def\usedoverlayheight {\dimexpr\d_overlay_height \relax}
+\permanent\def\usedoverlaydepth {\dimexpr\d_overlay_depth \relax}
+\permanent\def\usedoverlayoffset {\dimexpr\d_overlay_offset \relax}
+\permanent\def\usedoverlaylinewidth{\dimexpr\d_overlay_linewidth\relax}
+
+%D The next register is used to initialize overlays.
+
+\newtoks\everyoverlay
+
+%D An example of an initialization is the following (overlays can contain text
+%D and be executed under an regime where interlineskip is off).
+
+\installcorenamespace{overlay}
+\installcorenamespace{overlaybuiltin}
+
+\appendtoks
+ \oninterlineskip
+\to \everyoverlay
+
+\prependtoks
+ \hsize\d_overlay_width
+ \vsize\d_overlay_height
+\to \everyoverlay
+
+\permanent\tolerant\protected\def\defineoverlay[#1]#*[#2]% wil be overloaded
+ {\def\pack_framed_define_overlay_indeed##1{\defcsname\??overlay##1\endcsname{\executedefinedoverlay{##1}{#2}}}%
+ \processcommalist[#1]\pack_framed_define_overlay_indeed}
+
+\permanent\protected\def\executedefinedoverlay#1#2% we can share the definitions
+ {\bgroup % redundant grouping
+ \setlayoutcomponentattribute{\v!overlay:#1}%
+ \setbox\scratchbox\hbox \layoutcomponentboxattribute{\the\everyoverlay#2}%
+ \boxxoffset\scratchbox-.5\dimexpr\wd\scratchbox-\d_framed_target_wd\relax % was \d_overlay_width
+ \boxyoffset\scratchbox-.5\dimexpr\ht\scratchbox-\d_framed_target_ht+\d_framed_target_dp\relax % not \d_overlay_height !
+ \wd\scratchbox\d_framed_target_wd
+ \ht\scratchbox\d_framed_target_ht
+ \dp\scratchbox\d_framed_target_dp
+ \box\scratchbox
+ \egroup}
+
+%D \macros
+%D {overlayfakebox}
+
+\permanent\protected\def\overlayfakebox
+ {\hpack % redundant but needs testing
+ {\novrule
+ \s!width \d_overlay_width
+ \s!height\d_overlay_height
+ \s!depth \zeropoint}}
+
+%D For testing we provide:
+
+\permanent\def\doifelseoverlay#1% only tests external overlays
+ {\ifcsname\??overlay#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifoverlayelse\doifelseoverlay
+
+%D The content of the box will be (temporary) saved in a box. We also have an
+%D extra box for backgrounds.
+
+\newbox\b_framed_normal
+\newbox\b_framed_extra
+
+\newtoks\everybackgroundbox
+
+\let\m_framed_background\empty % we might need a public name
+
+\def\pack_framed_process_background
+ {\ifcsname\??overlaybuiltin\m_framed_background\endcsname
+ \expandafter\pack_framed_process_background_indeed_internal\lastnamedcs
+ \orelse\ifcsname\??overlay\m_framed_background\endcsname
+ \expandafter\pack_framed_process_background_indeed_external\lastnamedcs
+ \fi}
+
+\def\pack_framed_process_background_indeed_internal#1% % : in name
+ {\bgroup
+ \setbox\b_framed_extra\hpack\bgroup
+ \ifzeropt\framedbackgroundoffset\else
+ \kern-\framedbackgroundoffset
+ \fi
+ \hbox\bgroup#1\egroup
+ \egroup
+ \wd\b_framed_extra\zeropoint
+ \ht\b_framed_extra\framedbackgroundheight
+ \dp\b_framed_extra\framedbackgrounddepth
+ \box\b_framed_extra
+ \egroup}
+
+\let\pack_framed_overlay_initialize\relax
+
+\def\pack_framed_process_background_indeed_external
+ {\pack_framed_overlay_initialize
+ \pack_framed_process_background_indeed_internal}
+
+\def\pack_framed_process_backgrounds#1,#2% #2 gobbles spaces (we could avoid one catch if we have nextbackground)
+ {\edef\m_framed_background{#1}%
+ \ifx\m_framed_background\s!unknown\else
+ \pack_framed_process_background
+ \expandafter\pack_framed_process_backgrounds
+ \fi#2}
+
+%D Beware, a backgroundbox can be empty which is another reason why we set the
+%D width to zero instead of back-skipping.
+
+\newdimen\framedbackgroundwidth
+\newdimen\framedbackgroundheight
+\newdimen\framedbackgrounddepth
+\newdimen\framedbackgroundoffset
+
+\aliased\let\foregroundbox\relax
+
+\def\pack_framed_background_box_content% fuzzy but needed hack, this \vss, otherwise
+ {\vpack to \framedbackgroundheight{\vss\box\b_framed_normal\vss}} % vertical shift \backgroundheight
+
+\def\pack_framed_set_region % experiment
+ {\ifx\m_overlay_region\v!yes
+ \edef\m_overlay_region{\reservedautoregiontag}%
+ \fi}
+
+\def\pack_framed_add_region % experiment
+ {\anch_mark_tagged_box\b_framed_normal\m_overlay_region}
+
+\def\pack_framed_add_background
+ {\setbox\b_framed_normal\hpack % was vbox % see also *1*
+ {%\pack_framed_forgetall % can be relaxed
+ \boxmaxdepth\maxdimen
+ \framedbackgroundoffset\d_framed_backgroundoffset
+ \framedbackgroundwidth \wd\b_framed_normal
+ \framedbackgroundheight\ht\b_framed_normal
+ \framedbackgrounddepth \dp\b_framed_normal
+ \d_framed_target_wd\dimexpr\framedbackgroundwidth +2\framedbackgroundoffset\relax
+ \d_framed_target_ht\dimexpr\framedbackgroundheight+ \framedbackgroundoffset\relax
+ \d_framed_target_dp\dimexpr\framedbackgrounddepth + \framedbackgroundoffset+\framedparameter\c!backgrounddepth\relax
+ \let\pack_framed_overlay_initialize\pack_framed_overlay_initialize_indeed
+ \ifempty\p_framed_component
+ \resetlayoutcomponentattribute
+ \else
+ \setlayoutcomponentattribute{\v!background:\p_framed_component}%
+ \fi
+ \enforced\let\foregroundbox\pack_framed_background_box_content
+ \hpack \layoutcomponentboxattribute to \framedbackgroundwidth\bgroup % width in case 'foreground' is used as overlay
+ \the\everybackgroundbox % moved
+ \expandafter\pack_framed_process_backgrounds\p_framed_background,\s!unknown,\relax % hm, messy .. look into it
+ \box\b_framed_normal
+ \hss
+ \egroup}}
+
+\def\pack_framed_overlay_initialize_indeed
+ {\d_overlay_width \d_framed_target_wd
+ \d_overlay_height \dimexpr\d_framed_target_ht+\d_framed_target_dp\relax
+ \d_overlay_depth \d_framed_target_dp
+ \d_overlay_linewidth \d_framed_linewidth
+ \d_overlay_offset \framedbackgroundoffset\relax
+ % We expand these once:
+ \edef\overlaycolor {\framedparameter\c!backgroundcolor}%
+ \edef\overlaylinecolor{\framedparameter\c!framecolor}% only needed for layers
+ %
+ \let\pack_framed_overlay_initialize\relax}
+
+%D One can explictly insert the foreground box. For that purpose we introduce the
+%D overlay \type {foreground}.
+%D
+%D We predefine two already familiar backgrounds:
+
+%letvalue{\??overlaybuiltin\v!screen }\pack_framed_background_box_gray
+\letvalue{\??overlaybuiltin\v!color }\pack_framed_background_box_color
+\letvalue{\??overlaybuiltin\v!foreground}\pack_framed_background_box_content % replaces: \defineoverlay[\v!foreground][\foregroundbox]
+
+%D We can specify overlays as a comma separated list of overlays, a sometimes
+%D handy feature.
+%D
+%D Besides backgrounds (overlays) we also need some macros to draw outlines (ruled
+%D borders). Again we have to deal with square and round corners. The first category
+%D can be handled by \TEX\ itself, the latter one depends on the driver. This macro
+%D also support a negative offset.
+
+\def\pack_framed_add_outline
+ {\setbox\b_framed_normal\hpack % rules on top of box
+ {\d_framed_target_wd\dimexpr\wd\b_framed_normal+2\d_framed_frameoffset\relax
+ \d_framed_target_ht\dimexpr\ht\b_framed_normal+ \d_framed_frameoffset\relax
+ \d_framed_target_dp\dimexpr\dp\b_framed_normal+ \d_framed_frameoffset+\framedparameter\c!framedepth\relax
+ \ifdim\d_framed_target_dp<\zeropoint
+ \advance\d_framed_target_ht \d_framed_target_dp
+ \scratchdimen-\d_framed_target_dp
+ \d_framed_target_dp\zeropoint
+ \else
+ \scratchdimen\zeropoint
+ \fi
+ \edef\overlaylinecolor{\framedparameter\c!framecolor}% twice, also in background
+ \setbox\b_framed_extra\hpack
+ {\kern-\d_framed_frameoffset
+ \raise\scratchdimen
+ \hpack{\ifempty\overlaylinecolor\else\dousecolorparameter\overlaylinecolor\fi\pack_framed_stroked_box}}%
+ \wd\b_framed_extra\wd\b_framed_normal
+ \ht\b_framed_extra\ht\b_framed_normal
+ \dp\b_framed_extra\dp\b_framed_normal
+ \wd\b_framed_normal\zeropoint
+ \box\b_framed_normal
+ \box\b_framed_extra}}
+
+\def\pack_framed_stroked_box_normal_opened
+ {\setbox\scratchbox\vpack \bgroup
+ \csname\??framedtop\p_framed_frame\framedparameter\c!topframe\endcsname
+ \nointerlineskip % new (needed for fences)
+ \hpack \bgroup
+ \csname\??framedleft\p_framed_frame\framedparameter\c!leftframe\endcsname
+ \novrule
+ \s!width \d_framed_target_wd
+ \s!height\d_framed_target_ht
+ \s!depth \d_framed_target_dp
+ \csname\??framedright\p_framed_frame\framedparameter\c!rightframe\endcsname
+ \egroup
+ \nointerlineskip % new (needed for fences)
+ \csname\??framedbottom\p_framed_frame\framedparameter\c!bottomframe\endcsname
+ \egroup
+ \wd\scratchbox\d_framed_target_wd
+ \ht\scratchbox\d_framed_target_ht
+ \dp\scratchbox\d_framed_target_dp
+ \box\scratchbox}
+
+\def\pack_framed_stroked_box_normal_closed
+ {\hpack\bgroup
+ \scratchdimen.5\d_framed_linewidth
+ \hskip\scratchdimen
+ \clf_framedoutline
+ \dimexpr\d_framed_target_wd-\d_framed_linewidth\relax
+ \dimexpr\d_framed_target_ht-\scratchdimen\relax
+ \dimexpr\d_framed_target_dp-\scratchdimen\relax
+ \d_framed_linewidth
+ \relax
+ \egroup}
+
+\def\pack_framed_stroked_box_normal
+ {\ifx\p_framed_frame\v!closed
+ \pack_framed_stroked_box_normal_closed
+ \else
+ \pack_framed_stroked_box_normal_opened
+ \fi}
+
+\def\pack_framed_t_rule{\hrule\s!height\d_framed_linewidth\kern-\d_framed_linewidth}
+\def\pack_framed_b_rule{\kern-\d_framed_linewidth\hrule\s!height\d_framed_linewidth}
+\def\pack_framed_r_rule{\kern-\d_framed_linewidth\vrule\s!width\d_framed_linewidth}
+\def\pack_framed_l_rule{\vrule\s!width\d_framed_linewidth\kern-\d_framed_linewidth}
+
+\letvalue{\??framedtop \v!on \v!on}\pack_framed_t_rule
+\letvalue{\??framedtop \v!off\v!on}\pack_framed_t_rule
+\letvalue{\??framedtop \v!on }\pack_framed_t_rule
+
+\letvalue{\??framedbottom\v!on \v!on}\pack_framed_b_rule
+\letvalue{\??framedbottom\v!off\v!on}\pack_framed_b_rule
+\letvalue{\??framedbottom\v!on }\pack_framed_b_rule
+
+\letvalue{\??framedleft \v!on \v!on}\pack_framed_l_rule
+\letvalue{\??framedleft \v!off\v!on}\pack_framed_l_rule
+\letvalue{\??framedleft \v!on }\pack_framed_l_rule
+
+\letvalue{\??framedright \v!on \v!on}\pack_framed_r_rule
+\letvalue{\??framedright \v!off\v!on}\pack_framed_r_rule
+\letvalue{\??framedright \v!on }\pack_framed_r_rule
+
+% no overlapping rules
+
+\def\pack_framed_t_rules{\hpack{\kern\d_framed_linewidth\vrule\s!width\dimexpr\d_framed_target_wd-2\d_framed_linewidth\relax\s!height\d_framed_linewidth}\nointerlineskip\kern-\d_framed_linewidth}
+\def\pack_framed_b_rules{\kern-\d_framed_linewidth\nointerlineskip\hpack{\kern\d_framed_linewidth\vrule\s!width\dimexpr\d_framed_target_wd-2\d_framed_linewidth\relax\s!height\d_framed_linewidth}}
+\def\pack_framed_r_rules{\kern-\d_framed_linewidth\vrule\s!height\dimexpr\d_framed_target_ht-\d_framed_linewidth\relax\s!depth-\d_framed_linewidth\s!width\d_framed_linewidth}
+\def\pack_framed_l_rules{\vrule\s!height\dimexpr\d_framed_target_ht-\d_framed_linewidth\relax\s!depth-\d_framed_linewidth\s!width\d_framed_linewidth\kern-\d_framed_linewidth}
+
+\letvalue{\??framedtop \v!small\v!small}\pack_framed_t_rules
+\letvalue{\??framedtop \v!off \v!small}\pack_framed_t_rules
+\letvalue{\??framedtop \v!small }\pack_framed_t_rules
+
+\letvalue{\??framedbottom\v!small\v!small}\pack_framed_b_rules
+\letvalue{\??framedbottom\v!off \v!small}\pack_framed_b_rules
+\letvalue{\??framedbottom\v!small }\pack_framed_b_rules
+
+\letvalue{\??framedleft \v!small\v!small}\pack_framed_l_rules
+\letvalue{\??framedleft \v!off \v!small}\pack_framed_l_rules
+\letvalue{\??framedleft \v!small }\pack_framed_l_rules
+
+\letvalue{\??framedright \v!small\v!small}\pack_framed_r_rules
+\letvalue{\??framedright \v!off \v!small}\pack_framed_r_rules
+\letvalue{\??framedright \v!small }\pack_framed_r_rules
+
+% \framed
+% [width=4cm,height=3cm,rulethickness=3mm,
+% frame=off,rightframe=on,leftframe=on,topframe=on,bottomframe=on]
+% {}
+% \framed
+% [width=4cm,height=3cm,rulethickness=3mm,
+% frame=off,rightframe=small,leftframe=small,topframe=small,bottomframe=small]
+% {}
+% \framed
+% [width=4cm,height=3cm,rulethickness=3mm,
+% frame=off,rightframe=small,leftframe=small,topframe=small,bottomframe=on]
+% {}
+
+%D The next few macros are probably the most misused ones in \CONTEXT. They deal
+%D with putting rules around boxes, provide backgrounds, offer alignment features,
+%D and some more. We start with defining some booleans. These give an impression of
+%D what we are going to take into account.
+
+% todo : \c_framed_hasoffset
+% faster : \let\c_framed_hasoffset\falseconditional
+
+\newconditional\c_framed_has_offset
+\newconditional\c_framed_has_width
+\newconditional\c_framed_has_height
+\newconditional\c_framed_has_format
+\newconditional\c_framed_is_overlaid
+\newconditional\c_framed_has_frame
+\newconditional\c_framed_has_extra_offset
+\newconditional\c_framed_text_location_none
+
+\newconstant \c_framed_has_strut % 0=relaxes 1=pseudostruts 2=realstruts
+
+%D \macros
+%D {framed, setupframed}
+%D
+%D Ruled boxes are typeset using \type{\framed}. This command is quite versatile
+%D and, although some users will probably seldom use it, one cannot overlook its
+%D features.
+%D
+%D \showsetup{setupframed}
+%D \showsetup{framed}
+%D
+%D This general macro is a special version of an even more general case, that can
+%D easily be linked into other macros that need some kind of framing. The local
+%D version is called with an extra parameter: the variable identifier. The reason
+%D for passing this identifier between brackets lays in the mere fact that this way
+%D we can use the optional argument grabbers.
+
+\mutable\def\defaultframeoffset{.25\exheight}
+
+\installcorenamespace{regularframedlevel}
+
+\permanent\protected\def\installregularframed#1%
+ {\defineframed[#1]}
+
+\permanent\protected\def\presetlocalframed[#1]%
+ {\defineframed[#1]}
+
+% \presetlocalframed[\??framed]
+
+\newcount\c_pack_framed_nesting
+
+\permanent\tolerant\protected\def\framed[#1]%
+ {\bgroup
+ \advance\c_pack_framed_nesting\plusone
+ \letcsname\??framed>\the\c_pack_framed_nesting:\s!parent\endcsname\??framed
+ \edef\currentframed{>\the\c_pack_framed_nesting}%
+ \pack_framed_initialize
+ \bgroup
+ \setupcurrentframed[#1]% here !, seldom no argument so no need to optimize
+ \pack_framed_process_indeed}
+
+\permanent\tolerant\protected\def\startframed[#1]#*[#2]%
+ {\bgroup
+ \ifarguments
+ \pack_framed_initialize
+ \bgroup
+ \or
+ \ifhastok={#1}%
+ \advance\c_pack_framed_nesting\plusone
+ \letcsname\??framed>\the\c_pack_framed_nesting:\s!parent\endcsname\??framed
+ \edef\currentframed{>\the\c_pack_framed_nesting}%
+ \pack_framed_initialize
+ \bgroup
+ \setupcurrentframed[#1]% here !
+ \else
+ \edef\currentframed{#1}%
+ \pack_framed_initialize
+ \bgroup
+ \fi
+ \else
+ \edef\currentframed{#1}%
+ \pack_framed_initialize
+ \bgroup
+ \setupcurrentframed[#2]% here !
+ \fi
+ \pack_framed_process_indeed
+ \bgroup
+ \ignorespaces}
+
+% till here
+
+\permanent\protected\def\stopframed
+ {\removeunwantedspaces
+ \egroup}
+
+\permanent\protected\def\normalframedwithsettings[#1]%
+ {\bgroup
+ \advance\c_pack_framed_nesting\plusone
+ \letcsname\??framed>\the\c_pack_framed_nesting:\s!parent\endcsname\??framed
+ \bgroup
+ \edef\currentframed{>\the\c_pack_framed_nesting}%
+ \pack_framed_initialize
+ \setupcurrentframed[#1]%
+ \pack_framed_process_indeed}
+
+%D \startbuffer
+%D \setupframed [framecolor=yellow] \framed{A}
+%D \defineframed[myframed] [framecolor=blue] \myframed{B}
+%D \setupframed [myframed] [framecolor=red] \myframed{C}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \presetlocalframed[myframed]
+%D \localframed[myframed][framecolor=green]{oeps}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {ifinframed}
+%D
+%D The normal case first presets all parameters and next starts looking for the user
+%D supplied ones. The first step is omitted in the local case, because these are
+%D preset at declaration time and keep their values unless explictly changed. By
+%D presetting the variables everytime the normal command is called, we can use this
+%D command nested, without the unwanted side effect of inheritance. The boolean is
+%D used to speed up the color stack.
+
+\newif\ifinframed
+
+%D The next one is faster on multiple backgrounds per page. No dimensions can be
+%D set, only frames and backgrounds.
+
+%% \protected\def\fastlocalframed[#1]#2[#3]#4% 3-4
+
+\permanent\protected\def\fastlocalframed[#1]#*[#2]#3%
+ {\bgroup
+ \edef\currentframed{#1}%
+ \pack_framed_initialize
+ \setbox\b_framed_normal\hbox{#3}%
+ \iftrialtypesetting \else
+ \edef\m_overlay_region{\framedparameter\c!region}%
+ \ifempty\m_overlay_region\else
+ \pack_framed_set_region
+ \fi
+ \fi
+ \setupcurrentframed[#2]%
+ \edef\p_framed_rulethickness{\framedparameter\c!rulethickness}% also used in backgrounds
+ \d_framed_frameoffset\framedparameter\c!frameoffset\relax % also used in backgrounds
+ \edef\p_framed_frame{\framedparameter\c!frame}%
+ \edef\p_framed_background{\framedparameter\c!background}%
+ % not here, in calling macro: setups
+ \pack_framed_remove_depth
+ \ifx\p_framed_frame\v!overlay \orelse \ifx\p_framed_frame\v!none \else
+ \ifempty\p_framed_rulethickness\else
+ \d_framed_linewidth\p_framed_rulethickness\relax
+ \fi
+ \pack_framed_add_outline % real or invisible frame
+ \fi
+ \ifempty\p_framed_background \else
+ \edef\p_framed_backgroundoffset{\framedparameter\c!backgroundoffset}%
+ \d_framed_backgroundoffset
+ \ifx\p_framed_backgroundoffset\v!frame
+ \d_framed_frameoffset
+ \else
+ \p_framed_backgroundoffset
+ \fi
+ \edef\p_framed_component{\framedparameter\c!component}%
+ \pack_framed_add_background
+ \fi
+ \pack_framed_restore_depth
+ \iftrialtypesetting\orelse\ifempty\m_overlay_region\else
+ \pack_framed_add_region
+ \fi
+ \box\b_framed_normal
+ \egroup}
+
+%D The next macro uses a box and takes its natural width and height so these
+%D can better be correct.
+
+\protected\def\pack_framed_process_box_indeed#1#2% component box (assumes parameters set and grouped usage)
+ {\setbox\b_framed_normal\box#2% could actually be \let\b_framed_normal#2
+ \edef\m_overlay_region{\framedparameter\c!region}%
+ \ifempty\m_overlay_region\else
+ \pack_framed_set_region
+ \fi
+ \edef\p_framed_rulethickness{\framedparameter\c!rulethickness}% also used in backgrounds
+ \d_framed_frameoffset\framedparameter\c!frameoffset\relax % also used in backgrounds
+ \edef\p_framed_frame{\framedparameter\c!frame}%
+ \edef\p_framed_background{\framedparameter\c!background}%
+ \ifx\p_framed_frame\v!overlay \orelse \ifx\p_framed_frame\v!none \else
+ \ifempty\p_framed_rulethickness \else
+ \d_framed_linewidth\p_framed_rulethickness\relax
+ \fi
+ \pack_framed_add_outline % real or invisible frame
+ \fi
+ \ifempty\p_framed_background \else
+ \edef\p_framed_backgroundoffset{\framedparameter\c!backgroundoffset}%
+ \d_framed_backgroundoffset
+ \ifx\p_framed_backgroundoffset\v!frame
+ \d_framed_frameoffset
+ \else
+ \p_framed_backgroundoffset
+ \fi
+ \edef\p_framed_component{#1}%
+ \pack_framed_add_background
+ \fi
+ \ifempty\m_overlay_region\else
+ \pack_framed_add_region
+ \fi
+ \box\b_framed_normal
+ \egroup}
+
+\permanent\protected\def\localbackgroundframed#1% namespace component box
+ {\bgroup
+ \edef\currentframed{#1}%
+ \pack_framed_initialize
+ \pack_framed_process_box_indeed} % group ends here
+
+\mutable\let\postprocessframebox\relax
+
+%D A nice example by Aditya:
+%D
+%D \starttyping
+%D \setupframed
+%D [loffset=\framedparameter{hoffset},
+%D roffset=\framedparameter{hoffset},
+%D hoffset=\zeropoint]
+%D
+%D \defineframed[test][hoffset=1cm]
+%D \stoptyping
+
+\newdimen\d_framed_width
+\newdimen\d_framed_height
+\newdimen\d_framed_frameoffset
+\newdimen\d_framed_backgroundoffset
+\newdimen\d_framed_local_offset
+
+% todo: protect local \framednames
+
+\permanent\tolerant\protected\def\localframed[#1]#*[#2]%
+ {\bgroup
+ \bgroup
+ \edef\currentframed{#1}%
+ \pack_framed_initialize
+ \setupcurrentframed[#2]% here !
+ \pack_framed_process_indeed}
+
+\permanent\protected\def\directlocalframed[#1]% no optional
+ {\bgroup
+ \bgroup
+ \edef\currentframed{#1}%
+ \pack_framed_initialize
+ \pack_framed_process_indeed}
+
+\aliased\let\localframedwithsettings\localframed
+
+% done
+
+\defineinterfaceconstant {fr!analyze} {fr:analyze} % private option
+
+\aliased\let\delayedbegstrut\relax
+\aliased\let\delayedendstrut\relax
+\aliased\let\delayedstrut \relax
+
+\mutable\let\localoffset\empty
+\mutable\let\localwidth \empty
+\mutable\let\localheight\empty
+\mutable\let\localformat\empty
+\mutable\let\localstrut \empty
+
+\mutable\let\framedwidth \empty
+\mutable\let\framedheight\empty
+\mutable\let\framedoffset\empty
+
+\aliased\let\localbegstrut\relax
+\aliased\let\localendstrut\relax
+\aliased\let\localstrut \relax
+
+\protected\def\pack_framed_process_indeed
+ {\d_framed_frameoffset\framedparameter\c!frameoffset
+ \edef\p_framed_backgroundoffset{\framedparameter\c!backgroundoffset}%
+ \d_framed_backgroundoffset
+ \ifx\p_framed_backgroundoffset\v!frame
+ \d_framed_frameoffset
+ \else
+ \p_framed_backgroundoffset
+ \fi
+ % new, experimental dirty hook
+ \framedparameter\c!extras
+ % to get the right spacing
+ \edef\p_framed_foregroundstyle{\framedparameter\c!foregroundstyle}%
+ \ifempty\p_framed_foregroundstyle\else\dousestyleparameter\p_framed_foregroundstyle\fi
+ % beware, both the frame and background offset can be overruled
+ %
+ \edef\p_framed_setups{\framedparameter\c!setups}%
+ % the next macros are visible
+ \edef\localoffset{\framedparameter\c!offset}%
+ \edef\localwidth {\framedparameter\c!width}%
+ \edef\localheight{\framedparameter\c!height}%
+ \edef\localformat{\framedparameter\c!align}%
+ %
+ \edef\p_strut {\framedparameter\c!strut}%
+ % these are not
+ \edef\p_framed_autostrut {\framedparameter\c!autostrut}%
+ \edef\p_framed_frame {\framedparameter\c!frame}%
+ \edef\p_framed_location {\framedparameter\c!location}%
+ \edef\p_framed_orientation{\framedparameter\c!orientation}%
+ \edef\p_framed_anchoring {\framedparameter\c!anchoring}%
+ %
+ \edef\p_framed_autowidth {\framedparameter\c!autowidth}%
+ \edef\p_framed_franalyze {\framedparameter\c!fr!analyze}% experimental option
+ %
+ \ifx\p_framed_frame\v!overlay % no frame, no offset, no framewidth
+ \setfalse\c_framed_has_frame
+ \let\localoffset\v!overlay
+ \orelse\ifx\p_framed_frame\v!none % no frame, no framewidth
+ \setfalse\c_framed_has_frame
+ \else
+ \settrue\c_framed_has_frame
+ \fi
+ \ifconditional\c_framed_has_frame
+ \edef\p_framed_rulethickness{\framedparameter\c!rulethickness}%
+ \ifempty\p_framed_rulethickness\else
+ \d_framed_linewidth\p_framed_rulethickness\relax
+ \fi
+ \else
+ \d_framed_linewidth\zeropoint
+ \fi
+ % 2013/03/12: a change of order (sizes before align
+ \ifx\localwidth\v!local
+ \setlocalhsize
+ \fi
+ %
+ \forgetall % should happen after \localwidth but before align
+ %
+ \ifempty\localformat
+ \setfalse\c_framed_has_format
+ \else
+ \settrue\c_framed_has_format
+ \dosetraggedcommand\localformat % not that fast
+ \fi
+ %
+ \ifcsname\??framedoffsetalternative\localoffset\endcsname
+ \lastnamedcs
+ \else
+ \framed_offset_alternative_unknown
+ \fi
+ \ifcsname\??framedwidthalternative\localwidth\endcsname
+ \lastnamedcs
+ \else
+ \framed_width_alternative_unknown
+ \fi
+ \ifcsname\??framedheightalternative\localheight\endcsname
+ \lastnamedcs
+ \else
+ \framed_height_alternative_unknown
+ \fi
+ % the next check could move to heightalternative
+ \ifconditional\c_framed_has_height
+ % obey user set height, also downward compatible
+ \else
+ \edef\p_framed_lines{\framedparameter\c!lines}%
+ \ifempty\p_framed_lines
+ \orelse\ifcase\p_framed_lines
+ \else
+ \d_framed_height\p_framed_lines\lineheight
+ \edef\localheight{\the\d_framed_height}%
+ \settrue\c_framed_has_height
+ \fi
+ \fi
+ % this is now an option: width=local
+ %
+ % \ifdim\d_framed_width=\hsize
+ % \parindent\zeropoint
+ % \setlocalhsize
+ % \d_framed_width\localhsize
+ % \fi
+ % i.e. disable (colsetbackgroundproblemintechniek)
+ \advance\d_framed_width -2\d_framed_local_offset
+ \advance\d_framed_height -2\d_framed_local_offset
+ \ifcsname\??framedstrutalternative\p_strut\endcsname
+ \lastnamedcs
+ \else
+ \framed_offset_alternative_unknown
+ \fi
+ % the next check could move to strutalternative
+ \ifcase\c_framed_has_strut % none (not even noindent)
+ \enforced\let\localbegstrut\relax
+ \enforced\let\localendstrut\relax
+ \enforced\let\localstrut \relax
+ \or % no / overlay
+ \enforced\let\localbegstrut\pseudobegstrut
+ \enforced\let\localendstrut\pseudoendstrut
+ \enforced\let\localstrut \pseudostrut
+ \else
+ \enforced\let\localbegstrut\begstrut
+ \enforced\let\localendstrut\endstrut
+ \enforced\let\localstrut \strut
+ \fi
+ \ifx\p_framed_autostrut\v!yes
+ \enforced\let\delayedbegstrut\relax
+ \enforced\let\delayedendstrut\relax
+ \enforced\let\delayedstrut \relax
+ \else
+ \enforced\let\delayedbegstrut\localbegstrut
+ \enforced\let\delayedendstrut\localendstrut
+ \enforced\let\delayedstrut \localstrut
+ \enforced\let\localbegstrut \relax
+ \enforced\let\localendstrut \relax
+ \enforced\let\localstrut \relax
+ \fi
+ \ifconditional\c_framed_has_height
+ \enforced\let\\\pack_framed_vboxed_newline
+ \ifconditional\c_framed_has_width
+ \enforced\let\hairline\pack_framed_vboxed_hairline
+ \ifconditional\c_framed_has_format
+ \let\next\pack_framed_format_format_yes
+ \else
+ \let\next\pack_framed_format_format_nop
+ \fi
+ \else
+ \enforced\let\hairline\pack_framed_hboxed_hairline
+ \ifconditional\c_framed_has_format
+ \let\next\pack_framed_format_format_height
+ \else
+ \let\next\pack_framed_format_format_vsize
+ \fi
+ \fi
+ \orelse\ifconditional\c_framed_has_width
+ \ifconditional\c_framed_has_format
+ \enforced\let\\\pack_framed_vboxed_newline
+ \enforced\let\hairline\pack_framed_vboxed_hairline
+ \let\next\pack_framed_format_format_width
+ \else
+ \enforced\let\\\pack_framed_hboxed_newline
+ \enforced\let\hairline\pack_framed_hboxed_hairline
+ \let\next\pack_framed_format_format_hsize
+ \fi
+ \else
+ \enforced\let\\\pack_framed_hboxed_newline
+ \enforced\let\hairline\pack_framed_hboxed_hairline
+ \let\next\pack_framed_format_format_no_size
+ \fi
+ \pack_framed_check_extra_offsets
+ \edef\p_framed_background{\framedparameter\c!background}%
+% \ifempty\p_framed_background
+% \let\pack_framed_forgetall\forgetall
+% \else
+% \let\pack_framed_forgetall\relax
+% \forgetall
+% \fi
+ \edef\framedwidth {\the\ifdim\d_framed_width >\zeropoint \d_framed_width \else\zeropoint\fi}% public
+ \edef\framedheight{\the\ifdim\d_framed_height>\zeropoint \d_framed_height\else\zeropoint\fi}% public
+ \edef\framedoffset{\the\dimexpr\ifconditional\c_framed_has_offset\localoffset \else\zeropoint\fi}% public
+ % for now: i need to think of something better
+ % \bitwiseflip \normalizelinemode -\clipwidthcode % now handled in analyzer
+ % but that's for later
+ \ifempty\p_framed_orientation
+ \let\pack_framed_stop_orientation\relax
+ \else
+ \pack_framed_start_orientation
+ \fi
+ \afterassignment\pack_framed_restart
+ \setbox\b_framed_normal\next}
+
+% alternatives for width, height, strut and offset
+
+\installcorenamespace{framedwidthalternative}
+\installcorenamespace{framedheightalternative}
+\installcorenamespace{framedstrutalternative}
+\installcorenamespace{framedoffsetalternative}
+
+% widths
+
+\setvalue{\??framedwidthalternative\empty}%
+ {\ifconditional\c_framed_has_format
+ \settrue\c_framed_has_width
+ \d_framed_width\hsize
+ \else
+ \setfalse\c_framed_has_width
+ \d_framed_width\zeropoint
+ \fi}
+
+\setvalue{\??framedwidthalternative\v!fit}%
+ {\ifconditional\c_framed_has_format
+ \settrue\c_framed_has_width
+ \d_framed_width\hsize
+ \else
+ \setfalse\c_framed_has_width
+ \d_framed_width\zeropoint
+ \fi}
+
+\setvalue{\??framedwidthalternative\v!fixed}% equals \v!fit but no shapebox
+ {\ifconditional\c_framed_has_format
+ \settrue\c_framed_has_width
+ \d_framed_width\hsize
+ \else
+ \setfalse\c_framed_has_width
+ \d_framed_width\zeropoint
+ \fi}
+
+\setvalue{\??framedwidthalternative\v!broad}%
+ {\settrue\c_framed_has_width
+ \d_framed_width\hsize}
+
+\setvalue{\??framedwidthalternative\v!max}% idem broad
+ {\settrue\c_framed_has_width
+ \d_framed_width\hsize}
+
+\setvalue{\??framedwidthalternative\v!local}%
+ {\settrue\c_framed_has_width
+ %\setlocalhsize
+ \d_framed_width\localhsize}
+
+\setvalue{\??framedwidthalternative\s!unknown}%
+ {\settrue\c_framed_has_width
+ \d_framed_width\localwidth}
+
+\def\framed_width_alternative_unknown
+ {\settrue\c_framed_has_width
+ \d_framed_width\localwidth}
+
+% heights
+
+\setvalue{\??framedheightalternative\empty}%
+ {\setfalse\c_framed_has_height
+ \d_framed_height\zeropoint}
+
+\setvalue{\??framedheightalternative\v!fit}%
+ {\setfalse\c_framed_has_height
+ \d_framed_height\zeropoint}
+
+\setvalue{\??framedheightalternative\v!broad}%
+ {\setfalse\c_framed_has_height
+ \d_framed_height\zeropoint}
+
+\setvalue{\??framedheightalternative\v!max}%
+ {\settrue\c_framed_has_height
+ \d_framed_height\vsize}
+
+\setvalue{\??framedheightalternative\s!unknown}%
+ {\settrue\c_framed_has_height
+ \d_framed_height\localheight}
+
+\def\framed_height_alternative_unknown
+ {\settrue\c_framed_has_height
+ \d_framed_height\localheight}
+
+% struts (use let instead?)
+
+\setvalue{\??framedstrutalternative\v!no}%
+ {\c_framed_has_strut\plusone}
+
+\setvalue{\??framedstrutalternative\v!global}%
+ {\setstrut}
+
+\setvalue{\??framedstrutalternative\v!local}%
+ {\setfontstrut}
+
+\setvalue{\??framedstrutalternative\v!yes}%
+ {\setstrut}
+
+\setvalue{\??framedstrutalternative\s!unknown}%
+ {\setstrut}
+
+\def\framed_strut_alternative_unknown
+ {\setstrut}
+
+\setvalue{\??framedstrutalternative\v!none}% not even pseudo struts
+ {\c_framed_has_strut\zerocount}
+
+% offsets
+
+\setvalue{\??framedoffsetalternative\v!none}%
+ {\setfalse\c_framed_has_offset
+ \c_framed_has_strut\plusone
+ \setfalse\c_framed_is_overlaid
+ \d_framed_local_offset\d_framed_linewidth}
+
+\setvalue{\??framedoffsetalternative\v!overlay}%
+ {% \ifx\p_framed_frame\v!no \setfalse\c_framed_has_frame \fi % test first
+ \setfalse\c_framed_has_offset
+ \c_framed_has_strut\plusone
+ \settrue\c_framed_is_overlaid
+ \d_framed_local_offset\zeropoint}
+
+% \setvalue{\??framedoffsetalternative\v!strut}%
+% {\setfalse\c_framed_has_offset
+% \c_framed_has_strut\plustwo
+% \settrue\c_framed_is_overlaid
+% \d_framed_local_offset\zeropoint}
+
+\setvalue{\??framedoffsetalternative\v!default}% new per 2-6-2000
+ {\settrue \c_framed_has_offset
+ \c_framed_has_strut\plustwo
+ \setfalse\c_framed_is_overlaid
+ \let\localoffset\defaultframeoffset
+ \letframedparameter\c!offset\defaultframeoffset % brrr
+ \d_framed_local_offset\dimexpr\localoffset+\d_framed_linewidth\relax}
+
+\def\framed_offset_alternative_unknown
+ {\settrue \c_framed_has_offset
+ \c_framed_has_strut\plustwo
+ \setfalse\c_framed_is_overlaid
+ \let\defaultframeoffset\localoffset
+ \d_framed_local_offset\dimexpr\localoffset+\d_framed_linewidth\relax}
+
+\letvalue{\??framedoffsetalternative\s!unknown}\framed_offset_alternative_unknown
+
+% so far for alternatives
+
+\let\pack_framed_stop_orientation\relax
+
+\def\pack_framed_restart
+ {\aftergroup\pack_framed_finish}
+
+\def\pack_framed_do_top
+ {\raggedtopcommand
+ \framedparameter\c!top
+ \edef\p_blank{\framedparameter\c!blank}%
+ \ifx\p_blank\v!yes\else % auto or no
+ \doinhibitblank
+ \fi}
+
+\def\pack_framed_do_bottom
+ {\framedparameter\c!bottom
+ \raggedbottomcommand}
+
+%D Careful analysis of this macro will learn us that not all branches in the last
+%D conditionals can be encountered, that is, some assignments to \type{\next} will
+%D never occur. Nevertheless we implement the whole scheme, if not for future
+%D extensions.
+
+% %D \macros
+% %D {doassigncheckedframeoffset}
+% %D
+% %D Offset helper (see menus):
+%
+% \def\doassigncheckedframeoffset#1#2% could be a fast \csname .. \endcsname
+% {\edef\checkedframeoffset{#2}%
+% #1%
+% \ifempty\checkedframeoffset \zeropoint\orelse
+% \ifx\checkedframeoffset\v!overlay\zeropoint\orelse
+% \ifx\checkedframeoffset\v!none \zeropoint\orelse
+% \ifx\checkedframeoffset\v!frame \zeropoint\orelse
+% \ifx\checkedframeoffset\v!default\zeropoint\else
+% #2%
+% \fi
+% \relax}
+%
+% \def\doassigncheckedframeoffset#1#2% could be a fast \csname .. \endcsname
+% {#1\ifchkdim#2\or#2\else\zeropoint\fi}%
+
+%D \macros
+%D {ifreshapeframebox}
+%D
+%D The last few lines tell what to do after the content of the box is collected and
+%D passed to the next macro. In the case of a fixed width and centered alignment,
+%D the content is evaluated and used to determine the most natural width. The rest
+%D of the code deals with backgrounds and frames.
+
+\newif\ifreshapeframebox \reshapeframeboxtrue
+
+%D Beware: setting \type {top} and \type {bottom} to nothing, may
+%D result in a frame that is larger that the given height! try:
+%D
+%D \starttyping
+%D \framed
+%D [height=3cm,top=,bottom=,offset=overlay]
+%D {\strut test \shapefill \strut test}
+%D \stoptyping
+%D
+%D This is intended behaviour and not a bug! One can always set
+%D
+%D \starttyping
+%D ...,bottom=\kern0pt,...
+%D \stoptyping
+
+% experiment ... \p_framed_franalyze -> we could support 'first' as location key
+% option but then we will always do an analysis and reimplement the location
+% options (btw, beware of location settings of derived functionality that bleed
+% into this
+
+\def\pack_framed_finish_a
+ {\ifreshapeframebox
+ \pack_framed_reshape_process
+ \orelse\ifx\p_framed_franalyze\v!yes
+ \pack_framed_reshape_analyze
+ \else
+ \pack_framed_reshape_reset
+ \fi
+ \setfalse\c_framed_has_width}
+
+\def\pack_framed_finish_b
+ {\ifx\p_framed_franalyze\v!yes
+ \pack_framed_reshape_analyze
+ \else
+ \pack_framed_reshape_reset
+ \fi
+ \setfalse\c_framed_has_width}
+
+\def\pack_framed_finish_c
+ {\ifx\p_framed_franalyze\v!yes
+ \pack_framed_reshape_analyze
+ \else
+ \pack_framed_reshape_reset
+ \fi}
+
+\def\pack_framed_profile_box
+ {\profilegivenbox\p_profile\b_framed_normal
+ \setbox\b_framed_normal\vpack{\unvbox\b_framed_normal}}
+
+\def\pack_framed_reverse_box
+ {\ifvbox\b_framed_normal
+ \edef\p_linedirection{\framedparameter\c!linedirection}%
+ \ifx\p_linedirection\v!reverse
+ \reversevboxcontent\b_framed_normal
+ \setbox\b_framed_normal\vpack{\unvbox\b_framed_normal}%
+ \fi
+ \fi}
+
+\protected\def\pack_framed_finish
+ {%\pack_framed_stop_orientation % hm, wrong place ! should rotate the result (after reshape) .. moved down
+ \pack_framed_locator_before\p_framed_location
+ \ifconditional\c_framed_has_format
+ %\ifconditional\c_framed_has_height \else
+ % \edef\p_profile{\framedparameter\c!profile}%
+ % \ifempty\p_profile\else
+ % \pack_framed_profile_box
+ % \fi
+ %\fi
+ \ifempty\p_framed_anchoring\else
+ \pack_framed_reverse_box
+ \fi
+ \ifx\p_framed_autowidth\v!force
+ \pack_framed_finish_a
+ \orelse\ifx\localwidth\v!fit
+ \ifx\p_framed_autowidth\v!yes
+ \pack_framed_finish_a
+ \else
+ \pack_framed_finish_b
+ \fi
+ \orelse\ifx\localwidth\v!fixed
+ \pack_framed_finish_b
+ \else
+ \pack_framed_finish_c
+ \fi
+ \ifconditional\c_framed_has_height \else
+ \edef\p_profile{\framedparameter\c!profile}%
+ \ifempty\p_profile\else
+ \pack_framed_profile_box
+ \fi
+ \fi
+ \ifconditional\page_postprocessors_needed_box
+ % quite late
+ \page_postprocessors_linenumbers_box\b_framed_normal
+ \fi
+ \else
+ \pack_framed_finish_c
+ \fi
+ \ifconditional\c_framed_has_width
+ \wd\b_framed_normal\d_framed_width
+ \fi
+ \ifconditional\c_framed_has_height
+ \ht\b_framed_normal\d_framed_height
+ \else
+ \edef\p_framed_minheight{\framedparameter\c!minheight}%
+ \ifempty\p_framed_minheight \else
+ \ifdim\ht\b_framed_normal<\p_framed_minheight
+ \ht\b_framed_normal\p_framed_minheight
+ \fi
+ \fi
+ \fi
+ \edef\p_framed_empty{\framedparameter\c!empty}%
+ \ifx\p_framed_empty\v!yes
+ \pack_framed_fake_box
+ \fi
+ \ifempty\p_framed_anchoring\else
+ \pack_framed_handle_anchoring
+ \fi
+ \pack_framed_stop_orientation % moved here at 2014-05-25
+ \iftrialtypesetting \else
+ \edef\m_overlay_region{\framedparameter\c!region}%
+ \ifempty\m_overlay_region\else
+ \pack_framed_set_region
+ \fi
+ \fi
+ \d_framed_applied_offset
+ \ifconditional\c_framed_is_overlaid
+ \zeropoint
+ \else
+ \d_framed_linewidth
+ \fi
+ \ifconditional\c_framed_has_offset
+ \advance\d_framed_applied_offset\localoffset\relax
+ \fi
+ \ifconditional\c_framed_has_extra_offset
+ \pack_framed_apply_extra_offsets % includes \d_framed_applied_offset
+ \else
+ \ifzeropt\d_framed_applied_offset
+ \else
+ \pack_framed_widen_box
+ \fi
+ \fi
+ %
+ \ifrelax\postprocessframebox \else
+ % better: \pushmacro\\postprocessframebox etc
+ \let\next\postprocessframebox
+ \let\postprocessframebox\relax % prevent nesting
+ \next\b_framed_normal
+ \fi
+ \iftrialtypesetting
+ % new
+ \else
+ \ifconditional\c_framed_has_frame % real or invisible frame
+ \pack_framed_add_outline
+ \fi
+ \ifempty\p_framed_background \else
+ \edef\p_framed_component{\framedparameter\c!component}%
+ \pack_framed_add_background
+ \fi
+ \fi
+ \pack_framed_locator_after\p_framed_location
+ \iftrialtypesetting \else
+ \ifempty\m_overlay_region\else
+ \pack_framed_add_region
+ \fi
+ \fi
+ \box\b_framed_normal
+ \global\frameddimensionstate % global so to be used directly afterwards !
+ \ifconditional\c_framed_has_width
+ \ifconditional\c_framed_has_height \plusthree \else \plusone \fi
+ \else
+ \ifconditional\c_framed_has_height \plustwo \else \zerocount \fi
+ \fi
+ \egroup
+ \egroup}
+
+%D Anchoring is experimental and was prototyped around the ctx meeting in 2018 but
+%D never mede it into the core yet. It operates independent of the orientation
+%D mechanism already present. It's a rather efficient feature. Best is to use
+%D predefined orientations, like:
+%D
+%D \starttyping
+%D \defineorientation[leftflushleft] [orientation=left,horizontal=flushleft]
+%D
+%D \framed [anchoring={leftflushleft}] {anchoring}
+%D \stoptyping
+%D
+%D But this also works:
+%D
+%D \starttyping
+%D \framed [anchoring={flushleft,top,up}] {anchoring}
+%D \stoptyping
+%D
+%D When an anchoring is given (use \type {normal} for the default) you can also
+%D use the \type {xanchor} and \type {yanchor} offsets.
+
+% because we mess with the width later on, we need double wrapping:
+
+\def\pack_framed_handle_anchoring
+ {\scratchcounter\autoorientation\p_framed_anchoring\relax
+ \edef\p_xanchor{\framedparameter\c!xanchor}%
+ \edef\p_yanchor{\framedparameter\c!yanchor}%
+ \setbox\b_framed_normal\hpack{\hpack % here
+ % using the keyword approachs works ok, don't mess with orientation
+ % directly here using \boxorientation ... it doesn't work that well
+ \s!orientation\scratchcounter
+ \ifempty\p_xanchor\else \s!xoffset \p_xanchor\fi
+ \ifempty\p_yanchor\else \s!yoffset \p_yanchor\fi
+ {\box\b_framed_normal}}}
+
+\installcorenamespace{framedlocatorbefore}
+\installcorenamespace{framedlocatorafter}
+
+\newconstant\frameddimensionstate % global state: 0=unknown 1=width 2=height 3=both
+
+\def\pack_framed_fake_box
+ {\setbox\scratchbox\emptyhbox
+ \wd\scratchbox\wd\b_framed_normal
+ \ht\scratchbox\ht\b_framed_normal
+ \dp\scratchbox\dp\b_framed_normal
+ \setbox\b_framed_normal\box\scratchbox}
+
+\permanent\protected\def\installframedlocator#1#2#3%
+ {\defcsname\??framedlocatorbefore#1\endcsname{#2}%
+ \defcsname\??framedlocatorafter #1\endcsname{#3}}
+
+\def\pack_framed_locator_before#1{\begincsname\??framedlocatorbefore#1\endcsname}
+\def\pack_framed_locator_after #1{\begincsname\??framedlocatorafter #1\endcsname}
+
+\newdimen\d_framed_locator_ht
+\newdimen\d_framed_locator_dp
+\newdimen\d_framed_locator_lo
+\newdimen\d_framed_locator_ro
+
+\def\pack_framed_locator_set#1%
+ {\d_framed_locator_ht\dimexpr
+ #1+\d_framed_linewidth
+ \ifconditional\c_framed_has_offset
+ +\framedparameter\c!offset
+ \fi
+ +\framedparameter\c!toffset
+ \relax
+ \d_framed_locator_dp\dimexpr\ht\b_framed_normal-\d_framed_locator_ht\relax}
+
+\def\pack_framed_locator_set_lo
+ {\global\d_framed_locator_lo\dimexpr
+ \d_framed_linewidth
+ \ifconditional\c_framed_has_offset
+ +\framedparameter\c!offset
+ \fi
+ +\framedparameter\c!loffset
+ \relax}
+
+\def\pack_framed_locator_set_ro
+ {\global\d_framed_locator_ro\dimexpr
+ \d_framed_linewidth
+ \ifconditional\c_framed_has_offset
+ +\framedparameter\c!offset
+ \fi
+ +\framedparameter\c!roffset
+ \relax}
+
+% \ruledhbox
+% {A
+% \framed[width=2cm,align=middle,location=hanging]{location\\equals\\hanging}
+% \framed[width=2cm,align=middle,location=depth] {location\\equals\\depth}
+% \framed[width=2cm,align=middle,location=height] {location\\equals\\height}
+% B}
+% \vskip2cm
+% \ruledhbox
+% {A
+% \framed[width=2cm,align=middle,location=low] {location\\equals\\low}
+% \framed[width=2cm,align=middle,location=line] {location\\equals\\line}
+% \framed[width=2cm,align=middle,location=high] {location\\equals\\high}
+% B}
+% \vskip2cm
+% \ruledhbox
+% {A
+% \framed[width=2cm,align=middle,location=top] {location\\equals\\top}
+% \framed[width=2cm,align=middle,location=bottom] {location\\equals\\bottom}
+% \framed[width=2cm,align=middle,location=lohi] {location\\equals\\lohi}
+% \framed[width=2cm,align=middle,location=middle] {location\\equals\\middle}
+% B}
+
+\installframedlocator \v!hanging % best with strut=no *1* / see mail to list by SB
+ {}
+ {\scratchdimen\ht\b_framed_normal
+ \setbox\b_framed_normal\hpack{\lower\scratchdimen\box\b_framed_normal}%
+ \dp\b_framed_normal\scratchdimen
+ \ht\b_framed_normal\zeropoint
+ \box\b_framed_normal}
+
+\installframedlocator \v!depth % *1*
+ {}
+ {\setbox\b_framed_normal\hpack{\lower\strutdp\box\b_framed_normal}%
+ \ht\b_framed_normal\dimexpr\ht\b_framed_normal-\strutdp\relax
+ \dp\b_framed_normal\strutdp
+ \box\b_framed_normal}
+
+\installframedlocator \v!height % *1*
+ {}
+ {\scratchdimen\dimexpr \ht\b_framed_normal - \strutht \relax
+ \setbox\b_framed_normal\hpack{\lower\scratchdimen\box\b_framed_normal}%
+ \dp\b_framed_normal\dimexpr\ht\b_framed_normal-\strutht\relax
+ \ht\b_framed_normal\strutht
+ \box\b_framed_normal}
+
+\installframedlocator \v!high
+ {}
+ {\pack_framed_locator_set\strutht
+ \setbox\b_framed_normal\hpack{\lower\d_framed_locator_dp\box\b_framed_normal}%
+ \ht\b_framed_normal\strutht
+ \dp\b_framed_normal\strutdp
+ \hpack{\box\b_framed_normal}} % why do we pack .. dange of loosing?
+
+\installframedlocator \v!line
+ {}
+ {\setbox\b_framed_normal\hpack{\lower.5\ht\b_framed_normal\box\b_framed_normal}%
+ \ht\b_framed_normal.5\lineheight
+ \dp\b_framed_normal.5\lineheight
+ \hpack{\box\b_framed_normal}} % why do we pack .. dange of loosing?
+
+\installframedlocator \v!low
+ {}
+ {\pack_framed_locator_set\strutdp
+ \setbox\b_framed_normal\hpack{\lower\d_framed_locator_ht\box\b_framed_normal}%
+ \ht\b_framed_normal\strutht
+ \dp\b_framed_normal\strutdp
+ \box\b_framed_normal}
+
+\installframedlocator \v!top
+ {}
+ {\pack_framed_locator_set\strutht
+ \setbox\b_framed_normal\hpack{\lower\d_framed_locator_dp\box\b_framed_normal}%
+ \ht\b_framed_normal\d_framed_locator_ht
+ \dp\b_framed_normal\d_framed_locator_dp
+ \hpack{\box\b_framed_normal}} % why do we pack .. dange of loosing?
+
+\installframedlocator \v!middle
+ {}
+ {\scratchdimen.5\ht\b_framed_normal
+ \setbox\b_framed_normal\hpack{\lower\scratchdimen\box\b_framed_normal}%
+ \ht\b_framed_normal\scratchdimen
+ \dp\b_framed_normal\scratchdimen
+ \hpack{\box\b_framed_normal}} % why do we pack .. dange of loosing?
+
+\installframedlocator \v!lohi % maybe also \v!center
+ {\pack_framed_locator_before\v!middle}
+ {\pack_framed_locator_after \v!middle}
+
+\installframedlocator \v!bottom
+ {}
+ {\pack_framed_locator_set\strutdp
+ \setbox\b_framed_normal\hpack{\lower\d_framed_locator_ht\box\b_framed_normal}%
+ \ht\b_framed_normal\d_framed_locator_dp
+ \dp\b_framed_normal\d_framed_locator_ht
+ \hpack{\box\b_framed_normal}} % why do we pack .. dange of loosing?
+
+\installframedlocator \v!keep % retains height/depth
+ {\pack_framed_remove_depth}
+ {\pack_framed_restore_depth}
+
+\newdimen\d_framed_formula
+
+\installframedlocator \v!formula % private, will become a more generic name
+ {}
+ {\pack_framed_locator_set\d_framed_formula
+ \setbox\b_framed_normal\hpack{\lower\d_framed_locator_dp\box\b_framed_normal}%
+ \ht\b_framed_normal\d_framed_locator_ht
+ \dp\b_framed_normal\d_framed_locator_dp
+ \hpack{\box\b_framed_normal}} % why do we pack .. danger of loosing?
+
+% also used in fastlocalframed
+
+\newdimen\d_framed_original_wd
+\newdimen\d_framed_original_ht
+\newdimen\d_framed_original_dp
+
+\def\pack_framed_remove_depth
+ {\d_framed_original_wd\wd\b_framed_normal
+ \d_framed_original_ht\ht\b_framed_normal
+ \d_framed_original_dp\dp\b_framed_normal
+ \ifzeropt\d_framed_original_dp\else
+ \setbox\b_framed_normal\hpack{\raise\d_framed_original_dp\box\b_framed_normal}%
+ \fi
+ \wd\b_framed_normal\d_framed_original_wd
+ \ht\b_framed_normal\dimexpr\d_framed_original_ht+\d_framed_original_dp\relax
+ \dp\b_framed_normal\zeropoint}
+
+\def\pack_framed_restore_depth
+ {\ifzeropt\d_framed_original_dp \else
+ \setbox\b_framed_normal\hpack{\lower\d_framed_original_dp\box\b_framed_normal}%
+ \fi
+ \wd\b_framed_normal\d_framed_original_wd
+ \ht\b_framed_normal\d_framed_original_ht
+ \dp\b_framed_normal\d_framed_original_dp}
+
+% \framed[width=12cm,height=3cm,orientation=0]{\input ward\relax}
+% \framed[width=12cm,height=3cm,orientation=90]{\input ward\relax}
+% \framed[width=12cm,height=3cm,orientation=180]{\input ward\relax}
+% \framed[width=12cm,height=3cm,orientation=270]{\input ward\relax}
+% \framed[width=12cm,height=3cm,orientation=-90]{\input ward\relax}
+% \framed[width=12cm,height=3cm,orientation=-180]{\input ward\relax}
+% \framed[width=12cm,height=3cm,orientation=-270]{\input ward\relax}
+
+\def\pack_framed_start_orientation
+ {\ifcase\p_framed_orientation
+ \let\pack_framed_stop_orientation\relax
+ \else
+ \let\pack_framed_stop_orientation\pack_framed_stop_orientation_indeed
+ \fi}
+
+\def\pack_framed_stop_orientation_indeed
+ {\setbox\b_framed_normal\hpack{\dorotatebox\p_framed_orientation\hpack{\box\b_framed_normal}}%
+ \d_framed_height\ht\b_framed_normal
+ \d_framed_width \wd\b_framed_normal}
+
+%D The last conditional takes care of the special situation of in||line \inframed
+%D [height=3cm] {framed} boxes. Such boxes have to be \inframed {aligned} with the
+%D running text.
+
+\permanent\tolerant\protected\def\inframed[#1]{\framed[\c!location=\v!low,#1]}
+
+%D When we set \type{empty} to \type{yes}, we get ourselves a frame and/or background,
+%D but no content, so actually we have a sort of phantom framed box.
+
+%D \macros
+%D {mframed, minframed}
+%D
+%D When Tobias asked how to frame mathematical elements in formulas, Taco's posted the
+%D next macro:
+%D
+%D \starttyping
+%D \def\mframed#1%
+%D {\relax
+%D \ifmmode
+%D \vcenter{\hbox{\framed{$\ifinner\else\displaystyle\fi#1$}}}%
+%D \else
+%D \framed{$#1$}%
+%D \fi}
+%D \stoptyping
+%D
+%D Because \type {\ifinner} does not (always) reports what one would expect, we move the
+%D test to the outer level. We also want to pass arguments,
+%D
+%D \starttyping
+%D \def\mframed%
+%D {\dosingleempty\domframed}
+%D
+%D \def\domframed[#1]#2% % tzt \dowithnextmathbox ?
+%D {\relax
+%D \ifmmode
+%D \ifinner
+%D \inframed[#1]{$#2$}%
+%D \else
+%D \vcenter{\hbox{\framed[#1]{$\displaystyle#2$}}}%
+%D \fi
+%D \else
+%D \inframed[#1]{$#2$}%
+%D \fi}
+%D \stoptyping
+%D
+%D Still better is the next alternative, if only because it takes care of setting the super-
+%D and subscripts styles
+
+\newcount\c_framed_mstyle
+
+\protected\def\pack_framed_math_strut
+ {\Ustartmath
+ \triggermathstyle\c_framed_mstyle
+ \vphantom{(}%
+ \Ustopmath}
+
+\installcorenamespace{mathframed}
+
+\installframedcommandhandler \??mathframed {mathframed} \??mathframed
+
+\newcount\c_pack_framed_mathframed
+\newtoks \t_pack_framed_mathframed
+
+\appendtoks
+ \frozen\protected\instance\edefcsname\currentmathframed\endcsname{\pack_framed_mathframed[\currentmathframed]}%
+\to \everydefinemathframed
+
+\let\pack_framed_mc_one\relax
+\let\pack_framed_mc_two\relax
+
+\tolerant\protected\def\pack_framed_mathframed[#1]#*[#2]#:#3% needs testing !
+ {\begingroup
+ \edef\currentmathframed{#1}%
+ \setupcurrentmathframed[#2]%
+ \c_framed_mstyle\normalmathstyle
+ \edef\m_framed_location{\mathframedparameter\c!location}%
+ \ifx\m_framed_location\v!mathematics
+ \enforced\let\normalstrut\pack_framed_math_pos % I don't like this overload!
+ \orelse\ifx\m_framed_location\v!low\else
+ \enforced\let\normalstrut\pack_framed_math_strut
+ \fi
+ \inheritedmathframedframed\bgroup
+ \Ustartmath
+ \triggermathstyle\c_framed_mstyle
+ \the\t_pack_framed_mathframed
+ #3%
+ \Ustopmath
+ \egroup
+ \endgroup}
+
+\def\pack_framed_math_pos
+ {\global\advance\c_pack_framed_mathframed\plusone
+ \xdef\pack_framed_mc_one{mcf:1:\number\c_pack_framed_mathframed}%
+ \xdef\pack_framed_mc_two{mcf:2:\number\c_pack_framed_mathframed}%
+ \xypos\pack_framed_mc_two}
+
+\appendtoks
+ \mathraggedstatus\plustwo % makes \startalign work
+ \eqalignmode \zerocount % makes \startalign fit
+\to \t_pack_framed_mathframed
+
+\installframedlocator \v!mathematics
+ {}
+ {\lower\dimexpr\MPy\pack_framed_mc_two-\MPy\pack_framed_mc_one\relax
+ \hpack{\xypos\pack_framed_mc_one\box\b_framed_normal}}
+
+\definemathframed[mframed]
+\definemathframed[inmframed][\c!location=\v!low]
+\definemathframed[mcframed] [\c!location=\v!mathematics]
+
+%D So instead of the rather versatile \type {\framed}, we use \type {\mframed}:
+%D
+%D \startbuffer
+%D \startformula
+%D x \times \mframed{y} \times y^{z_z}
+%D x \times \inmframed{y} \times y^{z_z}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D And:
+%D
+%D \startbuffer
+%D \startformula
+%D x \times \mframed{y} \times y^{\mframed{z}_{\mframed{z}}}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D As usual, one can specify in what way the text should be framed. One should be
+%D aware of the fact that, inorder to preserve the proper spacing, the \type
+%D {offset} is set to \type {overlay} and \type {frameoffset} is used used instead.
+%D
+%D \startbuffer
+%D \startformula
+%D x \times y^{\mframed[framecolor=red]{z}_{z}}
+%D \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D For inline use, we also provide the \type {\inmframed} alternative: we want $x
+%D \times \inmframed{y}$ in inline math, right?
+
+%D This previous framing macros needs a lot of alternatives for putting rules around
+%D boxes, inserting offsets and aligning text. Each step is handled by separate macros.
+
+\newdimen\d_framed_applied_offset
+\newdimen\d_framed_loffset
+\newdimen\d_framed_roffset
+\newdimen\d_framed_toffset
+\newdimen\d_framed_boffset
+
+\def\pack_framed_check_extra_offsets % we could check h and v indepently
+ {\setfalse\c_framed_has_extra_offset
+ \d_framed_loffset\framedparameter\c!loffset\relax
+ \d_framed_roffset\framedparameter\c!roffset\relax
+ \d_framed_toffset\framedparameter\c!toffset\relax
+ \d_framed_boffset\framedparameter\c!boffset\relax
+ \ifzeropt\d_framed_loffset\else \advance\d_framed_width -\d_framed_loffset \settrue\c_framed_has_extra_offset \fi
+ \ifzeropt\d_framed_roffset\else \advance\d_framed_width -\d_framed_roffset \settrue\c_framed_has_extra_offset \fi
+ \ifzeropt\d_framed_toffset\else \advance\d_framed_height-\d_framed_toffset \settrue\c_framed_has_extra_offset \fi
+ \ifzeropt\d_framed_boffset\else \advance\d_framed_height-\d_framed_boffset \settrue\c_framed_has_extra_offset \fi}
+
+\def\pack_framed_apply_extra_offsets
+ {\setbox\b_framed_normal\vpack\bgroup
+ \advance\d_framed_toffset\d_framed_applied_offset
+ \advance\d_framed_boffset\d_framed_applied_offset
+ \advance\d_framed_loffset\d_framed_applied_offset
+ \advance\d_framed_roffset\d_framed_applied_offset
+ \kern\d_framed_toffset
+ \hpack\bgroup
+ \kern\d_framed_loffset
+ \box\b_framed_normal
+ \kern\d_framed_roffset
+ \egroup
+ \kern\d_framed_boffset
+ \egroup}
+
+\def\pack_framed_widen_box
+ {\setbox\b_framed_normal\vpack
+ {\kern\d_framed_applied_offset
+ \hpack{\kern\d_framed_applied_offset\box\b_framed_normal\kern\d_framed_applied_offset}%
+ \kern\d_framed_applied_offset}}
+
+%D Let's hope that the next few examples show us enough of what needs to be
+%D done by the auxiliary macros.
+%D
+%D \startbuffer
+%D \framed[height=1cm,offset=.5cm] {rule based learning}
+%D \framed[height=1cm,offset=0cm] {rule based learning}
+%D \framed[height=1cm,offset=none] {rule based learning}
+%D \framed[height=1cm,offset=overlay]{rule based learning}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \hbox{\getbuffer}
+%D \stoplinecorrection
+%D
+%D \startbuffer
+%D \framed[offset=.5cm] {rule based learning}
+%D \framed[offset=0cm] {rule based learning}
+%D \framed[offset=none] {rule based learning}
+%D \framed[offset=overlay]{rule based learning}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \hbox{\getbuffer}
+%D \stoplinecorrection
+%D
+%D \startbuffer
+%D \framed[strut=no,offset=.5cm] {rule based learning}
+%D \framed[strut=no,offset=0cm] {rule based learning}
+%D \framed[strut=no,offset=none] {rule based learning}
+%D \framed[strut=no,offset=overlay]{rule based learning}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \hbox{\getbuffer}
+%D \stoplinecorrection
+%D
+%D \startbuffer
+%D \framed[width=3cm,align=left] {rule\\based\\learning}
+%D \framed[width=3cm,align=middle] {rule\\based\\learning}
+%D \framed[width=3cm,align=right] {rule\\based\\learning}
+%D \framed[width=fit,align=middle] {rule\\based\\learning}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \hbox{\dontcomplain\getbuffer}
+%D \stoplinecorrection
+%D
+%D So now we're ready for the complicated stuff. We distinguish between borders with
+%D straight lines and those with round corners. When using the first alternative it
+%D is possible to turn off one or more lines. More fancy shapes are also possible by
+%D specifying dedicated backgrounds. Turning lines on and off is implemented as
+%D efficient as possible and as a result is interface language dependant. This next
+%D implementation evolved from simpler ones. It puts for instance the rules on top
+%D of the content and provides additional offset capabilities. The lot of calls to
+%D other macros makes this mechanism not that easy to comprehend.
+%D
+%D We handle left, right or middle alignment as well as fixed or free widths and
+%D heights. Each combination gets its own macro.
+%D
+%D The following code handles one-liners: \type {align={line,flushright}}. Beware,
+%D since we entered a group and either or not grab the next bgroup token, we need to
+%D finish the group in the oneliner mode.
+
+\ifdefined\raggedonelinerstate \else \newconditional\raggedonelinerstate \fi
+
+\permanent\protected\def\doformatonelinerbox % beware: assumes explicit preceding bgroup
+ {\ifconditional\raggedonelinerstate
+ \expandafter\dodoformatonelinerbox
+ \else
+ \expandafter\nodoformatonelinerbox
+ \fi}
+
+\permanent\protected\def\dodoformatonelinerbox
+ {\afterassignment\redoformatonelinerbox
+ \setbox\nextbox\hbox} % maybe \hpack
+
+\permanent\protected\def\redoformatonelinerbox
+ {\aftergroup\dododoformatonelinerbox
+ \ignorespaces}
+
+\permanent\protected\def\dododoformatonelinerbox
+ {\hpack to \hsize % was \hbox
+ {\ifcase\raggedstatus\or\hss\or\hss \fi
+ \unhbox\nextbox \removeunwantedspaces
+ \ifcase\raggedstatus\or \or\hss\or\hss\fi}%
+ \egroup}
+
+\permanent\protected\def\nodoformatonelinerbox % grabs {
+ {\let\next=}
+
+%D The handlers:
+
+% Beware, we have a \noindent so an empty line is indeed an empty line and
+% the \synchronizeinlinedirection triggers a vbox instead of a line.
+%
+% \startTEXpage[offset=0.5ex,align={lohi,middle}]
+%
+% \vbox{\hbox{x}}
+% \stopTEXpage
+%
+% \startTEXpage[offset=0.5ex,align={lohi,middle}]
+% \vbox{\hbox{x}}
+% \stopTEXpage
+
+% \def\pack_framed_forgetall{\forgetall}
+
+\def\pack_framed_set_foregroundcolor
+ {\edef\p_framed_foregroundcolor{\framedparameter\c!foregroundcolor}%
+ \ifempty\p_framed_foregroundcolor\else\dousecolorparameter\p_framed_foregroundcolor\fi}
+
+\def\pack_framed_do_setups
+ {\ifempty\p_framed_setups \else
+ \setups[\p_framed_setups]% \texsetup (or only one!)
+ % \fastsetup\p_framed_setup % singular would have been better
+ \fi}
+
+\def\pack_framed_format_format_yes
+ {\vbox to \d_framed_height
+ \bgroup
+ \let\postprocessframebox\relax
+ % \pack_framed_forgetall
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \oninterlineskip
+ \hsize\d_framed_width
+ \vsize\d_framed_height
+ \pack_framed_do_setups
+ \raggedcommand
+ \pack_framed_do_top
+ \synchronizeinlinedirection
+ \localbegstrut
+ \atendofgroup\localendstrut
+ \atendofgroup\pack_framed_do_bottom
+ \doformatonelinerbox}
+
+\def\pack_framed_format_format_nop
+ {\vbox to \d_framed_height
+ \bgroup
+ \let\postprocessframebox\relax
+ % \pack_framed_forgetall
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \oninterlineskip
+ \hsize\d_framed_width
+ \vsize\d_framed_height
+ \pack_framed_do_setups
+ \raggedcenter
+ \vss
+ \synchronizeinlinedirection
+ \localbegstrut
+ \atendofgroup\localendstrut
+ \atendofgroup\vss
+ \doformatonelinerbox}
+
+\def\pack_framed_format_format_height
+ {\vbox to \d_framed_height
+ \bgroup
+ \let\postprocessframebox\relax
+ % \pack_framed_forgetall
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \oninterlineskip
+ \pack_framed_do_setups
+ \raggedcommand
+ \vss
+ \synchronizeinlinedirection
+ \localbegstrut
+ \atendofgroup\localendstrut
+ \atendofgroup\vss
+ \doformatonelinerbox}
+
+\def\pack_framed_format_format_width
+ {\vbox
+ \bgroup
+ \let\postprocessframebox\relax
+ % \pack_framed_forgetall
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \oninterlineskip
+ \hsize\d_framed_width
+ \pack_framed_do_setups
+ \raggedcommand
+ \pack_framed_do_top
+ \synchronizeinlinedirection
+ \localbegstrut
+ \atendofgroup\localendstrut
+ \atendofgroup\pack_framed_do_bottom
+ \doformatonelinerbox}
+
+\def\pack_framed_format_format_vsize
+ {\vbox to \d_framed_height % no vpack .. maybe grid
+ \bgroup
+ \let\postprocessframebox\relax
+ % \pack_framed_forgetall
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \vsize\d_framed_height
+ \pack_framed_do_setups
+ \vss
+ \atendofgroup\vss
+ \hbox
+ \bgroup
+ \aftergroup\egroup
+ \synchronizeinlinedirection
+ \localstrut
+ \doformatonelinerbox}
+
+\def\pack_framed_format_format_hsize
+ {\hbox to \d_framed_width
+ \bgroup
+ \let\postprocessframebox\relax
+ % \pack_framed_forgetall
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \pack_framed_do_setups
+ \hss
+ \synchronizeinlinedirection
+ \localstrut
+ \atendofgroup\hss
+ \doformatonelinerbox}
+
+\def\pack_framed_format_format_no_size
+ {\hbox
+ \bgroup
+ \iftrialtypesetting \else
+ \pack_framed_set_foregroundcolor
+ \fi
+ \let\postprocessframebox\relax
+ \pack_framed_do_setups
+ \synchronizeinlinedirection
+ \localstrut
+ \doformatonelinerbox}
+
+%D On the next page we show some examples of how these macros come into action. The
+%D examples show us how \type {fit}, \type {broad} dimensions influence the
+%D formatting. Watch the visualized struts. \footnote {Here we used \type
+%D {\showstruts}.}
+%D
+%D \startpostponing
+%D \bgroup
+%D \showstruts
+%D \dontcomplain
+%D \starttabulate[|c|c|c|c|c|c|]
+%D % \HL
+%D \NC \framed[width=.2\hsize, height=.2\hsize, align=] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=broad, align=] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=fit, align=] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=.2\hsize, align=] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=broad, align=] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=fit, align=] {a\endgraf b\endgraf c}
+%D \NC \NR
+%D % \HL
+%D \NC \framed[width=.2\hsize, height=.2\hsize, align=yes] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=broad, align=yes] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=fit, align=yes] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=.2\hsize, align=yes] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=broad, align=yes] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=fit, align=yes] {a\endgraf b\endgraf c}
+%D \NC \NR
+%D % \HL
+%D \NC \framed[width=.2\hsize, height=.2\hsize, align=right] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=broad, align=right] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=fit, align=right] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=.2\hsize, align=right] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=broad, align=right] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=fit, align=right] {a\endgraf b\endgraf c}
+%D \NC \NR
+%D % \HL
+%D \NC \framed[width=.2\hsize, height=.2\hsize, align=left] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=broad, align=left] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=fit, align=left] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=.2\hsize, align=left] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=broad, align=left] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=fit, align=left] {a\endgraf b\endgraf c}
+%D \NC \NR
+%D % \HL
+%D \NC \framed[width=.2\hsize, height=.2\hsize, align=middle] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=broad, align=middle] {a\endgraf b\endgraf c}
+%D \NC \framed[width=.2\hsize, height=fit, align=middle] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=.2\hsize, align=middle] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=broad, align=middle] {a\endgraf b\endgraf c}
+%D \NC \framed[width=fit, height=fit, align=middle] {a\endgraf b\endgraf c}
+%D \NC \NR
+%D % \HL
+%D \stoptabulate
+%D \egroup
+%D \stoppostponing
+
+%D \macros
+%D {framednoflines, framedlastlength}
+%D
+%D It is possible to let the frame macro calculate the width of a centered box
+%D automatically (\type {fit}). When doing so, we need to reshape the box:
+
+\newcount\framednoflines
+\newdimen\framedfirstheight
+\newdimen\framedlastdepth
+\newdimen\framedminwidth
+\newdimen\framedmaxwidth
+\newdimen\framedaveragewidth
+
+\def\pack_framed_reshape_reset
+ {\framednoflines \zerocount
+ \framedfirstheight \zeropoint
+ \framedlastdepth \zeropoint
+ \framedminwidth \zeropoint
+ \framedmaxwidth \zeropoint
+ \framedaveragewidth\zeropoint}
+
+\def\pack_framed_reshape_process{\ifvbox\b_framed_normal\clf_doreshapeframedbox\b_framed_normal\relax\fi}
+\def\pack_framed_reshape_analyze{\ifvbox\b_framed_normal\clf_doanalyzeframedbox\b_framed_normal\relax\fi}
+
+% torture test / strange case (much depth) / method 2 needed
+%
+% \startTEXpage[frame=on]
+% \startformula \startalign \NC A \NC B \NR \intertext{test} \NC C \NC D \NR \stopalign \stopformula
+% test outside formula
+% \startformula \startalign \NC A \NC B \NR \intertext{test} \NC C \NC D \NR \stopalign \stopformula
+% \blank[big]
+% \startformula \startalign \NC \int_01 \NC B \NR \intertext{test} \NC \int_01 \NC D \NR \stopalign \stopformula
+% test outside formula
+% \startformula \startalign \NC \int_01 \NC B \NR \intertext{test} \NC \int_01 \NC D \NR \stopalign \stopformula
+% \stopTEXpage
+
+%D The examples on the next page show how one can give the frame as well as the
+%D background an additional offset and even a bit more depth. The blue outline is
+%D the frame, the red box is the background and the small black outline is the
+%D visualization of the resulting box, that is, we applied \type {\ruledhbox} to
+%D the result.
+%D
+%D \startpostponing
+%D \bgroup
+%D \unprotect
+%D \dontcomplain
+%D
+%D \startbuffer
+%D \unprotect
+%D \vbox to \vsize
+%D \bgroup
+%D \startalignment[middle]
+%D \vss
+%D \dontleavehmode\vbox to .8\vsize
+%D \bgroup
+%D \hsize=300pt
+%D \setupframed
+%D [background=color,
+%D backgroundcolorachtergrondkleur=darkred,
+%D width=300pt,
+%D height=60pt,
+%D framecolorkaderkleur=DemoBlue,
+%D rulethickness=2pt]
+%D \def\status%
+%D {backgroundoffset=\the\dimexpr\framedparameter\c!backgroundoffset\relax\\
+%D frameoffset=\the\dimexpr\framedparameter\c!frameoffset\relax\\
+%D depth=\the\dimexpr\framedparameter\c!depth\relax}
+%D \dontleavehmode \ruledhbox{\framed[backgroundoffset=0pt,frameoffset=0pt]{\status}}
+%D \vss
+%D \dontleavehmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=0pt]{\status}}
+%D \vss
+%D \dontleavehmode \ruledhbox{\framed[backgroundoffset=0pt,frameoffset=5pt]{\status}}
+%D \vss
+%D \dontleavehmode \ruledhbox{\framed[backgroundoffset=2pt,frameoffset=5pt]{\status}}
+%D \vss
+%D \dontleavehmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=2pt]{\status}}
+%D \vss
+%D \dontleavehmode \ruledhbox{\framed[backgroundoffset=5pt,frameoffset=5pt]{\status}}
+%D \egroup
+%D \vss
+%D \stopalignment
+%D \egroup
+%D \protect
+%D \stopbuffer
+%D
+%D \getbuffer \page
+%D
+%D {\setupframed[depth=4pt]\getbuffer} \page
+%D
+%D \protect
+%D \egroup
+%D \stoppostponing
+
+%D We can draw lines from left to right and top to bottom by using the normal \type
+%D {\hairline} command. Both directions need a different treatment.
+%D
+%D \startbuffer
+%D \framed[width=4cm] {alfa\hairline beta\hairline gamma}
+%D \framed[height=2cm] {alfa\hairline beta\hairline gamma}
+%D \framed[width=4cm,height=2cm]{alfa\hairline beta\hairline gamma}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlinecorrection
+%D \hbox{\getbuffer}
+%D \stoplinecorrection
+%D
+%D These macros try to adapt their behaviour as good as possible to the circumstances
+%D and act as natural as possible.
+
+\protected\def\pack_framed_vboxed_hairline % nasty overlay mess .. needed for autowidth
+ {\begingroup
+ \scratchoffset\ifconditional\c_framed_has_offset \localoffset \else \zeropoint \fi
+ \scratchwidth \dimexpr\scratchoffset+\d_framed_linewidth\relax
+ \par
+ \nointerlineskip
+ \kern\scratchoffset
+ \dontleavehmode
+ \hrule\s!height\d_framed_linewidth\s!depth\zeropoint
+ \par
+ \kern-\d_framed_linewidth
+ \dontleavehmode
+ \hpack to \zeropoint{\hss\vrule\s!height\d_framed_linewidth\s!depth\zeropoint\s!width\scratchwidth}%
+ \hfill
+ \hpack to \zeropoint{\vrule\s!height\d_framed_linewidth\s!depth\zeropoint\s!width\scratchwidth\hss}%
+ \par
+ \nointerlineskip
+ \kern\scratchoffset
+ \nointerlineskip
+ \endgraf
+ \nointerlineskip
+ \localbegstrut
+ \endgroup}
+
+\protected\def\pack_framed_hboxed_hairline % use framed dimen
+ {\bgroup
+ \scratchoffset\ifconditional\c_framed_has_offset \localoffset \else \zeropoint \fi
+ \ifconditional\c_framed_has_height
+ \dimen\scratchheight\dimexpr\localheight/\plustwo+\strutdp-\plustwo\d_framed_linewidth\relax
+ \dimen\scratchdepth \dimexpr\localheight/\plustwo-\strutdp+\plustwo\d_framed_linewidth\relax
+ \else
+ \dimen\scratchheight\dimexpr\strutht+\scratchoffset\relax
+ \dimen\scratchdepth \dimexpr\strutdp+\scratchoffset\relax
+ \fi
+ \unskip
+ \setbox\scratchbox\hpack
+ {\kern\scratchoffset
+ \vrule\s!height\dimen\scratchheight\s!depth\dimen\scratchdepth\s!width\d_framed_linewidth
+ \kern\scratchoffset}%
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox
+ \ignorespaces
+ \egroup}
+
+%D The argument of the frame command accepts \type{\\} as a sort of newline signal. In
+%D horizontal boxes it expands to a space.
+
+\protected\def\pack_framed_vboxed_newline
+ {\endgraf\ignorespaces}
+
+\protected\def\pack_framed_hboxed_newline
+ {\unskip\normalspace\ignorespaces}
+
+%D We can set each rule on or off. The default setting is inherited from
+%D \type {frame}. An earlier implementation use a bit different approach, but the new
+%D one seems more natural:
+%D
+%D \bgroup
+%D \setuptyping[margin=0pt]
+%D \startlinecorrection
+%D \startbuffer
+%D \framed[offset=overlay,frame=on]{\darkred\blackrule}
+%D \stopbuffer
+%D \hbox{\getbuffer\vbox{\typebuffer}}
+%D
+%D \startbuffer
+%D \framed[offset=overlay,frame=on,bottomframe=off]{\darkred\blackrule}
+%D \stopbuffer
+%D \hbox{\getbuffer\vbox{\typebuffer}}
+%D
+%D \startbuffer
+%D \framed[offset=overlay,frame=on,bottomframe=on]{\darkred\blackrule}
+%D \stopbuffer
+%D \hbox{\getbuffer\vbox{\typebuffer}}
+%D
+%D \startbuffer
+%D \framed[offset=overlay,frame=off]{\darkred\blackrule}
+%D \stopbuffer
+%D \hbox{\getbuffer\vbox{\typebuffer}}
+%D
+%D \startbuffer
+%D \framed[offset=overlay,frame=off,bottomframe=off]{\darkred\blackrule}
+%D \stopbuffer
+%D \hbox{\getbuffer\vbox{\typebuffer}}
+%D
+%D \startbuffer
+%D \framed[offset=overlay,frame=off,bottomframe=on]{\darkred\blackrule}
+%D \stopbuffer
+%D \hbox{\getbuffer\vbox{\typebuffer}}
+%D \stoplinecorrection
+%D \egroup
+
+%D \macros
+%D {startframedtext, setupframedtexts, defineframedtext}
+%D
+%D The general framing command we discussed previously, is not entirely suited for
+%D what we call framed texts, as for instance used in intermezzo's. The next
+%D examples show what we have in mind.
+%D
+%D \startbuffer[framed-0]
+%D \setupframedtexts
+%D [frame=off,
+%D width=\hsize,
+%D background=screen]
+%D
+%D \startframedtext
+%D By default the framed text is centered \dots
+%D \stopframedtext
+%D
+%D \startframedtext[right]
+%D \dots\ but we can also align left, middle and right.
+%D \stopframedtext
+%D \stopbuffer
+%D
+%D \startbuffer[framed-1]
+%D \defineframedtext
+%D [Example]
+%D [width=6cm,
+%D height=5cm]
+%D
+%D \startExample
+%D \typebuffer[framed-1]
+%D \stopExample
+%D \stopbuffer
+%D
+%D \startbuffer[framed-2]
+%D \defineframedtext
+%D [Example]
+%D [width=6cm]
+%D
+%D \startExample
+%D \typebuffer[framed-2]
+%D \stopExample
+%D \stopbuffer
+%D
+%D \startbuffer[framed-3]
+%D \defineframedtext
+%D [Example]
+%D [height=5cm]
+%D
+%D \startExample
+%D \typebuffer[framed-3]
+%D \stopExample
+%D \stopbuffer
+%D
+%D \startbuffer[framed-4]
+%D \defineframedtext
+%D [Example]
+%D [width=fit,height=broad]
+%D
+%D \Example{a very exciting example}
+%D \stopbuffer
+%D
+%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-0] \egroup
+%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-1] \egroup
+%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-2] \egroup
+%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-3] \egroup
+%D \bgroup \setuptyping[margin=0pt] \getbuffer[framed-4] \egroup
+%D
+%D Here we can see that we have a predefined framed text class as well as the
+%D tools for defining our own. So we have:
+%D
+%D \showsetup{setupframedtexts}
+%D
+%D as well as the definition command:
+%D
+%D \showsetup{defineframedtext}
+%D
+%D that generates two commands:
+%D
+%D \showsetup{start<<framedtext>>}
+%D \showsetup{<<framedtext>>}
+%D
+%D The next definition shows the defaults.
+
+\installcorenamespace{framedtext}
+\installcorenamespace{framedtextlocation}
+
+\installframedcommandhandler \??framedtext {framedtext} \??framedtext
+
+\let\setupframedtexts\setupframedtext
+
+\setupframedtext
+ [\c!width=.75\hsize,
+ \c!height=\v!fit,
+ \c!align=\v!yes,
+ %\c!top=,
+ \c!bottom=\vfill,
+ \c!offset=1em,
+ %\c!bodyfont=,
+ %\c!style=,
+ %\c!color=,
+ %\c!left=,
+ \c!right=\hfill,
+ \c!before=\blank,
+ \c!after=\blank,
+ %\c!inner=,
+ \c!frame=\v!on,
+ %\c!topframe=,
+ %\c!bottomframe=,
+ %\c!leftframe=,
+ %\c!rightframe=,
+ \c!radius=.5\bodyfontsize,
+ \c!corner=\v!rectangular,
+ %\c!orientation=,
+ %\c!indenting=,
+ %\c!foregroundcolor=,
+ %\c!foregroundstyle=,
+ %\c!background=,
+ %\c!backgroundcolor=,
+ \c!linecorrection=\v!on,
+ \c!depthcorrection=\v!on,
+ \c!margin=\v!standard]
+
+\appendtoks
+ \frozen\instance\protected\edefcsname\e!start\currentframedtext\endcsname{\pack_framed_text_start [\currentframedtext]}%
+ \frozen\instance\protected\edefcsname\e!stop \currentframedtext\endcsname{\pack_framed_text_stop }%
+ \frozen\instance\protected\edefcsname \currentframedtext\endcsname{\pack_framed_text_direct[\currentframedtext]}%
+\to \everydefineframedtext
+
+\setvalue{\??framedtextlocation\v!left }{\letframedtextparameter\c!left \relax
+ \letframedtextparameter\c!right\hfill}
+
+\setvalue{\??framedtextlocation\v!right }{\letframedtextparameter\c!left \hfill
+ \letframedtextparameter\c!right\relax}
+
+\setvalue{\??framedtextlocation\v!middle}{\letframedtextparameter\c!left \hfill
+ \letframedtextparameter\c!right\hfill}
+
+\setvalue{\??framedtextlocation\v!none }{\letframedtextparameter\c!left \relax
+ \letframedtextparameter\c!right\relax
+ \settrue\c_framed_text_location_none}
+
+\tolerant\protected\def\pack_framed_text_start[#1]#*[#2]#*[#3]% or #1#*[#2]#*[#3]% and pass {#1}
+ {\bgroup
+ \edef\currentframedtext{#1}
+ \doifelseassignment{#2}
+ {\pack_framed_text_start_continue\empty{#2}}
+ {\pack_framed_text_start_continue{#2}{#3}}}
+
+% todo: sort out first/lastline ht/dp
+
+\def\pack_framed_text_start_continue#1#2%
+ {\setupframedtexts[\currentframedtext][#2]%
+ \doifsomething{#1}{\setframedtextparameter\c!location{#1}}% does not listen to #3
+ \setfalse\c_framed_text_location_none
+ \csname\??framedtextlocation\framedtextparameter\c!location\endcsname
+ \resetframedtextparameter\c!location
+ \pack_framed_text_check
+ \setbox\b_framed_normal\vbox % \vpack
+ \startboxedcontent
+ \hsize\localhsize
+ % \insidefloattrue % ? better
+ \usebodyfontparameter\framedtextparameter
+ % \edef\p_framed_text_strut{\letframedtextparameter\c!strut}% to be used
+ \letframedtextparameter\c!strut\v!no
+ \inheritedframedtextframed\bgroup
+ \enforced\let\\\endgraf
+ \edef\p_framed_text_depthcorrection{\framedtextparameter\c!depthcorrection}%
+ \ifx\p_framed_text_depthcorrection\v!on
+ \pack_framed_text_start_depth_correction
+ \else
+ \bgroup
+ \fi
+ \vskip-\strutdp % brrr why is this needed ... needs to be sorted out, see testcase 1
+ \doinhibitblank
+ \useindentingparameter\framedtextparameter
+ \useframedtextstyleandcolor\c!style\c!color
+ \framedtextparameter\c!inner
+ \ignorespaces}
+
+% testcase 1:
+%
+% \showstruts
+% \startframedtext[align={normal,tolerant},offset=0pt] \input tufte \stopframedtext
+% \startframedtext[align={normal,tolerant},offset=0pt,depthcorrection=off] \input tufte \stopframedtext
+% \startframedtext[align={normal,tolerant},offset=0pt,depthcorrection=off] \inframed{x} \stopframedtext
+% \framed[align={normal,tolerant},offset=0pt]{\input tufte }
+
+%D The \type {none} option is handy for nested usage, as in the presentation
+%D styles, where we don't want interference.
+
+\defineplacement[\??framedtext][\s!parent=\??framedtext\currentframedtext]
+
+\protected\def\pack_framed_text_stop % no \baselinecorrection, see faq docs
+ {\endgraf
+ \removelastskip
+ \ifx\p_framed_text_depthcorrection\v!on
+ \pack_framed_text_stop_depth_correction
+ \else
+ \egroup
+ \fi
+ \stopboxedcontent
+ \ifconditional\c_framed_text_location_none
+ \egroup
+ \box\b_framed_normal
+ \orelse\ifinsidefloat
+ \egroup
+ \box\b_framed_normal
+ \else
+ \egroup
+ \placement[\??framedtext][\c!depthcorrection=\v!off]{\box\b_framed_normal}%
+ \fi
+ \egroup}
+
+%D We define the general (and original) case by just saying:
+
+\def\pack_framed_text_check % messy dependency
+ {\localhsize\hsize
+ \ifinsidefloat \orelse \ifdim\d_page_sides_vsize>\zeropoint % also possible: \c_page_sides_checks_done>\zeropoint
+ % \strut % rather clean way to invoke the sidefloat OTR
+ % \setbox0=\lastbox % and get the widths set, so from now on we
+ % \setlocalhsize % can have framed texts alongside sidefloats
+ \checksidefloat
+ \setlocalhsize
+ \fi}
+
+\def\pack_framed_text_start_depth_correction
+ {\bgroup
+ \ifhmode
+ \par
+ \fi
+ \ifvmode
+ \verticalstrut
+ % we need \nowhitespace in case of setups setting whitespace
+ % nb, not safe, text vs \vbox as next
+ \vskip-\struttotal
+ \nowhitespace
+ \fi} % na vskip ! new 20/05/2004, fails with next content being box (\scale{..})
+
+\def\pack_framed_text_stop_depth_correction
+ {\ifhmode
+ \par
+ \fi
+ \ifvmode
+ \forgetall
+ \vskip-\struttotal
+ \verticalstrut
+ \egroup
+ \forgetall % brrr too often
+ \vskip-\lineheight
+ \verticalstrut
+ \else
+ \egroup
+ \fi}
+
+%D Placement can be ignored:
+%D
+%D \starttyping
+%D \hbox to \hsize \bgroup
+%D \startframedtext[none][width=.5\textwidth] \input tufte \stopframedtext
+%D \startframedtext[none][width=.5\textwidth] \input zapf \stopframedtext
+%D \egroup
+%D
+%D \hbox to \hsize \bgroup
+%D \setupframedtexts[location=none]%
+%D \startframedtext[width=.5\textwidth] \input zapf \stopframedtext
+%D \startframedtext[width=.5\textwidth] \input tufte \stopframedtext
+%D \egroup
+%D \stoptyping
+
+%D The simple brace (or group) delimited case is typeset slightly different
+%D and is not aligned.
+
+\let\pack_framed_strut\relax
+
+\tolerant\protected\def\pack_framed_text_direct[#1]#*[#2]%
+ {\bgroup
+ \edef\currentframedtext{#1}%
+ \usebodyfontparameter\framedtextparameter
+ \setupcurrentframedtext[#2]%
+ \edef\p_framed_text_strut{\framedtextparameter\c!strut}%
+ \letframedtextparameter\c!strut\v!no
+ \inheritedframedtextframed\bgroup
+ \blank[\v!disable]%
+ \enforced\let\\\endgraf
+ \useframedtextstyleandcolor\c!style\c!color
+ \vskip-\strutdp % brrr why is this needed ... needs to be sorted out, see testcase 1
+ \framedtextparameter\c!inner
+ \ifx\p_framed_text_strut\v!no
+ \let\pack_framed_strut\relax
+ \else
+ \let\pack_framed_strut\strut
+ \fi
+ \bgroup
+ \aftergroup\pack_framed_text_stop_direct
+ \afterassignment\ignorespaces
+ \afterassignment\pack_framed_strut
+ \let\next=}
+
+\def\pack_framed_text_stop_direct
+ {\removelastskip
+ \egroup
+ \egroup}
+
+\defineframedtext
+ [\v!framedtext]
+
+%D \macros
+%D {defineframed}
+%D
+%D One can also define simple framed texts, using:
+%D
+%D \showsetup{defineframed}
+%D
+%D As suggested by Wolfgang we can now use the new \MKIV\ inheritance model instead
+%D of passing a combination of arguments. This also also simplified the \type
+%D {\setupframed} command. There are certainly more places where such improvements
+%D can be made.
+
+\appendtoks
+ \ifcsname\??regularframedlevel\currentframed\endcsname
+ % already defined, keeps settings
+ \else
+ \expandafter\newcount\csname\??regularframedlevel\currentframed\endcsname
+ \fi
+\to \everypresetframed
+
+\appendtoks
+ \frozen\instance\setuevalue{\currentframed}{\pack_framed_defined_process[\currentframed]}%
+\to \everydefineframed
+
+\newcount\c_temp_framed_crap
+
+% \permanent\tolerant\protected\def\pack_framed_defined_process[#1]#*[#2]% official (not much checking, todo: parent)
+% {\bgroup
+% \ifcsname\??regularframedlevel#1\endcsname
+% %\expandafter\let\expandafter\c_pack_framed_temp\csname\??regularframedlevel#1\endcsname
+% \expandafter\let\expandafter\c_pack_framed_temp\lastnamedcs
+% \else
+% \let\c_pack_framed_temp\c_temp_framed_crap
+% \fi
+% \advance\c_pack_framed_temp\plusone
+% \defcsname\??framed#1>\the\c_pack_framed_temp:\s!parent\endcsname{\??framed#1}% \inheritlocalframed
+% \bgroup
+% \edef\currentframed{#1>\the\c_pack_framed_temp}%
+% \pack_framed_initialize
+% \setupcurrentframed[#2]% here !
+% \pack_framed_process_indeed}
+
+\permanent\tolerant\protected\def\pack_framed_defined_process[#1]#*[#2]% official (not much checking, todo: parent)
+ {\bgroup
+ \ifcsname\??regularframedlevel#1\endcsname
+ %\expandafter\let\expandafter\c_pack_framed_temp\csname\??regularframedlevel#1\endcsname
+ \scratchcounter\lastnamedcs
+ \else
+ \scratchcounter\c_temp_framed_crap
+ \fi
+ \advance\scratchcounter\plusone
+ \defcsname\??framed#1>\the\scratchcounter:\s!parent\endcsname{\??framed#1}% \inheritlocalframed
+ \bgroup
+ \edef\currentframed{#1>\the\scratchcounter}%
+ \pack_framed_initialize
+ \setupcurrentframed[#2]% here !
+ \pack_framed_process_indeed}
+
+\aliased\let\placeframed\pack_framed_defined_process % new per 2012/04/23
+
+%D We can do:
+%D
+%D \starttyping
+%D \defineframed[\v!framed]
+%D \stoptyping
+%D
+%D but the existing one is ok as well (less csname messy too).
+
+%D New, for the moment private; let's see when GB finds out about this one and its
+%D obscure usage. It's used in:
+%D
+%D \startbuffer
+%D \defineframedtext
+%D [tabulateframe]
+%D [offset=overlay,
+%D backgroundoffset=3pt,
+%D background=color,
+%D backgroundcolor=green]
+%D
+%D \setuptabulate
+%D [tabulate]
+%D [frame=tabulateframe]
+%D
+%D \setuptables
+%D [frame=tabulateframe]
+%D
+%D \input tufte
+%D
+%D \starttabulate[|l|l|]
+%D \NC test \NC test \NC \NR \NC test \NC test \NC \NR
+%D \NC test \NC test \NC \NR \NC test \NC test \NC \NR
+%D \stoptabulate
+%D
+%D \input tufte
+%D
+%D \starttable[|l|l|]
+%D \NC test \NC test \NC \AR \NC test \NC test \NC \AR
+%D \NC test \NC test \NC \AR \NC test \NC test \NC \AR
+%D \stoptable
+%D \stopbuffer
+%D
+%D \typebuffer
+
+\installcorenamespace{framedcontent}
+
+\installframedcommandhandler \??framedcontent {framedcontent} \??framedcontent
+
+\setupframedcontent
+ [\c!leftoffset=\zeropoint,
+ %\c!rightoffset=\framedcontentparameter\c!leftoffset,
+ \c!rightoffset=\scratchleftoffset,
+ \c!topoffset=\zeropoint,
+ %\c!bottomoffset=\framedcontentparameter\c!topoffset,
+ \c!bottomoffset=\scratchtopoffset,
+ \c!strut=\v!no,
+ %\c!linecorrection=\v!no,
+ %\c!left=,
+ %\c!right=,
+ %\c!width=\v!fit,
+ \c!offset=\v!overlay]
+
+\permanent\let\stopframedcontent\relax
+
+\permanent\tolerant\protected\def\startframedcontent[#1]%
+ {\bgroup
+ \edef\currentframedcontent{#1}%
+ \ifx\currentframedcontent\v!off
+ \enforced\let\stopframedcontent\egroup
+ \else
+ \checkframedcontentparent
+ \enforced\let\stopframedcontent\pack_framed_stop_content_indeed
+ \expandafter\pack_framed_start_content_indeed
+ \fi}
+
+\def\pack_framed_start_content_indeed
+ {\setbox\b_framed_normal\hpack\bgroup
+ \setlocalhsize
+ \hsize\localhsize
+ \scratchleftoffset \framedcontentparameter\c!leftoffset \relax
+ \scratchrightoffset \framedcontentparameter\c!rightoffset \relax
+ \scratchtopoffset \framedcontentparameter\c!topoffset \relax
+ \scratchbottomoffset\framedcontentparameter\c!bottomoffset\relax
+ \advance\hsize\dimexpr-\scratchleftoffset-\scratchrightoffset \relax
+ \advance\vsize\dimexpr-\scratchtopoffset -\scratchbottomoffset\relax
+ \kern\scratchleftoffset
+ \vpack\bgroup
+ \vskip\scratchtopoffset
+ \vbox\bgroup
+ \forgetall
+ \blank[\v!disable]}
+
+\protected\def\pack_framed_stop_content_indeed
+ {\removelastskip
+ \egroup
+ \vskip\scratchbottomoffset
+ \egroup
+ \kern\scratchrightoffset
+ \egroup
+ \doif{\framedcontentparameter\c!width}\v!fit
+ {\letframedcontentparameter\c!width\v!fixed}% no shapebox
+ \ifinsidefloat
+ \donefalse
+ \else
+ \doifelse{\framedcontentparameter\c!linecorrection}\v!yes\donetrue\donefalse
+ \fi
+ % plaats ?
+ \ifdone\startlinecorrection\fi
+ \framedcontentparameter\c!left % new
+ \inheritedframedcontentframed{\box\b_framed_normal}% hm
+ \framedcontentparameter\c!right % new
+ \ifdone\stoplinecorrection\fi
+ \egroup}
+
+% A shared setting.
+
+\setuplinewidth
+ [\v!medium]
+
+%D A Goodie:
+
+\defineframed
+ [\v!unframed]
+ [\c!frame=\v!off,
+ \c!rulethickness=\zeropoint,
+ \c!foregroundstyle=\framedparameter\c!style,
+ \c!foregroundcolor=\framedparameter\c!color]
+
+%D Bonus (as defined in \type {pack-rul.lua}):
+%D
+%D \starttyping
+%D \setbox\scratchbox\vbox{a\par aa\par aaa\par}
+%D \the\dimexpr\themaxboxwidth\scratchbox\relax
+%D \stoptyping
+
+% \let\themaxboxwidth % defined at the lua end
+
+%D Fast enough for most cases (we could do a massive \type {\ifhastok} instead):
+
+\permanent\protected\def\doifelseframed#1%
+ {\iftok{#1\c!frame }{\v!on}\expandafter\firstoftwoarguments
+ \orelse\iftok{#1\c!topframe }{\v!on}\expandafter\firstoftwoarguments
+ \orelse\iftok{#1\c!bottomframe}{\v!on}\expandafter\firstoftwoarguments
+ \orelse\iftok{#1\c!leftframe }{\v!on}\expandafter\firstoftwoarguments
+ \orelse\iftok{#1\c!rightframe }{\v!on}\expandafter\firstoftwoarguments
+ \orelse\iftok{#1\c!background } {}\expandafter\secondoftwoarguments
+ \else \expandafter\firstoftwoarguments\fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-app.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-app.mkxl
new file mode 100644
index 00000000000..d75366cca0d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-app.mkxl
@@ -0,0 +1,205 @@
+%D \module
+%D [ file=page-app, % from meta-fig
+%D version=1998.01.15,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Independent page building,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Applications}
+
+%D This needs an update with a proper define and inheritance.
+
+\unprotect
+
+\installcorenamespace{fittingpage}
+
+\installframedcommandhandler \??fittingpage {fittingpage} \??fittingpage
+
+\newdimen\d_page_fitting_width
+\newdimen\d_page_fitting_height
+\newbox \b_page_fitting
+
+\definepapersize
+ [fittingpage]
+ [\c!width=\d_page_fitting_width,
+ \c!height=\d_page_fitting_height]
+
+\definemakeup
+ [fittingpage]
+ [\c!textstate=\v!empty,
+ \c!doublesided=\v!no,
+ \c!location=\v!top, % no topskip
+ \c!pagestate=\fittingpageparameter\c!pagestate]%
+
+\definelayout
+ [fittingpage]
+ [\v!page]
+ [\c!width=\d_page_fitting_width,
+ \c!height=\d_page_fitting_height,
+ \c!location=\v!middle]
+
+\appendtoks
+ \frozen\instance\protected\edefcsname\e!start\currentfittingpage\endcsname{\startfittingpage[\currentfittingpage]}%
+ \frozen\instance\protected\edefcsname\e!stop \currentfittingpage\endcsname{\stopfittingpage}%
+\to \everydefinefittingpage
+
+\permanent\tolerant\protected\def\startfittingpage[#1]#*[#2]%
+ {\page % this is kind of tricky! there can be preceding page refs
+ \autostarttext
+ \bgroup % resulting in a zero height page; test fig-make !
+ \setupinterlinespace[\v!top=0]% no topskip
+ \def\currentfittingpage{#1}%
+ \dontcomplain
+ % runs under current page regime, i.e. page variables passed to mp
+ \setupcurrentfittingpage[\c!paper=,#2]% auto or size or nothing
+ \doifelsenothing{\fittingpageparameter\c!command}
+ \page_fitting_start_normal
+ \page_fitting_start_command}
+
+\def\page_fitting_start_command
+ {\grabuntil{\e!stop\currentfittingpage}\page_fitting_stop_command}
+
+\def\page_fitting_stop_command#1%
+ {\page_fitting_start_normal
+ \fittingpageparameter\c!command{#1}%
+ \page_fitting_stop_normal}
+
+\def\page_fitting_start_normal
+ {\setbox\b_page_fitting\hbox
+ \bgroup
+ \inheritedfittingpageframed
+ \bgroup
+ \gobblespacetokens}
+
+\protected\def\page_fitting_stop_normal % todo: figure out why a small mp image gets shifted
+ {\removeunwantedspaces
+ \egroup
+ \egroup
+ % finalize
+ \doif{\fittingpageparameter\c!margin}\v!page
+ {\setbox\b_page_fitting\hpack\bgroup
+ \offsetbox
+ [\c!leftoffset=\backspace,
+ \c!rightoffset=\cutspace,
+ \c!topoffset=\topspace,
+ \c!bottomoffset=\bottomspace]
+ {\box\b_page_fitting}%
+ \egroup}%
+ \doifsomething{\fittingpageparameter\c!scale}
+ {\setbox\b_page_fitting\hpack\bgroup
+ \scale[\c!scale=\fittingpageparameter\c!scale]{\box\b_page_fitting}%
+ \egroup}%
+ \ifdim\ht\b_page_fitting=\zeropoint
+ \ht\b_page_fitting\onepoint
+ \fi
+ \ifdim\wd\b_page_fitting=\zeropoint
+ \wd\b_page_fitting\onepoint
+ \fi
+ \d_page_fitting_width \wd\b_page_fitting
+ \d_page_fitting_height\ht\b_page_fitting
+ \startlocallayout % hm, we change the papersize so we still need it
+ \enforced\let\checkcurrentlayout\relax % else interference with odd/even layout
+ \processaction
+ [\fittingpageparameter\c!paper]
+ [ \v!auto=>\let\scratchstringone\printpapersize,
+ \s!unknown=>\let\scratchstringone\commalistelement,
+ \s!default=>\def\scratchstringone{fittingpage}]%
+ \normalexpanded{\setuppapersize[fittingpage][\scratchstringone]}%
+ \startmakeup[fittingpage]%
+ \box\b_page_fitting
+ \stopmakeup
+ \stoplocallayout % we need to get rid of this and use the built-in
+ \egroup
+ \autostoptext}
+
+\permanent\let\stopfittingpage\page_fitting_stop_normal
+
+\setupfittingpage
+ [%\c!scale=1000,
+ %\c!pagestate=,
+ \c!strut=\v!no,
+ \c!align=\v!normal,
+ \c!offset=\v!overlay,
+ \c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!frame=\v!off]
+
+%D \TEX\ pages (for \METAPOST\ pages, see \type {meta-fig}):
+
+%D \starttyping
+%D \startTEXpage[align=normal,width=900pt,autowidth=force]
+%D \framed[width=800pt,frame=off]{\number\dimexpr800pt}\par
+%D \input tufte\par
+%D \stopTEXpage
+%D \stoptyping
+%D
+%D Beware: the width is the textwidth!
+
+\definefittingpage
+ [TEXpage]
+ [\c!align=\v!normal] % needed, else problems !
+
+\permanent\protected\def\setupTEXpage
+ {\setupfittingpage[TEXpage]}
+
+%D For Mojca:
+%D
+%D \starttyping
+%D \startTEXstream \chapter{Bla} Hello world!\crlf Hello world! \stopTEXstream
+%D \startTEXpage \chapter{Bla} Hello world!\crlf Hello world! \stopTEXpage
+%D \stoptyping
+%D
+%D maybe I should support stream=yes in framed.
+
+\permanent\tolerant\protected\def\startTEXstream[#1]% old code, to be redone
+ {\page
+ \enableoutputstream[tex]}
+
+\permanent\protected\def\stopTEXstream
+ {\disableoutputstream
+ \startTEXpage % [\c!align=\v!normal]
+ \outputstreamunvbox[tex]%
+ \stopTEXpage}
+
+%D Application pages, a quick \MKIV\ hack:
+
+\definetypesetting[TEXapplication]
+\definebuffer[TEXapplication]
+
+% we could use a counter and saves runs on numbering them.
+
+\permanent\def\TEXapplicationfilename{\jobname-texapplication.tex}
+
+\permanent\protected\def\stopTEXapplication
+ {\savebuffer[\thedefinedbuffer{TEXapplication}][\TEXapplicationfilename]%
+ \typesetfile[TEXapplication][\TEXapplicationfilename]\relax}
+
+%D \macros
+%D {startpagefigure}
+%D
+%D \starttyping
+%D \starttext \pagefigure[two.1] \stoptext
+%D \stoptyping
+
+\defineexternalfigure[\v!page:\v!figure][\c!offset=\v!overlay] % we force a parent
+
+\permanent\tolerant\protected\def\startpagefigure[#1]#*[#2]%
+ {\bgroup
+ \setupexternalfigure[\v!page:\v!figure][\c!offset=\v!overlay,#2]%
+ \startTEXpage[\c!offset=\namedexternalfigureparameter{\v!page:\v!figure}\c!offset,\c!align=\v!normal]%
+ \externalfigure[#1]\ignorespaces} % so we can put some text below the graphic
+
+\permanent\protected\def\stoppagefigure
+ {\stopTEXpage
+ \egroup}
+
+\permanent\tolerant\protected\def\pagefigure[#1]#*[#2]%
+ {\startpagefigure[#1][#2]\stoppagefigure}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-bck.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-bck.mkxl
new file mode 100644
index 00000000000..5683ba2505e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-bck.mkxl
@@ -0,0 +1,708 @@
+%D \module
+%D [ file=page-bck, % copied from main-001
+%D version=1997.03.31,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Backgrounds,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% Currently the text cells are fakes and no (foreground) frames which makes sense
+% as order might matter e.g. is text sticks in other cells. The page, text etc
+% areas do support foreground order change.
+
+\writestatus{loading}{ConTeXt Page Macros / Backgrounds}
+
+\unprotect
+
+% maybe use \currentframedhash here
+
+%D For special purposes, users can question the \type {*background} mode. This mode
+%D is only available when typesetting the pagebody.
+%D
+%D \starttyping
+%D \startmode[*background] ...
+%D \stoptyping
+
+\newconditional\c_page_backgrounds_new
+\newconditional\c_page_backgrounds_new_right
+\newconditional\c_page_backgrounds_new_left
+\newconditional\c_page_backgrounds_some
+
+\appendtoks
+ \ifconditional\c_page_backgrounds_some
+ \ifconditional\c_page_backgrounds_new
+ \setsystemmode\v!background
+ \fi
+ \fi
+\to \everybeforepagebody
+
+\permanent\protected\def\initializepagebackgrounds
+ {\setfalse\c_page_backgrounds_new
+ \setfalse\c_page_backgrounds_new_right
+ \setfalse\c_page_backgrounds_new_left
+ \setfalse\c_page_backgrounds_some}
+
+%D We keep calculations and checks to a minimum and also try to minimize
+%D the amount of tracing due to expansion.
+
+\mutable\let\currentotrbackground\empty
+
+%D This is the only spot where we have a low level dependency on the way
+%D parent chains are defined but we want the speed.
+
+\protected\def\page_backgrounds_check_background
+ {\ifcsname\??framed\currentotrbackground:\c!background\endcsname
+ %\edef\page_background_temp{\csname\??framed\currentotrbackground:\c!background\endcsname}%
+ \edef\page_background_temp{\lastnamedcs}%
+ \ifempty\page_background_temp
+ \expandafter\expandafter\expandafter\page_backgrounds_check_frame
+ \else
+ \page_backgrounds_set_yes
+ \fi
+ \else
+ \expandafter\page_backgrounds_check_frame
+ \fi}
+
+\def\page_backgrounds_check_frame
+ {\ifcsname\??framed\currentotrbackground:\c!frame\endcsname
+ %\edef\page_background_temp{\csname\??framed\currentotrbackground:\c!frame\endcsname}%
+ \edef\page_background_temp{\lastnamedcs}%
+ \ifx\page_background_temp\v!on
+ \page_backgrounds_set_yes
+ \else
+ \expandafter\expandafter\expandafter\page_backgrounds_check_leftframe
+ \fi
+ \else
+ \expandafter\page_backgrounds_check_leftframe
+ \fi}
+
+\def\page_backgrounds_check_leftframe
+ {\ifcsname\??framed\currentotrbackground:\c!leftframe\endcsname
+ %\edef\page_background_temp{\csname\??framed\currentotrbackground:\c!leftframe\endcsname}%
+ \edef\page_background_temp{\lastnamedcs}%
+ \ifx\page_background_temp\v!on
+ \page_backgrounds_set_yes
+ \else
+ \expandafter\expandafter\expandafter\page_backgrounds_check_rightframe
+ \fi
+ \else
+ \expandafter\page_backgrounds_check_rightframe
+ \fi}
+
+\def\page_backgrounds_check_rightframe
+ {\ifcsname\??framed\currentotrbackground:\c!rightframe\endcsname
+ %\edef\page_background_temp{\csname\??framed\currentotrbackground:\c!rightframe\endcsname}%
+ \edef\page_background_temp{\lastnamedcs}%
+ \ifx\page_background_temp\v!on
+ \page_backgrounds_set_yes
+ \else
+ \expandafter\expandafter\expandafter\page_backgrounds_check_topframe
+ \fi
+ \else
+ \expandafter\page_backgrounds_check_topframe
+ \fi}
+
+\def\page_backgrounds_check_topframe
+ {\ifcsname\??framed\currentotrbackground:\c!topframe\endcsname
+ %\edef\page_background_temp{\csname\??framed\currentotrbackground:\c!topframe\endcsname}%
+ \edef\page_background_temp{\lastnamedcs}%
+ \ifx\page_background_temp\v!on
+ \page_backgrounds_set_yes
+ \else
+ \expandafter\expandafter\expandafter\page_backgrounds_check_bottomframe
+ \fi
+ \else
+ \expandafter\page_backgrounds_check_bottomframe
+ \fi}
+
+\def\page_backgrounds_check_bottomframe
+ {\ifcsname\??framed\currentotrbackground:\c!bottomframe\endcsname
+ %\edef\page_background_temp{\csname\??framed\currentotrbackground:\c!bottomframe\endcsname}%
+ \edef\page_background_temp{\lastnamedcs}%
+ \ifx\page_background_temp\v!on
+ \page_backgrounds_set_yes
+ \else
+ \page_backgrounds_set_nop
+ \fi
+ \else
+ \page_backgrounds_set_nop
+ \fi}
+
+%D We don't use the commandhandler code as we want these multitude of backgrounds to be
+%D as fast as possible.
+
+\installcorenamespace{layoutbackgrounds}
+\installcorenamespace{layoutbackgroundcheck} % we need another hash as \??layoutbackgrounds<...> gets defined
+
+\def\page_backgrounds_set_yes{\letcsname\currentotrbackground\endcsname\relax }
+\def\page_backgrounds_set_nop{\letcsname\currentotrbackground\endcsname\undefined}
+
+\protected\def\page_backgrounds_check#1%
+ {\edef\currentotrbackground{\??layoutbackgrounds#1}%
+ \page_backgrounds_check_background}
+
+\permanent\def\doifelsesomebackground#1%
+ {\ifcsname\??layoutbackgrounds#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifsomebackgroundelse\doifelsesomebackground
+
+\permanent\def\doifsomebackground#1%
+ {\ifcsname\??layoutbackgrounds#1\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+%D The background mechanism falls back on the \type {\framed} macro. This means
+%D that all normal frame and overlay features can be used.
+
+\def\page_backgrounds_add_to_box#1% area
+ {\ifcsname\??layoutbackgrounds#1\endcsname
+ \expandafter\page_backgrounds_add_to_box_indeed
+ \else
+ \expandafter\gobblefourarguments
+ \fi#1}
+
+%D We don't need the dimensions here as this is a real framed but the question is:
+%D do we indeed need a real framed or can we use a fake (i.e. no foreground, only
+%D for hidden).
+
+\def\page_backgrounds_add_to_box_indeed#1#2#3#4% area box width height / always non zero
+ {\edef\p_page_backgrounds_setups{\namedframedparameter{\??layoutbackgrounds#1}\c!setups}%
+ \ifempty\p_page_backgrounds_setups \else
+ \doprocesslocalsetups\p_page_backgrounds_setups % should not produce funny spaces !
+ \fi
+ % #2 has the right dimensions already
+ \setbox#2\hpack{\localbackgroundframed{\??layoutbackgrounds#1}#1#2}}% a real framed (including foreground)
+
+
+%D There are quite some backgrounds. At the bottom layer, there is the {\em paper}
+%D background. This one is only used for special purposes, like annotations to
+%D documents.
+
+\def\page_backgrounds_add_to_print#1%
+ {\page_backgrounds_add_to_box\v!paper#1\printpaperwidth\printpaperheight}
+
+%D The page backgrounds can be put behind the {\em left page}, the {\em right page}
+%D or {\em each page}. As with the paper background, these are calculated on each
+%D page.
+
+\def\page_backgrounds_add_to_paper#1%
+ {\doifbothsidesoverruled
+ {\page_backgrounds_add_to_box\v!rightpage#1\paperwidth\paperheight}%
+ {\page_backgrounds_add_to_box\v!rightpage#1\paperwidth\paperheight}%
+ {\page_backgrounds_add_to_box\v!leftpage #1\paperwidth\paperheight}%
+ \page_backgrounds_add_to_box\v!page#1\paperwidth\paperheight}
+
+%D Then there are the 25 areas that make up the layout: {\em top, header, text,
+%D footer, bottom} times {\em left edge, left margin, text, right margin, right
+%D edge}. These are only recalculated when they change or when the \type {status}
+%D is set to \type {repeat}.
+
+\newbox\leftbackground % todo: rename
+\newbox\rightbackground % todo: rename
+
+%D Finaly there is an aditional {\em text} background, again useful for special
+%D purposes only. This one is calculated each time. The hidden backgrounds are not
+%D meant for users!
+
+\newconditional\c_page_backgrounds_hidden_enabled
+
+\def\page_backgrounds_add_to_text#1%
+ {\ifconditional\c_page_backgrounds_hidden_enabled
+ \page_backgrounds_add_to_box\v!hidden#1\makeupwidth\textheight % mine !
+ \fi
+ \page_backgrounds_add_to_box\v!text#1\makeupwidth\textheight}
+
+%D The next couple of macros implement the area backgrounds. As said, these are
+%D cached in dedicated boxes. The offsets and depth of the page are used for
+%D alignment purposes.
+
+%newdimen\pageoffset % bleed
+%newdimen\pagedepth % built-in
+
+%D We need a bit more clever mechanism in order to handle layers well. This means
+%D that we cannot calculate both background at the same time since something may
+%D have changed halfway a page.
+
+%D Margin swapping has been simplified: see mkii code in case of regression.
+%D Calculation is delayed till the page anyway so the state is known.
+
+\def\page_backgrounds_recalculate
+ {\global\settrue\c_page_backgrounds_new}
+
+\def\page_backgrounds_set_boxes
+ {\ifconditional\c_page_backgrounds_new
+ \page_backgrounds_set_boxes_r
+ \fi
+ \doifbothsides
+ \page_backgrounds_set_boxes_a
+ \page_backgrounds_set_boxes_b
+ \page_backgrounds_set_boxes_c
+ \ifx\p_page_backgrounds_state\v!repeat\else
+ \global\setfalse\c_page_backgrounds_new
+ \fi}
+
+\def\page_backgrounds_set_boxes_r
+ {\global\settrue\c_page_backgrounds_new_right
+ \global\settrue\c_page_backgrounds_new_left
+ \global\setbox\leftbackground\emptybox
+ \global\setbox\rightbackground\emptybox}
+
+\def\page_backgrounds_set_boxes_a
+ {\ifconditional\c_page_backgrounds_new_left
+ % \showmessage\m!layouts8\empty
+ \page_backgrounds_set_box\leftbackground % \conditionalfalse
+ \global\setfalse\c_page_backgrounds_new_left
+ \global\setfalse\c_page_backgrounds_new_right
+ \fi}
+
+\def\page_backgrounds_set_boxes_b
+ {\ifconditional\c_page_backgrounds_new_left
+ % \showmessage\m!layouts8\empty
+ \page_backgrounds_set_box\leftbackground
+ \global\setfalse\c_page_backgrounds_new_left
+ \fi}
+
+\def\page_backgrounds_set_boxes_c
+ {\ifconditional\c_page_backgrounds_new_right
+ % \showmessage\m!layouts8\empty
+ \page_backgrounds_set_box\rightbackground
+ \global\setfalse\c_page_backgrounds_new_right
+ \fi}
+
+\def\page_backgrounds_add_to_main#1% todo: dimension spec
+ {\ifconditional\c_page_backgrounds_some
+ \page_backgrounds_set_boxes
+ \setbox#1\vpack
+ {\offinterlineskip
+ \doifelsemarginswap{\copy\leftbackground}{\copy\rightbackground}%
+ \box#1}%
+ \fi}
+
+\newdimen\pagebackgroundhoffset % THESE WILL BECOME OBSOLETE
+\newdimen\pagebackgroundvoffset
+\newdimen\pagebackgroundoffset % used elsewhere
+\newdimen\pagebackgrounddepth
+\newdimen\pagebackgroundcompensation
+
+\def\page_backgrounds_set_offsets % used in menus (we can use ifcsname's here)
+ {\ifconditional\c_page_backgrounds_some \ifconditional\c_page_backgrounds_new
+ \page_backgrounds_set_offsets_indeed % indirect, less tracing
+ \fi \fi}
+
+\def\page_backgrounds_set_offsets_indeed
+ {\ifcsname\??layoutbackgrounds\v!text\v!text\endcsname
+ \page_backgrounds_set_offsets_yes
+ \orelse\ifcsname\??layoutbackgrounds\v!text\endcsname
+ \page_backgrounds_set_offsets_yes
+ \else
+ \page_backgrounds_set_offsets_nop
+ \fi}
+
+\def\page_backgrounds_set_offsets_nop
+ {\global\pagebackgroundhoffset \zeropoint
+ \global\pagebackgroundvoffset \zeropoint
+ \global\pagebackgrounddepth \zeropoint
+ \global\pagebackgroundcompensation\zeropoint}
+
+\def\page_backgrounds_set_offsets_yes
+ {\global\pagebackgroundoffset \d_page_backgrounds_depth
+ \global\pagebackgroundcompensation\d_page_backgrounds_offset\relax
+ \ifzeropt\pagebackgroundcompensation
+ \page_backgrounds_set_offsets_nop
+ \else
+ \ifcsname\??layoutbackgrounds\v!top\v!text\endcsname
+ \global\pagebackgroundhoffset\zeropoint
+ \orelse\ifcsname\??layoutbackgrounds\v!bottom\v!text\endcsname
+ \global\pagebackgroundhoffset\zeropoint
+ \else
+ \global\pagebackgroundhoffset\pagebackgroundcompensation
+ \fi
+ \ifcsname\??layoutbackgrounds\v!text\v!rightedge\endcsname
+ \global\pagebackgroundvoffset\zeropoint
+ \global\pagebackgrounddepth \zeropoint
+ \orelse\ifcsname\??layoutbackgrounds\v!text\v!leftedge\endcsname
+ \global\pagebackgroundvoffset\zeropoint
+ \global\pagebackgrounddepth \zeropoint
+ \else
+ \global\pagebackgroundvoffset\pagebackgroundcompensation
+ \global\pagebackgrounddepth \d_page_backgrounds_depth
+ \fi
+ \fi}
+
+\appendtoks
+ \page_backgrounds_set_offsets
+\to \everybeforepagebody
+
+\newconditional\swapbackgroundmargins \settrue\swapbackgroundmargins
+
+%D The swapping here is pretty nasty! Maybe we should find another way some day but
+%D for now we tweak.
+
+\def\page_backgrounds_set_box#1% #2%
+ {\global\setbox#1\vpack
+ {\dontcomplain
+ \page_layouts_swap_margins
+ \ifconditional\swapbackgroundmargins
+ \doifelsemarginswap \donothing
+ {\enforced\swapcsvalues\v!rightmargin\v!leftmargin
+ \enforced\swapcsvalues\v!rightedge \v!leftedge}%
+ \fi
+ \calculatereducedvsizes
+ \offinterlineskip
+ % \ifconditional#2\relax
+ % \page_layouts_swap_margins_indeed % hm, this one gets nilled in \page_layouts_swap_margin anyway
+ % \fi
+ \ifdim\topheight>\zeropoint
+ \kern\dimexpr-\topheight-\topdistance\relax
+ \page_backgrounds_set_box_row\v!top\topheight
+ \kern\topdistance
+ \fi
+ \ifdim\headerheight>\zeropoint
+ \page_backgrounds_set_box_row\v!header\headerheight
+ \kern\headerdistance
+ \fi
+ \ifdim\textheight>\zeropoint
+ \page_backgrounds_set_box_row\v!text\textheight
+ \fi
+ \ifdim\footerheight>\zeropoint
+ \kern\footerdistance
+ \page_backgrounds_set_box_row\v!footer\footerheight
+ \fi
+ \ifdim\bottomheight>\zeropoint
+ \kern\bottomdistance
+ \page_backgrounds_set_box_row\v!bottom\bottomheight
+ \fi
+ \vfilll}%
+ \smashbox#1}
+
+\def\page_backgrounds_set_box_row#1#2% maybe helper
+ {\setbox\scratchbox\vpack to #2
+ \bgroup\hpack\bgroup
+ \goleftonpage
+ \ifdim\leftedgewidth>\zeropoint
+ \ifcsname\??layoutbackgrounds#1\v!leftedge\endcsname
+ \page_backgrounds_set_box_cell#1\v!leftedge\leftedgewidth#2%
+ \else
+ \kern\leftedgewidth
+ \fi
+ \kern\leftedgedistance
+ \fi
+ \ifdim\leftmarginwidth>\zeropoint
+ \ifcsname\??layoutbackgrounds#1\v!leftmargin\endcsname
+ \page_backgrounds_set_box_cell#1\v!leftmargin\leftmarginwidth#2%
+ \else
+ \kern\leftmarginwidth
+ \fi
+ \kern\leftmargindistance
+ \fi
+ \ifcsname\??layoutbackgrounds#1\v!text\endcsname
+ \page_backgrounds_set_box_cell#1\v!text\makeupwidth#2%
+ \else
+ \kern\makeupwidth
+ \fi
+ \ifdim\rightmarginwidth>\zeropoint
+ \kern\rightmargindistance
+ \ifcsname\??layoutbackgrounds#1\v!rightmargin\endcsname
+ \page_backgrounds_set_box_cell#1\v!rightmargin\rightmarginwidth#2%
+ \else
+ \kern\rightmarginwidth
+ \fi
+ \fi
+ \ifdim\rightedgewidth>\zeropoint
+ \kern\rightedgedistance
+ \ifcsname\??layoutbackgrounds#1\v!rightedge\endcsname
+ \page_backgrounds_set_box_cell#1\v!rightedge\rightedgewidth#2%
+ \else
+ \kern\rightedgewidth
+ \fi
+ \fi
+ \egroup\egroup
+ \wd\scratchbox\zeropoint
+ \box\scratchbox\relax}
+
+% these are fake framed .. maybe it's nicer to honor foreground here as well
+% but it's probably a slow downer
+
+\def\page_backgrounds_set_box_cell#1#2#3#4% pos pos width height
+ {\begingroup
+ \edef\p_page_backgrounds_setups{\namedframedparameter{\??layoutbackgrounds#1#2}\c!setups}%
+ \ifempty\p_page_backgrounds_setups \else
+ \doprocesslocalsetups\p_page_backgrounds_setups % should not produce funny spaces !
+ \fi
+ \edef\p_page_backgrounds_command{\namedframedparameter{\??layoutbackgrounds#1#2}\c!command}%
+ \ifempty\p_page_backgrounds_command
+ \expandafter\page_backgrounds_set_box_cell_nop
+ \else
+ \expandafter\page_backgrounds_set_box_cell_yes
+ \fi#3#4%
+ \localbackgroundframed{\??layoutbackgrounds#1#2}{#1:#2}\scratchbox
+ \endgroup}
+
+\def\page_backgrounds_set_box_cell_nop#1#2%
+ {\setbox\scratchbox\emptyvbox
+ \wd\scratchbox#1%
+ \ht\scratchbox#2}
+
+\def\page_backgrounds_set_box_cell_yes#1#2%
+ {\setbox\scratchbox\vpack to #2{\vss\hpack to#1{\hss\p_page_backgrounds_command\hss}\vss}%
+ \dp\scratchbox\zeropoint}
+
+%D The background mechanism is quite demanding in terms or resources. We used to
+%D delay these definitions till runtime usage, but since today's \TEX's are large,
+%D we now do the work on forehand.
+%D
+%D \starttyping
+%D \setupbackgrounds [settings]
+%D \setupbackgrounds [paper,page,text,..] [settings]
+%D \setupbackgrounds [top,...] [leftedge,...] [settings]
+%D \stoptyping
+%D
+%D \showsetup{setupbackgrounds}
+%D
+%D Because the number of arguments runs from one to three, we need to check
+%D for it.
+
+\newtoks\everybackgroundssetup
+
+\permanent\tolerant\protected\def\setupbackgrounds[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ \expandafter\gobblethreeoptionals
+ \or
+ \expandafter\page_backgrounds_setup_basics
+ \or
+ \expandafter\page_backgrounds_setup_single
+ \or
+ \expandafter\page_backgrounds_setup_double
+ \fi[#1][#2][#3]}
+
+\newdimen\d_page_backgrounds_offset
+\newdimen\d_page_backgrounds_depth
+
+\appendtoks
+ \edef\p_page_backgrounds_offset{\namedframedparameter{\??layoutbackgrounds\v!page}\c!offset}%
+ \edef\p_page_backgrounds_depth {\namedframedparameter{\??layoutbackgrounds\v!page}\c!depth }%
+ \edef\p_page_backgrounds_state {\namedframedparameter{\??layoutbackgrounds }\c!state }%
+ \global\d_page_backgrounds_offset\ifempty\p_offset\zeropoint\else\p_page_backgrounds_offset\fi
+ \global\d_page_backgrounds_depth \ifempty\p_depth \zeropoint\else\p_page_backgrounds_depth \fi
+ \global\pagebackgroundoffset\d_page_backgrounds_offset\relax
+ \global\pagebackgrounddepth \d_page_backgrounds_depth \relax
+ \ifx\p_page_backgrounds_state\v!stop
+ \global\setfalse\c_page_backgrounds_new
+ \else
+ \global\settrue \c_page_backgrounds_new
+ \fi
+\to \everybackgroundssetup
+
+\def\v_page_backgrounds_double_set{\v!paper,\v!page,\v!leftpage,\v!rightpage}
+\def\v_page_backgrounds_single_set{\v!text,\v!hidden,\v!paper,\v!page,\v!leftpage,\v!rightpage}
+\def\v_page_backgrounds_common_set{\v!leftedge,\v!leftmargin,\v!text,\v!rightmargin,\v!rightedge}
+
+\let\page_backgrounds_setup_step_nested\relax
+
+\protected\def\page_backgrounds_setup_double[#1][#2][#3]% if needed we can speed this up
+ {\global\settrue\c_page_backgrounds_some
+ \def\page_backgrounds_setup_step##1%
+ {\doifelseinset{##1}\v_page_backgrounds_double_set
+ {\page_backgrounds_setup_and_check{##1}{#3}}
+ {\def\page_backgrounds_setup_step_nested####1{\page_backgrounds_setup_and_check{##1####1}{#3}}%
+ \processcommacommand[#2]\page_backgrounds_setup_step_nested}}%
+ \processcommacommand[#1]\page_backgrounds_setup_step
+ \the\everybackgroundssetup}
+
+\protected\def\page_backgrounds_setup_single[#1][#2][#-]%
+ {\global\settrue\c_page_backgrounds_some
+ \doifelsecommon{#1}\v_page_backgrounds_single_set
+ {\def\page_backgrounds_setup_step##1{\page_backgrounds_setup_and_check{##1}{#2}}%
+ \processcommacommand[#1]\page_backgrounds_setup_step
+ \the\everybackgroundssetup}%
+ {\page_backgrounds_setup_double[#1][\v_page_backgrounds_common_set][#2]}}
+
+\protected\def\page_backgrounds_setup_basics[#1][#-][#-]%
+ {\setupframed[\??layoutbackgrounds][#1]%
+ \the\everybackgroundssetup}
+
+\protected\def\page_backgrounds_setup_and_check#1#2% tag settings
+ {\edef\currentotrbackground{\??layoutbackgrounds#1}%
+ \setupframed[\currentotrbackground][#2]%
+ \page_backgrounds_check_background}
+
+%D Each areas (currently there are $1+3+25+1=30$ of them) has its own low level
+%D framed object associated.
+
+\definesimplifiedframed[\??layoutbackgrounds\v!paper]
+\definesimplifiedframed[\??layoutbackgrounds\v!page]
+\definesimplifiedframed[\??layoutbackgrounds\v!leftpage]
+\definesimplifiedframed[\??layoutbackgrounds\v!rightpage]
+
+\definesimplifiedframed[\??layoutbackgrounds\v!text]
+\definesimplifiedframed[\??layoutbackgrounds\v!hidden]
+
+\definesimplifiedframed[\??layoutbackgrounds\v!top\v!leftedge]
+\definesimplifiedframed[\??layoutbackgrounds\v!top\v!leftmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!top\v!text]
+\definesimplifiedframed[\??layoutbackgrounds\v!top\v!rightmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!top\v!rightedge]
+
+\definesimplifiedframed[\??layoutbackgrounds\v!header\v!leftedge]
+\definesimplifiedframed[\??layoutbackgrounds\v!header\v!leftmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!header\v!text]
+\definesimplifiedframed[\??layoutbackgrounds\v!header\v!rightmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!header\v!rightedge]
+
+\definesimplifiedframed[\??layoutbackgrounds\v!text\v!leftedge]
+\definesimplifiedframed[\??layoutbackgrounds\v!text\v!leftmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!text\v!text]
+\definesimplifiedframed[\??layoutbackgrounds\v!text\v!rightmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!text\v!rightedge]
+
+\definesimplifiedframed[\??layoutbackgrounds\v!footer\v!leftedge]
+\definesimplifiedframed[\??layoutbackgrounds\v!footer\v!leftmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!footer\v!text]
+\definesimplifiedframed[\??layoutbackgrounds\v!footer\v!rightmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!footer\v!rightedge]
+
+\definesimplifiedframed[\??layoutbackgrounds\v!bottom\v!leftedge]
+\definesimplifiedframed[\??layoutbackgrounds\v!bottom\v!leftmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!bottom\v!text]
+\definesimplifiedframed[\??layoutbackgrounds\v!bottom\v!rightmargin]
+\definesimplifiedframed[\??layoutbackgrounds\v!bottom\v!rightedge]
+
+\setupbackgrounds
+ [\v!page]
+ [\c!offset=\zeropoint, % hm, so we need to force overlay elsewhere
+ \c!depth=\zeropoint]
+
+%D General setup:
+
+\setupbackgrounds
+ [\c!state=\c!start]
+
+%D The hidden layer can be populated by extending the following comma separated
+%D list. This only happens in core modules.
+
+% todo page-2 .. page+2 achter pagina -> bleed
+% spread-2 .. spread+2 achter spread -> spread (repeat 2 times)
+
+\permanent\protected\def\enablehiddenbackground
+ {\global\settrue\c_page_backgrounds_hidden_enabled
+ \global\settrue\c_page_backgrounds_some
+ \page_backgrounds_recalculate}
+
+\permanent\protected\def\disablehiddenbackground
+ {\global\setfalse\c_page_backgrounds_hidden_enabled}
+
+\immutable\def\hiddenbackgroundlist
+ {\v!text-2,\v!text-1,\v!foreground,\v!text+1,\v!text+2}
+
+\permanent\protected\def\prependhiddenbackground#1{\enforced\edef\hiddenbackgroundlist{#1,\hiddenbackgroundlist}}
+\permanent\protected\def\appendhiddenbackground #1{\enforced\edef\hiddenbackgroundlist{\hiddenbackgroundlist,#1}}
+
+\defineoverlay[\v!text-2][\positionregionoverlay\textanchor{\v!text-2}] % no new anchor, we share text
+\defineoverlay[\v!text-1][\positionregionoverlay\textanchor{\v!text-1}]
+\defineoverlay[\v!text+1][\positionregionoverlay\textanchor{\v!text+1}]
+\defineoverlay[\v!text+2][\positionregionoverlay\textanchor{\v!text+2}]
+
+\setupbackgrounds
+ [\v!hidden]
+ [\c!background=\hiddenbackgroundlist]
+
+%D Because we haven't really set up backgrounds yet, we set the main efficiency
+%D switch to false.
+
+\setfalse\c_page_backgrounds_some
+
+%D Sometimes you have a document wide (page) background but need to overload it
+%D locally. In such case (at least in my experience) the only values that get set
+%D are the background and backgroundcolor (if set at all). A full inheritance chain
+%D would complicate things because then we need to use named backgrounds which in
+%D turn will make this mechanism slower. I considered independent local backgrounds
+%D but that also complicates the code (not that much) but isolation means that we
+%D need to set more parameters each time. The following simple approach proabbly
+%D suits most usage.
+%D
+%D \starttyping
+%D \starttext
+%D \setupbackgrounds[page][background=color,backgroundcolor=red]
+%D \input tufte \page
+%D \setupbackgrounds[page][background=,backgroundcolor=]
+%D \input tufte \page
+%D \setupbackgrounds[page][background=color,backgroundcolor=red]
+%D \input tufte \page
+%D \pushbackground[page]
+%D \setupbackgrounds[page][background=color,backgroundcolor=green]
+%D \input tufte \page
+%D \popbackground
+%D \input tufte \page
+%D \stoptext
+%D \stoptyping
+
+\permanent\protected\def\pushbackground[#1]%
+ {\pushmacro\popbackground
+ \edef\currentotrbackground{\??layoutbackgrounds#1}%
+ \enforced\permanent\protected\edef\popbackground
+ {\setupframed
+ [\currentotrbackground]
+ [\c!background=\namedframedparameter{\currentotrbackground}\c!background,
+ \c!backgroundcolor=\namedframedparameter{\currentotrbackground}\c!backgroundcolor]%
+ \page_backgrounds_check_background
+ \popmacro\popbackground}%
+ \setupframed
+ [\currentotrbackground]
+ [\c!background=,\c!backgroundcolor=]%
+ \page_backgrounds_check_background}
+
+\permanent\let\popbackground\relax
+
+\protect \endinput
+
+% %D The next series is used in local (for instance floating) backgrounds.
+%
+% \installsimplifiedframed{\??layoutbackgrounds\v!local}
+%
+% \getparameters
+% [\??layoutbackgrounds\v!local]
+% [\c!component=local,
+% \c!background=\localbackgroundlist]
+%
+% \def\localbackgroundlist
+% {\v!local-2,\v!local-1,\v!foreground,\v!local+1,\v!local+2}
+%
+% \defineoverlay[\v!local-2][\positionoverlay{\v!local-2}] % todo share
+% \defineoverlay[\v!local-1][\positionoverlay{\v!local-1}]
+% \defineoverlay[\v!local+1][\positionoverlay{\v!local+1}]
+% \defineoverlay[\v!local+2][\positionoverlay{\v!local+2}]
+%
+% \def\page_backgrounds_add_local_to_box
+% {\ifconditional\c_page_backgrounds_hidden_enabled
+% \expandafter\page_backgrounds_add_local_to_box_indeed
+% \else
+% \expandafter\gobbleoneargument
+% \fi}
+%
+% \def\page_backgrounds_add_local_to_box_indeed#1%
+% {\setbox#1\hbox{\localbackgroundframed{\??layoutbackgrounds\v!local}\v!local#1}%
+% \global\advance\localpositionnumber\plusone} % afterwards !
+%
+% \let\page_backgrounds_add_local_to_box\gobbleoneargument
+
+% Test how previous macro behaves with depth:
+%
+% \startcolumnset
+% \input tufte
+% \placefigure{none}{\framed[lines=5]{xxx}}
+% \input tufte
+% \placefigure{none}{\starttabulate\NC test\nc test\NC\NR\stoptabulate}
+% \input tufte
+% \stopcolumnset
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-box.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-box.mklx
new file mode 100644
index 00000000000..c8ceeb8282e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-box.mklx
@@ -0,0 +1,289 @@
+%D \module
+%D [ file=page-box,
+%D version=2011.12.05, % moved from other page-* files
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Boxing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Boxing}
+
+\unprotect
+
+%D The often two step approach (_indeed) saves skipping tokens which is nicer for
+%D tracing. Centering the paper area on the print area is determined by the \type
+%D {top}, \type {bottom}, \type {left} and \type {right} parameters.
+
+\def\page_boxes_apply_center#box% we could test for a difference (saves packing)
+ {\printpaperwidth \v_page_target_scale\printpaperwidth \relax
+ \printpaperheight\v_page_target_scale\printpaperheight\relax
+ \ifdim\printpaperheight>\ht#box\relax
+ \page_boxes_apply_center_indeed#box%
+ \orelse\ifdim\printpaperwidth>\wd#box\relax
+ \page_boxes_apply_center_indeed#box%
+ \fi}
+
+\def\page_boxes_apply_center_indeed_l_r#box%
+ {\v_page_target_left
+ \v_page_target_left_fill
+ \box#box%
+ \v_page_target_right_fill
+ \v_page_target_right}
+
+\def\page_boxes_apply_center_indeed_r_l#box%
+ {\v_page_target_right
+ \v_page_target_right_fill
+ \box#box%
+ \v_page_target_left_fill
+ \v_page_target_left}
+
+\def\page_boxes_apply_center_indeed#box% we could test for a difference (saves packing)
+ {\setbox#box\vpack to \printpaperheight
+ {\v_page_target_top
+ \v_page_target_top_fill
+ \hpack to \printpaperwidth
+ {\ifconditional\c_page_target_print_doublesided
+ \doifbothsides
+ {\page_boxes_apply_center_indeed_l_r#box}%
+ {\page_boxes_apply_center_indeed_l_r#box}%
+ {\page_boxes_apply_center_indeed_r_l#box}%
+ \else
+ \page_boxes_apply_center_indeed_l_r#box%
+ \fi}%
+ \par
+ \v_page_target_bottom_fill
+ \v_page_target_bottom}}
+
+\def\page_boxes_apply_offset % #box
+ {\unless\ifzeropt\topoffset
+ \expandafter\page_boxes_apply_offset_indeed
+ \orelse\ifzeropt\backoffset
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\page_boxes_apply_offset_indeed
+ \fi}
+
+\def\page_boxes_apply_offset_indeed#box%
+ {\scratchwidth \wd#box%
+ \scratchheight\ht#box%
+ \scratchdepth \dp#box%
+ \setbox#box\vpack
+ {\offinterlineskip
+ \vskip\topoffset
+ \hskip\doifbothsides\backoffset\backoffset{-\backoffset}%
+ \box#box}%
+ \wd#box\scratchwidth
+ \ht#box\scratchheight
+ \dp#box\scratchdepth}
+
+\def\page_boxes_apply_replicate
+ {\ifnum\layoutparameter\c!nx>\plusone
+ \expandafter\page_boxes_apply_replicate_indeed
+ \orelse\ifnum\layoutparameter\c!ny>\plusone
+ \expandafter\page_boxes_apply_replicate_indeed
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\page_boxes_apply_replicate_indeed#box%
+ {\setbox#box\vpack
+ {\offinterlineskip
+ \dorecurse{\layoutparameter\c!ny}
+ {\hpack{\dorecurse{\layoutparameter\c!nx}{\copy#box\kern\layoutparameter\c!dx}\unskip}%
+ \vskip\layoutparameter\c!dy}
+ \unskip}}
+
+\def\page_boxes_apply_orientate_paper#box%
+ {\ifnum\number\c_page_target_paper_orientation\number\c_page_target_paper_reverse>\zerocount
+ \page_boxes_apply_orientate_indeed\c_page_target_paper_orientation\c_page_target_paper_reverse{#box}%
+ \fi}
+
+\def\page_boxes_apply_orientate_print#box%
+ {\ifnum\number\c_page_target_print_orientation\number\c_page_target_print_reverse>\zerocount
+ \page_boxes_apply_orientate_indeed\c_page_target_print_orientation\c_page_target_print_reverse{#box}%
+ \fi}
+
+\def\page_boxes_apply_orientate_indeed#odd#even#box%
+ {\setbox#box\vpack
+ {\edef\p_rotation{\number\ifdoublesided\ifodd\realpageno#odd\else#even\fi\else#odd\fi}%
+ \dorotatebox\p_rotation\hpack{\box#box}}}
+
+\def\page_boxes_apply_mirror#box%
+ {\setbox#box\vpack{\mirror{\box#box}}}
+
+\def\page_boxes_apply_mirror_paper#box{\ifconditional\c_page_target_paper_mirror\page_boxes_apply_mirror{#box}\fi}
+\def\page_boxes_apply_mirror_print#box{\ifconditional\c_page_target_print_mirror\page_boxes_apply_mirror{#box}\fi}
+
+\aliased\let\page_boxes_apply_negate\negatecolorbox
+
+\def\page_boxes_apply_negate_print#box%
+ {\ifconditional\c_page_target_print_negate
+ \page_boxes_apply_negate#box%
+ \orelse\ifconditional\c_page_target_paper_negate
+ \page_boxes_apply_negate#box%
+ \fi}
+
+\let\p_page_layouts_scale\relax
+\let\p_page_layouts_sx \relax
+\let\p_page_layouts_sy \relax
+
+\def\page_boxes_apply_scale#box%
+ {\edef\p_page_layouts_scale{\layoutparameter\c!scale}%
+ \ifdim\p_page_layouts_scale\points=\onepoint
+ \edef\p_page_layouts_sx{\layoutparameter\c!sx}%
+ \edef\p_page_layouts_sy{\layoutparameter\c!sy}%
+ \ifdim\p_page_layouts_sx\points=\onepoint
+ \ifdim\layoutparameter\c!sy\points=\onepoint
+ % no scaling done (and no copying either)
+ \else
+ \page_boxes_apply_scale_indeed\p_page_layouts_sx\p_page_layouts_sy{#box}%
+ \fi
+ \else
+ \page_boxes_apply_scale_indeed\p_page_layouts_sx\p_page_layouts_sy{#box}%
+ \fi
+ \else
+ \page_boxes_apply_scale_indeed\p_page_layouts_scale\p_page_layouts_scale{#box}%
+ \fi}
+
+\def\page_boxes_apply_scale_indeed#sx#sy#box%
+ {\setbox#box\vpack{\scale[\c!sx=#sx,\c!sy=#sy]{\box#box}}% can be a fast one
+ \paperwidth #sx\paperwidth
+ \paperheight#sy\paperheight}
+
+% \setuppagenumbering[alternative=doublesided]
+% \setupcolors[state=start]
+% \setuppapersize[A4][A4,oversized]
+% \setuplayout[location=middle,clipoffset=5mm]
+% \setupbackgrounds
+% [page]
+% [frame=on,rulethickness=1mm,
+% backgroundoffset=10mm,background=color,backgroundcolor=red]
+% \starttext \dorecurse{10}{\input tufte \par} \stoptext
+
+% best use private variables
+
+\newconditional\c_page_boxes_clip_print \settrue\c_page_boxes_clip_print
+
+\def\page_boxes_apply_clip_print_left % box
+ {\ifconditional\c_page_boxes_clip_print
+ \expandafter\page_boxes_apply_clip_print_indeed\expandafter\conditionalfalse
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\page_boxes_apply_clip_print_right % box
+ {\ifconditional\c_page_boxes_clip_print
+ \expandafter\page_boxes_apply_clip_print_indeed\expandafter\conditionaltrue
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\page_boxes_apply_clip_print_indeed#right#box% can be made more efficient, see other clipper
+ {\scratchoffset\pagebackgroundoffset % can be zero which is valid % maybe another variable
+ \scratchwidth \wd#box%
+ \scratchheight\ht#box%
+ \scratchdepth \dp#box%
+ \setbox#box\hpack
+ {\advance\scratchheight\dimexpr\scratchdepth+2\scratchoffset\relax
+ \advance\scratchwidth \scratchoffset
+ \ifconditional#right\relax
+ \scratchdimen-\scratchoffset
+ \kern\scratchdimen
+ \else
+ \scratchdimen\zeropoint
+ \fi
+ \lower\scratchoffset\hpack
+ {\clip
+ [\c!hoffset=\scratchdimen,
+ \c!voffset=-\scratchoffset,
+ \c!width=\scratchwidth,
+ \c!height=\scratchheight]%
+ {\box#box}}}%
+ \wd#box\scratchwidth
+ \ht#box\scratchheight
+ \dp#box\scratchdepth}
+
+\def\page_boxes_apply_clip_paper
+ {\scratchoffset\layoutparameter\c!clipoffset\relax
+ \ifdim\scratchoffset>\zeropoint
+ \expandafter\page_boxes_apply_clip_paper_indeed
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\page_boxes_apply_clip_paper_indeed#box%
+ {\scratchwidth \wd#box%
+ \scratchheight\ht#box%
+ \scratchdepth \dp#box%
+ \setbox#box\hpack
+ {\advance\scratchheight\dimexpr\scratchdepth+2\scratchoffset\relax
+ \advance\scratchwidth \scratchoffset
+ \doifbothsides
+ {\advance\scratchwidth\scratchoffset
+ \scratchdimen-\scratchoffset
+ \kern\scratchdimen}%
+ {\scratchdimen\zeropoint}
+ {\scratchdimen-\scratchoffset
+ \kern\scratchdimen}%
+ \lower\scratchoffset\hpack
+ {\clip
+ [\c!hoffset=\scratchdimen,
+ \c!voffset=-\scratchoffset,
+ \c!width=\scratchwidth,
+ \c!height=\scratchheight]%
+ {\box#box}}}%
+ \wd#box\scratchwidth
+ \ht#box\scratchheight
+ \dp#box\scratchdepth}
+
+% nearly always some displacement so no speedup test needed
+
+\def\page_boxes_apply_offsets#1%
+ {\setbox#1\vpack to \paperheight
+ {\hsize\paperwidth
+ \vkern\topspace
+ \hkern\doifbothsides\backspace\backspace{\dimexpr\paperwidth-\backspace-\makeupwidth\relax}%
+ \box#1}%
+ \dp#1\zeropoint}
+
+%D This is rather specialized:
+
+\newconditional\c_page_areas_enabled
+\newbox \b_page_areas_registered
+
+\permanent\protected\def\enabletextarearegistration
+ {\global\settrue\c_page_areas_enabled}
+
+\def\page_areas_registered_box
+ {\ifconditional\c_page_areas_enabled
+ \expandafter\page_areas_registered_box_indeed
+ \else
+ \expandafter\page_areas_registered_box_forget
+ \fi}
+
+\def\page_areas_registered_box_forget#1#2#3% #1=lower-dp #2=correct-ht #3=box
+ {\box#3}
+
+\def\page_areas_registered_box_indeed#1#2#3% #1=lower-dp #2=correct-ht #3=box
+ {\anch_mark_flow_box{#3}}
+
+\def\page_areas_register_boxed#1%
+ {\begingroup
+ \setbox\scratchbox\vpack{#1}%
+ \wd\scratchbox\makeupwidth % somehow a space creeps in
+ \vpack{\page_areas_registered_box00\scratchbox}% 0 0 will go
+ \endgroup}
+
+\def\page_areas_register_direct#1#2%
+ {\scratchdepth\dp#2%
+ \dp#2\strutdepth
+ \anch_mark_flow_only{#2}%
+ \dp#2\scratchdepth
+ #1#2}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-brk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-brk.mkxl
new file mode 100644
index 00000000000..aca1d097d12
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-brk.mkxl
@@ -0,0 +1,483 @@
+%D \module
+%D [ file=page-brk, % moved from page-ini
+%D version=2011.12.07, % 2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Breaks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Breaks}
+
+\unprotect
+
+\ifdefined\resetcurrentstructuremarkswithpage \else \let\resetcurrentstructuremarkswithpage\relax \fi
+\ifdefined\noheaderandfooterlines \else \let\noheaderandfooterlines \relax \fi
+
+%D Page breaks.
+
+% \definepagebreak
+% [chapter]
+% [yes,header,right]
+%
+% \setuphead
+% [chapter]
+% [page=chapter,
+% header=empty,
+% footer=chapter]
+%
+% \definepagebreak % untested
+% [lastpage]
+% [left,{empty,right},{empty,left}]
+
+% public page handler, beware: definepage already in use (core-ref)
+%
+% \definepagebreak[instance][forsure]
+% \definepagebreak[forsure][yes,+4]
+
+\newconditional\c_page_breaks_enabled \settrue\c_page_breaks_enabled
+\newcount \c_page_breaks_prevpage
+
+\newtoks\everybeforepagebreak
+\newtoks\everyafterpagebreak
+
+\let\page_breaks_current_option \empty
+\let\page_breaks_current_options\empty
+
+\installcorenamespace{pagebreakmethod}
+\installcorenamespace{pagebreaks}
+
+\def\page_breaks_handle#1%
+ {\edef\page_breaks_current_options{#1}% handy for tracing
+ \processcommacommand[\page_breaks_current_options]\page_breaks_handle_step}
+
+\def\page_breaks_handle_step#1%
+ {\edef\page_breaks_current_option{#1}% can be used in handler
+ \ifcsname\??pagebreakmethod\page_breaks_current_option\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??pagebreaks\page_breaks_current_option\endcsname
+ \expandafter\page_breaks_handle\lastnamedcs
+ \else
+ \page_breaks_unknown
+ \fi}
+
+\def\page_breaks_handle_direct#1%
+ {\begincsname\??pagebreakmethod#1\endcsname}
+
+\permanent\protected\def\installpagebreakmethod#1#2% low level definer
+ {\defcsname\??pagebreakmethod#1\endcsname{#2}}
+
+\aliased\let\installpagebreakhandler\installpagebreakmethod % will go
+
+\permanent\tolerant\protected\def\definepagebreak[#1]#*[#2]%
+ {\defcsname\??pagebreaks#1\endcsname{#2}}
+
+\appendtoks
+ \flushnotes
+\to \everybeforepagebreak
+
+\permanent\tolerant\protected\def\pagebreak[#1]
+ {\par % else no vertical penalties, always before group so that we clear hangs etc (as in side floats)
+ \ifvmode % extra check
+ \begingroup
+ \the\everybeforepagebreak
+ \c_page_breaks_prevpage\realpageno
+ \ifcase\pageornamentstate \or
+ % disable reset after shipout
+ \global\pageornamentstate\plustwo
+ \fi
+ \ifparameter#1\or
+ \page_breaks_handle{#1}%
+ \else % so, no pagebreak when \pagebreak[] ! ! !
+ \page_breaks_handle_direct\v!yes
+ \fi
+ \relax
+ \ifnum\c_page_breaks_prevpage<\realpageno
+ \global\pageornamentstate\zerocount
+ \fi
+ \the\everyafterpagebreak
+ \endgroup
+ \fi}
+
+\aliased\let\page\pagebreak
+
+\permanent\protected\def\usepageparameter#1%
+ {\edef\m_page_breaks_asked{#1\c!page}%
+ \ifempty\m_page_breaks_asked\else
+ \pagebreak[\m_page_breaks_asked]%
+ \fi}
+
+\permanent\protected\def\dousepageparameter#1%
+ {\edef\m_page_breaks_asked{#1}%
+ \ifempty\m_spac_align_asked\else
+ \pagebreak[\m_page_breaks_asked]%
+ \fi}
+
+\permanent\protected\def\resetpagebreak % used elsewhere too
+ {\global\settrue\c_page_breaks_enabled}
+
+\permanent\protected\def\simplifypagebreak % to be used grouped !
+ {\enforced\permanent\protected\def\pagebreak[##1]{\goodbreak}}
+
+\permanent\protected\def\disablepagebreaks % to be used grouped !
+ {\enforced\let\pagebreak\gobbleoneoptional}
+
+\installpagebreakmethod \s!dummy
+ {\page_otr_command_flush_all_floats
+ \page_otr_command_next_page
+ \page_otr_insert_dummy_page}
+
+\installpagebreakmethod \v!frame
+ {\page
+ \begingroup
+ \showframe
+ \page[\v!empty]
+ \endgroup}
+
+\protected\def\page_breaks_unknown % how often called ?
+ {\doifelseinstring{+}\page_breaks_current_option
+ {\page_otr_command_flush_all_floats
+ \page_otr_command_next_page
+ \dorecurse\page_breaks_current_option\page_otr_insert_dummy_page}
+ {\doifelsenumber\page_breaks_current_option
+ {\page_otr_command_flush_all_floats
+ \page_otr_command_next_page
+ \doloop
+ {\ifnum\userpageno<\page_breaks_current_option\relax
+ \page_otr_insert_dummy_page
+ \else
+ \exitloop
+ \fi}}
+ {}}}
+
+\installpagebreakmethod \s!unknown
+ {\page_breaks_unknown}
+
+\installpagebreakmethod \s!default
+ {} % do nothing if empty
+
+\installpagebreakmethod \v!reset
+ {% better not: \global\pageornamentstate\zerocount
+ \resetpagebreak}
+
+\installpagebreakmethod \v!disable
+ {\global\setfalse\c_page_breaks_enabled}
+
+\installpagebreakmethod \v!yes
+ {\ifconditional\c_page_breaks_enabled
+ \page_otr_command_flush_all_floats
+ \page_otr_command_next_page
+ \ifinsidecolumns % this will move to MUL
+ \page_otr_eject_page % otherwise sometimes no change
+ \fi
+ \fi}
+
+\installpagebreakmethod \v!makeup
+ {\ifconditional\c_page_breaks_enabled
+ \page_otr_fill_and_eject_page
+ \fi}
+
+\installpagebreakmethod \v!blank
+ {\ifcase\pageornamentstate
+ \global\pageornamentstate\plusone
+ \fi}
+
+% also needed: \page \doifoddpageelse\relax{\page[\v!blank,\v!right]
+
+\installpagebreakmethod \v!no
+ {\ifconditional\c_page_breaks_enabled
+ \dosomebreak\nobreak
+ \fi}
+
+\installpagebreakmethod \v!preference
+ {\ifconditional\c_page_breaks_enabled
+ \ifinsidecolumns % this will move to MUL
+ \dosomebreak\goodbreak
+ \else
+ \testpage[3][\zeropoint]%
+ \fi
+ \fi}
+
+\installpagebreakmethod \v!bigpreference
+ {\ifconditional\c_page_breaks_enabled
+ \ifinsidecolumns % this will move to MUL
+ \dosomebreak\goodbreak
+ \else
+ \testpage[5][\zeropoint]%
+ \fi
+ \fi}
+
+% \installpagebreakmethod \v!empty {} % defined in page-txt.mkiv
+% \installpagebreakmethod \v!header{} % defined in page-txt.mkiv
+% \installpagebreakmethod \v!footer{} % defined in page-txt.mkiv
+
+\def\page_reset_marks_and_insert_dummy
+ {\resetcurrentstructuremarkswithpage\page_otr_insert_dummy_page}
+
+\installpagebreakmethod \v!left
+ {\page_otr_command_flush_all_floats
+ \page_otr_command_next_page_and_inserts
+ \doifbothsidesoverruled\donothing\page_reset_marks_and_insert_dummy\donothing}
+
+\installpagebreakmethod \v!right
+ {\page_otr_command_flush_all_floats
+ \page_otr_command_next_page_and_inserts
+ \doifbothsidesoverruled\donothing\donothing\page_reset_marks_and_insert_dummy}
+
+\installpagebreakmethod \v!even
+ {\page
+ \doifelseoddpage\page_reset_marks_and_insert_dummy\donothing}
+
+\installpagebreakmethod \v!odd
+ {\page
+ \doifelseoddpage\donothing\page_reset_marks_and_insert_dummy}
+
+\installpagebreakmethod \v!quadruple % not yet ok inside columnsets
+ {\ifdoublesided
+ \ifcase\modulonumber\plusfour\realpageno\else
+ \page_breaks_handle_direct\v!yes
+ \doloop
+ {\ifcase\modulonumber\plusfour\realpageno\relax
+ \exitloop
+ \else
+ \page_breaks_handle_direct\v!empty
+ \fi}%
+ \fi
+ \fi}
+
+\installpagebreakmethod \v!last
+ {\page_otr_command_flush_all_floats
+ \page_otr_command_next_page_and_inserts
+ \relax
+ \doifbothsidesoverruled
+ \page_facings_flush % hm
+ \donothing
+ {\noheaderandfooterlines
+ \page_otr_insert_dummy_page}%
+ \filluparrangedpages}
+
+\installpagebreakmethod \v!lastpage % handy for backpage preceded by empty pages
+ {\page_breaks_handle_direct\v!yes
+ \ifdoublesided
+ \page_breaks_handle_direct\v!left
+ \page_breaks_handle_direct\v!empty
+ \page_breaks_handle_direct\v!empty
+ \fi}
+
+\installpagebreakmethod \v!start {\global\settrue \c_otr_shipout_enabled}
+\installpagebreakmethod \v!stop {\global\setfalse\c_otr_shipout_enabled}
+
+\installpagebreakmethod{xy}% for Mojca
+ {\page_breaks_handle_direct\v!yes
+ \scratchcounterone \numexpr\rootlayouttargetparameter\c!nx*\rootlayouttargetparameter\c!ny\relax
+ \scratchcountertwo \luaexpr{math.mod(\the\realpageno-1,\the\scratchcounterone)}\relax
+ \scratchcounterthree\numexpr\scratchcounterone-\scratchcountertwo\relax
+ \dorecurse\scratchcounterthree{\page_breaks_handle_direct\v!empty}}
+
+% Column breaks.
+
+\installcorenamespace{columnbreakmethod}
+\installcorenamespace{columnbreaks}
+
+\newtoks\everybeforecolumnbreak
+\newtoks\everyaftercolumnbreak
+\newtoks\everysynchronizecolumn
+
+\let\page_breaks_columns_current_option \empty
+\let\page_breaks_columns_current_options\empty
+
+\def\page_breaks_columns_handle#1%
+ {\edef\page_breaks_columns_current_options{#1}%
+ \processcommacommand[#1]\page_breaks_columns_handle_step}
+
+\def\page_breaks_columns_handle_step#1%
+ {\edef\page_breaks_columns_current_option{#1}%
+ \ifcsname\??columnbreakmethod\currentoutputroutine:\page_breaks_columns_current_option\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??columnbreaks\page_breaks_columns_current_option\endcsname
+ %\expandafter\csname\page_breaks_columns_handle\??columnbreaks\page_breaks_columns_current_option\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??columnbreakmethod\currentoutputroutine:\s!unknown\endcsname
+ \lastnamedcs
+ \fi}
+
+\def\page_breaks_columns_handle_direct#1%
+ {\begincsname\??columnbreakmethod\currentoutputroutine:#1\endcsname}
+
+\permanent\protected\def\installcolumnbreakmethod#1#2#3% #1=otr-id #2=tag #3=action
+ {\defcsname\??columnbreakmethod#1:#2\endcsname{#3}}
+
+\aliased\let\installcolumnbreakhandler\installcolumnbreakmethod % will go
+
+\permanent\tolerant\protected\def\definecolumnbreak[#1]#*[#2]%
+ {\defcsname\??columnbreaks#1\endcsname{#2}}
+
+%D So, page ornaments are reset after a pagebreak command, unless set!
+
+\permanent\tolerant\protected\def\columnbreak[#1]%
+ {\par % else no vertical penalties
+ \begingroup
+ \the\everybeforecolumnbreak
+ \ifparameter#1\or
+ \page_breaks_columns_handle{#1}%
+ \else
+ \page_breaks_columns_handle_direct\v!yes
+ \fi
+ \relax
+ \the\everyaftercolumnbreak
+ \endgroup
+ % outside group e.g. setting hsize
+ \the\everysynchronizecolumn}
+
+\aliased\let\column\columnbreak
+
+\appendtoks
+ \page_otr_command_set_hsize
+\to \everysynchronizecolumn
+
+%D Test page breaks.
+
+% \newdimen \d_page_tests_test
+% \newconstant\c_page_tests_mode
+
+\newconstant\testpagemethod % old
+\newconstant\testpagetrigger % old
+
+\installcorenamespace {pagechecker}
+\installcorenamespace {pagecheckermethod}
+
+\installcommandhandler \??pagechecker {pagechecker} \??pagechecker
+
+\setuppagechecker
+ [\c!method=1,
+ \c!before=,
+ \c!after=,
+ \c!inbetween=,
+ \c!lines=\plusthree,
+ \c!offset=\zeropoint]
+
+\def\page_check_amount
+ {\dimexpr
+ \pagecheckerparameter\c!lines\lineheight
+ +\pagetotal
+ \ifdim\lastskip<\parskip+\parskip\fi
+ +\pagecheckerparameter\c!offset
+ \relax}
+
+\permanent\tolerant\protected\def\checkpage[#1]#*[#2]%
+ {\relax % needed before \if
+ \endgraf
+ \triggerpagebuilder
+ \relax
+ \ifconditional\c_page_breaks_enabled
+ \begingroup
+ \edef\currentpagechecker{#1}%
+ \setupcurrentpagechecker[#2]%
+ \csname\??pagecheckermethod\pagecheckerparameter\c!method\endcsname
+ \endgroup
+ \fi}
+
+\defcsname\??pagecheckermethod 0\endcsname
+ {\ifdim\pagegoal<\maxdimen \relax
+ \ifdim\pagetotal<\pagegoal \relax
+ \ifdim\page_check_amount>.99\pagegoal
+ \pagecheckerparameter\c!before
+ \penalty-\plustenthousand
+ \pagecheckerparameter\c!after
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi}
+
+\defcsname\??pagecheckermethod 1\endcsname
+ {\ifdim\pagegoal<\maxdimen \relax
+ \ifdim\pagetotal<\pagegoal \relax
+ \ifdim\dimexpr\page_check_amount-\pagegoal\relax>-\lineheight
+ \pagecheckerparameter\c!before
+ \penalty-\plustenthousand
+ \pagecheckerparameter\c!after
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \goodbreak
+ \pagecheckerparameter\c!inbetween
+ \fi}
+
+\defcsname\??pagecheckermethod 2\endcsname
+ {\ifdim\pagegoal<\maxdimen \relax
+ \ifdim\pagetotal<\pagegoal \relax
+ \getnoflines\pagegoal
+ \ifdim\dimexpr\page_check_amount-\noflines\lineheight\relax>-\lineheight
+ \pagecheckerparameter\c!before
+ \penalty-\plustenthousand
+ \pagecheckerparameter\c!after
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi}
+
+\defcsname\??pagecheckermethod 3\endcsname
+ {\ifdim\pagegoal<\maxdimen \relax
+ \ifdim\pagetotal<\pagegoal \relax
+ \ifdim\dimexpr\page_check_amount-10\scaledpoint\relax>\pagegoal
+ \pagecheckerparameter\c!before
+ \penalty-\plustenthousand
+ \pagecheckerparameter\c!after
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \orelse\ifdim\pagetotal>\pagegoal
+ \ifdim\dimexpr\pagetotal-\pageshrink\relax>\pagegoal
+ \goodbreak
+ \pagecheckerparameter\c!inbetween
+ \else
+ \pagecheckerparameter\c!before
+ \page
+ \pagecheckerparameter\c!after
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi
+ \else
+ \pagecheckerparameter\c!inbetween
+ \fi}
+
+\definepagechecker[\s!unknown:0] [\c!method=0,\c!before=,\c!after=,\c!inbetween=]
+\definepagechecker[\s!unknown:1][\s!unknown:0][\c!method=1]
+\definepagechecker[\s!unknown:2][\s!unknown:0][\c!method=2]
+\definepagechecker[\s!unknown:3][\s!unknown:0][\c!method=3]
+
+\permanent\tolerant\protected\def\testpage [#1]#*[#2]{\normalexpanded{\checkpage[\s!unknown:1][\c!lines=#1,\c!offset=\ifparameter#2\or#2\else\zeropoint\fi]}} %
+\permanent\tolerant\protected\def\testpageonly[#1]#*[#2]{\normalexpanded{\checkpage[\s!unknown:2][\c!lines=#1,\c!offset=\ifparameter#2\or#2\else\zeropoint\fi]}} % no penalties added to the mvl
+\permanent\tolerant\protected\def\testpagesync[#1]#*[#2]{\normalexpanded{\checkpage[\s!unknown:3][\c!lines=#1,\c!offset=\ifparameter#2\or#2\else\zeropoint\fi]}} % force sync
+
+%D Test column breaks.
+
+\permanent\tolerant\protected\def\testcolumn[#1]#*[#2]%
+ {\ifdefined\page_otr_command_test_column
+ \ifparameter#2\or
+ \page_otr_command_test_column[#1][#2]%
+ \else
+ \page_otr_command_test_column[#1][\zeropoint]%
+ \fi
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-col.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-col.mkxl
new file mode 100644
index 00000000000..43330c9a7c8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-col.mkxl
@@ -0,0 +1,165 @@
+%D \module
+%D [ file=page-col, % moved from page-ini
+%D version=2011.12.07, % 2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Column Helpers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Column Helpers}
+
+%D Here we implement a couple of helpers for dealing with columns. For
+%D the moment we keep the names. When the mul and set modules are redone
+%D these can be adapted or disappear.
+
+%D DO WE STILL NEED THESE?
+
+\unprotect
+
+%D We reserve a counter for the number of columns as well as the current
+%D column. Both are not to be changed by users!
+
+%newcount\nofcolumns \nofcolumns\plusone % already in core-ini.mkiv
+\newcount\mofcolumns \mofcolumns\plusone
+
+\newconstant\columndirection % 0:lr 1:rl
+
+\setnewconstant\maxnofcolumns 50
+\setnewconstant\allocatednofcolumns 0
+
+\newdimen\columnwidth
+\newdimen\columndistance
+
+%D During initialization the temporary boxes are allocated. This enables us to
+%D use as much columns as we want, without exhausting the pool of boxes too
+%D fast. We could have packed them in one box, but we've got enough boxes.
+%D
+%D Two sets of boxes are declared, the txtboxes are used for the text, the
+%D topboxes are for moved column floats.
+
+\installcorenamespace{columntext}
+\installcorenamespace{columnfooter}
+\installcorenamespace{columntop}
+\installcorenamespace{columnbottom}
+
+\permanent\protected\def\initializecolumns#1%
+ {\ifnum#1>\maxnofcolumns
+ \showmessage\m!columns1\maxnofcolumns
+ \nofcolumns\maxnofcolumns
+ \else
+ \nofcolumns#1\relax
+ \fi
+ \ifnum\nofcolumns>\allocatednofcolumns
+ \page_columns_allocate
+ \fi}
+
+\def\page_columns_allocate
+ {\dorecurse\nofcolumns
+ {\ifnum\recurselevel>\allocatednofcolumns\relax
+ \ifcsname\??columntext\recurselevel\endcsname \else
+ \expandafter\newbox\csname\??columntext \recurselevel\endcsname % text
+ \expandafter\newbox\csname\??columnfooter\recurselevel\endcsname % footer
+ \expandafter\newbox\csname\??columntop \recurselevel\endcsname % top insert
+ \expandafter\newbox\csname\??columnbottom\recurselevel\endcsname % bottom insert
+ \fi
+ \fi}%
+ \global\allocatednofcolumns\nofcolumns}
+
+\mutable\def\currentcolumn{1}
+
+\immutable\def\firstcolumnbox {\columntextbox\plusone}
+\immutable\def\currentcolumnbox{\columntextbox\mofcolumns}
+\immutable\def\lastcolumnbox {\columntextbox\nofcolumns}
+
+\immutable\def\firsttopcolumnbox {\columntopbox \plusone}
+\immutable\def\currenttopcolumnbox{\columntopbox \mofcolumns}
+\immutable\def\lasttopcolumnbox {\columntopbox \nofcolumns}
+
+\permanent\def\columntextbox#1{\csname\??columntext \number#1\endcsname}
+\permanent\def\columnfootbox#1{\csname\??columnfooter\number#1\endcsname}
+\permanent\def\columntopbox #1{\csname\??columntop \number#1\endcsname}
+\permanent\def\columnbotbox #1{\csname\??columnbottom\number#1\endcsname}
+
+\permanent\protected\def\columnsettextbox{\global\setbox\columntextbox}
+\permanent\protected\def\columnsetfootbox{\global\setbox\columnfootbox}
+\permanent\protected\def\columnsettopbox {\global\setbox\columntopbox}
+\permanent\protected\def\columnsetbotbox {\global\setbox\columnbotbox}
+
+\permanent\protected\def\columngettextbox {\copy\columntextbox}
+\permanent\protected\def\columngetfootbox {\copy\columnfootbox}
+\permanent\protected\def\columngettopbox {\copy\columntopbox}
+\permanent\protected\def\columngetbotbox {\copy\columnbotbox}
+
+\permanent\protected\def\columnerasetextboxes{\dorecurse\allocatednofcolumns{\columnsettextbox\recurselevel\emptybox}}
+\permanent\protected\def\columnerasefootboxes{\dorecurse\allocatednofcolumns{\columnsetfootbox\recurselevel\emptybox}}
+\permanent\protected\def\columnerasetopboxes {\dorecurse\allocatednofcolumns{\columnsettopbox \recurselevel\emptybox}}
+\permanent\protected\def\columnerasebotboxes {\dorecurse\allocatednofcolumns{\columnsetbotbox \recurselevel\emptybox}}
+
+%D Without going in details we present two macro's which handle the columns. The
+%D action which is transfered by the the first and only parameter can do something
+%D with \type {\currentcolumnbox}. In case of the mid columns, \type
+%D {\firstcolumnbox} and \type {\lastcolumnbox} are handled outside these macro's.
+
+\let\page_columns_action\relax
+
+\permanent\protected\def\dohandleallcolumnscs#1{\let\page_columns_action #1\page_columns_all_indeed}
+\permanent\protected\def\dohandleallcolumns #1{\def\page_columns_action{#1}\page_columns_all_indeed}
+
+\permanent\protected\def\dohandlerevcolumnscs#1{\let\page_columns_action #1\page_columns_rev_indeed}
+\permanent\protected\def\dohandlerevcolumns #1{\def\page_columns_action{#1}\page_columns_rev_indeed}
+
+\permanent\protected\def\dohandlemidcolumnscs#1{\let\page_columns_action #1\page_columns_mid_indeed}
+\permanent\protected\def\dohandlemidcolumns #1{\def\page_columns_action{#1}\page_columns_mid_indeed}
+
+\protected\def\page_columns_all_indeed
+ {\mofcolumns\plusone
+ \edef\currentcolumn{\the\mofcolumns}%
+ \page_columns_action\relax
+ \ifnum\mofcolumns<\nofcolumns
+ \expandafter\page_columns_all_next
+ \fi}
+
+\protected\def\page_columns_all_next
+ {\advance\mofcolumns\plusone
+ \edef\currentcolumn{\the\mofcolumns}%
+ \page_columns_action\relax
+ \ifnum\mofcolumns<\nofcolumns
+ \expandafter\page_columns_all_next
+ \fi}
+
+\protected\def\page_columns_rev_indeed
+ {\mofcolumns\nofcolumns
+ \edef\currentcolumn{\the\mofcolumns}%
+ \page_columns_action\relax
+ \ifnum\mofcolumns>\plusone
+ \expandafter\page_columns_rev_prev
+ \fi}
+
+\protected\def\page_columns_rev_prev
+ {\advance\mofcolumns\minusone
+ \edef\currentcolumn{\the\mofcolumns}%
+ \page_columns_action\relax
+ \ifnum\mofcolumns>\plusone
+ \expandafter\page_columns_rev_prev
+ \fi}
+
+\protected\def\page_columns_mid_indeed
+ {\mofcolumns\plustwo
+ \ifnum\mofcolumns<\nofcolumns
+ \expandafter\page_columns_mid_next
+ \fi}
+
+\protected\def\page_columns_mid_next
+ {\edef\currentcolumn{\the\mofcolumns}%
+ \page_columns_action\relax
+ \advance\mofcolumns\plusone
+ \ifnum\mofcolumns<\nofcolumns
+ \expandafter\page_columns_mid_next
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-com.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-com.mkxl
new file mode 100644
index 00000000000..a7d8aaba71a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-com.mkxl
@@ -0,0 +1,180 @@
+%D \module
+%D [ file=page-com, % moved from page-imp
+%D version=1998.01.15,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Comments,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This will become runtime loaded code.
+
+\writestatus{loading}{ConTeXt Page Macros / Page Comments}
+
+\unprotect
+
+%D \macros
+%D {setuppagecomment,startpagecomment}
+%D
+%D This command is not yet documented. Usage:
+%D
+%D \starttyping
+%D \setuppagecomment[state=start,location=right]
+%D
+%D \startpagecomment
+%D \input knuth
+%D \stoppagecomment
+%D \stoptyping
+
+\installcorenamespace{pagecomment}
+\installcorenamespace{pagecommentlocations}
+\installcorenamespace{pagecommentstates}
+
+\newdimen\d_page_comments_offset
+\newdimen\d_page_comments_page_width
+\newdimen\d_page_comments_page_height
+\newdimen\d_page_comments_width
+\newdimen\d_page_comments_height
+\newdimen\d_page_comments_distance
+
+\newconditional\c_page_comment_enabled
+
+\let\p_page_commands_location\v!none
+\let\p_page_commands_state \v!stop
+
+\installsetuponlycommandhandler \??pagecomment {pagecomment}
+
+\appendtoks
+ \edef\p_page_commands_location{\directpagecommentparameter\c!location}%
+ \edef\p_page_commands_state {\directpagecommentparameter\c!state}%
+ \expandnamespacemacro\??pagecommentstates\p_page_commands_state\v!none
+\to \everysetuppagecomment
+
+% \ifdefined\scrn_canvas_synchronize_simple
+
+ \appendtoks
+ \ifx\p_page_commands_state\v!start
+ \scrn_canvas_synchronize_simple
+ \else
+ \scrn_canvas_synchronize_complex
+ \fi
+ \to \everyshipout
+
+% \fi
+
+\setvalue{\??pagecommentstates\v!start}%
+ {\d_page_comments_offset \directpagecommentparameter\c!offset \relax
+ \d_page_comments_distance\directpagecommentparameter\c!distance\relax
+ \d_page_comments_width \directpagecommentparameter\c!width \relax
+ \d_page_comments_height \directpagecommentparameter\c!height \relax
+ \expandnamespacemacro\??pagecommentlocations\p_page_commands_location\v!none
+ \definepapersize[\v!pagecomment][\c!height=\d_page_comments_page_height,\c!width=\d_page_comments_page_width]%
+ \setuppapersize[\papersize][\v!pagecomment]%
+ \setupbackgrounds[\v!paper][\c!background=\v!pagecomment]} % maybe append if already set
+
+\setvalue{\??pagecommentstates\v!stop}%
+ {\setupbackgrounds[\v!paper][\c!background=]}
+
+\setvalue{\??pagecommentstates\v!none}%
+ {}
+
+\setvalue{\??pagecommentlocations\v!bottom}%
+ {\setuplayout[\c!location=]%
+ \setuppapersize[\c!top=\vskip\d_page_comments_offset,\c!bottom=\vss,\c!left=\hskip\d_page_comments_offset,\c!right=]%
+ \d_page_comments_page_height\dimexpr\paperheight+2\d_page_comments_offset+\d_page_comments_distance+\d_page_comments_height\relax
+ \d_page_comments_page_width \dimexpr\paperwidth +2\d_page_comments_offset\relax
+ \defineoverlay[\v!pagecomment][\page_comments_top_bottom]}
+
+\setvalue{\??pagecommentlocations\v!top}%
+ {\setuplayout[\c!location=]%
+ \setuppapersize[\c!top=\vss,\c!bottom=\vskip\d_page_comments_offset,\c!left=\hskip\d_page_comments_offset,\c!right=]%
+ \d_page_comments_page_height\dimexpr\paperheight+2\d_page_comments_offset+\d_page_comments_distance+\d_page_comments_height\relax
+ \d_page_comments_page_width \dimexpr\paperwidth +2\d_page_comments_offset\relax
+ \defineoverlay[\v!pagecomment][\page_comments_top_bottom]}
+
+\setvalue{\??pagecommentlocations\v!left}%
+ {\setuplayout[\c!location=]%
+ \setuppapersize[\c!top=\vskip\d_page_comments_offset,\c!bottom=,\c!left=\hss,\c!right=\hskip\d_page_comments_offset]%
+ \d_page_comments_page_height\dimexpr\paperheight+2\d_page_comments_offset\relax
+ \d_page_comments_page_width \dimexpr\paperwidth +2\d_page_comments_offset+\d_page_comments_distance+\d_page_comments_width\relax
+ \defineoverlay[\v!pagecomment][\page_comments_left_right]}
+
+\setvalue{\??pagecommentlocations\v!right}%
+ {\setuplayout[\c!location=]%
+ \setuppapersize[\c!top=\vskip\d_page_comments_offset,\c!bottom=,\c!left=\hskip\d_page_comments_offset,\c!right =\hss]%
+ \d_page_comments_page_height\dimexpr\paperheight+2\d_page_comments_offset\relax
+ \d_page_comments_page_width \dimexpr\paperwidth +2\d_page_comments_offset+\d_page_comments_distance+\d_page_comments_width\relax
+ \defineoverlay[\v!pagecomment][\page_comments_left_right]}
+
+\setvalue{\??pagecommentlocations\v!none}%
+ {}% \setuppapersize[\c!bottom=,\c!top=,\c!left=,\c!right=]}
+
+\protected\def\page_comments_top_bottom
+ {\vpack to \printpaperheight
+ {%\forgetall
+ \hsize\printpaperwidth\relax
+ \ifx\p_page_commands_location\v!bottom
+ \vskip\dimexpr\paperheight+\d_page_comments_distance+\d_page_comments_offset\relax
+ \else
+ \vskip\d_page_comments_offset
+ \vss
+ \fi
+ \hskip\d_page_comments_offset
+ \vbox to \d_page_comments_height
+ {%\forgetall
+ \hsize\paperwidth
+ \ifconditional\c_page_comment_enabled
+ \getbuffer[\v!pagecomment]%
+ \global\setfalse\c_page_comment_enabled
+ \fi}%
+ \hfill
+ \ifx\p_page_commands_location\v!bottom
+ \vss
+ \vskip\d_page_comments_offset
+ \else
+ \vskip\dimexpr\paperheight+\d_page_comments_distance+\d_page_comments_offset\relax
+ \fi}}
+
+\protected\def\page_comments_left_right
+ {\hpack to \printpaperwidth
+ {\ifx\p_page_commands_location\v!right
+ \hskip\dimexpr\paperwidth+\d_page_comments_distance+\d_page_comments_offset\relax
+ \else
+ \hskip\d_page_comments_offset
+ \hss
+ \fi
+ \vbox to \printpaperheight
+ {%\forgetall
+ \vskip\d_page_comments_offset
+ \hsize\d_page_comments_width
+ \ifconditional\c_page_comment_enabled
+ \getbuffer[\v!pagecomment]%
+ \global\setfalse\c_page_comment_enabled
+ \fi
+ \vss}%
+ \ifx\p_page_commands_location\v!right
+ \hss
+ \hskip\d_page_comments_offset
+ \else
+ \hskip\dimexpr\paperwidth+\d_page_comments_distance+\d_page_comments_offset\relax
+ \fi}}
+
+\permanent\protected\setvalue{\e!start\v!pagecomment}%
+ {\global\settrue\c_page_comment_enabled
+ \grabbufferdatadirect\v!pagecomment{\e!start\v!pagecomment}{\e!stop\v!pagecomment}}
+
+\aliased\letcsname\e!stop\v!pagecomment\endcsname\relax
+
+\setuppagecomment
+ [\c!state=, % \v!stop would invoke background calculation
+ \c!location=\v!bottom,
+ \c!offset=.5cm,
+ \c!distance=.5cm,
+ \c!height=5cm,
+ \c!width=10cm]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-cst.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-cst.mkxl
new file mode 100644
index 00000000000..a6f63a56219
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-cst.mkxl
@@ -0,0 +1,791 @@
+%D \module
+%D [ file=page-cst,
+%D version=2016.12.15,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Grids (aka Column Sets),
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo : markings per column
+
+%D This module is work in progress and in due time it will replace
+%D columnsets.
+
+\writestatus{loading}{ConTeXt Page Macros / Page Grids}
+
+\registerctxluafile{page-cst}{}
+
+\unprotect
+
+% maybe some protected def ([esc])
+
+%D Columnsets are kind of special. They are mostly meant for special products with
+%D magazine like properties. They are normally not mixed with single column layouts
+%D and not all features of \CONTEXT\ might cooperate well with a mechanism like
+%D this. We use the name page grid because (as with other reimplementations of
+%D \MKII\ features in \MKIV, we need another namespace in order to migrate stepwise.
+%D
+%D This implementation is not neccessarily better than the previous one but it might
+%D be easier to extend it. It should be a bit more efficient.
+%D
+%D When writing this code I occasionally needed a motivational musical time||out and
+%D watching the latest Snarky Puppy DVD brought me the musically and visually videos
+%D of Jacob Collier (Piano, voice, anything) on YouTube (and yes, music keeps amazing
+%D me). It's definitely more fun to watch that than to write code like this.
+
+\definesystemconstant{pagegrid}
+
+\installcorenamespace{pagegrid}
+
+\installframedcommandhandler \??pagegrid {pagegrid} \??pagegrid
+
+\setuppagegrid
+ [\c!distance=1.5\bodyfontsize,
+ \c!n=\plustwo,
+ \c!nleft=\pagegridparameter\c!n,
+ \c!nright=\pagegridparameter\c!n,
+ %\c!align=, % inherit
+ %\c!separator=\v!none,
+ %\c!setups=,
+ \c!lines=\layoutparameter\c!lines,
+ \c!frame=\v!off,
+ \c!strut=\v!no,
+ \c!offset=\v!overlay,
+ \c!alternative=\v!local,
+ \c!width=\v!auto,
+ \c!page=,
+ \c!direction=\v!normal, % todo
+ \c!maxheight=\textheight,
+ \c!maxwidth=\makeupwidth]
+
+\appendtoks % could become an option
+ \frozen\instance\setuevalue{\e!start\currentpagegrid}{\startpagegrid[\currentpagegrid]}%
+ \frozen\instance\setuevalue{\e!stop \currentpagegrid}{\stoppagegrid}%
+ \clf_definecolumnset {
+ name {\currentpagegrid}%
+ }%
+\to \everydefinepagegrid
+
+\newdimen\d_page_grd_column_width
+\newdimen\d_page_grd_max_height
+\newdimen\d_page_grd_max_width
+\newdimen\d_page_grd_distance
+
+\newdimen\d_page_grd_reserved_height
+\newdimen\d_page_grd_reserved_width
+\newcount\c_page_grd_reserved_state
+
+\newdimen\d_page_grd_gap_height
+
+\newcount\c_page_grd_n_of_left
+\newcount\c_page_grd_n_of_right
+\newcount\c_page_grd_n_of_rows
+\newcount\c_page_grd_first_column
+\newcount\c_page_grd_last_column
+
+\newbox \b_page_grd_collected
+\newbox \b_page_grd_column_rest
+\newbox \b_page_grd_column
+
+%D All the parameters are mandate!
+
+\permanent\tolerant\protected\def\setuppagegridlines[#1]#*[#2]#*[#3]#*[#4]% id page col value
+ {\clf_setcolumnsetlines{name {#1} page #2 column #3 value #4}}
+
+\permanent\tolerant\protected\def\setuppagegridstart[#1]#*[#2]#*[#3]#*[#4]% id page col value
+ {\clf_setcolumnsetstart{name {#1} page #2 column #3 value #4}}
+
+\protected\def\page_grd_check
+ {\dorecurse{\numexpr\pagegridparameter\c!nleft+\pagegridparameter\c!nright}
+ {\page_grd_check_column{##1}}}
+
+\protected\def\page_grd_check_column#1%
+ {\chaintocurrentpagegrid{\currentpagegrid:#1}%
+ \edef\p_distance{\namedpagegridparameter{\currentpagegrid:#1}\c!distance}%
+ \edef\p_width {\namedpagegridparameter{\currentpagegrid:#1}\c!width}%
+ \clf_setcolumnsetproperties {%
+ name {\currentpagegrid}
+ column \numexpr#1\relax
+ \ifempty\p_distance\else
+ distance \p_distance
+ \fi
+ \ifempty\p_width\orelse\ifx\p_width\v!auto\else
+ width \p_width
+ \fi
+ }}
+
+\appendtoks
+ \dorecurse{\numexpr\pagegridparameter\c!nleft+\pagegridparameter\c!nright}%
+ {\chaintocurrentpagegrid{\currentpagegrid:#1}}%
+\to \everydefinepagegrid
+
+\permanent\tolerant\protected\def\startpagegrid[#1]#*[#2]%
+ {\bgroup
+ \enforced\let\startpagegrid\page_grd_start_dummy
+ \ifarguments
+ \let\currentpagegrid\empty
+ \or
+ \ifhastok={#1}%
+ \let\currentpagegrid\empty
+ \setupcurrentpagegrid[#1]%
+ \else
+ \def\currentpagegrid{#1}%
+ \fi
+ \or
+ \edef\currentpagegrid{#1}%
+ \setupcurrentpagegrid[#2]%
+ \fi
+ \usepageparameter\pagegridparameter
+ \c_page_grd_n_of_left \pagegridparameter\c!nleft\relax
+ \c_page_grd_n_of_right\pagegridparameter\c!nright\relax
+ \c_page_grd_n_of_rows \pagegridparameter\c!lines\relax
+ \d_page_grd_max_width \pagegridparameter\c!maxwidth\relax
+ \d_page_grd_max_height\pagegridparameter\c!maxheight\relax
+ \d_page_grd_distance \pagegridparameter\c!distance\relax
+ %
+ \ifcase\c_page_grd_n_of_rows
+ \getrawnoflines{\dimexpr\d_page_grd_max_height-\strutheight+\topskip\relax}%
+ \c_page_grd_n_of_rows\noflines
+ \fi
+ \edef\p_width{\pagegridparameter\c!width}%
+ \insidecolumnstrue % will be different flag in addition
+ \clf_resetcolumnset {
+ name {\currentpagegrid}
+ nofrows \c_page_grd_n_of_rows
+ nofleft \c_page_grd_n_of_left
+ nofright \c_page_grd_n_of_right
+ lineheight \strutht
+ linedepth \strutdp
+ \ifx\p_width\v!auto
+ % sets \d_page_grd_column_width
+ \else
+ width \p_width
+ \fi
+ distance \d_page_grd_distance
+ maxwidth \d_page_grd_max_width
+ }%
+ %
+ \page_grd_check
+ %
+ \clf_flushcolumnsetareas{\currentpagegrid}\relax
+ \setupoutputroutine[\s!pagegrid]%
+ \page_grd_command_set_hsize
+ \page_grd_command_set_vsize
+ %
+ \columnwidth \d_page_grd_column_width
+ \columndistance\d_page_grd_distance
+ \nofcolumns \c_page_grd_n_of_left % not always ok
+ \textwidth \d_page_grd_column_width % kind of redundant but we had it so ...
+ %
+ }%\begingroup}
+
+\tolerant\def\page_grd_start_dummy[#1]#*[#2]%
+ {\let\page_grd_stop\egroup}
+
+\permanent\protected\def\stoppagegrid
+ {\page_grd_stop}
+
+\def\page_grd_stop
+ {\endgraf % needed, else wrong vsize in one par case
+ \vfill % otherwise weird \placenotes[endnotes]
+ \page_grd_command_set_vsize % needed
+ \penalty\c_page_otr_eject_penalty
+ \page_grd_command_flush_page
+ \page_otr_fill_and_eject_page
+ \page_grd_command_set_vsize % why here
+ \egroup
+ \page_otr_command_set_vsize
+ \page_otr_command_set_hsize}
+
+\permanent\protected\def\reservepagegrid[#1]%
+ {\begingroup
+ \letdummyparameter\c!c\plusone
+ \letdummyparameter\c!r\plusone
+ \letdummyparameter\c!nc\plusone
+ \letdummyparameter\c!nr\plusone
+ \getdummyparameters[#1]%
+ \clf_blockcolumnset {
+ name {\currentpagegrid}
+ c \dummyparameter\c!c
+ r \dummyparameter\c!r
+ nc \dummyparameter\c!nc
+ nr \dummyparameter\c!nr
+ }%
+ \endgroup}
+
+\permanent\tolerant\protected\def\setpagegrid[#1]%
+ {\begingroup
+ \letdummyparameter\c!c\zerocount
+ \letdummyparameter\c!r\zerocount
+ \letdummyparameter\c!method\v!here
+ \letdummyparameter\c!option\v!none
+ \getdummyparameters[#1]%
+ \dowithnextboxcs\page_grd_set_indeed\hbox}
+
+\def\page_grd_set_indeed
+ {\clf_checkcolumnset {
+ name {\currentpagegrid}
+ c \dummyparameter\c!c
+ r \dummyparameter\c!r
+ box \nextbox
+ method {\dummyparameter\c!method}
+ option {\dummyparameter\c!option}
+ }%
+ \ifcase\c_page_grd_reserved_state
+ \setbox\nextbox\vpack to \d_page_grd_reserved_height \bgroup
+ \vss
+ \hpack to \d_page_grd_reserved_width \bgroup
+ \box\nextbox
+ \hss
+ \egroup
+ \vss
+ \egroup
+ \wd\nextbox\d_page_grd_reserved_width
+ \clf_putincolumnset {
+ name {\currentpagegrid}
+ box \nextbox
+ }%
+ \fi
+ \endgroup}
+
+
+\protected\def\page_grd_command_set_vsize
+ {\clf_setvsizecolumnset{\currentpagegrid}%
+ \ifdim\d_page_grd_gap_height<\lineheight
+ \page_grd_command_flush_page
+ \page_otr_fill_and_eject_page
+ \fi
+ \global\vsize\d_page_grd_gap_height
+ \pagegoal\vsize}
+
+\protected\def\page_grd_command_set_hsize
+ {\clf_sethsizecolumnset{\currentpagegrid}%
+ \hsize\d_page_grd_column_width
+ \textwidth\d_page_grd_column_width}
+
+\protected\def\page_grd_command_routine
+ {\ifvoid\normalpagebox \else
+ \clf_addtocolumnset{\currentpagegrid}\normalpagebox
+ \fi
+ \page_grd_command_set_vsize
+ \page_grd_command_flush_saved_floats
+ \page_grd_command_set_vsize
+ \ifdim\d_page_grd_gap_height<\lineheight
+ \page_grd_command_flush_page
+ \fi
+ \page_grd_command_set_vsize
+ \clf_flushcolumnsetrest {\currentpagegrid}\normalpagebox
+ \ifvoid\normalpagebox \else
+ \unvbox\normalpagebox
+ \fi}
+
+\installoutputroutine\synchronizepagegrid
+ {\ifvoid\normalpagebox\else
+ \clf_addtocolumnset{\currentpagegrid}\normalpagebox
+ \page_grd_command_set_vsize
+ \clf_flushcolumnsetrest{\currentpagegrid}\normalpagebox
+ \ifvoid\normalpagebox \else
+ \unvbox\normalpagebox
+ \fi
+ \fi}
+
+% todo line numbers and marks
+
+\protected\def\page_grd_command_flush_page_column#1%
+ {\privatescratchcounter#1\relax % or just currentcolumn as in page-col.mkiv
+ \clf_flushcolumnsetcolumn{\currentpagegrid}\privatescratchcounter
+ \anch_mark_column_box\b_page_grd_column\privatescratchcounter
+ \page_marks_synchronize_column\c_page_grd_first_column\c_page_grd_last_column\privatescratchcounter\b_page_grd_column
+ \ifnum\privatescratchcounter>\c_page_grd_n_of_left
+ \advance\privatescratchcounter-\c_page_grd_n_of_left
+ \page_lines_add_numbers_to_box\b_page_grd_column\privatescratchcounter\c_page_grd_n_of_right\plustwo
+ \else
+ \page_lines_add_numbers_to_box\b_page_grd_column\privatescratchcounter\c_page_grd_n_of_left\plustwo
+ \fi
+ \begingroup
+ \edef\currentpagegrid{\currentpagegrid:#1}%
+ \inheritedpagegridframedbox\box\b_page_grd_column
+ \endgroup}
+
+\protected\def\page_grd_command_flush_page
+ {\deactivatecolor % puzzling, try ungrouped color \red or so
+ \setbox\b_page_grd_collected\hpack\bgroup
+ \clf_preparecolumnsetflush{\currentpagegrid}%
+ \letpagegridparameter\c!region\currentpagegrid
+ \doifelse{\pagegridparameter\c!direction}\v!reverse
+ {\dostepwiserecurse\c_page_grd_last_column\c_page_grd_first_column\minusone
+ {\page_grd_command_flush_page_column{##1}%
+ \ifnum##1>\plusone
+ \kern\namedpagegridparameter{\currentpagegrid:##1}\c!distance\relax
+ \fi}}%
+ {\dostepwiserecurse\c_page_grd_first_column\c_page_grd_last_column\plusone
+ {\page_grd_command_flush_page_column{##1}%
+ \ifnum##1<\c_page_grd_last_column
+ \kern\namedpagegridparameter{\currentpagegrid:##1}\c!distance\relax
+ \fi}}%
+ \clf_finishcolumnsetflush{\currentpagegrid}%
+ \egroup
+ \page_otr_construct_and_shipout\box\b_page_grd_collected\zerocount % three arguments
+ \clf_flushcolumnsetareas{\currentpagegrid}\relax
+ \page_grd_command_flush_saved_floats}
+
+% slow but robust
+
+\protected\def\page_grd_command_next_progress
+ {\strut
+ \page_grd_command_flush_all_floats
+ \page_otr_eject_page
+ \ifcase\clf_columnsetnoto\else
+ \expandafter\page_grd_command_next_progress
+ \fi}
+
+\protected\def\page_grd_command_handle_column
+ {\ifcase\clf_columnsetgoto{\currentpagegrid}{\page_breaks_columns_current_option}\relax\else
+ \expandafter\page_grd_command_next_progress
+ \fi}
+
+\installcolumnbreakmethod\s!pagegrid\s!unknown {\page_grd_command_handle_column}
+\installcolumnbreakmethod\s!pagegrid\v!yes {\page_grd_command_handle_column}
+
+\protected\def\page_grd_command_next_page
+ {\ifcase\clf_columnsetgoto{\currentpagegrid}{\v!page}\relax\else
+ \page_grd_command_flush_page
+ \fi}
+
+\protected\def\page_grd_command_next_page_and_inserts
+ {\page_grd_command_flush_all_floats
+ \page_grd_command_next_page}
+
+\let\page_grd_command_flush_all_floats\page_one_command_flush_all_floats
+\let\page_grd_command_package_contents\page_one_command_package_contents
+
+\protected\def\page_grd_command_flush_saved_floats
+ {\ifconditional\c_page_floats_flushing \else
+ \ifconditional\c_page_floats_some_waiting
+ \page_grd_command_flush_saved_floats_indeed
+ \fi
+ \fi}
+
+\protected\def\page_grd_command_flush_saved_floats_indeed
+ {\page_floats_flush\s!text\plusone
+ \clf_checkcolumnset {
+ name {\currentpagegrid}
+ method {\floatmethod}
+ width \wd\floatbox
+ height \ht\floatbox
+ }%
+ \ifcase\c_page_grd_reserved_state
+ \page_grd_place_float_here_indeed
+ \page_grd_command_set_vsize % needed
+ \ifconditional\c_page_floats_some_waiting
+ \doubleexpandafter\page_grd_command_flush_saved_floats_indeed
+ \fi
+ \else
+ \page_floats_resave\s!text
+ \fi}
+
+% needs checking
+
+\protected\def\page_grd_command_flush_floats
+ {\wait\global\settrue\c_page_floats_flushing
+ \ifconditional\c_page_floats_some_waiting
+ \par
+ \page_grd_command_flush_floats_indeed
+ \fi
+ \global\savednoffloats\zerocount
+ \global\setfalse\c_page_floats_some_waiting
+ \global\setfalse\c_page_floats_flushing}
+
+\def\page_grd_command_flush_floats_indeed % much in common with OTRSET
+ {\ifconditional\c_page_floats_some_waiting
+ \ifconditional\c_page_floats_compress_flushed
+ \page_floats_collect\s!text\hsize\d_page_floats_compress_distance
+ \ifcase\nofcollectedfloats
+ \page_floats_get
+ % \or
+ % \page_floats_get
+ \else
+ \setfalse\c_page_floats_center_box % not needed as we do call directly
+ \global\setbox\floatbox\hbox to \hsize
+ {\hfil
+ \dorecurse\nofcollectedfloats
+ {\ifcase\columndirection % nog document wide
+ \page_floats_flush\s!text\plusone
+ \else
+ \page_floats_flush\s!text{\the\numexpr\nofcollectedfloats-\recurselevel+1\relax}%
+ \fi
+ \ifdim\wd\floatbox>\makeupwidth % \hsize
+ \hbox to \makeupwidth{\hss\box\floatbox\hss}%
+ \else
+ \box\floatbox
+ \fi
+ \ifnum\recurselevel<\nofcollectedfloats
+ \hfil
+ \fi}%
+ \hfil}%
+ \fi
+ \else
+ \page_floats_get
+ \fi
+ \doplacefloatbox
+ \expandafter\page_grd_command_flush_floats_indeed
+ \fi}
+
+% so far
+
+\protected\def\page_grd_command_check_if_float_fits
+ {\clf_checkcolumnset {
+ name {\currentpagegrid}
+ method {\floatmethod}
+ % c \zerocount
+ % r \zerocount
+ box \floatbox
+ }%
+ \ifcase\c_page_grd_reserved_state
+ \global\settrue\c_page_floats_room
+ \else
+ \global\setfalse\c_page_floats_room
+ \fi}
+
+\protected\def\page_grd_place_float_here_indeed
+ {\setbox\floatbox\vpack to \d_page_grd_reserved_height \bgroup
+ \vss
+ \hpack to \d_page_grd_reserved_width \bgroup
+ % \hss % no
+ \box\floatbox
+ \hss
+ \egroup
+ \vss
+ \egroup
+ \clf_putincolumnset {
+ name {\currentpagegrid}
+ box \floatbox
+ }}
+
+\def\page_grd_place_float_slot
+ {% safeguard
+ \ifempty\floatmethod
+ \let\floatmethod\v!here
+ \fi
+ % synchronize
+ \penalty\c_page_otr_eject_penalty
+ % push
+ \setbox\savedfloatbox\box\floatbox
+ \page_grd_command_flush_saved_floats
+ \setbox\floatbox\box\savedfloatbox
+ % pop
+ \ifconditional\c_page_floats_some_waiting
+ \page_floats_save\s!text
+ \nonoindentation
+ \else
+ \clf_checkcolumnset {
+ name {\currentpagegrid}
+ method {\floatmethod}
+ \ifempty\floatcolumn \else
+ c \floatcolumn
+ \fi
+ \ifempty\floatrow \else
+ r \floatrow
+ \fi
+ box \floatbox
+ }%
+ \ifcase\c_page_grd_reserved_state
+ \page_grd_place_float_here_indeed
+ \else
+ \page_floats_save\s!text
+ \nonoindentation
+ \fi
+ \fi}
+
+\def\page_grd_place_float_fixed % todo: fallback on here
+ {\ifempty\floatcolumn
+ \let\floatmethod\v!here
+ \orelse\ifempty\floatrow
+ \let\floatmethod\v!here
+ \else
+ \let\floatmethod\v!fixed
+ \fi
+ \page_grd_place_float_slot}
+
+\def\page_grd_place_float_force
+ {% synchronize
+ \penalty\c_page_otr_eject_penalty
+ \clf_checkcolumnset {
+ name {\currentpagegrid}
+ method {\floatmethod}
+ box \floatbox
+ }%
+ \ifcase\c_page_grd_reserved_state
+ \page_grd_place_float_here_indeed
+ \else
+ \page_floats_save\s!text
+ \nonoindentation
+ \fi}
+
+\def\page_grd_place_float_page {\page_grd_place_float_slot} % todo: fallback on here
+
+\def\page_grd_place_float_here {\let\floatmethod\v!here\page_grd_place_float_slot}
+\def\page_grd_place_float_top {\page_grd_place_float_slot}
+\def\page_grd_place_float_bottom{\page_grd_place_float_slot}
+
+\installfloatmethod \s!pagegrid \v!here \page_grd_place_float_here
+\installfloatmethod \s!pagegrid \v!force \page_grd_place_float_force % todo
+%installfloatmethod \s!pagegrid \v!left
+%installfloatmethod \s!pagegrid \v!right
+%installfloatmethod \s!pagegrid \v!text
+\installfloatmethod \s!pagegrid \v!top \page_grd_place_float_top
+\installfloatmethod \s!pagegrid \v!bottom \page_grd_place_float_bottom
+%installfloatmethod \s!pagegrid \v!auto
+%installfloatmethod \s!pagegrid \v!margin
+%installfloatmethod \s!pagegrid \v!opposite
+\installfloatmethod \s!pagegrid \v!page \page_grd_place_float_page
+%installfloatmethod \s!pagegrid \v!leftpage
+%installfloatmethod \s!pagegrid \v!rightpage
+%installfloatmethod \s!pagegrid \v!inmargin
+%installfloatmethod \s!pagegrid \v!inleft
+%installfloatmethod \s!pagegrid \v!inright
+%installfloatmethod \s!pagegrid \v!leftmargin
+%installfloatmethod \s!pagegrid \v!rightmargin
+%installfloatmethod \s!pagegrid \v!leftedge
+%installfloatmethod \s!pagegrid \v!rightedge
+%installfloatmethod \s!pagegrid \v!somewhere
+%installfloatmethod \s!pagegrid \v!backspace
+%installfloatmethod \s!pagegrid \v!cutspace
+\installfloatmethod \s!pagegrid \s!tblr \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!lrtb \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!tbrl \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!rltb \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!fxtb \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!btlr \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!lrbt \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!btrl \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!rlbt \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!fxbt \page_grd_place_float_slot
+\installfloatmethod \s!pagegrid \s!fixd \page_grd_place_float_fixed
+
+\protected\def\page_grd_command_side_float_output
+ {} % nothing, reset anyway
+
+\protected\def\page_grd_command_flush_side_floats
+ {\page_sides_forget_floats}
+
+\protected\def\page_grd_command_synchronize_side_floats
+ {\page_sides_forget_floats}
+
+\protected\def\page_grd_command_synchronize_hsize
+ {\page_grd_command_set_hsize}
+
+\protected\def\page_grd_command_flush_all_floats
+ {\page_one_command_flush_all_floats}
+
+\defineoutputroutine
+ [\s!pagegrid]
+ [\s!page_otr_command_routine =\page_grd_command_routine,
+ \s!page_otr_command_package_contents =\page_grd_command_package_contents,
+ \s!page_otr_command_set_vsize =\page_grd_command_set_vsize,
+ \s!page_otr_command_set_hsize =\page_grd_command_set_hsize, % tricky, goes wrong
+ \s!page_otr_command_synchronize_hsize =\page_grd_command_synchronize_hsize,
+ \s!page_otr_command_next_page =\page_grd_command_next_page,
+ \s!page_otr_command_next_page_and_inserts =\page_grd_command_next_page_and_inserts,
+ % \s!page_otr_command_set_top_insertions =\page_grd_command_set_top_insertions,
+ % \s!page_otr_command_set_bottom_insertions =\page_grd_command_set_bottom_insertions,
+ % \s!page_otr_command_flush_top_insertions =\page_grd_command_flush_top_insertions,
+ % \s!page_otr_command_flush_bottom_insertions=\page_grd_command_flush_bottom_insertions,
+ \s!page_otr_command_check_if_float_fits =\page_grd_command_check_if_float_fits,
+ % \s!page_otr_command_set_float_hsize =\page_grd_command_set_float_hsize,
+ % \s!page_otr_command_flush_float_box =\page_grd_command_flush_float_box,
+ \s!page_otr_command_synchronize_side_floats=\page_grd_command_synchronize_side_floats,
+ \s!page_otr_command_side_float_output =\page_grd_command_side_float_output,
+ \s!page_otr_command_flush_floats =\page_grd_command_flush_floats,
+ \s!page_otr_command_flush_side_floats =\page_grd_command_flush_side_floats,
+ \s!page_otr_command_flush_saved_floats =\page_grd_command_flush_saved_floats,
+ \s!page_otr_command_flush_all_floats =\page_grd_command_flush_all_floats,
+ % \s!page_otr_command_flush_margin_blocks =\page_grd_command_flush_margin_blocks, % not used
+ ]
+
+% spans
+
+\installcorenamespace{pagegridspan}
+
+\installframedcommandhandler \??pagegridspan {pagegridspan} \??pagegridspan
+
+\setuppagegridspan
+ [\c!frame=\v!off,
+ \c!before=,
+ \c!after=,
+ \c!offset=\v!overlay,
+ \c!location=\v!left,
+ \c!linecorrection=\v!off,
+ \c!depthcorrection=\v!off,
+ \c!n=\plustwo,
+ \c!nlines=\zerocount,
+ \c!align=\v!normal,
+ \c!width=\d_page_grd_span_width,
+ \c!indenting=,
+ \c!indentnext=\v!yes,
+ \c!default=\v!here,
+ \c!alternative=\v!a]
+
+\newdimen\d_page_grd_span_width
+
+\let\page_grd_span_stop\relax
+
+\permanent\tolerant\protected\def\startpagegridspan[#1]#*[#2]#*[#3]% [#3] gobbles space
+ {\endgraf % else rubish output if forgotten
+ \synchronizepagegrid
+ \bgroup
+ \forgetall
+ \edef\currentpagegridspan{#1}%
+ \clf_sethsizecolumnspan{\currentpagegrid}\pagegridspanparameter\c!n\relax
+ \setbox\scratchbox\hbox\bgroup\inheritedpagegridspanframed\bgroup
+ \def\page_grd_span_stop{\page_grd_span_stop_indeed{#2}}%
+ \usepagegridspanstyleandcolor\c!style\c!color
+ \pagegridspanparameter\c!before
+ \ignorespaces}
+
+\protected\def\page_grd_span_stop_indeed#1%
+ {\removeunwantedspaces
+ \par
+ \verticalstrut
+ \kern-2\struttotal
+ \verticalstrut
+ \endgraf
+ \pagegridspanparameter\c!after
+ \egroup\egroup
+ \setpagegrid[#1]{\box\scratchbox}%
+ % todo: push into slot
+ \egroup
+ \endgraf}
+
+\permanent\protected\def\stoppagegridspan % indirectness permits aliasing
+ {\page_grd_span_stop}
+
+\permanent\def\pagegridspanwidth#1% assumes equal distances
+ {\the\dimexpr
+ #1\d_page_grd_column_width
+ +#1\d_page_grd_distance
+ - \d_page_grd_distance
+ \relax}
+
+% areas
+
+\installcorenamespace{pagegridarea}
+
+\installframedcommandhandler \??pagegridarea {pagegridarea} \??pagegridarea
+
+\setuppagegridarea
+ [\c!x=\plusone,
+ \c!y=\plusone,
+ \c!nx=\plusone,
+ \c!ny=\plusone,
+ \c!clipoffset=2\lineheight,
+ \c!leftoffset=\zeropoint,
+ \c!rightoffset=\zeropoint,
+ \c!offset=\v!overlay,
+ \c!strut=\v!no,
+ \c!frame=\v!off,
+ %\c!type=\v!next,
+ \c!align=\v!normal,
+ \c!page=\plusone,
+ \c!state=\v!stop]
+
+% type: both fixed left right next (not now), then better
+% lefttext and righttext or so
+
+\appendtoks
+ % \edef\p_type{}%
+ % \ifx\p_type\v!next
+ % \doifelseoddpage
+ % {\letpagegridareaparameter\c!type\v!right}%
+ % {\letpagegridareaparameter\c!type\v!left}%
+ % \fi
+ \clf_registercolumnsetarea {
+ name {\currentpagegridarea}
+ % type {\p_type}
+ % page \pagegridareaparameter\c!page
+ state {\pagegridareaparameter\c!state}
+ c \pagegridareaparameter\c!x
+ r \pagegridareaparameter\c!y
+ nc \pagegridareaparameter\c!nx
+ nr \pagegridareaparameter\c!ny
+ }%
+\to \everydefinepagegridarea
+
+\permanent\tolerant\protected\def\setuppagegridareatext[#1]#*[#2]%
+ {\edef\currentpagegridarea{#1}%
+ \setpagegridareaparameter\c!text{#2}}
+
+% maybe move the left/right correction to the tex end or the offset to lua
+
+% used nowhere:
+
+\permanent\protected\def\setpagegridarea#1#2#3#4#5#6#7#8% can be optimized
+ {\begingroup
+ \edef\currentpagegridarea{#2}%
+ \setpagegridareaparameter\c!width {#5\scaledpoint}%
+ \setpagegridareaparameter\c!height{#6\scaledpoint}%
+ \setbox\nextbox\hpack\bgroup\inheritedpagegridareaframed\bgroup
+ \usepagegridareastyleandcolor\c!style\c!color
+ \ignorespaces
+ \pagegridareaparameter\c!text
+ \egroup\egroup
+ %
+ \scratchdimen#8\scaledpoint
+ \ifdim\scratchdimen>\zeropoint
+ \setbox\scratchbox\vbox\bgroup
+ \clip
+ [ \c!offset=\pagegridareaparameter\c!clipoffset,%
+ \c!rightoffset=\pagegridareaparameter\c!rightoffset,%
+ \c!width=\scratchdimen,%
+ % \c!height=
+ ]%
+ {\copy\nextbox}%
+ \egroup
+ \clf_setcolumnsetarea{name {#1} box \scratchbox c #3 r #4}%
+ \setbox\scratchbox\vbox\bgroup
+ \hskip-\layoutparameter\c!backspace % todo: #9
+ \clip
+ [ \c!offset=\pagegridareaparameter\c!clipoffset,%
+ \c!leftoffset=\pagegridareaparameter\c!rightoffset,%
+ \c!hoffset=\scratchdimen,%
+ \c!width=\dimexpr\wd\nextbox-\scratchdimen\relax,%
+ % \c!height=
+ ]%
+ {\box\nextbox}%
+ \egroup
+ \clf_setcolumnsetarea{name {#1} box \scratchbox c #7 r #4}%
+ \else
+ \setbox\scratchbox\vbox\bgroup
+ \box\nextbox % wrapping needed
+ \egroup
+ \clf_setcolumnsetarea{name {#1} box \scratchbox c #3 r #4}%
+ \fi
+ \endgroup}
+
+% state start | repeat
+
+%D The old one, for now:
+
+\aliased\let\definecolumnset \definepagegrid
+\aliased\let\setupcolumnset \setuppagegrid
+\aliased\let\setupcolumnsetlines \setuppagegridlines
+\aliased\let\setupcolumnsetstart \setuppagegridstart
+\aliased\let\startcolumnset \startpagegrid
+\aliased\let\stopcolumnset \stoppagegrid
+\aliased\let\definecolumnsetspan \definepagegridspan
+\aliased\let\setupcolumnsetspan \setuppagegridspan
+\aliased\let\startcolumnsetspan \startpagegridspan
+\aliased\let\stopcolumnsetspan \stoppagegridspan
+\aliased\let\columnsetspanwidth \pagegridspanwidth
+\aliased\let\definecolumnsetarea \definepagegridarea
+\aliased\let\setupcolumnsetarea \setuppagegridarea
+\aliased\let\setupcolumnsetareatext\setuppagegridareatext
+
+%D It ends here.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-fac.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-fac.mkxl
new file mode 100644
index 00000000000..03fa9821724
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-fac.mkxl
@@ -0,0 +1,87 @@
+%D \module
+%D [ file=page-fac, % moved from page-ini
+%D version=2011.12.07, % 2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Facing Pages,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Facing Pages}
+
+\unprotect
+
+%D \macros
+%D {setupoppositeplacing,startopposite}
+%D
+%D \starttyping
+%D \starttext
+%D test \startopposite \blackrule[width=3cm,height=4cm] \stopopposite test
+%D test \startopposite \blackrule[width=3cm,height=4cm] \stopopposite test
+%D \stoptext
+%D \stoptyping
+
+\newbox \b_page_facings_content
+\newconditional\c_page_facings_busy
+
+\installcorenamespace{oppositeplacing}
+
+\installsetuponlycommandhandler \??oppositeplacing {oppositeplacing}
+
+\permanent\protected\def\startopposite
+ {\dowithnextboxcontent
+ {\hsize\makeupwidth}%
+ {\global\setbox\b_page_facings_content\vbox
+ {\ifvoid\b_page_facings_content
+ \directoppositeplacingparameter\c!before
+ \else
+ \directoppositeplacingparameter\c!inbetween
+ \unvbox\b_page_facings_content
+ \fi
+ \box\nextbox}}%
+ \vbox\bgroup}
+
+\permanent\protected\def\stopopposite
+ {\egroup}
+
+\def\page_facings_finish
+ {\ifvoid\b_page_facings_content \else
+ \global\setbox\b_page_facings_content\vbox to \makeupheight
+ {\unvbox\b_page_facings_content
+ \directoppositeplacingparameter\c!after
+ \vss}%
+ \fi}
+
+\def\page_facings_flush
+ {\ifconditional\c_page_facings_busy
+ \ifvoid\b_page_facings_content \else
+ \page_facings_flush_indeed
+ \fi
+ \fi}
+
+\def\page_facings_flush_indeed
+ {\ifnum\realpageno>\plusone
+ \begingroup
+ \pageornamentstate\plusone % tricky
+ \page_facings_finish
+ \page_boxes_shipout{\page_boxes_constructed_page\box\b_page_facings_content}%
+ \endgroup
+ \else
+ \global\setbox\b_page_facings_content\emptybox
+ \fi}
+
+\appendtoks
+ \doifelse{\directoppositeplacingparameter\c!state}\v!start\settrue\setfalse\c_page_facings_busy
+\to \everysetupoppositeplacing
+
+\setupoppositeplacing
+ [\c!state=\v!start,
+ \c!before=,
+ \c!inbetween=\blank,
+ \c!after=]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-ffl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-ffl.mkxl
new file mode 100644
index 00000000000..350c4500073
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-ffl.mkxl
@@ -0,0 +1,247 @@
+%D \module
+%D [ file=page-ffl,
+%D version=2018.01.04,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Facing floats,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Facing floats}
+
+%D The code below comes from a module made for Thomas Schmitz and is now part of the
+%D core. A simple example is given here:
+%D
+%D \starttyping
+%D \definefacingfloat
+%D [whatever]
+%D
+%D \setupfacingfloat
+%D [whatever]
+%D [style=bold,
+%D width=frame,
+%D offset=10pt,
+%D color=white]
+%D
+%D \setupfacingfloat
+%D [whatever:left]
+%D [background=color,
+%D backgroundcolor=red]
+%D
+%D \setupfacingfloat
+%D [whatever:right]
+%D [background=color,
+%D backgroundcolor=green]
+%D
+%D \startfacingfloat[whatever]
+%D {\dorecurse{10}{\samplefile{tufte} }}
+%D {\dorecurse{10}{\samplefile{ward} }}
+%D {\dorecurse{10}{\samplefile{tufte} }}
+%D {\dorecurse{10}{\samplefile{ward} }}
+%D \stopfacingfloat
+%D
+%D \startfacingfloat[whatever]
+%D \startcontent \dorecurse{10}{\samplefile{tufte} } \stopcontent
+%D \startcontent \dorecurse{10}{\samplefile{ward} } \stopcontent
+%D \startcontent \dorecurse{10}{\samplefile{tufte} } \stopcontent
+%D \startcontent \dorecurse{10}{\samplefile{ward} } \stopcontent
+%D \stopfacingfloat
+%D
+%D \dorecurse{10}{\samplefile{sapolsky} }
+%D \stoptyping
+%D
+%D The idea is to flush related floats more or less in parallel.
+
+\unprotect
+
+% width:
+%
+% -- fit : do nothing
+% -- dimension : use that
+% -- frame : use hsize minus frame offsets (based on preroll)
+
+\installcorenamespace {facingfloat}
+
+\installframedcommandhandler \??facingfloat {facingfloat} \??facingfloat
+
+\setupfacingfloat
+ [\c!spaceinbetween=\v!big,
+ \c!inbetween={\blank[\v!big]},
+ \c!width=\v!fit,
+ %\c!style,
+ %\c!color,
+ \c!page=\v!left]
+
+\appendtoks
+ \ifempty\currentfacingfloatparent
+ \normalexpanded{\definefacingfloat[\currentfacingfloat:\v!left ][\currentfacingfloat]}%
+ \normalexpanded{\definefacingfloat[\currentfacingfloat:\v!right][\currentfacingfloat]}%
+ \fi
+\to \everydefinefacingfloat
+
+\newcount\c_strc_floats_facing_saved
+\newcount\c_strc_floats_facing_flushed
+
+\newbox\b_strc_floats_facing_l
+\newbox\b_strc_floats_facing_r
+
+\let\m_strc_floats_state\relax
+
+\permanent\protected\def\strc_floats_facing_flush
+ {\ifnum\c_strc_floats_facing_flushed<\c_strc_floats_facing_saved
+ \ifodd\c_strc_floats_facing_flushed
+ \ifodd\realpageno
+ \strc_floats_facing_flush_indeed
+ \doifelsependingpagecontent\relax{\null\page}%
+ \fi
+ \else
+ \ifodd\realpageno\else
+ \strc_floats_facing_flush_indeed
+ \doifelsependingpagecontent\relax{\null\page}%
+ \fi
+ \fi
+ \fi}
+
+\permanent\protected\def\strc_floats_facing_flush_indeed
+ {\global\advance\c_strc_floats_facing_flushed\plusone
+ \floatingpenalty\zerocount
+ \insert\namedinsertionnumber\s!topfloat\bgroup
+ \forgetall
+ \ifconditional\c_page_one_top_of_insert
+ \ifconditional\c_page_one_correct_top_insert
+ \topskipcorrection % [xx] new: see icare topbleed
+ \kern-\lineskip
+ \par
+ \prevdepth\maxdimen
+ \fi
+ \fi
+ \directboxfromcache{\currentfacingfloat}{\number\c_strc_floats_facing_flushed}%
+ \vkern\s_page_one_between_top_insert
+ \egroup
+ \ifnum\c_strc_floats_facing_saved=\c_strc_floats_facing_flushed
+ \global\c_strc_floats_facing_saved \zerocount
+ \global\c_strc_floats_facing_flushed\zerocount
+ \resetboxesincache{\currentfacingfloat}%
+ \fi}
+
+\aliased\let\flushfacingfloats\strc_floats_facing_flush
+
+\protected\def\strc_floats_facing_setup
+ {\edef\currentfacingfloat{\currentfacingfloat:\m_strc_floats_state}%
+ \usefacingfloatstyleandcolor\c!style\v!color}
+
+\protected\def\strc_floats_facing_collect
+ {\ifx\m_strc_floats_state\v!left
+ \ifvoid\nextbox\orelse\ifzeropt\wd\nextbox\else
+ \ifvoid\b_strc_floats_facing_l
+ \setbox\b_strc_floats_facing_l\box\nextbox
+ \else
+ \setbox\b_strc_floats_facing_l\vbox\bgroup
+ \unvbox\b_strc_floats_facing_l
+ \facingfloatparameter\c!inbetween
+ \unvbox\nextbox
+ \egroup
+ \fi
+ \fi
+ \let\m_strc_floats_state\v!right
+ \orelse\ifx\m_strc_floats_state\v!right
+ \ifvoid\nextbox\orelse\ifzeropt\wd\nextbox\else
+ \ifvoid\b_strc_floats_facing_r
+ \setbox\b_strc_floats_facing_r\box\nextbox
+ \else
+ \setbox\b_strc_floats_facing_r\vbox\bgroup
+ \unvbox\b_strc_floats_facing_r
+ \facingfloatparameter\c!inbetween
+ \unvbox\nextbox
+ \egroup
+ \fi
+ \fi
+ \let\m_strc_floats_state\v!left
+ \else
+ \let\m_strc_floats_state\v!left
+ \fi}
+
+\protected\def\strc_floats_facing_handle
+ {\doifnextbgroupelse
+ \strc_floats_facing_handle_indeed
+ \strc_floats_facing_wrap_up}
+
+\protected\def\strc_floats_facing_handle_indeed
+ {\dowithnextboxcontent
+ \strc_floats_facing_setup
+ {\strc_floats_facing_collect\strc_floats_facing_handle}
+ \vbox}
+
+\permanent\protected\def\startfacingfloat[#1]%
+ {\begingroup
+ % todo: \usepageparameter
+% \edef\p_page{\facingfloatparameter\c!page}%
+% \ifempty\p_page\else
+% \page[\p_page]%
+% \fi
+ %
+ \edef\currentfacingfloat{#1}%
+ \edef\p_width{\facingfloatparameter\c!width}%
+ \letfacingfloatparameter\c!width\v!fit
+ \ifx\p_width\v!frame
+ \setbox\scratchbox\hpack{\inheritedfacingfloatframed{}}%
+ \advance\hsize-\wd\scratchbox
+ \orelse\ifx\p_width\v!fit
+ % whatever
+ \else
+ \hsize\p_width
+ \fi
+ \enforced\let\startcontent\bgroup
+ \enforced\let\stopcontent\egroup
+ \let\m_strc_floats_state\v!left
+ \strc_floats_facing_handle}
+
+\permanent\protected\def\stopfacingfloat
+ {\endgroup}
+
+\protected\def\strc_floats_facing_wrap_up
+ {\edef\p_spaceinbetween{\facingfloatparameter\c!spaceinbetween}%
+ \ifempty\p_spaceinbetween
+ \scratchdimen\zeropoint
+ \else
+ \setbox\scratchbox\vbox{\directvspacing\p_spaceinbetween}%
+ \scratchdimen\htdp\scratchbox
+ \fi
+ \ifvoid\b_strc_floats_facing_l\else
+ \page_postprocessors_linenumbers_box\b_strc_floats_facing_l
+ \fi
+ \ifvoid\b_strc_floats_facing_r\else
+ \page_postprocessors_linenumbers_box\b_strc_floats_facing_r
+ \fi
+ \doloop{%
+ \strc_floats_facing_flush_wrap\b_strc_floats_facing_l\v!left
+ \strc_floats_facing_flush_wrap\b_strc_floats_facing_r\v!right
+ \ifvoid\b_strc_floats_facing_l\relax\ifvoid\b_strc_floats_facing_r\relax
+ \exitloop
+ \fi\fi}}
+
+\def\strc_floats_facing_flush_wrap#1#2%
+ {\ifvoid#1\relax
+ % todo
+ \else
+ \begingroup
+ \setbox\scratchbox\vsplit#1 upto \textheight
+ \setbox\scratchbox\hpack\bgroup
+ \edef\currentfacingfloat{\currentfacingfloat:#2}%
+ \inheritedfacingfloatframed{\box\scratchbox}%
+ \egroup
+ \ifdim\ht\scratchbox<\dimexpr\textheight-\scratchdimen\relax
+ \setbox\scratchbox\vbox{\box\scratchbox\directvspacing\p_spaceinbetween}%
+ \else
+ \setbox\scratchbox\vbox to \textheight{\box\scratchbox\vss}%
+ \fi
+ \global\advance\c_strc_floats_facing_saved\plusone
+ \putboxincache{\currentfacingfloat}{\number\c_strc_floats_facing_saved}\scratchbox
+ \endgroup
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-flt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-flt.mkxl
new file mode 100644
index 00000000000..a97cc7f0d2e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-flt.mkxl
@@ -0,0 +1,333 @@
+%D \module
+%D [ file=page-flt,
+%D version=2010.04.08,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Float Management,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Float Management}
+
+%D This module has code that previously was in other modules. There is also float
+%D related code in \type {strc-flt.mkiv}.
+
+\registerctxluafile{page-flt}{}
+
+\unprotect
+
+\defineinsertion[\s!topfloat]
+\defineinsertion[\s!bottomfloat]
+\defineinsertion[\s!pagefloat]
+
+\newdimen \d_page_floats_inserted_bottom
+\newdimen \d_page_floats_inserted_top
+\newdimen \d_page_floats_inserted_page
+\newcount \c_page_floats_n_of_top \c_page_floats_n_of_top \plustwo
+\newcount \c_page_floats_n_of_bottom \c_page_floats_n_of_bottom\zerocount
+\newcount \c_page_floats_n_of_page \c_page_floats_n_of_page \plustwo
+
+\newconstant\c_page_floats_insertions_topskip_mode % 1 = no topskip
+
+\def\page_floats_report_total
+ {\showmessage\m!floatblocks4%
+ {\the\totalnoffloats
+ \ifempty\floatlocationmethod
+ \ifempty\floatlocation\else,\floatlocation\fi
+ \else
+ ,\floatlocationmethod
+ \fi}}
+
+\def\page_floats_report_flushed
+ {\showmessage\m!floatblocks3%
+ {\the\numexpr\totalnoffloats-\savednoffloats\relax}}
+
+%D Extra float registers.
+
+\newconditional\c_page_floats_room
+\newconditional\c_page_floats_some_waiting
+\newconditional\c_page_floats_not_permitted
+\newconditional\c_page_floats_flushing
+\newconditional\c_page_floats_center_box \settrue\c_page_floats_center_box
+\newconditional\c_page_floats_center_box_local
+\newconditional\c_page_floats_center_box_global
+\newconditional\c_page_floats_compress_flushed \settrue\c_page_floats_compress_flushed
+
+\newdimen \d_page_floats_compress_distance
+
+%D For the moment we keep this but they will become private too.
+
+\newcount\totalnoffloats % these will be redone ... handled at the lua end anyway
+\newcount\savednoffloats % these will be redone ... handled at the lua end anyway
+\newcount\nofcollectedfloats % communication channel
+\newdimen\maxcollectedfloatstotal % communication channel
+
+\newcount\noffloatinserts % these will be redone ... handled at the lua end anyway
+
+\newbox \floattext
+
+\newdimen\floatwidth
+\newdimen\floatheight
+\newdimen\floatdepth
+\newdimen\floattextwidth
+\newdimen\floattextheight
+
+\newbox \floatbox
+\newbox \savedfloatbox
+
+%D From now on we manage the float stack at the \LUA\ end instead of packing
+%D them in a box and splitting off stacked floats. It's not so much less code
+%D but it's cleaner this way. It also opens op some posibilities as we can now
+%D more conveniently cary additional information around.
+
+\newtoks \everyfloatscheck
+
+\appendtoks
+ \ifcase\savednoffloats
+ \global\setfalse\c_page_floats_some_waiting
+ \else
+ \global\settrue\c_page_floats_some_waiting
+ \fi
+\to \everyfloatscheck
+
+\protected\def\page_floats_flush#1#2%
+ {\clf_flushfloat{#1}#2\relax
+ \the\everyfloatscheck}
+
+\protected\def\page_floats_flush_by_label#1#2%
+ {\clf_flushlabeledfloat{#1}{#2}\relax
+ \the\everyfloatscheck}
+
+\protected\def\page_floats_save#1%
+ {\clf_savefloat{#1}\relax
+ \the\everyfloatscheck}
+
+\protected\def\page_floats_resave#1%
+ {\clf_resavefloat{#1}\relax
+ \the\everyfloatscheck}
+
+\protected\def\page_floats_push_saved
+ {\clf_pushfloat
+ \the\everyfloatscheck}
+
+\protected\def\page_floats_pop_saved
+ {\clf_popfloat
+ \the\everyfloatscheck}
+
+\protected\def\page_floats_get_info#1%
+ {\clf_consultfloat{#1}}
+
+\protected\def\page_floats_if_else#1%
+ {\clf_doifelsestackedfloats{#1}}
+
+\protected\def\page_floats_collect#1#2#3%
+ {\clf_collectfloat{#1}\dimexpr#2\relax\dimexpr#3\relax}
+
+\permanent\def\nofstackedfloatsincategory#1% was singular
+ {\clf_nofstackedfloats{#1}}
+
+\let\page_floats_column_push_saved\page_floats_push_saved % overloaded in page-mul
+\let\page_floats_column_pop_saved \page_floats_pop_saved % overloaded in page-mul
+
+\protected\def\page_floats_save_page_float#1#2%
+ {\clf_savespecificfloat{#1}{specification{#2}}\relax}
+
+\protected\def\page_floats_save_somewhere_float#1#2% #1=method
+ {\clf_savespecificfloat{#1}{specification{#2}label{\floatlabel}}\relax}
+
+%D This is an experimental new feature (for Alan Braslau), a prelude to more:
+%D
+%D \starttyping
+%D test \placefigure{}{}
+%D test \placefigure[somewhere:alpha][whatever]{}{}
+%D test \placefigure[somewhere:beta] [whatever]{}{}
+%D test \placefigure[somewhere:gamma][whatever]{}{}
+%D test \placefigure[somewhere:delta][whatever]{}{}
+%D test \placefigure{}{}
+%D
+%D in \in{figure}[whatever] bla bla
+%D
+%D \placenamedfloat[figure][*l*]
+%D \placenamedfloat[figure][gamma]
+%D \placenamedfloat[figure][beta]
+%D \stoptyping
+
+\permanent\tolerant\protected\def\placenamedfloat[#1]#*[#2]%
+ {\doloop
+ {\page_floats_flush_by_label\s!somewhere{#2}%
+ \ifvoid\floatbox
+ \exitloop
+ \else
+ \def\currentfloat{#1}%
+ \blank[\rootfloatparameter\c!spacebefore]%
+ \box\floatbox
+ \blank[\rootfloatparameter\c!spaceafter]%
+ \fi}}
+
+% \setupcaption [figure] [align=flushleft]
+% \setupcaption [figure-1] [align=flushleft,leftmargin=10mm]
+% \setupcaption [figure-2] [align=flushleft,leftmargin=10mm,rightmargin=-10mm,width=\textwidth]
+%
+% \startsetups somefigure
+% \ifdim\floatsetupwidth>\textwidth
+% \placesetupfloat[figure-2]
+% \else
+% \placesetupfloat[figure-1]
+% \fi
+% \stopsetups
+%
+% \placefloatwithsetups[somefigure]{}{\externalfigure[dummy][width=5cm,height=2cm]}
+
+\mutable\let\floatsetupcaption\empty
+\mutable\let\floatsetupcontent\empty
+\mutable\let\floatsetupwidth \empty
+\mutable\let\floatsetupheight \empty
+
+\aliased\let\placesetupfloat\gobbleoneoptional
+
+\permanent\tolerant\protected\def\placefloatwithsetups[#1]#*[#2]#*[#3]#:#4%
+ {\def\floatsetupcaption{#4}%
+ \def\floatsetupcontent{\copy\nextbox}%
+ \def\floatsetupwidth {\wd\nextbox}%
+ \def\floatsetupheight {\ht\nextbox}%
+ \enforced\permanent\protected\def\placesetupfloat[##1]%
+ {\placefloat[##1][#2][#3]{#4}{\floatsetupcontent}}% #4 and not \floatsetupcaption (protected)
+ \dowithnextbox{\setups[#1]}\vbox}
+
+%D The following code is in transition as we don't want to break the current single
+%D column, multi column, and columnset mechanism.
+%D
+%D First we reimplement some helpers.
+
+\def\page_floats_get_used_hsize{\hsize}
+
+\protected\def\page_floats_get
+ {\ifconditional\c_page_floats_some_waiting
+ \page_floats_flush\s!text\plusone
+ \ifconditional\c_page_floats_center_box
+ \ifdim\wd\globalscratchbox<\page_floats_get_used_hsize
+ \global\setbox\floatbox\hpack to \page_floats_get_used_hsize{\hss\box\floatbox\hss}%
+ \orelse\ifinsidecolumns
+ % retain special alignments
+ \ifdim\wd\floatbox>\makeupwidth
+ \wd\floatbox\makeupwidth
+ \fi
+ \fi
+ \fi
+ \else
+ \global\savednoffloats\zerocount
+ \global\setbox\floatbox\emptybox
+ \fi}
+
+\protected\def\page_floats_flush_saved
+ {\doloop
+ {\ifconditional\c_page_floats_some_waiting
+ \page_otr_command_check_if_float_fits
+ \ifconditional\c_page_floats_room
+ \page_floats_get
+ \doplacefloatbox
+ \else
+ \exitloop
+ \fi
+ \else
+ % \ifconditional\c_page_margin_blocks_present % not here, here just as many floats as fit
+ % \page_otr_command_flush_margin_blocks
+ % \else
+ \exitloop
+ % \fi
+ \fi}}
+
+%D This is a future mechamism that will be integrated once we're sure about it:
+%D
+%D \starttyping
+%D \dorecurse{10}
+%D {\input thuan
+%D \placefigure{}{\framed[height=1.5cm]{test}}
+%D \placefloatplaceholder}
+%D \stoptyping
+
+\permanent\protected\def\placefloatplaceholder
+ {\ifconditional\c_page_floats_room \else
+ \ifdim\dimexpr\pagegoal-\pagetotal-3\lineheight\relax>\zeropoint
+ \startlinecorrection[blank]
+ \mhbox{\inframed{\labeltexts{placeholder}{\m_strc_floats_last_caption_tag}}}%
+ \stoplinecorrection
+ \else
+ \allowbreak
+ \fi
+ \fi}
+
+\setuplabeltext
+ [placeholder={\Word{\lastplacedfloat}~, moved}]
+
+%D Page floats use different stacks.
+
+\newtoks\everybeforeflushedpagefloat
+
+\def\page_floats_flush_page_floats_indeed#1% future releases can do more clever things
+ {\page_floats_flush{#1}\plusone
+ \edef\floatspecification{\clf_getfloatvariable{specification}}% Is this okay?
+ \the\everybeforeflushedpagefloat
+ \vpack to \textheight
+ {\doifnotinset\v!high\floatspecification\vfill
+ \box\floatbox
+ \doifnotinset\v!low\floatspecification\vfill}%
+ \page_otr_fill_and_eject_page}
+
+\let\m_page_otr_checked_page_float\relax
+
+\protected\def\page_floats_flush_page_floats % used in postpone
+ {\edef\m_page_otr_checked_page_float{\clf_checkedpagefloat}%
+ \ifempty\m_page_otr_checked_page_float
+ % nothing
+ \orelse\ifx\m_page_otr_checked_page_float\v!empty
+ \emptyhbox \page_otr_fill_and_eject_page % why not dummy_page
+ \else
+ \page_floats_flush_page_floats_indeed\m_page_otr_checked_page_float
+ \fi}
+
+% temp hack, needed to prevent floatbox being forgotten during
+% output, this will change to using another box for flushing
+%
+% \dorecurse{700}{text } \placefigure[top][]{First} {\framed{bla 1}}
+% \placefigure[top][]{Second}{\framed{bla 2}}
+% \dorecurse {40}{text } \placefigure[top][]{Third} {\framed{bla 3}}
+
+\appendtoks
+ \global\setbox\savedfloatbox\box\floatbox
+\to \everybeforeoutput
+
+\appendtoks
+ \global\setbox\floatbox\box\savedfloatbox
+\to \everyafteroutput
+
+\protect \endinput
+
+% hm, where is this one used (was in save/restore, see old implementation)
+%
+% \permanent\protected\def\uncenteredfloatbox
+% {\ifconditional\c_page_floats_center_box
+% \ifhbox\floatbox\relax % remove centering
+% \ifdim\wd\floatbox=\hsize
+% \ifhbox\floatbox
+% \setbox\scratchbox\hbox
+% {\unhbox\floatbox
+% \unskip\unskip
+% \global\setbox\globalscratchbox\lastbox}%
+% \box\globalscratchbox
+% \else
+% \box\floatbox
+% \fi
+% \else
+% \box\floatbox
+% \fi
+% \else
+% \box\floatbox
+% \fi
+% \else
+% \box\floatbox
+% \fi}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-flw.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-flw.mkxl
new file mode 100644
index 00000000000..ef92b9b52f9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-flw.mkxl
@@ -0,0 +1,178 @@
+%D \module
+%D [ file=page-flw,
+%D version=2003.04.19, % from test-002 (1997) profile experiment
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Text Flows,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Text Flows}
+
+%D This is high experimental and especially flushing may change (proper spacing is
+%D the driving force here). It's an old mechanism used for playing with stepping
+%D through document threads. As it's a simple and effective mechanism we keep it
+%D around. It's not to be confused with upcoming stream support.
+%D
+%D \starttyping
+%D \setuppapersize [S6]
+%D \setuptolerance [verytolerant,stretch]
+%D \setupfooter [strut=no]
+%D \setupwhitespace[big]
+%D
+%D \setuplayout
+%D [rightedge=5cm,width=fit,margin=0pt,edgedistance=1cm,
+%D footer=4cm,footerdistance=1cm,header=0cm]
+%D
+%D \setuptexttexts [edge][][\vbox{\flushtextflow{alpha}}]
+%D \setupfootertexts[edge][][\vbox{\flushtextflow{beta}}]
+%D \setupfootertexts [\vbox{\flushtextflow{gamma}}][]
+%D
+%D \definetextflow [alfa] [width=\rightedgewidth]
+%D \definetextflow [beta] [width=\rightedgewidth]
+%D \definetextflow [gamma] [width=\footerheight]
+%D
+%D \starttext
+%D
+%D \dorecurse{50}
+%D {\getrandomnumber{\funny}{0}{8}
+%D \ifcase\funny \starttextflow[alfa] \input tufte.tex \stoptextflow
+%D \or \starttextflow[beta] \input knuth.tex \stoptextflow
+%D \or \starttextflow[gamma] \input materie.tex \stoptextflow
+%D \or {\bf TUFTE}\quad \input tufte \par
+%D \or {\bf TUFTE}\quad \input tufte \par
+%D \or {\bf KNUTH}\quad \input knuth \par
+%D \or {\bf KNUTH}\quad \input knuth \par
+%D \or {\bf MATERIE}\quad \input materie \par
+%D \else {\bf MATERIE}\quad \input materie \par
+%D \fi}
+%D
+%D \stoptext
+%D \stoptyping
+
+\unprotect
+
+\installcorenamespace{textflow}
+\installcorenamespace{textflowbox}
+
+\installcommandhandler \??textflow {textflow} \??textflow
+
+\setuptextflow
+ [%c!style=,
+ %c!color=,
+ \c!width=\availablehsize]
+
+\appendtoks
+ \ifcsname\??textflowbox\currenttextflow\endcsname
+ \setbox\csname\??textflowbox\currenttextflow\endcsname\emptybox
+ \else
+ \expandafter\newbox\csname\??textflowbox\currenttextflow\endcsname
+ \fi
+\to \everydefinetextflow
+
+\newcount\b_page_textflow_box
+
+\permanent\def\textflowcollector#1%
+ {\begincsname\??textflowbox#1\endcsname}
+
+\permanent\protected\def\doifelsetextflowcollector#1%
+ {\ifcsname\??textflowbox#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifelsetextflow#1%
+ {\unless\ifcsname\??textflowbox#1\endcsname
+ \expandafter\secondoftwoarguments
+ \orelse\ifvoid\csname\??textflowbox#1\endcsname
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doiftextflowcollectorelse\doifelsetextflowcollector
+\aliased\let\doiftextflowelse \doifelsetextflow
+
+\permanent\protected\def\starttextflow[#1]%
+ {\begingroup
+ \edef\currenttextflow{#1}%
+ \ifcsname\??textflowbox\currenttextflow\endcsname
+ \b_page_textflow_box\csname\??textflowbox\currenttextflow\endcsname
+ \global\setbox\b_page_textflow_box\vbox
+ \bgroup
+ \dontcomplain
+ \ifvoid\b_page_textflow_box\else
+ \unvbox\b_page_textflow_box
+ \fi
+ \hsize\textflowparameter\c!width\relax
+ \usetextflowstyleandcolor\c!style\c!color
+ \enforced\permanent\protected\def\stoptextflow{\endgraf\egroup\endgroup}%
+ \else
+ \enforced\aliased\let\stoptextflow\endgroup
+ \fi}
+
+\aliased\let\stoptextflow\relax
+
+\permanent\protected\def\flushtextflow#1%
+ {\begingroup
+ \edef\currenttextflow{#1}%
+ \ifcsname\??textflowbox\currenttextflow\endcsname
+ \b_page_textflow_box\csname\??textflowbox\currenttextflow\endcsname
+ \ifvoid\b_page_textflow_box
+ % sorry
+ \orelse\ifdim\ht\b_page_textflow_box>\vsize
+ \setbox\scratchbox\vsplit\b_page_textflow_box to \vsize
+ \ifvoid\scratchbox\else
+ \unvbox\scratchbox
+ \fi
+ \else
+ \unvbox\b_page_textflow_box
+ \fi
+ \fi
+ \endgroup}
+
+\protect \endinput
+
+% \setuppapersize [A6]
+% \setupbodyfont [pagella, 12pt]
+%
+% \definetextflow [even] [width=\textwidth]
+% \definetextflow [odd] [width=\textwidth]
+%
+% \starttextflow [even] \dorecurse{15}{\input bryson} \stoptextflow
+% \starttextflow [odd] \dorecurse {5}{\input knuth } \stoptextflow
+%
+% \starttext
+%
+% \doloop {
+% \ifodd\realpageno
+% \doiftextflowelse{odd} {
+% \bgroup
+% \vsize\dimexpr\textheight-\strutdp\relax
+% \flushtextflow{odd}
+% \egroup
+% } {
+% \null
+% }
+% \page
+% \else
+% \doiftextflowelse{even} {
+% \bgroup
+% \vsize\dimexpr\textheight-\strutdp\relax
+% \flushtextflow{even}
+% \egroup
+% } {
+% \null
+% }
+% \page
+% \fi
+% \doiftextflowelse{even}\donothing{\doiftextflowelse{odd}\donothing\exitloop}
+% }
+%
+% \stoptext
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-imp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-imp.mkxl
new file mode 100644
index 00000000000..4ee7c159be3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-imp.mkxl
@@ -0,0 +1,1404 @@
+%D \module
+%D [ file=page-imp, % was: core-pag,
+%D version=1998.01.15,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Pagebody Building (Imposition),
+%D author=Hans Hagen & Willi Egger,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% much of this can more to run time loading !
+
+\writestatus{loading}{ConTeXt Page Macros / Pagebody Building}
+
+\unprotect
+
+%D \macros
+%D {starttextdata}
+%D
+%D This is a user macro (appending to every last shipout is not really user
+%D friendly).
+
+\newtoks\t_page_text_data
+
+\permanent\protected\def\starttextdata#1\stoptextdata
+ {\glet\page_shipouts_flush_text_data\page_shipouts_flush_text_data_indeed
+ \globaladdtotoks\t_page_text_data{#1}}
+
+\aliased\let\stoptextdata\relax
+
+\def\page_shipouts_flush_text_data_indeed
+ {\vsmashed{\the\t_page_text_data}%
+ \global\t_page_text_data\emptytoks
+ \glet\page_shipouts_flush_text_data\relax}
+
+\let\page_shipouts_flush_text_data\relax
+
+\prependtoks
+ \page_shipouts_flush_text_data
+\to \everylastshipout
+
+%D Problem: we need to apply the finalizers to a to be shipped out page (as we can
+%D have positioning involved). However, we can also add stuff in the imposition,
+%D like cropmarks. Fortunately we do that with metapost so colors etc are dealt with
+%D at that end.
+
+% \starttypen
+% \def\pagestoshipout{1,3,5}
+% \stoptypen
+
+\installcorenamespace{shipoutmethod}
+\installcorenamespace{layoutarranger}
+\installcorenamespace{layoutarrangeoption}
+
+\newcount \shippedoutpages
+\newcount \combinedpagescounter
+\newconstant \whichpagetoshipout % 0=all 1=odd 2=even
+\newbox \shipoutscratchbox
+\setnewconstant\shipoutfinalizemethod\plusone
+
+\mutable\let\pagestoshipout\empty % {1,3,6}
+
+\permanent\protected\def\shipoutrange#1#2%
+ {\begingroup
+ \scratchtoks\emptytoks
+ \dostepwiserecurse{#1}{\numexpr#2-\plusone\relax}\plusone{\etoksapp\scratchtoks{##1,}}%
+ \xdef\pagestoshipout{\the\scratchtoks,\number#2}%
+ \doglobal\appendtoks
+ \ifnum\realpageno>\numexpr#2+\plusone\relax
+ \global\everypar{\normalend}%
+ \fi
+ \to\everyaftershipout
+ \endgroup}
+
+\permanent\protected\def\installshipoutmethod#1#2% % a handler takes one argument: something to be boxed
+ {\gdefcsname\??shipoutmethod#1\endcsname##1{#2{##1}}} % and shipped out (don't depend on the exact package)
+
+\aliased\let\installpagehandler\installshipoutmethod % will go
+
+\permanent\protected\def\invokepagehandler#1%
+ {\expandnamespacevalue\??shipoutmethod{#1}\v!normal}
+
+\def\page_shipouts_handle
+ {\ifcsname\??shipoutmethod\v_page_target_method\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\page_shipouts_ignore
+ \fi}
+
+\installshipoutmethod \v!normal
+ {\ifarrangingpages
+ \expandafter\page_shipouts_arrange
+ \else
+ \expandafter\page_shipouts_normal
+ \fi}
+
+\installshipoutmethod \v!none
+ {\page_shipouts_ignore}
+
+% extension mechanism
+
+\newcount\c_page_boxes_flush_n % set at the lua end
+
+\let\page_boxes_flush_before\relax
+\let\page_boxes_flush_after \relax
+
+% used here:
+
+\def\page_boxes_shipout#1% or: \page_shipouts_apply
+ {\dontcomplain % redundant
+ \ifcase\c_page_boxes_flush_n\else
+ \page_boxes_flush_before
+ \fi
+ \the\everybeforeshipout
+ \ifcase\shipoutfinalizemethod % not nice ... needs thinking
+ \page_shipouts_handle{#1}%
+ \else
+ \setbox\shipoutscratchbox\hpack
+ {#1}% just in case there are objects there, hook for testing (will go away)
+ \page_shipouts_handle
+ {\finalizeshipoutbox\shipoutscratchbox
+ \box\shipoutscratchbox}%
+ \fi
+ \setnextrealpageno % so this comes before \everyaftershipout so in fact:
+ \the\everyaftershipout % at this point we're already on the next realpage
+ \ifcase\c_page_boxes_flush_n\else
+ \page_boxes_flush_after
+ \fi}
+
+\def\page_shipouts_ignore#1%
+ {\begingroup
+ \writestatus\m!system
+ {\ifarrangingpages arranged \fi page
+ \ifarrangingpages\the\arrangeno\else\the\realpageno\fi\normalspace
+ not flushed}%
+ % \setbox\scratchbox\hpack
+ % {#1}% no finalize
+ \deadcycles\zerocount
+ \endgroup}
+
+\def\page_otr_flush_every_stuff
+ {\begingroup
+ \setbox\scratchbox\hpack
+ {% before the main one !
+ \ifcase\realfolio \or
+ \the\everyfirstshipout
+ \global\everyfirstshipout\emptytoks
+ \fi
+ % the main one
+ \the\everyshipout\relax
+ % always last (and after the main one)
+ \ifnum\realpageno=\lastpage\relax
+ \the\everylastshipout
+ \global\everylastshipout\emptytoks
+ \fi}%
+ \smashbox\scratchbox
+ \box\scratchbox
+ \endgroup}
+
+%D Also in normal \MKIV\ we nos use the indirect way so that we benefit from timing
+%D and tracing.
+
+\def\page_shipout_show_box{\showbox diagnose}
+
+\protected\def\page_shipout_box#1%
+ {\ifcase\tracingoutput\else
+ \page_shipout_show_box#1\relax
+ \fi
+ \clf_shipoutpage#1\relax
+ \global\setbox#1\emptybox
+ \global\deadcycles\zerocount}
+
+\def\page_shipouts_normal#1%
+ {\global\advance\shippedoutpages\plusone
+ % this is not resource safe!
+ \ifempty\pagestoshipout
+ \ifcase\whichpagetoshipout\relax
+ \donetrue
+ \or % 1
+ \ifodd\shippedoutpages\relax\donetrue\else\donefalse\fi
+ \or % 2
+ \ifodd\shippedoutpages\relax\donefalse\else\donetrue\fi
+ \else
+ \donetrue
+ \fi
+ \else % testen, aangepast / expanded nodig ?
+ \normalexpanded{\doifelseinset{\the\shippedoutpages}{\pagestoshipout}}\donetrue\donefalse
+ \fi
+ \ifdone
+ \setbox\shipoutscratchbox\hpack
+ {#1}% finalizes
+ \ifcase\shipoutfinalizemethod
+ \finalizeshipoutbox\shipoutscratchbox
+ \fi
+ \setbox\shipoutscratchbox\vpack
+ {\hpack
+ {\page_otr_flush_every_stuff
+ \page_otr_flush_special_content
+ \box\shipoutscratchbox}}%
+ \page_shipout_box\shipoutscratchbox % takes a box number!
+ \else
+ \page_shipouts_ignore{#1}%
+ \fi}
+
+\newconditional\c_page_shipouts_use_objects \settrue\c_page_shipouts_use_objects
+
+\installcorenamespace {arrangedpage}
+
+\def\page_shipouts_arrange#1%
+ {\begingroup
+ \setbox\shipoutscratchbox\hpack
+ {#1}% finalizes
+ \setbox\shipoutscratchbox\hpack
+ {\page_otr_flush_every_stuff
+ \page_otr_flush_special_content
+ \box\shipoutscratchbox}%
+ %
+ \ifconditional\c_page_shipouts_use_objects
+ \setobject\??arrangedpage{\the\realpageno}\hpack{\box\shipoutscratchbox}%
+ \setbox\shipoutscratchbox\hpack{\getobject\??arrangedpage{\the\realpageno}}%
+ \fi
+ %
+ \pusharrangedpage\shipoutscratchbox
+ \deadcycles\zerocount
+ \endgroup}
+
+%D We need a couple of boxes for duplex printing \unknown
+
+\newbox\arrangedpageA \newbox\arrangedpageB
+\newbox\arrangedpageC \newbox\arrangedpageD
+\newbox\arrangedpageE \newbox\arrangedpageF
+\newbox\arrangedpageG \newbox\arrangedpageH
+
+\newconditional\arrangedswapstate
+\newconditional\arrangednegatestate
+\newconditional\arrangedmirrorstate
+\newconditional\arrangeddoublestate
+\newconditional\arrangingdisabledstate
+\newconditional\arrangedbackgroundstate
+
+\mutable\def\arrangedrotationO{0}
+\mutable\def\arrangedrotationE{0}
+
+\newcount\arrangedpageN
+\newcount\arrangedpageM
+
+\newcount\arrangedpageT \arrangedpageT\plusone
+\newcount\arrangedpageX \arrangedpageX\plusone
+\newcount\arrangedpageY \arrangedpageY\plusone
+
+\def\page_paper_set_offsets
+ {\global\paperoffset\v_page_target_offset
+ \global\advance\paperwidth -2\dimexpr\paperoffset/\arrangedpageX\relax
+ \global\advance\paperheight-2\dimexpr\paperoffset/\arrangedpageY\relax}
+
+\permanent\protected\def\doinstallarrangedoption#1#2%
+ {\gdefcsname\??layoutarrangeoption#1\endcsname{#2}}
+
+\permanent\def\doinstalledarrangedoption#1%
+ {\ifcsname\??layoutarrangeoption#1\endcsname
+ \lastnamedcs
+ \else
+ \checkinstalledpagearrangement{#1}% this installs the arranger
+ \fi}
+
+\doinstallarrangedoption\empty
+ {} % no default and check if empty, we can have ,,,
+
+\doinstallarrangedoption\v!disable
+ {\global\settrue\arrangingdisabledstate}
+
+\doinstallarrangedoption\v!mirrored
+ {\global\settrue\arrangedmirrorstate}
+
+\doinstallarrangedoption\v!doublesided
+ {\global\settrue\arrangeddoublestate}
+
+\doinstallarrangedoption\v!negative
+ {\global\settrue\arrangednegatestate}
+
+\doinstallarrangedoption\v!rotated
+ {\gdef\arrangedrotationO {90}%
+ \gdef\arrangedrotationE{270}%
+ \swapcounts\c_page_marks_nx\c_page_marks_ny}
+
+\doinstallarrangedoption{90}
+ {\gdef\arrangedrotationO {90}%
+ \gdef\arrangedrotationE{270}%
+ \swapcounts\c_page_marks_nx\c_page_marks_ny}
+
+\doinstallarrangedoption{180}
+ {\gdef\arrangedrotationO{180}%
+ \gdef\arrangedrotationE {0}}
+
+\doinstallarrangedoption{270}
+ {\gdef\arrangedrotationO{270}%
+ \gdef\arrangedrotationE {90}%
+ \swapcounts\c_page_marks_nx\c_page_marks_ny}
+
+\doinstallarrangedoption\s!reset
+ {\global\arrangingpagesfalse}
+
+\doinstallarrangedoption\v!background
+ {\global\settrue\arrangedbackgroundstate}
+
+\aliased\let\poparrangedpages \relax
+\aliased\let\pusharrangedpage \gobbleoneargument
+\aliased\let\handlearrangedpage\relax
+
+\permanent\protected\def\setuparranging[#1]%
+ {\ifconditional\arrangingdisabledstate \else
+ %global\setfalse\arrangingdisabledstate
+ \global\arrangingpagestrue % will be conditional
+ \global\setfalse\arrangednegatestate
+ \global\setfalse\arrangedmirrorstate
+ \global\setfalse\arrangeddoublestate
+ \global\setfalse\arrangedswapstate
+ \gdef\arrangedrotationO{0}%
+ \gdef\arrangedrotationE{180}%
+ % if we use --arrange we have an initial "disable" here
+ \processcommalist[#1]\doinstalledarrangedoption
+ \ifrelax\poparrangedpages
+ \global\arrangingpagesfalse % nothing set yet
+ \fi
+ \setuppapersize
+ \ifarrangingpages
+ \ifconditional\c_page_shipouts_use_objects\else
+ \clf_disablejobsave
+ \clf_synctexdisable
+ %\disabledirective[job.save]%
+ \fi
+ \fi
+ \fi}
+
+\permanent\protected\def\installpagearrangement #1 % will change, no space
+ {\gdefcsname\??layoutarranger#1\endcsname}
+
+\permanent\def\checkinstalledpagearrangement#1% can be empty: aaa,,bbb
+ {\begincsname\??layoutarranger#1\endcsname}
+
+\permanent\protected\def\dosetuparrangement#1#2#3#4#5#6#7#8%
+ {\global\arrangedpageX #1%
+ \global\arrangedpageY #2%
+ \global\arrangedpageT #3%
+ \global\c_page_marks_nx#4%
+ \global\c_page_marks_ny#5%
+ \enforced\glet\pusharrangedpage #6%
+ \enforced\glet\poparrangedpages #7%
+ \enforced\glet\handlearrangedpage#8}
+
+\installpagearrangement {\v!normal}
+ {\global\arrangingpagesfalse}
+
+%D (WS:) Watch out with 2UP and A4/A5:
+%D
+%D \definepapersize [A4] [width=210mm,height=297mm]
+%D \definepapersize [A5] [width=148mm,height=210mm]
+%D
+%D 297 mm - 2 * 148 mm = 1 mm
+%D
+%D This is only visible when we have a full background.
+
+\installpagearrangement 2*16
+ {\dosetuparrangement{4}{4}{16}{5}{5}%
+ \pusharrangedpageTHIRTYTWO\poparrangedpagesAB\relax}
+
+\installpagearrangement 2*8
+ {\dosetuparrangement{4}{2}{8}{5}{3}%
+ \pusharrangedpageSIXTEEN\poparrangedpagesAB\relax}
+
+\installpagearrangement 2*4
+ {\dosetuparrangement{2}{2}{4}{3}{3}%
+ \pusharrangedpageEIGHT\poparrangedpagesAB\relax}
+
+\installpagearrangement 2*2
+ {\dosetuparrangement{2}{1}{2}{3}{2}%
+ \pusharrangedpageFOURA\poparrangedpagesAB\relax}
+
+\installpagearrangement 2**2
+ {\dosetuparrangement{2}{1}{2}{3}{2}%
+ \pusharrangedpageFOURB\poparrangedpagesAB\relax}
+
+\installpagearrangement 2SIDE
+ {\dosetuparrangement{2}{1}{2}{3}{2}%
+ \pusharrangedpageSIDETOP\poparrangedpagesTWO\handlearrangedpageSIDE}
+
+\installpagearrangement 2TOP
+ {\dosetuparrangement{1}{2}{2}{2}{3}%
+ \pusharrangedpageSIDETOP\poparrangedpagesTWO\handlearrangedpageTOP}
+
+\installpagearrangement 2UP
+ {\dosetuparrangement{2}{1}{4}{3}{2}%
+ \pusharrangedpageTWO\poparrangedpagesTWO\handlearrangedpageTWOUP}
+
+\installpagearrangement 2DOWN
+ {\dosetuparrangement{1}{2}{4}{2}{3}%
+ \pusharrangedpageTWO\poparrangedpagesTWO\handlearrangedpageTWODOWN}
+
+\installpagearrangement 2*4*2 % one defined by Willy Egger:
+ {\dosetuparrangement{2}{2}{4}{3}{3}%
+ \pusharrangedpageSIXTEENTWO\poparrangedpagesAtoD\relax}
+
+\installpagearrangement 2*2*4 % another one of Willy Egger
+ {\dosetuparrangement{2}{1}{8}{3}{2}%
+ \pusharrangedpageSIXTEENFOUR\poparrangedpagesAtoH\relax}
+
+\installpagearrangement 2TOPSIDE
+ {\dosetuparrangement{1}{2}{4}{2}{3}%
+ \pusharrangedpageTWOTOPSIDE\poparrangedpagesTWOTOPSIDE\handlearrangedpageTOP}
+
+\permanent\protected\def\filluparrangedpages % beware: \realpageno is 1 ahead
+ {\ifarrangingpages
+ \scratchcounter\numexpr\realpageno-\plusone\relax
+ \dosetmodulo\scratchcounter\arrangedpageT\scratchcounter
+ \ifcase\scratchcounter\else
+ \advance\scratchcounter \plusone
+ \dostepwiserecurse\scratchcounter\arrangedpageT\plusone
+ {\noheaderandfooterlines\page_otr_insert_dummy_page}%
+ \fi
+ \fi}
+
+\permanent\protected\def\handlearrangedpageXandY#1#2#3#4#5%
+ {\global\setbox#5\hpack to \arrangedpageX\paperwidth
+ {\setbox\scratchbox\vpack to \arrangedpageY\paperheight
+ {\offinterlineskip
+ \vskip#4\paperheight
+ \hskip#3\paperwidth
+ \dorotatebox{\ifcase#2 0\else180\fi}\hpack{\box#1}%
+ \vfill}%
+ \wd\scratchbox\zeropoint
+ \box\scratchbox\box#5\hss}}
+
+\permanent\protected\def\gotonextarrangepage
+ {\global\advance\arrangeno\plusone}
+
+\permanent\protected\def\outputarrangedbox#1%
+ {\begingroup
+ \forgetall % somehow we're back and need to redo this
+ \dontcomplain
+ \gotonextarrangepage
+ \ifnum\arrangedrotationO\arrangedrotationE>\zerocount
+ \setbox#1\vpack
+ {\ifconditional\arrangeddoublestate
+ \ifodd\arrangeno
+ \dorotatebox\arrangedrotationO\hpack{\box#1}%
+ \else
+ \dorotatebox\arrangedrotationE\hpack{\box#1}%
+ \fi
+ \else
+ \dorotatebox\arrangedrotationO\hpack{\box#1}%
+ \fi}%
+ \fi
+ \ifconditional\arrangedmirrorstate
+ \page_boxes_apply_mirror{#1}%
+ \fi
+ \ifconditional\arrangednegatestate
+ \page_boxes_apply_negate{#1}%
+ \fi
+ \page_marks_add_more #1%
+ \page_boxes_apply_center #1%
+ \page_boxes_apply_mirror_print #1%
+ \page_boxes_apply_orientate_print#1%
+ \page_boxes_apply_offset #1%
+ \page_boxes_apply_negate_print #1%
+ %
+ \ifconditional\arrangedbackgroundstate
+ \page_backgrounds_add_to_print#1%
+ \fi
+ %
+ \page_shipouts_normal{\box#1}%
+ \endgroup}
+
+%D The format file can be 16K smaller when we postpone the
+%D real arrangments. Some day ...
+
+\permanent\protected\def\reportarrangedpage#1%
+ {\showmessage\m!system{23}{\the\realpageno.\the\pageno\ifnum\subpageno>0 .\the\subpageno\fi,\number#1}}
+
+\permanent\protected\def\advancearrangedpageN
+ {\global\advance\arrangedpageN\plusone}
+
+% TOP
+
+% 32/16/8/4/SIDE
+
+\permanent\protected\def\poparrangedpagesAB
+ {\ifnum\arrangedpageN>\zerocount
+ \paperwidth\arrangedpageX\paperwidth
+ \paperheight\arrangedpageY\paperheight
+ \outputarrangedbox\arrangedpageA
+ \outputarrangedbox\arrangedpageB
+ \global\arrangedpageN\zerocount
+ \fi}
+
+\permanent\protected\def\pusharrangedpageTHIRTYTWO#1% taco's challenge
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}033\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}003\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}100\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}130\arrangedpageA % 4
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}130\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}033\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}003\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}102\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}132\arrangedpageB % 10
+ \or \handlearrangedpageXandY{#1}031\arrangedpageB % 11
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 12
+ \or \handlearrangedpageXandY{#1}031\arrangedpageA % 13
+ \or \handlearrangedpageXandY{#1}001\arrangedpageB % 14
+ \or \handlearrangedpageXandY{#1}102\arrangedpageB % 15
+ \or \handlearrangedpageXandY{#1}132\arrangedpageA % 16
+ \or \handlearrangedpageXandY{#1}122\arrangedpageA % 17
+ \or \handlearrangedpageXandY{#1}112\arrangedpageB % 18
+ \or \handlearrangedpageXandY{#1}011\arrangedpageB % 19
+ \or \handlearrangedpageXandY{#1}021\arrangedpageA % 20
+ \or \handlearrangedpageXandY{#1}011\arrangedpageA % 21
+ \or \handlearrangedpageXandY{#1}021\arrangedpageB % 22
+ \or \handlearrangedpageXandY{#1}122\arrangedpageB % 23
+ \or \handlearrangedpageXandY{#1}112\arrangedpageA % 24
+ \or \handlearrangedpageXandY{#1}013\arrangedpageA % 25
+ \or \handlearrangedpageXandY{#1}023\arrangedpageB % 26
+ \or \handlearrangedpageXandY{#1}120\arrangedpageB % 27
+ \or \handlearrangedpageXandY{#1}110\arrangedpageA % 28
+ \or \handlearrangedpageXandY{#1}120\arrangedpageA % 29
+ \or \handlearrangedpageXandY{#1}110\arrangedpageB % 30
+ \or \handlearrangedpageXandY{#1}013\arrangedpageB % 31
+ \or \handlearrangedpageXandY{#1}023\arrangedpageA % 32
+ \poparrangedpages
+ \fi}
+
+\permanent\protected\def\pusharrangedpageSIXTEEN#1% changed to match the official way of doing
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}031\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}001\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}031\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 4
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}130\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}100\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}130\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}120\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}110\arrangedpageB % 10
+ \or \handlearrangedpageXandY{#1}120\arrangedpageB % 11
+ \or \handlearrangedpageXandY{#1}110\arrangedpageA % 12
+ \or \handlearrangedpageXandY{#1}011\arrangedpageA % 13
+ \or \handlearrangedpageXandY{#1}021\arrangedpageB % 14
+ \or \handlearrangedpageXandY{#1}011\arrangedpageB % 15
+ \or \handlearrangedpageXandY{#1}021\arrangedpageA % 16
+ \poparrangedpages
+ \fi}
+
+\permanent\protected\def\pusharrangedpageEIGHT#1% changed to match the official way of doing
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}011\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}001\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}100\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}110\arrangedpageA % 4
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}110\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}011\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 8
+ \poparrangedpages
+ \fi}
+
+\permanent\protected\def\pusharrangedpageFOURA{\pusharrangedpageFOURdo01}
+\permanent\protected\def\pusharrangedpageFOURB{\pusharrangedpageFOURdo10}
+
+\permanent\protected\def\pusharrangedpageFOURdo#1#2#3%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#3}010\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#3}0{#1}0\arrangedpageB % 2/3 not {1}
+ \or \handlearrangedpageXandY{#3}0{#2}0\arrangedpageB % 3/2 not {1}
+ \or \handlearrangedpageXandY{#3}000\arrangedpageA % 4
+ \poparrangedpages
+ \fi}
+
+\permanent\protected\def\pusharrangedpageSIDETOP#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \poparrangedpages
+ \fi}
+
+\permanent\protected\def\handlearrangedpageSIDE
+ {\wd\arrangedpageA\paperwidth
+ \wd\arrangedpageB\paperwidth
+ \global\setbox\arrangedpageA\hpack
+ {\box\arrangedpageA\box\arrangedpageB}%
+ \ht\arrangedpageA\paperheight}
+
+\permanent\protected\def\handlearrangedpageTOP
+ {\ht\arrangedpageA\paperheight
+ \ht\arrangedpageB\paperheight
+ \global\setbox\arrangedpageA\vpack
+ {\offinterlineskip\vskip\paperheight
+ \box\arrangedpageA\box\arrangedpageB}%
+ \global\setbox\arrangedpageB\box\scratchbox} % ?
+
+% 2UP/2DOWN / 1pt prevents overflow
+
+\permanent\protected\def\splitoffarrangedpagesTWO
+ {\splittopskip\zeropoint
+ \global\setbox\arrangedpageA\vsplit\arrangedpageB to \onepoint
+ \scratchdimen\dimexpr\ht\arrangedpageB-\onepoint\relax
+ \ifdim\scratchdimen>\onepoint
+ \setbox\scratchbox\vsplit\arrangedpageB to \scratchdimen
+ \fi}
+
+\permanent\protected\def\handlearrangedpageTWOUP
+ {\splitoffarrangedpagesTWO
+ \ht\arrangedpageA\paperheight
+ \ht\arrangedpageB\paperheight
+ \ifconditional\arrangedswapstate
+ \global\setbox\arrangedpageA\hpack
+ {\page_boxes_apply_clip_print_left \arrangedpageA
+ \box\arrangedpageA
+ \page_boxes_apply_clip_print_right\arrangedpageB
+ \box\arrangedpageB}%
+ \setfalse\arrangedswapstate
+ \else
+ \global\setbox\arrangedpageA\hpack
+ {\page_boxes_apply_clip_print_left \arrangedpageB
+ \box\arrangedpageB
+ \page_boxes_apply_clip_print_right\arrangedpageA
+ \box\arrangedpageA}%
+ \settrue\arrangedswapstate
+ \fi
+ \global\setbox\arrangedpageB\box\scratchbox}
+
+\permanent\protected\def\handlearrangedpageTWODOWN
+ {\splitoffarrangedpagesTWO
+ \ht\arrangedpageA\paperheight
+ \ht\arrangedpageB\paperheight
+ \ifconditional\arrangedswapstate
+ \global\setbox\arrangedpageA\vpack
+ {\offinterlineskip
+ \vskip\paperheight
+ \box\arrangedpageA
+ \box\arrangedpageB}%
+ \setfalse\arrangedswapstate
+ \else
+ \global\setbox\arrangedpageA\vpack
+ {\offinterlineskip
+ \vskip\paperheight
+ \box\arrangedpageB
+ \box\arrangedpageA}%
+ \settrue\arrangedswapstate
+ \fi
+ \global\setbox\arrangedpageB\box\scratchbox}
+
+\permanent\protected\def\poparrangedpagesTWO
+ {\ifnum\arrangedpageN>\zerocount
+ \setfalse\arrangedswapstate
+ \doloop
+ {\handlearrangedpage
+ \bgroup
+ \paperwidth \arrangedpageX\paperwidth
+ \paperheight\arrangedpageY\paperheight
+ \ht\arrangedpageA\paperheight
+ \wd\arrangedpageA\paperwidth
+ \outputarrangedbox\arrangedpageA
+ \egroup
+ \ifdim\ht\arrangedpageB=\zeropoint
+ \exitloop
+ \fi}%
+ \global\arrangedpageN\zerocount
+ \fi}
+
+\permanent\protected\def\pusharrangedpageTWO#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \global\setbox\arrangedpageB\vpack
+ {\offinterlineskip
+ \unvbox\arrangedpageB
+ \allowbreak
+ \setbox#1\vpack{\box#1}% really needed in order to keep real dimensions
+ \ht#1\onepoint
+ \dp#1\zeropoint
+ \vpack{\box#1}}}
+
+\permanent\protected\def\poparrangedpagesTWOTOPSIDE
+ {\ifnum\arrangedpageN>\zerocount
+ \bgroup
+ \global\arrangedpageN\plustwo
+ \poparrangedpagesTWO
+ \let\arrangedpageA\arrangedpageC
+ \let\arrangedpageB\arrangedpageD
+ \global\arrangedpageN\plustwo
+ \poparrangedpagesTWO
+ \global\arrangedpageN\zerocount
+ \egroup
+ \fi}
+
+\permanent\protected\def\pusharrangedpageTWOTOPSIDE#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}000\arrangedpageC % 2
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 1
+ \or \handlearrangedpageXandY{#1}000\arrangedpageD % 2
+ \poparrangedpages
+ \fi}
+
+%D Willy Egger's sheet simulations:
+
+\permanent\protected\def\poparrangedpagesAtoH
+ {\ifnum\arrangedpageN>\zerocount
+ \paperwidth \arrangedpageX\paperwidth
+ \paperheight\arrangedpageY\paperheight
+ \outputarrangedbox\arrangedpageA
+ \outputarrangedbox\arrangedpageB
+ \outputarrangedbox\arrangedpageC
+ \outputarrangedbox\arrangedpageD
+ \outputarrangedbox\arrangedpageE
+ \outputarrangedbox\arrangedpageF
+ \outputarrangedbox\arrangedpageG
+ \outputarrangedbox\arrangedpageH
+ \global\arrangedpageN\zerocount
+ \fi}
+
+% to arrange 16 pages on 2 sheets to form one booklet
+
+\permanent\protected\def\poparrangedpagesAtoD
+ {\ifnum\arrangedpageN>\zerocount
+ \paperwidth\arrangedpageX\paperwidth
+ \paperheight\arrangedpageY\paperheight
+ \outputarrangedbox\arrangedpageA
+ \outputarrangedbox\arrangedpageB
+ \outputarrangedbox\arrangedpageC
+ \outputarrangedbox\arrangedpageD
+ \global\arrangedpageN\zerocount
+ \fi}
+
+% to arrange 16 pages on 4 sheets to form one booklet
+
+\permanent\protected\def\pusharrangedpageSIXTEENFOUR#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3
+ \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4
+ \or \handlearrangedpageXandY{#1}010\arrangedpageE % 5
+ \or \handlearrangedpageXandY{#1}000\arrangedpageF % 6
+ \or \handlearrangedpageXandY{#1}010\arrangedpageG % 7
+ \or \handlearrangedpageXandY{#1}000\arrangedpageH % 8
+ \or \handlearrangedpageXandY{#1}010\arrangedpageH % 9
+ \or \handlearrangedpageXandY{#1}000\arrangedpageG % 10
+ \or \handlearrangedpageXandY{#1}010\arrangedpageF % 11
+ \or \handlearrangedpageXandY{#1}000\arrangedpageE % 12
+ \or \handlearrangedpageXandY{#1}010\arrangedpageD % 13
+ \or \handlearrangedpageXandY{#1}000\arrangedpageC % 14
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 15
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 16
+ \poparrangedpages
+ \fi}
+
+% to arrange 16 pages on 2 sheets to form one booklet
+
+\permanent\protected\def\pusharrangedpageSIXTEENTWO#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}011\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}001\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}011\arrangedpageC % 3
+ \or \handlearrangedpageXandY{#1}001\arrangedpageD % 4
+ \or \handlearrangedpageXandY{#1}100\arrangedpageD % 5
+ \or \handlearrangedpageXandY{#1}110\arrangedpageC % 6
+ \or \handlearrangedpageXandY{#1}100\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}110\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}110\arrangedpageB % 10
+ \or \handlearrangedpageXandY{#1}100\arrangedpageC % 11
+ \or \handlearrangedpageXandY{#1}110\arrangedpageD % 12
+ \or \handlearrangedpageXandY{#1}011\arrangedpageD % 13
+ \or \handlearrangedpageXandY{#1}001\arrangedpageC % 14
+ \or \handlearrangedpageXandY{#1}011\arrangedpageB % 15
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 16
+ \poparrangedpages
+ \fi}
+
+%D Might be used if a printer is printing from a roll or creating mini-books from A4:
+%D This section has 16 pages. The folding scheme is first a Z-fold and at the end
+%D a final fold in the spine.
+%D Coding: [2*8*Z]
+
+\installpagearrangement 2*8*Z
+ {\dosetuparrangement{2}{4}{8}{3}{5}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageSIXTEENZ\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageSIXTEENZ#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}101\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}111\arrangedpageA % 4
+ \or \handlearrangedpageXandY{#1}012\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}002\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}103\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}113\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}103\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}113\arrangedpageB % 10
+ \or \handlearrangedpageXandY{#1}012\arrangedpageB % 11
+ \or \handlearrangedpageXandY{#1}002\arrangedpageA % 12
+ \or \handlearrangedpageXandY{#1}101\arrangedpageA % 13
+ \or \handlearrangedpageXandY{#1}111\arrangedpageB % 14
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 15
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 16
+ \poparrangedpages
+ \fi}
+
+%D Another Z-folded section with 12 pages
+%D Coding: [2*6*Z]
+
+\installpagearrangement 2*6*Z
+ {\dosetuparrangement{2}{3}{6}{3}{4}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageTWELVEZ\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageTWELVEZ#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1: rotation (0=upright),x (0=first column),y (0=first row)
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}101\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}111\arrangedpageA % 4
+ \or \handlearrangedpageXandY{#1}012\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}002\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}012\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}002\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}101\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}111\arrangedpageB % 10
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 11
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 12
+ \poparrangedpages
+ \fi}
+
+%D For Heinz' special greeting cards folding. This scheme is also used for the PocketDiary (module):
+%D Coding: [1*8]
+
+\installpagearrangement 1*8
+ {\dosetuparrangement{4}{2}{8}{5}{3}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageEIGHTSINGLESIDEDFOLDED\poparrangedpagesTWO\relax}
+
+\permanent\protected\def\pusharrangedpageEIGHTSINGLESIDEDFOLDED#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 2
+ \or \handlearrangedpageXandY{#1}030\arrangedpageA % 3
+ \or \handlearrangedpageXandY{#1}131\arrangedpageA % 4
+ \or \handlearrangedpageXandY{#1}121\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}111\arrangedpageA % 6
+ \or \handlearrangedpageXandY{#1}101\arrangedpageA % 7
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8
+ \poparrangedpages
+ \fi}
+
+%D This is not a section. \CONTEXT\ places 4 pages on a sheet of paper, singlesided
+%D Coding: [1*4]
+
+\installpagearrangement 1*4
+ {\dosetuparrangement{2}{2}{4}{3}{3}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageFOURSINGLESIDEDFOLDED\poparrangedpagesTWO\relax}
+
+\permanent\protected\def\pusharrangedpageFOURSINGLESIDEDFOLDED#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 2
+ \or \handlearrangedpageXandY{#1}011\arrangedpageA % 3
+ \or \handlearrangedpageXandY{#1}110\arrangedpageA % 4
+ \poparrangedpages
+ \fi}
+
+%D This imposition scheme was requested by Hraban Ramm, by Willi Egger 21-07-2003
+%D Coding: [3SIDE]
+
+\installpagearrangement 3SIDE
+ {\dosetuparrangement{3}{1}{3}{4}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageTHREESIDE\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageTHREESIDE#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 2
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 3
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 5
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 6
+ \poparrangedpages
+ \fi}
+
+%D FLYER in three parts and 6 pages 22-10-2010
+%D Coding: [TRYPTICHON]
+
+\installpagearrangement TRYPTICHON
+ {\dosetuparrangement{3}{1}{3}{4}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageFLYERSIX\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageFLYERSIX#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 5
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 6
+ \poparrangedpages
+ \fi}
+
+%D FLYER in Z-fold with 8 pages 22-01-2010
+%D Coding: [ZFLYER-8]
+
+\installpagearrangement ZFLYER-8
+ {\dosetuparrangement{4}{1}{4}{5}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageZFLYEREIGHT\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageZFLYEREIGHT#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}030\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 6
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 7
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 8
+ \poparrangedpages
+ \fi}
+
+%D FLYER in Z-fold with 10 pages 04-08-2010
+%D Coding: [ZFLYER-10]
+
+\installpagearrangement ZFLYER-10
+ {\dosetuparrangement{5}{1}{5}{6}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageZFLYERTEN\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageZFLYERTEN#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}040\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5
+ \or \handlearrangedpageXandY{#1}040\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 7
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}030\arrangedpageA % 10
+ \poparrangedpages
+ \fi}
+
+%D FLYER in Z-fold with 12 pages 04-08-2010
+%D Coding: [ZFLYER-12]
+
+\installpagearrangement ZFLYER-12
+ {\dosetuparrangement{6}{1}{6}{7}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageZFLYERTWELVE\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageZFLYERTWELVE#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}050\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5
+ \or \handlearrangedpageXandY{#1}040\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}050\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 10
+ \or \handlearrangedpageXandY{#1}030\arrangedpageA % 11
+ \or \handlearrangedpageXandY{#1}040\arrangedpageA % 12
+ \poparrangedpages
+ \fi}
+
+%D FLYER folded as a map with 6 pages per side.
+%D Coding: [MAPFLYER-12]
+
+\installpagearrangement MAPFLYER-12
+ {\dosetuparrangement{3}{2}{6}{4}{3}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageMFLYERTWELVE\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageMFLYERTWELVE#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}001\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}011\arrangedpageB % 5
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}021\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 9
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 10
+ \or \handlearrangedpageXandY{#1}011\arrangedpageA % 11
+ \or \handlearrangedpageXandY{#1}021\arrangedpageA % 12
+ \poparrangedpages
+ \fi}
+
+%D FLYER folded as double window with 4 pages per side.
+%D Coding: [DOUBLEWINDOW]
+
+\installpagearrangement DOUBLEWINDOW
+ {\dosetuparrangement{4}{1}{4}{5}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageDOUBLEWINDOWEIGHT\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageDOUBLEWINDOWEIGHT#1% Willi's approach
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}030\arrangedpageA % 2
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 4
+ \or \handlearrangedpageXandY{#1}020\arrangedpageB % 5
+ \or \handlearrangedpageXandY{#1}030\arrangedpageB % 6
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 7
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 8
+ \poparrangedpages
+ \fi}
+
+%D Imposition as requested by Jan Pohanka 26-08-2010, 4 pages, two verso, two recto,
+%D uneven pages upright and down, even pages top and rotated 180.
+%D Implementation with 2 pages for conference-name-display
+%D Coding: [1*2-Conference]
+
+\installpagearrangement 1*2-Conference
+ {\dosetuparrangement{1}{2}{4}{3}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageCONFERENCETWO\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageCONFERENCETWO#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 2
+ \poparrangedpages
+ \fi}
+
+%D Implementation with 4 pages for conference-name-display
+%D Coding: [1*4-Conference]
+
+\installpagearrangement 1*4-Conference
+ {\dosetuparrangement{1}{2}{4}{3}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageCONFERENCEFOUR\poparrangedpagesAB\relax}
+
+\permanent\protected\def\pusharrangedpageCONFERENCEFOUR#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}100\arrangedpageA % 2
+ \or \handlearrangedpageXandY{#1}011\arrangedpageB % 3
+ \or \handlearrangedpageXandY{#1}110\arrangedpageB % 4
+ \poparrangedpages
+ \fi}
+
+%D There should be arrangements for sections made of heavy and thick paper. i.e. the heavier the paper
+%D the fewer pages per section:
+%D Section with 8 pages put on two sheets of paper. Each sheet carries recto 2 and verso 2 pages.
+%D Coding: [2*2*2]
+
+\installpagearrangement 2*2*2
+ {\dosetuparrangement{2}{1}{2}{3}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageEIGHTTWO\poparrangedpagesAtoD\relax}
+
+\permanent\protected\def\pusharrangedpageEIGHTTWO#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3
+ \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4
+ \or \handlearrangedpageXandY{#1}010\arrangedpageD % 5
+ \or \handlearrangedpageXandY{#1}000\arrangedpageC % 6
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 7
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8
+ \poparrangedpages
+ \fi}
+
+%D Section with 12 pages, built from three sheets of paper.
+%D Each sheet carries 2 pages recto and verso.
+%D Coding: [2*2*3]
+
+\permanent\protected\def\poparrangedpagesAtoF
+ {\ifnum\arrangedpageN>\zerocount
+ \paperwidth \arrangedpageX\paperwidth
+ \paperheight\arrangedpageY\paperheight
+ \outputarrangedbox\arrangedpageA
+ \outputarrangedbox\arrangedpageB
+ \outputarrangedbox\arrangedpageC
+ \outputarrangedbox\arrangedpageD
+ \outputarrangedbox\arrangedpageE
+ \outputarrangedbox\arrangedpageF
+ \global\arrangedpageN\zerocount
+ \fi}
+
+\installpagearrangement 2*2*3
+ {\dosetuparrangement{2}{1}{2}{3}{2}% X,Y,Total,hcutmarks,vcutmarks
+ \pusharrangedpageTWELVETWO\poparrangedpagesAtoF\relax}
+
+\permanent\protected\def\pusharrangedpageTWELVETWO#1%
+ {\advancearrangedpageN
+ \reportarrangedpage\arrangedpageN
+ \ifcase\arrangedpageN
+ \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip
+ \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2
+ \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3
+ \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4
+ \or \handlearrangedpageXandY{#1}010\arrangedpageE % 5
+ \or \handlearrangedpageXandY{#1}000\arrangedpageF % 6
+ \or \handlearrangedpageXandY{#1}010\arrangedpageF % 7
+ \or \handlearrangedpageXandY{#1}000\arrangedpageE % 8
+ \or \handlearrangedpageXandY{#1}010\arrangedpageD % 9
+ \or \handlearrangedpageXandY{#1}000\arrangedpageC % 10
+ \or \handlearrangedpageXandY{#1}010\arrangedpageB % 11
+ \or \handlearrangedpageXandY{#1}000\arrangedpageA % 12
+ \poparrangedpages
+ \fi}
+
+% % From Wolfgang for Mari (mail on list) ... yes or no in core .. time for
+% % delayed loading ...
+% %
+% % http://stamphenge.wordpress.com/minibooks/meander-accordion-folded-book/
+%
+% \installpagearrangement MEANDER16
+% {\dosetuparrangement{4}{4}{16}{5}{5}%
+% \pusharrangedpageMEANDERSIXTEEN\poparrangedpagesXY\relax}
+%
+% \permanent\protected\def\pusharrangedpageMEANDERSIXTEEN#1%
+% {\advancearrangedpageN
+% \reportarrangedpage\arrangedpageN
+% \ifcase\arrangedpageN
+% \or \handlearrangedpageXandY{#1}000\arrangedpageA
+% \or \handlearrangedpageXandY{#1}010\arrangedpageA
+% \or \handlearrangedpageXandY{#1}020\arrangedpageA
+% \or \handlearrangedpageXandY{#1}030\arrangedpageA
+% \or \handlearrangedpageXandY{#1}131\arrangedpageA
+% \or \handlearrangedpageXandY{#1}121\arrangedpageA
+% \or \handlearrangedpageXandY{#1}111\arrangedpageA
+% \or \handlearrangedpageXandY{#1}101\arrangedpageA
+% \or \handlearrangedpageXandY{#1}002\arrangedpageA
+% \or \handlearrangedpageXandY{#1}012\arrangedpageA
+% \or \handlearrangedpageXandY{#1}022\arrangedpageA
+% \or \handlearrangedpageXandY{#1}032\arrangedpageA
+% \or \handlearrangedpageXandY{#1}133\arrangedpageA
+% \or \handlearrangedpageXandY{#1}123\arrangedpageA
+% \or \handlearrangedpageXandY{#1}113\arrangedpageA
+% \or \handlearrangedpageXandY{#1}103\arrangedpageA
+% \poparrangedpages
+% \fi}
+%
+% % \definepapersize[small][width=6cm,height=6cm]
+% % \definepapersize[big][width=30cm,height=30cm]
+% % \setuppapersize[small][big]
+% % \setuppagenumbering[location=]
+% % \setuparranging[MEANDER16]
+% % \setuplayout
+% % [location=middle,
+% % marking=on]
+% % \starttext
+% % \dorecurse{32}{\centerbox{\ssd\recurselevel}}
+% % \stoptext
+%
+% % By Willi:
+%
+% \installpagearrangement MEANDER9
+% {\dosetuparrangement{3}{3}{9}{4}{4}%
+% \pusharrangedpageMEANDERNINE\poparrangedpagesXY\relax}
+%
+% \permanent\protected\def\pusharrangedpageMEANDERNINE#1%
+% {\advancearrangedpageN
+% \reportarrangedpage\arrangedpageN
+% \ifcase\arrangedpageN
+% \or \handlearrangedpageXandY{#1}000\arrangedpageA
+% \or \handlearrangedpageXandY{#1}010\arrangedpageA
+% \or \handlearrangedpageXandY{#1}020\arrangedpageA
+% \or \handlearrangedpageXandY{#1}121\arrangedpageA
+% \or \handlearrangedpageXandY{#1}111\arrangedpageA
+% \or \handlearrangedpageXandY{#1}101\arrangedpageA
+% \or \handlearrangedpageXandY{#1}002\arrangedpageA
+% \or \handlearrangedpageXandY{#1}012\arrangedpageA
+% \or \handlearrangedpageXandY{#1}022\arrangedpageA
+% \poparrangedpages
+% \fi}
+
+% % handy for stickers etc, this way we can treat them as page
+%
+% \setuppapersize [XY][A4]
+% \setuppaper [topspace=5mm,backspace=5mm,dx=1mm,dy=1mm,nx=2,ny=6]
+% \setuplayout [page] [topspace=5mm,backspace=5mm]
+% \setuplayout [page]
+% \setuplayout [location=middle]
+% \setuparranging [XY]
+% \showframe
+%
+% \starttext \dorecurse{30}{test \recurselevel \page} \stoptext
+
+\permanent\protected\protected\def\pusharrangedpageXY#1%
+ {\advancearrangedpageN
+ \global\advance\arrangedpageM\plusone
+ \reportarrangedpage\arrangedpageN
+ \global\setbox\arrangedpageB\hpack \ifdim\v_page_target_width>\zeropoint to \v_page_target_width \fi
+ {\ifvoid\arrangedpageB\else
+ \unhbox\arrangedpageB
+ \ifdim\v_page_target_dx>\zeropoint
+ \hskip\v_page_target_dx
+ \else
+ \hss
+ \hskip\v_page_target_dx
+ \hss
+ \fi
+ \fi
+ \box#1}%
+ \ifnum\arrangedpageM<\arrangedpageX\else
+ \global\setbox\arrangedpageA\vpack \ifdim\v_page_target_height>\zeropoint to \v_page_target_height \fi
+ {\offinterlineskip
+ \ifvoid\arrangedpageA\else
+ \unvbox\arrangedpageA
+ \ifdim\v_page_target_dy>\zeropoint
+ \vskip\v_page_target_dy
+ \else
+ \vss
+ \vskip\v_page_target_dy
+ \vss
+ \fi
+ \fi
+ \box\arrangedpageB}%
+ \global\arrangedpageM\zerocount
+ \fi
+ \ifnum\arrangedpageN<\arrangedpageT\else
+ \poparrangedpages
+ \fi}
+
+\permanent\protected\protected\def\poparrangedpagesXY
+ {\ifnum\arrangedpageN>\zerocount
+ \paperwidth \arrangedpageX\paperwidth
+ \paperheight\arrangedpageY\paperheight
+ \outputarrangedbox\arrangedpageA
+ \global\arrangedpageN\zerocount
+ \global\arrangedpageM\zerocount
+ \fi}
+
+\installpagearrangement XY
+ {\dosetuparrangement
+ \v_page_target_nx
+ \v_page_target_ny
+ \v_page_target_xy
+ \zerocount
+ \zerocount
+ \pusharrangedpageXY\poparrangedpagesXY\relax}
+
+%D A crazy definition, don't guess who pushed me for the landscape option.
+
+\def\page_imposition_xy_height
+ {\dimexpr
+ \dimexpr
+ \v_page_target_height
+ -\numexpr\v_page_target_ny-1\relax\v_page_target_dy
+ \relax
+ /\v_page_target_ny
+ \relax}
+
+\def\page_imposition_xy_width
+ {\dimexpr
+ \dimexpr
+ \v_page_target_width
+ -\numexpr\v_page_target_nx-1\relax\v_page_target_dx
+ \relax
+ /\v_page_target_nx
+ \relax}
+
+\definepapersize
+ [XY]
+ [\c!height=\page_imposition_xy_height,
+ \c!width =\page_imposition_xy_width]
+
+\setuppaper
+ [\c!width =\dimexpr\printpaperwidth -2\v_page_target_backspace\relax,
+ \c!height=\dimexpr\printpaperheight-2\v_page_target_topspace \relax]
+
+% \definepageshift[test][horizontal][10pt,20pt,30pt,40pt,50pt]
+% \definepageshift[test][vertical] [10pt,20pt,30pt,40pt,50pt]
+%
+% \setuppageshift[test]
+% \setuppageshift[test][test]
+% \setuppageshift[test][none]
+% \setuppageshift[none][test]
+% \setuppageshift[paper][test][test] % arrange only
+% \setuppageshift[paper][test] % arrange only
+% \setuppageshift[print][test][test]
+%
+% \showframe \dorecurse{100}{\input tufte \par}
+
+% #1=name #2=horizontal|vertical #3=shiftlist
+
+% this will move to lua
+
+\installcorenamespace {pageshift}
+
+\permanent\protected\tolerant\def\definepageshift[#1]#*[#2]#*[#3]%
+ {\setvalue{\??pageshift#2:#1}{#3}}
+
+\let\page_boxes_h_shifts\empty
+\let\page_boxes_v_shifts\empty
+
+\newcount\c_page_boxes_h_shifts
+\newcount\c_page_boxes_v_shifts
+
+\newdimen\d_page_boxes_h_shifts
+\newdimen\d_page_boxes_v_shifts
+
+\let\page_boxes_apply_shift_print\gobbleoneargument
+\let\page_boxes_apply_shift_paper\gobbleoneargument
+
+\def\page_boxes_filter_shift#1#2#3% #1=\dimenx #2=\xpageshifts #3=\nofxpageshifts
+ {#1\zeropoint
+ \ifempty#2\else
+ \global\advance#3\plusone
+ \getfromcommacommand[#2][#3]%
+ \ifempty\commalistelement
+ \global#3\plusone % we cycle
+ \getfromcommacommand[#2][#3]%
+ \fi
+ \ifempty\commalistelement \else
+ #1\commalistelement\relax % the relax is really needed as there is an \if later on
+ \fi
+ \fi}
+
+\def\page_boxes_apply_shift#1%
+ {\page_boxes_filter_shift\d_page_boxes_h_shifts\page_boxes_h_shifts\c_page_boxes_h_shifts
+ \page_boxes_filter_shift\d_page_boxes_v_shifts\page_boxes_v_shifts\c_page_boxes_v_shifts
+ \ifzeropt\d_page_boxes_h_shifts
+ \ifzeropt\d_page_boxes_v_shifts
+ % nothing to shift
+ \else
+ \page_boxes_apply_shift_indeed#1%
+ \fi
+ \else
+ \page_boxes_apply_shift_indeed#1%
+ \fi}
+
+\def\page_boxes_apply_shift_indeed#1%
+ {\edef\next{\wd#1\the\wd#1\ht#1\the\ht#1\dp#1\the\dp#1}%
+ \setbox#1\vpack
+ {\offinterlineskip
+ \vskip\d_page_boxes_v_shifts
+ \hskip\d_page_boxes_h_shifts
+ \box#1}%
+ \next}
+
+\permanent\tolerant\protected\def\setuppageshift[#1]#*[#2]#*[#3]% page|paper horizontal vertical
+ {\ifarguments\or
+ \setuppageshift[\v!page][#1][#1]%
+ \or
+ \doifelseinset{#1}{\v!page,\v!paper}
+ {\setuppageshift[#1][#2][#2]}
+ {\setuppageshift[\v!page][#1][#2]}%
+ \or
+ %edef\page_boxes_h_shifts{\ifcsname\??pageshift\v!horizontal:#2\endcsname\csname\??pageshift\v!horizontal:#2\endcsname\fi}%
+ %edef\page_boxes_v_shifts{\ifcsname\??pageshift\v!vertical :#3\endcsname\csname\??pageshift\v!vertical :#3\endcsname\fi}%
+ \edef\page_boxes_h_shifts{\begincsname\??pageshift\v!horizontal:#2\endcsname}%
+ \edef\page_boxes_v_shifts{\begincsname\??pageshift\v!vertical :#3\endcsname}%
+ \doifelse{#1}\v!page {\let\page_boxes_apply_shift_print\page_boxes_apply_shift}{\let\page_boxes_apply_shift_print\gobbleoneargument}%
+ \doifelse{#1}\v!paper{\let\page_boxes_apply_shift_paper\page_boxes_apply_shift}{\let\page_boxes_apply_shift_paper\gobbleoneargument}%
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-inf.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-inf.mkxl
new file mode 100644
index 00000000000..7e65d6e9a80
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-inf.mkxl
@@ -0,0 +1,124 @@
+%D \module
+%D [ file=page-inf, % moved from page-ini
+%D version=2011.12.07, % 2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Tracing Info,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Tracing Info}
+
+% This needs a real cleanup .. will be a more detailed list.
+
+\unprotect
+
+\installcorenamespace{layoutinfo}
+
+\newtoks\everyresetversion
+\newtoks\everysetupversion
+
+\mutable\let\currentversioninfo\empty
+\mutable\let\currentversiontext\empty
+
+\let\page_info_add_to_box\gobbleoneargument
+
+\permanent\tolerant\protected\def\setupversion[#1]%
+ {\the\everyresetversion
+ \doifelseassignment{#1}%
+ {\getdummyparameters[\c!alternative=,\c!text=,#1]%
+ \edef\currentversioninfo{\dummyparameter\c!alternative}%
+ \edef\currentversiontext{\dummyparameter\c!text}}%
+ {\edef\currentversioninfo{#1}%
+ \let \currentversiontext\empty}%
+ \ifcsname\??layoutinfo\currentversioninfo\endcsname
+ \let\page_info_add_to_box\page_info_add_to_box_indeed
+ \else
+ \let\currentversioninfo\empty
+ \let\page_info_add_to_box\gobbleoneargument
+ \fi
+ \the\everysetupversion}
+
+\aliased\let\version\setupversion
+
+\protected\def\page_info_place_info % at the bottom of the page
+ {\begincsname\??layoutinfo\currentversioninfo\endcsname}
+
+\permanent\protected\def\installversioninfo#1#2%
+ {\setvalue{\??layoutinfo#1}{#2}}
+
+\permanent\protected\def\includeversioninfo#1%
+ {\begincsname\??layoutinfo#1\endcsname}
+
+\installversioninfo\v!final
+ {\let\currentversioninfo\empty
+ \let\page_info_add_to_box\gobbleoneargument}
+
+\installversioninfo\v!concept
+ {\vskip6\points
+ \hbox to \makeupwidth
+ {\infofont
+ \rlap{\v!concept:\space\currentdate}%
+ \hss
+ \currentversiontext
+ \hss
+ \llap{\page_adapts_status_info}}}
+
+\installversioninfo\v!file
+ {\vskip6\points
+ \hbox to \makeupwidth
+ {\infofont
+ \getmessage\m!system{27}:\space\currentdate\quad
+ \donefalse
+ \doif\currentproject\currentproduct{\doif\currentproduct\currentcomponent\donetrue}%
+ \ifdone
+ \donefalse
+ \else
+ \ifempty\currentproject \else \space\v!project :\space\currentproject \space \donetrue \fi
+ \ifempty\currentproduct \else \space\v!product :\space\currentproduct \space \donetrue \fi
+ \ifempty\currentcomponent \else \space\v!component:\space\currentcomponent\space \donetrue \fi
+ \fi
+ \ifdone \else \space\v!file :\space\jobname \space \fi
+ \hss
+ \currentversiontext
+ \quad
+ \page_adapts_status_info}}
+
+\installversioninfo\v!temporary
+ {\includeversioninfo\v!file}
+
+\appendtoks
+ \overfullrule\zeropoint
+\to \everyresetversion
+
+\appendtoks
+ \ifx\currentversioninfo\v!temporary
+ \overfullrule5\points
+ \fi
+\to \everysetupversion
+
+\newbox\b_page_versions
+
+\def\page_info_add_to_box_indeed#1%
+ {\scratchdimen\wd#1%
+ \setbox\b_page_versions\vpack to \ht#1%
+ {\vfill
+ \settexthoffset
+ \hsize\dimexpr\scratchdimen-2\texthoffset\relax
+ \hkern\texthoffset % brrrr
+ \vbox to \zeropoint{\vss\page_info_place_info}%
+ \vkern\bodyfontsize}%
+ \dp\b_page_versions\zeropoint
+ \wd\b_page_versions\scratchdimen
+ \setbox#1\hpack{\box#1\hkern-\scratchdimen\box\b_page_versions}}
+
+\setupversion % resets
+ [\v!final]
+
+\let\page_info_add_to_box\gobbleoneargument
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-ini.mkxl
new file mode 100644
index 00000000000..0a0993d44d3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-ini.mkxl
@@ -0,0 +1,376 @@
+%D \module
+%D [ file=page-ini,
+%D version=2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Initializations,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Initializations}
+
+\registerctxluafile{page-ini}{}
+
+%D The \type {\processpage} command has become obsolete. It's original
+%D purpose was to flush only parts of a document but nowadays we have
+%D project structure and modes. This is just one of those very early
+%D features that no longer makes sense.
+
+%D The \type {\couplepage} command has become obsolete. It's original
+%D purpose was to provide hooks for printer directives (like choosing
+%D different paper trays).
+
+\unprotect
+
+%D Shipout control.
+
+\newcount\nofshipouts
+
+\appendtoks
+ \global\advance\nofshipouts\plusone
+\to \everyaftershipout
+
+\newconditional\c_otr_shipout_enabled \settrue\c_otr_shipout_enabled
+
+\protected\def\page_otr_shipout % not used at all
+ {\ifconditional\c_otr_shipout_enabled
+ \expandafter\page_otr_shipout_yes
+ \else
+ \expandafter\page_otr_shipout_nop
+ \fi}
+
+\aliased\let\page_otr_shipout_yes\normalshipout
+
+\protected\def\page_otr_shipout_nop
+ {\writestatus\m!system{ignoring shipout of real page \the\realpageno}%
+ \global\advance\realpageno\minusone % else no flush of resources
+ \dowithnextbox{\deadcycles\zerocount}}
+
+% We don't support direct user \shipouts (weird: this does not work, bug?)
+%
+% \let\shipout\page_otr_shipout_nop
+
+%D Building pages.
+
+\newbox\pagebox
+
+% \ifdefined\page_postprocessors_column \else \let\page_postprocessors_column\gobbleoneargument \fi
+% \ifdefined\page_postprocessors_page \else \let\page_postprocessors_page \gobbleoneargument \fi
+% \ifdefined\page_postprocessors_box \else \let\page_postprocessors_box \gobbleoneargument \fi
+
+\newtoks\t_page_postprocessors_box
+\newtoks\t_page_postprocessors_page
+\newtoks\t_page_postprocessors_column
+
+\newconstant\b_page_postprocessor
+
+\def\page_apply_postprocessors_box#1%
+ {\b_page_postprocessor#1\relax
+ \the\t_page_postprocessors_box}
+
+\def\page_apply_postprocessors_page#1%
+ {\b_page_postprocessor#1\relax
+ \the\t_page_postprocessors_page}
+
+\def\page_apply_postprocessors_column#1%
+ {\b_page_postprocessor#1\relax
+ \the\t_page_postprocessors_column}
+
+%D Floats.
+
+\def\page_otr_flush_all_floats
+ {%\flushnotes already done
+ \ifconditional\c_page_floats_some_waiting
+ \page_otr_command_flush_all_floats
+ \fi}
+
+\def\page_otr_insert_dummy_page
+ {\par
+ \ifvmode
+ \page_otr_flush_all_floats
+ \fixedspace
+ \vfill
+ \page_otr_command_next_page
+ \fi}
+
+\def\page_otr_flush_pending_content
+ {\vkern\zeropoint\relax
+ \ifvoid\normalpagebox \else
+ \unvbox\normalpagebox
+ \penalty\outputpenalty
+ \fi}
+
+\newconstant\c_page_postponed_mode % \c_page_postponed_mode\plusone
+\newbox \b_page_otr_saved
+
+\installoutputroutine\savepagecontent
+ {\global\setbox\b_page_otr_saved\box\normalpagebox}
+
+\permanent\protected\def\flushpagecontent
+ {\ifvoid\b_page_otr_saved\else\unvbox\b_page_otr_saved\fi}
+
+% Test case: assumes one group level for output routine, todo:
+% a toks.
+%
+% \starttext
+%
+% \startbuffer[makeup]
+% \startpagemakeup[pagestate=start,page=no]
+% \color[red]{
+% \vfill
+% \dorecurse{3}{
+% \samplefile{klein}
+% \vfill
+% }
+% }
+% \stoppagemakeup
+% \stopbuffer
+%
+% \startcolor[green]
+% \dorecurse{10}{
+% \dontleavehmode{\bf 1:#1:} \samplefile{sapolsky}\par
+% }
+% \stopcolor
+%
+% \startpostponing % [+3]
+% \getbuffer[makeup]
+% \stoppostponing
+%
+% \startcolor[blue]
+% \dorecurse{25}{
+% \dontleavehmode{\bf 2:#1:} \samplefile{sapolsky}\par
+% }
+% \stopcolor
+%
+% \stoptext
+
+\def\page_otr_construct_and_shipout#1#2#3%
+ {\forgetall
+ \page_boxes_shipout{\page_boxes_constructed_page#1#2}% \hbox removed
+ \page_otr_flush_pending_content
+ % not really needed, replaced by \flushsavedfloats
+ \page_otr_check_for_pending_inserts
+ % but does not hurt either (we're still in the otr!)
+ \inpagebodytrue % needed for enabling \blank ! brrr
+ \pagebodymode\plusone % todo: \plustwo when spread
+ \page_otr_command_flush_saved_floats
+ \page_otr_command_set_vsize % this is needed for interacting components, like floats and multicolumns
+ \strc_pagenumbers_increment_counters % should hook into an every
+ \page_adapts_synchronize
+ \page_otr_check_for_pending_inserts
+ \page_otr_command_flush_facing_floats
+ \page_floats_flush_page_floats % before postponed blocks
+ \page_spread_flush % defined later
+ \ifnum#3=\plusone
+ % this is tricky! we need to make sure we are in the output group
+ \ifnum\c_page_postponed_mode=\plusone
+ \aftergroup\page_postponed_blocks_flush
+ \else
+ \page_postponed_blocks_flush
+ \fi
+ \else
+ \page_postponed_blocks_flush
+ \fi}
+
+% Can't we get rid of this hackery? It's used in some widgets
+% stuff so probably not.
+
+\newbox\b_page_otr_special
+
+\def\page_otr_flush_special_content
+ {\ifvoid\b_page_otr_special \else
+ \box\b_page_otr_special
+ \fi}
+
+\permanent\protected\def\page_otr_add_special_content
+ {\dowithnextboxcs\page_otr_add_special_content_indeed\hbox}
+
+\permanent\protected\def\page_otr_add_special_content_indeed
+ {\wd\nextbox\zeropoint
+ \ht\nextbox\zeropoint
+ \dp\nextbox\zeropoint
+ \global\setbox\b_page_otr_special\hbox
+ {%\hskip-\maxdimen % not here, fails in acrobat (clips)
+ \box\b_page_otr_special\box\nextbox}}% was \unhbox, is now box again
+
+\aliased\let\flushatshipout\page_otr_add_special_content % this might go away
+
+\maxdeadcycles\plusthousand
+
+\newtoks\afterpage \newtoks\aftereverypage
+\newtoks\beforepage \newtoks\beforeeverypage
+
+\newif\ifarrangingpages \arrangingpagesfalse
+
+\newconstant\pageornamentstate % 0=on 1=one-off 2=always-off
+
+% \appendtoks
+% \ifcase\pageornamentstate \or
+% \pageornamentstate\zerocount
+% \fi
+% \to \everyaftershipout
+
+\appendtoks
+ \ifcase\pageornamentstate \or
+ \global\pageornamentstate\zerocount
+ \fi
+\to \everyaftershipout
+
+% Mark synchronization
+
+\newconditional\c_page_marks_building_successive_pages \settrue\c_page_marks_building_successive_pages
+
+\def\page_marks_synchronize_page#1% box
+ {\strc_markings_synchronize\v!page{#1}{\ifconditional\c_page_marks_building_successive_pages\v!keep\fi}}
+
+\def\page_marks_synchronize_column#1#2#3#4% first last column box
+ {\ifnum#3=#1\relax
+ \strc_markings_synchronize{\number#3,\v!column:\number#3,\v!first,\v!column:\v!first}{#4}{}%
+ \orelse\ifnum#3=#2\relax
+ \strc_markings_synchronize{\number#3,\v!column:\number#3,\v!last, \v!column:\v!last }{#4}{}%
+ \else
+ \strc_markings_synchronize{\number#3,\v!column:\number#3 }{#4}{}%
+ \fi}
+
+% Page body building
+
+\newconditional\c_page_boxes_save_page_body
+\newbox \b_page_boxes_saved_page_body
+
+\def\page_boxes_constructed_page_body#1#2%
+ {\ifconditional\c_page_boxes_save_page_body \global\setbox\b_page_boxes_saved_page_body \fi \vpack \bgroup
+ \boxmaxdepth\maxdimen % new
+ \dontcomplain
+ \page_marks_synchronize_page {#2}% we could consider doing this for \pagebox (needs testing)
+ \page_boxes_construct_content \pagebox{#1}{#2}%
+ \page_backgrounds_add_to_main \pagebox
+ \page_boxes_apply_offsets \pagebox
+ \page_info_add_to_box \pagebox
+ \ifcase\pageornamentstate
+ \page_backgrounds_add_to_paper \pagebox
+ \fi
+ \anch_positions_register_page \pagebox
+ \ifarrangingpages
+ \page_boxes_apply_shift_paper \pagebox % \v!paper
+ \else
+ \page_boxes_apply_clip_paper \pagebox
+ \page_marks_add_page \pagebox
+ \page_boxes_apply_replicate \pagebox
+ \page_boxes_apply_scale \pagebox
+ \page_boxes_apply_mirror_paper \pagebox
+ \page_boxes_apply_orientate_paper \pagebox
+ \page_marks_add_more \pagebox
+ \page_boxes_apply_center \pagebox
+ \page_backgrounds_add_to_print \pagebox
+ \page_boxes_apply_mirror_print \pagebox
+ \page_boxes_apply_orientate_print \pagebox
+ \page_boxes_apply_shift_print \pagebox % \v!page
+ \page_boxes_apply_offset \pagebox
+ \page_boxes_apply_negate_print \pagebox
+ \fi
+ \box\pagebox
+ \egroup \ifconditional\c_page_boxes_save_page_body \copy\b_page_boxes_saved_page_body \fi}
+
+\appendtoks
+ \restoreglobalbodyfont
+ \pickupattributes
+\to \everybeforepagebody
+
+% if we really need this ... we need to bypass \permanent
+
+% \ifdefined\nestednewbox \else \newbox\nestednextbox \fi % hm, still needed?
+%
+% \prependtoks
+% \let\nextbox\nestednextbox
+% \to \everybeforepagebody
+
+\def\page_boxes_constructed_page#1#2%
+ {\vpack\bgroup % intercept spurious spaces
+ \the\everybeforepagebody
+ \starttextproperties
+ \checkmarginblocks
+ \the\beforeeverypage
+ \normalexpanded{\global\beforepage\emptytoks\the\beforepage}%
+ \inpagebodytrue
+ \pagebodymode\plusone % todo: \plustwo when spread
+ \page_boxes_constructed_page_body#1#2%
+ \normalexpanded{\global\afterpage \emptytoks\the\afterpage }%
+ \the\aftereverypage
+ \resetpagebreak
+ \resetlayouttextlines % will go to \aftereverypage
+ \stoptextproperties
+ \the\everyafterpagebody
+ \egroup}
+
+\permanent\def\doifelsetopofpage
+ {\ifdim\pagegoal=\maxdimen
+ \expandafter\firstoftwoarguments
+ \orelse\ifdim\pagegoal=\vsize
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiftopofpageelse\doifelsetopofpage
+
+% %D Idea:
+%
+% \newinsert\thispageinsert % <- installinsertion
+%
+% \def\flushatthispage
+% {\bgroup
+% \dowithnextbox{\insert\thispageinsert{\box\nextbox}\egroup}%
+% \hbox}
+%
+% \appendtoks
+% \ifvoid\thispageinsert\else\hbox{\smashedbox\thispageinsert}\fi
+% \to \everyshipout
+
+% not yet in i-* file
+
+\installcorenamespace{markedpage}
+
+% todo: just define at the lua end ... less hassle
+
+\permanent\tolerant\protected\def\markpage [#1]#*[#2]{\clf_markpage {#1}{#2}}
+\permanent \def\markedpages [#1]{\clf_markedpages {#1}} % expandable
+\permanent\protected \def\doifelsemarkedpage#1{\clf_doifelsemarkedpage{#1}}
+\permanent\protected \def\startmarkpages [#1]{\clf_startmarkpages {#1}}
+\permanent\protected \def\stopmarkpages {\clf_stopmarkpages }
+
+%D Experimental:
+
+\newconstant\c_page_force_strut_depth_trace_mode
+
+\installtextracker
+ {otr.forcestrutdepth}
+ {\c_page_force_strut_depth_trace_mode\plusone}
+ {\c_page_force_strut_depth_trace_mode\zerocount}
+
+\installoutputroutine\forcestrutdepth
+ {\clf_forcestrutdepth\normalpagebox\strutdp\c_page_force_strut_depth_trace_mode
+ \unvbox\normalpagebox}
+
+\installoutputroutine\forcestrutdepthplus % experimental
+ {\clf_forcestrutdepthplus\normalpagebox\strutdp\c_page_force_strut_depth_trace_mode
+ \unvbox\normalpagebox}
+
+% maybe better:
+%
+% \installoutputroutine\doforcestrutdepth
+% {\clf_forcestrutdepth\normalpagebox\strutdp\c_page_force_strut_depth_trace_mode
+% \unvbox\normalpagebox}
+%
+% \protected\def\forcestrutdepth
+% {\par\ifvmode\ifinner\else\doforcestrutdepth\fi\fi}
+
+% Also experimental:
+
+\permanent\protected\def\doifelsependingpagecontent{\clf_doifelsependingpagecontent}
+% \permanent\protected\let\doifelsependingpagecontent\clf_doifelsependingpagecontent
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-inj.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-inj.mklx
new file mode 100644
index 00000000000..fda757dbe57
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-inj.mklx
@@ -0,0 +1,228 @@
+%D \module
+%D [ file=page-inj,
+%D version=2013.02.10,
+%D title=\CONTEXT\ Page Module,
+%D subtitle=Injections,
+%D author=Wolfgang Schuster \& Hans Hagen,
+%D date=\currentdate,
+%D copyright=Wolfgang Schuster \& Hans Hagen,
+%D license=see context related readme files (gpl)]
+
+%D This module is based on an idea and prototype by Wolfgang but instead of
+%D injectioning into a page constructor we injection it into the regular shipout
+%D handler because that way we get page numbering. It's a sort of variation on
+%D postponing but the content does not end up in the regular page flow, so it's
+%D closer to page figures but without the usual page builder.
+%D
+%D This module is following the mkvi way of rendering setups and configuration and
+%D is somewhat experimental. Probably more alternatives will be added. This could
+%D become a delayed loaded module at some point (no real consequences).
+
+\unprotect
+
+\registerctxluafile{page-inj}{}
+
+\installcorenamespace {pageinjection}
+\installcorenamespace {pageinjectionalternative}
+\installcorenamespace {pageinjectionrenderings}
+
+\installframedcommandhandler \??pageinjection {pageinjection} \??pageinjection
+\installcommandhandler \??pageinjectionalternative {pageinjectionalternative} \??pageinjectionalternative
+
+\let\page_boxes_flush_before\clf_flushpageinjectionsbefore
+\let\page_boxes_flush_after \clf_flushpageinjectionsafter
+
+\def\page_injections_flush_indeed
+ {\scrn_canvas_synchronize_set{\pageinjectionparameter\c!width}{\pageinjectionparameter\c!height}%
+ \invokepagehandler\v!normal{\inheritedpageinjectionframed{\texsetup\p_page_injectionalternative_rederingsetup}}%
+ \scrn_canvas_synchronize_reset}
+
+\def\page_injections_flush_saved#name#parameters%
+ {\begingroup
+ \edef\currentpageinjection{#name}%
+ \getdummyparameters[#parameters]%
+ \edef\currentpageinjectionalternative {\pageinjectionparameter \c!alternative }%
+ \edef\p_page_injectionalternative_rederingsetup{\pageinjectionalternativeparameter\c!renderingsetup}%
+ \page_injections_nextpage
+ \page_injections_flush_indeed
+ \endgroup}
+
+\permanent\tolerant\protected\def\pageinjection[#1]#*[#2]% name parameters | settings parameters | name | parameters
+ {\begingroup
+ \ifhastok={#1}%
+ \ifhastok={#2}%
+ \page_injections_direct_settings_parameters{#1}{#2}%
+ \else
+ \page_injections_direct_parameters{#1}%
+ \fi
+ \else
+ \ifhastok={#2}%
+ \page_injections_direct_name_parameters{#1}{#2}%
+ \else
+ \page_injections_direct_name{#1}%
+ \fi
+ \fi}
+
+\def\page_injections_direct_settings_parameters#settings% #parameters%
+ {\let\currentpageinjection\empty
+ \checkpageinjectionparent
+ \setupcurrentpageinjection[#settings]%
+ \page_injections_direct_indeed}
+
+\def\page_injections_direct_parameters% #parameters%
+ {\let\currentpageinjection\empty
+ \checkpageinjectionparent
+ \page_injections_direct_indeed}
+
+\def\page_injections_direct_name_parameters#name% #parameters%
+ {\edef\currentpageinjection{#name}%
+ \checkpageinjectionparent
+ \page_injections_direct_indeed}
+
+\def\page_injections_direct_name#name%
+ {\edef\currentpageinjection{#name}%
+ \checkpageinjectionparent
+ \page_injections_direct_indeed{}}
+
+\def\page_injections_direct_indeed% #parameters%
+ {\edef\p_page_injections_state{\pageinjectionparameter\c!state}%
+ \edef\p_page_injections_delay{\pageinjectionparameter\c!delay}%
+ \edef\p_page_injections_page {\pageinjectionparameter\c!page }%
+ \ifx\p_page_injections_delay\v!yes
+ \expandafter\page_injections_direct_indeed_yes
+ \else
+ \expandafter\page_injections_direct_indeed_nop
+ \fi}
+
+\def\page_injections_direct_indeed_yes#parameters%
+ {\clf_savepageinjections
+ name {\currentpageinjection}%
+ state {\p_page_injections_state}%
+ userdata {\normalunexpanded{#parameters}}%
+ \relax
+ \endgroup}
+
+\def\page_injections_direct_indeed_nop#parameters%
+ {\ifempty\p_page_injections_page
+ \page
+ \else
+ \page[\p_page_injections_page]%
+ \fi
+ \getdummyparameters[#parameters]%
+ \page_injections_place
+ \endgroup}
+
+\def\page_injections_place
+ {\edef\currentpageinjectionalternative {\pageinjectionparameter \c!alternative }%
+ \edef\p_page_injectionalternative_rederingsetup{\pageinjectionalternativeparameter\c!renderingsetup}%
+ \page_injections_nextpage
+ \ifx\currentpageinjectionalternative\v!none \else % increment counter but don’t generate output
+ \forgetparindent
+ \dontcomplain
+ \setconstant\shipoutfinalizemethod\zerocount % this is messy
+ \page_injections_flush_indeed
+ \fi}
+
+\def\page_injections_nextpage
+ {\edef\p_page_injections_pagestate{\pageinjectionparameter\c!pagestate}%
+ \ifx\p_page_injections_pagestate\v!stop \else
+ \edef\p_page_injections_n{\pageinjectionparameter\c!n}%
+ \ifempty\p_page_injections_n
+ \let\p_page_injections_n\plusone
+ \fi
+ \dorecurse\p_page_injections_n
+ {\incrementcounter[\s!realpage]%
+ \incrementcounter[\s!userpage]}%
+ \fi}
+
+\definepageinjectionalternative[\v!figure][\c!renderingsetup=\??pageinjectionrenderings:\v!figure]
+\definepageinjectionalternative[\v!buffer][\c!renderingsetup=\??pageinjectionrenderings:\v!buffer]
+\definepageinjectionalternative[\v!setups][\c!renderingsetup=\??pageinjectionrenderings:\s!setup ]
+
+\startsetups[\??pageinjectionrenderings:\v!figure]
+ \dontleavehmode\externalfigure[\dummyparameter\c!name][\c!factor=\v!max]
+\stopsetups
+
+\startsetups[\??pageinjectionrenderings:\s!setup]
+ \setups[\dummyparameter\c!setups]% kind of redundant but fits in
+\stopsetups
+
+\startsetups[\??pageinjectionrenderings:\v!buffer]
+ \getbuffer[\dummyparameter\c!name]%
+\stopsetups
+
+% \setuppaper
+% [\c!method=\v!pageinjection]
+
+\setuppageinjection
+ [ \c!width=\paperwidth,
+ \c!height=\paperheight,
+ \c!frame=\v!off,
+ \c!offset=\v!overlay,
+ \c!delay=\v!no,
+ \c!state=\v!next,
+ \c!alternative=\v!figure]
+
+\definepageinjection[\v!previouspage][\c!delay=\v!yes,\c!state=\v!previous]
+\definepageinjection[\v!nextpage] [\c!delay=\v!yes,\c!state=\v!next]
+\definepageinjection[\v!here] % direct
+\definepageinjection[\v!empty] [\c!alternative=\v!none]
+
+%definepageinjection[\v!chapter] [\v!previouspage][\c!pagestate=\v!stop]
+
+\protect \endinput
+
+% \enabletrackers[pagebuilder.injections]
+%
+% \setuppageinjection[offset=2em]
+%
+% \definepageinjection[chapter][previouspage][pagestate=stop]
+% \definepageinjection[later] [delay=yes,state=+2]
+%
+% \useMPlibrary[dum]
+%
+% \starttext
+%
+% \startchapter[title={Insert pages before/after the current page}]
+%
+% \dorecurse{10}{one: \input ward\par}
+%
+% \pageinjection[previouspage][name=dummy-1]
+%
+% \dorecurse{20}{two: \input ward\par} \page
+%
+% \pageinjection[nextpage][name=dummy-2]
+% \pageinjection[later] [name=dummy-4]
+% \pageinjection[delay=yes,state=+2][name=dummy-5]
+%
+% \dorecurse{100}{three: \input ward\par}
+%
+% \stopchapter
+%
+% \startbuffer[whatever]
+% \dorecurse{10}{whatever #1 }
+% \stopbuffer
+%
+% \startchapter[title={Insert pages at the current location}]
+%
+% \dorecurse{10}{\input ward\par}
+%
+% \pageinjection[direct][name=dummy-3]
+% \pageinjection[direct][alternative=buffer,name=whatever]
+%
+% \dorecurse{10}{\input ward\par}
+%
+% \stopchapter
+%
+% \setuphead
+% [chapter]
+% [beforesection={\pageinjection[empty]},
+% insidesection={\pageinjection[chapter][name=\structureuservariable{image}]}]
+%
+% \startchapter[title={Combine both mechanism}][name=dummy-4]
+%
+% \dorecurse{10}{\input ward\par}
+%
+% \stopchapter
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-ins.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-ins.mkxl
new file mode 100644
index 00000000000..2ab7449ab11
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-ins.mkxl
@@ -0,0 +1,209 @@
+%D \module
+%D [ file=page-ins,
+%D version=2002.04.16, % was core-ins
+%D title=\CONTEXT\ Insertion Macros,
+%D subtitle=Insertions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Core Macros / Insertions}
+
+%D Insertions are special data collections that are associated to \TEX's internal
+%D page builder. When multiple footnote classes were introduced, I decided to
+%D isolate some of the functionality in a module.
+
+\registerctxluafile{page-ins}{}
+
+\unprotect
+
+%D Because we need to deal with inserts at the \LUA\ end as well, we provide a
+%D proper installer.
+
+\installcorenamespace{insertion}
+\installcorenamespace{insertionnumber}
+
+\installcommandhandler \??insertion {insertion} \??insertion
+
+\setupinsertion
+ [%c!n=\plusone,
+ %c!distance=\zeropoint,
+ %c!maxheight=\maxdimen,
+ %c!factor=\plusthousand,
+ \c!location=\v!page]
+
+\newconstant\insertionmigrationmode
+
+\def\page_inserts_check_anchoring
+ {\edef\p_anchoring{\namedinsertionparameter\empty\c!anchoring}%
+ \insertionmigrationmode\ifx\p_anchoring\v!auto\plusone\else\zerocount\fi
+ \clf_setinsertmigration{\p_anchoring}}
+
+\appendtoks
+ \ifempty\currentinsertionparent
+ \page_inserts_check_anchoring
+ \fi
+\to \everysetupinsertion
+
+\setupinsertion
+ [\c!anchoring=\v!auto]
+
+\appendtoks
+ \page_inserts_check_anchoring
+\to \everystarttext % or \everyjob
+
+\newcount\currentinsertionnumber % This is a count and not a macro !
+
+\newtoks\t_page_inserts_list
+
+\mutable\let\doprocessinsert\relax % probably kind of obsolete now
+
+%D Maybe some day we will move settings here.
+
+\permanent\protected\def\setcurrentinsertion#1%
+ {\edef\currentinsertion{#1}%
+ \currentinsertionnumber\csname\??insertionnumber\currentinsertion\endcsname}
+
+\permanent\def\namedinsertionnumber#1{\csname\??insertionnumber#1\endcsname}
+
+\protected\def\page_inserts_synchronize_registers
+ {\currentinsertionnumber\csname\??insertionnumber\currentinsertion\endcsname}
+
+% for practical reasons we still set these elsewhere but that might change in the future
+%
+% \global\count\currentinsertionnumber\numexpr\insertionparameter\c!factor/\insertionparameter\c!n\relax
+% \global\skip \currentinsertionnumber\insertionparameter\c!distance \relax
+% \global\dimen\currentinsertionnumber\insertionparameter\c!maxheight\relax}
+% \floatingpenalty\zerocount
+
+\appendtoks
+ \ifempty\currentinsertionparent \else
+ \page_inserts_synchronize_registers
+ \fi
+\to \everysetupinsertion
+
+\protected\def\page_inserts_process#1% beware, this adapts currentinsertion !
+ {\edef\currentinsertion{#1}%
+ \currentinsertionnumber\csname\??insertionnumber\currentinsertion\endcsname
+ \doprocessinsert\currentinsertionnumber} % old method
+
+\permanent\protected\def\processinsertions
+ {\the\t_page_inserts_list}
+
+\permanent\protected\def\synchronizeinsertions
+ {\let\doprocessinsert\page_inserts_synchronize
+ \processinsertions}
+
+\protected\def\page_inserts_synchronize#1% yes or no
+ {\ifvoid#1\else
+ \insert#1{\unvbox#1}%
+ \fi}
+
+\permanent\protected\def\doifelseinsertion#1%
+ {\ifcsname\??insertionnumber#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifinsertionelse\doifelseinsertion
+
+% \protected\def\startinsertion[#1]%
+% {\insert\csname\??insertionnumber#1\endcsname\bgroup}
+%
+% \protected\def\stopinsertion
+% {\egroup}
+
+% For the moment we use the regular insertion allocator so that users can still
+% define their own insertions (not that they will play nicely with all context
+% mechanisms then). We can use the dimensions at the \LUA\ end so we don't need to
+% pass pass them explicitly. Actually, when we see an insertion node at that end,
+% we already know the number.
+
+\appendtoks
+ \ifempty\currentinsertionparent
+ \ifcsname\??insertionnumber\currentinsertion\endcsname
+ % bad news
+ \else
+ \expandafter\newinsert\csname\??insertionnumber\currentinsertion\endcsname
+ \page_inserts_synchronize_registers
+ \clf_defineinsertion
+ {\currentinsertion}%
+ {%
+ number \currentinsertionnumber
+ }%
+ \relax
+ \t_page_inserts_list\expandafter\expandafter\expandafter
+ {\expandafter\the\expandafter\t_page_inserts_list
+ \expandafter\page_inserts_process\csname\??insertionnumber\currentinsertion\endcsname}%
+ \count\currentinsertionnumber\plusthousand
+ \skip \currentinsertionnumber\zeropoint
+ \dimen\currentinsertionnumber\maxdimen
+ \fi
+ \else
+ \letcsname\??insertionnumber\currentinsertion\expandafter\endcsname
+ \csname\??insertionnumber\currentinsertionparent\endcsname
+ \fi
+\to \everydefineinsertion
+
+\appendtoks
+ \ifempty\currentinsertionparent \else
+ \clf_setupinsertion
+ {\currentinsertion}
+ {%
+ location {\insertionparameter\c!location}%
+ }%
+ \relax
+ \fi
+\to \everysetupinsertion
+
+\protected\def\page_inserts_set_location#1#2% fast one
+ {\clf_setinsertionlocation{#1}{#2}}
+
+%D Auxiliary macros:
+
+\def\page_insert_insertion_height#1%
+ {\dimexpr\expandafter\page_insert_insertion_height_indeed\csname\??insertionnumber#1\endcsname\relax}
+
+\def\page_insert_insertion_height_indeed#1%
+ {\ifvoid#1\zeropoint\else1\skip#1+\ht#1\fi}
+
+%D Obsolete:
+
+% \installcorenamespace{insertionbackup}
+%
+% \protected\def\installbackupinsertion#1%
+% {\ifcsname\??insertionbackup\string#1\endcsname \else
+% \expandafter\newinsert\csname\??insertionbackup\string#1\endcsname
+% \count\csname\??insertionbackup\string#1\endcsname\zerocount
+% \skip \csname\??insertionbackup\string#1\endcsname\zeropoint
+% \dimen\csname\??insertionbackup\string#1\endcsname\maxdimen
+% \fi}
+%
+% \protected\def\saveinsertionbox#1% hm, actually unknown
+% {\global\setbox\csname\??insertionbackup\string#1\endcsname
+% \ifdim\ht#1>\zeropoint\box#1\else\emptybox\fi}
+%
+% \protected\def\restoreinsertionbox#1%
+% {\ifvoid\backupinsertion#1\else % if void, we keep the content
+% \global\setbox#1\box\csname\??insertionbackup\string#1\endcsname
+% \fi}
+%
+% \protected\def\eraseinsertionbackup#1%
+% {\global\setbox\csname\??insertionbackup\string#1\endcsname\emptybox}
+%
+% \protected\def\saveinsertiondata#1%
+% {\global\skip \csname\??insertionbackup\string#1\endcsname\skip #1%
+% \global\count\csname\??insertionbackup\string#1\endcsname\count#1%
+% \global\dimen\csname\??insertionbackup\string#1\endcsname\dimen#1}
+%
+% \protected\def\restoreinsertiondata#1%
+% {\global\skip #1\skip \csname\??insertionbackup\string#1\endcsname
+% \global\count#1\count\csname\??insertionbackup\string#1\endcsname
+% \global\dimen#1\dimen\csname\??insertionbackup\string#1\endcsname}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-lay.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-lay.mkxl
new file mode 100644
index 00000000000..21cb48f20de
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-lay.mkxl
@@ -0,0 +1,1630 @@
+%D \module
+%D [ file=page-lay,
+%D version=2000.10.20, % copied from main-001
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Layout Specification,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Layout Specification}
+
+%D Before you start wondering why some of the page related modules skip upward or
+%D left in order to place elements, you must realize that the reference point is the
+%D top left corner of the main typesetting area. One reason for this choice is that
+%D it suited some viewers that displayed page areas. Another reason is that margins,
+%D edges and top and bottom areas are kind of virtual, while the header, text and
+%D footer areas normally determine the text flow.
+
+\unprotect
+
+%D The dimensions related to layout areas are represented by real dimensions.
+
+\newdimen\paperheight \paperheight = 297mm
+\newdimen\paperwidth \paperwidth = 210mm
+\newdimen\paperoffset \paperoffset = \zeropoint
+\newdimen\paperbleed \paperbleed = \zeropoint
+\newdimen\spinewidth \spinewidth = \zeropoint
+
+\newdimen\printpaperheight \printpaperheight = \paperheight
+\newdimen\printpaperwidth \printpaperwidth = \paperwidth
+
+\newdimen\makeupheight % calculated
+\newdimen\makeupwidth % calculated
+
+\newdimen\textheight % calculated
+\newdimen\textwidth % calculated
+
+\newdimen\topspace \topspace = 2cm
+\newdimen\backspace \backspace = \topspace
+\newdimen\cutspace \cutspace = \zeropoint
+\newdimen\bottomspace \bottomspace = \zeropoint
+
+\newdimen\headerheight \headerheight = 2cm
+\newdimen\footerheight \footerheight = \headerheight
+
+\newdimen\topoffset \topoffset = \zeropoint
+\newdimen\backoffset \backoffset = \topoffset
+
+\newdimen\leftmarginwidth \leftmarginwidth = 3cm
+\newdimen\rightmarginwidth \rightmarginwidth = \leftmarginwidth
+
+\newdimen\leftedgewidth \leftedgewidth = 3cm
+\newdimen\rightedgewidth \rightedgewidth = \leftedgewidth
+
+\newdimen\topheight \topheight = \zeropoint
+\newdimen\bottomheight \bottomheight = \topheight
+
+\newcount\layoutlines \layoutlines = \zerocount
+\newcount\layoutcolumns \layoutcolumns = \zerocount
+\newdimen\layoutcolumndistance \layoutcolumndistance = \zeropoint
+\newdimen\layoutcolumnwidth \layoutcolumnwidth = \zeropoint
+
+\newdimen\totaltextwidth
+
+%D The next series of dimensions are complemented by left and rights ones.
+
+\newdimen\margindistance
+\newdimen\edgedistance
+\newdimen\marginwidth
+\newdimen\edgewidth
+
+%D Because a distance does not really makes sense when there is no area, we use a
+%D zero distance in case there is no area.
+
+%D The horizontal distances are:
+
+\newdimen\leftedgedistance
+\newdimen\rightedgedistance
+\newdimen\leftmargindistance
+\newdimen\rightmargindistance
+
+%D The vertical distances are:
+
+\newdimen\topdistance
+\newdimen\headerdistance
+\newdimen\textdistance
+\newdimen\footerdistance
+\newdimen\bottomdistance
+
+\newdimen\textovershoot % available in pagebuilder
+
+%D We need to calculate the extra distances:
+
+\newdimen\naturalmarginwidth
+\newdimen\naturaledgewidth
+\newdimen\naturalmargindistance
+\newdimen\naturaledgedistance
+\newdimen\naturalleftedgedistance
+\newdimen\naturalrightedgedistance
+\newdimen\naturalleftmargindistance
+\newdimen\naturalrightmargindistance
+\newdimen\naturaltopdistance
+\newdimen\naturalheaderdistance
+\newdimen\naturaltextdistance
+\newdimen\naturalfooterdistance
+\newdimen\naturalbottomdistance
+
+%D Now we've come to the setup and definition commands.
+
+\installcorenamespace{layout}
+\installcorenamespace{layoutlocation}
+\installcorenamespace{layoutalternative}
+\installcorenamespace{layoutcolumn}
+\installcorenamespace{layoutmethod}
+
+\installswitchcommandhandler \??layout {layout} \??layout
+
+\appendtoks
+ \doifnothing{\directlayoutparameter\c!state}{\letlayoutparameter\c!state\v!start}%
+\to \everydefinelayout
+
+\newconditional \layouthaschanged \settrue\layouthaschanged % used in mp
+
+\appendtoks
+ \ifcase\layoutsetupmode
+ % can't happen
+ \or % \setuplayout[name][key=value]
+ \ifx\previouslayout\currentlayout
+ \letlayoutparameter\c!state\v!normal % global ? still needed ?
+ \let\currentlayout\currentlayout
+ \page_layouts_synchronize
+ \page_layouts_check_next
+ \fi
+ \or % \setuplayout[key=value]
+ % we can inherit so we always synchronize
+ \letlayoutparameter\c!state\v!normal % global ? still needed ?
+ \let\currentlayout\previouslayout
+ \page_layouts_synchronize
+ \page_layouts_check_next
+ \or % \setuplayout[name]
+ \ifx\currentlayout\v!reset
+ \let\currentlayout\empty
+ \fi
+ \letlayoutparameter\c!state\v!normal % global ? still needed ?
+ \glet\currentlayout\currentlayout % global
+ \page_layouts_synchronize
+ \page_layouts_check_next
+ \or % \setuplayout (reverts to main layout)
+ \letlayoutparameter\c!state\v!normal % global ? still needed ?
+ \glet\currentlayout\empty % global
+ \page_layouts_synchronize
+ \page_layouts_check_next
+ \fi
+ \global\settrue\layouthaschanged
+\to \everysetuplayout
+
+\appendtoks
+ \global\setfalse\layouthaschanged
+\to \everyaftershipout
+
+\permanent\def\doifelselayoutdefined#1%
+ {\ifcsname\namedlayouthash{#1}\c!state\endcsname % maybe a helper
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiflayoutdefinedelse\doifelselayoutdefined
+
+\permanent\def\layoutdistance#1#2{\ifdim\zeropoint<#1#2\else\zeropoint\fi}
+
+% \newconditional\reverselayout
+
+\def\page_layouts_set_dimensions
+ {\global\naturalmarginwidth \layoutparameter\c!margin
+ \global\naturaledgewidth \layoutparameter\c!edge
+ \global\naturalmargindistance \layoutparameter\c!margindistance
+ \global\naturaledgedistance \layoutparameter\c!edgedistance
+ \global\naturalleftedgedistance \layoutparameter\c!leftedgedistance
+ \global\naturalrightedgedistance \layoutparameter\c!rightedgedistance
+ \global\naturalleftmargindistance \layoutparameter\c!leftmargindistance
+ \global\naturalrightmargindistance\layoutparameter\c!rightmargindistance
+ \global\naturaltopdistance \layoutparameter\c!topdistance
+ \global\naturalheaderdistance \layoutparameter\c!headerdistance
+ \global\naturaltextdistance \layoutparameter\c!textdistance
+ \global\naturalfooterdistance \layoutparameter\c!footerdistance
+ \global\naturalbottomdistance \layoutparameter\c!bottomdistance
+ %
+ \global\marginwidth \naturalmarginwidth
+ \global\edgewidth \naturaledgewidth
+ \global\margindistance \naturalmargindistance
+ \global\edgedistance \naturaledgedistance
+ %
+ \global\leftedgedistance \layoutdistance\leftedgewidth \naturalleftedgedistance
+ \global\rightedgedistance \layoutdistance\rightedgewidth \naturalrightedgedistance
+ \global\leftmargindistance \layoutdistance\leftmarginwidth \naturalleftmargindistance
+ \global\rightmargindistance \layoutdistance\rightmarginwidth\naturalrightmargindistance
+ \global\topdistance \layoutdistance\topheight \naturaltopdistance
+ \global\headerdistance \layoutdistance\headerheight \naturalheaderdistance
+ \global\textdistance \naturaltextdistance
+ \global\footerdistance \layoutdistance\footerheight \naturalfooterdistance
+ \global\bottomdistance \layoutdistance\bottomheight \naturalbottomdistance}
+
+\def\page_layouts_set_distances % local in \setreducedvsize
+ {\headerdistance\layoutdistance\headerheight\naturalheaderdistance
+ \footerdistance\layoutdistance\footerheight\naturalfooterdistance}
+
+% these are wrong in the running text, assumes some swapping, needs a cleanup
+
+\newtoks\extralayoutcalculations
+
+\def\page_layouts_calculate_extras
+ {\the\extralayoutcalculations}
+
+\newtoks\everyswapmargins % watch the order !
+
+\appendtoks
+ \enforced\swapcsvalues\leftmargindistance\rightmargindistance
+ \enforced\swapcsvalues\leftedgedistance \rightedgedistance
+ \enforced\swapcsvalues\leftmarginwidth \rightmarginwidth
+ \enforced\swapcsvalues\leftedgewidth \rightedgewidth
+ %
+ \enforced\swapcsvalues\leftmargintotal \rightmargintotal
+ \enforced\swapcsvalues\leftedgetotal \rightedgetotal
+ \enforced\swapcsvalues\leftsidetotal \rightsidetotal
+ \enforced\swapcsvalues\leftcombitotal \rightcombitotal
+ \enforced\swapcsvalues\innermargintotal \outermargintotal
+ \enforced\swapcsvalues\inneredgetotal \outeredgetotal
+ \enforced\swapcsvalues\innercombitotal \outercombitotal
+ \enforced\swapcsvalues\innersidetotal \outersidetotal
+\to \everyswapmargins
+
+%D The papersize macros have a long history and we don't want to change the commands
+%D so they keep looking a bit complex.
+
+%D \macros
+%D {definepapersize}
+%D
+%D Before we start calculating layout dimensions, we will first take care of paper
+%D sizes. The first argument can be either an assignment (for defaults) or an
+%D identifier, in which case the second argument is an assignment.
+%D
+%D \showsetup{definepapersize}
+%D
+%D Yet undocumented, let's see if it gets noticed.
+%D
+%D \starttyping
+%D \definepapersize[main] [A4] [A4]
+%D \definepapersize[extra][A4,landscape][A4,landscape]
+%D
+%D \starttext
+%D \setuppapersize[main]
+%D Page 1. \page
+%D Page 2. \page
+%D \setuppapersize[extra]
+%D Page 2 \page
+%D \setuppapersize[main]
+%D Page 3. \page
+%D Page 4. \page
+%D \adaptpapersize[extra]
+%D Page 5. \page
+%D Page 6. \page
+%D \stoptext
+%D \stoptyping
+%D
+%D \macros
+%D {setuppaper,setuppapersize}
+%D
+%D When setting up the papersize on which to typeset and print, we can also
+%D determine some more characteristics.
+%D
+%D \showsetup{setuppapersize}
+%D
+%D We keep track of these features with the following
+%D variables.
+
+\installcorenamespace{layouttarget}
+\installcorenamespace{layoutpaper}
+\installcorenamespace{layoutprint}
+\installcorenamespace{layoutcurrent}
+
+\installcommandhandler \??layouttarget {layouttarget} \??layouttarget % so this is a sort of mix, not really a user command / maybe switch handler
+
+\newconditional\c_page_target_paper_mirror
+\newconstant \c_page_target_paper_orientation
+\newconstant \c_page_target_paper_reverse
+\newconditional\c_page_target_paper_landscape
+\newconditional\c_page_target_paper_negate
+
+\newconditional\c_page_target_print_mirror
+\newconstant \c_page_target_print_orientation
+\newconstant \c_page_target_print_reverse
+\newconditional\c_page_target_print_landscape
+\newconditional\c_page_target_print_negate
+\newconditional\c_page_target_print_doublesided
+
+\let\v_page_target_left_fill \relax
+\let\v_page_target_right_fill \relax
+\let\v_page_target_top_fill \relax
+\let\v_page_target_bottom_fill\relax
+
+\let\papersize \empty
+\let\printpapersize\empty
+
+\def\v_page_target_top {\namedlayouttargetparameter\papersize\c!top }
+\def\v_page_target_bottom {\namedlayouttargetparameter\papersize\c!bottom}
+\def\v_page_target_left {\namedlayouttargetparameter\papersize\c!left }
+\def\v_page_target_right {\namedlayouttargetparameter\papersize\c!right }
+\def\v_page_target_method {\rootlayouttargetparameter \c!method}
+\def\v_page_target_scale {\rootlayouttargetparameter \c!scale }
+\def\v_page_target_nx {\numexpr\rootlayouttargetparameter \c!nx \relax}
+\def\v_page_target_ny {\numexpr\rootlayouttargetparameter \c!ny \relax}
+\def\v_page_target_dx {\dimexpr\rootlayouttargetparameter \c!dx \relax}
+\def\v_page_target_dy {\dimexpr\rootlayouttargetparameter \c!dy \relax}
+\def\v_page_target_width {\dimexpr\rootlayouttargetparameter \c!width \relax}
+\def\v_page_target_height {\dimexpr\rootlayouttargetparameter \c!height \relax}
+\def\v_page_target_topspace {\dimexpr\rootlayouttargetparameter \c!topspace \relax}
+\def\v_page_target_backspace{\dimexpr\rootlayouttargetparameter \c!backspace\relax}
+\def\v_page_target_offset {\dimexpr\rootlayouttargetparameter \c!offset \relax}
+
+\def\v_page_target_xy {\numexpr\v_page_target_nx*\v_page_target_ny\relax}
+
+%D Normally we will not use this command directly but for now it works out okay. In
+%D the future we might use more of the related commands.
+
+\setuplayouttarget
+ [% these are rather special
+ \c!nx=1,
+ \c!ny=1,
+ \c!dx=\zeropoint,
+ \c!dy=\zeropoint,
+ \c!topspace=\zeropoint,
+ \c!backspace=\zeropoint,
+ \c!width=\zeropoint,
+ \c!height=\zeropoint,
+ %
+ \c!method=\v!normal,
+ \c!option=\v!max,
+ \c!alternative=\v!normal,
+ %
+ \c!scale=1,
+ \c!offset=\zeropoint,
+ %
+ \c!top=,
+ \c!bottom=,
+ \c!left=,
+ \c!right=,
+ %
+ \c!location=]
+
+% \definepapersize[name][A4][A3]
+% \definepapersize[name][settings]
+
+\permanent\tolerant\protected\def\definepapersize[#1]#*[#2]#*[#3]%
+ {\edef\currentlayouttarget{#1}%
+ \ifempty\currentlayouttarget
+ % invalid target
+ \orelse\ifhastok={#2}%
+ \definelayouttarget[#1][#2]%
+ \else
+ \edefcsname\??layoutpaper#1\endcsname{#2}%
+ \ifparameter#3\or
+ \edefcsname\??layoutprint#1\endcsname{#3}%
+ \else
+ \edefcsname\??layoutprint#1\endcsname{#2}%
+ \fi
+ \fi}
+
+\appendtoks
+ \letcsname\??layoutcurrent\currentlayouttarget\endcsname\relax
+\to \everydefinelayouttarget
+
+%def\page_paper_the_paper_size#1{\ifcsname\??layoutpaper#1\endcsname\csname\??layoutpaper#1\endcsname\else#1\fi}
+%def\page_paper_the_print_size#1{\ifcsname\??layoutprint#1\endcsname\csname\??layoutprint#1\endcsname\else#1\fi}
+
+\def\page_paper_the_paper_size#1{\ifcsname\??layoutpaper#1\endcsname\lastnamedcs\else#1\fi}
+\def\page_paper_the_print_size#1{\ifcsname\??layoutprint#1\endcsname\lastnamedcs\else#1\fi}
+
+% \setuppaper [page=A4,paper=A3] % the k/v variant, changes the current page mapping
+% \setuppapersize[A4][a=b,c=d] % the k/v variant, changes nothing, just settings
+% \setuppapersize[A4][A3] % changes the current page mapping
+
+\let\page_paper_reinstate\relax
+\let\page_paper_restore \relax
+
+\permanent\tolerant\protected\def\setuppapersize[#1]#*[#2]%
+ {\ifarguments
+ \page_paper_restore
+ \orelse\ifhastok={#1}%
+ \page_paper_setup_size_settings[#1]%
+ \orelse\ifhastok={#2}%
+ \page_paper_setup_size_settings_by_name[#1][#2]%
+ \else
+ \page_paper_setup_size_change_size[#1][#2]%
+ \fi}
+
+\protected\def\page_paper_setup_size_settings[#1]% sometimes used to set paper/print size
+ {\let\currentlayouttarget\empty
+ \edef\m_layouttarget_paper_saved{\page_paper_the_paper_size{\layouttargetparameter\c!page }}%
+ \edef\m_layouttarget_print_saved{\page_paper_the_print_size{\layouttargetparameter\c!paper}}%
+ \setupcurrentlayouttarget[#1]%
+ \edef\m_layouttarget_paper{\page_paper_the_paper_size{\layouttargetparameter\c!page }}%
+ \edef\m_layouttarget_print{\page_paper_the_print_size{\layouttargetparameter\c!paper}}%
+ \ifx\m_layouttarget_paper_saved\m_layouttarget_paper
+ \ifx\m_layouttarget_print_saved\m_layouttarget_print
+ % we didn't change the size, maybe only sx or sy or so
+ \else
+ \page_paper_setup_size_settings_recalibrate
+ \fi
+ \else
+ \page_paper_setup_size_settings_recalibrate
+ \fi}
+
+\def\page_paper_setup_size_settings_recalibrate
+ {\ifempty\m_layouttarget_paper
+ % forget about it
+ \else
+ \ifempty\m_layouttarget_print
+ \let\m_layouttarget_print\m_layouttarget_paper
+ \fi
+ \page_paper_set_current[\m_layouttarget_paper][\m_layouttarget_print]%
+ \fi
+ \letlayouttargetparameter\c!page \papersize
+ \letlayouttargetparameter\c!paper\printpapersize}
+
+\protected\def\page_paper_setup_size_settings_by_name[#1][#2]%
+ {\def\currentlayouttarget{\page_paper_the_paper_size{#1}}%
+ \setuplayouttarget[#2]}
+
+\protected\def\page_paper_setup_size_change_size[#1][#2]%
+ {\doifelsenothing{#2}
+ {\page_paper_set_current[#1][#1]}
+ {\page_paper_set_current[#1][#2]}}
+
+\permanent\let\setuppaper\page_paper_setup_size_settings
+
+\permanent\protected\def\adaptpapersize
+ {\glet\page_paper_reinstate\page_paper_restore
+ \setuppapersize}
+
+\appendtoks
+ \page_paper_reinstate
+ \glet\page_paper_reinstate\relax
+\to \everyaftershipout
+
+\protected\def\page_paper_set_restore#1#2%
+ {\xdef\page_paper_restore{\page_paper_set_current_indeed[#1][#2]}}
+
+\protected\def\page_paper_set_current[#1][#2]%
+ {\normalexpanded{\page_paper_set_current_indeed
+ [\page_paper_the_paper_size{#1}]%
+ [\page_paper_the_print_size{#2}]}}
+
+\protected\def\page_paper_reset_paper
+ {\global\setfalse\c_page_target_paper_landscape
+ \global\setfalse\c_page_target_paper_mirror
+ \global\setfalse\c_page_target_paper_negate
+ \global\c_page_target_paper_orientation\uprotationangle
+ \global\c_page_target_paper_reverse \uprotationangle}
+
+\protected\def\page_paper_reset_print
+ {\global\setfalse\c_page_target_print_landscape
+ \global\setfalse\c_page_target_print_mirror
+ \global\setfalse\c_page_target_print_negate
+ \global\c_page_target_print_orientation\uprotationangle
+ \global\c_page_target_print_reverse \uprotationangle}
+
+\letvalue{\??layoutpaper\v!reset}\page_paper_reset_paper
+\letvalue{\??layoutprint\v!reset}\page_paper_reset_print
+
+\setvalue{\??layoutpaper\v!landscape }{\global\settrue\c_page_target_paper_landscape}
+\setvalue{\??layoutpaper\v!mirrored }{\global\settrue\c_page_target_paper_mirror}
+\setvalue{\??layoutpaper\v!negative }{\global\settrue\c_page_target_paper_negate}
+\setvalue{\??layoutpaper\v!rotated }{\global\c_page_target_paper_orientation\rightrotationangle
+ \global\c_page_target_paper_reverse \leftrotationangle}
+\setvalue{\??layoutpaper\number\rightrotationangle}{\global\c_page_target_paper_orientation\rightrotationangle
+ \global\c_page_target_paper_reverse \leftrotationangle}
+\setvalue{\??layoutpaper\number\downrotationangle }{\global\c_page_target_paper_orientation\downrotationangle
+ \global\c_page_target_paper_reverse \zerocount}
+\setvalue{\??layoutpaper\number\leftrotationangle }{\global\c_page_target_paper_orientation\leftrotationangle
+ \global\c_page_target_paper_reverse \rightrotationangle}
+
+\setvalue{\??layoutprint\v!landscape }{\global\settrue\c_page_target_print_landscape}
+\setvalue{\??layoutprint\v!mirrored }{\global\settrue\c_page_target_print_mirror}
+\setvalue{\??layoutprint\v!negative }{\global\settrue\c_page_target_print_negate}
+\setvalue{\??layoutprint\v!rotated }{\global\c_page_target_print_orientation\rightrotationangle
+ \global\c_page_target_print_reverse \leftrotationangle}
+\setvalue{\??layoutprint\number\rightrotationangle}{\global\c_page_target_print_orientation\rightrotationangle
+ \global\c_page_target_print_reverse \leftrotationangle}
+\setvalue{\??layoutprint\number\downrotationangle }{\global\c_page_target_print_orientation\downrotationangle
+ \global\c_page_target_print_reverse \zerocount}
+\setvalue{\??layoutprint\number\leftrotationangle }{\global\c_page_target_print_orientation\leftrotationangle
+ \global\c_page_target_print_reverse \rightrotationangle}
+
+%def\page_paper_handle_page_option #1{\ifcsname\??layoutpaper#1\endcsname\csname\??layoutpaper#1\endcsname\fi}
+%def\page_paper_handle_print_option#1{\ifcsname\??layoutprint#1\endcsname\csname\??layoutprint#1\endcsname\fi}
+
+\def\page_paper_handle_page_option #1{\begincsname\??layoutpaper#1\endcsname}
+\def\page_paper_handle_print_option#1{\begincsname\??layoutprint#1\endcsname}
+
+\protected\def\page_paper_identify_target#1%
+ {\ifcsname\??layoutcurrent#1\endcsname
+ \edef\currentlayouttarget{#1}%
+ \fi}
+
+\newdimen\d_page_minimum_paper_size \d_page_minimum_paper_size\luaexpr{math.pi}\onebasepoint
+
+\protected\def\page_paper_set_current_indeed[#1][#2]%
+ {\edef\m_page_asked_paper{#1}% can be the restores
+ \edef\m_page_asked_print{#2}%
+ %
+ \page_paper_set_restore\m_page_asked_paper\m_page_asked_print
+ %
+ % locate page target
+ \let\currentlayouttarget\empty
+ \page_paper_reset_paper
+ \processcommacommand[\m_page_asked_paper]\page_paper_identify_target
+ \ifempty\currentlayouttarget
+ \let\currentlayouttarget\currentpage
+ \fi
+ \glet\papersize\currentlayouttarget
+ \page_paper_reset_paper
+ \processcommacommand[\m_page_asked_paper]\page_paper_handle_page_option
+ \global\paperwidth \layouttargetparameter\c!width \relax
+ \global\paperheight\layouttargetparameter\c!height\relax
+ \ifdim\paperwidth<\d_page_minimum_paper_size
+ \global\paperwidth\d_page_minimum_paper_size
+ \fi
+ \ifdim\paperheight<\d_page_minimum_paper_size
+ \global\paperheight\d_page_minimum_paper_size
+ \fi
+ \ifconditional\c_page_target_paper_landscape
+ \enforced\global\swapcsvalues\paperwidth\paperheight
+ \fi
+ \ifinpagebody % local freeze
+ \normalexpanded{\setlayouttargetparameter\c!height{\the\paperheight}}%
+ \normalexpanded{\setlayouttargetparameter\c!width {\the\paperwidth }}%
+ \fi
+ %
+ \page_paper_set_offsets
+ % locate paper target
+ \page_paper_reset_print
+ \processcommacommand[\m_page_asked_print]\page_paper_identify_target
+ \glet\printpapersize\currentlayouttarget
+ \page_paper_reset_print
+ \processcommacommand[\m_page_asked_print]\page_paper_handle_print_option
+ \global\printpaperwidth \layouttargetparameter\c!width \relax
+ \global\printpaperheight\layouttargetparameter\c!height\relax
+ \ifdim\printpaperwidth<\onepoint
+ \global\printpaperwidth\paperwidth
+ \fi
+ \ifdim\printpaperheight<\onepoint
+ \global\printpaperheight\paperheight
+ \fi
+ \ifconditional\c_page_target_print_landscape
+ \enforced\global\swapcsvalues\printpaperwidth\printpaperheight
+ \fi
+ % this check can be confusing, so we've added the possibility
+ % to bypass this test: \setuppapersize[option=fit]
+ \edef\m_page_asked_option{\rootlayouttargetparameter\c!option}%
+ \ifx\m_page_asked_option\v!max % \v!fit is
+ \begingroup
+ % we need to pre-swap else we get the wrong paper size
+ \ifnum\c_page_target_paper_orientation=\rightrotationangle
+ \enforced\swapcsvalues\paperwidth\paperheight
+ \orelse\ifnum\c_page_target_paper_orientation=\leftrotationangle
+ \enforced\swapcsvalues\paperwidth\paperheight
+ \fi
+ \ifnum\c_page_target_print_orientation=\rightrotationangle
+ \enforced\swapcsvalues\printpaperwidth\printpaperheight
+ \orelse\ifnum\c_page_target_print_orientation=\leftrotationangle
+ \enforced\swapcsvalues\printpaperwidth\printpaperheight
+ \fi
+ \ifdim\paperheight>\printpaperheight
+ \global\printpaperheight\paperheight
+ \writestatus\m!system{print height forced to paper height}%
+ \fi
+ \ifdim\paperwidth>\printpaperwidth
+ \global\printpaperwidth\paperwidth
+ \writestatus\m!system{print width forced to paper width}%
+ \fi
+ \endgroup
+ \fi
+ %\writestatus{layout target}{(\the\paperwidth,\the\paperheight) -> (\the\printpaperwidth,\the\printpaperheight)}%
+ \page_layouts_synchronize
+ % new but we assume \setuplayout
+ \scrn_canvas_synchronize_only}
+
+\ifdefined\scrn_canvas_synchronize_only \else
+ \let\scrn_canvas_synchronize_only\relax
+\fi
+
+\ifdefined\page_paper_set_offsets \else
+
+ \def\page_paper_set_offsets % will move
+ {\global\paperoffset\v_page_target_offset
+ \global\advance\paperwidth -2\paperoffset
+ \global\advance\paperheight-2\paperoffset}
+
+\fi
+
+\ifdefined\synchronizegridsnapping \else
+ \let\synchronizegridsnapping\relax
+\fi
+
+\let\p_page_layouts_width \empty
+\let\p_page_layouts_height\empty
+
+\def\page_layouts_synchronize
+ {\setups[\layoutparameter\c!preset]\relax
+ \global\leftmarginwidth \layoutparameter\c!leftmargin
+ \global\rightmarginwidth\layoutparameter\c!rightmargin
+ \global\leftedgewidth \layoutparameter\c!leftedge
+ \global\rightedgewidth \layoutparameter\c!rightedge
+ \global\headerheight \layoutparameter\c!header
+ \global\footerheight \layoutparameter\c!footer
+ \global\bottomheight \layoutparameter\c!bottom
+ \global\topheight \layoutparameter\c!top
+ \global\backspace \layoutparameter\c!backspace
+ \global\topspace \layoutparameter\c!topspace
+ \page_layouts_set_dimensions
+ \synchronizegridsnapping
+ \usesetupsparameter\layoutparameter % depends on gridsnapping !
+ \synchronizewhitespace
+ \synchronizeblank
+ \setupinterlinespace[\v!reset]% \synchronizegloballinespecs
+ \global\cutspace\layoutparameter\c!cutspace\relax
+ \edef\p_page_layouts_width{\layoutparameter\c!width}%
+ \ifx\p_page_layouts_width\v!middle
+ \ifzeropt\cutspace
+ \global\cutspace\backspace
+ \fi
+ \global\makeupwidth\dimexpr\paperwidth-\backspace-\cutspace\relax
+ \orelse\ifx\p_page_layouts_width\v!fit
+ \ifzeropt\cutspace
+ \global\cutspace\backspace
+ \fi
+ \global\makeupwidth\dimexpr\paperwidth-\cutspace\relax
+ \scratchdimen\dimexpr\backspace
+ -\leftedgewidth -\leftedgedistance
+ -\leftmarginwidth-\leftmargindistance\relax
+ \ifdim\scratchdimen<\zeropoint
+ \scratchdimen\zeropoint
+ \fi
+ \global\advance\makeupwidth\dimexpr
+ -\rightmargindistance-\rightmarginwidth
+ -\rightedgedistance -\rightedgewidth
+ -\scratchdimen\relax
+ \else
+ \global\makeupwidth\p_page_layouts_width\relax
+ \ifzeropt\cutspace
+ \global\cutspace\dimexpr\paperwidth-\makeupwidth-\backspace\relax
+ % \else
+ % A kind of inconsistent specification, but used
+ % in for instance s-pre-19.tex; the cutspace is
+ % used only for determining some kind of right
+ % margin; don't use this in doublesided mode
+ \fi
+ \fi
+ \scratchdimen\layoutparameter\c!bottomspace\relax
+ %\ifzeropt\scratchdimen
+ % \scratchdimen\topspace
+ %\fi
+ \global\bottomspace\layoutparameter\c!bottomspace\relax
+ \global\layoutlines0\number\layoutparameter\c!lines\relax % may be empty
+ \ifcase\layoutlines
+ \edef\p_page_layouts_height{\layoutparameter\c!height}%
+ \ifx\p_page_layouts_height\v!middle
+ \ifzeropt\bottomspace
+ \global\bottomspace\topspace
+ \fi
+ \global\makeupheight\dimexpr\paperheight-\topspace-\bottomspace\relax
+ \orelse\ifx\p_page_layouts_height\v!fit
+ \ifzeropt\bottomspace
+ \global\bottomspace\topspace
+ \fi
+ \global\makeupheight\dimexpr\paperheight-\bottomspace\relax
+ \scratchdimen\dimexpr\topspace-\topheight-\topdistance\relax
+ \ifdim\scratchdimen<\zeropoint
+ \scratchdimen\zeropoint
+ \fi
+ \global\advance\makeupheight\dimexpr-\bottomdistance-\bottomheight-\scratchdimen\relax
+ \else
+ \global\makeupheight\layoutparameter\c!height\relax
+ \ifzeropt\bottomspace
+ \global\bottomspace\dimexpr\paperheight-\makeupheight-\topspace\relax
+ % \else
+ % inconsistent specification
+ \fi
+ \fi
+ \else
+ % beware, when the bodyfont changes (switched) this will change as well; implementing
+ % a global lineheight is tricky: should we take the bodyfont interlinespace or the one set
+ % independent of the bodyfont (before or after a layout spec); way too fuzzy, so we
+ % stick to the current method (after a night of experimenting ...2003/10/13)
+ \global\makeupheight\dimexpr
+ \layoutparameter\c!lines\lineheight-\strutheight+\topskip+
+ \headerdistance+\headerheight+\footerdistance+\footerheight\relax
+ \fi
+ \backoffset\layoutparameter\c!horoffset\relax
+ \topoffset \layoutparameter\c!veroffset\relax
+ \ifdim\makeupwidth<\onepoint
+ \global\makeupwidth\onepoint
+ \fi
+ \ifdim\makeupheight<\onepoint
+ \global\makeupheight\onepoint
+ \fi
+ % handy in page builder
+ \global\totaltextwidth\dimexpr
+ \leftedgetotal
+ +\leftmargintotal
+ +\makeupwidth
+ +\rightmargintotal
+ +\rightedgetotal
+ \relax
+ % \page_layouts_check_next % here ?
+ \page_layouts_check_direction
+ \page_layouts_calculate_extras
+ \page_target_check_centering
+ \calculatehsizes
+ \calculatevsizes
+ \page_layouts_check_pseudo_columns
+ \page_backgrounds_recalculate}
+
+\def\page_layouts_check_direction
+ {\edef\p_direction{\layoutparameter\c!direction}%
+ \ifx\p_direction\v!reverse
+ \enforced\global\swapcsvalues\naturalleftedgedistance \naturalrightedgedistance
+ \enforced\global\swapcsvalues\naturalleftmargindistance\naturalrightmargindistance
+ \enforced\global\swapcsvalues\leftedgedistance \rightedgedistance
+ \enforced\global\swapcsvalues\leftmargindistance \rightmargindistance
+ \enforced\global\swapcsvalues\leftmarginwidth \rightmarginwidth
+ \enforced\global\swapcsvalues\leftedgewidth \rightedgewidth
+ \enforced\global\swapcsvalues\backspace \cutspace
+ \expandafter\setsystemmode
+ \else
+ \expandafter\resetsystemmode
+ \fi{reverselayout}}
+
+\def\page_layouts_check_pseudo_columns
+ {\global\layoutcolumns\layoutparameter\c!columns
+ \global\layoutcolumndistance\layoutparameter\c!columndistance
+ \global\layoutcolumnwidth\dimexpr\makeupwidth-\layoutcolumns\layoutcolumndistance+\layoutcolumndistance\relax
+ \ifnum\layoutcolumns>\plusone
+ \global\divide\layoutcolumnwidth\layoutcolumns
+ \dorecurse\layoutcolumns\page_layouts_check_pseudo_column
+ \fi}
+
+\def\page_layouts_check_pseudo_column
+ {\xdefcsname\??layoutcolumn\recurselevel\endcsname
+ {\the\numexpr\recurselevel-\plusone\relax\dimexpr\layoutcolumnwidth+\layoutcolumndistance\relax}}
+
+\letvalue{\??layoutcolumn0}\zeropoint
+
+\permanent\def\layoutcolumnoffset#1% can travel around so we can't use \lastnamedcs
+ {\csname\??layoutcolumn\ifcsname\??layoutcolumn#1\endcsname#1\else0\fi\endcsname}
+
+\def\page_layouts_synchronize_at_start
+ {\ifdim\makeupheight=\layoutlines\lineheight \else % weird check
+ \page_layouts_synchronize
+ \fi}
+
+\appendtoks
+ \page_layouts_synchronize_at_start % still needed?
+\to \everystarttext
+
+% document:
+%
+% \setuplayout[odd][state=stop] \setuplayout[even][state=stop] \setuplayout[page]
+%
+% \startstandardmakeup[page=blank] ... \stopstandardmakeup
+
+\ifdefined\lastpage \else \let\lastpage\!!plusone \fi
+
+\permanent\protected\def\page_layouts_change#1%
+ {%\writestatus\m!layouts{changing to layout #1}%
+ \xdef\currentlayout{#1}%
+ \page_layouts_synchronize}
+
+\aliased\let\changetolayout\page_layouts_change % also public
+
+\def\v_real_page_normal {\the\realpageno}
+\def\v_real_page_reverse{-\the\numexpr\lastpage-\realpageno\relax}
+
+\def\v_real_page_odd_or_even
+ {\ifodd\pagenoshift
+ \ifodd\realpageno\v!even\else\v!odd \fi
+ \else
+ \ifodd\realpageno\v!odd \else\v!even\fi
+ \fi}
+
+\let\v_real_page_current\v!current
+
+\def\v_real_page_named
+ {\ifnum\lastpage=\realpageno
+ \v!last
+ \orelse\ifnum\plusone=\realpageno
+ \v!first
+ \else
+ \__unknown__
+ \fi}
+
+\let\v_page_layouts_pre_check\relax
+
+\def\page_layouts_check_default_indeed#1%
+ {\edef\m_page_check{#1}
+ \edef\m_page_state{\namedlayoutparameter\m_page_check\c!state}%
+ \ifx\m_page_state\v!start
+ \glet\v_page_layouts_pre_check\currentlayout
+ \glet\currentlayout\m_page_check
+ \page_layouts_synchronize
+ \orelse\ifx\m_page_state\v!repeat
+ \glet\v_page_layouts_pre_check\relax
+ \glet\currentlayout\m_page_check
+ \page_layouts_synchronize
+ \fi}
+
+\def\page_layouts_check_revert
+ {\glet\currentlayout\v_page_layouts_pre_check
+ \glet\v_page_layouts_pre_check\relax
+ \page_layouts_synchronize}
+
+\def\page_layouts_check_default
+ {\ifcsname\namedlayouthash\v_real_page_normal\c!state\endcsname
+ \page_layouts_check_default_indeed\v_real_page_normal
+ \orelse\ifcsname\namedlayouthash\v_real_page_reverse\c!state\endcsname
+ \page_layouts_check_default_indeed\v_real_page_reverse
+ \orelse\ifcsname\namedlayouthash\v_real_page_named\c!state\endcsname
+ \page_layouts_check_default_indeed\v_real_page_named
+ \orelse\ifcsname\namedlayouthash\v_real_page_current\c!state\endcsname
+ \page_layouts_check_default_indeed\v_real_page_current
+ \orelse\ifcsname\namedlayouthash\v_real_page_odd_or_even\c!state\endcsname
+ \page_layouts_check_default_indeed\v_real_page_odd_or_even
+ \orelse\ifrelax\v_page_layouts_pre_check
+ % okay
+ \else
+ \page_layouts_check_revert
+ \fi}
+
+\permanent\protected\def\installlayoutmethod#1#2%
+ {\gdefcsname\??layoutmethod#1\endcsname{#2}}
+
+\installlayoutmethod\v!default{\page_layouts_check_default}
+\installlayoutmethod\v!normal {\page_layouts_check_default}
+
+% \def\page_layouts_check_next
+% {\csname\??layoutmethod\ifcsname\??layoutmethod\layoutparameter\c!method\endcsname
+% \layoutparameter\c!method
+% \else
+% \v!normal
+% \fi\endcsname}
+
+\permanent\protected\def\page_layouts_check_next
+ {\ifcsname\??layoutmethod\layoutparameter\c!method\endcsname
+ \lastnamedcs
+ \else
+ \page_layouts_check_default
+ \fi}
+
+\aliased\let\checkcurrentlayout\page_layouts_check_next % public and used in naw, so this synonym will stay
+
+% inheritance
+%
+% \definelayout
+% [test]
+% [width=12cm,
+% height=10cm]
+%
+% \definelayout
+% [more]
+% [test]
+% [height=12cm]
+%
+% testcase
+%
+% \setuppagenumbering[alternative=doublesided]
+%
+% \setuplayout [width=11cm]
+% \definelayout [odd] [backspace=1cm]
+% \definelayout [even] [backspace=4cm]
+% \definelayout [5] [backspace=5cm]
+% \definelayout [6] [backspace=5cm]
+% \definelayout [-2] [backspace=0cm,cutspace=0cm]
+% \definelayout [last] [backspace=0cm,cutspace=0cm]
+%
+% \checkcurrentlayout \showframe
+%
+% \starttext
+% \dorecurse{20} {\input knuth \endgraf \input tufte \endgraf}
+% \stoptext
+
+% Because we want to keep the set parameters as they are we use some
+% helpers to communicate between the several page building related
+% mechanism.
+
+\appendtoks \page_layouts_check_next \to \everystarttext
+\appendtoks \page_layouts_check_next \to \everyaftershipout
+
+\newconditional\c_page_layouts_location_is_set
+\newconditional\c_page_layouts_location_is_middle
+
+\def\page_layouts_location_reset % we start in the left top and mirror right pages
+ {\setfalse\c_page_layouts_location_is_set
+ \setfalse\c_page_layouts_location_is_middle
+ \let\v_page_target_left_fill \relax
+ \let\v_page_target_right_fill \hss % ? \relax
+ \let\v_page_target_top_fill \relax
+ \let\v_page_target_bottom_fill\vss} % \relax}
+
+\setvalue{\??layoutlocation\v!right }{\settrue\c_page_layouts_location_is_set
+ \setfalse\c_page_layouts_location_is_middle
+ \let\v_page_target_left_fill \hss
+ \let\v_page_target_right_fill \relax}
+\setvalue{\??layoutlocation\v!left }{\settrue\c_page_layouts_location_is_set
+ \setfalse\c_page_layouts_location_is_middle
+ \let\v_page_target_left_fill \relax
+ \let\v_page_target_right_fill \hss}
+\setvalue{\??layoutlocation\v!bottom }{\settrue\c_page_layouts_location_is_set
+ \setfalse\c_page_layouts_location_is_middle
+ \let\v_page_target_top_fill \vss
+ \let\v_page_target_bottom_fill\relax}
+\setvalue{\??layoutlocation\v!top }{\settrue\c_page_layouts_location_is_set
+ \setfalse\c_page_layouts_location_is_middle
+ \let\v_page_target_top_fill \relax
+ \let\v_page_target_bottom_fill\vss}
+\setvalue{\??layoutlocation\v!middle }{\settrue\c_page_layouts_location_is_set
+ \settrue\c_page_layouts_location_is_middle
+ \let\v_page_target_left_fill \hss
+ \let\v_page_target_right_fill \hss
+ \let\v_page_target_top_fill \vss
+ \let\v_page_target_bottom_fill\vss}
+\setvalue{\??layoutlocation\empty }{\setfalse\c_page_layouts_location_is_set % default also signal to scrn_
+ \setfalse\c_page_layouts_location_is_middle
+ \let\v_page_target_right_fill \hss
+ \let\v_page_target_bottom_fill\hss}
+\setvalue{\??layoutlocation\v!doublesided}{\settrue \c_page_target_print_doublesided}
+\setvalue{\??layoutlocation\v!singlesided}{\setfalse\c_page_target_print_doublesided}
+
+\def\page_target_check_centering_indeed#1%
+ % {\ifcsname\??layoutlocation#1\endcsname\csname\??layoutlocation#1\endcsname\fi}
+ {\begincsname\??layoutlocation#1\endcsname}
+
+\protected\def\page_target_check_centering
+ {\setfalse\c_page_target_print_doublesided
+ \page_layouts_location_reset
+ \processcommacommand[\layoutparameter\c!location]\page_target_check_centering_indeed}
+
+% installers
+
+\permanent\protected\def\installlayoutalternative#1#2%
+ {\gdefcsname\??layoutalternative#1\endcsname{#2}}
+
+\def\page_boxes_construct_content_default#1#2#3% targetbox flusher box
+ {\setbox#1\vpack % was \vbox
+ {\offinterlineskip
+ \begingroup % needed ?
+ \uselayoutstyleandcolor\c!style\c!color
+ \offinterlineskip
+ \page_layouts_insert_elements % zero size
+ \endgroup
+ \page_insert_body#2#3}}% including footnotes
+
+\installlayoutalternative\v!default{\page_boxes_construct_content_default}
+\installlayoutalternative\v!normal {\page_boxes_construct_content_default}
+
+% \def\page_boxes_construct_content % targetbox flusher box
+% {\csname\??layoutalternative\ifcsname\??layoutalternative\layoutparameter\c!alternative\endcsname
+% \layoutparameter\c!alternative
+% \else
+% \v!normal
+% \fi\endcsname}
+
+\def\page_boxes_construct_content % targetbox flusher box
+ {\ifcsname\??layoutalternative\layoutparameter\c!alternative\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\page_boxes_construct_content_default
+ \fi}
+
+%D \macros
+%D {adaptlayout}
+
+\installcorenamespace{adaptlayout}
+\installcorenamespace{pageadaptations}
+
+\installsetuponlycommandhandler \??adaptlayout {adaptlayout}
+
+\newdimen\d_page_adepts_pushed_text_height
+\newdimen\d_page_adepts_pushed_footer_height
+\newdimen\d_page_adepts_height
+\newdimen\d_page_adapts_delta
+
+\permanent\protected\tolerant\def\adaptlayout[#1]#*[#2]%
+ {\ifparameter#2\or
+ \processcommalist[#1]{\page_adapts_layout_register{#2}}%
+ \page_adapts_check
+ \else
+ \page_adapts_layout_indeed{#1}%
+ \fi}
+
+\def\page_adapts_layout_register#1#2%
+ {\gdefcsname\??pageadaptations#2\endcsname{\page_adapts_layout_indeed{#1}}}
+
+\let\p_adapts_height\zeropoint
+\let\p_adapts_lines \zerocount
+
+\def\page_adapts_layout_indeed#1%
+ {\setupcurrentadaptlayout[\c!height=\zeropoint,\c!lines=\zerocount,#1]%
+ \page_adepts_push
+ \edef\p_adapts_height{\adaptlayoutparameter\c!height}%
+ \edef\p_adapts_lines {\adaptlayoutparameter\c!lines}%
+ \ifx\p_adapts_height\v!max
+ \global\d_page_adepts_height\footerheight
+ \else
+ \global\d_page_adepts_height\dimexpr
+ \ifnum\p_adapts_lines=\zerocount
+ \p_adapts_height
+ \else
+ \p_adapts_lines\openlineheight
+ \fi
+ \relax
+ \ifdim\d_page_adepts_height>\footerheight
+ \global\d_page_adepts_height\footerheight
+ \fi
+ \fi
+ \global\advance\textheight \d_page_adepts_height
+ \global\advance\footerheight-\d_page_adepts_height
+ \showmessage\m!layouts1{\the\d_page_adepts_height,\the\realpageno}%
+ % this will become a better one (do we need insert correction?)
+ \page_otr_command_set_vsize
+ %
+ \page_backgrounds_recalculate
+ \glet\page_adepts_push\relax
+ \glet\page_adepts_pop\page_adepts_pop_indeed}
+
+% \def\page_adapts_check
+% {\csname\??pageadaptations\the\ifcsname\??pageadaptations\the\realpageno\endcsname\realpageno\else\zerocount\fi\endcsname}
+%
+% \def\page_adapts_reset
+% {\ifcsname\??pageadaptations\the\realpageno\endcsname
+% \global\undefinevalue{\??pageadaptations\the\realpageno}%
+% \fi}
+
+\def\page_adapts_check
+ {\begincsname\??pageadaptations\the\realpageno\endcsname}
+
+\def\page_adapts_reset
+ {\ifcsname\??pageadaptations\the\realpageno\endcsname
+ \gletcsname\??pageadaptations\the\realpageno\endcsname\relax
+ \fi}
+
+\def\page_adepts_push_indeed
+ {\global\d_page_adepts_pushed_text_height \textheight
+ \global\d_page_adepts_pushed_footer_height\footerheight}
+
+\def\page_adepts_pop_indeed
+ {\global\textheight \d_page_adepts_pushed_text_height
+ \global\footerheight\d_page_adepts_pushed_footer_height
+ \page_layouts_synchronize
+ \glet\page_adepts_push\page_adepts_push_indeed
+ \glet\page_adepts_pop\relax}
+
+\appendtoks \page_adapts_check \to \everystarttext
+\appendtoks \page_adapts_reset \to \everyshipout
+
+\let\page_adepts_pop \relax
+\let\page_adepts_push\page_adepts_push_indeed
+
+\protected\def\page_adapts_synchronize % used elsewhere
+ {\page_adepts_pop
+ \page_adapts_check}
+
+\def\page_adapts_status_info % used elsewhere
+ {\ifrelax\page_adepts_pop \else
+ \space(adept: \the\d_page_adepts_height)\space
+ \fi
+ \the\realpageno}
+
+%D \macros
+%D {startlayout,stoplayout}
+
+\permanent\protected\def\startlayout[#1]%
+ {\page
+ \globalpushmacro\currentlayout
+ \doifelselayoutdefined{#1}{\setuplayout[#1]}\donothing} % {\setuplayout[\currentlayout]}}
+
+\permanent\protected\def\stoplayout
+ {\page
+ \globalpopmacro\currentlayout
+ \setuplayout[\currentlayout]}
+
+% NOG EENS NAGAAN WANNEER NU GLOBAL EN WANNEER NIET
+
+\ifdefined\doifelselayoutsomeline \else % eventually defined in page-txt
+ \let\doifelselayoutsomeline\secondofthreearguments
+\fi
+
+\permanent\def\compensatevsizeheader {\advance\textheight-\dimexpr\headerheight+\headerdistance\relax}
+\permanent\def\compensatevsizefooter {\advance\textheight-\dimexpr\footerheight+\footerdistance\relax}
+\permanent\def\globalcompensatevsizeheader{\global\advance\textheight-\dimexpr\headerheight+\headerdistance\relax}
+\permanent\def\globalcompensatevsizefooter{\global\advance\textheight-\dimexpr\footerheight+\footerdistance\relax}
+
+\permanent\def\compensatevsizeheaderzero{\headerheight\zeropoint\page_layouts_set_distances}
+\permanent\def\compensatevsizefooterzero{\footerheight\zeropoint\page_layouts_set_distances}
+
+\def\page_layouts_set_modes
+ {\ifzeropt\headerheight
+ \resetsystemmode\v!header
+ \else
+ \setsystemmode\v!header
+ \fi
+ \ifzeropt\footerheight
+ \resetsystemmode\v!footer
+ \else
+ \setsystemmode\v!footer
+ \fi}
+
+\permanent\protected\def\calculatevsizes % name will change
+ {\textheight\makeupheight
+ \doifelselayoutsomeline\v!header\compensatevsizeheader\donothing
+ \doifelselayoutsomeline\v!footer\compensatevsizefooter\donothing
+ \page_layouts_set_modes
+ \resetglobal
+ \page_otr_command_set_vsize}
+
+\permanent\protected\def\calculateglobalvsizes % name will change
+ {\global\textheight\makeupheight
+ \doifelselayoutsomeline\v!header\globalcompensatevsizeheader\donothing
+ \doifelselayoutsomeline\v!footer\globalcompensatevsizefooter\donothing
+ \page_layouts_set_modes
+ \page_otr_command_set_vsize}
+
+\permanent\protected\def\calculatereducedvsizes % name will change
+ {\textheight\makeupheight
+ \doifelselayoutsomeline\v!header\compensatevsizeheader\compensatevsizeheaderzero
+ \doifelselayoutsomeline\v!footer\compensatevsizefooter\compensatevsizefooterzero}
+
+\newdimen\innermakeupwidth % special purpose
+\newdimen\innermakeupmargin % special purpose
+
+\newconditional\innermakeupcompensation \settrue\innermakeupcompensation
+
+\permanent\def\compensatedinnermakeupmargin
+ {\dimexpr\ifconditional\innermakeupcompensation+\innermakeupmargin\else\zeropoint\fi\relax}
+
+\permanent\protected\def\freezetextwidth % name will change % \makeupwidth may be set to \textwidth
+ {\textwidth\makeupwidth % which is a tricky but valid value
+ \edef\m_currentlayout_text_width {\layoutparameter\c!textwidth }%
+ \edef\m_currentlayout_text_margin{\layoutparameter\c!textmargin}%
+ \ifempty\m_currentlayout_text_width \else
+ \textwidth\m_currentlayout_text_width % local
+ \fi
+ \global\innermakeupwidth\textwidth
+ \ifempty\m_currentlayout_text_margin
+ \global\innermakeupmargin\zeropoint
+ \else
+ \global\innermakeupmargin\m_currentlayout_text_margin\relax
+ \fi
+ \scratchdimen\dimexpr\innermakeupmargin+\innermakeupmargin\relax
+ \global\advance\innermakeupwidth-\scratchdimen
+ \advance\textwidth-\scratchdimen} % local
+
+\permanent\protected\def\calculatehsizes % name will change
+ {\freezetextwidth
+ \page_otr_command_set_hsize}
+
+%D When we start at an even page, we need to swap the layout differently. We cannot
+%D adapt the real page number, since it is used in cross referencing. The next
+%D switch is set when we start at an even page.
+
+% #single #left #right
+
+\permanent\def\doifelseoddpage
+ {\ifodd\pagenoshift
+ \expandafter\page_layouts_if_odd_else_yes
+ \else
+ \expandafter\page_layouts_if_odd_else_nop
+ \fi}
+
+\aliased\let\doifoddpageelse\doifelseoddpage
+
+\def\page_layouts_if_odd_else_yes
+ {\ifodd\realpageno
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\def\page_layouts_if_odd_else_nop
+ {\ifodd\realpageno
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\def\page_layouts_if_odd_else_again#1{\doifelseoddpage}
+
+\permanent\def\doifbothsidesoverruled
+ {\ifdoublesided
+ \expandafter\page_layouts_if_odd_else_again
+ \else
+ \expandafter\firstofthreearguments
+ \fi}
+
+\permanent\def\doifbothsides% #1 #2 #3
+ {\ifdoublesided
+ \expandafter\page_layouts_if_both_sides
+ \else
+ \expandafter\firstofthreearguments
+ \fi}
+
+\def\page_layouts_if_both_sides
+ {\ifsinglesided
+ \expandafter\firstofthreearguments
+ \else
+ \expandafter\page_layouts_if_odd_else_again
+ \fi}
+
+\newdimen\texthoffset
+
+\permanent\protected\def\settexthoffset % name will change
+ {\texthoffset\doifbothsides\backspace\backspace{\dimexpr\paperwidth-\backspace-\makeupwidth\relax}}
+
+\permanent\protected\def\goleftonpage % name will change (we could cache)
+ {\hkern-\dimexpr\leftmargindistance+\leftmarginwidth+\leftedgedistance+\leftedgewidth\relax}
+
+\permanent\def\doifelsemarginswap#1#2% protected?
+ {\doifbothsides{#1}{#1}{#2}}
+
+\aliased\let\doifmarginswapelse\doifelsemarginswap
+
+\protected\def\page_layouts_swap_margins % name will change, frozen?
+ {\doifelsemarginswap\relax\page_layouts_swap_margins_indeed}
+
+\protected\def\page_layouts_swap_margins_indeed
+ {\let\page_layouts_swap_margins \relax % to prevent local swapping
+ \let\page_layouts_swap_margins_indeed\relax % to prevent local swapping
+ \the\everyswapmargins}
+
+\permanent\def\rightorleftpageaction
+ {\ifdoublesided
+ \expandafter\page_layouts_right_or_left_page_action
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\def\page_layouts_right_or_left_page_action
+ {\ifsinglesided
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\doifelseoddpage
+ \fi}
+
+% swapping needs frozen
+
+\permanent\def\outermarginwidth {\rightorleftpageaction\rightmarginwidth \leftmarginwidth }
+\permanent\def\innermarginwidth {\rightorleftpageaction\leftmarginwidth \rightmarginwidth}
+\permanent\def\outermargindistance{\rightorleftpageaction\rightmargindistance\leftmargindistance }
+\permanent\def\innermargindistance{\rightorleftpageaction\leftmargindistance \rightmargindistance}
+
+\permanent\def\outeredgewidth {\rightorleftpageaction\rightedgewidth \leftedgewidth }
+\permanent\def\inneredgewidth {\rightorleftpageaction\leftedgewidth \rightedgewidth}
+\permanent\def\outeredgedistance {\rightorleftpageaction\rightedgedistance\leftedgedistance }
+\permanent\def\inneredgedistance {\rightorleftpageaction\leftedgedistance \rightedgedistance}
+
+\permanent\def\outerspacewidth {\rightorleftpageaction\cutspace \backspace}
+\permanent\def\innerspacewidth {\rightorleftpageaction\backspace\cutspace }
+
+\permanent\def\leftmargintotal {\dimexpr\leftmarginwidth +\leftmargindistance \relax}
+\permanent\def\rightmargintotal {\dimexpr\rightmarginwidth+\rightmargindistance\relax}
+\permanent\def\leftedgetotal {\dimexpr\leftedgewidth +\leftedgedistance \relax}
+\permanent\def\rightedgetotal {\dimexpr\rightedgewidth +\rightedgedistance \relax}
+
+\permanent\def\leftsidetotal {\dimexpr\leftmarginwidth +\leftedgetotal \relax}
+\permanent\def\rightsidetotal {\dimexpr\rightmarginwidth+\rightedgetotal\relax}
+\permanent\def\leftcombitotal {\dimexpr\leftmargintotal +\leftedgetotal \relax}
+\permanent\def\rightcombitotal {\dimexpr\rightmargintotal+\rightedgetotal\relax}
+
+\permanent\def\innermargintotal {\dimexpr\innermarginwidth+\innermargindistance\relax}
+\permanent\def\outermargintotal {\dimexpr\outermarginwidth+\outermargindistance\relax}
+\permanent\def\inneredgetotal {\dimexpr\inneredgewidth +\inneredgedistance \relax}
+\permanent\def\outeredgetotal {\dimexpr\outeredgewidth +\outeredgedistance \relax}
+
+\permanent\def\innercombitotal {\dimexpr\innermargintotal+\inneredgetotal\relax}
+\permanent\def\outercombitotal {\dimexpr\outermargintotal+\outeredgetotal\relax}
+\permanent\def\innersidetotal {\dimexpr\innermarginwidth+\inneredgetotal\relax}
+\permanent\def\outersidetotal {\dimexpr\outermarginwidth+\outeredgetotal\relax}
+
+%D \macros
+%D {startlocallayout}
+%D
+%D These macros should be used with care. They permit local layouts (as used in
+%D fitting pages, see \type {page-app.tex}). This is kind of obsolete now that we
+%D have \type {\definelayout}, so this hack will disappear in future versions.
+
+\permanent\protected\def\startlocallayout
+ {\globalpushmacro\page_paper_restore
+ \globalpushmacro\currentlayout}
+
+\permanent\protected\def\stoplocallayout
+ {\globalpopmacro\currentlayout
+ \globalpopmacro\page_paper_restore
+ \page_paper_restore
+ \setuplayout[\currentlayout]\relax} % explicit !
+
+%D \macros
+%D {showprint, showframe, showlayout, showsetups}
+%D
+%D We predefine a couple of tracing macros.
+%D
+%D \showsetup{showprint}
+%D \showsetup{showframe}
+%D \showsetup{showlayout}
+%D \showsetup{showsetups}
+%D %showsetup{showmargins}
+%D %showsetup{showgrid}
+%D %showsetup{showusage}
+
+\fetchmodulecommand \showprint \f!page_run
+\fetchmodulecommand \showframe \f!page_run
+\fetchmodulecommand \showlayout \f!page_run
+\fetchmodulecommand \showsetups \f!page_run
+\fetchmodulecommand \showmargins \f!page_run
+\fetchmodulecommand \showgrid \f!page_run
+
+\glet\page_grids_add_to_box\gobbleoneargument
+\glet\page_grids_add_to_one\gobbleoneargument
+\glet\page_grids_add_to_mix\gobbleoneargument
+
+%D The default dimensions are quite old and will not change. The funny fractions
+%D were introduced when we went from fixed dimensions to relative ones. Since
+%D \CONTEXT\ is a dutch package, the dimensions are based on the metric system. The
+%D asymmetrical layout is kind of handy for short quick||and||dirty stapled
+%D documents.
+%D
+%D Although valid, it is not a real good idea to use dimensions based on the \type
+%D {em} unit. First of all, since there are no fonts loaded yet, this dimension
+%D makes no sense, and second, you would loose track of values, since they could
+%D change while going to a new page, depending on the current font setting.
+
+\setuplayout
+ [ \c!topspace=.08417508418\paperheight, % 2.5cm
+ \c!top=\zeropoint,
+ \c!topdistance=\zeropoint,
+ \c!header=.06734006734\paperheight, % 2.0cm
+ \c!headerdistance=\zeropoint,
+ \c!height=.84175084175\paperheight, % 25.0cm
+ \c!footerdistance=\layoutparameter\c!headerdistance,
+ \c!footer=.06734006734\paperheight, % 2.0cm
+ \c!bottomdistance=\layoutparameter\c!topdistance,
+ \c!bottom=\zeropoint,
+ \c!backspace=.11904761905\paperwidth, % 2.5cm
+ \c!edge=\zeropoint,
+ \c!edgedistance=\layoutparameter\c!margindistance,
+ \c!margin=.12649983170\paperwidth, % snijwit-2*afstand
+ \c!margindistance=.02008341748\paperwidth, % 12.0pt
+ \c!leftedge=\layoutparameter\c!edge,
+ \c!leftedgedistance=\layoutparameter\c!edgedistance,
+ \c!leftmargin=\layoutparameter\c!margin,
+ \c!leftmargindistance=\layoutparameter\c!margindistance,
+ \c!width=.71428571429\paperwidth, % 15.0cm \dimexpr\
+ \c!rightmargindistance=\layoutparameter\c!margindistance,
+ \c!rightmargin=\layoutparameter\c!margin,
+ \c!rightedgedistance=\layoutparameter\c!edgedistance,
+ \c!rightedge=\layoutparameter\c!edge,
+ \c!veroffset=\zeropoint,
+ \c!bottomspace=\zeropoint,
+ \c!horoffset=\zeropoint,
+ \c!cutspace=\zeropoint,
+ \c!textwidth=, % dangerous option -> centered / local
+ \c!textmargin=, % dangerous option -> both sides
+ \c!textdistance=\zeropoint, % shift down on grid
+ \c!style=,
+ \c!color=,
+ \c!marking=\v!off,
+ \c!location=,% \v!singlesided, but unset is signal (no other default, spoils arrange)
+ \c!state=\v!start,
+ \c!scale=1,
+ \c!sx=1,
+ \c!sy=1,
+ \c!nx=1,
+ \c!ny=1,
+ \c!dx=\zeropoint,
+ \c!dy=\zeropoint,
+ \c!grid=\v!no,
+ \c!direction=\v!normal,
+ \c!preset=,
+ \c!setups=\systemsetupsprefix\s!default,
+ \c!clipoffset=\zeropoint,
+ \c!lines=0,
+ \c!paper=, % for foxet
+ \c!page=, % for foxet
+ \c!columns=\plusone,
+ \c!columndistance=\zeropoint]
+
+%D First we define a whole range of (DIN) papersizes, of which the A-series makes
+%D most sense. We enable checking.
+
+\definepapersize [A0] [\c!width=841mm,\c!height=1189mm]
+\definepapersize [A1] [\c!width=594mm,\c!height=841mm]
+\definepapersize [A2] [\c!width=420mm,\c!height=594mm]
+\definepapersize [A3] [\c!width=297mm,\c!height=420mm]
+\definepapersize [A4] [\c!width=210mm,\c!height=297mm]
+\definepapersize [A5] [\c!width=148mm,\c!height=210mm]
+\definepapersize [A6] [\c!width=105mm,\c!height=148mm]
+\definepapersize [A7] [\c!width=74mm,\c!height=105mm]
+\definepapersize [A8] [\c!width=52mm,\c!height=74mm]
+\definepapersize [A9] [\c!width=37mm,\c!height=52mm]
+\definepapersize [A10] [\c!width=26mm,\c!height=37mm]
+
+\definepapersize [A4/2][\c!width=\dimexpr297mm/2\relax,\c!height=210mm] % 148.5mm
+%definepapersize [2A5] [\c!width=296mm,\c!height=210mm] % doublewide
+
+\definepapersize [B0] [\c!width=1000mm,\c!height=1414mm]
+\definepapersize [B1] [\c!width=707mm,\c!height=1000mm]
+\definepapersize [B2] [\c!width=500mm,\c!height=707mm]
+\definepapersize [B3] [\c!width=353mm,\c!height=500mm] % [\c!width=354mm,\c!height=500mm]
+\definepapersize [B4] [\c!width=250mm,\c!height=353mm] % [\c!width=250mm,\c!height=354mm]
+\definepapersize [B5] [\c!width=176mm,\c!height=250mm] % [\c!width=177mm,\c!height=250mm]
+\definepapersize [B6] [\c!width=125mm,\c!height=176mm] % [\c!width=125mm,\c!height=177mm]
+\definepapersize [B7] [\c!width=88mm,\c!height=125mm]
+\definepapersize [B8] [\c!width=62mm,\c!height=88mm] % [\c!width=63mm,\c!height=88mm]
+\definepapersize [B9] [\c!width=44mm,\c!height=62mm] % [\c!width=44mm,\c!height=63mm]
+\definepapersize [B10] [\c!width=31mm,\c!height=44mm]
+
+\definepapersize [C0] [\c!width=917mm,\c!height=1297mm]
+\definepapersize [C1] [\c!width=648mm,\c!height=917mm] % [\c!width=649mm,\c!height=917mm]
+\definepapersize [C2] [\c!width=458mm,\c!height=648mm] % [\c!width=459mm,\c!height=649mm]
+\definepapersize [C3] [\c!width=324mm,\c!height=458mm] % [\c!width=324mm,\c!height=459mm]
+\definepapersize [C4] [\c!width=229mm,\c!height=324mm]
+\definepapersize [C5] [\c!width=162mm,\c!height=229mm]
+\definepapersize [C6] [\c!width=114mm,\c!height=162mm] % [\c!width=115mm,\c!height=162mm]
+\definepapersize [C7] [\c!width=81mm,\c!height=114mm] % [\c!width=81mm,\c!height=115mm]
+\definepapersize [C8] [\c!width=57mm,\c!height=81mm]
+\definepapersize [C9] [\c!width=40mm,\c!height=57mm]
+\definepapersize [C10] [\c!width=28mm,\c!height=40mm]
+
+%D Per August 2004 the rounding of some (seldom used) sizes were corrected top the
+%D latest DIN specifications. Peter Rolf came up with these and a few more missing
+%D sizes. Watch out: spaces and slashes!
+
+\definepapersize [4 A0] [\c!width=1682mm,\c!height=2378mm]
+\definepapersize [2 A0] [\c!width=1189mm,\c!height=1682mm]
+\definepapersize [C6/C5] [\c!width=114mm,\c!height=229mm]
+
+%D Because there are no standardized screen sizes, we define a bunch of sizes with
+%D $4:3$ ratios. The \type {S6} size is nearly as wide as a sheet of \type {A4}
+%D paper.
+
+\definepapersize [S3] [\c!width=300pt,\c!height=225pt]
+\definepapersize [S4] [\c!width=400pt,\c!height=300pt]
+\definepapersize [S5] [\c!width=500pt,\c!height=375pt]
+\definepapersize [S6] [\c!width=600pt,\c!height=450pt]
+\definepapersize [S8] [\c!width=800pt,\c!height=600pt]
+\definepapersize [SL] [\c!width=640pt,\c!height=400pt] % low
+\definepapersize [SM] [\c!width=720pt,\c!height=450pt] % medium
+\definepapersize [SW] [\c!width=800pt,\c!height=450pt] % wide
+\definepapersize [HD] [\c!width=1920pt,\c!height=1080pt]
+\definepapersize [HD+] [\c!width=1920pt,\c!height=1200pt]
+\definepapersize [HD-] [\c!width=960pt,\c!height=540pt]
+
+%D These are handy too:
+
+\definepapersize [S33] [\c!width=300pt,\c!height=300pt]
+\definepapersize [S44] [\c!width=400pt,\c!height=400pt]
+\definepapersize [S55] [\c!width=500pt,\c!height=500pt]
+\definepapersize [S66] [\c!width=600pt,\c!height=600pt]
+
+%D One may wonder if \TEX\ should be used for typesetting \CDROM\ covers, but it
+%D does not hurt to have the paper size ready.
+
+\definepapersize [CD] [\c!width=120mm,\c!height=120mm]
+
+%D The next series is for our English speaking friends who decided to stick to non
+%D metric values. Thanks to Nelson Beebe for completing the inch based list.
+
+\definepapersize [letter] [\c!width=8.5in,\c!height=11in]
+\definepapersize [ledger] [\c!width=11in,\c!height=17in]
+\definepapersize [tabloid] [\c!width=17in,\c!height=11in]
+
+\definepapersize [legal] [\c!width=8.5in,\c!height=14in]
+\definepapersize [folio] [\c!width=8.5in,\c!height=13in]
+\definepapersize [executive] [\c!width=7.25in,\c!height=10.5in]
+
+\definepapersize [A] [\c!width=8.5in,\c!height=11in] % 1 sheet
+\definepapersize [B] [\c!width=11in,\c!height=17in] % 2 sheets
+\definepapersize [C] [\c!width=17in,\c!height=22in] % 4 sheets
+
+%D The next set is for Tobias Burnus, who gave me the sizes.
+
+\definepapersize [envelope 9] [\c!width=8.88in,\c!height=3.88in]
+\definepapersize [envelope 10] [\c!width=9.5in,\c!height=4.13in]
+\definepapersize [envelope 11] [\c!width=10.38in,\c!height=4.5in]
+\definepapersize [envelope 12] [\c!width=11.0in,\c!height=4.75in]
+\definepapersize [envelope 14] [\c!width=11.5in,\c!height=5.0in]
+\definepapersize [monarch] [\c!width=7.5in,\c!height=3.88in]
+\definepapersize [check] [\c!width=8.58in,\c!height=3.88in]
+\definepapersize [DL] [\c!width=110mm,\c!height=220mm] % [\c!width=220mm,\c!height=110mm]
+\definepapersize [E4] [\c!width=280mm,\c!height=400mm]
+
+%D The next three sets are supplied by Taco:
+
+\definepapersize [RA0] [\c!width=860mm,\c!height=1220mm]
+\definepapersize [RA1] [\c!width=610mm,\c!height=860mm]
+\definepapersize [RA2] [\c!width=430mm,\c!height=610mm]
+\definepapersize [RA3] [\c!width=305mm,\c!height=430mm]
+\definepapersize [RA4] [\c!width=215mm,\c!height=305mm]
+
+%D ISO SRA (supplementary raw A) sizes:
+
+\definepapersize [SRA0] [\c!width=900mm,\c!height=1280mm]
+\definepapersize [SRA1] [\c!width=640mm,\c!height=900mm]
+\definepapersize [SRA2] [\c!width=450mm,\c!height=640mm]
+\definepapersize [SRA3] [\c!width=320mm,\c!height=450mm]
+\definepapersize [SRA4] [\c!width=225mm,\c!height=320mm]
+
+%D Swedish thesis formats:
+
+\definepapersize [G5] [\c!width=169mm,\c!height=239mm]
+\definepapersize [E5] [\c!width=155mm,\c!height=220mm]
+
+%D Industry invention:
+
+\definepapersize [A3plus] [\c!width=329mm,\c!height=483mm]
+
+%D For Alan:
+
+\definepapersize [business] [\c!width=85mm,\c!height=55mm]
+\definepapersize [businessUS] [\c!width=3.5in,\c!height=2in]
+
+%D We can now default to a reasonable size. We match the print paper size with the
+%D typeset paper size. This setting should come after the first layout specification
+%D (already done).
+
+% \definepapersize
+% [\v!default]
+% [ \c!width=\paperwidth,
+% \c!height=\paperheight]
+
+% \definepapersize
+% [samesized]
+% [ \c!width=\paperwidth,
+% \c!height=\paperheight]
+
+\setuppapersize
+ [\c!distance=1.5cm] % offset is already taken
+
+\definepapersize
+ [oversized]
+ [ \c!width=\dimexpr\paperwidth +\layouttargetparameter\c!distance\relax,
+ \c!height=\dimexpr\paperheight+\layouttargetparameter\c!distance\relax]
+
+\definepapersize
+ [undersized]
+ [ \c!width=\dimexpr\paperwidth -\layouttargetparameter\c!distance\relax,
+ \c!height=\dimexpr\paperheight-\layouttargetparameter\c!distance\relax]
+
+\definepapersize
+ [doublesized]
+ [ \c!width=\dimexpr \paperwidth \relax,
+ \c!height=\dimexpr2\paperheight\relax]
+
+\definepapersize
+ [doubleoversized]
+ [ \c!width=\dimexpr \paperwidth +\layouttargetparameter\c!distance\relax,
+ \c!height=\dimexpr2\paperheight+\layouttargetparameter\c!distance\relax]
+
+\definepapersize
+ [doublewide]
+ [ \c!width=\dimexpr2\paperwidth \relax,
+ \c!height=\dimexpr \paperheight\relax]
+
+% \setuppapersize
+% [A4][A4]
+
+\setuppapersize
+ [A4] % [samesized]
+
+%D A few goodies, first a full page layout:
+
+\definelayout
+ [\v!page]
+
+\setuplayout
+ [\v!page]
+ [\c!backspace=\zeropoint,
+ \c!cutspace=\zeropoint,
+ \c!topspace=\zeropoint,
+ \c!bottomspace=\zeropoint,
+ \c!margin=\zeropoint,
+ \c!edge=\zeropoint,
+ \c!header=\zeropoint,
+ \c!footer=\zeropoint,
+ \c!top=\zeropoint,
+ \c!bottom=\zeropoint,
+ \c!leftmargin=\zeropoint,
+ \c!rightmargin=\zeropoint,
+ \c!leftedge=\zeropoint,
+ \c!rightedge=\zeropoint,
+ \c!textdistance=\zeropoint,
+ \c!width=\v!middle,
+ \c!height=\v!middle,
+ \c!lines=0,
+ \c!grid=\v!no]
+
+%D A quick and dirty one:
+
+\definelayout
+ [\v!middle]
+ [\c!width=\v!middle,
+ \c!height=\v!middle]
+
+%D One for testing examples (5mm is often the non printable area):
+
+\definelayout
+ [\v!tight]
+
+\definelayout
+ [\v!tight]
+ [\v!page]
+ [\c!backspace=5mm,
+ \c!cutspace=5mm,
+ \c!topspace=5mm,
+ \c!bottomspace=5mm]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-lin.lmt b/Master/texmf-dist/tex/context/base/mkxl/page-lin.lmt
new file mode 100644
index 00000000000..f50031ab300
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-lin.lmt
@@ -0,0 +1,418 @@
+if not modules then modules = { } end modules ['page-lin'] = {
+ version = 1.001,
+ comment = "companion to page-lin.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, tonumber = next, tonumber
+
+local trace_numbers = false trackers.register("lines.numbers", function(v) trace_numbers = v end)
+
+local report_lines = logs.reporter("lines")
+
+local attributes = attributes
+local nodes = nodes
+local context = context
+
+local implement = interfaces.implement
+
+nodes.lines = nodes.lines or { }
+local lines = nodes.lines
+
+lines.data = lines.data or { } -- start step tag
+local data = lines.data
+local last = #data
+
+storage.register("lines/data", data, "nodes.lines.data")
+
+local variables = interfaces.variables
+
+local v_next = variables.next
+local v_page = variables.page
+local v_no = variables.no
+
+local properties = nodes.properties.data
+
+local nodecodes = nodes.nodecodes
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local whatsit_code = nodecodes.whatsit
+local glyph_code = nodecodes.glyph
+
+local linelist_code = nodes.listcodes.line
+
+local latelua_code = nodes.whatsitcodes.latelua
+
+local localtexrun = tex.runlocal
+
+----- a_displaymath = attributes.private('displaymath')
+local a_linenumber = attributes.private('linenumber')
+local a_linereference = attributes.private('linereference')
+
+local cross_references = { }
+
+local nuts = nodes.nuts
+
+local getid = nuts.getid
+local getattr = nuts.getattr
+local getlist = nuts.getlist
+local getbox = nuts.getbox
+local gettotal = nuts.gettotal
+
+local takebox = nuts.takebox
+
+local nexthlist = nuts.traversers.hlist
+local nextvlist = nuts.traversers.vlist
+local nextcontent = nuts.traversers.content
+local nextlist = nuts.traversers.list
+local nextnode = nuts.traversers.node
+local nextwhatsit = nuts.traversers.whatsit
+
+local is_display_math = nuts.is_display_math
+
+local ctx_convertnumber = context.convertnumber
+local ctx_makelinenumber = context.makelinenumber
+
+local paragraphs = typesetters.paragraphs
+local addtoline = paragraphs.addtoline
+local checkline = paragraphs.checkline
+
+-- cross referencing
+
+function lines.number(n)
+ n = tonumber(n)
+ local cr = cross_references[n] or 0
+ cross_references[n] = nil
+ return cr
+end
+
+function lines.finalize(t)
+ local getnumber = lines.number
+ for _,p in next, t do
+ for _,r in next, p do
+ local m = r.metadata
+ if m and m.kind == "line" then
+ local e = r.entries
+ local u = r.userdata
+ e.linenumber = getnumber(e.text or 0) -- we can nil e.text
+ e.conversion = u and u.conversion
+ r.userdata = nil -- hack
+ end
+ end
+ end
+end
+
+local filters = structures.references.filters
+local helpers = structures.helpers
+
+structures.references.registerfinalizer(lines.finalize)
+
+filters.line = filters.line or { }
+
+function filters.line.default(data)
+ ctx_convertnumber(data.entries.conversion or "numbers",data.entries.linenumber or "0")
+end
+
+function filters.line.page(data,prefixspec,pagespec) -- redundant
+ helpers.prefixpage(data,prefixspec,pagespec)
+end
+
+function filters.line.linenumber(data) -- raw
+ context(data.entries.linenumber or "0")
+end
+
+-- boxed variant, todo: use number mechanism
+
+local boxed = lines.boxed or { }
+lines.boxed = boxed
+
+-- todo: cache setups, and free id no longer used
+-- use interfaces.cachesetup(t)
+
+function boxed.register(configuration)
+ last = last + 1
+ data[last] = configuration
+ if trace_numbers then
+ report_lines("registering setup %a",last)
+ end
+ return last
+end
+
+implement {
+ name = "registerlinenumbering",
+ actions = { boxed.register, context },
+ arguments = {
+ {
+ { "continue" },
+ { "start", "integer" },
+ { "step", "integer" },
+ { "method" },
+ { "tag" },
+ }
+ }
+}
+
+function boxed.setup(n,configuration)
+ local d = data[n]
+ if d then
+ if trace_numbers then
+ report_lines("updating setup %a",n)
+ end
+ for k,v in next, configuration do
+ d[k] = v
+ end
+ else
+ if trace_numbers then
+ report_lines("registering setup %a (br)",n)
+ end
+ data[n] = configuration
+ end
+ return n
+end
+
+implement {
+ name = "setuplinenumbering",
+ actions = boxed.setup,
+ arguments = {
+ "integer",
+ {
+ { "continue" },
+ { "start", "integer" },
+ { "step", "integer" },
+ { "method" },
+ { "tag" },
+ }
+ }
+}
+
+local function resolve(n,m) -- we can now check the 'line' flag (todo)
+ for current, id, subtype, content in nextlist, n do
+ if content then
+ if id == hlist_code then
+ for current, subtype in nextwhatsit, content do
+ if subtype == latelua_code then -- this has to change!
+ local a = getattr(current,a_linereference)
+ if a then
+ cross_references[a] = m
+ end
+ end
+ end
+ end
+ resolve(content,m)
+ end
+ end
+end
+
+local function check_number(n,b,a,skip)
+ local d = data[a]
+ if d then
+ local tag = d.tag or ""
+ local s = d.start or 1
+ local okay = not skip and s % d.step == 0
+ if trace_numbers then
+ report_lines("%s number for setup %a, tag %a, line %s, continued %a",okay and "making" or "skipping",a,tag,s,d.continue or v_no)
+ end
+ if okay then
+ local p = checkline(n)
+ if p then
+ localtexrun(function() ctx_makelinenumber(tag,s,p.hsize,p.reverse and 1 or 0) end)
+ local l = takebox(b)
+ if l then
+ addtoline(n,l)
+ else
+ -- very unlikely
+ end
+ end
+ end
+ resolve(n,s)
+ d.start = s + 1
+ end
+end
+
+-- print(nodes.idstostring(list))
+
+-- hlists of type line will only have an attribute when the line number attribute
+-- still set at par building time which is not always the case unless we explicitly
+-- do a par before we end the line
+
+local function lineisnumbered(n) -- content node
+ for n, id, subtype, content in nextcontent, getlist(n) do
+ local a = getattr(n,a_linenumber)
+ if a and a > 0 then
+ return a
+ end
+ end
+end
+
+local function listisnumbered(list)
+ if list then
+ for n, subtype in nexthlist, list do
+ if subtype == linelist_code then
+ local a = getattr(n,a_linenumber)
+ if a then
+ -- a quick test for lines (only valid when \par before \stoplinenumbering)
+ return a > 0 and list or false
+ else
+ -- a bit slower one, assuming that we have normalized and anchored
+ if lineisnumbered(n) then
+ return list
+ end
+ end
+ end
+ end
+ end
+end
+
+local function findnumberedlist(list)
+ -- we assume wrapped boxes, only one with numbers
+ for n, id, subtype, content in nextcontent, list do
+ if id == hlist_code then
+ if subtype == linelist_code then
+ local a = getattr(n,a_linenumber)
+ if a then
+ return a > 0 and list
+ end
+ return
+ else
+ if lineisnumbered(content) then
+ return n
+ end
+ local okay = findnumberedlist(content)
+ if okay then
+ return okay
+ end
+ end
+ elseif id == vlist_code then
+ if listisnumbered(content) then
+ return content
+ end
+ local okay = findnumberedlist(content)
+ if okay then
+ return okay
+ end
+ elseif id == glyph_code then
+ return
+ end
+ end
+end
+
+-- reset ranges per page
+-- store first and last per page
+-- maybe just set marks directly
+
+local function findcolumngap(list)
+ -- we assume wrapped boxes, only one with numbers
+ for n, id, subtype, content in nextlist, list do
+ if id == hlist_code or id == vlist_code then
+ local p = properties[n]
+ if p and p.columngap then
+ if trace_numbers then
+ report_lines("first column gap %a",p.columngap)
+ end
+ return n
+ elseif content then
+ local okay = findcolumngap(content)
+ if okay then
+ return okay
+ end
+ end
+ end
+ end
+end
+
+function boxed.addlinenumbers(n,b,nested)
+ local box = getbox(n)
+ if not box then
+ return
+ end
+ local list = getlist(box)
+ if not list then
+ return
+ end
+ local last_a = nil
+ local last_v = -1
+ local skip = false
+
+ local function check()
+ for n, subtype in nexthlist, list do
+ if subtype ~= linelist_code then
+ -- go on
+ elseif gettotal(n) == 0 then
+ -- skip funny hlists -- todo: check line subtype
+ else
+ local a = lineisnumbered(n)
+ if a then
+ if last_a ~= a then
+ local da = data[a]
+ local ma = da.method
+ if ma == v_next then
+ skip = true
+ elseif ma == v_page then
+ da.start = 1 -- eventually we will have a normal counter
+ end
+ last_a = a
+ if trace_numbers then
+ report_lines("starting line number range %s: start %s, continue %s",a,da.start,da.continue or v_no)
+ end
+ end
+ -- if getattr(n,a_displaymath) then
+ -- check_number(n,b,a,skip)
+ -- else
+ check_number(n,b,a,skip)
+ -- end
+ skip = false
+ end
+ end
+ end
+ end
+
+ if nested == 0 then
+ if list then
+ check()
+ end
+ elseif nested == 1 then
+ local id = getid(box)
+ if id == vlist_code then
+ if listisnumbered(list) then
+ -- ok
+ else
+ list = findnumberedlist(list)
+ end
+ else -- hlist
+ list = findnumberedlist(list)
+ end
+ if list then
+ check()
+ end
+ elseif nested == 2 then
+ list = findcolumngap(list)
+ -- we assume we have a vlist
+ if not list then
+ return
+ end
+ for n in nextvlist, list do
+ local p = properties[n]
+ if p and p.columngap then
+ if trace_numbers then
+ report_lines("found column gap %a",p.columngap)
+ end
+ list = getlist(n)
+ if list then
+ check()
+ end
+ end
+ end
+ else
+ -- bad call
+ end
+end
+
+-- column attribute
+
+implement {
+ name = "addlinenumbers",
+ actions = boxed.addlinenumbers,
+ arguments = { "integer", "integer", "integer" }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-lin.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-lin.mklx
new file mode 100644
index 00000000000..3b3a3d71b73
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-lin.mklx
@@ -0,0 +1,604 @@
+%D \module
+%D [ file=page-lin,
+%D version=2007.11.29,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Line Numbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% generic or not ... maybe not bother too much and simplify to mkiv only get rid of
+% \mk* (left over from experimental times)
+%
+% to be redone (was experiment) .. can be hooked into margin code reshuffle
+% arguments
+%
+% this is early luatex code, 2007 or even before that, and it shows ... we have
+% better tricks now
+
+\writestatus{loading}{ConTeXt Core Macros / Line Numbering}
+
+\unprotect
+
+% todo: save settings
+%
+% low level interface
+%
+% we should use normal counters but then we need to sync settings
+
+% some line
+%
+% \startlocallinenumbering
+% some source code 1\par
+% some source code 2\par
+% some source code 3\par
+% \stoplocallinenumbering
+%
+% some line
+
+\registerctxluafile{page-lin}{autosuffix}
+
+\definesystemattribute[linenumber] [public]
+\definesystemattribute[linereference][public]
+
+\appendtoksonce
+ \c_attr_linenumber\attributeunsetvalue
+\to \everyforgetall
+
+\appendtoks
+ \c_attr_linenumber\attributeunsetvalue
+\to \everyinsidefloat
+
+\mutable\let\linenumber\!!zerocount
+
+\newbox \b_page_lines_scratch
+\newcount \c_page_lines_reference
+\newconstant\c_page_lines_nesting
+
+\newconditional\tracelinenumbering % we keep this for old times sake
+
+\installtextracker
+ {lines.numbers.show}
+ {\settrue \tracelinenumbering}
+ {\setfalse\tracelinenumbering}
+
+% id nr shift width leftskip dir
+
+\installcorenamespace{linenumberinginstance}
+
+% tag skipflag s getfield(n,"shift") getfield(n,"width") leftmarginwidth(getlist(n)) getfield(n,"dir"))
+
+\permanent\let\makelinenumber\gobblefivearguments % used at lua end
+
+\newconditional\page_postprocessors_needed_box
+
+\protected\def\page_postprocessors_linenumbers_page_indeed #tag{\page_lines_add_numbers_to_box{#tag}\plusone \plusone \zerocount}
+\protected\def\page_postprocessors_linenumbers_box_indeed #tag{\page_lines_add_numbers_to_box{#tag}\plusone \plusone \zerocount}
+\protected\def\page_postprocessors_linenumbers_deepbox_indeed#tag{\page_lines_add_numbers_to_box{#tag}\plusone \plusone \plusone }
+\protected\def\page_postprocessors_linenumbers_column_indeed #tag{\page_lines_add_numbers_to_box{#tag}\currentcolumn\nofcolumns\zerocount}
+
+\let\page_postprocessors_linenumbers_page \gobbleoneargument
+\let\page_postprocessors_linenumbers_box \gobbleoneargument
+\let\page_postprocessors_linenumbers_deepbox\gobbleoneargument
+\let\page_postprocessors_linenumbers_column \gobbleoneargument
+
+\protected\def\page_postprocessors_linenumbers_check
+ {\glet\page_postprocessors_linenumbers_check \relax
+ \glet\page_postprocessors_linenumbers_page \page_postprocessors_linenumbers_page_indeed
+ \glet\page_postprocessors_linenumbers_box \page_postprocessors_linenumbers_box_indeed
+ \glet\page_postprocessors_linenumbers_deepbox\page_postprocessors_linenumbers_deepbox_indeed
+ \glet\page_postprocessors_linenumbers_column \page_postprocessors_linenumbers_column_indeed}
+
+\def\page_lines_start_define
+ {\setxvalue{\??linenumberinginstance\currentlinenumbering}%
+ {\clf_registerlinenumbering
+ continue {\ifnum\c_page_lines_mode=\zerocount\v!yes\else\v!no\fi}%
+ start \linenumberingparameter\c!start
+ step \linenumberingparameter\c!step
+ method {\linenumberingparameter\c!method}%
+ tag {\currentlinenumbering}%
+ }}
+
+\def\page_lines_start_update
+ {\clf_setuplinenumbering
+ \csname\??linenumberinginstance\currentlinenumbering\endcsname
+ {%
+ continue {\ifnum\c_page_lines_mode=\zerocount\v!yes\else\v!no\fi}%
+ }%
+ \relax}
+
+\def\page_lines_setup
+ {\ifcsname \??linenumberinginstance\currentlinenumbering\endcsname
+ \clf_setuplinenumbering
+ \lastnamedcs
+ {%
+ continue {\ifnum\c_page_lines_mode=\zerocount\v!yes\else\v!no\fi}%
+ start \linenumberingparameter\c!start
+ step \linenumberingparameter\c!step
+ method {\linenumberingparameter\c!method}%
+ tag {\currentlinenumbering}%
+ }%
+ \relax
+ \fi}
+
+% we could make this a bit more efficient by putting the end reference
+% in the same table as the start one but why make things complex ...
+
+\protected\def\page_lines_some_reference#1#2#3%
+ {\dontleavehmode\begingroup
+ \global\advance\c_page_lines_reference\plusone
+ \c_attr_linereference\c_page_lines_reference
+ #3% todo: #3{#1} as there is no need to pass #1 as part of #3
+ % for the moment we use a simple system i.e. no prefixes etc .. todo: store as number
+ \c_strc_references_bind_state\zerocount % we don't want the prewordbreak and manage it here
+ \normalexpanded{\strc_references_set_named_reference{line}{#2}{conversion=\linenumberingparameter\c!conversion}{\the\c_page_lines_reference}}% kind labels userdata text
+ \endgroup}
+
+% \def\page_lines_reference_start#1{\page_lines_some_reference{#1}{lr:b:#1}{}} % reimplemented later
+% \def\page_lines_reference_stop #1{\page_lines_some_reference{#1}{lr:e:#1}{}} % reimplemented later
+
+% \def\mklinestartreference#1[#2]{\in{#1}[lr:b:#2]} % not interfaced/ not used
+% \def\mklinestopreference #1[#2]{\in{#1}[lr:e:#2]} % not interfaced/ not used
+
+\newif\ifnumberinglines % will change
+\newif\iftypesettinglines % will change
+
+\installcorenamespace{linenumbering}
+
+\installcommandhandler \??linenumbering {linenumbering} \??linenumbering
+
+\setnewconstant\c_page_lines_mode \plusone % 0=continue, 1=restart
+\setnewconstant\c_page_lines_location \plusone % 0=middle, 1=left, 2=right, 3=inner, 4=outer, 5=text, 6=begin, 7=end
+\setnewconstant\c_page_lines_alignment\plusfive % 0=middle, 1=left, 2=right, 5=auto
+
+\newdimen\d_page_lines_width
+\newdimen\d_page_lines_distance
+
+\newevery \beforeeverylinenumbering \relax
+\newevery \aftereverylinenumbering \relax
+\newevery \everylinenumber \relax
+
+\appendtoks
+ \page_lines_setup
+\to \everysetuplinenumbering
+
+\appendtoks
+ \page_lines_start_define
+\to \everydefinelinenumbering
+
+\setuplinenumbering
+ [\c!conversion=\v!numbers,
+ \c!start=1,
+ \c!step=1,
+ \c!method=\v!first,
+ \c!continue=\v!no,
+ \c!style=,
+ \c!color=,
+ \c!width=2\emwidth,
+ \c!left=,
+ \c!right=,
+ \c!command=,
+ \c!margin=2.5\emwidth,
+ \c!distance=\zeropoint,
+ \c!location=\v!default, % depends on direction, columns etc
+ \c!align=\v!auto]
+
+\definelinenumbering
+ []
+
+% \startlinenumbering[<startvalue>|continue|settings|name]
+% \startlinenumbering[name][<startvalue>|continue|settings]
+
+\newcount\c_pages_lines_nesting
+
+\permanent\tolerant\protected\def\startlinenumbering[#1]#*[#2]%
+ {\begingroup
+ \page_postprocessors_linenumbers_check
+ \advance\c_pages_lines_nesting\plusone
+ \ifnum\c_pages_lines_nesting>\plusone
+ \expandafter\gobbletwooptionals
+ \orelse\ifparameter#2\or
+ \expandafter\page_lines_start_two
+ \orelse\ifparameter#1\or
+ \expandafter\page_lines_start_one
+ \else
+ \expandafter\page_lines_start_zero
+ \fi[#1][#2]}
+
+\def\page_lines_start_zero[#1][#2]%
+ {\edef\m_argument{\linenumberingparameter\c!continue}%
+ \ifx\m_argument\v!yes
+ \c_page_lines_mode\zerocount
+ \else
+ \c_page_lines_mode\plusone
+ \fi
+ \page_lines_start_followup}
+
+\def\page_lines_start_one[#1][#2]% [continue|<number>|settings] % historic
+ {\edef\m_argument{#1}%
+ \ifx\m_argument\v!continue
+ \c_page_lines_mode\zerocount
+ \let\currentlinenumbering\empty
+ \else
+ \c_page_lines_mode\plusone
+ \ifx\m_argument\v!empty
+ \let\currentlinenumbering\empty
+ \orelse\ifhastok={#1}%
+ \let\currentlinenumbering\empty
+ \setupcurrentlinenumbering[#1]%
+ \else
+ \doifelsenumber\m_argument
+ {\let\currentlinenumbering\empty
+ \letlinenumberingparameter\c!start\m_argument}
+ {\let\currentlinenumbering\m_argument}%
+ \fi
+ \edef\p_continue{\linenumberingparameter\c!continue}%
+ \ifx\p_continue\v!yes
+ \c_page_lines_mode\zerocount
+ \fi
+ \fi
+ \page_lines_start_followup}
+
+\def\page_lines_start_two[#1][#2]% [tag][continue|<number>|settings]
+ {\edef\currentlinenumbering{#1}%
+ \edef\m_argument{#2}%
+ \ifx\m_argument\v!continue
+ \c_page_lines_mode\zerocount
+ \else
+ \c_page_lines_mode\plusone
+ \ifx\m_argument\v!empty \else
+ \doifelseassignment{#2}
+ {\setupcurrentlinenumbering[#2]}
+ {\doifnumber\m_argument
+ {\letlinenumberingparameter\c!start\m_argument}}%
+ \fi
+ \edef\p_continue{\linenumberingparameter\c!continue}%
+ \ifx\p_continue\v!yes
+ \c_page_lines_mode\zerocount
+ \fi
+ \fi
+ \page_lines_start_followup}
+
+\newconditional\c_page_lines_auto_narrow
+%\newconditional\c_page_lines_enabled
+
+\appendtoks
+ \ifconditional\page_postprocessors_needed_box
+ \page_postprocessors_linenumbers_page\b_page_postprocessor
+ \fi
+\to \t_page_postprocessors_page
+
+\appendtoks
+ \ifconditional\page_postprocessors_needed_box
+ \page_postprocessors_linenumbers_column\b_page_postprocessor
+ \fi
+\to \t_page_postprocessors_column
+
+\def\page_lines_start_followup
+ {\numberinglinestrue
+ \edef\p_location{\linenumberingparameter\c!location}%
+ \setfalse\c_page_lines_auto_narrow
+ \ifhmode \else
+ \ifx\p_location\v!text
+ \ifdim\leftskip>\zeropoint \else
+ \advance\leftskip\linenumberingparameter\c!margin
+ \settrue\c_page_lines_auto_narrow
+ \fi
+ \orelse\ifx\p_location\v!begin
+ \ifdim\leftskip>\zeropoint \else
+ \advance\leftskip\linenumberingparameter\c!margin
+ \settrue\c_page_lines_auto_narrow
+ \fi
+ \orelse\ifx\p_location\v!end
+ \ifdim\leftskip>\zeropoint \else
+ \advance\rightskip\linenumberingparameter\c!margin
+ \settrue\c_page_lines_auto_narrow
+ \fi
+ \fi
+ \fi
+ \the\beforeeverylinenumbering
+ %\global\settrue\c_page_lines_enabled %
+ \global\settrue\page_postprocessors_needed_box % see core-rul.mkiv
+ \ifcase\c_page_lines_mode\relax
+ \page_lines_start_update % continue
+ \or
+ \page_lines_start_define % only when assignment
+ \fi
+ \c_attr_linenumber\csname\??linenumberinginstance\currentlinenumbering\endcsname\relax}
+
+\permanent\protected\def\stoplinenumbering
+ {\ifconditional\c_pages_lines_nesting=\plusone
+ \c_attr_linenumber\attributeunsetvalue
+ \the\aftereverylinenumbering
+ \ifconditional\c_page_lines_auto_narrow\par\fi
+ \fi
+ \endgroup}
+
+% number placement .. will change into (the new) margin code
+
+\newconditional\c_page_lines_fake_number
+\newconstant \b_page_lines_number
+\newconstant \c_page_lines_column
+\newconstant \c_page_lines_last_column
+\newdimen \d_page_lines_line_width
+\settrue \c_page_lines_dir_left_to_right
+
+\installcorenamespace{linenumberinghandler}
+
+\def\page_line_swap_align % can become a helper
+ {\ifx\p_align\v!inner \let\p_align\v!outer \orelse
+ \ifx\p_align\v!outer \let\p_align\v!inner \orelse
+ \ifx\p_align\v!flushleft \let\p_align\v!flushright\orelse
+ \ifx\p_align\v!flushright\let\p_align\v!flushleft \orelse
+ \ifx\p_align\v!left \let\p_align\v!right \orelse
+ \ifx\p_align\v!right \let\p_align\v!left \fi}
+
+\let\page_lines_make_number_indeed\relax
+
+% \def\page_lines_rlap{\ifconditional\c_page_lines_dir_left_to_right\expandafter\rlap\else\expandafter\llap\fi}
+% \def\page_lines_llap{\ifconditional\c_page_lines_dir_left_to_right\expandafter\llap\else\expandafter\rlap\fi}
+
+\def\page_lines_add_numbers_to_box#box#column#max#nesting%
+ {\bgroup
+ \strc_tags_enable_indeed
+ \b_page_lines_number #box\relax
+ \c_page_lines_column #column\relax
+ \c_page_lines_last_column#max\relax
+ \fullrestoreglobalbodyfont
+ \enforced\let\makelinenumber\page_lines_make_number % used at lua end
+ \clf_addlinenumbers
+ \b_page_lines_number
+ \scratchbox
+ #nesting%
+ \relax
+ \egroup}
+
+\def\page_lines_make_number#tag#linenumber#width#dir% with hang and parindent and skips we have to compensate for \hsize
+ {\setbox\scratchbox\naturalhbox to \zeropoint \bgroup
+ \edef\currentlinenumbering{#tag}%
+ \def\linenumber{#linenumber}% unsafe
+ \d_page_lines_line_width#width\scaledpoint\relax
+ \d_page_lines_distance\linenumberingparameter\c!distance\relax
+ \edef\p_align{\linenumberingparameter\c!align}%
+ \edef\p_location{\linenumberingparameter\c!location}%
+ \ifcase#dir\relax
+ \settrue \c_page_lines_dir_left_to_right
+ \else
+ \setfalse\c_page_lines_dir_left_to_right
+ \fi
+ %
+ % maybe we also need an option to ignore columns, so that we renumber
+ % once but on the other hand this assumes aligned lines
+ %
+ \ifcase\c_page_lines_last_column\relax
+ \settrue \c_page_lines_fake_number % why
+ \or
+ % one column
+ \or
+ % two columns
+ \ifx\p_location\v!default % or just margin
+ \ifcase\c_page_lines_column\relax
+ \settrue \c_page_lines_fake_number % why
+ \or
+ \let\p_location\v!left % one
+ \else
+ \let\p_location\v!right % two
+ \page_line_swap_align % can become a helper
+ \fi
+ \fi
+ \fi
+ \ifx\p_location\v!default
+ \ifconditional\c_page_lines_dir_left_to_right
+ \let\p_location\v!left
+ \else
+ \let\p_location\v!right
+ \page_line_swap_align % yes or no
+ \fi
+ \fi
+ %
+ \begincsname\??linenumberinghandler\p_location\endcsname
+ \egroup}
+
+\def\page_lines_number_inject#align#width%
+ {\edef\p_width{\linenumberingparameter\c!width}%
+ \ifx\p_width\v!margin
+ \d_page_lines_width#width%
+ \else
+ \d_page_lines_width\p_width
+ \fi
+ \relax
+ \ifdim\d_page_lines_width>\zeropoint
+ \ifconditional\tracelinenumbering
+ \ruledhbox{\simplealignedbox\d_page_lines_width#align{\page_lines_number_inject_indeed}}%
+ \else
+ \simplealignedbox\d_page_lines_width#align{\page_lines_number_inject_indeed}%
+ \fi
+ \else
+ \ifconditional\tracelinenumbering
+ \ruledhbox
+ % \else
+ % \hbox
+ \fi
+ {\page_lines_number_inject_indeed}%
+ \fi}
+
+\def\page_lines_number_inject_indeed
+ {\uselinenumberingstyleandcolor\c!style\c!color
+ \linenumberingparameter\c!command
+ {\linenumberingparameter\c!left
+ \convertnumber{\linenumberingparameter\c!conversion}\linenumber
+ \linenumberingparameter\c!right}}
+
+% \def\dodorlap{\hbox to \zeropoint{\box\nextbox\normalhss}\endgroup}
+% \def\dodollap{\hbox to \zeropoint{\normalhss\box\nextbox}\endgroup}
+
+\def\page_line_handle_left#align#width#distance%
+ {\dostarttagged\t!linenumber\empty
+ \llap
+ {\page_lines_number_inject#align#width%
+ \dostarttagged\t!ignore\empty
+ \kern\dimexpr
+ #distance+\d_page_lines_distance
+ \ifconditional\c_page_lines_dir_left_to_right\else+\d_page_lines_line_width\fi
+ \relax
+ \the\everylinenumber
+ \hss
+ \dostoptagged}%
+ \dostoptagged}
+
+\def\page_line_handle_right#align#width#distance%
+ {\dostarttagged\t!linenumber\empty
+ \rlap
+ {\dostarttagged\t!ignore\empty
+ \kern\dimexpr
+ #distance+\d_page_lines_distance
+ \ifconditional\c_page_lines_dir_left_to_right+\d_page_lines_line_width\fi
+ \relax
+ \dostoptagged
+ \page_lines_number_inject#align#width%
+ \dostarttagged\t!ignore\empty
+ \the\everylinenumber
+ \hss
+ \dostoptagged}%
+ \dostoptagged}
+
+\setuvalue{\??linenumberinghandler\v!left}%
+ {\page_line_handle_left\p_align\leftmarginwidth\leftmargindistance}
+
+\setuvalue{\??linenumberinghandler\v!right}%
+ {\page_line_handle_right\p_align\rightmarginwidth\rightmargindistance}
+
+\setuvalue{\??linenumberinghandler\v!inner}%
+ {\ifodd\realpageno
+ \ifx\p_align\v!inner
+ \page_line_handle_left\v!flushleft\leftmarginwidth\leftmargindistance
+ \orelse\ifx\p_align\v!outer
+ \page_line_handle_left\v!flushright\leftmarginwidth\leftmargindistance
+ \else
+ \page_line_handle_left\p_align\leftmarginwidth\leftmargindistance
+ \fi
+ \else
+ \ifx\p_align\v!inner
+ \page_line_handle_right\v!flushright\rightmarginwidth\rightmargindistance
+ \orelse\ifx\p_align\v!outer
+ \page_line_handle_right\v!flushleft\rightmarginwidth\rightmargindistance
+ \else
+ \page_line_handle_right\p_align\rightmarginwidth\rightmargindistance
+ \fi
+ \fi}
+
+\setuvalue{\??linenumberinghandler\v!outer}%
+ {\ifodd\realpageno
+ \ifx\p_align\v!inner
+ \page_line_handle_right\v!flushleft\leftmarginwidth\leftmargindistance
+ \orelse\ifx\p_align\v!outer
+ \page_line_handle_right\v!flushright\leftmarginwidth\leftmargindistance
+ \else
+ \page_line_handle_right\p_align\leftmarginwidth\leftmargindistance
+ \fi
+ \else
+ \ifx\p_align\v!inner
+ \page_line_handle_left\v!flushright\rightmarginwidth\rightmargindistance
+ \orelse\ifx\p_align\v!outer
+ \page_line_handle_left\v!flushleft\rightmarginwidth\rightmargindistance
+ \else
+ \page_line_handle_left\p_align\rightmarginwidth\rightmargindistance
+ \fi
+ \fi}
+
+\def\page_line_handle_begin#align%
+ {\rlap
+ {\kern\d_page_lines_distance
+ \page_lines_number_inject#align\zeropoint
+ \the\everylinenumber}}
+
+\def\page_line_handle_end#align%
+ {\rlap
+ {\kern\d_page_lines_line_width\relax
+ \llap
+ {\page_lines_number_inject#align\zeropoint
+ \kern\d_page_lines_distance
+ \the\everylinenumber}}}
+
+\protected\defcsname\??linenumberinghandler\v!begin\endcsname{\page_line_handle_begin\p_align}
+\protected\defcsname\??linenumberinghandler\v!end \endcsname{\page_line_handle_end \p_align}
+\protected\defcsname\??linenumberinghandler\v!text \endcsname{\page_line_handle_begin\p_align}
+
+\letcsname\??linenumberinghandler\v!inleft \expandafter\endcsname\csname\??linenumberinghandler\v!left \endcsname
+\letcsname\??linenumberinghandler\v!inmargin\expandafter\endcsname\csname\??linenumberinghandler\v!left \endcsname
+\letcsname\??linenumberinghandler\v!margin \expandafter\endcsname\csname\??linenumberinghandler\v!left \endcsname
+\letcsname\??linenumberinghandler\v!inright \expandafter\endcsname\csname\??linenumberinghandler\v!right\endcsname
+
+% referencing: \permithyphenation, also removes leading spaces (new per 29-11-2013)
+
+\permanent\protected\def\someline [#1]{\page_lines_reference_start{#1}\page_lines_reference_stop{#1}} % was just a def
+\permanent\protected\def\startline[#1]{\page_lines_reference_start{#1}\permithyphenation\ignorespaces} %okay?
+\permanent\protected\def\stopline [#1]{\removeunwantedspaces\permithyphenation\page_lines_reference_stop{#1}}
+
+\def\page_lines_reference_show_start
+ {\ifconditional\tracelinenumbering
+ \expandafter\page_lines_reference_show_start_indeed
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\page_lines_reference_show_stop
+ {\ifconditional\tracelinenumbering
+ \expandafter\page_lines_reference_show_stop_indeed
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\page_lines_reference_show_start_indeed#1%
+ {\setbox\scratchbox\hpack{\llap
+ {\vrule\s!width\onepoint\s!depth\strutdp\s!height.8\strutht\raise.85\strutht\hpack{\llap{\tt\txx#1}}}}%
+ \smashbox\scratchbox
+ \box\scratchbox}
+
+\def\page_lines_reference_show_stop_indeed#1%
+ {\setbox\scratchbox\hpack{\rlap
+ {\raise.85\strutht\hpack{\rlap{\tt\txx#1}}\vrule\s!width\onepoint\s!depth\strutdp\s!height.8\strutht}}%
+ \smashbox\scratchbox
+ \box\scratchbox}
+
+\def\page_lines_reference_start#1{\page_lines_some_reference{#1}{lr:b:#1}{\page_lines_reference_show_start{#1}}}
+\def\page_lines_reference_stop #1{\page_lines_some_reference{#1}{lr:e:#1}{\page_lines_reference_show_stop {#1}}}
+
+% eventually we will do this in lua
+
+\permanent\def\currentreferencelinenumber{\clf_filterreference{linenumber}}
+
+\let\m_page_lines_from\empty
+\let\m_page_lines_to \empty
+
+\permanent\protected\def\doifelsesamelinereference#1#2#3%
+ {\doifelsereferencefound{lr:b:#1}
+ {\edef\m_page_lines_from{\currentreferencelinenumber}%
+ \doifelsereferencefound{lr:e:#1}
+ {\edef\m_page_lines_to{\currentreferencelinenumber}%
+ %[\m_page_lines_from,\m_page_lines_to]
+ \ifx\m_page_lines_from\m_page_lines_to#2\else#3\fi}%
+ {#2}}
+ {#2}}
+
+\aliased\let\doifsamelinereferenceelse\doifelsesamelinereference
+
+\permanent\protected\def\inline#1[#2]%
+ {\doifelsenothing{#1}%
+ {\doifelsesamelinereference{#2}%
+ {\in{\leftlabeltext\v!line}{\rightlabeltext\v!line}[lr:b:#2]}%
+ {\in{\leftlabeltext\v!lines}{}[lr:b:#2]--\in{}{\rightlabeltext\v!lines}[lr:e:#2]}}%
+ {\doifelsesamelinereference{#2}%
+ {\in{#1}[lr:b:#2]}%
+ {\in{#1}[lr:b:#2]--\in[lr:e:#2]}}}
+
+\permanent\protected\def\inlinerange[#1]%
+ {\doifelsesamelinereference{#1}%
+ {\in[lr:b:#1]}%
+ {\in[lr:b:#1]\endash\in[lr:e:#1]}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-mak.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-mak.mklx
new file mode 100644
index 00000000000..2a5cd435218
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-mak.mklx
@@ -0,0 +1,280 @@
+%D \module
+%D [ file=page-mak, % copied from main-001,
+%D version=1997.03.31,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Simple MakeUp,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / MakeUp}
+
+\unprotect
+
+%D \macros
+%D {definemakeup, setupmakeup, startmakeup}
+%D
+%D A makeup is a separate page, like a title page or colofon. There is one standard
+%D makeup page, but you can define more if needed.
+%D
+%D \starttyping
+%D \startstandardmakeup
+%D My Fancy Title
+%D \stopstandardmakeup
+%D \stoptyping
+%D
+%D The associated commands are:
+%D
+%D \showsetup{definemakeup}
+%D \showsetup{setupmakeup}
+%D \showsetup{startmakeup}
+%D
+%D New is that we have a layout with the same name so one can set up a special
+%D layout that then gets used.
+
+\installcorenamespace{makeup}
+\installcorenamespace{makeupdoublesided}
+
+\installcommandhandler \??makeup {makeup} \??makeup
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!start\currentmakeup\e!makeup}{\startmakeup[\currentmakeup]}%
+ \frozen\instance\setuevalue{\e!stop \currentmakeup\e!makeup}{\stopmakeup}%
+ \doifelselayoutdefined\currentmakeup\donothing{\definelayout[\currentmakeup]}% new
+\to \everydefinemakeup
+
+%D The \type {\start}||\type {\stop} macros are used for both the direct and
+%D indirect way. The parameterless call will build a simple box.
+
+\permanent\tolerant\protected\def\startmakeup[#1]#*[#2]%
+ {\ifparameters
+ \expandafter\page_makeup_start_nop
+ \else
+ \expandafter\page_makeup_start_yes
+ \fi[#1][#2]}
+
+\aliased\let\stopmakeup\relax
+
+%D The simple case: just a box with text dimensions.
+
+\protected\def\page_makeup_start_nop[#name][#settings]% dummies
+ {\page
+ \setupheader[\c!state=\v!empty]%
+ \setupfooter[\c!state=\v!empty]%
+ \bgroup
+ \forgetall % else indented flush
+ \dontcomplain
+ \vbox to \textheight \bgroup
+ \setsystemmode\v!makeup
+ \hsize\textwidth
+ \enforced\let\stopmakeup\page_makeup_stop_nop}
+
+\permanent\protected\def\page_makeup_stop_nop
+ {\egroup
+ \egroup
+ \page}
+
+%D The normal variant.
+
+\newbox \b_page_makeup
+\newtoks\t_page_makeup_every_setup
+
+\def\page_makeup_start_yes[#name]% [#settings]%
+ {\doifelsecommandhandler\??makeup{#name}\page_makeup_start_indeed\page_makeup_start_nop[#name]}%
+
+% case 1:
+%
+% \setuplayout[height=5cm]
+%
+% case 2:
+%
+% \definelayout[crap][height=10cm]
+% \definelayout[standard][crap]
+%
+% case 3:
+%
+% \setuplayout[standard][height=15cm]
+%
+% case 4:
+%
+% \definelayout[whatever][height=2cm]
+% \setuplayout[whatever]
+
+\def\page_makeup_start_indeed[#name][#settings]%
+ {% the next grouping hack is somewhat messy:
+ \begingroup
+ % we need to figure out the current layout
+ \xdef\m_page_makeup_name{#name}%
+ \let\currentmakeup\m_page_makeup_name
+ \let\currentlayout\m_page_makeup_name
+ \xdef\m_page_makeup_layout_parent{\layoutparameter\s!parent}%
+ \setupcurrentmakeup[#settings]%
+ \edef\p_page{\makeupparameter\c!page}%
+ \ifempty\p_page
+ \endgroup
+ \page % new, so best not have dangling mess here like references (we could capture then and flush embedded)
+ \orelse\ifx\p_page\v!no
+ % nothing
+ \endgroup
+ \else
+ \normalexpanded{\endgroup\page[\p_page]}%
+ \fi
+ % some dirty trickery (sorry) for determining if we have
+ % - a layout definition at all
+ % - inherit from the parent of that definition
+ % - inherit from the current layout otherwise
+ \ifx\m_page_makeup_name\currentlayout
+ % we already use the layout
+ \orelse\ifx\m_page_makeup_layout_parent\??layout
+ % we inherit from the current layout
+ \normalexpanded{\setuplayout[#name][\s!parent=\??layout\currentlayout]}% is remembered but checked later anyway
+ % \else
+ % we have an inherited layout
+ \fi
+ \startlayout[#name]% includes \page
+ \bgroup
+ %\edef\currentmakeup{#name}%
+ \let\currentmakeup\m_page_makeup_name
+ \setupcurrentmakeup[#settings]%
+ \setsystemmode\v!makeup
+ \the\t_page_makeup_every_setup
+ \makeupparameter\c!commands % hm, what is this one doing here ?
+ \forgetall % else indented flush
+ \dontcomplain
+ \global\setbox\b_page_makeup\vbox to \makeupparameter\c!height \bgroup
+ \usemakeupstyleandcolor\c!style\c!color
+ \hsize\makeupparameter\c!width
+ \usealignparameter\makeupparameter
+ \usesetupsparameter\makeupparameter % lua(..),xml(...,..),tex(..)
+ \makeupparameter\c!top
+ \enforced\let\stopmakeup\page_makeup_stop_yes}
+
+\permanent\protected\def\page_makeup_stop_yes
+ {\endgraf
+ \makeupparameter\c!bottom
+ \egroup
+ \strc_pagenumbers_page_state_push % new
+ \makeupparameter\c!before\relax
+ \begingroup
+ \ifdim\ht\b_page_makeup>\vsize
+ \ht\b_page_makeup\vsize % is already set to \textheight (maybe set dp to 0)
+ \fi
+ \setuppagenumber[\c!state=\makeupparameter\c!pagestate]%
+ \doif{\makeupparameter\c!location}\v!top{\topskip\zeropoint}%
+ \edef\p_reference{\makeupparameter\c!reference}%
+ \dontleavehmode
+ \usereferenceparameter\makeupparameter
+ \box\b_page_makeup % could be whole box being destination
+ \the\t_page_makeup_every_setup
+ \page
+ \endgroup
+ \makeupparameter\c!after\relax
+ \ifdoublesided \ifodd\realpageno \else
+ \csname\??makeupdoublesided\makeupparameter\c!doublesided\endcsname
+ \fi \fi
+ \strc_pagenumbers_page_state_pop % new
+ \egroup
+ \stoplayout % includes \page
+ \ifx\m_page_makeup_name\currentlayout
+ \orelse\ifx\m_page_makeup_layout_parent\??layout
+ \normalexpanded{\setuplayout[\m_page_makeup_name][\s!parent=\??layout]}% is remembered but checked later anyway
+ \fi}
+
+\setvalue{\??makeupdoublesided\v!yes}%
+ {\emptyhbox
+ \page}
+
+\setvalue{\??makeupdoublesided\v!empty}%
+ {\the\t_page_makeup_every_setup
+ % == \page[\v!dummy]
+ \page[\v!blank]%
+ \emptyhbox
+ \page}
+
+%D Probably obsolete (but used in one manual by Taco):
+
+\permanent\protected\def\startcolumnmakeup % don't change
+ {\bgroup
+ \getrawnoflines\textheight % raw as we can have topskip
+ \setbox\scratchbox\vbox to \dimexpr\noflines\lineheight-\lineheight+\topskip\relax
+ \bgroup
+ \forgetall}
+
+\permanent\protected\def\stopcolumnmakeup
+ {\egroup
+ \dp\scratchbox\zeropoint
+ \wd\scratchbox\textwidth
+ \box\scratchbox
+ \egroup
+ \page_otr_command_synchronize_hsize}
+
+%D The text surrounding the main body text can be influenced by setting their
+%D associated status variables. The connection between them is made by the following
+%D macro:
+
+\appendtoks % this might need a revision
+ \setupfooter[\c!state=\makeupparameter\c!footerstate]%
+ \setupheader[\c!state=\makeupparameter\c!headerstate]%
+ \setuptext [\c!state=\makeupparameter\c!textstate]%
+ \setupbottom[\c!state=\makeupparameter\c!bottomstate]%
+ \setuptop [\c!state=\makeupparameter\c!topstate]%
+\to \t_page_makeup_every_setup
+
+%D The standard page template is defined as follows:
+
+\setupmakeup
+ [\c!width=\innermakeupwidth, % example in manual / was \makeupwidth
+ \c!height=\textheight, % example in manual
+ %\c!commands=,
+ %\c!setups=,
+ %\c!color=,
+ %\c!align=,
+ %\c!before=,
+ %\c!after=,
+ %\c!location=,
+ \c!page=\v!right,
+ \c!doublesided=\v!empty,
+ \c!top=\vss,
+ \c!bottom=\vss,
+ \c!bottomstate=\v!normal,
+ \c!topstate=\v!normal,
+ \c!textstate=\v!normal,
+ \c!headerstate=\v!stop,
+ \c!footerstate=\v!stop,
+ \c!pagestate=\v!stop] % in manual ! ! !
+% \c!pagestate=\v!start]
+
+\definemakeup
+ [\v!standard]
+ [\c!width=\innermakeupwidth,
+ \c!height=\textheight,
+ \c!page=\v!right,
+ \c!doublesided=\v!empty]
+
+\definemakeup
+ [\v!text]
+ [\c!topstate=\v!start,
+ \c!headerstate=\v!start,
+ \c!textstate=\v!start,
+ \c!footerstate=\v!start,
+ \c!bottomstate=\v!start,
+ \c!doublesided=\v!no,
+ \c!page=\v!yes,
+ \c!top=\pseudostrut\ignorespaces,
+ \c!bottom=\obeydepth\vss]
+
+\definemakeup
+ [\v!page]
+
+\definemakeup
+ [\v!middle]
+ [\v!standard]
+
+% \definelayout[standard]
+% \definelayout[text]
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-mbk.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-mbk.mklx
new file mode 100644
index 00000000000..05b09c79e87
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-mbk.mklx
@@ -0,0 +1,243 @@
+%D \module
+%D [ file=page-mbk, % was part of page-mis.mkiv / 2008.11.17
+%D version=2011.11.23, % was part of page-flt.tex / 2000.10.20
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Margin Floats,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Margin Floats}
+
+\unprotect
+
+%D This is an old mechanism that is quite independent of other floats. There is for
+%D instance no save/restore used here. When there is need (and demand) we can use
+%D the float cache and then flush them in the text when they are left over. On the
+%D other hand, margin float are somewhat manual so it does not make too much sense
+%D to complicate the code. As an alternative users can fall back on margin texts:
+%D they do stack but don't float.
+%D
+%D \starttyping
+%D \dorecurse{9} {
+%D \placefigure[margin]{#1}{}
+%D \input ward
+%D }
+%D \page \null % will flush them
+%D \stoptyping
+
+% status=start: in margin
+% status=stop : in text
+%
+% todo: flush margin floats at end of text
+% todo: separate boxes for left and right
+
+\installcorenamespace{marginblock}
+\installcorenamespace{marginblocklocation}
+
+\installcommandhandler \??marginblock {marginblock} \??marginblock
+
+\aliased\let\setupmarginblocks\setupmarginblock
+
+\newconditional\c_page_margin_blocks_enabled % not really needed as we can check each time
+\newconditional\c_page_margin_blocks_present
+
+\newbox \b_page_margin_blocks
+\newbox \b_page_margin_blocks_prepared
+
+\appendtoks
+ \doifelse{\rootmarginblockparameter\c!state}\v!start
+ \settrue\setfalse\c_page_margin_blocks_enabled
+\to \everysetupmarginblock
+
+\setupmarginblocks
+ [\c!state=\v!start,
+ \c!location=\v!inmargin,
+ \c!width=\rightmarginwidth,
+ %\c!style=,
+ %\c!color=,
+ %\c!align=,
+ %\c!left=,
+ %\c!right=,
+ %\c!top=,
+ %\c!before=,
+ %\c!after=,
+ \c!inbetween=\blank,
+ \c!bottom=\vfill]
+
+\permanent\tolerant\protected\def\startmarginblock[#tag]%
+ {\begingroup
+ \edef\currentmarginblock{#tag}%
+ \doifelse{\marginblockparameter\c!state}\v!start
+ \page_margin_blocks_start_block_yes
+ \page_margin_blocks_start_block_nop}
+
+\permanent\protected\def\stopmarginblock
+ {\doifelse{\marginblockparameter\c!state}\v!start
+ \page_margin_blocks_stop_block_yes
+ \page_margin_blocks_stop_block_nop
+ \endgroup}
+
+\def\page_margin_blocks_start_block_yes % 2 maal \vbox ivm \unvbox elders
+ {\showmessage\m!layouts4\empty
+ \global\settrue\c_page_margin_blocks_present
+ \global\setbox\b_page_margin_blocks\vtop\bgroup\vbox\bgroup
+ \hsize\marginblockparameter\c!width
+ \ifvoid\b_page_margin_blocks\else
+ \unvbox\b_page_margin_blocks
+ \marginblockparameter\c!inbetween
+ \fi
+ \usealignparameter\marginblockparameter
+ \usemarginblockstyleandcolor\c!style\c!color
+ \begstrut
+ \ignorespaces}
+
+\def\page_margin_blocks_stop_block_yes
+ {\removeunwantedspaces
+ \endstrut
+ \egroup
+ \egroup}
+
+\def\page_margin_blocks_start_block_nop
+ {\showmessage\m!layouts5\empty
+ \marginblockparameter\c!before
+ \bgroup
+ \usemarginblockstyleandcolor\c!style\c!color}
+
+\def\page_margin_blocks_stop_block_nop
+ {\egroup
+ \marginblockparameter\c!after}
+
+\def\page_margin_blocks_reshape
+ {\ifdim\ht\b_page_margin_blocks_prepared>\zeropoint
+ \beginofshapebox
+ \unvbox\b_page_margin_blocks_prepared
+ \endofshapebox
+ \reshapebox
+ {\box\shapebox}%
+ \setbox\b_page_margin_blocks_prepared\vbox to \textheight
+ {\marginblockparameter\c!top
+ \flushshapebox
+ \marginblockparameter\c!bottom}%
+ \fi}
+
+\permanent\protected\def\checkmarginblocks
+ {\ifvoid\b_page_margin_blocks
+ \global\setfalse\c_page_margin_blocks_present
+ \else
+ \page_margin_blocks_check_indeed
+ \fi}
+
+\def\page_margin_blocks_check_indeed
+ {\ifcsname\??marginblocklocation\marginblockparameter\c!location\endcsname
+ \page_margin_blocks_prepare_box
+ \csname\??marginblocklocation\marginblockparameter\c!location\endcsname
+ \else
+ \global\setbox\b_page_margin_blocks\emptybox
+ \global\setfalse\c_page_margin_blocks_present
+ \fi}
+
+\def\page_margin_blocks_prepare_box
+ {\setbox\b_page_margin_blocks_prepared\vbox
+ {\forgetall
+ \splittopskip\topskip
+ \ifvoid\b_page_margin_blocks\else
+ \ifdim\ht\b_page_margin_blocks>\textheight
+ \vsplit\b_page_margin_blocks to \textheight
+ \else
+ \unvbox\b_page_margin_blocks
+ \fi
+ \fi}%
+ \page_margin_blocks_reshape
+ \setbox\b_page_margin_blocks_prepared\vbox
+ {\marginblockparameter\c!before
+ \box\b_page_margin_blocks_prepared
+ \marginblockparameter\c!after}}
+
+% inner outer
+
+\setvalue{\??marginblocklocation\v!left }{\page_margin_blocks_set_l_box}
+\setvalue{\??marginblocklocation\v!right }{\page_margin_blocks_set_r_box}
+\setvalue{\??marginblocklocation\v!inmargin}{\doifbothsidesoverruled
+ \page_margin_blocks_set_r_box
+ \page_margin_blocks_set_r_box
+ \page_margin_blocks_set_l_box}
+\setvalue{\??marginblocklocation\v!middle }{\doifbothsidesoverruled
+ \page_margin_blocks_set_r_box
+ \page_margin_blocks_set_l_box
+ \page_margin_blocks_set_r_box}
+
+\permanent\protected\def\page_margin_blocks_place_r_yes
+ {\setbox\b_page_margin_blocks_prepared\hbox to \rightmarginwidth
+ {\marginblockparameter\c!left
+ \box\b_page_margin_blocks_prepared
+ \marginblockparameter\c!right}%
+ \vsmashbox\b_page_margin_blocks_prepared
+ \box\b_page_margin_blocks_prepared}
+
+\permanent\protected\def\page_margin_blocks_place_l_yes
+ {\setbox\b_page_margin_blocks_prepared\hbox to \leftmarginwidth
+ {\marginblockparameter\c!right
+ \box\b_page_margin_blocks_prepared
+ \marginblockparameter\c!left}%
+ \vsmashbox\b_page_margin_blocks_prepared
+ \box\b_page_margin_blocks_prepared}
+
+\permanent\protected\def\page_margin_blocks_place_r_nop{\hskip\rightmarginwidth}
+\permanent\protected\def\page_margin_blocks_place_l_nop{\hskip\leftmarginwidth}
+
+\aliased\let\placerightmarginblock\page_margin_blocks_place_r_nop
+\aliased\let\placeleftmarginblock \page_margin_blocks_place_l_nop
+
+\def\page_margin_blocks_set_r_box{\enforced\aliased\let\placerightmarginblock\page_margin_blocks_place_r_yes}
+\def\page_margin_blocks_set_l_box{\enforced\aliased\let\placeleftmarginblock \page_margin_blocks_place_l_yes}
+
+% margin floats (keyword 'margin' in option list)
+
+\protected\def\page_margin_blocks_process_float
+ {\doifelse{\marginblockparameter\c!state}\v!start
+ \page_margin_blocks_process_float_yes
+ \page_margin_blocks_process_float_nop}
+
+\def\page_margin_blocks_process_float_yes
+ {\global\setbox\b_page_margin_blocks\vbox
+ {\hsize\marginblockparameter\c!width
+ \unvcopy\b_page_margin_blocks
+ \ifvoid\b_page_margin_blocks\else
+ \expandafter\marginblockparameter\expandafter\c!inbetween
+ \fi
+ \box\floatbox
+ \filbreak}%
+ \ifdim\ht\b_page_margin_blocks>\textheight
+ % page_floats_report_saved % no saving done anyway
+ \else
+ \page_floats_report_total
+ \fi}
+
+\def\page_margin_blocks_process_float_nop
+ {\handlefloatmethod\v!here}
+
+\def\page_margin_blocks_float_before
+ {\ifconditional\c_page_margin_blocks_enabled
+ \doifinset\v!margin\floatlocation\endgraf
+ \fi}
+
+\def\page_margin_blocks_float_set_hsize
+ {\ifconditional\c_page_margin_blocks_enabled
+ \doifinset\v!margin\floatlocation{\hsize\rootmarginblockparameter\c!width}%
+ \fi}
+
+\appendtoks
+ \ifvoid\b_page_margin_blocks \else
+ \writestatus\m!layouts{beware: there are left-over margin floats!}%
+ \fi
+\to \everystoptext
+
+\permanent\protected\def\flushmarginblocks
+ {\page_otr_command_flush_margin_blocks}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-mcl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-mcl.mkxl
new file mode 100644
index 00000000000..d36e76d94f4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-mcl.mkxl
@@ -0,0 +1,542 @@
+%D \module
+%D [ file=page-mcl,
+%D version=2020.07.26, % stripped down redone page-mul
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Multicolumns Limited,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Multicolumns Limited}
+
+\unprotect
+
+%D Columns are kind of hairy in \TEX\ and we would be better if no one needed them.
+%D Anyway, we do need some support and no mechanism can serve all. The original
+%D multicolumn mechanism of \MKII\ was never fully adapted to \MKIV, where mixed
+%D columns and page columns showed up instead. However, for some cases a dumb
+%D mechanism makes sense, so again we introduce multi columns, but without float
+%D hacks. This one will be optimized for mixed usage as for instance itemize needs.
+%D Their main advantage is that they can better deal with notes, which are hairy in
+%D themselves. This will (for now) only happen in \LMTX.
+%D
+%D The code is stripped down ancient \MKII\ code and might become cleaner as it
+%D evolves. Macros keep similar names but take a different namespace. The code is
+%D not yet perfect wrt spacing!
+
+% \enableexperiments[itemize.columns]
+%
+% \starttext
+%
+% \startmulticolumns
+% \dorecurse{10}{\samplefile{ward}\footnote{note #1}\par}
+% \stopmulticolumns
+%
+% \dorecurse{4}{\samplefile{ward}\par}
+%
+% \startitemize[packed,columns,two]
+% \dorecurse{100}{\startitem test #1 \footnote{this is a footnote #1}\stopitem}
+% \stopitemize
+%
+% \stoptext
+
+\ifdefined \startmulticolumns
+
+ % we're testing and don't want to remake the format
+
+\else
+
+\installcorenamespace {multicolumns}
+
+\installframedcommandhandler \??multicolumns {multicolumns} \??multicolumns
+
+\newdimen \d_page_mcl_available_width
+\newdimen \d_page_mcl_distance
+\newdimen \d_page_mcl_leftskip
+\newdimen \d_page_mcl_rightskip
+\newdimen \d_page_mcl_used_width
+\newdimen \d_page_mcl_temp
+\newdimen \d_page_mcl_saved_pagetotal % brrr, still needed ?
+
+\newcount \c_page_mcl_balance_minimum
+\newcount \c_page_mcl_n_of_lines
+
+\newbox \b_page_mcl_preceding
+\newdimen \d_page_mcl_preceding_height
+\newconditional\c_page_mcl_preceding_present
+
+\newbox \b_page_mcl_rest_of_page
+\newbox \b_page_mcl_page
+
+\newconditional\c_page_mcl_reverse
+\newconditional\c_page_mcl_balance
+
+\newconstant \c_page_mcl_routine
+
+\setnewconstant\c_page_mcl_routine_regular \zerocount
+\setnewconstant\c_page_mcl_routine_intercept \plusone
+\setnewconstant\c_page_mcl_routine_continue \plustwo
+\setnewconstant\c_page_mcl_routine_balance \plusthree
+\setnewconstant\c_page_mcl_routine_error \plusfour
+
+\newbox \b_page_mcl_balance_content
+\newconstant \c_page_mcl_balance_tries_max
+\newcount \c_page_mcl_balance_tries
+\newdimen \d_page_mcl_balance_target
+\newdimen \d_page_mcl_balance_natural_height
+\newdimen \d_page_mcl_balance_step
+\newconditional\c_page_mcl_balance_possible
+
+\c_page_mcl_balance_tries_max 250 % 100 is too small when floats are involved
+
+%def\m_page_mcl_overshoot_ratio{\ifgridsnapping0\else.5\fi}
+\def\m_page_mcl_overshoot_ratio{.5}
+
+\fi
+
+\protected\def\page_mcl_command_set_hsize
+ {\d_page_mcl_available_width\dimexpr
+ \makeupwidth
+ -\d_page_mcl_leftskip
+ -\d_page_mcl_rightskip
+ -\nofcolumns\d_page_mcl_distance
+ +\d_page_mcl_distance
+ \relax
+ \d_page_mcl_used_width\dimexpr
+ \d_page_mcl_available_width/\nofcolumns
+ \relax
+ \textwidth\d_page_mcl_used_width
+ \hsize\d_page_mcl_used_width}
+
+\protected\def\page_mcl_set_n_of_lines#1%
+ {\d_page_mcl_temp\dimexpr
+ +\textheight
+ \ifdim\d_page_mcl_preceding_height>\zeropoint -\d_page_mcl_preceding_height \fi
+% \ifdim\ht\b_page_mcl_preceding>\zeropoint -\ht\b_page_mcl_preceding \fi
+ -#1%
+ \relax
+ \getnoflines\d_page_mcl_temp
+ \ifnum\layoutlines>\zerocount \ifnum\noflines>\layoutlines
+ \noflines\layoutlines
+ \fi \fi
+ \c_page_mcl_n_of_lines\noflines}
+
+% \protected\def\page_mcl_command_set_vsize
+% {%%\page_one_command_set_vsize % indeed?
+% \page_mcl_set_n_of_lines\zeropoint
+% \d_page_mcl_temp\nofcolumns\dimexpr
+% \c_page_mcl_n_of_lines\openlineheight
+% % +\m_page_mcl_overshoot_ratio\openlineheight % collect enough data
+% \relax
+% \global\vsize \d_page_mcl_temp
+% \global\pagegoal\d_page_mcl_temp} % let's do it only here, reports maxdimen anyway
+
+\protected\def\page_mcl_command_set_vsize
+ {\global\vsize\dimexpr\nofcolumns\textheight+\nofcolumns\lineheight\relax
+ \pagegoal\dimexpr
+ \vsize
+% -\d_page_floats_inserted_top % needs checking
+% -\d_page_floats_inserted_bottom % needs checking
+ -\c_page_mix_n_of_columns\insertheights
+ \relax}
+
+\protected\def\page_mcl_command_routine
+ {\ifcase\c_page_mcl_routine
+ \page_one_command_routine
+ \or
+ \page_mcl_routine_intercept
+ \or
+ \page_mcl_routine_continue
+ \or
+ \page_mcl_routine_balance
+ \or
+ \page_mcl_routine_error
+ \fi}
+
+\let\page_mcl_command_package_contents\page_one_command_package_contents
+
+\def\page_mcl_routine_intercept
+ {\global\setbox\b_page_mcl_preceding\vbox
+ {\page_otr_command_flush_top_insertions
+ \unvbox\normalpagebox}}
+
+\def\page_mcl_routine_error
+ {\showmessage\m!columns3\empty
+ \page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
+
+\protected\def\page_mcl_initialize_variables
+ {\reseteverypar % maybe still freeze ....
+ \dontcomplain
+ \settopskip
+ \setmaxdepth
+ \topskip 1\topskip
+ \splittopskip \topskip
+ \splitmaxdepth \maxdepth
+ \boxmaxdepth \maxdepth % dangerous
+ \emergencystretch\zeropoint
+ \relax}
+
+\def\page_mcl_flush_preceding_normal
+ {\unvbox\b_page_mcl_preceding}
+
+\def\page_mcl_flush_preceding_ongrid
+ {\scratchdimen\dimexpr
+ \d_page_mcl_saved_pagetotal
+ -\d_page_mcl_preceding_height
+ -\topskip
+ \relax
+ \box\b_page_mcl_preceding
+ \kern\scratchdimen}
+
+\def\page_mcl_flush_packaged_columns_continued
+ {\page_mcl_flush_packaged_columns_indeed
+ \box\b_page_mcl_page}
+
+\def\page_mcl_flush_packaged_columns_balanced
+ {\bgroup
+ \page_mcl_flush_packaged_columns_indeed
+ \getnoflines{\htdp\b_page_mcl_page}%
+ \ht\b_page_mcl_page\dimexpr
+ \noflines\openlineheight
+ -\openstrutdepth
+ \ifgridsnapping
+ % quick hack (at least it works with itemize)
+ \else
+ -\openlineheight
+ +\topskip
+ \fi
+ \relax
+ \dp\b_page_mcl_page\openstrutdepth
+ \box\b_page_mcl_page
+ \egroup}
+
+\def\page_mcl_synchronize_marks
+ {\dohandleallcolumns{\page_marks_synchronize_column\plusone\nofcolumns\mofcolumns\currentcolumnbox}}
+
+\def\page_mcl_flush_packaged_columns_indeed
+ {\ifvoid\b_page_mcl_preceding
+ \setfalse\c_page_mcl_preceding_present % will be set elsewhere
+ \else
+ \settrue\c_page_mcl_preceding_present
+ \page_apply_postprocessors_box\b_page_mcl_preceding
+ \fi
+ \forgetall
+ \page_mcl_initialize_variables
+ \page_mcl_synchronize_marks
+ \setbox\b_page_mcl_page\vpack
+ {\ifconditional\c_page_mcl_reverse\reversehpack\else\naturalhpack\fi to \makeupwidth
+ {\hskip\ifconditional\c_page_mcl_reverse\d_page_mcl_rightskip\else\d_page_mcl_leftskip\fi\relax
+ \dohandleallcolumns
+ {\wd\currentcolumnbox\d_page_mcl_used_width
+ \setbox\scratchbox\hpack{\strut\box\currentcolumnbox}% hm, why strut
+ \anch_mark_column_box\scratchbox\currentcolumn
+ \box\scratchbox
+ \hfil}%
+ \unskip
+ \hskip\ifconditional\c_page_mcl_reverse\d_page_mcl_leftskip\else\d_page_mcl_rightskip\fi}}%
+ \ifconditional\c_page_mcl_preceding_present
+ \settrue\c_page_mcl_preceding_present
+ \ifgridsnapping
+ \page_mcl_flush_preceding_ongrid % obey grid settings, force on grid
+ \else
+ \page_mcl_flush_preceding_normal % ignore grid settings, not on grid
+ \fi
+ \fi
+ \global\d_page_mcl_preceding_height\zeropoint
+ \page_otr_command_set_vsize
+ \dosomebreak\nobreak % hm, only needed when topstuff
+ \ifgridsnapping \else
+ \ifconditional\c_page_mcl_preceding_present
+ \nointerlineskip
+ \vskip\dimexpr\openstrutheight-\topskip\relax
+ \fi
+ \fi
+ \prevdepth\openstrutdepth
+ \nointerlineskip
+ \dp\b_page_mcl_page\zeropoint}
+
+\def\page_mcl_split_column#1#2% copy or box
+ {\global\setbox\currentcolumnbox\vsplit#1 upto #2}
+
+\def\page_mcl_routine_continue
+ {\bgroup
+ \forgetall
+ \page_mcl_initialize_variables
+ \settotalinsertionheight
+ \page_mcl_set_n_of_lines\totalinsertionheight
+ \d_page_mcl_balance_target\c_page_mcl_n_of_lines\openlineheight
+ \dohandleallcolumns{\page_mcl_split_column\normalpagebox\d_page_mcl_balance_target}%
+ \setbox\b_page_mcl_rest_of_page\vpack{\unvbox\normalpagebox}%
+ \dohandleallcolumns
+ {\global\setbox\currentcolumnbox\vpack to \d_page_mcl_balance_target
+ {\unvbox\currentcolumnbox % wel of niet \unvbox ?
+ \vfill}}%
+ \setbox\b_page_mcl_preceding\vpack{\page_mcl_flush_packaged_columns_continued}%
+ \page_otr_construct_and_shipout\box\b_page_mcl_preceding\zerocount % three arguments
+ \page_otr_command_set_hsize
+ \page_otr_command_set_vsize
+ \unvbox\b_page_mcl_rest_of_page
+ \egroup}
+
+\def\page_mcl_routine_balance
+ {\bgroup
+ % why no \forgetall here
+ \page_mcl_initialize_variables
+ \widowpenalty\zerocount
+ \setbox\b_page_mcl_balance_content\vpack{\unvbox\normalpagebox}%
+ \ifdim\ht\b_page_mcl_balance_content>\openlineheight % at least one line
+ \ifnum\c_page_mcl_balance_minimum<\plustwo % balance anyway
+ \settrue\c_page_mcl_balance_possible
+ \else % check criterium to available lines
+ \getnoflines{\ht\b_page_mcl_balance_content}%
+ \divide\noflines \nofcolumns \relax
+ \ifnum\noflines<\c_page_mcl_balance_minimum \relax
+ \ifdim\dimexpr\ht\b_page_mcl_balance_content+\openlineheight\relax>\makeupheight
+ \settrue\c_page_mcl_balance_possible % column exceeding text height
+ \else
+ \setfalse\c_page_mcl_balance_possible % it seems to fit
+ \fi
+ \else
+ \settrue\c_page_mcl_balance_possible % balance indeed
+ \fi
+ \fi
+ \else
+ \setfalse\c_page_mcl_balance_possible % balancing does not make sense
+ \fi
+ \ifconditional\c_page_mcl_balance_possible % start balancing, was: \ifdim\ht\b_page_mcl_balance_content>\openlineheight
+ \page_mcl_balance_try_one
+ \page_mcl_balance_try_two
+ \else
+ % a one liner is not properly handled here, so best rewrite the text then
+ \showmessage\m!columns{10}\empty
+ \global\setbox\firstcolumnbox\vpack{\unvbox\b_page_mcl_balance_content}%
+ \fi
+ \c_page_mcl_routine\c_page_mcl_routine_error
+ %\baselinebottom % forces depth in separation rule
+ \page_mcl_flush_packaged_columns_balanced
+ %\allowbreak
+ \egroup}
+
+% \showmakeup
+
+\def\page_mcl_balance_try_one
+ {\d_page_mcl_balance_target\dimexpr\ht\b_page_mcl_balance_content+\topskip-\baselineskip\relax
+ \divide\d_page_mcl_balance_target \nofcolumns
+ \vbadness\plustenthousand
+ \c_page_mcl_balance_tries\zerocount
+ \bgroup
+ \ifgridsnapping
+ \d_page_mcl_balance_step\lineheight
+ \else
+ \d_page_mcl_balance_step\spacingfactor\onepoint % rubish
+ \fi
+ \doloop\page_mcl_balance_try_one_attempt
+ \global\setbox\b_page_mcl_rest_of_page\box\voidbox
+ \ifnum\c_page_mcl_balance_tries>\c_page_mcl_balance_tries_max\relax
+ \showmessage\m!columns7\empty
+ \else
+ \showmessage\m!columns8{\the\c_page_mcl_balance_tries}%
+ \fi
+ \egroup}
+
+\def\page_mcl_balance_try_one_attempt
+ {\advance\c_page_mcl_balance_tries \plusone
+ \global\setbox\b_page_mcl_rest_of_page\copy\b_page_mcl_balance_content\relax
+ \dohandleallcolumns{\page_mcl_split_column\b_page_mcl_rest_of_page\d_page_mcl_balance_target}%
+ \d_page_mcl_balance_natural_height\zeropoint
+ \dohandleallcolumns\page_mcl_balance_try_one_attempt_step
+\advance\d_page_mcl_balance_natural_height-33\scaledpoint % some slack
+ \ifnum\c_page_mcl_balance_tries>\c_page_mcl_balance_tries_max\relax
+ \exitloop
+ \orelse\ifdim\ht\b_page_mcl_rest_of_page>\zeropoint
+ \advance\d_page_mcl_balance_target\d_page_mcl_balance_step\relax
+ \orelse\ifdim\d_page_mcl_balance_natural_height>\ht\firstcolumnbox\relax
+ \advance\d_page_mcl_balance_target\d_page_mcl_balance_step\relax
+ \else
+ \exitloop
+ \fi}
+
+\def\page_mcl_balance_try_one_attempt_step
+ {\ifcase\currentcolumn\or\else
+ \ifdim\ht\currentcolumnbox>\d_page_mcl_balance_natural_height\relax
+ \d_page_mcl_balance_natural_height\ht\currentcolumnbox
+ \fi
+ \fi}
+
+\def\page_mcl_balance_try_two % hm ... can probably go
+ {\dohandleallcolumnscs\page_mcl_balance_try_two_step}
+
+% \def\page_mcl_balance_try_two_step
+% {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox
+% {\box\currentcolumnbox
+% \vfill}}
+
+\def\page_mcl_balance_try_two_step
+ {%\global\setbox\currentcolumnbox\box\currentcolumnbox
+ \ht\currentcolumnbox\ht\firstcolumnbox}
+
+\permanent\tolerant\protected\def\startmulticolumns[#1]%
+ {\bgroup
+ \ifinsidecolumns
+ \page_mcl_start_nop
+ \else
+ \setupmulticolumns[#1]%
+ \nofcolumns\multicolumnsparameter\c!n\relax
+ \ifnum\nofcolumns>\plusone
+ \page_mcl_start_yes
+ \nofmulticolumns\nofcolumns
+ \else
+ \page_mcl_start_nop
+ \fi
+ \fi}
+
+\aliased\let\stopmulticolumns\relax
+
+\def\page_mcl_start_nop
+ {\enforced\let\stopmulticolumns\page_mcl_stop_nop}
+
+\permanent\protected\def\page_mcl_stop_nop
+ {\egroup}
+
+\protected\def\page_mcl_start_yes
+ {\whitespace
+ \begingroup
+ \enforced\let\stopmulticolumns\page_mcl_stop_indeed
+ \global\insidecolumnstrue
+ \global\insidemulticolumnstrue
+ %
+ \d_page_mcl_distance\multicolumnsparameter\c!distance\relax
+ %
+ \edef\p_direction{\multicolumnsparameter\c!direction}%
+ \ifx\p_direction\v!right
+ \setfalse\c_page_mcl_reverse
+ \else
+ \settrue\c_page_mcl_reverse
+ \fi
+ %
+ \edef\p_balance{\multicolumnsparameter\c!balance}%
+ \ifx\p_balance\v!yes
+ \settrue\c_page_mcl_balance
+ \else
+ \setfalse\c_page_mcl_balance
+ \fi
+ %
+ \usealignparameter\multicolumnsparameter
+ %
+ \edef\p_blank{\multicolumnsparameter\c!blank}%
+ \ifempty\p_blank \else
+ \setupblank[\p_blank]%
+ \fi
+ %
+ \ifdim\s_spac_whitespace_parskip>\zeropoint\relax
+ \setupwhitespace[\p_blank]%
+ \fi
+ \c_page_mcl_balance_minimum\multicolumnsparameter\c!ntop\relax
+ %
+ \begingroup
+ %
+ \d_page_mcl_leftskip \leftskip
+ \d_page_mcl_rightskip\rightskip
+ \leftskip \zeropoint
+ \rightskip \zeropoint
+ \hangafter \zerocount
+ \hangindent \zeropoint
+ %
+ \widowpenalty \zerocount % will become option
+ \clubpenalty \zerocount % will become option
+ %
+ \ifdim\dimexpr\pagetotal+\parskip+\openlineheight\relax<\pagegoal
+ \allowbreak
+ \else
+ \break % sometimes fails
+ \fi
+ \appendtoks
+ \topskip1\topskip % best a switch
+ \to \everybodyfont
+ \the\everybodyfont % ugly here
+ \saveinterlinespace % ugly here
+ %
+ \initializecolumns\nofcolumns
+ %
+ \reseteverypar % todo
+ %
+ \ifdim\pagetotal=\zeropoint \else
+ \verticalstrut
+ \vskip-\struttotal
+ \fi
+ \global\d_page_mcl_saved_pagetotal\pagetotal
+ \setupoutputroutine[\s!multicolumn]%
+ \c_page_mcl_routine\c_page_mcl_routine_intercept
+ \page_otr_trigger_output_routine
+ \global\d_page_mcl_preceding_height\ht\b_page_mcl_preceding
+ \c_page_mcl_routine\c_page_mcl_routine_continue
+ \page_otr_command_set_hsize
+ \page_otr_command_set_vsize}
+
+\permanent\protected\def\page_mcl_stop_indeed
+ {\relax
+ \synchronizeoutput
+ \par
+ \ifconditional\c_page_mcl_balance
+ \c_page_mcl_routine\c_page_mcl_routine_continue
+ \goodbreak
+ \c_page_mcl_routine\c_page_mcl_routine_balance
+ \else
+ \goodbreak
+ \fi
+ % still the multi column routine
+ \page_otr_trigger_output_routine % the prevdepth is important, try e.g. toclist in
+ \prevdepth\zeropoint % columns before some noncolumned text text
+ %
+ \c_page_mcl_routine\c_page_mcl_routine_regular
+ %
+ \ifvoid\b_page_mcl_preceding\else
+ \unvbox\b_page_mcl_preceding
+ \fi
+ \global\d_page_mcl_preceding_height\zeropoint
+ \endgroup % here
+ \nofcolumns\plusone
+ \nofmulticolumns\plusone
+ \page_otr_command_set_vsize
+ \dosomebreak\allowbreak
+ \page_floats_column_pop_saved
+ %
+ \global\insidemulticolumnsfalse
+ \global\insidecolumnsfalse
+ \endgroup
+ \egroup}%
+
+\setupmulticolumns
+ [\c!n=2,
+ \c!ntop=1,
+ \c!direction=\v!right,
+ \c!distance=1.5\bodyfontsize, % influenced by switching
+ \c!balance=\v!yes,
+ \c!align={\v!text,\v!tolerant},
+ \c!blank={\v!line,\v!fixed}]
+
+\defineoutputroutine
+ [\s!multicolumn]
+ [\s!page_otr_command_routine =\page_mcl_command_routine,
+ \s!page_otr_command_package_contents=\page_mcl_command_package_contents,
+ \s!page_otr_command_set_vsize =\page_mcl_command_set_vsize,
+ \s!page_otr_command_set_hsize =\page_mcl_command_set_hsize]
+
+\let\strc_itemgroups_start_columns_old\strc_itemgroups_start_columns
+\let\strc_itemgroups_stop_columns_old \strc_itemgroups_stop_columns
+
+\def\strc_itemgroups_start_columns_new{\startmulticolumns[\c!n=\itemgroupparameter\c!n]}
+\def\strc_itemgroups_stop_columns_new {\stopmulticolumns}
+
+\installtexexperiment
+ {itemize.columns}
+ {\let\strc_itemgroups_start_columns\strc_itemgroups_start_columns_new
+ \let\strc_itemgroups_stop_columns \strc_itemgroups_stop_columns_new}
+ {\let\strc_itemgroups_start_columns\strc_itemgroups_start_columns_old
+ \let\strc_itemgroups_stop_columns \strc_itemgroups_stop_columns_old}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-mix.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-mix.mkxl
new file mode 100644
index 00000000000..433d94ab6bb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-mix.mkxl
@@ -0,0 +1,1086 @@
+%D \module
+%D [ file=page-mix,
+%D version=2012.07.12,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Mixed Columns,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Mixed Columns}
+
+%D This is a very experimental module. Eventually it will replace the current
+%D multi column mechanism (that then will be an instance). The \LUA\ part of
+%D the interface will quite probably change so don't use that one directly
+%D (yet).
+
+% todo:
+%
+% consult note class
+% notes per page
+% notes in each column
+% notes in last column
+% notes local/global
+% top and bottom inserts
+% wide floats
+% move floats
+% offsets (inner ones, so we change the hsize ... needed with backgrounds
+% when no content we currently loose the page
+
+% luatex buglet:
+%
+% \ctxlua{tex.setbox("global",0,node.hpack(nodes.pool.glyph("a",font.current())))}\box0
+
+\registerctxluafile{page-mix}{}
+
+\unprotect
+
+%D The mixed output routine replaces the traditional multi column handler that
+%D started out in \MKII. One of the complications of a routine is that it needs
+%D to align nicely when mixed in a single column layout. Instead of using all
+%D kind of shift juggling in this mechanism we simply switch to grid mode
+%D locally. After all, columns don't look nice when not on a. As the grid
+%D snapper in \MKIV\ is more advanced not that much extra code is needed.
+
+%D We use the command handler but the parent settings are not to be changed.
+%D Instead we could have used a dedicated root setup, but it's not worth the
+%D trouble.
+
+\installcorenamespace{mixedcolumns}
+
+\installframedcommandhandler \??mixedcolumns {mixedcolumns} \??mixedcolumns
+
+% old multicolumns mechanism
+%
+% \c!ntop=1,
+% \c!rule=\v!off, : now separator=rule
+% \c!height=,
+% \c!blank={\v!line,\v!fixed},
+% \c!rulethickness=\linewidth,
+% \c!offset=.5\bodyfontsize,
+
+\setupmixedcolumns
+ [\c!distance=1.5\bodyfontsize,
+ \c!n=\plustwo,
+ %\c!align=, % inherit (also replaces tolerance)
+ %\c!before=,
+ %\c!after=,
+ %\c!separator=\v!none,
+ %\c!setups=,
+ %\c!balance=\v!no,
+ %\c!blank={\v!line,\v!fixed}, yes or no
+ \c!frame=\v!off,
+ \c!strut=\v!no,
+ \c!offset=\v!overlay,
+ \c!alternative=\v!local,
+ \c!maxheight=\textheight,
+ \c!maxwidth=\makeupwidth,
+ \c!grid=\v!tolerant,
+ \c!internalgrid=\v!line,
+ \c!step=.25\lineheight, % needs some experimenting
+ %\c!splitmethod=\v!fixed, % will be default
+ \c!direction=\v!normal, % new (also todo in the new columnsets)
+ \c!notes=\v!yes,
+ \c!define=\v!yes,
+ \c!method=\ifinner\s!box\else\s!otr\fi] % automatic as suggested by WS
+
+ \let\startmixedcolumns\relax % defined later
+\aliased\let\stopmixedcolumns \relax % defined later
+
+\appendtoks % could become an option
+ \ifcstok{\mixedcolumnsparameter\c!define}\v!yes
+ \frozen\instance\setuevalue{\e!start\currentmixedcolumns}{\startmixedcolumns[\currentmixedcolumns]}%
+ \frozen\instance\setuevalue{\e!stop \currentmixedcolumns}{\stopmixedcolumns}%
+ \fi
+\to \everydefinemixedcolumns
+
+%D In order to avoid a mixup we use quite some local registers.
+
+\newdimen \d_page_mix_column_width
+\newdimen \d_page_mix_max_height
+\newdimen \d_page_mix_max_width
+\newdimen \d_page_mix_distance
+\newcount \c_page_mix_n_of_columns
+\newdimen \d_page_mix_threshold
+\newdimen \d_page_mix_leftskip
+\newdimen \d_page_mix_rightskip
+
+\newdimen \d_page_mix_balance_step
+\setnewconstant\c_page_mix_balance_cycles 500
+
+\setnewconstant\c_page_mix_break_forced -123
+
+\newbox \b_page_mix_preceding
+\newdimen \d_page_mix_preceding_height
+
+\newbox \b_page_mix_collected
+
+\newconstant \c_page_mix_routine
+
+\setnewconstant\c_page_mix_routine_regular \zerocount
+\setnewconstant\c_page_mix_routine_intercept\plusone
+\setnewconstant\c_page_mix_routine_continue \plustwo
+\setnewconstant\c_page_mix_routine_balance \plusthree
+\setnewconstant\c_page_mix_routine_error \plusfour
+
+\newconditional\c_page_mix_process_notes
+\newconditional\c_page_mix_grid_snapping
+
+%D The main environment is called as follows:
+%D
+%D \starttyping
+%D \startmixedcolumns[instance][settings]
+%D \startmixedcolumns[instance]
+%D \startmixedcolumns[settings]
+%D \stoptyping
+%D
+%D However, best is not to use this one directly but define an instance and
+%D use that one.
+
+% % For the moment only on my machine:
+%
+% \definemixedcolumns
+% [\v!columns]
+%
+% \protected\def\setupcolumns
+% {\setupmixedcolumns[\v!columns]}
+
+%D In itemizations we also need columns, so let's define a apecial instance
+%D for them. These need to work well in situations like this:
+%D
+%D \starttyping
+%D \input zapf
+%D
+%D \startnarrower
+%D \startitemize[columns,two,packed][before=,after=]
+%D \dorecurse{10}{\startitem item #1 \stopitem}
+%D \stopitemize
+%D \stopnarrower
+%D
+%D \input zapf
+%D
+%D \startnarrower
+%D \startitemize[columns,two][before=,after=]
+%D \dorecurse{10}{\startitem item #1 \stopitem}
+%D \stopitemize
+%D \stopnarrower
+%D
+%D \input zapf
+%D
+%D \startnarrower
+%D \startitemize[columns,two]
+%D \dorecurse{10}{\startitem item #1 \stopitem}
+%D \stopitemize
+%D \stopnarrower
+%D
+%D \input zapf
+%D \stoptyping
+
+\ifdefined\s!itemgroupcolumns \else \def\s!itemgroupcolumns{itemgroupcolumns} \fi
+
+\definemixedcolumns
+ [\s!itemgroupcolumns]
+ [\c!n=\itemgroupparameter\c!n,
+ \c!direction=\itemgroupparameter\c!direction,
+ \c!separator=\v!none,
+ \c!splitmethod=\v!none,
+ \c!grid=\v!tolerant,
+ \c!internalgrid=\v!halfline, % new, we may still revert to \v!line
+ \c!balance=\v!yes,
+ \c!notes=\v!no] % kind of hidden
+
+% better
+
+\setupmixedcolumns
+ [\s!itemgroupcolumns]
+ [\c!splitmethod=\v!fixed,
+ \c!grid=\v!yes,
+ \c!internalgrid=\v!line]
+
+% even better:
+
+\setupitemgroup
+ [\c!grid=\v!tolerant:10] % 10 pct tolerance in columns snapping
+
+\setupmixedcolumns
+ [\s!itemgroupcolumns]
+ [\c!grid=\itemgroupparameter\c!grid]
+
+% the fast hooks:
+
+\protected\def\strc_itemgroups_start_columns
+ {\startmixedcolumns[\s!itemgroupcolumns]} % we could have a fast one
+
+\protected\def\strc_itemgroups_stop_columns
+ {\stopmixedcolumns}
+
+%D The mixed output routine can be in different states. First we need to intercept
+%D the already present content. This permits mixed single and multi column usage.
+%D Then we have the continuous routine, one that intercepts pages in sequence.
+%D Finally, when we finish the mixed columns mode, we can (optionally) balance the
+%D last page.
+
+\protected\def\page_mix_command_routine
+ {\ifcase\c_page_mix_routine
+ \page_one_command_routine
+ \or
+ \page_mix_routine_intercept
+ \or
+ \page_mix_routine_continue
+ \or
+ \page_mix_routine_balance
+ \or
+ \page_mix_routine_error
+ \fi}
+
+%D The interceptor is quite simple, at least for the moment.
+
+\def\page_mix_routine_intercept
+ {\ifdim\pagetotal>\pagegoal
+ % testcase: preceding-001 ... if we don't do this, text can disappear as
+ % preceding is overwritten ... needs to be figured out some day
+ \page_one_command_routine
+ \fi
+ \global\setbox\b_page_mix_preceding\vbox % pack ?
+ {\forgetall
+ \page_otr_command_flush_top_insertions
+ \ifdim\htdp\b_page_mix_preceding=\zeropoint \else
+ \writestatus\m!columns{preceding error}%
+ \unvbox\b_page_mix_preceding
+ \fi
+ \unvbox\normalpagebox}}
+
+%D The error routine is there but unlikely to be called. It is a left-over from
+%D the traditional routine that might come in handy some day.
+
+\def\page_mix_construct_and_shipout#1#2#3%
+ {\ifconditional\c_page_mix_grid_snapping\else\gridsnappingfalse\fi % maybe only for notes (bottom alignment)
+ \page_otr_construct_and_shipout#1#2#3%
+ \ifconditional\c_page_mix_grid_snapping \gridsnappingtrue \fi}
+
+
+\def\page_mix_routine_error
+ {\showmessage\m!columns3\empty
+ \page_mix_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
+
+%D Some settings (and actions) depend on the current output routine and setting the
+%D hsize and vsize is among them. The calculation of the hsize is done elsewhere.
+
+\protected\def\page_mix_command_set_hsize
+ {\hsize\d_page_mix_column_width
+ \columnwidth\d_page_mix_column_width}
+
+%D When setting the vsize we make sure that we collect a few more lines than needed
+%D so that we have enough to split over the columns. Collecting too much is somewhat
+%D tricky as they will spill over to the next page.
+
+\protected\def\page_mix_command_set_vsize
+ {\vsize\dimexpr\c_page_mix_n_of_columns\textheight+\c_page_mix_n_of_columns\lineheight\relax
+ \pagegoal\dimexpr
+ \vsize
+% -\d_page_floats_inserted_top % needs checking
+% -\d_page_floats_inserted_bottom % needs checking
+ -\c_page_mix_n_of_columns\insertheights
+ \relax}
+
+%D As we use \LUA\ there is the usual amount of tracing at that end. At the tex end
+%D we only visualize boxes.
+
+\let\page_mix_hbox\hbox
+\let\page_mix_vbox\vbox
+
+\installtextracker
+ {mixedcolumns.boxes}
+ {\let\page_mix_hbox\ruledhbox
+ \let\page_mix_vbox\ruledvbox}
+ {\let\page_mix_hbox\hbox
+ \let\page_mix_vbox\vbox}
+
+%D We provide a few column break options. Interesting is that while forcing a new
+%D column in the traditional mechanism was a pain, here it works quite well.
+
+\installcolumnbreakmethod \s!mixedcolumn \v!preference
+ {\goodbreak}
+
+\installcolumnbreakmethod \s!mixedcolumn \v!yes
+ {\par
+ \penalty\c_page_mix_break_forced\relax}
+
+%D As we operate in grid snapping mode, we use a dedicated macro to enable this
+%D mechamism.
+
+\def\page_mix_enable_grid_snapping
+ {\edef\p_grid{\mixedcolumnsparameter\c!grid}%
+ \setfalse\c_page_mix_grid_snapping
+ \ifempty\p_grid
+ % just follow the default grid settings
+ \else
+ \ifgridsnapping\settrue\c_page_mix_grid_snapping\fi
+ \gridsnappingtrue
+ \setsystemmode\v!grid
+ \spac_grids_snap_value_set\p_grid
+ \fi}
+
+%D Between columns there is normally just spacing unless one enforces a rule.
+%D
+%D \starttyping
+%D \input zapf
+%D
+%D \startnarrower
+%D \startmixedcolumns[n=2,background=color,backgroundcolor=red,rulethickness=1mm,rulecolor=green,separator=rule]
+%D \input zapf
+%D \stopmixedcolumns
+%D \stopnarrower
+%D
+%D \input zapf
+%D \stoptyping
+
+\installcorenamespace{mixedcolumnsseparator}
+
+\permanent\protected\def\installmixedcolumnseparator#1#2%
+ {\setvalue{\??mixedcolumnsseparator#1}{#2}}
+
+\installmixedcolumnseparator\v!rule
+ {\vrule
+ \s!width \mixedcolumnsparameter\c!rulethickness
+ \s!height\mixedcolumnseparatorheight
+ \s!depth \mixedcolumnseparatordepth
+ \relax}
+
+\protected\def\page_mix_command_inject_separator
+ {\begingroup
+ \setbox\scratchbox\hbox to \zeropoint \bgroup
+ \hss
+ \starttextproperties
+ \usemixedcolumnscolorparameter\c!rulecolor
+ \begincsname\??mixedcolumnsseparator\p_separator\endcsname % was \c!rule
+ \stoptextproperties
+ \hss
+ \egroup
+ \ht\scratchbox\zeropoint
+ \dp\scratchbox\zeropoint
+ \hss
+ \box\scratchbox
+ \hss
+ \endgroup}
+
+%D We've now arrived at the real code. The start command mostly sets up the
+%D environment and variables that are used in the splitter. One of the last
+%D things happening at the start is switching over to the mixed continuous
+%D routine.
+
+\installcorenamespace{mixedcolumnsbefore}
+\installcorenamespace{mixedcolumnsstart}
+\installcorenamespace{mixedcolumnsstop}
+\installcorenamespace{mixedcolumnsafter}
+
+%D For practical reasons there is always a first argument needed that
+%D indicates the class.
+%D
+%D \starttyping
+%D \startmixedcolumns[n=3,alternative=global]
+%D \dorecurse{200}{Zomaar wat #1 met een footnote\footnote{note #1}. }
+%D \stopmixedcolumns
+%D \stoptyping
+
+\mutable\let\currentmixedcolumnsmethod\empty
+
+\installmacrostack\currentmixedcolumns
+\installmacrostack\currentmixedcolumnsmethod
+
+\permanent\tolerant\protected\def\startmixedcolumns[#1]#*[#2]%
+ {\push_macro_currentmixedcolumns
+ \push_macro_currentmixedcolumnsmethod
+ \ifparameters
+ \expandafter\page_mix_start_columns_c
+ \or
+ \expandafter\page_mix_start_columns_b
+ \or
+ \expandafter\page_mix_start_columns_a
+ \fi[#1][#2]}
+
+\def\page_mix_start_columns_checked#1#2%
+ {\edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
+ \ifx\currentmixedcolumnsmethod\v!box
+ \expandafter#1%
+ \orelse\ifinsidecolumns
+ \expandafter#2%
+ \else
+ \expandafter#1%
+ \fi}
+
+\def\page_mix_start_columns_a[#1]% [#2]%
+ {\edef\currentmixedcolumns{#1}%
+ \page_mix_start_columns_checked
+ \page_mix_start_columns_a_yes
+ \page_mix_start_columns_a_nop}
+
+\def\page_mix_start_columns_a_yes[#1]%
+ {\mixedcolumnsparameter\c!before\relax
+ \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \setupcurrentmixedcolumns[#1]%
+ \page_mix_initialize_columns
+ \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname
+ \enforced\let\stopmixedcolumns\page_mix_columns_stop_yes}
+
+\def\page_mix_start_columns_a_nop[#1]%
+ {\begingroup
+ \enforced\let\stopmixedcolumns\page_mix_columns_stop_nop}
+
+\def\page_mix_start_columns_b[#1][#2]%
+ {\ifhastok={#1}%
+ \let\currentmixedcolumns\empty
+ \page_mix_error_b
+ \page_mix_start_columns_checked\page_mix_start_columns_b_yes\page_mix_start_columns_b_nop[#1]%
+ \else
+ \edef\currentmixedcolumns{#1}%
+ \page_mix_start_columns_checked\page_mix_start_columns_b_yes\page_mix_start_columns_b_nop[#2]%
+ \fi}
+
+\def\page_mix_start_columns_b_yes[#1]%
+ {\mixedcolumnsparameter\c!before\relax % so, it doesn't listen to local settings !
+ \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \setupcurrentmixedcolumns[#1]%
+ \page_mix_initialize_columns
+ \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname % no \relax
+ \enforced\let\stopmixedcolumns\page_mix_columns_stop_yes}
+
+\def\page_mix_start_columns_b_nop[#1]%
+ {\begingroup
+ \enforced\let\stopmixedcolumns\page_mix_columns_stop_nop}
+
+\def\page_mix_error_b
+ {\writestatus\m!columns{best use an instance of mixed columns}}
+
+\def\page_mix_start_columns_c[#1][#2]%
+ {\let\currentmixedcolumns\empty
+ \page_mix_start_columns_checked
+ \page_mix_start_columns_c_yes
+ \page_mix_start_columns_c_nop}
+
+\def\page_mix_start_columns_c_yes
+ {\mixedcolumnsparameter\c!before\relax
+ \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \page_mix_initialize_columns
+ \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname
+ \enforced\let\stopmixedcolumns\page_mix_columns_stop_yes}
+
+\def\page_mix_start_columns_c_nop
+ {\begingroup
+ \enforced\let\stopmixedcolumns\page_mix_columns_stop_nop}
+
+\protected\def\page_mix_fast_columns_start#1%
+ {\push_macro_currentmixedcolumns
+ \push_macro_currentmixedcolumnsmethod
+ \edef\currentmixedcolumns{#1}%
+ \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
+ \mixedcolumnsparameter\c!before\relax % so, it doesn't listen to local settings !
+ \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \page_mix_initialize_columns
+ \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname % no \relax
+ \let\page_mix_fast_columns_stop\page_mix_columns_stop_yes}
+
+%D When we stop, we switch over to the balancing routine. After we're done we
+%D make sure to set the sizes are set, a somewhat redundant action when we
+%D already have flushed but better be safe.
+
+\let\page_mix_fast_columns_stop\relax
+
+\newtoks\t_page_mix_at_the_end
+
+\def\page_mix_finalize_columns
+ {\ifconditional\c_page_mix_process_notes \else
+ \global\t_page_mix_at_the_end{\stoppostponingnotes}%
+ \fi}
+
+\protected\def\page_mix_columns_stop_yes
+ {\begincsname\??mixedcolumnsstop\currentmixedcolumnsmethod\endcsname % no \relax
+ \page_mix_finalize_columns
+ \endgroup
+ \begincsname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax
+ \mixedcolumnsparameter\c!after\relax
+ \pop_macro_currentmixedcolumnsmethod
+ \pop_macro_currentmixedcolumns
+ \the\t_page_mix_at_the_end\global\t_page_mix_at_the_end\emptytoks}
+
+\protected\def\page_mix_columns_stop_nop
+ {\page_mix_finalize_columns
+ \endgroup
+ \pop_macro_currentmixedcolumnsmethod
+ \pop_macro_currentmixedcolumns
+ \the\t_page_mix_at_the_end\global\t_page_mix_at_the_end\emptytoks}
+
+% \protected\def\page_mix_columns_stop_yes
+% {\begincsname\??mixedcolumnsstop \currentmixedcolumnsmethod\endcsname % no \relax
+% \endgroup
+% \begincsname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax
+% \mixedcolumnsparameter\c!after\relax
+% \ifx\currentmixedcolumnsmethod\s!otr
+% \pop_macro_currentmixedcolumnsmethod
+% \pop_macro_currentmixedcolumns
+% \synchronizeoutput % brrr, otherwise sometimes issues in itemize
+% \else
+% \pop_macro_currentmixedcolumnsmethod
+% \pop_macro_currentmixedcolumns
+% \fi
+% }
+
+%D This is how the fast one is used:
+
+\protected\def\strc_itemgroups_start_columns
+ {\page_mix_fast_columns_start\s!itemgroupcolumns}
+
+\protected\def\strc_itemgroups_stop_columns
+ {\page_mix_fast_columns_stop} % set by start
+
+% not used nor documented so commented:
+%
+% \setupmixedcolumns
+% [\s!itemgroupcolumns]
+% [\c!grid=\itemgroupparameter\c!grid]
+%
+% \setupitemgroup
+% [\c!grid=\v!yes] % we need a value
+
+% better
+
+%D The common initialization:
+
+\def\page_mix_initialize_columns
+ {\page_mix_enable_grid_snapping
+ %
+ \d_page_mix_distance \mixedcolumnsparameter\c!distance
+ \c_page_mix_n_of_columns\mixedcolumnsparameter\c!n
+ \d_page_mix_max_height \mixedcolumnsparameter\c!maxheight
+ \d_page_mix_max_width \mixedcolumnsparameter\c!maxwidth
+ \d_page_mix_balance_step\mixedcolumnsparameter\c!step
+ %
+ \d_page_mix_max_width\dimexpr\d_page_mix_max_width-\leftskip-\rightskip\relax
+ \d_page_mix_leftskip \leftskip
+ \d_page_mix_rightskip\rightskip
+ \leftskip \zeropoint
+ \rightskip\zeropoint
+ %
+ \doifelse{\mixedcolumnsparameter\c!notes}\v!yes\settrue\setfalse\c_page_mix_process_notes
+ \ifconditional\c_page_mix_process_notes \else
+ \startpostponingnotes
+ \fi
+ %
+ \d_page_mix_threshold\zeropoint
+ %
+ \d_page_mix_column_width\dimexpr(\d_page_mix_max_width-\d_page_mix_distance*\numexpr(\c_page_mix_n_of_columns-\plusone)\relax)/\c_page_mix_n_of_columns\relax
+ %
+ \columnwidth \d_page_mix_column_width
+ \columndistance\d_page_mix_distance
+ \nofcolumns \c_page_mix_n_of_columns
+ \textwidth \d_page_mix_column_width % kind of redundant but we had it so ...
+ %
+ \usemixedcolumnscolorparameter\c!color
+ %
+ \insidecolumnstrue % new
+ %
+ \usealignparameter \mixedcolumnsparameter
+ \useblankparameter \mixedcolumnsparameter
+ \useprofileparameter\mixedcolumnsparameter % new
+ %
+ \nofcolumns\c_page_mix_n_of_columns} % public
+
+%D The otr method related hooks are defined next:
+
+% \setvalue{\??mixedcolumnsbefore\s!otr}%
+% {\par
+% \ifdim\pagetotal=\zeropoint \else
+% \verticalstrut % probably no longer needed
+% \vskip-\struttotal % probably no longer needed
+% \fi}
+
+\newcount\c_page_mix_otr_nesting
+
+% \setvalue{\??mixedcolumnsbefore\s!otr}%
+% {\par
+% \global\advance\c_page_mix_otr_nesting\plusone
+% \ifcase\c_page_mix_otr_nesting\or
+% \ifdim\pagetotal=\zeropoint \else
+% \obeydepth % we could handle this in pre material
+% \fi
+% \fi}
+
+\setvalue{\??mixedcolumnsbefore\s!otr}%
+ {\par
+ \global\advance\c_page_mix_otr_nesting\plusone
+ \ifcase\c_page_mix_otr_nesting\or
+ \ifdim\pagetotal=\zeropoint \else
+ % make sure that whitespace an dblanks are done
+ \strut
+ \vskip-\lineheight
+ % no, bad spacing: \obeydepth % we could handle this in pre material
+ \fi
+ \fi}
+
+\setvalue{\??mixedcolumnsstart\s!otr}%
+ {\ifcase\c_page_mix_otr_nesting\or
+ \scratchwidth\textwidth
+ \setupoutputroutine[\s!mixedcolumn]%
+ \c_page_mix_routine\c_page_mix_routine_intercept
+ \page_otr_trigger_output_routine
+ %
+ \holdinginserts\maxdimen
+ %
+ \ifvoid\b_page_mix_preceding \else
+ % moved here, before the packaging
+ \page_postprocessors_linenumbers_deepbox\b_page_mix_preceding
+ % we need to avoid unvboxing with successive balanced on one page
+ \global\setbox\b_page_mix_preceding\vpack{\box\b_page_mix_preceding}%
+ \wd\b_page_mix_preceding\scratchwidth % \makeupwidth
+ \page_grids_add_to_one\b_page_mix_preceding
+ \fi
+ \global\d_page_mix_preceding_height\ht\b_page_mix_preceding
+ \c_page_mix_routine\c_page_mix_routine_continue
+ %
+ \page_mix_command_set_vsize
+ \page_mix_command_set_hsize
+ \fi
+ \usealignparameter\mixedcolumnsparameter
+ \usesetupsparameter\mixedcolumnsparameter}
+
+% \setvalue{\??mixedcolumnsstop\s!otr}%
+% {\par
+% \ifcase\c_page_mix_otr_nesting\or
+% \c_page_mix_routine\c_page_mix_routine_balance
+% \page_otr_trigger_output_routine
+% \fi}
+
+\setvalue{\??mixedcolumnsstop\s!otr}%
+ {\par
+ \ifcase\c_page_mix_otr_nesting\or
+ \doifelse{\mixedcolumnsparameter\c!balance}\v!yes
+ {\c_page_mix_routine\c_page_mix_routine_balance}%
+ {\penalty-\plustenthousand}% weird hack, we need to trigger the otr sometimes (new per 20140306, see balancing-001.tex)
+ \page_otr_trigger_output_routine
+ \ifvoid\b_page_mix_preceding \else
+ % empty columns so we need to make sure pending content is flushed
+ \unvbox\b_page_mix_preceding % new per 2014.10.25
+ \fi
+ \fi}
+
+\setvalue{\??mixedcolumnsafter\s!otr}%
+ {\ifcase\c_page_mix_otr_nesting\or
+ \prevdepth\strutdp
+ \page_otr_command_set_vsize
+ \page_otr_command_set_hsize
+ \fi
+ \global\advance\c_page_mix_otr_nesting\minusone}
+
+%D The splitting and therefore balancing is done at the \LUA\ end. This gives
+%D more readable code and also makes it easier to deal with insertions like
+%D footnotes. Eventually we will have multiple strategies available.
+
+\protected\def\page_mix_routine_construct#1%
+ {\d_page_mix_max_height\mixedcolumnsparameter\c!maxheight % can have changed due to header=high
+ \ifconditional\c_page_mix_process_notes
+ \totalnoteheight\zeropoint
+ \else
+ \settotalinsertionheight
+ \fi
+ \clf_mixsetsplit
+ box \b_page_mix_collected
+ nofcolumns \c_page_mix_n_of_columns
+ maxheight \d_page_mix_max_height
+ noteheight \totalnoteheight
+ step \d_page_mix_balance_step
+ cycles \c_page_mix_balance_cycles
+ preheight \d_page_mix_preceding_height
+ prebox \b_page_mix_preceding
+ strutht \strutht
+ strutdp \strutdp
+ threshold \d_page_mix_threshold
+ splitmethod {\mixedcolumnsparameter\c!splitmethod}%
+ balance {#1}%
+ alternative {\mixedcolumnsparameter\c!alternative}%
+ internalgrid {\mixedcolumnsparameter\c!internalgrid}%
+ grid \ifgridsnapping tru\else fals\fi e %
+ notes \ifconditional\c_page_mix_process_notes tru\else fals\fi e %
+ \relax
+ \deadcycles\zerocount}
+
+\newdimen\mixedcolumnseparatorheight
+\newdimen\mixedcolumnseparatordepth
+\newdimen\mixedcolumnseparatorwidth
+
+\def\page_mix_routine_package_step
+ {% needs packaging anyway
+ \setbox\scratchbox\page_mix_command_package_column
+ \page_lines_add_numbers_to_box\scratchbox\recurselevel\c_page_mix_n_of_columns\plusone % new
+ \page_marks_synchronize_column\plusone\c_page_mix_n_of_columns\recurselevel\scratchbox
+ % backgrounds
+ \anch_mark_column_box\scratchbox\recurselevel
+ % for the moment a quick and dirty patch .. we need to go into the box (hence the \plusone) .. a slowdowner
+ % moved to start: \page_lines_add_numbers_to_box\scratchbox\recurselevel\c_page_mix_n_of_columns\plusone
+ % the framed needs a reset of strut, align, setups etc
+ \mixedcolumnseparatorheight\ht\scratchbox
+ \mixedcolumnseparatordepth \dp\scratchbox
+ \inheritedmixedcolumnsframedbox\currentmixedcolumns\scratchbox}
+
+\def\page_mix_routine_package_separate
+ {\ifcsname\??mixedcolumnsseparator\p_separator\endcsname
+ \page_mix_command_inject_separator
+ \else
+ \hss
+ \fi}
+
+\protected\def\page_mix_routine_package
+ {\clf_mixfinalize
+ \setbox\b_page_mix_collected\vbox \bgroup
+ \ifvoid\b_page_mix_preceding \else
+ % \page_postprocessors_linenumbers_deepbox\b_page_mix_preceding % already done
+ \vpack\bgroup
+ \box\b_page_mix_preceding
+ \egroup
+ \global\d_page_mix_preceding_height\zeropoint
+ \nointerlineskip
+ % no no:
+ % \prevdepth\strutdepth
+ \fi
+ \hskip\d_page_mix_leftskip
+ \page_mix_hbox to \d_page_mix_max_width \bgroup
+ \edef\p_separator{\mixedcolumnsparameter\c!separator}%
+ \mixedcolumnseparatorwidth\d_page_mix_distance % \mixedcolumnsparameter\c!rulethickness\relax
+ \edef\p_direction{\mixedcolumnsparameter\c!direction}%
+ \ifx\p_direction\v!reverse
+ \dostepwiserecurse\c_page_mix_n_of_columns\plusone\minusone
+ {\page_mix_routine_package_step
+ \ifnum\recurselevel>\plusone
+ \page_mix_routine_package_separate
+ \fi}%
+ \else
+ \dorecurse\c_page_mix_n_of_columns
+ {\page_mix_routine_package_step
+ \ifnum\recurselevel<\c_page_mix_n_of_columns
+ \page_mix_routine_package_separate
+ \fi}%
+ \fi
+ \egroup
+ \hskip\d_page_mix_rightskip
+ \egroup
+ \wd\b_page_mix_collected\dimexpr
+ \d_page_mix_max_width
+ +\d_page_mix_rightskip
+ +\d_page_mix_leftskip
+ \relax }
+
+\protected\def\page_mix_command_package_column
+ {\page_mix_hbox to \d_page_mix_column_width \bgroup
+ % maybe intercept empty
+ \clf_mixgetsplit\recurselevel\relax
+ \hskip-\d_page_mix_column_width
+ \vbox \bgroup
+ \hsize\d_page_mix_column_width
+ \ifconditional\c_page_mix_process_notes
+ \placenoteinserts
+ \fi
+ \egroup
+ \hss
+ \egroup}
+
+% \protected\def\page_mix_command_package_column
+% {\page_mix_hbox to \d_page_mix_column_width \bgroup
+% % maybe intercept empty
+% \ruledhpack\bgroup
+% \clf_mixgetsplit\recurselevel\relax
+% \egroup
+% \hskip-\d_page_mix_column_width
+% \ruledhpack \bgroup
+% \hsize\d_page_mix_column_width
+% \ifconditional\c_page_mix_process_notes
+% \placenoteinserts
+% \fi
+% \egroup
+% \hss
+% \egroup}
+
+\protected\def\page_mix_routine_continue
+ {\bgroup
+ \forgetall
+ \dontcomplain
+ \setbox\b_page_mix_collected\vpack{\unvbox\normalpagebox}% brrr we need to make a tight box (combine this in lua)
+ \page_mix_routine_construct\v!no
+ \page_mix_routine_package
+ \page_mix_construct_and_shipout\box\b_page_mix_collected\zerocount % three arguments
+ \clf_mixflushrest
+ \clf_mixcleanup
+ \egroup}
+
+\protected\def\page_mix_routine_balance
+ {\bgroup
+ \forgetall
+ \dontcomplain
+ \setbox\b_page_mix_collected\vpack{\unvbox\normalpagebox}% brrr we need to make a tight box (combine this in lua)
+ \doloop
+ {%writestatus\m!columns{construct continue (\the\htdp\b_page_mix_collected)}%
+ \page_mix_routine_construct\v!no
+ \ifcase\clf_mixstate\relax
+ % 0 = okay, we can balance
+ \setbox\b_page_mix_collected\vpack{\clf_mixflushlist}% we could avoid this
+ %writestatus\m!columns{construct balance}%
+ \page_mix_routine_construct\v!yes
+ \page_mix_routine_package
+ \c_page_mix_routine\c_page_mix_routine_regular
+ % \setupoutputroutine[\s!singlecolumn]%
+ \page_otr_command_set_vsize
+ \page_otr_command_set_hsize
+ \par
+ %writestatus\m!columns{flush balance}%
+ \page_grids_add_to_mix\b_page_mix_collected % no linenumbers here
+ \box\b_page_mix_collected
+ \vskip\zeropoint % triggers recalculation of page stuff (weird that this is needed but it *is* needed, see mixed-001.tex)
+ \par
+ \nointerlineskip
+ \prevdepth\strutdp
+ \clf_mixflushrest% rubish
+ \clf_mixcleanup % rubish
+ \exitloop
+ \or
+ % 1 = we have stuff left, so flush and rebalance
+ %writestatus\m!columns{flush continue}%
+ \page_mix_routine_package
+ \page_mix_construct_and_shipout\box\b_page_mix_collected\zerocount % three arguments
+ \setbox\b_page_mix_collected\vpack{\clf_mixflushrest}% we could avoid this
+ \clf_mixcleanup
+ \ifdim\ht\b_page_mix_collected=\zeropoint
+ \exitloop
+ \fi
+ \fi}%
+ \egroup}
+
+%D We also implement a variant compatible with the so called simple columns
+%D mechanism:
+%D
+%D \starttyping
+%D \startboxedcolumns
+%D \input zapf
+%D \stopboxedcolumns
+%D \stoptyping
+%D
+%D This is a rather mininimalistic variant.
+
+% Maybe we also need a variant with obeydepth before and prevdepth after so
+% that we get a nice spacing.
+
+\definemixedcolumns
+ [boxedcolumns]
+ [\c!balance=\v!yes,
+ \c!n=2,
+ \c!method=\s!box,
+ \c!strut=\v!yes,
+ \c!maxwidth=\availablehsize]
+
+%D Boxed columns can be used nested:
+%D
+%D \starttyping
+%D \setupmixedcolumns
+%D [boxedcolumns]
+%D [n=2,
+%D background=color,
+%D backgroundcolor=darkred,
+%D color=white,
+%D backgroundoffset=1mm]
+%D
+%D \definemixedcolumns
+%D [nestedboxedcolumns]
+%D [boxedcolumns]
+%D [n=2,
+%D background=color,
+%D backgroundcolor=white,
+%D color=darkred,
+%D strut=yes,
+%D backgroundoffset=0mm]
+%D
+%D \startboxedcolumns
+%D \input zapf \par \input ward \par \obeydepth
+%D \startnestedboxedcolumns
+%D \input zapf
+%D \stopnestedboxedcolumns
+%D \par \input zapf \par \obeydepth
+%D \startnestedboxedcolumns
+%D \input zapf
+%D \stopnestedboxedcolumns
+%D \par \input zapf
+%D \stopboxedcolumns
+%D \stoptyping
+
+%D Next we define the hooks:
+
+\letvalue{\??mixedcolumnsbefore\s!box}\donothing
+\letvalue{\??mixedcolumnsafter \s!box}\donothing
+
+\setvalue{\??mixedcolumnsstart\s!box}%
+ {\edef\p_page_mix_strut{\mixedcolumnsparameter\c!strut}%
+ \setbox\b_page_mix_collected\vbox \bgroup
+ \let\currentoutputroutine\s!mixedcolumn % makes \column work
+ \forgetall
+ \usegridparameter\mixedcolumnsparameter
+ % \useprofileparameter\mixedcolumnsparameter
+ \page_mix_command_set_hsize
+ \ifx\p_page_mix_strut\v!yes
+ \begstrut
+ \ignorespaces
+ \fi}
+
+\setvalue{\??mixedcolumnsstop\s!box}%
+ {\ifx\p_page_mix_strut\v!yes
+ \removeunwantedspaces
+ \endstrut
+ \fi
+ \egroup
+ \edef\p_profile{\mixedcolumnsparameter\c!profile}%
+ \ifempty\p_profile \else
+ % this can never be ok because we cheat with depth and height
+ % and glue in between and when we're too large we run into issues
+ % so mayb best limit correction to one line
+ \profilegivenbox\p_profile\b_page_mix_collected
+ \setbox\b_page_mix_collected\vpack{\unvbox\b_page_mix_collected}%
+ % tracing
+ % \addprofiletobox\b_page_mix_collected
+ \fi
+ \page_mix_box_balance}
+
+%D The related balancer is only a few lines:
+
+\protected\def\page_mix_box_balance
+ {\bgroup
+ \dontcomplain
+ \page_mix_routine_construct\v!yes
+ \page_mix_routine_package
+ \dontleavehmode\box\b_page_mix_collected
+ \clf_mixflushrest
+ \clf_mixcleanup
+ \egroup}
+
+%D As usual, floats complicates matters and this is where experimental code
+%D starts.
+
+\let\page_mix_command_package_contents\page_one_command_package_contents
+\let\page_mix_command_flush_float_box \page_one_command_flush_float_box
+
+\protected\def\page_mix_command_check_if_float_fits
+ {\ifpostponecolumnfloats
+ \global\setfalse\c_page_floats_room
+ \orelse\ifconditional\c_page_floats_not_permitted
+ \global\setfalse\c_page_floats_room
+ \else
+% \bgroup
+% \getcolumnstatus{\count255}{\dimen0}{\dimen2}%
+% \page_floats_get_info\s!text
+% \setbox\scratchbox\vbox % tricky met objecten ?
+% {\blank[\rootfloatparameter\c!spacebefore]
+% \snaptogrid\vbox{\vskip\floatheight}}% copy?
+% \advance\dimen0\dimexpr\ht\scratchbox+2\openlineheight+.5\lineheight\relax\relax % needed because goal a bit higher
+% \ifdim\dimen0>\dimen2
+% \global\setfalse\c_page_floats_room
+% \else
+ \global\settrue\c_page_floats_room
+ \fi
+ \ifdim\floatwidth>\hsize
+ \showmessage\m!columns{11}\empty
+ \global\setfalse\c_page_floats_room
+ \fi}
+
+\protected\def\page_mix_command_flush_floats
+ {\page_one_command_flush_floats}
+
+\protected\def\page_mix_command_flush_saved_floats
+ {\page_one_command_flush_saved_floats}
+
+% \protected\def\page_mix_command_flush_top_insertions
+% {\page_one_command_flush_top_insertions}
+
+\protected\def\page_mix_place_float_top
+ {\showmessage\m!columns4\empty\page_one_place_float_here}
+
+\protected\def\page_mix_place_float_bottom
+ {\showmessage\m!columns5\empty\page_one_place_float_here}
+
+\protected\def\page_mix_place_float_here
+ {\page_one_place_float_here}
+
+\protected\def\page_mix_place_float_force
+ {\page_one_place_float_force}
+
+\protected\def\page_mix_command_side_float_output
+ {\page_mix_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
+
+\protected\def\page_mix_command_synchronize_side_floats
+ {\page_sides_forget_floats}
+
+\protected\def\page_mix_command_flush_side_floats
+ {\page_sides_forget_floats}
+
+\protected\def\page_mix_command_next_page
+ {\page_otr_eject_page}
+
+\protected\def\page_mix_command_next_page_and_inserts
+ {\page_otr_eject_page_and_flush_inserts}
+
+%D Moved here and dedicated:
+
+\tolerant\protected\def\page_mix_command_test_column[#1]#*[#2]% works on last column
+ {\par
+ \begingroup
+ \scratchdimen\dimexpr#1\lineheight\ifparameter#2\or+#2\fi\relax
+ \ifdim\scratchdimen>\zeropoint
+ \c_attr_checkedbreak\number\scratchdimen % why \number
+ \penalty\c_page_mix_break_forced\relax
+ \fi
+ \endgroup}
+
+%D We need to hook some handlers into the output routine and we define
+%D a dedicated one:
+
+\let\page_mix_command_flush_all_floats\page_one_command_flush_all_floats
+
+\defineoutputroutine
+ [\s!mixedcolumn]
+ [\s!page_otr_command_routine =\page_mix_command_routine,
+ \s!page_otr_command_package_contents =\page_mix_command_package_contents,
+ \s!page_otr_command_set_vsize =\page_mix_command_set_vsize,
+ \s!page_otr_command_set_hsize =\page_mix_command_set_hsize,
+ % \s!page_otr_command_synchronize_hsize =\page_mix_command_synchronize_hsize,
+ \s!page_otr_command_next_page =\page_mix_command_next_page,
+ \s!page_otr_command_next_page_and_inserts =\page_mix_command_next_page_and_inserts,
+ % \s!page_otr_command_set_top_insertions =\page_mix_command_set_top_insertions,
+ % \s!page_otr_command_set_bottom_insertions =\page_mix_command_set_bottom_insertions,
+ % \s!page_otr_command_flush_top_insertions =\page_mix_command_flush_top_insertions,
+ % \s!page_otr_command_flush_bottom_insertions=\page_mix_command_flush_bottom_insertions,
+ \s!page_otr_command_check_if_float_fits =\page_mix_command_check_if_float_fits,
+ % \s!page_otr_command_set_float_hsize =\page_mix_command_set_float_hsize,
+ \s!page_otr_command_flush_float_box =\page_mix_command_flush_float_box,
+ \s!page_otr_command_side_float_output =\page_mix_command_side_float_output,
+ \s!page_otr_command_synchronize_side_floats=\page_mix_command_synchronize_side_floats,
+ \s!page_otr_command_flush_floats =\page_mix_command_flush_floats,
+ \s!page_otr_command_flush_side_floats =\page_mix_command_flush_side_floats,
+ \s!page_otr_command_flush_saved_floats =\page_mix_command_flush_saved_floats,
+ \s!page_otr_command_flush_all_floats =\page_mix_command_flush_all_floats,
+ % \s!page_otr_command_flush_margin_blocks =\page_mix_command_flush_margin_blocks, % not used
+ \s!page_otr_command_test_column =\page_mix_command_test_column
+ ]
+
+%D Only a few float placement options are supported:
+
+\installfloatmethod \s!mixedcolumn \v!here \page_mix_place_float_here
+\installfloatmethod \s!mixedcolumn \v!force \page_mix_place_float_force
+\installfloatmethod \s!mixedcolumn \v!top \page_mix_place_float_top
+\installfloatmethod \s!mixedcolumn \v!bottom \page_mix_place_float_bottom
+
+\installfloatmethod \s!mixedcolumn \v!local \somelocalfloat
+
+%D It ends here.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-mrk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-mrk.mkxl
new file mode 100644
index 00000000000..143fab180e1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-mrk.mkxl
@@ -0,0 +1,229 @@
+%D \module
+%D [ file=page-mrk,
+%D version=2011.05.23, % moved from page-lay
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Cutmarks and Colorbars,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Cutmarks and Colorbars}
+
+\unprotect
+
+%D This module implements curmarks and depends on \METAPOST.
+%D
+%D \starttyping
+%D \setuplayout[marking=on,location=middle]
+%D \setuplayout[marking=page,location=middle]
+%D \setuplayout[marking=empty,location=middle]
+%D \setuplayout[marking={color,page},location=middle]
+%D \setuplayout[marking={one,page},location=middle]
+%D \setuplayout[marking={two,page},location=middle]
+%D \setuplayout[marking={four,page},location=middle]
+%D \stoptyping
+
+\mutable\def\pagecutmarklength {5mm}
+\mutable\def\pagecutmarkoffset {3mm} % slightly larger than before, and now a fixed size
+\mutable\def\pagecutmarkmargin{10cm}
+
+% maybe all these should be global
+
+\newconditional\c_page_marks_add_more_color
+\newconditional\c_page_marks_add_more_marking
+\newconditional\c_page_marks_add_more_lines
+\newconditional\c_page_marks_add_page_lines
+\newconditional\c_page_marks_add_more_number
+
+\newcount\c_page_marks_max
+\newcount\c_page_marks_nx
+\newcount\c_page_marks_ny
+
+\startuniqueMPgraphic{print:color}{w,h,l,o}
+ loadmodule "crop" ;
+ page_marks_add_color(\MPvar w,\MPvar h,\MPvar l,\MPvar o) ;
+\stopuniqueMPgraphic
+
+\startuniqueMPgraphic{print:marking}{w,h,l,o}
+ loadmodule "crop" ;
+ page_marks_add_marking(\MPvar w,\MPvar h,\MPvar l,\MPvar o) ;
+\stopuniqueMPgraphic
+
+\startuniqueMPgraphic{print:lines}{w,h,l,o,x,y}
+ loadmodule "crop" ;
+ page_marks_add_lines(\MPvar w,\MPvar h,\MPvar l,\MPvar o,\MPvar x,\MPvar y,
+ \ifconditional\c_page_marks_add_page_lines tru\else fals\fi e) ;
+\stopuniqueMPgraphic
+
+\startuseMPgraphic{print:number}{w,h,l,o,n}
+ loadmodule "crop" ;
+ page_marks_add_number(\MPvar w,\MPvar h,\MPvar l,\MPvar o,\MPvar n) ;
+\stopuseMPgraphic
+
+\def\page_marks_add_color
+ {\setlayoutcomponentattribute{\v!print:\v!color}%
+ \setbox\scratchbox\hpack \layoutcomponentboxattribute\bgroup
+ \uniqueMPgraphic
+ {print:color}%
+ {w=\the\scratchwidth,h=\the\scratchheight,%
+ l=\pagecutmarklength,o=\pagecutmarkoffset}%
+ \egroup
+ \wd\scratchbox\scratchwidth\ht\scratchbox\scratchheight\dp\scratchbox\scratchdepth
+ \hskip-\scratchwidth\box\scratchbox}
+
+\def\page_marks_add_marking
+ {\setlayoutcomponentattribute{\v!print:\v!marking}%
+ \setbox\scratchbox\hpack \layoutcomponentboxattribute\bgroup
+ \uniqueMPgraphic
+ {print:marking}%
+ {w=\the\scratchwidth,h=\the\scratchheight,%
+ l=\pagecutmarklength,o=\pagecutmarkoffset}%
+ \egroup
+ \wd\scratchbox\scratchwidth\ht\scratchbox\scratchheight\dp\scratchbox\scratchdepth
+ \hskip-\scratchwidth\box\scratchbox}
+
+\def\page_marks_add_lines
+ {\setlayoutcomponentattribute{\v!print:\v!lines}%
+ \setbox\scratchbox\hpack \layoutcomponentboxattribute\bgroup
+ \uniqueMPgraphic
+ {print:lines}%
+ {w=\the\scratchwidth,h=\the\scratchheight,%
+ l=\pagecutmarklength,o=\pagecutmarkoffset,%
+ x=\the\c_page_marks_nx,y=\the\c_page_marks_ny}%
+ \egroup
+ \wd\scratchbox\scratchwidth\ht\scratchbox\scratchheight\dp\scratchbox\scratchdepth
+ \hskip-\scratchwidth\box\scratchbox}
+
+\def\page_marks_add_number
+ {\setlayoutcomponentattribute{\v!print:\v!number}%
+ \setbox\scratchbox\hpack \layoutcomponentboxattribute\bgroup
+ \useMPgraphic
+ {print:number}%
+ {w=\the\scratchwidth ,h=\the\scratchheight,%
+ l=\pagecutmarklength,o=\pagecutmarkoffset,%
+ n=\number\ifcase\arrangeno\realpageno\else\arrangeno\fi}%
+ \egroup
+ \wd\scratchbox\scratchwidth\ht\scratchbox\scratchheight\dp\scratchbox\scratchdepth
+ \hskip-\scratchwidth\box\scratchbox}
+
+\def\page_marks_add_page_indeed#1%
+ {\setbox#1\hpack\bgroup
+ \resetvisualizers
+ \let\objectoffset\pagecutmarkmargin
+ %\infofont
+ \scratchwidth \wd#1%
+ \scratchheight\ht#1%
+ \scratchdepth \dp#1%
+ \box#1\relax
+ \page_marks_add_lines
+ \egroup}
+
+\def\page_marks_add_more_indeed#1%
+ {\setbox#1\hbox\bgroup
+ \resetvisualizers
+ \let\objectoffset\pagecutmarkmargin
+ \infofont
+ \scratchwidth \wd#1% these can move out
+ \scratchheight\ht#1%
+ \scratchdepth \dp#1%
+ \box#1\relax
+ \ifconditional\c_page_marks_add_more_color
+ \page_marks_add_color
+ \fi
+ \ifconditional\c_page_marks_add_more_marking
+ \page_marks_add_marking
+ \fi
+ \ifconditional\c_page_marks_add_more_lines
+ \page_marks_add_lines
+ \fi
+ \ifconditional\c_page_marks_add_more_number
+ \page_marks_add_number
+ \fi
+ \global\advance\c_page_marks_max\minusone
+ \ifnum\c_page_marks_max>\zerocount\else
+ \glet\page_marks_add_more\gobbleoneargument
+ \glet\page_marks_add_page\gobbleoneargument
+ \fi
+ \egroup}
+
+\let\page_marks_add_page\gobbleoneargument
+\let\page_marks_add_more\gobbleoneargument
+
+\installcorenamespace{layoutmarking}
+
+\permanent\protected\def\installpagecutmark#1#2%
+ {\defcsname\??layoutmarking#1\endcsname{#2}}
+
+\installpagecutmark\v!on
+ {%settrue\c_page_marks_add_page_lines
+ \settrue\c_page_marks_add_more_lines
+ \settrue\c_page_marks_add_more_number}
+
+\installpagecutmark\v!page
+ {\settrue\c_page_marks_add_page_lines
+ \settrue\c_page_marks_add_more_lines
+ \settrue\c_page_marks_add_more_number}
+
+\installpagecutmark\v!empty
+ {%settrue\c_page_marks_add_page_lines
+ \settrue\c_page_marks_add_more_lines}
+
+\installpagecutmark\v!color
+ {%settrue\c_page_marks_add_page_lines
+ \settrue\c_page_marks_add_more_lines
+ \settrue\c_page_marks_add_more_color
+ \settrue\c_page_marks_add_more_marking
+ \settrue\c_page_marks_add_more_number}
+
+\installpagecutmark\v!one {\global\c_page_marks_max\plusone} % first page
+\installpagecutmark\v!two {\global\c_page_marks_max\plustwo} % first two pages
+\installpagecutmark\v!four{\global\c_page_marks_max\plusfour} % first four pages
+
+\permanent\protected\def\page_marks_set#1%
+ {\begincsname\??layoutmarking#1\endcsname}
+
+\appendtoks
+ \setfalse\c_page_marks_add_page_lines
+ \setfalse\c_page_marks_add_more_color
+ \setfalse\c_page_marks_add_more_marking
+ \setfalse\c_page_marks_add_more_lines
+ \setfalse\c_page_marks_add_more_number
+ \global\c_page_marks_max\maxcount
+ \rawprocesscommacommand[\layoutparameter\c!marking]\page_marks_set
+ \ifnum\c_page_marks_max<\maxcount
+ \ifconditional\c_page_marks_add_page_lines \orelse
+ \ifconditional\c_page_marks_add_more_color \orelse
+ \ifconditional\c_page_marks_add_more_marking\orelse
+ \ifconditional\c_page_marks_add_more_lines \orelse
+ \ifconditional\c_page_marks_add_more_number \else
+ \settrue\c_page_marks_add_page_lines
+ \settrue\c_page_marks_add_more_number
+ \fi
+ \fi
+ \ifconditional\c_page_marks_add_page_lines
+ \let\page_marks_add_page\page_marks_add_page_indeed
+ \else
+ \let\page_marks_add_page\gobbleoneargument
+ \fi
+ % we can get a double set of (overlayed) lines .. needs to be redone (too many objects anyway)
+ \ifconditional\c_page_marks_add_more_lines
+ \let\page_marks_add_more\page_marks_add_more_indeed
+ \orelse\ifconditional\c_page_marks_add_more_color
+ \let\page_marks_add_more\page_marks_add_more_indeed
+ \orelse\ifconditional\c_page_marks_add_more_marking
+ \let\page_marks_add_more\page_marks_add_more_indeed
+ \orelse\ifconditional\c_page_marks_add_more_number
+ \let\page_marks_add_more\page_marks_add_more_indeed
+ \else
+ \let\page_marks_add_more\gobbleoneargument
+ \fi
+\to \everysetuplayout
+
+\setuplayout
+ [\c!marking=\v!off]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-not.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-not.mkxl
new file mode 100644
index 00000000000..82523cc66a3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-not.mkxl
@@ -0,0 +1,32 @@
+%D \module
+%D [ file=page-nnt,
+%D version=2002.04.16,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Footnotes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Footnotes}
+
+%D We've moved some footnote handling to a separate page module. The macros below
+%D are used in the single and multi column page handlers and permit mixed usage of
+%D column and page notes.
+
+\unprotect
+
+\newdimen\totalinsertionheight
+
+\permanent\protected\def\settotalinsertionheight
+ {\calculatetotalnoteheight
+ \totalinsertionheight\dimexpr
+ \totalnoteheight
+ +\page_insert_insertion_height\s!topfloat
+ +\page_insert_insertion_height\s!bottomfloat
+ \relax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-one.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-one.mkxl
new file mode 100644
index 00000000000..9d0fb2065a2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-one.mkxl
@@ -0,0 +1,709 @@
+%D \module
+%D [ file=page-one,
+%D version=2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Default Routine,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Default Routine}
+
+%D This is just the good old \CONTEXT\ output routine, which
+%D has been there right from the start.
+
+\unprotect
+
+% OTRONE: basic single column
+
+\newconstant \c_page_one_float_method
+
+\protected\def\page_one_command_next_page
+ {\page_otr_eject_page}
+
+\protected\def\page_one_command_next_page_and_inserts
+ {\page_otr_eject_page_and_flush_inserts}
+
+\protected\def\page_one_command_set_hsize
+ {\global\hsize\textwidth
+ \columnwidth\textwidth} % bonus so that it's not zero
+
+\protected\def\page_one_command_set_float_hsize
+ {\global\hsize\textwidth}
+
+\protected\def\page_one_command_set_vsize
+ {\ifgridsnapping
+ \ifcase\layoutlines
+ \getrawnoflines\textheight
+ \else
+ \noflines\layoutlines
+ \fi
+ \global\vsize\noflines\openlineheight
+ \else
+ \global\vsize\textheight
+ \fi
+ \global\advance\vsize\d_page_adapts_delta
+ % alternatively we could set it in builders.buildpage_filter
+ % \ifdim\pagegoal<\maxdimen .. \fi
+ \pagegoal\dimexpr
+ \vsize
+ -\d_page_floats_inserted_top
+ -\d_page_floats_inserted_bottom
+ -\insertheights
+ \relax}
+
+% 1 = partial page, 2 = whole page, 3 = partial page
+
+% We really need a setting! Todo, what key to use?
+
+\pushoverloadmode
+ \setnewconstant\kindofpagetextareas\plustwo % \plusone can become default some day
+ \overloaded\mutable\let\kindofpagetextareas\kindofpagetextareas % fo rnow mutable, can become a key/value
+\popoverloadmode
+
+\def\page_one_registered_text_area_a % two arguments: (un)vbox n
+ {\ifconditional\c_page_areas_enabled
+ \expandafter\page_one_registered_text_area_a_indeed
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\def\page_one_registered_text_area_b % one arguments: content
+ {\ifconditional\c_page_areas_enabled
+ \expandafter\page_one_registered_text_area_b_indeed
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\def\page_one_registered_text_area_a_indeed % two arguments: (un)vbox n
+ {\ifcase\kindofpagetextareas
+ \expandafter\firstofoneargument
+ \or % partial page (experimental)
+ \expandafter\page_areas_register_direct
+ \or % whole page (default)
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\def\page_one_registered_text_area_b_indeed % one arguments: content
+ {\ifcase\kindofpagetextareas
+ % \expandafter\firstofoneargument
+ \or % partial page (experimental)
+ % \expandafter\firstofoneargument
+ \or % whole page (default)
+ \expandafter\page_areas_register_boxed
+ \else
+ % \expandafter\firstofoneargument
+ \fi}
+
+\newdimen\d_page_one_natural_depth
+\newbox \b_page_one_bottom_notes
+\newbox \b_page_one_contents
+
+\let\page_one_command_package_show_state\relax
+
+% \fakepagenotes ... needs checking
+%
+% we can also have bottom notes on top of bottom insertions
+
+\protected\def\page_one_command_package_contents#1#2% \box<n> \unvbox<n> % this one will be redone (checked)
+ {\bgroup
+ \strc_notes_check_if_bottom_present
+ \d_page_one_natural_depth\dp#2\relax
+ % we need to set the height as otherwise the shrink will not kick in so the following
+ % no longer applies:
+ %
+ % \setbox\b_page_one_contents\vbox \ifconditional\c_notes_bottom_present to \textheight \fi
+ %
+ \setbox\b_page_one_contents\vbox to \textheight % probably no pack
+ {\page_otr_command_flush_top_insertions
+ % this is messy ... we will provide a more tight area (no big deal as we can
+ % do that at the lua end)
+% \parfillskip\zeropoint
+ \page_one_registered_text_area_a#1#2% \unvbox <box>
+ %
+ \ifgridsnapping
+ \unskip % new per 2019-06-18, otherwise weird bottom floats
+ \vkern\dimexpr\openstrutdepth-\d_page_one_natural_depth\relax
+ \prevdepth\openstrutdepth
+ \page_otr_command_flush_bottom_insertions
+ \vfil
+ \orelse\ifcase\bottomraggednessmode
+ % ragged (default)
+ \unskip % new per 2019-06-18, otherwise weird bottom floats
+ \vkern\dimexpr\openstrutdepth-\d_page_one_natural_depth\relax
+ \prevdepth\openstrutdepth
+ % these have whitespace before but we can have some more options
+ % like a \vfill or so
+ \page_otr_command_flush_bottom_insertions
+ \vfil
+ \or
+ % align (normal)
+ \page_otr_command_flush_bottom_insertions
+ \or
+ % baseline
+ \unskip % new per 2019-06-18, otherwise weird bottom floats
+ \vkern\dimexpr\maxdepth-\d_page_one_natural_depth\relax
+ \page_otr_command_flush_bottom_insertions
+ \fi
+ \fakepagenotes}%
+ \page_one_command_package_show_state
+ \ifconditional\c_notes_bottom_present
+ \ifgridsnapping
+ \ifcase\layoutlines
+ \getrawnoflines\textheight
+ \else
+ \noflines\layoutlines
+ \fi
+ \scratchoffset\dimexpr\numexpr\noflines-\plusone\relax\lineheight+\topskip\relax
+ \else
+ \scratchoffset\ht\b_page_one_contents
+ \fi
+ \setbox\b_page_one_bottom_notes\hpack
+ {\lower\scratchoffset\vbox{\placebottomnotes\par\kern\zeropoint}}% kerns makes notes sit on bottom % pack ?
+ \smashbox\b_page_one_bottom_notes
+ \ht\b_page_one_contents\zeropoint
+ \page_one_registered_text_area_b
+ {\vpack to \textheight
+ {\box\b_page_one_contents
+ \box\b_page_one_bottom_notes}}%
+ \else
+ \ht\b_page_one_contents\textheight
+ \page_one_registered_text_area_b
+ {\box\b_page_one_contents}%
+ \fi
+ \egroup}
+
+\protected\def\page_one_command_side_float_output
+ {\page_otr_construct_and_shipout\unvbox\normalpagebox\plusone} % three arguments, we need to be in the output group
+
+\protected\def\page_one_command_routine
+ {\page_sides_output_routine}
+
+%D Insertions
+
+\newconditional\c_page_one_top_of_insert
+\newconditional\c_page_one_correct_top_insert \settrue\c_page_one_correct_top_insert % false moves up (tight)
+\newskip \s_page_one_between_top_insert
+
+\def\page_one_prepare_top_float
+ {\ifdim\d_page_floats_inserted_top=\zeropoint
+ \settrue\c_page_one_top_of_insert
+ \else
+ \setfalse\c_page_one_top_of_insert
+ \fi
+ \s_page_one_between_top_insert\ifdim\d_strc_floats_top>\d_strc_floats_bottom\d_strc_floats_top\else\d_strc_floats_bottom\fi\relax
+ \global\advance\d_page_floats_inserted_top\dimexpr\ht\floatbox+\dp\floatbox+\s_page_one_between_top_insert\relax}
+
+\def\page_one_insert_top_float % maybe remember last beforeskip
+ {\floatingpenalty\zerocount
+ \insert\namedinsertionnumber\s!topfloat\bgroup
+ \forgetall
+ \ifconditional\c_page_one_top_of_insert
+ \ifconditional\c_page_one_correct_top_insert
+ \topskipcorrection % [xx] new: see icare topbleed
+ \kern-\lineskip
+ \par
+ \prevdepth\maxdimen
+ \fi
+ \fi
+ \page_otr_command_flush_float_box
+ \vskip\s_page_one_between_top_insert
+ \egroup}
+
+\mutable\let\totaltopinserted\!!zeropoint
+\mutable\let\totalbotinserted\!!zeropoint
+
+\protected\def\page_one_command_set_top_insertions
+ {\bgroup
+ \ifconditional\c_page_floats_some_waiting
+ \noffloatinserts\zerocount
+ \let\totaltopinserted\!!zeropoint
+ \page_one_command_set_top_insertions_indeed
+ \ifnum\rootfloatparameter\c!nbottom=\zerocount
+ \ifnum\rootfloatparameter\c!nlines>\zerocount
+ \ifdim\totaltopinserted>\zeropoint\relax
+ \ifdim\dimexpr\rootfloatparameter\c!nlines\lineheight+\totaltopinserted\relax>\textheight
+ \showmessage\m!floatblocks8{\rootfloatparameter\c!nlines}%
+ \page_otr_fill_and_eject_page % was tripple: vfilll
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \egroup}
+
+\def\page_one_command_set_top_insertions_indeed
+ {\ifnum\noffloatinserts<\c_page_floats_n_of_top
+ \page_floats_get
+ \page_one_prepare_top_float
+ \ifdim\d_page_floats_inserted_top<\textheight\relax
+ \xdef\totaltopinserted{\the\d_page_floats_inserted_top}%
+ \page_one_insert_top_float
+ \ifconditional\c_page_floats_some_waiting
+ \advance\noffloatinserts \plusone
+ \else
+ \noffloatinserts\c_page_floats_n_of_top\relax
+ \fi
+ \page_floats_report_flushed
+ \else
+ \page_floats_resave\s!text
+ \noffloatinserts\c_page_floats_n_of_top\relax
+ \fi
+ \else
+ \ifconditional\c_page_floats_some_waiting
+ \showmessage\m!floatblocks6{\the\c_page_floats_n_of_top}%
+ \fi
+ \let\page_one_command_set_top_insertions_indeed\relax
+ \fi
+ \page_one_command_set_top_insertions_indeed}
+
+\protected\def\page_one_command_set_bottom_insertions
+ {\bgroup
+ \ifconditional\c_page_floats_some_waiting
+ \noffloatinserts\zerocount
+ \page_one_command_set_bottom_insertions_indeed
+ \fi
+ \egroup}
+
+\def\page_one_command_set_bottom_insertions_indeed
+ {\ifnum\noffloatinserts<\c_page_floats_n_of_bottom\relax
+ \page_floats_get
+ \global\advance\d_page_floats_inserted_bottom\dimexpr\ht\floatbox+\dp\floatbox+\d_strc_floats_top\relax
+ \ifdim\d_page_floats_inserted_bottom<\pagegoal\relax
+ \floatingpenalty\zerocount
+ \insert\namedinsertionnumber\s!bottomfloat\bgroup
+ \forgetall
+ \blank[\rootfloatparameter\c!spacebefore]%
+ \page_otr_command_flush_float_box
+ \egroup
+ \ifconditional\c_page_floats_some_waiting
+ \advance\noffloatinserts \plusone
+ \else
+ \noffloatinserts\c_page_floats_n_of_bottom
+ \fi
+ \page_floats_report_flushed
+ \else
+ \page_floats_resave\s!text
+ \noffloatinserts\c_page_floats_n_of_bottom\relax
+ \fi
+ \global\settrue\c_page_floats_not_permitted % vgl topfloats s!
+ \else
+ \ifconditional\c_page_floats_some_waiting
+ \showmessage\m!floatblocks7{\the\c_page_floats_n_of_bottom}%
+ \fi
+ \let\page_one_command_set_bottom_insertions_indeed\relax
+ \fi
+ \page_one_command_set_bottom_insertions_indeed}
+
+\protected\def\page_one_command_flush_top_insertions
+ {\ifvoid\namedinsertionnumber\s!topfloat\else
+ \page_one_command_flush_top_insertions_indeed % less tracing
+ \fi
+ \global\d_page_floats_inserted_top\zeropoint}
+
+\def\page_one_command_flush_top_insertions_indeed
+ {\ifgridsnapping
+ \box\namedinsertionnumber\s!topfloat
+ \vkern-\topskip
+ \vkern\strutheight % [xx] new: see icare topbleed
+ \else
+ \ifcase\c_page_floats_insertions_topskip_mode
+ % 0: default, do nothing
+ \or
+ % 1: no topskip (crossed fingers)
+ \vskip-\topskip % skip !
+ \vkern\strutheight
+ \fi
+ \unvbox\namedinsertionnumber\s!topfloat
+ \fi}
+
+\protected\def\page_one_command_flush_bottom_insertions
+ {\ifvoid\namedinsertionnumber\s!bottomfloat\else
+ \page_one_command_flush_bottom_insertions_indeed
+ \fi
+ \global\d_page_floats_inserted_bottom\zeropoint
+ \global\setfalse\c_page_floats_not_permitted}
+
+\def\page_one_command_flush_bottom_insertions_indeed
+ {\ifgridsnapping
+ % \floatparameter\c!bottombefore
+ \snaptogrid\hbox{\box\namedinsertionnumber\s!bottomfloat}%
+ % \floatparameter\c!bottomafter
+ \else
+ \floatparameter\c!bottombefore
+ \unvbox\namedinsertionnumber\s!bottomfloat
+ \floatparameter\c!bottomafter
+ \fi}
+
+\protected\def\page_one_command_flush_floats
+ {\global\settrue\c_page_floats_flushing
+ \ifconditional\c_page_floats_some_waiting
+ \par
+ % if kept, then option and definitely off in gridmode ! ! ! !
+ % \ifvmode \prevdepth\maxdimen \fi % prevents whitespace; problematic in icare tests
+ \page_one_command_flush_floats_indeed
+ \fi
+ \global\savednoffloats\zerocount
+ \global\setfalse\c_page_floats_some_waiting
+ \global\setfalse\c_page_floats_flushing}
+
+\protected\def\page_one_command_flush_float_box
+ {\ifconditional\c_page_floats_center_box \ifdim\wd\floatbox<\hsize
+ \global\setbox\floatbox\hpack to \hsize{\hss\box\floatbox\hss}%
+ \fi \fi
+ \snaptogrid\hpack{\box\floatbox}} % was copy
+
+\def\page_one_command_floats_get_compressed
+ {\setfalse\c_page_floats_center_box % not needed as we do call directly
+ %% no longer (interferes with footnotes):
+ %%
+ %% \page_one_command_set_vsize % test 2011.06.24.001
+ %%
+ \global\setbox\floatbox\hbox to \hsize
+ {\hfil
+ \dorecurse\nofcollectedfloats
+ {\ifcase\columndirection % nog document wide
+ \page_floats_flush\s!text\plusone
+ \else
+ \page_floats_flush\s!text{\the\numexpr\nofcollectedfloats-\recurselevel+1\relax}%
+ \fi
+ \hpack to \ifdim\naturalfloatwd>\makeupwidth\makeupwidth\else\naturalfloatwd\fi
+ {\hss\box\floatbox\hss}%
+ \ifnum\recurselevel<\nofcollectedfloats
+ \hfil
+ \fi}%
+ \hfil}}
+
+\def\page_one_command_flush_floats_indeed
+ {\ifconditional\c_page_floats_some_waiting
+ \ifconditional\c_page_floats_pack_flushed
+ \page_floats_collect\s!text\hsize\d_page_floats_compress_distance
+ \ifcase\nofcollectedfloats
+ \page_floats_get
+ \or
+ \page_floats_get
+ \else
+ \page_one_command_floats_get_compressed
+ \fi
+ \else
+ \page_floats_get
+ \fi
+ % there is a chance that due to rounding errors, the float
+ % fits on a page where it was first rejected, in which case
+ % the prevdepth is -maxdimen and we cannot obey the grid
+ \doplacefloatbox
+ \expandafter\page_one_command_flush_floats_indeed
+ \fi}
+
+\protected\def\page_one_command_flush_margin_blocks
+ {\ifconditional\c_page_margin_blocks_present % \ifvoid\b_page_margin_blocks \else
+ \ifdim\pagetotal=\zeropoint
+ \null % \fixedspace
+ \fi
+ \page_otr_command_next_page % \page
+ \ifvoid\b_page_margin_blocks
+ \global\setfalse\c_page_margin_blocks_present
+ \else
+ \doubleexpandafter\page_one_command_flush_margin_blocks
+ \fi
+ \fi}
+
+\protected\def\page_one_command_check_if_float_fits
+ {\ifconditional\c_page_floats_not_permitted
+ \global\setfalse\c_page_floats_room
+ \else
+ % new per 31/5/2004, should be an option, only one column mode
+ \begingroup
+ \scratchdimen\dimexpr\pagetotal+\lineheight\relax
+ \ifdim\scratchdimen>\pagegoal
+ \goodbreak % hack ?
+ \fi
+ % should be an option
+ \endgroup
+ \scratchdimenone\dimexpr
+ \pagetotal
+ +\floatheight
+ +\d_strc_floats_top
+ +\d_strc_floats_overflow
+ -\pageshrink
+ \relax
+ \scratchdimentwo\pagegoal
+ \relax % needed
+ \ifcase\c_page_one_float_method
+ % method 0 : raw
+ \or
+ % method 1 : safe
+ % too fuzzy as it can change and for a high page it's a lot : \scratchdimentwo .99\pagegoal
+ \advance\scratchdimentwo -\strutdp
+ \or
+ % method 2 : tight
+ \advance\scratchdimenone -\onepoint
+ \fi
+ \relax % really needed ! ! ! !
+ \ifdim\scratchdimenone>\scratchdimentwo
+ \global\setfalse\c_page_floats_room
+ \else
+ \global\settrue\c_page_floats_room
+ \fi
+ \fi}
+
+\protected\def\page_one_command_flush_saved_floats
+ {\global\d_page_floats_inserted_top\zeropoint
+ \global\d_page_floats_inserted_bottom\zeropoint
+ \ifconditional\c_page_floats_flushing \else
+ \page_one_command_set_top_insertions
+ \page_one_command_set_bottom_insertions
+ \ifconditional\c_page_floats_some_waiting
+ \doif{\rootfloatparameter\c!cache}\v!no\page_one_command_flush_floats % could be _otr_
+ \orelse\ifconditional\c_page_margin_blocks_present
+ \page_one_command_flush_floats
+ \fi
+ \fi}
+
+% \def\page_one_place_float_here_indeed
+% {\ifgridsnapping
+% % otherwise real bad outcome
+% \else
+% \baselinecorrection % this has to be done better (and definitely not in column mode)
+% \fi
+% \doplacefloatbox
+% \page_floats_report_total
+% \dohandlenextfloatindent}
+
+\def\page_one_place_float_here_indeed
+ {\ifgridsnapping
+ % otherwise real bad outcome
+ \else
+ % this was not really applied (delayed)
+ % \baselinecorrection % this has to be done better (and definitely not in column mode)
+ % so we now use this:
+ \checkprevdepth
+ \fi
+ \doplacefloatbox
+ \page_floats_report_total
+ \dohandlenextfloatindent}
+
+\def\page_one_place_float_force
+ {\showmessage\m!floatblocks9\empty
+ \page_one_place_float_here_indeed}
+
+\def\page_one_place_float_side_indeed#1%
+ {\setbox\floatbox\vpack{\box\floatbox}% ? can go
+ \wd\floatbox\floatwidth
+ #1{\box\floatbox}%
+ \doifinset\v!tall\floatlocationmethod\page_sides_flush_floats_after_par}
+
+\def\page_one_place_float_left
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_left
+ \presetindentation}
+
+\def\page_one_place_float_right
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_right}
+
+\def\page_one_place_float_margin
+ {\page_margin_blocks_process_float
+ \nonoindentation} % new, due to popular request
+
+\def\page_one_place_float_leftmargin
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_leftmargin
+ \nonoindentation} % new, due to popular request
+
+\def\page_one_place_float_rightmargin
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_rightmargin
+ \nonoindentation} % new, due to popular request
+
+\def\page_one_place_float_leftedge
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_leftedge}
+
+\def\page_one_place_float_rightedge
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_rightedge}
+
+\def\page_one_place_float_inmargin
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_cutspace}
+
+\def\page_one_place_float_backspace
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_backspace}
+
+\def\page_one_place_float_cutspace
+ {\page_one_place_float_side_indeed
+ \page_sides_process_float_cutspace}
+
+\def\page_one_place_float_page {\page_floats_save_page_float \s!page \floatlocationmethod}
+\def\page_one_place_float_leftpage {\page_floats_save_page_float \s!leftpage \floatlocationmethod}
+\def\page_one_place_float_rightpage {\page_floats_save_page_float \s!rightpage\floatlocationmethod}
+\def\page_one_place_float_somewhere {\page_floats_save_somewhere_float\s!somewhere\floatlocationmethod}
+
+\def\page_one_place_float_here
+ {\page_one_place_float_otherwise_here}
+
+\def\page_one_place_float_auto
+ {\page_one_place_float_otherwise
+ \nonoindentation} % new, due to popular request
+
+\def\page_one_place_float_top
+ {\page_one_place_float_otherwise
+ \nonoindentation}
+
+\def\page_one_place_float_bottom
+ {\page_one_place_float_otherwise
+ \nonoindentation} % new, due to popular request
+
+\def\page_one_place_float_otherwise
+ {\doifelseinset\v!here\floatlocationmethod
+ \page_one_place_float_otherwise_here
+ \page_one_place_float_otherwise_else}
+
+\def\page_one_place_float_otherwise_here
+ {\doifelseinset\v!always\floatlocationmethod
+ {\page[\v!preference]%
+ \page_otr_command_check_if_float_fits
+ \ifconditional\c_page_floats_room
+ \page_one_place_float_here_indeed
+ \else
+ \showmessage\m!floatblocks9\empty
+ \page_floats_resave\s!text
+ \fi}
+ {\ifconditional\c_page_floats_some_waiting
+ \page_floats_save\s!text
+ \nonoindentation
+ \else
+ \page[\v!preference]%
+ \page_otr_command_check_if_float_fits
+ \ifconditional\c_page_floats_room
+ \page_one_place_float_here_indeed
+ \else
+ \page_floats_save\s!text
+ \nonoindentation
+ \fi
+ \fi}}
+
+\def\page_one_place_float_otherwise_else
+ {\doifelseinset\v!always\floatlocationmethod
+ {\page_otr_command_check_if_float_fits
+ \ifconditional\c_page_floats_room
+ \page_one_place_float_auto_top_bottom
+ \else
+ \showmessage\m!floatblocks9\empty
+ \page_floats_resave\s!text
+ \fi}
+ {\page_otr_command_check_if_float_fits
+ \ifconditional\c_page_floats_room
+ \page_one_place_float_auto_top_bottom
+ \else
+ \page_floats_save\s!text
+ \nonoindentation
+ \fi}}
+
+\mutable\def\floatautofactor{.5}
+
+\def\page_one_place_float_auto_top_bottom
+ {\ifx\floatmethod\v!auto
+ \ifdim\pagetotal<\floatautofactor\pagegoal % when empty page, maxdimen
+ \page_one_place_float_top_indeed
+ \else
+ \page_one_place_float_bottom_indeed
+ \fi
+ \else
+ \ifx\floatmethod\v!top
+ \page_one_place_float_top_indeed
+ \orelse\ifx\floatmethod\v!bottom
+ \page_one_place_float_bottom_indeed
+ \else
+ \page_one_place_float_here_indeed
+ \fi
+ \fi}
+
+\def\page_one_place_float_top_indeed % maybe remember last beforeskip
+ {\page_one_prepare_top_float
+ \page_one_insert_top_float
+ \page_floats_report_total}
+
+\def\page_one_place_float_bottom_indeed
+ {\global\advance\d_page_floats_inserted_bottom\dimexpr\ht\floatbox+\dp\floatbox+\d_strc_floats_top\relax
+ \floatingpenalty\zerocount
+ \insert\namedinsertionnumber\s!bottomfloat\bgroup
+ \forgetall
+ \blank[\rootfloatparameter\c!spacebefore]%
+ \page_otr_command_flush_float_box
+ \egroup
+ \page_floats_report_total}
+
+\def\page_one_place_float_face % links, rechts, midden, hoog, midden, laag
+ {%\checkwaitingfloats{#1}%
+ \startopposite
+ \page_otr_command_flush_float_box
+ \stopopposite
+ }%\page_floats_report_total}
+
+\protected\def\page_one_command_flush_side_floats
+ {\page_sides_flush_floats}
+
+\protected\def\page_one_command_synchronize_side_floats
+ {\page_sides_synchronize_floats}
+
+\protected\def\page_one_command_test_page
+ {\testpage}
+
+\protected\def\page_one_command_flush_all_floats
+ {\ifconditional\c_page_floats_some_waiting
+ \begingroup
+ \c_page_floats_n_of_top\plusthousand
+ \c_page_floats_n_of_bottom\zerocount
+ % this is needed in case a float that has been stored
+ % ends up at the current page; this border case occurs when
+ % the calculated room is 'eps' smaller that the room available
+ % when just flushing; so now we have (maybe optional):
+ \pagebaselinecorrection % hm, needs checking, not needed when no floats
+ % alas, this is tricky but needed (first surfaced in prikkels)
+ \page_otr_command_flush_floats
+ \endgroup
+ \fi}
+
+\protected\def\page_one_command_flush_facing_floats
+ {\strc_floats_facing_flush}
+
+\defineoutputroutine
+ [\s!singlecolumn]
+ [\s!page_otr_command_routine =\page_one_command_routine,
+ \s!page_otr_command_package_contents =\page_one_command_package_contents,
+ \s!page_otr_command_set_vsize =\page_one_command_set_vsize,
+ \s!page_otr_command_set_hsize =\page_one_command_set_hsize,
+ % \s!page_otr_command_synchronize_hsize =\page_one_command_synchronize_hsize,
+ \s!page_otr_command_next_page =\page_one_command_next_page,
+ \s!page_otr_command_next_page_and_inserts =\page_one_command_next_page_and_inserts,
+ \s!page_otr_command_set_top_insertions =\page_one_command_set_top_insertions,
+ \s!page_otr_command_set_bottom_insertions =\page_one_command_set_bottom_insertions,
+ \s!page_otr_command_flush_top_insertions =\page_one_command_flush_top_insertions,
+ \s!page_otr_command_flush_bottom_insertions=\page_one_command_flush_bottom_insertions,
+ \s!page_otr_command_check_if_float_fits =\page_one_command_check_if_float_fits,
+ % \s!page_otr_command_set_float_hsize =\page_one_command_set_float_hsize,
+ \s!page_otr_command_flush_float_box =\page_one_command_flush_float_box,
+ \s!page_otr_command_side_float_output =\page_one_command_side_float_output,
+ \s!page_otr_command_synchronize_side_floats=\page_one_command_synchronize_side_floats,
+ \s!page_otr_command_flush_floats =\page_one_command_flush_floats,
+ \s!page_otr_command_flush_side_floats =\page_one_command_flush_side_floats,
+ \s!page_otr_command_flush_saved_floats =\page_one_command_flush_saved_floats,
+ \s!page_otr_command_flush_all_floats =\page_one_command_flush_all_floats,
+ \s!page_otr_command_flush_margin_blocks =\page_one_command_flush_margin_blocks,
+ \s!page_otr_command_test_column =\page_one_command_test_page,
+ \s!page_otr_command_flush_facing_floats =\page_one_command_flush_facing_floats
+]
+
+% \setupoutputroutine
+% [\s!singlecolumn]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-otr.lmt b/Master/texmf-dist/tex/context/base/mkxl/page-otr.lmt
new file mode 100644
index 00000000000..5c0bc96a9c5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-otr.lmt
@@ -0,0 +1,14 @@
+if not modules then modules = { } end modules ['page-otr'] = {
+ version = 1.001,
+ comment = "companion to page-otr.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+interfaces.implement {
+ name = "triggerpagebuilder",
+ public = true,
+ protected = true,
+ actions = tex.triggerbuildpage,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-otr.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-otr.mklx
new file mode 100644
index 00000000000..f2a341d4bc0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-otr.mklx
@@ -0,0 +1,333 @@
+%D \module
+%D [ file=page-otr,
+%D version=2012.01.25,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Output Routines,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Output Routines}
+
+%D This module will get some of the code from other modules. At the same time we
+%D provide a bit more control.
+
+% When issuing two \par\penalty-\plustenthousand's, only the first triggers the
+% otr. Is this an obscure feature or an optimization?
+
+\registerctxluafile{page-otr}{autosuffix}
+
+\unprotect
+
+% triggerpagebuilder % defined at the lua end
+
+\installcorenamespace{outputroutine}
+
+\installswitchcommandhandler \??outputroutine {outputroutine} \??outputroutine
+
+\newtoks\t_page_otr_commands
+\newtoks\t_page_otr_tracers
+
+\permanent\protected\def\defineoutputroutinecommand[#name]% doing multiple on one go saves syncing
+ {\processcommalist[#name]\page_otr_commands_define}
+
+\protected\def\page_otr_commands_define#name%
+ {\ifcsname#name\endcsname \else
+ \letcsname#name\endcsname\relax
+ \normalexpanded{\t_page_otr_commands{\the\t_page_otr_commands\noexpand\page_otr_commands_process{#name}}}%
+ \fi}
+
+\let\page_otr_commands_process\gobbleoneargument
+
+\appendtoks
+ \let\page_otr_commands_process\page_otr_specifics_preset
+ \the\t_page_otr_commands
+ \let\page_otr_commands_process\gobbleoneargument
+\to \everyswitchoutputroutine
+
+\protected\def\page_otr_specifics_preset#name%
+ {\edef\page_otr_specifics_command{\directoutputroutineparameter{#name}}% no inheritance of commands
+ \ifempty\page_otr_specifics_command
+ \writestatus{\currentoutputroutine}{- \expandafter\strippedcsname\csname#name\endcsname}%
+ \letcsname#name\endcsname\relax
+ \else
+ \writestatus{\currentoutputroutine}{+ \expandafter\strippedcsname\csname#name\endcsname}%
+ \letcsname#name\expandafter\endcsname\page_otr_specifics_command
+ \fi}
+
+\protected\def\page_otr_specifics_preset_normal#name%
+ {\edef\page_otr_specifics_command{\directoutputroutineparameter{#name}}% no inheritance of commands
+ \ifempty\page_otr_specifics_command
+ \letcsname#name\endcsname\relax
+ \else
+ \letcsname#name\expandafter\endcsname\page_otr_specifics_command
+ \fi}
+
+\protected\def\page_otr_specifics_preset_traced#name%
+ {\edef\page_otr_specifics_command{\directoutputroutineparameter{#name}}% no inheritance of commands
+ \ifempty\page_otr_specifics_command
+ \writestatus{\currentoutputroutine}{preset: - \expandafter\strippedcsname\csname#name\endcsname}%
+ \letcsname#name\endcsname\relax
+ \else
+ \writestatus{\currentoutputroutine}{preset: + \expandafter\strippedcsname\csname#name\endcsname}%
+ \letcsname#name\expandafter\endcsname\page_otr_specifics_command
+ \fi}
+
+\let\page_otr_specifics_preset\page_otr_specifics_preset_normal
+
+\permanent\protected\def\traceoutputroutines
+ {\the\t_page_otr_tracers}
+
+\appendtoks
+ \let\page_otr_specifics_preset\page_otr_specifics_preset_traced
+\to \t_page_otr_tracers
+
+%D We have a couple of output routines and the default one is
+%D the single column routine. Then there is a multicolumn variant
+%D that can be used mixed, and a columnset variant that is more
+%D exclusive.
+
+\installcorenamespace{otrtriggers}
+
+\newconstant\c_page_otr_eject_penalty \c_page_otr_eject_penalty -\plustenthousand
+\newconstant\c_page_otr_super_penalty \c_page_otr_super_penalty -\plustwentythousand
+\newcount \c_page_otr_trigger_penalty \c_page_otr_trigger_penalty -100010
+
+\newcount \c_page_otr_columns % we will share this one
+
+\newif \ifinotr % we keep this (name) for old times sake
+
+% \def\page_otr_update_page_goal#1#2%
+% {\global\c_page_otr_columns#2\relax
+% \pagegoal\dimexpr\vsize-\c_page_otr_columns\insertheights\relax}
+
+\appendtoks
+ \insertheights\zeropoint
+\to \everyaftershipout
+
+\protected\def\page_otr_message_b{\page_otr_message_s+}
+\protected\def\page_otr_message_e{\page_otr_message_s-}
+
+\protected\def\page_otr_message_s#sign#what%
+ {\writestatus
+ \currentoutputroutine
+ {#sign\space \space
+ #what\space \space
+ p:\the\outputpenalty,\space
+ r:\the\realpageno ,\space
+ c:\number\mofcolumns,\space
+ v:\the\vsize ,\space
+ g:\the\pagegoal ,\space
+ t:\the\pagetotal ,\space
+ i:\the\insertheights
+ \ifdim\pagetotal>\pagegoal
+ ,\space
+ d:\the\dimexpr\pagetotal-\pagegoal\relax
+ \fi}}
+
+\protected\def\page_otr_trigger#penalty%
+ {\begingroup
+ \par
+ \penalty#penalty%
+ \endgroup}
+
+\permanent\protected\def\installoutputroutine#invoke#action% \invoke \action
+ {\global\advance\c_page_otr_trigger_penalty\minusone
+ \frozen\protected\edef#invoke{\page_otr_trigger{\number\c_page_otr_trigger_penalty}}%
+ \setvalue{\??otrtriggers\number\c_page_otr_trigger_penalty}{#action}}
+
+\protected\def\page_otr_triggered_output_routine_traced
+ {\ifcsname\??otrtriggers\the\outputpenalty\endcsname
+ \page_otr_message_b{special}%
+ \csname\??otrtriggers\the\outputpenalty\endcsname % \lastnamedcs can be gone
+ \page_otr_message_e{special}%
+ \else
+ \page_otr_message_b{normal}%
+ \page_otr_command_routine
+ \page_otr_message_e{normal}%
+ \fi}
+
+\protected\def\page_otr_triggered_output_routine_normal
+ {\ifcsname\??otrtriggers\the\outputpenalty\endcsname
+ \lastnamedcs
+ \else
+ \page_otr_command_routine
+ \fi}
+
+\let\page_otr_triggered_output_routine\page_otr_triggered_output_routine_normal
+
+\appendtoks
+ \let\page_otr_triggered_output_routine\page_otr_triggered_output_routine_traced
+\to \t_page_otr_tracers
+
+%D The real routine handler:
+
+\ifdefined\everybeforeoutput \else \newtoks\everybeforeoutput \fi
+\ifdefined\everyafteroutput \else \newtoks\everyafteroutput \fi
+
+\def\page_otr_set_engine_output_routine#content%
+ {\global\output
+ {\inotrtrue
+ \the\everybeforeoutput
+ #content\relax
+ \the\everyafteroutput}}
+
+% Just as fuzzy (and in 'one' we are okay with \aftergroup anyway):
+%
+% \ifdefined\everybeforeoutputgroup \else \newtoks\everybeforeoutputgroup \fi
+% \ifdefined\everyafteroutputgroup \else \newtoks\everyafteroutputgroup \fi
+%
+% \def\page_otr_set_engine_output_routine#content%
+% {\the\everybeforeoutputgroup
+% \global\output
+% {\inotrtrue
+% \the\everybeforeoutput
+% #content\relax
+% \the\everyafteroutput
+% \aftergroup\the\aftergroup\everyafteroutputgroup}}
+%
+% \appendtoks
+% \ifnum\c_page_postponed_mode=\plusone
+% \page_postponed_blocks_flush % and then not in \page_otr_construct_and_shipout
+% \fi
+% \to \everyafteroutputgroup
+
+\page_otr_set_engine_output_routine\page_otr_triggered_output_routine
+
+\installoutputroutine\synchronizeoutput % use \triggerpagebuilder instead
+ {\ifvoid\normalpagebox\else
+ \unvbox\normalpagebox
+ % not \pagediscards as it does more harm than good
+ \fi}
+
+\installoutputroutine\discardpage
+ {\setbox\scratchbox\box\normalpagebox}
+
+% todo: \resetpagebreak -> everyejectpage
+
+\def\page_otr_trigger_output_routine
+ {\par
+ \ifvmode
+ \penalty\c_page_otr_eject_penalty
+ \fi
+ \resetpagebreak}
+
+\def\page_otr_fill_and_eject_page
+ {\par
+ \ifvmode
+ \vfill
+ \penalty\c_page_otr_eject_penalty
+ \fi
+ \resetpagebreak}
+
+\def\page_otr_eject_page
+ {\par
+ \ifvmode
+ \ifdim\pagetotal>\pagegoal \else
+ \normalvfil
+ \fi
+ \penalty\c_page_otr_eject_penalty
+ \fi
+ \resetpagebreak}
+
+\def\page_otr_eject_page_and_flush_inserts % can be an installed one
+ {\par
+ \ifvmode
+ \ifdim\pagetotal>\pagegoal \else
+ \normalvfil
+ \fi
+ \penalty\c_page_otr_super_penalty
+ \fi
+ \resetpagebreak}
+
+\def\page_otr_check_for_pending_inserts
+ {\ifnum\outputpenalty>\c_page_otr_super_penalty \else
+ \ifnum\insertpenalties>\zerocount
+ % something is being held over so we force a new page
+ \page_otr_force_another_page
+ \fi
+ \fi}
+
+\def\page_otr_force_another_page
+ {% we should actually remove the dummy line in the otr
+ \hpack to \hsize{}%
+ \kern-\topskip
+ \nobreak
+ \vfill
+ \penalty\c_page_otr_super_penalty
+ \resetpagebreak}
+
+%D For those who've read the plain \TEX\ book, we provide the next
+%D macro:
+
+\permanent\protected\def\bye
+ {\writestatus\m!system{Sorry, you're not done yet, so no goodbye!}}
+
+%D We define a few constants because that (1) provides some checking
+%D and (2) is handier when aligning definitions (checks nicer). Most
+%D routines will use ard codes names but sometimes we want to adapt,
+%D which is why we have these:
+
+\definesystemconstant{page_otr_command_routine}
+\definesystemconstant{page_otr_command_package_contents}
+\definesystemconstant{page_otr_command_set_vsize}
+\definesystemconstant{page_otr_command_set_hsize}
+\definesystemconstant{page_otr_command_synchronize_hsize}
+\definesystemconstant{page_otr_command_next_page}
+\definesystemconstant{page_otr_command_next_page_and_inserts}
+\definesystemconstant{page_otr_command_set_top_insertions}
+\definesystemconstant{page_otr_command_set_bottom_insertions}
+\definesystemconstant{page_otr_command_flush_top_insertions}
+\definesystemconstant{page_otr_command_flush_bottom_insertions}
+\definesystemconstant{page_otr_command_check_if_float_fits}
+\definesystemconstant{page_otr_command_set_float_hsize}
+\definesystemconstant{page_otr_command_flush_float_box}
+\definesystemconstant{page_otr_command_side_float_output}
+\definesystemconstant{page_otr_command_synchronize_side_floats}
+\definesystemconstant{page_otr_command_flush_floats}
+\definesystemconstant{page_otr_command_flush_side_floats}
+\definesystemconstant{page_otr_command_flush_saved_floats}
+\definesystemconstant{page_otr_command_flush_all_floats}
+\definesystemconstant{page_otr_command_flush_margin_blocks}
+\definesystemconstant{page_otr_command_test_column}
+\definesystemconstant{page_otr_command_flush_facing_floats}
+
+\definesystemconstant{singlecolumn}
+\definesystemconstant{multicolumn} % will move
+\definesystemconstant{columnset} % will move
+\definesystemconstant{pagecolumn} % will move
+
+\defineoutputroutinecommand
+ [\s!page_otr_command_routine,
+ \s!page_otr_command_package_contents,
+ \s!page_otr_command_set_vsize,
+ \s!page_otr_command_set_hsize,
+ \s!page_otr_command_synchronize_hsize, % for columns of different width
+ \s!page_otr_command_next_page,
+ \s!page_otr_command_next_page_and_inserts,
+ \s!page_otr_command_set_top_insertions,
+ \s!page_otr_command_set_bottom_insertions,
+ \s!page_otr_command_flush_top_insertions,
+ \s!page_otr_command_flush_bottom_insertions,
+ \s!page_otr_command_check_if_float_fits,
+ \s!page_otr_command_set_float_hsize,
+ \s!page_otr_command_flush_float_box,
+ \s!page_otr_command_side_float_output, % name will change as will hooks
+ \s!page_otr_command_synchronize_side_floats,
+ \s!page_otr_command_flush_floats,
+ \s!page_otr_command_flush_side_floats,
+ \s!page_otr_command_flush_saved_floats,
+ \s!page_otr_command_flush_all_floats,
+ \s!page_otr_command_flush_margin_blocks,
+ \s!page_otr_command_test_column,
+ \s!page_otr_command_flush_facing_floats]
+
+\appendtoks
+ \setupoutputroutine[\s!singlecolumn]%
+\to \everydump
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-par.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-par.mkxl
new file mode 100644
index 00000000000..c198784dc4d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-par.mkxl
@@ -0,0 +1,73 @@
+%D \module
+%D [ file=page-par, % copied from page-lin
+%D version=1997.03.31,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Line Numbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Paragraph Numbering}
+
+\unprotect
+
+\installcorenamespace {paragraphnumbering}
+\installcorenamespace {paragraphnumberingvariants}
+
+\installsimplecommandhandler \??paragraphnumbering {paragraphnumbering} \??paragraphnumbering
+
+\definecounter[\v!paragraph]
+
+\permanent\let\showparagraphnumber\relax
+
+\appendtoks
+ \page_par_check_state
+\to \everysetupparagraphnumbering
+
+\protected\def\page_par_check_state
+ {\rawprocesscommacommand[\paragraphnumberingparameter\c!state]\page_par_check_state_step}
+
+\def\page_par_check_state_step#1%
+ {\ifcsname\??paragraphnumberingvariants#1\endcsname
+ \lastnamedcs
+ \fi}
+
+\setvalue{\??paragraphnumberingvariants\v!start}%
+ {\enforced\let\showparagraphnumber\page_par_show_number_normal}
+
+\setvalue{\??paragraphnumberingvariants\v!stop}%
+ {\enforced\let\showparagraphnumber\relax}
+
+\setvalue{\??paragraphnumberingvariants\v!line}%
+ {\enforced\let\showparagraphnumber\page_par_show_number_lines}
+
+\setvalue{\??paragraphnumberingvariants\v!reset}%
+ {\strc_counters_reset\v!paragraph
+ \enforced\let\showparagraphnumber\page_par_show_number_normal}
+
+\permanent\protected\def\page_par_show_number_normal
+ {\strc_counters_increment\v!paragraph
+ \inleftmargin % todo: \c!location, only a few make sense
+ {\hfill % no complaints
+ \tf % \tf normalizes em
+ \useparagraphnumberingstyleandcolor\c!style\c!color
+ \convertedcounter[\v!paragraph]%
+ \kern\paragraphnumberingparameter\c!distance}}
+
+\permanent\protected\def\page_par_show_number_lines
+ {\ifnumberinglines
+ \page_par_show_number_normal
+ \fi}
+
+\setupparagraphnumbering
+ [\c!state=\v!stop,
+ %\c!location,
+ %\c!style=,
+ %\c!color=,
+ \c!distance=\ifcase\c_page_lines_location2\emwidth\else\zeropoint\fi] % will change
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-pcl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-pcl.mkxl
new file mode 100644
index 00000000000..6d186a4cd4c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-pcl.mkxl
@@ -0,0 +1,853 @@
+%D \module
+%D [ file=page-pcl,
+%D version=2017.11.08,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Columns,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Page Columns}
+
+%D This is very experimental code! We took a bit from the mixed columns and single
+%D column page code. This one works acceptable with floats and is for whole double
+%D column documents. We don't balance (yet). Footnotes are per column. One can have
+%D side floats too. No balancing and other fancy features.
+%D
+%D Don't use this in production! Although the main approach will stay there might be
+%D changes in the way floats are dealt with. Not much testing has been done but as
+%D we stay close to the single column mode we expect most to just work. Only floats
+%D are the (usual) pain. Backgrounds, line numbering, etc.\ not tested either.
+
+\unprotect
+
+\definemeasure[onecolumn] [\columnwidth]
+\definemeasure[twocolumns] [\dimexpr\plustwo \columnwidth+ \columndistance\relax]
+\definemeasure[threecolumns][\dimexpr\plusthree\columnwidth+\plustwo \columndistance\relax]
+\definemeasure[fourcolumns] [\dimexpr\plusfour \columnwidth+\plusthree\columndistance\relax]
+
+\newcount \c_page_col_n_of_columns \c_page_col_n_of_columns\plusone
+\newcount \c_page_col_current \c_page_col_current \plusone
+\newdimen \d_page_col_distance
+\newdimen \d_page_col_max_height
+\newdimen \d_page_col_max_width
+%newdimen \d_page_col_balance_step
+\newdimen \d_page_col_column_width
+\newdimen \d_page_col_top_height
+\newdimen \d_page_col_top_width
+\newdimen \d_page_col_available
+\newdimen \d_page_col_sofar
+\newconditional\c_page_col_page
+
+%D We need to step over empty columns.
+
+\protected\def\page_col_command_next_page
+ {\page_col_eject_page}
+
+\permanent\protected\def\page_col_column
+ {\page_otr_eject_page}
+
+\protected\def\page_col_eject_page
+ {\begingroup
+ \scratchcountertwo \realpageno
+ \page_otr_eject_page
+ \scratchcounterone \c_page_col_current
+ \scratchcounterthree\zerocount
+ \doloop{%
+ \ifnum\scratchcounterthree>\plushundred
+ % too many attempts
+ \exitloop
+ \orelse\ifnum\realpageno>\scratchcountertwo
+ % we advanced at least one page so we're done
+ \exitloop
+ \else
+ \ifnum\scratchcounterone=\c_page_col_current
+ \dontleavehmode\null
+ \fi
+ \page_otr_eject_page
+ \scratchcounterone\c_page_col_current
+ \advance\scratchcounterthree\plusone
+ \fi
+ }%
+ \endgroup}
+
+%D \unknown
+
+\protected\def\page_col_command_next_page_and_inserts
+ {\page_otr_eject_page_and_flush_inserts}
+
+%D \unknown
+
+\protected\def\page_col_command_set_hsize
+ {\global\hsize\d_page_col_column_width\relax
+ \global\d_page_col_available\dimexpr
+ \numexpr\c_page_col_n_of_columns-\c_page_col_current+\plusone\relax\d_page_col_column_width
+ + \numexpr\c_page_col_n_of_columns-\c_page_col_current \relax\d_page_col_distance
+ \relax
+ \global\d_page_col_sofar
+ \ifnum\c_page_col_n_of_columns=\plusone
+ \zeropoint
+ \else
+ \numexpr\c_page_col_n_of_columns-\plusone\relax
+ \dimexpr\d_page_col_column_width+\d_page_col_distance\relax
+ \fi
+ % consistent with mixed:
+ \textwidth\d_page_col_column_width}
+
+%D \unknown
+
+\protected\def\page_col_command_set_vsize % \page_one_command_set_vsize minus the pagegoal setting
+ {\ifgridsnapping
+ \ifcase\layoutlines
+ \getrawnoflines\textheight
+ \else
+ \noflines\layoutlines
+ \fi
+ \global\vsize\noflines\openlineheight
+ \else
+ \global\vsize\textheight
+ \fi}
+
+%D \unknown
+
+\def\page_col_registered_text_area_b#1%
+ {\begingroup
+ \makeupwidth\d_page_col_column_width
+ \page_one_registered_text_area_b{#1}%
+ \endgroup}
+
+\protected\def\page_col_command_package_contents#1#2% \box<n> \unvbox<n> % this one will be redone (checked)
+ {\bgroup
+ \setbox\b_page_one_contents\vbox to \textheight
+ {\page_one_registered_text_area_a#1#2}%
+ \page_one_command_package_show_state
+ \ht\b_page_one_contents\textheight
+ \page_col_registered_text_area_b
+ {\box\b_page_one_contents}%
+ \egroup}
+
+\protected\def\page_col_command_package_contents_one#1#2% \box<n> \unvbox<n> % this one will be redone (checked)
+ {\bgroup
+ \forgetall
+ % see one for comments as it is similar
+ \strc_notes_check_if_bottom_present
+ \d_page_one_natural_depth\dp#2\relax
+ \setbox\b_page_one_contents\vbox to \textheight
+ {\page_col_command_flush_top_insertions
+ \page_one_registered_text_area_a#1#2%
+ \hsize\d_page_col_column_width
+ \ifgridsnapping
+ \vskip\dimexpr\openstrutdepth-\d_page_one_natural_depth\relax
+ \prevdepth\openstrutdepth
+ \page_col_command_flush_bottom_insertions
+ \vfil
+ \orelse\ifcase\bottomraggednessmode
+ % ragged (default)
+ \vskip\dimexpr\openstrutdepth-\d_page_one_natural_depth\relax
+ \prevdepth\openstrutdepth
+ \page_col_command_flush_bottom_insertions
+ \vfil
+ \or
+ % align (normal)
+ \page_col_command_flush_bottom_insertions
+ \or
+ % baseline
+ \kern\dimexpr\maxdepth-\d_page_one_natural_depth\relax
+ \page_col_command_flush_bottom_insertions
+ \fi
+ \fakepagenotes}%
+ \page_one_command_package_show_state
+ \ifconditional\c_notes_bottom_present
+ \ifgridsnapping
+ \ifcase\layoutlines
+ \getrawnoflines\textheight
+ \else
+ \noflines\layoutlines
+ \fi
+ \scratchoffset\dimexpr\numexpr\noflines-\plusone\relax\lineheight+\topskip\relax
+ \else
+ \scratchoffset\ht\b_page_one_contents
+ \fi
+ \setbox\b_page_one_bottom_notes\hpack
+ {\hsize\d_page_col_column_width
+ \setupnotes[\c!width=\textwidth]%
+ \lower\scratchoffset\vbox{\placebottomnotes\par\kern\zeropoint}}%
+ \ht\b_page_one_contents \zeropoint
+ \wd\b_page_one_contents \zeropoint
+ \ht\b_page_one_bottom_notes\zeropoint
+ \wd\b_page_one_bottom_notes\zeropoint
+ \wd\b_page_one_bottom_notes\d_page_col_column_width
+ \page_col_registered_text_area_b
+ {\vpack to \textheight
+ {\hpack{\box\b_page_one_contents\box\b_page_one_bottom_notes}}}%
+ \else
+ \ht\b_page_one_contents\textheight
+ \wd\b_page_one_contents\d_page_col_column_width
+ \page_col_registered_text_area_b
+ {\box\b_page_one_contents}%
+ \fi
+ \egroup}
+
+%D \unknown
+
+\protected\def\page_col_command_side_float_output
+ {% % %
+ \ifvoid\namedinsertionnumber\s!topfloat\else
+ \scratchwidth\wd\namedinsertionnumber\s!topfloat
+ \ifdim\scratchwidth>\d_page_col_top_width
+ \global\d_page_col_top_width \scratchwidth
+ \fi
+ \global\d_page_col_top_height\ht\namedinsertionnumber\s!topfloat
+ \fi
+ % % %
+ \setbox\scratchbox\vbox\bgroup
+ \page_col_command_package_contents_one\unvbox\normalpagebox
+ \egroup
+ \putboxincache\s!pagecolumn{\number\c_page_col_current}\scratchbox
+ \ifnum\c_page_col_current=\c_page_col_n_of_columns
+ \page_col_routine_package
+ \page_otr_construct_and_shipout\box\normalpagebox\plusone
+ \global\c_page_col_current\plusone
+ \global\d_page_col_top_height\zeropoint
+ \global\d_page_col_top_width\zeropoint
+ % \page_col_command_flush_top_insertions
+ % \page_col_command_flush_floats
+ \else
+ \ifdim\d_page_col_top_width>\zeropoint
+ \ifdim\dimexpr\d_page_col_top_width>\d_page_col_sofar\relax
+ \begingroup
+ \floatingpenalty\zerocount
+ \insert\namedinsertionnumber\s!topfloat\bgroup
+ \vbox to \d_page_col_top_height{\vss}
+ % can be an option
+ \page_col_command_flush_top_insertions
+ \page_col_command_flush_floats
+ % so far till option
+ \egroup
+ \endgroup
+ \fi
+ \fi
+ \global\advance\c_page_col_current\plusone
+ \fi
+ %
+ \page_col_command_set_vsize
+ \page_col_command_set_hsize}
+
+% use \currentmixedcolumns instead of \recurselevel
+
+\def\page_col_routine_package_step
+ {% needs packaging anyway
+ \getboxfromcache{\s!pagecolumn}{\number\recurselevel}\scratchbox
+ \page_lines_add_numbers_to_box\scratchbox\recurselevel\c_page_col_n_of_columns\plusone % new
+ \page_marks_synchronize_column\plusone\c_page_col_n_of_columns\recurselevel\scratchbox
+ % backgrounds
+ \anch_mark_column_box\scratchbox\recurselevel
+ \mixedcolumnseparatorheight\ht\scratchbox
+ \mixedcolumnseparatordepth \dp\scratchbox
+ \inheritedpagecolumnsframedbox\recurselevel\scratchbox}
+
+\def\page_col_routine_package
+ {\global\setbox\normalpagebox\hbox to \makeupwidth\bgroup
+ \edef\p_separator{\pagecolumnsparameter\c!separator}%
+ \pagecolumnseparatorwidth\d_page_col_distance
+ \edef\p_direction{\pagecolumnsparameter\c!direction}%
+ \ifx\p_direction\v!reverse
+ \dostepwiserecurse\c_page_col_n_of_columns\plusone\minusone
+ {\page_col_routine_package_step
+ \ifnum\recurselevel>\plusone
+ \page_col_routine_package_separate
+ \fi}%
+ \else
+ \dorecurse\c_page_col_n_of_columns
+ {\page_col_routine_package_step
+ \ifnum\recurselevel<\c_page_col_n_of_columns
+ \page_col_routine_package_separate
+ \fi}%
+ \fi
+ \egroup
+ \resetboxesincache{\s!pagecolumn}}
+
+%D \unknown
+
+% \protected\def\page_col_command_check_if_float_fits
+% {\ifconditional\c_page_floats_not_permitted
+% \global\setfalse\c_page_floats_room
+% %\orelse\ifabsdim\dimexpr\d_page_col_available-\naturalfloatwd\relax<\onepoint
+% \orelse\ifdim\dimexpr\d_page_col_available-\naturalfloatwd\relax>-\onepoint
+% \global\settrue\c_page_floats_room
+% \else
+% \global\setfalse\c_page_floats_room
+% \fi
+% \ifconditional\c_page_floats_room
+% \begingroup
+% \scratchdimen\dimexpr\pagetotal+\lineheight\relax
+% \ifdim\scratchdimen>\pagegoal
+% \goodbreak
+% \ifdim\dimexpr\d_page_col_available-\naturalfloatwd\relax>\onepoint
+% \global\setfalse\c_page_floats_room
+% \else
+% \global\settrue\c_page_floats_room
+% \fi
+% \fi
+% \endgroup
+% \fi
+% \ifconditional\c_page_floats_room
+% \ifdim\pagetotal>\zeropoint
+% \scratchdimenone\dimexpr\pagetotal+\floatheight+\d_strc_floats_top-\pageshrink\relax
+% \scratchdimentwo\pagegoal
+% \relax % needed
+% \ifcase\c_page_one_float_method
+% % method 0 : raw
+% \or
+% % method 1 : safe
+% \advance\scratchdimentwo -\strutdp
+% \or
+% % method 2 : tight
+% \advance\scratchdimenone -\onepoint
+% \fi
+% \relax % really needed ! ! ! !
+% \ifdim\scratchdimenone>\scratchdimentwo
+% \global\setfalse\c_page_floats_room
+% \fi
+% \fi
+% \ifconditional\c_page_floats_room
+% \global\setbox\floatbox\hpack to \d_page_col_available{\hss\box\floatbox\hss}%
+% \fi
+% \fi}
+
+\protected\def\page_col_command_check_if_float_fits
+ {\ifconditional\c_page_floats_not_permitted
+ % forget about it anyway
+ \global\setfalse\c_page_floats_room
+ \else
+ % first we check the current column
+% \ifdim\dimexpr\d_page_col_width-\naturalfloatwd\relax>-\onepoint
+ \ifdim\dimexpr\hsize-\naturalfloatwd\relax>-\onepoint
+ \global\settrue\c_page_floats_room
+ \else
+ \global\setfalse\c_page_floats_room
+ \fi
+ \ifconditional\c_page_floats_room
+ % we fit in the column but do we have room
+ \ifdim\dimexpr\pagetotal+\lineheight\relax>\pagegoal
+ % try again later
+ \goodbreak
+ \fi
+ \ifdim\pagetotal>\zeropoint
+ \scratchdimenone\dimexpr\pagetotal+\floatheight+\d_strc_floats_top-\pageshrink\relax
+ \scratchdimentwo\pagegoal
+ \relax % needed
+ \ifcase\c_page_one_float_method
+ % method 0 : raw
+ \or
+ % method 1 : safe
+ \advance\scratchdimentwo -\strutdp
+ \or
+ % method 2 : tight
+ \advance\scratchdimenone -\onepoint
+ \fi
+ \relax % really needed ! ! ! !
+ \ifdim\scratchdimenone>\scratchdimentwo
+ % there is no room, give up
+ \global\setfalse\c_page_floats_room
+ % now we can decide on a top float
+% \fi
+ \else
+% \ifconditional\c_page_floats_room
+% \global\setbox\floatbox\hpack to \d_page_col_float_available{\hss\box\floatbox\hss}%
+% \fi
+ \fi
+ \fi
+ \fi
+ \fi}
+
+%D \unknown
+
+\def\page_col_set_float_pack_hsize
+ {\ifnum\c_page_col_current=\c_page_col_n_of_columns
+ \c_page_col_current\plusone
+ \else
+ \advance\c_page_col_current\plusone
+ \fi
+ \page_col_command_set_hsize
+ \hsize\d_page_col_available}
+
+\protected\def\page_col_command_flush_floats
+ {\global\settrue\c_page_floats_flushing
+ \ifconditional\c_page_floats_some_waiting
+ \par
+ \page_col_set_float_pack_hsize
+ \page_col_command_flush_floats_indeed
+ \fi
+ \global\savednoffloats\zerocount
+ \global\setfalse\c_page_floats_some_waiting
+ \global\setfalse\c_page_floats_flushing}
+
+\def\page_floats_show_pack_state_indeed#1%
+ {\llap{\smash{\backgroundline[black]{\strut\smallinfofont\white#1\space\the\nofcollectedfloats\space of\space\the\savednoffloats:\the\hsize}}\hskip.25\emwidth}}
+
+\installtextracker
+ {floats.collecting}
+ {\let\page_floats_show_pack_state\page_floats_show_pack_state_indeed}
+ {\let\page_floats_show_pack_state\gobbleoneargument}
+
+\let\page_floats_show_pack_state\gobbleoneargument
+
+\def\page_col_command_flush_floats_indeed % much in common with OTRSET
+ {\ifconditional\c_page_floats_some_waiting
+ \ifconditional\c_page_floats_compress_flushed
+ \setfalse\c_page_floats_center_box % not needed as we do call directly
+ \page_floats_collect\s!text\hsize\d_page_floats_compress_distance
+ %
+ \ifnum\nofcollectedfloats=\plusone
+ \ifdim\naturalfloatwd>\hsize
+ \nofcollectedfloats\zerocount
+ \fi
+ \fi
+ \ifnum\nofcollectedfloats>\zerocount
+ \global\setbox\floatbox\hpack to \hsize
+ {\page_floats_show_pack_state F%
+ \hfil
+ \dorecurse\nofcollectedfloats
+ {\ifcase\columndirection % nog document wide
+ \page_floats_flush\s!text\plusone
+ \else
+ \page_floats_flush\s!text{\the\numexpr\nofcollectedfloats-\recurselevel+1\relax}%
+ \fi
+ % this could happen at the lua end instead
+ \scratchdimen\dimexpr\wd\floatbox-\naturalfloatwd\relax
+ \ifdim\scratchdimen<\zeropoint
+ \global\setbox\floatbox\hpack spread -\scratchdimen{\hss\box\floatbox\hss}%
+ \fi
+ %
+ \ifdim\wd\floatbox>\textwidth % \hsize
+ \hpack to \textwidth{\hss\box\floatbox\hss}% \textwidth
+ \else
+ \box\floatbox
+ \fi
+ \ifnum\recurselevel<\nofcollectedfloats
+ \hfil
+ \fi}%
+ \hfil}%
+ \doplacefloatbox
+ % \page_one_insert_top_float
+ \doubleexpandafter\page_col_command_flush_floats_indeed
+ \else
+ % todo
+ \fi
+ \else
+ \page_floats_get
+ % \page_one_insert_top_float
+ \doplacefloatbox
+ \doubleexpandafter\page_col_command_flush_floats_indeed
+ \fi
+ \fi}
+
+\protected\def\page_col_command_flush_saved_floats % like one
+ {\global\d_page_floats_inserted_top\zeropoint
+ \global\d_page_floats_inserted_bottom\zeropoint
+ \ifconditional\c_page_floats_flushing \else
+ \page_col_command_set_top_insertions
+ \page_col_command_set_bottom_insertions
+ \ifconditional\c_page_floats_some_waiting
+ \doif{\rootfloatparameter\c!cache}\v!no\page_col_command_flush_floats % could be _otr_
+ \orelse\ifconditional\c_page_margin_blocks_present
+ \page_col_command_flush_floats
+ \fi
+ \fi}
+
+\protected\def\page_col_command_set_top_insertions
+ {\bgroup
+ \ifconditional\c_page_floats_some_waiting
+ \noffloatinserts\zerocount
+ \let\totaltopinserted\!!zeropoint
+ \page_col_set_float_pack_hsize
+ \page_col_command_set_top_insertions_indeed
+ \ifnum\rootfloatparameter\c!nbottom=\zerocount
+ \ifnum\rootfloatparameter\c!nlines>\zerocount
+ \ifdim\totaltopinserted>\zeropoint\relax
+ \ifdim\dimexpr\rootfloatparameter\c!nlines\lineheight+\totaltopinserted\relax>\textheight
+ \showmessage\m!floatblocks8{\rootfloatparameter\c!nlines}%
+ \page_otr_fill_and_eject_page % was tripple: vfilll
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \egroup}
+
+\permanent\def\d_page_col_collected_top_float_height % pseudo
+ {\dimexpr
+ \d_page_floats_inserted_top +
+ \maxcollectedfloatstotal +
+ \ifdim\d_strc_floats_top>\d_strc_floats_bottom
+ \d_strc_floats_top
+ \else
+ \d_strc_floats_bottom
+ \fi
+ \relax}
+
+\def\page_col_command_set_top_insertions_indeed
+ {\ifnum\noffloatinserts<\c_page_floats_n_of_top
+ \ifcase\savednoffloats
+ \let\page_col_command_set_top_insertions_indeed\relax
+ \else
+ \page_floats_collect\s!text\hsize\emwidth
+ \ifdim\d_page_col_collected_top_float_height<\textheight
+ \global\setbox\floatbox\hpack to \hsize
+ {\page_floats_show_pack_state T%
+ \hfil
+ \dorecurse\nofcollectedfloats
+ {\ifcase\columndirection % nog document wide
+ \page_floats_flush\s!text\plusone
+ \else
+ \page_floats_flush\s!text{\the\numexpr\nofcollectedfloats-\recurselevel+1\relax}%
+ \fi
+ % this could happen at the lua end instead
+ \scratchdimen\dimexpr\wd\floatbox-\naturalfloatwd\relax
+ \ifdim\scratchdimen<\zeropoint
+ \global\setbox\floatbox\hpack spread -\scratchdimen{\hss\box\floatbox\hss}%
+ \fi
+ %
+ \ifdim\wd\floatbox>\makeupwidth % \hsize
+ \hpack to \makeupwidth{\hss\box\floatbox\hss}%
+ \else
+ \box\floatbox
+ \fi
+ \ifnum\recurselevel<\nofcollectedfloats
+ \hfil
+ \fi}%
+ \hfil}%
+ \page_one_prepare_top_float
+ \xdef\totaltopinserted{\the\d_page_floats_inserted_top}%
+ \page_one_insert_top_float
+ \ifconditional\c_page_floats_some_waiting
+ \advance\noffloatinserts \plusone
+ \else
+ \noffloatinserts\c_page_floats_n_of_top\relax
+ \fi
+ \page_floats_report_flushed
+ \else
+ \let\page_col_command_set_top_insertions_indeed\relax
+ \fi
+ \fi
+ \else
+ \ifconditional\c_page_floats_some_waiting
+ \showmessage\m!floatblocks6{\the\c_page_floats_n_of_top}%
+ \fi
+ \let\page_col_command_set_top_insertions_indeed\relax
+ \fi
+ \page_col_command_set_top_insertions_indeed}
+
+\let\page_col_command_flush_top_insertions \page_one_command_flush_top_insertions
+\let\page_col_command_flush_bottom_insertions\page_one_command_flush_bottom_insertions
+
+\let\page_col_command_set_bottom_insertions \page_one_command_set_bottom_insertions
+
+\let\page_col_command_flush_float_box \page_one_command_flush_float_box
+\let\page_col_command_synchronize_side_floats\page_one_command_synchronize_side_floats
+\let\page_col_command_flush_side_floats \page_one_command_flush_side_floats
+\let\page_col_command_flush_margin_blocks \page_one_command_flush_margin_blocks
+\let\page_col_command_test_page \page_one_command_test_page
+
+%D The separator code is more or less the same as mixed columns but we need
+%D to compensate for the top floats so we comment a bit for now.
+
+\newdimen\pagecolumnseparatorheight
+\newdimen\pagecolumnseparatordepth
+\newdimen\pagecolumnseparatorwidth
+
+% \installcorenamespace{pagecolumnsseparator}
+%
+% \protected\def\installpagecolumnseparator#1#2%
+% {\setvalue{\??pagecolumnsseparator#1}{#2}}
+%
+% \installpagecolumnseparator\v!rule
+% {\vrule
+% \s!width \pagecolumnsparameter\c!rulethickness
+% \s!height\pagecolumnseparatorheight
+% \s!depth \pagecolumnseparatordepth
+% \relax}
+%
+% \def\page_col_routine_package_separate
+% {\ifcsname\??pagecolumnsseparator\p_separator\endcsname
+% \page_col_command_inject_separator
+% \else
+% \hss
+% \fi}
+%
+% \protected\def\page_col_command_inject_separator
+% {\begingroup
+% \setbox\scratchbox\hbox to \zeropoint \bgroup
+% \hss
+% \starttextproperties
+% \usepagecolumnscolorparameter\c!rulecolor
+% \begincsname\??pagecolumnsseparator\p_separator\endcsname % was \c!rule
+% \stoptextproperties
+% \hss
+% \egroup
+% \ht\scratchbox\zeropoint
+% \dp\scratchbox\zeropoint
+% \hss
+% \box\scratchbox
+% \hss
+% \endgroup}
+
+\def\page_col_routine_package_separate
+ {\hss}
+
+%D \unknown
+
+\protected\def\page_col_command_routine
+ {\page_sides_output_routine}
+
+% % not:
+%
+% \protected\def\page_col_command_routine
+% {\ifconditional\c_page_sides_short
+% \page_sides_output_routine_yes_column
+% \else
+% \page_sides_output_routine_nop_column
+% \fi}
+%
+% \let\page_sides_output_routine_nop_column\page_sides_output_routine
+%
+% \def\page_sides_output_routine_yes_column % this might become the main one too
+% {\unvbox\normalpagebox % bah, and the discards?
+% %\page_col_column
+% \column % \page
+% % why was this \global\holdinginserts\zerocount
+% \global\setfalse\c_page_sides_short}
+
+\let\page_col_command_flush_all_floats\relax
+
+%D \unknown
+
+\defineoutputroutine
+ [\s!pagecolumn]
+ [\s!page_otr_command_routine =\page_col_command_routine,
+ \s!page_otr_command_package_contents =\page_col_command_package_contents,
+ \s!page_otr_command_set_vsize =\page_col_command_set_vsize,
+ \s!page_otr_command_set_hsize =\page_col_command_set_hsize,
+ % \s!page_otr_command_synchronize_hsize =\page_col_command_synchronize_hsize, % not done
+ \s!page_otr_command_next_page =\page_col_command_next_page,
+ \s!page_otr_command_next_page_and_inserts =\page_col_command_next_page_and_inserts,
+ \s!page_otr_command_set_top_insertions =\page_col_command_set_top_insertions,
+ \s!page_otr_command_set_bottom_insertions =\page_col_command_set_bottom_insertions,
+ \s!page_otr_command_flush_top_insertions =\page_col_command_flush_top_insertions,
+ \s!page_otr_command_flush_bottom_insertions=\page_col_command_flush_bottom_insertions,
+ \s!page_otr_command_check_if_float_fits =\page_col_command_check_if_float_fits,
+ % \s!page_otr_command_set_float_hsize =\page_col_command_set_float_hsize, % not done
+ \s!page_otr_command_flush_float_box =\page_col_command_flush_float_box,
+ \s!page_otr_command_side_float_output =\page_col_command_side_float_output,
+ \s!page_otr_command_synchronize_side_floats=\page_col_command_synchronize_side_floats,
+ \s!page_otr_command_flush_floats =\page_col_command_flush_floats,
+ \s!page_otr_command_flush_side_floats =\page_col_command_flush_side_floats,
+ \s!page_otr_command_flush_saved_floats =\page_col_command_flush_saved_floats,
+ \s!page_otr_command_flush_all_floats =\page_col_command_flush_all_floats,
+ \s!page_otr_command_flush_margin_blocks =\page_col_command_flush_margin_blocks,
+ \s!page_otr_command_test_column =\page_col_command_test_page
+ ]
+
+%D \unknown
+
+\installfloatmethod \s!pagecolumn \v!here \page_one_place_float_here
+\installfloatmethod \s!pagecolumn \v!force \page_one_place_float_force
+\installfloatmethod \s!pagecolumn \v!left \page_one_place_float_left
+\installfloatmethod \s!pagecolumn \v!right \page_one_place_float_right
+\installfloatmethod \s!pagecolumn \v!text \page_one_place_float_text
+\installfloatmethod \s!pagecolumn \v!top \page_one_place_float_top
+\installfloatmethod \s!pagecolumn \v!bottom \page_one_place_float_bottom
+\installfloatmethod \s!pagecolumn \v!auto \page_one_place_float_auto
+\installfloatmethod \s!pagecolumn \v!margin \page_one_place_float_margin
+\installfloatmethod \s!pagecolumn \v!opposite \page_one_place_float_face
+\installfloatmethod \s!pagecolumn \v!page \page_one_place_float_page
+\installfloatmethod \s!pagecolumn \v!leftpage \page_one_place_float_leftpage
+\installfloatmethod \s!pagecolumn \v!rightpage \page_one_place_float_rightpage
+\installfloatmethod \s!pagecolumn \v!inmargin \page_one_place_float_inmargin
+\installfloatmethod \s!pagecolumn \v!inleft \page_one_place_float_leftmargin
+\installfloatmethod \s!pagecolumn \v!inright \page_one_place_float_rightmargin
+\installfloatmethod \s!pagecolumn \v!leftmargin \page_one_place_float_leftmargin
+\installfloatmethod \s!pagecolumn \v!rightmargin \page_one_place_float_rightmargin
+\installfloatmethod \s!pagecolumn \v!leftedge \page_one_place_float_leftedge
+\installfloatmethod \s!pagecolumn \v!rightedge \page_one_place_float_rightedge
+\installfloatmethod \s!pagecolumn \v!somewhere \page_one_place_float_somewhere
+\installfloatmethod \s!pagecolumn \v!backspace \page_one_place_float_backspace
+\installfloatmethod \s!pagecolumn \v!cutspace \page_one_place_float_cutspace
+%installfloatmethod \s!pagecolumn \s!tblr \page_one_place_float_top
+%installfloatmethod \s!pagecolumn \s!lrtb \page_one_place_float_top
+%installfloatmethod \s!pagecolumn \s!tbrl \page_one_place_float_top
+%installfloatmethod \s!pagecolumn \s!fxtb \page_one_place_float_top
+%installfloatmethod \s!pagecolumn \s!rltb \page_one_place_float_top
+%installfloatmethod \s!pagecolumn \s!btlr \page_one_place_float_bottom
+%installfloatmethod \s!pagecolumn \s!lrbt \page_one_place_float_bottom
+%installfloatmethod \s!pagecolumn \s!btrl \page_one_place_float_bottom
+%installfloatmethod \s!pagecolumn \s!rlbt \page_one_place_float_bottom
+%installfloatmethod \s!pagecolumn \s!fxbt \page_one_place_float_bottom
+%installfloatmethod \s!pagecolumn \s!fixd \page_one_place_float_force
+
+\installfloatmethod \s!pagecolumn \v!local \somelocalfloat
+
+%D The main interface:
+
+\installcorenamespace{pagecolumns}
+
+\installframedcommandhandler \??pagecolumns {pagecolumns} \??pagecolumns
+
+\setuppagecolumns
+ [\c!distance=1.5\bodyfontsize,
+ \c!n=\plustwo,
+ \c!page=\v!yes,
+ %\c!align=, % inherit (also replaces tolerance)
+ %\c!before=,
+ %\c!after=,
+ %\c!separator=\v!none,
+ %\c!setups=,
+ %\c!balance=\v!no,
+ %\c!blank={\v!line,\v!fixed}, yes or no
+ \c!frame=\v!off,
+ \c!strut=\v!no,
+ \c!offset=\v!overlay,
+ %\c!maxheight=\textheight,
+ \c!maxwidth=\makeupwidth,
+ %\c!grid=\v!tolerant,
+ %\c!internalgrid=\v!line,
+ \c!direction=\v!normal]
+
+\appendtoks % could become an option
+ \frozen\instance\setuevalue{\e!start\currentpagecolumns}{\startpagecolumns[\currentpagecolumns]}%
+ \frozen\instance\setuevalue{\e!stop \currentpagecolumns}{\stoppagecolumns}%
+\to \everydefinepagecolumns
+
+\def\page_col_pickup_preceding
+ {\begingroup
+ \setupoutputroutine[\s!mixedcolumn]%
+ \c_page_mix_routine\c_page_mix_routine_intercept
+ \page_otr_trigger_output_routine
+ \ifvoid\b_page_mix_preceding \else
+ % moved here, before the packaging
+ \page_postprocessors_linenumbers_deepbox\b_page_mix_preceding
+ % we need to avoid unvboxing with successive balanced on one page
+ \global\setbox\b_page_mix_preceding\vbox\bgroup
+ % yes or no: \forcestrutdepth
+ \unvbox\b_page_mix_preceding
+ \forcestrutdepth
+ \egroup
+ \wd\b_page_mix_preceding\makeupwidth
+ \global\d_page_mix_preceding_height\ht\b_page_mix_preceding
+ \fi
+ \endgroup}
+
+\def\page_col_flush_preceding
+ {\ifvoid\b_page_mix_preceding \else
+ % this is just one method but ok for now
+ \begingroup
+ % we might need more but for now this is ok
+ \setupfloat[\c!spacebefore=,\c!spaceafter=]%
+ \startplacefigure[\c!location={\v!top,\v!none}]%
+ \box\b_page_mix_preceding
+ \stopplacefigure
+ \endgroup
+ \fi}
+
+\permanent\tolerant\protected\def\startpagecolumns[#1]%
+ {\begingroup
+ \begingroup
+ \doifelseassignment{#1}%
+ {\let\currentpagecolumns\empty
+ \setuppagecolumns[#1]}%
+ {\edef\currentpagecolumns{#1}}%
+ \edef\p_page{\pagecolumnsparameter\c!page}%
+ \ifempty\p_page
+ \setfalse\c_page_col_page
+ \orelse\ifx\p_page\v!no
+ \setfalse\c_page_col_page
+ \else
+ \settrue\c_page_col_page
+ \page[\p_page]%
+ \fi
+ \c_page_col_n_of_columns\pagecolumnsparameter\c!n\relax
+ \ifnum\c_page_col_n_of_columns>\plusone
+ \expandafter\page_col_start_yes
+ \else
+ \expandafter\page_col_start_nop
+ \fi} % public
+
+\aliased\let\stoppagecolumns\relax
+
+\protected\def\page_col_start_yes
+ {\d_page_col_distance \pagecolumnsparameter\c!distance\relax
+ % \d_page_col_max_height \pagecolumnsparameter\c!maxheight
+ \d_page_col_max_width \pagecolumnsparameter\c!maxwidth
+ % \d_page_col_balance_step \pagecolumnsparameter\c!step
+ \c_page_col_current \plusone
+ %
+ \d_page_col_column_width\dimexpr(\d_page_col_max_width-\d_page_col_distance*\numexpr(\c_page_col_n_of_columns-\plusone)\relax)/\c_page_col_n_of_columns\relax
+ %
+ \columnwidth \d_page_col_column_width
+ \columndistance \d_page_col_distance
+ \nofcolumns \c_page_col_n_of_columns
+ \textwidth \columnwidth % kind of redundant
+ %
+ \nopenalties
+ %
+ % \insidecolumnstrue % NO!
+ %
+ \enforced\let\column\page_col_column
+ %
+ \def\page_floats_get_used_hsize{\makeupwidth} % a bit of a hack
+ %
+ \usealignparameter \pagecolumnsparameter
+ \useblankparameter \pagecolumnsparameter
+ % \useprofileparameter\pagecolumnsparameter
+ %
+ \usepagecolumnscolorparameter\c!color
+ %
+ \setupnotes[\c!width=\textwidth]%
+ %
+ \usesetupsparameter\pagecolumnsparameter
+ %
+ % This will become a method but for now it's good enough
+ %
+ \ifconditional\c_page_col_page\else
+ \page_col_pickup_preceding
+ \fi
+ \setupoutputroutine[\s!pagecolumn]%
+ \ifconditional\c_page_col_page\else
+ \page_col_flush_preceding
+ \fi
+ %
+ \setupfloats[\c!ntop=\plusthousand]%
+ % \setupfloats[\c!nbottom=\plusthousand]%
+ %
+ \page_col_command_set_vsize
+ \page_col_command_set_hsize
+ %
+ \enforced\permanent\protected\def\startpagecolumns[##1]{\page_col_start_nop}%
+ %
+ \enforced\let\stoppagecolumns\page_col_stop_yes}
+
+\protected\def\page_col_start_nop
+ {\nofcolumns\c_page_mix_n_of_columns
+ \enforced\let\stoppagecolumns\page_col_stop_nop}
+
+\protected\def\page_col_stop_yes
+ {\column % \page_otr_eject_page
+ \page
+ \endgroup
+ % \setupoutputroutine[\s!singlecolumn]%
+ \page_otr_command_set_vsize
+ \page_otr_command_set_hsize
+ \page
+ \endgroup}
+
+\protected\def\page_col_stop_nop
+ {\page
+ \endgroup
+ \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-plg.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-plg.mkxl
new file mode 100644
index 00000000000..10cc1447bf0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-plg.mkxl
@@ -0,0 +1,213 @@
+%D \module
+%D [ file=page-pls,
+%D version=2003.03.16,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Setup,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Extra Page Building}
+
+%D This still needs some work, especially the dimensions need to be checked cq.\
+%D optimized. Beware: method has become alternative!
+
+%D This feature has been present for a while but has never been exploited: pluggable
+%D pagebuilders. The next example code demonstrates the application of one such a
+%D plug-in. This variant support \type {page}, \type {leftpage} and \type
+%D {rightpage} definitions where specific areas are placed with the \type
+%D {\pagearea} command.
+%D
+%D \starttyping
+%D \setupheadertexts[the header text]
+%D \setupfootertexts[a pretty long left footer text][something right]
+%D \setupbottomtexts[a not so long bottom text][another right thing]
+%D \setuptexttexts [margin][something marginal][indeed]
+%D
+%D \startpagelayout[leftpage]
+%D \setupTABLE[offset=overlay]
+%D \setupTABLE[c][1][width=\leftmarginwidth]
+%D \setupTABLE[c][2][width=\textwidth]
+%D \setupTABLE[c][3][width=\rightmarginwidth]
+%D \bTABLE
+%D \bTR
+%D \bTD[nx=3,background=color,backgroundcolor=green]
+%D \pagearea[header][text][middle]
+%D \eTD
+%D \eTR
+%D \bTR
+%D \bTD \pagearea[text][margin][left] \eTD
+%D \bTD[nx=2] \pagearea[text] \eTD
+%D \eTR
+%D \bTR
+%D \bTD[nx=3,offset=overlay]
+%D {\bTABLE
+%D \bTR
+%D \bTD \pagearea[footer][text][left] \eTD
+%D \bTD \pagearea[bottom][text][left] \eTD
+%D \eTR
+%D \eTABLE}
+%D \eTD
+%D \eTR
+%D \eTABLE
+%D \stoppagelayout
+%D
+%D \startpagelayout[rightpage]
+%D \setupTABLE[offset=overlay]
+%D \setupTABLE[c][1][width=\leftmarginwidth]
+%D \setupTABLE[c][2][width=\textwidth]
+%D \setupTABLE[c][3][width=\rightmarginwidth]
+%D \bTABLE
+%D \bTR
+%D \bTD[nx=3] \pagearea[header][text][middle] \eTD
+%D \eTR
+%D \bTR
+%D \bTD \pagearea[text][margin][left] \eTD
+%D \bTD[nx=2] \pagearea[text] \eTD
+%D \eTR
+%D \bTR
+%D \bTD[nx=3,offset=overlay]
+%D {\bTABLE
+%D \bTR
+%D \bTD \pagearea[bottom][text][right] \eTD
+%D \bTD \pagearea[footer][text][right] \eTD
+%D \eTR
+%D \eTABLE}
+%D \eTD
+%D \eTR
+%D \eTABLE
+%D \stoppagelayout
+%D
+%D \setupcolors[state=start]
+%D
+%D \setuppagenumbering[alternative=doublesided,location=]
+%D
+%D \setuplayout[alternative=makeup]
+%D
+%D \definetextbackground
+%D [test]
+%D [state=start,
+%D background=color,
+%D backgroundcolor=yellow]
+%D
+%D \starttext
+%D
+%D \dorecurse{10}{\input tufte \par}
+%D
+%D \input tufte \starttest \input tufte \stoptest \input tufte
+%D
+%D \starttabulate
+%D \NC test \NC \starttest \input tufte \stoptest \NC \NR
+%D \stoptabulate
+%D
+%D \dorecurse{10}{\input tufte \par}
+%D
+%D \stoptext
+%D \stoptyping
+
+\unprotect
+
+\installcorenamespace{layoutmakeupalternative}
+
+\def\page_boxes_construct_content_makeup#1#2#3% targetbox flusher box
+ {\setbox#1\hbox
+ {\vbox to \textheight
+ {\offinterlineskip
+ \vskip\dimexpr-1\topskip+\strutheight\relax % could be an option
+ \textwidth\makeupwidth
+ \hsize\textwidth
+ \boxmaxdepth\maxdepth
+ \noindent
+ \page_otr_command_package_contents#2#3}}%
+ \wd#1\makeupwidth
+ \ht#1\textheight
+ \dp#1\zeropoint
+ \hsize\paperwidth
+ \vsize\paperheight
+ \setbox#1\vbox{\csname\??layoutmakeupalternative\doifbothsidesoverruled\v!page\v!rightpage\v!leftpage\endcsname}%
+ \wd#1\paperwidth
+ \ht#1\paperheight
+ \dp#1\zeropoint}
+
+\installlayoutalternative\v!makeup{\page_boxes_construct_content_makeup}
+
+\newbox\b_page_area
+
+\permanent\tolerant\protected\def\pagearea[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ \expandafter\gobblethreeoptionals
+ \or
+ \expandafter\page_area_one
+ \or
+ \expandafter\page_area_two
+ \or
+ \expandafter\page_area_three
+ \fi[#1][#2][#3]}
+
+\def\page_area_three[#1][#2][#3]%
+ {\edef\m_page_area_three{#3}%
+ \ifx\m_page_area_three\v!left
+ \let\m_page_area_three\c!lefttext
+ \orelse\ifx\m_page_area_three\v!right
+ \let\m_page_area_three\c!righttext
+ \else
+ \let\m_page_area_three\c!middletext
+ \fi
+ \page_area_indeed{#1}{#2}\m_page_area_three}
+
+\def\page_area_two[#1][#2][#3]%
+ {\edef\m_page_area_three{\doifbothsidesoverruled\c!righttext\c!righttext\c!lefttext}%
+ \page_area_indeed{#1}{#2}\m_page_area_three}
+
+\def\page_area_one[#1][#2][#3]%
+ {\edef\m_page_area_one{#1}
+ \ifx\m_page_area_one\v!text % copy due to trial runs in TABLE
+ \iftrialtypesetting
+ \fakebox\pagebox
+ \else
+ \page_backgrounds_add_to_text\pagebox
+ \page_grids_add_to_box\pagebox
+ \box\pagebox
+ \fi
+ \fi}
+
+\def\page_area_indeed#1#2#3%
+ {\setbox\b_page_area\vbox{\getspecificlayouttext{#1}{#2}{#3}}%
+ \iftrialtypesetting
+ \fakebox\b_page_area
+ \orelse\ifcsname\??layoutbackgrounds#1#2\endcsname
+ \page_area_indeed_yes{#1}{#2}%
+ \else
+ \box\b_page_area
+ \fi}
+
+\def\page_area_indeed_yes#1#2%
+ {% todo: \localbackgroundframed{\??layoutbackgrounds#1#2}{#1:#2}
+ \localframed % todo:
+ [\??layoutbackgrounds#1#2]
+ [\c!width=\wd\b_page_area,
+ \c!height=\ht\b_page_area,
+ \c!offset=\v!overlay]
+ {\box\b_page_area}}
+
+% to be done nicely (proper namespacing)
+
+\setvalue{\??layoutmakeupalternative\v!leftpage }{\csname\??layoutmakeupalternative\v!page\endcsname}
+\setvalue{\??layoutmakeupalternative\v!rightpage}{\csname\??layoutmakeupalternative\v!page\endcsname}
+
+\permanent\protected\def\startpagelayout
+ {\bgroup
+ \catcode\endoflineasciicode\ignorecatcode
+ \page_layouts_start_layout}
+
+\aliased\let\stoppagelayout\relax
+
+\def\page_layouts_start_layout[#1]#2\stoppagelayout
+ {\egroup
+ \setvalue{\??layoutmakeupalternative#1}{#2}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-pst.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-pst.mkxl
new file mode 100644
index 00000000000..026c64d629e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-pst.mkxl
@@ -0,0 +1,112 @@
+%D \module
+%D [ file=page-mis,
+%D version=2008.11.17, % was part of page-flt.tex / 2000.10.20
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Postponing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Postponing}
+
+\registerctxluafile{page-pst}{}
+
+\unprotect
+
+% postponing
+
+\newevery\everytopofpage\relax
+
+\appendtoks
+ \the\everytopofpage
+\to \everystarttext
+
+\appendtoks
+ \global\everytopofpage\emptytoks
+\to \everystoptext
+
+% \startpostponing [pagenumber] [+pageoffset]
+%
+% \startpostponing[2]
+% PAGE 2 \blank
+% \stoppostponing
+%
+% \startpostponing[+1]
+% PAGE +1 \blank
+% \stoppostponing
+%
+% \startpostponing[+2]
+% PAGE +2 \blank
+% \stoppostponing
+%
+% \starttext \dorecurse{4}{\input tufte \page} \stoptext
+
+%D There is no real need to use \LUA\ here but the bookkeeping is somewhat easier
+%D and we also can erase buffers easier when we keep a local cache, especially as we
+%D can flush per page.
+
+\newcount \c_page_postponed_blocks_next_page % set at the lua end
+\newconditional\c_page_postponed_busy
+
+\mutable\let\currentpostponedpage\empty
+
+\permanent\protected\setvalue{\e!start\v!postponing}%
+ {\bgroup
+ \obeylines
+ \doifelsenextoptional{\egroup\page_postponed_blocks_start}{\egroup\page_postponed_blocks_start[0]}}
+
+\permanent\protected\setvalue{\e!stop\v!postponing}%
+ {\clf_registerpostponedblock{\currentpostponedpage}\relax}
+
+\def\page_postponed_blocks_start[#1]%
+ {\edef\currentpostponedpage{#1}%
+ \grabbufferdatadirect{postponedblock}{\e!start\v!postponing}{\e!stop\v!postponing}}
+
+%D Officially we should flush again after a flush as there can be new future pages
+%D but that will be looked into when we run into it.
+
+\c_page_postponed_mode\plusone
+
+\protected\def\page_postponed_blocks_flush_indeed
+ {\begingroup
+ \setsystemmode\v!postponing
+ \settrue\c_page_postponed_busy
+ \ifnum\c_page_postponed_mode=\plusone
+ \savepagecontent
+ \fi
+ \pagetotal\zeropoint % here? still needed? (was after flush pagefloats)
+ \the\everytopofpage\relax
+ %\flushrestfloats
+ \page_floats_flush_page_floats
+ \setnormalcatcodes % postponing in verbatim
+ \uncatcodespacetokens % postponing in startlines
+ \restoreglobalbodyfont % otherwise problems inside split verbatim
+ \clf_flushpostponedblocks
+ % page {123}
+ \relax
+ \page_otr_command_flush_floats % new but potential dangerous, maybe we need a classification
+ \ifnum\c_page_postponed_mode=\plusone
+ \flushpagecontent
+ \fi
+ \endgroup} % of blocks: with and without flush
+
+\protected\def\page_postponed_blocks_flush
+ {\ifconditional\c_page_postponed_busy
+ % probably a nested flush
+ \orelse\ifnum\c_page_postponed_blocks_next_page=\zerocount
+ % nothing in cache
+ \orelse\ifnum\c_page_postponed_blocks_next_page<\zerocount
+ % generic cache
+ \page_postponed_blocks_flush_indeed
+ \orelse\ifnum\c_page_postponed_blocks_next_page>\realpageno
+ % future content
+ \else
+ % pending content
+ \page_postponed_blocks_flush_indeed
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-sel.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-sel.mklx
new file mode 100644
index 00000000000..1ec1b40e3df
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-sel.mklx
@@ -0,0 +1,420 @@
+%D \module
+%D [ file=page-sel, % moved from page-imp
+%D version=1998.01.15,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Selection,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This code relates to old texexec features and one can wonder if it needs
+%D to be in the core. So, this could become runtime loaded code. Some of
+%D the alternatives need checking.
+
+\writestatus{loading}{ConTeXt Page Macros / Page Selection}
+
+\unprotect
+
+%D One can (mis)use this mechanism to (re)arrange pages of already produced files.
+%D
+%D \starttyping
+%D \insertpages[file.pdf][1,3][n=30,width=18cm]
+%D \stoptyping
+%D
+%D The pages are inserted in the text area, and even pages are repositioned
+%D according to the width. In this example empty pages are added after page 1 and 3.
+%D
+%D Selecting pages can be accomplished by:
+%D
+%D \starttyping
+%D \filterpages[file.pdf][1,3,5][n=30,width=18cm]
+%D \stoptyping
+%D
+%D One may pass \type {odd} or \type {even} instead of a comma separated list. A
+%D third alternative is:
+%D
+%D \starttyping
+%D \copypages[file.pdf][n=30,scale=950]
+%D \stoptyping
+%D
+%D This macros inserts the page, according to the settings provided.
+
+%D Beware: width is not the width of the image, but height can be used to control
+%D its dimensions.
+
+\installcorenamespace{withpages}
+
+\installsetuponlycommandhandler \??withpages {withpages}
+% \installdirectcommandhandler \??withpages {withpages}
+
+\newcount\c_page_selectors_n
+
+\permanent\tolerant\protected\def\insertpages[#filename]#spacer[#emptylist]#spacer[#settings]%
+ {\doifelseassignment{#emptylist}
+ {\page_selectors_insert_indeed[#filename][][#emptylist]}
+ {\page_selectors_insert_indeed[#filename][#emptylist][#settings]}}
+
+\def\page_selectors_insert_indeed[#filename][#emptylist][#settings]%
+ {\bgroup
+ \dontcomplain
+ \getfiguredimensions[#filename]%
+ \setupcurrentwithpages
+ [\c!width=\zeropoint,%
+ \c!n=\noffigurepages,%
+ \c!category=,%
+ #settings]%
+ \global\c_page_selectors_n\directwithpagesparameter\c!n\relax
+ \scratchwidth\directwithpagesparameter\c!width\relax
+ \doifinset0{#emptylist}
+ {\emptyhbox\page}%
+ \dorecurse\c_page_selectors_n
+ {\page_selectors_filter_a_page{#filename}\recurselevel
+ \doifinset\recurselevel{#emptylist}
+ {\emptyhbox\page}}%
+ \egroup}
+
+\let\page_selectors_filter_step_indeed\gobbleoneargument
+\let\page_selectors_filter_step \gobbleoneargument
+
+\permanent\tolerant\protected\def\filterpages[#filename]#spacer[#selection]#spacer[#settings]% % \noffigurepages not yet supported
+ {\bgroup
+ \dontcomplain
+ \getfiguredimensions[#filename]%
+ \setupcurrentwithpages
+ [\c!width=\zeropoint,%
+ \c!n=\noffigurepages,%
+ \c!category=,%
+ #settings]%
+ \global\c_page_selectors_n\directwithpagesparameter\c!n\relax
+ \scratchwidth\directwithpagesparameter\c!width\relax
+ \edef\p_selection{#selection}%
+ \ifx\p_selection\v!even
+ \dorecurse\c_page_selectors_n
+ {\ifodd\recurselevel\else
+ \page_selectors_filter_a_page{#filename}\recurselevel
+ \fi}%
+ \orelse\ifx\p_selection\v!odd
+ \dorecurse\c_page_selectors_n
+ {\ifodd\recurselevel\relax
+ \page_selectors_filter_a_page{#filename}\recurselevel
+ \fi}%
+ \else
+ \def\page_selectors_filter_step_indeed#page%
+ {\ifnum#page>\c_page_selectors_n\else
+ \page_selectors_filter_a_page{#filename}{#page}%
+ \fi}%
+ \def\page_selectors_filter_step#step%
+ {\dowithrange{#step}\page_selectors_filter_step_indeed}%
+ \processcommacommand[\p_selection]\page_selectors_filter_step
+ \fi
+ \egroup}
+
+\def\page_selectors_filter_a_page#filename#page%
+ {\hpack to \textwidth
+ {\ifdim\scratchwidth>\zeropoint
+ \rightorleftpageaction{\scratchwidth\zeropoint}{\hfill}%
+ \fi
+ \setbox\scratchbox\hpack
+ {\hskip-\scratchwidth
+ \edef\p_category{\directwithpagesparameter\c!category}% \useexternalfigure[foo][width=\textwidth]
+ \ifempty\p_category
+ \externalfigure[#filename][\c!page=#page,\c!height=\textheight]%
+ \else
+ \externalfigure[#filename][\p_category][\c!page=#page]%
+ \fi
+ \hss}%
+ \wd\scratchbox\zeropoint
+ \box\scratchbox}
+ \page}
+
+\permanent\tolerant\protected\def\copypages[#filename]#spacer[#settings]#spacer[#figuresettings]%
+ {\bgroup
+ \getfiguredimensions[#filename]%
+ \setupcurrentwithpages
+ [\c!marking=\v!off,%
+ \c!offset=\zeropoint,%
+ \c!n=\noffigurepages,%
+ \c!category=,%
+ #settings]%
+ \global\c_page_selectors_n\directwithpagesparameter\c!n\relax
+ \scratchoffset\directwithpagesparameter\c!offset\relax
+ \dorecurse\c_page_selectors_n
+ {\vbox to \textheight
+ {\hsize\textwidth
+ \centeredbox
+ {\doifelse{\directwithpagesparameter\c!marking}\v!on\cuthbox\hpack % only place where cuthbox is used
+ {\ifdim\scratchoffset>\zeropoint\relax
+ \advance\vsize -2\scratchoffset
+ \advance\hsize -2\scratchoffset
+ \externalfigure[#filename][\c!page=\recurselevel,#figuresettings,\c!scale=,\c!factor=\v!max,\c!offset=\v!overlay]%
+ \else
+ \externalfigure[#filename][\c!page=\recurselevel,#figuresettings,\c!offset=\v!overlay]%
+ \fi}}}
+ \page}%
+ \egroup}
+
+%D \macros
+%D {combinepages}
+%D
+%D Yet another way of postprocessing is handles by \type {\combinepages}. This macro
+%D builds a matrix of pages from a file, for example:
+%D
+%D \starttyping
+%D \setuppapersize
+%D [A4][A4] % or [A4,landscape][A4,landscape]
+%D
+%D \setuplayout
+%D [header=0pt,footer=1cm,
+%D backspace=1cm,topspace=1cm,
+%D width=middle,height=middle]
+%D
+%D \setupfootertexts
+%D [presentation---\currentdate\space---\space\pagenumber]
+%D
+%D \starttext
+%D \combinepages[slides][nx=2,ny=3,frame=on]
+%D \stoptext
+%D \stoptyping
+%D
+%D One can influence the way the pages are combined. (This will be explained some
+%D time.)
+
+\installcorenamespace{combinepagesalternative}
+
+\permanent\tolerant\protected\def\combinepages[#filename]#spacer[#settings]% a=perpag b=free
+ {\bgroup
+ \dontcomplain
+ \getfiguredimensions[#filename]%
+ \setupcurrentwithpages
+ [\c!alternative=\v!a,
+ \c!n=\noffigurepages,\c!nx=\plustwo,\c!ny=\plustwo,
+ \c!start=\plusone,\c!stop=\maxcard,
+ \c!distance=\bodyfontsize,
+ \c!bottom=\vfill,\c!top=\vss,\c!left=\hss,\c!right=\hss,
+ \c!before=\page,\c!after=\page,\c!inbetween=\blank,
+ \c!frame=,\c!background=,\c!backgroundcolor=,
+ \c!name={#filename},
+ \c!category=,
+ #settings]%
+ \global\c_page_selectors_n\directwithpagesparameter\c!n\relax
+ \directwithpagesparameter\c!before
+ \scratchnx\directwithpagesparameter\c!nx
+ \scratchny\directwithpagesparameter\c!ny
+ \scratchdistance\directwithpagesparameter\c!distance\relax
+ \scratchwidth\dimexpr(\textwidth-\scratchnx\scratchdistance+\scratchdistance)/\scratchnx\relax
+ \scratchheight\dimexpr(\textheight-\scratchny\scratchdistance+\scratchdistance)/\scratchny\relax
+ \expandnamespaceparameter\??combinepagesalternative\directwithpagesparameter\c!alternative\v!b
+ \directwithpagesparameter\c!after
+ \egroup}
+
+\setvalue{\??combinepagesalternative\v!a}% use hpacks
+ {\global\combinedpagescounter\directwithpagesparameter\c!start\relax
+ \doloop
+ {\vbox to \textheight
+ {\dorecurse\scratchny
+ {\hbox to \textwidth
+ {\dorecurse\scratchnx
+ {\vbox to \scratchheight
+ {\hsize\scratchwidth
+ \vsize\scratchheight
+ \directwithpagesparameter\c!top
+ \hbox to \hsize
+ {\directwithpagesparameter\c!left
+ \ifnum\combinedpagescounter>\directwithpagesparameter\c!stop\relax
+ \global\c_page_selectors_n\zerocount
+ \orelse\ifnum\combinedpagescounter>\c_page_selectors_n \else
+ \externalfigure
+ [\directwithpagesparameter\c!name]
+ [\c!object=\v!no,
+ \c!page=\number\combinedpagescounter,
+ \c!factor=\v!max,
+ \c!background=\directwithpagesparameter\c!background,
+ \c!backgroundcolor=\directwithpagesparameter\c!backgroundcolor,
+ \c!frame=\directwithpagesparameter\c!frame]%
+ \fi
+ \directwithpagesparameter\c!right}
+ \directwithpagesparameter\c!bottom}%
+ \global\advance\combinedpagescounter\plusone
+ \hfil}%
+ \hfilneg}
+ \vfil}%
+ \vfilneg}%
+ \page
+ \ifnum\combinedpagescounter>\c_page_selectors_n
+ \exitloop
+ \fi}}
+
+\setvalue{\??combinepagesalternative\v!c}%
+ {\global\combinedpagescounter\directwithpagesparameter\c!start\relax
+ \doloop
+ {\vbox to \textheight
+ {\hbox to \textwidth
+ {\dorecurse\scratchnx
+ {\directwithpagesparameter\c!left
+ \vbox to \textheight
+ {\hsize\scratchwidth
+ \dorecurse\scratchny
+ {\directwithpagesparameter\c!top
+ \hbox to \hsize
+ {\vbox to \scratchheight
+ {\hsize\scratchwidth
+ \vsize\scratchheight
+ \ifnum\combinedpagescounter>\directwithpagesparameter\c!stop\relax
+ \global\c_page_selectors_n\zerocount
+ \orunless\ifnum\combinedpagescounter>\c_page_selectors_n
+ \externalfigure
+ [\directwithpagesparameter\c!name]
+ [\c!object=\v!no,
+ \c!page=\number\combinedpagescounter,
+ \c!factor=\v!max,
+ \c!background=\directwithpagesparameter\c!background,
+ \c!backgroundcolor=\directwithpagesparameter\c!backgroundcolor,
+ \c!frame=\directwithpagesparameter\c!frame]%
+ \fi}}
+ \global\advance\combinedpagescounter\plusone
+ \directwithpagesparameter\c!bottom}}%
+ \hfil}%
+ \hfilneg}}%
+ \page
+ \ifnum\combinedpagescounter>\c_page_selectors_n
+ \exitloop
+ \fi}}
+
+\letcsname\??combinepagesalternative\v!horizontal\expandafter\endcsname\csname\??combinepagesalternative\v!a\endcsname
+\letcsname\??combinepagesalternative\v!vertical \expandafter\endcsname\csname\??combinepagesalternative\v!c\endcsname
+
+\setvalue{\??combinepagesalternative\v!b}%
+ {\global\combinedpagescounter\directwithpagesparameter\c!start\relax
+ \doloop
+ {\startbaselinecorrection
+ \hbox to \textwidth
+ {\dorecurse\scratchnx
+ {\global\advance\combinedpagescounter\plusone
+ \ifnum\combinedpagescounter>\c_page_selectors_n \else
+ \normalexpanded{\externalfigure
+ [\directwithpagesparameter\c!name]
+ [\c!page=\number\combinedpagescounter,
+ \c!width=\the\scratchwidth,
+ \c!background=\directwithpagesparameter\c!background,
+ \c!backgroundcolor=\directwithpagesparameter\c!backgroundcolor,
+ \c!frame=\directwithpagesparameter\c!frame]}%
+ \hfill
+ \fi}%
+ \hfillneg}%
+ \stopbaselinecorrection
+ \ifnum\combinedpagescounter<\c_page_selectors_n\relax
+ \directwithpagesparameter\c!inbetween
+ \else
+ \exitloop
+ \fi}}
+
+% This macro cuts a page into n parts that can be pasted together.
+
+\permanent\tolerant\protected\def\slicepages[#filename]#spacer[#oddsettings]#spacer[#evensettings]%
+ {\ifparameter#evensettings\or
+ \page_selectors_slice_indeed[#filename][#oddsettings][#evensettings]%
+ \else
+ \page_selectors_slice_indeed[#filename][#oddsettings][#oddsettings]%
+ \fi}
+
+\mutable\let\slicedpagenumber\!!zerocount
+\mutable\let\slicedpagestepx \!!zerocount
+\mutable\let\slicedpagestepy \!!zerocount
+
+\def\page_selectors_slice_indeed[#filename][#oddsettings][#evensettings]%
+ {\bgroup
+ \dontcomplain
+ \glet\slicedpagenumber\!!zerocount
+ \getfiguredimensions[#filename]%
+ \setupcurrentwithpages
+ [\c!offset=\zeropoint,%
+ \c!hoffset=\zeropoint,%
+ \c!voffset=\zeropoint,
+ \c!width=\figurewidth,%
+ \c!height=\figureheight,%
+ \c!n=\noffigurepages,%
+ \c!category=,%
+ #oddsettings]%
+ \global\c_page_selectors_n\directwithpagesparameter\c!n\relax
+ \ifnum\c_page_selectors_n>\zerocount
+ \definepapersize
+ [\s!dummy][\c!height=\directwithpagesparameter\c!height,\c!width=\directwithpagesparameter\c!width]%
+ \setuppapersize
+ [\s!dummy][\s!dummy]%
+ \setuplayout
+ [\c!backspace=\zeropoint,\c!topspace=\zeropoint,
+ \c!height=\v!middle,\c!width=\v!middle,
+ \c!textdistance=\zeropoint,
+ \c!header=\zeropoint,\c!footer=\zeropoint]%
+ \fi
+ \dorecurse\noffigurepages
+ {\glet\slicedpagenumber\recurselevel
+ \ifnum\c_page_selectors_n>\plusone
+ \dorecurse\c_page_selectors_n
+ {\let\slicedpagestepx\recurselevel
+ \dorecurse\c_page_selectors_n
+ {\let\slicedpagestepy\recurselevel
+ \clip
+ [\c!nx=\c_page_selectors_n,\c!ny=\c_page_selectors_n,\c!x=\slicedpagestepx,\c!y=\slicedpagestepy]
+ {\scale
+ [\c!scale=\number\c_page_selectors_n000]
+ {\externalfigure[#filename][\c!page=\slicedpagenumber]}}%
+ \page}}%
+ \else
+ \ifodd\slicedpagenumber\relax
+ \setupcurrentwithpages[#oddsettings]%
+ \else
+ \setupcurrentwithpages[#evensettings]%
+ \fi
+ \hskip\directwithpagesparameter\c!offset\relax
+ \clip
+ [\c!hoffset=\directwithpagesparameter\c!hoffset,\c!voffset=\directwithpagesparameter\c!voffset,
+ \c!height=\directwithpagesparameter\c!height,\c!width=\directwithpagesparameter\c!width]
+ {\externalfigure[#filename][\c!page=\slicedpagenumber]}%
+ \page
+ \fi}
+ \egroup}
+
+% \starttext \slicepages[slice1.pdf][n=3] \stoptext
+
+\permanent\protected\def\trimpages[#1]% was for a over decade in p-pdf-51.tex
+ {\begingroup
+ \getdummyparameters
+ [\c!file=dummy,
+ \c!hoffset=\zeropoint,
+ \c!voffset=\zeropoint,
+ \c!width=17cm,
+ \c!height=24cm,
+ \c!x=\zeropoint,
+ \c!y=\zeropoint,
+ #1]
+ \getfiguredimensions
+ [\dummyparameter\c!file]
+ [\c!object=\v!no]
+ \dorecurse\noffigurepages
+ {\scale
+ [\c!width=\paperwidth,
+ \c!height=\paperheight]
+ {\offset
+ [\c!x=\dummyparameter\c!x,
+ \c!y=\dummyparameter\c!y]
+ {\clip
+ [\c!hoffset=\dummyparameter\c!hoffset,
+ \c!voffset=\dummyparameter\c!voffset,
+ \c!width=\dummyparameter\c!width,
+ \c!height=\dummyparameter\c!height]
+ {% we correct by default, if not needed, introduce option
+ \setbox\nextbox\hbox
+ {\externalfigure[\dummyparameter\c!file][\c!page=##1]}%
+ \ifdim\wd\nextbox>\ht\nextbox
+ \rotate[\c!rotation=90]{\box\nextbox}%
+ \else
+ \box\nextbox
+ \fi}}}}%
+ \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-sid.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-sid.mkxl
new file mode 100644
index 00000000000..3c1f1f7beea
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-sid.mkxl
@@ -0,0 +1,1019 @@
+%D \module
+%D [ file=page-sid,
+%D version=2000.10.20,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Side Floats,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Side Floats}
+
+\unprotect
+
+%D These macro deal with side floats. We started with Daniel Comenetz macros as
+%D published in TUGBoat Volume 14 (1993), No.\ 1: Anchored Figures at Either Margin.
+%D I extended and patched the macros to suite our needs which results in a messy
+%D module.
+%D
+%D A complication is that we need to deal with spacing differently before and after
+%D the float. Also, whitespace can interfere as does the prevdepth. There is no real
+%D universal solution. So, by now not much is left of that code, if only because we
+%D need to match \CONTEXT\ spacing module, because we have more placement options
+%D and control and because the math hackery is not suitable for \CONTEXT\ anyway.
+%D
+%D This code had been redone many times because we kept running into spacing issues
+%D and it's not that much fun (or rewarding). It's probably the module that made
+%D me go into distraciton mode most often (like watching amusing Walk of The
+%D Earth, sophisticated Massive Attack video clips, impressive Davie504 movies
+%D and so on).
+
+\newdimen \d_page_sides_margin
+\newdimen \d_page_sides_height % includes the topskip
+\newdimen \d_page_sides_width
+\newdimen \d_page_sides_hsize
+\newdimen \d_page_sides_vsize
+\newdimen \d_page_sides_vsize_reset
+\newdimen \d_page_sides_progress
+\newdimen \d_page_sides_page_total
+
+\newdimen \d_page_sides_leftoffset
+\newdimen \d_page_sides_rightoffset
+
+%newbox \b_page_sides_bottom
+
+\newcount \c_page_sides_lines_done
+\newcount \c_page_sides_checks_done
+\newcount \c_page_sides_n_of_lines
+\newcount \c_page_sides_n_of_hang
+\newconstant \c_page_sides_float_type
+\newcount \c_page_sides_hangafter
+
+\newconditional \c_page_sides_short
+\newconditional \c_page_sides_flag
+
+\newdimen \d_page_sides_shift
+\newdimen \d_page_sides_extrashift
+\newdimen \d_page_sides_leftshift
+\newdimen \d_page_sides_rightshift
+\newdimen \d_page_sides_leftskip
+\newdimen \d_page_sides_rightskip
+\newdimen \d_page_sides_maximum
+\newdimen \d_page_sides_topskip
+\newdimen \d_page_sides_bottomskip
+\newdimen \d_page_sides_midskip
+\newdimen \d_page_sides_downshift
+\newdimen \d_page_sides_pagetotal
+\newdimen \d_page_sides_topoffset
+\newdimen \d_page_sides_bottomoffset
+\newdimen \d_page_sides_toptotal
+\newdimen \d_page_sides_bottomtotal
+
+\newconstant \c_page_sides_align
+\newconstant \c_page_sides_skipmode
+\newconstant \c_page_sides_tolerance
+
+\newconstant \c_page_sides_method % sort of obsolete
+
+\newdimen \d_page_sides_progression
+
+\newcount \c_page_sides_m_of_lines
+\newconditional \c_page_sides_delayed
+
+\newconditional \c_page_sides_check_same_page
+
+\newif \iftracesidefloats % public (might change)
+
+%D Defaults:
+
+\d_page_sides_vsize_reset -\onepoint
+%d_page_sides_vsize_reset \zeropoint % could be an option, needs testing
+
+%D We have some basic (and colorful) tracing:
+
+\def\page_sides_floats_legend
+ {\showmessage\m!floatblocks{16}\empty
+ \glet\page_sides_floats_legend\relax}
+
+\installtextracker{floats.anchoring}
+ {\page_sides_floats_legend
+ \tracesidefloatstrue}
+ {\tracesidefloatsfalse}
+
+%D The horizontal shifts depend on the location: left or right in the text, margin
+%D or edge. These shifts are rather stable and don't interfere with the page flow
+%D as much as the vertical ones do.
+
+\def\page_sides_process_float_backspace {\global\c_page_sides_float_type\plusone \page_sides_handle_float}
+\def\page_sides_process_float_leftedge {\global\c_page_sides_float_type\plustwo \page_sides_handle_float}
+\def\page_sides_process_float_leftmargin {\global\c_page_sides_float_type\plusthree\page_sides_handle_float}
+\def\page_sides_process_float_left {\global\c_page_sides_float_type\plusfour \page_sides_handle_float}
+\def\page_sides_process_float_right {\global\c_page_sides_float_type\plusfive \page_sides_handle_float}
+\def\page_sides_process_float_rightmargin{\global\c_page_sides_float_type\plussix \page_sides_handle_float}
+\def\page_sides_process_float_rightedge {\global\c_page_sides_float_type\plusseven\page_sides_handle_float}
+\def\page_sides_process_float_cutspace {\global\c_page_sides_float_type\pluseight\page_sides_handle_float}
+\def\page_sides_process_float_margin {\global\c_page_sides_float_type\pluseight\page_sides_handle_float}
+
+\def\page_sides_check_horizontal_skips
+ {\ifcase\c_page_sides_skipmode
+ \or % high
+ \or % low
+ \or % fit
+ \global\d_page_sides_margin\zeropoint
+ \fi}
+
+\def\page_sides_apply_horizontal_shift
+ {\ifdim\d_page_sides_maximum>\zeropoint
+ \ifcase\c_page_sides_float_type
+ % invalid
+ \or
+ % backspace
+ \or
+ \global\d_page_sides_shift\dimexpr
+ -\d_page_sides_maximum
+ -\rightorleftpageaction \leftedgedistance \rightedgedistance
+ -\rightorleftpageaction \leftmarginwidth \rightmarginwidth
+ -\rightorleftpageaction \leftmargindistance \rightmargindistance
+ -\compensatedinnermakeupmargin
+ \relax
+ \or
+ \global\d_page_sides_shift\dimexpr
+ -\d_page_sides_maximum
+ -\rightorleftpageaction \leftmargindistance \rightmargindistance
+ -\compensatedinnermakeupmargin
+ \relax
+ \or
+ % left
+ \or
+ % right
+ \or
+ \global\d_page_sides_shift\dimexpr
+ -\d_page_sides_maximum
+ -\rightorleftpageaction \leftmargindistance \rightmargindistance
+ -\compensatedinnermakeupmargin
+ \relax
+ \or
+ \global\d_page_sides_shift\dimexpr
+ -\d_page_sides_maximum
+ -\rightorleftpageaction \leftedgedistance \rightedgedistance
+ -\rightorleftpageaction \leftmarginwidth \rightmarginwidth
+ -\rightorleftpageaction \leftmargindistance \rightmargindistance
+ -\compensatedinnermakeupmargin
+ \relax
+ \or
+ % cutspace
+ \fi
+ \fi
+ \ifdim\d_page_sides_shift=\zeropoint \relax
+ \ifnum\c_page_sides_float_type=\plusfour
+ \global\advance\d_page_sides_shift\d_page_sides_extrashift
+ \global\d_page_sides_extrashift\zeropoint
+ \orelse\ifnum\c_page_sides_float_type=\plusfive
+ \global\advance\d_page_sides_shift\d_page_sides_extrashift
+ \global\d_page_sides_extrashift\zeropoint
+ \fi
+ \else
+ \ifnum\c_page_sides_float_type<\plusfour
+ \global\c_page_sides_float_type\plusfour
+ \orelse\ifnum\c_page_sides_float_type>\plusfive
+ \global\c_page_sides_float_type\plusfive
+ \fi
+ \fi}
+
+\def\page_sides_set_skips
+ {\global\d_page_sides_rightskip\zeropoint
+ \global\d_page_sides_leftskip \zeropoint
+ \ifcase\c_page_sides_float_type
+ \or % backspace
+ \global\d_page_sides_leftskip\dimexpr
+ +\rightorleftpageaction \backspace \cutspace
+ +\compensatedinnermakeupmargin
+ \relax
+ \or % leftedge
+ \global\d_page_sides_leftskip\dimexpr
+ +\rightorleftpageaction \leftmargindistance \rightmargindistance
+ +\rightorleftpageaction \leftmarginwidth \rightmarginwidth
+ +\rightorleftpageaction \leftedgedistance \rightedgedistance
+ +\compensatedinnermakeupmargin
+ \relax
+ \or % leftmargin
+ \global\d_page_sides_leftskip\dimexpr
+ +\rightorleftpageaction \leftmargindistance \rightmargindistance
+ +\compensatedinnermakeupmargin
+ \relax
+ \or % leftside
+ \or % rightside
+ \or % rightmargin
+ \global\d_page_sides_rightskip\dimexpr
+ +\rightorleftpageaction \rightmargindistance \leftmargindistance
+ +\compensatedinnermakeupmargin
+ \relax
+ \or % rightedge
+ \global\d_page_sides_rightskip\dimexpr
+ +\rightorleftpageaction \rightmargindistance \leftmargindistance
+ +\rightorleftpageaction \rightmarginwidth \leftmarginwidth
+ +\rightorleftpageaction \rightedgedistance \leftedgedistance
+ +\compensatedinnermakeupmargin
+ \relax
+ \or % cutspace
+ \global\d_page_sides_rightskip\dimexpr
+ +\rightorleftpageaction \cutspace \backspace
+ +\compensatedinnermakeupmargin
+ \relax
+ \fi
+ \global\d_page_sides_leftoffset \d_page_sides_rightskip
+ \global\d_page_sides_rightoffset\d_page_sides_leftskip
+ \ifdim\d_page_sides_rightskip>\zeropoint
+ \global\advance\d_page_sides_rightskip\rightskip
+ \fi
+ \ifdim\d_page_sides_leftskip >\zeropoint
+ \global\advance\d_page_sides_leftskip \leftskip
+ \fi}
+
+%D Shifts get applied to the float box:
+
+\def\page_sides_relocate_float#1%
+ {\global\setbox\floatbox\hpack
+ {\ifnum\c_page_sides_float_type=\plusfour
+ \kern\d_page_sides_leftshift
+ \orelse\ifnum\c_page_sides_float_type=\plusone
+ \kern\d_page_sides_leftshift
+ \fi
+ \ifnum\c_page_sides_float_type>\plusfour
+ \kern-\d_page_sides_extrashift
+ \else
+ \kern\d_page_sides_shift
+ \fi
+ \vbox{#1\ifnum\c_page_sides_align=\plusfour \removedepth \fi}%
+ \ifnum\c_page_sides_float_type>\plusfour
+ \kern\d_page_sides_shift
+ \else
+ \kern-\d_page_sides_extrashift
+ \fi
+ \ifnum\c_page_sides_float_type=\pluseight
+ \kern\d_page_sides_rightshift
+ \orelse\ifnum\c_page_sides_float_type=\plusfive
+ \kern\d_page_sides_rightshift
+ \fi}}
+
+%D The vertical skips are a nightmare and this mechanism is about as complex
+%D as one can get it.
+
+\def\page_sides_check_vertical_skips
+ {\ifdim\d_page_sides_topskip <\zeropoint\d_page_sides_topskip \zeropoint\fi
+ \ifdim\d_page_sides_bottomskip<\zeropoint\d_page_sides_bottomskip\zeropoint\fi
+ \ifdim\d_page_sides_midskip <\zeropoint\d_page_sides_midskip \zeropoint\fi
+ %
+ \global\d_page_sides_toptotal \dimexpr\d_page_sides_topskip +\d_page_sides_topoffset \relax
+ \global\d_page_sides_bottomtotal\dimexpr\d_page_sides_bottomskip+\d_page_sides_bottomoffset\relax
+ \ifcase\c_page_sides_skipmode
+ \or % high
+ \global\d_page_sides_toptotal \d_page_sides_topoffset
+ \or % low
+ \global\d_page_sides_bottomtotal\d_page_sides_bottomoffset
+ \or % fit
+ \global\d_page_sides_toptotal \d_page_sides_topoffset
+ \global\d_page_sides_bottomtotal\d_page_sides_bottomoffset
+ \fi}
+
+%D These shifts get (selectively) applied with a bit of optional tracing.
+
+\def\page_sides_apply_vertical_shift_normal
+ {\global\setbox\floatbox\hpack % why extra box
+ {\vpack
+ {\forgetall
+ \hsize\wd\floatbox
+ \vskip\privatescratchdimen
+ \offinterlineskip
+ \box\floatbox
+ % somehow we need this \scratchbox magic, but at least it's the same as the
+ % tracer now
+ \setbox\scratchbox\emptybox
+ \wd\scratchbox\hsize
+ \ht\scratchbox\d_page_sides_bottomtotal
+ \box\scratchbox
+ \vskip-\d_page_sides_bottomtotal
+ \ifnum\c_page_sides_align=\plusfive
+ \vskip-\lineheight
+ \fi}}}
+
+\def\page_sides_apply_vertical_shift_traced
+ {\global\setbox\floatbox\hpack % why extra box
+ {\backgroundline[trace:r]{\ruledhpack{\vpack
+ {\forgetall
+ \hsize\wd\floatbox
+ \vskip\privatescratchdimen
+ \offinterlineskip
+ \backgroundline
+ [trace:g]%
+ {\ruledhpack{\box\floatbox}}%
+ \par
+ \blackrule
+ [\c!color=trace:s,%
+ \c!height=\d_page_sides_bottomtotal,%
+ \c!depth=\zeropoint,%
+ \c!width=\hsize]%
+ \vskip-\d_page_sides_bottomtotal
+ \ifnum\c_page_sides_align=\plusfive
+ \vskip-\lineheight
+ \fi}}}}}
+
+\def\page_sides_apply_vertical_shift
+ {\ifnum\c_page_sides_align=\plusfour
+ \getnoflines{\ht\floatbox}%
+ \privatescratchdimen\dimexpr\noflines\lineheight-\strutdp\relax
+ \getrawnoflines\d_page_sides_toptotal
+ \advance\privatescratchdimen\noflines\lineheight
+ \page_sides_force_depth
+ \ht\floatbox\privatescratchdimen
+ \dp\floatbox\zeropoint
+ \fi
+ \ifcase\c_page_sides_align \else
+ \global\d_page_sides_toptotal\zeropoint
+ \fi
+ \privatescratchdimen
+ \ifnum\c_page_sides_float_type<\plusfour
+ \d_page_sides_toptotal
+ \orelse\ifnum\c_page_sides_float_type>\plusfive
+ \d_page_sides_toptotal
+ \else
+ \zeropoint
+ \fi
+ % the top of the box is at the previous baseline
+ \ifcase\c_page_sides_align
+ % 0 normal
+ \advance\privatescratchdimen\strutdp % or \openstrutdepth
+ \or % 1 height
+ \advance\privatescratchdimen\strutdp % or \openstrutdepth
+ \or % 2 line
+ \or % 3 depth
+ \advance\privatescratchdimen\lineheight % or \openlineheight
+ \advance\privatescratchdimen\strutdp % or \openstrutdepth
+ \or % 4 grid
+ \privatescratchdimen\zeropoint
+ \or
+ \advance\privatescratchdimen\strutht % or \openstrutheight
+ \fi
+ % new
+ \global\c_page_sides_lines_done\zerocount
+ \ifnum\c_page_sides_n_of_lines>\zerocount
+ \advance\privatescratchdimen\c_page_sides_n_of_lines\lineheight
+ \fi
+ \iftracesidefloats
+ \page_sides_apply_vertical_shift_traced % uses \privatescratchdimen
+ \else
+ \page_sides_apply_vertical_shift_normal % uses \privatescratchdimen
+ \fi
+ \ifnum\c_page_sides_float_type<\plusfour
+ \global\d_page_sides_toptotal\zeropoint
+ \orelse\ifnum\c_page_sides_float_type>\plusfive
+ \global\d_page_sides_toptotal\zeropoint
+ \fi
+ \global\d_page_sides_downshift\zeropoint}
+
+%D We have a few virtual dimensions. I'm not sure what to do with \type
+%D {\pagedepth} and \type {\pageshrink} in the next two. If we ever need
+%D that it will become options.
+
+\permanent\def\d_page_sides_flush_criterium
+ {\dimexpr
+ \d_page_sides_vsize
+ -\d_page_sides_bottomtotal
+ -\pagetotal
+ \relax}
+
+\permanent\def\d_page_sides_room_criterium
+ {\dimexpr
+ \d_page_sides_vsize
+ -\d_page_sides_bottomtotal % added here too
+ -\pagetotal
+ \relax}
+
+%D In order to get a consistent spacing we force a strutdepth unless the
+%D preceding material has more depth than that already. This way anchoring
+%D becomes predictable.
+
+\protected\def\page_sides_force_depth
+ {\iftracesidefloats
+ \begingroup
+ \c_page_force_strut_depth_trace_mode\plusone
+ \ifconditional\c_page_sides_check_same_page
+ \forcestrutdepthplus
+ \else
+ \forcestrutdepth
+ \fi
+ \endgroup
+ \else
+ \ifconditional\c_page_sides_check_same_page
+ \forcestrutdepthplus
+ \else
+ \forcestrutdepth
+ \fi
+ \fi
+ \page_otr_command_set_vsize} % new
+
+\def\page_sides_flush_floats
+ {\par
+ \ifdim\d_page_sides_flush_criterium>\zeropoint
+ \page_sides_flush_floats_progress
+ \page_sides_flush_floats_after_next
+ \fi
+ \page_sides_flush_floats_reset}
+
+\def\page_sides_flush_floats_text
+ {\par
+ % what with \c_anch_backgrounds_text_level>\plusone
+ \ifdim\d_page_sides_flush_criterium>\zeropoint
+ \page_sides_flush_floats_progress
+ \page_sides_flush_floats_after_none
+ \fi
+ \page_sides_flush_floats_reset}
+
+\def\page_sides_flush_floats_reset
+ {\global\d_page_sides_vsize\d_page_sides_vsize_reset
+ % also here if used at all \global\holdinginserts\zerocount
+ \global\setfalse\c_page_sides_short
+ \global\setfalse\c_page_sides_flag
+ \global\c_page_sides_checks_done\zerocount}
+
+\def\page_sides_flush_floats_after_none % we force a flush
+ {\ifdim\d_page_sides_midskip>\zeropoint
+ \blank[\the\d_page_sides_midskip]
+ \fi
+ \ignoreparskip
+ \blank[\v!disable]}
+
+\def\page_sides_flush_floats_after_next % we have two successive ones
+ {\ifdim\d_page_sides_bottomskip>\zeropoint
+ \blank[\the\d_page_sides_bottomskip]
+ \fi
+ \ignoreparskip
+ \blank[\v!disable]}
+
+%D A rudimentary checker:
+
+\permanent\protected\def\doifelsesidefloat
+ {\par
+ \ifdim\d_page_sides_room_criterium>\zeropoint % -\pagedepth
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifsidefloatelse\doifelsesidefloat
+
+%D Sometimes we need to fill up the space alongside a side float and this
+%D is where we define the helpers. A user can enforce a smaller step. We use
+%D large steps when possible.
+
+\installcorenamespace{sidefloatsteps}
+
+\setvalue{\??sidefloatsteps\v!line }{\strut}
+\setvalue{\??sidefloatsteps\v!big }{\strut}
+\setvalue{\??sidefloatsteps\v!medium}{\halflinestrut} % was \halfstrut
+\setvalue{\??sidefloatsteps\v!small }{\noheightstrut} % was \quarterstrut
+
+\def\page_sides_flush_floats_tracer
+ {\dontleavehmode
+ \ruledhpack\bgroup\backgroundline[trace:b]{%
+ \llap{\smash{\vrule\s!width4\points\s!height.4\points\s!depth.4\points}}%
+ \ifnum\recurselevel=\plusone
+ \llap{\smash{\smallinfofont\the\scratchdimen}\hskip.5\leftmargindistance}%
+ \orelse\ifodd\recurselevel
+ \llap{\smash{\smallinfofont\recurselevel}\hskip.5\leftmargindistance}%
+ \fi
+ \page_sides_flush_floats_normal
+ \kern\hsize
+ \egroup}}
+
+\def\page_sides_flush_floats_normal
+ {\ifdim\scratchdimen>\htdp\strutbox
+ \strut
+ \else
+ \m_pages_strut
+ \fi}
+
+\def\page_sides_flush_floats_progress
+ {\begingroup
+ \page_sides_force_depth
+ \parskip\zeropoint
+ \let\page_sides_flush_floats\relax
+ \edef\m_pages_strut
+ {\ifcsname\??sidefloatsteps\rootfloatparameter\c!step\endcsname
+ \lastnamedcs
+ \else
+ \noheightstrut
+ \fi}%
+ \forgetall
+ \offinterlineskip
+ \doloop
+ {\scratchdimen\d_page_sides_flush_criterium
+ \ifdim\scratchdimen>\onepoint % good enough, can become configurable
+ \ifnum\recurselevel>\plushundred % safeguard, sort of deadcycles
+ \exitloop
+ \orelse\iftracesidefloats
+ \page_sides_flush_floats_tracer\par
+ \else
+ \page_sides_flush_floats_normal\par
+ \fi
+ \else
+ \page_sides_force_depth
+ \exitloop
+ \fi}%
+ \endgroup}
+
+%D We force a parskip and ignore it afterwards. We can nil it by setting the
+%D \type {spacebeforeside} parameter. We can have a leading blank so we need
+%D to make sure that we use blank to inject the parskip and then ignore
+%D the one injected by the engine.
+
+\def\page_sides_inject_before
+ {\page_sides_force_depth
+ \ifdim\parskip>\zeropoint
+ \ifdim\parskip>\d_strc_floats_top
+ \ifdim\d_strc_floats_top>\zeropoint
+ \ignoreparskip
+ \blank[\v!white]%
+ \else
+ \checkedblank[\rootfloatparameter\c!spacebeforeside]%
+ \fi
+ \else
+ \checkedblank[\rootfloatparameter\c!spacebeforeside]%
+ \fi
+ \else
+ \checkedblank[\rootfloatparameter\c!spacebeforeside]%
+ \fi}
+
+%D We are now done with \type {spacebefore} and the parskip is already
+%D injected. The dummy line makes sure that we anchor properly and it
+%D also can serve as tracer.
+
+\def\page_sides_inject_dummy_line_normal
+ {\hpack to \availablehsize{\strut\hss}}
+
+\def\page_sides_inject_dummy_line_traced
+ {\ruledhpack to \availablehsize{\backgroundline[trace:c]{\page_sides_inject_dummy_line_normal}}}
+
+\def\page_sides_inject_dummy_lines
+ {\par
+ \nointerlineskip
+ % \ifnum\lastpenalty>\zerocount
+ % \penalty\plustenthousand
+ % \fi
+ \dontleavehmode
+ \iftracesidefloats
+ \page_sides_inject_dummy_line_traced
+ \else
+ \page_sides_inject_dummy_line_normal
+ \fi
+ \par
+ % on an empty page we have topskip, say 12pt
+ \ignoreparskip
+ % this can be 18.5pt
+ \kern-\dimexpr\lineheight+\strutdp\relax
+ % so we can actually have a -2.5pt skip on top
+ \ignoreparskip
+ \blank[\v!samepage]
+ \blank[\v!disable]
+ % now say we are negative now
+ \ifdim\pagetotal<\zeropoint
+ % then we're at the top of the page ... quite messy .. i really need to
+ % make the page builder a bit more flexible .. should we do something now?
+ \fi}
+
+%D Checkers:
+
+\def\page_sides_check_floats_after_par
+ {\page_sides_check_floats_indeed
+ \ifdim\d_page_sides_pagetotal=\pagetotal \else
+ \glet\page_sides_check_floats\page_sides_check_floats_indeed
+ \page_sides_flush_floats
+ \global\c_page_sides_n_of_lines\zerocount % here !
+ \fi}
+
+\protected\def\page_sides_flush_floats_after_par
+ {\global\d_page_sides_pagetotal\pagetotal
+ \glet\page_sides_check_floats\page_sides_check_floats_after_par}
+
+\protected\def\page_sides_forget_floats
+ {\global\d_page_sides_vsize\d_page_sides_vsize_reset
+ \global\c_page_sides_n_of_lines\zerocount
+ % also here if used at all \global\holdinginserts\zerocount
+ \global\setfalse\c_page_sides_short
+ \global\setfalse\c_page_sides_flag}
+
+%D Here comes the output routine. We either go the fast route or we use the
+%D normal one (stored in \type {\page_otr_command_side_float_output}. We no
+%D longer have this fuzzy code around with penalties and indentation and
+%D such.
+
+\def\page_sides_output_routine
+ {\page_otr_command_side_float_output
+ \ifconditional\c_page_sides_short
+ \global\setfalse\c_page_sides_short
+ \else
+ \global\d_page_sides_vsize\d_page_sides_vsize_reset
+ \global\c_page_sides_n_of_lines\zerocount
+ \fi}
+
+\def\page_sides_place_float
+ {\ifnum\c_page_sides_float_type=\plusfour \kern\d_page_sides_toptotal \fi
+ \ifnum\c_page_sides_float_type=\plusfive \kern\d_page_sides_toptotal \fi
+ \ifgridsnapping
+ \page_sides_place_float_grid
+ \else
+ \page_sides_place_float_normal
+ \fi
+ \par
+ \kern-\d_page_sides_height
+ \penalty10001 % oeps, this will change
+ \normalbaselines}
+
+\def\page_sides_place_float_normal
+ {\page_sides_push_float_inline\firstofoneargument}
+
+%D The following needs some more work .. consider this a quick hack. We probably
+%D need an mkiv hanging grid option.
+
+\def\page_sides_place_snap_to_grid#1%
+ {\edef\p_grid{\floatparameter\c!grid}%
+ \ifempty\p_grid\else
+ \snaptogrid[\p_grid]%
+ \fi
+ \hpack{#1}}
+
+\def\page_sides_place_float_grid
+ {\getrawnoflines\d_page_sides_height % raw ?
+ \d_page_sides_height\noflines\lineheight
+ \page_sides_push_float_inline\page_sides_place_snap_to_grid}
+
+\let\strc_floats_mark_par_as_free\relax
+
+\def\page_sides_push_float_inline#1%
+ {\begingroup
+ \reseteverypar % needed !
+ \parskip\zeropoint % needed !
+ \nointerlineskip
+ \page_sides_set_skips
+ \page_floats_report_total
+ \relax
+ \lefttoright
+ \strc_floats_mark_par_as_free
+ \ifcase\c_page_sides_float_type
+ % invalid
+ \or % backspace
+ \noindent#1{\llap{\rlap{\box\floatbox}\kern\d_page_sides_leftskip}}\hfill
+ \or % leftedge
+ \noindent#1{\llap{\box\floatbox\kern\d_page_sides_leftskip}}\hfill
+ \or % leftmargin
+ \noindent#1{\llap{\box\floatbox\kern\d_page_sides_leftskip}}\hfill
+ \or % leftside
+ \noindent#1{\box\floatbox}\hfill
+ \or % rightside
+ \hfill#1{\box\floatbox}%
+ \or % rightmargin
+ \hfill#1{\rlap{\kern\d_page_sides_rightskip\box\floatbox}}%
+ \or % rightedge
+ \hfill#1{\rlap{\kern\d_page_sides_rightskip\box\floatbox}}%
+ \or % cutspace
+ \hfill#1{\rlap{\kern\d_page_sides_rightskip\llap{\box\floatbox}}}%
+ \fi
+ \endgroup}
+
+% \def\page_sides_analyse_progress
+% {\d_page_sides_progress\d_page_sides_vsize
+% \ifconditional\c_page_sides_flag
+% \advance\d_page_sides_progress-\d_page_sides_page_total
+% \global\setfalse\c_page_sides_flag
+% \else
+% \advance\d_page_sides_progress-\pagetotal
+% \fi}
+
+% test case
+%
+% \usemodule[art-01]
+% \starttext
+% \dorecurse{40}{\line{#1}}
+% \placefigure[left]{}{}
+% \input ward
+% \startitemize
+% \item word \item word \item word \item word
+% \stopitemize
+% \input ward
+% \page
+% \stoptext
+
+\def\page_sides_analyse_progress
+ {%\page_otr_command_set_vsize % this is new, otherwise topfloats are not taken into account
+ \d_page_sides_progress\d_page_sides_vsize
+ \ifconditional\c_page_sides_flag
+ \advance\d_page_sides_progress-\d_page_sides_page_total
+ \global\setfalse\c_page_sides_flag
+ \else
+ \ifdim\dimexpr\d_page_sides_progress+\d_page_sides_bottomtotal\relax>\pagegoal
+ % we adapt pagegoal because we can already have placed something with
+ % everypar and we hope that it triggers a flush, see test above
+ \pagegoal\dimexpr\pagegoal-\d_page_sides_bottomtotal\relax
+ \fi
+ \advance\d_page_sides_progress-\pagetotal
+ \fi}
+
+\def\page_sides_analyse_space_stage_one
+ {\global\settrue\c_page_sides_flag
+% \ifdim\pagegoal=\maxdimen
+% \pagegoal\textheight % maybe
+% \fi
+ \global\d_page_sides_page_total \pagetotal % global
+ \ifnum\c_page_sides_float_type<\plusfour
+ \global\d_page_sides_width \zeropoint
+ \orelse\ifnum\c_page_sides_float_type>\plusfive
+ \global\d_page_sides_width\zeropoint
+ \else
+ \global\d_page_sides_width\dimexpr\wd\floatbox+\d_page_sides_margin\relax
+ \fi
+ \ifdim\d_page_sides_width<\zeropoint
+ \global\d_page_sides_width\zeropoint
+ \fi
+ \global\d_page_sides_hsize \dimexpr\hsize-\d_page_sides_width\relax
+ \global\d_page_sides_height\dimexpr\ht\floatbox+\dp\floatbox+\d_page_sides_toptotal\relax
+ \global\d_page_sides_vsize \dimexpr\d_page_sides_height+\d_page_sides_page_total\relax
+ \scratchdimenone\d_page_sides_vsize
+ \scratchdimentwo\pagegoal
+ \ifcase\c_page_sides_tolerance
+ \ifcase\c_page_sides_method
+ % method 0 : raw
+ \or
+ % method 1 : safe (default)
+ \advance\scratchdimentwo -\strutdp
+ \or
+ % method 2 : tight (grid default)
+ \advance\scratchdimenone -\onepoint
+ \fi
+ \or
+ % tolerant
+ \advance\scratchdimentwo -.5\strutdp
+ \or
+ % verytolerant
+ % \advance\scratchdimenone -\onepoint (maybe)
+ \else
+ \advance\scratchdimentwo -\strutdp
+ \fi}
+
+\def\page_sides_analyse_space_stage_two
+ {% how about \pagedepth
+ \ifdim\scratchdimenone>\scratchdimentwo
+ \global\setfalse\c_page_floats_room
+ \else
+ \ifdim\dimexpr\pagegoal-\d_page_sides_vsize\relax<\d_page_sides_bottomtotal
+ % just weird: \global\advance\d_page_sides_vsize \scratchdimenone
+ \global\settrue\c_page_sides_short
+ % why was this \global\holdinginserts\plusone
+ \else
+ \global\advance\d_page_sides_vsize \d_page_sides_bottomtotal % wins over inbetween
+ \global\setfalse\c_page_sides_short
+ \fi
+ \global\settrue\c_page_floats_room
+ \fi}
+
+\def\page_sides_analyse_space
+ {\page_sides_analyse_space_stage_one
+ \ifconditional\c_page_sides_check_same_page
+ \ifdim\d_spac_prevcontent>\zeropoint
+ \ifdim\dimexpr\scratchdimenone+\d_spac_prevcontent>\scratchdimentwo
+ \clf_pushatsame
+ \setbox\scratchbox\vpack{\clf_popatsame}%
+ \page
+ \box\scratchbox
+ \vskip-\lineskip
+ \page_sides_analyse_space_stage_one
+ \fi
+ \fi
+ \fi
+ \page_sides_analyse_space_stage_two}
+
+%D As we have no clear end of one or more paragraphs we only have pre float
+%D skips.
+
+\newconstant\c_page_sides_page_method % will be: \c_page_sides_page_method\plusone
+
+\def\page_otr_force_new_page_one
+ {\vskip\d_page_sides_height
+ \penalty\outputpenalty
+ \vskip-\dimexpr\d_page_sides_height-\strutdp\relax
+ \prevdepth\strutdp}
+ %\ignoreparskip}
+
+\def\page_sides_handle_float#1%
+ {\page_sides_initialize_checker
+ \page_sides_check_horizontal_skips
+ \page_sides_check_vertical_skips
+ \page_sides_apply_horizontal_shift
+ \page_sides_check_previous_float
+ \page_sides_inject_before
+ \page_sides_inject_dummy_lines
+ \page_sides_relocate_float{#1}%
+ \page_sides_apply_vertical_shift
+ \page_sides_analyse_space
+ \ifconditional\c_page_floats_room
+ \global\setfalse\c_page_sides_delayed
+ % we're ok
+ \else
+ \global\settrue\c_page_sides_delayed
+ \global\c_page_sides_m_of_lines\c_page_sides_n_of_lines
+ \ifcase\c_page_sides_page_method
+ \page_otr_fill_and_eject_page
+ \or
+ \page_otr_force_new_page_one
+ \else
+ \page_otr_fill_and_eject_page
+ \fi
+ \global\c_page_sides_n_of_lines\c_page_sides_m_of_lines
+ \page_sides_analyse_space
+ %\page_sides_inject_before
+ \page_sides_inject_dummy_lines
+ \fi
+ \page_sides_place_float
+ \global\setfalse\c_page_sides_delayed
+ \page_sides_check_floats_reset
+ \page_sides_wrapup}
+
+\def\page_sides_wrapup
+ {% we need to do this aftergroup
+ \aftergroup\par
+ \aftergroup\ignoreparskip
+ \aftergroup\ignorespaces}
+
+\def\page_sides_check_floats_indeed
+ {\page_sides_analyse_progress
+ \ifdim\d_page_sides_progress>\zeropoint
+ \page_sides_check_floats_set
+ \else
+ \page_sides_check_floats_reset
+ \fi
+ \parskip\s_spac_whitespace_parskip} % not needed
+
+% \let\page_sides_check_floats\page_sides_check_floats_indeed
+
+\let\page_sides_check_floats\relax
+
+\def\page_sides_initialize_checker
+ {\ifrelax\page_sides_check_floats
+ \glet\page_sides_check_floats\page_sides_check_floats_indeed
+ \clf_enablesidefloatchecker
+ \glet\page_sides_initialize_checker\relax
+ \fi}
+
+\protected\def\page_sides_check_floats_tracer
+ {\begingroup
+ \dontleavehmode
+ \ifnum\c_page_sides_float_type>\plusfour
+ \rlap
+ {\hskip\availablehsize % d_page_sides_width % kern
+ \color[trace:o]%
+ {\rlap{\kern.25\bodyfontsize\showstruts\strut}%
+ \vrule\s!height.5\points\s!depth.5\points\s!width\d_page_sides_width}}%
+ \else
+ \hskip-\d_page_sides_width % kern
+ \color[trace:o]%
+ {\vrule\s!height.5\points\s!depth.5\points\s!width\d_page_sides_width
+ \llap{\showstruts\strut\kern.25\bodyfontsize}}%
+ \fi
+ \endgroup}
+
+% tricky test:
+
+% \starttext
+% \dorecurse{33}{\line{#1}}
+% \placefigure[left]{}{}
+% \input ward
+% \startitemize
+% \item word \item word \item word \item word
+% \stopitemize
+% \input ward
+% \page
+% \placefigure[left]{}{}
+% \dontleavehmode \begingroup \input ward \par \endgroup
+% \dontleavehmode \begingroup \input ward \par \endgroup
+% \dontleavehmode \begingroup \input ward \par \endgroup
+% \input ward
+% \stoptext
+
+\protected\def\page_sides_check_floats_set
+ {\edef\p_sidethreshold{\floatparameter\c!sidethreshold}%
+ \ifconditional\c_page_sides_delayed
+ % For Alan's hanging right float that moved to the next page.
+ \d_page_sides_progress\zeropoint
+ \fi
+ \ifx\p_sidethreshold\v!old
+ \d_page_sides_progression\dimexpr\d_page_sides_progress+\strutht-\roundingeps\relax
+ \c_page_sides_n_of_hang\d_page_sides_progression
+ \divide\c_page_sides_n_of_hang \baselineskip\relax
+ \else
+ \d_page_sides_progression
+ \ifempty\p_sidethreshold
+ \d_page_sides_progress
+ \else
+ \dimexpr\d_page_sides_progress-\p_sidethreshold\relax
+ \fi
+ \getnoflines\d_page_sides_progression
+ % this can be an option
+ \ifdim\dimexpr\noflines\lineheight>\dimexpr\pagegoal-\pagetotal\relax
+ \getrawnoflines\d_page_sides_progression
+ \fi
+ %
+ \c_page_sides_n_of_hang\noflines
+ \fi
+ \global\c_page_sides_hangafter\zerocount
+ \ifnum\c_page_sides_n_of_hang>\zerocount
+ \ifcase\c_page_sides_n_of_lines
+ \else
+ \ifcase\c_page_sides_lines_done
+ \global\c_page_sides_lines_done\c_page_sides_n_of_hang
+ \else
+ \privatescratchcounter\c_page_sides_lines_done
+ \advance\privatescratchcounter-\c_page_sides_n_of_hang
+ \global\advance\c_page_sides_n_of_lines-\privatescratchcounter
+ \fi
+ \fi
+ \ifnum\c_page_sides_n_of_lines>\zerocount
+ \privatescratchtoks\emptytoks
+ \privatescratchcounter\c_page_sides_n_of_lines
+ \privatescratchdimen\dimexpr\hsize-\d_page_sides_width\relax
+ \dorecurse\c_page_sides_n_of_lines
+ {\toksapp\privatescratchtoks{\zeropoint\hsize}}%
+ \ifnum\c_page_sides_n_of_hang>\c_page_sides_n_of_lines
+ \advance\c_page_sides_n_of_hang -\c_page_sides_n_of_lines\relax
+ \advance\privatescratchcounter\c_page_sides_n_of_hang
+ \dorecurse\c_page_sides_n_of_hang % weird, shouldn't that be scratchcounter
+ {\ifnum\c_page_sides_float_type>\plusfour
+ \toksapp\privatescratchtoks{\zeropoint\privatescratchdimen}%
+ \else
+ \toksapp\privatescratchtoks{\d_page_sides_width\privatescratchdimen}%
+ \fi}%
+ \fi
+ \parshape
+ \numexpr\privatescratchcounter+\plusone\relax
+ \the\privatescratchtoks
+ \zeropoint \hsize
+ \relax
+ \else
+ \hangindent \ifnum\c_page_sides_float_type>\plusfour -\fi\d_page_sides_width
+ \hangafter-\c_page_sides_n_of_hang
+ \global\c_page_sides_hangafter\hangafter
+ \fi
+ \fi
+ \global\advance\c_page_sides_checks_done \plusone
+ \iftracesidefloats
+ \page_sides_check_floats_tracer
+ \fi}
+
+\protected\def\page_sides_check_floats_reset
+ {\ifcase\c_page_sides_checks_done\else
+ \ifcase\c_page_sides_hangafter\else
+ % we need to deal with par's ending in a group which would restore
+ % hang parameters
+ \global\c_page_sides_hangafter\zerocount
+ \hangindent\zeropoint
+ \fi
+ % \global % no, otherwise a next hangindent won't work
+ \c_page_sides_checks_done\zerocount
+ \fi}
+
+\protected\def\page_sides_synchronize_floats
+ {\ifinner \else
+ \page_sides_check_floats
+ \fi}
+
+\protected\def\page_sides_check_previous_float
+ {\page_sides_analyse_progress
+ \ifdim\d_page_sides_progress>\zeropoint \relax
+ \ifconditional\c_page_sides_short
+ \global\setfalse\c_page_sides_short
+ \page_otr_fill_and_eject_page
+ \else
+ \kern\d_page_sides_progress
+ \fi
+ \fi}
+
+% \def\adjustsidefloatdisplaylines % public, will change
+% {\aftergroup\page_sides_adjust_display_lines}
+%
+% \def\page_sides_adjust_display_lines
+% {\par
+% \noindent
+% \ignorespaces}
+
+%D We need to hook it into the other otr's. This code will be adapted once we rename
+%D the callers. We use \type {\def} as they can be redefined! Some will become obsolete
+
+\permanent\protected\def\checksidefloat {\page_sides_check_floats}
+\permanent\protected\def\flushsidefloats {\page_sides_flush_floats_text}
+\permanent\protected\def\flushsidefloatsafterpar{\page_sides_flush_floats_after_par}
+\permanent\protected\def\forgetsidefloats {\page_sides_forget_floats}
+%permanent\protected\def\synchronizesidefloats {\page_sides_synchronize_floats}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-smp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-smp.mkxl
new file mode 100644
index 00000000000..ccb0e989e82
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-smp.mkxl
@@ -0,0 +1,56 @@
+%D \module
+%D [ file=page-smp, % was: core-mul, page-mul
+%D version=1998.03.15,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Simple Multi Column Output,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D The old one:
+
+\definemixedcolumns
+ [\v!columns]
+ % compatible with columns default :
+ [\c!balance=\v!yes,
+ \c!blank={\v!line,\v!fixed}]
+
+\permanent\protected\def\setupcolumns
+ {\setupmixedcolumns[\v!columns]}
+
+%D This will be replaced by mixed box.
+
+\permanent\tolerant\protected\def\startsimplecolumns[#1]%
+ {\bgroup
+ \setsimplecolumnshsize[#1]%
+ \nopenalties
+ \setbox\scratchbox\vbox\bgroup
+ \forgetall} % \blank[\v!disable]
+
+\permanent\protected\def\stopsimplecolumns
+ {\removebottomthings
+ \egroup
+ \rigidcolumnbalance\scratchbox
+ \egroup}
+
+\permanent\protected\def\setsimplecolumnshsize[#1]%
+ {\getdummyparameters
+ [\c!width=\hsize,
+ \c!distance=1.5\bodyfontsize,
+ \c!n=2,
+ \c!lines=0,
+ #1]%
+ \edef\rigidcolumnlines
+ {\directdummyparameter\c!lines}%
+ \setrigidcolumnhsize
+ {\directdummyparameter\c!width}%
+ {\directdummyparameter\c!distance}%
+ {\directdummyparameter\c!n}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-spr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-spr.mkxl
new file mode 100644
index 00000000000..49285c96fef
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-spr.mkxl
@@ -0,0 +1,129 @@
+%D \module
+%D [ file=page-spr,
+%D version=2002.11.11,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Spreading,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Spreading}
+
+% This module is experimental and not yet official!
+
+\unprotect
+
+\newbox \b_page_spread_content
+\newconditional\c_page_spread_busy
+\newconditional\c_page_spread_once % when true only one flush (writes etc)
+
+\permanent\overloaded\protected\def\normalsettextpagecontent#1#2#3% #2 and #3 will disappear
+ {\setbox#1\hpack
+ {\setlayoutcomponentattribute{\v!page:\v!text}%
+ \vpack \layoutcomponentboxattribute to \textheight
+ {\offinterlineskip
+ \freezetextwidth
+ \hsize\textwidth % local variant of \sethsize
+ \boxmaxdepth\maxdepth
+ \noindent % content can be < \hsize
+ \page_otr_command_package_contents#2#3}}%
+ \dp#1\zeropoint
+ \ifconditional\c_page_spread_busy
+ \normalsettextpagecontent_spread{#1}%
+ \else
+ \normalsettextpagecontent_normal{#1}%
+ \fi}
+
+\def\normalsettextpagecontent_normal#1%
+ {\ifdim\wd#1=\makeupwidth\else
+ \setbox#1\hpack to \makeupwidth
+ {\hss\box#1\hss}% never change the \hss's
+ \fi}
+
+\def\normalsettextpagecontent_spread#1%
+ {\setbox#1\hpack to \makeupwidth
+ {\ifvoid\b_page_spread_content
+ \ifconditional\c_page_spread_once
+ \box#1%
+ \else
+ \global\setbox\b_page_spread_content\box#1%
+ \copy\b_page_spread_content
+ \fi
+ \hss % left page
+ \else
+ \hss % right page
+ \ifarrangingpages
+ % writes don't matter anyway
+ \orelse\ifconditional\c_page_spread_once
+ \global\setbox\b_page_spread_content\emptyhbox
+ \wd\b_page_spread_content\makeupwidth
+ \ht\b_page_spread_content\textheight
+ \else
+ % writes can interfere (todo: write stripper, but how about hyperlinks)
+ \fi
+ \box\b_page_spread_content
+ \fi}}
+
+\protected\def\page_spread_flush
+ {\ifconditional\c_page_spread_busy \ifvoid\b_page_spread_content\else
+ % this page will be discarded later
+ \emptyhbox \page
+ \fi \fi }
+
+\permanent\protected\def\startspread
+ {\unless\ifdoublesided
+ \expandafter\page_spread_start_nop
+ \orelse\ifconditional\c_page_spread_busy
+ \expandafter\page_spread_start_nop
+ \else
+ \expandafter\page_spread_start_yes
+ \fi}
+
+\aliased\let\stopspread\relax
+
+\protected\def\page_spread_start_yes
+ {\bgroup
+ \page[\v!left]%
+ %\setsystemmode{spread}%
+ \settrue\c_page_spread_busy
+ \expanded{\setuplayout[\c!textwidth=\the\dimexpr2\textwidth+2\backspace\relax]}%
+ \enforced\let\stopspread\page_spread_stop_yes}
+
+\permanent\protected\def\page_spread_stop_yes
+ {\kern\zeropoint
+ \page
+ \setuplayout[\c!textwidth=\textwidth]%
+ \page[\v!left]%
+ \egroup}
+
+\protected\def\page_spread_start_nop
+ {\bgroup
+ \let\stopspread\page_spread_stop_nop}
+
+\protected\def\page_spread_stop_nop
+ {\egroup}
+
+\protect \endinput
+
+% texexec --arr --pdf test
+%
+% \setuplayout[width=middle]
+% \setuppapersize[A4][A3,landscape]
+% \setuppagenumbering[alternative=doublesided]
+% \setuparranging[2UP]
+%
+% \starttext
+%
+% \dorecurse{3}{\input tufte }
+%
+% \startspread
+% \dorecurse{10}{\input tufte }
+% \stopspread
+%
+% \dorecurse{3}{\input tufte }
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-str.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-str.mkxl
new file mode 100644
index 00000000000..d6e01ea595c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-str.mkxl
@@ -0,0 +1,128 @@
+%D \module
+%D [ file=page-str,
+%D version=2010.03.13, % 2006.03.21,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Page Streams,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Page Streams}
+
+%D The first version of this component of \CONTEXT\ was written for Thomas Schmitz
+%D who asked for parallel page streams. While playing with the code, I decided to
+%D make it into a component that can be used to construct all kind of stream related
+%D mechanisms. Because I could apply this feature in a project, there is some
+%D additional code here (related to graphics).
+%D
+%D These macros were written while listening to and watching the DVD \quotation
+%D {Rush In Rio}.
+%D
+%D The reimplementation (or rather experimenting with the complete rewrite) was done
+%D while looping over \quotation {Wende Snijders No.9}.
+%D
+%D Remark: marknotes are gone, at least for a while.
+
+\registerctxluafile{page-str}{}
+
+\unprotect
+
+\mutable\let\currentoutputstream\empty
+
+\newif \ifinoutputstream % will become a conditional or mode
+\newtoks \everyenableoutputstream
+
+\appendtoks
+ \page_otr_command_flush_side_floats
+\to \everyenableoutputstream
+
+\permanent\protected\def\initializeoutputstreams
+ {\clf_initializestream
+ \enforced\glet\initializeoutputstreams\relax}
+
+\permanent\protected\def\enableoutputstream[#1]% could be \startoutputsubstream
+ {\initializeoutputstreams
+ \the\everyenableoutputstream
+ \inoutputstreamtrue
+ \xdef\currentoutputstream{#1}%
+ \clf_enablestream{#1}
+ \prevdepth\zeropoint} % maybe more is needed i.e. we need to get rid of leading glue
+
+\permanent\protected\def\disableoutputstream
+ {\inoutputstreamfalse
+ \glet\currentoutputstream\s!default
+ \clf_disablestream}
+
+\permanent\protected\def\startoutputstream[#1]%
+ {\begingroup
+ \initializeoutputstreams
+ \the\everyenableoutputstream
+ \inoutputstreamtrue
+ \xdef\currentoutputstream{#1}%
+ \clf_startstream{#1}}
+
+\permanent\protected\def\stopoutputstream
+ {\clf_stopstream
+ \endgroup}
+
+\permanent\protected\def\startoutputsubstream[#1]% just push/pop instead
+ {\globalpushmacro\currentoutputstream
+ \enableoutputstream[#1]}
+
+\permanent\protected\def\stopoutputsubstream
+ {\globalpopmacro\currentoutputstream
+ \enableoutputstream[\currentoutputstream]}
+
+\permanent \protected\def\flushoutputstream [#1]{\clf_flushstream{#1}}
+\permanent \protected\def\outputstreambox [#1]{\vbox{\clf_flushstream{#1}}}
+\permanent \protected\def\outputstreamcopy [#1]{\vbox{\clf_flushstreamcopy{#1}}}
+\permanent \protected\def\outputstreamunvbox [#1]{\clf_flushstream{#1}}
+\permanent \protected\def\outputstreamunvcopy[#1]{\clf_flushstreamcopy{#1}}
+\permanent \protected\def\synchronizestreams [#1]{\clf_synchronizestream{#1}}
+\permanent\tolerant\protected\def\pushoutputstream [#1]{\clf_pushstream{#1}}
+
+\protect \endinput
+
+% \enabletrackers[streams.flushing]
+%
+% \setuplayout[grid=yes] \showgrid
+%
+% \starttext
+%
+% \input tufte
+%
+% \startoutputstream[nl]
+%
+% Wat doen we hier?
+%
+% \enableoutputstream[en]
+%
+% Are you sleeping, brother John?\footnote{xxx}
+%
+% \dorecurse{4}{x \footnote{note \recurselevel}\input tufte \par \pushoutputstream}
+%
+% \enableoutputstream[de]
+%
+% Bruder Jakob, schläfst du noch?\footnote{yyy}
+%
+% \dorecurse{4}{x \footnote{note \recurselevel}\input ward \par \pushoutputstream}
+%
+% \disableoutputstream
+%
+% \stopoutputstream
+%
+% Vader Jacob, slaap je nog?\footnote{zzz}
+%
+% \input tufte
+%
+% \synchronizestreams[en,de,nl]
+%
+% \page \flushoutputstream[en] \input knuth
+% \page \flushoutputstream[de] \input knuth
+% \page \flushoutputstream[nl] \input knuth
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-txt.mklx b/Master/texmf-dist/tex/context/base/mkxl/page-txt.mklx
new file mode 100644
index 00000000000..f8db27f38e7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-txt.mklx
@@ -0,0 +1,959 @@
+% macros=mkvi
+
+%D \module
+%D [ file=page-txt, % copied from main-001,
+%D version=1997.03.31,
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Texts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Texts}
+
+\unprotect
+
+\newtoks\toptextcontent \newtoks\leftedgetextcontent
+\newtoks\headertextcontent \newtoks\leftmargintextcontent
+\newtoks\footertextcontent \newtoks\rightmargintextcontent
+\newtoks\bottomtextcontent \newtoks\rightedgetextcontent
+
+\newtoks\texttextcontent
+
+%D \macros
+%D {setuptop, setupheader, setuptext,setupfooter, setupbottom}
+%D
+%D The macros in this module sometimes look a bit more complicated than needed,
+%D which is a direct result of the fact that their ancestors are quite old and
+%D upward compatibility is a must.
+%D
+%D \showsetup{setuptop}
+%D \showsetup{setupheader}
+%D \showsetup{setuptext}
+%D \showsetup{setupfooter}
+%D \showsetup{setupbottom}
+
+\installcorenamespace{layouttexts}
+\installcorenamespace{layouttextsline}
+\installcorenamespace{layouttextsreset}
+\installcorenamespace{layouttextssynchronize}
+\installcorenamespace{layouttextstrut}
+\installcorenamespace{layouttextspecial}
+\installcorenamespace{layouttextcontent}
+
+\installcommandhandler \??layouttexts {layoutelement} \??layouttexts
+
+% \appendtoks
+% \resetlayoutelementparameter\c!lefttext % resolves better
+% \resetlayoutelementparameter\c!middletext
+% \resetlayoutelementparameter\c!righttext
+% \to \everydefinelayoutelement
+
+\definelayoutelement[\v!top ]
+\definelayoutelement[\v!header]
+\definelayoutelement[\v!text ]
+\definelayoutelement[\v!footer]
+\definelayoutelement[\v!bottom]
+
+\definelayoutelement[\v!top :\v!text] [\v!top ][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!header:\v!text] [\v!header][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!text :\v!text] [\v!text ][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!footer:\v!text] [\v!footer][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!bottom:\v!text] [\v!bottom][\c!lefttext=,\c!middletext=,\c!righttext=]
+
+\definelayoutelement[\v!top :\v!margin][\v!top ][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!header:\v!margin][\v!header][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!text :\v!margin][\v!text ][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!footer:\v!margin][\v!footer][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!bottom:\v!margin][\v!bottom][\c!lefttext=,\c!middletext=,\c!righttext=]
+
+\definelayoutelement[\v!top :\v!edge] [\v!top ][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!header:\v!edge] [\v!header][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!text :\v!edge] [\v!text ][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!footer:\v!edge] [\v!footer][\c!lefttext=,\c!middletext=,\c!righttext=]
+\definelayoutelement[\v!bottom:\v!edge] [\v!bottom][\c!lefttext=,\c!middletext=,\c!righttext=]
+
+\permanent\tolerant\protected\def\setuplayouttext[#vertical]#spacer[#horizontal]#spacer[#settings]%
+ {\ifnum\lastarguments>\plustwo
+ \setuplayoutelement[#vertical:#horizontal][#settings]%
+ \else
+ \setuplayoutelement[#vertical][#horizontal]%
+ \fi}
+
+\appendtoks
+ \ifempty\currentlayoutelement\else
+ \page_layouts_synchronize_element\currentlayoutelement % brr, can be vertical:horizontal
+ \fi
+\to \everysetuplayoutelement
+
+\mutable\let\currenttextstate \empty
+\mutable\let\previoustextstate\empty
+
+\def\page_layouts_reset_element_status#vertical%
+ {\gdefcsname\??layouttextsreset#vertical\endcsname{\page_layouts_set_element_status_normal#vertical}}
+
+\def\page_layouts_set_element_status_normal#vertical%
+ {\gletcsname\namedlayoutelementhash#vertical\c!state\endcsname\v!normal
+ \gletcsname\??layouttextsreset#vertical\endcsname\relax
+ \page_layouts_synchronize_element{#vertical}}
+
+\def\page_layouts_synchronize_element#vertical%
+ {\xdef\previoustextstate{\csname\??layouttextssynchronize#vertical\endcsname}% can be a let
+ \edef\currenttextstate {\namedlayoutelementparameter{#vertical}\c!state}%
+ %\writestatus{>>}{[#vertical:\currenttextstate/\previoustextstate]}%
+ \ifx\currenttextstate\previoustextstate \else
+ \page_layouts_synchronize_element_indeed{#vertical}%
+ \fi}
+
+\def\page_layouts_synchronize_element_indeed#vertical%
+ {\ifx\currenttextstate \v!high \calculatevsizes\page_backgrounds_recalculate \orelse
+ \ifx\previoustextstate\v!high \calculatevsizes\page_backgrounds_recalculate \orelse
+ \ifx\currenttextstate \v!none \calculatevsizes\page_backgrounds_recalculate \orelse
+ \ifx\previoustextstate\v!none \calculatevsizes\page_backgrounds_recalculate \fi
+ \gletcsname\??layouttextssynchronize#vertical\endcsname\currenttextstate}
+
+\permanent\protected\def\setuptop {\setuplayouttext[\v!top ]}
+\permanent\protected\def\setupheader{\setuplayouttext[\v!header]}
+\permanent\protected\def\setuptext {\setuplayouttext[\v!text ]}
+\permanent\protected\def\setupfooter{\setuplayouttext[\v!footer]}
+\permanent\protected\def\setupbottom{\setuplayouttext[\v!bottom]}
+
+%D We inherit some settings:
+
+\setuplayoutelement
+ [ \c!leftstyle=\layoutelementparameter\c!style,
+ \c!middlestyle=\layoutelementparameter\c!style,
+ \c!rightstyle=\layoutelementparameter\c!style,
+ \c!leftcolor=\layoutelementparameter\c!color,
+ \c!middlecolor=\layoutelementparameter\c!color,
+ \c!rightcolor=\layoutelementparameter\c!color,
+ \c!leftwidth=\layoutelementparameter\c!width,
+ \c!middlewidth=\layoutelementparameter\c!width,
+ \c!rightwidth=\layoutelementparameter\c!width]
+
+%D \macros
+%D {noheaderandfooterlines,notopandbottomlines}
+%D
+%D Although not really needed, the following shortcuts sometimes come in handy.
+%D
+%D \showsetup{noheaderandfooterlines}
+%D \showsetup{notopandbottomlines}
+
+\permanent\protected\def\noheaderandfooterlines
+ {\setuplayoutelement[\v!header][\c!state=\v!empty]%
+ \setuplayoutelement[\v!footer][\c!state=\v!empty]}
+
+\permanent\protected\def\notopandbottomlines
+ {\setuplayoutelement[\v!top ][\c!state=\v!empty]%
+ \setuplayoutelement[\v!bottom][\c!state=\v!empty]}
+
+%D \macros
+%D {setuptoptexts,setupheadertexts,setuptexttexts,setupfootertexts,setupbottomtexts}
+%D
+%D The next macros take one or more arguments. The exact setup depends on the number
+%D of arguments. Although not that intuitive, the current scheme evolved out of the
+%D original. When margin and edge texts as well as middle texts showed up, the
+%D current odd|/|even scheme surfaced.
+%D
+%D \showsetup{setuptoptexts}
+%D \showsetup{setupheadertexts}
+%D \showsetup{setuptexttexts}
+%D \showsetup{setupfootertexts}
+%D \showsetup{setupbottomtexts}
+%D
+%D Only the following have checking for pagenumber, date and mark built in, so when
+%D someone uses the key|/|value interface these things have to be set explicitly as
+%D part of the text.
+
+\permanent\protected\def\setuptoptexts {\setuptexts[\v!top ]}
+\permanent\protected\def\setupheadertexts{\setuptexts[\v!header]}
+\permanent\protected\def\setuptexttexts {\setuptexts[\v!text ]}
+\permanent\protected\def\setupfootertexts{\setuptexts[\v!footer]}
+\permanent\protected\def\setupbottomtexts{\setuptexts[\v!bottom]}
+
+\mutable\let\currentlayoutelementstate\empty
+
+\permanent\tolerant\protected\def\setuptexts[#vertical]#spacer[#horizontal]#spacer[#a]#spacer[#b]#spacer[#c]#spacer[#d]%
+ {\ifarguments
+ % there is always one
+ \or
+ \edef\currentlayoutelement{#vertical:\v!text}%
+ \resetlayoutelementparameter\c!lefttext
+ \resetlayoutelementparameter\c!righttext
+ \resetlayoutelementparameter\c!middletext
+ \edef\currentlayoutelement{#vertical:\v!margin}%
+ \resetlayoutelementparameter\c!lefttext
+ \resetlayoutelementparameter\c!righttext
+ \resetlayoutelementparameter\c!middletext
+ \edef\currentlayoutelement{#vertical:\v!edge}%
+ \resetlayoutelementparameter\c!lefttext
+ \resetlayoutelementparameter\c!righttext
+ \resetlayoutelementparameter\c!middletext
+ \or
+ \edef\currentlayoutelement{#vertical:\v!text}%
+ \resetlayoutelementparameter\c!lefttext
+ \resetlayoutelementparameter\c!righttext
+ \setlayoutelementparameter \c!middletext{\page_layouts_process_element_single{#horizontal}}%
+ \or
+ \edef\currentlayoutelement{#vertical:\v!text}%
+ \setlayoutelementparameter\c!lefttext {\page_layouts_process_element_single{#horizontal}}%
+ \setlayoutelementparameter\c!righttext{\page_layouts_process_element_single{#a}}%
+ \or
+ \edef\currentlayoutelement{#vertical:#horizontal}%
+ \setlayoutelementparameter\c!lefttext {\page_layouts_process_element_single{#a}}%
+ \setlayoutelementparameter\c!righttext{\page_layouts_process_element_single{#b}}%
+ \or
+ \edef\currentlayoutelement{#vertical:\v!text}%
+ \setlayoutelementparameter\c!lefttext {\page_layouts_process_element_double{#horizontal}{#c}}%
+ \setlayoutelementparameter\c!righttext{\page_layouts_process_element_double{#a}{#b}}%
+ \or
+ \edef\currentlayoutelement{#vertical:#horizontal}%
+ \setlayoutelementparameter\c!lefttext {\page_layouts_process_element_double{#a}{#d}}%
+ \setlayoutelementparameter\c!righttext{\page_layouts_process_element_double{#b}{#c}}%
+ \fi}
+
+%D Left and right texts are swapped on odd and even pages, but only when double
+%D sided typesetting is enabled.
+
+\setvalue{\??layouttextstrut\v!yes}{\setstrut\strut} % maybe more variants
+
+\let\m_page_layouts_element_content\empty
+
+\protected\def\page_layouts_process_element_single#content%
+ {\edef\m_page_layouts_element_content{\detokenize{#content}}% so no \v!xxx
+ \ifcsname\??layouttextspecial\m_page_layouts_element_content\endcsname
+ \lastnamedcs
+ \else
+ \doifelsemarking\m_page_layouts_element_content
+ {\getmarking[\m_page_layouts_element_content][\v!first]}%
+ {#content}%
+ \fi}
+
+\protected\def\page_layouts_process_element_double#first#second%
+ {\doifelseoddpage
+ {\page_layouts_process_element_single{#first}}
+ {\page_layouts_process_element_single{#second}}}
+
+\protected\def\page_layouts_process_element_indeed#style#color#width%
+ {\begingroup
+ \uselayoutelementstyleandcolor#style#color%
+ \begincsname\??layouttextstrut\layoutelementparameter\c!strut\endcsname
+ \ignorecrlf
+ \edef\p_width{\layoutelementparameter#width}%
+ \ifempty\p_width\else
+ \expandafter\page_layouts_process_element_limited
+ \fi\p_text
+ \endgroup}
+
+\def\page_layouts_process_element_limited#content% are the {}{}{} still needed?
+ {\limitated
+ left \p_width
+ text {#content}
+ sentinel {\unknown}
+ \relax}
+
+\setvalue{\??layouttextspecial\v!pagenumber}{\page_layouts_place_page_number}
+\setvalue{\??layouttextspecial\v!date }{\currentdate}
+
+%D When specified, the texts are automatically limited in length.
+
+\appendtoks \page_layouts_place_text_line\v!top \topheight \to \toptextcontent
+\appendtoks \page_layouts_place_text_line\v!header\headerheight \to \headertextcontent
+\appendtoks \page_layouts_place_text_line\v!text \textheight \to \texttextcontent
+\appendtoks \page_layouts_place_text_line\v!footer\footerheight \to \footertextcontent
+\appendtoks \page_layouts_place_text_line\v!bottom\bottomheight \to \bottomtextcontent
+
+%D Texts can be disabled, moved up and ignored, depending in the \type {status}
+%D variable. This is handled by the next couple of macros.
+
+\newcount\c_page_layouts_element_state_n
+
+\mutable\let\textlinestatus\empty
+
+\def\page_layouts_set_element_status#vertical%
+ {\c_page_layouts_element_state_n=0\namedlayoutelementparameter#vertical\c!n\relax
+ \ifcase\c_page_layouts_element_state_n
+ \edef\textlinestatus{\namedlayoutelementparameter#vertical\c!state}%
+ \else
+ \xdefcsname\namedlayoutelementhash#vertical\c!n\endcsname{\the\numexpr\c_page_layouts_element_state_n+\minusone}%
+ \let\textlinestatus\v!stop
+ \fi}
+
+\appendtoks
+ \doifinset\v!header\floatspecification{\xdefcsname\namedlayoutelementhash\v!header\c!n\endcsname{1}}%
+ \doifinset\v!footer\floatspecification{\xdefcsname\namedlayoutelementhash\v!footer\c!n\endcsname{1}}%
+\to \everybeforeflushedpagefloat
+
+\protected\def\page_layouts_place_text_line#vertical%
+ {\page_layouts_set_element_status#vertical\relax
+ \ifcsname\??layouttextsline\textlinestatus\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\page_layouts_place_text_line_unknown
+ \fi#vertical}
+
+\permanent\protected\def\doifelselayouttextline#vertical% shown or not
+ {\edef\currentlayoutelementstate{\namedlayoutelementparameter{#vertical}\c!state}%
+ \ifx\currentlayoutelementstate\v!normal
+ \expandafter\firstoftwoarguments
+ \orelse\ifx\currentlayoutelementstate\v!start
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifelselayoutsomeline#vertical% present or not
+ {\edef\currentlayoutelementstate{\namedlayoutelementparameter{#vertical}\c!state}%
+ \ifx\currentlayoutelementstate\v!none
+ \expandafter\secondoftwoarguments
+ \orelse\ifx\currentlayoutelementstate\v!high
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doiflayouttextlineelse\doifelselayouttextline
+\aliased\let\doiflayoutsomelineelse\doifelselayoutsomeline
+
+\newconditional\resyncaftertextline
+
+\setvalue{\??layouttextsline\v!normal}{\page_layouts_place_text_line_indeed}
+\setvalue{\??layouttextsline\empty }{\page_layouts_place_text_line_indeed}
+
+\letvalue{\??layouttextsline\v!none}\gobbletwoarguments
+\letvalue{\??layouttextsline\v!stop}\gobbletwoarguments
+
+\setvalue{\??layouttextsline\v!high}#vertical#height%
+ {\global\settrue\resyncaftertextline
+ \page_layouts_reset_element_status#vertical}
+
+\setvalue{\??layouttextsline\v!empty}#vertical#height%
+ {\page_layouts_reset_element_status#vertical}
+
+\setvalue{\??layouttextsline\v!start}#vertical#height%
+ {\page_layouts_reset_element_status#vertical%
+ \page_layouts_place_text_line_indeed#vertical#height}
+
+\setvalue{\??layouttextsline\v!nomarking}#vertical#height%
+ {\bgroup
+ \page_layouts_reset_element_status#vertical%
+ \settrue\inhibitgetmarking
+ \page_layouts_place_text_line_indeed#vertical#height%
+ \egroup}
+
+% \setupheadertexts [11]
+% \definetext [title] [header] [aa]
+% \setupheadertexts [11] [22]
+% \definetext [title] [header] [aa] [bb]
+% \setupheadertexts [text] [11] [22]
+% \definetext [title] [header] [text] [aa] [bb]
+% \setupheadertexts [11] [22] [33] [44]
+% \definetext [title] [header] [aa] [bb] [cc] [dd]
+% \setupheadertexts [text] [11] [22] [33] [44]
+% \definetext [title] [header] [text] [aa] [bb] [cc] [dd]
+
+\def\page_layouts_place_text_line_unknown#vertical#height%
+ {\global\settrue\resyncaftertextline
+ \begingroup % new
+ \page_layouts_reset_element_status#vertical%
+ \begincsname\namedlayoutelementhash{#vertical}\textlinestatus\endcsname
+ \begincsname\namedlayoutelementhash{#vertical:\v!text}\textlinestatus\endcsname
+ \begincsname\namedlayoutelementhash{#vertical:\v!margin}\textlinestatus\endcsname
+ \begincsname\namedlayoutelementhash{#vertical:\v!edge}\textlinestatus\endcsname
+ \page_layouts_place_text_line_indeed#vertical#height%
+ \endgroup}
+
+\letvalue{\??layouttextsline\s!unknown}\page_layouts_place_text_line_unknown
+
+%D The following macro has to be called after a page is flushed.
+
+\permanent\protected\def\resetlayouttextlines % public
+ {\begincsname\??layouttextsreset\v!top \endcsname
+ \begincsname\??layouttextsreset\v!header\endcsname
+ \begincsname\??layouttextsreset\v!text \endcsname
+ \begincsname\??layouttextsreset\v!footer\endcsname
+ \begincsname\??layouttextsreset\v!bottom\endcsname
+ \ifconditional\resyncaftertextline
+ \calculateglobalvsizes
+ \page_backgrounds_recalculate
+ \global\setfalse\resyncaftertextline
+ \fi}
+
+\permanent\def\getspecificlayouttext#vertical#horizontal#what%
+ {\begincsname\namedlayoutelementhash{#vertical:#horizontal}#what\endcsname}
+
+% \settext[header][text][middle][xxx][yyy]
+
+\permanent\tolerant\protected\def\settextcontent[#vertical]#spacer[#horizontal]#spacer[#one]#spacer[#two]#spacer[#three]% header text middle text/text
+ {\ifarguments\or\or\or % 3
+ \defcsname\namedlayoutelementhash{#vertical:#horizontal}\c!middletext\endcsname
+ {\page_layouts_process_element_double{#one}{#one}}%
+ \or % 4
+ \defcsname\namedlayoutelementhash{#vertical:#horizontal}\begincsname\??layouttextcontent\v!text:#one\endcsname\c!middletext\endcsname
+ {\page_layouts_process_element_double{#two}{#two}}%
+ \or % 5
+ \defcsname\namedlayoutelementhash{#vertical:#horizontal}\begincsname\??layouttextcontent\v!text:#one\endcsname\c!middletext\endcsname
+ {\page_layouts_process_element_double{#two}{#three}}%
+ \fi}
+
+\let\currentlayoutelement\relax
+
+%D This will be redefined, so no \type {\frozen} here!
+
+\permanent\tolerant\protected\def\resettextcontent[#vertical]#spacer[#horizontal]#spacer[#tag]% header text middle
+ {\ifarguments\or\or % 2
+ \edef\currentlayoutelement{#vertical:#horizontal}%
+ \resetlayoutelementparameter\c!lefttext
+ \resetlayoutelementparameter\c!middletext
+ \resetlayoutelementparameter\c!righttext
+ \or % 3
+% \edef\currentlayoutelement{#vertical:#horizontal}%
+% \letcsname\layoutelementhash\begincsname\??layouttextcontent\v!text:#tag\endcsname\c!middletext\endcsname\empty
+ \letcsname\namedlayoutelementhash{#vertical:#horizontal}\begincsname\??layouttextcontent\v!text:#tag\endcsname\c!middletext\endcsname\empty
+ \fi}
+
+\letcsname\??layouttextcontent\v!text:\c!middle\endcsname\c!middletext
+\letcsname\??layouttextcontent\v!text:\c!left \endcsname\c!lefttext
+\letcsname\??layouttextcontent\v!text:\c!right \endcsname\c!righttext
+
+%D The placement of a whole line is handled by the next two macros. These are hooked
+%D into the general purpose token list registers mentioned before.
+
+\mutable\let\currentlayouttextline\relax
+
+\def\page_layouts_place_text_line_indeed#vertical#height%
+ {\let\currentlayouttextline#vertical%
+ \ifdim#height>\zeropoint\relax % prevents pagenumbers when zero height
+ \page_layouts_place_text_line_left_or_right{#height}%
+ \fi}
+
+\def\page_layouts_place_text_line_left_or_right#height%
+ {\goleftonpage
+ \setbox\b_page_layouts_element\vbox to #height
+ {\vsize#height\relax
+ %\hsize\zeropoint % hack so that e.g. after=\hairline gives predictable results
+ \hsize\totaltextwidth
+ \normalbaselines
+ \enforced\let\\\ignoredlinebreak
+ \enforced\let\crlf\ignoredlinebreak
+ \namedlayoutelementparameter\currentlayouttextline\c!before
+ \doifbothsidesoverruled
+ \page_layouts_place_text_line_right
+ \page_layouts_place_text_line_right
+ \page_layouts_place_text_line_left
+ \namedlayoutelementparameter\currentlayouttextline\c!after
+ \vkern\zeropoint}% keep the \dp, beware of \vtops, never change this!
+ \dp\b_page_layouts_element\zeropoint
+ \box\b_page_layouts_element
+ \vkern-#height\relax}
+
+\let\page_layouts_place_extra_text_left \empty % historic
+\let\page_layouts_place_extra_text_right\empty % historic
+
+\def\page_layouts_place_text_line_right
+ {\hpack
+ {\ifdim\leftedgewidth>\zeropoint
+ \page_layouts_left_edge_element\c!lefttext\c!leftstyle\c!leftcolor\c!leftwidth
+ \fi
+ \ifdim\leftmarginwidth>\zeropoint
+ \page_layouts_left_margin_element\c!lefttext\c!leftstyle\c!leftcolor\c!leftwidth\zerocount
+ \fi
+ \ifdim\makeupwidth>\zeropoint
+ \page_layouts_text_body_element_l_m_r_e
+ \fi
+ \ifdim\rightmarginwidth>\zeropoint
+ \page_layouts_right_margin_element\c!righttext\c!rightstyle\c!rightcolor\c!rightwidth\plusone
+ \fi
+ \ifdim\rightedgewidth>\zeropoint
+ \page_layouts_right_edge_element\c!righttext\c!rightstyle\c!rightcolor\c!rightwidth
+ \fi}}
+
+\def\page_layouts_place_text_line_left
+ {\hpack
+ {\ifdim\leftedgewidth>\zeropoint
+ \page_layouts_left_edge_element\c!righttext\c!rightstyle\c!rightcolor\c!rightwidth
+ \fi
+ \ifdim\leftmarginwidth>\zeropoint
+ \page_layouts_left_margin_element\c!righttext\c!rightstyle\c!rightcolor\c!rightwidth\zerocount
+ \fi
+ \ifdim\makeupwidth>\zeropoint
+ \page_layouts_text_body_element_e_r_m_l
+ \fi
+ \ifdim\rightmarginwidth>\zeropoint
+ \page_layouts_right_margin_element\c!lefttext\c!leftstyle\c!leftcolor\c!leftwidth\plusone
+ \fi
+ \ifdim\rightedgewidth>\zeropoint
+ \page_layouts_right_edge_element\c!lefttext\c!leftstyle\c!leftcolor\c!leftwidth
+ \fi}}
+
+\def\page_layouts_left_edge_element_indeed#text#style#color#width%
+ {\letfromlayoutelementparameter\p_text#text%
+ \ifempty\p_text
+ \kern\leftedgewidth
+ \else
+ \hbox to \leftedgewidth\bgroup
+ \hss
+ \page_layouts_process_element_indeed#style#color#width%
+ \egroup
+ \fi}
+
+\def\page_layouts_left_edge_element#text#style#color#width%
+ {\edef\currentlayoutelement{\currentlayouttextline:\v!edge}%
+ \page_layouts_place_element_indeed\leftedgewidth
+ {\page_layouts_left_edge_element_indeed#text#style#color#width}%
+ \kern\leftedgedistance}
+
+\def\page_layouts_right_edge_element_indeed#text#style#color#width%
+ {\letfromlayoutelementparameter\p_text#text%
+ \ifempty\p_text
+ \kern\rightedgewidth
+ \else
+ \hbox to \rightedgewidth\bgroup
+ \page_layouts_process_element_indeed#style#color#width%
+ \hss
+ \egroup
+ \fi}
+
+\def\page_layouts_right_edge_element#text#style#color#width%
+ {\edef\currentlayoutelement{\currentlayouttextline:\v!edge}%
+ \kern\rightedgedistance
+ \page_layouts_place_element_indeed\rightedgewidth
+ {\page_layouts_right_edge_element_indeed#text#style#color#width}}
+
+% margin needs checking!
+
+\def\page_layouts_left_margin_element_indeed#text#style#color#width#margintoo%
+ {\letfromlayoutelementparameter\p_text#text%
+ \ifempty\p_text
+ \kern\leftmarginwidth
+ \else
+ \hbox to \leftmarginwidth\bgroup
+ \hss
+ \page_layouts_process_element_indeed#style#color#width%
+ \egroup
+ \fi
+ \ifcase#margintoo\or
+ \letfromlayoutelementparameter\p_text\c!margintext
+ \ifempty\p_text\else
+ \kern-\leftmarginwidth
+ \hbox to \leftmarginwidth\bgroup
+ \hss
+ \p_text % styling ?
+ \egroup
+ \fi
+ \fi}
+
+\def\page_layouts_left_margin_element#text#style#color#width#margintoo%
+ {\edef\currentlayoutelement{\currentlayouttextline:\v!margin}%
+ \page_layouts_place_element_indeed\leftmarginwidth
+ {\page_layouts_left_margin_element_indeed#text#style#color#width#margintoo}%
+ \kern\leftmargindistance}
+
+\def\page_layouts_right_margin_element_indeed#text#style#color#width#margintoo%
+ {\letfromlayoutelementparameter\p_text#text%
+ \ifempty\p_text
+ \kern\rightmarginwidth
+ \else
+ \hbox to \rightmarginwidth\bgroup
+ \page_layouts_process_element_indeed#style#color#width%
+ \hss
+ \egroup
+ \fi
+ \ifcase#margintoo\or
+ \letfromlayoutelementparameter\p_text\c!margintext
+ \ifempty\p_text\else
+ \kern-\rightmarginwidth
+ \hbox to \rightmarginwidth\bgroup
+ \p_text % ? styling
+ \hss
+ \egroup
+ \fi
+ \fi}
+
+\def\page_layouts_right_margin_element#text#style#color#width#margintoo%
+ {\edef\currentlayoutelement{\currentlayouttextline:\v!margin}%
+ \kern\rightmargindistance
+ \page_layouts_place_element_indeed\rightmarginwidth
+ {\page_layouts_right_margin_element_indeed#text#style#color#width#margintoo}}
+
+\def\page_layouts_text_body_element_indeed_l_m_r_e
+ {\letfromlayoutelementparameter\p_text\c!lefttext
+ \ifempty\p_text
+ \ifempty\page_layouts_place_extra_text_left\else
+ \hbox to \makeupwidth\bgroup
+ \page_layouts_place_extra_text_left
+ \hss
+ \egroup
+ \kern-\makeupwidth
+ \fi
+ \else
+ \hbox to \makeupwidth\bgroup
+ \page_layouts_place_extra_text_left
+ \page_layouts_process_element_indeed\c!leftstyle\c!leftcolor\c!leftwidth
+ \hss
+ \egroup
+ \kern-\makeupwidth
+ \fi
+ \letfromlayoutelementparameter\p_text\c!middletext
+ \ifempty\p_text\else
+ \hbox to \makeupwidth\bgroup
+ \hss
+ \page_layouts_process_element_indeed\c!middlestyle\c!middlecolor\c!middlewidth
+ \hss
+ \egroup
+ \kern-\makeupwidth
+ \fi
+ \letfromlayoutelementparameter\p_text\c!righttext
+ \ifempty\p_text
+ \kern\makeupwidth
+ \else
+ \hbox to \makeupwidth\bgroup
+ \hss
+ \page_layouts_process_element_indeed\c!rightstyle\c!rightcolor\c!rightwidth
+ \egroup
+ \fi}
+
+\def\page_layouts_text_body_element_indeed_e_r_m_l
+ {\letfromlayoutelementparameter\p_text\c!righttext
+ \ifempty\p_text\else
+ \hbox to \makeupwidth\bgroup
+ \page_layouts_process_element_indeed\c!rightstyle\c!rightcolor\c!rightwidth
+ \hss
+ \egroup
+ \kern-\makeupwidth
+ \fi
+ \letfromlayoutelementparameter\p_text\c!middletext
+ \ifempty\p_text\else
+ \hbox to \makeupwidth\bgroup
+ \hss
+ \page_layouts_process_element_indeed\c!middlestyle\c!middlecolor\c!middlewidth
+ \hss
+ \egroup
+ \kern-\makeupwidth
+ \fi
+ \letfromlayoutelementparameter\p_text\c!lefttext
+ \ifempty\p_text
+ \ifempty\page_layouts_place_extra_text_right
+ \kern\makeupwidth
+ \else
+ \hbox to \makeupwidth\bgroup
+ \hss
+ \page_layouts_place_extra_text_right
+ \egroup
+ \fi
+ \else
+ \hbox to \makeupwidth\bgroup
+ \hss
+ \page_layouts_process_element_indeed\c!leftstyle\c!leftcolor\c!leftwidth
+ \page_layouts_place_extra_text_right
+ \egroup
+ \fi}
+
+\def\page_layouts_text_body_element_l_m_r_e
+ {\edef\currentlayoutelement{\currentlayouttextline:\v!text}%
+ \page_layouts_place_element_indeed\makeupwidth\page_layouts_text_body_element_indeed_l_m_r_e}
+
+\def\page_layouts_text_body_element_e_r_m_l
+ {\edef\currentlayoutelement{\currentlayouttextline:\v!text}%
+ \page_layouts_place_element_indeed\makeupwidth\page_layouts_text_body_element_indeed_e_r_m_l}
+
+\def\page_layouts_place_element_indeed#width#content%
+ {\vbox % to \vsize
+ {\hsize#width\relax
+ \layoutelementparameter\c!before
+ \setlayoutcomponentattribute\currentlayoutelement
+ \hbox \layoutcomponentboxattribute to #width{#content}%
+ \layoutelementparameter\c!after}}
+
+%D Although it is far better to use backgrounds for this purpose, one can add a rule
+%D in the following way. This method makes the rules disappear in case of an empty
+%D text line. Consider this a feature.
+%D
+%D \starttyping
+%D \setupheadertexts[left][right]
+%D
+%D \setupheader[text][after=\hrule,style=bold]
+%D
+%D \starttext
+%D \input tufte \page
+%D \setupheader[state=empty]
+%D \input tufte \page
+%D \stoptext
+%D \stoptyping
+
+%D This code will move to \type {page-flt.tex}.
+
+\appendtoks \placerightmarginblock \kern-\rightmarginwidth \to \rightmargintextcontent
+\appendtoks \placeleftmarginblock \kern-\leftmarginwidth \to \leftmargintextcontent
+
+%D \macros
+%D {definetext}
+%D
+%D Some macros ago, we implemented the \type {status} option \type {unknown}. This
+%D one is used to take care of symbolic texts handlers.
+%D
+%D \showsetup{definetext}
+%D
+%D The next example demonstrates how we can use this mechanism to provide page
+%D (event) dependent text lines.
+%D
+%D \starttyping
+%D \definetext[chapter][footer][pagenumber]
+%D \setuphead[chapter][header=high,footer=chapter]
+%D \setupheadertexts[pagenumber]
+%D \setupfootertexts[left][right]
+%D \chapter{eerste} \dorecurse{20}{\input tufte \relax}
+%D \chapter{tweede} \dorecurse{20}{\input tufte \relax}
+%D \stoptyping
+
+\permanent\tolerant\protected\def\definetext[#tag]#spacer[#vertical]#spacer[#horizontal]#spacer[#a]#spacer[#b]#spacer[#c]#spacer[#d]%
+ {\ifnum\lastarguments<\plusfour
+ \defcsname\namedlayoutelementhash {#vertical}#tag\endcsname{\setuptexts[#vertical][#horizontal]\relax}% 1, 2, 3
+ \orelse\ifarguments\or\or\or\or
+ \defcsname\namedlayoutelementhash {#vertical}#tag\endcsname{\setuptexts[#vertical][#horizontal][#a]\relax}% 4
+ \or
+ \defcsname\namedlayoutelementhash{#vertical:#horizontal}#tag\endcsname{\setuptexts[#vertical][#horizontal][#a][#b]\relax}% 5
+ \or
+ \defcsname\namedlayoutelementhash {#vertical}#tag\endcsname{\setuptexts[#vertical][#horizontal][#a][#b][#c]\relax}% 6
+ \or
+ \defcsname\namedlayoutelementhash{#vertical:#horizontal}#tag\endcsname{\setuptexts[#vertical][#horizontal][#a][#b][#c][#d]\relax}% 7
+ \fi}
+
+%D A few more page breakers:
+
+\installpagebreakmethod \v!empty
+ {\page_otr_flush_all_floats
+ \page_otr_command_next_page
+ \doifnot{\namedlayoutelementparameter\v!header\c!state}\v!stop{\setuplayoutelement[\v!header][\c!state=\v!empty]}%
+ \doifnot{\namedlayoutelementparameter\v!footer\c!state}\v!stop{\setuplayoutelement[\v!footer][\c!state=\v!empty]}%
+ \page_otr_insert_dummy_page}
+
+\installpagebreakmethod \v!header
+ {\doifnot{\namedlayoutelementparameter\v!header\c!state}\v!stop{\setuplayoutelement[\v!header][\c!state=\v!empty]}}
+
+\installpagebreakmethod \v!footer
+ {\doifnot{\namedlayoutelementparameter\v!footer\c!state}\v!stop{\setuplayoutelement[\v!footer][\c!state=\v!empty]}}
+
+%D While the header and footer lines are moved away from the main text, the top and
+%D bottom lines are centered.
+
+\setuplayoutelement[\v!top ][\c!state=\v!normal,\c!n=0,\c!before=\vss,\c!after=\vss,\c!strut=]
+\setuplayoutelement[\v!header][\c!state=\v!normal,\c!n=0,\c!before=, \c!after=\vss,\c!strut=\v!yes]
+\setuplayoutelement[\v!text ][\c!state=\v!normal,\c!n=0,\c!before=\vss,\c!after=\vss,\c!strut=]
+\setuplayoutelement[\v!footer][\c!state=\v!normal,\c!n=0,\c!before=\vss,\c!after=, \c!strut=\v!yes]
+\setuplayoutelement[\v!bottom][\c!state=\v!normal,\c!n=0,\c!before=\vss,\c!after=\vss,\c!strut=]
+
+%D Moved here from strc-pag:
+%D
+%D We reset a previous location but only when it has a pagenumber associated. This
+%D is a rather messy test but better than the MkII way where we use states and keep
+%D settings.
+
+\let\m_page_layouts_page_number_location \relax
+\let\m_page_layouts_page_number_location_v\relax
+\let\m_page_layouts_page_number_location_h\relax
+\let\m_page_layouts_page_number_location_x\relax
+
+\def\page_layouts_place_page_number % also elsewhere .. beware, not \protected else
+ {\strc_pagenumbers_place_location} % test below fails
+
+\def\page_layouts_reset_page_number_location
+ {\ifrelax\m_page_layouts_page_number_location_v\else
+ % this can be done better now, no meaning hackery needed
+ \edef\currentlayoutelement{\m_page_layouts_page_number_location_v:\m_page_layouts_page_number_location_h}%
+ \edef\tempstring{\detokenizedlayoutelementparameter\m_page_layouts_page_number_location_x}%
+ \doif{\meaning\tempstring}{\meaning\page_layouts_place_page_number}
+ {\resetlayoutelementparameter\m_page_layouts_page_number_location_x}%
+ \fi}
+
+\def\page_layouts_set_page_number_location
+ {\edef\currentlayoutelement{\m_page_layouts_page_number_location_v:\m_page_layouts_page_number_location_h}%
+ \letlayoutelementparameter\m_page_layouts_page_number_location_x\page_layouts_place_page_number
+ \ifx\m_page_layouts_page_number_location_x\c!marginedgetext
+ \let\page_layouts_place_extra_text_left \page_layouts_place_page_number_left
+ \let\page_layouts_place_extra_text_right\page_layouts_place_page_number_right
+ \else
+ \let\page_layouts_place_extra_text_left \empty
+ \let\page_layouts_place_extra_text_right\empty
+ \fi}
+
+\def\page_layouts_identify_page_number_location
+ {\let\m_page_layouts_page_number_location_v\v!footer
+ \let\m_page_layouts_page_number_location_h\v!text
+ \let\m_page_layouts_page_number_location_x\c!middletext
+ \processallactionsinset[\directpagenumberingparameter\c!location]
+ [ \v!header=>\let\m_page_layouts_page_number_location_v\v!header,
+ \v!footer=>\let\m_page_layouts_page_number_location_v\v!footer,
+ \v!middle=>\let\m_page_layouts_page_number_location_h\v!text
+ \let\m_page_layouts_page_number_location_x\c!middletext,
+ \v!left=>\let\m_page_layouts_page_number_location_h\v!text
+ \let\m_page_layouts_page_number_location_x\c!lefttext,
+ \v!right=>\let\m_page_layouts_page_number_location_h\v!text
+ \let\m_page_layouts_page_number_location_x\c!righttext,
+ \v!inleft=>\let\m_page_layouts_page_number_location_h\v!margin
+ \let\m_page_layouts_page_number_location_x\c!lefttext,
+ \v!inright=>\let\m_page_layouts_page_number_location_h\v!margin
+ \let\m_page_layouts_page_number_location_x\c!righttext,
+ \v!inmargin=>\let\m_page_layouts_page_number_location_h\v!margin
+ \def\m_page_layouts_page_number_location_x{\ifdoublesided\c!margintext\else\c!righttext\fi},
+ \v!margin=>\let\m_page_layouts_page_number_location_h\v!margin
+ \def\m_page_layouts_page_number_location_x{\ifdoublesided\c!margintext\else\c!righttext\fi},
+ \v!atmargin=>\let\m_page_layouts_page_number_location_h\v!text
+ \let\m_page_layouts_page_number_location_x\c!marginedgetext,
+ \v!marginedge=>\let\m_page_layouts_page_number_location_h\v!text
+ \let\m_page_layouts_page_number_location_x\c!marginedgetext]}
+
+\protected\def\strc_pagenumbers_set_location
+ {\edef\p_strc_pagenumbers_location{\directpagenumberingparameter\c!location}%
+ \ifx\p_strc_pagenumbers_location\m_page_layouts_page_number_location
+ % unchanged
+ \else
+ \let\m_page_layouts_page_number_location\p_strc_pagenumbers_location
+ \page_layouts_reset_page_number_location
+ \ifempty\p_strc_pagenumbers_location
+ % set otherwise
+ \orelse\ifx\p_strc_pagenumbers_location\v!none
+ % set otherwise
+ \else
+ \page_layouts_identify_page_number_location
+ \page_layouts_set_page_number_location
+ \fi
+ \fi}
+
+\def\page_layouts_place_page_number_left % historic
+ {\begingroup
+ \setbox\scratchbox\hbox{\ignorespaces\layoutelementparameter\c!marginedgetext\removeunwantedspaces}%
+ \ifzeropt\wd\scratchbox\else
+ \edef\p_strc_pagenumbers_width{\directpagenumberingparameter\c!width}%
+ \ifempty\p_strc_pagenumbers_width
+ \box\scratchbox\tfskip
+ \else
+ \hpack to \p_strc_pagenumbers_width{\box\scratchbox\hss}%
+ \fi
+ \fi
+ \endgroup}
+
+\def\page_layouts_place_page_number_right % historic
+ {\begingroup
+ \setbox\scratchbox\hbox{\ignorespaces\layoutelementparameter\c!marginedgetext\removeunwantedspaces}%
+ \ifzeropt\wd\scratchbox\else
+ \edef\p_strc_pagenumbers_width{\directpagenumberingparameter\c!width}%
+ \ifempty\p_strc_pagenumbers_width
+ \tfskip\box\scratchbox
+ \else
+ \hpack to \p_strc_pagenumbers_width{\hss\box\scratchbox}%
+ \fi
+ \fi
+ \endgroup}
+
+\strc_pagenumbers_set_location % initializes
+
+\newbox\b_page_layouts_element
+
+\def\page_layouts_insert_elements
+ {\ifcase\pageornamentstate
+ \page_layouts_place_elements_indeed % we could have a special flag for always ignored
+ \fi}
+
+\def\page_layouts_place_elements_indeed
+ {\setbox\b_page_layouts_element\vpack
+ {\dontcomplain
+ \calculatereducedvsizes
+ \page_layouts_swap_margins
+ \offinterlineskip
+ \vkern\dimexpr-\topheight-\topdistance\relax
+ \the\toptextcontent
+ \vkern\dimexpr\topheight+\topdistance\relax
+ \the\headertextcontent
+ \vkern\dimexpr\headerheight+\headerdistance+\textdistance\relax
+ \anch_positions_place_anchors
+ \vkern\dimexpr-\textdistance-\textheight\relax
+ \the\texttextcontent
+ \vkern\textheight
+ \the\everyendoftextbody
+ \vkern\footerdistance
+ \the\footertextcontent
+ \vkern\dimexpr\footerheight+\bottomdistance\relax
+ \the\bottomtextcontent
+ \vkern\bottomheight
+ \vfilll}%
+ \smashbox\b_page_layouts_element
+ \box\b_page_layouts_element}
+
+% \ifdefined\page_prepare_backgrounds\else
+% \let\page_prepare_backgrounds\gobbleoneargument
+% \fi
+
+% only for very special controlled cases or experiments:
+
+\let\page_scale_text_box\gobbleoneargument
+
+\def\page_insert_body#1#2%
+ {\setbox\b_page_layouts_element\vpack
+ {\offinterlineskip
+ \calculatereducedvsizes
+ \calculatehsizes
+ \page_layouts_swap_margins
+ \vkern\dimexpr\headerheight+\headerdistance+\textdistance\relax
+ \dontleavehmode
+ %\page_prepare_backgrounds{#2}%
+ \hpack to \makeupwidth
+ {\begingroup
+ \page_layouts_swap_margins
+ \goleftonpage
+ \ifdim\leftedgewidth>\zeropoint
+ \the\leftedgetextcontent
+ \kern\dimexpr\leftedgewidth+\leftedgedistance\relax
+ \fi
+ \ifdim\leftmarginwidth>\zeropoint
+ \the\leftmargintextcontent
+ \kern\dimexpr\leftmarginwidth+\leftmargindistance\relax
+ \fi
+ \endgroup
+ \page_apply_postprocessors_page{#2}%
+ \settextpagecontent\b_page_layouts_element{#1}{#2}%
+ \page_backgrounds_add_to_text\b_page_layouts_element
+ \page_grids_add_to_box\b_page_layouts_element
+ \page_scale_text_box\b_page_layouts_element
+ \box\b_page_layouts_element
+ \begingroup
+ \ifdim\rightmarginwidth>\zeropoint
+ \kern\rightmargindistance
+ \the\rightmargintextcontent
+ \kern\rightmarginwidth
+ \fi
+ \ifdim\rightedgewidth>\zeropoint
+ \kern\rightedgedistance
+ \the\rightedgetextcontent
+ \kern\rightedgewidth
+ \fi
+ \endgroup
+ \hss}}%
+ \smashbox\b_page_layouts_element
+ \box\b_page_layouts_element}
+
+%D The main text area has to be combined with some additional (tracing) information.
+%D
+%D This will be stored as normal and overloaded in page-lyr and later in page-spr we
+%D overload the the stored version .. eventually i will clear up the experimental
+%D mess.
+
+\protected\def\settextpagecontent#1#2#3% #2 and #3 will disappear / is overloaded
+ {\setbox#1\hpack to \makeupwidth
+ {\hss % so don't change this
+ \setlayoutcomponentattribute{\v!page:\v!text}%
+ \vpack \layoutcomponentboxattribute to \textheight
+ {\offinterlineskip
+ \freezetextwidth
+ \hsize\textwidth % local variant of \sethsize <<< in columns?
+ \boxmaxdepth\maxdepth
+ \noindent % content can be < \hsize
+ \page_otr_command_package_contents#2#3}% this will vbox
+ \hss}%
+ \dp#1\zeropoint}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/page-var.mkxl b/Master/texmf-dist/tex/context/base/mkxl/page-var.mkxl
new file mode 100644
index 00000000000..1c7fd5b252d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/page-var.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=page-var,
+%D version=2011.12.05, % moved from other page-* files
+%D title=\CONTEXT\ Page Macros,
+%D subtitle=Variables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Page Macros / Variables}
+
+\unprotect
+
+% just for temporary moved variables
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/phys-dim.mkxl b/Master/texmf-dist/tex/context/base/mkxl/phys-dim.mkxl
new file mode 100644
index 00000000000..51ebbe17209
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/phys-dim.mkxl
@@ -0,0 +1,817 @@
+%D \module
+%D [ file=phys-dim,
+%D version=2011-06-13, % was digits and units 1997.03.19,
+%D title=\CONTEXT\ Physics,
+%D subtitle=Digits and Units,
+%D author={Hans Hagen},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{phys-dim}{}
+
+% TAGGING NEEDS CHECKING ... WILL DO WHEN PARSER IS OK
+
+\unprotect
+
+%D \macros
+%D {digits, setdigitmode, setdigitsign}
+%D
+%D This is an update of the \MKII\ digits mechanism. Beware, space delimited mode is
+%D now resticted!
+%D
+%D Depending on the digit mode the command \type {\digits} normalizes number
+%D patterns depending on the language set.
+%D
+%D \starttyping
+%D This will never be a \digits{1.000.000} seller.
+%D \stoptyping
+%D
+%D We still support the space delimited case but this is only for special purposes.
+%D When used in the text, you'd better use the argument variant.
+%D
+%D \startbuffer
+%D 1 \setdigitmode {1} \setdigitorder{0} \digits {12.345,90}
+%D 2 \setdigitmode {2} \setdigitorder{0} \digits {12.345,90}
+%D 3 \setdigitmode {3} \setdigitorder{0} \digits {12.345,90}
+%D 4 \setdigitmode {4} \setdigitorder{0} \digits {12.345,90}
+%D 5 \setdigitmode {5} \setdigitorder{0} \digits {12.345,90}
+%D 6 \setdigitmode {6} \setdigitorder{0} \digits {12.345,90}
+%D 1 \setdigitmode {1} \setdigitorder{1} \digits {12.345,90}
+%D 2 \setdigitmode {2} \setdigitorder{1} \digits {12.345,90}
+%D 3 \setdigitmode {3} \setdigitorder{1} \digits {12.345,90}
+%D 4 \setdigitmode {4} \setdigitorder{1} \digits {12.345,90}
+%D 5 \setdigitmode {5} \setdigitorder{1} \digits {12.345,90}
+%D 6 \setdigitmode {6} \setdigitorder{1} \digits {12.345,90}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This is typeset as:
+%D
+%D \startlines \getbuffer \stoplines
+%D
+%D The sign can be typeset as is or within the space of a digit.
+%D
+%D \startbuffer
+%D \setdigitsign 0 \digits {+12.345,90}
+%D \setdigitsign 1 \digits {+12.345,90}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This is typset as:
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D The digit modes are:
+%D
+%D \startitemize[n,packed]
+%D \item periods/comma
+%D \item commas/period
+%D \item thinmuskips/comma
+%D \item thinmuskips/period
+%D \item thickmuskips/comma
+%D \item thickmuskips/period
+%D \stopitemize
+%D
+%D The digit parser handles a bunch of special characters as well as different
+%D formats. We strongly suggest you to use the grouped call.
+%D
+%D \starttabulate[|l|l|l|]
+%D \NC \type{.} \NC , . \NC comma or period \NC \NR
+%D \NC \type{,} \NC , . \NC comma or period \NC \NR
+%D \NC \type{:} \NC \NC invisible period \NC \NR
+%D \NC \type{;} \NC \NC invisible comma \NC \NR
+%D \NC \type{_} \NC \NC invisible space \NC \NR
+%D \NC \type{/} \NC \NC invisible sign \NC \NR
+%D \NC \type{-} \NC $-$ \NC minus sign \NC \NR
+%D \NC \type{+} \NC $+$ \NC plus sign \NC \NR
+%D \NC \type{//} \NC \NC invisible high sign \NC \NR
+%D \NC \type{--} \NC $\negative$ \NC high minus sign \NC \NR
+%D \NC \type{++} \NC $\positive$ \NC high plus sign \NC \NR
+%D \NC \type{=} \NC $\zeroamount$ \NC zero padding \NC \NR
+%D \stoptabulate
+%D
+%D These triggers are used in the following examples.
+%D
+%D \starttabulate[|l|r|]
+%D \NC \type{1} \NC \ruledhbox{\strut\digits{1}} \NC \NR
+%D \NC \type{12} \NC \ruledhbox{\strut\digits{12}} \NC \NR
+%D \NC \type{12.34} \NC \ruledhbox{\strut\digits{12.34}} \NC \NR
+%D \NC \type{123,456} \NC \ruledhbox{\strut\digits{123,456}} \NC \NR
+%D \NC \type{123,456.78} \NC \ruledhbox{\strut\digits{123,456.78}} \NC \NR
+%D \NC \type{12,34} \NC \ruledhbox{\strut\digits{12,34}} \NC \NR
+%D \NC \type{.1234} \NC \ruledhbox{\strut\digits{.1234}} \NC \NR
+%D \NC \type{1234} \NC \ruledhbox{\strut\digits{1234}} \NC \NR
+%D \NC \type{123,456.78^9} \NC \ruledhbox{\strut\digits{123,456.78^9}} \NC \NR
+%D \NC \type{123,456.78e9} \NC \ruledhbox{\strut\digits{123,456.78e9}} \NC \NR
+%D \NC \type{/123,456.78e-9} \NC \ruledhbox{\strut\digits{/123,456.78e-9}} \NC \NR
+%D \NC \type{-123,456.78e-9} \NC \ruledhbox{\strut\digits{-123,456.78e-9}} \NC \NR
+%D \NC \type{+123,456.78e-9} \NC \ruledhbox{\strut\digits{+123,456.78e-9}} \NC \NR
+%D \NC \type{//123,456.78e-9} \NC \ruledhbox{\strut\digits{//123,456.78e-9}} \NC \NR
+%D \NC \type{--123,456.78e-9} \NC \ruledhbox{\strut\digits{--123,456.78e-9}} \NC \NR
+%D \NC \type{++123,456.78e-9} \NC \ruledhbox{\strut\digits{++123,456.78e-9}} \NC \NR
+%D \NC \type{___,___,123,456,789.00} \NC \ruledhbox{\strut\digits{___,___,123,456,789.00}} \NC \NR
+%D \NC \type{___,___,_12,345,678.==} \NC \ruledhbox{\strut\digits{___,___,_12,345,678.==}} \NC \NR
+%D \stoptabulate
+
+\newconstant\c_phys_digits_order
+\newconstant\c_phys_digits_method
+\newconstant\c_phys_digits_sign % we has sized (text script scriptscript)
+
+\permanent\protected\def\setdigitmethod#1{\c_phys_digits_method #1\relax}
+\permanent\protected\def\setdigitsign #1{\c_phys_digits_sign #1\relax}
+\permanent\protected\def\setdigitorder #1{\c_phys_digits_order #1\relax}
+
+\aliased\let\setdigitmode\setdigitmethod % compatibility
+
+\def\phys_digits_normalized % we could calculate once and remember
+ {\ifcase\c_phys_digits_sign
+ \expandafter\secondoftwoarguments
+ \orelse\ifmmode
+ \expandafter\phys_digits_normalized_math
+ \else
+ \expandafter\phys_digits_normalized_text
+ \fi}
+
+\def\phys_digits_normalized_math#1#2%
+ {\setbox\scratchbox\hbox{\normalstartimath\Ustack{#1}\normalstopimath}%
+ \hbox to \wd\scratchbox{\hss{\normalstartimath\Ustack{#2}\normalstopimath}\hss}}
+
+\def\phys_digits_normalized_text#1#2%
+ {\setbox\scratchbox\hbox{#1}%
+ \hbox to \wd\scratchbox{\hss#2\hss}}
+
+\def\phys_digits_raised
+ {\ifmmode
+ \expandafter\normalsuperscript
+ \else
+ \expandafter\unitshigh
+ \fi}
+
+% we could use a symbolset but how many symbols are there ?
+
+% \definesymbol[units][times][\times]
+% \definesymbol[units][times][\cdots]
+
+% \def\digitstimessymbol{\symbol[units][times]}
+
+% \definesymbol[units][times][\times]
+% \definesymbol[units][times][\cdots]
+% \definesymbol[units][times][\invisibletimes]
+% \definesymbol[units][times][\ifmmode\cdot\else\kern.2\emwidth\cdot\kern.2\emwidth\fi]
+
+\permanent\protected\def\digitstextbinop#1% assumes preceding
+ {\ifmmode#1\else\fourperemspace\nobreak#1\fourperemspace\fi}
+
+%frozen\def\digitstimessymbol{\ifmmode\cdot\else\digitstextbinop\cdot\fi}
+\frozen\def\digitstimessymbol{\digitstextbinop\times}
+
+\frozen\protected\def\digitszeropadding {\hphantom{0}}
+%frozen\protected\def\digitsnegative {\phys_digits_normalized{0}{\phys_digits_raised{\textminus}}}
+%frozen\protected\def\digitspositive {\phys_digits_normalized{0}{\phys_digits_raised{\textplus}}}
+\frozen\protected\def\digitsnegative {\phys_digits_normalized{0}{\mathematics{\negative}}}
+\frozen\protected\def\digitspositive {\phys_digits_normalized{0}{\mathematics{\positive}}}
+%frozen\protected\def\digitsminus {\phys_digits_normalized{0}{\mathematics{-}}}
+%frozen\protected\def\digitsplus {\phys_digits_normalized{0}{\mathematics{+}}}
+\frozen\protected\def\digitsminus {\phys_digits_normalized{0}{\mathminus}}
+\frozen\protected\def\digitsplus {\phys_digits_normalized{0}{\mathplus}}
+\frozen\protected\def\digitsplusminus {\phys_digits_normalized{0}{\mathplusminus}}
+\frozen\protected\def\digitsspace {\hphantom{0}}
+ \protected\def\digitsseparatorspace{\hphantom{.}}
+\frozen\protected\def\digitssignspace {\hphantom{\digitsminus}}
+\frozen\protected\def\digitshighspace {\hphantom{\digitspositive}}
+\frozen\protected\def\digitspower #1{\digitstimessymbol10\phys_digits_raised{#1}}
+\frozen\protected\def\digitspowerplus #1{\digitstimessymbol10\phys_digits_raised{\digitsplus#1}}
+\frozen\protected\def\digitspowerminus #1{\digitstimessymbol10\phys_digits_raised{\digitsminus#1}}
+\frozen\protected\def\digitsdigit #1{#1}
+
+\frozen\protected\def\normaldigitscommasymbol {,}
+\frozen\protected\def\normaldigitsperiodsymbol{.}
+
+\frozen\let\normaldigitsseparatorspace\digitsseparatorspace
+
+\installcorenamespace{digitscomma}
+\installcorenamespace{digitsperiod}
+\installcorenamespace{digitsspace}
+
+\letcsname\??digitscomma 0\endcsname\normaldigitscommasymbol
+\letcsname\??digitsperiod0\endcsname\normaldigitsperiodsymbol
+\letcsname\??digitsspace 0\endcsname\normaldigitsseparatorspace
+
+\letcsname\??digitscomma 1\endcsname\normaldigitsperiodsymbol
+\letcsname\??digitsperiod1\endcsname\normaldigitscommasymbol
+\letcsname\??digitsspace 1\endcsname\normaldigitsseparatorspace
+
+\letcsname\??digitscomma 2\endcsname\normaldigitscommasymbol
+\letcsname\??digitsperiod2\endcsname\normaldigitsperiodsymbol
+\letcsname\??digitsspace 2\endcsname\normaldigitsseparatorspace
+
+\letcsname\??digitscomma 3\endcsname\thinspace
+\letcsname\??digitsperiod3\endcsname\normaldigitscommasymbol
+\letcsname\??digitsspace 3\endcsname\thinspace
+
+\letcsname\??digitscomma 4\endcsname\thinspace
+\letcsname\??digitsperiod4\endcsname\normaldigitsperiodsymbol
+\letcsname\??digitsspace 4\endcsname\thinspace
+
+\letcsname\??digitscomma 5\endcsname\thickspace
+\letcsname\??digitsperiod5\endcsname\normaldigitscommasymbol
+\letcsname\??digitsspace 5\endcsname\thickspace
+
+\letcsname\??digitscomma 6\endcsname\thickspace
+\letcsname\??digitsperiod6\endcsname\normaldigitsperiodsymbol
+\letcsname\??digitsspace 6\endcsname\thickspace
+
+\frozen\protected\def\digitscommasymbol {\csname\??digitscomma \number\c_phys_digits_method\endcsname}
+\frozen\protected\def\digitsperiodsymbol {\csname\??digitsperiod\number\c_phys_digits_method\endcsname}
+\frozen\protected\def\digitsseparatorspace {\csname\??digitsspace \number\c_phys_digits_method\endcsname}
+
+\frozen\protected\def\digitsfinalcomma {\digitsperiodsymbol} % more for tracing
+\frozen\protected\def\digitsfinalperiod {\digitsperiodsymbol} % more for tracing
+\frozen\protected\def\digitsintermediatecomma {\digitscommasymbol } % more for tracing
+\frozen\protected\def\digitsintermediateperiod{\digitscommasymbol } % more for tracing
+
+%D The user macro:
+
+\protected\def\phys_digits_indeed#1%
+ {\dontleavehmode
+ \begingroup
+ \ifcase\c_phys_digits_order\expandafter\clf_digits_normal\else\expandafter\clf_digits_reverse\fi{\detokenize{#1}}%
+ \endgroup
+ \settrue\c_phys_units_dospace}
+
+\permanent\protected\def\digits
+ {\doifelsenextbgroup\phys_digits_argument\phys_digits_spaced}
+
+\def\phys_digits_argument#1%
+ {\phys_digits_indeed{#1}}
+
+\def\phys_digits_spaced#1 % space delimited
+ {\phys_digits_indeed{#1}}
+
+%D \macros
+%D {unit}
+%D
+%D We have been using the units module (and its predecessor) for over a decade now
+%D but when we moved on to \LUATEX\ a variant was prototyped that permits a less
+%D texie coding. I finally picked up that thread and cleaned up the code a bit so
+%D users can now play with it. (The main reason was that I wanted to test
+%D exporting.)
+%D
+%D \startbuffer
+%D 01: $10\unit{km/h}$
+%D 02: $\unit{10 km/h}$
+%D 03: \unit{km/h}
+%D 04: \unit{10 km/h}
+%D 05: \unit{10 km/h}
+%D 06: \unit{~1 km/h}
+%D 07: 10\unit{km/h}
+%D 08: 10 \unit{km/h}
+%D 09: $10 \unit{km/h}$
+%D 10: 10 \unit{KiloMeter/Hour}
+%D 11: 10 \unit{kilometer/hour}
+%D 12: 10 \unit{km/h}
+%D 13: 10 \unit{kilometer per hour}
+%D 14: 10 \unit{km / h}
+%D 15: 10 \unit{ km / h }
+%D 16: 10 \unit{km/ms2}
+%D 17: 10 \unit{meter per second}
+%D 18: 10 \unit{cubic meter}
+%D 19: 10 \unit{cubic meter per second}
+%D 21: 10 \unit{cubic meter / second}
+%D 22: $10 \unit{cubic meter / second}$
+%D 23: 30 \unit{kilo pascal }
+%D 24: 30 \unit{kilo pascal square meter / second}
+%D 25: 30 \unit{kilo pascal square meter / kelvin second}
+%D 26: \unit{30 kilo pascal square meter / kelvin second}
+%D 27: $30 \unit{kilo pascal square meter / kelvin second }$
+%D 28: 30 \unit{crap}
+%D 29: 30 \unit{AC}
+%D 30: $\frac{10 \unit{m/s}}{20 \unit{m/s}} $
+%D 31: {\ss 30 \unit{kilo pascal square meter / second kelvin}}
+%D 32: \unit{123.22^-3 km/s}
+%D 33: \unit{123.22e-3 km/s}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Result: \startlines \getbuffer \stoplines
+%D
+%D Depending on needs we can add more tweaks (also depends on to what extent we need
+%D to be compatible with \MKII.
+%D
+%D Formatting is supported too:
+%D
+%D \startbuffer
+%D \starttabulate[|l|l|l|]
+%D \HL
+%D \NC \unit{10 kilo gram} \NC \digits{10} \NC \unit{10} \NC \NR
+%D \NC \unit{1 kilogram} \NC \digits{1} \NC \unit{1} \NC \NR
+%D \NC \unit{0.1 kilogram} \NC \digits{0.1} \NC \unit{0.1} \NC \NR
+%D \NC \unit{1.1 kilogram} \NC \digits{1.1} \NC \unit{1.1} \NC \NR
+%D \NC \unit{11 kilogram} \NC \digits{11} \NC \unit{11} \NC \NR
+%D \HL
+%D \NC \unit{00,000.10 kilogram} \NC \digits{00,000.10} \NC \unit{00,000.10} \NC \NR
+%D \NC \unit{@@,@@0.10 kilogram} \NC \digits{@@,@@0.10} \NC \unit{@@,@@0.10} \NC \NR
+%D \NC \unit{__,___.10 kilogram} \NC \digits{__,___.10} \NC \unit{__,___.10} \NC \NR
+%D \NC \unit{__,__0:10 kilogram} \NC \digits{__,__0:10} \NC \unit{__,__0:10} \NC \NR
+%D \NC \unit{__,___:10 kilogram} \NC \digits{__,___:10} \NC \unit{__,___:10} \NC \NR
+%D \HL
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Punctuation can be configures usiing \type {method}:
+%D
+%D \startbuffer
+%D \starttabulate[|l|l|l|]
+%D \HL
+%D \NC \NC \setupunits[method=0]\unit{00,000.10 kilogram} \NC \setupunits[method=0]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \NC 1 \NC \setupunits[method=1]\unit{00,000.10 kilogram} \NC \setupunits[method=1]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \NC 2 \NC \setupunits[method=2]\unit{00,000.10 kilogram} \NC \setupunits[method=2]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \NC 3 \NC \setupunits[method=3]\unit{00,000.10 kilogram} \NC \setupunits[method=3]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \NC 4 \NC \setupunits[method=4]\unit{00,000.10 kilogram} \NC \setupunits[method=4]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \NC 5 \NC \setupunits[method=5]\unit{00,000.10 kilogram} \NC \setupunits[method=5]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \NC 6 \NC \setupunits[method=6]\unit{00,000.10 kilogram} \NC \setupunits[method=6]\unit{@@,@@0.10 kilogram} \NC \NR
+%D \HL
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+% only a space when a number is part of the unit
+
+\installcorenamespace {unit}
+\installcorenamespace {unitseparator}
+\installcorenamespace {unitspace}
+
+\installcommandhandler \??unit {unit} \??unit
+
+\setupunit
+ [\c!alternative=, % done: text
+ \c!separator=\v!normal, % done: cdot|big|medium|space
+ \s!language=\currentlanguage, % done: (no interface yet)
+ \c!order=\v!normal, % ,. (reverse: .,)
+ \c!method=0,
+ %\c!grid=\v!yes, % (maybe)
+ %\c!style=..., % done
+ %\c!color=..., % done
+ %\c!space=..., % (maybe) small medium big
+ ]
+
+\definehigh[unitshigh][\c!style=\txx]
+\definelow [unitslow] [\c!style=\txx]
+
+\aliased\let\setupunits\setupunit
+
+\newconstant \c_phys_units_mode % 0=text 1=math 2=textinmath 3=mathintext
+\newconstant \c_phys_units_state % 0=start 1=suffix 2=operator 3=unit 4=prefix 5=number
+\newconditional\c_phys_units_quantity
+\newconditional\c_phys_units_number
+\newconditional\c_phys_units_dospace
+
+% [\unit {micro ohm}]\par % no space before unit
+% [10\unit {micro ohm}]\par % no space before unit
+% [10 \unit{micro ohm}]\par % space before unit
+% [ \unit {micro ohm}]\par % space before unit
+% [\unit{10 micro ohm}]\par % space before unit
+
+\frozen\protected\def\unitssmallspace {\thinspace}
+\frozen\protected\def\unitsmediumspace{\medspace}
+\frozen\protected\def\unitsbigspace {\thickspace}
+\frozen\protected\def\unitsbackspace {\negthinspace}
+
+\permanent\protected\def\installunitsseparator#1#2%
+ {\defcsname\??unitseparator#1\endcsname{#2}}
+
+\protected\def\phys_units_separator
+ {\ifcsname\??unitseparator\unitparameter\c!separator\endcsname\lastnamedcs\else\cdot\fi}
+
+\installunitsseparator\v!normal {\cdot}
+\installunitsseparator\v!big {\unitsbigspace}
+\installunitsseparator\v!medium {\unitsmediumspace}
+\installunitsseparator\v!small {\unitssmallspace}
+\installunitsseparator\v!none {}
+
+\permanent\protected\def\installunitsspace#1#2%
+ {\defcsname\??unitspace#1\endcsname{#2}}
+
+\protected\def\phys_units_space
+ {\unskip % weird, why is unskip needed
+ \ifcsname\??unitspace\unitparameter\c!space\endcsname\lastnamedcs\else\unitsmediumspace\fi}
+
+\installunitsspace\v!normal{\unitsmediumspace}
+\installunitsspace\v!big {\unitsbigspace}
+\installunitsspace\v!medium{\unitsmediumspace}
+\installunitsspace\v!small {\unitssmallspace}
+\installunitsspace\v!none {}
+
+\newtoks \everyunits % we keep the old \units command so we need a longer one
+
+\appendtoks
+ \disablemathpunctuation
+ \nocharacteralign
+\to \everyunits
+
+\appendtoks
+ \frozen\instance\setuevalue\currentunit{\phys_units_direct{\currentunit}}
+\to \everydefineunit
+
+\protected\def\phys_units_direct#1%
+ {\begingroup
+ \the\everyunits
+ \ifdim\lastskip>\zeropoint
+ \settrue\c_phys_units_dospace
+ \removelastskip
+ \fi
+ \c_phys_digits_method\unitparameter\c!method\relax
+ \ifmmode\else\dontleavehmode\fi
+ \edef\currentunit{#1}%
+ \enforced\edef\unitlanguage{\unitparameter\s!language}%
+ \enforced\let\prefixlanguage\unitlanguage
+ \enforced\let\operatorlanguage\unitlanguage
+% \the\everyunits
+ %\removeunwantedspaces % not ok yet
+ \useunitstyleandcolor\c!style\c!color
+ \edef\scratchstringone{\unitparameter\c!alternative}%
+ \ifmmode
+ \ifx\scratchstringone\v!text
+ \expandafter\expandafter\expandafter\phys_units_direct_text_in_math
+ \else
+ \expandafter\expandafter\expandafter\phys_units_direct_math
+ \fi
+ \else
+ \ifx\scratchstringone\v!mathematics
+ \expandafter\expandafter\expandafter\phys_units_direct_math_in_text
+ \else
+ \expandafter\expandafter\expandafter\phys_units_direct_text
+ \fi
+ \fi}
+
+\protected\def\phys_units_direct_text_in_math#1%
+ {\mathtext{%
+ \c_phys_units_mode\plustwo
+ \phys_units_indeed{#1}%
+ \phys_units_finish
+ }%
+ \endgroup}
+
+\protected\def\phys_units_direct_math#1%
+ {\c_phys_units_mode\plusone
+ \rm\tf % slow
+ \mathtf
+ \phys_units_indeed{#1}%
+ \phys_units_finish
+ \endgroup}
+
+\protected\def\phys_units_direct_text#1%
+ {\phys_units_indeed{#1}%
+ \phys_units_finish
+ \endgroup}
+
+\protected\def\phys_units_direct_math_in_text#1%
+ {\removeunwantedspaces % brr
+ \startimath
+ \c_phys_units_mode\plusthree
+ \rm\tf
+ \mathtf
+ \phys_units_indeed{#1}%
+ \phys_units_finish
+ \stopimath
+ \endgroup}
+
+\protected\def\phys_units_direct_nested#1#2%
+ {\phys_units_indeed{#2}}
+
+\appendtoks
+ \let\phys_units_direct\phys_units_direct_nested
+\to \everyunits
+
+\protected\def\phys_units_indeed#1%
+ {\edef\p_order{\unitparameter\c!order}%
+ \ifx\p_order\v!reverse\expandafter\clf_unit_reverse\else\expandafter\clf_unit_normal\fi{\detokenize{#1}}}
+
+\permanent\protected\def\digitstextbinnop#1%
+ {\ifmmode#1\else#1\fourperemspace\fi}
+
+\permanent\protected\def\unitsPUS#1#2#3{\phys_units_next\prefixtext{#1}\unittext{#2}\unitsraise{\suffixtext{#3}}\c_phys_units_state\plusone} % suffix
+\permanent\protected\def\unitsPU #1#2{\phys_units_next\prefixtext{#1}\unittext{#2}\c_phys_units_state\plusthree} % unit
+\permanent\protected\def\unitsPS #1#2{\phys_units_next\prefixtext{#1}\unitsraise{\suffixtext{#2}}\c_phys_units_state\plusone} % suffix
+\permanent\protected\def\unitsUS #1#2{\phys_units_next\unittext{#1}\unitsraise{\suffixtext{#2}}\c_phys_units_state\plusone} % suffix
+\permanent\protected\def\unitsP #1{\phys_units_next\prefixtext{#1}1\c_phys_units_state\plusfour} % prefix
+\permanent\protected\def\unitsU #1{\phys_units_next\unittext{#1}\c_phys_units_state\plusthree} % unit
+\permanent\protected\def\unitsS #1{\phys_units_start{}\unitsraise{\suffixtext{#1}}\c_phys_units_state\plusone} % suffix
+\permanent\protected\def\unitsO #1{\phys_units_start\operatortext{#1}\c_phys_units_state\plustwo} % operator
+%permanent\protected\def\unitsN #1{\phys_units_start#1\c_phys_units_state\plusfive} % number
+\permanent\protected\def\unitsC #1{\removeunwantedspaces\unittext{#1}\c_phys_units_state\plussix} % connected
+\permanent\protected\def\unitsQ #1{\removeunwantedspaces\unitslower{#1}\c_phys_units_state\zerocount}
+\permanent\protected\def\unitsR #1#2{% todo: tagging
+ \ifmmode
+ #2%
+ \orelse\ifnum#1=\plusone
+ \digitstextbinop{#2}% before and after
+ \else
+ \digitstextbinnop{#2}% after
+ \fi
+ \c_phys_units_state\zerocount
+ \setfalse\c_phys_units_dospace
+ \setfalse\c_phys_units_number
+ \setfalse\c_phys_units_quantity}
+\permanent\protected\def\unitsRPM {\unitsR\plusone {±}} % todo: symbols
+\permanent\protected\def\unitsRTO {\unitsR\plusone {–}} % todo: symbols
+\permanent\protected\def\unitsRabout {\unitsR\zerocount{±}} % todo: symbols
+\permanent\protected\def\unitsPopen {(}
+\permanent\protected\def\unitsPclose {)}
+\permanent\protected\def\unitrange #1{}
+
+% Fonts can have a celsius and lack a fahrenheit symbol and as we want to be
+% consistent so we check for the counterparts as well. It's slow but ok. Of course
+% we could go virtual instead.
+
+\permanent\protected\def\phys_units_text_prime {\textacute}
+\permanent\protected\def\phys_units_text_doubleprime{\textacute\kern-.25em\textacute}
+\permanent\protected\def\phys_units_text_celsius {°C}
+\permanent\protected\def\phys_units_text_fahrenheit {°F}
+
+\permanent\protected\def\checkedtextprime
+ {\iffontchar\font"2032\relax\iffontchar\font"2033\relax
+ ′\else\phys_units_text_prime\fi\else\phys_units_text_prime
+ \fi}
+
+\permanent\protected\def\checkedtextdoubleprime
+ {\iffontchar\font"2033\relax\iffontchar\font"2032\relax
+ ″\else\phys_units_text_doubleprime\fi\else\phys_units_text_doubleprime
+ \fi}
+
+% \permanent\protected\def\checkedtextcelsius
+% {\ifmmode
+% \phys_units_text_celsius
+% \orelse\iffontchar\font"2103\relax
+% ℃\else\phys_units_text_celsius
+% \fi}
+%
+% \permanent\protected\def\checkedtextfahrenheit
+% {\ifmmode
+% \phys_units_text_fahrenheit
+% \orelse\iffontchar\font"2109\relax
+% ℉\else\phys_units_text_fahrenheit
+% \fi}
+%
+% % but, as users don't like this ...
+
+\aliased\let\checkedtextcelsius \phys_units_text_celsius
+\aliased\let\checkedtextfahrenheit\phys_units_text_fahrenheit
+
+\setelementnature[unit] [mixed]
+\setelementnature[quantity][mixed]
+
+\let\phys_units_finish\relax
+
+\permanent\protected\def\unitsNstartindeed
+ {\ifmmode \else
+ \settrue\c_phys_units_quantity
+ \dostarttagged\t!quantity\empty
+ \settrue\c_phys_units_number
+ \dostarttagged\t!number\empty
+ \fi}
+
+\permanent\protected\def\unitsNstop
+ {\ifconditional\c_phys_units_number
+ \setfalse\c_phys_units_number
+ \dostoptagged
+ \fi
+ \c_phys_units_state\plusfive}
+
+% This is a hack: for some reason \unit {micro meter} like patterns give
+% \unitsNstart \unitsNstop so there is a buglet in the parser
+
+% \aliased\let\unitsNstartindeed\unitsNstart
+
+\permanent\protected\def\unitsNstart
+ {\doifelsenextchar\unitsNstop\gobbleoneargument\unitsNstartindeed}
+
+% End of hack.
+
+\permanent\protected\def\unitsNspace
+ {\space}
+
+\permanent\protected\def\unitsN#1%
+ {\unitsNstart#1\unitsNstop}
+
+\def\phys_units_start
+ {\ifmmode
+ \dostarttagged\t!maction\t!unit
+ \bgroup % make an mrow
+ \else
+ \dostarttagged\t!unit\empty
+ \fi
+ \let\phys_units_finish\phys_units_stop
+ \let\phys_units_start\relax}
+
+\def\phys_units_stop
+ {\ifconditional\c_phys_units_number
+ \setfalse\c_phys_units_number
+ \dostoptagged
+ \fi
+ \ifconditional\c_phys_units_quantity
+ \setfalse\c_phys_units_quantity
+ \dostoptagged
+ \fi
+ \dostoptagged
+ \ifmmode
+ \egroup
+ \fi}
+
+\permanent\def\unitsraise
+ {\ifcase\c_phys_units_mode
+ \expandafter\unitshigh
+ \or
+ \expandafter\normalsuperscript
+ \or
+ \expandafter\unitshigh
+ \or
+ \expandafter\normalsuperscript
+ \fi}
+
+\permanent\def\unitslower
+ {\ifcase\c_phys_units_mode
+ \expandafter\unitslow
+ \or
+ \expandafter\normalsubscript
+ \or
+ \expandafter\unitslow
+ \or
+ \expandafter\normalsubscript
+ \fi}
+
+\protected\def\phys_units_next
+ {\ifcase\c_phys_units_state % start
+ \ifconditional\c_phys_units_dospace
+ % \ifdim\lastskip=\zeropoint
+ \phys_units_space
+ % \else
+ % % too tricky ... we could remove and add
+ % \fi
+ \fi
+ \or % 1: suffix
+ {\phys_units_separator}%
+ \or % 2: operator
+ \or % 3: unit
+ {\phys_units_separator}%
+ \or % 4: prefix
+ \or % 5: number
+ \phys_units_space
+ \or % 6: symbol (connected)
+ \fi
+ \setfalse\c_phys_units_dospace
+ \phys_units_start}
+
+\permanent\protected\def\unitsTIMES
+ {\ifnum\c_phys_units_state=\plusone % suffix
+ \else
+ \unitssmallspace
+ \fi
+ \cdot} % or \times
+
+\permanent\protected\def\unitsOUTOF
+ {\ifnum\c_phys_units_state=\plusone % suffix
+ \else
+ \unitssmallspace
+ \fi
+ :}
+
+\permanent\protected\def\unitsSOLIDUS
+ {\ifnum\c_phys_units_state=\plusone % suffix
+ \unitsbackspace
+ \fi
+ {/}%
+ }%\unitsbackspace}
+
+\definelabelclass [unit] [2]
+\definelabelclass [operator] [2]
+\definelabelclass [prefix] [2]
+\definelabelclass [suffix] [2] % This is only a label because we want to show them in a table.
+
+\clf_definelabels{prefix}{prefixes}\s!false\relax
+\clf_definelabels{unit}{units}\s!false\relax
+\clf_definelabels{operator}{operators}\s!false\relax
+\clf_definelabels{suffix}{suffixes}\s!false\relax
+
+%D You can define additional units:
+%D
+%D \starttyping
+%D \registerunit
+%D [unit]
+%D [point=point,
+%D basepoint=basepoint,
+%D scaledpoint=scaledpoint,
+%D didot=didot,
+%D cicero=cicero]
+%D \stoptyping
+%D
+%D Possible categories are: \type {prefix}, \type {unit}, \type {operator}, \type
+%D {suffix}, \type {symbol},\type {packaged}. You also need to define labels:
+%D
+%D \starttyping
+%D \setupunittext
+%D [point=pt,
+%D basepoint=bp,
+%D scaledpoint=sp,
+%D didot=dd,
+%D cicero=cc]
+%D \stoptyping
+
+\permanent\tolerant\protected\def\registerunit[#1]#*[#2]% todo: public implementer
+ {\clf_registerunit{#1}{#2}}
+
+%D You can generate a list as follows:
+%D
+%D \starttyping
+%D \usemodule[phy-01]
+%D
+%D \ShowUnitsTable % [prefixes]
+%D \stoptyping
+
+%D Now we define the standard units command:
+
+\defineunit
+ [unit]
+
+%D Example:
+%D
+%D \startbuffer[definitions]
+%D \startluacode
+%D languages.data.labels.prefixes.whatever = {
+%D Kilo = "olik"
+%D }
+%D
+%D languages.data.labels.units.whatever = {
+%D Meter = "retem",
+%D Second = "dnoces",
+%D }
+%D
+%D languages.data.labels.operators.whatever = {
+%D Solidus = " rep "
+%D }
+%D \stopluacode
+%D \stopbuffer
+%D
+%D \startbuffer[sample]
+%D \startlines
+%D \lunit{10 km/s}
+%D \lunit{10 Kilo Meter/s}
+%D \lunit{10 kilo Meter/s}
+%D \lunit{10 Kilo m/s}
+%D \lunit{10 k Meter/s}
+%D \stoplines
+%D \stopbuffer
+%D
+%D \typebuffer[definitions] \getbuffer[definitions]
+%D
+%D \startbuffer
+%D \typebuffer[sample]
+%D
+%D \defineunits[lunit] \getbuffer[sample]
+%D \defineunits[lunit][label=test] \getbuffer[sample]
+%D \defineunits[lunit][label=whatever] \getbuffer[sample]
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Another example:
+%D
+%D \starttyping
+%D \startluacode
+%D languages.data.labels.units.foo = {
+%D Liter = "l"
+%D }
+%D languages.data.labels.units.bar = {
+%D Liter = "L"
+%D }
+%D \stopluacode
+%D
+%D \defineunits[lunit] \lunit{10 l/s}\par
+%D \defineunits[funit][label=foo] \funit{10 l/s}\par
+%D \defineunits[bunit][label=bar] \bunit{10 l/s}\par
+%D \stoptyping
+
+%D Bonus:
+%D
+%D \starttyping
+%D \pi\ = \hyphenateddigits[\unknown]{3.141592653589793238462643383279502884197169399375105} \blank
+%D \pi\ = \hyphenateddigits{3.141592653589793238462643383279502884197169399375105} \blank
+%D x $\pi = \hyphenateddigits[\unknown]{3.141592653589793238462643383279502884197169399375105}$ \blank
+%D x $\pi = \hyphenateddigits{3.141592653589793238462643383279502884197169399375105}$ \blank
+%D \stoptyping
+
+\mutable\protected\def\digitsbreak{\hskip\zeropoint\s!plus\onepoint\relax\ifmmode\allowbreak\fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/publ-inc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/publ-inc.mkxl
new file mode 100644
index 00000000000..9beae595fa2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/publ-inc.mkxl
@@ -0,0 +1,63 @@
+%D \module
+%D [ file=publ-inc,
+%D version=2018.06.23,
+%D title=\CONTEXT\ Publication Macros,
+%D subtitle=XML inclusion,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Publication Macros / XML inclusion}
+
+\registerctxluafile{publ-inc}{}
+
+%D A teaser for Alan.
+
+\unprotect
+
+\definesymbol[btxattachment][{\infofont\darkred btx}]
+\definesymbol[btxcomment] [{\infofont\darkblue btx}]
+
+\protected\def\btx_add_blob#1#2%
+ {\relax
+ \clf_btxentrytobuffer{\currentbtxdataset}{\currentbtxtag}{temp-btx-export}%
+ #2%
+ [\c!symbol=#1,%
+ \c!space=\v!yes,
+ \c!buffer=temp-btx-export,%
+ \c!name={\currentbtxtag.bib}]%
+ \relax}
+
+\permanent\protected\def\btxattach
+ {\iftrialtypesetting\orelse\ifexporting\iflocation
+ \dostarttagged\t!ignore\empty
+ \btx_add_blob{btxattachment}\attachment
+ \dostoptagged
+ \fi\fi}
+
+\permanent\protected\def\btxcomment
+ {\iftrialtypesetting\orelse\ifexporting\iflocation
+ \dostarttagged\t!ignore\empty
+ \btx_add_blob{btxcomment}\comment
+ \dostoptagged
+ \fi\fi}
+
+%D This kind of feature creep is not yet configurable, nor documented.
+
+\permanent\protected\def\btxaddsource
+ {\iftrialtypesetting\orelse\ifexporting\iflocation
+ \dostarttagged\t!ignore\empty
+ \llap{%
+ \btx_add_blob{btxattachment}\attachment
+ \quad
+ \btx_add_blob{btxcomment}\comment
+ \hskip\leftmargindistance
+ }%
+ \dostoptagged
+ \fi\fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/publ-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/publ-ini.mkxl
new file mode 100644
index 00000000000..2f4d817f24b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/publ-ini.mkxl
@@ -0,0 +1,2038 @@
+%D \module
+%D [ file=publ-ini,
+%D version=2013.05.12,
+%D title=\CONTEXT\ Publication Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% TODO: some clf_ can be public implementers instead
+
+% TODO: s! vs v! for default and neutral key/values
+% todo: too many refs in list
+
+% TODO A.-B. Foo (dash as connector, see JMH)
+
+% todo: no need for all these %'s
+
+% todo: tagging
+% todo: we cannot use 'default' as this wipes metadata names (maybe no longer do that)
+% todo: \v!cite => \s!cite
+% todo: interface with (ml)bibtex (export -> call -> import)
+% todo: check if 'all' etc are ok ... either use list or use other criterium
+% todo: \the\everysetupbtxciteplacement probably too often
+
+% \definecolor[btx:field] [darkred]
+% \definecolor[btx:crossref][darkblue]
+% \definecolor[btx:key] [darkgreen]
+% \definecolor[btx:todo] [darkyellow]
+
+%D We operate on several axis:
+%D
+%D \startitemize[packed]
+%D \startitem we can have several databases (or combinations) \stopitem
+%D \startitem we can add entries to them if needed (coded in tex) \stopitem
+%D \startitem we can have several lists each using one of the databases \stopitem
+%D \startitem we can render each list or citation independently \stopitem
+%D \stopitemize
+%D
+%D We assume that the rendering of a list entry is consistent in a document,
+%D although one can redefine properties if needed. Adding more granularity would
+%D complicate the user interface beyond comprehension.
+
+\writestatus{loading}{ConTeXt Publication Support / Initialization}
+
+\registerctxluafile{publ-dat}{}
+\registerctxluafile{publ-ini}{}
+\registerctxluafile{publ-sor}{}
+\registerctxluafile{publ-aut}{}
+\registerctxluafile{publ-usr}{}
+\registerctxluafile{publ-oth}{} % this could become an option
+\registerctxluafile{publ-fnd}{} % new method (for the moment only local)
+\registerctxluafile{publ-jrn}{}
+\registerctxluafile{publ-reg}{}
+\registerctxluafile{publ-tra}{} % has more general helpers too
+
+\unprotect
+
+\startcontextdefinitioncode
+
+%D These are currently not interfaced.
+
+\definesystemconstant {btx}
+
+\definesystemconstant {btxset}
+\definesystemconstant {btxref}
+\definesystemconstant {btxint}
+\definesystemconstant {btxltx}
+\definesystemconstant {btxrtx}
+\definesystemconstant {btxatx}
+\definesystemconstant {btxbtx}
+\definesystemconstant {btxspc}
+\definesystemconstant {btxlst}
+\definesystemconstant {btxcom}
+
+\defineinterfacevariable {btxcite} {btxcite}
+\defineinterfacevariable {btxlist} {btxlist}
+\defineinterfacevariable {btxrendering} {btxrendering}
+
+\definelabelclass[btxlabel][2]
+
+\clf_definelabels{btxlabel}{btx}\s!false\relax
+
+% It is not that trivial to come up with a proper organization of setup
+% and control commands for publications. This is because we have complex
+% inline as well as extensive list rendering. The rules are partially
+% driven by somewhat archaic bibtex specifications and evolving journal
+% (or field) specific demands. The logic in the standards is often so
+% complex that it looks like manual rendering is assumed. But, we want to
+% automate the process as much as possible.
+%
+% Another complication is that in manuals we want to demonstrate different
+% properties of the implementation and therefore we need a way to handle
+% independent standards, databases, etc. This has resulted in the following
+% organization:
+%
+% - general setup (rather minimal)
+% - list setup (rendering)
+% - cite setup
+% - dataset setup
+%
+% The rendering is mostly driven by setups. In there we can call for fields
+% in the database but also for virtual fields or combinations.
+
+% The main rendering style (standard driven).
+
+%D We assume that a specification is global or used grouped. It doesn't make much sense
+%D to split between cite and list here as it only complicates matters (timing) and is
+%D not clear either.
+
+\mutable\let\currentbtxspecification \empty
+\mutable\let\currentbtxspecificationfallback\empty
+
+\installmacrostack\currentbtxspecification
+\installmacrostack\currentbtxspecificationfallback
+
+\permanent\protected\def\startbtxrenderingdefinitions[#1]%
+ {\unprotect
+ \push_macro_currentbtxspecification
+ \edef\currentbtxspecification{#1}}
+
+\permanent\protected\def\stopbtxrenderingdefinitions
+ {\pop_macro_currentbtxspecification
+ \protect}
+
+\permanent\protected\def\loadfoundpublicationfile#1#2% name foundname / not a user command
+ {\input{#2}}
+
+\permanent\protected\def\loadbtxdefinitionfile [#1]{\clf_btxloaddefinitionfile {#1}} % can be public implementer
+\permanent\protected\def\loadbtxreplacementfile[#1]{\clf_btxloadreplacementfile{#1}} % can be public implementer
+
+\protected\def\publ_specification_push#1%
+ {\push_macro_currentbtxspecification
+ \push_macro_currentbtxspecificationfallback
+ \edef\currentbtxspecification{#1}%
+ \edef\currentbtxspecificationfallback{\namedbtxparameter\currentbtxspecification\c!default}%
+ \ifx\currentbtxspecificationfallback\currentbtxspecification
+ \let\currentbtxspecificationfallback\empty
+ \fi
+ \clf_btxsetspecification{\currentbtxspecification}}
+
+\protected\def\publ_specification_pop
+ {\pop_macro_currentbtxspecificationfallback
+ \pop_macro_currentbtxspecification
+ \clf_btxsetspecification{\currentbtxspecification}}
+
+\protected\def\publ_specification_set#1% beware: is global
+ {\edef\currentbtxspecification{#1}%
+ \edef\currentbtxspecificationfallback{\namedbtxparameter\currentbtxspecification\c!default}%
+ \ifx\currentbtxspecificationfallback\currentbtxspecification
+ \let\currentbtxspecificationfallback\empty
+ \fi
+ % has to be done explicitly: \loadbtxdefinitionfile[\currentbtxspecification]%
+ \ifempty\currentbtxspecification
+ % we set default at the end
+ \else
+ \clf_btxsetspecification{\currentbtxspecification}%
+ \fi}% todo: ,true == also load
+
+\installcorenamespace {btx}
+
+\installswitchcommandhandler \??btx {btx} \??btx
+
+% because we have lots of setups we provide a checker for sloppy users
+
+\protected\def\btx_check_chain#1#2#3%
+ {\doifelsesomething{#3}
+ {\writestatus{btx #1}{defining\space"#2"\space as\space descendant\space of\space"#3"}% we're in definition regime (no space)
+ \definebtx[#2][#3]}
+ {\writestatus{btx #1}{defining\space"#2"}%
+ \definebtx[#2]}}
+
+% \protected\def\btxcheckdefine#1#2{\doifelsecommandhandler\??btx{#1}\donothing{\btx_check_chain{define}{#1}{#2}}}
+% \protected\def\btxchecksetup #1#2{\doifelsecommandhandler\??btx{#1}\donothing{\btx_check_chain {setup}{#1}{#2}}}
+
+\permanent\protected\def\btxcheckdefine#1{\doifelsecommandhandler\??btx{#1}\gobbleoneargument{\btx_check_chain{define}{#1}}} % {#2}
+\permanent\protected\def\btxchecksetup #1{\doifelsecommandhandler\??btx{#1}\gobbleoneargument{\btx_check_chain {setup}{#1}}} % {#2}
+
+% for the moment experimental:
+
+\permanent\protected\def\btxenableautodefine
+ {\prependtoks
+ \clf_checkinterfacechain{\currentbtx}{btxcheckdefine}%
+ \to \everydefinebtx
+ \prependtoks
+ \ifnum\btxsetupmode=\doingrootsetupnamed
+ \clf_checkinterfacechain{\currentbtx}{btxchecksetup}%
+ \fi
+ \to \everysetupbtx
+ \let\btxenableautodefine\relax}
+
+\appendtoks
+ \ifnum\btxsetupmode=\doingrootsetuproot
+ \publ_specification_set{\btxparameter\c!specification}%
+ \orelse\ifnum\btxsetupmode=\doingrootsetupnamed
+ \doifelsecommandhandler\??btx\currentbtx
+ {\publ_specification_set{\btxparameter\c!specification}}%
+ {}% maybe a warning
+ \fi
+\to \everysetupbtx
+
+\appendtoks
+ \ifnum\btxsetupmode=\doingrootsetuproot
+ \edef\currentbtxdataset{\clf_btxsetdataset{\btxparameter\c!dataset}{\currentbtxdataset}}%
+ \fi
+\to \everysetupbtx
+
+\appendtoks
+ \publ_specification_set{\btxparameter\c!specification}%
+\to \everyjob
+
+\permanent\protected\def\startusingbtxspecification[#1]%
+ {\publ_specification_push{#1}}
+
+\permanent\protected\def\stopusingbtxspecification
+ {\publ_specification_pop}
+
+% \setupbtxlist[alternative=paragraph,width=auto,distance=\emwidth]
+% \setupbtxlist[alternative=paragraph,width=auto,distance=\emwidth,margin=2em] % useless
+% \setupbtxlist[alternative=paragraph,width=fit,distance=\emwidth]
+% \setupbtxlist[alternative=paragraph,width=fit,distance=\emwidth,margin=2em]
+
+% here starts the bib stuff
+
+\installcorenamespace {btxdataset}
+\installcorenamespace {btxrendering}
+\installcorenamespace {btxregister}
+\installcorenamespace {btxcommand}
+\installcorenamespace {btxrenderingdefinition}
+
+\installcommandhandler \??btxdataset {btxdataset} \??btxdataset
+\installcommandhandler \??btxregister {btxregister} \??btxregister
+\installcommandhandler \??btxrendering {btxrendering} \??btxrendering
+
+\permanent\protected\def\setbtxparameterset#1#2%
+ {\edef\currentbtx
+ {\ifcsname\??btx\currentbtxspecification:#1:#2:\s!parent\endcsname
+ \currentbtxspecification:%
+ \orelse\ifempty\currentbtxspecificationfallback
+ \orelse\ifcsname\??btx\currentbtxspecificationfallback:#1:#2:\s!parent\endcsname
+ \currentbtxspecificationfallback:%
+ \fi#1:#2}}
+
+\permanent\protected\def\setbtxparametersetroot#1%
+ {\edef\currentbtx
+ {\ifcsname\??btx\currentbtxspecification:#1:\s!parent\endcsname
+ \currentbtxspecification:#1%
+ \orelse\ifempty\currentbtxspecificationfallback
+ \orelse\ifcsname\??btx\currentbtxspecificationfallback:#1:\s!parent\endcsname
+ \currentbtxspecificationfallback:#1%
+ \fi}}
+
+\permanent\protected\def\setbtxrendering
+ {\edef\currentbtxrendering
+ {\ifcsname\??btx\currentbtxspecification:\s!parent\endcsname
+ \currentbtxspecification
+ \orelse\ifempty\currentbtxspecificationfallback
+ \orelse\ifcsname\??btx\currentbtxspecificationfallback:\s!parent\endcsname
+ \currentbtxspecificationfallback
+ \fi}}
+
+\permanent\protected\def\setbtxlist % maybe simplify this one, always list=rendering?
+ {\edef\currentbtxlist
+ {\ifcsname\??btx\currentbtxrendering:\s!parent\endcsname
+ \currentbtxrendering
+ \orelse\ifcsname\??btx\currentbtxspecification:\s!parent\endcsname
+ \currentbtxspecification
+ \orelse\ifempty\currentbtxspecificationfallback
+ \orelse\ifcsname\??btx\currentbtxspecificationfallback:\s!parent\endcsname
+ \currentbtxspecificationfallback
+ \fi}%
+ \edef\currentlist{\s!btx:\currentbtxlist}}
+
+\tolerant\permanent\protected\def\usebtxdataset[#1]#*[#2]#*[#3]%
+ {\begingroup
+ \getdummyparameters[\c!specification=\currentbtxspecification,#3]%
+ \ifparameter#2\or
+ \clf_btxusedataset
+ specification {\dummyparameter\c!specification}%
+ dataset {#1}%
+ filename {#2}%
+ \relax
+ \orelse\ifparameter#1\or
+ \clf_btxusedataset
+ specification {\dummyparameter\c!specification}%
+ dataset {\v!default}%
+ filename {#1}%
+ \relax
+ \fi
+ \endgroup}
+
+\definebtxdataset
+ [\v!default]
+% [\c!language=] % nothing set so use current
+
+% \usebtxdataset
+% [default]
+% [mybibs.bib]
+
+%D These can be overloaded in the traditional module so we go \type {\frozen}
+%D instead of \type {\permanent}.
+
+\frozen\let\stoppublication\relax
+
+\frozen\tolerant\protected\def\startpublication[#1]#*[#2]%
+ {\begingroup
+ \catcode\commentasciicode\othercatcode
+ \ifparameters
+ \expandafter\publ_set_publication_default
+ \or
+ \expandafter\publ_set_publication_checked
+ \or
+ \expandafter\publ_set_publication_indeed
+ \fi{#1}{#2}}
+
+\def\publ_set_publication_default#1#2%
+ {\publ_set_publication_indeed\v!default{#1}}
+
+\def\publ_set_publication_checked#1#2%
+ {\doifelseassignment{#1}
+ {\publ_set_publication_indeed\v!default{#1}}
+ {\publ_set_publication_indeed{#1}{}}}
+
+\def\publ_set_publication_indeed#1#2#3\stoppublication
+ {\clf_btxaddentry{#1}{#2}{\detokenize{#3}}%
+ \endgroup
+ \ignorespaces}
+
+% commands
+
+\permanent\protected\def\btxcommand#1%
+ {\ifcsname\??btxcommand#1\endcsname
+ \expandafter\publ_command_yes
+ \else
+ \expandafter\publ_command_nop
+ \fi{#1}}
+
+\newtoks\t_btx_cmd
+\newbox \b_btx_cmd
+
+\t_btx_cmd{\global\setbox\b_btx_cmd\hpack{\clf_btxcmdstring}}
+
+\aliased\let\btxcmd\btxcommand
+
+\def\publ_command_yes#1%
+ {\csname\??btxcommand#1\endcsname}
+
+\def\publ_command_nop#1%
+ {\ifcsname#1\endcsname
+ \showmessage\m!publications{10}{#1,#1}%
+ \gletcsname\??btxcommand#1\expandafter\endcsname\csname#1\endcsname
+ \orelse\ifcsname\utfupper{#1}\endcsname
+ \showmessage\m!publications{10}{#1}{\utfupper{#1}}%
+ \gletcsname\??btxcommand#1\expandafter\endcsname\csname\utfupper{#1}\endcsname
+ \else
+ \showmessage\m!publications{11}{#1}%
+ \setugvalue{\??btxcommand#1}{\underbar{\tttf#1}}%
+ \fi
+ \publ_command_yes{#1}}
+
+\permanent\protected\def\definebtxcommand#1% {body} #1..#n{body}
+ {\setuvalue{\??btxcommand\csstring#1}}%
+
+% access
+
+\let\currentbtxtag \empty
+\let\currentbtxdataset\v!default
+
+\permanent\protected\def\setbtxentry[#1]% or maybe btxsetentry
+ {\edef\currentbtxtag{\clf_btxsetentry{\currentbtxdataset}{#1}}}
+
+% \let\btxsetdataset\setbtxdataset
+% \let\btxsetentry \setbtxentry
+
+% todo: no need for the currents as we can keep them at the lua end so we will have
+%
+% \btxfield : current
+% \btxspecificfield : dataset,tag,key
+
+% todo we can pick up the current's at the lua end (implementers)
+
+%permanent\def\btxfield #1{\clf_btxfield {\currentbtxdataset}{\currentbtxtag}{#1}}
+%permanent\def\btxdetail #1{\clf_btxdetail {\currentbtxdataset}{\currentbtxtag}{#1}}
+%permanent\def\btxflush #1{\clf_btxflush {\currentbtxdataset}{\currentbtxtag}{#1}}
+%permanent\def\btxdirect #1{\clf_btxdirect {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxfieldname #1{\clf_btxfieldname {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxfieldtype #1{\clf_btxfieldtype {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxfoundname #1{\clf_btxfoundname {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxfoundtype #1{\clf_btxfoundtype {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxauthorfield#1{\clf_btxauthorfield \currentbtxauthorindex{#1}}
+\permanent\def\btxdoifelse #1{\clf_btxdoifelse {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxdoif #1{\clf_btxdoif {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\def\btxdoifnot #1{\clf_btxdoifnot {\currentbtxdataset}{\currentbtxtag}{#1}}
+
+\aliased\let\btxsetup\fastsetup
+
+\permanent\def\btxrawfield #1{\clf_btxfield {\currentbtxdataset}{\currentbtxtag}{#1}}
+
+\permanent\def\btxfield #1{\dostarttagged\t!pubfld{#1}\clf_btxfield {\currentbtxdataset}{\currentbtxtag}{#1}\dostoptagged}
+\permanent\def\btxdetail #1{\dostarttagged\t!pubfld{#1}\clf_btxdetail{\currentbtxdataset}{\currentbtxtag}{#1}\dostoptagged}
+\permanent\def\btxflush #1{\dostarttagged\t!pubfld{#1}\clf_btxflush {\currentbtxdataset}{\currentbtxtag}{#1}\dostoptagged}
+\permanent\def\btxdirect #1{\dostarttagged\t!pubfld{#1}\clf_btxdirect{\currentbtxdataset}{\currentbtxtag}{#1}\dostoptagged}
+%permanent\def\btxauthorfield#1{\dostarttagged\t!pubfld{#1}\clf_btxauthorfield \currentbtxauthorindex{#1}\dostoptagged}
+
+% Also possible but inefficient:
+%
+% \permanent\def\btxfield #1{\localcontrolled{\dostarttagged\t!pubfld{#1}}\clf_btxfield {\currentbtxdataset}{\currentbtxtag}{#1}\localcontrolled{\dostoptagged}}
+% \permanent\def\btxdetail #1{\localcontrolled{\dostarttagged\t!pubfld{#1}}\clf_btxdetail{\currentbtxdataset}{\currentbtxtag}{#1}\localcontrolled{\dostoptagged}}
+% \permanent\def\btxflush #1{\localcontrolled{\dostarttagged\t!pubfld{#1}}\clf_btxflush {\currentbtxdataset}{\currentbtxtag}{#1}\localcontrolled{\dostoptagged}}
+% \permanent\def\btxdirect #1{\localcontrolled{\dostarttagged\t!pubfld{#1}}\clf_btxdirect{\currentbtxdataset}{\currentbtxtag}{#1}\localcontrolled{\dostoptagged}}
+% %permanent\def\btxauthorfield#1{\localcontrolled{\dostarttagged\t!pubfld{#1}}\clf_btxauthorfield \currentbtxauthorindex{#1}\localcontrolled{\dostoptagged}}
+
+%D How complex will we go? Can we assume that e.g. an apa style will not be mixed
+%D with another one? I think this assumption is okay. For manuals we might want to
+%D mix but we can work around it.
+
+%D Rendering.
+
+\permanent\protected\def\btxspace {\removeunwantedspaces\space}
+\permanent\protected\def\btxnobreakspace {\removeunwantedspaces\nobreakspace} % these two are
+\permanent\protected\def\btxnbsp {\removeunwantedspaces\nbsp} % the same anyway
+\permanent\protected\def\btxperiod {\removeunwantedspaces.\space}
+\permanent\protected\def\btxcomma {\removeunwantedspaces,\space}
+\permanent\protected\def\btxcommabreak {\removeunwantedspaces,\hskip\zeropoint plus .5\emwidth\relax}
+\permanent\protected\def\btxcolon {\removeunwantedspaces:\space}
+\permanent\protected\def\btxsemicolon {\removeunwantedspaces;\space}
+\permanent\protected\def\btxlparent {\removeunwantedspaces\space(} % obsolete
+\permanent\protected\def\btxrparent {\removeunwantedspaces)\space} % obsolete
+\permanent\protected\def\btxleftparenthesis {\removeunwantedspaces\space(}
+\permanent\protected\def\btxrightparenthesis {\removeunwantedspaces)\space}
+\permanent\protected\def\btxrightparenthesisperiod{\removeunwantedspaces).\space}
+\permanent\protected\def\btxrightparenthesiscomma {\removeunwantedspaces),\space}
+\permanent\protected\def\btxleftbracket {\removeunwantedspaces\space[}
+\permanent\protected\def\btxrightbracket {\removeunwantedspaces]\space}
+\permanent\protected\def\btxrightbracketperiod {\removeunwantedspaces].\space}
+\permanent\protected\def\btxrightbracketcomma {\removeunwantedspaces],\space}
+
+%D Variables:
+
+\mutable\let\currentbtxbacklink \empty \permanent\protected\def\btxsetbacklink {\def\currentbtxbacklink}
+\mutable\let\currentbtxcategory \empty \permanent\protected\def\btxsetcategory {\def\currentbtxcategory}
+\mutable\let\currentbtxcombis \empty \permanent\protected\def\btxsetcombis {\def\currentbtxcombis}
+\mutable\let\currentbtxdataset \empty \permanent\protected\def\btxsetdataset {\def\currentbtxdataset}
+\mutable\let\currentbtxfirst \empty \permanent\protected\def\btxsetfirst {\def\currentbtxfirst}
+\mutable\let\currentbtxsecond \empty \permanent\protected\def\btxsetsecond {\def\currentbtxsecond}
+\mutable\let\currentbtxsuffix \empty \permanent\protected\def\btxsetsuffix {\def\currentbtxsuffix}
+\mutable\let\currentbtxinternal \empty \permanent\protected\def\btxsetinternal {\def\currentbtxinternal}
+\mutable\let\currentbtxlefttext \empty \permanent\protected\def\btxsetlefttext {\def\currentbtxlefttext}
+\mutable\let\currentbtxrighttext \empty \permanent\protected\def\btxsetrighttext {\def\currentbtxrighttext}
+\mutable\let\currentbtxbefore \empty \permanent\protected\def\btxsetbefore {\def\currentbtxbefore}
+\mutable\let\currentbtxafter \empty \permanent\protected\def\btxsetafter {\def\currentbtxafter}
+\mutable\let\currentbtxlanguage \empty \permanent\protected\def\btxsetlanguage {\def\currentbtxlanguage}
+\mutable\let\currentbtxtag \empty \permanent\protected\def\btxsettag {\def\currentbtxtag}
+\mutable\let\currentbtxnumber \empty \permanent\protected\def\btxsetnumber {\def\currentbtxnumber}
+\mutable\let\currentbtxfirstinternal\empty \permanent\protected\def\btxsetfirstinternal{\def\currentbtxfirstinternal}
+\mutable\let\currentbtxlastinternal \empty \permanent\protected\def\btxsetlastinternal {\def\currentbtxlastinternal}
+
+\mutable\let\currentbtxauthorvariant\v!normal \permanent\protected\def\btxsetauthorvariant{\def\currentbtxauthorvariant}
+\mutable\let\currentbtxfirstnames \empty \permanent\protected\def\btxsetfirstnames {\let\currentbtxfirstnames\currentbtxfirstnames_indeed}
+\mutable\let\currentbtxinitials \empty \permanent\protected\def\btxsetinitials {\let\currentbtxinitials \currentbtxinitials_indeed }
+\mutable\let\currentbtxjuniors \empty \permanent\protected\def\btxsetjuniors {\let\currentbtxjuniors \currentbtxjuniors_indeed }
+\mutable\let\currentbtxsurnames \empty \permanent\protected\def\btxsetsurnames {\let\currentbtxsurnames \currentbtxsurnames_indeed }
+\mutable\let\currentbtxvons \empty \permanent\protected\def\btxsetvons {\let\currentbtxvons \currentbtxvons_indeed }
+
+\newconstant\currentbtxoverflow \permanent\protected\def\btxsetoverflow #1{\currentbtxoverflow #1\relax}
+\newconstant\currentbtxconcat \permanent\protected\def\btxsetconcat #1{\currentbtxconcat #1\relax}
+\newconstant\currentbtxcount \permanent\protected\def\btxsetcount #1{\currentbtxcount #1\relax}
+\newconstant\currentbtxauthorindex %permanent\protected\def\btxsetauthorindex#1{\currentbtxauthorindex#1\relax} % passed directly
+\newconstant\currentbtxauthorcount %permanent\protected\def\btxsetauthorcount#1{\currentbtxauthorcount#1\relax} % passed directly
+\newconstant\currentbtxauthorstate \permanent\protected\def\btxsetauthorstate#1{\currentbtxauthorstate#1\relax}
+
+\protected\def\currentbtxfirstnames_indeed{\clf_btxcurrentfirstnames\numexpr\currentbtxauthorindex\relax}
+\protected\def\currentbtxinitials_indeed {\clf_btxcurrentinitials \numexpr\currentbtxauthorindex\relax}
+\protected\def\currentbtxjuniors_indeed {\clf_btxcurrentjuniors \numexpr\currentbtxauthorindex\relax}
+\protected\def\currentbtxsurnames_indeed {\clf_btxcurrentsurnames \numexpr\currentbtxauthorindex\relax}
+\protected\def\currentbtxvons_indeed {\clf_btxcurrentvons \numexpr\currentbtxauthorindex\relax}
+
+\mutable\let\currentbtxfirstpage \empty \permanent\protected\def\btxsetfirstpage#1{\def\currentbtxfirstpage{\btx_page_number{#1}}}
+\mutable\let\currentbtxlastpage \empty \permanent\protected\def\btxsetlastpage #1{\def\currentbtxlastpage {\btx_page_number{#1}}}
+
+\mutable\let\currentbtxfield \empty
+\mutable\let\currentbtxfielddoi \empty
+\mutable\let\currentbtxfieldmonth \empty
+\mutable\let\currentbtxfieldyear \empty
+\mutable\let\currentbtxfieldurl \empty
+\mutable\let\currentbtxcriterium \empty
+\mutable\let\currentbtxlist \empty
+\mutable\let\currentbtxlistentry \empty
+\mutable\let\currentbtxlistindex \empty
+\mutable\let\currentbtxreference \empty
+\mutable\let\currentbtxrenderingtitle \empty
+\mutable\let\currentbtxcitetag \empty
+
+\mutable\def\currentbtxauthorvariant{normal}
+
+\protected\def\btx_reset_list % not needed as we're grouped
+ {\let\currentbtxcombis \empty
+ \let\currentbtxcategory \empty
+ \let\currentbtxinternal \empty
+ \let\currentbtxlefttext \empty
+ \let\currentbtxrighttext\empty
+ \let\currentbtxbefore \empty
+ \let\currentbtxafter \empty
+ \let\currentbtxbacklink \empty
+ \let\currentbtxlanguage \empty
+ \let\currentbtxsuffix \empty
+ %\let\currentbtxdataset \empty % will always be set
+ %\let\currentbtxtag \empty % will always be set
+ \let\currentbtxnumber \empty}
+
+\protected\def\btx_reset_cite % check for less .. not all resets needed when we're grouped (only subcites)
+ {\let \currentbtxfirst \empty
+ \let \currentbtxsecond \empty
+ \let \currentbtxsuffix \empty
+ \let \currentbtxinternal \empty
+ \let \currentbtxlefttext \empty
+ \let \currentbtxrighttext \empty
+ \let \currentbtxbefore \empty
+ \let \currentbtxafter \empty
+ \let \currentbtxbacklink \empty
+ \let \currentbtxlanguage \empty
+ %\let \currentbtxdataset \empty % will always be set, beware of local reset ~
+ %\let \currentbtxtag \empty % will always be set, beware of local reset ~
+ \let \currentbtxnumber \empty
+ \setconstant\currentbtxoverflow \zerocount
+ \setconstant\currentbtxconcat \zerocount
+ \setconstant\currentbtxcount \zerocount}
+
+\protected\def\btx_reset_page % probably not needed
+ {\let \currentbtxfirstpage \empty
+ \let \currentbtxlastpage \empty
+ \let \currentbtxfirstinternal\empty
+ \let \currentbtxlastinternal \empty
+ \setconstant\currentbtxoverflow \zerocount
+ \setconstant\currentbtxconcat \zerocount
+ \setconstant\currentbtxcount \zerocount}
+
+\protected\def\btx_reset_numbering % probably not needed
+ {\let \currentbtxfirst \empty
+ \let \currentbtxsecond\empty
+ \let \currentbtxsuffix\empty
+ \setconstant\currentbtxconcat\zerocount}
+
+%D Pages:
+
+\protected\def\btx_page_number#1%
+ {\def\currentlistindex{#1}%
+ \structurelistpagenumber}
+
+%D Language:
+
+\mutable\def\mainbtxlanguage{\currentmainlanguage}
+
+\protected\def\btx_check_language
+ {\let\mainbtxlanguage\currentlanguage
+ \ifempty\currentbtxlanguage
+ \let\currentbtxlanguage\currentlanguage
+ \else
+ \btx_check_language_indeed
+ \fi}
+
+\protected\def\btx_check_language_indeed
+ {\edef\currentbtxlanguage{\reallanguagetag\currentbtxlanguage}%
+ \ifempty\currentbtxlanguage
+ \let\currentbtxlanguage\currentlanguage
+ \orelse\ifx\currentbtxlanguage\currentlanguage\else
+ \setcurrentlanguage\currentmainlanguage\currentbtxlanguage
+ \fi}
+
+%D Tracing
+
+\newconditional\c_btx_trace % not used yet
+
+\installtextracker
+ {btxrendering}
+ {\settrue \c_btx_trace}
+ {\setfalse\c_btx_trace}
+
+%D Rendering lists and citations.
+
+\permanent\protected\def\btxtodo#1{[#1]}
+
+%D Lists:
+
+\newdimen\d_publ_number_width
+
+\ifdefined\btxblock \else \newcount\btxblock \fi \btxblock\plusone
+\ifdefined\btxcitecounter \else \newcount\btxcitecounter \fi % maybe pass this to lua
+
+\newtoks\everysetupbtxlistplacement % name will change
+\newtoks\everysetupbtxciteplacement % name will change
+
+\definelist % only used for selecting
+ [\s!btx]
+
+\setuplist
+ [\s!btx]
+ [\c!prefixstopper=:,
+ \c!state=\v!start,
+ \c!alternative=a,
+ \c!interaction=\v!none,
+ %\c!alternative=\v!paragraph,
+ %\c!width=\v!auto,
+ %\c!distance=\emwidth,
+ \c!before=\blank,
+ \c!after=\blank]
+
+\permanent\tolerant\protected\def\setupbtxlist[#1]#*[#2]%
+ {\ifarguments\or
+ \setuplist[\s!btx][#1]%
+ \or
+ \setuplist[\s!btx:#1][#2]%
+ \fi}
+
+\appendtoks
+ \ifempty\currentbtxrenderingparent
+ \definelist
+ [\s!btx:\currentbtxrendering]%
+ [\s!btx]%
+ \orelse\ifx\currentbtxrenderingparent\s!btx
+ \definelist
+ [\s!btx:\currentbtxrendering]%
+ [\s!btx]%
+ \else
+ \definelist
+ [\s!btx:\currentbtxrendering]%
+ [\s!btx:\currentbtxrenderingparent]%
+ \fi
+\to \everydefinebtxrendering
+
+\newconditional\c_btx_list_texts
+
+\appendtoks
+ \doifelse{\btxrenderingparameter\c!textstate}\v!start
+ \settrue\setfalse\c_btx_list_texts
+\to \everysetupbtxlistplacement
+
+\newconditional\c_btx_list_pages
+
+\appendtoks
+ \doifelse{\btxrenderingparameter\c!pagestate}\v!start
+ \settrue\setfalse\c_btx_list_pages
+\to \everysetupbtxlistplacement
+
+\protected\def\btx_entry_inject_pages % for the moment only normal
+ {\dontleavehmode
+ \begingroup
+ \setbtxlist % probably already set
+ \btx_reset_page
+ \setbtxparameterset\s!list\s!page
+ \btxparameter\c!command
+ {\usebtxstyleandcolor\c!style\c!color
+ \btxparameter\c!left
+ \clf_btxflushpages{\currentbtxdataset}{\currentbtxtag}%
+ \btxparameter\c!right}%
+ \endgroup}
+
+\permanent\protected\def\btxpagesetup#1% there will be no left|right|command|style at this inner level
+ {\begingroup
+ \publ_fast_setup\plusfive\s!list\s!page
+ \endgroup
+ \btx_reset_page} % probably not needed
+
+\permanent\protected\def\btxnumberingsetup#1%
+ {\begingroup
+ \dostarttagged\t!listtag\empty
+ \setbtxparameterset{\c!list:\s!numbering}\currentbtxnumbering % brrrr \setbtxlist
+ \btxparameter\c!left
+ \publ_fast_setup\plusthree{\s!list:\s!numbering}{#1}%
+ \btxparameter\c!right
+ \dostoptagged
+ \endgroup
+ \btx_reset_numbering} % probably not needed
+
+% end of page stuff
+
+\permanent\protected\def\btxflushlisttext
+ {\begingroup
+ \usebtxstyleandcolor\c!style\c!color
+ \ignorespaces
+ \publ_fast_setup\plusfour\s!list\currentbtxcategory
+ \removeunwantedspaces
+ \endgroup}
+
+\permanent\protected\def\btxflushlistcombis
+ {\begingroup
+ \processcommacommand[\currentbtxcombis]\btx_entry_inject_combi % maybe in lua
+ \endgroup}
+
+\def\btx_entry_inject_list_text
+ {\publ_fast_setup\plusfour\s!list\s!text}
+
+\ifdefined\dotagpublication \else \aliased\let\dotagpublication\gobbletwoarguments \fi
+
+\protected\def\btx_entry_inject
+ {\begingroup
+ \dostarttagged\t!publication\empty
+ \dotagpublication\currentbtxdataset\currentbtxtag
+ \redoconvertfont % see (**) in strc-lst, this will become an configuration option
+ \edef\currentbtxcategory{\btxrawfield{category}}%
+ \ignorespaces
+ \ifconditional\c_btx_list_texts
+ \dostarttagged\t!listtext\s!left
+ \currentbtxbefore
+ \dostoptagged
+ \fi
+ %\dostarttagged\t!listcontent\empty
+ \btx_entry_inject_list_text
+ %\dostoptagged
+ \ifconditional\c_btx_list_pages
+ \dostarttagged\t!listpage\empty
+ \btx_entry_inject_pages
+ \dostoptagged
+ \fi
+ \ifconditional\c_btx_list_texts
+ \dostarttagged\t!listtext\s!right
+ \currentbtxafter
+ \dostoptagged
+ \fi
+ \dostoptagged
+ \endgroup}
+
+\permanent\tolerant\protected\def\btxshowentryinline[#1]#*[#2]%
+ {\ifarguments
+ \ctxcommand{showbtxentry("\currentbtxdataset","\currentbtxtag")}
+ \or
+ \ctxcommand{showbtxentry("\currentbtxdataset","#1")}
+ \or
+ \ctxcommand{showbtxentry("#1","#2")}
+ \fi}
+
+\permanent\protected\def\btxstartcombientry
+ {\begingroup}
+
+\permanent\protected\def\btxstopcombientry
+ {\endgroup}
+
+\permanent\protected\def\btxhandlecombientry
+ {\btx_reference_indeed}
+
+\def\btx_entry_inject_combi#1%
+ {\begingroup
+ \def\currentbtxtag{#1}%
+ \ignorespaces
+ \publ_fast_setup\plusfour\s!list\currentbtxcategory
+ \removeunwantedspaces
+ \endgroup}
+
+% uses reference when set
+
+% \def\btx_entry_inject_combi#1%
+% {\begingroup
+% \def\currentbtxtag{#1}%
+% \ignorespaces
+% \btxdoifelsecombiinlist\currentbtxdataset\currentbtxtag
+% {\clf_btxflushlistcombi{\currentbtxdataset}{\currentbtxtag}}
+% {\publ_fast_setup\plusfour\s!list\currentbtxcategory}%
+% \removeunwantedspaces
+% \endgroup}
+
+\newtoks\everybtxlistrendering
+
+\appendtoks
+ \setbtxlist
+ %
+ \edef\currentbtxcriterium{\btxrenderingparameter\c!criterium}% \v!cite will become \s!cite
+ \ifempty\currentbtxcriterium
+ \let\currentbtxcriterium\v!previous
+ \orelse\ifx\currentbtxcriterium\v!cite
+ \let\currentbtxcriterium\v!here
+ \fi
+ %
+ \iflocation
+ \letinteractionparameter\c!style\empty
+% \letinteractionparameter\c!color\empty
+% \letinteractionparameter\c!contrastcolor\empty
+ \fi
+\to \everybtxlistrendering
+
+\mutable\let\nofbtxlistentries \!!zerocount
+\mutable\let\currentbtxlistentry\!!zerocount
+\mutable\let\currentbtxlistindex\!!zerocount % only for internal use (points back to big list)
+
+\newconditional\c_publ_prefixed
+
+\permanent\protected\def\btxsetnoflistentries #1{\edef\nofbtxlistentries {#1}}
+\permanent\protected\def\btxsetcurrentlistentry#1{\edef\currentbtxlistentry{#1}}
+\permanent\protected\def\btxsetcurrentlistindex#1{\edef\currentbtxlistindex{#1}}
+
+\permanent\protected\def\btxdoifelsesameaspreviouschecked#1#2% #1 == always | doublesided
+ {\clf_btxdoifelsesameasprevious
+ {\currentbtxdataset}%
+ \currentbtxlistentry%
+ {#2}%
+ \c_btx_list_reference
+ {#1}}
+
+\permanent\protected\def\btxdoifelsesameasprevious
+ {\btxdoifelsesameaspreviouschecked\v!doublesided}
+
+\permanent\protected\def\btxdoifelsecombiinlist#1#2%
+ {\clf_btxdoifelsecombiinlist{#1}{#2}}
+
+\aliased\let\btxdoifsameaspreviouscheckedelse\btxdoifelsesameaspreviouschecked
+\aliased\let\btxdoifsameaspreviouselse \btxdoifelsesameasprevious
+\aliased\let\btxdoifcombiinlistelse \btxdoifelsecombiinlist
+
+\tolerant\def\publ_place_list_indeed#1#2#*[#3]#*[#4]%
+ {\begingroup
+ \ifparameters\or\or
+ \let\currentbtxrendering\currentbtxspecification
+ \or
+ \ifhastok={#3}%
+ % [settings]
+ \let\currentbtxrendering\currentbtxspecification
+ \setupcurrentbtxrendering[#3]%
+ \edef\p_specification{\btxrenderingparameter\c!specification}%
+ \ifempty\p_specification\else
+ \let\currentbtxspecification\p_specification
+ \let\currentbtxrendering\currentbtxspecification % tricky
+ \fi
+ \else
+ \edef\currentbtxrendering{#3}%
+ \edef\p_specification{\btxrenderingparameter\c!specification}%
+ \ifempty\p_specification\else
+ \let\currentbtxspecification\p_specification
+ \fi
+ \fi
+ \or
+ % [rendering] [settings]
+ \edef\currentbtxrendering{#3}%
+ \setupcurrentbtxrendering[#4]%
+ \edef\p_specification{\btxrenderingparameter\c!specification}%
+ \ifempty\p_specification\else
+ \let\currentbtxspecification\p_specification
+ \fi
+ \fi
+ \setbtxparameterset\currentbtxspecification\s!list
+ \the\everybtxlistrendering
+ \ifconditional#1\relax
+ \edef\currentbtxrenderingtitle{\btxrenderingparameter\c!title}%
+ \ifempty\currentbtxrenderingtitle
+ \normalexpanded{\startnamedsection[\v!chapter][\c!reference=\currentbtxrendering,\c!title={\headtext{\currentbtxrendering}}]}%
+ \else
+ \normalexpanded{\startnamedsection[\v!chapter][\c!reference=\currentbtxrendering,\c!title={\currentbtxrenderingtitle}]}%
+ \fi
+ \fi
+ \ifempty\currentbtxrendering
+ \setbtxrendering % hm
+ \fi
+ \edef\currentbtxdataset{\btxrenderingparameter\c!dataset}%
+ \uselanguageparameter\btxdatasetparameter % new
+ \setbtxlist
+ \the\everystructurelist
+ \the\everysetupbtxlistplacement
+ % why not pass this with collect .. todo
+ % here we just collect items
+ \clf_btxcollectlistentries
+ names {\s!btx}%
+ criterium {\currentbtxcriterium}%
+ reference {\btxrenderingparameter\c!reference}%
+ method {\btxrenderingparameter\c!method}%
+ dataset {\currentbtxdataset}%
+ keyword {\btxrenderingparameter\c!keyword}%
+ sorttype {\btxrenderingparameter\c!sorttype}%
+ repeated {\btxrenderingparameter\c!repeat}%
+ ignored {\btxrenderingparameter\c!ignore}%
+ group {\btxrenderingparameter\c!group}%
+ filter {\btxrenderingparameter\c!filter}%
+ \relax
+ \ifnum\nofbtxlistentries>\zerocount
+ \forgetall
+ \btxrenderingparameter\c!before
+ \ifconditional#2\relax
+ \edef\p_command{\btxrenderingparameter\c!command}%
+ \ifempty\p_command
+ \edef\p_setups{\btxrenderingparameter\c!setups}%
+ \ifempty\p_setups
+ \else
+ \directsetup{\p_setups}%
+ \fi
+ \else
+ \expandafter\p_command\expandafter{\number\nofbtxlistentries}\relax
+ \fi
+ \else
+ \dostarttagged\t!publications\currentbtxrendering
+ \dostarttagged\t!list{btx}%
+ \startpacked[\v!blank]%
+ % sorting and so
+ \clf_btxpreparelistentries{\currentbtxdataset}% could be put in collect
+ % next we analyze the width
+ \ifempty\currentbtxnumbering \else
+ \edef\p_width{\listparameter\c!width}%
+ \ifx\p_width\v!auto
+ \setbox\scratchbox\vbox \bgroup
+ \settrialtypesetting
+ \clf_btxfetchlistentries{\currentbtxdataset}%
+ \egroup
+ \d_publ_number_width\wd\scratchbox
+ \letlistparameter\c!width\d_publ_number_width
+ \fi
+ \fi
+ \doifelse{\listparameter\c!prefix}\v!yes\settrue\setfalse\c_publ_prefixed
+ % this actually typesets them, we loop here as otherwise the whole
+ % bunch gets flushed at once
+ \dorecurse\nofbtxlistentries
+ {\let\currentbtxlistentry\recurselevel
+ \clf_btxflushlistentry{\currentbtxdataset}\currentbtxlistentry\relax}%
+ \stoppacked
+ \dostoptagged
+ \dostoptagged
+ \fi
+ \btxrenderingparameter\c!after
+ \fi
+ \ifconditional#1\relax
+ \stopnamedsection
+ \fi
+ \global\advance\btxblock\plusone
+ \endgroup}
+
+\permanent\protected\def\placebtxrendering {\publ_place_list_indeed\conditionalfalse\conditionalfalse}
+\permanent\protected\def\completebtxrendering{\publ_place_list_indeed\conditionaltrue \conditionalfalse}
+\permanent\protected\def\flushbtxrendering {\publ_place_list_indeed\conditionalfalse\conditionaltrue }
+
+\aliased\let\completelistofpublications\completebtxrendering % for old times sake
+\aliased\let\placelistofpublications \placebtxrendering % for old times sake
+
+%D This is somewhat special (for Alan of course):
+%D
+%D \starttyping
+%D % #1 is number of entries
+%D
+%D \starttexdefinition mutable protected btx:for:alan:wrapper #1
+%D \bTABLE
+%D % we can have a command or setups
+%D \flushbtxentries[command=\texdefinition{btx:for:alan:content}]
+%D \eTABLE
+%D \stoptexdefinition
+%D
+%D % #1 is tag
+%D
+%D \starttexdefinition mutable protected btx:for:alan:content #1
+%D \bTR
+%D \bTD
+%D \btxsettag{#1}
+%D \btxfield{name}
+%D \eTD
+%D \eTR
+%D \stoptexdefinition
+%D
+%D % we can have a command or setups
+%D
+%D \flushbtxrendering [method=dataset,command=\texdefinition{btx:for:alan:wrapper}]
+%D \stoptyping
+%D
+%D Because we want to be ungrouped we use a special loop construct.
+
+\permanent\protected\def\btxsetlisttag#1%
+ {\clf_btxflushlisttag{\currentbtxdataset}#1\relax}
+
+\newcount\c_btx_list_index
+
+\let\m_btx_list_action\empty
+
+\def\publ_flush_list_step_command
+ {\btxsetlisttag{\c_btx_list_index}
+ \expandafter\m_btx_list_action\expandafter{\currentbtxtag}%
+ \ifnum\c_btx_list_index<\nofbtxlistentries
+ \advance\c_btx_list_index\plusone
+ \expandafter\publ_flush_list_step_command
+ \else
+ \glet\m_btx_list_action\relax
+ \fi}
+
+\def\publ_flush_list_step_setup
+ {\btxsetlisttag{\c_btx_list_index}
+ \directsetup{\m_btx_list_action}%
+ \ifnum\c_btx_list_index<\nofbtxlistentries
+ \advance\c_btx_list_index\plusone
+ \expandafter\publ_flush_list_step_setup
+ \else
+ \glet\m_btx_list_action\relax
+ \fi}
+
+\permanent\protected\def\flushbtxentries[#1]%
+ {\begingroup
+ \getdummyparameters[\c!command=,\c!setups=,#1]%
+ \xdef\m_btx_list_action{\dummyparameter\c!command}%
+ \ifempty\m_btx_list_action
+ \xdef\m_btx_list_action{\dummyparameter\c!setups}%
+ \ifempty\m_btx_list_action
+ \endgroup
+ \c_btx_list_index\zerocount
+ \else
+ \endgroup
+ \c_btx_list_index\plusone
+ \doubleexpandafter\publ_flush_list_step_command
+ \fi
+ \else
+ \endgroup
+ \c_btx_list_index\plusone
+ \expandafter\publ_flush_list_step_command
+ \fi}
+
+%D So far.
+
+\permanent\def\currentbtxblock{\number\btxblock}
+
+% called at the lua end, for determining the width
+
+\permanent\protected\def\btxchecklistentry
+ {\begingroup
+ % todo, switch to font
+ \hbox{\btx_reference_checked}%
+ \par
+ \endgroup}
+
+% called at the lua end, the real rendering
+
+% we could have a yes and no where o nils the btx_reference_indeed ... saves a check there
+
+\installstructurelistprocessor{\s!btx}
+ {\let\currentlistentrynumber \btx_reference_indeed
+ \let\currentlistentrytitle \btx_entry_indeed
+ \let\currentlistentrypagenumber\btx_page_indeed
+ \strc_lists_apply_renderingsetup}
+
+\def\btx_entry_indeed
+ {\dostarttagged\t!listcontent\empty
+ \btx_list_reference_inject
+ \btx_entry_inject
+ \dostoptagged}
+
+\def\btx_page_indeed
+ {}
+
+\permanent\protected\def\btxhandlelistentry
+ {\strc_lists_entry_process}
+
+\permanent\protected\def\btxstartlistentry % maybe pass i
+ {\begingroup
+ \global\advance\c_btx_list_reference\plusone}
+
+\permanent\protected\def\btxstoplistentry
+ {\iftrialtypesetting
+ \global\advance\c_btx_list_reference\minusone
+ \fi
+ \endgroup}
+
+\newtoks\everybtxlistentry
+
+\permanent\protected\def\btxlistsetup#1% used for the reference in the list
+ {\the\everybtxlistentry
+ \everybtxlistentry\emptytoks % so only once per entry to be sure
+ \publ_fast_setup\plusfour\s!list{#1}}
+
+\appendtoks
+ \btx_check_language
+\to \everybtxlistentry
+
+\protected\def\btx_reference_indeed
+ {\begingroup
+ % redundant will go away:
+ \setbtxparameterset{\c!list:\s!numbering}\currentbtxnumbering
+ %
+ \ifempty\currentbtxnumbering
+ % nothing
+ \orelse\ifx\currentbtxnumbering\v!no
+ % nothing
+ \else
+ \usebtxstyleandcolor\c!style\c!color % new, needed?
+ \ifconditional\c_publ_prefixed\btxlistprefixednumber\fi
+ \clf_btxlistvariant % some can go
+ {\currentbtxdataset}%
+ {\currentbtxblock}%
+ {\currentbtxtag}%
+ {\currentbtxnumbering}%
+ {\currentbtxnumber}%
+ \relax
+ \fi
+ \endgroup}
+
+\permanent\protected\def\btxlistprefixednumber % hack but alan needs it
+ {\clf_listprefixednumber
+ {\currentlist}%
+ \currentbtxlistindex
+ {%
+ prefix {\listparameter\c!prefix}%
+ separatorset {\listparameter\c!prefixseparatorset}%
+ conversionset {\listparameter\c!prefixconversionset}%
+ starter {\listparameter\c!prefixstarter}%
+ stopper {\listparameter\c!prefixstopper}%
+ set {\listparameter\c!prefixset}%
+ segments {\listparameter\c!prefixsegments}%
+ connector {\listparameter\c!prefixconnector}%
+ }%
+ \relax}
+
+\protected\def\btx_reference_checked
+ {\dontleavehmode\hbox\bgroup % \hpack
+ \btx_reference_indeed
+ \egroup}
+
+\newcount\c_btx_list_reference
+
+\protected\def\btx_list_reference_inject
+ {\dontleavehmode\begingroup % no box
+ \iftrialtypesetting\else
+ \btx_list_reference_inject_now
+ \fi
+ % \btx_reference_indeed % else double entry in list
+ \endgroup}
+
+\def\btx_list_reference_inject_now
+ {\strc_references_direct_full_user
+ {\ifx\currentbtxdataset\v!default\else\s!btxset=\currentbtxdataset,\fi%
+ \s!btxref=\currentbtxtag,%
+ \s!btxspc=\currentbtxspecification,%
+ \s!btxlst=\number\c_btx_list_reference,% check if needed
+ %\ifempty\currentbtxcombis \else\s!btxcom={\currentbtxcombis},\fi%
+ \ifempty\currentbtxbefore \else\s!btxbtx={\currentbtxbefore},\fi%
+ \ifempty\currentbtxafter \else\s!btxatx={\currentbtxafter },\fi%
+ \ifempty\currentbtxbacklink\else\s!btxint=\number\currentbtxbacklink\fi
+ }%
+ {\s!btx::\v!list::\number\c_btx_list_reference}%
+ {\currentbtxnumber}}
+
+\newconditional\c_btx_cite_reference_injected
+
+\permanent\protected\def\btx_cite_reference_inject
+ {\ifconditional\c_btx_cite_reference_injected \else
+ \dontleavehmode
+ \iftrialtypesetting \else
+ \ifempty\currentbtxbacklink
+ % can be made empty when combining author / year
+ \orelse\ifnum\currentbtxbacklink>\zerocount
+ \btx_cite_reference_inject_indeed
+ \settrue\c_btx_cite_reference_injected
+ \fi
+ \fi
+ \fi}
+
+\newtoks\t_btx_reference_inject
+
+\def\btx_cite_reference_inject_indeed
+ {\the\t_btx_reference_inject
+ \strc_lists_inject_direct % todo: make like \btx_list_reference_inject_now with { }
+ [\s!btx]%
+ [\c!type=\s!btx]% \c!location=\v!none
+ [\ifx\currentbtxdataset\v!default\else\s!btxset=\currentbtxdataset,\fi%
+ \s!btxref=\currentbtxtag,%
+ %\ifempty\currentbtxcombis \else\s!btxcom={\currentbtxcombis},\fi%
+ \ifempty\currentbtxbefore \else\s!btxbtx={\currentbtxbefore},\fi%
+ \ifempty\currentbtxafter \else\s!btxatx={\currentbtxafter },\fi%
+ \ifempty\currentbtxbacklink\else\s!btxint=\number\currentbtxbacklink,\fi
+ \ifempty\currentbtxciteuservariables\else,\currentbtxciteuservariables\fi]}
+
+\permanent\def\currentbtxuservariable #1{\clf_btxuservariable {\currentbtxdataset}{#1}}
+\permanent\def\btxdoifelseuservariable#1{\clf_btxdoifelseuservariable{\currentbtxdataset}{#1}}
+
+\aliased\let\btxdoifuservariableelse\btxdoifelseuservariable
+
+\aliased\let\btxcitereference\btx_cite_reference_inject
+
+\mutable\let\currentbtxnumbering \empty
+\mutable\let\currentbtxcitealternative \empty
+
+\appendtoks
+ \edef\currentbtxnumbering{\btxrenderingparameter\c!numbering}%
+ \ifx\currentbtxnumbering\v!yes
+ \def\currentbtxnumbering{num}% convenient alias
+ \letbtxrenderingparameter\c!numbering\currentbtxnumbering
+ \letlistparameter\c!headnumber\v!always
+ \orelse\ifx\currentbtxnumbering\v!no
+ \letlistparameter\c!headnumber\v!no
+ \let\currentbtxnumbering\empty
+ % \letlistparameter\c!textcommand\outdented % needed? we can use titlealign
+ \letlistparameter\c!symbol \v!none
+ \letlistparameter\c!aligntitle \v!yes
+ \letlistparameter\c!numbercommand\firstofoneargument % for the moment, no doubling needed
+ \else
+ \letlistparameter\c!headnumber\v!always
+ \fi
+ \let\currentlistmethod\s!btx
+\to \everysetupbtxlistplacement
+
+\permanent\tolerant\protected\def\btxremapauthor[#1]#*[#2]%
+ {\clf_btxremapauthor{#1}{#2}}
+
+\permanent\protected\def\btxshowauthorremapping
+ {\clf_btxshowauthorremapping}
+
+\permanent\protected\def\btxflushauthor
+ {\doifelsenextoptionalcs\btx_flush_author_yes\btx_flush_author_nop}
+
+\permanent\protected\def\btxflushsuffix
+ {\ifempty\currentbtxsuffix
+ % nothing
+ \else
+ \characters{\currentbtxsuffix}% todo : rendering specific converter
+ \fi}
+
+\def\btx_flush_author_yes[#1]{\btx_flush_author{#1}}
+\def\btx_flush_author_nop {\btx_flush_author{\btxparameter\c!authorconversion}}
+
+\protected\def\btx_flush_author#1#2%
+ {\begingroup
+ \edef\currentbtxfield{#2}%
+ \setbtxparameterset\s!list\currentbtxfield
+% \let\currentbtxlistvariant\currentbtxfield
+ \clf_btxauthor
+ {\currentbtxdataset}%
+ {\currentbtxtag}%
+ {\currentbtxfield}%
+ {%
+ combiner {#1}%
+ kind {list}%
+ etallimit {\btxparameter\c!etallimit}%
+ etaldisplay {\btxparameter\c!etaldisplay}%
+ etaloption {\btxparameter\c!etaloption}%
+ symbol {\btxparameter{\c!stopper:initials}}%
+ connector {\btxparameter{\c!connector:initials}}%
+ }%
+ \relax
+ \endgroup}
+
+% yes or no: maybe just \flushauthor{...}{...}
+
+\permanent\protected\def\btxflushauthorname {\btx_flush_author{name}} % #1
+\permanent\protected\def\btxflushauthornormal {\btx_flush_author{normal}} % #1
+\permanent\protected\def\btxflushauthornormalshort {\btx_flush_author{normalshort}} % #1
+\permanent\protected\def\btxflushauthorinverted {\btx_flush_author{inverted}} % #1
+\permanent\protected\def\btxflushauthorinvertedshort{\btx_flush_author{invertedshort}} % #1
+
+\mutable\let\currentbtxauthorfield\s!author
+
+\permanent\protected\def\btxsetauthorfield#1{\edef\currentbtxauthorfield{#1}}
+
+\permanent\protected\def\currentbtxciteauthorbyfield
+ {\begingroup
+ %\setbtxparameterset\s!cite\s!author
+ % the alternatives inherit from cite:author
+ % and APA distinguishes authoryears from authoryear ("and" vs. "&")
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \clf_btxauthor
+ {\currentbtxdataset}%
+ {\currentbtxtag}%
+ {\currentbtxauthorfield}%
+ {%
+ combiner {\btxparameter\c!authorconversion}%
+ kind {cite}%
+ etallimit {\btxparameter\c!etallimit}%
+ etaldisplay {\btxparameter\c!etaldisplay}%
+ etaloption {\btxparameter\c!etaloption}%
+ symbol {\btxparameter{\c!stopper:initials}}%
+ }%
+ \relax
+ \endgroup}
+
+\permanent\protected\def\currentbtxciteauthor
+ {\let\currentbtxauthorfield\s!author
+ \currentbtxciteauthorbyfield} % always author
+
+\permanent\protected\def\btxstartauthor#1#2#3% a state > 0 signals that some authors can clash
+ {\begingroup
+ \currentbtxauthorindex#1\relax
+ \currentbtxauthorcount#2\relax
+ \currentbtxauthorstate#3\relax}
+
+\permanent\protected\def\btxstopauthor
+ {\endgroup}
+
+\permanent\protected\def\btxciteauthorsetup#1{\fastsetup{\s!btx:\s!cite:\s!author:#1}}
+\permanent\protected\def\btxlistauthorsetup#1{\fastsetup{\s!btx:\s!list:\s!author:#1}}
+
+% \btxflushauthor{author}
+% \btxflushauthor{editor}
+%
+% \btxflushauthor[name]{author}
+% \btxflushauthor[normal]{author}
+% \btxflushauthor[normalshort]{author}
+% \btxflushauthor[inverted]{author}
+% \btxflushauthor[invertedshort]{author}
+
+% \btxflushauthor{author}
+% \btxflushauthor{editor}
+
+% Interaction
+%
+% Because we have more complex entries in lists we don't use the normal list
+% interaction features.
+
+\newconditional\btxinteractive
+\newconditional\btxinteractivenumber
+\newconditional\btxinteractivetext
+\newconditional\btxinteractivepage
+
+\mutable\let\currentbtxinteraction\empty
+
+\installcorenamespace{btxinteraction}
+
+\setvalue{\??btxinteraction\v!number}{\settrue\btxinteractivenumber}
+\setvalue{\??btxinteraction\v!text }{\settrue\btxinteractivetext}
+\setvalue{\??btxinteraction\v!page }{\settrue\btxinteractivepage}
+\setvalue{\??btxinteraction\v!all }{\settrue\btxinteractivenumber
+ \settrue\btxinteractivetext
+ \settrue\btxinteractivepage}
+
+% \setupbtx[interaction=page] % or text or number or all
+% \setupbtxrendering[pagestate=start]
+
+\appendtoks
+ \iflocation
+ \edef\currentbtxinteraction{\btxparameter\c!interaction}%
+ \ifx\currentbtxinteraction\v!stop
+ \setfalse\btxinteractive
+ \else
+ \enforced\let\structurelistlocation\empty
+ \settrue\btxinteractive
+ \begincsname\??btxinteraction\currentbtxinteraction\endcsname
+ \fi
+ \else
+ \setfalse\btxinteractive
+ \fi
+\to \everysetupbtxlistplacement
+
+\appendtoks
+ \iflocation
+ \edef\currentbtxinteraction{\btxparameter\c!interaction}%
+ \ifx\currentbtxinteraction\v!stop
+ \setfalse\btxinteractive
+ \else
+ \settrue\btxinteractive
+ \fi
+ \else
+ \setfalse\btxinteractive
+ \fi
+\to \everysetupbtxciteplacement
+
+%D When a publication is cited, we need to signal that somehow. This is done with the
+%D following (not user) command. We could tag without injecting a node but this way
+%D we also store the location, which makes it possible to ask local lists.
+
+%D \macros{cite,nocite,citation,nocitation,usecitation}
+%D
+%D The inline \type {\cite} command creates a (often) short reference to a publication
+%D and for historic reasons uses a strict test for brackets. This means, at least
+%D in the default case that spaces are ignored in the argument scanner. The \type
+%D {\citation} commands is more liberal but also gobbles following spaces. Both
+%D commands insert a reference as well as a visual clue.
+%D
+%D The \type {no} commands all do the same (they are synonyms): they make sure that
+%D a reference is injected but show nothing. However, they do create a node so best
+%D attach them to some text in order to avoid spacing interferences. A slightly
+%D less efficient alternative is \type {\cite[none][tag]}.
+
+% [tags]
+% [settings|variant][tags]
+% [base::tags]
+% [settings|variant][base::tags]
+
+% these need to be sort of protected:
+
+% methods:
+%
+% hidden : mark for list, don't show in text
+% list : mark for list, show in text only when in list
+% text : not to list, show in text
+% always : mark for list, show in text
+
+\let\p_publ_cite_before \empty
+\let\p_publ_cite_after \empty
+\let\p_publ_cite_lefttext \empty
+\let\p_publ_cite_righttext\empty
+
+\mutable\let\currentbtxciteuservariables\empty
+\mutable\let\currentbtxcitealternative \empty
+
+\permanent\protected\def\btxhybridcite % so one can alias the old
+ {\dontleavehmode
+ \begingroup
+ \strictdoifelsenextoptional\publ_cite_tags_options\publ_cite_tags_indeed}
+
+\protected\def\publ_cite_tags_options[#1]%
+ {\strictdoifelsenextoptional{\publ_cite_tags_options_indeed{#1}}{\publ_cite_tags_indeed{#1}}}
+
+\protected\def\publ_cite_tags_indeed#1%
+ {\letinteractionparameter\c!style\empty
+ \setbtxparametersetroot\s!cite % we need to get the default
+ \edef\currentbtxcitealternative{\btxparameter\c!alternative}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \edef\currentbtxcitetag{#1}%
+ \the\everysetupbtxciteplacement
+ \publ_cite_variant
+ \endgroup}
+
+\protected\def\publ_cite_tags_options_indeed#1%
+ {\doifelseassignment{#1}\publ_cite_tags_settings_indeed\publ_cite_tags_variants_indeed{#1}}
+
+\def\publ_cite_tags_settings_indeed#1[#2]%
+ {\letinteractionparameter\c!style\empty
+ %\letinteractionparameter\c!color\empty
+ \letdummyparameter\c!reference \empty
+ \letdummyparameter\c!alternative\empty
+ \letdummyparameter\c!before \empty
+ \letdummyparameter\c!after \empty
+ \letdummyparameter\c!lefttext \empty
+ \letdummyparameter\c!righttext \empty
+ \getdummyparameters[#1]%
+ \edef\p_reference{\dummyparameter\c!reference}%
+ \ifempty\p_reference
+ \edef\currentbtxcitetag{#2}%
+ \else
+ \let\currentbtxcitetag\p_reference
+ \edef\currentbtxciteuservariables{#2}%
+ \fi
+ \edef\p_specification{\dummyparameter\c!specification}%
+ \ifempty\p_specification
+ \else
+ \let\currentbtxspecification\p_specification
+ \fi
+ \edef\p_alternative{\dummyparameter\c!alternative}%
+ \ifempty\p_alternative
+ \setbtxparametersetroot\s!cite
+ \edef\currentbtxcitealternative{\btxparameter\c!alternative}%
+ \else
+ \let\currentbtxcitealternative\p_alternative
+ \fi
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \setupcurrentbtx[#1]%
+ %
+ \edef\p_publ_cite_before {\dummyparameter\c!before}%
+ \edef\p_publ_cite_after {\dummyparameter\c!after}%
+ \edef\p_publ_cite_lefttext {\dummyparameter\c!lefttext}%
+ \edef\p_publ_cite_righttext{\dummyparameter\c!righttext}%
+ %
+ \the\everysetupbtxciteplacement
+ \publ_cite_variant
+ \endgroup}
+
+\def\publ_cite_tags_variants_indeed#1[#2]%
+ {\letinteractionparameter\c!style\empty
+ \edef\currentbtxcitealternative{#1}%
+ \edef\currentbtxcitetag{#2}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \the\everysetupbtxciteplacement
+ \publ_cite_variant
+ \endgroup}
+
+\newconditional\btxcitecompress
+
+\mutable\let\currentbtxcitemethod\v!hidden
+
+\def\publ_cite_variant
+ {\begingroup
+ \publ_cite_handle_variant_indeed[\currentbtxcitetag]}
+
+\protected\def\publ_cite_handle_variant#1%
+ {\begingroup
+ \edef\currentbtxcitealternative{#1}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \the\everysetupbtxciteplacement
+ \publ_cite_handle_variant_indeed}
+
+\permanent\protected\def\publ_cite_handle_variant_blob
+ {\clf_btxhandlecite
+ dataset {\currentbtxdataset}%
+ reference {\currentbtxreference}%
+ method {\currentbtxcitemethod}%
+ variant {\currentbtxcitealternative}%
+ sorttype {\btxparameter\c!sorttype}%
+ compress {\btxparameter\c!compress}%
+ author {\btxparameter\c!author}%
+ authorconversion {\c!authorconversion}%
+ lefttext {\p_publ_cite_lefttext}%
+ righttext {\p_publ_cite_righttext}%
+ before {\p_publ_cite_before}%
+ after {\p_publ_cite_after}%
+ \relax
+ \iftrialtypesetting\else
+ %\clf_btxflushmarked
+ \fi}
+
+\aliased\let\dobtxcitevariantblob\publ_cite_handle_variant_blob % command can use it via lua
+
+\tolerant\def\publ_cite_handle_variant_indeed[#1]%
+ {\letbtxparameter\c!alternative\currentbtxcitealternative
+ \edef\currentbtxreference{#1}%
+ \saverunningstyleandcolor
+ \usebtxstyleandcolor\c!style\c!color
+ \uselanguageparameter\btxdatasetparameter % new
+ \btxparameter\c!left
+ \btxparameter\c!command{\dobtxcitevariantblob}% {\publ_cite_handle_variant_blob}%
+ \btxparameter\c!right
+ \endgroup}
+
+\permanent\protected\def\btxlistcitation {\publ_citation\v!list}
+\permanent\protected\def\btxtextcitation {\publ_citation\v!text}
+\permanent\protected\def\btxalwayscitation{\publ_citation\v!always}
+
+\tolerant\def\publ_citation#1[#2]#*[#3]% could be made more efficient but not now
+ {\dontleavehmode
+ \begingroup
+ \let\currentbtxcitemethod#1%
+ \ifparameter#3\or
+ \publ_cite_tags_options_indeed{#2}[#3]%
+ \else
+ \publ_cite_tags_indeed{#2}%
+ \fi}
+
+\permanent\tolerant\protected\def\btxhiddencitation[#1]%
+ {\iftrialtypesetting \else
+ \begingroup
+ \let\currentbtxcitemethod\v!hidden
+ \edef\currentbtxreference{#1}%
+ \clf_btxhandlenocite
+ method {\currentbtxcitemethod}%
+ dataset {\currentbtxdataset}%
+ reference {\currentbtxreference}%
+ \relax
+ %\clf_btxflushmarked
+ \endgroup
+ \fi}
+
+\permanent\protected\def\btxmissing#1%
+ {\dontleavehmode{\tttf<#1>}}
+
+%D Compatibility:
+
+\aliased\let\hiddencitation\btxhiddencitation \aliased\let\hiddencite\hiddencitation
+\aliased\let\listcitation \btxlistcitation \aliased\let\listcite \listcitation
+\aliased\let\textcitation \btxtextcitation \aliased\let\textcite \textcitation
+\aliased\let\alwayscitation\btxalwayscitation \aliased\let\alwayscite\alwayscitation
+
+\permanent\protected\def\citation {\doifelsenextoptionalcs\btxlistcitation \btxdirectlistcite}
+\permanent\protected\def\nocitation{\doifelsenextoptionalcs\btxhiddencitation\btxdirecthiddencite}
+
+\aliased\let\cite \citation
+\aliased\let\nocite \nocitation
+\aliased\let\usecitation\nocitation
+
+\protected\def\publ_entry_citation {\doifelsenextoptionalcs\btxlistcitation \btxdirectlistcite}
+\protected\def\publ_entry_nocitation{\doifelsenextoptionalcs\btxhiddencitation\btxdirecthiddencite}
+
+\appendtoks
+ \enforced\let\cite \publ_entry_citation
+ \enforced\let\nocite\publ_entry_nocitation
+\to \everybtxlistrendering
+
+\permanent\protected\def\btxdirectlistcite #1{\btxlistcitation [#1]\relax} % no optional arguments
+\permanent\protected\def\btxdirecthiddencite#1{\btxhiddencitation[#1]\relax} % no optional arguments
+
+%D Setup helpers, beware, we need to wrap this .. now we need to know
+%D how setups are implemented.
+
+\setvalue{\??setup:\s!btx:\s!unknown}#1{\inframed{\tttf#1}}
+
+\def\publ_fast_setup_yes#1#2%
+ {\csname\??setup:\s!btx:%
+ \ifcsname\??setup:\s!btx:\currentbtxspecification:#1:#2\endcsname
+ \currentbtxspecification:#1:#2%
+ \orelse\ifcsname\??setup:\s!btx:\currentbtxspecificationfallback:#1:#2\endcsname
+ \currentbtxspecificationfallback:#1:#2%
+ \orelse\ifcsname\??setup:\s!btx:#1:#2\endcsname
+ #1:#2%
+ \orelse\ifcsname\??setup:\s!btx:\currentbtxspecification:#1:\s!unknown\endcsname
+ \currentbtxspecification:#1:\s!unknown
+ \orelse\ifcsname\??setup:\s!btx:\currentbtxspecificationfallback:#1:\s!unknown\endcsname
+ \currentbtxspecificationfallback:#1:\s!unknown
+ \else
+ #1:\s!unknown
+ \fi
+ \endcsname{#2}}
+
+\def\publ_fast_setup_nop#1#2%
+ {\csname\??setup:\s!btx:%
+ \ifcsname\??setup:\s!btx:\currentbtxspecification:#1:#2\endcsname
+ \currentbtxspecification:#1:#2%
+ \orelse\ifcsname\??setup:\s!btx:#1:#2\endcsname
+ #1:#2%
+ \orelse\ifcsname\??setup:\s!btx:\currentbtxspecification:#1:\s!unknown\endcsname
+ \currentbtxspecification:#1:\s!unknown
+ \else
+ #1:\s!unknown
+ \fi
+ \endcsname{#2}}
+
+\newconstant\btxsetuptype
+
+% 0 = unknown darkred
+% 1 = cite darkblue
+% 2 = subcite darkgreen
+% 3 = numbering darkorange
+% 4 = list darkcyan
+% 5 = page darkmagenta
+% 6 = unknown darkred
+
+\protected\def\publ_fast_btx_setup_chain_inbetween{\allowbreak->\allowbreak}
+\protected\def\publ_fast_btx_setup_colon_inbetween{\allowbreak :\allowbreak}
+
+\protected\def\publ_fast_btx_setup_chain_yes#1#2%
+ {\dontleavehmode\begingroup
+ \let\:\publ_fast_btx_setup_colon_inbetween
+ \infofont
+ \ifcase\btxsetuptype\darkred\or\darkblue\or\darkgreen\or\darkcyan\or\darkmagenta\else\darkred\fi
+ [\prewordbreak
+ \currentbtxspecification \:#1\:#2\ifcsname\??setup:\s!btx:\currentbtxspecification:#1:#2\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ \currentbtxspecificationfallback\:#1\:#2\ifcsname\??setup:\s!btx:\currentbtxspecificationfallback:#1:#2\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ #1\:#2\ifcsname\??setup:\s!btx:#1:#2\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ \currentbtxspecification \:#1\:\s!unknown\ifcsname\??setup:\s!btx:\currentbtxspecification:#1:\s!unknown\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ \currentbtxspecificationfallback\:#1\:\s!unknown\ifcsname\??setup:\s!btx:\currentbtxspecificationfallback:#1:\s!unknown\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ unset\fi\fi\fi\fi\fi
+ \space @\space
+ \currentbtx
+ \prewordbreak]%
+ \endgroup}
+
+\protected\def\publ_fast_btx_setup_chain_nop#1#2%
+ {\dontleavehmode\begingroup
+ \let\:\publ_fast_btx_setup_colon_inbetween
+ \infofont
+ \darkred
+ [\prewordbreak
+ \currentbtxspecification\:#1\:#2\ifcsname\??setup:\s!btx:\currentbtxspecification:#1:#2\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ #1\:#2\ifcsname\??setup:\s!btx:#1:#2\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ \currentbtxspecification\:#1\:\s!unknown\ifcsname\??setup:\s!btx:\currentbtxspecification:#1:\s!unknown\endcsname\else
+ \publ_fast_btx_setup_chain_inbetween
+ unset\fi\fi\fi
+ \space @\space
+ \currentbtx
+ \prewordbreak]%
+ \endgroup}
+
+\protected\def\publ_fast_btx_setup_normal#1%
+ {\btxsetuptype#1\relax
+ \ifempty\currentbtxspecificationfallback
+ \expandafter\publ_fast_setup_nop
+ \else
+ \expandafter\publ_fast_setup_yes
+ \fi}
+
+\protected\def\publ_fast_btx_setup_visual#1#2#3%
+ {\btxsetuptype#1\relax
+ \ifempty\currentbtxspecificationfallback
+ \expandafter\publ_fast_btx_setup_chain_nop
+ \else
+ \expandafter\publ_fast_btx_setup_chain_yes
+ \fi{#2}{#3}%
+ \ifempty\currentbtxspecificationfallback
+ \expandafter\publ_fast_setup_nop
+ \else
+ \expandafter\publ_fast_setup_yes
+ \fi{#2}{#3}}
+
+\installtextracker
+ {publications.setups}
+ {\let\publ_fast_setup\publ_fast_btx_setup_visual}
+ {\let\publ_fast_setup\publ_fast_btx_setup_normal}
+
+\let\publ_fast_setup\publ_fast_btx_setup_normal
+
+%D Cite helpers:
+
+\newtoks\everybtxciteentry
+
+\prependtoks
+ \setfalse\c_btx_cite_reference_injected
+\to \everybtxciteentry
+
+\permanent\protected\def\btxcitesetup#1%
+ {\the\everybtxciteentry
+ \everybtxciteentry\emptytoks % tricky maybe not when subcites
+ \publ_fast_setup\plusone\s!cite{#1}} % no \btxcitereset as we loose dataset and such
+
+\permanent\protected\def\btxsubcitesetup#1%
+ {\the\everybtxciteentry
+ \everybtxciteentry\emptytoks % tricky maybe not when subcites
+ \publ_fast_setup\plustwo\s!cite{#1}} % no \btxcitereset as we loose dataset and such
+
+\appendtoks
+ \btx_check_language
+\to \everybtxciteentry
+
+\permanent\protected\def\btxstartsubcite#1%
+ {\begingroup
+ \btx_reset_cite % todo: limited set
+ % \saverunningstyleandcolor % let's see when Alan needs it
+ \def\currentbtxcitealternative{#1}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \usebtxstyleandcolor\c!style\c!color
+ \btxparameter\c!left
+ \relax}
+
+\permanent\protected\def\btxstopsubcite
+ {\relax
+ \btxparameter\c!right
+ \endgroup}
+
+\permanent\protected\def\btxstartciterendering[#1]%
+ {\begingroup
+ \edef\currentbtxcitealternative{#1}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \usebtxstyleandcolor\c!style\c!color
+ \btxparameter\c!left
+ \relax}
+
+\permanent\protected\def\btxstopciterendering
+ {\relax
+ \btxparameter\c!right
+ \endgroup}
+
+\aliased\let\btxstartciteauthor\begingroup
+\aliased\let\btxstopciteauthor \endgroup
+
+\permanent\protected\def\btxstartcite{\begingroup\btx_reset_cite}
+\aliased \let\btxstopcite \endgroup
+
+%D Whatever helpers:
+
+\permanent\protected\def\btxsingularplural#1{\clf_btxsingularorplural{\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\protected\def\btxoneorrange #1{\clf_btxoneorrange {\currentbtxdataset}{\currentbtxtag}{#1}}
+\permanent\protected\def\btxfirstofrange #1{\clf_btxfirstofrange {\currentbtxdataset}{\currentbtxtag}{#1}}
+
+\aliased\let\btxsingularorplural\btxsingularplural
+
+\stopcontextdefinitioncode
+
+%D Journals
+
+\permanent\protected\def\btxloadjournallist [#1]{\clf_btxloadjournallist{#1}}
+\permanent\protected\def\btxsavejournallist [#1]{\clf_btxsavejournallist{#1}}
+\permanent\protected\def\btxaddjournal [#1][#2]{\clf_btxaddjournal{#1}{#2}}
+\permanent \def\btxexpandedjournal #1{\clf_btxexpandedjournal{#1}} % \protected ?
+\permanent \def\btxabbreviatedjournal#1{\clf_btxabbreviatedjournal{#1}} % \protected ?
+
+% \installcorenamespace{btxjournal}
+%
+% \letvalue{\s!btxjournal\v!long }\btxexpandedjournal
+% \letvalue{\s!btxjournal\v!short }\btxabbreviatedjournal
+% \letvalue{\s!btxjournal\v!normal}\firstofoneargument
+%
+% \protected\def\btxcheckedjournal
+% {\expandnamespaceparameter\s!btxjournal\btxrenderingparameter\c!journalconversion}
+
+% \btxloadjournallist[list.txt] % Foo Journal of Bars = FBJ \n ....
+%
+% \btxexpandedjournal[fbj]
+% \btxabbreviatedjournal[foo journal of bars]
+
+%D Saving data:
+
+\tolerant\permanent\protected\def\savebtxdataset[#1]#*[#2]#*[#3]
+ {\ifarguments
+ % bad news
+ \or
+ \ifhastok={#1}%
+ \publ_save_dataset_indeed[\s!default][\jobname-saved.bib][#1]%
+ \else
+ \publ_save_dataset_indeed[\s!default][#1][]%
+ \fi
+ \or
+ \ifhastok={#2}%
+ \publ_save_dataset_indeed[\s!default][#1][#2]%
+ \else
+ \publ_save_dataset_indeed[#1][#2][]%
+ \fi
+ \or
+ \publ_save_dataset_indeed[#1][#2][#3]%
+ \fi}
+
+\protected\def\publ_save_dataset_indeed[#1][#2][#3]%
+ {\begingroup
+ \getdummyparameters
+ [\c!criterium=\v!all,%
+ \c!type=,%
+ \c!dataset=#1,%
+ \c!file=#2,%
+ #3]% % all or used
+ \clf_btxsavedataset
+ dataset {\dummyparameter\c!dataset}%
+ filename {\dummyparameter\c!file}%
+ filetype {\dummyparameter\c!type}%
+ criterium {\dummyparameter\c!criterium}%
+ \relax
+ \endgroup}
+
+% \savebtxdataset[default][e:/tmp/foo.bib]
+% \savebtxdataset[default][e:/tmp/foo.lua]
+% \savebtxdataset[default][e:/tmp/foo.xml]
+
+%D In-text entries:
+
+\permanent\protected\def\placecitation{\citation[entry]} % [#1]
+
+\permanent\protected\def\btxhandleciteentry
+ {\dontleavehmode
+ \begingroup
+ \def\currentbtxcitealternative{entry}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative % needs checking
+ \btxcitereference
+ \btx_entry_inject
+ \endgroup}
+
+%D Registers
+
+% \setupbtxregister
+% [\c!state=\v!start,
+% \c!dataset=\v!all,
+% \c!method=\v!always]
+
+\protected\def\publ_registers_set
+ {\ifempty\currentbtxregister \else
+ \clf_btxsetregister
+ specification {\currentbtxspecification}%
+ name {\currentbtxregister}%
+ state {\btxregisterparameter\c!state}%
+ dataset {\btxregisterparameter\c!dataset}%
+ field {\btxregisterparameter\c!field}%
+ register {\btxregisterparameter\c!register}%
+ method {\btxregisterparameter\c!method}%
+ alternative {\btxregisterparameter\c!alternative}%
+ \relax
+ \fi}
+
+\appendtoks
+ \publ_registers_set
+\to \everydefinebtxregister
+
+\appendtoks
+ \publ_registers_set
+\to \everysetupbtxregister
+
+\appendtoks
+ \normalexpanded{%
+ \defineprocessor
+ [\s!btx:r:\currentbtxregister]%
+ [\c!style=\noexpand\namedbtxregisterparameter{\currentbtxregister}\noexpand\c!style,
+ \c!color=\noexpand\namedbtxregisterparameter{\currentbtxregister}\noexpand\c!color]}%
+\to \everydefinebtxregister
+
+\appendtoks
+ \clf_btxtoregister{\currentbtxdataset}{\currentbtxtag}%
+\to \t_btx_reference_inject
+
+\permanent\protected\def\btxindexedauthor#1#2#3#4#5#6% alternative von last initials first junior
+ {\begingroup
+ \def\currentbtxcitealternative{#1}%
+ \ifempty\currentbtxcitealternative
+ \edef\currentbtxcitealternative{invertedshort}% maybe we need some default here too?
+ \fi
+ %let\currentbtxlistvariant\currentbtxcitealternative % we inherit
+ \the\everysetupbtxciteplacement
+ \def\currentbtxvons {#2}%
+ \def\currentbtxsurnames {#3}%
+ \def\currentbtxinitials {#4}%
+ \def\currentbtxfirstnames {#5}%
+ \def\currentbtxjuniors {#6}%
+ \setbtxparameterset\s!cite\currentbtxcitealternative
+ \fastsetup{\s!btx:\s!cite:\s!author:\currentbtxcitealternative}%
+ \endgroup}
+
+\permanent\protected\def\btxregisterauthor
+ {\doifelsenextoptionalcs\publ_register_author_yes\publ_register_author_nop}
+
+\def\publ_register_author_yes[#1]#2%
+ {\clf_btxauthortoregister{#1}{#2}\relax}
+
+\def\publ_register_author_nop#1%
+ {\clf_btxauthortoregister{\currentbtxdataset}{#1}\relax}
+
+%D We hook some setters in the definition sets:
+
+% \installdefinitionsetmember \??btx {btxspecification} \??btxcitevariant {btxcitevariant}
+% \installdefinitionsetmember \??btx {btxspecification} \??btxlistvariant {btxlistvariant}
+% \installdefinitionsetmember \??btx {btxspecification} \??btxlist {btxlist}
+% \installdefinitionsetmember \??btx {btxspecification} \??btxrendering {btxrendering}
+% \installdefinitionsetmember \??btx {btxspecification} \??btx {btx}
+
+%D And more helpers ... a never ending story these publications:
+
+% \definebtx
+% [btx:apa:list:article:title]
+% [style=bolditalic,
+% command=\WORD]
+%
+% \btxstartstyle[btx:apa:list:article:title]
+% \btxusecommand[btx:apa:list:article:title]{foo}
+% \btxstopstyle
+
+\mutable\let\m_btx_saved_current\empty
+
+\permanent\protected\def\btxstartstyle[#1]%
+ {\begingroup
+ \let\m_btx_saved_current\currentbtx
+ \def\currentbtx{#1}%
+ \usebtxstyleparameter\c!style
+ \let\currentbtx\m_btx_saved_current}
+
+\permanent\protected\def\btxstartcolor[#1]%
+ {\begingroup
+ \let\m_btx_saved_current\currentbtx
+ \def\currentbtx{#1}%
+ \usebtxcolorparameter\c!color
+ \let\currentbtx\m_btx_saved_current}
+
+\permanent\protected\def\btxstartstyleandcolor[#1]%
+ {\begingroup
+ \let\m_btx_saved_current\currentbtx
+ \def\currentbtx{#1}%
+ \usebtxstyleandcolor\c!style\c!color
+ \let\currentbtx\m_btx_saved_current}
+
+\aliased\let\btxstopstyle \endgroup
+\aliased\let\btxstopcolor \endgroup
+\aliased\let\btxstopstyleandcolor\endgroup
+
+\permanent\protected\def\btxusecommand[#1]#2% using #2 permits space after []
+ {\namedbtxparameter{#1}\c!command{#2}}
+
+\permanent\protected\def\startbtxrunningstyleandcolor
+ {\dontleavehmode
+ \begingroup
+ \restorerunningstyleandcolor}
+
+\permanent\protected\def\stopbtxrunningstyleandcolor
+ {\endgroup}
+
+%D Maybe handy:
+
+\permanent\protected\def\btxdoifelsematches#1#2#3%
+ {\clf_btxdoifelsematches{#1}{#2}{#3}}
+
+%D Defaults:
+
+\setupbtxrendering
+ [\c!interaction=\v!start, % \v!all
+ \c!specification=\btxparameter\c!specification,
+ \c!dataset=\v!default,
+ \c!repeat=\v!no,
+ \c!continue=\v!no,
+ \c!method=\v!global,
+ % \c!setups=btx:\btxrenderingparameter\c!alternative:initialize, % not the same usage as cite !
+ \c!sorttype=\v!default,
+ \c!criterium=\v!text,
+ \c!refcommand=authoryears, % todo
+ \c!numbering=\v!yes,
+ %\c!saveinlist=\v!no, % maybe for before/after
+ \c!pagestate=\v!stop,
+ \c!textstate=\v!start,
+ \c!width=\v!auto,
+ \c!separator={\removepunctuation;\space},
+ \c!distance=1.5\emwidth]
+
+% Quite some interpunction and labels are the same of at least consistent within
+% a standard when citations and list entries are involved. We assume that each
+% standard defines its own set but it can fall back on these defaults.
+
+\setupbtx
+ [\c!interaction=\v!start,
+ \c!alternative=num, % default cite form, normally defined in the cite namespace
+ \c!inbetween=\btxspace,
+ % \c!range=\endash, % separator:range?
+ \c!range=\directdiscretionary\endash,
+ \c!compress=\v!yes, % was no?
+ \c!authorconversion=normal,
+ \c!sorttype=normal, % normal, reverse or none
+ \c!etallimit=3,
+ \c!etaldisplay=\btxparameter\c!etallimit,
+ \c!otherstext={\btxspace et al.},
+ \c!separator:firstnames={\btxspace},
+ \c!separator:juniors={\btxspace},
+ \c!separator:vons={\btxspace},
+ \c!separator:initials={\btxspace},
+ \c!stopper:initials={.},
+ %\c!surnamesep={\btxcomma}, % is this used anywhere?
+ \c!separator:invertedinitials={\btxcomma},
+ \c!separator:invertedfirstnames={\btxcomma},
+ \c!separator:names:2={\btxcomma}, % separates multiple names
+ \c!separator:names:3=\btxparameter{\c!separator:2}, % before last name in a list
+ \c!separator:names:4=\btxparameter{\c!separator:2}, % between only two names
+ \c!separator:2={\btxsemicolon}, % aka pubsep - separates multiple objects
+ \c!separator:3=\btxparameter{separator:2}, % before last object in a list
+ \c!separator:4=\btxparameter{separator:2}] % between only two objects
+
+% Do we want these in the format? Loading them delayed is somewhat messy.
+
+\loadbtxdefinitionfile[commands]
+\loadbtxdefinitionfile[definitions]
+
+\loadbtxdefinitionfile[cite]
+\loadbtxdefinitionfile[list]
+\loadbtxdefinitionfile[page]
+\loadbtxdefinitionfile[author]
+
+% we assume that the users sets up the right specification and if not ... well,
+% hope for the best that something shows up and consult the manual otherwise
+
+\permanent\protected\def\usebtxdefinitions[#1]%
+ {\loadbtxdefinitionfile[#1]% % for hh
+ \setupbtx[\c!specification=#1]} % for ab
+
+\setupbtx
+ [\c!specification=\s!default,
+ \c!dataset=\v!default,
+ \c!default=\v!default]
+
+\loadbtxdefinitionfile
+ [\s!default]
+
+%D Delayed loading:
+
+\fetchmodulecommand \showbtxdatasetfields \f!publ_tra
+\fetchmodulecommand \showbtxdatasetcompleteness \f!publ_tra
+\fetchmodulecommand \showbtxdatasetauthors \f!publ_tra
+\fetchmodulecommand \showbtxhashedauthors \f!publ_tra
+\fetchmodulecommand \showbtxfields \f!publ_tra
+\fetchmodulecommand \showbtxtables \f!publ_tra
+
+%D Some potential crap:
+%D
+%D Because I consider this bad data management and a weird mix of languages only one
+%D accessor is provided.
+
+\permanent\tolerant\protected\def\btxshortcut[#1]#:#2%
+ {\clf_btxshortcut{\ifparameter#1\or#1\else\s!default\fi}{#2}}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/publ-old.mkxl b/Master/texmf-dist/tex/context/base/mkxl/publ-old.mkxl
new file mode 100644
index 00000000000..e5cc98a7448
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/publ-old.mkxl
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=publ-old,
+%D version=2013.12.24,
+%D title=\CONTEXT\ Publication Support,
+%D subtitle=Old Fashioned \BIBTEX,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+% No protection because we have a module that overloads them!
+
+\mutable\protected\def\setupbibtex {\usemodule[oldbibtex]\setupbibtex}
+\mutable\protected\def\setuppublications {\usemodule[oldbibtex]\setuppublications}
+\mutable\protected\def\setuppublicationlist{\usemodule[oldbibtex]\setuppublicationlist}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/publ-xml.mkxl b/Master/texmf-dist/tex/context/base/mkxl/publ-xml.mkxl
new file mode 100644
index 00000000000..74fb47e08bd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/publ-xml.mkxl
@@ -0,0 +1,111 @@
+%D \module
+%D [ file=publ-xml,
+%D version=2013.12.24,
+%D title=\CONTEXT\ Publication Support,
+%D subtitle=XML,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Publication Support / XML}
+
+\unprotect
+
+\permanent\tolerant\protected\def\convertbtxdatasettoxml[#1]%
+ {\clf_convertbtxdatasettoxml{\ifparameter#1\or#1\else\s!default\fi}} % or current when not empty
+
+% \startxmlsetups btx:initialize
+% \xmlregistereddocumentsetups{#1}{}
+% \xmlsetsetup{#1}{bibtex|entry|field}{btx:*}
+% \xmlmain{#1}
+% \stopxmlsetups
+
+\startxmlsetups btx:initialize
+ \xmlsetsetup{#1}{bibtex|entry|field}{btx:*}
+ \xmlmain{#1}
+\stopxmlsetups
+
+% \startxmlsetups btx:entry
+% \xmlflush{#1}
+% \stopxmlsetups
+
+\startxmlsetups btx:field
+ \xmlflushcontext{#1}
+\stopxmlsetups
+
+\protect \endinput
+
+% \startxmlsetups bibtex:entry:getkeys
+% \xmladdsortentry{bibtex}{#1}{\xmlfilter{#1}{/field[@name='author']/text()}}
+% \xmladdsortentry{bibtex}{#1}{\xmlfilter{#1}{/field[@name='year' ]/text()}}
+% \xmladdsortentry{bibtex}{#1}{\xmlatt{#1}{tag}}
+% \stopxmlsetups
+
+% \startbuffer
+% \startxmlsetups xml:bibtex:sorter
+% \xmlresetsorter{bibtex}
+% % \xmlfilter{#1}{entry/command(bibtex:entry:getkeys)}
+% \xmlfilter{#1}{
+% bibtex
+% /entry[@category='article']
+% /field[@name='author' and find(text(),'Knuth')]
+% /../command(bibtex:entry:getkeys)}
+% \xmlsortentries{bibtex}
+% \xmlflushsorter{bibtex}{bibtex:entry:flush}
+% \stopxmlsetups
+% \stopbuffer
+
+% \bgroup
+% \setups[bibtex-commands]
+% \getbuffer
+% \egroup
+
+% \startxmlsetups bibtex:entry:flush
+% \xmlfilter{#1}{/field[@name='author']/context()} / %
+% \xmlfilter{#1}{/field[@name='year' ]/context()} / %
+% \xmlatt{#1}{tag}\par
+% \stopxmlsetups
+
+% \startpacked
+% \getbuffer
+% \stoppacked
+
+
+% \protected\def\btx_xml_list_handle_entry
+% {\begingroup
+% \ignorespaces
+% \xmlfilter{btx:\currentbtxrendering}{/bibtex/entry[@tag='\currentbtxtag']/command(btx:format)}%
+% \removeunwantedspaces
+% \endgroup}
+
+% \startxmlsetups btx:format
+% \btxlistparameter\c!before\relax % prevents lookahead
+% \edef\currentbibxmlnode {#1}
+% \edef\currentbibxmltag {\xmlatt{#1}{tag}}
+% \edef\currentbtxcategory{\xmlatt{#1}{category}}
+% \ignorespaces
+% \xmlcommand{#1}{.}{btx:\currentbtxformat:\currentbibxmlcategory}
+% \removeunwantedspaces
+% \btxlistparameter\c!after\relax % prevents lookahead
+% \stopxmlsetups
+
+% \startxmlsetups btx:list
+% \xmlfilter{#1}{/bibtex/entry/command(bibtex:format)}
+% \stopxmlsetups
+
+% \startxmlsetups btx:btx
+% \xmlfilter{#1}{/entry/command(btx:format)}
+% \stopxmlsetups
+
+% \protected\def\btx_xml_doifelse#1{\xmldoifelse\currentbibxmlnode{/field[@name='#1']}}
+% \protected\def\btx_xml_doif #1{\xmldoif \currentbibxmlnode{/field[@name='#1']}}
+% \protected\def\btx_xml_doifnot #1{\xmldoifnot \currentbibxmlnode{/field[@name='#1']}}
+% \def\btx_xml_flush #1{\xmlcontext \currentbibxmlnode{/field[@name='#1']}}
+% \def\btx_xml_setup {\xmlsetup \currentbibxmlnode} % {#1}
+% \protected\def\btx_xml_todo #1{[#1]}
+
+% \xmlfilter{#1}{/field[@name='\currentbtxfield']/btxconcat('\currentbtxfield')}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/regi-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/regi-ini.mkxl
new file mode 100644
index 00000000000..515b6afd6f9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/regi-ini.mkxl
@@ -0,0 +1,37 @@
+%D \module
+%D [ file=regi-ini,
+%D version=2005.04.25,
+%D title=\CONTEXT\ Regime Macros,
+%D subtitle=Regime Support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{regi-ini}{}
+
+\unprotect
+
+%D \macros
+%D {enableregime,disableregime,
+%D startregime,stopregime,
+%D currentregime}
+%D
+%D Beware, the enable and disable commands are global switches, so best use the
+%D start|/|stop commands.
+
+\mutable\let\currentregime\empty
+
+\permanent\protected\def\enableregime[#1]{\clf_enableregime{#1}}
+\permanent\protected\def\disableregime {\clf_disableregime}
+\permanent\protected\def\startregime [#1]{\clf_startregime{#1}}
+\permanent\protected\def\stopregime {\clf_stopregime}
+
+% only for diagnostics:
+
+% \def\codepagename#1{\cldcontext{os.tocodepage("#1")}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-bar.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-bar.mklx
new file mode 100644
index 00000000000..4cfcbde817e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-bar.mklx
@@ -0,0 +1,404 @@
+%D \module
+%D [ file=scrn-bar, % was part of scrn-int
+%D version=1995.01.01,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Progress Bars,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Screen Macros / Progress Bars}
+
+\unprotect
+
+%D The code is a bit upgraded to \MKIV\ but the output is mostly the same. In
+%D retrospect this should have been a module. We can move some definitions to
+%D scrn-run-bar.mkiv if needed. We can also make the code a bit more efficient.
+
+% todo: replace blackrule by stupid rules
+
+%D \starttyping
+%D \setupinteraction
+%D [state=start]
+%D
+%D \setupsubpagenumber
+%D [state=start]
+%D
+%D \setuplayout
+%D [middle]
+%D
+%D \setuppapersize
+%D [S4][S4]
+%D
+%D \startsetups bars
+%D \ruledvbox to \textheight \bgroup
+%D a \ruledhbox{\interactionbar[a]}\vss
+%D b \ruledhbox{\interactionbar[b]}\vss
+%D c \ruledhbox{\interactionbar[c]}\vss
+%D d \ruledhbox{\interactionbar[d]}\vss
+%D e \ruledhbox{\interactionbar[e]}\vss
+%D f \ruledhbox{\interactionbar[f]}\vss
+%D g \ruledhbox{\interactionbar[g]}\vss
+%D \egroup
+%D \stopsetups
+%D
+%D \setuptexttexts[\setups{bars}]
+%D
+%D \starttext
+%D \dorecurse {12} {
+%D \startstandardmakeup
+%D \stopstandardmakeup
+%D }
+%D \stoptext
+%D \stoptyping
+
+\installcorenamespace{interactionbar}
+
+\installframedcommandhandler \??interactionbar {interactionbar} \??interactionbar
+
+\permanent\tolerant\protected\def\interactionbar[#tag]#spacer[#settings]% somewhat messy
+ {\iflocation
+ \begingroup
+ \ifhastok={#tag}%
+ \let\currentinteractionbar\empty
+ \setupcurrentinteractionbar[#tag]%
+ \edef\currentinteractionbar{\interactionbarparameter\c!alternative}%
+ \else
+ \edef\currentinteractionbar{#tag}%
+ \setupcurrentinteractionbar[#settings]%
+ \fi
+ \doif{\interactionbarparameter\c!state}\v!start
+ {\interactionbarparameter\c!command}%
+ \endgroup
+ \fi}
+
+\newdimen\d_scrn_bar_width
+\newdimen\d_scrn_bar_height
+\newdimen\d_scrn_bar_depth
+\newdimen\d_scrn_bar_distance
+
+%D Interaction buttons, in fact a row of tiny buttons, are typically only used for
+%D navigational purposed. The next macro builds such a row based on a specification
+%D list.
+%D
+%D \startbuffer
+%D \interactionbuttons[width=\hsize][page,PreviousJump,ExitViewer]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D gives
+%D
+%D \getbuffer
+%D
+%D Apart from individual entries, one can use \type{page} and \type {subpage} as
+%D shortcuts to their four associated buttons. The symbols are derived from the
+%D symbols linked to the entries.
+
+\permanent\tolerant\protected\def\interactionbuttons[#settings]#spacer[#list]%
+ {\iflocation
+ \ifcstok{\interactionbarparameter\c!state}\v!start
+ \ifparameter#list\or
+ \scrn_bar_buttons_indeed{#settings}{#list}%
+ \else
+ \scrn_bar_buttons_indeed{}{#settings}%
+ \fi
+ \fi
+ \fi}
+
+\def\scrn_bar_buttons_indeed#settings#list%
+ {\begingroup
+ %\let\currentinteractionbar\empty
+ \setupcurrentinteractionbar[#settings]%
+ \d_scrn_bar_width \interactionbarparameter\c!width\relax
+ \d_scrn_bar_distance\interactionbarparameter\c!distance\relax
+ \ifzeropt\d_scrn_bar_width
+ \d_scrn_bar_width1.5\emwidth
+ \fi
+ \doifnothing{\interactionbarparameter\c!height}{\letinteractionbarparameter\c!height\v!broad}%
+ \doifnothing{\interactionbarparameter\c!depth }{\letinteractionbarparameter\c!depth\zeropoint}%%%
+ \letinteractionbarparameter\c!background\empty
+ \setbox2\hbox{\inheritedinteractionbarframed{\symbol[\interactionparameter\c!symbolset][\v!previouspage]}}%
+ \scratchheight\ht2 % needed because we default to nothing
+ \letinteractionbarparameter\c!strut\v!no
+ % \letinteractionparameter\c!width\zeropoint
+ \scratchcounterone\zerocount % new, was 1
+ \processallactionsinset
+ [#list]
+ [ \v!page=>\advance\scratchcounterone\plusfour,
+ \v!subpage=>\advance\scratchcounterone\plusfour,
+ \s!unknown=>\advance\scratchcounterone\plusone]%
+ \ifzeropt\d_scrn_bar_width
+ \scratchdimenone\dimexpr2\emwidth+\d_scrn_bar_distance\relax
+ \scratchdimentwo\dimexpr\scratchcounterone\scratchdimenone-\d_scrn_bar_distance\relax
+ \else
+ \scratchdimenone\d_scrn_bar_width
+ \scratchdimentwo\dimexpr\scratchcounterone\d_scrn_bar_distance-\d_scrn_bar_distance\relax
+ \advance\scratchdimenone -\scratchdimentwo
+ \divide\scratchdimenone \scratchcounterone
+ \scratchdimentwo\d_scrn_bar_width
+ \fi
+ \hbox to \scratchdimentwo
+ {\setnostrut
+ \startsymbolset[\interactionparameter\c!symbolset]%
+ \setupbuttons
+ [#settings,%
+ \c!height=\the\scratchheight,%
+ \c!width=\the\scratchdimenone]%
+ \processallactionsinset
+ [#list]
+ [ \v!page=>\scrn_bar_goto\v!firstpage
+ \scrn_bar_goto\v!nextpage
+ \scrn_bar_goto\v!previouspage
+ \scrn_bar_goto\v!lastpage,
+ \v!subpage=>\scrn_bar_goto\v!firstsubpage
+ \scrn_bar_goto\v!nextsubpage
+ \scrn_bar_goto\v!previoussubpage
+ \scrn_bar_goto\v!lastsubpage,
+ \s!unknown=>\scrn_bar_goto\commalistelement]%
+ \unskip
+ \stopsymbolset}%
+ \endgroup}
+
+\def\scrn_bar_goto#action%
+ {\button{\symbol[#action]}[#action]\hss}
+
+% todo: this will be \letblackruleparameter\c!width\scratchdimenone (faster)
+% todo: make these setups
+
+\def\scrn_bar_alternative_a
+ {\d_scrn_bar_width \interactionbarparameter\c!width
+ \d_scrn_bar_distance\interactionbarparameter\c!distance
+ \d_scrn_bar_height \interactionbarparameter\c!height
+ \d_scrn_bar_depth \interactionbarparameter\c!depth
+ \noindent\hbox to \d_scrn_bar_width \bgroup
+ \dontcomplain
+ \setupblackrules[\c!height=\v!max,\c!depth=\v!max]%
+ \scratchdimentwo\dimexpr\d_scrn_bar_width-4\emwidth\relax
+ \processaction
+ [\interactionbarparameter\c!step]
+ [ \v!small=>\scratchcounter 20,
+ \v!medium=>\scratchcounter 10,
+ \v!big=>\scratchcounter 5,
+ \s!unknown=>\scratchcounter 10]%
+ \scratchdimenone\dimexpr\scratchdimentwo/\scratchcounter\relax
+ \setupblackrules[\c!width=\scratchdimenone]%
+ \setbox\scratchbox\hbox to \d_scrn_bar_width
+ {\hskip2\emwidth
+ \setbox\scratchbox\hpack{\blackrule[\c!color=\interactionbarparameter\c!backgroundcolor]}%
+ \dorecurse\scratchcounter
+ {\hss\normalexpanded{\directgotodumbbox{\copy\scratchbox}[page(\the\numexpr\recurselevel*\lastpage/\scratchcounter\relax)]}}%
+ \hss
+ \hskip2\emwidth}%
+ \wd\scratchbox\zeropoint
+ \box \scratchbox
+ \setupblackrules[\c!width=\emwidth]%
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!firstpage]}%
+ \hskip\emwidth
+ \ifnum\realpageno>\plusone
+ \hskip\zeropoint\s!plus\numexpr\realpageno-\plustwo\relax \s!sp\relax % cm gives overflow
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!previouspage)]}%
+ \fi
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[page(\number\realpageno)]}% todo: \v!currentpage
+ \ifnum\realpageno<\lastpage\relax
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!nextpage]}%
+ \hskip\zeropoint\s!plus\numexpr\lastpage-\realpageno-\plusone\relax \s!sp\relax % cm gives overflow
+ \fi
+ \hskip\emwidth
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!lastpage]}%
+ \egroup}
+
+\def\scrn_bar_alternative_b
+ {\ifnum\lastpage>\firstpage\relax
+ \interactionbuttons[\v!firstpage,\v!previouspage,\v!nextpage,\v!lastpage]%
+ \fi}
+
+\def\scrn_bar_alternative_c
+ {\ifnum\lastpage>\plusone
+ \d_scrn_bar_width\interactionbarparameter\c!width
+ \hbox to \d_scrn_bar_width
+ {\setupblackrules[\c!height=\interactionbarparameter\c!height,\c!depth=\interactionbarparameter\c!depth,\c!width=\emwidth]%
+ \scratchdimen\dimexpr(\d_scrn_bar_width-4\emwidth)/\numexpr\lastpage+\minusone\relax\relax
+ \scratchdimenone\numexpr\realpageno+\minusone\relax\scratchdimen
+ \scratchdimentwo\numexpr\lastpage-\realpageno\relax\scratchdimen
+ \directgotospecbox\interactionbarparameter{\blackrule}[\v!firstpage]%
+ \hss
+ \directgotospecbox\interactionbarparameter{\blackrule[\c!width=\scratchdimenone]}[\v!previouspage]%
+ \blackrule[\c!color=\interactionbarparameter\c!contrastcolor]%
+ \directgotospecbox\interactionbarparameter{\blackrule[\c!width=\scratchdimentwo]}[\v!nextpage]%
+ \hss
+ \directgotospecbox\interactionbarparameter{\blackrule}[\v!lastpage]}%
+ \fi}
+
+\protected\def\scrn_bar_goto_a#whereto%
+ {\symbol[\ifcase#whereto\v!previous\or\v!somewhere\or\v!next\fi]}
+
+\protected\def\scrn_bar_goto_b#whereto%
+ {\vrule\s!height\d_scrn_bar_height\s!depth\d_scrn_bar_depth\s!width\scratchdimenone\relax}
+
+\protected\def\scrn_bar_goto_c#whereto%
+ {\symbol[\ifcase#whereto\v!previous\or\v!somewhere\or\v!somewhere\or\v!somewhere\or\v!next\fi}
+
+\protected\def\scrn_bar_goto_d#whereto%
+ {\vrule \s!width\scratchdimenone \ifcase#whereto%
+ \s!height \d_scrn_bar_height \s!depth \d_scrn_bar_depth \or
+ \s!height.5\d_scrn_bar_height \s!depth.5\d_scrn_bar_depth \or
+ \s!height \d_scrn_bar_height \s!depth \d_scrn_bar_depth \or
+ \s!height.5\d_scrn_bar_height \s!depth.5\d_scrn_bar_depth \else
+ \s!height \d_scrn_bar_height \s!depth \d_scrn_bar_depth \fi}
+
+\newconstant\c_scrn_bar_mode
+
+\let\scrn_bar_goto_indeed\relax
+
+\protected\def\scrn_bar_goto_x#command%
+ {\doifelse{\interactionbarparameter\c!symbol}\v!yes
+ {\setupsymbolset[\interactionparameter\c!symbolset]%
+ \let\scrn_bar_goto_indeed\scrn_bar_goto_a}
+ {\let\scrn_bar_goto_indeed\scrn_bar_goto_b}%
+ \dorecurse\nofsubpages
+ %{\scratchcounter\numexpr\recurselevel+\firstsubpage+\minusone\relax
+ {\scratchcounter\therealsubpageno\recurselevel
+ \c_scrn_bar_mode
+ \ifnum\scratchcounter<\realpageno \zerocount \else
+ \ifnum\scratchcounter=\realpageno \plusone \else
+ \plustwo \fi\fi
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\scrn_bar_goto_indeed\c_scrn_bar_mode}[page(\the\scratchcounter)]}%
+ #command}%
+ \unskip}
+
+\def\scrn_bar_alternative_d
+ {\ifnum\nofsubpages>\plusone \doif{\namedcounterparameter\s!subpage\c!state}\v!start{%
+ \d_scrn_bar_width \interactionbarparameter\c!width
+ \d_scrn_bar_distance\interactionbarparameter\c!distance
+ \d_scrn_bar_height \interactionbarparameter\c!height
+ \d_scrn_bar_depth \interactionbarparameter\c!depth
+ \scratchdimenone\d_scrn_bar_width
+ \noindent\hbox{\scrn_bar_goto_x{\hskip\d_scrn_bar_distance}}% \hpack ?
+ }\fi}
+
+\def\scrn_bar_alternative_e
+ {\ifnum\nofsubpages>\plusone \doif{\namedcounterparameter\s!subpage\c!state}\v!start{%
+ \d_scrn_bar_width \interactionbarparameter\c!width
+ \d_scrn_bar_distance\interactionbarparameter\c!distance
+ \d_scrn_bar_height \interactionbarparameter\c!height
+ \d_scrn_bar_depth \interactionbarparameter\c!depth
+ \scratchdimentwo\dimexpr\nofsubpages\d_scrn_bar_distance-\d_scrn_bar_distance\relax % (n-1)
+ \scratchdimenone\dimexpr(\d_scrn_bar_width-\scratchdimentwo)/\nofsubpages\relax
+ \ifdim\scratchdimenone<\d_scrn_bar_distance
+ \scrn_bar_alternative_f
+ \else
+ \noindent\hbox to \d_scrn_bar_width{\scrn_bar_goto_x{\hss}\unskip}% \hpack ?
+ \fi
+ }\fi}
+
+\def\scrn_bar_alternative_f
+ {\ifnum\nofsubpages>\plusone \doif{\namedcounterparameter\s!subpage\c!state}\v!start{%
+ \d_scrn_bar_width \interactionbarparameter\c!width
+ \d_scrn_bar_distance\interactionbarparameter\c!distance
+ \d_scrn_bar_height \interactionbarparameter\c!height
+ \d_scrn_bar_depth \interactionbarparameter\c!depth
+ \noindent \hbox to \d_scrn_bar_width \bgroup
+ \doloop
+ {\scratchcounterthree\numexpr(\nofsubpages/\recurselevel)+\plusone\relax % rounding
+ \scratchdimentwo\d_scrn_bar_distance
+ \multiply\scratchdimentwo \scratchcounterthree
+ \advance\scratchdimentwo -\d_scrn_bar_distance
+ \scratchdimenone\d_scrn_bar_width
+ \advance\scratchdimenone -\scratchdimentwo
+ \divide\scratchdimenone \scratchcounterthree
+ \ifdim\scratchdimenone<\d_scrn_bar_distance\else
+ \scratchcountertwo\recurselevel
+ \exitloop
+ \fi}%
+ \ifnum\scratchcounterthree>\plusone
+ % this is not that well tested
+ \advance\scratchcounterthree \minustwo
+ \scratchdimenone-\d_scrn_bar_distance
+ \scratchdimenone\scratchcounterthree\scratchdimenone
+ \advance\scratchdimenone \d_scrn_bar_width
+ \advance\scratchcounterthree \plusone
+ \divide\scratchdimenone \scratchcounterthree
+ \fi
+ \doifelse{\interactionbarparameter\c!symbol}\v!yes
+ {\setupsymbolset[\interactionparameter\c!symbolset]%
+ \let\scrn_bar_goto_indeed\scrn_bar_goto_c}%
+ {\let\scrn_bar_goto_indeed\scrn_bar_goto_d}%
+ \scratchcounterthree\numexpr\realpageno-\plustwo\relax
+ \scratchcounterfour\numexpr\realpageno+\plustwo\relax
+ \ifnum\scratchcounterthree<\plusone \scratchcounterthree\plusone \fi
+ \scratchcounterfive\zerocount
+ \dostepwiserecurse\firstsubpage\lastsubpage\plusone
+ {\donefalse
+ \advance\scratchcounterfive \plusone
+ \ifnum\recurselevel=\firstsubpage\relax \donetrue \fi
+ \ifnum\recurselevel=\lastsubpage \relax \donetrue \fi
+ \scratchcountersix\therealsubpageno\recurselevel\relax
+ \c_scrn_bar_mode
+ \ifdone
+ \ifnum\scratchcountersix<\realpageno
+ \zerocount
+ \orelse\ifnum\scratchcountersix>\realpageno
+ \plustwo
+ \else
+ \plusfour
+ \fi
+ \else
+ \ifnum\scratchcounterfive=\scratchcountertwo
+ \ifnum\scratchcountersix<\realpageno
+ \plusone
+ \orelse\ifnum\scratchcountersix>\realpageno
+ \plusthree
+ \else
+ \plustwo
+ \fi
+ \else
+ \minusone
+ \fi
+ \fi
+ \ifnum\c_scrn_bar_mode<\zerocount\else
+ \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\scrn_bar_goto_indeed\c_scrn_bar_mode}[realpage(\the\scratchcountersix)]}%
+ \hss
+ \scratchcounterfive\zerocount
+ \fi}%
+ \unskip
+ \egroup
+ }\fi}
+
+\def\scrn_bar_alternative_g
+ {\ifnum\lastsubpage>\firstsubpage\relax % no test for state?
+ \interactionbuttons[\v!firstsubpage,\v!previoussubpage,\v!nextsubpage,\v!lastsubpage]%
+ \fi}
+
+\setupinteractionbar
+ [\c!state=\v!start,
+ \c!alternative=a,
+ \c!symbol=\v!no,
+ \c!width=10\emwidth,
+ \c!height=.5\emwidth,
+ \c!depth=\zeropoint,
+ \c!distance=.5\emwidth,
+ \c!step=\v!medium,
+ \c!foregroundcolor=\interactionbarparameter\c!color,
+ \c!foregroundstyle=\interactionbarparameter\c!style,
+ \c!color=\interactionparameter\c!color,
+ \c!contrastcolor=\interactionparameter\c!contrastcolor,
+ \c!style=,
+ \c!frame=\v!on,
+ \c!background=color,
+ \c!backgroundcolor=gray,
+ \c!samepage=\v!yes]
+
+\defineinteractionbar[a][\c!command=\scrn_bar_alternative_a]
+\defineinteractionbar[b][\c!command=\scrn_bar_alternative_b,\c!height=\v!broad]
+\defineinteractionbar[c][\c!command=\scrn_bar_alternative_c,\c!height=\v!max,\c!depth=\v!max]
+\defineinteractionbar[d][\c!command=\scrn_bar_alternative_d,\c!width=.5\emwidth]
+\defineinteractionbar[e][\c!command=\scrn_bar_alternative_e]
+\defineinteractionbar[f][\c!command=\scrn_bar_alternative_f]
+\defineinteractionbar[g][\c!command=\scrn_bar_alternative_g,\c!height=\v!broad]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-but.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-but.mklx
new file mode 100644
index 00000000000..9832ffbbb26
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-but.mklx
@@ -0,0 +1,1049 @@
+%D \module
+%D [ file=scrn-but, % moved code
+%D version=1995.01.01,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Interaction,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Screen Macros / Buttons}
+
+\registerctxluafile{scrn-but}{}
+
+%D This really needs testing in lmtx ...
+
+\unprotect
+
+%D Buttons are just what their names says: things that can be clicked (pushed) on.
+%D They are similar to \type{\goto}, except that the text argument is not
+%D interpreted. Furthermore one can apply anything to them that can be done with
+%D \type {\framed}.
+%D
+%D \startbuffer
+%D \button[width=3cm,height=1.5cm]{Exit}[ExitViewer]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D gives
+%D
+%D \getbuffer
+%D
+%D This command is formally specified as:
+%D
+%D \showsetup{button}
+%D
+%D The characteristics can be set with:
+%D
+%D \showsetup{setupbuttons}
+
+\installcorenamespace{button}
+\installcorenamespace{buttonlocation}
+
+\installframedcommandhandler \??button {button} \??button
+
+\aliased\let\setupbuttons\setupbutton
+
+\appendtoks
+ \frozen\instance\setuevalue\currentbutton{\scrn_button_direct{\currentbutton}}%
+\to \everydefinebutton
+
+\protected\def\scrn_button_direct#tag%
+ {\begingroup
+ \edef\currentbutton{#tag}%
+ \iflocation
+ \expandafter\scrn_button_direct_status
+ \else
+ \expandafter\scrn_button_direct_ignore
+ \fi}
+
+\def\scrn_button_direct_status
+ {\ifcstok{\buttonparameter\c!state}\v!start
+ \expandafter\scrn_button_direct_indeed
+ \else
+ \expandafter\scrn_button_direct_ignore
+ \fi}
+
+% empty=yes
+%
+% \button[settings]{}[action] % normally used at the tex end
+
+\tolerant\def\scrn_button_direct_indeed[#settings]#:#text[#action]%
+ {\setupcurrentbutton[#settings]%
+ % no \dontleavehmode as it will force a vbox to \hsize which then
+ % spoils a tight box
+ \hpack\bgroup
+ \scrn_button_make
+ \buttonparameter
+ \inheritedbuttonframed
+ \letbuttonparameter
+ \setbuttonparameter
+ {#text}%
+ {#action}%
+ \egroup
+ \endgroup}
+
+\tolerant\def\scrn_button_direct_ignore[#settings]#:#text[#destination]%
+ {\endgroup}
+
+\definebutton[button] % english
+
+\setupbuttons
+ [\c!state=\v!start,
+ \c!width=\v!fit,
+ \c!height=\v!broad,
+ \c!offset=0.25\emwidth,
+ \c!frame=\v!on,
+ \c!background=,
+ \c!backgroundcolor=,
+ \c!foregroundstyle=\buttonparameter\c!style,
+ \c!foregroundcolor=\buttonparameter\c!color,
+ \c!style=\interactionparameter\c!style,
+ \c!color=\interactionparameter\c!color,
+ \c!contrastcolor=\interactionparameter\c!contrastcolor,
+ \c!samepage=\v!yes,
+ \c!unknownreference=\v!yes,
+ \c!distance=\zeropoint] % for menubuttons
+
+%D \macros
+%D {overlaybutton}
+%D
+%D For converience we provide:
+%D
+%D \starttyping
+%D \overlaybutton[reference]
+%D \stoptyping
+%D
+%D This command can be used to define overlays an/or can be used in the whatevertext
+%D areas, like:
+%D
+%D \starttyping
+%D \defineoverlay[PrevPage][\overlaybutton{PrevPage}]
+%D \setupbackgrounds[page][background=PrevPage]
+%D \setuptexttexts[\overlaybutton{NextPage}]
+%D \stoptyping
+%D
+%D For practical reasons, this macro accepts square brackets as well as braces.
+
+\permanent\tolerant\protected\def\overlaybutton[#1]#;#=%
+ {\iflocation
+ \gotobox{\overlayfakebox}[#1]%
+ \fi}
+
+%D The renderers:
+
+\aliased\letcsname\??buttonlocation\v!yes \endcsname\zerocount
+\aliased\letcsname\??buttonlocation\v!empty \endcsname\plusone
+\aliased\letcsname\??buttonlocation\v!no \endcsname\plustwo
+\aliased\letcsname\??buttonlocation\v!none \endcsname\plusthree
+\aliased\letcsname\??buttonlocation\v!normal \endcsname\plusone % default
+\aliased\letcsname\??buttonlocation\s!default\endcsname\plusone % default
+\aliased\letcsname\??buttonlocation \endcsname\plusone % default
+
+\newconditional\c_scrn_button_skipped
+\newconstant \locationboxpagestate
+
+\def\scrn_button_make#currentparameter#inheritedframed#letparameter#setparameter#text#action%
+ {\begingroup
+ \c_attr_reference\attributeunsetvalue
+ \global\setfalse\c_scrn_button_skipped
+ \locationboxpagestate\csname\??buttonlocation#currentparameter\c!samepage\endcsname % ?? bt: todo
+ \doifelsereferencefound{#action}\scrn_button_make_yes\scrn_button_make_nop
+ #currentparameter%
+ #inheritedframed%
+ #letparameter%
+ #setparameter
+ {#text}%
+ \endgroup}
+
+\def\scrn_button_make_yes
+ {\scratchcounter\referencerealpage\relax % called in otr
+ \ifcase\scratchcounter
+ \expandafter\scrn_button_make_normal % no state : something else than a page reference
+ \orelse\ifnum\scratchcounter=\realpageno
+ \expandafter\scrn_button_make_yes_same
+ \else
+ \expandafter\scrn_button_make_yes_other
+ \fi}
+
+\def\scrn_button_make_yes_same
+ {\ifcase\locationboxpagestate\relax
+ \expandafter\scrn_button_make_contrast % same page: yes: same page or not ... todo
+ \or
+ \expandafter\scrn_button_make_empty % same page: empty but frame: no click
+ \or
+ \expandafter\scrn_button_make_nothing % same page: empty no frame: no
+ \else
+ \expandafter\scrn_button_make_skipped % same page: nothing at all
+ \fi}
+
+\def\scrn_button_make_yes_other
+ {\ifcase\locationboxpagestate\relax
+ \expandafter\scrn_button_make_normal % other page: yes: same page or not ... todo
+ \or
+ \expandafter\scrn_button_make_normal % other page: empty but frame: no click
+ \or
+ \expandafter\scrn_button_make_normal % other page: empty no frame: no
+ \else
+ \expandafter\scrn_button_make_skipped % other page: nothing at all
+ \fi}
+
+\def\scrn_button_make_nop
+ {\ifcase\locationboxpagestate\relax
+ \expandafter\scrn_button_make_framed
+ \or
+ \expandafter\scrn_button_make_empty
+ \or
+ \expandafter\scrn_button_make_nothing
+ \or
+ \expandafter\scrn_button_make_skipped
+ \fi}
+
+\def\scrn_button_make_framed#currentparameter#inheritedframed#letparameter#setparameter#text%
+ {#inheritedframed{\ignorespaces#text\removeunwantedspaces}}
+
+\def\scrn_button_make_skipped#currentparameter#inheritedframed#letparameter#setparameter#text%
+ {\global\settrue\c_scrn_button_skipped}
+
+\def\scrn_button_make_normal#currentparameter#inheritedframed#letparameter#setparameter#text%
+ {\clf_injectcurrentreference
+ \hbox attr \referenceattribute \lastreferenceattribute
+ {#inheritedframed{\ignorespaces#text\removeunwantedspaces}}}
+
+\def\scrn_button_make_contrast#currentparameter#inheritedframed#letparameter#setparameter#text%
+ {\clf_injectcurrentreference
+ \hbox attr \referenceattribute \lastreferenceattribute
+ {#setparameter\c!foregroundcolor{#currentparameter\c!contrastcolor}%
+ #inheritedframed{\ignorespaces#text\removeunwantedspaces}}}
+
+\def\scrn_button_make_empty#currentparameter#inheritedframed#letparameter#setparameter#text%
+ {#letparameter\c!empty\v!yes
+ #inheritedframed{\ignorespaces#text\removeunwantedspaces}}
+
+\def\scrn_button_make_nothing#currentparameter#inheritedframed#letparameter#setparameter#text%
+ {#letparameter\c!empty\v!yes
+ #letparameter\c!frame\empty
+ #letparameter\c!background\empty
+ #inheritedframed{\ignorespaces#text\removeunwantedspaces}}
+
+%D Menus:
+%D
+%D \starttyping
+%D \setuppapersize
+%D [S6][S6]
+%D
+%D \setuplayout
+%D [backspace=6cm, cutspace=6cm,
+%D leftedge=3cm, rightedge=3cm,
+%D leftmargin=1cm, rightmargin=1cm,
+%D margindistance=5mm, edgedistance=5mm,
+%D topspace=4cm, bottomspace=4cm,
+%D header=0pt, footer=0pt,
+%D top=1cm, bottom=1cm,
+%D topdistance=5mm, bottomdistance=5mm,
+%D width=middle, height=middle]
+%D
+%D \setupinteraction
+%D [state=start,
+%D menu=on]
+%D
+%D \setupinteractionmenu
+%D [right]
+%D [state=start,background=color,frame=off,backgroundcolor=red,color=white,contrastcolor=blue]
+%D \setupinteractionmenu
+%D [left]
+%D [state=start,background=color,frame=off,backgroundcolor=green,color=white]
+%D \setupinteractionmenu
+%D [top]
+%D [state=start,background=color,frame=off,backgroundcolor=blue,color=white]
+%D \setupinteractionmenu
+%D [bottom]
+%D [state=start,background=color,frame=off,backgroundcolor=yellow,color=white]
+%D
+%D \setupinteractionmenu
+%D [left]
+%D [state=local]
+%D \setupinteractionmenu
+%D [bottom]
+%D [state=local]
+%D
+%D \startinteractionmenu[right]
+%D \startbut [page(2)] Page 2 \stopbut
+%D \startbut [page(1)] Page 1 \stopbut
+%D \includemenu[left]
+%D \includemenu[bottom]
+%D \stopinteractionmenu
+%D
+%D \startinteractionmenu[left]
+%D \startbut [page(1)] Page 1 \stopbut
+%D \startbut [page(2)] Page 2 \stopbut
+%D \stopinteractionmenu
+%D
+%D \startinteractionmenu[top]
+%D \startbut [page(1)] Page 1 \stopbut
+%D \startbut [page(2)] Page 2 \stopbut
+%D \stopinteractionmenu
+%D
+%D \startinteractionmenu[bottom]
+%D \startbut [page(2)] Page 2 \stopbut
+%D \startbut [page(1)] Page 1 \stopbut
+%D \stopinteractionmenu
+%D \stoptyping
+%D
+%D \starttyping
+%D \startinteractionmenu[rechts]
+%D \startbut [eerste] eerste \stopbut
+%D \starttxt hello world \stoptxt
+%D \startbut [tweede] tweede \stopbut
+%D \startnop \stopnop
+%D \startbut [tweede] tweede \stopbut
+%D \startrul whow \stoprul
+%D \startbut [tweede] tweede \stopbut
+%D \startraw hello world \stopraw
+%D \startbut [tweede] tweede \stopbut
+%D \startcom \vfill \stopcom
+%D \startbut [derde] derde \stopbut
+%D \stopinteractionmenu
+%D \stoptyping
+%D
+%D \starttyping
+%D \setupinteractionmenu[right][samepage=yes, unknownreference=yes]
+%D \setupinteractionmenu[right][samepage=empty,unknownreference=empty]
+%D \setupinteractionmenu[right][samepage=no, unknownreference=no]
+%D \setupinteractionmenu[right][samepage=none, unknownreference=none]
+%D \stoptyping
+
+\installcorenamespace{menu}
+\installcorenamespace{menutoks}
+\installcorenamespace{menustate}
+\installcorenamespace{menupacker}
+\installcorenamespace{menualign}
+
+\installframedcommandhandler \??menu {interactionmenu} \??menu
+
+\aliased\let\setupinteractionmenus\setupinteractionmenu
+
+\mutable\let\currentinteractionmenudistance\empty
+\mutable\let\currentinteractionmenustate \empty
+
+\let\scrn_menu_action\relax
+
+\let\scrn_menu_define_original\defineinteractionmenu
+
+\pushoverloadmode
+
+\permanent\tolerant\protected\def\defineinteractionmenu[#tag]#spacer[#category]#spacer[#settings]% category reflects location, settings can be parent
+ {\ifarguments\or
+ \scrn_menu_define_original[#tag]% % root definition
+ \or
+ \ifhastok={#category}%
+ \scrn_menu_define_original[#tag][#category]% % root definition
+ \else
+ \scrn_menu_define_original[#tag][#category][\c!category=#category]% % child definition
+ \scrn_menu_register{#tag}{#category}%
+ \fi
+ \or
+ \ifhastok={#settings}%
+ \scrn_menu_define_original[#tag][#category][\c!category=#category,#settings]% child definition
+ \else
+ \scrn_menu_define_original[#tag][#settings][\c!category=#category]% % child definition
+ \fi
+ \scrn_menu_register{#tag}{#category}%
+ \fi}
+
+\popoverloadmode
+
+\def\scrn_menu_register#tag#category%
+ {\ifcsname\??menutoks#category\endcsname \else
+ \expandafter\newtoks \csname\??menutoks#category\endcsname
+ \expandafter\setfalse\csname\??menustate#category\endcsname
+ \fi
+ % \normalexpanded{\csname\??menutoks#category\endcsname{\the\csname\??menutoks#category\endcsname\scrn_menu_action{#tag}}}}
+ \toksapp\csname\??menutoks#category\endcsname{\scrn_menu_action{#tag}}} % needs testing
+
+\def\scrn_menu_actions#category%
+ {\the\csname\??menutoks#category\endcsname}
+
+%D Fill menus:
+
+\aliased\letcsname\e!stop\v!interactionmenu\endcsname\relax
+
+%normalexpanded{\permanent\defcsname\e!start\v!interactionmenu\endcsname[#tag]#content\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}%
+% \normalexpanded{\permanent\expandafter\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#tag]#content\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}%
+\normalexpanded{\permanent\expandafter\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#tag]#content\csname\e!stop\v!interactionmenu\endcsname}%
+ {\def\currentinteractionmenu{#tag}%
+ \expandafter\settrue\csname\??menustate\interactionmenuparameter\c!category\endcsname
+ \setinteractionmenuparameter\c!menu{#content}}
+
+\permanent\protected\def\resetinteractionmenu[#tag]%
+ {\def\currentinteractionmenu{#tag}%
+ \resetinteractionmenuparameter\c!menu}
+
+%D Placement of menus:
+%D
+%D The offset mechanism is not the same as in in \MKII. There we adapted
+%D automatically to offsets in the text backgrounds. Here we have a bit more (but
+%D manual) control.
+%D
+%D \starttyping
+%D \setupbackgrounds
+%D [text][text]
+%D [background=color,backgroundcolor=gray,backgroundoffset=2mm]
+%D
+%D \setupbackgrounds
+%D [text]
+%D [rightedge,leftedge]
+%D [background=color,backgroundcolor=gray]
+%D
+%D \setupbackgrounds
+%D [top,bottom]
+%D [text]
+%D [background=color,backgroundcolor=gray]
+%D
+%D \setupinteractionmenu
+%D [right]
+%D [topoffset=0mm,bottomoffset=0mm]
+%D
+%D \setupinteractionmenu
+%D [top]
+%D [topoffset=2mm,bottomoffset=2mm,rightoffset=2mm,leftoffset=2mm]
+%D \stoptyping
+%D
+%D The no longer hard coded text areas offset compensation makes tuning easier.
+%D After all, menus need some setup anyway. The offsets are added to the width or
+%D height (this is different from \MKII).
+
+\newbox \b_scrn_menu
+
+\newdimen\d_scrn_menu_next_distance
+\newdimen\d_scrn_menu_final_width
+\newdimen\d_scrn_menu_final_height
+\newdimen\d_scrn_menu_used_width
+\newdimen\d_scrn_menu_used_height
+\newdimen\d_scrn_menu_asked_width
+\newdimen\d_scrn_menu_asked_height
+\newdimen\d_scrn_menu_offset_top
+\newdimen\d_scrn_menu_offset_bottom
+\newdimen\d_scrn_menu_offset_left
+\newdimen\d_scrn_menu_offset_right
+
+\newconditional\c_scrn_menu_zerodimensions
+
+\mutable\let\currentinteractionmenulocation\empty
+
+\def\scrn_menu_set_used
+ {\d_scrn_menu_offset_left \ifchkdim\interactionmenuparameter\c!leftoffset \or\lastchkdim\else\zeropoint\fi
+ \d_scrn_menu_offset_right \ifchkdim\interactionmenuparameter\c!rightoffset \or\lastchkdim\else\zeropoint\fi
+ \d_scrn_menu_offset_top \ifchkdim\interactionmenuparameter\c!topoffset \or\lastchkdim\else\zeropoint\fi
+ \d_scrn_menu_offset_bottom\ifchkdim\interactionmenuparameter\c!bottomoffset\or\lastchkdim\else\zeropoint\fi
+ \d_scrn_menu_asked_width \interactionmenuparameter\c!maxwidth
+ \d_scrn_menu_asked_height \interactionmenuparameter\c!maxheight
+ \d_scrn_menu_used_width\dimexpr
+ \d_scrn_menu_asked_width + \d_scrn_menu_offset_left + \d_scrn_menu_offset_right
+ \relax
+ \d_scrn_menu_used_height\dimexpr
+ \d_scrn_menu_asked_height + \d_scrn_menu_offset_top + \d_scrn_menu_offset_bottom
+ \relax}
+
+\def\scrn_menu_set_final
+ {\d_scrn_menu_final_width \namedinteractionmenuparameter\currentinteractionmenulocation\c!maxwidth
+ \d_scrn_menu_final_height\namedinteractionmenuparameter\currentinteractionmenulocation\c!maxheight}
+
+\def\scrn_menu_apply_final
+ {\ifconditional\c_scrn_menu_zerodimensions
+ \wd\b_scrn_menu\zeropoint
+ \ht\b_scrn_menu\zeropoint
+ \else
+ \wd\b_scrn_menu\d_scrn_menu_final_width
+ \ht\b_scrn_menu\d_scrn_menu_final_height
+ \fi
+ \dp\b_scrn_menu\zeropoint}
+
+\def\scrn_menu_apply_used
+ {\ifzeropt\d_scrn_menu_offset_left \else
+ \setbox\b_scrn_menu\hbox{\hskip-\d_scrn_menu_offset_left \box\b_scrn_menu}%
+ \fi
+ \ifzeropt\d_scrn_menu_offset_bottom \else
+ \setbox\b_scrn_menu\hbox{\lower \d_scrn_menu_offset_bottom \box\b_scrn_menu}%
+ \fi
+ \wd\b_scrn_menu\d_scrn_menu_asked_width
+ \ht\b_scrn_menu\d_scrn_menu_asked_height
+ \dp\b_scrn_menu\zeropoint}
+
+\setvalue{\??menualign\v!right }{\let\scrn_menu_left_align\raggedright}
+\setvalue{\??menualign\v!left }{\let\scrn_menu_left_align\raggedleft}
+\setvalue{\??menualign\v!flushright}{\let\scrn_menu_left_align\raggedleft}
+\setvalue{\??menualign\v!flushleft }{\let\scrn_menu_left_align\raggedright}
+\setvalue{\??menualign\v!middle }{\let\scrn_menu_left_align\raggedcenter}
+\setvalue{\??menualign\v!low }{\let\scrn_menu_top_align\vss\let\scrn_menu_bottom_align\relax}
+\setvalue{\??menualign\v!high }{\let\scrn_menu_top_align\relax\let\scrn_menu_bottom_align\vss}
+\setvalue{\??menualign\v!lohi }{\let\scrn_menu_top_align\vss\let\scrn_menu_bottom_align\vss}
+
+\let\scrn_menu_left_align \relax
+\let\scrn_menu_right_align \relax
+\let\scrn_menu_top_align \relax
+\let\scrn_menu_bottom_align\relax
+
+\def\scrn_menu_set_align
+ {\csname\??menualign\interactionmenuparameter\c!itemalign\endcsname}
+
+%D Hook into the pagebuilder (as less testing as possible):
+
+\def\scrn_menu_insert
+ {\iflocation
+ \expandafter\scrn_menu_insert_checked
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\scrn_menu_insert_checked#location%
+ {\ifconditional\csname\??menustate#location\endcsname
+ \scrn_menu_insert_indeed{#location}%
+ \fi}
+
+\def\scrn_menu_insert_indeed#location%
+ {\begingroup
+ \xdef\currentinteractionmenulocation{#location}%
+ \scrn_menu_set_final
+ \ifcase\d_scrn_menu_final_width \orelse \ifcase\d_scrn_menu_final_height \else
+ \forgetall
+ \global\d_scrn_menu_next_distance\zeropoint
+ \let\scrn_menu_action\scrn_menu_package_indeed
+ \the\everysetmenucommands
+ \csname\??menupacker\namedinteractionmenuparameter\currentinteractionmenulocation\c!alternative\endcsname
+ \fi
+ \endgroup}
+
+%D This calls: % can be \c!command for vertical/horizontal
+
+\let\scrn_menu_packager\relax
+
+\setvalue{\??menupacker\v!vertical}% all menus
+ {\let\scrn_menu_packager\scrn_menu_packager_vertical
+ \setbox\b_scrn_menu\hbox{\scrn_menu_actions\currentinteractionmenulocation}%
+ \scrn_menu_apply_final
+ \box\b_scrn_menu}
+
+\setvalue{\??menupacker\v!horizontal}% all menus
+ {\let\scrn_menu_packager\scrn_menu_packager_horizontal
+ \setbox\b_scrn_menu\vbox{\scrn_menu_actions\currentinteractionmenulocation}%
+ \scrn_menu_apply_final
+ \box\b_scrn_menu}
+
+% stop : skipped
+% start: processed
+% local: skipped but can be included
+% empty: processed but invisible
+
+\protected\def\scrn_menu_package_indeed#tag% one menu
+ {\begingroup
+ \edef\currentinteractionmenu{#tag}%
+ \edef\currentinteractionmenustate{\interactionmenuparameter\c!state}%
+ \ifx\currentinteractionmenustate\v!start
+ \scrn_menu_packager
+ \orelse\ifx\currentinteractionmenustate\v!empty
+ \scrn_menu_packager
+ \fi
+ \endgroup}
+
+%D With the packager being one of:
+
+\let\currentinteractionmenudistance\!!zeropoint
+
+\def\scrn_menu_packager_vertical
+ {\scrn_menu_set_used
+ \hskip\d_scrn_menu_next_distance
+ \setbox\b_scrn_menu\hbox to \d_scrn_menu_used_width
+ {\ifx\currentinteractionmenustate\v!empty \else
+ \interactionmenuparameter\c!left
+ \scrn_menu_package_vertical{\directinteractionmenuparameter\c!menu}%
+ \interactionmenuparameter\c!right
+ \fi}%
+ \edef\currentinteractionmenudistance{\interactionmenuparameter\c!distance}%
+ \ifx\currentinteractionmenudistance\v!overlay
+ \global\d_scrn_menu_next_distance\zeropoint
+ \wd\b_scrn_menu\zeropoint
+ \else
+ \global\d_scrn_menu_next_distance\currentinteractionmenudistance
+ \scrn_menu_apply_used
+ \fi
+ \box\b_scrn_menu}
+
+\def\scrn_menu_packager_horizontal
+ {\scrn_menu_set_used
+ \vskip\d_scrn_menu_next_distance
+ \scrn_menu_set_align
+ \setbox\b_scrn_menu\vbox to \d_scrn_menu_used_height
+ {\ifx\currentinteractionmenustate\v!none \else
+ \scrn_menu_top_align
+ \interactionmenuparameter\c!before
+ \scrn_menu_package_horizontal{\directinteractionmenuparameter\c!menu}%
+ \interactionmenuparameter\c!after
+ \scrn_menu_bottom_align
+ \fi}%
+ \edef\currentinteractionmenudistance{\interactionmenuparameter\c!distance}%
+ \ifx\currentinteractionmenudistance\v!overlay
+ \global\d_scrn_menu_next_distance\zeropoint
+ \offinterlineskip
+ \dp\b_scrn_menu\zeropoint
+ \ht\b_scrn_menu\zeropoint
+ \else
+ \global\d_scrn_menu_next_distance\currentinteractionmenudistance
+ \scrn_menu_apply_used
+ \fi
+ \box\b_scrn_menu}
+
+%D For a right menu, a sequence of calls to \type {right_menu_button} is generated.
+%D
+%D \starttyping
+%D right_menu_button (n, p, s=0/1/2, x, y, w, h, d) ;
+%D \stoptyping
+%D
+%D Here, n is the number of the button, s a status variable, while the rest is
+%D positional info. The status variable is 0, 1 or~2: not found, found and found but
+%D current page.
+
+\newcount \c_scrn_menu_position
+\newconstant \c_scrn_menu_page_mode % 0=notfound 1=found 2=currentpage
+\newconditional\c_scrn_menu_positioning
+\newtoks \t_scrn_menu_meta_data
+
+\def\scrn_menu_button_meta_template
+ {\currentinteractionmenulocation _menu_button(%
+ \number\c_scrn_menu_position,%
+ \number\c_scrn_menu_page_mode,%
+ \MPpos{\currentinteractionmenulocation:\number\c_scrn_menu_position}%
+ );}
+
+\permanent\def\MPmenubuttons#1{\the\t_scrn_menu_meta_data}
+
+\appendtoks
+ \global\t_scrn_menu_meta_data\emptytoks
+\to \everyshipout
+
+\def\scrn_menu_whole_position % cannot happen in previous due to align
+ {\setbox\b_scrn_menu\hbox \bgroup
+ \hpos{menu:\currentinteractionmenulocation:\the\realpageno}{\box\b_scrn_menu}%
+ \egroup}
+
+% removed: \restorestandardblank (vspacing) ... should happen elsewhere
+
+\let\scrn_menu_between_action_indeed\relax
+
+\def\scrn_menu_package_vertical#content%
+ {\begingroup
+ \global\c_scrn_menu_position\zerocount
+ \def\scrn_menu_between_action_indeed{\interactionmenuparameter\c!inbetween}%
+ \doifelse{\interactionmenuparameter\c!position}\v!yes\settrue\setfalse\c_scrn_menu_positioning
+ \scrn_menu_set_align
+ \setbox\b_scrn_menu\vbox to \d_scrn_menu_used_height \bgroup
+ \hsize\d_scrn_menu_used_width
+ \scrn_menu_left_align
+ \interactionmenuparameter\c!before\relax
+ \ignorespaces#content\unskip
+ \interactionmenuparameter\c!after
+ \scrn_menu_right_align
+ \egroup
+ \ifconditional\c_scrn_menu_positioning
+ \scrn_menu_whole_position
+ \fi
+ \box\b_scrn_menu
+ \endgroup}
+
+\def\scrn_menu_package_horizontal#content%
+ {\begingroup
+ \global\c_scrn_menu_position\zerocount
+ \def\scrn_menu_between_action_indeed{\interactionmenuparameter\c!middle}%
+ \doifelse{\interactionmenuparameter\c!position}\v!yes\settrue\setfalse\c_scrn_menu_positioning
+ \setbox\b_scrn_menu\hbox to \d_scrn_menu_used_width \bgroup
+ \interactionmenuparameter\c!left\relax
+ \ignorespaces#content\unskip
+ \interactionmenuparameter\c!right
+ \egroup
+ \ifconditional\c_scrn_menu_positioning
+ \scrn_menu_whole_position
+ \fi
+ \box\b_scrn_menu
+ \endgroup}
+
+\def\scrn_menu_action_start
+ {\dontleavehmode
+ \begingroup}
+
+\def\scrn_menu_action_stop
+ {\ifconditional\c_scrn_button_skipped \else
+ \scrn_menu_between_action_indeed
+ \fi
+ \endgroup
+ \ignorespaces}
+
+\aliased\let\startraw \aliased\let\stopraw\relax \ifdefined\raw\else \aliased\let\raw\relax \fi
+\aliased\let\startbut \aliased\let\stopbut\relax \ifdefined\but\else \aliased\let\but\relax \fi
+\aliased\let\startgot \aliased\let\stopgot\relax \ifdefined\got\else \aliased\let\got\relax \fi
+\aliased\let\startnop \aliased\let\stopnop\relax \ifdefined\nop\else \aliased\let\nop\relax \fi
+\aliased\let\starttxt \aliased\let\stoptxt\relax \ifdefined\txt\else \aliased\let\txt\relax \fi
+\aliased\let\startrul \aliased\let\stoprul\relax \ifdefined\rul\else \aliased\let\rul\relax \fi
+\aliased\let\startcom \aliased\let\stopcom\relax \ifdefined\com\else \aliased\let\com\relax \fi
+
+\protected\def\scrn_menu_raw_start[#action]#text\stopraw
+ {\scrn_menu_action_start
+ \gotobox{\ignorespaces#text\unskip}[#action]%
+ \scrn_menu_action_stop}
+
+\protected\def\scrn_menu_but_start[#action]#text\stopbut
+ {\scrn_menu_action_start
+ \ifconditional\c_scrn_menu_positioning
+ \expandafter\scrn_button_make_position
+ \else
+ \expandafter\scrn_button_make
+ \fi
+ \interactionmenuparameter
+ \inheritedinteractionmenuframed
+ \letinteractionmenuparameter
+ \setinteractionmenuparameter
+ {#text}%
+ {#action}%
+ \scrn_menu_action_stop}
+
+\def\scrn_button_make_position#currentparameter#inheritedframed#letparameter#setparameter#text#action%
+ {\global\advance\c_scrn_menu_position\plusone
+ \doifelsereferencefound{#action}% 0=not found, 1=same page, >1=elsewhere
+ {\c_scrn_menu_page_mode\ifnum\currentreferencerealpage=\realpageno\plusone\else\plustwo\fi}%
+ {\c_scrn_menu_page_mode\plustwo}%
+ \xtoksapp\t_scrn_menu_meta_data{\scrn_menu_button_meta_template}%
+ \hpos
+ {\currentinteractionmenulocation:\number\c_scrn_menu_position}%
+ {\scrn_button_make
+ #currentparameter%
+ #inheritedframed%
+ #letparameter%
+ #setparameter%
+ {#text}%
+ {#action}}}
+
+\permanent\protected\def\scrn_menu_got_start[#action]#text\stopgot
+ {\scrn_menu_action_start
+ \letinteractionmenuparameter\c!frame\v!off
+ \letinteractionmenuparameter\c!background\empty
+ \scrn_button_make
+ \interactionmenuparameter
+ \inheritedinteractionmenuframed
+ \letinteractionmenuparameter
+ \setinteractionmenuparameter
+ {#text}%
+ {#action}%
+ \scrn_menu_action_stop}
+
+\permanent\protected\def\scrn_menu_nop_start#text\stopnop
+ {\scrn_menu_action_start
+ \letinteractionmenuparameter\c!frame\v!off
+ \letinteractionmenuparameter\c!background\empty
+ \letinteractionmenuparameter\c!empty\v!yes
+ \inheritedinteractionmenuframed{\ignorespaces#text\removeunwantedspaces}%
+ \scrn_menu_action_stop}
+
+\permanent\protected\def\scrn_menu_txt_start#text\stoptxt
+ {\scrn_menu_action_start
+ \letinteractionmenuparameter\c!frame\v!off
+ \letinteractionmenuparameter\c!background\empty
+ \inheritedinteractionmenuframed{\ignorespaces#text\removeunwantedspaces}%
+ \scrn_menu_action_stop}
+
+\permanent\protected\def\scrn_menu_rul_start#text\stoprul
+ {\scrn_menu_action_start
+ \inheritedinteractionmenuframed{\ignorespaces#text\removeunwantedspaces}%
+ \scrn_menu_action_stop}
+
+\permanent\protected\def\scrn_menu_com_start#text\stopcom
+ {\ignorespaces#text\removeunwantedspaces
+ \ignorespaces}
+
+\permanent\protected\def\scrn_menu_raw#content\\{\scrn_menu_raw_start#content\stopraw} \aliased\let\stopraw\relax
+\permanent\protected\def\scrn_menu_but#content\\{\scrn_menu_but_start#content\stopbut} \aliased\let\stopbut\relax
+\permanent\protected\def\scrn_menu_got#content\\{\scrn_menu_got_start#content\stopgot} \aliased\let\stopgot\relax
+\permanent\protected\def\scrn_menu_nop#content\\{\scrn_menu_nop_start#content\stopnop} \aliased\let\stopnop\relax
+\permanent\protected\def\scrn_menu_txt#content\\{\scrn_menu_txt_start#content\stoptxt} \aliased\let\stoptxt\relax
+\permanent\protected\def\scrn_menu_rul#content\\{\scrn_menu_rul_start#content\stoprul} \aliased\let\stoprul\relax
+\permanent\protected\def\scrn_menu_com#content\\{\scrn_menu_com_start#content\stopcom} \aliased\let\stopcom\relax
+
+\newtoks\everysetmenucommands % public
+
+\appendtoks
+ \enforced\let\raw\scrn_menu_raw \enforced\let\startraw\scrn_menu_raw_start \enforced\let\stopraw\relax
+ \enforced\let\but\scrn_menu_but \enforced\let\startbut\scrn_menu_but_start \enforced\let\stopbut\relax
+ \enforced\let\got\scrn_menu_got \enforced\let\startgot\scrn_menu_got_start \enforced\let\stopgot\relax
+ \enforced\let\nop\scrn_menu_nop \enforced\let\startnop\scrn_menu_nop_start \enforced\let\stopnop\relax
+ \enforced\let\txt\scrn_menu_txt \enforced\let\starttxt\scrn_menu_txt_start \enforced\let\stoptxt\relax
+ \enforced\let\rul\scrn_menu_rul \enforced\let\startrul\scrn_menu_rul_start \enforced\let\stoprul\relax
+ \enforced\let\com\scrn_menu_com \enforced\let\startcom\scrn_menu_com_start \enforced\let\stopcom\relax
+\to \everysetmenucommands
+
+%D Sometimes handy:
+%D
+%D \starttyping
+%D \setupinteractionmenu
+%D [left]
+%D [state=local]
+%D
+%D \startinteractionmenu[right]
+%D ...
+%D \includemenu[left]
+%D ...
+%D \stopinteractionmenu
+%D \stoptyping
+
+\permanent\protected\def\includemenu[#tag]%
+ {\begingroup
+ \edef\currentinteractionmenu{#tag}%
+ \doif{\interactionmenuparameter\c!state}\v!local
+ {\letinteractionmenuparameter\c!state\v!start
+ \directinteractionmenuparameter\c!menu}%
+ \endgroup}
+
+%D Direct call (todo):
+
+\permanent\tolerant\protected\def\interactionmenu[#tag]#spacer[#settings]%
+ {\begingroup
+ \edef\currentinteractionmenu{#tag}%
+ \setupcurrentinteractionmenu[#settings]%
+ \scrn_menu_insert{#tag}%
+ \endgroup}
+
+%D Plugin handler:
+
+\protected\def\scrn_menu_insert_content_indeed
+ {\iflocation % here as we can have a fast turn-off
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\let\scrn_menu_insert_content_ignore\gobbleoneargument
+
+\appendtoks
+ \doifelse{\interactionparameter\c!menu}\v!on
+ {\let\scrn_menu_insert_content\scrn_menu_insert_content_indeed}%
+ {\let\scrn_menu_insert_content\scrn_menu_insert_content_ignore}%
+\to \everysetupinteraction
+
+%D Plugs into the page builder:
+
+\protected\def\scrn_menu_leftedge
+ {\hbox to \leftedgewidth \bgroup
+ \hsize\leftedgewidth
+ \settrue\c_scrn_menu_zerodimensions
+ \scrn_menu_insert\v!left
+ \egroup
+ \hskip-\leftedgewidth}
+
+\protected\def\scrn_menu_rightedge
+ {\hbox to \rightedgewidth \bgroup
+ \hsize\rightedgewidth
+ \settrue\c_scrn_menu_zerodimensions
+ \scrn_menu_insert\v!right
+ \egroup
+ \hskip-\rightedgewidth}
+
+\protected\def\scrn_menu_top
+ {\vbox to \topheight \bgroup
+ \vsize\topheight
+ \settrue\c_scrn_menu_zerodimensions
+ \scrn_menu_insert\v!top
+ \kern\zeropoint
+ \egroup
+ \vskip-\topheight}
+
+\protected\def\scrn_menu_bottom
+ {\vbox to \bottomheight \bgroup
+ \vsize\bottomheight
+ \settrue\c_scrn_menu_zerodimensions
+ \scrn_menu_insert\v!bottom
+ \kern\zeropoint
+ \egroup
+ \vskip-\bottomheight}
+
+\appendtoks \scrn_menu_insert_content\scrn_menu_leftedge \to \leftedgetextcontent
+\appendtoks \scrn_menu_insert_content\scrn_menu_rightedge \to \rightedgetextcontent
+\appendtoks \scrn_menu_insert_content\scrn_menu_top \to \toptextcontent
+\appendtoks \scrn_menu_insert_content\scrn_menu_bottom \to \bottomtextcontent
+
+%D Initialization (root definitions, main builders):
+
+\defineinteractionmenu [\v!vertical] [\c!alternative=\v!vertical]
+\defineinteractionmenu [\v!horizontal] [\c!alternative=\v!horizontal]
+
+%D Initialization (parent definitions, 4 area builders):
+
+\defineinteractionmenu [\v!right ] [\v!right ] [\v!vertical ]
+\defineinteractionmenu [\v!left ] [\v!left ] [\v!vertical ]
+\defineinteractionmenu [\v!top ] [\v!top ] [\v!horizontal]
+\defineinteractionmenu [\v!bottom] [\v!bottom] [\v!horizontal]
+
+\setupinteraction
+ [\c!menu=\v!off]
+
+\setupinteractionmenu
+ [\c!offset=.25em,
+ \c!position=\v!no,
+ \c!frame=\v!on,
+ \c!maxwidth=\hsize,
+ \c!maxheight=\vsize,
+ \c!background=,
+ \c!backgroundcolor=,
+ \c!foregroundstyle=\interactionmenuparameter\c!style,
+ \c!foregroundcolor=\interactionmenuparameter\c!color,
+ \c!style=\interactionparameter\c!style,
+ \c!color=\interactionparameter\c!color,
+ \c!contrastcolor=\interactionparameter\c!contrastcolor,
+ \c!state=\v!start,
+ \c!samepage=\v!yes,
+ \c!unknownreference=\v!empty,
+ \c!distance=\bodyfontsize,
+ \c!topoffset=\zeropoint,
+ \c!bottomoffset=\zeropoint,
+ \c!leftoffset=\zeropoint,
+ \c!rightoffset=\zeropoint]
+
+\setupinteractionmenu
+ [\v!vertical] % not really a menu
+ [\c!inbetween=\blank,
+ \c!before=,
+ \c!after=\vfil,
+ %\c!width=\v!fit,
+ \c!height=\v!broad]
+
+\setupinteractionmenu
+ [\v!horizontal] % not really a menu
+ [\c!middle=\hfil,
+ %\c!left=\hss,
+ %\c!right=\hss,
+ \c!width=\v!fit,
+ \c!height=\v!broad]
+
+\setupinteractionmenu
+ [\v!left]
+ [\c!itemalign=\v!flushright,
+ \c!maxwidth=\leftedgewidth,
+ \c!maxheight=\makeupheight]
+
+\setupinteractionmenu
+ [\v!right]
+ [\c!itemalign=\v!flushleft,
+ \c!maxwidth=\rightedgewidth,
+ \c!maxheight=\makeupheight]
+
+\setupinteractionmenu
+ [\v!top]
+ [\c!itemalign=\v!high,
+ \c!maxwidth=\makeupwidth,
+ \c!maxheight=\topheight]
+
+\setupinteractionmenu
+ [\v!bottom]
+ [\c!itemalign=\v!low,
+ \c!maxwidth=\makeupwidth,
+ \c!maxheight=\bottomheight]
+
+%D Lists:
+
+\definelistalternative [\v!left ] [\c!renderingsetup=strc:lists:rendering:menu]
+\definelistalternative [\v!right ] [\c!renderingsetup=strc:lists:rendering:menu]
+\definelistalternative [\v!top ] [\c!renderingsetup=strc:lists:rendering:menu]
+\definelistalternative [\v!bottom] [\c!renderingsetup=strc:lists:rendering:menu]
+
+\startsetups[strc:lists:rendering:menu]
+ \startbut[internal(\currentlistentrylocation)]
+ \limitatetext\currentlistentrytitle{\listparameter\c!maxwidth}\unknown
+ \stopbut
+\stopsetups
+
+%D Sometimes handy:
+
+\def\scrn_button_direct_status
+ {\doifelse{\buttonparameter\c!state}\v!start
+ {\dosingleempty\scrn_button_direct_indeed}%
+ {\dosingleempty\scrn_button_direct_ignore}}
+
+\permanent\protected\def\menubutton % tag settings text action
+ {\iflocation
+ \expandafter\scrn_menu_menu_button_indeed
+ \else
+ \expandafter\scrn_menu_menu_button_ignore
+ \fi}
+
+\tolerant\def\scrn_menu_menu_button_indeed[#menutag]#spacer[#settings]#:#text[#action]%
+ {\ifcstok{\interactionmenuparameter\c!state}\v!start
+ \ifparameter#settings\or
+ \expandafter\scrn_menu_menu_button_a
+ \orelse\ifhastok={#menutag}%
+ \expandafter\scrn_menu_menu_button_b
+ \else
+ \expandafter\scrn_menu_menu_button_c
+ \fi{#menutag}{#settings}{#text}{#action}%
+ \fi}
+
+\tolerant\def\scrn_menu_menu_button_ignore[#menutag]#spacer[#settings]#:#text[#action]%
+ {}
+
+\def\scrn_menu_menu_button_a#tag#settings#text#action%
+ {\dontleavehmode \begingroup
+ \edef\currentinteractionmenu{#tag}%
+ \setupcurrentinteractionmenu[#settings]%
+ \scrn_button_make
+ \interactionmenuparameter
+ \inheritedinteractionmenuframed
+ \letinteractionmenuparameter
+ \setinteractionmenuparameter
+ {#text}%
+ {#action}%
+ \endgroup}
+
+\def\scrn_menu_menu_button_b#settings#dummy#text#action%
+ {\dontleavehmode \begingroup
+ \let\currentbutton\empty
+ \setupcurrentbutton[#settings]%
+ \scrn_button_make
+ \buttonparameter
+ \inheritedbuttonframed
+ \letbuttonparameter
+ \setbuttonparameter
+ {#text}%
+ {#action}%
+ \endgroup}
+
+\def\scrn_menu_menu_button_c#tag#dummy#text#action%
+ {\dontleavehmode \begingroup
+ \edef\currentinteractionmenu{#tag}%
+ \scrn_button_make
+ \interactionmenuparameter
+ \inheritedinteractionmenuframed
+ \letinteractionmenuparameter
+ \setinteractionmenuparameter
+ {#text}%
+ {#action}%
+ \endgroup}
+
+%D Untested:
+
+\permanent\tolerant\protected\def\registermenubuttons[#menu]#spacer[#register]%
+ {\begingroup
+ \ifparameter#register\or
+ \clf_registerbuttons{#menu}{#register}{\currentlanguage}%
+ \else
+ \clf_registerbuttons{}{#menu}{\currentlanguage}%
+ \fi
+ \removeunwantedspaces
+ \endgroup}
+
+\permanent\protected\def\doregistermenubutton#1#2#3% used at lua end
+ {\ifcstok{\interactionmenuparameter\c!state}\v!start
+ \scrn_menu_menu_button_c{#1}{}{#2}{#3}%
+ \fi
+ \space}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-fld.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-fld.mklx
new file mode 100644
index 00000000000..6c134268c60
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-fld.mklx
@@ -0,0 +1,1040 @@
+%D \module
+%D [ file=scrn-fld,
+%D version=1997.05.18,
+%D title=\CONTEXT\ Screen Macros,
+%D subtitle=Fields,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D There is still some leftover code from mkii, where we need to be sparse with hash
+%D entries and so have a somewhat complex setup mechanism. It doesn't pay off to
+%D waste time on improving this because one never knows what happens with widgets
+%D in \PDF; it has a history of dropping features like this. Also, the implementation
+%D of fields fluctuates with versions.
+
+% interaction checking
+
+\writestatus{loading}{ConTeXt Screen Macros / Fields}
+
+\unprotect
+
+\registerctxluafile{scrn-fld}{}
+
+%D In \MKII\ we had to cheat a bit with setups in order not to run out of memory
+%D with thousands of fields, which we happen to need at that time. In \MKIV\ we can
+%D store some data at the \LUA\ end and use a somewhat slower but quite okay
+%D inheritance mechanism. For this reason we now have split definitions, although
+%D the old method is still somewhat supported. The clone and copy commands are
+%D somewhat obsolete for several reasons: we can now use inheritance and autocloning
+%D has been supported for a while. In most cases cloning (especially with check
+%D boxes) the acrobat browser is not stable enough with respect to appearance
+%D handling.
+%D
+%D A fieldcategory is nothing more than a collection of settings.
+%D
+%D \starttyping
+%D \definefieldcategory
+%D [all-email]
+%D [height=1cm,
+%D width=14cm,
+%D style=sstf]
+%D \stoptyping
+%D
+%D A definition can refer to this category:
+%D
+%D \starttyping
+%D \definefieldbody [email] [type=line,category=all-email,default=pragma@xs4all.nl]
+%D \stoptyping
+%D
+%D A copy of a field is made as follows:
+%D
+%D \starttyping
+%D \definefieldbody [xmail] [email]
+%D \stoptyping
+%D
+%D You can also be more specific:
+%D
+%D \starttyping
+%D \definefieldbody[buttona][type=check,values={one,two}]
+%D \definefieldbody[buttonb][type=check,values={three,four}]
+%D \definefieldbody[buttonc][buttona][values={three,four}]
+%D \stoptyping
+%D
+%D Actually typesetting a field happens this way:
+%D
+%D \starttyping
+%D \fieldbody [Email]
+%D \fieldbody [Email] [width=6cm]
+%D \fieldbody [eMAIL]
+%D \fieldbody [eMAIL] [width=7cm]
+%D
+%D \fieldbody [buttona]
+%D \fieldbody [buttona]
+%D \fieldbody [buttonb]
+%D \fieldbody [buttonb]
+%D \fieldbody [buttonc]
+%D \fieldbody [buttonc]
+%D \stoptyping
+%D
+%D So, you can call up a field many times and quite some parameters can be set.
+%D
+%D Because there are persistent problems with acrobat rendering associated
+%D appearance streams (some messy /MK interferende) we also support native (built-in
+%D dingbat) symbols: check, circle, cross, diamond, square and star.
+%D
+%D \starttyping
+%D \definefield[test1][check]
+%D \definefield[test2][check]
+%D
+%D \fieldbody[test1][width=1em,height=\strutht,depth=\strutdp,symbol=check]
+%D \fieldbody[test1][width=1em,height=\strutht,depth=\strutdp,symbol=circle]
+%D \fieldbody[test2][width=1em,height=\strutht,depth=\strutdp,symbol=square]
+%D \stoptyping
+%D
+%D When submitting a form, we need to tell the driver module that we want \FDF\ or
+%D \HTML.
+
+\installcorenamespace {forms}
+
+\installdirectcommandhandler \??forms {forms}
+
+\appendtoks
+ \clf_setformsmethod{\formsparameter\c!method}%
+\to \everysetupforms
+
+\setupforms
+ [\c!method=XML] % no need for everyjob initialization as this is the default
+
+\mutable\let\currentfieldbackgroundcolor \empty
+\mutable\let\currentfieldbackgroundcolorvalue\empty
+\mutable\let\currentfieldbodycategory \empty
+\mutable\let\currentfieldframecolor \empty
+\mutable\let\currentfieldframecolorvalue \empty
+\mutable\let\currentfieldlabel \empty
+\mutable\let\currentfieldstackname \empty
+
+\appendtoks
+ \iflocation
+ \clf_exportformdata{\formsparameter\c!export}%
+ \fi
+\to \everystoptext
+
+%D We need to initialize symbols in a special way so that they can be used as
+%D rendering for a widget.
+
+\permanent\protected\def\presetfieldsymbols[#list]% slow
+ {\processcommacommand[#list]\scrn_symbols_preset}
+
+\def\scrn_symbols_preset#set%
+ {\processcommalist[#set]\scrn_symbols_preset_indeed}%
+
+\permanent\protected\def\scrn_symbols_preset_indeed#tag%
+ {\doifelseobjectfound{SYM}{#tag}
+ {}
+ {\settightobject{SYM}{#tag}\hbox{\symbol[#tag]}% % todo: set this as immediate xform
+ \page_otr_add_special_content{\hskip-\maxdimen\getobject{SYM}{#tag}}}} % and then force it into the file
+
+\aliased\let\dosetfieldsymbol\scrn_symbols_preset_indeed
+
+\permanent\def\dogetfieldsymbol#tag%
+ {\getobject{SYM}{#tag}}
+
+\permanent\protected\def\definedefaultsymbols % used ?
+ {\definesymbol[defaultyes][\mathematics{\times}]%
+ \definesymbol[defaultno] [\mathematics{\cdot }]}
+
+%D Now comes the real code:
+
+\installcorenamespace{fieldcategory}
+\installcorenamespace{fieldbody}
+
+\installcommandhandler \??fieldcategory {fieldcategory} \??fieldcategory
+\installcommandhandler \??fieldbody {fieldbody} \??fieldbody
+
+\newbox\b_scrn_field_body
+
+\setupfieldcategory
+ [\c!alternative=\v!normal, % normal clone copy
+ \c!type=\v!line, % line text ...
+ \c!width=5em,
+ \c!height=1em,
+ \c!depth=\zeropoint,
+ \c!align=\v!flushleft,
+ \c!option=\v!printable, % maybe we need a globaloptions and marge them
+ \c!n=1024]
+
+\def\scrn_field_check_category
+ {\edef\currentfieldbodycategory{\fieldbodyparameter\c!category}%
+ \ifempty\currentfieldbodycategory
+ \letfieldbodyparameter\s!parent\??fieldcategory
+ %\setevalue{\currentfieldbodyhash\s!parent}{\namedfieldcategoryhash\empty}% to WS: not hash !
+ \else
+ \normalexpanded{\setfieldbodyparameter{\s!parent}{\??fieldcategory\currentfieldbodycategory}}%
+ %\setevalue{\currentfieldbodyhash\s!parent}{\namedfieldcategoryhash\currentfieldbodycategory}% to WS: not hash !
+ \fi}
+
+\appendtoks % we cannot use parent .. maybe s!parent has to change
+ \ifempty\currentfieldbodyparent
+ \scrn_field_check_category
+ \clf_definefield
+ name {\currentfieldbody}%
+ alternative {normal}%
+ type {\fieldbodyparameter\c!type}%
+ category {\fieldbodyparameter\c!category}%
+ values {\fieldbodyparameter\c!values}%
+ default {\fieldbodyparameter\c!default}%
+ \relax
+ \else
+ \clf_clonefield
+ children {\currentfieldbody}%
+ alternative {clone}%
+ parent {\currentfieldbodyparent}%
+ category {\fieldbodyparameter\c!category}%
+ values {\fieldbodyparameter\c!values}%
+ default {\fieldbodyparameter\c!default}%
+ \relax
+ \fi
+\to \everydefinefieldbody
+
+\permanent\tolerant\protected\def\fieldbody[#tag]#spacer[#settings]%
+ {\iflocation
+ \hbox\bgroup
+ \edef\currentfieldbody{#tag}%
+ \setupcurrentfieldbody[#settings]%
+ \scrn_field_body_typeset
+ \box\b_scrn_field_body
+ \egroup
+ \fi}
+
+\def\scrn_field_body_typeset % todo: fieldsymbol (checkfields /MK mess)
+ {\edef\currentfieldframecolor{\fieldbodyparameter\c!fieldframecolor}%
+ \ifempty\currentfieldframecolor\else
+ \getcolorattributevalue\currentfieldframecolor\currentfieldframecolorvalue
+ % == \edef\currentfieldframecolorvalue{\thecolorattribute\currentfieldframecolor}%
+ \fi
+ \edef\currentfieldbackgroundcolor{\fieldbodyparameter\c!fieldbackgroundcolor}%
+ \ifempty\currentfieldbackgroundcolor\else
+ \getcolorattributevalue\currentfieldbackgroundcolor\currentfieldbackgroundcolorvalue
+ % == \edef\currentfieldbackgroundcolorvalue{\thecolorattribute\currentfieldbackgroundcolor}%
+ \fi
+ \usefieldbodystyleandcolor\c!style\c!color
+ \clf_insertfield
+ {\currentfieldbody}%
+ {%
+ title {\currentfieldbody}
+ width \dimexpr\fieldbodyparameter\c!width \relax
+ height \dimexpr\fieldbodyparameter\c!height\relax
+ depth \dimexpr\fieldbodyparameter\c!depth \relax
+ align {\fieldbodyparameter\c!align}%
+ length {\fieldbodyparameter\c!n}%
+ fontstyle {\fontstyle}%
+ fontalternative {\fontalternative}%
+ fontsize {\fontbody}%
+ fontsymbol {\fieldbodyparameter\c!symbol}%
+ color {\fieldbodyparameter\c!color}%
+ colorvalue \c_attr_color
+ \ifempty\currentfieldbackgroundcolor \else
+ backgroundcolor {\currentfieldbackgroundcolor}%
+ backgroundcolorvalue \numexpr\currentfieldbackgroundcolorvalue\relax
+ \fi
+ \ifempty\currentfieldframecolor \else
+ framecolor {\currentfieldframecolor}%
+ framecolorvalue \numexpr\currentfieldframecolorvalue\relax
+ \fi
+ layer {\fieldbodyparameter\c!fieldlayer}%
+ option {\fieldbodyparameter\c!option}%
+ clickin {\fieldbodyparameter\c!clickin}%
+ clickout {\fieldbodyparameter\c!clickout}%
+ regionin {\fieldbodyparameter\c!regionin}%
+ regionout {\fieldbodyparameter\c!regionout}%
+ afterkey {\fieldbodyparameter\c!afterkey}%
+ format {\fieldbodyparameter\c!format}%
+ validate {\fieldbodyparameter\c!validate}%
+ calculate {\fieldbodyparameter\c!calculate}%
+ focusin {\fieldbodyparameter\c!focusin}%
+ focusout {\fieldbodyparameter\c!focusout}%
+ openpage {\fieldbodyparameter\c!openpage}%
+ closepage {\fieldbodyparameter\c!closepage}%
+ }%
+ \relax}
+
+%D The sets are used in grouped calculations.
+%D
+%D [name] [set]
+
+\permanent\tolerant\protected\def\definefieldbodyset[#tag]#spacer[#list]%
+ {\clf_definefieldset{#tag}{#list}}
+
+\aliased\let\dodefinefieldset\definefieldbodyset % compatibility
+
+%D A few testing macros (expandable for historic reasons):
+
+\permanent\def\doifelsefieldbody #tag{\clf_doifelsefield{#tag}}
+\permanent\def\doifelsefieldcategory#tag{\clf_doifelsefieldcategory{#tag}}
+
+\aliased\let\doiffieldbodyelse \doifelsefieldbody
+\aliased\let\doiffieldcategoryelse\doifelsefieldcategory
+
+\aliased\let\doiffieldelse \doifelsefieldbody % compatibility / will be dropped
+\aliased\let\doifelsefield \doifelsefieldbody % compatibility / will be dropped
+
+%D We still support the traditional method of defining fields:
+%D
+%D \starttyping
+%D \definefield [name] [type] [category] [values] [default]
+%D
+%D \definefield [WWWW] [text] [textsetup] [default text]
+%D \definefield [XXXX] [push] [pushsetup] [yes,no] [yes]
+%D \definefield [XXXX] [check] [checksetup] [yes,no] [yes]
+%D \definefield [YYYY] [combo] [combosetup] [a,b,c,d] [b]
+%D \definefield [ZZZZ] [radio] [radiosetup] [W,X,Y,Z] [Y]
+%D
+%D \definesubfield [W] [subsetup] [p,q]
+%D \definesubfield [X,Y] [subsetup] [p,r]
+%D \definesubfield [Z] [subsetup] [y,z]
+%D
+%D evt \definemainfield ... wanneer geplaatst voor subs gegeven
+%D
+%D \clonefield [XXXX] [XX,YY] [mysetup] [on,off]
+%D \clonefield [Z] [AA,BB] [somesetup] [true,false]
+%D \clonefield [Z] [CC,DD] [anothersetup]
+%D
+%D \copyfield [XXXX] [PP,QQ,RR]
+%D \stoptyping
+%D
+%D Keep in mind that you can also use \type {\definefieldbody} to achieve the same.
+
+\permanent\tolerant\protected\def\definefield[#tag]#spacer[#type]#spacer[#category]#spacer[#values]#spacer[#default]%
+ {\definefieldbody[#tag][\c!type=#type,\c!category=#category,\c!values={#values},\c!default={#default}]}
+
+\permanent\tolerant\protected\def\definesubfield[#tag]#spacer[#category]#spacer[#values]%
+ {\definefieldbody[#tag][\c!type=sub,\c!category=#category,\c!values={#values}]}
+
+\permanent\tolerant\protected\def\clonefield[#parent]#spacer[#tag]#spacer[#category]#spacer[#values]%
+ {\definefieldbody[#tag][#parent][\c!category=#category,\c!values={#values}]}
+
+\permanent\tolerant\protected\def\copyfield[#parent]#spacer[#tag]%
+ {\definefieldbody[#tag][#parent]}
+
+\aliased\let\definemainfield\definefield % obsolete !
+
+%D We hook fields into the (viewer based) layering mechanism
+%D (implemented as properties).
+
+\appendtoks
+ \let\currentfieldcategory\empty
+ \doifelse{\interactionparameter\c!fieldlayer}\v!auto
+ {\setupcurrentfieldcategory[\c!fieldlayer=\currentviewerlayer]}%
+ {\setupcurrentfieldcategory[\c!fieldlayer=]}%
+\to \everysetupinteraction
+
+\setupinteraction
+ [\c!fieldlayer=\v!auto] % auto by default
+
+%D The \type {\fieldbody} is the more bare one. One step further goes \type
+%D {\fitfield}, in fact it (now) uses a dedicated instance of framed: \type
+%D {fitfieldframed}.
+%D
+%D \starttyping
+%D \ruledhbox{\fieldbody[Email][height=\strutht,depth=\strutdp,style=normal]}
+%D \ruledhbox{\fitfield[Email][height=\strutht,depth=\strutdp,style=normal]}
+%D \ruledhbox{\fitfield[buttona]}
+%D \stoptyping
+
+\newbox\b_scrn_field_fit_symbol
+
+\defineframed
+ [fitfieldframed]
+ [\c!strut=\v!no,
+ \c!frame=off,
+ \c!offset=\v!overlay,
+ \c!align=]
+
+\permanent\tolerant\protected\def\fitfield[#tag]#spacer[#settings]%
+ {\iflocation
+ \begingroup
+ \setbox\b_scrn_field_fit_symbol\hbox\bgroup
+ \normalexpanded{\symbol[\clf_getdefaultfieldvalue{#tag}]}%
+ \egroup
+ \fitfieldframed
+ {\fieldbody[#tag]
+ [\c!width=\wd\b_scrn_field_fit_symbol,
+ \c!height=\ht\b_scrn_field_fit_symbol,
+ \c!depth=\dp\b_scrn_field_fit_symbol,
+ #settings]}%
+ \endgroup
+ \fi}
+
+%D The traditional field command does some labeling and boxing:
+
+\installcorenamespace{fieldlabel}
+\installcorenamespace{fieldcontent}
+\installcorenamespace{fieldtotal}
+
+\installparameterhandler \??fieldlabel {fieldlabelframed}
+\installparameterhandler \??fieldcontent {fieldcontentframed}
+\installparameterhandler \??fieldtotal {fieldtotalframed}
+
+\installparametersethandler \??fieldcontent {fieldcontentframed}
+
+\installsetuphandler \??fieldlabel {fieldlabelframed}
+\installsetuphandler \??fieldcontent {fieldcontentframed}
+\installsetuphandler \??fieldtotal {fieldtotalframed}
+
+\installinheritedframed {fieldlabelframed}
+\installinheritedframed {fieldcontentframed}
+\installinheritedframed {fieldtotalframed}
+
+\setupfieldcontentframed
+ [\c!align=\v!flushleft,
+ \c!strut=\v!no,
+ \s!parent=\??regularframed] % needs checking
+
+\setupfieldcontentframed % independent
+ [\c!alternative=\v!normal,
+ \c!type=\v!line,
+ \c!width=5em,
+ \c!height=\lineheight,
+ \c!depth=\zeropoint,
+ \c!align=\v!flushleft,
+ \c!option=\v!printable,
+ \c!n=1024]
+
+\setupfieldlabelframed
+ [\c!style=,
+ \c!color=,
+ \c!align=\v!flushleft,
+ \s!parent=\??regularframed] % needs checking
+
+\setupfieldtotalframed
+ [%\c!alternative={\v!label,\v!frame,\v!horizontal},
+ \c!strut=\v!no,
+ \c!align=,
+ \s!parent=\??regularframed] % needs checking
+
+% \setupcurrent
+
+\permanent\tolerant\protected\def\setupfield[#tag]#spacer[#variant]#spacer[#totalsettings]#spacer[#labelsettings]#spacer[#fieldsettings]%
+ {\ifarguments\or\or
+ \definefieldcategory[#tag][\s!parent=\??fieldcontent#tag,#variant]%
+ \setupfieldtotalframed [#tag][\s!parent=\??fieldtotal]%
+ \setupfieldlabelframed [#tag][\s!parent=\??fieldlabel]%
+ \setupfieldcontentframed[#tag][\s!parent=\??fieldcontent,#variant]%
+ \or
+ \definefieldcategory[#tag][\s!parent=\??fieldcontent#tag,#totalsettings]%
+ \setupfieldtotalframed [#tag][\s!parent=\??fieldtotal,\c!alternative={#variant}]%
+ \setupfieldlabelframed [#tag][\s!parent=\??fieldlabel]%
+ \setupfieldcontentframed[#tag][\s!parent=\??fieldcontent,#totalsettings]%
+ \or
+ \definefieldcategory[#tag][\s!parent=\??fieldcontent#tag,#labelsettings]%
+ \setupfieldtotalframed [#tag][\s!parent=\??fieldtotal,\c!alternative={#variant},#totalsettings]%
+ \setupfieldlabelframed [#tag][\s!parent=\??fieldlabel]%
+ \setupfieldcontentframed[#tag][\s!parent=\??fieldcontent,#labelsettings]%
+ \or
+ \definefieldcategory[#tag][\s!parent=\??fieldcontent#tag,#fieldsettings]%
+ \setupfieldtotalframed [#tag][\s!parent=\??fieldtotal,\c!alternative={#variant},#totalsettings]%
+ \setupfieldlabelframed [#tag][\s!parent=\??fieldlabel,#labelsettings]%
+ \setupfieldcontentframed[#tag][\s!parent=\??fieldcontent,#fieldsettings]%
+ \fi}
+
+\permanent\tolerant\protected\def\setupfields[#variant]#spacer[#totalsettings]#spacer[#labelsettings]#spacer[#fieldsettings]%
+ {\ifarguments\or
+ \setupfieldtotalframed [#variant]%
+ \or
+ \setupfieldtotalframed [#variant]%
+ \setupfieldcontentframed[#totalsettings]%
+ \or
+ \setupfieldtotalframed [#variant]%
+ \setupfieldlabelframed [#totalsettings]%
+ \setupfieldcontentframed[#labelsettings]%
+ \or
+ \setupfieldtotalframed [\c!alternative={#variant},#totalsettings]%
+ \setupfieldlabelframed [#labelsettings]%
+ \setupfieldcontentframed[#fieldsettings]%
+ \fi}
+
+% just to get the chain right for no category:
+
+\definefieldcategory [][\s!parent=\??fieldcontent]
+%setupfieldtotalframed [][\s!parent=\??fieldtotal]
+%setupfieldlabelframed [][\s!parent=\??fieldlabel]
+%setupfieldcontentframed[][\s!parent=\??fieldcontent]
+
+% no longer supported:
+
+% \let\resetfields\relax
+
+\def\scrn_field_load_scripts{\useJSscripts[fld]\glet\scrn_field_load_scripts\relax}
+
+\newconditional\fieldlabelshown
+\newconditional\fieldframeshown
+\newconditional\fieldisvertical
+\newconditional\fieldishorizontal
+
+\mutable\let\currentfieldtotalframed \empty
+\mutable\let\currentfieldlabelframed \empty
+\mutable\let\currentfieldcontentframed\empty
+
+\mutable\let\fieldtotalframedparameterhash \empty % weird that we have to flag this
+\mutable\let\fieldlabelframedparameterhash \empty % idem
+\mutable\let\fieldcontentframedparameterhash\empty % idem
+
+\permanent\tolerant\protected\def\field[#tag]#spacer[#label]% can be sped up with \setupcurrentfieldtotalframed etc
+ {\iflocation
+ \dontleavehmode
+ \begingroup
+ \scrn_field_load_scripts
+ \edef\currentfieldbody {#tag}%
+ \edef\currentfieldlabel {#label}%
+ \edef\currentfieldcategory{\clf_getfieldcategory{#tag}}%
+ \ifempty\currentfieldlabel
+ \let\currentfieldlabel\currentfieldbody
+ \fi
+ \ifempty\currentfieldcategory
+ \setupfieldtotalframed [\currentfieldbody][\s!parent=\??fieldtotal]%
+ \setupfieldlabelframed [\currentfieldbody][\s!parent=\??fieldlabel]%
+ \setupfieldcontentframed[\currentfieldbody][\s!parent=\??fieldcontent]%
+ \definefieldcategory [\currentfieldbody]%
+ \setupfieldbody [\currentfieldbody][\c!category=\currentfieldbody]%
+ \let\currentfieldcategory\currentfieldbody
+ \fi
+ \let\currentfieldtotalframed \currentfieldcategory
+ \let\currentfieldlabelframed \currentfieldcategory
+ \let\currentfieldcontentframed\currentfieldcategory
+ \scrn_field_analyze_setups
+ \ifconditional\fieldframeshown
+ \inheritedfieldtotalframedframed % lower framedoffset
+ \bgroup
+ \else
+ \vbox
+ \bgroup
+ \fi
+ \dontcomplain
+ \ifconditional\fieldlabelshown
+ \scrn_field_set_label_box
+ \fi
+ \scrn_field_set_content_box
+ \ifconditional\fieldlabelshown
+ \ifconditional\fieldisvertical
+ \scrn_field_flush_vertical
+ \else
+ \scrn_field_flush_horizontal
+ \fi
+ \else
+ \scrn_field_flush_content
+ \fi
+ \egroup
+ \endgroup
+ \fi}
+
+% opties: veld, label, kader, vertikaal/horizontaal
+
+\newbox\b_scrn_field_label
+\newbox\b_scrn_field_content
+
+% lower framedoffset
+
+\def\scrn_field_set_label_box
+ {\setbox\b_scrn_field_label\hbox
+ {\reshapeframeboxtrue % else wrong dimensions % still needed?
+ \inheritedfieldlabelframedframed{\currentfieldlabel}}}
+
+% \c!fieldoffset=-\framedoffset,\c!fieldbackgroundcolor=,
+% \hbox{\lower\@@fdfieldoffset\hbox{\typesetfield}}
+
+\def\scrn_field_set_content_box
+ {\setbox\b_scrn_field_content\hbox
+ {\reshapeframeboxtrue % else wrong dimensions (to be checked)
+ \doifnothing{\fieldcontentframedparameter\c!height}
+ {\ifconditional\fieldisvertical
+ \setfieldcontentframedparameter\c!height{6ex}%
+ \orelse\ifconditional\fieldishorizontal
+ \setfieldcontentframedparameter\c!height{\vsize}%
+ \else
+ \setfieldcontentframedparameter\c!height{2cm}%
+ \fi}%
+ \doifnothing{\fieldcontentframedparameter\c!width}
+ {\ifconditional\fieldisvertical
+ \setfieldcontentframedparameter\c!width{\hsize}%
+ \orelse\ifconditional\fieldishorizontal
+ \setfieldcontentframedparameter\c!width{20em}%
+ \else
+ \setfieldcontentframedparameter\c!width{2cm}%
+ \fi}%
+ \inheritedfieldcontentframedframed % lower framedoffset
+ {\fieldbody
+ [\currentfieldbody]
+ [\c!width=\framedwidth,\c!height=\framedheight]}}}
+
+
+\def\scrn_field_flush_vertical
+ {\vbox
+ {\copy\b_scrn_field_label
+ \fieldtotalframedparameter\c!inbetween
+ \copy\b_scrn_field_content}}
+
+\def\scrn_field_flush_horizontal
+ {\hbox
+ {\vbox \ifdim\ht\b_scrn_field_content>\ht\b_scrn_field_label to \ht\b_scrn_field_content \fi
+ {\fieldtotalframedparameter\c!before
+ \copy\b_scrn_field_label
+ \fieldtotalframedparameter\c!after}%
+ \hskip\fieldtotalframedparameter\c!distance
+ \vbox \ifdim\ht\b_scrn_field_label>\ht\b_scrn_field_content to \ht\b_scrn_field_label \fi
+ {\fieldtotalframedparameter\c!before
+ \box\b_scrn_field_content
+ \fieldtotalframedparameter\c!after}}}
+
+\def\scrn_field_flush_content
+ {\box\b_scrn_field_content}
+
+%D todo: replace \processallactionsinset
+
+\def\scrn_field_analyze_setups
+ {\setfalse\fieldlabelshown
+ \setfalse\fieldframeshown
+ \setfalse\fieldishorizontal
+ \setfalse\fieldisvertical
+ \normalexpanded{\processallactionsinset[\fieldtotalframedparameter\c!alternative]}
+ [ \v!reset=>\setfalse\fieldlabelshown
+ \setfalse\fieldframeshown
+ \setfalse\fieldishorizontal
+ \setfalse\fieldisvertical,
+ \v!label=>\settrue\fieldlabelshown,
+ \v!frame=>\settrue\fieldframeshown,
+ \v!horizontal=>\settrue\fieldishorizontal,
+ \v!vertical=>\settrue\fieldisvertical]%
+ \ifconditional\fieldisvertical
+ \setupfieldtotalframed[\c!distance=\zeropoint,\c!inbetween=\vskip\d_framed_local_offset,\c!align=\v!right,\c!width=20em]%
+ \orelse\ifconditional\fieldishorizontal
+ \setupfieldtotalframed[\c!distance=\d_framed_local_offset,\c!inbetween=,\c!align=\c!left,\c!height=10ex]%
+ \else
+ \setupfieldtotalframed[\c!distance=\zeropoint,\c!inbetween=,\c!align=\c!left]%
+ \fi
+ \setupfieldtotalframed[\c!n=,\c!before=,\c!after=\vss,\c!style=,\c!color=]}
+
+%D Common stuff (obsolete)
+
+\newcount\c_scrn_field_system_n
+
+\permanent\def\currentsystemfield{sys::\number\c_scrn_field_system_n}
+
+\permanent\protected\def\nextsystemfield
+ {\global\advance\c_scrn_field_system_n\plusone}
+
+%D \CONTEXT\ had tooltips right from the moment that it supported fields. Due to the
+%D at that moment somewhat limited \PDF\ specification, they were implemented using
+%D \JAVASCRIPT, but nowadays more kind of actions are supported, so we can do
+%D without. The \MKIV\ version also supports definition of tooltips and
+%D configuration.
+%D
+%D \starttyping
+%D before \tooltip[right]{inbetween}{a very nice tip} after\par
+%D before \tooltip[align=normal]{inbetween}{a very\\nice tip} after\par
+%D before \tooltip[middle]{inbetween}{a very nice tip} after\par
+%D before \tooltip[left]{inbetween}{a very nice tip} after\par
+%D \stoptyping
+
+\newbox \b_scrn_tooltip_anchor
+\newbox \b_scrn_tooltip_text
+\newcount\c_scrn_tooltip_n
+
+\installcorenamespace{tooltip}
+
+\installframedcommandhandler \??tooltip {tooltip} \??tooltip
+
+\setuptooltip
+ [\c!location=\v!right,
+ \c!frame=\v!off,
+ \c!offset=.1ex,
+ \c!background=\v!color,
+ \c!backgroundcolor=gray]
+
+\appendtoks
+ \frozen\instance\setuevalue\currenttooltip{\scrn_tooltip_direct{\currenttooltip}}%
+\to \everydefinetooltip
+
+\protected\def\scrn_tooltip_direct#tag%
+ {\def\currenttooltip{#tag}%
+ \iflocation
+ \expandafter\scrn_tooltip_indeed
+ \else
+ \expandafter\scrn_tooltip_ignore
+ \fi}
+
+\tolerant\def\scrn_tooltip_ignore[#settings]#:#anchortext#tiptext%
+ {#anchortext}
+
+\mutable\let\currenttooltipname\empty
+
+\tolerant\def\scrn_tooltip_indeed[#settings]#:#anchortext#tiptext% a more modern aproach (push buttons)
+ {\dontleavehmode \hbox \bgroup
+ \dontcomplain
+ \global\advance\c_scrn_tooltip_n\plusone
+ \edef\currenttooltipname{tooltip:\number\c_scrn_tooltip_n}%
+ \setbox\b_scrn_tooltip_anchor\hbox
+ {\strut#anchortext}%
+ \doifelseassignment{#settings}
+ {\setupcurrenttooltip[#settings]}%
+ {\setupcurrenttooltip[\c!location=#settings]}%
+ \setbox\b_scrn_tooltip_text\hbox
+ {\lettooltipparameter\c!location\empty
+ \inheritedtooltipframed{#tiptext}}%
+ \definesymbol
+ [\currenttooltipname:txt]
+ [\copy\b_scrn_tooltip_text]%
+ \definefieldbody
+ [\currenttooltipname:txt]
+ [\c!type=push,
+ \c!width=\wd\b_scrn_tooltip_text,
+ \c!height=\ht\b_scrn_tooltip_text,
+ \c!depth=\dp\b_scrn_tooltip_text,
+ \c!option=\v!hidden,
+ \c!values=\currenttooltipname:txt]%
+ \setbox\b_scrn_tooltip_text\hbox
+ {\fieldbody[\currenttooltipname:txt]}%
+ \setbox\b_scrn_tooltip_text\hbox
+ {\strut\lower\dimexpr.25ex+\ht\b_scrn_tooltip_text\relax\box\b_scrn_tooltip_text}%
+ \edef\p_location{\tooltipparameter\c!location}%
+ \ifx\p_location\v!left
+ \hsmashed{\hskip\wd\b_scrn_tooltip_anchor\llap{\box\b_scrn_tooltip_text}}%
+ \orelse\ifx\p_location\v!middle
+ \hsmashed to \wd\b_scrn_tooltip_anchor{\hss\box\b_scrn_tooltip_text\hss}%
+ \else
+ \hsmashed{\box\b_scrn_tooltip_text}%
+ \fi
+ \definesymbol
+ [\currenttooltipname:but]
+ [\hphantom{\copy\b_scrn_tooltip_anchor}]%
+ \definefieldbody
+ [\currenttooltipname:but]
+ [\c!type=push,
+ \c!regionin=action(show{\currenttooltipname:txt}),
+ \c!regionout=action(hide{\currenttooltipname:txt}),
+ \c!width=\wd\b_scrn_tooltip_anchor,
+ \c!height=\ht\b_scrn_tooltip_anchor,
+ \c!depth=\dp\b_scrn_tooltip_anchor]%
+ \hsmashed{\fieldbody[\currenttooltipname:but]}%
+ \egroup
+ #anchortext}% when hyphenated the text wil stick out ... such are fields and we cannot use a link here
+
+\definetooltip[tooltip]
+
+%D From messages on the mailing list we can conclude that fieldstacks are used so we
+%D keep them in the core:
+%D
+%D \starttyping
+%D \definesymbol[one] [one]
+%D \definesymbol[two] [two]
+%D \definesymbol[three][three]
+%D
+%D \definefieldstack[mine][one,two,three]
+%D \fieldstack[mine]
+%D \fieldstack[mine]
+%D
+%D \goto{walk field}[Walk{mine}]
+%D \stoptyping
+
+% todo: expand #symbols
+
+\installcorenamespace {fieldstack}
+
+\permanent\tolerant\protected\def\definefieldstack[#tag]#spacer[#symbols]#spacer[#settings]%
+ {\ifcsname\??fieldstack#tag\endcsname
+ % already done
+ \else
+ %setgvalue{\??fieldstack#tag}{\scrn_fieldstack_construct[#tag][#symbols][#settings]}%
+ \setxvalue{\??fieldstack#tag}{\scrn_fieldstack_construct[#tag][#symbols][\normalunexpanded{#settings}]}%
+ \fi}
+
+\permanent\tolerant\protected\def\fieldstack[#tag]#spacer[#symbols]#spacer[#settings]%
+ {\ifparameter#symbols\or
+ \definefieldstack[#tag][#symbols][#settings]%
+ \fi
+ \csname\??fieldstack#tag\endcsname}
+
+\newbox\b_scrn_fieldstack_box
+
+\definesymbol[\s!empty][]
+
+\def\scrn_fieldstack_add#tag#settings#symbol%
+ {\advance\scratchcounter\plusone
+ \edef\currentfieldstackname{#tag:\number\scratchcounter}%
+ \ifnum\scratchcounter=\fieldcategoryparameter\c!start\relax
+ \definefieldbody[\currentfieldstackname][\c!type=check,\c!values={#symbol,\s!empty},\c!default={#symbol}]%
+ \else
+ \definefieldbody[\currentfieldstackname][\c!type=check,\c!values={#symbol,\s!empty},\c!default=\s!empty]%
+ \fi
+ \setbox\b_scrn_fieldstack_box\hbox{\symbol[#symbol]}%
+ \setcollector
+ [fieldstack]
+ {\fieldbody
+ [\currentfieldstackname]
+ [\c!option={\v!readonly},
+ \c!width=\wd\b_scrn_fieldstack_box,
+ \c!height=\ht\b_scrn_fieldstack_box,
+ \c!depth=\dp\b_scrn_fieldstack_box,
+ #settings]}}
+
+\protected\def\scrn_fieldstack_construct[#tag][#symbols][#settings]% start=n, 0 == leeg
+ {\iflocation
+ \dontleavehmode
+ \begingroup
+ \setupfieldcategory[\c!start=1,#settings]% was just \??fieldcategory
+ \scrn_field_load_scripts
+ \definecollector
+ [fieldstack]%
+ [\c!corner=\v!middle,
+ \c!location=\v!middle]%
+ \scratchcounter\zerocount
+ \processcommalist[#symbols]{\scrn_fieldstack_add{#tag}{#settings}}%
+ \flushcollector[fieldstack]%
+ \endgroup
+ \fi}
+
+%D Another goodie. Two actions can be hookes into an overlay.
+%D
+%D \starttyping
+%D \defineviewerlayer[test]
+%D
+%D \startviewerlayer[test]Hide Me\stopviewerlayer
+%D
+%D \defineoverlay
+%D [WithTest]
+%D [{\overlayrollbutton[HideLayer{test}][VideLayer{test}]}]
+%D
+%D \framed[background=WithTest]{toggle}
+%D \stoptyping
+
+\newcount\c_scrn_rollbutton_n
+
+\permanent\tolerant\protected\def\overlayrollbutton[#1]#*[#2]#;#=#=%
+ {\iflocation
+ \bgroup
+ \global\advance\c_scrn_rollbutton_n_button\plusone
+ \global\advance\c_scrn_rollbutton_n_symbol\plusone
+ \definesymbol
+ [rollsymbol:\number\c_scrn_rollbutton_n_symbol]
+ [{\framed[\c!frame=\v!off,\c!width=\overlaywidth,\c!height=\overlayheight]{}}]%
+ % \definefieldbody
+ % [rollbutton:\number\c_scrn_rollbutton_n_button]
+ % [\c!type=push,
+ % \c!values=rollsymbol:\number\c_scrn_rollbutton_n_symbol,
+ % \c!default=rollsymbol:\number\c_scrn_rollbutton_n_symbol]%
+ % \fitfield
+ % [rollbutton:\number\c_scrn_rollbutton_n_button]%
+ % [\c!regionin={#regionin},
+ % \c!regionout={#regionout}]%
+ %
+ \definefield
+ [rollbutton:\number\c_scrn_rollbutton_n_button][push][rollbutton]
+ [rollsymbol:\number\c_scrn_rollbutton_n_symbol]%
+ \fitfield
+ [rollbutton:\number\c_scrn_rollbutton_n_button]%
+ [\c!regionin={#1#3},\c!regionout={#2#4}]%
+ %
+ \egroup
+ \fi}
+
+% \protect \endinput % THE FOLLOWING CODE IS NOT CHECKED
+
+%D I will redo these when I need them.
+
+% \setupinteraction[state=start]
+%
+% \definepushbutton [reset]
+%
+% \startuniqueMPgraphic{whatever}{color}
+% fill fullcircle xysized (OverlayWidth,OverlayHeight) withcolor \MPvar{color} ;
+% \stopuniqueMPgraphic
+%
+% \definepushsymbol [reset] [n] [\uniqueMPgraphic{whatever}{color=red}]
+% \definepushsymbol [reset] [r] [\uniqueMPgraphic{whatever}{color=green}]
+% \definepushsymbol [reset] [d] [\uniqueMPgraphic{whatever}{color=blue}]
+%
+% \starttext
+% \startTEXpage
+% \pushbutton [reset] [page(2)]
+% \stopTEXpage
+% \startTEXpage
+% \pushbutton [reset] [page(1)]
+% \stopTEXpage
+% \stoptext
+
+\newcount\c_scrn_pushbutton_n
+
+\permanent\tolerant\protected\def\definepushbutton[#tag]#spacer[#settings]%
+ {\scrn_pushbutton_define_variant{#tag}{n}{push}%
+ \scrn_pushbutton_define_variant{#tag}{r}{\symbol[pushsymbol:#tag:n]}%
+ \scrn_pushbutton_define_variant{#tag}{d}{\symbol[pushsymbol:#tag:r]}%
+ \setvalue{pushbutton:#tag}{\scrn_pushbutton_handle{#tag}{#settings}}}
+
+\def\scrn_pushbutton_define_variant#tag#variant#content%
+ {\doifelsesymboldefined{pushsymbol:#tag:#variant}
+ \donothing
+ {\definesymbol[pushsymbol:#tag:#variant][{#content}]}}
+
+\def\scrn_pushbutton_handle#tag#settings#reference%
+ {\bgroup
+ \global\advance\c_scrn_pushbutton_n\plusone
+ \setupfield
+ [pushbutton]%
+ [\c!frame=\v!overlay,%
+ \c!offset=\v!overlay,%
+ \c!clickout={#reference},%
+ #settings]%
+ \definefield
+ [pushbutton:\number\c_scrn_pushbutton_n]%
+ [push]%
+ [pushbutton]%
+ [pushsymbol:#tag:n,pushsymbol:#tag:r,pushsymbol:#tag:d]%
+ \fitfield
+ [pushbutton:\number\c_scrn_pushbutton_n]%
+ \egroup}
+
+\permanent\tolerant\protected\def\definepushsymbol[#tag]#spacer[#variant]% [#reference]
+ {\definesymbol[pushsymbol:#tag:#variant]}
+
+\permanent\tolerant\def\pushbutton[#tag]#spacer[#variant]%
+ {\executeifdefined{pushbutton:#tag}\gobbleoneargument{#variant}}
+
+%D We plug into the menu system
+
+\permanent\permanent\protected\def\scrn_menu_psh_start[#reference]#text\stoppsh
+ {\starttxt\pushbutton[\currentinteractionmenu][#reference]\stoptxt}
+
+\permanent\permanent\protected\def\scrn_menu_psh_direct[#reference]#text\\
+ {\scrn_menu_psh_start[#reference]\stoprob}
+
+\aliased\let\startpsh\relax % maybe mutable
+\aliased\let\stoppsh \relax % maybe mutable
+\aliased\let\psh \relax % maybe mutable
+
+\appendtoks
+ \enforced\let\startpsh\scrn_menu_psh_start
+ \enforced\let\stoppsh \relax
+ \enforced\let\psh \scrn_menu_psh_direct
+\to \everysetmenucommands
+
+%D Another goodie: (unchecked in \MKIV)
+
+% calls:
+% {..} [JS..]
+% [left] {..} [JS..]
+% [a=b] {..} [JS..]
+% [left] [a=b] {..} [JS..]
+%
+% \setupbuttons[offset=0pt,frame=off] % alternative=hidden
+%
+% \rollbutton {Manuals} [JS(Goto_File{show-man.pdf})]
+% \rollbutton {Articles} [JS(Goto_File{show-art.pdf})]
+% \rollbutton {Papers} [JS(Goto_File{show-pap.pdf})]
+% \rollbutton {Presentations} [JS(Goto_File{show-pre.pdf})]
+% \rollbutton {Resources} [JS(Goto_File{show-res.pdf})]
+%
+% \rob [JS(...)] bla bla \\
+
+% \definecolor[rollover:n][red]
+% \definecolor[rollover:r][green]
+% \definecolor[rollover:d][blue]
+
+\definepalet
+ [rollover]
+ [n=red,
+ r=green,
+ d=blue]
+
+\newcount\c_scrn_rollbutton_n_button
+\newcount\c_scrn_rollbutton_n_symbol
+
+\permanent\def\lastrollbuttonindex{\the\c_scrn_rollbutton_n_button}
+
+\setupfield
+ [rollbutton]
+ [\c!frame=\v!off,
+ \c!offset=\v!overlay]
+
+\let\scrn_rollbutton_symbol\relax
+
+\def\scrn_rollbutton_symbol_m
+ {\scrn_rollbutton_symbol_indeed
+ \interactionmenuparameter
+ \inheritedinteractionmenuframed
+ \setinteractionmenuparameter
+ \useinteractionmenustyleandcolor}
+
+\def\scrn_rollbutton_symbol_b
+ {\scrn_rollbutton_symbol_indeed
+ \buttonparameter
+ \inheritedbuttonframed
+ \setbuttonparameter
+ \usebuttonstyleandcolor}
+
+\permanent\tolerant\protected\def\rollbutton[#tag]#spacer[#settings]#:#text[#reference]%
+ {\dontleavehmode
+ \bgroup
+ \global\advance\c_scrn_rollbutton_n_button\plusone
+ \global\advance\c_scrn_rollbutton_n_symbol\plusone
+ \ifparameter#tag\or
+ \ifparameter#settings\or
+ \edef\currentinteractionmenu{#tag}%
+ \setupcurrentinteractionmenu[#settings]%
+ \let\scrn_rollbutton_symbol\scrn_rollbutton_symbol_m
+ \orelse\ifhastok={#tag}%
+ \let\currentbutton\empty
+ \setupcurrentbutton[#tag]%
+ \let\scrn_rollbutton_symbol\scrn_rollbutton_symbol_b
+ \else
+ \edef\currentinteractionmenu{#tag}%
+ \let\scrn_rollbutton_symbol\scrn_rollbutton_symbol_m
+ \fi
+ \else
+ \let\scrn_rollbutton_symbol\scrn_rollbutton_symbol_b
+ \fi
+ % todo: share symbols, tricky since different dimensions (maybe \unique...)
+ \definesymbol[rollsymbol:\number\c_scrn_rollbutton_n_symbol:n][\scrn_rollbutton_symbol{n}{#text}]%
+ \definesymbol[rollsymbol:\number\c_scrn_rollbutton_n_symbol:r][\scrn_rollbutton_symbol{r}{#text}]%
+ \definesymbol[rollsymbol:\number\c_scrn_rollbutton_n_symbol:d][\scrn_rollbutton_symbol{d}{#text}]%
+ \definefield
+ [rollbutton:\number\c_scrn_rollbutton_n_button][push][rollbutton]
+ [rollsymbol:\number\c_scrn_rollbutton_n_symbol:n,%
+ rollsymbol:\number\c_scrn_rollbutton_n_symbol:r,%
+ rollsymbol:\number\c_scrn_rollbutton_n_symbol:d]%
+ \fitfield
+ [rollbutton:\number\c_scrn_rollbutton_n_button]%
+ [\c!clickout={#reference}]%
+ \egroup}
+
+\protected\def\scrn_rollbutton_symbol_indeed#getparameter#inheritedframed#setparameter#usestyleandcolor#what#text%
+ {\definecolor[rollover][rollover:#what]%
+ \doifelse{#what}{n}% ?
+ {\doifelse{#getparameter\c!alternative}\v!hidden\phantom\hbox}\hbox
+ {#setparameter\c!framecolor {rollover}%
+ #setparameter\c!backgroundcolor{rollover}%
+ #setparameter\c!color {rollover}%
+ #inheritedframed{#usestyleandcolor\c!style\c!color{#text}}}}
+
+%D We plug into the menu system
+
+\permanent\protected\def\scrn_menu_rob_start[#reference]#text\stoprob
+ {\starttxt\rollbutton[\currentinteractionmenu]{\ignorespaces#text\unskip}[#reference]\stoptxt}
+
+\permanent\protected\def\scrn_menu_rob_direct[#reference]#text\\
+ {\scrn_menu_rob_start[#reference]#text\stoprob}
+
+\aliased\let\startrob\relax % maybe mutable
+\aliased\let\stoprob \relax % maybe mutable
+\aliased\let\rob \relax % maybe mutable
+
+\appendtoks
+ \enforced\let\startrob\scrn_menu_rob_start
+ \enforced\let\stoprob \relax
+ \enforced\let\rob \scrn_menu_rob_direct
+\to \everysetmenucommands
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-hlp.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-hlp.mklx
new file mode 100644
index 00000000000..da2afd0d0c1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-hlp.mklx
@@ -0,0 +1,160 @@
+%D \module
+%D [ file=scrn-hlp,
+%D version=1998.10.10,
+%D title=\CONTEXT\ Screen Macros,
+%D subtitle=Help (Experimental),
+%D author={Hans Hagen \& Ton Otten},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Screen Macros / Help popups}
+
+%D As this functionality was in the core and as I don't know how often it is used,
+%D we'll keep it around. However, it is upgraded and usage has changed a bit. We
+%D also use some \LUA\ magic in order to avoid multiple passes.
+
+\registerctxluafile{scrn-hlp}{}
+
+%D Using help boils down to plugging the placement macro someplace visible, like:
+%D
+%D \starttyping
+%D \setuptexttexts[\centerbox{\placehelp}]
+%D \stoptyping
+%D
+%D When this is done, the following should work out okay:
+%D
+%D \starttyping
+%D test \helptext{word}{tip top 1 is somewhat longer} test
+%D test \helptext{word}{tip top 2} test
+%D
+%D \starthelptext[oeps]
+%D \input tufte
+%D \stophelptext
+%D
+%D test test \showhelp{some help}[oeps] test
+%D test test \button[location=depth]{\helpsignal{oeps}OEPS}[oeps] test
+%D
+%D test test \button[location=depth]{next}[page(2)] test
+%D
+%D \page
+%D
+%D test \helptext{word}{tip top one} test
+%D test \helptext{word}{tip top two} test
+%D \stoptyping
+%D
+%D Currently you need to use the signal in custom macros but that might change at
+%D some point.
+
+\unprotect
+
+% also status
+
+\newbox \b_scrn_help_box
+\newcount\c_scrn_help_n
+
+\definesystemattribute[help][public]
+
+\installcorenamespace{popuphelp}
+
+\installframedcommandhandler \??popuphelp {help} \??popuphelp
+
+\setuphelp
+ [\c!frame=\v!off,
+ \c!align=\v!normal,
+ \c!background=\v!color,
+ \c!backgroundcolor=gray]
+
+\appendtoks
+ \frozen\instance\protected\edefcsname \currenthelp\endcsname{\scrn_help_argument{\currenthelp}}%
+ \frozen\instance\protected\edefcsname\e!start\currenthelp\endcsname{\scrn_help_start {\currenthelp}}%
+ \frozen\instance\protected\edefcsname\e!stop \currenthelp\endcsname{\scrn_help_stop }%
+\to \everydefinehelp
+
+\mutable\let\currenthelpreference\empty
+\mutable\let\currenthelpname \empty
+
+\protected\def\scrn_help_argument#category%
+ {\def\currenthelp{#category}%
+ \global\advance\c_scrn_help_n\plusone
+ \edef\currenthelpname{help:\number\c_scrn_help_n}%
+ \doifelselocation\scrn_help_argument_indeed\scrn_help_argument_ignore}
+
+\tolerant\def\scrn_help_argument_indeed[#reference]#:#text#target%
+ {\edef\currenthelpreference{#reference}%
+ \dontleavehmode \hbox \bgroup
+ \dontcomplain
+ \setbox\b_scrn_help_box\hbox{\strut#text}%
+ \scrn_help_register{#target}%
+ \egroup % can be usernode instead
+ \goto
+ {\helpsignal{\number\c_scrn_help_n}#target}%
+ [\helpaction{\number\c_scrn_help_n}]}
+
+\tolerant\def\scrn_help_argument_ignore[#reference]#:#text#target%
+ {#target}
+
+\tolerant\protected\def\scrn_help_start#category#spacer[#reference]%
+ {\global\advance\c_scrn_help_n\plusone
+ \edef\currenthelp{#category}%
+ \edef\currenthelpreference{#reference}%
+ \edef\currenthelpname{help:\number\c_scrn_help_n}%
+ \grabbufferdatadirect\currenthelp{\e!start\currenthelp}{\e!stop\currenthelp}}
+
+\protected\def\scrn_help_stop
+ {\iflocation
+ \scrn_help_register{\getbuffer[\currenthelp]}%
+ \fi}
+
+\def\scrn_help_register#text%
+ {\setbox\b_scrn_help_box\hbox{\inheritedhelpframed{#text}}% \hpack ?
+ \definesymbol
+ [\currenthelpname]
+ [\copy\b_scrn_help_box]%
+ \definefieldbody
+ [\currenthelpname]
+ [\c!type=push,
+ \c!width=\wd\b_scrn_help_box,
+ \c!height=\ht\b_scrn_help_box,
+ \c!depth=\dp\b_scrn_help_box,
+ \c!option=\v!hidden,
+ \c!clickin=action(hide{\currenthelpname}),
+ \c!closepage=action(hide{\currenthelpname}),
+ \c!values=\currenthelpname]%
+ \setbox\b_scrn_help_box\hbox
+ {\fieldbody[\currenthelpname]}%
+ \clf_registerhelp
+ number \c_scrn_help_n
+ name {\currenthelpreference}%
+ box \b_scrn_help_box
+ \relax}
+
+\permanent\protected\def\doifelsehelp
+ {\ifcase\c_scrn_help_n
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifhelpelse\doifelsehelp
+
+\permanent\protected\def\placehelp % was \helpdata
+ {\ifinpagebody\ifcase\c_scrn_help_n\else
+ \clf_collecthelp\normalpagebox
+ \fi\fi}
+
+\permanent\def\helpreference#category{\clf_helpreference{#category}} % could be public implementors
+\permanent\def\helpaction #category{\clf_helpaction {#category}} % could be public implementors
+
+\permanent\protected\def\helpsignal#category%
+ {\hbox attr \helpattribute \helpreference{#category}{}}
+
+\permanent\protected\def\showhelp#target[#category]%
+ {\goto{\helpsignal{#category}#target}[#category]}
+
+\definehelp[\v!helptext]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-ini.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-ini.mklx
new file mode 100644
index 00000000000..c0246bc8328
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-ini.mklx
@@ -0,0 +1,254 @@
+%D \module
+%D [ file=scrn-ini,
+%D version=2011.02.27,
+%D title=\CONTEXT\ Interaction Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Interaction Macros / Initialization}
+
+\unprotect
+
+\registerctxluafile{scrn-ini}{}
+
+%D There is no interaction at all unless enabled by saying:
+%D
+%D \starttyping
+%D \setupinteraction[state=start]
+%D \stoptyping
+%D
+%D The other settings are:
+%D
+%D \showsetup{setupinteraction}
+
+\installcorenamespace{interaction}
+
+\installswitchcommandhandler \??interaction {interaction} \??interaction
+
+\appendtoks
+ \ifempty\previousinteraction
+ \checkinteractionstate
+ \orelse\ifx\currentinteraction\previousinteraction
+ \checkinteractionstate
+ \fi
+\to \everysetupinteraction
+
+\permanent\protected\def\checkinteractionstate
+ {\doifelse{\interactionparameter\c!state}\v!start
+ {\locationtrue \setsystemmode \v!interaction}%
+ {\locationfalse \resetsystemmode\v!interaction}}
+
+\permanent\def\doifelselocation % expandable
+ {\iflocation
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiflocationelse\doifelselocation
+
+\setupinteraction
+ [\c!state=\v!stop]
+
+\setupinteraction
+ [\c!title=,
+ \c!subtitle=,
+ \c!author=,
+ \c!keyword=,
+ \c!date=]
+
+\appendtoks
+ \setupinteraction % todo: remember info at the lua end (already possible)
+\to \everyjob
+
+% it makes no sense to create an environment as we will seldom have structured
+% interactions so a general start-stop will do
+%
+% \appendtoks
+% \setuevalue \currentinteraction {\scrn_interaction_direct{\currentinteraction}}%
+% \setuevalue{\e!start\currentinteraction}{\scrn_interaction_start {\currentinteraction}}%
+% \setuevalue{\e!stop \currentinteraction}{\scrn_interaction_stop }%
+% \to \everydefineinteraction
+%
+% \protected\def\scrn_interaction_direct#1%
+% {\edef\currentinteraction{#1}}
+%
+% \protected\def\scrn_interaction_start#1%
+% {\pushmacro\currentinteraction
+% \edef\currentinteraction{#1}}
+%
+% \protected\def\scrn_interaction_stop
+% {\popmacro\currentinteraction}
+%
+% \protected\def\setinteraction[#1]%
+% {\def\currentinteraction{#1}}
+%
+% \defineinteraction[\v!interaction]
+
+\permanent\protected\def\startinteraction[#1]%
+ {\pushmacro\currentinteraction
+ \edef\currentinteraction{#1}}
+
+\permanent\protected\def\stopinteraction
+ {\popmacro\currentinteraction}
+
+\permanent\protected\def\setinteraction[#1]%
+ {\def\currentinteraction{#1}}
+
+\defineinteraction % keep it simple
+ [\v!hidden]
+
+\setupinteraction
+ [\v!hidden]
+ [\c!state=\v!start,
+ \c!color=,
+ \c!contrastcolor=,
+ \c!style=,
+ \c!click=\v!off]
+
+%D As long as there a natural feeling of what can be considered
+%D hyper active or not, we have to tell users where they can
+%D possibly click. We've already seen a few macros that deal
+%D with this visualization, something we definitely do not let
+%D up to the viewer. One way of telling is using a distinctive
+%D typeface, another way is using color.
+%D
+%D There are two colors involved: one for normal hyperlinks,
+%D and one for those that point to the currentpage, the
+%D contrast color.
+
+\definecolor [interactioncolor] [r=0, g=.6, b=0]
+\definecolor [interactioncontrastcolor] [r=.8, g=0, b=0]
+
+%D The next few macros are responsible for highlighting hyper
+%D links. The first one, \type{\showlocation}, is used in those
+%D situations where the typeface is handled by the calling
+%D macro.
+
+%D When we're dealing with pure page references, contrast
+%D colors are used when we are already at the page mentioned.
+
+\permanent\protected\def\setlocationcolorspecified#1% not grouped !
+ {\ifnum#1=\plusone
+ \edef\p_contrastcolor{\interactionparameter\c!contrastcolor}%
+ \ifempty\p_contrastcolor
+ \useinteractioncolorparameter\c!color
+ \else
+ \useinteractioncolorparameter\c!contrastcolor
+ \fi
+ \else % we could just set and if > 0 set again
+ \useinteractioncolorparameter\c!color
+ \fi}
+
+\permanent\protected\def\setlocationattributesspecified#1% not grouped !
+ {\ifnum#1=\plusone
+ \edef\p_contrastcolor{\interactionparameter\c!contrastcolor}%
+ \ifempty\p_contrastcolor
+ \useinteractionstyleandcolor\c!style\c!color
+ \else
+ \useinteractionstyleandcolor\c!style\c!contrastcolor
+ \fi
+ \else % we could just set and if > 0 set again
+ \useinteractionstyleandcolor\c!style\c!color
+ \fi}
+
+\permanent\protected\def\setlocationcolorspecspecified#1#2% \resolver
+ {\ifnum#1=\plusone
+ \edef\p_contrastcolor{#2\c!contrastcolor}%
+ \ifempty\p_contrastcolor
+ \colo_helpers_activate{#2\c!color}%
+ \else
+ \colo_helpers_activate\p_contrastcolor
+ \fi
+ \else
+ \colo_helpers_activate{#2\c!color}%
+ \fi}
+
+\permanent\protected\def\setlocationcolor {\setlocationcolorspecified \referencepagestate}
+\permanent\protected\def\setlocationattributes{\setlocationattributesspecified\referencepagestate}
+\permanent\protected\def\setlocationcolorspec {\setlocationcolorspecspecified \referencepagestate}
+
+\permanent\protected\def\setlocationfont % not grouped !
+ {\useinteractionstyleparameter\c!style}
+
+\setupinteraction
+ [\c!style=\v!bold,
+ \c!color=interactioncolor,
+ \c!contrastcolor=interactioncontrastcolor]
+
+%D Identity
+
+% \newconditional\c_scrn_identity_preroll
+%
+% \installtexdirective
+% {interaction.identity.preroll}
+% {\settrue \c_scrn_identity_preroll}
+% {\setfalse\c_scrn_identity_preroll}
+%
+% \def\scrn_identity_prerolled#1%
+% {\begingroup
+% \edef\tempstring{\interactionparameter#1}%
+% \ifempty\tempstring
+% \endgroup
+% \else
+% \the\everypreroll
+% \nodestostring\tempstring{\tempstring}%
+% \normalexpanded{\endgroup\setexpandedinteractionparameter{#1}{\tempstring}}%
+% \fi}
+%
+% \def\scrn_identity_synchronize
+% {\begingroup
+% \ifconditional\c_scrn_identity_preroll
+% \scrn_identity_prerolled\c!title
+% \scrn_identity_prerolled\c!subtitle
+% \scrn_identity_prerolled\c!author
+% \scrn_identity_prerolled\c!date
+% \scrn_identity_prerolled\c!keyword
+% \fi
+% \clf_setupidentity
+% title {\interactionparameter\c!title}%
+% subtitle {\interactionparameter\c!subtitle}%
+% author {\interactionparameter\c!author}%
+% % creator {ConTeXt - \contextversion}%
+% date {\interactionparameter\c!date}%
+% keywords {\interactionparameter\c!keyword}%
+% \relax
+% \endgroup}
+
+\def\scrn_identity_synchronize
+ {\clf_setupidentity
+ title {\prerolltostring{\interactionparameter\c!title}}%
+ subtitle {\prerolltostring{\interactionparameter\c!subtitle}}%
+ author {\prerolltostring{\interactionparameter\c!author}}%
+ % creator {ConTeXt - \contextversion}% fixed
+ date {\prerolltostring{\interactionparameter\c!date}}%
+ keywords {\prerolltostring{\interactionparameter\c!keyword}}%
+ \relax}
+
+\appendtoks
+ \scrn_identity_synchronize
+\to \everysetupinteraction
+
+% this comes before starttext
+
+\def\scrn_identity_document#1#2%
+ {\doifdocumentvariable{metadata:#1}{\setupinteraction[#2=\documentvariable{metadata:#1}]}}
+
+\appendtoks % not interfaced i.e. english
+ \scrn_identity_document {title}\c!title
+ \scrn_identity_document {subject}\c!subtitle
+ \scrn_identity_document{subtitle}\c!subtitle
+ \scrn_identity_document {author}\c!author
+ \scrn_identity_document {authors}\c!author
+ \scrn_identity_document {keyword}\c!keyword
+ \scrn_identity_document{keywords}\c!keyword
+ \scrn_identity_document {date}\c!date
+\to \everystartdocument % or stop
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-pag.lmt b/Master/texmf-dist/tex/context/base/mkxl/scrn-pag.lmt
new file mode 100644
index 00000000000..210a57707db
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-pag.lmt
@@ -0,0 +1,56 @@
+if not modules then modules = { } end modules ['scrn-pag'] = {
+ version = 1.001,
+ comment = "companion to scrn-pag.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+interactions = interactions or { }
+interactions.pages = interactions.pages or { }
+local pages = interactions.pages
+
+local implement = interfaces.implement
+
+pages.setupcanvas = layouts.setupcanvas
+
+local codeinjections = backends.codeinjections
+
+function pages.setpagetransition(specification)
+ codeinjections.setpagetransition(specification)
+end
+
+implement {
+ name = "setupcanvas",
+ actions = pages.setupcanvas,
+ arguments = {
+ {
+ { "mode" },
+ { "singlesided", "boolean" },
+ { "doublesided", "boolean" },
+ { "leftoffset", "dimen" },
+ { "topoffset", "dimen" },
+ { "width", "dimen" },
+ { "height", "dimen" },
+ { "paperwidth", "dimen" },
+ { "paperheight", "dimen" },
+ { "cropoffset", "dimen" },
+ { "bleedoffset", "dimen" },
+ { "artoffset", "dimen" },
+ { "trimoffset", "dimen" },
+ { "copies", "integer" },
+ { "print", "string" }, -- , tohash
+ }
+ }
+}
+
+implement {
+ name = "setpagetransition",
+ actions = pages.setpagetransition,
+ arguments = {
+ {
+ { "n" },
+ { "delay", "integer" },
+ }
+ }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-pag.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-pag.mklx
new file mode 100644
index 00000000000..09c542678fd
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-pag.mklx
@@ -0,0 +1,324 @@
+%D \module
+%D [ file=scrn-pag,
+%D version=1998.01.15,
+%D title=\CONTEXT\ Screen Macros,
+%D subtitle=Pages, % moved code
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% pagecomments will be done differently
+
+\writestatus{loading}{ConTeXt Screen Macros / Pages}
+
+\registerctxluafile{scrn-pag}{autosuffix}
+
+\unprotect
+
+\installcorenamespace{interactionscreen}
+
+\installparameterhandler \??interactionscreen {interactionscreen}
+\installsetuphandler \??interactionscreen {interactionscreen}
+
+\newdimen\canvaswidth
+\newdimen\canvasheight
+\newdimen\canvasbackoffset
+\newdimen\canvastopoffset
+\newdimen\canvasmaxwidth
+\newdimen\canvasmaxheight
+\newdimen\canvascropoffset
+\newdimen\canvastrimoffset
+\newdimen\canvasbleedoffset
+\newdimen\canvasartoffset
+
+\mutable\let\currentinteractionscreendelay \empty
+\mutable\let\currentinteractionscreenheight\empty
+\mutable\let\currentinteractionscreenwidth \empty
+
+\newconditional\c_scrn_canvas_tight_page
+
+\def\scrn_canvas_calculate
+ {\begingroup
+ \edef\currentinteractionscreenwidth {\interactionscreenparameter\c!width }%
+ \edef\currentinteractionscreenheight{\interactionscreenparameter\c!height}%
+ \canvasbackoffset\backspace
+ \canvastopoffset\topspace
+ \donetrue
+ \ifx\currentinteractionscreenwidth\v!max
+ \global\canvaswidth\printpaperwidth
+ \orelse\ifx\currentinteractionscreenwidth\v!fit
+ \donefalse
+ \global\canvaswidth\dimexpr
+ \leftcombitotal
+ + \makeupwidth
+ + \rightcombitotal
+ + 2\dimexpr
+ \interactionscreenparameter\c!backspace
+ + \interactionscreenparameter\c!horoffset
+ \relax
+ \relax
+ \orelse\ifx\currentinteractionscreenwidth\v!tight
+ \donefalse
+ \ifdim\backspace>\canvaswidth
+ \ifdim\backspace>\zeropoint\relax
+ \global\advance\canvasbackoffset -\canvaswidth
+ \fi
+ \fi
+ \global\advance\canvaswidth\dimexpr
+ \makeupwidth
+ + \rightcombitotal
+ + 2\dimexpr
+ \interactionscreenparameter\c!backspace
+ + \interactionscreenparameter\c!horoffset
+ \relax
+ \relax
+ \else
+ \donefalse
+ \global\canvaswidth\currentinteractionscreenwidth
+ \fi
+ \ifx\currentinteractionscreenheight\v!max
+ \global\canvasheight\printpaperheight
+ \orelse\ifx\currentinteractionscreenheight\v!fit
+ \donefalse
+ \global\canvasheight\dimexpr
+ \topheight
+ + \topdistance
+ + \makeupheight
+ + \bottomdistance
+ + \bottomheight
+ + 2\dimexpr
+ \interactionscreenparameter\c!topspace
+ + \interactionscreenparameter\c!veroffset
+ \relax
+ \relax
+ \orelse\ifx\currentinteractionscreenheight\v!tight
+ \donefalse
+ \global\canvasheight\dimexpr\topheight+\topdistance\relax
+ \ifdim\topspace>\canvasheight
+ \ifdim\topspace>\zeropoint\relax
+ \global\advance\canvastopoffset -\canvasheight
+ \fi
+ \fi
+ \global\advance\canvasheight\dimexpr
+ \makeupheight
+ + \bottomdistance
+ + \bottomheight
+ + 2\dimexpr
+ \interactionscreenparameter\c!topspace
+ + \interactionscreenparameter\c!veroffset
+ \relax
+ \relax
+ \else
+ \donefalse
+ \global\canvasheight\currentinteractionscreenheight
+ \fi
+ \ifconditional\c_page_layouts_location_is_set
+ \global\setfalse\c_scrn_canvas_tight_page
+ \fi
+ \ifconditional\c_scrn_canvas_tight_page
+ \global\canvasmaxwidth \paperwidth
+ \global\canvasmaxheight\paperheight
+ \else
+ \global\canvasmaxwidth \printpaperwidth
+ \global\canvasmaxheight\printpaperheight
+ \fi
+ % new:
+ \ifdone
+ \global\canvascropoffset \layoutcropoffset
+ \global\canvastrimoffset \layouttrimoffset
+ \global\canvasbleedoffset\layoutbleedoffset
+ \global\canvasartoffset \layoutartoffset
+ \else
+ \global\canvascropoffset \zeropoint
+ \global\canvastrimoffset \zeropoint
+ \global\canvasbleedoffset\zeropoint
+ \global\canvasartoffset \zeropoint
+ \fi
+ \endgroup}
+
+\newdimen\layoutcropoffset
+\newdimen\layouttrimoffset
+\newdimen\layoutbleedoffset
+\newdimen\layoutartoffset
+
+\appendtoks
+ \edef\p_cropoffset{\layoutparameter\c!cropoffset}%
+ \ifx\p_cropoffset\v!auto
+ \ifconditional\c_page_layouts_location_is_middle
+ \scratchheight\dimexpr(\printpaperheight-\paperheight)/2\relax
+ \scratchwidth \dimexpr(\printpaperwidth -\paperwidth )/2\relax
+ \layoutcropoffset\ifdim\scratchheight>\scratchwidth\scratchheight\else\scratchwidth\fi
+ \layouttrimoffset \layoutparameter\c!trimoffset
+ \layoutbleedoffset\layoutparameter\c!bleedoffset
+ \layoutartoffset \layoutparameter\c!artoffset
+ \else
+ \writestatus\m!system{auto crop etc only supported when location=middle}%
+ \layoutcropoffset \zeropoint
+ \layouttrimoffset \zeropoint
+ \layoutbleedoffset\zeropoint
+ \layoutartoffset \zeropoint
+ \fi
+ \else
+ \layoutcropoffset \layoutparameter\c!cropoffset
+ \layouttrimoffset \layoutparameter\c!trimoffset
+ \layoutbleedoffset\layoutparameter\c!bleedoffset
+ \layoutartoffset \layoutparameter\c!artoffset
+ \fi
+ \relax
+\to \everysetuplayout
+
+\setuplayout
+ [\c!cropoffset=\zeropoint,
+ \c!trimoffset=\zeropoint,
+ \c!bleedoffset=\zeropoint,
+ \c!artoffset=\zeropoint]
+
+% test the next when something is changed here:
+%
+% \setuppapersize[A5][A3] \showframe
+%
+% % \setuplayout[location=middle] \setupinteractionscreen[option=fit]
+% % \setuplayout[location=middle] \setupinteractionscreen[option=auto]
+% % \setupinteractionscreen[option=fit]
+% % \setupinteractionscreen[option=auto]
+%
+% \starttext \input ward \stoptext
+
+\let\scrn_canvas_synchronize_simple \relax
+\let\scrn_canvas_synchronize_complex\relax
+
+\appendtoks
+ \glet\scrn_canvas_synchronize_simple \scrn_canvas_synchronize_simple_indeed
+ \glet\scrn_canvas_synchronize_complex\scrn_canvas_synchronize_complex_indeed
+\to \everysetuplayout
+
+\appendtoks
+ \glet\scrn_canvas_synchronize_simple \scrn_canvas_synchronize_simple_indeed
+ \glet\scrn_canvas_synchronize_complex\scrn_canvas_synchronize_complex_indeed
+\to \everysetuplayouttarget
+
+\def\scrn_canvas_synchronize_common {
+ mode {\interactionscreenparameter\c!option}%
+ singlesided \ifsinglesided true\else false\fi\space
+ doublesided \ifdoublesided true\else false\fi\space
+ copies \numexpr\interactionscreenparameter\c!copies\relax
+ print {\interactionscreenparameter\c!print}%
+}
+
+\appendtoks
+ \let\scrn_canvas_synchronize_common\empty
+\to \everyaftershipout
+
+\def\scrn_canvas_synchronize_only
+ {\clf_setupcanvas
+ paperwidth \printpaperwidth
+ paperheight \printpaperheight
+ \scrn_canvas_synchronize_common
+ \relax}
+
+\def\scrn_canvas_synchronize_set#width#height%
+ {\clf_setupcanvas
+ paperwidth \dimexpr#width\relax
+ paperheight \dimexpr#height\relax
+ \relax}
+
+\let\scrn_canvas_synchronize_reset\scrn_canvas_synchronize_only
+
+\def\scrn_canvas_synchronize_simple_indeed
+ {\scrn_canvas_synchronize_only
+ %\glet\scrn_canvas_synchronize_simple \relax
+ \glet\scrn_canvas_synchronize_complex\relax}
+
+\def\scrn_canvas_synchronize_complex_indeed
+ {\scrn_canvas_calculate % otherwise we need to hook it into setuppage etc
+ \clf_setupcanvas
+ leftoffset \canvasbackoffset
+ topoffset \canvastopoffset
+ width \canvaswidth
+ height \canvasheight
+ paperwidth \canvasmaxwidth
+ paperheight \canvasmaxheight
+ cropoffset \canvascropoffset
+ trimoffset \canvastrimoffset
+ bleedoffset \canvasbleedoffset
+ artoffset \canvasartoffset
+ \scrn_canvas_synchronize_common
+ \relax
+ %\glet\scrn_canvas_synchronize_simple \relax
+ % one shot only
+ \glet\scrn_canvas_synchronize_complex\relax}
+
+\appendtoks
+ \begingroup
+ \edef\p_option{\interactionscreenparameter\c!option}%
+ \ifx\p_option\v!max
+ \global\settrue \c_scrn_canvas_tight_page
+ \orelse\ifx\p_option\v!fit
+ \global\settrue \c_scrn_canvas_tight_page
+ \else
+ \global\setfalse\c_scrn_canvas_tight_page
+ \fi
+ \endgroup
+\to \everysetupinteractionscreen
+
+\setupinteractionscreen
+ [\c!width=\printpaperwidth,
+ \c!height=\printpaperheight,
+ \c!horoffset=\zeropoint,
+ \c!veroffset=\zeropoint,
+ \c!backspace=\backspace,
+ \c!topspace=\topspace,
+ \c!copies=\plusone, % not the best place but backend anyway
+ \c!option=\v!auto]
+
+\appendtoks
+ \glet\scrn_canvas_synchronize_simple \scrn_canvas_synchronize_simple_indeed
+ \glet\scrn_canvas_synchronize_complex\scrn_canvas_synchronize_complex_indeed
+\to \everysetupinteractionscreen
+
+%D Conditional page breaks:
+
+\permanent\tolerant\protected\def\screen[#list]%
+ {\iflocation
+ \page[#list]%
+ \fi}
+
+%D Page transitions:
+
+\let\scrn_transitions_list\empty
+
+\permanent\tolerant\protected\def\setuppagetransitions[#list]%
+ {\edef\scrn_transitions_list{#list}%
+ \ifx\scrn_transitions_list\v!reset
+ \let\scrn_transitions_list\empty
+ \fi}
+
+\def\scrn_transitions_set
+ {\iflocation \ifempty\scrn_transitions_list \else
+ \scrn_transitions_set_indeed
+ \fi \fi}
+
+\def\scrn_transitions_set_indeed
+ {\begingroup
+ \edef\currentinteractionscreendelay{\interactionscreenparameter\c!delay}%
+ \clf_setpagetransition
+ n {\scrn_transitions_list}%
+ delay \ifx\currentinteractionscreendelay\v!none \zerocount\else\currentinteractionscreendelay\fi
+ \relax
+ \endgroup}
+
+\prependtoks
+ \scrn_transitions_set
+\to \everyshipout
+
+\setupinteractionscreen
+ [\c!delay=\v!none]
+
+\setuppagetransitions
+ [\v!reset]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-ref.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-ref.mklx
new file mode 100644
index 00000000000..63f76a0920d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-ref.mklx
@@ -0,0 +1,107 @@
+%D \module
+%D [ file=scrn-ref,
+%D version=1998.01.15,
+%D title=\CONTEXT\ Screen Macros,
+%D subtitle=References, % moved code
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Screen Macros / References}
+
+\registerctxluafile{scrn-ref}{}
+
+\unprotect
+
+\appendtoks
+ \doifsomething{\interactionparameter\c!calculate}%
+ {\clf_setfieldcalculationset{\interactionparameter\c!calculate}}%
+ \doifelse{\interactionparameter\c!click }\v!yes
+ \settrue\setfalse\highlighthyperlinks
+ \doifelse{\interactionparameter\c!display}\v!new
+ \settrue\setfalse\gotonewwindow
+ \doifnot {\interactionparameter\c!page}\v!no
+ \scrn_reference_enable_page_destinations
+\to \everysetupinteraction
+
+\def\scrn_reference_enable_page_destinations % no reset
+ {\clf_setreferencelinkmethod{\interactionparameter\c!page}}
+
+\setupinteraction % start fit page and reset form
+ [\c!page=\v!no,
+ \c!click=\v!yes,
+ \c!display=\v!normal,
+ \c!focus=\v!fit,
+ %\c!focusoffset=\zeropoint, % when tested with al views it will become 5\points
+ \c!focusoffset=5\points,
+ \c!calculate=,
+ % rendering:
+ \c!width=\emwidth,
+ \c!height=\zeropoint,
+ \c!depth=\zeropoint,
+ \c!symbolset=]
+
+% for now here, might become per link
+
+\appendtoks
+ \enabledirectives[destinations.offset=\the\dimexpr\interactionparameter\c!focusoffset\relax]%
+\to \everysetupinteraction
+
+\mutable\let\currentinteractioncloseaction \empty
+\mutable\let\currentinteractionclosepageaction\empty
+\mutable\let\currentinteractionopenaction \empty
+\mutable\let\currentinteractionopenpageaction \empty
+
+%D We have to make sure of some settings:
+
+\def\scrn_reference_set_text_actions
+ {\iflocation
+ \edef\currentinteractionopenaction {\interactionparameter\c!openaction }%
+ \edef\currentinteractioncloseaction{\interactionparameter\c!closeaction}%
+ \ifempty\currentinteractionopenaction \else
+ \clf_setopendocumentaction{\currentinteractionopenaction}%
+ \fi
+ \ifempty\currentinteractioncloseaction \else
+ \clf_setclosedocumentaction{\currentinteractioncloseaction}%
+ \fi
+ \glet\scrn_reference_set_text_actions\relax
+ \fi}
+
+\def\scrn_reference_set_page_actions
+ {\iflocation
+ \edef\currentinteractionopenpageaction {\interactionparameter\c!openpageaction }%
+ \edef\currentinteractionclosepageaction{\interactionparameter\c!closepageaction}%
+ \ifempty\currentinteractionopenpageaction \else
+ \clf_setopenpageaction{\currentinteractionopenpageaction}%
+ \fi
+ \ifempty\currentinteractionclosepageaction \else
+ \clf_setclosepageaction{\currentinteractionclosepageaction}%
+ \fi
+ \fi}
+
+\appendtoks \scrn_reference_set_text_actions \to \everyshipout
+\appendtoks \scrn_reference_set_page_actions \to \everyshipout
+
+%D delayed ...
+
+% \def\scrn_reference_enable_references
+% {\ifproductionrun
+% \clf_enableinteraction % only once anyway
+% \glet\scrn_reference_enable_references\relax
+% \fi}
+%
+% \appendtoks
+% \scrn_reference_enable_references
+% \to \everysetupinteraction
+
+\setupinteraction % start fit page and reset form
+ [\c!openaction=,
+ \c!closeaction=,
+ \c!openpageaction=,
+ \c!closepageaction=]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrn-wid.mklx b/Master/texmf-dist/tex/context/base/mkxl/scrn-wid.mklx
new file mode 100644
index 00000000000..5d1d67a99d2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrn-wid.mklx
@@ -0,0 +1,757 @@
+%D \module
+%D [ file=scrn-int,
+%D version=2011.02.27, % moved from scrn-int
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Widgets,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Interaction Macros / Widgets}
+
+\registerctxluafile{scrn-wid}{}
+
+% todo: expansion in comments (default is expanded)
+% todo: check renderings ... acrobat crashes too easily on missing one
+
+\unprotect
+
+%D Attachments (mkiv upgraded):
+%D
+%D As usual in \CONTEXT\ we separate the general definition (frontend)
+%D and the rendering (backend).
+
+% hack: x\footnote{x\inleftmargin[scope=local]{\attachment[location=high,file=i-context.pdf]}}x
+
+% old but stil valid method:
+%
+% \useattachment[test.tex]
+% \useattachment[whatever][test.tex]
+% \useattachment[whatever][newname][test.tex]
+% \useattachment[whatever][title][newname][test.tex]
+%
+% new method:
+%
+% \registerattachment[sometag][specification] % name file author title subtitle
+%
+% \attachment[sometag][extra specs]
+% \attachment[test.tex]
+% \attachment[file=test.tex]
+% \attachment[file=test.tex,method=hidden]
+% \attachment[name=newname,file=test.tex]
+% \attachment[title=mytitle,name=newname,file=test.tex]
+%
+% indirect
+%
+% \defineattachment[whatever5][file=test.tex] \attachment[whatever5][method=hidden]
+% \defineattachment[whatever5][file=test.tex,method=hidden] \attachment[whatever5]
+%
+% direct (no definitions)
+%
+% \attachment[test][file=oeps.tex,title=Oeps,author=Hans,subtitle=TeX File,method=hidden]
+% \attachment[label=test,file=oeps.tex,title=Oeps,author=Hans,subtitle=TeX File,method=hidden]
+%
+% autolabel:
+%
+% \attachment[file=oeps.tex,title=Oeps,author=Hans,subtitle=TeX File,method=hidden]
+%
+% % \setupattachments[\c!symbol={symbol-normal,symbol-down}]
+
+% startattachment -> temp file
+
+\newbox\b_scrn_attachment_collect
+\newbox\b_scrn_attachment_link
+\newbox\b_scrn_attachment_symbol
+
+\installcorenamespace{attachment}
+\installcorenamespace{attachmentlocation}
+\installcorenamespace{attachmentmethod}
+
+\installcommandhandler \??attachment {attachment} \??attachment
+
+\aliased\let\setupattachments\setupattachment % convenience and compatibility
+
+\setupattachment
+ [\c!state=\v!start,
+ \c!color=\interactionparameter\c!color,
+ %\c!textlayer=,
+ %\c!symbol=,
+ %\c!title=,
+ %\c!subtitle=,
+ %\c!file=, % input filename
+ %\c!name=, % new filename
+ %\c!author=,
+ %\c!method=, % \v!hidden = not in menu
+ %\c!buffer=
+ \c!symbol=,
+ \c!distance=\emwidth,
+ \c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!depth=\v!fit,
+ \c!location=\v!high]
+
+\mutable\let\currentattachmentdepth \empty
+\mutable\let\currentattachmentheight \empty
+\mutable\let\currentattachmentregistered\empty
+\mutable\let\currentattachmentsymbol \empty
+\mutable\let\currentattachmentwidth \empty
+
+\permanent\tolerant\protected\def\registerattachment[#tag]#spacer[#settings]% we save (globally) at the lua end
+ {\ifarguments\else
+ \begingroup
+ \def\currentattachment{_}%
+ \setupcurrentattachment[#settings,\s!parent=\??attachment]%
+ \clf_registerattachment
+ tag {#tag}%
+ registered {#tag}%
+ title {\attachmentparameter\c!title}%
+ subtitle {\attachmentparameter\c!subtitle}%
+ author {\attachmentparameter\c!author}%
+ file {\attachmentparameter\c!file}%
+ name {\attachmentparameter\c!name}%
+ buffer {\attachmentparameter\c!buffer}%
+ mimetype {\attachmentparameter\c!type}%
+ \relax
+ \endgroup
+ \fi}
+
+\appendtoks
+ \frozen\instance\setuevalue \currentattachment {\scrn_attachment_direct[\currentattachment]}%
+ \frozen\instance\setuevalue{\e!start\currentattachment}{\scrn_attachment_start [\currentattachment]}%
+ \frozen\instance\setuevalue{\e!stop \currentattachment}{\scrn_attachment_stop }%
+\to \everydefineattachment
+
+\permanent\tolerant\protected\def\scrn_attachment_direct[#tag]#spacer[#registered]#spacer[#settings]%
+ {\iflocation
+ \bgroup
+ \edef\currentattachment{#tag}%
+ \ifcstok{\attachmentparameter\c!state}\v!start
+ \iftok{#registered}\emptytoks
+ \scrn_attachment_inject[\v!auto][]%
+ \orelse\ifhastok={#registered}%
+ \scrn_attachment_inject[\v!auto][#registered]%
+ \else
+ \scrn_attachment_inject[#registered][#settings]%
+ \fi
+ \fi
+ \egroup
+ \fi}
+
+\tolerant\protected\def\scrn_attachment_start[#tag]#spacer[#registered]#spacer[#settings]%
+ {\edef\currentattachment{#tag}%
+ \unless\iflocation
+ \expandafter\scrn_attachment_start_ignore
+ \orelse\ifcstok{\attachmentparameter\c!state}\v!start
+ \expandafter\scrn_attachment_start_indeed
+ \else
+ \expandafter\scrn_attachment_start_ignore
+ \fi[#registered][#settings]}
+
+\protected\def\scrn_attachment_stop
+ {}
+
+\def\scrn_attachment_start_indeed[#registered][#settings]%
+ {\bgroup
+ \doifelsenothing{#registered}
+ {\def\scrn_attachment_stop{\scrn_attachment_inject[\v!auto][\c!buffer=\v!attachment]\egroup}}%
+ {\doifelseassignment{#registered}
+ {\def\scrn_attachment_stop{\scrn_attachment_inject[\v!auto][\c!buffer=\v!attachment,#registered]\egroup}}%
+ {\def\scrn_attachment_stop{\scrn_attachment_inject[#registered][\c!buffer=\v!attachment,#settings]\egroup}}}%
+ \grabbufferdatadirect\v!attachment{\e!start\currentattachment}{\e!stop\currentattachment}}
+
+\def\scrn_attachment_start_ignore
+ {\expandafter\gobbleuntil\csname\e!stop\currentattachment\endcsname}
+
+\def\scrn_attachment_inject[#registered][#settings]%
+ {\edef\currentattachmentregistered{#registered}%
+ \setupattachment[\currentattachment][#settings]%
+ \expandnamespaceparameter\??attachmentmethod\attachmentparameter\c!method\v!normal}
+
+\setvalue{\??attachmentmethod\v!normal}%
+ {\edef\currentattachmentsymbol{\attachmentparameter\c!symbol}%
+ \edef\currentattachmentwidth {\attachmentparameter\c!width }%
+ \edef\currentattachmentheight{\attachmentparameter\c!height}%
+ \edef\currentattachmentdepth {\attachmentparameter\c!depth }%
+ \ifempty\currentattachmentsymbol
+ \ifx\currentattachmentwidth \v!fit\edef\currentattachmentwidth {.5\emwidth}\fi
+ \ifx\currentattachmentheight\v!fit\edef\currentattachmentheight{.5\emwidth}\fi
+ \ifx\currentattachmentdepth \v!fit\let \currentattachmentdepth \zeropoint\fi
+ \else
+ \clf_presetsymbollist{\attachmentparameter\c!symbol}%
+ % we cannot yet ask for the wd/ht/dp of an xform else we could use those
+ \setbox\b_scrn_attachment_symbol\hbox{\symbol[\lastpredefinedsymbol]}%
+ \ifx\currentattachmentwidth \v!fit\edef\currentattachmentwidth {\wd\b_scrn_attachment_symbol}\fi
+ \ifx\currentattachmentheight\v!fit\edef\currentattachmentheight{\ht\b_scrn_attachment_symbol}\fi
+ \ifx\currentattachmentdepth \v!fit\edef\currentattachmentdepth {\dp\b_scrn_attachment_symbol}\fi
+ \fi
+ \clf_insertattachment
+ tag {\currentattachment}%
+ registered {\currentattachmentregistered}%
+ width \dimexpr\currentattachmentwidth \relax
+ height \dimexpr\currentattachmentheight\relax
+ depth \dimexpr\currentattachmentdepth \relax
+ color {\attachmentparameter\c!color}%
+ colormodel \c_attr_colormodel
+ colorvalue \rawcolorattribute{\attachmentparameter\c!color}%
+ transparencyvalue \rawtransparencyattribute{\attachmentparameter\c!color}%
+ symbol {\currentattachmentsymbol}%
+ layer {\attachmentparameter\c!textlayer}%
+ % these will be overloaded by registered when available
+ title {\attachmentparameter\c!title}%
+ subtitle {\attachmentparameter\c!subtitle}%
+ author {\attachmentparameter\c!author}%
+ file {\attachmentparameter\c!file}%
+ name {\attachmentparameter\c!name}%
+ buffer {\attachmentparameter\c!buffer}%
+ mimetype {\attachmentparameter\c!type}%
+ \relax
+ \wd\b_scrn_attachment_link\currentattachmentwidth
+ \ht\b_scrn_attachment_link\currentattachmentheight
+ \dp\b_scrn_attachment_link\currentattachmentdepth
+ \expandnamespaceparameter\??attachmentlocation\attachmentparameter\c!location\s!unknown}
+
+\setvalue{\??attachmentmethod\v!hidden}%
+ {\clf_insertattachment
+ tag {\currentattachment}%
+ registered {\currentattachmentregistered}%
+ method {\v!hidden}%
+ title {\attachmentparameter\c!title}%
+ subtitle {\attachmentparameter\c!subtitle}%
+ author {\attachmentparameter\c!author}%
+ file {\attachmentparameter\c!file}%
+ name {\attachmentparameter\c!name}%
+ buffer {\attachmentparameter\c!buffer}%
+ mimetype {\attachmentparameter\c!type}%
+ \relax}
+
+\protected\def\scrn_attachment_flush_traced
+ {\hpack\bgroup
+ \blackrule % can be a fast one
+ [ \c!color=trace:r,
+ \c!width=\wd\b_scrn_attachment_link,
+ \c!height=\ht\b_scrn_attachment_link,
+ \c!depth=\dp\b_scrn_attachment_link]%
+ \kern-\wd\b_scrn_attachment_link
+ \box\b_scrn_attachment_link
+ \egroup}
+
+\protected\def\scrn_attachment_flush_normal
+ {\box\b_scrn_attachment_link}
+
+\installtextracker
+ {attachments.anchors}
+ {\let\scrn_attachment_flush\scrn_attachment_flush_traced}
+ {\let\scrn_attachment_flush\scrn_attachment_flush_normal}
+
+\let\scrn_attachment_flush\scrn_attachment_flush_normal
+
+\setvalue{\??attachmentlocation\v!inmargin }{\inmargin {\scrn_attachment_flush}}
+\setvalue{\??attachmentlocation\v!leftedge }{\inleftedge {\scrn_attachment_flush}}
+\setvalue{\??attachmentlocation\v!rightedge }{\inrightedge {\scrn_attachment_flush}}
+\setvalue{\??attachmentlocation\v!leftmargin }{\inleftmargin {\scrn_attachment_flush}}
+\setvalue{\??attachmentlocation\v!rightmargin}{\inrightmargin{\scrn_attachment_flush}}
+\setvalue{\??attachmentlocation\v!high }{\high {\scrn_attachment_flush}}
+
+\setvalue{\??attachmentlocation\v!none}%
+ {\global\setbox\b_scrn_attachment_collect\hbox\bgroup
+ \ifvoid\b_scrn_attachment_collect\else
+ \box\b_scrn_attachment_collect
+ \hskip\attachmentparameter\c!distance\relax
+ \fi
+ \scrn_attachment_flush
+ \egroup}
+
+\setvalue{\??attachmentlocation\s!unknown}%
+ {\ifvoid\b_scrn_attachment_collect\else
+ \box\b_scrn_attachment_collect
+ \fi}
+
+\permanent\protected\def\placeattachments
+ {\ifvoid\b_scrn_attachment_collect\else
+ \box\b_scrn_attachment_collect
+ \fi}
+
+\defineattachment[attachment]
+
+% \ifx\currentinterface\defaultinterface \else
+% \defineattachment[\v!attachment]
+% \fi
+
+% backward compatible:
+
+\permanent\tolerant\protected\def\useattachment[#tag]#spacer[#title]#spacer[#name]#spacer[#file]%
+ {\ifarguments\or
+ \registerattachment[#tag][title=#tag,name=#tag,file=#tag]%
+ \or
+ \registerattachment[#tag][title=#title,name=#title,file=#title]%
+ \or
+ \registerattachment[#tag][title=#title,name=#title,file=#name]%
+ \or
+ \registerattachment[#tag][title=#title,name=#name,file=#file]%
+ \fi}
+
+%D Comments:
+
+% test
+%
+% \startcomment
+% hello beautiful\\world
+% \stopcomment
+%
+% test
+%
+% \startcomment[hello]
+% hello << eerste >>
+% beautiful
+% world
+% \stopcomment
+%
+% test
+%
+% \startcomment[hello][color=green,width=10cm,height=3cm]
+% hello
+% beautiful
+% world
+% \stopcomment
+%
+% test
+%
+% \startcomment[hello][color=red,width=4cm,height=3cm]
+% hello
+%
+% beautiful
+%
+% world
+% \stopcomment
+%
+% test
+%
+% \startcomment[symbol=Help]
+% Do we want this kind of rubish?
+% \stopcomment
+%
+% test
+%
+% \definesymbol [comment-normal][{\externalfigure[cow.pdf]}]
+% \definesymbol [comment-down] [{\externalfigure[cow.pdf]}]
+%
+% \def\CowSymbol#1#2%
+% {\scale
+% [\c!height=#1]
+% {\startMPcode
+% loadfigure "koe.mp" number 1 ;
+% refill currentpicture withcolor #2 ;
+% \stopMPcode}}
+%
+% \definesymbol [comment-normal]
+% [\CowSymbol{4ex}{red}]
+%
+% \definesymbol [comment-down]
+% [\CowSymbol{4ex}{green}]
+%
+% \setupcomment
+% [\c!symbol={comment-normal,comment-down},
+% \c!option=\v!buffer]
+%
+% \startcomment[hello]
+% oeps
+% \stopcomment
+%
+% test
+%
+% \setupcomment
+% [\c!symbol=normal,
+% \c!option=max,width=10cm]
+%
+% \startcomment[hello]
+% oeps
+% \stopcomment
+%
+% test
+
+%D The implementation is mostly the same as for attachments but sharing code
+%D will not make it cleaner.
+
+\installcorenamespace{comment}
+\installcorenamespace{commentlocation}
+
+\installcommandhandler \??comment {comment} \??comment
+
+\newbox\b_scrn_comment_collect
+\newbox\b_scrn_comment_rendering
+\newbox\b_scrn_comment_link
+\newbox\b_scrn_comment_symbol
+
+\setupcomment
+ [\c!state=\v!start,
+ \c!distance=\emwidth,
+ \c!color=\interactionparameter\c!color,
+ \c!space=\v!no,
+ \c!symbol=,
+ %\c!title=,
+ %\c!option=,
+ %\c!textlayer=,
+ \c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!depth=\v!fit,
+ \c!nx=40,
+ \c!ny=10,
+ \c!buffer=\v!comment,
+ \c!location=\v!high]
+
+\mutable\let\currentcommentdepth \empty
+\mutable\let\currentcommentheight\empty
+\mutable\let\currentcommentsymbol\empty
+\mutable\let\currentcommentwidth \empty
+
+\appendtoks
+ \frozen\instance\setuevalue \currentcomment {\scrn_comment_argument[\currentcomment]}%
+ \frozen\instance\setuevalue{\e!start\currentcomment}{\scrn_comment_start [\currentcomment]}%
+ \frozen\instance\setuevalue{\e!stop \currentcomment}{\scrn_comment_stop }%
+\to \everydefinecomment
+
+\tolerant\protected\def\scrn_comment_argument[#category]#spacer[#title]#spacer[#settings]#:#text%%
+ {\iflocation
+ \def\currentcomment{#category}%
+ \ifcstok{\commentparameter\c!state}\v!start
+ \ifhastok={#title}
+ \setupcurrentcomment[#title]%
+ \else
+ \setupcurrentcomment[\c!title=#title,#settings]%
+ \fi
+ \clf_assignbuffer{\v!comment}{#text}\catcodetable\relax% todo: expansion control, but expanded by default (xml)
+ \scrn_comment_inject
+ \fi
+ \fi
+ \ignorespaces}
+
+\tolerant\protected\def\scrn_comment_start[#category]#spacer[#title]#spacer[#settings]%
+ {\def\currentcomment{#category}%
+ \unless\iflocation
+ \expandafter\scrn_comment_start_ignore
+ \orelse\ifcstok{\commentparameter\c!state}\v!start
+ \expandafter\scrn_comment_start_indeed
+ \else
+ \expandafter\scrn_comment_start_ignore
+ \fi[#title][#settings]}
+
+\def\scrn_comment_start_indeed[#title][#settings]%
+ {\bgroup
+ \doifelseassignment{#title}
+ {\setupcurrentcomment[#title]}
+ {\setupcurrentcomment[\c!title=#title,#settings]}%
+ \protected\def\scrn_comment_stop{\scrn_comment_inject\egroup}%
+ \grabbufferdatadirect\v!comment{\e!start\currentcomment}{\e!stop\currentcomment}}
+
+\def\scrn_comment_start_ignore
+ {\expandafter\gobbleuntil\csname\e!stop\currentcomment\endcsname}
+
+\protected\def\scrn_comment_stop
+ {}
+
+\installcorenamespace{commentmethods}
+
+\protected\def\scrn_comment_inject
+ {\expandnamespaceparameter\??commentmethods\commentparameter\c!method\v!normal}
+
+%D Beware: comments symbols don't scale in acrobat (cf. spec but somewhat
+%D weird, esp because for instance attachment symbols do scale).
+
+\setvalue{\??commentmethods\v!normal}%
+ {\edef\currentcommentsymbol{\commentparameter\c!symbol}%
+ \edef\currentcommentwidth {\commentparameter\c!width }%
+ \edef\currentcommentheight{\commentparameter\c!height}%
+ \edef\currentcommentdepth {\commentparameter\c!depth }%
+ \ifempty\currentcommentsymbol
+ \ifx\currentcommentwidth \v!fit\edef\currentcommentwidth {.5\emwidth}\fi
+ \ifx\currentcommentheight\v!fit\edef\currentcommentheight{.5\emwidth}\fi
+ \ifx\currentcommentdepth \v!fit\let \currentcommentdepth \zeropoint\fi
+ \else
+ \clf_presetsymbollist{\commentparameter\c!symbol}%
+ % we cannot yet ask for the wd/ht/dp of an xform else we could use those
+ \setbox\b_scrn_comment_symbol\hbox{\symbol[\lastpredefinedsymbol]}%
+ \ifx\currentcommentwidth \v!fit\edef\currentcommentwidth {\wd\b_scrn_comment_symbol}\fi
+ \ifx\currentcommentheight\v!fit\edef\currentcommentheight{\ht\b_scrn_comment_symbol}\fi
+ \ifx\currentcommentdepth \v!fit\edef\currentcommentdepth {\dp\b_scrn_comment_symbol}\fi
+ \fi
+ \clf_insertcomment
+ tag {\currentcomment}%
+ title {\commentparameter\c!title}%
+ subtitle {\commentparameter\c!subtitle}%
+ author {\commentparameter\c!author}%
+ width \dimexpr\currentcommentwidth\relax
+ height \dimexpr\currentcommentheight\relax
+ depth \dimexpr\currentcommentdepth\relax
+ nx {\commentparameter\c!nx}%
+ ny {\commentparameter\c!ny}%
+ colormodel \c_attr_colormodel
+ colorvalue \rawcolorattribute{\commentparameter\c!color}%
+ transparencyvalue \rawtransparencyattribute{\commentparameter\c!color}%
+ option {\commentparameter\c!option}% % todo
+ symbol {\commentparameter\c!symbol}%
+ buffer {\commentparameter\c!buffer}% {\v!comment}%
+ layer {\commentparameter\c!textlayer}%
+ space {\commentparameter\c!space}%
+ \relax
+ \wd\b_scrn_comment_link\currentcommentwidth
+ \ht\b_scrn_comment_link\currentcommentheight
+ \dp\b_scrn_comment_link\currentcommentdepth
+ \expandnamespaceparameter\??commentlocation\commentparameter\c!location\s!unknown}
+
+\letvalue{\??commentmethods\v!hidden}\donothing
+
+\protected\def\scrn_comment_flush_traced
+ {\hpack\bgroup
+ \blackrule % can be a fast one
+ [ \c!color=trace:y,
+ \c!width=\wd\b_scrn_comment_link,
+ \c!height=\ht\b_scrn_comment_link,
+ \c!depth=\dp\b_scrn_comment_link]%
+ \kern-\wd\b_scrn_comment_link
+ \box\b_scrn_comment_link
+ \egroup}
+
+\protected\def\scrn_comment_flush_normal
+ {\box\b_scrn_comment_link}
+
+\installtextracker
+ {comments.anchors}
+ {\let\scrn_comment_flush\scrn_comment_flush_traced}
+ {\let\scrn_comment_flush\scrn_comment_flush_normal}
+
+\let\scrn_comment_flush\scrn_comment_flush_normal
+
+% todo: dedicated margin classes
+
+\setvalue{\??commentlocation\v!inmargin }{\inmargin {\scrn_comment_flush}}
+\setvalue{\??commentlocation\v!leftedge }{\inleftedge {\scrn_comment_flush}}
+\setvalue{\??commentlocation\v!rightedge }{\inrightedge {\scrn_comment_flush}}
+\setvalue{\??commentlocation\v!leftmargin }{\inleftmargin {\scrn_comment_flush}}
+\setvalue{\??commentlocation\v!rightmargin}{\inrightmargin{\scrn_comment_flush}}
+\setvalue{\??commentlocation\v!high }{\high {\scrn_comment_flush}}
+
+\setvalue{\??commentlocation\v!none}%
+ {\global\setbox\b_scrn_comment_collect\hbox\bgroup
+ \ifvoid\b_scrn_comment_collect\else
+ \box\b_scrn_comment_collect
+ \hskip\commentparameter\c!distance\relax
+ \fi
+ \scrn_comment_flush
+ \egroup}
+
+\setvalue{\??commentlocation\s!unknown}%
+ {\ifvoid\b_scrn_comment_collect\else
+ \box\b_scrn_comment_collect
+ \fi}
+
+\permanent\protected\def\placecomments
+ {\ifvoid\b_scrn_comment_collect\else
+ \box\b_scrn_comment_collect
+ \fi}
+
+\definecomment[comment]
+
+% \ifx\currentinterface\defaultinterface \else
+% \definecomment[\v!comment]
+% \fi
+
+%D Soundclips:
+%D
+%D Defining sound tracks:
+%D
+%D \starttyping
+%D \useexternalsoundtrack[label][file]
+%D \stoptyping
+%D
+%D associated actions: StartSound StopSound PauseSound ResumeSound
+%D
+%D Todo: like external figures, also search on path,
+%D although, they need to be present ar viewing time, so ...
+
+\installcorenamespace{externalsoundtracks}
+
+\installsetuponlycommandhandler \??externalsoundtracks {externalsoundtracks}
+
+\setupexternalsoundtracks
+ [\c!option=]
+
+\permanent\tolerant\protected\def\useexternalsoundtrack[#tag]#spacer[#filename]%
+ {\clf_registersoundclip
+ tag {#tag}%
+ file {#filename}%
+ \relax}
+
+\permanent\def\checksoundtrack#tag% yet untested in mkiv (also move management to lua)
+ {\iflocation
+ \clf_insertsoundclip
+ tag {#tag}%
+ repeat {\directexternalsoundtracksparameter\c!option}%
+ \relax
+ \fi}
+
+%D Renderings (not yet tested in mkvi):
+
+% Todo: multiple instances and inheritance .. will be done when needed i.e. when I
+% see usage. Probaly by that time this feature is dropped (as are other media
+% rel;ated ones).
+
+\mutable\let\currentrendering \empty
+\mutable\let\currentrenderingtype \empty
+\mutable\let\currentrenderingwindow\empty
+
+\definereference[StartCurrentRendering] [\v!StartRendering {\currentrendering}]
+\definereference[StopCurrentRendering] [\v!StopRendering {\currentrendering}]
+\definereference[PauseCurrentRendering] [\v!PauseRendering {\currentrendering}]
+\definereference[ResumeCurrentRendering][\v!ResumeRendering{\currentrendering}]
+
+\permanent\tolerant\protected\def\useexternalrendering[#tag]#spacer[#mime]#spacer[#file]#spacer[#option]%
+ {\clf_registerrendering
+ type {external}%
+ label {#tag}%
+ mime {#mime}%
+ filename {#file}%
+ option {#option}%
+ \relax}
+
+\permanent\tolerant\protected\def\setinternalrendering[#tag]#spacer[#option]% {content} % crappy
+ {\bgroup
+ \dowithnextbox
+ {\clf_registerrendering
+ type {internal}%
+ label {#tag}%
+ mime {IRO}% brrr
+ filename {#tag}%
+ option {#option}%
+ \relax
+ \let\objectoffset\zeropoint
+ \setobject{IRO}{#tag}\hpack{\box\nextbox}%
+ \egroup}%
+ \hbox}
+
+\permanent\def\renderingtype #tag{\clf_renderingvar{#tag}{type}}
+\permanent\def\renderingoption#tag{\clf_renderingvar{#tag}{option}}
+
+\newdimen\d_scrn_rendering_width \d_scrn_rendering_width 8cm
+\newdimen\d_scrn_rendering_height \d_scrn_rendering_height 6cm
+
+\let\m_scrn_rendering_page\!!zerocount
+
+\installcorenamespace{renderingwindow}
+
+\installframedcommandhandler \??renderingwindow {renderingwindow} \??renderingwindow
+
+\appendtoks
+ \letrenderingwindowparameter\c!openpageaction \empty
+ \letrenderingwindowparameter\c!closepageaction\empty
+ \setrenderingwindowparameter\c!width {\d_scrn_rendering_width }%
+ \setrenderingwindowparameter\c!height {\d_scrn_rendering_height}%
+\to \everypresetrenderingwindow
+
+\permanent\tolerant\protected\def\placerenderingwindow[#window]#spacer[#rendering]% do all in lua
+ {\bgroup
+ \edef\currentrendering{\ifparameter#rendering\or#rendering\else#window\fi}%
+ % create fall back if needed
+ \edef\currentrenderingwindow{\namedrenderingwindowparameter{#window}\c!width}% stupid test, we need a proper one here
+ \ifempty\currentrenderingwindow
+ \let\currentrenderingwindow\s!default
+ \definerenderingwindow[\currentrenderingwindow]% why not global
+ \else
+ \edef\currentrenderingwindow{#window}%
+ \fi
+ \edef\currentrenderingtype{\renderingtype\currentrendering}%
+ \ifx\currentrenderingtype\s!internal
+ \getobjectdimensions{IRO}\currentrendering
+ \d_scrn_rendering_height\dimexpr\objectheight+\objectdepth\relax
+ \d_scrn_rendering_width\objectwidth\relax
+ \getobjectreferencepage{IRO}\currentrendering\m_scrn_rendering_page
+ \orelse\ifx\currentrenderingwindow\s!default
+ \d_scrn_rendering_height\vsize
+ \d_scrn_rendering_width \hsize
+ \let\m_scrn_rendering_page\realpageno
+ \else
+ \d_scrn_rendering_height\renderingwindowparameter\c!height
+ \d_scrn_rendering_width \renderingwindowparameter\c!width
+ \let\m_scrn_rendering_page\realpageno
+ \fi
+ % todo:
+ % \handlereferenceactions{\renderingwindowparameter\c!openpageaction }\dosetuprenderingopenpageaction
+ % \handlereferenceactions{\renderingwindowparameter\c!closepageaction}\dosetuprenderingclosepageaction
+ \letrenderingwindowparameter\c!offset\v!overlay
+ \inheritedrenderingwindowframed
+ {\vfilll
+ \dontleavehmode
+ \clf_insertrenderingwindow
+ label {\currentrendering}%
+ width \d_scrn_rendering_width
+ height \d_scrn_rendering_height
+ option {\renderingoption\currentrendering}%
+ page \m_scrn_rendering_page
+ \relax
+ \hfill}%
+ \egroup}
+
+%D Linkedlists (not tested in mkvi):
+
+% %D The next mechanism, linked lists, is quite old and
+% %D is \MKIV'd for completeness. I will finish the
+% %D configuration part when I need it.
+% %D
+% %D \starttyping
+% %D \setupinteraction[state=start]
+% %D \definelinkedlist[demo]
+% %D \dorecurse{10}{\linkedlistelement[demo]{link \recurselevel} \page}
+% %D \stoptyping
+%
+% \definesystemvariable {lk} % LinK
+%
+% \installcommandhandler\??lk{linkedlist}\??lk
+%
+% \let\setupbutton\setuplinkedlists\setuplinkedlist
+%
+% \appendtoks
+% \clf_definelinkedlist{\currentlinkedlist}%
+% \to \everydefinelinkedlist
+%
+% \def\setlinkedlistproperties#1#2#3#4#5#6%
+% {\def\currentlink {#1}%
+% \def\noflinks {#2}%
+% \def\firstlink {#3}%
+% \def\previouslink{#4}%
+% \def\nextlink {#5}%
+% \def\lastlink {#6}}
+%
+% \def\linkedlistelement[#1]#2% currently no view support
+% {\dontleavehmode\hbox\bgroup
+% #2%
+% \iflocation
+% \edef\currentlinkedlist{#1}%
+% \ifcsname\??lk\currentlinkedlist\s!parent\endcsname
+% \hskip\linkedlistparameter\c!distance\relax
+% \clf_addlinklistelement{\currentlinkedlist}%
+% \expanded{\ctxlatecommand{enhancelinkedlist("\currentlinkedlist",\currentlink)}}% can also be done at the lua end
+% \dogotosomepage {\??lk\currentlinkedlist}\gotobegincharacter \firstlink
+% \ifnum\noflinks>\plustwo
+% \dogotosomepage{\??lk\currentlinkedlist}\gobackwardcharacter\previouslink
+% \dogotosomepage{\??lk\currentlinkedlist}\goforwardcharacter \nextlink
+% \fi
+% \dogotosomepage {\??lk\currentlinkedlist}\gotoendcharacter \lastlink
+% \else
+% \writestatus\m!interactions{no such linked list: \currentlinkedlist}%
+% \fi
+% \fi
+% \egroup}
+%
+% \setuplinkedlists
+% [\c!distance=.25em,
+% \c!width=\v!fit,
+% \c!location=\v!low,
+% \c!color=\interactionparameter\c!color,
+% \c!frame=\v!off,
+% \c!background=,
+% \c!backgroundcolor=]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/scrp-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/scrp-ini.mkxl
new file mode 100644
index 00000000000..77412738b4d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/scrp-ini.mkxl
@@ -0,0 +1,111 @@
+%D \module
+%D [ file=scrp-ini,
+%D version=2009.02.06,
+%D title=\CONTEXT\ Script Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The script handler has been upgraded mid June 2020 so there might be issues as I
+%D didn't test everything (yet). It should all be a bit more efficient now in
+%D \LUAMETATEX, although not spectacular. We use no attributes in \LUAMETATEX\ and
+%D only one in \LUATEX\ now.
+%D
+%D A glyph can have (an dget) all kind of properties. Except from the state, these
+%D are assigned within \TEX's grouping model!
+%D
+%D \startitemize
+%D \startitem font \stopitem
+%D \startitem character \stopitem
+%D \startitem language (4 properties packed in an unsigned integer) \stopitem
+%D \startitem data (used for dynamics) \stopitem
+%D \startitem script (used for special script processing) \stopitem
+%D \startitem state (used for analyzing, related to font features) \stopitem
+%D \stopitemize
+%D
+%D Additional properties are set via node list handlers. The state is stored in the
+%D glyph, but additional properties are stored in a node's property table. In the
+%D future the state might get used for something else, in which case we will stire
+%D the state in the property table too. On the other hand, we can now (if we want)
+%D influence the state at the\TEX\ end (not that we do that yet).
+
+%D Todo: as in bidi mode we need a way to globally keep the setting.
+
+\registerctxluafile{scrp-ini}{}
+\registerctxluafile{scrp-cjk}{}
+\registerctxluafile{scrp-eth}{}
+\registerctxluafile{scrp-tha}{}
+\registerctxluafile{scrp-tib}{}
+
+\definesystemattribute[script][public,pickup]
+
+%D Since scripts need specific \LUA\ code we use hard coded attribute values, but we
+%D might have more tricks at some time, so we use a proper define macro too.
+
+\unprotect
+
+\installcorenamespace{script}
+
+\installcommandhandler \??script {script} \??script
+
+\aliased\let\setupscripts\setupscript % be nice
+
+% presets are global and are currently defined in lua
+
+% \appendtoks
+% \setuevalue\currentscript{\setscript[\currentscript]}%
+% \to \everydefinescript
+
+\permanent\protected\def\setlocalscript[#1]%
+ {\edef\currentscript{#1}%
+ \clf_setscript{\currentscript}{\scriptparameter\c!method}{\scriptparameter\c!preset}}
+
+\permanent\protected\def\setglobalscript[#1]%
+ {\setlocalscript[#1]%
+ \pickupscriptattribute} % pagebuilder
+
+\aliased\let\setscript\setlocalscript
+
+\permanent\protected\def\resetscript
+ {\clf_resetscript}
+
+\permanent\protected\def\startscript[#1]%
+ {\begingroup
+ \setlocalscript[#1]}
+
+\permanent\protected\def\stopscript
+ {\endgroup}
+
+% \setscript[hangul] \startscript[hangul]
+
+\definescript [hangul] [\c!method=hangul]
+\definescript [hanzi] [\c!method=hanzi]
+\definescript [nihongo] [\c!method=nihongo]
+\definescript [ethiopic] [\c!method=ethiopic]
+\definescript [thai] [\c!method=thai]
+\definescript [tibetan] [\c!method=tibetan]
+
+\definescript [latin] [\c!method=] % resets the attribute (also currentscript)
+
+\definescript [test] [\c!method=test]
+
+% a new trick (at some point we will predefine more scripts and consider a link with the above)
+
+\appendtoks
+ \ifempty\currentscript
+ \doifelse{\scriptparameter\s!features}\v!auto\enableautofontscript\disableautofontscript
+ \fi
+\to \everysetupscript
+
+\permanent\protected\def\enableautofontscript {\clf_enableautofontscript } % can be public implementor
+\permanent\protected\def\disableautofontscript{\clf_disableautofontscript} % can be public implementor
+
+\definefontfeature[latn][script=latn]
+\definefontfeature[grek][script=grek]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/sort-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/sort-ini.mkxl
new file mode 100644
index 00000000000..d9f9628d523
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/sort-ini.mkxl
@@ -0,0 +1,19 @@
+%D \module
+%D [ file=sort-ini,
+%D version=2005.08.08,
+%D title=\CONTEXT\ Sort Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Sorting Macros / Initialization}
+
+\registerctxluafile{sort-ini}{}
+\registerctxluafile{sort-lan}{}
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-ali.lmt b/Master/texmf-dist/tex/context/base/mkxl/spac-ali.lmt
new file mode 100644
index 00000000000..5c434fcdf2a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-ali.lmt
@@ -0,0 +1,134 @@
+if not modules then modules = { } end modules ['spac-ali'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to spac-ali.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local div = math.div
+local format = string.format
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+
+local nuts = nodes.nuts
+
+local setlist = nuts.setlist
+local setlink = nuts.setlink
+local getdirection = nuts.getdirection
+local takeattr = nuts.takeattr
+local getwidth = nuts.getwidth
+local findtail = nuts.tail
+
+local righttoleft_code = nodes.dirvalues.righttoleft
+local linelist_code = nodes.listcodes.line
+
+local hpack_nodes = nuts.hpack
+local nextlist = nuts.traversers.list
+
+local new_stretch = nuts.pool.stretch
+
+local a_realign = attributes.private("realign")
+
+local texsetattribute = tex.setattribute
+local texgetcount = tex.getcount
+
+local isleftpage = layouts.status.isleftpage
+
+typesetters = typesetters or { }
+local alignments = { }
+typesetters.alignments = alignments
+
+local report_realign = logs.reporter("typesetters","margindata")
+local trace_realign = trackers.register("typesetters.margindata", function(v) trace_margindata = v end)
+
+local nofrealigned = 0
+
+-- leftskip rightskip parfillskip
+-- raggedleft 0 + 0 -
+-- raggedright 0 0 fil
+-- raggedcenter 0 + 0 + -
+
+local function handler(head,leftpage,realpageno) -- traverse_list
+ for current, id, subtype, list in nextlist, head do
+ if subtype == linelist_code then
+ local a = takeattr(current,a_realign)
+ if a and a > 0 then
+ local pageno = div(a,10)
+ if pageno ~= realpageno then
+ local align = a % 10
+ local action = 0
+ if align == 1 then -- flushright
+ action = leftpage and 1 or 2
+ elseif align == 2 then -- flushleft
+ action = leftpage and 2 or 1
+ end
+ -- WS: watch this
+ local direction = getdirection(current)
+ -- or should this happen at the tex end:
+ if direction == righttoleft_code then
+ if action == 1 then
+ action = 2
+ elseif action == 2 then
+ action = 1
+ end
+ end
+ -- Currently we just prepend and append which kind of spoils the normalization unless
+ -- we extend our specs and also introduce left/right anchored stuff.
+ if action == 1 then
+ local head = list
+ setlink(findtail(list),new_stretch(3)) -- append
+ setlist(current,hpack_nodes(head,getwidth(current),"exactly",direction))
+ if trace_realign then
+ report_realign("flushing left, align %a, page %a, realpage %a",align,pageno,realpageno)
+ end
+ elseif action == 2 then
+ local head = setlink(new_stretch(3),list) -- prepend
+ setlist(current,hpack_nodes(head,getwidth(current),"exactly",direction))
+ if trace_realign then
+ report_realign("flushing right, align %a, page %a, realpage %a",align,pageno,realpageno)
+ end
+ elseif trace_realign then
+ report_realign("invalid flushing, align %a, page %a, realpage %a",align,pageno,realpageno)
+ end
+ nofrealigned = nofrealigned + 1
+ end
+ end
+ end
+ handler(list,leftpage,realpageno)
+ end
+ return head
+end
+
+function alignments.handler(head)
+ return handler(head,isleftpage(),texgetcount("realpageno"))
+end
+
+local enabled = false
+
+function alignments.set(n)
+ if not enabled then
+ enableaction("shipouts","typesetters.alignments.handler")
+ enabled = true
+ if trace_realign then
+ report_realign("enabled")
+ end
+ end
+ texsetattribute(a_realign,texgetcount("realpageno") * 10 + n)
+end
+
+interfaces.implement {
+ name = "setrealign",
+ actions = alignments.set,
+ arguments = "integer",
+}
+
+statistics.register("realigning", function()
+ if nofrealigned > 0 then
+ return format("%s processed",nofrealigned)
+ else
+ return nil
+ end
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-ali.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-ali.mkxl
new file mode 100644
index 00000000000..78d09295db3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-ali.mkxl
@@ -0,0 +1,1218 @@
+%D \module
+%D [ file=spac-ali,
+%D version=2009.10.16, % 1997.03.31, was core-spa.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Alignments,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Alignments}
+
+\unprotect
+
+%D The \type {new} and \type {old} methods are gone as we now have \type {flush*}
+%D variants. Starting at the last day of 2011 both methods are merged into one and
+%D caching has been added, which makes switching twice as fast.
+
+\registerctxluafile{spac-ali}{autosuffix,optimize}
+
+% Used once so ... replace it or use if more frequently ...
+
+\installcorenamespace {alignswitch}
+
+% maybe \setconstantvalue or so
+
+\expandafter\integerdef\csname\??alignswitch\v!left \endcsname\zerocount
+\expandafter\integerdef\csname\??alignswitch\v!middle\endcsname\plusone
+\expandafter\integerdef\csname\??alignswitch\v!right \endcsname\plustwo
+
+\mutable\integerdef\alignmentswitch\zerocount
+
+\permanent\protected\def\setalignmentswitch#1%
+ {\integerdef\alignmentswitch\csname\??alignswitch\ifcsname\??alignswitch#1\endcsname#1\else\v!left\fi\endcsname}
+
+% till here
+
+\definesystemattribute[realign] [public] % might be combined with the next one
+\definesystemattribute[alignstate][public] % will make a single attributes for several states
+
+\appendtoks
+ \c_attr_realign \attributeunsetvalue
+ \c_attr_alignstate\attributeunsetvalue
+\to \everyforgetall
+
+\permanent\protected\def\signalinnerrealign{\clf_setrealign\plustwo}
+\permanent\protected\def\signalouterrealign{\clf_setrealign\plusone}
+
+\installcorenamespace{aligncommand}
+\installcorenamespace{alignhorizontal}
+\installcorenamespace{alignvertical}
+
+% \installcorenamespace{alignmentnormalcache}
+% \installcorenamespace{alignmentraggedcache}
+
+% nasty hack:
+
+\installcorenamespace{alignmentnormalcacheodd}
+\installcorenamespace{alignmentnormalcacheeven}
+
+\installcorenamespace{alignmentraggedcacheodd}
+\installcorenamespace{alignmentraggedcacheeven}
+
+\immutable\def\??alignmentnormalcache{\ifodd\realpageno\??alignmentnormalcacheodd\else\??alignmentnormalcacheeven\fi}
+\immutable\def\??alignmentraggedcache{\ifodd\realpageno\??alignmentraggedcacheodd\else\??alignmentraggedcacheeven\fi}
+
+\newtoks\everyresetalign % todo
+
+% This will become a more advanced layout controller soon:
+
+\newconditional\layoutlefttoright \settrue\layoutlefttoright
+\newconditional\displaylefttoright \settrue\displaylefttoright
+\newconditional\inlinelefttoright \settrue\inlinelefttoright
+
+\permanent\protected\def\lefttoright
+ {\ifvmode
+ \spac_directions_lefttoright_vmode
+ \else
+ \spac_directions_lefttoright_hmode
+ \fi}
+
+\permanent\protected\def\righttoleft
+ {\ifvmode
+ \spac_directions_righttoleft_vmode
+ \else
+ \spac_directions_righttoleft_hmode
+ \fi}
+
+\protected\def\spac_directions_lefttoright_vmode
+ {\settrue\displaylefttoright
+ \settrue\inlinelefttoright
+ \textdirection\directionlefttoright
+ \pardirection \directionlefttoright}
+
+\protected\def\spac_directions_righttoleft_vmode
+ {\setfalse\displaylefttoright
+ \setfalse\inlinelefttoright
+ \textdirection\directionrighttoleft
+ \pardirection \directionrighttoleft}
+
+\protected\def\spac_directions_lefttoright_hmode
+ {\linedirection\directionlefttoright % linedir keeps subtype of skip
+ \settrue\inlinelefttoright}
+
+\protected\def\spac_directions_righttoleft_hmode
+ {\linedirection\directionrighttoleft % linedir keeps subtype of skip
+ \setfalse\inlinelefttoright}
+
+\permanent\protected\def\synchronizelayoutdirection
+ {\ifconditional\layoutlefttoright
+ \spac_directions_synchronize_lr
+ \else
+ \spac_directions_synchronize_rl
+ \fi}
+
+\permanent\protected\def\synchronizedisplaydirection
+ {\ifconditional\displaylefttoright
+ \spac_directions_synchronize_lr
+ \else
+ \spac_directions_synchronize_rl
+ \fi}
+
+\def\spac_directions_synchronize_lr
+ {\settrue\inlinelefttoright
+ \textdirection\directionlefttoright
+ \pardirection \directionlefttoright}
+
+\def\spac_directions_synchronize_rl
+ {\setfalse\inlinelefttoright
+ \textdirection\directionrighttoleft
+ \pardirection \directionrighttoleft}
+
+\permanent\protected\def\synchronizeinlinedirection
+ {% why not \linedirection here
+ \textdirection\ifconditional\inlinelefttoright\directionlefttoright\else\directionrighttoleft\fi}
+
+\permanent\protected\def\checkedlefttoright
+ {\ifvmode
+ \spac_directions_lefttoright_vmode
+ \else
+ \spac_directions_lefttoright_hmode_checked
+ \fi}
+
+\permanent\protected\def\checkedrighttoleft
+ {\ifvmode
+ \spac_directions_righttoleft_vmode
+ \else
+ \spac_directions_righttoleft_hmode_checked
+ \fi}
+
+\protected\def\spac_directions_lefttoright_hmode_checked
+ {\ifconditional\inlinelefttoright\else
+ \lefttoright
+ \fi}
+
+\protected\def\spac_directions_righttoleft_hmode_checked
+ {\ifconditional\inlinelefttoright
+ \righttoleft
+ \fi}
+
+\installcorenamespace{bidi}
+
+\letcsname\??bidi\v!left \endcsname\checkedlefttoright \letcsname\??bidi l2r\endcsname\checkedlefttoright
+\letcsname\??bidi\v!right\endcsname\checkedrighttoleft \letcsname\??bidi r2l\endcsname\checkedrighttoleft
+
+\permanent\protected\def\usebidiparameter#1%
+ {\begincsname\??bidi#1\c!bidi\endcsname}
+
+\permanent\protected\def\showdirections
+ {\dontleavehmode
+ \begingroup\infofont\textdirection\directionlefttoright[\space
+ layout: \ifconditional\layoutlefttoright l2r\else r2l\fi\space
+ display: \ifconditional\displaylefttoright l2r\else r2l\fi\space
+ inline: \ifconditional\inlinelefttoright l2r\else r2l\fi\space
+ ]\endgroup}
+
+\permanent\protected\def\righttolefthbox#1#{\reversehbox#1\bgroup\righttoleft\let\next} \aliased\let\rtlhbox\righttolefthbox
+\permanent\protected\def\lefttorighthbox#1#{\naturalhbox#1\bgroup\lefttoright\let\next} \aliased\let\ltrhbox\lefttorighthbox
+\permanent\protected\def\righttoleftvbox#1#{\reversevbox#1\bgroup\righttoleft\let\next} \aliased\let\rtlvbox\righttoleftvbox
+\permanent\protected\def\lefttorightvbox#1#{\naturalvbox#1\bgroup\lefttoright\let\next} \aliased\let\ltrvbox\lefttorightvbox
+\permanent\protected\def\righttoleftvtop#1#{\reversevtop#1\bgroup\righttoleft\let\next} \aliased\let\rtlvtop\righttoleftvtop
+\permanent\protected\def\lefttorightvtop#1#{\naturalvtop#1\bgroup\lefttoright\let\next} \aliased\let\ltrvtop\lefttorightvtop
+
+\permanent\protected\def\autodirhbox#1#{\hbox#1\bgroup\synchronizeinlinedirection\let\next}
+\permanent\protected\def\autodirvbox#1#{\vbox#1\bgroup\synchronizeinlinedirection\let\next} % maybe also pardir or maybe just a \vbox
+\permanent\protected\def\autodirvtop#1#{\vtop#1\bgroup\synchronizeinlinedirection\let\next} % maybe also pardir or maybe just a \vtop
+
+\permanent\protected\def\leftorrighthbox{\ifconditional\displaylefttoright\expandafter\lefttorighthbox\else\expandafter\righttolefthbox\fi}
+\permanent\protected\def\leftorrightvbox{\ifconditional\displaylefttoright\expandafter\lefttorightvbox\else\expandafter\righttoleftvbox\fi}
+\permanent\protected\def\leftorrightvtop{\ifconditional\displaylefttoright\expandafter\lefttorightvtop\else\expandafter\righttoleftvtop\fi}
+
+% Tolerance and hyphenation
+
+\ifdefined\lesshyphens \else \let\lesshyphens\relax \fi
+\ifdefined\morehyphens \else \let\morehyphens\relax \fi
+\ifdefined\nohyphens \else \let\nohyphens \relax \fi
+\ifdefined\dohyphens \else \let\dohyphens \relax \fi
+
+\newconstant\c_spac_tolerance_default \c_spac_tolerance_default 1500 % shouldn't that be 200
+\newconstant\c_spac_tolerance_minimum \c_spac_tolerance_minimum 1500
+\newconstant\c_spac_tolerance_normal \c_spac_tolerance_normal 3000
+\newconstant\c_spac_tolerance_extreme \c_spac_tolerance_extreme 4500
+
+\def\spac_align_set_raggedness_left {\plustwo\bodyfontsize}
+\def\spac_align_set_raggedness_right {\plustwo\bodyfontsize}
+\def\spac_align_set_raggedness_middle{\plussix\bodyfontsize} % overloaded below
+
+% oeps, hsize can be 0pt in which case we get a strange division
+% was: 6\bodyfontsize, fails on: \placefigure{x $x=x$ x}{}
+
+\def\spac_align_set_raggedness_middle{\ifzeropt\hsize\plussix\bodyfontsize\else.5\hsize\fi}
+
+\permanent\protected\def\setraggedness#1% tricky .. we keep the global tolerance otherwise ... to be reconsidered
+ {\ifnum\tolerance<\c_spac_tolerance_minimum
+ \tolerance\c_spac_tolerance_minimum % small values have unwanted side effects
+ \else
+ % todo: take set value or none .. better done elsewhere (200 is normal)
+ \fi
+ \ifrelax\dohyphens % was 2.5 in old implementation using scratch registers
+ \hyphenpenalty\dimexpr2.8\hsize/\dimexpr#1\relax\relax % 50 in raggedright/raggedleft
+ %\else
+ % no need to do something as we're in \nohyphens
+ \fi}
+
+\protected\def\spac_align_set_tolerant
+ {\tolerance\c_spac_tolerance_normal}
+
+\protected\def\spac_align_set_very_tolerant
+ {\tolerance\c_spac_tolerance_extreme}
+
+\protected\def\spac_align_set_stretch
+ {\emergencystretch\bodyfontsize}
+
+\protected\def\spac_align_set_extreme_stretch
+ {\emergencystretch10\bodyfontsize}
+
+% Vertical
+
+\newconstant\c_spac_align_state_vertical
+
+\protected\def\spac_align_set_vertical_none
+ {\enforced\let\raggedtopcommand \relax
+ \enforced\let\raggedbottomcommand\relax}
+
+\protected\def\spac_align_set_vertical_lohi
+ {\enforced\let\raggedtopcommand \vfilll
+ \enforced\let\raggedbottomcommand\vfilll}
+
+\protected\def\spac_align_set_vertical_low
+ {\enforced\let\raggedtopcommand \vfilll
+ \enforced\let\raggedbottomcommand\relax}
+
+\protected\def\spac_align_set_vertical_high
+ {\enforced\let\raggedtopcommand \relax
+ \enforced\let\raggedbottomcommand\vfilll}
+
+\def\spac_align_flush_vertical
+ {\ifcase\c_spac_align_state_vertical
+ \spac_align_set_vertical_none
+ \or
+ \spac_align_set_vertical_lohi
+ \or
+ \spac_align_set_vertical_low
+ \or
+ \spac_align_set_vertical_high
+ \fi}
+
+% Horizontal
+
+\ifdefined\raggedonelinerstate \else
+ \newconditional\raggedonelinerstate % public
+\fi
+
+% \appendtoks
+% \setfalse\raggedonelinerstate
+% \to \everyforgetall
+
+\newconstant\raggedstatus % public
+
+\newconstant\c_spac_align_state_horizontal
+\newconstant\c_spac_align_state_broad
+\newconstant\c_spac_align_state_par_fill
+
+\def\v_spac_align_fill_amount {\plusone fil}
+\def\v_spac_align_fill_amount_hard {\plusone fill}
+\def\v_spac_align_fill_amount_extreme {\plustenthousand filll}
+\def\v_spac_align_fill_amount_negative {\minusone fil}
+\def\v_spac_align_fill_amount_double {\plustwo fil}
+\def\v_spac_align_fill_amount_space {\plustwo fil} % can be added to xspace if we have a key
+\def\v_spac_align_fill_amount_half {.5fil}
+\let\v_spac_align_space_amount \interwordspace
+\def\v_spac_align_space_amount_x {.5\emwidth}
+
+\newskip\s_zero_plus_one_fil \s_zero_plus_one_fil = 0pt plus 1fil
+\newskip\s_zero_plus_zero \s_zero_plus_zero = 0pt plus 0pt
+
+% \s!plus ... slower than inline
+
+\permanent\protected\def\spac_align_set_horizontal_none % should also relax \updateraggedskips
+ {\raggedstatus \zerocount
+ \c_attr_alignstate\attributeunsetvalue
+ \leftskip \plusone\leftskip
+ \rightskip \plusone\rightskip
+ \spaceskip \zeropoint
+ \xspaceskip \zeropoint
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_one_fil % new
+ \setfalse \raggedonelinerstate % now here
+ \enforced\aliased\let\updateraggedskips\relax} % no need for adaption
+
+\permanent\protected\def\spac_align_set_horizontal_left
+ {\setraggedness \spac_align_set_raggedness_left
+ \raggedstatus \plusone
+ \c_attr_alignstate\plusone
+ \leftskip \plusone\leftskip \s!plus\spac_align_set_raggedness_left
+ \rightskip \plusone\rightskip\s!plus\zeropoint
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_center
+ {\setraggedness \spac_align_set_raggedness_middle
+ \raggedstatus \plustwo
+ \c_attr_alignstate\plustwo
+ \leftskip \plusone\leftskip \s!plus\spac_align_set_raggedness_middle
+ \rightskip \plusone\rightskip\s!plus\spac_align_set_raggedness_middle
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_right
+ {\setraggedness \spac_align_set_raggedness_right
+ \raggedstatus \plusthree
+ \c_attr_alignstate\plusthree
+ \leftskip \plusone\leftskip \s!plus\zeropoint
+ \rightskip \plusone\rightskip\s!plus\spac_align_set_raggedness_right
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_one_fil
+ %\parindent \parindent
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_very_left
+ {\raggedstatus \plusone
+ \c_attr_alignstate\plusone
+ \leftskip \plusone\leftskip \s!plus\v_spac_align_fill_amount
+ \rightskip \plusone\rightskip\s!plus\zeropoint
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_very_center
+ {\raggedstatus \plustwo
+ \c_attr_alignstate\plustwo
+ \leftskip \plusone\leftskip \s!plus\v_spac_align_fill_amount
+ \rightskip \plusone\rightskip\s!plus\v_spac_align_fill_amount
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_very_right
+ {\raggedstatus \plusthree
+ \c_attr_alignstate\plusthree
+ \leftskip \plusone\leftskip \s!plus\zeropoint
+ \rightskip \plusone\rightskip\s!plus\v_spac_align_fill_amount
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ %\parindent \parindent
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_wide_center
+ {\setraggedness \spac_align_set_raggedness_middle
+ \raggedstatus \plustwo
+ \c_attr_alignstate\plustwo
+ \leftskip \plusone\leftskip \s!plus\v_spac_align_fill_amount_half
+ \rightskip \plusone\rightskip\s!plus\v_spac_align_fill_amount_half
+ \spaceskip \v_spac_align_space_amount
+ \xspaceskip \v_spac_align_space_amount_x
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_centered_last_line
+ {\raggedstatus \zerocount
+ \c_attr_alignstate\attributeunsetvalue
+ \leftskip \plusone\leftskip \s!plus\v_spac_align_fill_amount\relax
+ \rightskip \plusone\rightskip\s!plus\v_spac_align_fill_amount_negative\relax
+ \spaceskip \zeropoint\relax
+ \xspaceskip \zeropoint\relax
+ \parfillleftskip \zeropoint
+ \parfillskip \zeropoint\s!plus\v_spac_align_fill_amount_double\relax
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_flushedright_last_line
+ {\raggedstatus \zerocount
+ \c_attr_alignstate\attributeunsetvalue
+ \leftskip \plusone\leftskip \s!plus\v_spac_align_fill_amount\relax
+ \rightskip \plusone\rightskip\s!plus\v_spac_align_fill_amount_negative\relax
+ \spaceskip \zeropoint\relax
+ \xspaceskip \zeropoint\relax
+ \parfillskip \zeropoint
+ \parfillleftskip \zeropoint\s!plus\v_spac_align_fill_amount_extreme\relax
+ \parindent \zeropoint
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_right_tt % a plain command
+ {\tttf % brrr
+ \raggedstatus \plusthree
+ \c_attr_alignstate\plusthree
+ \leftskip \plusone\leftskip \s!plus\zeropoint\relax
+ \rightskip \plusone\rightskip\s!plus\spac_align_set_raggedness_right\relax
+ \spaceskip \zeropoint\relax
+ \xspaceskip \zeropoint\relax
+ \parfillleftskip \zeropoint
+ \parfillskip \s_zero_plus_zero
+ %\parindent \parindent
+ \relax}
+
+\permanent\protected\def\spac_align_set_horizontal_extra
+ {\xspaceskip\zeropoint\s!plus\v_spac_align_fill_amount_space\relax}
+
+\def\spac_align_flush_horizontal
+ {\ifcase\c_spac_align_state_horizontal
+ % 0
+ \spac_align_set_horizontal_none
+ \or
+ % 1 center
+ \ifcase\c_spac_align_state_broad
+ \spac_align_set_horizontal_center
+ \or
+ \spac_align_set_horizontal_very_center
+ \or
+ \spac_align_set_horizontal_wide_center
+ \fi
+ \or
+ % 2 flush left
+ \ifcase\c_spac_align_state_broad
+ \spac_align_set_horizontal_right
+ \else
+ \spac_align_set_horizontal_very_right
+ \fi
+ \or
+ % 3 flush right
+ \ifcase\c_spac_align_state_broad
+ \spac_align_set_horizontal_left
+ \else
+ \spac_align_set_horizontal_very_left
+ \fi
+ \or
+ % 4 inner
+ \ifdoublesided
+ \signalinnerrealign
+ \fi
+ \rightorleftpageaction\spac_align_set_horizontal_right\spac_align_set_horizontal_left
+ \or
+ % 5 outer
+ \ifdoublesided
+ \signalouterrealign
+ \fi
+ \rightorleftpageaction\spac_align_set_horizontal_left\spac_align_set_horizontal_right
+ \or
+ % 6 oneliner
+ \ifcase\c_spac_align_state_broad
+ \spac_align_set_horizontal_right
+ \else
+ \spac_align_set_horizontal_very_right
+ \fi
+ \parfillskip\zeropoint
+ \or
+ % 7 centered last line
+ \spac_align_set_horizontal_centered_last_line
+ \or
+ % 8 right aligned last line
+ \spac_align_set_horizontal_flushedright_last_line
+ \or
+ % 9 paragraph
+ \parfillskip\zeropoint
+ \fi
+ \relax}
+
+% Page spacing:
+
+\newconstant\c_spac_align_state_page
+
+\mutable\def\bottomalignlimit{\plusthree\lineheight}
+
+\newconstant\bottomraggednessmode % 0=ragged 1=normal/align 2=baseline
+
+\permanent\protected\def\raggedbottom
+ {\bottomraggednessmode\zerocount
+ \settopskip}
+
+\permanent\protected\def\alignbottom
+ {\bottomraggednessmode\plusone
+ \settopskip}
+
+\permanent\protected\def\baselinebottom
+ {\bottomraggednessmode\plustwo
+ \settopskip}
+
+\aliased\let\normalbottom\alignbottom % downward compatible
+
+\permanent\protected\def\setbottomalignmode#1%
+ {\bottomraggednessmode#1%
+ \settopskip}
+
+\def\spac_align_flush_page
+ {\ifcase\c_spac_align_state_page
+ % keep state
+ \or
+ \raggedbottom
+ \or
+ \alignbottom
+ \or
+ \baselinebottom
+ \fi}
+
+% Directions
+
+\newconstant\c_spac_align_state_direction
+
+\def\spac_align_flush_direction
+ {\ifcase\c_spac_align_state_direction
+ % keep state
+ \or
+ \lefttoright
+ \or
+ \righttoleft
+ \fi}
+
+% Interesting is that the non cached version is also pretty efficient
+% and as we cache we seldom call that one now so one can debate the
+% speedup.
+
+\newtoks\t_spac_align_collected
+
+\aliased\let\raggedcommand \relax
+\aliased\let\updateraggedskips\relax
+
+\def\spac_align_add_to_cache
+ {\enforced\let\raggedbox\relax % why
+ % we inherit hyphenation and tolerance
+ \t_spac_align_collected \emptytoks
+ \c_spac_align_state_broad \zerocount
+ \c_spac_align_state_horizontal\zerocount
+ \c_spac_align_state_vertical \zerocount
+ \c_spac_align_state_direction \zerocount % what is default ?
+ \c_spac_align_state_page \zerocount
+ \c_spac_align_state_par_fill \zerocount
+ \ifcsname\??aligncommand\m_spac_align_asked\endcsname
+ \lastnamedcs
+ \else
+ \rawprocesscommacommand[\m_spac_align_asked]\spac_align_collect
+ \fi
+ \normalexpanded{\t_spac_align_collected
+ {\setfalse\raggedonelinerstate % bad
+ \the\t_spac_align_collected
+ \spac_align_flush_horizontal
+ \spac_align_flush_vertical
+ \spac_align_flush_direction
+ \spac_align_flush_page
+ \spac_align_flush_parfill
+ }}% kept, nice for tracing
+ \enforced\permanent\protected\edef\raggedcommand {\the\t_spac_align_collected }%
+ \enforced\permanent\protected\edef\updateraggedskips{\spac_align_flush_horizontal}%
+ \enforced\aliased\gletcsname\??alignmentnormalcache\m_spac_align_asked\endcsname\raggedcommand
+ \enforced\aliased\gletcsname\??alignmentraggedcache\m_spac_align_asked\endcsname\updateraggedskips}
+
+\def\spac_align_collect#1%
+ {\csname\??aligncommand#1\endcsname}
+
+% The local (key driven) setter:
+
+\permanent\protected\def\spac_align_prepare#1% deferred
+ {\edef\m_spac_align_asked{#1}%
+ \enforced\aliased\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
+ \ifrelax\raggedcommand
+ \spac_align_add_to_cache
+ \else
+ \enforced\aliased\expandafter\let\expandafter\updateraggedskips\csname\??alignmentraggedcache\m_spac_align_asked\endcsname
+ \fi}
+
+\aliased\let\dosetraggedcommand\spac_align_prepare % sort of public
+
+% The regular align setter:
+
+\permanent\tolerant\protected\def\setupalign[#1]% immediate
+ {\edef\m_spac_align_asked{#1}%
+ \enforced\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
+ \ifrelax\raggedcommand
+ \spac_align_add_to_cache
+ \else
+ \enforced\aliased\expandafter\let\expandafter\updateraggedskips\csname\??alignmentraggedcache\m_spac_align_asked\endcsname
+ \fi
+ \raggedcommand}
+
+\permanent\protected\def\usealignparameter#1% faster local variant
+ {\edef\m_spac_align_asked{#1\c!align}%
+ \ifempty\m_spac_align_asked\else
+ \spac_align_use_indeed
+ \fi}
+
+\permanent\protected\def\dousealignparameter#1% faster local variant
+ {\edef\m_spac_align_asked{#1}%
+ \ifempty\m_spac_align_asked\else
+ \spac_align_use_indeed
+ \fi}
+
+\def\spac_align_use_indeed
+ {\enforced\aliased\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
+ \ifrelax\raggedcommand
+ \spac_align_add_to_cache
+ \else
+ \enforced\aliased\expandafter\let\expandafter\updateraggedskips\csname\??alignmentraggedcache\m_spac_align_asked\endcsname
+ \fi
+ \raggedcommand}
+
+% experiment
+
+\protected\def\spac_align_use_later#1%
+ {\begingroup
+ \edef\m_spac_align_asked{#1}%
+ \enforced\aliased\expandafter\let\expandafter\raggedcommand\csname\??alignmentnormalcache\m_spac_align_asked\endcsname
+ \ifrelax\raggedcommand
+ \spac_align_add_to_cache
+ \fi
+ \endgroup}
+
+\protected\def\spac_align_use_now#1%
+ {\csname\??alignmentnormalcache#1\endcsname}
+
+% Maybe we need something different in columns.
+
+\permanent\protected\def\installalign#1#2% beware: commands must be unexpandable!
+ {\ifcsname\??aligncommand#1\endcsname \else
+ \defcsname\??aligncommand#1\endcsname{\toksapp\t_spac_align_collected{#2}}%
+ \fi}
+
+% The keywords:
+
+\letcsname\??aligncommand\empty \endcsname\empty
+\defcsname\??aligncommand\v!broad \endcsname{\c_spac_align_state_broad \plusone }
+\defcsname\??aligncommand\v!wide \endcsname{\c_spac_align_state_broad \plustwo }
+
+\defcsname\??aligncommand\v!bottom \endcsname{\c_spac_align_state_page \plusone }
+\defcsname\??aligncommand\v!height \endcsname{\c_spac_align_state_page \plustwo }
+\defcsname\??aligncommand\v!line \endcsname{\c_spac_align_state_page \plusthree
+ % this will become another keyword (undocumented anyway)
+ \toksapp\t_spac_align_collected{\settrue\raggedonelinerstate}}
+
+\defcsname\??aligncommand\v!high \endcsname{\c_spac_align_state_vertical \plusthree}
+\defcsname\??aligncommand\v!low \endcsname{\c_spac_align_state_vertical \plustwo }
+\defcsname\??aligncommand\v!lohi \endcsname{\c_spac_align_state_vertical \plusone }
+
+\defcsname\??aligncommand\v!flushright \endcsname{\c_spac_align_state_horizontal\plusthree}
+\defcsname\??aligncommand\v!flushleft \endcsname{\c_spac_align_state_horizontal\plustwo }
+\defcsname\??aligncommand\v!middle \endcsname{\c_spac_align_state_horizontal\plusone }
+\defcsname\??aligncommand\v!no \endcsname{\c_spac_align_state_horizontal\plustwo }
+\defcsname\??aligncommand\v!yes \endcsname{\c_spac_align_state_horizontal\zerocount}
+\defcsname\??aligncommand\v!width \endcsname{\c_spac_align_state_horizontal\zerocount}
+\defcsname\??aligncommand\v!normal \endcsname{\c_spac_align_state_horizontal\zerocount}
+\defcsname\??aligncommand\v!reset \endcsname{\c_spac_align_state_page \zerocount
+ \c_spac_align_state_horizontal\zerocount}
+\defcsname\??aligncommand\v!inner \endcsname{\c_spac_align_state_horizontal\plusfour }
+\defcsname\??aligncommand\v!outer \endcsname{\c_spac_align_state_horizontal\plusfive }
+\defcsname\??aligncommand\v!flushinner \endcsname{\c_spac_align_state_horizontal\plusfour }
+\defcsname\??aligncommand\v!flushouter \endcsname{\c_spac_align_state_horizontal\plusfive }
+\defcsname\??aligncommand\v!right \endcsname{\c_spac_align_state_horizontal\plustwo }
+\defcsname\??aligncommand\v!left \endcsname{\c_spac_align_state_horizontal\plusthree}
+\defcsname\??aligncommand\v!center \endcsname{\c_spac_align_state_horizontal\plusone
+ \c_spac_align_state_broad \plustwo }
+\defcsname\??aligncommand\v!disable \endcsname{\c_spac_align_state_horizontal\plussix }
+\defcsname\??aligncommand\v!last \endcsname{\c_spac_align_state_horizontal\plusseven}
+\defcsname\??aligncommand\v!end \endcsname{\c_spac_align_state_horizontal\pluseight}
+\defcsname\??aligncommand\v!paragraph \endcsname{\c_spac_align_state_horizontal\plusnine}
+
+\defcsname\??aligncommand\v!lefttoright \endcsname{\c_spac_align_state_direction \plusone }
+\defcsname\??aligncommand\v!righttoleft \endcsname{\c_spac_align_state_direction \plustwo }
+\defcsname\??aligncommand l2r\endcsname{\c_spac_align_state_direction \plusone }
+\defcsname\??aligncommand r2l\endcsname{\c_spac_align_state_direction \plustwo }
+
+\defcsname\??aligncommand\v!table \endcsname{\c_spac_align_state_vertical \plusthree
+ \c_spac_align_state_broad \plusone
+ \c_spac_align_state_horizontal\plustwo }
+
+\defcsname\??aligncommand\v!lesshyphenation\endcsname{\toksapp\t_spac_align_collected{\lesshyphens}}
+\defcsname\??aligncommand\v!morehyphenation\endcsname{\toksapp\t_spac_align_collected{\morehyphens}}
+
+\defcsname\??aligncommand\v!hanging \endcsname{\toksapp\t_spac_align_collected{\font_protruding_enable }}
+\defcsname\??aligncommand\v!hangingboth \endcsname{\toksapp\t_spac_align_collected{\font_protruding_enable_b}}
+\defcsname\??aligncommand\v!nothanging \endcsname{\toksapp\t_spac_align_collected{\font_protruding_disable}}
+\defcsname\??aligncommand\v!hz \endcsname{\toksapp\t_spac_align_collected{\font_expansion_enable }}
+\defcsname\??aligncommand\v!fullhz \endcsname{\toksapp\t_spac_align_collected{\font_expansion_enable_k}}
+\defcsname\??aligncommand\v!nohz \endcsname{\toksapp\t_spac_align_collected{\font_expansion_disable }}
+%defcsname\??aligncommand\v!spacing \endcsname{\toksapp\t_spac_align_collected{\normalspacing\zerocount}} % not yet
+%defcsname\??aligncommand\v!nospacing \endcsname{\toksapp\t_spac_align_collected{\normalspacing\plusone}} % not yet
+\defcsname\??aligncommand\v!hyphenated \endcsname{\toksapp\t_spac_align_collected{\dohyphens}}
+\defcsname\??aligncommand\v!nothyphenated \endcsname{\toksapp\t_spac_align_collected{\nohyphens}}
+
+\defcsname\??aligncommand\v!tolerant \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_tolerant}}
+\defcsname\??aligncommand\v!verytolerant \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_very_tolerant}}
+\defcsname\??aligncommand\v!stretch \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_stretch}}
+\defcsname\??aligncommand\v!extremestretch \endcsname{\toksapp\t_spac_align_collected{\spac_align_set_extreme_stretch}}
+
+\defcsname\??aligncommand \v!final\endcsname{\c_spac_align_state_par_fill\plusone}
+\defcsname\??aligncommand1*\v!final\endcsname{\c_spac_align_state_par_fill\plusone}
+\defcsname\??aligncommand2*\v!final\endcsname{\c_spac_align_state_par_fill\plustwo} % hardcoded multiplier
+\defcsname\??aligncommand3*\v!final\endcsname{\c_spac_align_state_par_fill\plusthree}
+\defcsname\??aligncommand4*\v!final\endcsname{\c_spac_align_state_par_fill\plusfour}
+
+% a one shot (only usefull in e.g. framed, also needs tolerance and stretch)
+
+\defcsname\??aligncommand \v!more\endcsname{\toksapp\t_spac_align_collected{\looseness\plusone}}
+\defcsname\??aligncommand1*\v!more\endcsname{\toksapp\t_spac_align_collected{\looseness\plusone}}
+\defcsname\??aligncommand2*\v!more\endcsname{\toksapp\t_spac_align_collected{\looseness\plustwo}}
+
+%defcsname\??aligncommand ...\endcsname{\toksapp\t_spac_align_collected{\nopenalties}}
+%defcsname\??aligncommand ...\endcsname{\toksapp\t_spac_align_collected{\setdefaultpenalties}}
+
+% experiment
+
+\defcsname\??aligncommand\v!always\endcsname{\toksapp\t_spac_align_collected{\bitwiseflip\hyphenationmode\forcecheckhyphenationmodecode}}
+
+\definehspace [\v!final] [\emspaceamount]
+
+\def\spac_align_flush_parfill
+ {\ifcase\c_spac_align_state_par_fill\else
+ \spac_align_flush_parfill_indeed{\number\c_spac_align_state_par_fill}%
+ \fi}
+
+\protected\def\spac_align_flush_parfill_indeed#1%
+ {\parfillskip
+ #1\directhspaceamount\v!final
+ % plus \dimexpr\availablehsize-#1\directhspaceamount\v!final\relax
+ plus 1fill
+ \relax}
+
+%D For Wolfgang:
+
+\newtoks \t_spac_every_swap_align
+
+\appendtoks
+ \defcsname\??aligncommand\v!right\endcsname{\c_spac_align_state_horizontal\plusthree}%
+ \defcsname\??aligncommand\v!left \endcsname{\c_spac_align_state_horizontal\plustwo }%
+\to \t_spac_every_swap_align
+
+\permanent\protected\def\enablereversealignment
+ {\the\t_spac_every_swap_align
+ \t_spac_every_swap_align\emptytoks}
+
+% Visible commands:
+
+\aliased\let\notragged \spac_align_set_horizontal_none
+\aliased\let\raggedleft \spac_align_set_horizontal_left
+\aliased\let\raggedcenter \spac_align_set_horizontal_center
+\aliased\let\raggedright \spac_align_set_horizontal_right
+\aliased\let\veryraggedleft \spac_align_set_horizontal_very_left
+\aliased\let\veryraggedcenter \spac_align_set_horizontal_very_center
+\aliased\let\veryraggedright \spac_align_set_horizontal_very_right
+\aliased\let\raggedwidecenter \spac_align_set_horizontal_wide_center
+\aliased\let\centeredlastline \spac_align_set_horizontal_centered_last_line
+\aliased\let\flushedrightlastline\spac_align_set_horizontal_flushedright_last_line
+\aliased\let\ttraggedright \spac_align_set_horizontal_right_tt % a plain command
+\aliased\let\forgetragged \spac_align_set_horizontal_none
+
+\appendtoks
+ \spac_align_set_horizontal_none
+\to \everyforgetall
+
+% Box commands.
+
+\permanent\protected\def\ibox
+ {\vbox\bgroup
+ \forgetall
+ \enforced\let\\\endgraf
+ \ifdoublesided\signalinnerrealign\fi
+ \doifelserightpage\spac_align_set_horizontal_right\spac_align_set_horizontal_left
+ \let\next}
+
+\permanent\protected\def\obox
+ {\vbox\bgroup
+ \forgetall
+ \enforced\let\\\endgraf
+ \ifdoublesided\signalouterrealign\fi
+ \doifelserightpage\spac_align_set_horizontal_left\spac_align_set_horizontal_right
+ \let\next}
+
+\let\raggedbox\relax
+
+\permanent\protected\def\dosetraggedvbox#1% can be more keys .. how about caching here (but seldom used)
+ {\enforced\let\raggedbox\vbox
+ \processcommacommand[#1]\spac_align_set_ragged_vbox}
+
+\permanent\protected\def\dosetraggedhbox#1% can be more keys .. how about caching here (but seldom used)
+ {\enforced\let\raggedbox\hbox
+ \processcommacommand[#1]\spac_align_set_ragged_hbox}
+
+\def\spac_align_set_ragged_vbox#1%
+ {\ifcsname\??alignvertical#1\endcsname
+ \lastnamedcs
+ \quitcommalist
+ \fi}
+
+\def\spac_align_set_ragged_hbox#1%
+ {\ifcsname\??alignhorizontal#1\endcsname
+ \lastnamedcs
+ \quitcommalist
+ \fi}
+
+\permanent\protected\def\spac_align_vertical_left {\lbox}
+\permanent\protected\def\spac_align_vertical_right {\rbox}
+\permanent\protected\def\spac_align_vertical_middle {\cbox}
+\permanent\protected\def\spac_align_vertical_inner {\ibox}
+\permanent\protected\def\spac_align_vertical_outer {\obox}
+\permanent\protected\def\spac_align_vertical_flushleft {\rbox}
+\permanent\protected\def\spac_align_vertical_flushright{\lbox}
+\permanent\protected\def\spac_align_vertical_center {\cbox}
+\permanent\protected\def\spac_align_vertical_no {\vbox\bgroup\spac_align_set_horizontal_right\let\next=}
+
+\permanent\protected\def\spac_align_horizontal_left {\doalignedline\v!left}
+\permanent\protected\def\spac_align_horizontal_right {\doalignedline\v!right}
+\permanent\protected\def\spac_align_horizontal_middle {\doalignedline\v!middle}
+\permanent\protected\def\spac_align_horizontal_inner {\doalignedline\v!inner}
+\permanent\protected\def\spac_align_horizontal_outer {\doalignedline\v!outer}
+\permanent\protected\def\spac_align_horizontal_flushleft {\doalignedline\v!right}
+\permanent\protected\def\spac_align_horizontal_flushright{\doalignedline\v!left}
+\permanent\protected\def\spac_align_horizontal_center {\doalignedline\v!middle}
+
+\defcsname\??alignvertical \v!left \endcsname{\enforced\let\raggedbox\spac_align_vertical_left}
+\defcsname\??alignvertical \v!right \endcsname{\enforced\let\raggedbox\spac_align_vertical_right}
+\defcsname\??alignvertical \v!middle \endcsname{\enforced\let\raggedbox\spac_align_vertical_middle}
+\defcsname\??alignvertical \v!inner \endcsname{\enforced\let\raggedbox\spac_align_vertical_inner}
+\defcsname\??alignvertical \v!outer \endcsname{\enforced\let\raggedbox\spac_align_vertical_outer}
+\defcsname\??alignvertical \v!flushleft \endcsname{\enforced\let\raggedbox\spac_align_vertical_flushleft }
+\defcsname\??alignvertical \v!flushright\endcsname{\enforced\let\raggedbox\spac_align_vertical_flushright}
+\defcsname\??alignvertical \v!center \endcsname{\enforced\let\raggedbox\spac_align_vertical_center}
+\defcsname\??alignvertical \v!no \endcsname{\enforced\let\raggedbox\spac_align_vertical_no}
+
+\defcsname\??alignhorizontal\v!left \endcsname{\enforced\let\raggedbox\spac_align_horizontal_left}
+\defcsname\??alignhorizontal\v!right \endcsname{\enforced\let\raggedbox\spac_align_horizontal_right}
+\defcsname\??alignhorizontal\v!middle \endcsname{\enforced\let\raggedbox\spac_align_horizontal_middle}
+\defcsname\??alignhorizontal\v!inner \endcsname{\enforced\let\raggedbox\spac_align_horizontal_inner}
+\defcsname\??alignhorizontal\v!outer \endcsname{\enforced\let\raggedbox\spac_align_horizontal_outer}
+\defcsname\??alignhorizontal\v!flushleft \endcsname{\enforced\let\raggedbox\spac_align_horizontal_flushleft}
+\defcsname\??alignhorizontal\v!flushright\endcsname{\enforced\let\raggedbox\spac_align_horizontal_flushright}
+\defcsname\??alignhorizontal\v!center \endcsname{\enforced\let\raggedbox\spac_align_horizontal_center}
+
+% The next one can be in use so we keep it around but oen should
+% be aware of possible interference.
+
+\permanent\protected\def\setraggedskips#1#2#3#4#5#6#7% never change this name (todo: inline this one .. less tracingall)
+ {\enforced\permanent\protected\def\updateraggedskips{\dosetraggedskips{#1}{#2}{#3}{#4}{#5}{#6}{#7}}%
+ \updateraggedskips}
+
+\permanent\protected\def\dosetraggedskips#1#2#3#4#5#6#7%
+ {\raggedstatus #1\relax
+ \leftskip 1\leftskip \s!plus#2\relax
+ \rightskip 1\rightskip\s!plus#3\relax
+ \spaceskip #4\relax
+ \xspaceskip #5\relax
+ \parfillskip \zeropoint\s!plus#6\relax
+ \parindent #7\relax
+ \c_attr_alignstate\ifcase\raggedstatus\attributeunsetvalue\else\raggedstatus\fi}
+
+% older (context) names:
+
+\aliased\let\spaceamount \interwordspace
+\aliased\let\emspaceamount\emwidth
+
+% tracing:
+
+\def\spac_show_par_data#1%
+ {\ifrelax#1\else
+ \hbox{\string#1: \the#1}\endgraf
+ \expandafter\spac_show_par_data
+ \fi}
+
+\permanent\protected\def\showpardata
+ {\normalexpanded{\dontleavehmode\inleftmargin{\vsmash{\infofont\framed[\c!align=\v!right]{%
+ \hbox{font: \fontname\font}\endgraf
+ \spac_show_par_data
+ \interwordspace \interwordstretch \interwordshrink \emwidth \exheight \extraspace
+ \hsize \vsize
+ \leftskip \rightskip
+ \spaceskip \xspaceskip
+ \parindent \parfillskip
+ \hyphenpenalty \exhyphenpenalty \automatichyphenpenalty \explicithyphenpenalty
+ \displaywidowpenalty \widowpenalty \clubpenalty \brokenpenalty
+ \doublehyphendemerits \finalhyphendemerits \adjdemerits
+ \relax}}}}}
+
+\permanent\protected\def\startshowpardata
+ {\begingroup
+ \showstruts
+ \tracingparagraphs\maxdimen
+ \appendtoksonce\showpardata\to\everypar}
+
+\permanent\protected\def\stopshowpardata
+ {\endgraf
+ \endgroup}
+
+% Structure:
+
+\protected\def\startalignment
+ {\par
+ \begingroup
+ \setupalign}
+
+\protected\def\stopalignment
+ {\par
+ \endgroup}
+
+\setnewconstant\alignstrutmode\plusone
+
+% see later for the real definition, which in the simple case is:
+
+\newtoks \everyleftofalignedline
+\newtoks \everyrightofalignedline
+
+\permanent\protected\def\shiftalignedline#1#2#3#4% left, right, inner, outer
+ {\rightorleftpageaction
+ {\everyleftofalignedline {\hskip\dimexpr#1+#3\relax}%
+ \everyrightofalignedline{\hskip\dimexpr#2+#4\relax}}
+ {\everyleftofalignedline {\hskip\dimexpr#1+#4\relax}%
+ \everyrightofalignedline{\hskip\dimexpr#2+#3\relax}}}
+
+\permanent\protected\def\doalignline#1#2% \\ == newline
+ {\noindentation % was \noindent
+ \dontleavehmode % added in marrakesch at TUG 2006\begingroup
+ \begingroup
+ \setlocalhsize % new
+ \enforced\permanent\protected\def\\{\egroup\par\doalignline{#1}{#2}\bgroup}%
+ \dowithnextbox
+ {\hbox to \localhsize
+ {\ifcase\alignstrutmode\or\strut\fi
+ \the\everyleftofalignedline
+ #1\unhbox\nextbox#2\relax
+ \the\everyrightofalignedline}%
+ \endgroup}
+ \hbox}
+
+% plain commands
+
+\ifdefined\line \else \permanent\protected\def\line {\hbox to\hsize} \fi
+\ifdefined\leftline \else \permanent\protected\def\leftline #1{\line{#1\hss}} \fi
+\ifdefined\rightline \else \permanent\protected\def\rightline #1{\line{\hss#1}} \fi
+\ifdefined\centerline \else \permanent\protected\def\centerline#1{\line{\hss#1\hss}} \fi
+
+% direct commands
+
+\installcorenamespace{alignwrapper}
+
+\protected\defcsname\??alignwrapper\v!left \endcsname{\doalignline\relax \hss }
+\protected\defcsname\??alignwrapper\v!middle\endcsname{\doalignline\hss \hss }
+\protected\defcsname\??alignwrapper\v!right \endcsname{\doalignline\hss \relax}
+\protected\defcsname\??alignwrapper\v!max \endcsname{\doalignline\relax \relax}
+
+\def\spac_align_wrapper_middle{\doalignline\hss\hss}
+
+\def\spac_align_wrapper_handle#1%
+ {\ifcsname\??alignwrapper#1\endcsname\expandafter\lastnamedcs\else\expandafter\spac_align_wrapper_middle\fi}
+
+\permanent\tolerant\protected\def\startlinealignment[#1]%
+ {\spac_align_wrapper_handle{#1}%
+ \bgroup\ignorespaces}
+
+\protected\def\spac_align_wrapper_stop
+ {\removeunwantedspaces\egroup}
+
+\permanent\let\stoplinealignment\spac_align_wrapper_stop
+
+\permanent\protected\def\startleftaligned {\startlinealignment[\v!left ]} \permanent\let\stopleftaligned \spac_align_wrapper_stop
+\permanent\protected\def\startmiddlealigned{\startlinealignment[\v!middle]} \permanent\let\stopmiddlealigned\spac_align_wrapper_stop
+\permanent\protected\def\startrightaligned {\startlinealignment[\v!right ]} \permanent\let\stoprightaligned \spac_align_wrapper_stop
+\permanent\protected\def\startmaxaligned {\startlinealignment[\v!max ]} \permanent\let\stopmaxaligned \spac_align_wrapper_stop
+
+\aliased\let\startmidaligned \startmiddlealigned \aliased\let\stopmidaligned \stopmiddlealigned
+\aliased\let\startcenteraligned\startmiddlealigned \aliased\let\stopcenteraligned\stopmiddlealigned
+
+\permanent\protected\def\leftaligned {\spac_align_wrapper_handle\v!left }
+\permanent\protected\def\middlealigned{\spac_align_wrapper_handle\v!middle}
+\permanent\protected\def\rightaligned {\spac_align_wrapper_handle\v!right }
+\permanent\protected\def\maxaligned {\spac_align_wrapper_handle\v!max }
+
+\aliased\let\midaligned \middlealigned
+\aliased\let\centeraligned\middlealigned
+
+\installcorenamespace{alignline}
+
+\letcsname\??alignline\v!left \endcsname\leftaligned
+\letcsname\??alignline\v!right \endcsname\rightaligned
+\letcsname\??alignline\v!middle \endcsname\midaligned
+\letcsname\??alignline\v!flushleft \endcsname\rightaligned % beware, makes no sense in locations / we will have a special locations handler
+\letcsname\??alignline\v!flushright\endcsname\leftaligned % beware, makes no sense in locations / we will have a special locations handler
+\letcsname\??alignline\v!center \endcsname\midaligned
+\letcsname\??alignline\v!max \endcsname\maxaligned
+
+%D Experimental (will be redone when floats are redone as it's real messy
+%D now). It can also be made faster (if needed).
+
+\permanent\protected\def\doxalignline#1#2#3#4#5#6%
+ {\noindentation % was \noindent
+ \dontleavehmode % added in marrakesch at TUG 2006\begingroup
+ \begingroup
+ \setlocalhsize
+ \enforced\permanent\protected\def\\{\egroup\par\doxalignline#1#2#3#4#5#6\bgroup}% inefficient
+ \dowithnextbox
+ {\hbox to \localhsize
+ {#1\hskip\ifdone#2\else#3\fi#4%
+ \hbox to \localhsize
+ {\the\everyleftofalignedline
+ \ifcase\alignstrutmode\or\strut\fi
+ \ifdone#5\unhbox\nextbox#6\else#6\unhbox\nextbox#5\fi
+ \the\everyrightofalignedline}%
+ \hss}%
+ \endgroup}
+ \hbox}
+
+\permanent\protected\def\doxcheckline % used for floats so multipass anyway
+ {\signalrightpage\doifelserightpage\donetrue\donefalse}
+
+\defcsname\??alignline\v!inner \endcsname{\doxalignline\doxcheckline++\zeropoint \relax\hss }
+\defcsname\??alignline\v!outer \endcsname{\doxalignline\doxcheckline++\zeropoint \hss \relax}
+\defcsname\??alignline\v!innermargin\endcsname{\doxalignline\doxcheckline-+\innermargintotal\relax\hss }
+\defcsname\??alignline\v!outermargin\endcsname{\doxalignline\doxcheckline+-\outermargintotal\hss \relax}
+\defcsname\??alignline\v!inneredge \endcsname{\doxalignline\doxcheckline-+\inneredgetotal \relax\hss }
+\defcsname\??alignline\v!outeredge \endcsname{\doxalignline\doxcheckline+-\outeredgetotal \hss \relax}
+\defcsname\??alignline\v!backspace \endcsname{\doxalignline\doxcheckline-+\backspace \relax\hss }
+\defcsname\??alignline\v!cutspace \endcsname{\doxalignline\doxcheckline+-\cutspace \hss \relax}
+
+\defcsname\??alignline\v!leftmargin \endcsname{\doxalignline\donefalse --\leftmargintotal \hss \relax}
+\defcsname\??alignline\v!rightmargin\endcsname{\doxalignline\donefalse ++\rightmargintotal\relax\hss }
+\defcsname\??alignline\v!leftedge \endcsname{\doxalignline\donefalse --\leftedgetotal \hss \relax}
+\defcsname\??alignline\v!rightedge \endcsname{\doxalignline\donefalse ++\rightedgetotal \relax\hss }
+
+\permanent\protected\def\doalignedline#1{\begincsname\??alignline#1\endcsname}
+
+\permanent\protected\def\alignedline#1#2% setting default
+ {\ifcsname\??alignline#1\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \csname\??alignline#2\expandafter\endcsname
+ \fi}
+
+% beware: \wordright{whatever\kern-\rightskip} should work!
+% so, no funny boxing here
+
+%D We need to pick up a box (downward compatible in case \type {\bgroup}
+%D is used (test suite index example).
+
+\permanent\tolerant\protected\def\wordright[#1]%
+ {\dowithnextbox
+ {\normalexpanded{\spac_word_right_indeed{#1}{\hpack{\thebox\nextbox}}}}
+ \hbox}
+
+\protected\def\spac_word_right_indeed#1#2%
+ {\registerparwrapper
+ {\v!word:\v!right}
+ {\begingroup
+ \frozen\parfillskip \zeropoint
+ \frozen\finalhyphendemerits\zerocount
+ \endgroup}
+ {\doifelseparwrapper{\v!word:\v!right}%
+ {\doifelse{#1}\v!right{\kern-\rightskip}{\doifsomething{#1}{\kern-#1}}%
+ \strut \removeunwantedspaces
+ \hfill
+ \allowbreak % changed back from \hskip\zeropoint
+ \quad}%
+ {\allowbreak % changed back from \hskip\zeropoint
+ \break}%
+ \unregisterparwrapper{\v!word:\v!right}%
+ \strut
+ \hfill
+ \nobreak
+ #2%
+ \allowbreak}}
+
+% \dorecurse{5}{something} \wordright{--someone} \endgraf
+% \dorecurse{6}{something} \wordright{--someone} \endgraf
+% \dorecurse{7}{something} \wordright{--someone} \endgraf
+%
+% \dorecurse{5}{something} \wordright{--someone else entirely} \endgraf
+% \dorecurse{6}{something} \wordright{--someone else entirely} \endgraf
+% \dorecurse{7}{something} \wordright{--someone else entirely} \endgraf
+%
+% \wordright[\rightskip]{whatever}
+
+% Trick posted by WS on mailing list, generalized a bit. The bottom text only shows
+% op when there is one line space available. We could add some extra space if needed.
+
+% v\protected\def\bottomword
+% {\par
+% \dowithnextbox
+% {\leaders\box\nextbox\vfil\page}
+% \vbox to \lineheight}
+%
+% \permanent\protected\def\bottomword
+% {\par
+% \groupedcommand
+% {\leaders
+% \vbox to \lineheight\bgroup}
+% {\egroup
+% \vfil
+% \page}}
+
+% \simplealignedbox{2cm}{right}{x}
+
+\installcorenamespace{alignsimple}
+\installcorenamespace{alignsimplereverse}
+
+% todo: also handle \bgroup ... \egroup
+
+\protected\def\spac_align_simple_left #1{{#1\hss}}
+\protected\def\spac_align_simple_right #1{{\hss#1}}
+\protected\def\spac_align_simple_middle#1{{\hss#1\hss}}
+
+\letcsname\??alignsimple \v!right \endcsname\spac_align_simple_left
+\letcsname\??alignsimple \v!outer \endcsname\spac_align_simple_left % not managed! see linenumbers
+\letcsname\??alignsimple \v!flushleft \endcsname\spac_align_simple_left
+\letcsname\??alignsimple \v!left \endcsname\spac_align_simple_right
+\letcsname\??alignsimple \v!inner \endcsname\spac_align_simple_right % not managed! see linenumbers
+\letcsname\??alignsimple \v!flushright\endcsname\spac_align_simple_right
+\letcsname\??alignsimple \v!middle \endcsname\spac_align_simple_middle
+
+\letcsname\??alignsimplereverse\v!right \endcsname\spac_align_simple_right
+\letcsname\??alignsimplereverse\v!outer \endcsname\spac_align_simple_right % not managed! see linenumbers
+\letcsname\??alignsimplereverse\v!flushleft \endcsname\spac_align_simple_right
+\letcsname\??alignsimplereverse\v!left \endcsname\spac_align_simple_left
+\letcsname\??alignsimplereverse\v!inner \endcsname\spac_align_simple_left % not managed! see linenumbers
+\letcsname\??alignsimplereverse\v!flushright\endcsname\spac_align_simple_left
+\letcsname\??alignsimplereverse\v!middle \endcsname\spac_align_simple_middle
+
+\permanent\protected\def\simplealignedbox#1#2%
+ {\hbox \ifdim#1>\zeropoint to #1
+ \ifcsname\??alignsimple#2\endcsname
+ \doubleexpandafter\lastnamedcs
+ \else
+ \doubleexpandafter\spac_align_simple_left
+ \fi
+ \fi}
+
+\permanent\protected\def\simplealignedspreadbox#1#2%
+ {\hbox \ifdim#1>\zeropoint spread #1
+ \ifcsname\??alignsimple#2\endcsname
+ \doubleexpandafter\lastnamedcs
+ \else
+ \doubleexpandafter\spac_align_simple_left
+ \fi
+ \fi}
+
+\permanent\protected\def\simplealignedboxplus#1#2#3%
+ {\hbox #3 \ifdim#1>\zeropoint to #1
+ \ifcsname\??alignsimple#2\endcsname
+ \doubleexpandafter\lastnamedcs
+ \else
+ \doubleexpandafter\spac_align_simple_left
+ \fi
+ \fi}
+
+\newconditional\alignsimplelefttoright \settrue\alignsimplelefttoright
+
+\permanent\protected\def\simplereversealignedbox#1#2%
+ {\hbox \ifdim#1>\zeropoint to #1
+ \ifcsname\??alignsimplereverse#2\endcsname
+ \doubleexpandafter\lastnamedcs
+ \else
+ \doubleexpandafter\spac_align_simple_left
+ \fi
+ \fi}
+
+\permanent\protected\def\simplereversealignedboxplus#1#2#3%
+ {\hbox #3 \ifdim#1>\zeropoint to #1
+ \ifcsname\??alignsimplereverse#2\endcsname
+ \doubleexpandafter\lastnamedcs
+ \else
+ \doubleexpandafter\spac_align_simple_left
+ \fi
+ \fi}
+
+% Some obsolete (old) helpers:
+
+\permanent\tolerant\protected\def\definehbox[#1]#*[#2]%
+ {\ifarguments\else\frozen\instance\protected\defcsname hbox#1\endcsname##1{\hbox to #2{\begstrut##1\endstrut\hss}}\fi}
+
+%D Some direction related helpers:
+
+\installcorenamespace {reverse}
+
+\defcsname\??reverse\v!normal \endcsname{\ifconditional\inlinelefttoright\else\s!reverse\fi}
+\defcsname\??reverse\v!reverse\endcsname{\ifconditional\inlinelefttoright \s!reverse\fi}
+
+\permanent\def\usedirectionparameterreverse#1{\begincsname\??reverse#1\c!direction\endcsname}
+
+%D Now official:
+
+\permanent\protected\def\spaceorpar{\endgraf\ifhmode\space\fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-chr.lmt b/Master/texmf-dist/tex/context/base/mkxl/spac-chr.lmt
new file mode 100644
index 00000000000..ac90926e1ab
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-chr.lmt
@@ -0,0 +1,348 @@
+if not modules then modules = { } end modules ['spac-chr'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to spac-chr.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local byte, lower = string.byte, string.lower
+
+-- beware: attribute copying is bugged ... there will be a proper luatex helper
+-- for this
+
+-- to be redone: characters will become tagged spaces instead as then we keep track of
+-- spaceskip etc
+
+-- todo: only setattr when export / use properties
+
+local next = next
+
+local trace_characters = false trackers.register("typesetters.characters", function(v) trace_characters = v end)
+local trace_nbsp = false trackers.register("typesetters.nbsp", function(v) trace_nbsp = v end)
+
+local report_characters = logs.reporter("typesetting","characters")
+
+local nodes, node = nodes, node
+
+local nuts = nodes.nuts
+
+local getboth = nuts.getboth
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getlanguage = nuts.getlanguage
+local setchar = nuts.setchar
+local setattrlist = nuts.setattrlist
+local getfont = nuts.getfont
+local setsubtype = nuts.setsubtype
+local isglyph = nuts.isglyph
+
+local setcolor = nodes.tracers.colors.set
+
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+----- traverse_id = nuts.traverse_id
+----- traverse_char = nuts.traverse_char
+local nextchar = nuts.traversers.char
+local nextglyph = nuts.traversers.glyph
+
+local copy_node = nuts.copy
+
+local nodepool = nuts.pool
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+local new_kern = nodepool.kern
+local new_rule = nodepool.rule
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+
+local glyph_code = nodecodes.glyph
+local spaceskip_code = gluecodes.spaceskip
+
+local chardata = characters.data
+local is_punctuation = characters.is_punctuation
+
+local typesetters = typesetters
+
+local unicodeblocks = characters.blocks
+
+local characters = typesetters.characters or { } -- can be predefined
+typesetters.characters = characters
+
+local fonthashes = fonts.hashes
+local fontparameters = fonthashes.parameters
+local fontcharacters = fonthashes.characters
+local fontquads = fonthashes.quads
+
+local setmetatableindex = table.setmetatableindex
+
+local a_character = attributes.private("characters")
+local a_alignstate = attributes.private("alignstate")
+
+local c_zero = byte('0')
+local c_period = byte('.')
+
+local function inject_quad_space(unicode,head,current,fraction)
+ if fraction ~= 0 then
+ fraction = fraction * fontquads[getfont(current)]
+ end
+ local glue = new_glue(fraction)
+ setattrlist(glue,current)
+ setattrlist(current) -- why reset all
+ setattr(glue,a_character,unicode)
+ return insert_node_after(head,current,glue)
+end
+
+local function inject_char_space(unicode,head,current,parent)
+ local font = getfont(current)
+ local char = fontcharacters[font][parent]
+ local glue = new_glue(char and char.width or fontparameters[font].space)
+ setattrlist(glue,current)
+ setattrlist(current) -- why reset all
+ setattr(glue,a_character,unicode)
+ return insert_node_after(head,current,glue)
+end
+
+local function inject_nobreak_space(unicode,head,current,space,spacestretch,spaceshrink)
+ local glue = new_glue(space,spacestretch,spaceshrink)
+ local penalty = new_penalty(10000)
+ setattrlist(glue,current)
+ setattrlist(current) -- why reset all
+ setattr(glue,a_character,unicode) -- bombs
+ head, current = insert_node_after(head,current,penalty)
+ if trace_nbsp then
+ local rule = new_rule(space)
+ local kern = new_kern(-space)
+ local penalty = new_penalty(10000)
+ setcolor(rule,"orange")
+ head, current = insert_node_after(head,current,rule)
+ head, current = insert_node_after(head,current,kern)
+ head, current = insert_node_after(head,current,penalty)
+ end
+ return insert_node_after(head,current,glue)
+end
+
+local function nbsp(head,current)
+ local para = fontparameters[getfont(current)]
+ local attr = getattr(current,a_alignstate) or 0
+ if attr >= 1 or attr <= 3 then -- flushright
+ head, current = inject_nobreak_space(0x00A0,head,current,para.space,0,0)
+ else
+ head, current = inject_nobreak_space(0x00A0,head,current,para.space,para.spacestretch,para.spaceshrink)
+ end
+ setsubtype(current,spaceskip_code)
+ return head, current
+end
+
+-- assumes nuts or nodes, depending on callers .. so no tonuts here
+
+function characters.replacenbsp(head,original)
+ local head, current = nbsp(head,original)
+ return remove_node(head,original,true)
+end
+
+function characters.replacenbspaces(head)
+ local wipe = false
+ for current, char, font in nextglyph, head do -- can be anytime so no traverse_char
+ if char == 0x00A0 then
+ if wipe then
+ head = remove_node(h,current,true)
+ wipe = false
+ end
+ local h = nbsp(head,current)
+ if h then
+ wipe = current
+ end
+ end
+ end
+ if wipe then
+ head = remove_node(head,current,true)
+ end
+ return head
+end
+
+-- This initialization might move someplace else if we need more of it. The problem is that
+-- this module depends on fonts so we have an order problem.
+
+local nbsphash = { } setmetatableindex(nbsphash,function(t,k)
+ -- this needs checking !
+ for i=unicodeblocks.devanagari.first,unicodeblocks.devanagari.last do nbsphash[i] = true end
+ for i=unicodeblocks.kannada .first,unicodeblocks.kannada .last do nbsphash[i] = true end
+ setmetatableindex(nbsphash,nil)
+ return nbsphash[k]
+end)
+
+local methods = {
+
+ -- The next one uses an attribute assigned to the character but still we
+ -- don't have the 'local' value.
+
+ -- maybe also 0x0008 : backspace
+
+ [0x001F] = function(head,current) -- kind of special
+ local next = getnext(current)
+ if next then
+ local char, font = isglyph(next)
+ if char then
+ head, current = remove_node(head,current,true)
+ if not is_punctuation[char] then
+ local p = fontparameters[font]
+ head, current = insert_node_before(head,current,new_glue(p.space,p.space_stretch,p.space_shrink))
+ end
+ end
+ end
+ end,
+
+ [0x00A0] = function(head,current) -- nbsp
+ local prev, next = getboth(current)
+ if next then
+ local char = isglyph(current)
+ if not char then
+ -- move on
+ elseif char == 0x200C or char == 0x200D then -- nzwj zwj
+ next = getnext(next)
+ if next then
+ char = isglyph(next)
+ if char and nbsphash[char] then
+ return false
+ end
+ end
+ elseif nbsphash[char] then
+ return false
+ end
+ end
+ if prev then
+ local char = isglyph(prev)
+ if char and nbsphash[char] then
+ return false
+ end
+ end
+ return nbsp(head,current)
+ end,
+
+ [0x00AD] = function(head,current) -- softhyphen
+ return insert_node_after(head,current,languages.explicithyphen(current))
+ end,
+
+ [0x2000] = function(head,current) -- enquad
+ return inject_quad_space(0x2000,head,current,1/2)
+ end,
+
+ [0x2001] = function(head,current) -- emquad
+ return inject_quad_space(0x2001,head,current,1)
+ end,
+
+ [0x2002] = function(head,current) -- enspace
+ return inject_quad_space(0x2002,head,current,1/2)
+ end,
+
+ [0x2003] = function(head,current) -- emspace
+ return inject_quad_space(0x2003,head,current,1)
+ end,
+
+ [0x2004] = function(head,current) -- threeperemspace
+ return inject_quad_space(0x2004,head,current,1/3)
+ end,
+
+ [0x2005] = function(head,current) -- fourperemspace
+ return inject_quad_space(0x2005,head,current,1/4)
+ end,
+
+ [0x2006] = function(head,current) -- sixperemspace
+ return inject_quad_space(0x2006,head,current,1/6)
+ end,
+
+ [0x2007] = function(head,current) -- figurespace
+ return inject_char_space(0x2007,head,current,c_zero)
+ end,
+
+ [0x2008] = function(head,current) -- punctuationspace
+ return inject_char_space(0x2008,head,current,c_period)
+ end,
+
+ [0x2009] = function(head,current) -- breakablethinspace
+ return inject_quad_space(0x2009,head,current,1/8) -- same as next
+ end,
+
+ [0x200A] = function(head,current) -- hairspace
+ return inject_quad_space(0x200A,head,current,1/8) -- same as previous (todo)
+ end,
+
+ [0x200B] = function(head,current) -- zerowidthspace
+ return inject_quad_space(0x200B,head,current,0)
+ end,
+
+ [0x202F] = function(head,current) -- narrownobreakspace
+ return inject_nobreak_space(0x202F,head,current,fontquads[getfont(current)]/8)
+ end,
+
+ [0x205F] = function(head,current) -- math thinspace
+ return inject_nobreak_space(0x205F,head,current,4*fontquads[getfont(current)]/18)
+ end,
+
+ -- The next one is also a bom so maybe only when we have glyphs around it
+
+ -- [0xFEFF] = function(head,current) -- zerowidthnobreakspace
+ -- return head, current
+ -- end,
+
+}
+
+characters.methods = methods
+
+-- function characters.handler(head) -- todo: use traverse_id
+-- local current = head
+-- while current do
+-- local char, id = isglyph(current)
+-- if char then
+-- local next = getnext(current)
+-- local method = methods[char]
+-- if method then
+-- if trace_characters then
+-- report_characters("replacing character %C, description %a",char,lower(chardata[char].description))
+-- end
+-- local h = method(head,current)
+-- if h then
+-- head = remove_node(h,current,true)
+-- end
+-- end
+-- current = next
+-- else
+-- current = getnext(current)
+-- end
+-- end
+-- return head
+-- end
+
+-- this also works ok in math as we run over glyphs and these stay glyphs ... not sure
+-- about scripts and such but that is not important anyway ... some day we can consider
+-- special definitions in math
+
+function characters.handler(head)
+ local wipe = false
+ for current, char in nextchar, head do
+ local method = methods[char]
+ if method then
+ if wipe then
+ head = remove_node(head,wipe,true)
+ wipe = false
+ end
+ if trace_characters then
+ report_characters("replacing character %C, description %a",char,lower(chardata[char].description))
+ end
+ local h = method(head,current)
+ if h then
+ wipe = current
+ end
+ end
+ end
+ if wipe then
+ head = remove_node(head,wipe,true)
+ end
+ return head
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-chr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-chr.mkxl
new file mode 100644
index 00000000000..82c8be0ec34
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-chr.mkxl
@@ -0,0 +1,78 @@
+%D \module
+%D [ file=spac-chr,
+%D version=2011.05.24, % code moved from core-spa.mkiv
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Characters,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Characters}
+
+\unprotect
+
+\registerctxluafile{spac-chr}{autosuffix,optimize}
+
+\definesystemattribute[characters][public]
+
+%D The nobreakspace is special as it needs to keep track of the space being
+%D flexible. Another option is to use space (glue) nodes and tag them as characters
+%D instead. No big deal but takes a moment of coding. So, as a prelude we
+%D tag the nobreakspace only. Interesting is that it also fits better in the
+%D tagging mechanism.
+%D
+%D But we need to do some checking as from then on we no longer can compare
+%D them in a string. So this needs thinking. Not that we can compare the
+%D nobreakspace now anyway.
+
+\pushoverloadmode
+
+%immutable\edef\nobreakspace {\begingroup\setalignstateattribute\normalUchar"00A0\endgroup} % 1 = left
+\immutable\edef\nobreakspace {\normalUchar"00A0} % space
+\immutable\edef\softhyphen {\normalUchar"00AD} % softhyohen
+\immutable\edef\enquad {\normalUchar"2000} % quad/2
+\immutable\edef\emquad {\normalUchar"2001} % quad
+\immutable\edef\twoperemspace {\normalUchar"2002} % quad/2
+% % "2003 % quad == \quad == \hskip\emwidth
+\immutable\edef\threeperemspace {\normalUchar"2004} % quad/3
+\immutable\edef\fourperemspace {\normalUchar"2005} % quad/4
+%immutable\edef\fiveperemspace {\normalUchar"001E} % quad/5 (bonus)
+\immutable\edef\sixperemspace {\normalUchar"2006} % quad/6
+\immutable\edef\figurespace {\normalUchar"2007} % width of zero
+\immutable\edef\punctuationspace {\normalUchar"2008} % width of period
+\immutable\edef\breakablethinspace {\normalUchar"2009} % quad/8
+\immutable\edef\hairspace {\normalUchar"200A} % quad/8
+\immutable\edef\zerowidthspace {\normalUchar"200B} % 0
+\immutable\edef\zerowidthnonjoiner {\normalUchar"200C} % 0
+\immutable\edef\zerowidthjoiner {\normalUchar"200D} % 0
+\immutable\edef\narrownobreakspace {\normalUchar"202F} % quad/8
+%immutable\edef\mediummathspace {\normalUchar"205F} % space/8 (math)
+%immutable\edef\zerowidthnobreakspace{\normalUchar"FEFF}
+%immutable\edef\fiveperemspace {\normalUchar"001E}
+
+\immutable\protected\def\zerowidthnobreakspace{\penalty\plustenthousand\kern\zeropoint}
+\immutable\protected\def\fiveperemspace {\hskip\dimexpr\emwidth/5\relax}
+
+\aliased\let\ideographicspace \enquad
+\aliased\let\ideographichalffillspace \emquad
+
+\immutable\let\zwnj \zerowidthnonjoiner
+\immutable\let\zwj \zerowidthjoiner
+\immutable\let\nbsp \nobreakspace
+
+\popoverloadmode
+
+\immutable\chardef\optionalspace"1F % will be space unless before punctuation
+
+% Shortcuts:
+
+% unexpanded as otherwise we need to intercept / cleanup a lot
+
+\protected\def~{\nobreakspace}
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-def.mkxl
new file mode 100644
index 00000000000..294843cce71
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-def.mkxl
@@ -0,0 +1,91 @@
+%D \module
+%D [ file=spac-def,
+%D version=2009.10.16, % 1997.03.31, was core-spa.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Definitions}
+
+%D Ideally these definitions should be moved to where they first appear and this
+%D will happen at some point.
+
+\unprotect
+
+% maybe \everysynchronizeglobalspacing
+% maybe \everysynchronizelocalspacing
+
+\appendtoks
+ \forgetverticalstretch
+ \presetnormallineheight
+ \setnormalbaselines % check if redundant (\forgetverticalstretch does it too)
+ \setstrut % check if redundant (\forgetverticalstretch does it too)
+ \settopskip % factors set in \forgetverticalstretch
+ \setmaxdepth % factors set in \forgetverticalstretch
+ \synchronizeindenting
+ \synchronizeskipamounts
+ \synchronizeblank
+ \synchronizewhitespace
+ \synchronizespacecodes % not needed, frozen factors
+ \setrelativeinterlinespace
+\to \everybodyfont
+
+% why exception
+
+\appendtoks
+ \updateraggedskips % under test
+\to \everyfontswitch
+
+% maybe more
+
+\prependtoks
+ \enforced\let\par\normalpar
+\to \everybeforepagebody % see \fillinline (was endgraf)
+
+% needs checking:
+
+\appendtoks
+ \synchronizespacecodes
+\to \everydefinedfont % not needed, frozen factors
+
+\setupinterlinespace
+ [\c!minheight=\zeropoint, % only special purpose
+ \c!mindepth=\zeropoint, % only special purpose
+ \c!height=.72,
+ \c!depth=.28,
+ \c!top=1.0,
+ \c!bottom=0.4,
+ \c!distance=\onepoint, % \dimexpr\openlineheight/10\relax
+ \c!line=2.8\exheight,
+ \c!stretch=\zerocount,
+ \c!shrink=\zerocount]
+
+\setupblank
+ [\v!big]
+
+\setuptolerance
+ [\v!horizontal,\v!verystrict]
+
+\setuptolerance
+ [\v!vertical,\v!strict]
+
+\setupalign
+ [\v!bottom,
+ \v!width]
+
+\setupspacing
+ [\v!packed]
+
+\indenting
+ [\v!never]
+
+\setupindenting
+ [\v!none]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-flr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-flr.mkxl
new file mode 100644
index 00000000000..3ea90bc2c15
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-flr.mkxl
@@ -0,0 +1,196 @@
+%D \module
+%D [ file=spac-fil,
+%D version=2013.01.13,
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Fillers,
+%D author={Hans Hagen and Wolfgang Schuster},
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Fillers}
+
+\unprotect
+
+%D A bit more extensive variant of a prototype posted by Wolfgang to the development
+%D list. Instead of dealing with a few leader types it makes sense to support them
+%D all as well as simple rule fillers. Eventually we can also use in for the
+%D mathfillers. We distinguish between alternatives and with them methods, and a
+%D checker is provided for use in applying e.g.\ fillers in lists.
+
+\installcorenamespace{filler}
+\installcorenamespace{filleralternative}
+\installcorenamespace{fillerleadermethod}
+
+\installcommandhandler \??filler {filler} \??filler
+
+\aliased\let\setupfillers\setupfiller
+
+\permanent\protected\def\checkedfiller#1%
+ {\ifcsname\namedfillerhash{#1}\s!parent\endcsname
+ \filler[#1]%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi{#1}}
+
+\permanent\tolerant\protected\def\filler[#1]%
+ {\removeunwantedspaces
+ \begingroup
+ \edef\currentfiller{#1}%
+ \usefillerstyleandcolor\c!style\c!color
+ \scratchdimen\fillerparameter\c!leftmargin\relax
+ \ifzeropt\scratchdimen\else
+ \hskip\scratchdimen
+ \fi
+ \fillerparameter\c!left\relax
+ \expandnamespaceparameter\??filleralternative\fillerparameter\c!alternative\s!unknown\relax
+ \fillerparameter\c!right\relax
+ \scratchdimen\fillerparameter\c!rightmargin\relax
+ \ifzeropt\scratchdimen\else
+ \hskip\scratchdimen
+ \fi
+ \endgroup
+ \ignorespaces}
+
+% \definefiller
+% [MyFiller]
+% [offset=.25\emwidth,
+% rightmargindistance=-\rightskip,
+% method=middle]
+%
+% \startitemize[packed,joinedup][rightmargin=5em]
+% \startitem
+% \input sapolsky \fillupto[MyFiller]{RS}
+% \stopitem
+% \stopitemize
+
+\permanent\tolerant\protected\def\fillupto[#1]#:#2%
+ {\removeunwantedspaces
+ \begingroup
+ \edef\currentfiller{#1}%
+ \scratchdimen\dimexpr\fillerparameter\c!rightmargindistance\relax
+ \ifzeropt\scratchdimen\else
+ \parfillskip \scratchdimen\s!plus \plusone\s!fil\relax
+ \fi
+ \filler[#1]%
+ \doifsomething{#2}{\hbox{#2}}%
+ \par
+ \endgroup
+ \ignorespaces}
+
+\setvalue{\??filleralternative\s!unknown}%
+ {}
+
+\setvalue{\??filleralternative\v!symbol}%
+ {\expandnamespaceparameter\??fillerleadermethod\fillerparameter\c!method\v!local
+ \ifdim\fillerparameter\c!offset>\zeropoint
+ \simplealignedspreadbox
+ {2\dimexpr\fillerparameter\c!offset\relax}%
+ {\fillerparameter\c!align}%
+ {\fillerparameter\c!symbol}%
+ \else
+ \simplealignedbox
+ {\fillerparameter\c!width}%
+ {\fillerparameter\c!align}%
+ {\fillerparameter\c!symbol}%
+ \fi
+ \hfill}
+
+\setvalue{\??filleralternative\v!stretch}%
+ {\hfill}
+
+\setvalue{\??filleralternative\v!space}%
+ {\hskip\fillerparameter\c!distance\relax}
+
+\setvalue{\??filleralternative\v!rule}%
+ {\expandnamespaceparameter\??fillerleadermethod\fillerparameter\c!method\v!local
+ \hrule
+ \s!height\fillerparameter\c!height
+ \s!depth \fillerparameter\c!depth
+ \hfill}
+
+\letvalue{\??fillerleadermethod\s!local }\normalleaders % overflow ends up inbetween (current box)
+\letvalue{\??fillerleadermethod\v!global}\normalgleaders % overflow ends up inbetween (outermost box)
+\letvalue{\??fillerleadermethod\v!middle}\normalcleaders % overflow ends up before, after (current box)
+\letvalue{\??fillerleadermethod\v!broad }\normalxleaders % overflow ends up before, inbetween, after (current box)
+
+\setupfillers
+ [\c!width=\emwidth,
+ \c!symbol=.,
+ \c!distance=\emwidth,
+ \c!offset=\zeropoint,
+ \c!align=\v!middle,
+ \c!height=.1\exheight,
+ \c!depth=\zeropoint,
+ \c!leftmargin=\zeropoint,
+ \c!rightmargin=\zeropoint,
+ \c!rightmargindistance=\zeropoint,
+ \c!alternative=\v!symbol,
+ \c!method=\s!local]
+
+\definefiller
+ [\v!sym]
+ [\c!method=\v!global,
+ \c!width=.5\emwidth,
+ \c!leftmargin=.5\emwidth,
+ \c!rightmargin=.5\emwidth]
+
+\definefiller
+ [\v!symbol]
+ [\c!method=\v!global,
+ \c!offset=.125\emwidth,
+ \c!leftmargin=.5\emwidth,
+ \c!rightmargin=.5\emwidth]
+
+\definefiller
+ [\v!rule]
+ [\c!alternative=\v!rule,
+ \c!leftmargin=.5\emwidth,
+ \c!rightmargin=.5\emwidth]
+
+\definefiller
+ [\v!width]
+ [\c!alternative=\v!stretch]
+
+\definefiller
+ [\v!space]
+ [\c!alternative=\v!space,
+ \c!distance=3\emwidth]
+
+% bonus:
+
+\definefiller
+ [dotfill]
+ [\c!symbol=\textperiod,
+ \c!style=\v!normal,
+ \c!method=\v!middle,
+ \c!width=.5\emwidth,
+ \c!leftmargin=.2\emwidth,
+ \c!rightmargin=.2\emwidth]
+
+% If users really want it:
+%
+% \permanent\protected\def\mathdotfill{\cleaders\hbox{$\mathsurround\zeropoint\mkern1.5mu.\mkern1.5mu$}\hfill} % plain
+% \permanent\protected\def\textdotfill{\filler[dotfill]}
+% \permanent\protected\def\dotfill {\mathortext\mathdotfill\textdotfill}
+
+\permanent\protected\def\dotfill{\filler[dotfill]}
+
+% maybe box -> symbol
+
+\protect \endinput
+
+% \definefiller[test-a][alternative=stretch]
+% \definefiller[test-b][alternative=symbol,symbol=!]
+% \definefiller[test-c][alternative=rule,height=.1ex,leftmargin=.5em,rightmargin=.25em]
+
+% \starttext
+% text\filler[test-a]text \par
+% text\filler[test-b]text \par
+% text\filler[test-c]text \par
+% text\checkedfiller{<nothing>}text \par
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-grd.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-grd.mkxl
new file mode 100644
index 00000000000..65739cabea7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-grd.mkxl
@@ -0,0 +1,377 @@
+%D \module
+%D [ file=spac-grd,
+%D version=2009.10.16, % 1998.03.10, was core-grd.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Grid Snapping,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module will be merged into spac-ver.mkiv.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Grid Snapping}
+
+\unprotect
+
+%D A rather crappy macro that we need to avoid and as such it will probably
+%D disappear:
+
+\installcorenamespace{lastnodepusher}
+
+\permanent\let\poplastnode\relax
+
+\permanent\protected\def\pushlastnode
+ {\csname\??lastnodepusher
+ \ifcsname\??lastnodepusher\the\lastnodetype\endcsname
+ \the\lastnodetype
+ \else
+ \s!unknown
+ \fi
+ \endcsname}
+
+\defcsname\??lastnodepusher\number\kernnodecode\endcsname
+ {\enforced\permanent\protected\edef\poplastnode{\kern\the\lastkern\relax}%
+ \kern-\lastkern}
+
+\defcsname\??lastnodepusher\number\gluenodecode\endcsname
+ {\enforced\permanent\protected\edef\poplastnode{\vskip\the\lastskip\relax}%
+ \vskip-\lastskip}
+
+\defcsname\??lastnodepusher\number\penaltynodecode\endcsname
+ {\enforced\permanent\protected\edef\poplastnode{\penalty\the\lastpenalty\relax}%
+ \nobreak}
+
+\defcsname\??lastnodepusher\s!unknown\endcsname
+ {\enforced\permanent\let\poplastnode\relax}
+
+%D Moved from supp-box:
+
+%D \macros
+%D {startbaselinecorrection,baselinecorrection,
+%D showbaselinecorrection,offbaselinecorrection}
+%D
+%D Spacing around ruled boxes can get pretty messed up. The
+%D next macro tries as good as possible to fix this.
+%D
+%D \startbuffer[1]
+%D \startbaselinecorrection
+%D \ruledhbox{Rule Brittanica}
+%D \stopbaselinecorrection
+%D \stopbuffer
+%D
+%D \typebuffer[1]
+%D
+%D The macros put some white space around the box:
+%D
+%D \getbuffer[1]
+%D
+%D A simple alternative is \type {\baselinecorrection}, which
+%D only looks at the previous line.
+%D
+%D \startbuffer[2]
+%D \baselinecorrection
+%D \ruledhbox{Rule Brittanica}
+%D \baselinecorrection
+%D \stopbuffer
+%D
+%D \typebuffer[2]
+%D
+%D This time the last preceding line gets a correction,%
+%D dependant on the depth.
+%D
+%D \getbuffer[2]
+%D
+%D One can make the correction visible by saying \type
+%D {\showbaselinecorrection}. Part of the correction is
+%D calculated from the dimensions of a~(. One can disble the
+%D correction by calling \type {\offbaselinecorrection}.
+%D
+%D When visualize the first example looks like:
+%D
+%D {\showbaselinecorrection\getbuffer[1]}
+%D
+%D and the second one comes out as:
+%D
+%D {\showbaselinecorrection\getbuffer[2]}
+
+% \definecolor[GridLineColor][red]
+% \definecolor[GridTextColor][blue]
+
+\newdimen\d_spac_lines_correction_before
+\newdimen\d_spac_lines_correction_after
+
+\newbox \b_spac_lines_correction_before
+\newbox \b_spac_lines_correction_after
+
+\def\spac_lines_initialize_corrections
+ {\setbox\b_spac_lines_correction_before\hpack{\setstrut\strut}%
+ \setbox\b_spac_lines_correction_after \hbox {(}%
+ \d_spac_lines_correction_before\dimexpr\ht\b_spac_lines_correction_before-\ht\b_spac_lines_correction_after\relax
+ \d_spac_lines_correction_after \dimexpr\dp\b_spac_lines_correction_before-\dp\b_spac_lines_correction_after\relax
+ \ifdim\d_spac_lines_correction_before<\zeropoint\d_spac_lines_correction_before\zeropoint\fi
+ \ifdim\d_spac_lines_correction_after <\zeropoint\d_spac_lines_correction_after \zeropoint\fi}
+
+% experiment, todo: proper mkiv mechanism
+%
+% \input ward \par
+% \startframedtext test \stopframedtext
+% \input ward \par
+% \startlinecorrection \framed{xxx} \stoplinecorrection
+% \input ward \par
+%
+% \setupwhitespace[big]
+%
+% \input ward \par
+% \startframedtext test \stopframedtext
+% \input ward \par
+% \startlinecorrection \framed{xxx} \stoplinecorrection
+% \input ward \par
+
+% to be redone:
+
+\permanent\protected\def\dotopbaselinecorrection{\expandafter\blank\expandafter[\the\d_spac_lines_correction_before]}
+\permanent\protected\def\dobotbaselinecorrection{\expandafter\blank\expandafter[\the\d_spac_lines_correction_after ]}
+
+\permanent\def\showbaselinecorrection
+ {\enforced\permanent\protected\def\dobaselinecorrection % visualization is not watertight!
+ {\bgroup
+ \ifdim\prevdepth>\zeropoint
+ \kern-\prevdepth
+ \fi
+ \setbox\scratchbox\emptyhbox
+ \wd\scratchbox\hsize
+ \dp\scratchbox\strutdp
+ \nointerlineskip
+ \forgetall
+ \ruledvpack{\box\scratchbox}%
+ \egroup
+ \prevdepth\strutdp}%
+ \enforced\permanent\protected\def\dotopbaselinecorrection{\hrule\s!height\d_spac_lines_correction_before}%
+ \enforced\permanent\protected\def\dobotbaselinecorrection{\hrule\s!height\d_spac_lines_correction_after }}
+
+% \def\dobaselinecorrection % beware, this one is redefined below
+% {\ifdim\prevdepth>\zeropoint\kern-\prevdepth\fi
+% \kern\strutdp
+% \prevdepth\strutdp}
+
+\permanent\protected\def\baselinecorrection
+ {\endgraf
+ \ifvmode
+ \ifdim\prevdepth<\maxdimen
+ \ifdim\prevdepth<\zeropoint \else
+ \ifdim\prevdepth<\strutdepth \relax
+ \pushlastnode
+ \dobaselinecorrection
+ \poplastnode
+ \fi
+ \fi
+ \fi
+ \fi}
+
+\permanent\protected\def\pagebaselinecorrection
+ {\ifdim\pagegoal<\maxdimen
+ \ifdim\pagetotal>\lineheight % or \topskip
+ \scratchdimen\pagetotal
+ \advance\scratchdimen\lineheight
+ \ifdim\scratchdimen<\pagegoal
+ \baselinecorrection
+ \fi
+ \fi
+ \fi}
+
+\permanent\protected\def\startbaselinecorrection
+ {\bgroup
+ \enforced\let\stopbaselinecorrection\egroup
+ \ifcase\baselinecorrectionmode
+ \or % normal
+ \baselinecorrection
+ \ifvmode
+ \setbox\scratchbox\vbox\bgroup\ignorespaces
+ \enforced\let\stopbaselinecorrection\donormalstopbaselinecorrection
+ \fi
+ \or % off
+ \or % force
+ \baselinecorrection
+ \ifvmode
+ \setbox\scratchbox\vbox\bgroup\ignorespaces
+ \enforced\let\stopbaselinecorrection\doforcedstopbaselinecorrection
+ \fi
+ \fi}
+
+\permanent\let\stopbaselinecorrection\relax
+
+\permanent\protected\def\donormalstopbaselinecorrection % I have to check columns yet.
+ {\egroup
+ \topbaselinecorrection
+ \box\scratchbox
+ \botbaselinecorrection
+ \egroup}
+
+\permanent\protected\def\doforcedstopbaselinecorrection % I have to check columns yet.
+ {\egroup
+ \forcedtopbaselinecorrection
+ \box\scratchbox
+ \forcedbotbaselinecorrection
+ \egroup}
+
+%D We do a bit more checking than needed. The pageborder check
+%D is not needed, but I want to look the visualization as good
+%D as possible too.
+
+\setnewconstant\baselinecorrectionmode\plusone
+
+\permanent\protected\def\onbaselinecorrection {\baselinecorrectionmode\plusone }
+\permanent\protected\def\offbaselinecorrection {\baselinecorrectionmode\plustwo }
+\permanent\protected\def\forcebaselinecorrection{\baselinecorrectionmode\plusthree}
+
+%D \macros
+%D {topbaselinecorrection,botbaselinecorrection}
+%D
+%D The actual top and bottom corrections are implemented as:
+
+\permanent\protected\def\topbaselinecorrection
+ {\ifvmode \ifdim\pagegoal<\maxdimen
+ \forcedtopbaselinecorrection
+ \fi \fi}
+
+% \permanent\protected\def\forcedtopbaselinecorrection
+% {\ifvmode
+% \bgroup
+% \spac_lines_initialize_corrections
+% \whitespace % no longer ok
+% \nointerlineskip
+% \dotopbaselinecorrection
+% \egroup
+% \fi}
+
+\permanent\protected\def\botbaselinecorrection
+ {\ifvmode
+ \bgroup
+ \spac_lines_initialize_corrections
+ \dobotbaselinecorrection
+ \allowbreak % new, otherwise problems when many in a row
+ \prevdepth\strutdp
+ \egroup
+ \fi}
+
+% nointerlineskip
+%
+% startpacked
+% \startlinecorrection \framed{test} \stoplinecorrection
+% \startlinecorrection \framed{test} \stoplinecorrection
+% \stoppacked
+
+\permanent\protected\def\forcedtopbaselinecorrection
+ {\ifvmode
+ \bgroup
+ \spac_lines_initialize_corrections
+ \vspacing[\v!white]
+ %\nointerlineskip %
+ \dotopbaselinecorrection
+ \egroup
+ \fi}
+
+\aliased\let\forcedbotbaselinecorrection\botbaselinecorrection
+
+\permanent\protected\def\dobaselinecorrection
+ {\ifdim\prevdepth>\zeropoint
+ \vspacing[\the\dimexpr-\prevdepth+\strutdp\relax]%
+ \else
+ \vspacing[\the\dimexpr\strutdp\relax]%
+ \fi
+ \prevdepth\strutdp}
+
+% \permanent\protected\def\dobaselinecorrection
+% {\scratchdimen\dimexpr\ifdim\prevdepth>\zeropoint-\prevdepth+\fi\strutdp\relax
+% \vspacing[\the\scratchdimen]%
+% \prevdepth\strutdp}
+%
+% some day:
+%
+% \permanent\protected\def\dobaselinecorrection
+% {\clf_checkstrutdepth\strutdp}
+%
+% \permanent\protected\def\baselinecorrection
+% {\endgraf
+% \ifvmode
+% \ifdim\prevdepth<\maxdimen
+% \ifdim\prevdepth<\zeropoint \else
+% \ifdim\prevdepth<\strutdepth \relax
+% % works in mvl:
+% % \forcestrutdepth
+% % doesn't work immediately (unless enforced with penalty):
+% % \pushlastnode
+% % \dobaselinecorrection
+% % \poplastnode
+% % works ok:
+% \clf_checkstrutdepth\strutdp
+% \fi
+% \fi
+% \fi
+% \fi}
+
+%D For the moment only with placefloats (should work in mvl and boxes):
+
+\permanent\protected\def\checkprevdepth
+ {\endgraf
+ \ifvmode
+ \ifdim\prevdepth<\maxdimen
+ \ifdim\prevdepth<\zeropoint \else
+ \ifdim\prevdepth<\strutdepth \relax
+ \clf_checkstrutdepth\strutdp
+ \fi
+ \fi
+ \fi
+ \fi}
+
+\aliased\let\normalstartbaselinecorrection\startbaselinecorrection
+
+\pushoverloadmode
+
+\permanent\protected\def\startbaselinecorrection
+ {\ifgridsnapping
+ \snaptogrid[\v!normal]\vbox\bgroup
+ \enforced\let\stopbaselinecorrection\egroup
+ \else
+ \normalstartbaselinecorrection
+ \fi}
+
+\popoverloadmode
+
+% This is new (and experimental) and might replace some of the above. beware it doesn't always work
+% out well, e.g. when used grouped and such (e.g. before display math doesn't work out well).
+
+% \permanent\protected\def\fakenextstrutline
+% {\par
+% \begingroup
+% \reseteverypar
+% \dontleavehmode\hpack to \zeropoint{\page_sides_anchor\hss\strut}% just a tracer
+% \vskip-\parskip
+% \vskip-\struttotal
+% \endgroup}
+
+% \permanent\protected\def\fakenextstrutline
+% {\par
+% \begingroup
+% \reseteverypar
+% \dontleavehmode\hpack{\strut}\par
+% \clf_fakenextstrutline
+% \ifdim\pagetotal>\lineheight
+% \pagetotal\dimexpr\pagetotal-\lineheight\relax
+% \fi
+% \endgroup}
+
+% \permanent\protected\def\fakenextstrutline
+% {\par
+% \begingroup
+% \reseteverypar
+% \forgetall
+% \dontleavehmode\hpack{\strut}\par
+% \clf_removelastline
+% \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-hor.lmt b/Master/texmf-dist/tex/context/base/mkxl/spac-hor.lmt
new file mode 100644
index 00000000000..7db8d4d6f3f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-hor.lmt
@@ -0,0 +1,38 @@
+if not modules then modules = { } end modules ['spac-hor'] = {
+ version = 1.001,
+ comment = "companion to spac-hor.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local chardata = characters.data
+local peekchar = tokens.scanners.peekchar
+local ctx_space = context.space
+
+local can_have_space = table.tohash {
+ "lu", "ll", "lt", "lm", "lo", -- letters
+ -- "mn", "mc", "me", -- marks
+ "nd", "nl", "no", -- numbers
+ "ps", "pi", -- initial
+ -- "pe", "pf", -- final
+ -- "pc", "pd", "po", -- punctuation
+ "sm", "sc", "sk", "so", -- symbols
+ -- "zs", "zl", "zp", -- separators
+ -- "cc", "cf", "cs", "co", "cn", -- others
+}
+
+interfaces.implement {
+ name = "autoinsertnextspace",
+ protected = true,
+ public = true,
+ actions = function()
+ local char = peekchar()
+ if char then
+ local d = chardata[char]
+ if d and can_have_space[d.category] then
+ ctx_space()
+ end
+ end
+ end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-hor.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-hor.mkxl
new file mode 100644
index 00000000000..f64207a2cb5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-hor.mkxl
@@ -0,0 +1,1218 @@
+%D \module
+%D [ file=spac-hor,
+%D version=2009.10.16, % 1997.03.31, was core-spa.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Horizontal,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Horizontal}
+
+\unprotect
+
+\registerctxluafile{spac-hor}{autosuffix}
+
+\aliased\let\parfillrightskip\parfillskip
+
+\bitwiseflip \normalizelinemode \parindentskipcode
+\bitwiseflip \normalizelinemode \normalizelinecode
+\bitwiseflip \normalizelinemode \clipwidthcode
+
+\let\v_spac_indentation_current\empty % amount/keyword
+
+\newdimen \d_spac_indentation_par
+\parindent\d_spac_indentation_par % for the show
+
+\newconditional\c_spac_indentation_indent_first \settrue\c_spac_indentation_indent_first
+\newconstant \c_spac_indentation_toggle_state
+
+%D After a blank or comparable situation (left side floats) we
+%D need to check if the next paragraph has to be indented.
+
+\permanent\protected\def\presetindentation
+ {\doifoutervmode{\ifconditional\c_spac_indentation_indent_first\else\spac_indentation_variant_no\fi}}
+
+\permanent\protected\def\setupindenting
+ {\doifelsenextoptionalcs\spac_indentation_setup_options\spac_indentation_setup_size}
+
+\permanent\protected\def\spac_indentation_setup_size
+ {\assigndimension\v_spac_indentation_current\d_spac_indentation_par{1\emwidth}{1.5\emwidth}{2\emwidth}%
+ \ifzeropt\parindent\else
+ \parindent\d_spac_indentation_par\relax % new per 2019-04-12 : just in case it has been set beforehand
+ \fi}
+
+\aliased\let\synchronizeindenting\spac_indentation_setup_size
+
+\let\m_spac_indentation_options\empty
+
+\def\spac_indentation_setup_options[#1]%
+ {\edef\m_spac_indentation_options{#1}% comma separated list
+ \ifempty\m_spac_indentation_options \else
+ \spac_indentation_setup_indeed
+ \fi}
+
+\def\spac_indentation_setup_indeed
+ {% not here: \settrue\c_spac_indentation_indent_first
+ % not here: \parindent\d_spac_indentation_par
+ % not here: \c_spac_indentation_toggle_state\zerocount
+ \processcommacommand[\m_spac_indentation_options]\spac_indentation_apply_step_one % catch small, medium, etc
+ \processcommacommand[\m_spac_indentation_options]\spac_indentation_apply_step_two % catch rest
+ \ifzeropt\parindent\else
+ \doifemptytoks\everypar\spac_indentation_set_everypar
+ \fi
+ \ifconditional\c_spac_indentation_indent_first
+ \spac_indentation_variant_yes % better than: \let\checkindentation\relax
+ \else
+ \spac_indentation_variant_no
+ \fi
+ \spac_indentation_check_toggle}
+
+\def\spac_indentation_set_everypar
+ {\everypar{\checkindentation}}
+
+\permanent\protected\def\useindentingparameter#1% faster local variant
+ {\edef\m_spac_indentation_options{#1\c!indenting}%
+ \ifempty\m_spac_indentation_options \else
+ \spac_indentation_setup_indeed
+ \fi}
+
+% \defineindenting[whatever][yes,2cm]
+% %defineindenting[whatever][yes,-2cm]
+%
+% \setupindenting[yes,-2em] \input ward \par
+% \setupindenting[yes,2em] \input ward \par
+% \setupindenting[whatever] \input ward \par
+
+\installcorenamespace {indentingpreset}
+
+\permanent\tolerant\protected\def\defineindenting[#1]#*[#2]% todo: mixes
+ {\ifarguments\else\setevalue{\??indentingpreset#1}{#2}\fi}
+
+\def\spac_indentation_apply_step_one_nested
+ {\expandafter\processcommacommand\expandafter[\lastnamedcs]\spac_indentation_apply_step_one}
+
+\def\spac_indentation_apply_step_two_nested
+ {\expandafter\processcommacommand\expandafter[\lastnamedcs]\spac_indentation_apply_step_two}
+
+\def\spac_indentation_apply_step_one#1%
+ {\ifcsname\??indentingpreset#1\endcsname
+ \spac_indentation_apply_step_one_nested
+ \orelse\ifcsname\??indentingmethod#1\endcsname
+ % case two
+ \else
+ \edef\v_spac_indentation_current{#1}% single entry in list
+ \let\v_spac_indentation_normal\v_spac_indentation_current
+ \spac_indentation_setup_size
+ \fi}
+
+\def\spac_indentation_apply_step_two#1%
+ {\ifcsname\??indentingpreset#1\endcsname
+ \spac_indentation_apply_step_two_nested
+ \orelse\ifcsname\??indentingmethod#1\endcsname
+ \lastnamedcs
+ \else
+ % case one
+ \fi}
+
+\permanent\protected\def\indenting % kind of obsolete
+ {\doifelsenextoptionalcs\spac_indentation_setup_options\relax}
+
+% use \noindentation to suppress next indentation
+
+\installcorenamespace{indentingmethod}
+
+\permanent\protected\def\installindentingmethod#1#2%
+ {\defcsname\??indentingmethod#1\endcsname{#2}}
+
+\installindentingmethod \v!no {\parindent\zeropoint}
+\installindentingmethod \v!not {\parindent\zeropoint}
+
+\installindentingmethod \v!first {\settrue\c_spac_indentation_indent_first}
+\installindentingmethod \v!next {\setfalse\c_spac_indentation_indent_first}
+
+\installindentingmethod \v!yes {\parindent\d_spac_indentation_par\relax} % not \indent !
+\installindentingmethod \v!always{\parindent\d_spac_indentation_par\relax} % not \indent !
+
+\installindentingmethod \v!never {\parindent\zeropoint\relax % no \indent !
+ \c_spac_indentation_toggle_state\zerocount}
+
+\installindentingmethod \v!odd {\c_spac_indentation_toggle_state\plusone}
+\installindentingmethod \v!even {\c_spac_indentation_toggle_state\plustwo}
+
+\installindentingmethod \v!normal{\ifempty\v_spac_indentation_normal\else
+ \let\v_spac_indentation_current\v_spac_indentation_normal
+ \spac_indentation_setup_size
+ \fi}
+
+\installindentingmethod \v!reset {\settrue\c_spac_indentation_indent_first
+ \parindent\zeropoint
+ \c_spac_indentation_toggle_state\zerocount}
+
+\installindentingmethod \v!toggle{\parindent\ifzeropt\parindent
+ \d_spac_indentation_par
+ \else
+ \zeropoint
+ \fi\relax}
+
+\permanent\protected\def\noindenting{\indenting[\v!no, \v!next ]}
+\permanent\protected\def\doindenting{\indenting[\v!yes,\v!first]}
+
+%D Here come the handlers (still rather messy ... we need states).
+
+%newif\ifindentation \indentationtrue % will become a mode
+
+\aliased\let\checkindentation\relax
+
+\installmacrostack\checkindentation
+
+%D Actually we can forget about indent and let everypar insert a kern or so,
+%D but on the other hand it's kind of consistent to keep it and make the
+%D width zero. We can do it in \LUA\ or do it natively (which actually makes
+%D sense.
+
+% \permanent\protected\def\undent
+% {\begingroup
+% % todo: check subtype too
+% \ifnum\lastnodetype=\gluenodecode
+% \unskip % we also need to check for the subtype
+% \orelse\ifnum\lastnodetype=\hlistnodecode
+% \setbox\scratchbox\lastbox % we also need to check for the subtype
+% \else
+% % forget about it
+% \fi
+% \endgroup}
+
+\def\spac_indentation_remove
+ {\ifzeropt\parindent \else
+ \undent
+ \fi}
+
+\def\spac_indentation_kill_indeed
+ {%\global\indentationfalse
+ \spac_indentation_remove}
+
+\permanent\protected\def\spac_indentation_do_toggle_indeed
+ {%\global\indentationfalse
+ \enforced\glet\checkindentation\spac_indentation_no_toggle_indeed
+ \spac_indentation_remove}
+
+\permanent\protected\def\spac_indentation_no_toggle_indeed
+ {%\global\indentationtrue
+ \enforced\glet\checkindentation\spac_indentation_do_toggle_indeed}
+
+\permanent\protected\def\spac_indentation_do_indeed
+ {}%\global\indentationtrue}
+
+\permanent\protected\def\spac_indentation_do_toggle
+ {\enforced\glet\checkindentation\spac_indentation_do_toggle_indeed}
+
+\permanent\protected\def\spac_indentation_no_toggle
+ {\enforced\glet\checkindentation\spac_indentation_no_toggle_indeed}
+
+\permanent\protected\def\spac_indentation_check_toggle
+ {\ifcase\c_spac_indentation_toggle_state
+ % nothing
+ \or
+ \spac_indentation_no_toggle
+ \or
+ \spac_indentation_do_toggle
+ \fi}
+
+\permanent\protected\def\spac_indentation_variant_yes
+ {\enforced\glet\checkindentation\spac_indentation_do_indeed}
+
+\permanent\protected\def\spac_indentation_no_next_check
+ {\spac_indentation_remove
+ \enforced\glet\checkindentation\spac_indentation_do_indeed}
+
+\permanent\protected\def\spac_indentation_variant_no % made global
+ {\ifinpagebody \else
+ %\global\indentationfalse
+ \enforced\glet\checkindentation\spac_indentation_no_next_check
+ \fi}
+
+\permanent\protected\def\nonoindentation % bv bij floats
+ {\ifinpagebody \else
+ %\global\indentationtrue
+ \enforced\glet\checkindentation\spac_indentation_do_indeed
+ \fi}
+
+\permanent\protected\def\spac_indentation_variant_force
+ {\ifvmode \ifzeropt\parindent \else
+ % was : \hskip\parindent
+ % can be: \indent
+ % but we test:
+ \noindent\hskip\parindent % hm, we cannot undent this one
+ \fi \fi}
+
+% public:
+
+\aliased\let\indentation \spac_indentation_variant_force
+\aliased\let\noindentation\spac_indentation_variant_no % public
+\aliased\let\doindentation\spac_indentation_variant_yes % public
+
+\permanent\protected\def\dontrechecknextindentation % public (in macros)
+ {\global\enforced\let\dorechecknextindentation\relax}
+
+\aliased\let\dorechecknextindentation\relax % public (in macros)
+
+\permanent\protected\protected\def\spac_indentation_check_next_indentation
+ {\global\enforced\let\dorechecknextindentation\relax
+ \doifelsenextchar\par\donothing\spac_indentation_variant_no} % messy check as next is seldom \par
+
+\def\spac_indentation_variant_auto
+ {\global\enforced\let\dorechecknextindentation\spac_indentation_check_next_indentation}
+
+%D This one sets up the local indentation behaviour (i.e. either or not
+%D a next paragraph will be indented).
+
+\installcorenamespace{indentnext}
+
+\permanent\protected\def\checknextindentation[#1]%
+ {\begincsname\??indentnext#1\endcsname}
+
+\permanent\protected\def\useindentnextparameter#1% new, the more efficient variant
+ {\edef\p_indentnext{#1\c!indentnext}%
+ \ifempty\p_indentnext\else
+ \begincsname\??indentnext\p_indentnext\endcsname
+ \fi}
+
+\letcsname\??indentnext \endcsname\donothing
+\letcsname\??indentnext\v!yes \endcsname\spac_indentation_variant_yes
+\letcsname\??indentnext\v!no \endcsname\spac_indentation_variant_no
+\letcsname\??indentnext\v!auto\endcsname\spac_indentation_variant_auto
+
+%D An example of usage:
+%D
+%D \starttyping
+%D \setupindenting[small,yes]
+%D
+%D \setupitemize [indentnext=auto]
+%D \setuptyping [indentnext=auto]
+%D \setupformulas[indentnext=auto]
+%D
+%D \input tufte \startitemize \item itemize \stopitemize
+%D \input tufte \startitemize \item itemize \stopitemize
+%D \input tufte \startitemize \item itemize \stopitemize
+%D
+%D \page
+%D
+%D \input tufte
+%D \starttyping
+%D verbatim
+%D \stoptyping
+%D
+%D \input tufte
+%D \starttyping
+%D verbatim
+%D \stoptyping
+%D
+%D \input tufte
+%D \starttyping
+%D verbatim
+%D \stoptyping
+%D
+%D \page
+%D
+%D \input tufte \startformula a = b \stopformula
+%D \input tufte \startformula a = b \stopformula
+%D \input tufte \startformula a = b \stopformula
+%D \stoptyping
+
+% maybe an everyforgetparindent
+
+\permanent\protected\def\forgetparindent
+ {\settrue\c_spac_indentation_indent_first % recently added
+ \d_spac_indentation_par\zeropoint
+ \parindent\zeropoint
+ \let\v_spac_indentation_current\v!none}
+
+\appendtoks
+ \forgetparindent
+\to \everyforgetall
+
+\permanent\protected\def\forgethorizontalstretch
+ {\emergencystretch\zeropoint}
+
+\appendtoks
+ \forgethorizontalstretch
+\to \everyforgetall % needed in pagebody
+
+%D Helper:
+
+\permanent\protected\def\softbreak
+ {\relax\ifhmode\hskip\parfillskip\break\fi}
+
+%D \macros
+%D {frenchspacing,nonfrenchspacing}
+%D
+%D Somehow \type{\frenchspacing} can lead to hyphenation between dashes so we now
+%D have \type {\newfrenchspacing} (moved from \type {syst-chr}). Maybe it's not
+%D needed any more.
+
+%D Hm ... todo:
+
+\installcorenamespace{spacecodemethod}
+
+\sfcode`\)=\zerocount
+\sfcode`\'=\zerocount
+\sfcode`\]=\zerocount
+
+\def\spac_spacecodes_set_fixed#1%
+ {\sfcode`\.#1\relax \sfcode`\,#1\relax
+ \sfcode`\?#1\relax \sfcode`\!#1\relax
+ \sfcode`\:#1\relax \sfcode`\;#1\relax}
+
+\def\spac_spacecodes_set_stretch
+ {\sfcode`\.3000 \sfcode`\,1250
+ \sfcode`\?3000 \sfcode`\!3000
+ \sfcode`\:2000 \sfcode`\;1500 }
+
+\permanent\protected\def\frenchspacing {\spac_spacecodes_set_fixed\plusthousand}
+\permanent\protected\def\newfrenchspacing{\spac_spacecodes_set_fixed{1050}}
+\permanent\protected\def\nonfrenchspacing{\spac_spacecodes_set_stretch}
+
+\permanent\protected\def\installspacingmethod#1#2{\setvalue{\??spacecodemethod#1}{#2}}
+
+\installspacingmethod \empty {} % keep values
+\installspacingmethod \v!fixed {\frenchspacing } % equal spaces everywhere
+\installspacingmethod \v!packed {\newfrenchspacing} % slighly more after punctuation
+\installspacingmethod \v!broad {\nonfrenchspacing} % more depending on what punctuation
+
+\permanent\tolerant\protected\def\setupspacing[#1]%
+ {\ifarguments\or
+ \begincsname\??spacecodemethod#1\endcsname
+ \fi
+ \updateraggedskips}
+
+%D Here's a tweak .. if needed one can configure it in the configuration
+%D so that initialization happens more efficient.
+%D
+%D \starttyping
+%D \startoverlay
+%D {
+%D \green
+%D \enabledirectives[characters.spaceafteruppercase=normal]%
+%D \vbox{\hsize 5em x. X\par x.\ X\par X. X\par X.\ X\par}
+%D } {
+%D \blue
+%D \enabledirectives[characters.spaceafteruppercase=traditional]%
+%D \vbox{\hsize 5em x. X\par x.\ X\par X. X\par X.\ X\par}
+%D }
+%D \stopoverlay
+%D \stoptyping
+
+% This is not needed, as \updateraggedskips is taking care of it:
+
+\permanent\protected\def\synchronizespacecodes{\updateraggedskips}
+
+% \dorecurse{100}{\recurselevel\spacefactor 800 \space} \par
+% \dorecurse{100}{\recurselevel\spacefactor1200 \space} \par
+% \dorecurse{100}{\recurselevel\spacefactor 800 \normalspaceprimitive} \par
+% \dorecurse{100}{\recurselevel\spacefactor1200 \normalspaceprimitive} \par
+
+% When we don't add the % here, we effectively get \<endlinechar> and
+% since we have by default \def\^^M{\ } we get into a loop.
+
+\aliased\let\normalspaceprimitive=\ % space-comment is really needed
+
+%D As the \type{\ } is convenient in:
+%D
+%D \starttyping
+%D \TEX\space x\crlf
+%D \TEX\ x\crlf
+%D \TEX{} x\crlf
+%D \stoptyping
+%D
+%D from now on we treat it as a normal space and not as a space with \type
+%D {sfcode} 1000.
+
+% \permanent\protected\def\specialspaceprimitive
+% {\begingroup
+% % so, no fancy extra spacing after: foo i.e.\ bar
+% \nonfrenchspacing\normalspaceprimitive
+% \endgroup}
+
+\permanent\protected\def\specialspaceprimitive
+ {% is a current state, set explicitly or when a character is appended
+ \ifhmode
+ \spacefactor\plusthousand
+ \else
+ \dontleavehmode
+ \fi
+ \normalspaceprimitive}
+
+% \permanent\protected\def\normalnotobeyedspace
+% {\mathortext\normalspaceprimitive\specialspaceprimitive} % no \dontleavehmode\space (else no frenchspacing)
+
+\permanent\protected\def\normalnotobeyedspace
+ {\ifmmode\normalspaceprimitive\else\specialspaceprimitive\fi} % no \dontleavehmode\space (else no frenchspacing)
+
+\pushoverloadmode
+
+\overloaded\let\ =\normalnotobeyedspace % so we redefine the primitive!
+
+\popoverloadmode
+
+% Because I strip spaces at the end of lines (in the editor) we need a bit of
+% a trick to define slash+newline, so \space and \<newline> are the same
+
+% We need to be careful with \ and \space and the definition of ~ which uses \ as
+% we need to associate unicode spacing with it. There is some messy aspect that
+% I forgot to note down so I will revision the \ once I ran into it again.
+
+% \ruledhbox spread 10pt {\frenchspacing xx xx\ X}
+% \ruledhbox spread 10pt {\nonfrenchspacing xx xx\ X}
+% \ruledhbox spread 10pt {\frenchspacing xx xx X}
+% \ruledhbox spread 10pt {\nonfrenchspacing xx xx X}
+% \ruledhbox spread 10pt {\frenchspacing xx xx~X}
+% \ruledhbox spread 10pt {\nonfrenchspacing xx xx~X}
+
+% \ruledhbox spread 10pt {\frenchspacing xx dr.\ X}
+% \ruledhbox spread 10pt {\nonfrenchspacing xx dr.\ X}
+% \ruledhbox spread 10pt {\frenchspacing xx dr. X}
+% \ruledhbox spread 10pt {\nonfrenchspacing xx dr. X}
+% \ruledhbox spread 10pt {\frenchspacing xx dr.~X}
+% \ruledhbox spread 10pt {\nonfrenchspacing xx dr.~X}
+
+\permanent\protected\def\nonbreakablespace{\penalty\plustenthousand\normalspaceprimitive} % no space in math
+
+\letcatcodecommand \ctxcatcodes \tildeasciicode \nonbreakablespace % overloaded later
+
+% \def\space { }
+\permanent\protected\def\removelastspace{\ifhmode\unskip\fi}
+\permanent\protected\def\nospace {\removelastspace\ignorespaces}
+
+\ifdefined\nospaces
+ \permanent\protected\def\nospacing{\normalnospaces\plusone}
+ \permanent\protected\def\dospacing{\normalnospaces\zerocount}
+\else
+ \permanent\protected\def\nospacing{\spaceskip\scaledpoint \xspaceskip\zeropoint}
+ \permanent\protected\def\dospacing{\spaceskip\currentspaceskip\xspaceskip\zeropoint} % what
+\fi
+
+\ifdefined\softhyphen \else
+ \let\softhyphen\explicitdiscretionary
+\fi
+
+% todo:
+
+\cldcontext{"\string\\protected\string\\def\string\\\string\n{\string\\space}"}
+%cldcontext{"\string\\let\string\\\string\n=\string\\space"}
+
+% in tables we need:
+%
+% \def\fixedspace {\hskip.5em\relax}
+%
+% but, since not all fonts have .5em digits:
+
+\permanent\protected\def\fixedspace
+ {\setbox\scratchbox\hpack{\mathortext{0}{0}}% was \hbox
+ \hskip\wd\scratchbox\relax}
+
+\permanent\protected\def\fixedspaces
+ {\letcatcodecommand \ctxcatcodes \tildeasciicode\fixedspace
+ \enforced\let~\fixedspace} % we need to renew it
+
+\appendtoks
+ \enforced\let~\space
+ \enforced\let\ \space
+\to \everysimplifycommands
+
+\newsignal\d_spac_keep_unwanted_space_signal
+
+% \parindentmode\plusone
+
+\permanent\protected\def\keepunwantedspaces
+ {\ifhmode
+ \ifdim\lastskip=\d_spac_keep_unwanted_space_signal\else
+ \hskip\d_spac_keep_unwanted_space_signal\relax
+ \fi
+ \fi}
+
+\permanent\protected\def\removeunwantedspaces
+ {\ifhmode
+ \expandafter \spac_helpers_remove_unwantedspace
+ \fi}
+
+\def\spac_helpers_remove_unwantedspace
+ {\ifnum\lastnodetype=\gluenodecode\relax
+ \ifnum\lastnodesubtype=\indentskipsubtypecode\relax
+ % keep parindent
+ \orelse\ifdim\lastskip=\d_spac_keep_unwanted_space_signal\relax
+ \unskip
+ \else
+ \unskip
+ \doubleexpandafter\spac_helpers_remove_unwantedspace
+ \fi
+ \fi}
+
+\permanent\protected\def\onlynonbreakablespace
+ {\ifdim\lastskip=\interwordspace
+ \unskip
+ \nonbreakablespace
+ \fi
+ \ignorespaces}
+
+% \startbuffer
+% \startlines \tt \fixedspaces
+% 0~1~~2~~~3~~~~4~~~~~5
+% 0~~~~~~~~~~~~~~~~~~~5
+% $0~1~~2~~~3~~~~4~~~~~5$
+% $0~~~~~~~~~~~~~~~~~~~5$
+% \stoplines
+%
+% \starttabulate[|~|]
+% \NC 0~1~~2~~~3~~~~4~~~~~5 \NC \NR \NC 0~~~~~~~~~~~~~~~~~~~5 \NC \NR
+% \NC $0~1~~2~~~3~~~~4~~~~~5$ \NC \NR \NC $0~~~~~~~~~~~~~~~~~~~5$ \NC \NR
+% \stoptabulate
+%
+% \starttable[||]
+% \NC 0~1~~2~~~3~~~~4~~~~~5 \NC \AR \NC 0~~~~~~~~~~~~~~~~~~~5 \NC \AR
+% \NC $0~1~~2~~~3~~~~4~~~~~5$ \NC \AR \NC $0~~~~~~~~~~~~~~~~~~~5$ \NC \AR
+% \stoptable
+% \stopbuffer
+%
+% \setupbodyfont[cmr] \getbuffer
+% \setupbodyfont[lbr] \getbuffer
+
+%D A couple of plain macros:
+
+\ifdefined\enspace \else \permanent\protected\def\enspace{\kern .5\emwidth} \fi
+\ifdefined\quad \else \permanent\protected\def\quad {\hskip \emwidth\relax} \fi
+\ifdefined\enskip \else \permanent\protected\def\enskip {\hskip.5\emwidth\relax} \fi
+\ifdefined\qquad \else \permanent\protected\def\qquad {\hskip 2\emwidth\relax} \fi
+
+\permanent\protected\def\negenspace{\kern-.5\emwidth}
+\permanent\protected\def\negemspace{\kern- \emwidth}
+
+\aliased\let\emspace\quad
+
+\permanent\protected\def\charspace{ } % the unexpandable \space (as space can also be delimiter for numbers)
+
+\permanent\tolerant\protected\def\quads[#1]% we do robust checking
+ {\dontleavehmode
+ \begingroup
+ %\scratchcounter\ifparameter#1\or#1\else\plusthree\fi
+ \scratchcounter\ifchknum#1\or#1\else\plusthree\fi
+ \zwj\dorecurse\scratchcounter{\hskip\emwidth\zwj}%
+ \endgroup}
+
+% Suggested by GB (not the name) so we keep it for sentimental reasons:
+
+\permanent\protected\def\rapfillskip{.5\hsize plus .092\hsize minus .5\hsize} % D.A.'s value
+
+% Bovendien definieren we enkele extra \fill's:
+
+\newskip\s_spac_fil \s_spac_fil = \zeropoint \s!plus 1\s!fil \relax
+\newskip\s_spac_fill \s_spac_fill = \zeropoint \s!plus 1\s!fill \relax
+\newskip\s_spac_filll \s_spac_filll = \zeropoint \s!plus 1\s!filll\relax
+
+\newskip\s_spac_neg_fil \s_spac_neg_fil = \zeropoint \s!plus-1\s!fil \relax
+\newskip\s_spac_neg_fill \s_spac_neg_fill = \zeropoint \s!plus-1\s!fill \relax
+\newskip\s_spac_neg_filll \s_spac_neg_filll = \zeropoint \s!plus-1\s!filll\relax
+
+\permanent\protected\def\hfilll {\hskip\s_spac_filll\relax}
+\permanent\protected\def\vfilll {\vskip\s_spac_filll\relax}
+
+\permanent\protected\def\hfillneg {\hskip\s_spac_neg_fill \relax}
+\permanent\protected\def\hfilllneg{\hskip\s_spac_neg_filll\relax}
+\permanent\protected\def\vfillneg {\vskip\s_spac_neg_fill \relax}
+\permanent\protected\def\vfilllneg{\vskip\s_spac_neg_filll\relax}
+
+\permanent\protected\def\tfskip {\begingroup\tf\hskip\emwidth\endgroup}
+\permanent\protected\def\dotfskip#1{\begingroup\tf\hskip #1\endgroup} % used elsewhere
+
+% maybe we should hash the analysis
+
+\installcorenamespace{narrower}
+\installcorenamespace{narrowermethod}
+
+\newskip\s_spac_narrower_left
+\newskip\s_spac_narrower_right
+\newskip\s_spac_narrower_middle
+
+\installcommandhandler \??narrower {narrower} \??narrower
+
+\setupnarrower
+ [\c!before=\endgraf,
+ \c!after=\endgraf,
+ \c!left=1.5\emwidth,
+ \c!right=1.5\emwidth,
+ \c!middle=1.5\emwidth,
+ \c!default=\v!middle]
+
+\appendtoks
+ \frozen\instance\protected\edefcsname\e!start\currentnarrower\endcsname{\spac_narrower_start[\currentnarrower]}%
+ \frozen\instance\protected\edefcsname\e!stop \currentnarrower\endcsname{\spac_narrower_stop}%
+\to \everydefinenarrower
+
+\permanent\protected\def\installnarrowermethod#1#2%
+ {\defcsname\??narrowermethod#1\endcsname{#2}}
+
+\protected\def\spac_narrower_method_analyze#1%
+ {\ifcsname\??narrowermethod#1\endcsname
+ \lastnamedcs
+ \else
+ \global\advance\s_spac_narrower_middle#1\relax
+ \fi}
+
+\def\spac_narrower_initialize[#1]% hm, can be dorepeat directly
+ {\dorepeatwithcommand[#1]\spac_narrower_method_analyze}
+
+\installnarrowermethod \v!left {\global\advance\s_spac_narrower_left \narrowerparameter\c!left \relax}
+\installnarrowermethod \v!middle {\global\advance\s_spac_narrower_middle \narrowerparameter\c!middle\relax}
+\installnarrowermethod \v!right {\global\advance\s_spac_narrower_right \narrowerparameter\c!right \relax}
+\installnarrowermethod{-\v!left }{\global\advance\s_spac_narrower_left -\narrowerparameter\c!left \relax}
+\installnarrowermethod{-\v!middle}{\global\advance\s_spac_narrower_middle-\narrowerparameter\c!middle\relax}
+\installnarrowermethod{-\v!right }{\global\advance\s_spac_narrower_right -\narrowerparameter\c!right \relax}
+\installnarrowermethod \v!reset {\global \s_spac_narrower_left \zeropoint
+ \global \s_spac_narrower_middle \zeropoint
+ \global \s_spac_narrower_right \zeropoint\relax}
+\installnarrowermethod \v!none {}
+\installnarrowermethod \v!reverse {} % never seen
+
+\permanent\overloaded\tolerant\protected\def\spac_narrower_start[#1]#*[#2]%
+ {\begingroup
+ \edef\currentnarrower{#1}%
+ \ifparameter#2\or
+ \spac_narrower_start_apply{#2}%
+ \else
+ \spac_narrower_start_apply{\narrowerparameter\v!default}%
+ \fi}
+
+\newskip \s_spac_narrower_left_last
+\newskip \s_spac_narrower_right_last
+\newconditional\s_spac_narrower_last_swap
+
+\def\spac_narrower_start_apply#1%
+ {\narrowerparameter\c!before
+ \global\s_spac_narrower_left \zeropoint
+ \global\s_spac_narrower_right \zeropoint
+ \global\s_spac_narrower_middle\zeropoint
+ \edef\scratchstringone{#1}%
+ \ifx\scratchstringone\v!reverse
+ \ifconditional\s_spac_narrower_last_swap
+ \frozen\leftskip \s_spac_narrower_right_last
+ \frozen\rightskip\s_spac_narrower_left_last
+ \setfalse\s_spac_narrower_last_swap
+ \else
+ \frozen\leftskip \s_spac_narrower_left_last
+ \frozen\rightskip\s_spac_narrower_right_last
+ \settrue\s_spac_narrower_last_swap
+ \fi
+ \else
+ \normalexpanded{\processcommalistwithparameters[\scratchstringone]}\spac_narrower_initialize
+ \frozen\advance\leftskip \dimexpr\s_spac_narrower_left +\s_spac_narrower_middle\relax
+ \frozen\advance\rightskip\dimexpr\s_spac_narrower_right+\s_spac_narrower_middle\relax
+ \fi
+ \seteffectivehsize}
+
+\permanent\protected\def\spac_narrower_stop
+ {\narrowerparameter\c!after
+ \normalexpanded{%
+ \endgroup
+ \s_spac_narrower_left_last \the\leftskip \relax
+ \s_spac_narrower_right_last\the\rightskip\relax
+ \ifconditional\s_spac_narrower_last_swap
+ \setfalse\s_spac_narrower_last_swap
+ \else
+ \settrue\s_spac_narrower_last_swap
+ \fi
+ }}
+
+\permanent\tolerant\protected\def\startnarrower[#1]%
+ {\begingroup
+ \let\currentnarrower\empty
+ \ifparameter#1\or
+ \spac_narrower_start_apply{#1}%
+ \else
+ \spac_narrower_start_apply{\narrowerparameter\v!default}%
+ \fi}
+
+\aliased\let\stopnarrower\spac_narrower_stop
+
+\permanent\tolerant\protected\def\startnarrow[#1]#*[#2]% current how
+ {\begingroup
+ \ifnum\lastarguments=\plustwo
+ \expandafter\spac_narrower_start_named_two
+ \else
+ \expandafter\spac_narrower_start_named_one
+ \fi[#1][#2]}
+
+\def\spac_narrower_start_named_one[#1]%
+ {\doifelseassignment{#1}\spac_narrower_start_named_one_yes\spac_narrower_start_named_one_nop[#1]}
+
+\def\spac_narrower_start_named_one_yes[#1][#2]% [settings] []
+ {\setupcurrentnarrower[#1]%
+ \spac_narrower_start_apply{\narrowerparameter\v!default}}
+
+\def\spac_narrower_start_named_one_nop[#1][#2]% [tag] []
+ {\edef\currentnarrower{#1}%
+ \spac_narrower_start_apply{\narrowerparameter\v!default}}
+
+\def\spac_narrower_start_named_two[#1]%
+ {\doifelseassignment{#1}\spac_narrower_start_named_settings_how\spac_narrower_start_named_tag_unknown[#1]}
+
+\def\spac_narrower_start_named_settings_how[#1][#2]% [settings] [how]
+ {\setupcurrentnarrower[#1]%
+ \spac_narrower_start_apply{#2}}
+
+\def\spac_narrower_start_named_tag_unknown[#1][#2]% [tag] [...]
+ {\doifelseassignment{#2}\spac_narrower_start_named_tag_settings\spac_narrower_start_named_tag_how[#1][#2]}
+
+\def\spac_narrower_start_named_tag_settings[#1][#2]% [tag] [settings]
+ {\edef\currentnarrower{#1}%
+ \setupcurrentnarrower[#2]%
+ \spac_narrower_start_apply{\narrowerparameter\v!default}}
+
+\def\spac_narrower_start_named_tag_how[#1][#2]% [tag] [how]
+ {\edef\currentnarrower{#1}%
+ \spac_narrower_start_apply{#2}}
+
+\aliased\let\stopnarrow\spac_narrower_stop
+
+\newdimen\d_spac_effective_hsize \permanent\protected\def\effectivehsize {\hsize}
+\newdimen\d_spac_effective_leftskip \permanent\protected\def\effectiveleftskip {\dimexpr\leftskip \relax}
+\newdimen\d_spac_effective_rightskip \permanent\protected\def\effectiverightskip{\dimexpr\rightskip\relax}
+
+\permanent\protected\def\seteffectivehsize
+ {\setlocalhsize
+ \d_spac_effective_hsize \localhsize
+ \d_spac_effective_leftskip 1\leftskip
+ \d_spac_effective_rightskip1\rightskip
+ \enforced\let\effectivehsize \d_spac_effective_hsize
+ \enforced\let\effectiveleftskip \d_spac_effective_leftskip
+ \enforced\let\effectiverightskip\d_spac_effective_rightskip}
+
+\installcorenamespace{skipadaptionleft}
+\installcorenamespace{skipadaptionright}
+
+\newskip\leftskipadaption
+\newskip\rightskipadaption
+
+\defcsname\??skipadaptionleft \v!yes\endcsname{\ifzeropt\d_spac_indentation_par\narrowerparameter\c!left\else\d_spac_indentation_par\fi}
+\letcsname\??skipadaptionleft \v!no \endcsname\zeropoint
+\letcsname\??skipadaptionleft \empty\endcsname\zeropoint
+\defcsname\??skipadaptionright\v!yes\endcsname{\narrowerparameter\c!right}
+\letcsname\??skipadaptionright\v!no \endcsname\zeropoint
+\letcsname\??skipadaptionright\empty\endcsname\zeropoint
+
+\letcsname\??skipadaptionleft \v!standard\expandafter\endcsname\csname\??skipadaptionleft \v!yes\endcsname
+\letcsname\??skipadaptionright\v!standard\expandafter\endcsname\csname\??skipadaptionright\v!yes\endcsname
+
+\permanent\protected\def\dosetleftskipadaption #1{\leftskipadaption \ifcsname\??skipadaptionleft #1\endcsname\lastnamedcs\else#1\fi\relax}
+\permanent\protected\def\dosetrightskipadaption#1{\rightskipadaption\ifcsname\??skipadaptionright#1\endcsname\lastnamedcs\else#1\fi\relax}
+
+\permanent\protected\def\doadaptleftskip #1{\normalexpanded{\dosetleftskipadaption {#1}}\permanent\advance\leftskip \leftskipadaption }
+\permanent\protected\def\doadaptrightskip#1{\normalexpanded{\dosetrightskipadaption{#1}}\permanent\advance\rightskip\rightskipadaption}
+
+\permanent\protected\def\forgetbothskips
+ {\leftskip\zeropoint
+ \rightskip\zeropoint
+ \relax}
+
+\appendtoks
+ \forgetbothskips
+\to \everyforgetall
+
+% in spac-ver.mkiv
+%
+% \protected\def\forgetparskip
+% {\s_spac_whitespace_parskip\zeropoint
+% \parskip\zeropoint
+% \let\v_spac_whitespace_current\v!none}
+%
+% \appendtoks
+% \forgetparskip
+% \to \everyforgetall
+
+%D Tolerance (can also be set with align):
+
+\installcorenamespace{tolerancemethods}
+
+\mutable\let\bottomtolerance\empty % why not zero (not used annyway, but for now we keep it)
+
+\permanent\protected\def\installtolerancemethod#1#2#3%
+ {\defcsname\??tolerancemethods#1:#2\endcsname{#3}}
+
+\installtolerancemethod \v!vertical \v!verystrict {\let\bottomtolerance\empty} % why not zero
+\installtolerancemethod \v!vertical \v!strict {\def\bottomtolerance{.050}}
+\installtolerancemethod \v!vertical \v!tolerant {\def\bottomtolerance{.075}}
+\installtolerancemethod \v!vertical \v!verytolerant {\def\bottomtolerance{.100}}
+
+\installtolerancemethod \v!horizontal \v!stretch {\emergencystretch\bodyfontsize\relax}
+\installtolerancemethod \v!horizontal \v!space {\spaceskip.5em\s!plus.25em\s!minus.25em\relax}
+\installtolerancemethod \v!horizontal \v!verystrict {\tolerance\plustwohundred}
+\installtolerancemethod \v!horizontal \v!strict {\tolerance1500 }
+\installtolerancemethod \v!horizontal \v!tolerant {\tolerance3000 }
+\installtolerancemethod \v!horizontal \v!verytolerant {\tolerance4500 }
+
+\appendetoks
+ \pretolerance\plushundred
+ \tolerance \plustwohundred
+\to\everyforgetall
+
+\def\spac_tolerances_step_vertical #1{\csname\??tolerancemethods\v!vertical :#1\endcsname}
+\def\spac_tolerances_step_horizontal#1{\csname\??tolerancemethods\v!horizontal:#1\endcsname}
+
+\permanent\tolerant\protected\def\setuptolerance[#1]%
+ {\ifarguments\or
+ \doifelseinset\v!vertical{#1}%
+ {\processcommacommand[#1]\spac_tolerances_step_vertical }
+ {\processcommacommand[#1]\spac_tolerances_step_horizontal}%
+ \fi}
+
+% %D \macros
+% %D {pushindentation,popindentation}
+% %D
+% %D The pushing and popping is done by:
+%
+% \newbox\b_spac_indentations_a
+% \newbox\b_spac_indentations_b
+%
+% \permanent\protected\def\pushindentation
+% {\begingroup
+% \ifhmode
+% \unskip
+% \setbox\b_spac_indentations_a\lastbox % get \strut if present
+% \unskip
+% \setbox\b_spac_indentations_b\lastbox % get \indent generated box
+% \unskip
+% \else
+% \dontleavehmode % was \hskip\zeropoint % switch to horizontal mode
+% \unskip
+% \setbox\b_spac_indentations_a\lastbox % get \indent generated box
+% \setbox\b_spac_indentations_b\emptybox
+% \fi}
+%
+% \permanent\protected\def\popindentation
+% {\box\b_spac_indentations_b
+% \box\b_spac_indentations_a
+% \endgroup}
+
+%D The only complication lays in \type{\strut}. In \PLAIN\ \TEX\ a \type{\strut} is
+%D defined as:
+%D
+%D \starttyping
+%D \def\strut%
+%D {\relax\ifmmode\copy\strutbox\else\unhcopy\strutbox\fi}
+%D \stoptyping
+%D
+%D But what is a \type {\strut}? Normally it's a rule of width zero, but when made
+%D visual, it's a rule and a negative skip. The mechanism for putting things in the
+%D margins described here cannot handle this situation very well. One characteristic
+%D of \type {\strut} is that the \type {\unhcopy} results in entering horizontal
+%D mode, which in return leads to some indentation.
+%D
+%D To serve our purpose a bit better, the macro \type {\strut} can be redefined as:
+%D \starttyping
+%D \def\strut
+%D {\relax\ifmmode\else\hskip0pt\fi\copy\strutbox}
+%D \stoptyping
+%D
+%D Or more compatible:
+%D
+%D \starttyping
+%D \def\strut
+%D {\relax\ifmmode
+%D \copy\strutbox
+%D \else
+%D \bgroup\setbox\strutbox=\hbox{\box\strutbox}\unhcopy\strutbox\egroup
+%D \fi}
+%D \stoptyping
+%D
+%D In \CONTEXT\ however we save some processing time by putting an extra \type
+%D {\hbox} around the \type {\strutbox}.
+%D
+%D \starttyping
+%D % \setuplayout[gridgrid=yes] \showgrid
+%D
+%D \startbuffer
+%D test 1\crlf
+%D test 2\crlf
+%D
+%D \crlf test 3
+%D
+%D test 4\crlf
+%D test 5
+%D
+%D \crlf
+%D \crlf
+%D \crlf
+%D test 6
+%D \stopbuffer
+%D
+%D \hbox
+%D {\hsize5em
+%D \ruledvtop{\getbuffer}\enspace
+%D \ruledvtop{\showstruts\getbuffer}\enspace
+%D \hsize15em \setuptyping[before=,after=]%
+%D \ruledvtop{\typebuffer}}
+%D \stoptyping
+
+\permanent\protected\def\justonespace{\removeunwantedspaces\space}
+%permanent\protected\def\justaperiod {\removeunwantedspaces.}
+%permanent\protected\def\justacomma {\removeunwantedspaces,}
+
+\installcorenamespace{hspace}
+
+\permanent\protected\def\ignorecrlf
+ {\enforced\let\crlf\justonespace
+ \enforced\let\\\crlf}
+
+\permanent\tolerant\protected\def\definehspace[#1]#*[#2]#*[#3]% #1 = optional namespace
+ {\ifparameter#3\or
+ \defcsname\??hspace#1:#2\endcsname{#3}%
+ \else
+ \defcsname\??hspace :#1\endcsname{#2}%
+ \fi}
+
+\permanent\tolerant\protected\def\hspace[#1]#*[#2]%
+ {\ifhmode
+ \removeunwantedspaces
+ \hskip % always a skip even when 0pt
+ \ifparameter#2\or
+ \hspaceamount{#1}{#2}%
+ \orelse\ifparameter#1\or
+ \hspaceamount\empty{#1}%
+ \else
+ \hspaceamount\empty\s!default
+ \fi
+ \relax
+ \expandafter\ignorespaces
+ \fi}
+
+\permanent\overloaded\def\hspaceamount #1#2{\dimexpr\ifcsname\??hspace#1:#2\endcsname\lastnamedcs\else\zeropoint\fi\relax}
+\permanent\overloaded\def\directhspaceamount #1{\dimexpr\ifcsname\??hspace :#1\endcsname\lastnamedcs\else\zeropoint\fi\relax}
+
+% no installhspace here (this is already an old command)
+
+\definehspace [\v!small] [.25\emspaceamount]
+\definehspace [\v!medium] [.5\emspaceamount]
+\definehspace [\v!big] [1\emspaceamount]
+\definehspace [\v!normal] [1\spaceamount]
+\definehspace [\v!default] [\spaceamount]
+\definehspace [\v!none] [\zeropoint]
+
+%D Taken from Taco's math module (cq. \AMS\ macros), but
+%D adapted to \type {\hspace}:
+
+\permanent\protected\def\textormathspace #1#2#3{\ifmmode\mskip#1#2\else\kern #1\hspaceamount\empty{#3}\fi\relax}
+\permanent\protected\def\textormathspacecommand #1#2#3{\ifmmode\mskip#1#2\else#3\fi\relax}
+\permanent\protected\def\breakabletextormathspace#1#2#3{\ifmmode\mskip#1#2\else\hskip#1\hspaceamount\empty{#3}\fi\relax}
+
+\overloaded\permanent\protected \def\hairspace {\textormathspace+\hairmuskip{.5}}
+\overloaded\permanent\protected \def\thinspace {\textormathspace+\thinmuskip 1}
+%overloaded\permanent\protected \def\medspace {\textormathspace+\medmuskip 2} % 4/18 em
+\overloaded\permanent\protected \def\thickspace {\textormathspace+\thickmuskip3}
+\overloaded\permanent\protected \def\neghairspace {\textormathspace-\thinmuskip{.5}}
+\overloaded\permanent\protected \def\negthinspace {\textormathspace-\thinmuskip 1}
+\overloaded\permanent\protected \def\negmedspace {\textormathspace-\medmuskip 2}
+\overloaded\permanent\protected \def\negthickspace{\textormathspace-\thickmuskip3}
+\overloaded\permanent\protected\edef\medspace {\textormathspacecommand+\medmuskip{\Uchar"205F}}
+
+% needed for unicode:
+
+% \protected\def\breakablethinspace {\breakabletextormathspace+\thinmuskip1}
+% \protected\def\twoperemspace {\hskip\dimexpr\emwidth/2\relax} % == \enspace
+% \protected\def\threeperemspace {\hskip\dimexpr\emwidth/3\relax}
+% \protected\def\fourperemspace {\hskip\dimexpr\emwidth/4\relax}
+% \protected\def\fiveperemspace {\hskip\dimexpr\emwidth/5\relax} % goodie
+% \protected\def\sixperemspace {\hskip\dimexpr\emwidth/6\relax}
+% \protected\def\figurespace {\begingroup\setbox\scratchbox\hbox{0}\hskip\wd\scratchbox\endgroup} % there is a command for this
+% \protected\def\punctuationspace {\begingroup\setbox\scratchbox\hbox{.}\hskip\wd\scratchbox\endgroup}
+% \protected\def\ideographicspace {\hskip\dimexpr\emwidth/1\relax}
+% \protected\def\ideographichalffillspace{\hskip\dimexpr\emwidth/2\relax}
+% \protected\def\nobreakspace {\penalty\plustenthousand\kern\interwordspace}
+% \protected\def\narrownobreakspace {\penalty\plustenthousand\thinspace}
+% \protected\def\zerowidthnobreakspace {\penalty\plustenthousand\kern\zeropoint}
+% \protected\def\zerowidthspace {\hskip\zeropoint}
+
+\definehspace[.5][.1250\emwidth] % hair
+\definehspace[1] [.1667\emwidth] % thin
+\definehspace[2] [.2222\emwidth] % med
+\definehspace[3] [.2777\emwidth] % thick
+
+\suggestedalias \, \thinspace
+\suggestedalias \: \medspace
+\suggestedalias \; \thickspace
+\suggestedalias \! \negthinspace
+
+% plain ...
+%
+% \ifdefined\> \else \protected\def\>{\mskip \medmuskip } \fi
+% \ifdefined\* \else \protected\def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}} \fi
+
+\permanent\def\flexiblespaceamount#1#2#3%
+ {#1\interwordspace
+ \s!plus#2\interwordstretch
+ \s!minus#3\interwordshrink}
+
+\permanent\def\fixedspaceamount#1%
+ {#1\interwordspace}
+
+% moved from page-lin
+%
+% the following code is used in startlines\stoplines
+%
+% do we need \normalspaceprimitive here?
+
+\installcorenamespace{spacemethods}
+
+\permanent\protected\def\installspacemethod#1#2% needs to set \obeyedspace
+ {\defcsname\??spacemethods#1\endcsname{#2}}
+
+\permanent\def\activatespacehandler#1%
+ {\csname\??spacemethods\ifcsname\??spacemethods#1\endcsname#1\else\v!off\fi\endcsname}
+
+\permanent\protected\def\spac_spaces_checked_control{\mathortext\normalspace{\dontleavehmode{\tt\controlspace}}}%
+\permanent\protected\def\spac_spaces_checked_normal {\mathortext\normalspace{\dontleavehmode\normalspace}}%
+\permanent\protected\def\spac_spaces_checked_fixed {\mathortext\normalspace{\dontleavehmode\fixedspace}}%
+
+% hm, order matters when we \let in \obeyspaces
+
+\installspacemethod \v!on
+ {\obeyspaces
+ \enforced\let\obeyedspace\spac_spaces_checked_control
+ \enforced\let\ =\obeyedspace}
+
+\installspacemethod \v!yes
+ {\obeyspaces
+ \enforced\let\obeyedspace\spac_spaces_checked_normal
+ \enforced\let\ =\obeyedspace}
+
+\installspacemethod \v!off % == default
+ {\normalspaces
+ \enforced\let\obeyedspace\normalspace
+ \enforced\let\ =\normalspaceprimitive} % was \normalspace
+
+\installspacemethod \v!fixed
+ {\obeyspaces
+ \enforced\let\obeyedspace\spac_spaces_checked_fixed
+ \enforced\let\ =\obeyedspace}
+
+\appendtoks
+ \normalspaces % to be sure
+\to \everybeforeoutput
+
+%D A more robust variant of the \MKII\ one:
+%D
+%D \startbuffer
+%D bla \TEX\autoinsertnextspace bla
+%D bla \TEX\autoinsertnextspace (bla)
+%D bla (\TEX\autoinsertnextspace) bla
+%D bla \TEX\autoinsertnextspace\ bla
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+% \autoinsertnextspace % defined at the lua end
+
+%D Moved from bib module:
+
+\permanent\protected\def\outdented#1%
+ {\hskip-\hangindent#1\relax}
+
+%D Beware: due to char-def this becomes an active character but that might change
+%D sometime when we will replace all these specials to node insertions. We might
+%D even expand it to utf then as it then can be used in string comparison (not that
+%D much needed anyway).
+
+% \chardef\zwnj="200C
+% \chardef\zwj ="200D
+
+% TODO (but used in languages):
+
+\def\spac_glues_text_or_math#1#2%
+ {\begingroup
+ \ifmmode
+ \mskip#1%
+ \else
+ \scratchdimen#1\hspaceamount\empty{#2}%
+ \scratchskip\scratchdimen\s!plus.5\scratchdimen\s!minus.3\scratchdimen
+ \hskip\scratchskip
+ \fi
+ \endgroup}
+
+\permanent\protected\def\thinglue {\spac_glues_text_or_math\thinmuskip \v!small}
+\permanent\protected\def\medglue {\spac_glues_text_or_math\medmuskip \v!medium}
+\permanent\protected\def\thickglue{\spac_glues_text_or_math\thickmuskip\v!big}
+
+%D A rather unknown one:
+
+\permanent\protected\def\widened % moved from cont-new
+ {\doifelsenextoptionalcs\spac_widened_yes\spac_widened_nop}
+
+\def\spac_widened_yes[#1]#2{\hbox \s!spread #1{\hss#2\hss}}
+\def\spac_widened_nop #1{\hbox \s!spread \emwidth{\hss#1\hss}}
+
+%D For the moment here (used in page-txt):
+
+\permanent\protected\def\ignoredlinebreak{\unskip\space\ignorespaces}
+
+%D \macros
+%D {startignorespaces}
+%D
+%D I'll probably forget that this one exists:
+%D
+%D \starttyping
+%D \ruledhbox
+%D {\startignorespaces
+%D \def\oeps{a}
+%D \startignorespaces
+%D \def\oeps{a}
+%D \stopignorespaces
+%D \def\oeps{a}
+%D \stopignorespaces
+%D \oeps}
+%D \stoptyping
+
+\newsignal\d_spac_ignore_spaces_signal
+\newcount \c_spac_ignore_spaces
+
+\permanent\protected\def\startignorespaces
+ {\advance\c_spac_ignore_spaces\plusone
+ \ifcase\c_spac_ignore_spaces\or \ifhmode
+ \hskip\d_spac_ignore_spaces_signal
+ \fi \fi
+ \ignorespaces}
+
+\permanent\protected\def\stopignorespaces
+ {\ifcase\c_spac_ignore_spaces \or
+ \ifhmode
+ \doloop\spac_ignore_spaces_body
+ \fi
+ \fi
+ \advance\c_spac_ignore_spaces\minusone}
+
+\def\spac_ignore_spaces_body
+ {\ifzeropt\lastskip
+ \exitloop
+ \orelse\ifdim\lastskip=\d_spac_ignore_spaces_signal
+ \unskip
+ \exitloop
+ \else
+ \unskip
+ \fi}
+
+%D \macros
+%D {obeyfollowingtoken}
+
+\permanent\def\obeyfollowingtoken{{}} % end \cs scanning
+
+%D Something new:
+
+\permanent\protected\def\interwordspacebefore{\wordboundary\zwnj\hskip\interwordspace\relax}
+\permanent\protected\def\interwordspaceafter {\hskip\interwordspace\relax\zwnj\wordboundary}
+
+\permanent\protected\def\interwordspacesbefore#1{\dofastloopcs{#1}\interwordspacebefore}
+\permanent\protected\def\interwordspacesafter #1{\dofastloopcs{#1}\interwordspaceafter}
+\permanent\protected\def\interwordspaces #1{\wordboundary\zwnj\dofastloopcs{\numexpr#1+\minusone}\interwordspaceafter}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-lin.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-lin.mkxl
new file mode 100644
index 00000000000..53da64923af
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-lin.mkxl
@@ -0,0 +1,211 @@
+%D \module
+%D [ file=spac-lin,
+%D version=2012.01.08, % 2009.10.16 (core-ver) 1997.03.31 (core-spa)
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Vertical,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Lines}
+
+\unprotect
+
+%D When spacing is active we need to handle commands in a special way:
+%D
+%D \starttyping
+%D \setuplines[space=on]
+%D
+%D \startlines
+%D Let's talk about this{\ttsl\gobbleoneargument or}that.
+%D \stoplines
+%D
+%D \startlines
+%D Let's talk about this{\getvalue{ttsl}or}that.
+%D \stoplines
+%D \stoptyping
+%D
+%D One can indent in several ways:
+%D
+%D \starttyping
+%D \setupindenting[medium] \setuplines[indenting=odd] % no yes odd even
+%D
+%D \startlines
+%D first
+%D second
+%D third
+%D fourth
+%D \stoplines
+%D \stoptyping
+
+%D Contrary to \MKII\ we can now define classes of lines (generalized by
+%D Wolfgang). I will probably rewrite bits in \LUA.
+
+\installcorenamespace{lines}
+
+\installcommandhandler \??lines {lines} \??lines
+
+\setuplines
+ [\c!option=,
+ \c!command=,
+ \c!before=\blank,
+ \c!after=\blank,
+ \c!inbetween=\blank,
+ \c!indenting=\v!no,
+ \c!space=\v!default]
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!start\currentlines}{\spac_lines_start[\currentlines]}%
+ \frozen\instance\letvalue {\e!stop \currentlines }\spac_lines_stop
+\to \everydefinelines
+
+\let\p_lines_option\empty
+
+\let\spac_lines_break \relax
+\let\spac_after_first_obeyed_line\relax
+\let\spac_lines_indent \relax
+
+%D See \type {indentation-005.tex}:
+%D
+%D \starttyping
+%D \setuplines[indentlist={0pt,10pt,15pt,20pt}]
+%D
+%D \setupindenting[yes,1em]
+%D \setuplines[indentlist={0pt,*,*}]
+%D
+%D \setuplines[indentlist={0pt,1em,*}]
+%D \starttyping
+
+\newcount\c_spac_lines_indent_cnt
+\newcount\c_spac_lines_indent_max
+\newdimen\d_spac_lines_indent
+
+\def\spac_lines_indent_indeed
+ {\ifnum\c_spac_lines_indent_cnt=\c_spac_lines_indent_max
+ \c_spac_lines_indent_cnt\plusone
+ \else
+ \advance\c_spac_lines_indent_cnt\plusone
+ \fi
+ \getfromcommacommand[\m_spac_lines_indentlist][\c_spac_lines_indent_cnt]%
+ \ifx\commalistelement\wildcardsymbol
+ \hskip\d_spac_lines_indent
+ \else
+ \scratchdimen\dimexpr\commalistelement\relax
+ \hskip
+ \ifdim\scratchdimen=\zeropoint
+ \zeropoint
+ \else
+ \scratchdimen
+ \d_spac_lines_indent\scratchdimen
+ \fi
+ \fi
+ \relax}
+
+\permanent\tolerant\protected\def\spac_lines_start[#1]%
+ {\bgroup
+ \edef\currentlines{#1}%
+ \obeylines
+ \spac_lines_start_indeed}
+
+\tolerant\def\spac_lines_start_indeed[#1]% new: optional second argument (WS)
+ {\ifarguments\or
+ \setupcurrentlines[#1]%
+ \fi
+ \edef\p_lines_option{\linesparameter\c!option}%
+ \ifx\p_lines_option\v!packed
+ \let\spac_lines_break\nobreak
+ \else
+ \let\spac_lines_break\relax
+ \fi
+ \linesparameter\c!before
+ \push_macro_checkindentation
+ \whitespace
+ \dostarttaggedchained\t!lines\currentlines\??lines
+ \begingroup
+ \uselinesstyleandcolor\c!style\c!color
+ \useindentingparameter\linesparameter
+ \usealignparameter\linesparameter
+ \typesettinglinestrue
+ \setupwhitespace[\v!none]% todo use fast variant
+ %\obeylines % move upwards to keep spaces in the first line due to optional argument
+ %
+ \edef\m_spac_lines_indentlist{\linesparameter\c!indentlist}%
+ \getcommacommandsize[\m_spac_lines_indentlist]%
+ \c_spac_lines_indent_max\commalistsize
+ \c_spac_lines_indent_cnt\zerocount
+ \ifnum\c_spac_lines_indent_max>\plusone
+ \let\spac_lines_indent\spac_lines_indent_indeed
+ \d_spac_lines_indent\d_spac_indentation_par
+ \else
+ \let\spac_lines_indent\relax
+ \fi
+ %
+ \ignorespaces
+ \glet\spac_after_first_obeyed_line\spac_lines_after_first_obeyed_line_a
+ \enforced\let\obeyedline\spac_lines_obeyed_line
+ \activatespacehandler{\linesparameter\c!space}%
+ \dostarttagged\t!line\empty
+ \ignorepars}
+
+
+% we could have states instead and handle option in there
+
+\def\spac_lines_after_first_obeyed_line_a % tzt two pass, like itemize
+ {\linesparameter\c!command
+ \spac_lines_indent
+ \linesparameter\c!left
+ \glet\spac_after_first_obeyed_line\spac_lines_after_first_obeyed_line_b}
+
+\def\spac_lines_after_first_obeyed_line_b
+ {\spac_lines_break
+ \linesparameter\c!command
+ \spac_lines_indent
+ \linesparameter\c!left}
+
+\def\spac_lines_obeyed_line
+ {\ifdone\linesparameter\c!right\fi
+ \dostoptagged % can be a dummy one as we don't look ahead
+ \par
+ \dostarttagged\t!line\empty
+ \futurelet\nexttoken\spac_lines_between}
+
+\protected\def\spac_lines_stop
+ {\dostoptagged
+ \endgroup
+ \dostoptagged
+ \pop_macro_checkindentation
+ \linesparameter\c!after
+ \egroup}
+
+\def\spac_lines_between
+ {\ifx\nexttoken\spac_lines_stop
+ \donefalse
+ \else
+ \doifelsemeaning\nexttoken\obeyedline % brrr
+ {\donefalse\linesparameter\c!inbetween}
+ {\donetrue\spac_after_first_obeyed_line}%
+ \fi}
+
+\definelines[\v!lines]
+
+\permanent\tolerant\protected\def\emptylines[#1]%
+ {\endgraf
+ \begingroup
+ %\forgetall % debatable
+ \ifhmode
+ \crlf % finish the current line
+ \fi
+ \scratchcounter\ifparameter#1\or#1\else3\fi\relax
+ \dorecurse\scratchcounter{\strut\crlf}%
+ \endgroup}
+
+\ifdefined\startlines \else
+ \aliased\expandafter\let\expandafter\startlines\csname\e!start\v!lines\endcsname
+ \aliased\expandafter\let\expandafter\stoplines \csname\e!stop \v!lines\endcsname
+\fi
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-pag.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-pag.mkxl
new file mode 100644
index 00000000000..d9f7c67a1ee
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-pag.mkxl
@@ -0,0 +1,170 @@
+%D \module
+%D [ file=spac-pag,
+%D version=2009.10.16, % 1997.03.31, was core-spa.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Pages,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Pages}
+
+\unprotect
+
+\newif \ifpagestatemismatch
+\newcount \realpagestateno
+\newconstant\frozenpagestate
+
+\permanent\protected\def\dotrackpagestate#1#2%
+ {\ifdoublesided \ifinpagebody \else
+ \doforcedtrackpagestate{#1}{#2}%
+ \fi \fi}
+
+\permanent\protected\def\doforcedtrackpagestate#1#2%
+ {\ifcase\frozenpagestate
+ \global\advance#2\plusone
+ \setpagestate[#1][\number#2]%
+ \fi}
+
+\let\m_spac_pagestates_realpage\empty
+
+\newconditional\c_spac_pagestates_found
+
+\permanent\protected\def\docheckpagestate#1#2%
+ {\ifcase\frozenpagestate
+ \setpagestaterealpageno{#1}{\number#2}%
+ \fi}
+
+\permanent\protected\def\doifelserightpagestate#1#2% not expandable !
+ {\ifcase\frozenpagestate
+ \pagestatemismatchfalse
+ \realpagestateno\realfolio
+ \ifinpagebody
+ \ifdoublesided
+ \ifodd\realpageno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \else
+ \settrue\c_spac_pagestates_found
+ \fi
+ \orelse\ifdoublesided
+ \edef\m_spac_pagestates_realpage{\pagestaterealpage{#1}{\number#2}}%
+ \ifempty\m_spac_pagestates_realpage
+ \ifodd\realpageno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \else
+ \realpagestateno\m_spac_pagestates_realpage\relax
+ \ifnum\realpagestateno=\realpageno \else
+ \pagestatemismatchtrue
+ \fi
+ \ifodd\realpagestateno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \fi
+ \else
+ \settrue\c_spac_pagestates_found
+ \fi
+ \else
+ \ifodd\realpagestateno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \fi
+ \ifconditional\c_spac_pagestates_found
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifelseforcedrightpagestate#1#2%
+ {\ifcase\frozenpagestate
+ \pagestatemismatchfalse
+ \realpagestateno\realfolio
+ \edef\m_spac_pagestates_realpage{\pagestaterealpage{#1}{\number#2}}%
+ \ifempty\m_spac_pagestates_realpage
+ \ifodd\realpageno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \else
+ \realpagestateno\m_spac_pagestates_realpage\relax
+ \ifnum\realpagestateno=\realpageno \else
+ \pagestatemismatchtrue
+ \fi
+ \ifodd\realpagestateno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \fi
+ \else
+ \ifodd\realpagestateno\relax
+ \settrue\c_spac_pagestates_found
+ \else
+ \setfalse\c_spac_pagestates_found
+ \fi
+ \fi
+ \ifconditional\c_spac_pagestates_found
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifrightpagestateelse \doifelserightpagestate
+\aliased\let\doifforcedrightpagestateelse\doifelseforcedrightpagestate
+
+\permanent\protected\def\freezepagestate {\frozenpagestate\plusone }
+\permanent\protected\def\defrostpagestate{\frozenpagestate\zerocount}
+
+% we can make more of these on top, but how to deal with mixed frozen states
+
+% \dorecurse{500}{\dontleavehmode\signalrightpage\doifrightpageelse{right}{left}\par} % given doublesided
+
+\newcount\nofraggedparagraphs
+
+\definepagestate[\s!paragraph]
+
+\permanent\protected\def\signalrightpage {\dotrackpagestate \s!paragraph\nofraggedparagraphs} % use \dontleavehmode if needed
+\permanent\protected\def\doifelserightpage{\doifelserightpagestate\s!paragraph\nofraggedparagraphs}
+\permanent\protected\def\rightpageorder {\pagestaterealpageorder\s!paragraph\nofraggedparagraphs}
+
+\aliased\let\doifrightpageelse\doifelserightpage
+
+\installcorenamespace{pagechanges}
+
+\newif\ifpagechanged \mutable\let\lastchangedpage\empty
+
+\def\spac_pagechanges_check#1#2#3%
+ {\pagechangedfalse
+ \doforcedtrackpagestate{#2}{#3}%
+ \edef\m_spac_pagestates_realpage{\pagestaterealpage{#2}{\number#3}}%
+ \ifempty\m_spac_pagestates_realpage \orelse\ifnum\m_spac_pagestates_realpage>0\csname\??pagechanges#2:#1\endcsname\relax
+ \pagechangedtrue
+ \fi
+ \ifpagechanged
+ \letgvalue{\??pagechanges#2:#1}\m_spac_pagestates_realpage
+ \glet\lastchangedpage\m_spac_pagestates_realpage
+ \else
+ \glet\lastchangedpage\realfolio
+ \fi}
+
+\permanent\protected\def\changedpagestate#1#2%
+ {\executeifdefined{\??pagechanges#2:#1}0}
+
+\permanent\protected\def\checkpagechange#1{\spac_pagechanges_check{#1}\s!paragraph\nofraggedparagraphs}
+\permanent\protected\def\changedpage #1{\changedpagestate{#1}\s!paragraph}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-par.lmt b/Master/texmf-dist/tex/context/base/mkxl/spac-par.lmt
new file mode 100644
index 00000000000..179fc1f4380
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-par.lmt
@@ -0,0 +1,81 @@
+if not modules then modules = { } end modules ['spac-par'] = {
+ version = 1.001,
+ comment = "companion to spac-par.mkxl",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local context = context
+local implement = interfaces.implement
+
+local collected = utilities.storage.allocate()
+local tobesaved = utilities.storage.allocate()
+local wrappers = { }
+
+local jobparwrappers = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.parwrappers = jobparwrappers
+
+local function initializer()
+ tobesaved = jobparwrappers.tobesaved
+ collected = jobparwrappers.collected
+end
+
+local function finalizer()
+ -- nothing yet
+end
+
+job.register('job.parwrappers.collected', tobesaved, initializer, finalizer)
+
+implement {
+ name = "newparwrapper",
+ arguments = "string",
+ actions = function(id)
+ local t = tobesaved[id]
+ local n
+ if t then
+ n = #t + 1
+ t[n] = 0
+ else
+ n = 1
+ tobesaved[id] = { 0 }
+ end
+ wrappers[id] = n
+ end
+}
+
+implement {
+ name = "setparwrapper",
+ arguments = "string",
+ protected = true,
+ actions = function(id)
+ local t = tobesaved[id]
+ local n = #t
+ t[n] = t[n] + 1
+ end
+}
+
+implement {
+ name = "getparwrapper",
+ arguments = "string",
+ public = true,
+ actions = function(id)
+ local t = tobesaved[id]
+ context(t and t[#t] or 0)
+ end
+}
+
+implement {
+ name = "lastparwrapper",
+ arguments = "string",
+ public = true,
+ actions = function(id)
+ local t = collected and collected[id]
+ context(t and t[wrappers[id]] or 0)
+ end
+}
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-par.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-par.mkxl
new file mode 100644
index 00000000000..134a60e2a02
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-par.mkxl
@@ -0,0 +1,456 @@
+%D \module
+%D [ file=spac-par,
+%D version=2009.10.16, % 1997.03.31, was core-spa.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Paragraphs,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Paragraphs}
+
+\registerctxluafile{spac-par}{autosuffix}
+
+\unprotect
+
+% TODO: \appendtoks \strut \wrapuppar{\strut}\to \everypar
+
+%D New experimental stuff:
+
+% this might move to syst-ini.mkxl
+
+\setnewconstant\paragraphupdatecodes\numexpr
+ \frozentolerancecode
+ + \frozenloosenesscode
+ + \frozenlinepenaltycode
+ + \frozenwidowpenaltycode
+ + \frozenclubpenaltycode
+ + \frozenbrokenpenaltycode
+ + \frozendemeritscode
+\relax
+
+\setnewconstant\paragraphpenaltycodes\numexpr
+ \frozenlinepenaltycode
+ + \frozenwidowpenaltycode
+ + \frozenclubpenaltycode
+ + \frozenbrokenpenaltycode
+\relax
+
+\setnewconstant\paragraphdemeritcodes\numexpr
+ \frozendemeritscode
+\relax
+
+\setnewconstant\paragraphshapecodes\numexpr
+ \frozenhangcode
+ + \frozenskipcode
+ + \frozenparfillcode
+ + \frozenshapecode
+\relax
+
+\setnewconstant\paragraphlinecodes\numexpr
+ \frozenlinecode
+\relax
+
+\setnewconstant\paragraphhyphenationcodes\numexpr
+ \frozenhyphenationcode
+\relax
+
+\setnewconstant\frozenparagraphdefault\numexpr
+ \frozenhsizecode % \hsize
+ + \frozenskipcode % \leftskip \rightskip
+ + \frozenhangcode % \hangindent \hangafter
+ + \frozenindentcode % \parindent
+ + \frozenparfillcode % \parfillskip \parfillleftskip
+ + \frozenadjustcode % \adjustspacing
+ + \frozenprotrudecode % \protrudechars
+ + \frozentolerancecode % \tolerance \pretolerance
+ + \frozenstretchcode % \emergcystretch
+ + \frozenloosenesscode % \looseness
+ + \frozenlastlinecode % \lastlinefit
+ + \frozenlinepenaltycode % \linepenalty \interlinepenalty \interlinepenalties
+ + \frozenclubpenaltycode % \clubpenalty \clubpenalties
+ + \frozenwidowpenaltycode % \widowpenalty \widowpenalties
+ + \frozendisplaypenaltycode % \displaypenalty \displaypenalties
+ + \frozenbrokenpenaltycode % \brokenpenalty
+ + \frozendemeritscode % \doublehyphendemerits \finalhyphendemerits \adjdemerits
+ + \frozenshapecode % \parshape
+ % \frozenlinecode % \baselineskip \lineskip \lineskiplimit
+ + \frozenhyphenationcode % \hyphenationmode
+\relax
+
+\permanent\protected\def\freezeparagraphproperties {\snapshotpar\frozenparagraphdefault}
+\permanent\protected\def\defrostparagraphproperties{\snapshotpar\zerocount}
+
+\permanent\protected\def\updateparagraphproperties{\ifhmode\snapshotpar\paragraphupdatecodes \fi}
+\permanent\protected\def\updateparagraphpenalties {\ifhmode\snapshotpar\paragraphpenaltycodes\fi}
+\permanent\protected\def\updateparagraphdemerits {\ifhmode\snapshotpar\paragraphdemeritcodes\fi}
+\permanent\protected\def\updateparagraphshapes {\ifhmode\snapshotpar\paragraphshapecodes \fi}
+\permanent\protected\def\updateparagraphlines {\ifhmode\snapshotpar\paragraphlinecodes \fi}
+
+% so far
+
+\let\spac_paragraph_freeze\relax
+
+\permanent\protected\def\setparagraphfreezing {\enforced\let\spac_paragraph_freeze\freezeparagraphproperties}
+\permanent\protected\def\forgetparagraphfreezing{\enforced\let\spac_paragraph_freeze\relax}
+
+\installcorenamespace {bparwrap}
+\installcorenamespace {eparwrap}
+\installcorenamespace {parwrapbefore}
+\installcorenamespace {parwrapafter}
+\installcorenamespace {parwrapcount}
+
+\let\spac_paragraph_wrap\relax
+
+\newcount\c_spac_paragraph_group_level
+
+\protected\def\spac_paragraph_update
+ {\c_spac_paragraph_group_level\currentgrouplevel\relax
+ \ifcsname\??bparwrap\the\c_spac_paragraph_group_level\endcsname
+ \the\lastnamedcs
+ \relax
+ \dontleavehmode % just in case
+ \wrapuppar{\the\csname\??eparwrap\the\c_spac_paragraph_group_level\endcsname\relax}%
+ \fi}
+
+\permanent\protected\def\registerparwrapper {\spac_register_par_wrapper\toksapp\tokspre}
+\permanent\protected\def\registerparwrapperreverse{\spac_register_par_wrapper\tokspre\toksapp}
+
+\protected\def\spac_paragraph_install
+ {\expandafter\newtoks\csname\??bparwrap\the\currentgrouplevel\endcsname
+ \expandafter\newtoks\csname\??eparwrap\the\currentgrouplevel\endcsname}
+
+\def\spac_paragraph_install_count#1%
+ {\expandafter\newcount\csname\??parwrapcount#1\endcsname}
+
+\def\spac_paragraph_install_pair#1#2#3%
+ {\expandafter\newtoks\csname\??parwrapbefore#3\endcsname
+ \expandafter\newtoks\csname\??parwrapafter #3\endcsname
+ #1\csname\??bparwrap\the\currentgrouplevel\endcsname{\the\csname\??parwrapbefore#3\endcsname}%
+ #2\csname\??eparwrap\the\currentgrouplevel\endcsname{\the\csname\??parwrapafter #3\endcsname}}
+
+\def\spac_register_par_wrapper_yes#1#2#3#4#5%
+ {\ifcsname\??bparwrap\the\currentgrouplevel\endcsname \else
+ \spac_paragraph_install
+ \fi
+ \ifcsname\??parwrapcount#3\endcsname \else
+ \spac_paragraph_install_count{#3}%
+ \fi
+ \ifcsname\??parwrapbefore#3\endcsname \else
+ \spac_paragraph_install_pair#1#2{#3}%
+ \fi
+ #1\csname\??parwrapbefore#3\endcsname{\advance\csname\??parwrapcount#3\endcsname\plusone\relax
+ \clf_setparwrapper{#3}#4}%
+ #2\csname\??parwrapafter #3\endcsname{#5}%
+ \clf_newparwrapper{#3}%
+ \enforced\let\spac_paragraph_wrap\spac_paragraph_update}
+
+\def\spac_register_par_wrapper_nop#1#2#3#4#5%
+ {\ifcsname\??parwrapcount#3\endcsname \else
+ \spac_paragraph_install_count{#3}%
+ \fi
+ \csname\??parwrapcount#3\endcsname\plusone
+ #4\wrapuppar{#5}}
+
+\protected\def\spac_register_par_wrapper
+ {\ifhmode
+ \expandafter\spac_register_par_wrapper_nop
+ \else
+ \expandafter\spac_register_par_wrapper_yes
+ \fi}
+
+\permanent\protected\def\forgetparwrapper
+ {\csname\??bparwrap\the\currentgrouplevel\endcsname\emptytoks
+ \csname\??eparwrap\the\currentgrouplevel\endcsname\emptytoks}
+
+\permanent\protected\def\unregisterparwrapper#1%
+ {\csname\??parwrapcount#1\endcsname\zerocount
+ \ifcsname\??parwrapbefore#1\endcsname
+ \lastnamedcs\emptytoks
+ \csname\??parwrapafter#1\endcsname\emptytoks
+ \fi}
+
+\permanent\def\directparwrapper#1#2%
+ {#1\wrapuppar{#2}}
+
+\permanent\protected\def\doifelseparwrapper#1%
+ {\unless\ifcsname\??parwrapcount#1\endcsname
+ \expandafter\secondoftwoarguments
+ \orelse\ifcase\lastnamedcs
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+% \getparwrapper % defined in lua
+% \lastparwrapper % defined in lua
+
+\permanent\protected\def\showparwrapperstate#1%
+ {\begingroup
+ \infofont ¶#1\hilo
+ {\smallinfofont\getparwrapper {#1}}%
+ {\smallinfofont\lastparwrapper{#1}}%
+ \endgroup}
+
+%appendtoks\updateparwrapperindeed\to\everypar
+%appendtoks\spac_paragraph_wrap \to\everypar
+%appendtoks\spac_paragraph_freeze \to\everypar
+
+\setparagraphfreezing
+
+\appendtoks\enforced\let\spac_paragraph_wrap\relax\to\everyforgetall
+
+%D In due time, the code below will be upgraded using the above mechanisms.
+
+%D The dreadful sequence \type {\bgroup} \unknown\ \type {\carryoverpar} \unknown\
+%D \type {\egroup} is needed when for instance sidefloats are used in combination
+%D with something that starts with a group. This is because otherwise the
+%D indentation as set (by the output routine) inside the group are forgotten
+%D afterwards. (I must not forget its existence).
+
+\mutable\let\currentparagraphproperties\empty % visible for tracing
+
+\permanent\def\carryoverpar#1% #1 can be \endgroup or \egroup or ... expandable !
+ {\normalexpanded
+ {\noexpand#1%
+ \hangindent\the\hangindent
+ \hangafter \the\hangafter
+ \parskip \the\parskip
+ \leftskip \the\leftskip
+ \rightskip \the\rightskip
+ \relax}}
+
+\permanent\protected\def\pushparagraphproperties
+ {\edef\currentparagraphproperties{\carryoverpar\relax}%
+ \pushmacro\currentparagraphproperties}
+
+\permanent\protected\def\popparagraphproperties
+ {\popmacro\currentparagraphproperties
+ \currentparagraphproperties}
+
+\permanent\protected\def\flushparagraphproperties
+ {\popmacro\currentparagraphproperties}
+
+%D Beware, changing this will break some code (like pos/backgrounds) but it has been
+%D changed anyway so let's see where things go wrong.
+
+\installcorenamespace{paragraphintro}
+
+\let\spac_paragraphs_flush_intro\relax % hook into everypar
+
+\newtoks\t_spac_paragraphs_intro_first
+\newtoks\t_spac_paragraphs_intro_next
+\newtoks\t_spac_paragraphs_intro_each
+
+\newconditional\c_spac_paragraphs_intro_first
+\newconditional\c_spac_paragraphs_intro_next
+\newconditional\c_spac_paragraphs_intro_each
+
+\let\spac_paragraphs_flush_intro\relax
+\let\spac_paragraphs_intro_step \relax
+
+\permanent\protected\tolerant\def\setupparagraphintro[#1]#*[#2]%
+ {\def\spac_paragraphs_intro_step##1%
+ {\csname\??paragraphintro\ifcsname\??paragraphintro##1\endcsname##1\fi\endcsname{#2}}%
+ \processcommacommand[#1]\spac_paragraphs_intro_step}
+
+\letcsname\??paragraphintro\empty\endcsname\gobbleoneargument
+
+\defcsname\??paragraphintro\v!reset\endcsname#1%
+ {\global\setfalse\c_spac_paragraphs_intro_first
+ \global\setfalse\c_spac_paragraphs_intro_next
+ \global\setfalse\c_spac_paragraphs_intro_each
+ \global\t_spac_paragraphs_intro_first\emptytoks
+ \global\t_spac_paragraphs_intro_next \emptytoks
+ \global\t_spac_paragraphs_intro_each \emptytoks
+ \glet\spac_paragraphs_flush_intro\relax}
+
+\defcsname\??paragraphintro\v!first\endcsname#1%
+ {\global\settrue\c_spac_paragraphs_intro_first
+ \gtoksapp\t_spac_paragraphs_intro_first{#1}%
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
+
+\defcsname\??paragraphintro\v!next\endcsname#1%
+ {\global\settrue\c_spac_paragraphs_intro_next
+ \gtoksapp\t_spac_paragraphs_intro_next{#1}%
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
+
+\defcsname\??paragraphintro\v!each\endcsname#1%
+ {\global\settrue\c_spac_paragraphs_intro_each
+ \gtoksapp\t_spac_paragraphs_intro_each{#1}%
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
+
+%D We can say:
+%D
+%D \starttyping
+%D \setupparagraphintro[first][\index{Knuth}]
+%D \stoptyping
+%D
+%D Maybe more convenient is:
+%D
+%D \starttyping
+%D \flushatparagraph{\index{Zapf}}
+%D \stoptyping
+%D
+%D \starttyping
+%D \setupparagraphintro[first][\hbox to 3.5em{\tt FIRST \hss}]
+%D \setupparagraphintro[first][\hbox to 3.5em{\tt TSRIF \hss}]
+%D \setupparagraphintro[next] [\hbox to 3.5em{\tt NEXT \hss}]
+%D \setupparagraphintro[next] [\hbox to 3.5em{\tt TXEN \hss}]
+%D \setupparagraphintro[each] [\hbox to 3.0em{\tt EACH \hss}]
+%D \setupparagraphintro[each] [\hbox to 3.0em{\tt HCEA \hss}]
+%D
+%D some paragraph \par
+%D some paragraph \par
+%D some paragraph \par
+%D some paragraph \par
+%D
+%D \setupparagraphintro[first][\hbox to 3.5em{\tt FIRST \hss}]
+%D \setupparagraphintro[first][\hbox to 3.5em{\tt TSRIF \hss}]
+%D
+%D some paragraph \par
+%D some paragraph \par
+%D
+%D \setupparagraphintro[reset]
+%D
+%D some paragraph \par
+%D \stoptyping
+
+\permanent\protected\def\flushatparagraph#1%
+ {\global\c_spac_paragraphs_intro_first\plusone
+ \gtoksapp\t_spac_paragraphs_intro_first{#1}%
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
+
+%D Here comes the flusher (we misuse the one level expansion of token registers to
+%D feed a nice stream into the paragraph.)
+
+\protected\def\spac_paragraphs_flush_intro_indeed % we make sure that the token lists expand directly
+ {\normalexpanded{% % after another so the first code is there twice
+ \ifconditional\c_spac_paragraphs_intro_each
+ \ifconditional\c_spac_paragraphs_intro_next
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_next
+ \else
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_each
+ \fi
+ \ifconditional\c_spac_paragraphs_intro_first
+ \global\setfalse\c_spac_paragraphs_intro_first
+ \global\t_spac_paragraphs_intro_first\emptytoks
+ \the\t_spac_paragraphs_intro_first
+ \fi
+ \the\t_spac_paragraphs_intro_each
+ \else
+ \ifconditional\c_spac_paragraphs_intro_next
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_next
+ \fi
+ \ifconditional\c_spac_paragraphs_intro_first
+ \global\setfalse\c_spac_paragraphs_intro_first
+ \global\t_spac_paragraphs_intro_first\emptytoks
+ \the\t_spac_paragraphs_intro_first
+ \fi
+ \fi}}
+
+\protected\def\spac_paragraphs_flush_intro_next
+ {\normalexpanded{%
+ \global\setfalse\c_spac_paragraphs_intro_next
+ \global\t_spac_paragraphs_intro_next\emptytoks
+ \ifconditional\c_spac_paragraphs_intro_each
+ \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_each
+ \the\t_spac_paragraphs_intro_next
+ \the\t_spac_paragraphs_intro_each
+ \else
+ \glet\spac_paragraphs_flush_intro\relax
+ \the\t_spac_paragraphs_intro_next
+ \fi}}
+
+\protected\def\spac_paragraphs_flush_intro_each
+ {\the\t_spac_paragraphs_intro_each}
+
+%D \macros
+%D {flushatnextpar}
+%D
+%D This macro collects data that will be flushed at the next paragraph. By using
+%D this macro you can avoid interfering nodes (writes, etc).
+
+\aliased\let\flushpostponednodedata\relax % hook into everypar
+
+\newbox \b_spac_postponed_data
+%newcount\c_spac_postponed_data
+
+% \installcorenamespace {postponednodesstack}
+%
+% \initializeboxstack\??postponednodesstack
+%
+% \protected\def\pushpostponednodedata
+% {\global\advance\c_spac_postponed_data\plusone
+% \savebox\??postponednodesstack{\the\c_spac_postponed_data}{\box\b_spac_postponed_data}}
+%
+% \protected\def\poppostponednodedata
+% {\global\setbox\b_spac_postponed_data\hbox{\foundbox\??postponednodesstack{\the\c_spac_postponed_data}}%
+% \global\advance\c_spac_postponed_data\minusone
+% \ifvoid\b_spac_postponed_data\else
+% \enforced\glet\flushpostponednodedata\spac_postponed_data_flush
+% \fi}
+
+\newtoks\everyflushatnextpar
+
+\permanent\protected\def\pushpostponednodedata
+ {\globalpushbox\b_spac_postponed_data}
+
+\permanent\protected\def\poppostponednodedata
+ {\globalpopbox\b_spac_postponed_data
+ \ifvoid\b_spac_postponed_data\else
+ \enforced\glet\flushpostponednodedata\spac_postponed_data_flush
+ \fi}
+
+\permanent\protected\def\flushatnextpar
+ {\begingroup
+ \the\everyflushatnextpar
+ \enforced\glet\flushpostponednodedata\spac_postponed_data_flush
+ \dowithnextboxcs\spac_postponed_data_finish\hpack}
+
+% \def\spac_postponed_data_finish
+% {\global\setbox\b_spac_postponed_data\hpack % to\zeropoint
+% {\box\b_spac_postponed_data\box\nextbox}%
+% \endgroup}
+%
+% This is better when used with protrusion which does not like too deeply nested
+% boxes:
+%
+% \def\spac_postponed_data_finish
+% {\dontcomplain
+% \global\setbox\b_spac_postponed_data\hpack to \zeropoint
+% {\unhbox\b_spac_postponed_data\unhbox\nextbox}%
+% \endgroup}
+%
+% We could do this if there is content with widths ... not that is should be used
+% that way, but this way we don't overflow:
+
+\def\spac_postponed_data_finish
+ {\dontcomplain
+ \global\setbox\b_spac_postponed_data\hpack % to \zeropoint
+ {\unhbox\b_spac_postponed_data
+ \scratchdimen\wd\nextbox
+ \unhbox\nextbox
+ \ifcase\scratchdimen\else\kern-\b_spac_postponed_data\fi}%
+ \endgroup}
+
+\def\spac_postponed_data_flush
+ {%\iftrialtypesetting \else
+ \ifvoid\b_spac_postponed_data\else
+ \hpack{\smashedbox\b_spac_postponed_data}% \box\b_spac_postponed_data
+ \fi
+ \enforced\glet\flushpostponednodedata\relax
+ }%\fi}
+
+\permanent\protected\def\doflushatpar % might be renamed
+ {\ifvmode
+ \expandafter\flushatnextpar
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-prf.lmt b/Master/texmf-dist/tex/context/base/mkxl/spac-prf.lmt
new file mode 100644
index 00000000000..2223c77302d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-prf.lmt
@@ -0,0 +1,949 @@
+ if not modules then modules = { } end modules ['spac-prf'] = {
+ version = 1.001,
+ comment = "companion to spac-prf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is a playground, a byproduct of some experiments in a project where
+-- we needed something like this where it works ok, but nevertheless it's
+-- still experimental code. It is very likely to change (or extended).
+
+local unpack, rawget = unpack, rawget
+
+local formatters = string.formatters
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+local listcodes = nodes.listcodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+local penalty_code = nodecodes.penalty
+local glue_code = nodecodes.glue
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local unset_code = nodecodes.unset
+local math_code = nodecodes.math
+local rule_code = nodecodes.rule
+local marginkern_code = nodecodes.marginkern
+
+local leaders_code = gluecodes.leaders
+local lineskip_code = gluecodes.lineskip
+local baselineskip_code = gluecodes.baselineskip
+
+local linelist_code = listcodes.line
+
+local texlists = tex.lists
+local settexattribute = tex.setattribute
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local tonode = nuts.tonode
+
+local getreplace = nuts.getreplace
+local getattr = nuts.getattr
+local getid = nuts.getid
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local gettexbox = nuts.getbox
+local getwhd = nuts.getwhd
+local getglue = nuts.getglue
+local getkern = nuts.getkern
+local getshift = nuts.getshift
+local getwidth = nuts.getwidth
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+local getboxglue = nuts.getboxglue
+
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+local setattr = nuts.setattr
+local setwhd = nuts.setwhd
+local setshift = nuts.setshift
+local setwidth = nuts.setwidth
+local setheight = nuts.setheight
+local setdepth = nuts.setdepth
+
+local properties = nodes.properties.data
+local setprop = nuts.setprop
+local getprop = nuts.getprop
+local theprop = nuts.theprop
+
+local floor = math.floor
+local ceiling = math.ceil
+
+local new_rule = nuts.pool.rule
+local new_glue = nuts.pool.glue
+local new_kern = nuts.pool.kern
+local hpack_nodes = nuts.hpack
+local find_node_tail = nuts.tail
+local setglue = nuts.setglue
+
+local a_visual = attributes.private("visual")
+local a_snapmethod = attributes.private("snapmethod")
+local a_profilemethod = attributes.private("profilemethod")
+----- a_specialcontent = attributes.private("specialcontent")
+
+local variables = interfaces.variables
+local v_none = variables.none
+local v_fixed = variables.fixed
+local v_strict = variables.strict
+
+local setcolor = nodes.tracers.colors.set
+local settransparency = nodes.tracers.transparencies.set
+
+local enableaction = nodes.tasks.enableaction
+
+local profiling = { }
+builders.profiling = profiling
+
+local report = logs.reporter("profiling")
+
+local show_profile = false trackers.register("profiling.show", function(v) show_profile = v end)
+local trace_profile = false trackers.register("profiling.trace",function(v) trace_profile = v end)
+
+local function getprofile(line,step)
+
+ -- only l2r
+ -- no hz yet
+
+ local line = tonut(line)
+ local current = getlist(line)
+
+ if not current then
+ return
+ end
+
+ local glue_set, glue_order, glue_sign = getboxglue(line)
+
+ local heights = { }
+ local depths = { }
+ local width = 0
+ local position = 0
+ local step = step or 65536 -- * 2 -- 2pt
+ local margin = step / 4
+ local min = 0
+ local max = ceiling(getwidth(line)/step) + 1
+ local wd = 0
+ local ht = 0
+ local dp = 0
+
+ for i=min,max do
+ heights[i] = 0
+ depths [i] = 0
+ end
+
+ -- remember p
+
+ local function progress()
+ position = width
+ width = position + wd
+ p = floor((position - margin)/step + 0.5)
+ w = floor((width + margin)/step - 0.5)
+ if p < 0 then
+ p = 0
+ end
+ if w < 0 then
+ w = 0
+ end
+ if p > w then
+ w, p = p, w
+ end
+ if w > max then
+ for i=max+1,w+1 do
+ heights[i] = 0
+ depths [i] = 0
+ end
+ max = w
+ end
+ for i=p,w do
+ if ht > heights[i] then
+ heights[i] = ht
+ end
+ if dp > depths[i] then
+ depths[i] = dp
+ end
+ end
+ end
+
+ local function process(current) -- called nested in disc replace
+ while current do
+ local id = getid(current)
+ if id == glyph_code then
+ wd, ht, dp = getwhd(current)
+ progress()
+ elseif id == kern_code then
+ wd = getkern(current)
+ ht = 0
+ dp = 0
+ progress()
+ elseif id == disc_code then
+ local replace = getreplace(current)
+ if replace then
+ process(replace)
+ end
+ elseif id == glue_code then
+ local width, stretch, shrink, stretch_order, shrink_order = getglue(current)
+ if glue_sign == 1 then
+ if stretch_order == glue_order then
+ wd = width + stretch * glue_set
+ else
+ wd = width
+ end
+ elseif glue_sign == 2 then
+ if shrink_order == glue_order then
+ wd = width - shrink * glue_set
+ else
+ wd = width
+ end
+ else
+ wd = width
+ end
+ if getsubtype(current) >= leaders_code then
+ local leader = getleader(current)
+ local w
+ w, ht, dp = getwhd(leader) -- can become getwhd(current) after 1.003
+ else
+ ht = 0
+ dp = 0
+ end
+ progress()
+ elseif id == hlist_code then
+ -- we could do a nested check .. but then we need to push / pop glue
+ local shift = getshift(current)
+ local w, h, d = getwhd(current)
+ -- if getattr(current,a_specialcontent) then
+ if getprop(current,"specialcontent") then
+ -- like a margin note, maybe check for wd
+ wd = w
+ ht = 0
+ dp = 0
+ else
+ wd = w
+ ht = h - shift
+ dp = d + shift
+ end
+ progress()
+ elseif id == vlist_code or id == unset_code then
+ local shift = getshift(current) -- todo
+ wd, ht, dp = getwhd(current)
+ progress()
+ elseif id == rule_code then
+ wd, ht, dp = getwhd(current)
+ progress()
+ elseif id == math_code then
+ wd = getkern(current) + getwidth(current) -- surround
+ ht = 0
+ dp = 0
+ progress()
+ elseif id == marginkern_code then
+ -- not in lmtx
+ wd = getwidth(current)
+ ht = 0
+ dp = 0
+ progress()
+ else
+-- print(nodecodes[id])
+ end
+ current = getnext(current)
+ end
+ end
+
+ process(current)
+
+ return {
+ heights = heights,
+ depths = depths,
+ min = min, -- not needed
+ max = max,
+ step = step,
+ }
+
+end
+
+profiling.get = getprofile
+
+local function getpagelist()
+ local pagehead = texlists.page_head
+ if pagehead then
+ pagehead = tonut(texlists.page_head)
+ pagetail = find_node_tail(pagehead)
+ else
+ pagetail = nil
+ end
+ return pagehead, pagetail
+end
+
+local function setprofile(n,step)
+ local p = rawget(properties,n)
+ if p then
+ local pp = p.profile
+ if not pp then
+ pp = getprofile(n,step)
+ p.profile = pp
+ end
+ return pp
+ else
+ local pp = getprofile(n,step)
+ properties[n] = { profile = pp }
+ return pp
+ end
+end
+
+local function hasprofile(n)
+ local p = rawget(properties,n)
+ if p then
+ return p.profile
+ end
+end
+
+local function addstring(height,depth)
+ local typesetters = nuts.typesetters
+ local hashes = fonts.hashes
+ local infofont = fonts.infofont()
+ local emwidth = hashes.emwidths [infofont]
+ local exheight = hashes.exheights[infofont]
+ local httext = height
+ local dptext = depth
+ local httext = typesetters.tohpack(height,infofont)
+ local dptext = typesetters.tohpack(depth,infofont)
+ setshift(httext,- 1.2 * exheight)
+ setshift(dptext, 0.6 * exheight)
+ local text = hpack_nodes(setlink(
+ new_kern(-getwidth(httext)-emwidth),
+ httext,
+ new_kern(-getwidth(dptext)),
+ dptext
+ ))
+ setwhd(text,0,0,0)
+ return text
+end
+
+local function addprofile(node,profile,step)
+
+ local line = tonut(node)
+
+ if not profile then
+ profile = setprofile(line,step)
+ end
+
+ if not profile then
+ report("some error")
+ return node
+ end
+
+ if profile.shown then
+ return node
+ end
+
+ local list = getlist(line)
+ profile.shown = true
+
+ local heights = profile.heights
+ local depths = profile.depths
+ local step = profile.step
+
+ local head = nil
+ local tail = nil
+
+ local lastht = 0
+ local lastdp = 0
+ local lastwd = 0
+
+ local visual = "f:s:t" -- this can change !
+
+ local function progress()
+ if lastwd == 0 then
+ return
+ end
+ local what = nil
+ if lastht == 0 and lastdp == 0 then
+ what = new_kern(lastwd)
+ else
+ what = new_rule(lastwd,lastht,lastdp)
+ setcolor(what,visual)
+ settransparency(what,visual)
+ end
+ if tail then
+ setlink(tail,what)
+ else
+ head = what
+ end
+ tail = what
+ end
+
+-- inspect(profile)
+
+ for i=profile.min,profile.max do
+ local ht = heights[i]
+ local dp = depths[i]
+ if ht ~= lastht or dp ~= lastdp and lastwd > 0 then
+ progress()
+ lastht = ht
+ lastdp = dp
+ lastwd = step
+ else
+ lastwd = lastwd + step
+ end
+ end
+ if lastwd > 0 then
+ progress()
+ end
+
+ local rule = hpack_nodes(head)
+
+ setwhd(rule,0,0,0)
+
+ -- if texttoo then
+ --
+ -- local text = addstring(
+ -- formatters["%0.4f"](getheight(rule)/65536),
+ -- formatters["%0.4f"](getdepth(rule) /65536)
+ -- )
+ --
+ -- setlink(text,rule)
+ --
+ -- rule = text
+ --
+ -- end
+
+ setlink(rule,list)
+ setlist(line,rule)
+
+end
+
+profiling.add = addprofile
+
+local methods = { }
+
+local function getdelta(t_profile,b_profile)
+ local t_heights = t_profile.heights
+ local t_depths = t_profile.depths
+ local t_max = t_profile.max
+ local b_heights = b_profile.heights
+ local b_depths = b_profile.depths
+ local b_max = b_profile.max
+
+ local max = t_max
+ local delta = 0
+
+ if t_max > b_max then
+ for i=b_max+1,t_max do
+ b_depths [i] = 0
+ b_heights[i] = 0
+ end
+ max = t_max
+ elseif b_max > t_max then
+ for i=t_max+1,b_max do
+ t_depths [i] = 0
+ t_heights[i] = 0
+ end
+ max = b_max
+ end
+
+ for i=0,max do
+ local ht = b_heights[i]
+ local dp = t_depths[i]
+ local hd = ht + dp
+ if hd > delta then
+ delta = hd
+ end
+ end
+
+ return delta
+end
+
+-- local properties = theprop(bot)
+-- local unprofiled = properties.unprofiled
+-- if not unprofiled then -- experiment
+-- properties.unprofiled = {
+-- height = height,
+-- strutht = strutht,
+-- }
+-- end
+
+-- lineskip | lineskiplimit
+
+local function inject(top,bot,amount) -- todo: look at penalties
+ local glue = new_glue(amount)
+ --
+ setattr(glue,a_profilemethod,0)
+ setattr(glue,a_visual,getattr(top,a_visual))
+ --
+ setlink(top,glue,bot)
+end
+
+methods[v_none] = function()
+ return false
+end
+
+methods[v_strict] = function(top,bot,t_profile,b_profile,specification)
+
+ local top = tonut(top)
+ local bot = tonut(bot)
+
+ local strutht = specification.height or texdimen.strutht
+ local strutdp = specification.depth or texdimen.strutdp
+ local lineheight = strutht + strutdp
+
+ local depth = getdepth(top)
+ local height = getheight(bot)
+ local total = depth + height
+ local distance = specification.distance or 0
+ local delta = lineheight - total
+
+ -- there is enough room between the lines so we don't need
+ -- to add extra distance
+
+ if delta >= distance then
+ inject(top,bot,delta)
+ return true
+ end
+
+ local delta = getdelta(t_profile,b_profile)
+ local skip = delta - total + distance
+
+ -- we don't want to be too tight so we limit the skip and
+ -- make sure we have at least lineheight
+
+ inject(top,bot,skip)
+ return true
+
+end
+
+-- todo: also set ht/dp of first / last (but what is that)
+
+methods[v_fixed] = function(top,bot,t_profile,b_profile,specification)
+
+ local top = tonut(top)
+ local bot = tonut(bot)
+
+ local strutht = specification.height or texdimen.strutht
+ local strutdp = specification.depth or texdimen.strutdp
+ local lineheight = strutht + strutdp
+
+ local depth = getdepth(top)
+ local height = getheight(bot)
+ local total = depth + height
+ local distance = specification.distance or 0
+ local delta = lineheight - total
+
+ local snapmethod = getattr(top,a_snapmethod)
+
+ if snapmethod then
+
+ -- no distance (yet)
+
+ if delta < lineheight then
+ setdepth(top,strutdp)
+ setheight(bot,strutht)
+ return true
+ end
+
+ local delta = getdelta(t_profile,b_profile)
+
+ local dp = strutdp
+ while depth > lineheight - strutdp do
+ depth = depth - lineheight
+ dp = dp + lineheight
+ end
+ setdepth(top,dp)
+ local ht = strutht
+ while height > lineheight - strutht do
+ height = height - lineheight
+ ht = ht + lineheight
+ end
+ setheight(bot,ht)
+ local lines = floor(delta/lineheight)
+ if lines > 0 then
+ inject(top,bot,-lines * lineheight)
+ end
+
+ return true
+
+ end
+
+ if total < lineheight then
+ setdepth(top,strutdp)
+ setheight(bot,strutht)
+ return true
+ end
+
+ if depth < strutdp then
+ setdepth(top,strutdp)
+ total = total - depth + strutdp
+ end
+ if height < strutht then
+ setheight(bot,strutht)
+ total = total - height + strutht
+ end
+
+ local delta = getdelta(t_profile,b_profile)
+
+ local target = total - delta
+ local factor = specification.factor or 1
+ local step = lineheight / factor
+ local correction = 0
+ local nofsteps = 0
+ while correction < target - step - distance do -- a loop is more accurate, for now
+ correction = correction + step
+ nofsteps = nofsteps + 1
+ end
+
+ if trace_profile then
+ report("top line : %s %05i > %s",t_profile.shown and "+" or "-",top,nodes.toutf(getlist(top)))
+ report("bottom line : %s %05i > %s",b_profile.shown and "+" or "-",bot,nodes.toutf(getlist(bot)))
+ report(" depth : %p",depth)
+ report(" height : %p",height)
+ report(" total : %p",total)
+ report(" lineheight : %p",lineheight)
+ report(" delta : %p",delta)
+ report(" target : %p",target)
+ report(" factor : %i",factor)
+ report(" distance : %p",distance)
+ report(" step : %p",step)
+ report(" nofsteps : %i",nofsteps)
+ -- report(" max lines : %s",lines == 0 and "unset" or lines)
+ report(" correction : %p",correction)
+ end
+
+ inject(top,bot,-correction) -- we could mess with the present glue (if present)
+
+ return true -- remove interlineglue
+
+end
+
+function profiling.distance(top,bot,specification)
+ local step = specification.step
+ local method = specification.method
+ local ptop = getprofile(top,step)
+ local pbot = getprofile(bot,step)
+ local action = methods[method or v_strict] or methods[v_strict]
+ return action(top,bot,ptop,pbot,specification)
+end
+
+local specifications = { } -- todo: save these !
+
+function profiling.fixedprofile(current)
+ local a = getattr(current,a_profilemethod)
+ if a then
+ local s = specifications[a]
+ if s then
+ return s.method == v_fixed
+ end
+ end
+ return false
+end
+
+local function profilelist(line,mvl)
+
+ local current = line
+
+ local top = nil
+ local bot = nil
+
+ local t_profile = nil
+ local b_profile = nil
+
+ local specification = nil
+ local lastattr = nil
+ local method = nil
+ local action = nil
+
+ local distance = 0
+ local lastglue = nil
+
+ local pagehead = nil
+ local pagetail = nil
+
+ if mvl then
+
+ pagehead, pagetail = getpagelist()
+
+ if pagetail then
+ local current = pagetail
+ while current do
+ local id = getid(current)
+ if id == hlist_code then
+ local subtype = getsubtype(current)
+ if subtype == linelist_code then
+ t_profile = hasprofile(current)
+ if t_profile then
+ top = current
+ end
+ end
+ break
+ elseif id == glue_code then
+ local wd = getwidth(current)
+ if not wd or wd == 0 then
+ -- go on
+ else
+ break
+ end
+ elseif id == penalty_code then
+ -- ok
+ else
+ break
+ end
+ current = getnext(current)
+ end
+ end
+
+ end
+
+ while current do
+
+ local attr = getattr(current,a_profilemethod)
+
+ if attr then
+
+ if attr ~= lastattr then
+ specification = specifications[attr]
+ method = specification and specification.method
+ action = method and methods[method] or methods[v_strict]
+ lastattr = attr
+ end
+
+ local id = getid(current)
+
+ if id == hlist_code then -- check subtype
+ local subtype = getsubtype(current)
+ if subtype == linelist_code then
+ if top == current then
+ -- skip
+ bot = nil -- to be sure
+ elseif top then
+ bot = current
+ b_profile = setprofile(bot)
+ if show_profile then
+ addprofile(bot,b_profile)
+ end
+ if not t_profile.done then
+ if action then
+ local ok = action(top,bot,t_profile,b_profile,specification)
+ if ok and lastglue and distance ~= 0 then
+ setglue(lastglue)
+ end
+ end
+ t_profile.done = true
+ end
+ top = bot
+ bot = nil
+ t_profile = b_profile
+ b_profile = nil
+ distance = 0
+ else
+ top = current
+ t_profile = setprofile(top)
+ bot = nil
+ if show_profile then
+ addprofile(top,t_profile)
+ end
+ end
+ else
+ top = nil
+ bot = nil
+ end
+ elseif id == glue_code then
+ if top then
+ local subtype = getsubtype(current)
+ -- if subtype == lineskip_code or subtype == baselineskip_code then
+ local wd = getwidth(current)
+ if wd > 0 then
+ distance = wd
+ lastglue = current
+ elseif wd < 0 then
+ top = nil
+ bot = nil
+ else
+ -- ok
+ end
+ -- else
+ -- top = nil
+ -- bot = nil
+ -- end
+ else
+ top = nil
+ bot = nil
+ end
+ elseif id == penalty_code then
+ -- okay
+ else
+ top = nil
+ bot = nil
+ end
+ else
+ top = nil
+ bot = nil
+ end
+ current = getnext(current)
+ end
+ if top then
+ t_profile = setprofile(top)
+ if show_profile then
+ addprofile(top,t_profile)
+ end
+ end
+end
+
+profiling.list = profilelist
+
+local enabled = false
+
+function profiling.set(specification)
+ if not enabled then
+ enableaction("mvlbuilders", "builders.profiling.pagehandler")
+ -- too expensive so we expect that this happens explicitly, we keep for reference:
+ -- enableaction("vboxbuilders","builders.profiling.vboxhandler")
+ enabled = true
+ end
+ local n = #specifications + 1
+ specifications[n] = specification
+ settexattribute(a_profilemethod,n)
+end
+
+function profiling.profilebox(specification)
+ local boxnumber = specification.box
+ local current = getlist(gettexbox(boxnumber))
+ local top = nil
+ local bot = nil
+ local t_profile = nil
+ local b_profile = nil
+ local method = specification and specification.method
+ local action = method and methods[method] or methods[v_strict]
+ local lastglue = nil
+ local distance = 0
+ while current do
+ local id = getid(current)
+ if id == hlist_code then
+ local subtype = getsubtype(current)
+ if subtype == linelist_code then
+ if top then
+ bot = current
+ b_profile = setprofile(bot)
+ if show_profile then
+ addprofile(bot,b_profile)
+ end
+ if not t_profile.done then
+ if action then
+ local ok = action(top,bot,t_profile,b_profile,specification)
+ if ok and lastglue and distance ~= 0 then
+ setglue(lastglue)
+ end
+ end
+ t_profile.done = true
+ end
+ top = bot
+ t_profile = b_profile
+ b_profile = nil
+ distance = 0
+ else
+ top = current
+ t_profile = setprofile(top)
+ if show_profile then
+ addprofile(top,t_profile)
+ end
+ bot = nil
+ end
+ else
+ top = nil
+ bot = nil
+ end
+ elseif id == glue_code then
+ local subtype = getsubtype(current)
+ if subtype == lineskip_code or subtype == baselineskip_code then
+ if top then
+ local wd = getwidth(current)
+ if wd > 0 then
+ distance = wd
+ lastglue = current
+ elseif wd < 0 then
+ top = nil
+ bot = nil
+ else
+ -- ok
+ end
+ else
+ top = nil
+ bot = nil
+ end
+ else
+ top = nil
+ bot = nil
+ end
+ elseif id == penalty_code then
+ -- okay
+ else
+ top = nil
+ bot = nil
+ end
+ current = getnext(current)
+ end
+
+ if top then
+ t_profile = setprofile(top) -- not needed
+ if show_profile then
+ addprofile(top,t_profile)
+ end
+ end
+
+end
+
+-- local ignore = table.tohash {
+-- "split_keep",
+-- "split_off",
+-- -- "vbox",
+-- }
+--
+-- function profiling.vboxhandler(head,where)
+-- if head and not ignore[where] then
+-- if getnext(head) then
+-- profilelist(head)
+-- end
+-- end
+-- return head
+-- end
+
+function profiling.pagehandler(head)
+ if head then
+ profilelist(head,true)
+ end
+ return head
+end
+
+interfaces.implement {
+ name = "setprofile",
+ actions = profiling.set,
+ arguments = {
+ {
+ { "name" },
+ { "height", "dimen" },
+ { "depth", "dimen" },
+ { "distance", "dimen" },
+ { "factor", "integer" },
+ { "lines", "integer" },
+ { "method" }
+ }
+ }
+}
+
+interfaces.implement {
+ name = "profilebox",
+ actions = profiling.profilebox,
+ arguments = {
+ {
+ { "box", "integer" },
+ { "height", "dimen" },
+ { "depth", "dimen" },
+ { "distance", "dimen" },
+ { "factor", "integer" },
+ { "lines", "integer" },
+ { "method" }
+ }
+ }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-prf.mklx b/Master/texmf-dist/tex/context/base/mkxl/spac-prf.mklx
new file mode 100644
index 00000000000..6b4a966f2af
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-prf.mklx
@@ -0,0 +1,113 @@
+%D \module
+%D [ file=spac-prf,
+%D version=2015.11.16, % moved from test module mathplus
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Profiling,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Profiling}
+
+%D This is experimental code that we need for a project. The interface and details
+%D can change. I moved it into the core because that way we can use it on servers.
+
+% framed: first reflow then profile
+
+\unprotect
+
+\registerctxluafile{spac-prf}{autosuffix}
+
+\definesystemattribute[profilemethod][public]
+
+\installcorenamespace {profile}
+\installcorenamespace {profiles}
+
+\installcommandhandler \??profile {profile} \??profile
+
+\setupprofile
+ [\c!height=\strutht,
+ \c!depth=\strutdp,
+ \c!distance=\normallineskip,
+ \c!lines=\zerocount,
+ \c!factor=\plusone]
+
+\defineprofile
+ [\v!strict]
+ [\c!method=\v!strict]
+
+\defineprofile
+ [\v!fixed]
+ [\c!method=\v!fixed]
+
+\defineprofile
+ [\v!none]
+ [\c!method=\v!none,
+ \c!height=\zeropoint,
+ \c!depth=\zeropoint,
+ \c!distance=\zeropoint,
+ \c!factor=\plusone]
+
+\defineprofile[halffixed] [\v!fixed][\c!factor=\plustwo]
+\defineprofile[quarterfixed][\v!fixed][\c!factor=\plusfour]
+\defineprofile[eightsfixed] [\v!fixed][\c!factor=\pluseight]
+
+\permanent\protected\def\setprofile[#profile]%
+ {\edef\currentprofile{#profile}%
+ \clf_setprofile
+ name {\currentprofile}%
+ height \dimexpr\profileparameter\c!height\relax
+ depth \dimexpr\profileparameter\c!depth\relax
+ distance \dimexpr\profileparameter\c!distance\relax
+ factor \numexpr\profileparameter\c!factor\relax
+ lines \numexpr\profileparameter\c!lines\relax
+ method {\profileparameter\c!method}%
+ \relax}
+
+\permanent\protected\def\resetprofile
+ {\c_attr_profilemethod\attributeunsetvalue}
+
+\permanent\protected\def\useprofileparameter#getparameter%
+ {\edef\m_spac_profile_asked{#getparameter\c!profile}%
+ \ifempty\m_spac_profile_asked
+ \resetprofile
+ \else
+ \setprofile[\m_spac_profile_asked]%
+ \fi}
+
+\permanent\tolerant\protected\def\profiledbox[#profile]#spacer[#settings]%
+ {\vbox\bgroup
+ \ifparameter#settings\or
+ \edef\currentprofile{#profile}%
+ \setupcurrentprofile[#settings]%
+ \orelse\ifhastok={#profile}%
+ \let\currentprofile\v!none
+ \setupcurrentprofile[#profile]%
+ \else
+ \edef\currentprofile{#profile}%
+ \fi
+ \dowithnextbox
+ {\profilegivenbox\currentprofile\nextbox
+ \unvbox\nextbox
+ \egroup}%
+ \vbox}
+
+\permanent\protected\def\profilegivenbox#profile#box%
+ {\begingroup
+ \edef\currentprofile{#profile}%
+ \clf_profilebox
+ box \numexpr#box\relax
+ height \dimexpr\profileparameter\c!height\relax
+ depth \dimexpr\profileparameter\c!depth\relax
+ distance \dimexpr\profileparameter\c!distance\relax
+ factor \numexpr\profileparameter\c!factor\relax
+ lines \numexpr\profileparameter\c!lines\relax
+ method {\profileparameter\c!method}%
+ \relax
+ \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-ver.lmt b/Master/texmf-dist/tex/context/base/mkxl/spac-ver.lmt
new file mode 100644
index 00000000000..2d4aace2437
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-ver.lmt
@@ -0,0 +1,2516 @@
+if not modules then modules = { } end modules ['spac-ver'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to spac-ver.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- we also need to call the spacer for inserts!
+
+-- somehow lists still don't always have proper prev nodes so i need to
+-- check all of the luatex code some day .. maybe i should replece the
+-- whole mvl handler by lua code .. why not
+
+-- todo: use lua nodes with lua data (>0.79)
+-- see ** can go when 0.79
+
+-- needs to be redone, too many calls and tests now ... still within some
+-- luatex limitations
+
+-- this code dates from the beginning and is kind of experimental; it
+-- will be optimized and improved soon .. it's way too complex now but
+-- dates from less possibilities
+--
+-- the collapser will be redone with user nodes; also, we might get make
+-- parskip into an attribute and appy it explicitly thereby getting rid
+-- of automated injections; eventually i want to get rid of the currently
+-- still needed tex -> lua -> tex > lua chain (needed because we can have
+-- expandable settings at the tex end
+
+-- todo: strip baselineskip around display math
+
+local next, type, tonumber = next, type, tonumber
+local gmatch, concat = string.gmatch, table.concat
+local ceil, floor, abs = math.ceil, math.floor, math.abs
+local lpegmatch = lpeg.match
+local unpack = unpack or table.unpack
+local allocate = utilities.storage.allocate
+local todimen = string.todimen
+local formatters = string.formatters
+
+local nodes = nodes
+local trackers = trackers
+local attributes = attributes
+local context = context
+local tex = tex
+
+local texlists = tex.lists
+local texget = tex.get
+local texgetcount = tex.getcount
+local texgetdimen = tex.getdimen
+local texgetglue = tex.getglue
+local texset = tex.set
+local texsetdimen = tex.setdimen
+local texsetcount = tex.setcount
+local texnest = tex.nest
+local texgetbox = tex.getbox
+
+local buildpage = tex.triggerbuildpage
+
+local variables = interfaces.variables
+local implement = interfaces.implement
+
+local v_local = variables["local"]
+local v_global = variables["global"]
+local v_box = variables.box
+----- v_page = variables.page -- reserved for future use
+local v_split = variables.split
+local v_min = variables.min
+local v_max = variables.max
+local v_none = variables.none
+local v_line = variables.line
+local v_noheight = variables.noheight
+local v_nodepth = variables.nodepth
+local v_line = variables.line
+local v_halfline = variables.halfline
+local v_line_m = "-" .. v_line
+local v_halfline_m = "-" .. v_halfline
+local v_first = variables.first
+local v_last = variables.last
+local v_top = variables.top
+local v_bottom = variables.bottom
+local v_minheight = variables.minheight
+local v_maxheight = variables.maxheight
+local v_mindepth = variables.mindepth
+local v_maxdepth = variables.maxdepth
+local v_offset = variables.offset
+local v_strut = variables.strut
+
+local v_hfraction = variables.hfraction
+local v_dfraction = variables.dfraction
+local v_bfraction = variables.bfraction
+local v_tlines = variables.tlines
+local v_blines = variables.blines
+
+-- vertical space handler
+
+local trace_vbox_vspacing = false trackers.register("vspacing.vbox", function(v) trace_vbox_vspacing = v end)
+local trace_page_vspacing = false trackers.register("vspacing.page", function(v) trace_page_vspacing = v end)
+local trace_page_builder = false trackers.register("builders.page", function(v) trace_page_builder = v end)
+local trace_collect_vspacing = false trackers.register("vspacing.collect", function(v) trace_collect_vspacing = v end)
+local trace_vspacing = false trackers.register("vspacing.spacing", function(v) trace_vspacing = v end)
+local trace_vsnapping = false trackers.register("vspacing.snapping", function(v) trace_vsnapping = v end)
+local trace_specials = false trackers.register("vspacing.specials", function(v) trace_specials = v end)
+
+local remove_math_skips = true directives.register("vspacing.removemathskips", function(v) remnove_math_skips = v end)
+
+local report_vspacing = logs.reporter("vspacing","spacing")
+local report_collapser = logs.reporter("vspacing","collapsing")
+local report_snapper = logs.reporter("vspacing","snapping")
+local report_specials = logs.reporter("vspacing","specials")
+
+local a_skipcategory = attributes.private('skipcategory')
+local a_skippenalty = attributes.private('skippenalty')
+local a_skiporder = attributes.private('skiporder')
+local a_snapmethod = attributes.private('snapmethod')
+local a_snapvbox = attributes.private('snapvbox')
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local setlink = nuts.setlink
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getattr = nuts.getattr
+local getattrs = nuts.getattrs
+local setattr = nuts.setattr
+local getsubtype = nuts.getsubtype
+local getbox = nuts.getbox
+local getwhd = nuts.getwhd
+local setwhd = nuts.setwhd
+local getprop = nuts.getprop
+local setprop = nuts.setprop
+local getglue = nuts.getglue
+local setglue = nuts.setglue
+local getkern = nuts.getkern
+local getpenalty = nuts.getpenalty
+local setshift = nuts.setshift
+local setwidth = nuts.setwidth
+local getwidth = nuts.getwidth
+local setheight = nuts.setheight
+local getheight = nuts.getheight
+local setdepth = nuts.setdepth
+local getdepth = nuts.getdepth
+local setnext = nuts.setnext
+
+local find_node_tail = nuts.tail
+local flush_node = nuts.flush_node
+local remove_node = nuts.remove
+local count_nodes = nuts.countall
+local hpack_node = nuts.hpack
+local vpack_node = nuts.vpack
+local start_of_par = nuts.start_of_par
+
+local write_node = nuts.write
+
+local nextnode = nuts.traversers.node
+local nexthlist = nuts.traversers.hlist
+
+local nodereference = nuts.reference
+
+local theprop = nuts.theprop
+
+local listtoutf = nodes.listtoutf
+local nodeidstostring = nodes.idstostring
+
+local nodepool = nuts.pool
+
+local new_penalty = nodepool.penalty
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+local new_rule = nodepool.rule
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+----- penaltycodes = nodes.penaltycodes
+----- listcodes = nodes.listcodes
+
+local penalty_code = nodecodes.penalty
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local rule_code = nodecodes.rule
+local par_code = nodecodes.par
+
+local userskip_code = gluecodes.userskip
+local lineskip_code = gluecodes.lineskip
+local baselineskip_code = gluecodes.baselineskip
+local parskip_code = gluecodes.parskip
+local topskip_code = gluecodes.topskip
+local splittopskip_code = gluecodes.splittopskip
+
+local linelist_code = nodes.listcodes.line
+
+local properties = nodes.properties.data
+
+local vspacing = builders.vspacing or { }
+builders.vspacing = vspacing
+
+local vspacingdata = vspacing.data or { }
+vspacing.data = vspacingdata
+
+local snapmethods = vspacingdata.snapmethods or { }
+vspacingdata.snapmethods = snapmethods
+
+storage.register("builders/vspacing/data/snapmethods", snapmethods, "builders.vspacing.data.snapmethods")
+
+do
+
+ local default = {
+ [v_maxheight] = true,
+ [v_maxdepth] = true,
+ [v_strut] = true,
+ [v_hfraction] = 1,
+ [v_dfraction] = 1,
+ [v_bfraction] = 0.25,
+ }
+
+ local fractions = {
+ [v_minheight] = v_hfraction, [v_maxheight] = v_hfraction,
+ [v_mindepth] = v_dfraction, [v_maxdepth] = v_dfraction,
+ [v_box] = v_bfraction,
+ [v_top] = v_tlines, [v_bottom] = v_blines,
+ }
+
+ local values = {
+ offset = "offset"
+ }
+
+ local colonsplitter = lpeg.splitat(":")
+
+ local function listtohash(str)
+ local t = { }
+ for s in gmatch(str,"[^, ]+") do
+ local key, detail = lpegmatch(colonsplitter,s)
+ local v = variables[key]
+ if v then
+ t[v] = true
+ if detail then
+ local k = fractions[key]
+ if k then
+ detail = tonumber("0" .. detail)
+ if detail then
+ t[k] = detail
+ end
+ else
+ k = values[key]
+ if k then
+ detail = todimen(detail)
+ if detail then
+ t[k] = detail
+ end
+ end
+ end
+ end
+ else
+ detail = tonumber("0" .. key)
+ if detail then
+ t[v_hfraction] = detail
+ t[v_dfraction] = detail
+ end
+ end
+ end
+ if next(t) then
+ t[v_hfraction] = t[v_hfraction] or 1
+ t[v_dfraction] = t[v_dfraction] or 1
+ return t
+ else
+ return default
+ end
+ end
+
+ function vspacing.definesnapmethod(name,method)
+ local n = #snapmethods + 1
+ local t = listtohash(method)
+ snapmethods[n] = t
+ t.name = name -- not interfaced
+ t.specification = method -- not interfaced
+ context(n)
+ end
+
+end
+
+local function validvbox(parentid,list)
+ if parentid == hlist_code then
+ local id = getid(list)
+ if id == par_code and start_of_par(list) then
+ list = getnext(list)
+ if not next then
+ return nil
+ end
+ end
+ local done = nil
+ for n, id in nextnode, list do
+ if id == vlist_code or id == hlist_code then
+ if done then
+ return nil
+ else
+ done = n
+ end
+ elseif id == glue_code or id == penalty_code then
+ -- go on
+ else
+ return nil -- whatever
+ end
+ end
+ if done then
+ local id = getid(done)
+ if id == hlist_code then
+ return validvbox(id,getlist(done))
+ end
+ end
+ return done -- only one vbox
+ end
+end
+
+local function already_done(parentid,list,a_snapmethod) -- todo: done when only boxes and all snapped
+ -- problem: any snapped vbox ends up in a line
+ if list and parentid == hlist_code then
+ local id = getid(list)
+ if id == par_code and start_of_par(list) then
+ list = getnext(list)
+ if not list then
+ return false
+ end
+ end
+ for n, id in nextnode, list do
+ if id == hlist_code or id == vlist_code then
+ -- local a = getattr(n,a_snapmethod)
+ -- if not a then
+ -- -- return true -- not snapped at all
+ -- elseif a == 0 then
+ -- return true -- already snapped
+ -- end
+ local p = getprop(n,"snapper")
+ if p then
+ return p
+ end
+ elseif id == glue_code or id == penalty_code then -- or id == kern_code then
+ -- go on
+ else
+ return false -- whatever
+ end
+ end
+ end
+ return false
+end
+
+-- quite tricky: ceil(-something) => -0
+
+local function ceiled(n)
+ if n < 0 or n < 0.01 then
+ return 0
+ else
+ return ceil(n)
+ end
+end
+
+local function floored(n)
+ if n < 0 or n < 0.01 then
+ return 0
+ else
+ return floor(n)
+ end
+end
+
+-- check variables.none etc
+
+local function fixedprofile(current)
+ local profiling = builders.profiling
+ return profiling and profiling.fixedprofile(current)
+end
+
+local function snap_hlist(where,current,method,height,depth) -- method[v_strut] is default
+ if fixedprofile(current) then
+ return
+ end
+ local list = getlist(current)
+ local t = trace_vsnapping and { }
+ if t then
+ t[#t+1] = formatters["list content: %s"](listtoutf(list))
+ t[#t+1] = formatters["snap method: %s"](method.name) -- not interfaced
+ t[#t+1] = formatters["specification: %s"](method.specification) -- not interfaced
+ end
+ local snapht, snapdp
+ if method[v_local] then
+ -- snapping is done immediately here
+ snapht = texgetdimen("bodyfontstrutheight")
+ snapdp = texgetdimen("bodyfontstrutdepth")
+ if t then
+ t[#t+1] = formatters["local: snapht %p snapdp %p"](snapht,snapdp)
+ end
+ elseif method[v_global] then
+ snapht = texgetdimen("globalbodyfontstrutheight")
+ snapdp = texgetdimen("globalbodyfontstrutdepth")
+ if t then
+ t[#t+1] = formatters["global: snapht %p snapdp %p"](snapht,snapdp)
+ end
+ else
+ -- maybe autolocal
+ -- snapping might happen later in the otr
+ snapht = texgetdimen("globalbodyfontstrutheight")
+ snapdp = texgetdimen("globalbodyfontstrutdepth")
+ local lsnapht = texgetdimen("bodyfontstrutheight")
+ local lsnapdp = texgetdimen("bodyfontstrutdepth")
+ if snapht ~= lsnapht and snapdp ~= lsnapdp then
+ snapht, snapdp = lsnapht, lsnapdp
+ end
+ if t then
+ t[#t+1] = formatters["auto: snapht %p snapdp %p"](snapht,snapdp)
+ end
+ end
+
+ local wd, ht, dp = getwhd(current)
+
+ local h = (method[v_noheight] and 0) or height or ht
+ local d = (method[v_nodepth] and 0) or depth or dp
+ local hr = method[v_hfraction] or 1
+ local dr = method[v_dfraction] or 1
+ local br = method[v_bfraction] or 0
+ local ch = h
+ local cd = d
+ local tlines = method[v_tlines] or 1
+ local blines = method[v_blines] or 1
+ local done = false
+ local plusht = snapht
+ local plusdp = snapdp
+ local snaphtdp = snapht + snapdp
+ local extra = 0
+
+ if t then
+ t[#t+1] = formatters["hlist: wd %p ht %p (used %p) dp %p (used %p)"](wd,ht,h,dp,d)
+ t[#t+1] = formatters["fractions: hfraction %s dfraction %s bfraction %s tlines %s blines %s"](hr,dr,br,tlines,blines)
+ end
+
+ if method[v_box] then
+ local br = 1 - br
+ if br < 0 then
+ br = 0
+ elseif br > 1 then
+ br = 1
+ end
+ local n = ceiled((h+d-br*snapht-br*snapdp)/snaphtdp)
+ local x = n * snaphtdp - h - d
+ plusht = h + x / 2
+ plusdp = d + x / 2
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_box,plusht,plusdp)
+ end
+ elseif method[v_max] then
+ local n = ceiled((h+d)/snaphtdp)
+ local x = n * snaphtdp - h - d
+ plusht = h + x / 2
+ plusdp = d + x / 2
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_max,plusht,plusdp)
+ end
+ elseif method[v_min] then
+ -- we catch a lone min
+ if method.specification ~= v_min then
+ local n = floored((h+d)/snaphtdp)
+ local x = n * snaphtdp - h - d
+ plusht = h + x / 2
+ plusdp = d + x / 2
+ if plusht < 0 then
+ plusht = 0
+ end
+ if plusdp < 0 then
+ plusdp = 0
+ end
+ end
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_min,plusht,plusdp)
+ end
+ elseif method[v_none] then
+ plusht, plusdp = 0, 0
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_none,0,0)
+ end
+ end
+ -- for now, we actually need to tag a box and then check at several points if something ended up
+ -- at the top of a page
+ if method[v_halfline] then -- extra halfline
+ extra = snaphtdp/2
+ plusht = plusht + extra
+ plusdp = plusdp + extra
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_halfline,plusht,plusdp)
+ end
+ end
+ if method[v_line] then -- extra line
+ extra = snaphtdp
+ plusht = plusht + extra
+ plusdp = plusdp + extra
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_line,plusht,plusdp)
+ end
+ end
+ if method[v_halfline_m] then -- extra halfline
+ extra = - snaphtdp/2
+ plusht = plusht + extra
+ plusdp = plusdp + extra
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_halfline_m,plusht,plusdp)
+ end
+ end
+ if method[v_line_m] then -- extra line
+ extra = - snaphtdp
+ plusht = plusht + extra
+ plusdp = plusdp + extra
+ if t then
+ t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_line_m,plusht,plusdp)
+ end
+ end
+ if method[v_first] then
+ local thebox = current
+ local id = getid(thebox)
+ if id == hlist_code then
+ thebox = validvbox(id,getlist(thebox))
+ id = thebox and getid(thebox)
+ end
+ if thebox and id == vlist_code then
+ local list = getlist(thebox)
+ local lw, lh, ld
+ for n in nexthlist, list do
+ lw, lh, ld = getwhd(n)
+ break
+ end
+ if lh then
+ local wd, ht, dp = getwhd(thebox)
+ if t then
+ t[#t+1] = formatters["first line: height %p depth %p"](lh,ld)
+ t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
+ end
+ local delta = h - lh
+ ch, cd = lh, delta + d
+ h, d = ch, cd
+ local shifted = hpack_node(getlist(current))
+ setshift(shifted,delta)
+ setlist(current,shifted)
+ done = true
+ if t then
+ t[#t+1] = formatters["first: height %p depth %p shift %p"](ch,cd,delta)
+ end
+ elseif t then
+ t[#t+1] = "first: not done, no content"
+ end
+ elseif t then
+ t[#t+1] = "first: not done, no vbox"
+ end
+ elseif method[v_last] then
+ local thebox = current
+ local id = getid(thebox)
+ if id == hlist_code then
+ thebox = validvbox(id,getlist(thebox))
+ id = thebox and getid(thebox)
+ end
+ if thebox and id == vlist_code then
+ local list = getlist(thebox)
+ local lw, lh, ld
+ for n in nexthlist, list do
+ lw, lh, ld = getwhd(n)
+ end
+ if lh then
+ local wd, ht, dp = getwhd(thebox)
+ if t then
+ t[#t+1] = formatters["last line: height %p depth %p" ](lh,ld)
+ t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
+ end
+ local delta = d - ld
+ cd, ch = ld, delta + h
+ h, d = ch, cd
+ local shifted = hpack_node(getlist(current))
+ setshift(shifted,delta)
+ setlist(current,shifted)
+ done = true
+ if t then
+ t[#t+1] = formatters["last: height %p depth %p shift %p"](ch,cd,delta)
+ end
+ elseif t then
+ t[#t+1] = "last: not done, no content"
+ end
+ elseif t then
+ t[#t+1] = "last: not done, no vbox"
+ end
+ end
+ if method[v_minheight] then
+ ch = floored((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
+ if t then
+ t[#t+1] = formatters["minheight: %p"](ch)
+ end
+ elseif method[v_maxheight] then
+ ch = ceiled((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
+ if t then
+ t[#t+1] = formatters["maxheight: %p"](ch)
+ end
+ else
+ ch = plusht
+ if t then
+ t[#t+1] = formatters["set height: %p"](ch)
+ end
+ end
+ if method[v_mindepth] then
+ cd = floored((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
+ if t then
+ t[#t+1] = formatters["mindepth: %p"](cd)
+ end
+ elseif method[v_maxdepth] then
+ cd = ceiled((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
+ if t then
+ t[#t+1] = formatters["maxdepth: %p"](cd)
+ end
+ else
+ cd = plusdp
+ if t then
+ t[#t+1] = formatters["set depth: %p"](cd)
+ end
+ end
+ if method[v_top] then
+ ch = ch + tlines * snaphtdp
+ if t then
+ t[#t+1] = formatters["top height: %p"](ch)
+ end
+ end
+ if method[v_bottom] then
+ cd = cd + blines * snaphtdp
+ if t then
+ t[#t+1] = formatters["bottom depth: %p"](cd)
+ end
+ end
+ local offset = method[v_offset]
+ if offset then
+ -- we need to set the attr
+ if t then
+ local wd, ht, dp = getwhd(current)
+ t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,wd,ht,dp)
+ end
+ local shifted = hpack_node(getlist(current))
+ setshift(shifted,offset)
+ setlist(current,shifted)
+ if t then
+ local wd, ht, dp = getwhd(current)
+ t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,wd,ht,dp)
+ end
+ setattr(shifted,a_snapmethod,0)
+ setattr(current,a_snapmethod,0)
+ end
+ if not height then
+ setheight(current,ch)
+ if t then
+ t[#t+1] = formatters["forced height: %p"](ch)
+ end
+ end
+ if not depth then
+ setdepth(current,cd)
+ if t then
+ t[#t+1] = formatters["forced depth: %p"](cd)
+ end
+ end
+ local lines = (ch+cd)/snaphtdp
+ if t then
+ local original = (h+d)/snaphtdp
+ local whatever = (ch+cd)/(texgetdimen("globalbodyfontstrutheight") + texgetdimen("globalbodyfontstrutdepth"))
+ t[#t+1] = formatters["final lines : %p -> %p (%p)"](original,lines,whatever)
+ t[#t+1] = formatters["final height: %p -> %p"](h,ch)
+ t[#t+1] = formatters["final depth : %p -> %p"](d,cd)
+ end
+-- todo:
+--
+-- if h < 0 or d < 0 then
+-- h = 0
+-- d = 0
+-- end
+ if t then
+ report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[getid(current)],t)
+ end
+ if not method[v_split] then
+ -- so extra will not be compensated at the top of a page
+ extra = 0
+ end
+ return h, d, ch, cd, lines, extra
+end
+
+local categories = {
+ [0] = "discard",
+ [1] = "largest",
+ [2] = "force",
+ [3] = "penalty",
+ [4] = "add",
+ [5] = "disable",
+ [6] = "nowhite",
+ [7] = "goback",
+ [8] = "packed",
+ [9] = "overlay",
+ [10] = "enable",
+ [11] = "notopskip",
+}
+
+categories = allocate(table.swapped(categories,categories))
+vspacing.categories = categories
+
+function vspacing.tocategories(str)
+ local t = { }
+ for s in gmatch(str,"[^, ]") do -- use lpeg instead
+ local n = tonumber(s)
+ if n then
+ t[categories[n]] = true
+ else
+ t[b] = true
+ end
+ end
+ return t
+end
+
+function vspacing.tocategory(str) -- can be optimized
+ if type(str) == "string" then
+ return set.tonumber(vspacing.tocategories(str))
+ else
+ return set.tonumber({ [categories[str]] = true })
+ end
+end
+
+vspacingdata.map = vspacingdata.map or { } -- allocate ?
+vspacingdata.skip = vspacingdata.skip or { } -- allocate ?
+
+storage.register("builders/vspacing/data/map", vspacingdata.map, "builders.vspacing.data.map")
+storage.register("builders/vspacing/data/skip", vspacingdata.skip, "builders.vspacing.data.skip")
+
+do
+
+ local P, C, R, S, Cc, Cs = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs
+
+ vspacing.fixed = false
+
+ local map = vspacingdata.map
+ local skip = vspacingdata.skip
+
+ local sign = S("+-")^0
+ local multiplier = C(sign * R("09")^1) * P("*")
+ local singlefier = Cs(sign * Cc(1))
+ local separator = S(", ")
+ local category = P(":") * C((1-separator)^1)
+ local keyword = C((1-category-separator)^1)
+ local splitter = (multiplier + Cc(1)) * keyword * (category + Cc(false))
+
+ local k_fixed = variables.fixed
+ local k_flexible = variables.flexible
+ local k_category = "category"
+ local k_penalty = "penalty"
+ local k_order = "order"
+
+ function vspacing.setmap(from,to)
+ map[from] = to
+ end
+
+ function vspacing.setskip(key,value,grid)
+ if value ~= "" then
+ if grid == "" then grid = value end
+ skip[key] = { value, grid }
+ end
+ end
+
+ local expandmacro = token.expand_macro
+ -- local runlocal = tex.runlocal
+ -- local setmacro = tokens.setters.macro
+ -- local settoks = tex.settoks
+ local toscaled = tex.toscaled
+
+ local setattrs = nuts.setattrs
+
+ local b_done = false
+ local b_packed = false
+
+ local b_amount = 0
+ local b_stretch = 0
+ local b_shrink = 0
+ local b_category = false
+ local b_penalty = false
+ local b_order = false
+ local b_fixed = false
+ local b_grid = false
+
+ local pattern = nil
+
+ local packed = categories.packed
+
+ local gluefactor = .25
+
+ local ctx_ignoreparskip = context.core.ignoreparskip
+
+ local function before()
+ b_amount = 0
+ b_stretch = 0
+ b_shrink = 0
+ b_category = 1
+ b_penalty = false
+ b_order = false
+ b_fixed = b_grid
+ end
+
+ local function after()
+ if b_fixed then
+ b_stretch = 0
+ b_shrink = 0
+ else
+ b_stretch = gluefactor * b_amount
+ b_shrink = gluefactor * b_amount
+ end
+ end
+
+ -- use a cache for predefined ones
+
+ local function inject()
+ local n = new_glue(b_amount,b_stretch,b_shrink)
+ setattrs(n,false,a_skipcategory,b_category,a_skippenalty,b_penalty,a_skiporder,b_order or 1)
+ write_node(n)
+ end
+
+ local function flush()
+ after()
+ if b_done then
+ inject()
+ b_done = false
+ end
+ before()
+ end
+
+ -- local cmd = token.create("vspacingfromtempstring")
+ -- local cmd = token.create("vspacingpredefinedvalue") -- not yet known
+
+ local function handler(multiplier, keyword, detail)
+ if not keyword then
+ report_vspacing("unknown directive %a",s)
+ else
+ local mk = map[keyword]
+ if mk then
+ lpegmatch(pattern,mk)
+ elseif keyword == k_fixed then
+ b_fixed = true
+ elseif keyword == k_flexible then
+ b_flexible = false
+ elseif keyword == k_category then
+ local category = tonumber(detail)
+ if category == packed then
+ b_packed = true
+ elseif category then
+ b_category = category
+ b_done = true
+ flush()
+ end
+ elseif keyword == k_order and detail then
+ local order = tonumber(detail)
+ if order then
+ b_order = order
+ end
+ elseif keyword == k_penalty and detail then
+ local penalty = tonumber(detail)
+ if penalty then
+ flush()
+ b_done = true
+ b_category = 3
+ b_penalty = penalty
+ flush()
+ end
+ else
+ local amount, stretch, shrink
+ multiplier = tonumber(multiplier) or 1
+ local sk = skip[keyword]
+ if sk then
+ -- multiplier, keyword
+ -- best, for now, todo: runlocal with arguments
+ expandmacro("vspacingpredefinedvalue",true,keyword)
+ -- expandmacro(cmd,true,keyword)
+ -- setmacro("tempstring",keyword)
+ -- runlocal(cmd)
+ -- nicest
+ -- runlocal(cache[keyword])
+ -- fast
+ -- settoks("scratchtoks",keyword)
+ -- runlocal("vspacingfromscratchtoks")
+ -- middleground
+ -- setmacro("tempstring",keyword)
+ -- runlocal(ctx_vspacingfromtempstring)
+ --
+ amount, stretch, shrink = texgetglue("scratchskip")
+ if not stretch then
+ stretch = 0
+ end
+ if not shrink then
+ shrink = 0
+ end
+ if stretch == 0 and shrink == 0 then
+ stretch = gluefactor * amount -- always unless grid
+ shrink = stretch -- always unless grid
+ end
+ else -- no check, todo: parse plus and minus
+ amount = toscaled(keyword)
+ stretch = gluefactor * amount -- always unless grid
+ shrink = stretch -- always unless grid
+ end
+ -- we look at fixed later
+ b_amount = b_amount + multiplier * amount
+ b_stretch = b_stretch + multiplier * stretch
+ b_shrink = b_shrink + multiplier * shrink
+ b_done = true
+ end
+ end
+ end
+
+ -- alternatively we can make a table and have a keyword -> split cache but this is probably
+ -- not really a bottleneck
+
+ local splitter = ((multiplier + singlefier) * keyword * (category + Cc(false))) / handler
+ pattern = (splitter + separator^1)^0
+
+ function vspacing.inject(grid,str)
+ if trace_vspacing then
+ -- ctx_pushlogger(report_vspacing)
+ end
+ b_done = false
+ b_packed = false
+ b_grid = grid
+ before()
+ lpegmatch(pattern,str)
+ after()
+ if b_done then
+ inject()
+ end
+ if b_packed then
+ ctx_ignoreparskip()
+ end
+ if trace_vspacing then
+ -- ctx_poplogger()
+ end
+ end
+
+ function vspacing.injectpenalty(penalty)
+ local n = new_glue()
+ setattrs(n,false,a_skipcategory,categories.penalty,a_skippenalty,penalty,a_skiporder,1)
+ write_node(n)
+ end
+
+ function vspacing.injectskip(amount)
+ local n = new_glue(amount)
+ setattrs(n,false,a_skipcategory,categories.largest,a_skippenalty,false,a_skiporder,1)
+ write_node(n)
+ end
+
+ function vspacing.injectdisable(amount)
+ local n = new_glue()
+ setattrs(n,false,a_skipcategory,categories.disable,a_skippenalty,false,a_skiporder,1)
+ write_node(n)
+ end
+
+end
+
+-- implementation
+
+-- alignment box begin_of_par vmode_par hmode_par insert penalty before_display after_display
+
+function vspacing.snapbox(n,how)
+ local sv = snapmethods[how]
+ if sv then
+ local box = getbox(n)
+ local list = getlist(box)
+ if list then
+ local s = getattr(list,a_snapmethod)
+ if s == 0 then
+ if trace_vsnapping then
+ -- report_snapper("box list not snapped, already done")
+ end
+ else
+ local wd, ht, dp = getwhd(box)
+ if false then -- todo: already_done
+ -- assume that the box is already snapped
+ if trace_vsnapping then
+ report_snapper("box list already snapped at (%p,%p): %s",
+ ht,dp,listtoutf(list))
+ end
+ else
+ local h, d, ch, cd, lines, extra = snap_hlist("box",box,sv,ht,dp)
+ setprop(box,"snapper",{
+ ht = h,
+ dp = d,
+ ch = ch,
+ cd = cd,
+ extra = extra,
+ current = current,
+ })
+ setwhd(box,wd,ch,cd)
+ if trace_vsnapping then
+ report_snapper("box list snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
+ h,d,ch,cd,sv.name,sv.specification,"direct",lines,listtoutf(list))
+ end
+ setattr(box,a_snapmethod,0) --
+ setattr(list,a_snapmethod,0) -- yes or no
+ end
+ end
+ end
+ end
+end
+
+-- I need to figure out how to deal with the prevdepth that crosses pages. In fact,
+-- prevdepth is often quite interfering (even over a next paragraph) so I need to
+-- figure out a trick. Maybe use something other than a rule. If we visualize we'll
+-- see the baselineskip in action:
+--
+-- \blank[force,5*big] { \baselineskip1cm xxxxxxxxx \par } \page
+-- \blank[force,5*big] { \baselineskip1cm xxxxxxxxx \par } \page
+-- \blank[force,5*big] { \baselineskip5cm xxxxxxxxx \par } \page
+
+-- We can register and copy the rule instead.
+
+do
+
+ local insert_node_after = nuts.insert_after
+ local insert_node_before = nuts.insert_before
+
+ local abovedisplayskip_code = gluecodes.abovedisplayskip
+ local belowdisplayskip_code = gluecodes.belowdisplayskip
+ local abovedisplayshortskip_code = gluecodes.abovedisplayshortskip
+ local belowdisplayshortskip_code = gluecodes.belowdisplayshortskip
+
+ local w, h, d = 0, 0, 0
+ ----- w, h, d = 100*65536, 65536, 65536
+
+ local trace_list = { }
+ local tracing_info = { }
+ local before = ""
+ local after = ""
+
+ local function nodes_to_string(head)
+ local current = head
+ local t = { }
+ while current do
+ local id = getid(current)
+ local ty = nodecodes[id]
+ if id == penalty_code then
+ t[#t+1] = formatters["%s:%s"](ty,getpenalty(current))
+ elseif id == glue_code then
+ t[#t+1] = formatters["%s:%s:%p"](ty,gluecodes[getsubtype(current)],getwidth(current))
+ elseif id == kern_code then
+ t[#t+1] = formatters["%s:%p"](ty,getkern(current))
+ else
+ t[#t+1] = ty
+ end
+ current = getnext(current)
+ end
+ return concat(t," + ")
+ end
+
+ local function reset_tracing(head)
+ trace_list, tracing_info, before, after = { }, false, nodes_to_string(head), ""
+ end
+
+ local function trace_skip(str,sc,so,sp,data)
+ trace_list[#trace_list+1] = { "skip", formatters["%s | %p | category %s | order %s | penalty %s"](str, getwidth(data), sc or "-", so or "-", sp or "-") }
+ tracing_info = true
+ end
+
+ local function trace_natural(str,data)
+ trace_list[#trace_list+1] = { "skip", formatters["%s | %p"](str, getwidth(data)) }
+ tracing_info = true
+ end
+
+ local function trace_info(message, where, what)
+ trace_list[#trace_list+1] = { "info", formatters["%s: %s/%s"](message,where,what) }
+ end
+
+ local function trace_node(what)
+ local nt = nodecodes[getid(what)]
+ local tl = trace_list[#trace_list]
+ if tl and tl[1] == "node" then
+ trace_list[#trace_list] = { "node", formatters["%s + %s"](tl[2],nt) }
+ else
+ trace_list[#trace_list+1] = { "node", nt }
+ end
+ end
+
+ local function show_tracing(head)
+ if tracing_info then
+ after = nodes_to_string(head)
+ for i=1,#trace_list do
+ local tag, text = unpack(trace_list[i])
+ if tag == "info" then
+ report_collapser(text)
+ else
+ report_collapser(" %s: %s",tag,text)
+ end
+ end
+ report_collapser("before: %s",before)
+ report_collapser("after : %s",after)
+ end
+ end
+
+ local function trace_done(str,data)
+ if getid(data) == penalty_code then
+ trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,getpenalty(data)) }
+ else
+ trace_list[#trace_list+1] = { "glue", formatters["%s | %p"](str,getwidth(data)) }
+ end
+ tracing_info = true
+ end
+
+ local function forced_skip(head,current,width,where,trace) -- looks old ... we have other tricks now
+ if head == current then
+ if getsubtype(head) == baselineskip_code then
+ width = width - getwidth(head)
+ end
+ end
+ if width == 0 then
+ -- do nothing
+ elseif where == "after" then
+ head, current = insert_node_after(head,current,new_rule(w,h,d))
+ head, current = insert_node_after(head,current,new_kern(width))
+ head, current = insert_node_after(head,current,new_rule(w,h,d))
+ else
+ local c = current
+ head, current = insert_node_before(head,current,new_rule(w,h,d))
+ head, current = insert_node_before(head,current,new_kern(width))
+ head, current = insert_node_before(head,current,new_rule(w,h,d))
+ current = c
+ end
+ if trace then
+ report_vspacing("inserting forced skip of %p",width)
+ end
+ return head, current
+ end
+
+ -- penalty only works well when before skip
+
+ local discard = categories.discard
+ local largest = categories.largest
+ local force = categories.force
+ local penalty = categories.penalty
+ local add = categories.add
+ local disable = categories.disable
+ local nowhite = categories.nowhite
+ local goback = categories.goback
+ local packed = categories.packed
+ local overlay = categories.overlay
+ local enable = categories.enable
+ local notopskip = categories.notopskip
+
+ -- [whatsits][hlist][glue][glue][penalty]
+
+ local special_penalty_min = 32250
+ local special_penalty_max = 35000
+ local special_penalty_xxx = 0
+
+ -- this is rather messy and complex: we want to make sure that successive
+ -- header don't break but also make sure that we have at least a decent
+ -- break when we have succesive ones (often when testing)
+
+ -- todo: mark headers as such so that we can recognize them
+
+ local specialmethods = { }
+ local specialmethod = 1
+
+ specialmethods[1] = function(pagehead,pagetail,start,penalty)
+ --
+ if not pagehead or penalty < special_penalty_min or penalty > special_penalty_max then
+ return
+ end
+ local current = pagetail
+ --
+ -- nodes.showsimplelist(pagehead,0)
+ --
+ if trace_specials then
+ report_specials("checking penalty %a",penalty)
+ end
+ while current do
+ local id = getid(current)
+ if id == penalty_code then
+ local p = properties[current]
+ if p then
+ local p = p.special_penalty
+ if not p then
+ if trace_specials then
+ report_specials(" regular penalty, continue")
+ end
+ elseif p == penalty then
+ if trace_specials then
+ report_specials(" context penalty %a, same level, overloading",p)
+ end
+ return special_penalty_xxx
+ elseif p > special_penalty_min and p < special_penalty_max then
+ if penalty < p then
+ if trace_specials then
+ report_specials(" context penalty %a, lower level, overloading",p)
+ end
+ return special_penalty_xxx
+ else
+ if trace_specials then
+ report_specials(" context penalty %a, higher level, quitting",p)
+ end
+ return
+ end
+ elseif trace_specials then
+ report_specials(" context penalty %a, higher level, continue",p)
+ end
+ else
+ local p = getpenalty(current)
+ if p < 10000 then
+ -- assume some other mechanism kicks in so we seem to have content
+ if trace_specials then
+ report_specials(" regular penalty %a, quitting",p)
+ end
+ break
+ else
+ if trace_specials then
+ report_specials(" regular penalty %a, continue",p)
+ end
+ end
+ end
+ end
+ current = getprev(current)
+ end
+ -- none found, so no reson to be special
+ if trace_specials then
+ if pagetail then
+ report_specials(" context penalty, discarding, nothing special")
+ else
+ report_specials(" context penalty, discarding, nothing preceding")
+ end
+ end
+ return special_penalty_xxx
+ end
+
+ -- This will be replaced after 0.80+ when we have a more robust look-back and
+ -- can look at the bigger picture.
+
+ -- todo: look back and when a special is there before a list is seen penalty keep ut
+
+ -- we now look back a lot, way too often
+
+ -- userskip
+ -- lineskip
+ -- baselineskip
+ -- parskip
+ -- abovedisplayskip
+ -- belowdisplayskip
+ -- abovedisplayshortskip
+ -- belowdisplayshortskip
+ -- topskip
+ -- splittopskip
+
+ -- we could inject a vadjust to force a recalculation .. a mess
+ --
+ -- So, the next is far from robust and okay but for the moment this overlaying
+ -- has to do. Always test this with the examples in spac-ver.mkvi!
+
+ local function snap_topskip(current,method)
+ local w = getwidth(current)
+ setwidth(current,0)
+ return w, 0
+ end
+
+ local function check_experimental_overlay(head,current)
+ local p = nil
+ local c = current
+ local n = nil
+ local function overlay(p,n,mvl)
+ local p_wd, p_ht, p_dp = getwhd(p)
+ local n_wd, n_ht, n_dp = getwhd(n)
+ local skips = 0
+ --
+ -- We deal with this at the tex end .. we don't see spacing .. enabling this code
+ -- is probably harmless but then we need to test it.
+ --
+ -- we could calculate this before we call
+ --
+ -- problem: prev list and next list can be unconnected
+ --
+ local c = getnext(p)
+ local l = c
+ while c and c ~= n do
+ local id = getid(c)
+ if id == glue_code then
+ skips = skips + getwidth(c)
+ elseif id == kern_code then
+ skips = skips + getkern(c)
+ end
+ l = c
+ c = getnext(c)
+ end
+ local c = getprev(n)
+ while c and c ~= n and c ~= l do
+ local id = getid(c)
+ if id == glue_code then
+ skips = skips + getwidth(c)
+ elseif id == kern_code then
+ skips = skips + getkern(c)
+ end
+ c = getprev(c)
+ end
+ --
+ local delta = n_ht + skips + p_dp
+ texsetdimen("global","d_spac_overlay",-delta) -- for tracing
+ -- we should adapt pagetotal ! (need a hook for that) .. now we have the wrong pagebreak
+ local k = new_kern(-delta)
+ head = insert_node_before(head,n,k)
+ if n_ht > p_ht then
+ local k = new_kern(n_ht-p_ht)
+ head = insert_node_before(head,p,k)
+ end
+ if trace_vspacing then
+ report_vspacing("overlaying, prev height: %p, prev depth: %p, next height: %p, skips: %p, move up: %p",p_ht,p_dp,n_ht,skips,delta)
+ end
+ return remove_node(head,current,true)
+ end
+
+ -- goto next line
+ while c do
+ local id = getid(c)
+ if id == glue_code or id == penalty_code or id == kern_code then
+ -- skip (actually, remove)
+ c = getnext(c)
+ elseif id == hlist_code then
+ n = c
+ break
+ else
+ break
+ end
+ end
+ if n then
+ -- we have a next line, goto prev line
+ c = current
+ while c do
+ local id = getid(c)
+ if id == glue_code or id == penalty_code then -- kern ?
+ c = getprev(c)
+ elseif id == hlist_code then
+ p = c
+ break
+ else
+ break
+ end
+ end
+ if not p then
+ if a_snapmethod == a_snapvbox then
+ -- quit, we're not on the mvl
+ else
+ -- inefficient when we're at the end of a page
+ local c = tonut(texlists.page_head)
+ while c and c ~= n do
+ local id = getid(c)
+ if id == hlist_code then
+ p = c
+ end
+ c = getnext(c)
+ end
+ if p and p ~= n then
+ return overlay(p,n,true)
+ end
+ end
+ elseif p ~= n then
+ return overlay(p,n,false)
+ end
+ end
+ -- in fact, we could try again later ... so then no remove (a few tries)
+ return remove_node(head,current,true)
+ end
+
+ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail
+ if trace then
+ reset_tracing(head)
+ end
+ local current = head
+ local oldhead = head
+ local glue_order = 0
+ local glue_data
+ local force_glue = false
+ local penalty_order = 0
+ local penalty_data
+ local natural_penalty
+ local special_penalty
+ local parskip
+ local ignore_parskip = false
+ local ignore_following = false
+ local ignore_whitespace = false
+ local keep_together = false
+ local lastsnap
+ local pagehead
+ local pagetail
+ --
+ -- todo: keep_together: between headers
+ --
+ local function getpagelist()
+ if not pagehead then
+ pagehead = texlists.page_head
+ if pagehead then
+ pagehead = tonut(pagehead)
+ pagetail = find_node_tail(pagehead) -- no texlists.page_tail yet-- no texlists.page_tail yet
+ end
+ end
+ end
+ --
+ local function compensate(n)
+ local g = 0
+ while n and getid(n) == glue_code do
+ g = g + getwidth(n)
+ n = getnext(n)
+ end
+ if n then
+ local p = getprop(n,"snapper")
+ if p then
+ local extra = p.extra
+ if extra and extra < 0 then -- hm, extra can be unset ... needs checking
+ local h = p.ch -- getheight(n)
+ -- maybe an extra check
+ -- if h - extra < g then
+ setheight(n,h-2*extra)
+ p.extra = 0
+ if trace_vsnapping then
+ report_snapper("removed extra space at top: %p",extra)
+ end
+ -- end
+ end
+ end
+ return n
+ end
+ end
+ --
+ local function removetopsnap()
+ getpagelist()
+ if pagehead then
+ local n = pagehead and compensate(pagehead)
+ if n and n ~= pagetail then
+ local p = getprop(pagetail,"snapper")
+ if p then
+ local e = p.extra
+ if e and e < 0 then
+ local t = texget("pagetotal")
+ if t > 0 then
+ local g = texget("pagegoal") -- 1073741823 is signal
+ local d = g - t
+ if d < -e then
+ local penalty = new_penalty(1000000)
+ setlink(penalty,head)
+ head = penalty
+ report_snapper("force pagebreak due to extra space at bottom: %p",e)
+ end
+ end
+ end
+ end
+ end
+ elseif head then
+ compensate(head)
+ end
+ end
+ --
+ local function getavailable()
+ getpagelist()
+ if pagehead then
+ local t = texget("pagetotal")
+ if t > 0 then
+ local g = texget("pagegoal")
+ return g - t
+ end
+ end
+ return false
+ end
+ --
+ local function flush(why)
+ if penalty_data then
+ local p = new_penalty(penalty_data)
+ if trace then
+ trace_done("flushed due to " .. why,p)
+ end
+ if penalty_data >= 10000 then -- or whatever threshold?
+ local prev = getprev(current)
+ if getid(prev) == glue_code then -- maybe go back more, or maybe even push back before any glue
+ -- tricky case: spacing/grid-007.tex: glue penalty glue
+ head = insert_node_before(head,prev,p)
+ else
+ head = insert_node_before(head,current,p)
+ end
+ else
+ head = insert_node_before(head,current,p)
+ end
+ -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then
+ local props = properties[p]
+ if props then
+ props.special_penalty = special_penalty or penalty_data
+ else
+ properties[p] = {
+ special_penalty = special_penalty or penalty_data
+ }
+ end
+ -- end
+ end
+ if glue_data then
+ if force_glue then
+ if trace then
+ trace_done("flushed due to forced " .. why,glue_data)
+ end
+ head = forced_skip(head,current,getwidth(glue_data,width),"before",trace)
+ flush_node(glue_data)
+ else
+ local width, stretch, shrink = getglue(glue_data)
+ if width ~= 0 then
+ if trace then
+ trace_done("flushed due to non zero " .. why,glue_data)
+ end
+ head = insert_node_before(head,current,glue_data)
+ elseif stretch ~= 0 or shrink ~= 0 then
+ if trace then
+ trace_done("flushed due to stretch/shrink in" .. why,glue_data)
+ end
+ head = insert_node_before(head,current,glue_data)
+ else
+ -- report_vspacing("needs checking (%s): %p",gluecodes[getsubtype(glue_data)],w)
+ flush_node(glue_data)
+ end
+ end
+ end
+
+ if trace then
+ trace_node(current)
+ end
+ glue_order, glue_data, force_glue = 0, nil, false
+ penalty_order, penalty_data, natural_penalty = 0, nil, nil
+ parskip, ignore_parskip, ignore_following, ignore_whitespace = nil, false, false, false
+ end
+ --
+ if trace_vsnapping then
+ report_snapper("global ht/dp = %p/%p, local ht/dp = %p/%p",
+ texgetdimen("globalbodyfontstrutheight"),
+ texgetdimen("globalbodyfontstrutdepth"),
+ texgetdimen("bodyfontstrutheight"),
+ texgetdimen("bodyfontstrutdepth")
+ )
+ end
+ if trace then
+ trace_info("start analyzing",where,what)
+ end
+ if snap and where == "page" then
+ removetopsnap()
+ end
+ while current do
+ local id = getid(current)
+ if id == hlist_code or id == vlist_code then
+ -- needs checking, why so many calls
+ if snap then
+ lastsnap = nil
+ local list = getlist(current)
+ local s = getattr(current,a_snapmethod)
+ if not s then
+ -- if trace_vsnapping then
+ -- report_snapper("mvl list not snapped")
+ -- end
+ elseif s == 0 then
+ if trace_vsnapping then
+ report_snapper("mvl %a not snapped, already done: %s",nodecodes[id],listtoutf(list))
+ end
+ else
+ local sv = snapmethods[s]
+ if sv then
+ -- check if already snapped
+ local done = list and already_done(id,list,a_snapmethod)
+ if done then
+ -- assume that the box is already snapped
+ if trace_vsnapping then
+ local w, h, d = getwhd(current)
+ report_snapper("mvl list already snapped at (%p,%p): %s",h,d,listtoutf(list))
+ end
+ else
+ local h, d, ch, cd, lines, extra = snap_hlist("mvl",current,sv,false,false)
+ lastsnap = {
+ ht = h,
+ dp = d,
+ ch = ch,
+ cd = cd,
+ extra = extra,
+ current = current,
+ }
+ setprop(current,"snapper",lastsnap)
+ if trace_vsnapping then
+ report_snapper("mvl %a snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
+ nodecodes[id],h,d,ch,cd,sv.name,sv.specification,where,lines,listtoutf(list))
+ end
+ end
+ elseif trace_vsnapping then
+ report_snapper("mvl %a not snapped due to unknown snap specification: %s",nodecodes[id],listtoutf(list))
+ end
+ setattr(current,a_snapmethod,0)
+ end
+ else
+ --
+ end
+ -- tex.prevdepth = 0
+ flush("list")
+ current = getnext(current)
+ elseif id == penalty_code then
+ -- natural_penalty = getpenalty(current)
+ -- if trace then
+ -- trace_done("removed penalty",current)
+ -- end
+ -- head, current = remove_node(head,current,true)
+ current = getnext(current)
+ elseif id == kern_code then
+ if snap and trace_vsnapping and getkern(current) ~= 0 then
+ report_snapper("kern of %p kept",getkern(current))
+ end
+ flush("kern")
+ current = getnext(current)
+ elseif id == glue_code then
+ local subtype = getsubtype(current)
+ if subtype == userskip_code then
+ local sc, so, sp = getattrs(current,a_skipcategory,a_skiporder,a_skippenalty)
+ if not so then
+ so = 1 -- the others have no default value
+ end
+ if sp and sc == penalty then
+ if where == "page" then
+ getpagelist()
+ local p = specialmethods[specialmethod](pagehead,pagetail,current,sp)
+ if p then
+ -- todo: other tracer
+ --
+ -- if trace then
+ -- trace_skip("previous special penalty %a is changed to %a using method %a",sp,p,specialmethod)
+ -- end
+ special_penalty = sp
+ sp = p
+ end
+ end
+ if not penalty_data then
+ penalty_data = sp
+ elseif penalty_order < so then
+ penalty_order, penalty_data = so, sp
+ elseif penalty_order == so and sp > penalty_data then
+ penalty_data = sp
+ end
+ if trace then
+ trace_skip("penalty in skip",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ elseif not sc then -- if not sc then
+ if glue_data then
+ if trace then
+ trace_done("flush",glue_data)
+ end
+ head = insert_node_before(head,current,glue_data)
+ if trace then
+ trace_natural("natural",current)
+ end
+ current = getnext(current)
+ glue_data = nil
+ else
+ -- not look back across head
+ -- todo: prev can be whatsit (latelua)
+ local previous = getprev(current)
+ if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then
+ local pwidth, pstretch, pshrink, pstretch_order, pshrink_order = getglue(previous)
+ local cwidth, cstretch, cshrink, cstretch_order, cshrink_order = getglue(current)
+ if pstretch_order == 0 and pshrink_order == 0 and cstretch_order == 0 and cshrink_order == 0 then
+ setglue(previous,pwidth + cwidth, pstretch + cstretch, pshrink + cshrink)
+ if trace then
+ trace_natural("removed",current)
+ end
+ head, current = remove_node(head,current,true)
+ if trace then
+ trace_natural("collapsed",previous)
+ end
+ else
+ if trace then
+ trace_natural("filler",current)
+ end
+ current = getnext(current)
+ end
+ else
+ if trace then
+ trace_natural("natural (no prev)",current)
+ end
+ current = getnext(current)
+ end
+ end
+ glue_order = 0
+ elseif sc == disable or sc == enable then
+ local next = getnext(current)
+ if next then
+ ignore_following = sc == disable
+ if trace then
+ trace_skip(sc == disable and "disable" or "enable",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ else
+ current = next
+ end
+ elseif sc == packed then
+ if trace then
+ trace_skip("packed",sc,so,sp,current)
+ end
+ -- can't happen !
+ head, current = remove_node(head,current,true)
+ elseif sc == nowhite then
+ local next = getnext(current)
+ if next then
+ ignore_whitespace = true
+ head, current = remove_node(head,current,true)
+ else
+ current = next
+ end
+ elseif sc == discard then
+ if trace then
+ trace_skip("discard",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ elseif sc == overlay then
+ -- todo (overlay following line over previous
+ if trace then
+ trace_skip("overlay",sc,so,sp,current)
+ end
+ -- beware: head can actually be after the affected nodes as
+ -- we look back ... some day head will the real head
+ head, current = check_experimental_overlay(head,current,a_snapmethod)
+ elseif ignore_following then
+ if trace then
+ trace_skip("disabled",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ elseif not glue_data then
+ if trace then
+ trace_skip("assign",sc,so,sp,current)
+ end
+ glue_order = so
+ head, current, glue_data = remove_node(head,current)
+ elseif glue_order < so then
+ if trace then
+ trace_skip("force",sc,so,sp,current)
+ end
+ glue_order = so
+ flush_node(glue_data)
+ head, current, glue_data = remove_node(head,current)
+ elseif glue_order == so then
+ -- is now exclusive, maybe support goback as combi, else why a set
+ if sc == largest then
+ local cw = getwidth(current)
+ local gw = getwidth(glue_data)
+ if cw > gw then
+ if trace then
+ trace_skip("largest",sc,so,sp,current)
+ end
+ flush_node(glue_data)
+ head, current, glue_data = remove_node(head,current)
+ else
+ if trace then
+ trace_skip("remove smallest",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ end
+ elseif sc == goback then
+ if trace then
+ trace_skip("goback",sc,so,sp,current)
+ end
+ flush_node(glue_data)
+ head, current, glue_data = remove_node(head,current)
+ elseif sc == force then
+ -- last one counts, some day we can provide an accumulator and largest etc
+ -- but not now
+ if trace then
+ trace_skip("force",sc,so,sp,current)
+ end
+ flush_node(glue_data)
+ head, current, glue_data = remove_node(head,current)
+ elseif sc == penalty then
+ if trace then
+ trace_skip("penalty",sc,so,sp,current)
+ end
+ flush_node(glue_data)
+ glue_data = nil
+ head, current = remove_node(head,current,true)
+ elseif sc == add then
+ if trace then
+ trace_skip("add",sc,so,sp,current)
+ end
+ local cwidth, cstretch, cshrink = getglue(current)
+ local gwidth, gstretch, gshrink = getglue(glue_data)
+ setglue(glue_data,gwidth + cwidth, gstretch + cstretch,gshrink + cshrink)
+ -- toto: order
+ head, current = remove_node(head,current,true)
+ else
+ if trace then
+ trace_skip("unknown",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ end
+ else
+ if trace then
+ trace_skip("unknown",sc,so,sp,current)
+ end
+ head, current = remove_node(head,current,true)
+ end
+ if sc == force then
+ force_glue = true
+ end
+ elseif subtype == lineskip_code then
+ if snap then
+ local s = getattr(current,a_snapmethod)
+ if s and s ~= 0 then
+ setattr(current,a_snapmethod,0)
+ setwidth(current,0)
+ if trace_vsnapping then
+ report_snapper("lineskip set to zero")
+ end
+ else
+ if trace then
+ trace_skip("lineskip",sc,so,sp,current)
+ end
+ flush("lineskip")
+ end
+ else
+ if trace then
+ trace_skip("lineskip",sc,so,sp,current)
+ end
+ flush("lineskip")
+ end
+ current = getnext(current)
+ elseif subtype == baselineskip_code then
+ if snap then
+ local s = getattr(current,a_snapmethod)
+ if s and s ~= 0 then
+ setattr(current,a_snapmethod,0)
+ setwidth(current,0)
+ if trace_vsnapping then
+ report_snapper("baselineskip set to zero")
+ end
+ else
+ if trace then
+ trace_skip("baselineskip",sc,so,sp,current)
+ end
+ flush("baselineskip")
+ end
+ else
+ if trace then
+ trace_skip("baselineskip",sc,so,sp,current)
+ end
+ flush("baselineskip")
+ end
+ current = getnext(current)
+ elseif subtype == parskip_code then
+ -- parskip always comes later
+ if ignore_whitespace then
+ if trace then
+ trace_natural("ignored parskip",current)
+ end
+ head, current = remove_node(head,current,true)
+ elseif glue_data then
+ local w = getwidth(current)
+ if w ~= 0 and w > getwidth(glue_data) then
+flush_node(glue_data)
+ glue_data = current
+ if trace then
+ trace_natural("taking parskip",current)
+ end
+ head, current = remove_node(head,current)
+ else
+ if trace then
+ trace_natural("removed parskip",current)
+ end
+ head, current = remove_node(head,current,true)
+ end
+ else
+ if trace then
+ trace_natural("honored parskip",current)
+ end
+ head, current, glue_data = remove_node(head,current)
+ end
+ elseif subtype == topskip_code or subtype == splittopskip_code then
+ local next = getnext(current)
+ if next and getattr(next,a_skipcategory) == notopskip then
+ nuts.setglue(current) -- zero
+ end
+ if snap then
+ local s = getattr(current,a_snapmethod)
+ if s and s ~= 0 then
+ setattr(current,a_snapmethod,0)
+ local sv = snapmethods[s]
+ local w, cw = snap_topskip(current,sv)
+ if trace_vsnapping then
+ report_snapper("topskip snapped from %p to %p for %a",w,cw,where)
+ end
+ else
+ if trace then
+ trace_skip("topskip",sc,so,sp,current)
+ end
+ flush("topskip")
+ end
+ else
+ if trace then
+ trace_skip("topskip",sc,so,sp,current)
+ end
+ flush("topskip")
+ end
+ current = getnext(current)
+ elseif subtype == abovedisplayskip_code and remove_math_skips then
+ --
+ if trace then
+ trace_skip("above display skip (normal)",sc,so,sp,current)
+ end
+ flush("above display skip (normal)")
+ current = getnext(current)
+ --
+ elseif subtype == belowdisplayskip_code and remove_math_skips then
+ --
+ if trace then
+ trace_skip("below display skip (normal)",sc,so,sp,current)
+ end
+ flush("below display skip (normal)")
+ current = getnext(current)
+ --
+ elseif subtype == abovedisplayshortskip_code and remove_math_skips then
+ --
+ if trace then
+ trace_skip("above display skip (short)",sc,so,sp,current)
+ end
+ flush("above display skip (short)")
+ current = getnext(current)
+ --
+ elseif subtype == belowdisplayshortskip_code and remove_math_skips then
+ --
+ if trace then
+ trace_skip("below display skip (short)",sc,so,sp,current)
+ end
+ flush("below display skip (short)")
+ current = getnext(current)
+ --
+ else -- other glue
+ if snap and trace_vsnapping then
+ local w = getwidth(current)
+ if w ~= 0 then
+ report_snapper("glue %p of type %a kept",w,gluecodes[subtype])
+ end
+ end
+ if trace then
+ trace_skip(formatters["glue of type %a"](subtype),sc,so,sp,current)
+ end
+ flush("some glue")
+ current = getnext(current)
+ end
+ else
+ flush(trace and formatters["node with id %a"](id) or "other node")
+ current = getnext(current)
+ end
+ end
+ if trace then
+ trace_info("stop analyzing",where,what)
+ end
+ -- if natural_penalty and (not penalty_data or natural_penalty > penalty_data) then
+ -- penalty_data = natural_penalty
+ -- end
+ if trace and (glue_data or penalty_data) then
+ trace_info("start flushing",where,what)
+ end
+ local tail
+ if penalty_data then
+ tail = find_node_tail(head)
+ local p = new_penalty(penalty_data)
+ if trace then
+ trace_done("result",p)
+ end
+ setlink(tail,p)
+ -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then
+ local props = properties[p]
+ if props then
+ props.special_penalty = special_penalty or penalty_data
+ else
+ properties[p] = {
+ special_penalty = special_penalty or penalty_data
+ }
+ end
+ -- end
+ end
+ if glue_data then
+ if not tail then tail = find_node_tail(head) end
+ if trace then
+ trace_done("result",glue_data)
+ end
+ if force_glue then
+ head, tail = forced_skip(head,tail,getwidth(glue_data),"after",trace)
+ flush_node(glue_data)
+ glue_data = nil
+ elseif tail then
+ setlink(tail,glue_data)
+setnext(glue_data)
+ else
+ head = glue_data
+ end
+ texnest[texnest.ptr].prevdepth = 0 -- appending to the list bypasses tex's prevdepth handler
+ end
+ if trace then
+ if glue_data or penalty_data then
+ trace_info("stop flushing",where,what)
+ end
+ show_tracing(head)
+ if oldhead ~= head then
+ trace_info("head has been changed from %a to %a",nodecodes[getid(oldhead)],nodecodes[getid(head)])
+ end
+ end
+ return head
+ end
+
+ local stackhead, stacktail, stackhack = nil, nil, false
+
+ local function report(message,where,lst)
+ if lst and where then
+ report_vspacing(message,where,count_nodes(lst,true),nodeidstostring(lst))
+ else
+ report_vspacing(message,count_nodes(lst,true),nodeidstostring(lst))
+ end
+ end
+
+ -- ugly code: we get partial lists (check if this stack is still okay) ... and we run
+ -- into temp nodes (sigh)
+
+ local forceflush = false
+
+ function vspacing.pagehandler(newhead,where)
+ if newhead then
+ local newtail = find_node_tail(newhead) -- best pass that tail, known anyway
+ local flush = false
+ stackhack = true -- todo: only when grid snapping once enabled
+ -- todo: fast check if head = tail
+ for n, id, subtype in nextnode, newhead do -- we could just look for glue nodes
+ if id ~= glue_code then
+ flush = true
+ elseif subtype == userskip_code then
+ if getattr(n,a_skipcategory) then
+ stackhack = true
+ else
+ flush = true
+ end
+ elseif subtype == parskip_code then
+ -- if where == new_graf then ... end
+ if texgetcount("c_spac_vspacing_ignore_parskip") > 0 then
+ -- texsetcount("c_spac_vspacing_ignore_parskip",0)
+ setglue(n)
+ -- maybe removenode
+ end
+ end
+ end
+ texsetcount("c_spac_vspacing_ignore_parskip",0)
+
+ if forceflush then
+ forceflush = false
+ flush = true
+ end
+
+ if flush then
+ if stackhead then
+ if trace_collect_vspacing then report("%s > appending %s nodes to stack (final): %s",where,newhead) end
+ setlink(stacktail,newhead)
+ newhead = stackhead
+ stackhead = nil
+ stacktail = nil
+ end
+ if stackhack then
+ stackhack = false
+ if trace_collect_vspacing then report("%s > processing %s nodes: %s",where,newhead) end
+ newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
+ else
+ if trace_collect_vspacing then report("%s > flushing %s nodes: %s",where,newhead) end
+ end
+ return newhead
+ else
+ if stackhead then
+ if trace_collect_vspacing then report("%s > appending %s nodes to stack (intermediate): %s",where,newhead) end
+ setlink(stacktail,newhead)
+ else
+ if trace_collect_vspacing then report("%s > storing %s nodes in stack (initial): %s",where,newhead) end
+ stackhead = newhead
+ end
+ stacktail = newtail
+ end
+ end
+ return nil
+ end
+
+ function vspacing.pageoverflow()
+ local h = 0
+ if stackhead then
+ for n, id in nextnode, stackhead do
+ if id == glue_code then
+ h = h + getwidth(n)
+ elseif id == kern_code then
+ h = h + getkern(n)
+ end
+ end
+ end
+ return h
+ end
+
+ function vspacing.forcepageflush()
+ forceflush = true
+ end
+
+ local ignore = table.tohash {
+ "split_keep",
+ "split_off",
+ -- "vbox",
+ }
+
+ function vspacing.vboxhandler(head,where)
+ if head and not ignore[where] and getnext(head) then
+ if getnext(head) then -- what if a one liner and snapping?
+ head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper
+ return head
+ end
+ end
+ return head
+ end
+
+ function vspacing.collapsevbox(n,aslist) -- for boxes but using global a_snapmethod
+ local box = getbox(n)
+ if box then
+ local list = getlist(box)
+ if list then
+ list = collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod)
+ if aslist then
+ setlist(box,list) -- beware, dimensions of box are wrong now
+ else
+ setlist(box,vpack_node(list))
+ end
+ end
+ end
+ end
+
+end
+
+-- This one is needed to prevent bleeding of prevdepth to the next page
+-- which doesn't work well with forced skips. I'm not that sure if the
+-- following is a good way out.
+
+do
+
+ local outer = texnest[0]
+
+ local enabled = true
+ local trace = false
+ local report = logs.reporter("vspacing")
+
+ trackers.register("vspacing.synchronizepage",function(v)
+ trace = v
+ end)
+
+ directives.register("vspacing.synchronizepage",function(v)
+ enabled = v
+ end)
+
+ local ignoredepth = -65536000
+
+ -- A previous version analyzed the number of lines moved to the next page in
+ -- synchronizepage because prevgraf is unreliable in that case. However, we cannot
+ -- tweak that parameter because it is also used in postlinebreak and hangafter, so
+ -- there is a danger for interference. Therefore we now do it dynamically.
+
+ -- We can also support other lists but there prevgraf probably is ok.
+
+ function vspacing.getnofpreviouslines(head)
+ if enabled then
+ if not thead then
+ head = texlists.page_head
+ end
+ local noflines = 0
+ if head then
+ local tail = find_node_tail(tonut(head))
+ while tail do
+ local id = getid(tail)
+ if id == hlist_code then
+ if getsubtype(tail) == linelist_code then
+ noflines = noflines + 1
+ else
+ break
+ end
+ elseif id == vlist_code then
+ break
+ elseif id == glue_code then
+ local subtype = getsubtype(tail)
+ if subtype == baselineskip_code or subtype == lineskip_code then
+ -- we're ok
+ elseif subtype == parskip_code then
+ if getwidth(tail) > 0 then
+ break
+ else
+ -- we assume we're ok
+ end
+ end
+ elseif id == penalty_code then
+ -- we're probably ok
+ elseif id == rule_code or id == kern_code then
+ break
+ else
+ -- ins, mark, boundary, whatsit
+ end
+ tail = getprev(tail)
+ end
+ end
+ return noflines
+ end
+ end
+
+ interfaces.implement {
+ name = "getnofpreviouslines",
+ public = true,
+ actions = vspacing.getnofpreviouslines,
+ }
+
+ function vspacing.synchronizepage()
+ if enabled then
+ if trace then
+ local newdepth = outer.prevdepth
+ local olddepth = newdepth
+ if not texlists.page_head then
+ newdepth = ignoredepth
+ texset("prevdepth",ignoredepth)
+ outer.prevdepth = ignoredepth
+ end
+ report("page %i, prevdepth %p => %p",texgetcount("realpageno"),olddepth,newdepth)
+ -- report("list %s",nodes.idsandsubtypes(head))
+ else
+ if not texlists.page_head then
+ texset("prevdepth",ignoredepth)
+ outer.prevdepth = ignoredepth
+ end
+ end
+ end
+ end
+
+ local trace = false
+ local last = nil
+ local vmode_code = tex.modelevels.vertical
+ local temp_code = nodecodes.temp
+ local getnest = tex.getnest
+ local getlist = tex.getlist
+
+ trackers.register("vspacing.forcestrutdepth",function(v) trace = v end)
+
+ -- abs : negative is inner
+
+ function vspacing.checkstrutdepth(depth)
+ local nest = getnest()
+ if abs(nest.mode) == vmode_code and nest.head then
+ local tail = nest.tail
+ local id = tail.id
+ if id == hlist_code then
+ if tail.depth < depth then
+ tail.depth = depth
+ end
+ nest.prevdepth = depth
+ elseif id == temp_code and getnest("ptr") == 0 then
+ local head = getlist("page_head")
+ if head then
+ tail = nodes.tail(head)
+ if tail and tail.id == hlist_code then
+ if tail.depth < depth then
+ tail.depth = depth
+ end
+ nest.prevdepth = depth
+ -- only works in lmtx
+ texset("pagedepth",depth)
+ end
+ end
+ end
+ end
+ end
+
+ interfaces.implement {
+ name = "checkstrutdepth",
+ arguments = "dimension",
+ actions = vspacing.checkstrutdepth,
+ }
+
+ function vspacing.forcestrutdepth(n,depth,trace_mode,plus)
+ local box = texgetbox(n)
+ if box then
+ box = tonut(box)
+ local head = getlist(box)
+ if head then
+ local tail = find_node_tail(head)
+ if tail then
+ if getid(tail) == hlist_code then
+ local dp = getdepth(tail)
+ if dp < depth then
+ setdepth(tail,depth)
+ outer.prevdepth = depth
+ if trace or trace_mode > 0 then
+ nuts.setvisual(tail,"depth")
+ end
+ end
+ end
+ last = nil
+ if plus then
+ -- penalty / skip ...
+ local height = 0
+ local sofar = 0
+ local same = false
+ local seen = false
+ local list = { }
+ last = nil
+ while tail do
+ local id = getid(tail)
+ if id == hlist_code or id == vlist_code then
+ local w, h, d = getwhd(tail)
+ height = height + h + d + sofar
+ sofar = 0
+ last = tail
+ elseif id == kern_code then
+ sofar = sofar + getkern(tail)
+ elseif id == glue_code then
+ if seen then
+ sofar = sofar + getwidth(tail)
+ seen = false
+ else
+ break
+ end
+ elseif id == penalty_code then
+ local p = getpenalty(tail)
+ if p >= 10000 then
+ same = true
+ seen = true
+ else
+ break
+ end
+ else
+ break
+ end
+ tail = getprev(tail)
+ end
+ texsetdimen("global","d_spac_prevcontent",same and height or 0)
+ end
+ end
+ end
+ end
+ end
+
+ function vspacing.pushatsame()
+ -- needs better checking !
+ if last then -- setsplit
+ nuts.setnext(getprev(last))
+ nuts.setprev(last)
+ end
+ end
+
+ function vspacing.popatsame()
+ -- needs better checking !
+ nuts.write(last)
+ end
+
+end
+
+-- interface
+
+do
+
+ interfaces.implement {
+ name = "injectvspacing",
+ actions = vspacing.inject,
+ arguments = { "integer", "string" },
+ }
+
+ interfaces.implement {
+ name = "injectvpenalty",
+ actions = vspacing.injectpenalty,
+ arguments = "integer",
+ }
+
+ interfaces.implement {
+ name = "injectvskip",
+ actions = vspacing.injectskip,
+ arguments = "dimension",
+ }
+
+ interfaces.implement {
+ name = "injectdisable",
+ actions = vspacing.injectdisable,
+ }
+
+ --
+
+ implement {
+ name = "synchronizepage",
+ actions = vspacing.synchronizepage,
+ scope = "private"
+ }
+
+ implement {
+ name = "forcestrutdepth",
+ arguments = { "integer", "dimension", "integer" },
+ actions = vspacing.forcestrutdepth,
+ scope = "private"
+ }
+
+ implement {
+ name = "forcestrutdepthplus",
+ arguments = { "integer", "dimension", "integer", true },
+ actions = vspacing.forcestrutdepth,
+ scope = "private"
+ }
+
+ implement {
+ name = "pushatsame",
+ actions = vspacing.pushatsame,
+ scope = "private"
+ }
+
+ implement {
+ name = "popatsame",
+ actions = vspacing.popatsame,
+ scope = "private"
+ }
+
+ implement {
+ name = "vspacingsetamount",
+ actions = vspacing.setskip,
+ scope = "private",
+ arguments = "string",
+ }
+
+ implement {
+ name = "vspacingdefine",
+ actions = vspacing.setmap,
+ scope = "private",
+ arguments = "2 strings",
+ }
+
+ implement {
+ name = "vspacingcollapse",
+ actions = vspacing.collapsevbox,
+ scope = "private",
+ arguments = "integer"
+ }
+
+ implement {
+ name = "vspacingcollapseonly",
+ actions = vspacing.collapsevbox,
+ scope = "private",
+ arguments = { "integer", true }
+ }
+
+ implement {
+ name = "vspacingsnap",
+ actions = vspacing.snapbox,
+ scope = "private",
+ arguments = { "integer", "integer" }
+ }
+
+ implement {
+ name = "definesnapmethod",
+ actions = vspacing.definesnapmethod,
+ scope = "private",
+ arguments = "2 strings",
+ }
+
+ -- local remove_node = nodes.remove
+ -- local find_node_tail = nodes.tail
+ --
+ -- interfaces.implement {
+ -- name = "fakenextstrutline",
+ -- actions = function()
+ -- local head = texlists.page_head
+ -- if head then
+ -- local head = remove_node(head,find_node_tail(head),true)
+ -- texlists.page_head = head
+ -- buildpage()
+ -- end
+ -- end
+ -- }
+
+ implement {
+ name = "removelastline",
+ actions = function()
+ local head = texlists.page_head
+ if head then
+ local tail = find_node_tail(head)
+ if tail then
+ -- maybe check for hlist subtype 1
+ local head = remove_node(head,tail,true)
+ texlists.page_head = head
+ buildpage()
+ end
+ end
+ end
+ }
+
+ implement {
+ name = "showpagelist", -- will improve
+ actions = function()
+ local head = texlists.page_head
+ if head then
+ print("start")
+ while head do
+ print(" " .. tostring(head))
+ head = head.next
+ end
+ end
+ end
+ }
+
+ implement {
+ name = "pageoverflow",
+ actions = { vspacing.pageoverflow, context }
+ }
+
+ implement {
+ name = "forcepageflush",
+ actions = vspacing.forcepageflush
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/spac-ver.mkxl b/Master/texmf-dist/tex/context/base/mkxl/spac-ver.mkxl
new file mode 100644
index 00000000000..d116113399d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/spac-ver.mkxl
@@ -0,0 +1,2430 @@
+%D \module
+%D [ file=spac-ver,
+%D version=2009.10.16, % 1997.03.31, was core-spa.tex
+%D title=\CONTEXT\ Spacing Macros,
+%D subtitle=Vertical,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Vertical}
+
+\unprotect
+
+\registerctxluafile{spac-ver}{autosuffix,optimize}
+
+% todo: use usernodes ?
+
+% todo: itemize : intro ... only when there is one or two lines preceding and then
+% keep these together i.e. \blank[intro]
+
+% Isn't it about time to get rid of topskip i.e. make it equivalent to
+% \openstrutheight so that we can remove delta code.
+%
+% There might be more namespace protection.
+
+%D There are two ways to influence the interline spacing. The most general and often
+%D most consistent way is using
+%D
+%D \showsetup{setupinterlinespace}
+%D
+%D For instance
+%D
+%D \starttyping
+%D \setupinterlinespace[line=2.8ex]
+%D \stoptyping
+%D
+%D This setting adapts itself to the bodyfontsize, while for instance saying
+%D
+%D \starttyping
+%D \setupinterlinespace[line=12pt]
+%D \stoptyping
+%D
+%D sets things fixed for all sizes, which is definitely not what we want. Therefore
+%D one can also say:
+%D
+%D \starttyping
+%D \definebodyfontenvironment[9pt][interlinespace=11pt]
+%D \stoptyping
+%D
+%D One can still use \type {\setupinterlinespace} (without arguments) to set the
+%D interline space according to the current font, e.g. a \type {\bfa}.
+
+% will be cleaned up but it will stay messy because we accept so
+% many variants
+
+\newif\iflocalinterlinespace
+
+\newskip \s_spac_vspacing_temp \s_spac_vspacing_temp\bigskipamount
+
+\mutable\def\skipfactor {.75}
+\mutable\def\skipgluefactor{.25}
+
+\permanent\def\normalskipamount
+ {\openlineheight
+ \ifgridsnapping \orelse \ifconditional\c_spac_whitespace_flexible
+ \s!plus \skipgluefactor\openlineheight
+ \s!minus\skipgluefactor\openlineheight
+ \fi
+ \relax}
+
+\ifdefined\bodyfontinterlinespace \else
+ \let\bodyfontinterlinespace\empty
+\fi
+
+\permanent\protected\def\presetnormallineheight % each bodyfont
+ {\edef\normallineheight{\interlinespaceparameter\c!line}%
+ \iflocalinterlinespace \else
+ \edef\m_spac_normallineheight{\bodyfontinterlinespace}%
+ \ifempty\m_spac_normallineheight \else
+ \let\normallineheight\m_spac_normallineheight
+ \fi
+ \fi}
+
+\permanent\protected\def\setupspecifiedinterlinespace[#1]%
+ {\setupcurrentinterlinespace[#1]%
+ \spac_linespacing_setup_specified_interline_space}
+
+\def\spac_linespacing_setup_specified_interline_space
+ {\edef\strutheightfactor {\interlinespaceparameter\c!height }%
+ \edef\strutdepthfactor {\interlinespaceparameter\c!depth }%
+ \edef\minimumstrutheight {\interlinespaceparameter\c!minheight}%
+ \edef\minimumstrutdepth {\interlinespaceparameter\c!mindepth }%
+ \edef\minimumlinedistance {\interlinespaceparameter\c!distance }%
+ \edef\normallineheight {\interlinespaceparameter\c!line }%
+ \edef\topskipfactor {\interlinespaceparameter\c!top }%
+ \edef\maxdepthfactor {\interlinespaceparameter\c!bottom }%
+ \edef\m_spac_vertical_baseline_stretch_factor{\interlinespaceparameter\c!stretch}%
+ \edef\m_spac_vertical_baseline_shrink_factor {\interlinespaceparameter\c!shrink }%
+ % often topskip does more bad than good, so:
+ \ifx\topskipfactor\v!height
+ \let\topskipfactor\strutheightfactor
+ \fi
+ \setfontparameters % redundant, can be \setstrut, test first
+ \updateraggedskips} % yes indeed
+
+\installcorenamespace{interlinespacerelative}
+
+\aliased\let\setrelativeinterlinespace \relax % used elsewhere
+\mutable\let\currentrelativeinterlinespace\empty
+
+\defcsname\??interlinespacerelative\v!on \endcsname{\oninterlineskip}
+\defcsname\??interlinespacerelative\v!off \endcsname{\offinterlineskip}
+\defcsname\??interlinespacerelative\v!reset\endcsname{\enforced\let\currentrelativeinterlinespace\empty
+ \enforced\let\setrelativeinterlinespace\relax
+ \setfontparameters}
+\defcsname\??interlinespacerelative\v!auto \endcsname{\enforced\let\setrelativeinterlinespace\spac_linespacing_set_relative_interlinespace}
+
+\def\spac_linespacing_set_specified_relative_interlinespace#1% fragile?
+ {\doifelsedimenstring{#1}%
+ {\setupspecifiedinterlinespace[\c!line=#1]}%
+ {\assignvalue{#1}\currentrelativeinterlinespace{1.00}{1.25}{1.50}%
+ \spacing\currentrelativeinterlinespace}}
+
+\permanent\protected\def\setuprelativeinterlinespace[#1]%
+ {\processcommalist[#1]\spac_linespacing_setup_relative_interlinespace}
+
+\def\spac_linespacing_setup_relative_interlinespace#1%
+ {\ifcsname\??interlinespacerelative#1\endcsname
+ \lastnamedcs
+ \else
+ \spac_linespacing_set_specified_relative_interlinespace{#1}%
+ \fi}
+
+\permanent\protected\def\spac_linespacing_set_relative_interlinespace
+ {\ifempty\currentrelativeinterlinespace\else
+ \spacing\currentrelativeinterlinespace
+ \fi}
+
+\protected\def\spac_linespacing_setup_use
+ {\ifcsname\namedinterlinespacehash\m_spac_interlinespace\s!parent\endcsname
+ \let\currentinterlinespace\m_spac_interlinespace
+ \spac_linespacing_setup_specified_interline_space
+ % \else
+ % we only support named interlinespaces
+ \fi}
+
+\permanent\protected\def\useinterlinespaceparameter#1% see footnotes
+ {\edef\m_spac_interlinespace{#1\c!interlinespace}%
+ \ifempty\m_spac_interlinespace \else
+ \spac_linespacing_setup_use
+ \fi}
+
+\newtoks\everysetupglobalinterlinespace
+\newtoks\everysetuplocalinterlinespace
+
+\newconditional\interlinespaceisset
+
+\installcorenamespace{interlinespace}
+
+\installcommandhandler \??interlinespace {interlinespace} \??interlinespace
+
+\installmacrostack\currentinterlinespace
+
+\permanent\overloaded\tolerant\protected\def\setupinterlinespace[#1]%
+ {\ifarguments
+ \settrue\interlinespaceisset
+ \spac_linespacing_synchronize_local
+ \else
+ \settrue\interlinespaceisset
+ \ifcsname\namedinterlinespacehash{#1}\s!parent\endcsname
+ \edef\currentinterlinespace{#1}%
+ \spac_linespacing_setup_specified_interline_space
+ %\dosetupspecifiedinterlinespaceindeed
+ \else
+ \spac_linespacing_setup_specified_or_relative[#1]%
+ \fi
+ \fi}
+
+\def\spac_linespacing_setup_specified_or_relative[#1]%
+ {\doifelseassignment{#1}\setupspecifiedinterlinespace\setuprelativeinterlinespace[#1]%
+ \the\iflocalinterlinespace\everysetuplocalinterlinespace\else\everysetupglobalinterlinespace\fi}
+
+\def\spac_linespacing_synchronize_local % adapts to the font
+ {\localinterlinespacetrue
+ \setfontparameters
+ \updateraggedskips % funny one here
+ \the\everysetuplocalinterlinespace
+ \localinterlinespacefalse}
+
+\permanent\protected\def\dosetupcheckedinterlinespace#1% often a chain
+ {\edef\p_spac_checked_interlinespace{#1}%
+ \ifempty\p_spac_checked_interlinespace
+ \spac_linespacing_synchronize_local
+ \orelse\ifcsname\namedinterlinespacehash\p_spac_checked_interlinespace\s!parent\endcsname % we could have a \s!check
+ \push_macro_currentinterlinespace
+ \let\currentinterlinespace\p_spac_checked_interlinespace
+ \spac_linespacing_setup_specified_interline_space % \dosetupspecifiedinterlinespaceindeed
+ \iflocalinterlinespace
+ \the\everysetuplocalinterlinespace
+ \else
+ \localinterlinespacetrue
+ \the\everysetuplocalinterlinespace
+ \localinterlinespacefalse
+ \fi
+ \pop_macro_currentinterlinespace
+ \else
+ \normalexpanded{\noexpand\doifelseassignment{\p_spac_checked_interlinespace}%
+ \setupspecifiedinterlinespace\setuprelativeinterlinespace[\p_spac_checked_interlinespace]}%
+ \iflocalinterlinespace
+ \the\everysetuplocalinterlinespace
+ \else
+ \localinterlinespacetrue
+ \the\everysetuplocalinterlinespace
+ \localinterlinespacefalse
+ \fi
+ \fi}
+
+\permanent\protected\def\setuplocalinterlinespace[#1]%
+ {\localinterlinespacetrue
+ \push_macro_currentinterlinespace
+ \setupinterlinespace[#1]%
+ \pop_macro_currentinterlinespace
+ \localinterlinespacefalse}
+
+\aliased\let\switchtointerlinespace\setuplocalinterlinespace
+
+%D Helpers
+
+\newskip \s_spac_lastskip
+\newdimen\d_spac_prevdepth
+\newcount\c_spac_spacefactor
+\newdimen\d_spac_prevcontent % set by lua
+
+% \permanent\overloaded\protected\def\removelastskip
+% {\ifvmode\ifzeropt\lastskip\else\vskip-\lastskip\fi\fi}
+
+\permanent\def\doifoutervmode
+ {\unless\ifvmode
+ \expandafter\gobbleoneargument
+ \orelse\ifinner
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\dosomebreak#1% should be replaced by something \blank
+ {\doifoutervmode
+ {\s_spac_lastskip\lastskip
+ \removelastskip
+ #1\relax
+ \ifzeropt\s_spac_lastskip
+ % avoid interference with footnotes
+ \else
+ \vskip\s_spac_lastskip
+ \fi}}
+
+\permanent\protected\def\packed
+ {\nointerlineskip}
+
+\permanent\protected\def\godown[#1]%
+ {\relax
+ \ifhmode\endgraf\fi
+ \ifvmode\nointerlineskip\vskip#1\relax\fi}
+
+\permanent\protected\def\smallskip{\vskip\smallskipamount}
+\permanent\protected\def\medskip {\vskip\medskipamount}
+\permanent\protected\def\bigskip {\vskip\bigskipamount}
+
+\permanent\protected\def\smallbreak
+ {\par
+ \ifvmode\ifdim\lastskip<\smallskipamount
+ \removelastskip
+ \penalty-\plusfifty
+ \smallskip
+ \fi\fi}
+
+\permanent\protected\def\medbreak
+ {\par
+ \ifvmode\ifdim\lastskip<\medskipamount
+ \removelastskip
+ \penalty-\plusonehundred
+ \medskip
+ \fi\fi}
+
+\permanent\protected\def\bigbreak
+ {\par
+ \ifvmode\ifdim\lastskip<\bigskipamount
+ \removelastskip
+ \penalty-\plustwohundred
+ \bigskip
+ \fi\fi}
+
+\permanent\protected\def\break {\penalty-\plustenthousand} % can be hmode or vmode
+\permanent\protected\def\nobreak {\penalty \plustenthousand} % can be hmode or vmode
+\permanent\protected\def\allowbreak{\penalty \zerocount} % can be hmode or vmode
+
+\permanent\protected\def\goodbreak {\par\ifvmode\penalty-\plusfivehundred\relax\fi} % forces vmode
+\permanent\protected\def\filbreak {\par\ifvmode\vfil\penalty-\plustwohundred\vfilneg\fi} % forces vmode
+
+%D Made slightly more readable:
+
+\permanent\protected\def\vglue {\afterassignment\spac_helpers_vglue_indeed\s_spac_lastskip=}
+\permanent\protected\def\hglue {\afterassignment\spac_helpers_hglue_indeed\s_spac_lastskip=}
+\permanent\protected\def\topglue{\par\ifvmode\nointerlineskip\vglue-\topskip\vglue\fi}
+
+\def\spac_helpers_vglue_indeed
+ {\par
+ \ifvmode
+ \d_spac_prevdepth\prevdepth
+ \hrule\s!height\zeropoint
+ \nobreak
+ \vskip\s_spac_lastskip
+ \prevdepth\d_spac_prevdepth
+ \fi}
+
+\def\spac_helpers_hglue_indeed
+ {\dontleavehmode
+ \c_spac_spacefactor\spacefactor
+ \vrule\s!width\zeropoint
+ \nobreak
+ \hskip\s_spac_lastskip
+ \spacefactor\c_spac_spacefactor}
+
+%D We adapt plain's \type {\removelastskip} a bit:
+
+\pushoverloadmode
+
+\permanent\overloaded\protected\def\removelastskip % also in supp-box
+ {\ifvmode\ifzeropt\lastskip\else\vskip-\lastskip\fi\fi}
+
+\popoverloadmode
+
+% The whitespace handler. We could cache settings but normally there are not
+% that many in a set.
+
+\installcorenamespace{whitespacemethod}
+
+\newskip \s_spac_whitespace_parskip \s_spac_whitespace_parskip\zeropoint
+\newconditional\c_spac_whitespace_flexible \settrue\c_spac_whitespace_flexible
+\newconstant \c_spac_whitespace_grid_mode % option in layout / 1=permit_half_lines
+
+%def\v_spac_whitespace_current{\zeropoint}
+\let\v_spac_whitespace_current\v!none
+
+\permanent\tolerant\protected\def\setupwhitespace[#1]%
+ {\ifarguments
+ \spac_whitespace_setup_nop
+ \orelse\iftok{#1}\emptytoks
+ \spac_whitespace_setup_nop
+ \else
+ \edef\v_spac_whitespace_current{#1}%
+ \spac_whitespace_setup
+ \fi}
+
+\permanent\protected\def\spac_whitespace_setup_nop
+ {\ifx\v_spac_whitespace_current\v!none\else
+ \spac_whitespace_setup
+ \fi}
+
+\aliased\let\synchronizewhitespace\spac_whitespace_setup_nop
+
+\def\spac_whitespace_setup % quick test for no list
+ {\ifcsname\??whitespacemethod\v_spac_whitespace_current\endcsname
+ \lastnamedcs
+ \else
+ \expandafter\processcommalist\expandafter[\v_spac_whitespace_current]\spac_whitespace_setup_method % can be raw
+ \fi\relax
+ \ifgridsnapping
+ \spac_whitespace_setup_grid
+ \else
+ \spac_whitespace_setup_normal
+ \fi
+ \parskip\s_spac_whitespace_parskip}
+
+\def\spac_whitespace_setup_normal
+ {\ifconditional\c_spac_whitespace_flexible \else
+ \s_spac_whitespace_parskip\plusone\s_spac_whitespace_parskip
+ \fi}
+
+\def\spac_whitespace_setup_grid
+ {\setfalse\c_spac_whitespace_flexible
+ \ifdim\s_spac_whitespace_parskip>\zeropoint
+ \s_spac_whitespace_parskip
+ \ifcase\c_spac_whitespace_grid_mode
+ \baselineskip
+ \or
+ \ifdim\scratchdimen=\baselineskip % maybe range
+ \baselineskip
+ \else
+ \numexpr\s_spac_whitespace_parskip/\dimexpr.5\lineheight\relax\relax\dimexpr.5\lineheight\relax
+ \fi
+ \else
+ \baselineskip
+ \fi
+ \fi}
+
+\permanent\protected\def\installwhitespacemethod#1#2%
+ {\defcsname\??whitespacemethod#1\endcsname{#2}}
+
+\installwhitespacemethod \v!fix {}
+\installwhitespacemethod \v!fixed {\setfalse\c_spac_whitespace_flexible}
+\installwhitespacemethod \v!flexible {\settrue \c_spac_whitespace_flexible}
+
+\installwhitespacemethod \v!line {\s_spac_whitespace_parskip \baselineskip}
+\installwhitespacemethod \v!halfline {\s_spac_whitespace_parskip .5\baselineskip}
+\installwhitespacemethod \v!quarterline {\s_spac_whitespace_parskip.25\baselineskip}
+\installwhitespacemethod \v!none {\s_spac_whitespace_parskip \zeropoint}
+\installwhitespacemethod \v!big {\s_spac_whitespace_parskip \bigskipamount}
+\installwhitespacemethod \v!medium {\s_spac_whitespace_parskip \medskipamount}
+\installwhitespacemethod \v!small {\s_spac_whitespace_parskip \smallskipamount}
+
+\installwhitespacemethod \s!default {\spac_whitespace_setup_nop} % also covers none
+
+\def\spac_whitespace_setup_method#1%
+ {\ifcsname\??whitespacemethod#1\endcsname
+ \lastnamedcs
+ \else
+ \s_spac_whitespace_parskip#1\fi
+ \relax}
+
+\permanent\protected\def\forgetparskip
+ {\s_spac_whitespace_parskip\zeropoint
+ \parskip\zeropoint
+ \let\v_spac_whitespace_current\v!none}
+
+\appendtoks
+ \forgetparskip
+\to \everyforgetall
+
+% \installwhitespacemethod \s!unknown {\s_spac_whitespace_parskip\commalistelement\relax}
+%
+% \def\spac_whitespace_setup_method#1%
+% {\csname\??whitespacemethod\ifcsname\??whitespacemethod#1\endcsname#1\else\s!unknown\endcsname\relax}
+
+\permanent\protected\def\nowhitespace{\directcheckedvspacing\v!nowhite} % {\vspacing[\v!nowhite]}
+\permanent\protected\def\whitespace {\directcheckedvspacing\v!white} % {\vspacing[\v!white]}
+
+\setupwhitespace
+ [\v!none]
+
+% Packed:
+
+% todo: when packed blocks blank, we need to enable forced
+
+\newconditional\c_spac_packed_blank \settrue\c_spac_packed_blank
+\newcount \c_spac_packed_level
+
+\permanent\tolerant\protected\def\startpacked[#1]%
+ {\global\advance\c_spac_packed_level\plusone
+ \par
+ \ifnum\c_spac_packed_level=\plusone \ifvmode
+ \begingroup
+ \whitespace % not combined
+ \directcheckedvspacing\v!disable % \blank[\v!disable]% or \inhibitblank
+ \doifelse{#1}\v!blank\settrue\setfalse\c_spac_packed_blank
+ \setupwhitespace[\v!none]% or \forgetparskip
+ \fi \fi}
+
+\permanent\protected\def\stoppacked
+ {\par
+ \ifnum\c_spac_packed_level=\plusone \ifvmode
+ \endgroup
+ \fi \fi
+ \global\advance\c_spac_packed_level\minusone}
+
+\permanent\protected\def\startunpacked
+ {\directdefaultvspacing % \blank
+ \begingroup}
+
+\permanent\protected\def\stopunpacked
+ {\endgroup
+ \directdefaultvspacing}% \blank}
+
+% \prevdepth crosses pageboundaries!
+%
+% todo: a version that works ok inside a box
+
+% global : outer hsize + keep skips
+% local : inner hsize + reset skips
+
+\installcorenamespace{linesaround}
+
+\let\spac_lines_vbox\vbox
+
+\installtextracker
+ {linecorrection.boxes}
+ {\letfrozen\let\spac_lines_vbox\ruledvbox}
+ {\letfrozen\let\spac_lines_vbox\vbox}
+
+\let\v_spac_lines_around_action_set\relax
+\let\m_spac_lines_around \empty
+
+\newconstant\c_spac_lines_correction_mode
+
+\setvalue{\??linesaround\v!blank }{\blank}
+\letvalue{\??linesaround\empty }\relax
+\setvalue{\??linesaround\s!unknown}{\directcheckedvspacing\m_spac_lines_around} % \blank[\m_spac_lines_around]}
+
+\def\spac_lines_action_around % we used to let this one but it's cleaner this way
+ {\csname\??linesaround % i.e. do it twice
+ \ifcsname\??linesaround\m_spac_lines_around\endcsname\m_spac_lines_around\else\s!unknown\fi
+ \endcsname}
+
+\permanent\tolerant\protected\def\startlinecorrection [#1]{\spac_lines_start_correction\plusone{#1}}
+\permanent\tolerant\protected\def\startlocallinecorrection[#1]{\spac_lines_start_correction\plustwo{#1}}
+
+\permanent\protected\def\spac_lines_start_correction#1#2%
+ {\endgraf
+ \begingroup
+ \setconstant\c_spac_lines_correction_mode#1%
+ \edef\m_spac_lines_around{#2}%
+ \spac_lines_action_around
+ \d_spac_prevdepth\prevdepth
+ \spac_lines_initialize_corrections
+ \offbaselinecorrection % ???
+ \setbox\scratchbox\spac_lines_vbox\bgroup
+ \ifcase\c_spac_lines_correction_mode
+ % nothing
+ \or
+ % global
+ \or
+ % local
+ \setlocalhsize
+ \hsize\localhsize
+ \forgetbothskips
+ \fi
+ \ignorespaces}
+
+\permanent\protected\def\spac_lines_stop_correction
+ {\removeunwantedspaces
+ \egroup
+ \ifgridsnapping
+ \spac_lines_stop_correction_ongrid
+ \else
+ \spac_lines_stop_correction_normal
+ \fi
+ \endgroup}
+
+\protected\def\spac_lines_stop_correction_ongrid
+ {\directcheckedvspacing\v!white % \blank[\v!white]%
+ \spac_lines_action_around
+ \snaptogrid\hpack{\box\scratchbox}%
+ \directcheckedvspacing\v!white
+ \spac_lines_action_around}
+
+\protected\def\spac_lines_stop_correction_normal
+ {\directcheckedvspacing\v!nowhite % \blank[\v!nowhite]%
+ \ifdim\parskip>\zeropoint
+ % too fuzzy otherwise
+ \else
+ % doesn't like whitespace
+ \ifdim\d_spac_prevdepth<\maxdimen
+ \unless\ifdim\d_spac_prevdepth<\zeropoint
+ \ifdim\d_spac_prevdepth<\strutdp \relax
+ \pushlastnode
+ \ifdim\d_spac_prevdepth>\zeropoint
+ \kern-\d_spac_prevdepth
+ \fi
+ \kern\strutdp
+ \prevdepth\strutdp
+ \poplastnode
+ \fi
+ \fi
+ \fi
+ \fi
+ \ifdim\pagegoal<\maxdimen
+ % \blank[\v!white,\the\d_spac_lines_correction_before]% \blank[\v!white]\dotopbaselinecorrection
+ \directcheckedvspacing{\v!white,\the\d_spac_lines_correction_before}% \blank[\v!white]\dotopbaselinecorrection
+ \fi
+ \nointerlineskip % new
+ \noindent % not \dontleavehmode !
+ \ifcase\c_spac_lines_correction_mode
+ % nothing
+ \or
+ % global
+ \hskip-\leftskip % more tricky would be hangindent so we ignore that one
+ \or
+ % local
+ \fi
+ \box\scratchbox
+ \endgraf
+ %
+ % eventually i'll get it right ... (i also need to check all whitespace code elsewhere)
+ %
+ % \blank[\the\d_spac_lines_correction_after]% \dobotbaselinecorrection
+ % \directcheckedvspacing{\the\d_spac_lines_correction_after}% \dobotbaselinecorrection
+ \directcheckedvspacing{\v!white,\the\d_spac_lines_correction_after}% \dobotbaselinecorrection
+ % \allowbreak % new, otherwise problems when many in a row
+ \prevdepth\strutdp
+ \spac_lines_action_around}
+
+\aliased\let\stoplinecorrection \spac_lines_stop_correction
+\aliased\let\stoplocallinecorrection\spac_lines_stop_correction
+
+% todo:
+
+\permanent\protected\def\correctwhitespace
+ {\dowithnextboxcs\correctwhitespacefinish\vbox}
+
+\permanent\protected\def\correctwhitespacefinish
+ {\startbaselinecorrection
+ \flushnextbox
+ \stopbaselinecorrection}
+
+\permanent\protected\def\verticalstrut {\vpack{\hsize\zeropoint\forgetall\strut}}
+\permanent\protected\def\horizontalstrut{\hpack {\strut}}
+
+%D Here follow some presets related to interline spacing and therefore also struts.
+%D The values 2.8, 0.07, 0.72 and 0.28 originate in \INRSTEX, a package that we used
+%D a while after we decided that \LATEX\ was not flexible enough. After that
+%D \CONTEXT\ evolved, from some wrapper code around (old) \LATEX\ (on a floppy
+%D disk), to using modules from \INRSTEX\ (which also fit on a floppy) and finally
+%D all written from scratch. I simply didn't understand all that \TEX\ code at that
+%D time, and it was easier to figure it out myself. But \unknown\ some settings
+%D stayed, as the height|/|depth ratios, and they never proved to be bad ones! The
+%D same is true for the font size relations.
+
+%D \starttabulate
+%D \NC \type {\lineheight} \NC the height of a line \NC \NR
+%D \NC \type {\spacing{number}} \NC adapting the interline space \NC \NR
+%D \NC \type {\normalbaselines} \NC initialize the interline spacing \NC \NR
+%D \NC \type {\setstrut} \NC initialize \type {\strut} \NC \NR
+%D \NC \type {\setnostrut} \NC disable the \type {\strut}, \type {\endstrut}, \type {\begstrut} \NC \NR
+%D \NC \type {\setteststrut} \NC initialize the visual \type {\strut} \NC \NR
+%D \NC \type {\resetteststrut} \NC disable the visual \type {\strut} \NC \NR
+%D \NC \type {\setfontparameters} \NC synchronize parameters with foints \NC \NR
+%D \stoptabulate
+%D
+%D \unknown\ and many more (this is a decades old list).
+%D
+%D The lineheight is the sum of the height and depth of \type {strut}, which is
+%D an invisible blob that can be used to enforce the proper dimensions.
+%D
+%D Such a blob, when placed at the beginning of a paragraph can have side effects
+%D that can be prevented with \type {\dontleavehmode}. Never use \type
+%D {\leavevmode}!
+
+\newdimen\strutdimen
+\newdimen\lineheight
+\newdimen\openlineheight
+\newdimen\openstrutheight
+\newdimen\openstrutdepth
+\newdimen\topskipgap
+
+\newdimen\strutheight
+\newdimen\strutdepth
+\newdimen\struttotal
+\newdimen\strutwidth
+
+\let\m_spac_vertical_baseline_stretch_factor \zerocount
+\let\m_spac_vertical_baseline_shrink_factor \zerocount
+
+\mutable\def\strutheightfactor {.72}
+\mutable\def\strutdepthfactor {.28}
+\mutable\def\baselinefactor {2.8}
+\mutable\def\topskipfactor {1.0}
+\mutable\def\maxdepthfactor {0.5}
+
+\mutable\def\minimumstrutheight {\zeropoint}
+\mutable\def\minimumstrutdepth {\zeropoint}
+
+\mutable\def\normallineheight {\baselinefactor\exheight}
+\mutable\def\minimumlinedistance {\lineskip}
+
+\mutable\let\spacingfactor \plusone
+
+\mutable\def\systemtopskipfactor {\topskipfactor}
+\mutable\def\systemmaxdepthfactor{\maxdepthfactor}
+
+\ifdefined\globalbodyfontsize \else
+ \newdimen\globalbodyfontsize
+ \globalbodyfontsize=12pt
+\fi
+
+\ifdefined\normalizedbodyfontsize \else
+ \def\normalizedbodyfontsize{12pt}
+\fi
+
+\permanent\protected\def\topskipcorrection
+ {\simpletopskipcorrection
+ \vskip-\struttotal
+ \verticalstrut}
+
+\permanent\protected\def\simpletopskipcorrection
+ {\ifdim\topskip>\openstrutheight
+ % == \vskip\topskipgap
+ \vskip\topskip
+ \vskip-\openstrutheight
+ \fi}
+
+\permanent\protected\def\settopskip % the extra test is needed for the lbr family
+ {\topskip
+ \ifgridsnapping
+ \zeropoint
+ \else
+ \systemtopskipfactor\globalbodyfontsize
+ \ifcase\bottomraggednessmode % ragged bottom
+ \s!plus5\globalbodyfontsize
+ \fi
+ \fi
+ %\relax
+ \topskipgap\topskip
+ \advance\topskipgap -\openstrutheight\relax
+ \ifdim\minimumstrutheight>\zeropoint
+ \ifdim\topskip<\minimumstrutheight
+ \topskip\minimumstrutheight\relax
+ \fi
+ \else
+ \ifdim\topskip<\strutheightfactor\openlineheight
+ \topskip\strutheightfactor\openlineheight\relax
+ \fi
+ \fi}
+
+\permanent\protected\def\setmaxdepth
+ {\maxdepth\systemmaxdepthfactor\globalbodyfontsize}
+
+\newskip \usedbaselineskip % These used to be \normal... but that isn't pretty
+\newskip \usedlineskip % in the token interface, so thes few now have new
+\newdimen\usedlineskiplimit % names. They are public but not really user commands.
+
+\permanent\protected\def\normalbaselines
+ {\baselineskip \usedbaselineskip
+ \lineskip \usedlineskip
+ \lineskiplimit\usedlineskiplimit}
+
+\permanent\protected\def\flexiblebaselines
+ {\baselineskip \usedbaselineskip
+ \lineskip 1\usedlineskip \s!plus 1\s!fill
+ \lineskiplimit\usedlineskiplimit}
+
+\permanent\protected\def\setnormalbaselines % used in overload
+ {\ifdim\normallineheight>\zeropoint
+ \lineheight\normallineheight
+ \fi
+ \openlineheight\spacingfactor\lineheight
+ \openstrutheight \ifdim\minimumstrutheight>\zeropoint
+ \minimumstrutheight % new
+ \else
+ \strutheightfactor\openlineheight
+ \fi
+ \openstrutdepth \ifdim\minimumstrutdepth>\zeropoint
+ \minimumstrutdepth % new
+ \else
+ \strutdepthfactor \openlineheight
+ \fi
+ \ifdim\dimexpr\minimumstrutdepth+\minimumstrutheight\relax>\zeropoint
+ \openlineheight\dimexpr\openstrutheight+\openstrutdepth\relax % new
+ \fi
+ \usedbaselineskip\openlineheight
+ \ifgridsnapping\else
+ \s!plus \m_spac_vertical_baseline_stretch_factor\openlineheight
+ \s!minus\m_spac_vertical_baseline_shrink_factor \openlineheight
+ \fi
+ \usedlineskip\minimumlinedistance\relax % \onepoint\relax
+ \usedlineskiplimit\zeropoint\relax
+ \normalbaselines}
+
+\permanent\protected\def\spacing#1% vertical
+ {\ifgridsnapping
+ \let\spacingfactor\plusone
+ \else
+ \permanent\edef\spacingfactor{#1}%
+ \fi
+ \edef\systemtopskipfactor {\thewithoutunit\dimexpr#1\dimexpr\topskipfactor \points}%
+ \edef\systemmaxdepthfactor{\thewithoutunit\dimexpr#1\dimexpr\maxdepthfactor\points}%
+ \setnormalbaselines
+ \setstrut}
+
+% \protected\def\forgetverticalstretch % \forgetspacing
+% {\spacing\plusone}
+
+\permanent\protected\def\forgetverticalstretch
+ {\let\spacingfactor \plusone
+ \let\systemtopskipfactor \topskipfactor
+ \let\systemmaxdepthfactor\maxdepthfactor
+ \setnormalbaselines
+ \setstrut}
+
+\appendtoks
+ \forgetverticalstretch
+\to \everyforgetall % needed in otr
+
+%D Sometimes one needs to freeze the interlinespacing
+%D
+%D \starttyping
+%D \rm \saveinterlinespace .... {\ss \restoreinterlinespace .... \endgraf}
+%D \stoptyping
+
+\aliased\let\restoreinterlinespace\relax
+
+\permanent\protected\def\saveinterlinespace
+ {\enforced\permanent\protected\edef\restoreinterlinespace
+ {\lineheight \the\lineheight
+ \openstrutheight \the\openstrutheight
+ \openstrutdepth \the\openstrutdepth
+ \openlineheight \the\openlineheight
+ \usedbaselineskip \the\usedbaselineskip
+ \usedlineskip \the\usedlineskip
+ \usedlineskiplimit \the\usedlineskiplimit
+ \noexpand\def\noexpand\normallineheight{\the\dimexpr\normallineheight}%
+ \noexpand\normalbaselines}}
+
+%D This is the plain definition:
+%D
+%D \starttyping
+%D \def\strut{\relax\ifmmode\copy\strutbox\else\unhcopy\strutbox\fi}
+%D \stoptyping
+%D
+%D which could be:
+%D
+%D \starttyping
+%D \def\strut{\relax\ifmmode\copy\else\unhcopy\fi\strutbox}
+%D \stoptyping
+%D
+%D But we do things differently.
+
+\newbox\strutbox
+
+\setbox\strutbox\hpack{\vrule\s!height8.5pt\s!depth3.5pt\s!width\zeropoint} % just a start
+
+% \permanent\protected\def\strut{\relax\ifmmode\copy\else\unhcopy\fi\strutbox}
+%
+% \protected\def\strut
+% {\relax
+% \ifmmode\copy\else\dontleavehmode\unhcopy\fi\strutbox}
+%
+% \aliased\let\normalstrut\strut
+
+%D The double \type {\hbox} construction enables us to backtrack boxes.
+
+\overloaded\let\strutht\undefined \newdimen\strutht
+\overloaded\let\strutdp\undefined \newdimen\strutdp
+
+\permanent\protected\def\setstrut
+ {\ifgridsnapping
+ \setstrutgridyes
+ \else
+ \setstrutgridnop
+ \fi}
+
+\permanent\protected\def\setstrutgridyes
+ {\strutht\spacingfactor\dimexpr
+ \ifdim\minimumstrutheight>\zeropoint
+ \minimumstrutheight
+ \else
+ \strutheightfactor\dimexpr\normallineheight
+ \fi
+ \relax
+ \strutdp\dimexpr
+ \ifdim\minimumstrutdepth>\zeropoint
+ \minimumstrutdepth
+ \else
+ \normallineheight-\strutht
+ \fi
+ \relax
+ \dosetstrut}
+
+\permanent\protected\def\setstrutgridnop
+ {\strutht\spacingfactor\dimexpr
+ \ifdim\minimumstrutheight>\zeropoint
+ \minimumstrutheight
+ \else
+ \strutheightfactor\dimexpr\normallineheight
+ \fi
+ \relax
+ \strutdp\spacingfactor\dimexpr
+ \ifdim\minimumstrutdepth>\zeropoint
+ \minimumstrutdepth
+ \else
+ \strutdepthfactor\dimexpr\normallineheight
+ \fi
+ \relax
+ \dosetstrut}
+
+\permanent\protected\def\setcharstrut#1%
+ {\setbox\strutbox\hbox{#1}% no \hpack, in case we have smallcaps
+ \strutht\ht\strutbox
+ \strutdp\dp\strutbox
+ \dosetstrut}
+
+\permanent\protected\def\settightstrut
+ {\setcharstrut{(}}
+
+\permanent\protected\def\setfontstrut
+ {\setcharstrut{(gplQT}}
+
+\permanent\protected\def\setcapstrut% could be M, but Q has descender
+ {\setcharstrut{Q}}
+
+%D Handy for math (used in mathml):
+
+\permanent\protected\def\charhtstrut
+ {\begingroup
+ \setcharstrut{GJY}%
+ \vrule\s!width\zeropoint\s!depth\zeropoint\s!height\strutht
+ \endgroup}
+
+\permanent\protected\def\chardpstrut
+ {\begingroup
+ \setcharstrut{gjy}%
+ \vrule\s!width\zeropoint\s!depth\strutdp\s!height\zeropoint
+ \endgroup}
+
+%D Because of all the callbacks in mkiv, we avoid unnecessary boxes ... maybe use an
+%D attribute so that we can tag boxes that don't need a treatment; tests with using
+%D an attribute so far have shown that it's slower because testing the attribute
+%D takes time too.
+
+\permanent\protected\def\dosetstrut
+ {\enforced\let\strut\normalstrut
+ \ifabsnum\dimexpr\strutht+\strutdp-\lineheight\relax<\plustwo
+ % compensate rounding error /- 1sp to avoid too many
+ % 1sp baselineskips in for instance verbatim
+ % \strutht\dimexpr\lineheight-\strutdp\relax
+ % better:
+ \strutdp\dimexpr\lineheight-\strutht\relax
+ \struttotal\lineheight
+ \else
+ \struttotal\dimexpr\strutht+\strutdp\relax
+ \fi
+ \strutheight\strutht
+ \strutdepth \strutdp
+ \ifdim\strutwidth=\zeropoint
+ \spac_struts_set_hide
+ \else
+ \spac_struts_set_vide
+ \fi}
+
+\def\spac_struts_set_hide
+ {\setbox\strutbox\hpack
+ {\vrule
+ \s!width \zeropoint
+ \s!height\strutht
+ \s!depth \strutdp}}
+
+\newconstant\c_strut_visual_mode
+
+\def\spac_struts_set_vide
+ {\setbox\strutbox\hpack % at some time this extra wrapping was needed
+ {\spac_struts_vide_hbox to \zeropoint
+ {\ifcase\c_strut_visual_mode
+ \spac_struts_black
+ \or
+ \spac_struts_color
+ \else
+ \spac_struts_black
+ \fi}}}
+
+\def\spac_struts_black
+ {\vrule
+ \s!width \strutwidth
+ \s!height\strutht
+ \s!depth \strutdp
+ \hss}
+
+\def\spac_struts_color
+ {\hss % new, will be option
+ \scratchwidth.1\struthtdp
+ \begingroup
+ \directcolor[f:b:t]%
+ \vrule
+ \s!width \scratchwidth
+ \s!height\strutht
+ \s!depth \strutdp
+ \kern-\scratchwidth
+ \vrule
+ \s!width \scratchwidth
+ \s!height\zeropoint
+ \s!depth \strutdp
+ \endgroup
+ \kern-.625\scratchwidth
+ \vrule
+ \s!width .25\scratchwidth
+ \s!height\strutht
+ \s!depth \strutdp
+ \hss}
+
+\let\spac_struts_vide_hbox\hbox % overloaded in trac-vis.mkiv
+
+%D The dimen \type {\struttotal} holds the exact size of the strut; occasionally a
+%D one scaled point difference can show up with the lineheight. This is more
+%D efficient (less callbacks):
+
+\newbox\b_spac_struts_empty \setbox\b_spac_struts_empty\emptyhbox
+
+\def\spac_struts_set_hide
+ {\setbox\strutbox\copy\b_spac_struts_empty
+ \ht\strutbox\strutht
+ \dp\strutbox\strutdp}
+
+\permanent\protected\def\strut % still callbacks for \hbox{\strut}
+ {\relax
+ \dontleavehmode
+ \copy\strutbox}
+
+% \protected\def\strut % slightly faster
+% {\relax
+% \ifmmode\copy\else\dontleavehmode\unhcopy\fi\strutbox}
+
+\aliased\let\normalstrut\strut
+
+\permanent\protected\def\halfstrut
+ {\relax
+ \dontleavehmode
+ \begingroup
+ \setbox\scratchbox\copy\strutbox
+ \ht\scratchbox\dimexpr\strutht/\plustwo\relax
+ \dp\scratchbox\dimexpr\strutdp/\plustwo\relax
+ \box\scratchbox
+ \endgroup}
+
+\permanent\protected\def\quarterstrut
+ {\relax
+ \dontleavehmode
+ \begingroup
+ \setbox\scratchbox\copy\strutbox
+ \ht\scratchbox\dimexpr\strutht/\plusfour\relax
+ \dp\scratchbox\dimexpr\strutdp/\plusfour\relax
+ \box\scratchbox
+ \endgroup}
+
+\permanent\protected\def\depthstrut
+ {\relax
+ \dontleavehmode
+ \begingroup
+ \setbox\scratchbox\copy\strutbox
+ \ht\scratchbox\dimexpr\strutht-\struthtdp/\plustwo\relax % assumes that ht > lineheight/2
+ \box\scratchbox
+ \endgroup}
+
+\permanent\protected\def\halflinestrut
+ {\relax
+ \dontleavehmode
+ \begingroup
+ \setbox\scratchbox\copy\strutbox
+ \ht\scratchbox\dimexpr\strutht-.5\strutht-.5\strutdp\relax
+ \box\scratchbox
+ \endgroup}
+
+\permanent\protected\def\noheightstrut
+ {\relax
+ \dontleavehmode
+ \begingroup
+ \setbox\scratchbox\copy\strutbox
+ \ht\scratchbox\zeropoint
+ \box\scratchbox
+ \endgroup}
+
+%D Sometimes a capstrut comes in handy
+%D
+%D \starttabulate[|Tl|l|l|]
+%D \NC yes \NC normal strut \NC {\showstruts\setupstrut[yes]\strut} \NC \NR
+%D \NC no \NC no strut \NC {\showstruts\setupstrut[no]\strut} \NC \NR
+%D \NC kap \NC a capital strut (i.e. Q) \NC {\showstruts\setupstrut[cap]\strut} \NC \NR
+%D \NC A B \unknown \NC a character strut (e.g. A) \NC {\showstruts\setupstrut[A]\strut} \NC \NR
+%D \NC \NC a normal strut \NC {\showstruts\setupstrut\strut} \NC \NR
+%D \stoptabulate
+%D
+%D Beware: using an unknown value results in char struts.
+
+\installcorenamespace{struts}
+
+\permanent\tolerant\protected\def\setupstrut[#1]%
+ {\edef\m_strut{#1}%
+ \ifcsname\??struts\m_strut\endcsname
+ \lastnamedcs
+ \else
+ \setcharstrut\m_strut
+ \fi}
+
+\permanent\protected\def\synchronizestrut#1% no [] parsing, faster for internal
+ {\edef\m_strut{#1}%
+ \ifcsname\??struts\m_strut\endcsname
+ \lastnamedcs
+ \else
+ \setcharstrut\m_strut
+ \fi}
+
+\permanent\protected\def\dosynchronizestrut#1% no [] parsing, faster for internal
+ {\ifcsname\??struts#1\endcsname
+ \lastnamedcs
+ \else
+ \setcharstrut{#1}%
+ \fi}
+
+\permanent\protected\def\showstruts % adapts .. is wrong
+ {\c_strut_visual_mode\zerocount
+ \setteststrut
+ \settestcrlf}
+
+\permanent\protected\def\showcolorstruts % adapts .. is wrong
+ {\c_strut_visual_mode\plusone
+ \setteststrut
+ \settestcrlf}
+
+\permanent\protected\def\setteststrut
+ {\strutwidth.8\onepoint
+ \setstrut}
+
+\permanent\protected\def\dontshowstruts
+ {\unsetteststrut
+ \settestcrlf}
+
+\permanent\protected\def\unsetteststrut
+ {\strutwidth\zeropoint
+ \setstrut}
+
+\mutable\def\autostrutfactor{1.1}
+
+\permanent\protected\def\setautostrut
+ {\begingroup
+ \setbox\scratchbox\copy\strutbox
+ \setstrut
+ \ifdim\strutht>\autostrutfactor\ht\scratchbox
+ \endgroup \setstrut
+ \orelse\ifdim\strutdp>\autostrutfactor\dp\scratchbox
+ \endgroup \setstrut
+ \else
+ \endgroup
+ \fi}
+
+\newbox\nostrutbox \setbox\nostrutbox\emptyhbox
+
+\newtoks\everysetnostrut
+
+\permanent\protected\def\setnostrut
+ {\the\everysetnostrut}
+
+\appendtoks
+ \setbox\strutbox\copy\nostrutbox
+ \enforced\let\strut \empty
+ \enforced\let\endstrut\empty
+ \enforced\let\begstrut\empty
+\to \everysetnostrut
+
+%D When enabled, sigstruts will remove themselves if nothing goes inbetween. For
+%D practical reasons we define some boundary characters here.
+
+\permanent\protected\def\leftboundary {\protrusionboundary\plusone}
+\permanent\protected\def\rightboundary {\protrusionboundary\plustwo}
+\permanent\protected\def\signalcharacter{\boundary\plusone\char\zerocount\boundary\plustwo} % not the same as strut signals
+
+\newsignal\d_spac_struts_signal \setfalse\sigstruts
+
+\permanent\protected\def\begstrut
+ {\relax\ifcase\strutht
+ % \ignorespaces % maybe
+ \else
+ \spac_struts_beg
+ \fi}
+
+\def\spac_struts_beg
+ {\ifconditional\sigstruts
+ \spac_struts_beg_signal
+ \else
+ \spac_struts_beg_normal
+ \fi
+ \ignorespaces}
+
+\def\spac_struts_beg_signal
+ {\noindent\horizontalstrut
+ \penalty\plustenthousand
+ \hskip-\d_spac_struts_signal
+ \hskip\d_spac_struts_signal}
+
+\def\spac_struts_beg_normal
+ {\boundary\plusone
+ \strut
+ %\boundary\plusone
+ \penalty\plustenthousand
+ %\boundary\plusone
+ \hskip\zeropoint}
+
+\permanent\protected\def\endstrut
+ {\relax\ifhmode
+ \ifcase\strutht
+ % \removeunwantedspaces % maybe
+ \else
+ \spac_struts_end
+ \fi
+ \fi}
+
+\def\spac_struts_end
+ {\ifconditional\sigstruts
+ \spac_struts_end_signal
+ \else
+ \spac_struts_end_normal
+ \fi}
+
+\def\spac_struts_end_signal
+ {\ifdim\lastskip=\d_spac_struts_signal
+ \unskip
+ \unskip
+ \unpenalty
+ \setbox\scratchbox\lastbox
+ \else
+ \penalty\plustenthousand
+ \hskip\zeropoint
+ \strut
+ \fi}
+
+\def\spac_struts_end_normal
+ {\removeunwantedspaces
+ \penalty\plustenthousand
+ %\boundary\plustwo
+ \hskip\zeropoint
+ %\boundary\plustwo
+ \strut
+ \boundary\plustwo}
+
+% unsave:
+%
+% \def\pseudostrut
+% {\bgroup
+% \setnostrut
+% \normalstrut
+% \egroup}
+%
+% try:
+%
+% \startchemie
+% \chemie[ONE,Z0,SB15,MOV1,SB15,Z0][C,C]
+% \stopchemie
+%
+% so:
+
+\permanent\protected\def\pseudostrut
+ {\noindent} % better: \dontleavehmode
+
+\aliased\let\pseudobegstrut\pseudostrut
+\aliased\let\pseudoendstrut\removeunwantedspaces
+
+\permanent\protected\def\resetteststrut
+ {\strutwidth\zeropoint
+ \setstrut}
+
+\ifdefined\setfontparameters \else
+ \def\setfontparameters{\the\everybodyfont}
+\fi
+
+%D Keyword based strutting:
+
+\letcsname\??struts\v!yes \endcsname\setstrut
+\letcsname\??struts\v!auto \endcsname\setautostrut
+\letcsname\??struts\v!no \endcsname\setnostrut
+\letcsname\??struts\v!cap \endcsname\setcapstrut
+\letcsname\??struts\v!fit \endcsname\setfontstrut
+\letcsname\??struts\v!line \endcsname\setstrut
+\letcsname\??struts\s!default\endcsname\setstrut
+\letcsname\??struts\empty \endcsname\setstrut
+
+%D Handy:
+
+\permanent\def\baselinedistance{\the\lineheight}
+
+%D We need \type {\normaloffinterlineskip} because the new definition contains an
+%D assignment, and |<|don't ask me why|>| this assignment gives troubles in for
+%D instance the visual debugger.
+
+\pushoverloadmode
+
+\overloaded\permanent\protected\def\offinterlineskip
+ {\baselineskip-\thousandpoint
+ \lineskip \zeropoint
+ \lineskiplimit\maxdimen
+ % We also need this here now; thanks to taco for figuring that out!
+ \let\minimumlinedistance\zeropoint}
+
+\overloaded\permanent\protected\def\nointerlineskip
+ {\prevdepth-\thousandpoint}
+
+\aliased\let\normaloffinterlineskip\offinterlineskip % knuth's original
+
+\popoverloadmode
+
+%D This is tricky. The prevdepth value is still set to the last one even if there is
+%D nothing on the page. The same is true for prevgraf, which doesn't resemble the
+%D value on the current page.
+%D
+%D So, here we kick in a checker but it has to happen after the output group and it
+%D only has to be done once (output can trigger itself!).
+%D
+%D However, prevgraf is somehow bound to hangindent so we can get very nasty side
+%D effects. So, in tne end we use our own variable!
+
+\ifdefined\getnofpreviouslines
+ % defined public at the lua end
+\else
+ \let\getnofpreviouslines\!!zerocount
+\fi
+
+\protected\def\page_otr_synchronize_page_yes
+ {\aftergroup\page_otr_synchronize_page_indeed
+ \global\enforced\let\page_otr_synchronize_page\relax}
+
+% This has to become an otr method: \s!page_otr_command_synchonize_page
+
+\protected\def\page_otr_synchronize_page_indeed
+ {\ifx\currentoutputroutine\s!multicolumn\else\clf_synchronizepage\fi
+ \global\enforced\let\page_otr_synchronize_page\page_otr_synchronize_page_yes}
+
+\let\page_otr_synchronize_page\page_otr_synchronize_page_yes
+
+\appendtoks
+ \page_otr_synchronize_page
+\to \everyaftershipout
+
+%D My own one:
+
+\protected\def\spac_helpers_push_interlineskip_yes
+ {\enforced\edef\oninterlineskip
+ {\baselineskip \the\baselineskip
+ \lineskip \the\lineskip
+ \lineskiplimit\the\lineskiplimit
+ \noexpand\edef\noexpand\minimumlinedistance{\the\dimexpr\minimumlinedistance}%
+ \enforced\let\noexpand\offinterlineskip\noexpand\normaloffinterlineskip}} % \noexpand not needed
+
+\protected\def\spac_helpers_push_interlineskip_nop
+ {\enforced\let\oninterlineskip\setnormalbaselines}
+
+\pushoverloadmode
+
+\overloaded\permanent\protected\def\offinterlineskip
+ {\ifdim\baselineskip>\zeropoint
+ \spac_helpers_push_interlineskip_yes
+ \else
+ \spac_helpers_push_interlineskip_nop
+ \fi
+ \normaloffinterlineskip}
+
+\permanent\let\oninterlineskip\relax
+
+\popoverloadmode
+
+\permanent\protected\def\resetpenalties#1%
+ {\ifdefined#1%
+ \frozen#1\minusone
+ \fi}
+
+\permanent\protected\def\setpenalties#1#2#3%
+ {\ifdefined#1% space before #3 prevents lookahead problems, needed when #3=text
+ \frozen#1\numexpr#2+\plusone\relax\space\doexpandedrecurse{\the\numexpr#2\relax}{ #3}\zerocount\relax
+ \fi}
+
+%D \macros
+%D {keeplinestogether}
+%D
+%D Dirty hack, needed in margin content that can run of a page.
+
+% just before margintexts ... will eventually be done differently in mkiv using
+% attributes
+
+\newcount\c_spac_keep_lines_together
+
+\aliased\let\restoreinterlinepenalty\relax
+
+\protected\def\spac_penalties_restore
+ {\global\enforced\let\restoreinterlinepenalty\relax
+ \overloaded\global\resetpenalties\interlinepenalties
+ \global\c_spac_keep_lines_together\zerocount}
+
+\permanent\protected\def\keeplinestogether#1%
+ {\ifnum#1>\c_spac_keep_lines_together
+ \global\c_spac_keep_lines_together#1%
+ \overloaded\global\setpenalties\interlinepenalties\c_spac_keep_lines_together\plustenthousand
+ \global\enforced\let\restoreinterlinepenalty\spac_penalties_restore
+ \fi}
+
+\immutable\integerdef\defaultdisplaywidowpenalty 50
+\immutable\integerdef\defaultwidowpenalty 2000 % was: 1000
+\immutable\integerdef\defaultclubpenalty 2000 % was: 800
+\immutable\integerdef\defaultbrokenpenalty 100
+\immutable\integerdef\defaultdoublehyphendemerits 10000
+\immutable\integerdef\defaultfinalhyphendemerits 5000
+\immutable\integerdef\defaultadjdemerits 10000
+
+\immutable\integerdef\defaultgriddisplaywidowpenalty 0
+\immutable\integerdef\defaultgridwidowpenalty 0
+\immutable\integerdef\defaultgridclubpenalty 0
+\immutable\integerdef\defaultgridbrokenpenalty 0
+\immutable\integerdef\defaultgriddoublehyphendemerits 10000 % always was so
+\immutable\integerdef\defaultgridfinalhyphendemerits 5000 % always was so
+\immutable\integerdef\defaultgridadjdemerits 10000 % always was so
+
+\permanent\protected\def\nopenalties
+ {\frozen\widowpenalty \zerocount
+ \frozen\clubpenalty \zerocount
+ \frozen\brokenpenalty \zerocount
+ \frozen\doublehyphendemerits\zerocount
+ \frozen\finalhyphendemerits \zerocount
+ \frozen\adjdemerits \zerocount}
+
+\permanent\protected\def\setdefaultpenalties
+ {\directsetup{\systemsetupsprefix\s!default}}
+
+\startsetups [\systemsetupsprefix\s!reset]
+ \resetpenalties\widowpenalties
+ \resetpenalties\clubpenalties
+ \resetpenalties\interlinepenalties
+\stopsetups
+
+%D We use \directsetup because it's faster and we know there is no csl:
+
+\startsetups [\systemsetupsprefix\s!default]
+
+ \directsetup{\systemsetupsprefix\s!reset}
+
+ \frozen\widowpenalty \defaultwidowpenalty
+ \frozen\clubpenalty \defaultclubpenalty
+ \frozen\displaywidowpenalty \defaultdisplaywidowpenalty
+ \frozen\brokenpenalty \defaultbrokenpenalty
+ \frozen\doublehyphendemerits\defaultdoublehyphendemerits
+ \frozen\finalhyphendemerits \defaultfinalhyphendemerits
+ \frozen\adjdemerits \defaultadjdemerits
+
+\stopsetups
+
+\startsetups [\v!grid] [\systemsetupsprefix\s!default]
+
+ \directsetup{\systemsetupsprefix\s!reset}
+
+ \frozen\widowpenalty \defaultgridwidowpenalty
+ \frozen\clubpenalty \defaultgridclubpenalty
+ \frozen\displaywidowpenalty \defaultgriddisplaywidowpenalty
+ \frozen\brokenpenalty \defaultgridbrokenpenalty
+ \frozen\doublehyphendemerits\defaultgriddoublehyphendemerits
+ \frozen\finalhyphendemerits \defaultgridfinalhyphendemerits
+ \frozen\adjdemerits \defaultgridadjdemerits
+
+\stopsetups
+
+%D As an illustration:
+
+\startsetups [\systemsetupsprefix\v!strict]
+
+ \directsetup{\systemsetupsprefix\s!reset}
+
+ \setpenalties \widowpenalties \plustwo \maxdimen
+ \setpenalties \clubpenalties \plustwo \maxdimen
+ \frozen \brokenpenalty \maxdimen
+ \frozen \doublehyphendemerits \defaultdoublehyphendemerits
+ \frozen \finalhyphendemerits \defaultfinalhyphendemerits
+ \frozen \adjdemerits \defaultadjdemerits
+
+\stopsetups
+
+\setdefaultpenalties % will happen later in \setuplayout
+
+%D To be checked:
+
+\newbox\b_spac_struts_saved
+
+\permanent\protected\def\savestrut {\setbox\b_spac_struts_saved\copy\strutbox}
+\permanent\protected\def\savedstrut{\copy \b_spac_struts_saved}
+
+%D Good old blank redone:
+
+%definesystemattribute[kernchars] [public]
+\definesystemattribute[skipcategory] [public]
+\definesystemattribute[skippenalty] [public]
+\definesystemattribute[skiporder] [public]
+\definesystemattribute[snapmethod] [public]
+\definesystemattribute[snapvbox] [public]
+%definesystemattribute[snapcategory] [public]
+
+% TODO: NAMED SNAPPERS
+
+\installcorenamespace{gridsnappers}
+\installcorenamespace{gridsnapperattributes}
+\installcorenamespace{gridsnappersets}
+
+\newskip \bodyfontlineheight
+\newdimen \bodyfontstrutheight
+\newdimen \bodyfontstrutdepth
+
+\newskip \globalbodyfontlineheight % why a skip
+\newdimen \globalbodyfontstrutheight
+\newdimen \globalbodyfontstrutdepth
+
+\permanent\def\snappedvboxattribute{\ifgridsnapping attr\snapvboxattribute\c_attr_snapmethod\fi}
+\permanent\def\setlocalgridsnapping{\ifgridsnapping \c_attr_snapvbox \c_attr_snapmethod\fi}
+
+\def\spac_grids_set_local_snapping#1%
+ {\ifgridsnapping
+ \doifsomething{#1}%
+ {\spac_grids_snap_value_set{#1}%
+ \c_attr_snapvbox\c_attr_snapmethod}%
+ \fi}
+
+\def\spac_grids_expand_snapper#1%
+ {\edef\m_spac_snapper
+ {\ifempty\m_spac_snapper\else\m_spac_snapper,\fi
+ \ifcsname\??gridsnappersets#1\endcsname\lastnamedcs\else#1\fi}}
+
+\permanent\protected\def\installsnapvalues#1#2%
+ {\let\m_spac_snapper\empty
+ \rawprocesscommacommand[#2]\spac_grids_expand_snapper
+ \edef\p_spac_snapper{#1:\m_spac_snapper}%
+ \ifcsname\??gridsnapperattributes\p_spac_snapper\endcsname
+ \scratchcounter\lastnamedcs % already defined
+ \else
+ \scratchcounter\clf_definesnapmethod{#1}{\m_spac_snapper}%
+ \edefcsname\??gridsnapperattributes\p_spac_snapper\endcsname{\the\scratchcounter}%
+ \fi
+ \edefcsname\??gridsnappers#1\endcsname{\c_attr_snapmethod\the\scratchcounter\relax}%
+ \letcsname\??gridsnappersets#1\endcsname\m_spac_snapper}
+
+\permanent\def\theexpandedsnapperset#1{\begincsname\??gridsnappersets#1\endcsname} % only for manuals
+
+\permanent\protected\def\usegridparameter#1% no checking here
+ {\edef\m_spac_grid_asked{#1\c!grid}%
+ \ifempty\m_spac_grid_asked
+ \c_attr_snapvbox\attributeunsetvalue
+ \else
+ \spac_grids_snap_value_set\m_spac_grid_asked
+ \c_attr_snapvbox\c_attr_snapmethod
+ \fi}
+
+\permanent\tolerant\protected\def\definegridsnapping[#1]#*[#2]%
+ {\installsnapvalues{#1}{#2}}
+
+\edef\spac_grids_snap_value_reset
+ {%\gridsnappingfalse
+ \c_attr_snapmethod\attributeunsetvalue}
+
+\def\spac_grids_snap_value_set#1%
+ {%\gridsnappingtrue
+ \begincsname\??gridsnappers#1\endcsname}
+
+% maybe:
+%
+% \def\spac_grids_snap_value_set#1%
+% {%\gridsnappingtrue
+% \ifcsname\??gridsnappers#1\endcsname
+% \lastnamedcs
+% \else
+% \definegridsnapping[#1][#1]%
+% \begincsname\??gridsnappers#1\endcsname
+% \fi}
+
+\def\spac_grids_snap_value_auto#1%
+ {\ifcsname\??gridsnappers#1\endcsname
+ \lastnamedcs
+ \else
+ \installsnapvalues\s!dummy{#1}%
+ \csname\??gridsnappers\s!dummy\endcsname
+ \fi}
+
+% \installsnapvalues{loose} {\v!maxdepth:0.8,\v!maxheight:0.8,\v!strut}
+% \installsnapvalues{normal}{\v!maxdepth:1.0,\v!maxheight:1.0,\v!strut}
+% \installsnapvalues{tight} {\v!maxdepth:1.2,\v!maxheight:1.2,\v!strut}
+
+% none don't enlarge
+% halfline enlarge by halfline/halfline
+% line enlarge by line/line
+% strut enlarge by ht/dp (default)
+% first align to top line
+% last align to bottom line
+% mindepth round depth down
+% maxdepth round depth up
+% minheight round height down
+% maxheight round height up
+% local use local interline space
+% offset:-3tp vertical shift within box
+% bottom:lines
+% top:lines
+% box centers a box rounded upwards (box:.5 -> tolerance)
+% min centers a box rounded downwards
+% max centers a box rounded upwards
+
+%D We're not downward compatible with \MKII ! Not yet in interface file:
+
+\definegridsnapping[\v!normal] [\v!maxheight,\v!maxdepth,\v!strut]
+\definegridsnapping[\v!standard] [\v!maxheight,\v!maxdepth,\v!strut]
+\definegridsnapping[\v!yes] [\v!maxheight,\v!maxdepth,\v!strut]
+
+\definegridsnapping[\v!strict] [\v!maxdepth:0.8,\v!maxheight:0.8,\v!strut]
+\definegridsnapping[\v!tolerant] [\v!maxdepth:1.2,\v!maxheight:1.2,\v!strut]
+\definegridsnapping[\v!verytolerant] [\v!maxdepth:1.4,\v!maxheight:1.4,\v!strut]
+
+\definegridsnapping[\v!tolerant:10] [\v!maxdepth:1.1,\v!maxheight:1.1,\v!strut] % 10 pct tolerance
+\definegridsnapping[\v!tolerant:20] [\v!maxdepth:1.2,\v!maxheight:1.2,\v!strut] % 20 pct tolerance
+\definegridsnapping[\v!tolerant:30] [\v!maxdepth:1.3,\v!maxheight:1.3,\v!strut] % 30 pct tolerance
+\definegridsnapping[\v!tolerant:40] [\v!maxdepth:1.4,\v!maxheight:1.4,\v!strut] % 40 pct tolerance
+
+\definegridsnapping[\v!top] [\v!minheight,\v!maxdepth,\v!strut]
+\definegridsnapping[\v!bottom] [\v!maxheight,\v!mindepth,\v!strut]
+\definegridsnapping[\v!both] [\v!minheight,\v!mindepth,\v!strut]
+
+\definegridsnapping[\v!broad] [\v!maxheight,\v!maxdepth,\v!strut,0.8] % maybe 0.85
+\definegridsnapping[\v!fit] [\v!maxheight,\v!maxdepth,\v!strut,1.2] % tight 0.15
+
+\definegridsnapping[\v!first] [\v!first]
+\definegridsnapping[\v!last] [\v!last]
+\definegridsnapping[\v!high] [\v!minheight,\v!maxdepth,\v!none]
+\definegridsnapping[\v!one] [\v!minheight,\v!mindepth]
+\definegridsnapping[\v!low] [\v!maxheight,\v!mindepth,\v!none]
+\definegridsnapping[\v!none] [\v!none]
+\definegridsnapping[\v!line] [\v!line]
+\definegridsnapping[\v!strut] [\v!strut]
+\definegridsnapping[\v!box] [\v!box]
+\definegridsnapping[\v!min] [\v!min]
+\definegridsnapping[\v!max] [\v!max]
+
+\definegridsnapping[\v!middle] [\v!maxheight,\v!maxdepth] % used in placement
+
+\definegridsnapping[\v!math] [\v!maxdepth:1.05,\v!maxheight:1.05,\v!strut] % experimental, maybe 1.1
+\definegridsnapping[\v!math:\v!line] [\v!math,\v!line,\v!split]
+\definegridsnapping[\v!math:\v!halfline] [\v!math,\v!halfline,\v!split]
+\definegridsnapping[\v!math:-\v!line] [\v!math,-\v!line,\v!split]
+\definegridsnapping[\v!math:-\v!halfline][\v!math,-\v!halfline,\v!split]
+
+\permanent\protected\def\synchronizelocallinespecs
+ {\bodyfontlineheight \normallineheight
+ \bodyfontstrutheight\strutht
+ \bodyfontstrutdepth \strutdp}
+
+\permanent\protected\def\synchronizegloballinespecs
+ {\global\globalbodyfontlineheight \normallineheight
+ \global\globalbodyfontstrutheight\strutht
+ \global\globalbodyfontstrutdepth \strutdp}
+
+\appendtoks
+ \synchronizegloballinespecs
+ \synchronizelocallinespecs
+\to \everysetupglobalinterlinespace
+
+\appendtoks
+ \synchronizelocallinespecs
+\to \everysetuplocalinterlinespace
+
+%D We still have to synchronize these:
+
+\permanent\protected\def\synchronizeskipamounts
+ {\bigskipamount
+ \skipfactor\baselineskip
+ \s!plus\skipgluefactor\baselineskip
+ \s!minus\skipgluefactor\baselineskip
+ \relax
+ \medskipamount \bigskipamount \divide\medskipamount \plustwo
+ \smallskipamount\bigskipamount \divide\smallskipamount\plusfour}
+
+%D Snapping.
+
+% \newif\ifgridsnapping % already defined
+
+\permanent\tolerant\protected\def\startgridsnapping[#1]%
+ {\snaptogrid[#1]\vbox\bgroup}
+
+\permanent\protected\def\stopgridsnapping
+ {\egroup}
+
+\permanent\tolerant\protected\def\placeongrid[#1]%
+ {\snaptogrid[#1]\vbox} % mark as done
+
+\permanent\tolerant\protected\def\snaptogrid[#1]% list or predefined
+ {\ifgridsnapping
+ \expandafter\spac_grids_snap_to_indeed % todo: move inline
+ \else
+ \expandafter\gobbleoneargument
+ \fi{#1}}
+
+\def\spac_grids_snap_to_indeed#1%
+ {\bgroup
+ \spac_grids_snap_value_reset
+ \dowithnextbox{\spac_grids_snap_to_finish{#1}}}
+
+% eventually there will always be a line snap
+
+\def\spac_grids_snap_to_finish#1%
+ {\ifvbox\nextbox % this will go away
+ \clf_vspacingcollapse\nextbox\relax % isn't that already done?
+ \fi
+ \doifelsenothing{#1}{\spac_grids_snap_value_set\v!normal}{\spac_grids_snap_value_set{#1}}%
+ \clf_vspacingsnap\nextbox\c_attr_snapmethod\relax
+ \ifvbox\nextbox\vbox\else\hbox\fi attr \snapmethodattribute \zerocount {\box\nextbox}% no pack (?), we snap
+ \egroup}
+
+\def\spac_grids_check_nop
+ {\gridsnappingfalse
+ \resetsystemmode\v!grid
+ \spac_grids_snap_value_reset}
+
+\def\spac_grids_check_yes
+ {\gridsnappingtrue
+ \setsystemmode\v!grid
+ \spac_grids_snap_value_set\p_grid}
+
+\permanent\protected\def\synchronizegridsnapping
+ {\edef\p_grid{\layoutparameter\c!grid}%
+ \ifx\p_grid\v!no % official
+ \spac_grids_check_nop
+ \orelse\ifx\p_grid\v!off % for taco and luigi
+ \spac_grids_check_nop
+ \orelse\ifempty\p_grid % to be sure
+ \spac_grids_check_nop
+ \else
+ \spac_grids_check_yes
+ \fi}
+
+\permanent\protected\def\setupgridsnapping[#1]% less overhead than setuplayout (needs testing)
+ {\setlayoutparameter\c!grid{#1}\synchronizegridsnapping}
+
+\mutable\let\checkedgridmethod\empty % these can become private
+\mutable\let\checkedgridscope \v!local % these can become private
+
+\permanent\protected\def\checkgridmethod#1%
+ {\edef\p_grid{#1}%
+ \ifempty\p_grid
+ \let\checkedgridmethod\empty
+ \let\checkedgridscope \v!local
+ \else
+ \splitatcolon\p_grid\checkedgridscope\checkedgridmethod
+ \ifempty\checkedgridmethod
+ \ifx\checkedgridscope\v!local\orelse\ifx\checkedgridscope\v!global\else
+ \let\checkedgridmethod\checkedgridscope
+ \let\checkedgridscope \v!local
+ \fi
+ \fi
+ \fi}
+
+\permanent\protected\def\applygridmethod#1#2#3% content localsettings (used in head rendering)
+ {\checkgridmethod{#1}%
+ \ifx\checkedgridscope\v!global
+ \ifempty\checkedgridmethod \else
+ % we assume that the call is grouped because grouping here has the side
+ % effect that the eventually constructed line will get the value outside
+ % the group
+ %
+ % overkill: \setupgridsnapping[\checkedgridmethod]%
+ % maybe : \spac_grids_snap_value_auto\checkedgridmethod
+ \spac_grids_snap_value_set\checkedgridmethod
+ \fi
+ \hbox{#3}%
+ \else
+ % the extra hbox will trigger the global snapper on top of the local and
+ % we really need that in this case (compatibility etc etc) so here we don't
+ % het an already done hit (otherwise we would not snap)
+ \hbox\bgroup
+ \ifempty\checkedgridmethod\orelse\ifconditional\headisdisplay
+ #2%
+ \fi
+ \snaptogrid[\checkedgridmethod]\hbox{#3}%
+ \egroup
+ \fi}
+
+\protected\gdef\page_layouts_calculate_overshoot
+ {\ifgridsnapping\ifcase\layoutlines
+ \getnoflines\textheight
+ \textovershoot\dimexpr\noflines\globalbodyfontlineheight-\textheight\relax
+ \fi\fi}
+
+\protected\def\page_layouts_report_overshoot
+ {\page_layouts_calculate_overshoot
+ \ifdim\textovershoot>\zeropoint
+ \writestatus\m!layouts{gridmode,\space
+ noflines: \the\noflines,\space
+ textheight: \the\textheight,\space
+ textovershoot: \the\textovershoot\space
+ (maybe set number of lines instead)%
+ }%
+ \fi
+ \glet\page_layouts_report_overshoot\page_layouts_calculate_overshoot}
+
+\appendtoks
+ \page_layouts_report_overshoot
+\to \everybeforepagebody
+
+%D Visualization:
+
+\definepalet
+ [grid]
+ [ one=red,
+ two=green,
+ three=blue,
+ four=gray]
+
+\permanent\protected\def\setgridtracebox#1[#2]% % maybe reverse the order
+ {\setbox\nextbox#1%
+ {\hbox
+ {\hbox to \zeropoint
+ {\setlayoutcomponentattribute{\v!grid:\v!test}%
+ \color[grid:#2]{\ruledhbox \layoutcomponentboxattribute {\fakebox\nextbox}}%
+ \hss}%
+ \flushnextbox}}}
+
+\setnewconstant\gridboxlinenomode\plusone % 0:nothing 1:all 2:lines 3:frame 4:l/r
+\setnewconstant\gridboxlinemode \plusone
+
+\permanent\protected\def\gridboxvbox
+ {\ifcase\gridboxlinemode
+ \vpack
+ \or
+ \ruledvpack
+ \or
+ \vpack
+ \or
+ \ruledvpack
+ \else
+ \ruledvpack
+ \fi}
+
+\permanent\def\gridboxwidth{\ifcase\gridboxlinemode0\or.5\or.5\or0\else.5\fi\linewidth}
+
+\permanent\protected\def\setgridbox#1#2#3% maybe ifgridsnapping at outer level
+ {\setbox#1\gridboxvbox to #3 % given size
+ {\forgetall
+ \resetvisualizers
+ \resetteststrut
+ \offinterlineskip
+ \hsize#2%
+ \ifcase\gridboxlinenomode\or\or\or
+ \gridboxlinenomode\doifoddpageelse\plusone\plustwo % 3: outer
+ \or
+ \gridboxlinenomode\doifoddpageelse\plustwo\plusone % 4: inner
+ \fi
+ \topskipcorrection
+ \gridboxvbox % calculated size
+ {\getrawnoflines{#3}% \getnoflines{#3}%
+ \scratchdimen\dimexpr#2+\lineheight\relax
+ \dorecurse\noflines
+ {\strut
+ \hskip-.5\lineheight\relax
+ \ifcase\gridboxlinenomode\or
+ \rlap
+ {\hskip\dimexpr.2\bodyfontsize+\scratchdimen\relax
+ \infofont\hbox to \emwidth{\hss\recurselevel}}%
+ \or
+ \llap
+ {\infofont\hbox to \emwidth{\hss\recurselevel}%
+ \hskip.2\bodyfontsize}%
+ \fi
+ \vrule
+ \s!height \gridboxwidth
+ \s!depth \gridboxwidth
+ \s!width \scratchdimen
+ \par}}
+ \vfill}}
+
+%D This has become obsolete:
+
+% \def\moveboxontogrid#1#2#3% will become obsolete, but it needs checking
+% {}
+
+%D Helper:
+
+\protected\def\spac_helpers_assign_skip#1#2% ook nog \v!halfline+fuzzysnap
+ {\doifelse{#2}\v!line
+ {#1\ifgridsnapping
+ \bodyfontlineheight
+ \else
+ \openlineheight
+ \fi}
+ {\ifgridsnapping
+ \assigndimension{#2}{#1}{.25\bodyfontlineheight}{.5\bodyfontlineheight}\bodyfontlineheight
+ \else
+ \assigndimension{#2}{#1}\smallskipamount\medskipamount\bigskipamount
+ \fi}%
+ \relax}
+
+% \start \dosetstretch{.25em} \setuptolerance[tolerant,stretch] \input tufte \endgraf \stop
+% \start \dosetstretch{.5em} effe flink doorfietsen \stop
+
+% experimental code, not yet interfaced:
+
+% category:
+%
+% 0 == discard discard
+% 1 == only if larger largest
+% 2 == force even if smaller force
+% 3 == only take penalty component penalty
+% 4 == add to existing skip add
+% 5 == disable (ignore following) disable
+% 6 == kill whitespace nowhite
+% 7 == discard previous back
+% 10 == no topskip
+%
+% penalty: larger wins
+% order: larger wins
+% category:2,order:5,penalty:10000,skip:value|kw
+%
+% \defineblankmethod [\v!joinedup] {\ifvmode\nointerlineskip\fi}
+
+% todo, in grid mode: builders.vspacing.fixed = false
+%
+% \ifgridsnapping will go
+
+\installcorenamespace{vspacing}
+\installcorenamespace{vspacingamount}
+
+\permanent\tolerant\protected\def\definevspacingamount[#1]#*[#2]#*[#3]% can be combined
+ {\ifarguments
+ %defcsname\??vspacingamount#1\endcsname{\lineheight}%
+ \or
+ \defcsname\??vspacingamount#1\endcsname{\lineheight}%
+ \or
+ \defcsname\??vspacingamount#1\endcsname{\ifgridsnapping\lineheight\else#2\fi}%
+ \or
+ \defcsname\??vspacingamount#1\endcsname{\ifgridsnapping#3\else#2\fi}%
+ \fi
+ \clf_vspacingsetamount{#1}}
+
+\def\spac_vspacing_no_topskip % use grouped
+ {\c_attr_skipcategory\pluseleven}
+
+%permanent\def\vspacingfromscratchtoks {\scratchdimen\dimexpr\csname\??vspacingamount\the\scratchtoks\endcsname\relax}
+\permanent\def\vspacingpredefinedvalue#1{\scratchskip\glueexpr\csname\??vspacingamount#1\endcsname\relax}
+%permanent\def\vspacingfromtempstring {\scratchdimen\dimexpr\csname\??vspacingamount\tempstring\endcsname\relax}
+
+% \installcorenamespace{vspacingamountnormal}
+% \installcorenamespace{vspacingamountgrid}
+
+% \def\spac_vspacing_define_amount[#1][#2][#3]% can be combined
+% {\ifcsname n>#1\endcsname\else
+% \expandafter\newtoks\csname n>#1\endcsname
+% \expandafter\newtoks\csname g>#1\endcsname
+% \fi
+% \csname n>#1\endcsname{#2}%
+% \csname g>#1\endcsname{#3}%
+% \clf_vspacingsetamount{#1}}
+
+\permanent\tolerant\protected\def\definevspacing[#1]#*[#2]%
+ {\clf_vspacingdefine{#1}{#2}}
+
+%D The injector code (generated at the \LUA\ end). This will go away!
+
+\newtoks\everybeforeblankhandling
+\newtoks\everyafterblankhandling
+
+\newconditional\c_space_vspacing_done
+\newconditional\c_space_vspacing_fixed
+\newconditional\c_space_ignore_parskip
+
+\appendtoks
+ \s_spac_vspacing_temp\zeropoint
+ \c_attr_skipcategory\plusone
+ \c_attr_skippenalty \attributeunsetvalue
+ \c_attr_skiporder \attributeunsetvalue
+ \ifgridsnapping
+ \settrue\c_space_vspacing_fixed
+ \else
+ \setfalse\c_space_vspacing_fixed
+ \fi
+\to \everybeforeblankhandling
+
+\appendtoks
+ \s_spac_vspacing_temp\plusone\s_spac_vspacing_temp
+ \ifconditional\c_space_vspacing_fixed \else
+ \s!plus \skipgluefactor\s_spac_vspacing_temp
+ \s!minus\skipgluefactor\s_spac_vspacing_temp
+ \fi
+ \relax
+\to \everyafterblankhandling
+
+% The main spacer:
+
+\permanent\tolerant\protected\def\vspacing[#1]%
+ {\ifarguments
+ \spac_vspacing_yes[\currentvspacing]% in the new mechanism no [] needed
+ \else
+ \spac_vspacing_yes[#1]% in the new mechanism no [] needed
+ \fi}
+
+\def\spac_vspacing_yes
+ {\ifinpagebody % somewhat weird
+ \expandafter\spac_vspacing_yes_indeed
+ \orelse\ifconditional\c_spac_packed_blank
+ \expandafter\spac_vspacing_yes_indeed
+ \else
+ \expandafter\spac_vspacing_yes_ignore
+ \fi}
+
+\def\spac_vspacing_nop
+ {\ifinpagebody % somewhat weird
+ \expandafter\spac_vspacing_nop_indeed
+ \orelse\ifconditional\c_spac_packed_blank
+ \expandafter\spac_vspacing_nop_indeed
+ \else
+ \expandafter\spac_vspacing_nop_ignore
+ \fi}
+
+\def\spac_vspacing_yes_indeed[#1]{\ifmmode\else\par\ifvmode\clf_injectvspacing\ifgridsnapping\plusone\else\zerocount\fi{#1}\fi\fi}
+\def\spac_vspacing_nop_indeed {\ifmmode\else\par\ifvmode\clf_injectvspacing\ifgridsnapping\plusone\else\zerocount\fi{\currentvspacing}\fi\fi}
+
+\def\spac_vspacing_yes_ignore[#1]{\ifmmode\else\par\fi}
+\def\spac_vspacing_nop_ignore {\ifmmode\else\par\fi}
+
+\permanent\protected\def\directvspacing#1%
+ {\ifmmode\else\par\ifvmode\clf_injectvspacing\ifgridsnapping\plusone\else\zerocount\fi{\iftok{#1}\emptytoks\currentvspacing\else#1\fi}\fi\fi}
+
+\permanent\protected\def\directdefaultvspacing
+ {\ifinpagebody % somewhat weird
+ \directvspacing\currentvspacing
+ \orelse\ifconditional\c_spac_packed_blank
+ \directvspacing\currentvspacing
+ \fi}
+
+\permanent\protected\def\directcheckedvspacing
+ {\ifinpagebody % somewhat weird
+ \expandafter\directvspacing
+ \orelse\ifconditional\c_spac_packed_blank
+ \expandafter\directvspacing
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\useblankparameter#1% faster local variant
+ {\edef\m_spac_blank_asked{#1\c!blank}%
+ \ifempty\m_spac_blank_asked\else
+ \directvspacing\m_spac_blank_asked
+ \fi}
+
+%D Handy (and faster):
+
+\permanent\protected\def\directvpenalty#1{\ifmmode\else\par\ifvmode\clf_injectvpenalty#1\relax\fi\fi}
+\permanent\protected\def\directvskip #1{\ifmmode\else\par\ifvmode\clf_injectvskip #1\relax\fi\fi}
+
+%D These depend on bigskipamount cum suis so we'd better sync them:
+
+\mutable\let\currentvspacing\s!default % hm, default, standard ...
+
+\permanent\tolerant\protected\def\setupvspacing[#1]%
+ {\ifarguments\else
+ \edef\currentvspacing{#1}%
+ \fi
+ \synchronizevspacing}
+
+\permanent\protected\def\synchronizevspacing
+ {\ifempty\currentvspacing % mistakenly had an \else
+ \let\currentvspacing\s!default
+ \fi
+ \spac_whitespace_setup_nop}
+
+\permanent\protected\def\restorestandardblank % or default ?
+ {\let\currentvspacing\v!standard}
+
+%D The \type {category:4} is default.
+
+\definevspacingamount[\v!none] [\zeropoint] [\zeropoint]
+\definevspacingamount[\v!big] [\bigskipamount] [\bodyfontlineheight]
+\definevspacingamount[\v!medium] [\medskipamount] [.5\bodyfontlineheight]
+\definevspacingamount[\v!small] [\smallskipamount] [.25\bodyfontlineheight]
+\definevspacingamount[\v!line] [\openlineheight] [\bodyfontlineheight]
+\definevspacingamount[\v!halfline] [.5\openlineheight] [.5\bodyfontlineheight]
+\definevspacingamount[\v!quarterline] [.25\openlineheight] [.25\bodyfontlineheight]
+\definevspacingamount[\v!formula] [\medskipamount] [.5\bodyfontlineheight]
+\definevspacingamount[\v!white] [\parskip] [\bodyfontwhitespace]
+\definevspacingamount[\v!height] [\strutht] [\bodyfontstrutheight]
+\definevspacingamount[\v!depth] [\strutdp] [\bodyfontstrutdepth]
+
+\definevspacingamount[\v!standard] [.75\openlineheight] [.75\openlineheight] % mkii compatible
+
+\permanent\def\bodyfontwhitespace
+ {\dimexpr
+ \ifzeropt\parskip
+ \zeropoint
+ \orelse\ifgridsnapping
+ \bodyfontlineheight
+ \else
+ \parskip
+ \fi
+ \relax}
+
+%D used in itemize \unknown\ always test this:
+
+\newdimen\d_spac_overlay
+
+\def\spac_overlay_lines
+ {\directcheckedvspacing{\v!back,\v!overlay}% \blank[\v!back,\v!overlay]%
+ \nointerlineskip}
+
+% \startitemize[n]
+% \item \input zapf
+% \item \startitemize[a]
+% \item \input knuth
+% \stopitemize
+% \stopitemize
+%
+% \strut \hfill first line \blank[overlay] second line \hfill \strut
+%
+% \ruledvbox {
+% \strut \hfill line 1 \blank[overlay]
+% line 2 \hfill \strut \blank[overlay]
+% \strut \hfill line 3 \hfill \strut
+% }
+%
+% \dorecurse{50}
+% {\startitemize[n] \startitem \startitemize[a] \item #1 \stopitemize \stopitem \stopitemize}
+
+\definevspacing[\v!preference][penalty:-500] % goodbreak
+\definevspacing[\v!samepage] [penalty:10000] % nobreak
+
+\definevspacing[\v!always] [category:0] % hm, internally it's discard
+\definevspacing[\v!max] [category:1]
+\definevspacing[\v!force] [category:2]
+\definevspacing[\v!disable] [category:5]
+\definevspacing[\v!nowhite] [category:6]
+\definevspacing[\v!back] [category:7]
+\definevspacing[\v!packed] [category:8] % noparskip (kind of special)
+\definevspacing[\v!overlay] [category:9]
+\definevspacing[\v!enable] [category:10]
+
+%definevspacing[\v!noparskip] [category:8]
+%definevspacing[\v!notopskip] [category:11]
+
+\definevspacing[\v!weak] [order:0]
+\definevspacing[\v!strong] [order:100]
+
+\definevspacing[\s!default] [\v!white] % was big for a while
+
+\newcount\c_spac_vspacing_special_base \c_spac_vspacing_special_base = 32250 % 4000
+\newcount\c_spac_vspacing_special_step \c_spac_vspacing_special_step = 10 % 250
+\newcount\c_spac_vspacing_special_done
+
+% 2019-05-31 : upgraded a bit to more distinctive samepage-[level]-[0|1|2] names
+
+\protected\def\spac_vspacing_define_same_step#1#2% alternatively we could have samepage-n-m
+ {\begingroup
+ \scratchcounterone\numexpr\plusthree*#1+#2\relax
+ \scratchcountertwo\numexpr\c_spac_vspacing_special_base+\c_spac_vspacing_special_step*\scratchcounterone\relax
+ %\writestatus{defined}{\v!samepage-\number#1-\number#2\space=>\space penalty:\the\scratchcountertwo}%
+ \normalexpanded{\definevspacing[\v!samepage-\number#1-\number#2][penalty:\the\scratchcountertwo]}%
+ \endgroup}
+
+\protected\def\spac_vspacing_define_same_page#1%
+ {\dostepwiserecurse\c_spac_vspacing_special_done{#1}\plusone
+ {\spac_vspacing_define_same_step\recurselevel\zerocount % before
+ \spac_vspacing_define_same_step\recurselevel\plusone % after
+ \spac_vspacing_define_same_step\recurselevel\plustwo}% % whatever
+ \global\c_spac_vspacing_special_done#1\relax}
+
+\spac_vspacing_define_same_page{12} % 12 levels should be more than enough as a start
+
+\def\spac_vspacing_same_page#1#2% level offset (starts at 0)
+ {\ifnum#1>\c_spac_vspacing_special_done
+ \spac_vspacing_define_same_page{#1}%
+ \fi
+ %\writestatus{used}{\v!samepage-\number#1-\number#2}%
+ \vspacing[\v!samepage-\number#1-\number#2]}
+
+\definevspacing[\v!default] [\v!big] % todo: needs to adapt to \setupblank
+\definevspacing[\v!before] [\v!default] % but we need to avoid circular references
+\definevspacing[\v!inbetween][\v!default] % then
+\definevspacing[\v!after] [\v!before]
+
+\setupvspacing
+ [\v!big] % alternatively [\v!standard]
+
+%D Maybe at some point we will differ between \type {\vspacing} and \type {\blank}
+%D (we needed the first one while playing with the new code).
+
+% We keep this one as reference
+%
+% \protected\def\inhibitblank
+% {\vspacing[\v!disable]}
+%
+% but use the following more efficient variant instead:
+
+\permanent\protected\def\inhibitblank{\ifmmode\else\par\ifvmode\clf_injectdisable\fi\fi}
+
+\aliased\let\doinhibitblank\inhibitblank % keep this command for a while, used in styles
+
+%D We use \type {vspacing} because at some point we had the blank mechanism alongside
+%D a new experimental variant and I aliased it locally till it all worked out well. So,
+%D we're kind of stuck with synonyms now. Some day we will drop the vspacing and use the
+%D old aliases instead.
+
+\aliased\let\defineblank \definevspacing
+\aliased\let\setupblank \setupvspacing
+\aliased\let\blank \vspacing
+\aliased\let\synchronizeblank \synchronizevspacing
+\aliased\let\defineblankmethod\definevspacingamount
+
+%D The following command is for Wolfgang. It has to be used with care as it does
+%D {\em not} work in tandem with the other spacing commands.
+
+\installcorenamespace{vspace}
+
+\permanent\tolerant\protected\def\definevspace[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ \or
+ \letcsname\??vspace:#1\endcsname\empty
+ \or
+ \defcsname\??vspace:#1\endcsname{#2}%
+ \or
+ \defcsname\??vspace#1:#2\endcsname{#3}%
+ \fi}
+
+\letvalue{\??vspace:\s!unknown}\zeropoint
+
+\def\spac_vspace_unknown
+ {\csname\??vspace:\s!unknown\endcsname}
+
+\permanent\tolerant\protected\def\vspace[#1]#*[#2]%
+ {\par
+ \ifvmode
+ \removelastskip
+ \vskip
+ \ifparameter#2\or
+ \ifcsname\??vspace#1:#2\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??vspace:#2\endcsname
+ \lastnamedcs
+ \else
+ \spac_vspace_unknown
+ \fi
+ \orelse\ifparameter#1\or
+ \ifcsname\??vspace:#1\endcsname
+ \lastnamedcs
+ \else
+ \spac_vspace_unknown
+ \fi
+ \else
+ \ifcsname\??vspace:\s!default\endcsname
+ \lastnamedcs
+ \else
+ \spac_vspace_unknown
+ \fi
+ \fi
+ \relax
+ \fi}
+
+%D Some preliminary code: a simple and fast hanger, for usage in macros.
+
+\installcorenamespace {hanging}
+
+\installdirectcommandhandler \??hanging {hanging}
+
+\setuphanging
+ [\c!distance=.5\emwidth,
+ \c!location=\v!left,
+ \c!n=\zerocount]
+
+\let\m_spac_hanging_location\empty
+
+\permanent\tolerant\protected\def\starthanging[#1]%
+ {\dontleavehmode\bgroup
+ \ifhastok={#1}%
+ \let\m_spac_hanging_location\empty
+ \setupcurrenthanging[#1]%
+ \else
+ \edef\m_spac_hanging_location{#1}%
+ \fi
+ \ifempty\m_spac_hanging_location
+ \edef\m_spac_hanging_location{\directhangingparameter\c!location}%
+ \fi
+ \dowithnextboxcs\spac_hanging_finish\hbox}
+
+\permanent\protected\def\stophanging
+ {\endgraf
+ \egroup}
+
+\def\spac_hanging_finish
+ {\scratchdistance\directhangingparameter\c!distance\relax
+ \ifdim\ht\nextbox>\strutht
+ \setbox\nextbox\tbox{\box\nextbox}%
+ \fi
+ \scratchcounter\directhangingparameter\c!n\relax
+ \ifnum\scratchcounter>\zerocount
+ \frozen\hangafter-\scratchcounter
+ \else
+ \getboxheight\scratchdimen\of\box\nextbox
+ \getnoflines\scratchdimen
+ \frozen\hangafter-\noflines
+ \fi
+ \ht\nextbox\strutht
+ \dp\nextbox\strutdp
+ \scratchwidth\dimexpr\wd\nextbox+\scratchdistance\relax
+ \ifx\m_spac_hanging_location\v!right
+ \frozen\hangindent\ifconditional\displaylefttoright-\fi\scratchwidth
+ \rlap{\hskip\dimexpr\hsize-\leftskip-\wd\nextbox\relax\box\nextbox}% \leftskip is new
+ \else
+ \frozen\hangindent\ifconditional\displaylefttoright\else-\fi\scratchwidth
+ \llap{\box\nextbox\hskip\scratchdistance}%
+ \fi
+ \ignorespaces}
+
+%D \macros
+%D {startfixed}
+%D
+%D \starttyping
+%D \startitemize
+%D \startitem \externalfigure[cow][height=1cm] \stopitem
+%D \startitem \externalfigure[cow][height=1cm] \stopitem
+%D
+%D \startitem \startfixed \externalfigure[cow][height=1cm]\stopfixed \stopitem
+%D \startitem \startfixed[high]\externalfigure[cow][height=1cm]\stopfixed \stopitem
+%D \startitem \startfixed[low] \externalfigure[cow][height=1cm]\stopfixed \stopitem
+%D \startitem \startfixed[lohi]\externalfigure[cow][height=1cm]\stopfixed \stopitem
+%D
+%D \startitem test \par \startfixed \externalfigure[koe][height=1cm]\stopfixed \stopitem
+%D \startitem test \par \startfixed[high]\externalfigure[koe][height=1cm]\stopfixed \stopitem
+%D \startitem test \par \startfixed[low] \externalfigure[koe][height=1cm]\stopfixed \stopitem
+%D \startitem test \par \startfixed[lohi]\externalfigure[koe][height=1cm]\stopfixed \stopitem
+%D \stopitemize
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\installcorenamespace{fixedalternatives}
+
+\aliased\let\stopfixed\relax
+
+\permanent\protected\def\startfixed
+ {\bgroup
+ \ifhmode
+ \expandafter\typo_fixed_start_h
+ \else
+ \expandafter\typo_fixed_start_v
+ \fi}
+
+\tolerant\def\typo_fixed_start_h[#1]%
+ {\enforced\let\stopfixed\typo_fixed_stop_h
+ \dowithnextbox{\typo_fixed_finish{#1}}%
+ \vbox\bgroup
+ %ignorespaces
+ \setlocalhsize}
+
+\protected\def\typo_fixed_stop_h
+ {%removeunwantedspaces
+ \egroup
+ \egroup}
+
+\tolerant\def\typo_fixed_start_v[#1]%
+ {\enforced\let\stopfixed\typo_fixed_stop_v
+ \startbaselinecorrection}
+
+\protected\def\typo_fixed_stop_v
+ {\stopbaselinecorrection
+ \egroup}
+
+\letcsname\??fixedalternatives \v!high\endcsname\bbox
+\letcsname\??fixedalternatives \v!low\endcsname\tbox
+\letcsname\??fixedalternatives \v!middle\endcsname\vcenter
+\letcsname\??fixedalternatives \v!lohi\endcsname\vcenter
+\letcsname\??fixedalternatives\s!unknown\endcsname\tbox
+\letcsname\??fixedalternatives\s!default\endcsname\tbox
+
+\protected\def\typo_fixed_finish#1%
+ {\expandnamespacevalue\??fixedalternatives{#1}\s!default{\box\nextbox}}
+
+% %D Forgotten already:
+%
+% \def\shapefill{\vskip\zeropoint\s!plus\lineheight\s!minus\lineheight\relax}
+
+%D Nasty:
+
+% \writestatus{1}{\the\prevdepth} \blank[force,5*big] { \writestatus{1}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
+% \writestatus{2}{\the\prevdepth} \blank[force,5*big] { \writestatus{2}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
+% \writestatus{3}{\the\prevdepth} \blank[force,5*big] { \writestatus{3}{\the\prevdepth} \baselineskip5cm xxxxxxxxx \par } \page
+% \writestatus{4}{\the\prevdepth} \input tufte \page
+% \writestatus{5}{\the\prevdepth} \input tufte \page
+% \writestatus{6}{\the\prevdepth} \blank[force,5*big] { \writestatus{6}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
+
+% \writestatus{1}{\the\prevdepth} \null\vskip4cm { \writestatus{1}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
+% \writestatus{2}{\the\prevdepth} \null\vskip4cm { \writestatus{2}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
+% \writestatus{3}{\the\prevdepth} \null\vskip4cm { \writestatus{3}{\the\prevdepth} \baselineskip5cm xxxxxxxxx \par } \page
+% \writestatus{4}{\the\prevdepth} \input tufte \page
+% \writestatus{5}{\the\prevdepth} \input tufte \page
+% \writestatus{6}{\the\prevdepth} \null\vskip4cm { \writestatus{6}{\the\prevdepth} \baselineskip1cm xxxxxxxxx \par } \page
+
+\appendtoks
+ \ifvmode\prevdepth\zeropoint\fi % consistent, else first page -1000pt .. needed for fixed,3*big first/successive pages consistency
+\to \everystarttext
+
+\prevdepth\zeropoint
+
+%D Helper:
+
+\permanent\protected\def\checkedblank[#1]%
+ {\edef\p_blank{#1}%
+ \ifempty\p_blank
+ % ignore
+ \orelse\ifx\p_blank\v!none
+ % ignore
+ \else
+ \blank[\p_blank]%
+ \fi}
+
+% \setupwhitespace[line]
+% \prerollblank[2*line] \the\prerolledblank
+% \prerollblank[-2*line] \the\prerolledblank
+
+\newskip\prerolledblank
+
+\permanent\protected\def\prerollblank[#1]%
+ {\begingroup
+ \edef\p_blank{#1}%
+ \ifempty\p_blank
+ \global\prerolledblank\zeropoint
+ \orelse\ifx\p_blank\v!none
+ \global\prerolledblank\zeropoint
+ \else
+ % don't mess with \arskip here!
+ \scratchskip\plusten\lineheight
+ \setbox\scratchbox\vbox
+ {\vskip\scratchskip
+ \kern\zeropoint
+ \blank[\p_blank]}%
+ % \dimexpr doesn't work well with skips
+ \advance\scratchskip-\ht\scratchbox
+ \global\prerolledblank-\scratchskip
+ \fi
+ \endgroup}
+
+\newcount\c_spac_vspacing_ignore_parskip
+
+% \setupwhitespace[line]
+% \setuphead[subject][after={\blank[packed]},style=\bfb]
+% \subject{foo}
+% test \par
+% test \par
+% \blank[packed] % \ignoreparskip
+% test \par
+% test \par
+% \ignoreparskip
+% test \par
+% test \par
+% \setuphead[subject][after={\blank[nowhite]},style=\bfb]
+% \subject{foo}
+% test \par
+% test \par
+
+\permanent\protected\def\ignoreparskip{\c_spac_vspacing_ignore_parskip\plusone}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-bkm.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-bkm.mkxl
new file mode 100644
index 00000000000..c5bd8732421
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-bkm.mkxl
@@ -0,0 +1,182 @@
+%D \module
+%D [ file=strc-bkm,
+%D version=2009.04.01,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Bookmarks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Bookmarks}
+
+\registerctxluafile{strc-bkm}{}
+
+% \enabledirectives[references.bookmarks.preroll]
+
+\unprotect
+
+%D Bookmarks are a very viewer dependent feature. They are mostly used as additional
+%D table of contents and therefore relate directly to lists.
+%D
+%D A bookmark list is added to the document only when interaction is enabled. The
+%D given lists are bookmarked and a second argument specifies the opened bookmark
+%D trees.
+%D
+%D \starttyping
+%D \placebookmarks
+%D [chapter,section,subsection,mylist]
+%D [chapter]
+%D \stoptyping
+%D
+%D You can overloads the last set bookmark in a sectioning command:
+%D
+%D \starttyping
+%D \chapter {the first chapter}
+%D \bookmark {the first bookmark}
+%D \stoptyping
+%D
+%D However, in practice you can better use \type {\startchapter} and set the \type
+%D {bookmark} parameter.
+%D
+%D You can add entries to the bookmarklist:
+%D
+%D \starttyping
+%D \bookmark[mylist]{whatever}
+%D \stoptyping
+%D
+%D Use force to get titles in the bookmarklist. This is somewhat tricky as one does
+%D not want \quotation {Contents} in a table of contents but it has to be in the
+%D bookmark list.
+
+\installcorenamespace{bookmark}
+
+\installsetuponlycommandhandler \??bookmark {bookmark} % installdirectparametersethandler
+
+\setupbookmark
+ [\c!force=\v!no, % it's easier to force that to inhibit
+ \c!number=\v!yes, % might become v!no
+ \c!sectionblock=\v!no] % show sectionblock level + title
+
+\aliased\let\setupbookmarks\setupbookmark
+
+\permanent\protected\def\bookmark
+ {\iflocation
+ \expandafter\strc_bookmarks_bookmark_yes
+ \else
+ \expandafter\strc_bookmarks_bookmark_nop
+ \fi}
+
+\tolerant\def\strc_bookmarks_bookmark_yes[#1]#:#2%
+ {\begingroup
+ \simplifycommands
+ \ifnum\thenamedheadlevel{#1}>\zerocount
+ \clf_overloadbookmark{#1}{\detokenize\expandafter{\normalexpanded{#2}}}%
+ \else
+ \writetolist[#1]{#2}{}% todo: a dedicated bookmark writer
+ \fi
+ \endgroup}
+
+\tolerant\def\strc_bookmarks_bookmark_nop[#1]#:#2%
+ {}
+
+\permanent\protected\def\placebookmarks
+ {\iflocation
+ \expandafter\strc_bookmarks_place_yes
+ \else
+ \expandafter\gobblethreeoptionals
+ \fi}
+
+\let\m_bookmarks_names \empty
+\let\m_bookmarks_opened\empty
+
+\tolerant\def\strc_bookmarks_place_yes[#1]#*[#2]#*[#3]%
+ {\begingroup
+ \edef\m_bookmarks_names{#1}%
+ \edef\m_bookmarks_opened{#2}%
+ \ifempty\m_bookmarks_names
+ \edef\m_bookmarks_names{\namedlistparameter\v!content\c!list}%
+ \fi
+ \ifempty\m_bookmarks_names
+ \let\m_bookmarks_names\v!all
+ \fi
+ \ifparameters\or\or
+ \doifelseassignment{#2}{\let\m_bookmarks_opened\empty\setupcurrentbookmark[#2]}\donothing
+ \or
+ \setupcurrentbookmark[#3]% no every so not all possible
+ \fi
+ \clf_registerbookmark
+ names {\m_bookmarks_names}%
+ opened {\m_bookmarks_opened}%
+ force {\bookmarkparameter\c!force}%
+ number {\bookmarkparameter\c!number}%
+ \relax
+ \endgroup}
+
+\appendtoks
+ \clf_setupbookmarks
+ separatorset {\bookmarkparameter\c!numberseparatorset}%
+ conversionset {\bookmarkparameter\c!numberconversionset}%
+ starter {\bookmarkparameter\c!numberstarter}%
+ stopper {\bookmarkparameter\c!numberstopper}%
+ segments {\bookmarkparameter\c!numbersegments}%
+ showblocktitle {\bookmarkparameter\c!sectionblock}%
+ \relax
+\to \everysetupbookmark
+
+%D There is a plugin mechanism but this is for experts only. The intermediate
+%D data structures are stable.
+%D
+%D \starttyping
+%D \startluacode
+%D structures.bookmarks.installhandler("check before","before",function(levels)
+%D logs.report("extra bookmarks","before (normal bookmarks)")
+%D inspect(levels)
+%D logs.report("extra bookmarks","before (extra bookmarks)")
+%D inspect(structures.bookmarks.extras.get())
+%D return levels
+%D end)
+%D structures.bookmarks.installhandler("check after", "after", function(levels)
+%D logs.report("extra bookmarks","after (merged bookmarks)")
+%D inspect(levels)
+%D return levels
+%D end)
+%D \stopluacode
+%D \starttyping
+%D
+%D This mechanism was added when bookmark inclusion became (optional) part of graphic
+%D inclusion (which is needed by Taco).
+%D
+%D \starttyping
+%D \getfiguredimensions[somefile.pdf]
+%D \dorecurse {\noffigurepages} {
+%D \startTEXpage
+%D \externalfigure[somefile.pdf][interaction=bookmark,page=\recurselevel]
+%D \stopTEXpage
+%D }
+%D \starttyping
+
+\protect \endinput
+
+% \starttext
+% \setupinteraction[state=start]\setupinteractionscreen[option=bookmark]
+% \placebookmarks[chapter,section,subsection][chapter]
+% \chapter{First}
+% \bookmark{The First Indeed}
+% \section{alpha}
+% \bookmark[chapter]{The First Indeed Again}
+% \section{beta}
+% \chapter{Second}
+% \bookmark{The Second Indeed}
+% \section{gamma \tex{radiation}}
+% \subsection{a}
+% \subsection{b}
+% \section{delta}
+% \section{epsilon}
+% \chapter{Third \relax}
+% \chapter{我〈能吞下玻璃而不傷身〉體。} % whatever that means
+% \chapter{Idris Samawi Hamid ادريس سماوي حامد}
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-blk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-blk.mkxl
new file mode 100644
index 00000000000..4cfbd51eb58
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-blk.mkxl
@@ -0,0 +1,171 @@
+%D \module
+%D [ file=strc-blk,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Blockmoves,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Blockmoves}
+
+\registerctxluafile{strc-blk}{}
+
+\unprotect
+
+% we run on top of buffers and sections
+%
+% todo: prefix numbers (needs further integration elsewhere)
+% check functionality
+% alternative files (needs further integration elsewhere)
+%
+% order matters: \c!before (think of: \c!before=\startitemize)
+%
+% no \endgroups
+
+\installcorenamespace {block}
+\installcorenamespace {blocktemp}
+
+\installcommandhandler \??block {block} \??block
+
+\appendtoks
+ \clf_definestructureblock{\currentblock}%
+ \frozen\setuevalue{\e!begin\currentblock}{\strc_blocks_begin{\currentblock}}%
+ \frozen\letvalue {\e!end \currentblock}\donothing
+\to \everydefineblock
+
+% We need to prevent too much lookahead which will gobble newlines that are needed
+% for buffers. See blocks-002.tex as example.
+
+% maybe: systemmode "block:<name>"
+
+\let\m_block \empty
+\let\m_subblock\empty
+
+\protected\def\strc_blocks_begin#1%
+ {\edef\m_block {#1}%
+ \let \m_subblock\empty
+ \doifelsenextoptionalcs\strc_blocks_begin_yes\strc_blocks_begin_nop}
+
+\protected\def\strc_blocks_begin_yes[#1]%
+ {\doifelseassignmentcs{#1}%
+ \strc_blocks_begin_indeed
+ \strc_blocks_begin_tagged
+ {#1}}
+
+\protected\def\strc_blocks_begin_tagged#1%
+ {\edef\m_subblock{#1}%
+ \doifelsenextoptionalcs\strc_blocks_begin_yes_yes\strc_blocks_begin_nop}
+
+\protected\def\strc_blocks_begin_yes_yes[#1]%
+ {\strc_blocks_begin_indeed{#1}}
+
+\protected\def\strc_blocks_begin_nop
+ {\strc_blocks_begin_indeed{}}
+
+\protected\def\strc_blocks_begin_indeed#1%
+ {\normalexpanded{\buff_pickup{\??block}{\e!begin\m_block}{\e!end\m_block}}%
+ {}%
+ {\clf_savestructureblock{\m_block}{\m_subblock}{#1}{\??block}}%
+ \plusone}
+
+\let\strc_blocks_setup\relax
+
+\newconstant \c_strc_blocks_index
+\newconditional\c_strc_blocks_display
+
+\permanent\protected\def\dostarthiddenblock % called at lua end
+ {\begingroup
+ \visiblefalse % blocks float
+ \startnointerference
+ \strc_start_block}
+
+\permanent\protected\def\dostophiddenblock % called at lua end
+ {\strc_stop_block
+ \stopnointerference
+ \endgroup}
+
+\permanent\protected\def\dostartnormalblock % called at lua end
+ {\begingroup
+ \visibletrue
+ \strc_start_block}
+
+\permanent\protected\def\dostopnormalblock % called at lua end
+ {\strc_stop_block
+ \endgroup}
+
+\def\strc_start_block#1#2%
+ {\edef\currentblock{#2}%
+ \c_strc_blocks_index#1\relax
+ \strc_blocks_setup
+ \let\strc_blocks_setup\relax
+ \edef\p_alternative{\blockparameter\c!alternative}%
+ \ifx\p_alternative\v!text
+ \setfalse\c_strc_blocks_display
+ \else
+ \settrue\c_strc_blocks_display
+ \fi
+ \ifconditional\c_strc_blocks_display
+ \blockparameter\c!before
+ \fi
+ \begingroup
+ \usesetupsparameter\blockparameter\relax
+ \dostarttagged\t!block\currentblock
+ \useblockstyleandcolor\c!style\c!color
+ \blockparameter\c!inner % old
+ \ifconditional\c_strc_blocks_display
+ \usealignparameter\blockparameter
+ \else
+ \blockparameter\c!left
+ \fi
+ \ignorespaces}
+
+\def\strc_stop_block
+ {\removeunwantedspaces
+ \ifconditional\c_strc_blocks_display
+ \par
+ \else
+ \blockparameter\c!right
+ \fi
+ \dostoptagged
+ \endgroup
+ \ifconditional\c_strc_blocks_display
+ \blockparameter\c!after
+ \fi}
+
+\tolerant\def\strc_blocks_set_state[#1]#*[#2]#*[#3]% state name tag % todo: we could use the lua one directly
+ {\clf_setstructureblockstate{#1}{#2}{#3}}
+
+\tolerant\def\strc_blocks_select[#1]#*[#2]#*[#3]#*[#4]% state name tag setups
+ {\begingroup
+ \ifhastok={#3}%
+ \getparameters[\??blocktemp][\c!criterium=\v!text,#3]%
+ \def\strc_blocks_setup{\setupcurrentblock[#3]}%
+ \clf_selectstructureblock{#1}{#2}{}{\csname\??blocktemp\c!criterium\endcsname}%
+ \else
+ \getparameters[\??blocktemp][\c!criterium=\v!text,#4]%
+ \def\strc_blocks_setup{\setupcurrentblock[#4]}%
+ \clf_selectstructureblock{#1}{#2}{#3}{\csname\??blocktemp\c!criterium\endcsname}%
+ \fi
+ \endgroup}
+
+\permanent\def\blockuservariable#1%
+ {\clf_structureblockuservariable\c_strc_blocks_index{#1}}
+
+% hide : save, if [+] also hidden execute
+% keep : save and normal execute
+% use : normal execute unless [-]
+% process: hidden execute unless [-]
+% select : idem use
+
+\permanent\protected\def\hideblocks {\strc_blocks_set_state[hide]}
+\permanent\protected\def\keepblocks {\strc_blocks_set_state[keep]}
+\permanent\protected\def\useblocks {\strc_blocks_select [use]}
+\permanent\protected\def\processblocks{\strc_blocks_select [process]}
+\permanent\protected\def\selectblocks {\strc_blocks_select [use]}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-con.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-con.mklx
new file mode 100644
index 00000000000..5e0ceb8338c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-con.mklx
@@ -0,0 +1,1186 @@
+%D \module
+%D [ file=strc-con,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Constructions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Constructions}
+
+\registerctxluafile{strc-con}{}
+
+% todo: check why \copy .. probably mkii leftover
+%
+% todo: check if commands similar to lists
+% todo: \strc_constructions_setup_counter\strc_constructions_setup_counter{#1} in setup ... synchronization
+%
+% ? : \def\showdnlisttext{\constructionparameter\c!listtext} % space in default
+%
+% maybe: pickup text and store in buffer ...
+%
+% \@@notemakeconstruction[##1]{}{##2}%
+% \expandafter\endgroup\noteparameter\c!next}}
+%
+% todo: \currentconstructionattribute : can be a counter instead
+
+%D Constructions are just descriptions but a bit more abstract so that we can
+%D conveniently build upon them. They are not really meant for users but module
+%D writers might find them useful. We mention a couple of parameters but these are
+%D only mentioned because in the shared code we map all resolvers to constructions.
+%D
+%D On top of constructions we build descriptions, enumerations, notes, labels and
+%D maybe some day more.
+
+\unprotect
+
+%D Todo:
+
+\installcorenamespace{construction}
+
+\installcommandhandler \??construction {construction} \??construction
+
+\aliased\let\setupconstructions\setupconstruction
+
+\setupconstructions[%
+ %c!title=,
+ %c!text=,
+ %
+ %c!style=,
+ %c!color=,
+ %c!command=,
+ %c!align=,
+ %
+ %c!headstyle=,
+ %c!headcolor=,
+ %c!headalign=,
+ %
+ %c!titlestyle=,
+ %c!titlecolor=,
+ %c!titlecommand=,
+ %c!titleleft=,
+ %c!titleright=,
+ %
+ %c!closesymbol=,
+ %c!closecommand=,
+ %
+ \c!alternative=\v!left,
+ \c!display=\v!yes,
+ \c!width=7\emwidth,
+ \c!distance=\emwidth,
+ \c!stretch=.50,
+ \c!shrink=.25,
+ \c!titledistance=.5\emwidth,
+ %c!hang=,
+ %c!sample=,
+ \c!margin=\v!no,
+ \c!before=\blank,
+ \c!inbetween=\blank,
+ \c!after=\blank,
+ %c!indentnext=,
+ %c!indenting=,
+ %
+ \c!expansion=\v!no,
+ %c!xmlsetup=,
+ %s!catcodes=,
+]
+
+%D Constructions and alike uses similar structures. In order to prevent issues
+%D we freeze some states. There is some overhead in the intermediate define
+%D step (as we could set them directly) but this is more flexible (and looks
+%D nicer). Keep in mind that descriptions, enumeration and notations are all
+%D independent and that we just remap the resolvers.
+
+% \defineconstruction[test][handler=description,level=1]
+% \defineconstruction[test][parent][handler=description,level=3]
+
+\installcorenamespace{constructionmain} % frozen after definition
+\installcorenamespace{constructionlevel} % frozen after definition
+\installcorenamespace{constructionclass} % frozen after definition
+
+%aliased\let\currentconstructionhash \??construction
+
+\mutable\let\currentconstructionmain \empty
+\mutable\let\currentconstructionlevel \empty
+\mutable\let\currentconstructionhandler \empty
+
+\mutable\let\currentconstructionattribute \empty
+\mutable\let\currentconstructionbookmark \empty
+\mutable\let\currentconstructioncatcodes \empty
+\mutable\let\currentconstructioncoding \empty
+\mutable\let\currentconstructionexpansion \empty
+\mutable\let\currentconstructionincrementnumber\empty
+\mutable\let\currentconstructionlabel \empty
+\mutable\let\currentconstructionlist \empty
+\mutable\let\currentconstructionlistentry \empty
+\mutable\let\currentconstructionlistnumber \empty
+\mutable\let\currentconstructionmarking \empty
+\mutable\let\currentconstructionnumber \empty
+\mutable\let\currentconstructionreference \empty
+\mutable\let\currentconstructionreferencetext \empty
+\mutable\let\currentconstructionreferenceprefix\empty
+\mutable\let\currentconstructionsynchronize \empty
+\mutable\let\currentconstructiontitle \empty
+\mutable\let\currentconstructionxmlsetup \empty
+
+\settrue\c_strc_constructions_define_commands % use with care, might become a proper key
+
+\appendtoks
+ \ifempty\currentconstructionparent
+ \letvalue{\??constructionmain\currentconstruction}\currentconstruction
+ \definelist[\currentconstruction]% goodie
+ \else
+ \letvalue{\??constructionmain\currentconstruction}\currentconstructionparent
+ \definelist[\currentconstruction][\currentconstructionparent]% goodie
+ \fi
+ \setevalue{\??constructionlevel\currentconstruction}{\number\constructionparameter\c!level}%
+ \setevalue{\??constructionclass\currentconstruction}{\constructionparameter\s!handler}%
+\to \everydefineconstruction
+
+% todo: inhibit this when we have a different handle .. current we cannot do \frozen
+
+\appendtoks
+ \ifconditional\c_strc_constructions_define_commands
+ %% \instance\setuevalue{\e!start\currentconstruction}{\strc_constructions_start{\currentconstruction}}%
+ \instance\setuevalue{\e!start\currentconstruction}{\strc_constructions_start[\currentconstruction]}%
+ \instance\setuevalue{\e!stop \currentconstruction}{\strc_constructions_stop}%
+ \fi
+\to \everydefineconstruction
+
+%D Just a basic environment (mostly for testing). We will provide a 'setup' based
+%D plugin once the rest is sorted out.
+
+\tolerant\protected\def\strc_constructions_start[#1]#*[#2]#*[#3]%
+ {\begingroup
+ \strc_constructions_initialize{#1}%
+ \strc_constructions_register[#3][\c!label={\constructionparameter\c!text},\c!reference=,\c!title=,\c!bookmark=,\c!list=,\c!referencetext=,#2]%
+ \csname\??constructionstarthandler\currentconstructionhandler\endcsname}
+
+\protected\def\strc_constructions_stop
+ {\csname\??constructionstophandler\currentconstructionhandler\endcsname
+ \endgroup}
+
+%D As we will build on top of the generic construction mechanism we have handlers
+%D as a sort of plug in mechanism. In order to have some consistency we share
+%D some namespaces.
+
+\installcorenamespace{constructioninitializer}
+\installcorenamespace{constructionfinalizer}
+
+\installcorenamespace{constructionmainhandler}
+\installcorenamespace{constructioncommandhandler}
+\installcorenamespace{constructionstarthandler}
+\installcorenamespace{constructionstophandler}
+
+\installcorenamespace{constructiontexthandler}
+\installcorenamespace{constructionnotehandler}
+
+\protected\def\strc_constructions_initialize#1% class instance
+ {\edef\currentconstruction{#1}%
+ \enforced\let\currentconstructionhash\??construction
+ \let\currentconstructionlistentry\!!zerocount
+ \enforced\expandafter\let\expandafter\currentconstructionmain \csname\??constructionmain \currentconstruction\endcsname
+ \enforced\expandafter\let\expandafter\currentconstructionlevel \csname\??constructionlevel\currentconstruction\endcsname
+ \enforced\expandafter\let\expandafter\currentconstructionhandler\csname\??constructionclass\currentconstruction\endcsname
+ \csname\??constructioninitializer\currentconstructionhandler\endcsname}
+
+\protected\def\strc_constructions_finalize
+ {\csname\??constructionfinalizer\currentconstructionhandler\endcsname
+ \strc_constructions_discard}
+
+\newconditional\c_strc_constructions_number_state
+\newconditional\c_strc_constructions_title_state
+
+\protected\setvalue{\??constructioninitializer\v!construction}%
+ {\setfalse\c_strc_constructions_number_state
+ \setfalse\c_strc_constructions_title_state}
+
+\protected\setvalue{\??constructionfinalizer\v!construction}%
+ {}
+
+%D We keep the command variant around but rather would move to the start-stop one.
+%D Also, passing the title as argument has some history so we need to keep that as
+%D well.
+
+% \ifdefined\dotagsetconstruction \else \aliased\let\dotagsetconstruction\relax \fi
+
+% \startuseMPgraphic{MyFrame}
+% picture p ; numeric o ; path a, b ; pair c ;
+% p := textext.rt("\FunnyFramed{\currentconstructiontext}") ;
+% % p := textext.rt("\wrappedconstruction{\FunnyFramed{\currentconstructiontext}}") ;
+% % p := textext.rt("\FunnyFramed{\wrappedconstruction{\currentconstructiontext}}") ;
+% % p := textext.rt("\FunnyFramed{\wrappedcurrentconstruction}") ;
+% ...
+% \stopuseMPgraphic
+%
+% \defineoverlay[MyFrame][\useMPgraphic{MyFrame}]
+%
+% \defineframedtext
+% [MyText]
+% [frame=off,
+% background=MyFrame]
+%
+% \defineenumeration[Theorem]
+% [alternative=empty,
+% before=\startMyText,
+% after=\stopMyText]
+
+\newtoks\everyconstruction
+
+\permanent\protected\def\currentconstructiontext
+ {\begstrut
+ \csname\??constructiontexthandler\currentconstructionhandler\endcsname
+ \endstrut}
+
+\permanent\protected\def\currentconstructionsample
+ {\begstrut
+ \constructionparameter\c!text
+ \p_strc_constructions_sample
+ \endstrut}
+
+\permanent\protected\def\wrappedconstruction#1%
+ {\begingroup
+ \iftrialtypesetting \else
+ \currentconstructionsynchronize
+ \c_attr_destination\currentconstructionattribute\relax
+ \fi
+ #1%
+ \endgroup}
+
+\permanent\protected\def\wrappedcurrentconstruction
+ {\begingroup
+ \iftrialtypesetting \else
+ \currentconstructionsynchronize
+ \c_attr_destination\currentconstructionattribute\relax
+ \fi
+ \currentconstructiontext
+ \endgroup}
+
+\protected\setvalue{\??constructionmainhandler\v!construction}#1%
+ {\iftrialtypesetting \else
+ \begingroup
+ \currentconstructionsynchronize % reinstated
+ \c_attr_destination\currentconstructionattribute\relax % todo, whole text
+ \signalcharacter
+ \endgroup
+ \fi#1}
+
+\protected\setvalue{\??constructiontexthandler\v!construction}%
+ {\begingroup
+ \useconstructionstyleandcolor\c!headstyle\c!headcolor % move to \currentconstructiontext
+ \the\everyconstruction
+ \constructionparameter\c!headcommand
+ {\strut
+ \constructionparameter\c!text
+ \clf_savedlisttitle{\currentconstructionmain}\currentconstructionlistentry\relax}%
+ \endgroup}
+
+\protected\def\strc_constructions_stored_start
+ {\begingroup % brrr \endgroup elsewhere ... will be sorted out
+ \csname\??constructionstarthandler\currentconstructionhandler\endcsname}
+
+\protected\def\strc_constructions_stored_stop
+ {\csname\??constructionstophandler\currentconstructionhandler\endcsname}
+
+\newconstant\c_strc_constructions_nested_state % to be redone
+
+% not used any more
+%
+% \def\resetconstructions % to be used in e.g. footnotes
+% {\c_strc_constructions_nested_state\zerocount}
+
+\protected\setvalue{\??constructioncommandhandler\v!construction}%
+ {\endgroup}
+
+\let\p_strc_constructions_sample \empty
+\let\p_strc_constructions_align \empty
+\let\p_strc_constructions_indenting \empty
+\let\p_strc_constructions_width \empty
+\let\p_strc_constructions_aligntitle \empty
+\let\p_strc_constructions_renderingsetup\empty
+\let\p_strc_constructions_headalign \empty
+\let\p_strc_constructions_hang \empty
+\let\p_strc_constructions_inbetween \empty
+\let\p_strc_constructions_closesymbol \empty
+\let\p_strc_constructions_distance \empty
+
+\newconditional\c_strc_constructions_distance_none
+
+\def\strc_constructions_set_width_and_distance
+ {\assignwidth
+ \p_strc_constructions_width
+ \constructionsheadwidth
+ {\unhcopy\constructionheadbox}
+ \constructionsheaddistance}
+
+\def\strc_constructions_preroll_head#content%
+ {\setbox\constructionheadbox\hbox
+ {\forgetall
+ \dontcomplain
+ \settrialtypesetting
+ \csname\??constructionmainhandler\currentconstructionhandler\endcsname#content}}
+
+\def\strc_constructions_ignore_head
+ {\constructionsheaddistance\zeropoint
+ \constructionsheadwidth \zeropoint
+ % we also need to make sure that no stretch creeps in (new per 2015-02-02, for Alan)
+ \settrue\c_strc_constructions_distance_none}
+
+\protected\setvalue{\??constructionstarthandler\v!construction}% this will be redone (reorganized) .. too much boxing
+ {\dostarttaggedchained\t!construction\currentconstruction\currentconstructionhash
+% \dotagsetconstruction
+ \constructionparameter\c!before
+ \begingroup
+ \edef\currentconstructionalternative{\constructionparameter\c!alternative}%
+ \edef\p_strc_constructions_renderingsetup{\constructionalternativeparameter\c!renderingsetup}%
+ % catch fuzzyness
+ \ifempty\p_strc_constructions_renderingsetup
+ \letconstructionparameter\c!alternative\v!left
+ \let\currentconstructionalternative\v!left
+ \edef\p_strc_constructions_renderingsetup{\constructionalternativeparameter\c!renderingsetup}%
+ \else
+ \doifnotsetups\p_strc_constructions_renderingsetup
+ {\letconstructionparameter\c!alternative\v!left
+ \let\currentconstructionalternative\v!left
+ \edef\p_strc_constructions_renderingsetup{\constructionalternativeparameter\c!renderingsetup}}%
+ \fi
+ %
+ \doadaptleftskip{\constructionparameter\c!margin}%
+ \setlocalhsize % so we can use \localhsize in width assignments
+ \edef\p_strc_constructions_distance{\constructionalternativeparameter\c!distance}%
+ \edef\p_strc_constructions_width{\constructionalternativeparameter\c!width}%
+ \ifx\p_strc_constructions_distance\v!none
+ \settrue\c_strc_constructions_distance_none
+ \constructionsheaddistance\zeropoint
+ \else
+ \setfalse\c_strc_constructions_distance_none
+ \constructionsheaddistance\p_strc_constructions_distance\relax
+ \ifdim\constructionsheaddistance=\zeropoint
+ \ifx\p_strc_constructions_width\v!broad
+ \constructionsheaddistance\emwidth
+ \orelse\ifx\p_strc_constructions_width\v!line
+ \constructionsheaddistance\emwidth
+ \fi
+ \fi
+ \fi
+ % inefficient and not always needed, for instance not with margins so we will make checkers
+ % per alternative some day (especially in labels this is unwanted overhead)
+ %
+ % maybe we should have an option as i cannot oversee the consequences now
+ %
+ % \edef\p_strc_constructions_sample{\constructionparameter\c!sample}%
+ % \ifempty\p_strc_constructions_sample
+ % \strc_constructions_preroll_head\currentconstructiontext
+ % \else
+ % \strc_constructions_preroll_head\currentconstructiontextsample
+ % \fi
+ % \strc_constructions_set_width_and_distance
+ %
+ \strc_constructions_preroll_head\currentconstructiontext
+ \ifzeropt\wd\constructionheadbox
+ \strc_constructions_ignore_head
+ \else
+ \edef\p_strc_constructions_sample{\constructionparameter\c!sample}%
+ \ifempty\p_strc_constructions_sample
+ \strc_constructions_set_width_and_distance
+ \else
+ \strc_constructions_preroll_head\currentconstructionsample
+ \ifzeropt\wd\constructionheadbox
+ \strc_constructions_ignore_head
+ \else
+ \strc_constructions_set_width_and_distance
+ \fi
+ \fi
+ \fi
+ %
+ \dostarttagged\t!constructiontag\empty % todo
+ \setbox\constructionheadbox\hbox
+ {\forgetall
+ \dontcomplain
+ \ifx\currentconstructionalternative\v!serried % brrr, hack, will change
+ \csname\??constructionmainhandler\currentconstructionhandler\endcsname\currentconstructiontext
+ \else
+ \csname\??constructionmainhandler\currentconstructionhandler\endcsname{\boxedconstructionhead\currentconstructiontext}%
+ \fi}%
+ \edef\p_strc_constructions_aligntitle{\constructionparameter\c!aligntitle}%
+ \ifx\p_strc_constructions_aligntitle\v!no
+ \leftconstructionskip \leftskip
+ \rightconstructionskip\rightskip
+ \else
+ \ifcase\c_strc_constructions_nested_state
+ \leftconstructionskip \leftskip
+ \rightconstructionskip\rightskip
+ \fi
+ \fi
+ \edef\p_strc_constructions_align{\constructionparameter\c!align}%
+ \ifempty\p_strc_constructions_align \else
+ \setupalign[\p_strc_constructions_align]% \use...
+ \fi
+ \ifcase\c_strc_constructions_nested_state
+ \c_strc_constructions_nested_state\plusone
+ \or
+ \c_strc_constructions_nested_state\plustwo
+ \fi
+ \strc_constructions_close_symbol_reset
+ %
+ \edef\p_strc_constructions_headalign{\constructionparameter\c!headalign}%
+ %
+ \directsetup\p_strc_constructions_renderingsetup\relax
+ % moved to here 2014-07-03
+ \edef\p_strc_constructions_indenting{\constructionparameter\c!indenting}%
+ \ifempty\p_strc_constructions_indenting \else
+ \indenting[\p_strc_constructions_indenting]% \use...
+ \fi
+ %
+ \dostoptagged % tag
+ \dostarttagged\t!constructioncontent\empty
+ %\freezeparagraphproperties
+ \updateparagraphshapes
+ \ignorespaces} % args not needed
+
+\protected\setvalue{\??constructionstophandler\v!construction}%
+ {\strc_constructions_close_symbol_place
+ \doifnot{\constructionparameter\c!display}\v!no\par
+ \dostoptagged % content
+ \dostoptagged % construction
+ \endgroup
+ \constructionparameter\c!after
+ \useindentnextparameter\constructionparameter
+ \strc_constructions_finalize
+% \endgroup
+ \dorechecknextindentation}
+
+\permanent\protected\def\boxedconstructionhead#1%
+ {\doifelse{\constructionalternativeparameter\c!alignbottom}{\v!yes}\vbox\vtop
+ {\hsize\constructionsheadwidth
+ \edef\p_strc_constructions_headalign{\constructionparameter\c!headalign}%
+ \ifempty\p_strc_constructions_headalign \else
+ \setupalign[\p_strc_constructions_headalign]% \use...
+ \fi
+ #1}}
+
+\installcorenamespace{constructionalternative}
+\installcorenamespace{constructionrenderings}
+
+% see lists: alternativemethods ... todo
+%
+% there will be a proper interface to variables
+
+\installcommandhandler \??constructionalternative {constructionalternative} \??constructionalternative
+
+\setupconstructionalternative
+ [%c!left=, % no inheritance
+ %c!right=,% no inheritance
+ \c!width=\constructionparameter\c!width,
+ \c!distance=\constructionparameter\c!distance,
+ \c!stretch=\constructionparameter\c!stretch,
+ \c!shrink=\constructionparameter\c!shrink]
+
+\defineconstructionalternative
+ [\v!left]
+ [%\c!width=7em,
+ %\c!distance=1em,
+ \c!renderingsetup=\??constructionrenderings:\v!left]
+
+\defineconstructionalternative
+ [\v!right]
+ [%\c!width=7em,
+ %\c!distance=1em,
+ \c!renderingsetup=\??constructionrenderings:\v!right]
+
+\defineconstructionalternative
+ [\v!inmargin]
+ [\c!renderingsetup=\??constructionrenderings:\v!inmargin]
+
+\defineconstructionalternative
+ [\v!inleft]
+ [\c!renderingsetup=\??constructionrenderings:\v!inleft]
+
+\defineconstructionalternative
+ [\v!inright]
+ [\c!renderingsetup=\??constructionrenderings:\v!inright]
+
+\defineconstructionalternative
+ [\v!margin]
+ [\c!renderingsetup=\??constructionrenderings:\v!margin]
+
+\defineconstructionalternative
+ [\v!leftmargin]
+ [\c!renderingsetup=\??constructionrenderings:\v!leftmargin]
+
+\defineconstructionalternative
+ [\v!rightmargin]
+ [\c!renderingsetup=\??constructionrenderings:\v!rightmargin]
+
+\defineconstructionalternative
+ [\v!innermargin]
+ [\c!renderingsetup=\??constructionrenderings:\v!innermargin]
+
+\defineconstructionalternative
+ [\v!outermargin]
+ [\c!renderingsetup=\??constructionrenderings:\v!outermargin]
+
+\defineconstructionalternative
+ [\v!serried]
+ [\c!renderingsetup=\??constructionrenderings:\v!serried]
+
+\defineconstructionalternative
+ [\v!hanging]
+ [%\c!width=.75\emwidth
+ \c!renderingsetup=\??constructionrenderings:\v!hanging]
+
+\defineconstructionalternative
+ [\v!top]
+ [\c!renderingsetup=\??constructionrenderings:\v!top,
+ \c!width=\localhsize]
+
+\defineconstructionalternative
+ [\v!command]
+ [\c!renderingsetup=\??constructionrenderings:\v!command]
+
+\defineconstructionalternative
+ [\v!empty]
+ [\c!renderingsetup=\??constructionrenderings:\v!empty]
+
+\defineconstructionalternative
+ [\v!label]
+ [\c!renderingsetup=\??constructionrenderings:\v!label,
+ \c!width=]
+
+\newbox \constructionheadbox
+\newskip \leftconstructionskip
+\newskip \rightconstructionskip
+\newdimen \constructionsheadwidth
+\newdimen \constructionsheaddistance
+
+\def\strc_constructions_set_hang_box#1% messy left/rightskip
+ {\setbox\constructionheadbox\vtop % \vbox gaat fout in hang
+ {\forgetall
+ \dontcomplain
+ \hsize\constructionsheadwidth\relax
+ \ifempty\p_strc_constructions_headalign
+ \setupalign[#1]% use fast one
+ \else
+ \setupalign[\p_strc_constructions_headalign]% use fast one
+ \fi
+ \ifconditional\c_strc_constructions_distance_none \else
+ \advance\ifx#1\v!flushleft\rightskip\else\leftskip\fi\constructionsheaddistance % only difference and wrong anyway
+ \fi
+ \flushconstructionheadbox}%
+ \setbox\constructionheadbox\hpack{\box\constructionheadbox}% needed in case of e.g. a real big head font, see descriptions-006.tex
+ \ht\constructionheadbox\strutht
+ \dp\constructionheadbox\strutdp}
+
+\def\strc_constructions_set_pure_box#1% default ... better set up differently
+ {\setbox\constructionheadbox\vtop
+ {\forgetall
+ \dontcomplain
+ \hsize\constructionsheadwidth\relax
+ \ifempty\p_strc_constructions_headalign
+ \setupalign[#1]% use fast one
+ \else
+ \setupalign[\p_strc_constructions_headalign]% use fast one
+ \fi
+ \flushconstructionheadbox}%
+ \setbox\constructionheadbox\hpack{\box\constructionheadbox}% needed in case of e.g. a real big head font, see descriptions-006.tex
+ \ht\constructionheadbox\strutht
+ \dp\constructionheadbox\strutdp}
+
+\def\strc_constructions_set_hang#1%
+ {\ifempty\p_strc_constructions_hang
+ % nothing
+ \orelse\ifx\p_strc_constructions_hang\v!fit
+ \scratchdimen\htdp\constructionheadbox
+ \getnoflines\scratchdimen
+ \hangafter-\noflines
+ \orelse\ifx\p_strc_constructions_hang\v!broad
+ \scratchdimen\dimexpr\htdp\constructionheadbox+.5\strutht\relax
+ \getnoflines\scratchdimen
+ \hangafter-\noflines
+ \else
+ \hangafter-\p_strc_constructions_hang
+ \fi
+ \relax
+ \hangindent\ifx#1\v!right-\fi\constructionsheadwidth}
+
+\permanent\protected\def\flushconstructionheadbox
+ {\ifhbox\constructionheadbox\unhbox\else\box\fi\constructionheadbox}
+
+% The setups. These only deal with placement of the descriptor and initializing the
+% environment. The wrapping happens elsewhere.
+
+% todo: optimize the setups with
+%
+% \ifconditional\c_strc_constructions_distance_none : no need for skip
+% \ifzeropt\wd\constructionheadbox : no need for box and skips
+
+\startsetups[\??constructionrenderings:\v!left]
+ \edef\p_strc_constructions_hang{\constructionparameter\c!hang}%
+ \doifelsesetups{\??constructionrenderings:\v!left:\p_strc_constructions_hang} {
+ \directsetup{\??constructionrenderings:\v!left:\p_strc_constructions_hang}
+ } {
+ \directsetup{\??constructionrenderings:\v!left:\v!hanging}
+ }
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!right]
+ \edef\p_strc_constructions_hang{\constructionparameter\c!hang}
+ \doifelsesetups{\??constructionrenderings:\v!right:\p_strc_constructions_hang} {
+ \directsetup{\??constructionrenderings:\v!right:\p_strc_constructions_hang}
+ } {
+ \directsetup{\??constructionrenderings:\v!right:\v!hanging}
+ }
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!left:\v!none]
+ \enforced\let\\\crlf
+ \noindent
+ \strc_constructions_set_pure_box\v!flushleft
+ \leftskip\dimexpr\leftconstructionskip+\constructionsheadwidth+\constructionsheaddistance\relax
+ \rightskip\rightconstructionskip\relax
+ \llap {
+ \hpack to \leftskip {
+ \hskip\leftconstructionskip
+ \box\constructionheadbox % was copy
+ \hss
+ }
+ }
+ \nobreak
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!left:0]
+ \directsetup{\??constructionrenderings:\v!left:\v!none}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!left:]
+ \directsetup{\??constructionrenderings:\v!left:\v!none}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!right:\v!none]
+ \enforced\let\\\crlf
+ \noindent
+ \leftskip\leftconstructionskip
+ \rightskip\dimexpr\rightconstructionskip+\constructionsheadwidth\relax
+ %\strc_constructions_pure_hang_box\raggedleft
+ \strc_constructions_set_pure_box\v!flushright
+ \rlap {
+ \hskip\dimexpr\hsize-\leftskip-\rightskip\relax
+ \copy\constructionheadbox
+ \hskip\rightconstructionskip
+ }
+ \nobreak
+ \advance\rightskip \constructionsheaddistance
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!right:0]
+ \directsetup{\??constructionrenderings:\v!right:\v!none}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!right:]
+ \directsetup{\??constructionrenderings:\v!right:\v!none}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!left:\v!margin]%
+ \enforced\let\\\crlf
+ \noindent
+ \strc_constructions_set_pure_box\v!flushleft
+ \llap {
+ \hpack to \constructionsheadwidth {
+ \box\constructionheadbox % was copy
+ \hss
+ }
+ \hskip\constructionsheaddistance
+ }
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!right:\v!margin]%
+ \enforced\let\\\crlf
+ \noindent
+ \strc_constructions_set_pure_box\v!flushright
+ \rlap {
+ \hskip\constructionsheaddistance
+ \hpack to \constructionsheadwidth {
+ \box\constructionheadbox % was copy
+ \hss
+ }
+ }
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!left:\v!hanging]%
+ \enforced\let\\\crlf
+ \dontcomplain
+ \advance\constructionsheadwidth \constructionsheaddistance
+ \strc_constructions_set_hang_box\v!flushleft
+ \strc_constructions_set_hang\v!left
+ \noindent
+ \llap {
+ \tpack to \zeropoint {
+ \box\constructionheadbox
+ }
+ }
+ \nobreak
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!right:\v!hanging]%
+ \enforced\let\\\crlf
+ \dontcomplain
+ \advance\constructionsheadwidth \constructionsheaddistance
+ \strc_constructions_set_hang_box\v!flushright
+ \strc_constructions_set_hang\v!right
+ \noindent
+ \rlap {
+ \hpack to \dimexpr\hsize-\leftskip-\rightskip\relax {
+ \hss
+ \tpack to \zeropoint {
+ \box\constructionheadbox
+ }
+ }
+ }
+ \nobreak
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!top]
+ % \strc_sectioning_prevent_page_break % commented per 31/5/2019
+ \enforced\let\\\space
+ \noindent
+ \copy\constructionheadbox % why copy ?
+ \par
+ \nobreak
+ \edef\p_strc_constructions_inbetween{\constructionparameter\c!inbetween}%
+ \ifempty\p_strc_constructions_inbetween \else
+ \p_strc_constructions_inbetween
+ % shouldn't this move to after the fi?
+ \directcheckedvspacing\v!samepage
+ \fi
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+% why copy? historic reason?
+
+\startsetups[\??constructionrenderings:\v!margin]
+ \enforced\let\\\crlf
+ \noindent
+ \inmargin[\c!scope=\v!local]{\flushconstructionheadbox}%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!leftmargin]
+ \enforced\let\\\crlf
+ \noindent
+ \inleft[\c!scope=\v!local]{\flushconstructionheadbox}%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!rightmargin]
+ \enforced\let\\\crlf
+ \noindent
+ \inright[\c!scope=\v!local]{\flushconstructionheadbox}%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!innermargin]
+ \enforced\let\\\crlf
+ \noindent
+ \ininner[\c!scope=\v!local]{\flushconstructionheadbox}%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!outermargin]
+ \enforced\let\\\crlf
+ \noindent
+ \inouter[\c!scope=\v!local]{\flushconstructionheadbox}%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!inmargin]
+ \directsetup{\??constructionrenderings:\v!margin}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!inleft]
+ \directsetup{\??constructionrenderings:\v!leftmargin}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!inright]
+ \directsetup{\??constructionrenderings:\v!rightmargin}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!serried]
+ % already set?
+ \edef\p_strc_constructions_width{\constructionparameter\c!width}% CHECK ! ! ! wrong parameter namespace
+ \doifelsesetups{\??constructionrenderings:\v!serried:\p_strc_constructions_width} {
+ \directsetup{\??constructionrenderings:\v!serried:\p_strc_constructions_width}
+ } {
+ \directsetup{\??constructionrenderings:\v!serried:\v!wide}
+ }
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!serried:\v!fit]
+ \enforced\let\\\crlf
+ \noindent
+ \flushconstructionheadbox
+ \nobreak
+ \hskip\constructionsheaddistance\relax
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!serried:\v!broad]
+ \enforced\let\\\crlf
+ \noindent
+ \flushconstructionheadbox
+ \ifconditional\c_strc_constructions_distance_none \else
+ \nobreak
+ \hskip\constructionsheaddistance
+ \s!plus \constructionalternativeparameter\c!stretch\constructionsheaddistance
+ \s!minus\constructionalternativeparameter\c!shrink \constructionsheaddistance
+ \relax
+ \fi
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!serried:]
+ \directsetup{\??constructionrenderings:\v!serried:\v!broad}
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!serried:\v!wide]
+ \enforced\let\\\crlf
+ \noindent
+ \hbox to \constructionsheadwidth {
+ \flushconstructionheadbox
+ \hss
+ }
+ \nobreak
+ \hskip\constructionsheaddistance\relax
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!hanging]
+ % tricky: leftskipadaption is somewhat unpredictable
+ \enforced\let\\\crlf
+ \noindent
+ \advance\leftskip-\leftskipadaption\relax
+ \ifdim\leftskipadaption=\zeropoint
+ \leftskipadaption1.5\emwidth\relax % just some default
+ \ifnum\c_strc_constructions_nested_state=\plusone
+ \ifdim\leftskip>\zeropoint \relax
+ \leftskipadaption\leftskip
+ \fi
+ \fi
+ \fi
+ \ifnum\c_strc_constructions_nested_state>\zerocount % was \ifnum\c_strc_constructions_nested_state=\plusone
+ \advance\leftskip\leftskipadaption % but we're already further on
+ \fi
+ \hskip-\leftskipadaption\relax
+ \ifhbox\constructionheadbox \unhbox\else\box\fi\constructionheadbox
+ \ifconditional\c_strc_constructions_distance_none \else
+ % we used to have just a kern, but long lines look bad then
+ \scratchdistance\ifdim\constructionsheaddistance=\zeropoint .75\emwidth\else\constructionsheaddistance\fi
+ \hskip \scratchdistance
+ \s!plus \constructionalternativeparameter\c!stretch\scratchdistance
+ \s!minus\constructionalternativeparameter\c!shrink \scratchdistance
+ \fi
+ \ifhmode
+ \ifx\p_strc_constructions_width\v!line
+ \hfill\break
+ \else
+ \allowbreak
+ \fi
+ \fi
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+%D \starttyping
+%D \setupfootnotation[location=command,headcommand=\llap]
+%D \stoptyping
+
+\startsetups[\??constructionrenderings:\v!command]
+ \noindent
+ \constructionparameter\c!headcommand{\flushconstructionheadbox}
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!empty]
+ \noindent
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+\startsetups[\??constructionrenderings:\v!label]
+ \dontleavehmode
+ \begingroup
+ \constructionparameter\c!headcommand{\flushconstructionheadbox}
+ \endgroup
+\stopsetups
+
+\ifcase\contextlmtxmode \else
+ % just an empty check so that we can grep for contextlmtxmode in mkiv files
+\fi
+
+% \definedescription[whatever][alternative=right:bottom]
+%
+% \startwhatever{just a small\\example}
+% \input ward
+% \stopwhatever
+
+\defineconstructionalternative
+ [\v!right:\v!bottom]
+ [%\c!width=7em,
+ %\c!distance=1em,
+ \c!alignbottom=\v!yes,
+ \c!renderingsetup=\??constructionrenderings:\v!right:\v!bottom]
+
+\startsetups[\??constructionrenderings:\v!right:\v!bottom]
+ \enforced\let\\\crlf
+ \noindent
+ \leftskip\leftconstructionskip
+ \rightskip\dimexpr\rightconstructionskip+\constructionsheadwidth+\constructionsheaddistance\relax
+ \parfillskip\zeropoint
+ \strc_constructions_set_pure_box\v!flushright
+ \directparwrapper \relax {%
+ \hfilll
+ \rlap
+ {\kern\constructionsheaddistance
+ \copy\constructionheadbox}%
+ }%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+%D This next feature will get an lmtx implementation (probably by using a new generic
+%D close mechanism).
+
+% you can use \placeclosesymbol or \qed to place a symbol at the end of a
+% construction
+
+\installcorenamespace{constructionclosesymbol}
+
+\mutable\let\placeclosesymbol\donothing
+\mutable\let\qed \donothing
+
+\def\strc_constructions_close_symbol_reset % no need for global
+ {\global\expandafter\settrue\csname\??constructionclosesymbol\currentconstruction\endcsname
+ \let\placeclosesymbol\strc_constructions_close_symbol_place
+ \let\qed \strc_constructions_close_symbol_place}
+
+\protected\def\strc_constructions_close_symbol_place
+ {\ifconditional\csname\??constructionclosesymbol\currentconstruction\endcsname
+ \global\expandafter\setfalse\csname\??constructionclosesymbol\currentconstruction\endcsname
+ \edef\p_strc_constructions_closesymbol{\constructionparameter\c!closesymbol}%
+ \ifempty\p_strc_constructions_closesymbol \else
+ \constructionparameter\c!closecommand{\p_strc_constructions_closesymbol}% hm why {} maybe because box expected
+ \fi
+ \fi}
+
+\newif\ifnoconstructioncaption
+
+%D The storage macros:
+
+\mutable\let\currentconstructionlistentry\!!zerocount
+
+\protected\def\strc_constructions_register
+ {\clf_doifelselisthasentry\numexpr\currentconstructionlistentry\relax
+ \strc_constructions_register_nop
+ \strc_constructions_register_yes}
+
+% \def\strc_constructions_register{\strc_constructions_register_yes}
+
+\def\strc_constructions_register_nop[#1][#2]% #1=optional user data #2=interfaced-settings
+ %{\writestatus{constructions}{reusing \currentconstruction: \number\currentconstructionlistentry}}
+ {}
+
+\def\strc_constructions_discard
+ {\iftrialtypesetting
+ % \writestatus{constructions}{discarding \currentconstruction: \number\currentconstructionlistentry}%
+ \clf_discardfromlist\currentconstructionlistentry\relax
+ \fi}
+
+\let\currentconstructionlistnumber \!!zerocount
+\let\currentconstructionsynchronize\relax
+\let\currentconstructionattribute \attributeunsetvalue
+
+\def\strc_constructions_register_yes[#1][#2]% #1=optional user data #2=interfaced-settings
+ {\begingroup % similar to structure so we might generalize this
+ \setupcurrentconstruction[#2]% % xdef's will become edef's
+ \xdef\currentconstructionexpansion {\constructionparameter\c!expansion}%
+ \xdef\currentconstructionxmlsetup {\constructionparameter\c!xmlsetup}%
+ \xdef\currentconstructioncatcodes {\constructionparameter\s!catcodes}%
+ \xdef\currentconstructionlabel {\constructionparameter\c!label}%
+ \xdef\currentconstructionreference {\constructionparameter\c!reference}%
+ \xdef\currentconstructionreferencetext {\constructionparameter\c!referencetext}%
+ \xdef\currentconstructionreferenceprefix{\constructionparameter\c!referenceprefix}%
+ %xdef\currentconstructionshownumber {\constructionparameter\c!number}%
+ \xdef\currentconstructionincrementnumber{\constructionparameter\c!incrementnumber}%
+ %
+ \ifempty\currentconstructionexpansion
+ \glet\currentconstructionexpansion\v!no
+ \fi
+ %
+ \ifempty\currentconstructionreferenceprefix
+ \glet\currentconstructionreferenceprefix\referenceprefix
+ \fi
+ \ifx\currentconstructionexpansion\s!xml
+ \xmlstartraw
+ \xdef\currentconstructiontitle {\constructionparameter\c!title}%
+ \xdef\currentconstructionbookmark {\constructionparameter\c!bookmark}%
+ \xdef\currentconstructionmarking {\constructionparameter\c!marking}%
+ \xdef\currentconstructionlist {\constructionparameter\c!list}%
+ \xdef\currentconstructionreferencetext{\constructionparameter\c!referencetext}%
+ \xmlstopraw
+ \ifempty\currentconstructionlist
+ \glet\currentconstructionlist\currentconstructiontitle
+ \fi
+ \glet\currentconstructioncoding\s!xml
+ \else
+ \ifx\currentconstructionexpansion\v!yes
+ \xdef\currentconstructiontitle {\constructionparameter\c!title}%
+ \xdef\currentconstructionbookmark {\constructionparameter\c!bookmark}%
+ \xdef\currentconstructionmarking {\constructionparameter\c!marking}%
+ \xdef\currentconstructionlist {\constructionparameter\c!list}%
+ \xdef\currentconstructionreferencetext{\constructionparameter\c!referencetext}%
+ \else
+ \xdef\currentconstructiontitle {\detokenizedconstructionparameter\c!title}%
+ \xdef\currentconstructionbookmark {\detokenizedconstructionparameter\c!bookmark}%
+ \xdef\currentconstructionmarking {\detokenizedconstructionparameter\c!marking}%
+ \xdef\currentconstructionlist {\detokenizedconstructionparameter\c!list}%
+ \xdef\currentconstructionreferencetext{\detokenizedconstructionparameter\c!referencetext}%
+ \iflocation \ifempty\currentconstructionbookmark
+ \begingroup
+ \simplifycommands
+ \xdef\currentconstructionbookmark{\detokenize\expandafter{\normalexpanded{\constructionparameter\c!title}}}%
+ \endgroup
+ \fi \fi
+ \fi
+ \ifempty\currentconstructionlist
+ \glet\currentconstructionlist\currentconstructiontitle
+ \fi
+ \glet\currentconstructioncoding\s!tex
+ \fi
+ %
+ \ifx\currentconstructiontitle\v!none % will become obsolete
+ \global\noconstructioncaptiontrue
+ \fi
+ %
+ \ifnoconstructioncaption % then why analyze anyway?
+ \endgroup
+ \let\currentconstructionlistnumber \relax
+ \let\currentconstructionsynchronize\relax
+ \let\currentconstructionattribute \relax
+ \else
+ \setnextinternalreferences{construction}\currentconstructionmain % plural
+ \relax
+ \scratchcounter\clf_addtolist
+ metadata {
+ kind {construction}
+ name {\currentconstructionmain}
+ % level structures.sections.currentlevel()
+ catcodes \catcodetable
+ }
+ references {
+ internal \locationcount
+ order \locationorder
+ reference {\currentconstructionreference}
+ prefix {\currentconstructionreferenceprefix}
+ % block {\currentsectionblock}
+ % section structures.sections.currentid(),
+ }
+ titledata {
+ label {\detokenize\expandafter{\currentconstructionlabel}}
+ title {\detokenize\expandafter{\currentconstructiontitle}}
+ \ifx\currentconstructionbookmark\currentconstructiontitle \else
+ bookmark {\detokenize\expandafter{\currentconstructionbookmark}}
+ \fi
+ \ifx\currentconstructionreferencetext\currentconstructiontitle \else
+ reference {\detokenize\expandafter{\currentconstructionreferencetext}}
+ \fi
+ \ifx\currentconstructionlist\currentconstructiontitle \else
+ list {\detokenize\expandafter{\currentconstructionlist}}
+ \fi
+ }
+ \ifconditional\c_strc_constructions_number_state
+ prefixdata {
+ prefix {\constructionparameter\c!prefix}
+ separatorset {\constructionparameter\c!prefixseparatorset}
+ conversion {\constructionparameter\c!prefixconversion}
+ conversionset {\constructionparameter\c!prefixconversionset}
+ set {\constructionparameter\c!prefixset}
+ segments {\constructionparameter\c!prefixsegments}
+ connector {\constructionparameter\c!prefixconnector}
+ }
+ numberdata {
+ numbers {\currentconstructionnumber}
+ separatorset {\constructionparameter\c!numberseparatorset}
+ conversion {\constructionparameter\c!numberconversion}
+ conversionset {\constructionparameter\c!numberconversionset}
+ starter {\constructionparameter\c!numberstarter}
+ stopper {\constructionparameter\c!numberstopper}
+ segments {\constructionparameter\c!numbersegments}
+ }
+ \or
+ % symbol
+ \fi
+ userdata {\detokenize{#1}}
+ \relax
+ % \writestatus{constructions}{registering \currentconstruction: \number\scratchcounter}%
+ \clf_setinternalreference
+ prefix {\currentconstructionreferenceprefix}%
+ reference {\currentconstructionreference}%
+ internal \locationcount
+ view {\interactionparameter\c!focus}%
+ \relax
+ \normalexpanded {%
+ \endgroup
+ \edef\noexpand\currentconstructionlistentry {\the\scratchcounter}%
+ \edef\noexpand\currentconstructionattribute {\the\lastdestinationattribute}%
+ \edef\noexpand\currentconstructionsynchronize{\clf_deferredenhancelist\the\scratchcounter}%
+ }%
+ \fi}
+
+% This is not so nice, some day I'll just store the number with the entry
+% and deal with all at the Lua end, but first we need to be able to set
+% macros.
+
+\permanent\protected\def\reinstateconstructionnumberentry#1% was xdef
+ {\edef\currentconstructionattribute {\clf_getinternallistreference#1}%
+ \edef\currentconstructionsynchronize{\clf_deferredenhancelist\number#1}}
+
+\permanent\protected\def\reinstatecachedconstructionnumberentry#1% was xdef | #1 = cached index can be different from real
+ {\edef\currentconstructionattribute {\clf_getinternalcachedlistreference#1}% destination
+ \edef\currentconstructionsynchronize{\clf_deferredenhancelist\number#1}}
+
+\installstructurelistprocessor{construction}{\usestructurelistprocessor{number+title}}
+
+% Helpers:
+
+\def\currentconstructionnumber{\constructionparameter\s!counter}
+
+\protected\def\strc_constructions_next_indeed #get#tag#level{\strc_counters_increment_sub{#get{#tag}\s!counter}{#level}}
+\protected\def\strc_constructions_reset_indeed#get#tag#level{\strc_counters_reset_sub {#get{#tag}\s!counter}{#level}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-def.mkxl
new file mode 100644
index 00000000000..52b3b3ad972
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-def.mkxl
@@ -0,0 +1,338 @@
+%D [ file=strc-def,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Definitions}
+
+%registerctxluafile{strc-def}{}
+
+\unprotect
+
+% \setupsectionblock[appendix][sectionconversionset=appendix]
+% \setuphead[chapter][sectionconversionset=\sectionblockparameter\c!sectionconversionset] % \sectionblockparameter]
+
+% \defineresetset[default][0,1,1,0][1] % this will not reset parts and subsections
+% \setuphead[part][sectionresetset=default]
+% \starttext
+% \placelist[chapter,section,subsection][criterium=all,width=3cm]
+% \part{first} \chapter{alpha} \section{a} \section{b} \subsection{x} \subsection{y} \chapter{beta}
+% \part{second} \chapter{gamma} \section{a} \section{b} \subsection{x} \subsection{y} \chapter{delta}
+% \stoptext
+
+\defineseparatorset [\s!default] [] [.]
+\defineconversionset [\s!default] [] [numbers]
+\defineresetset [\s!default] [] [1] % each level
+\defineprefixset [\s!default] [section-1,section-2,section-3] []
+
+\defineconversionset [\v!number] [] [numbers]
+\defineconversionset [\v!pagenumber] [] [numbers]
+
+\defineprefixset [\v!all] [section-1,section-2,section-3,section-4,section-5,section-6,section-7,section-8] []
+\defineprefixset [\v!none] [] []
+
+\defineprefixset [\v!part] [section-1] []
+\defineprefixset [\v!chapter] [section-2] []
+
+\defineseparatorset [\v!appendix:\s!default] [] [.]
+\defineconversionset [\v!appendix:\s!default] [Romannumerals,Characters] [numbers]
+\defineresetset [\v!appendix:\s!default] [] [1] % why was this 0
+
+\setupuserpagenumber
+ [\c!numberconversionset=\v!pagenumber]
+
+\setupcounters
+ [\c!numberconversionset=\v!number]
+
+% \startsetups defaults:frontpart:pagenumbers:roman
+% \defineconversionset[\c!frontpart:\c!pagenumber][][romannumerals]
+% \setupuserpagenumber[\c!way=\v!by\v!block]
+% \stopsetups
+
+% \definesectionblock
+
+\definesectionblock [\v!frontpart] [\v!frontmatter] [\c!number=\v!no]
+\definesectionblock [\v!bodypart] [\v!bodymatter] [\c!number=\v!yes]
+\definesectionblock [\v!appendix] [\v!appendices] [\c!number=\v!yes]
+\definesectionblock [\v!backpart] [\v!backmatter] [\c!number=\v!no]
+
+\setsectionblock [\v!bodypart] % default
+
+% \setuphead[sectionsegments=\currentheadlevel]
+% \setuphead[sectionsegments=current]
+%
+% \startchapter[title=One,ownnumber={A}]
+% \startsection[title=OneOne,ownnumber={A.B}]
+% \startsubsection[title=OneOneOne,ownnumber={A.B.C}]
+% test
+% \stopsubsection
+% \stopsection
+% \stopchapter
+
+% \appendtoks
+% \setsectionblock[\v!bodypart]% default
+% \to \everyjob
+%
+% \appendtoks
+% \clf_setinitialsectionblock{\v!bodypart}%
+% \to \everystarttext
+
+\appendtoks
+ \clf_setinitialsectionblock{\v!bodypart}%
+\to \everyjob
+
+% \definesection (could become \definehead with one parameter)
+
+\definesection[\s!section-1] % part
+\definesection[\s!section-2] % chapter
+\definesection[\s!section-3] % section
+\definesection[\s!section-4] % subsection
+\definesection[\s!section-5] % subsubsection
+\definesection[\s!section-6] % subsubsubsection
+\definesection[\s!section-7] % subsubsubsubsection
+\definesection[\s!section-8] % subsubsubsubsubsection
+\definesection[\s!section-9] % subsubsubsubsubsubsection
+\definesection[\s!section-10] % subsubsubsubsubsubsubsection
+\definesection[\s!section-11] % subsubsubsubsubsubsubsubsection
+\definesection[\s!section-12] % subsubsubsubsubsubsubsubsubsection
+
+% \definehead
+
+\definehead
+ [\v!part]
+ [\c!section=\s!section-1]
+
+\definehead
+ [\v!chapter]
+ [\c!section=\s!section-2]
+
+\definehead
+ [\v!section]
+ [\c!section=\s!section-3]
+
+\definehead
+ [\v!subsection]
+ [\c!section=\s!section-4,
+ \c!default=\v!section]
+
+\definehead
+ [\v!subsubsection]
+ [\c!section=\s!section-5,
+ \c!default=\v!subsection]
+
+\definehead
+ [\v!subsubsubsection]
+ [\c!section=\s!section-6,
+ \c!default=\v!subsubsection]
+
+\definehead
+ [\v!subsubsubsubsection]
+ [\c!section=\s!section-7,
+ \c!default=\v!subsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsection]
+ [\c!section=\s!section-8,
+ \c!default=\v!subsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsection]
+ [\c!section=\s!section-9,
+ \c!default=\v!subsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsection]
+ [\c!section=\s!section-10,
+ \c!default=\v!subsubsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsection]
+ [\c!section=\s!section-11,
+ \c!default=\v!subsubsubsubsubsubsubsection]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsubsection]
+ [\c!section=\s!section-12,
+ \c!default=\v!subsubsubsubsubsubsubsubsection]
+
+\definehead
+ [\v!title]
+ [\c!coupling=\v!chapter,
+ \c!default=\v!chapter,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subject]
+ [\c!coupling=\v!section,
+ \c!default=\v!section,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubject]
+ [\c!coupling=\v!subsection,
+ \c!default=\v!subsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubject]
+ [\c!coupling=\v!subsubsection,
+ \c!default=\v!subsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubject]
+ [\c!coupling=\v!subsubsubsection,
+ \c!default=\v!subsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsection,
+ \c!default=\v!subsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+\definehead
+ [\v!subsubsubsubsubsubsubsubsubsubject]
+ [\c!coupling=\v!subsubsubsubsubsubsubsubsubsection,
+ \c!default=\v!subsubsubsubsubsubsubsubsubsection,
+ \c!incrementnumber=\v!no]
+
+% setups
+
+\setuphead
+ [\v!part]
+ [\c!placehead=\v!no]
+
+\setuphead
+ [\v!chapter]
+ [\v!appendix\c!label=\v!appendix,
+ \v!bodypart\c!label=\v!chapter] % bijlageconversie=\Character
+
+\setuphead
+ [\v!section]
+ [\v!appendix\c!label=\v!section,
+ \v!bodypart\c!label=\v!section] % bijlageconversie=\Character
+
+\setuphead
+ [\v!subsection]
+ [\v!appendix\c!label=\v!subsection,
+ \v!bodypart\c!label=\v!subsection] % bijlageconversie=\Character
+
+\setuphead
+ [\v!subsubsection]
+ [\v!appendix\c!label=\v!subsubsection,
+ \v!bodypart\c!label=\v!subsubsection] % bijlageconversie=\Character
+
+% prefixes
+
+\defineprefixset
+ [\v!all]
+ [section-1,section-2,section-3,section-4,section-5,section-6,section-7,%
+ section-8,section-9,section-10,section-11,section-12]
+ []
+
+% \setuphead
+
+\setuphead
+ [\v!part,\v!chapter]
+ [%\c!align=,
+ %\c!indentnext=\v!no,
+ \c!continue=\v!no,
+ \c!page=\v!right,
+ \c!marking=\v!page,
+ \c!header=,
+ \c!style=\tfc,
+ \c!distance=.75\emwidth,
+ \c!textdistance=\emwidth plus \emwidth minus .25\emwidth,
+ \c!before={\blank[2*\v!big]},
+ \c!after={\blank[2*\v!big]}]
+
+\setuphead
+ [\v!section]
+ [%\c!align=,
+ %\c!indentnext=\v!no,
+ \c!style=\tfa,
+ \c!distance=.75\emwidth,
+ \c!textdistance=\emwidth plus \emwidth minus .25\emwidth,
+ \c!before={\blank[2*\v!big]},
+ \c!after=\blank]
+
+\setuphead % nieuw
+ [\v!subsection]
+ [\c!page=]
+
+% brrr
+
+% \definecounter[\v!page][\c!start=1] % todo: setup
+
+% lists
+
+\definecombinedlist
+ [\v!content]
+ [\v!part,
+ \v!chapter,
+ \v!section,
+ \v!subsection,
+ \v!subsubsection,
+ \v!subsubsubsection,
+ \v!subsubsubsubsection]
+ [\c!level=\v!subsubsubsubsection,
+ \c!criterium=\v!local]
+
+\setuplist
+ [\v!part]
+ [\c!before={\blank[\v!preference,\v!big]}, % sort of mkii compatible, watch columns
+ \c!after=\blank,
+ \c!label=\v!yes,
+ \c!distance=\emwidth]
+
+\setuplist
+ [\v!chapter]
+ [\c!before={\blank[\v!preference,\v!big]}, % sort of mkii compatible, watch columns
+ \c!after=]
+
+\setuplist [\v!part] [\c!width=0\emwidth]
+\setuplist [\v!chapter] [\c!width=2\emwidth]
+\setuplist [\v!section] [\c!width=3\emwidth]
+\setuplist [\v!subsection] [\c!width=4\emwidth]
+\setuplist [\v!subsubsection] [\c!width=5\emwidth]
+\setuplist [\v!subsubsubsection] [\c!width=6\emwidth]
+\setuplist [\v!subsubsubsubsection] [\c!width=7\emwidth]
+\setuplist [\v!subsubsubsubsubsection] [\c!width=8\emwidth]
+\setuplist [\v!subsubsubsubsubsubsection] [\c!width=9\emwidth]
+\setuplist [\v!subsubsubsubsubsubsubsection] [\c!width=10\emwidth]
+\setuplist [\v!subsubsubsubsubsubsubsubsection] [\c!width=11\emwidth]
+\setuplist [\v!subsubsubsubsubsubsubsubsubsection] [\c!width=12\emwidth]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-des.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-des.mklx
new file mode 100644
index 00000000000..b44593a3218
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-des.mklx
@@ -0,0 +1,199 @@
+%D \module
+%D [ file=strc-des,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Descriptions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Descriptions}
+
+\unprotect
+
+\installcorenamespace{description}
+
+\installcommandhandler \??description {description} \??description
+
+\aliased\let\setupdescriptions\setupdescription
+
+% \setupdescription % check with old
+% [\c!alternative=\descriptionparameter\c!location,
+% \c!location=\v!left]
+
+\setupdescription % check with old
+ [\c!alternative=\v!left,
+ \c!headstyle=\v!bold,
+ \c!titlestyle=\v!bold,
+ %\c!style=,
+ %\c!color=,
+ %\c!headcolor=,
+ %\c!titlecolor=,
+ \c!width=7\emwidth,
+ \c!distance=\emwidth,
+ \c!titledistance=.5\emwidth,
+ %\c!hang=,
+ %\c!sample=,
+ %\c!align=,
+ %\c!headalign=,
+ \c!margin=\v!no,
+ \c!before=\blank,
+ \c!inbetween=\blank,
+ \c!after=\blank,
+ \c!indentnext=\v!yes,
+ \c!indenting=\v!never,
+ \c!titleleft=(,
+ \c!titleright=),
+ %\c!closesymbol=,
+ \c!closecommand=\wordright,
+ \c!display=\v!yes,
+ %\c!command=,
+ \c!titlecommand=,
+ \c!expansion=\v!no,
+ %\c!xmlsetup=,
+ %\s!catcodes=,
+ \c!title=\v!yes,
+ %\c!text=,
+ ]
+
+\appendtoks
+ \ifempty\currentdescriptionparent
+ \defineconstruction[\currentdescription][\currentdescriptionparent][\s!handler=\v!description,\c!level=1]%
+ \else
+ \defineconstruction[\currentdescription][\s!handler=\v!description,\c!level=1]%
+ \fi
+ % We can combine these but in tracing (or errors) using a different caller is nicer.
+ \frozen\instance\setuevalue {\currentdescription}{\strc_descriptions_command[\currentdescription]}%
+ \frozen\instance\setuevalue{\e!start\currentdescription}{\strc_descriptions_start [\currentdescription]}%
+ \frozen\instance\setuevalue{\e!stop \currentdescription}{\strc_descriptions_stop}%
+\to \everydefinedescription
+
+\let\p_strc_constructions_title \empty
+
+\protected\setvalue{\??constructioninitializer\v!description}%
+ {\let \currentdescription \currentconstruction
+ \enforced\let\constructionparameter \descriptionparameter
+ \enforced\let\constructionnamespace \??description
+ \enforced\let\detokenizedconstructionparameter\detokenizeddescriptionparameter
+ \enforced\let\letconstructionparameter \letdescriptionparameter
+ \enforced\let\useconstructionstyleandcolor \usedescriptionstyleandcolor
+ \enforced\let\setupcurrentconstruction \setupcurrentdescription
+ \edef\p_strc_constructions_title{\constructionparameter\c!title}%
+ \ifx\p_strc_constructions_title\v!yes
+ \settrue\c_strc_constructions_title_state
+ \else
+ \setfalse\c_strc_constructions_title_state
+ \fi}
+
+% We keep the command variant around but rather would move to the
+% start-stop one. Also, passing the title as argument has some
+% history so we need to keep that as well.
+
+% \startdescription [reference] text \stopdescription
+% \startdescription [reference]{title} text \stopdescription
+% \startdescription [title=,title=,reference=] text \stopdescription
+
+% A bit over the top, the optional title but mkii has it too.
+
+\tolerant\protected\def\strc_descriptions_start[#1]#*[#2]%
+ {\begingroup
+ \strc_constructions_initialize{#1}%
+ \ifhastok={#2}%
+ \expandafter\strc_descriptions_start_setups
+ \orelse\ifconditional\c_strc_constructions_title_state
+ \expandafter\strc_descriptions_start_title
+ \else
+ \expandafter\strc_descriptions_start_reference
+ \fi[#2]}
+
+% we could use this:
+%
+% \def\strc_descriptions_preset
+% {\setdescriptionparameter\c!label{\descriptionparameter\c!text}%
+% \resetdescriptionparameter\c!bookmark
+% \resetdescriptionparameter\c!list
+% \resetdescriptionparameter\c!referencetext}
+
+\protected\def\strc_descriptions_start_setups[#1]%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference=,\c!title=,\c!bookmark=,\c!list=,\c!referencetext=,#1]%
+ \csname\??constructionstarthandler\currentconstructionhandler\endcsname}
+
+\protected\tolerant\def\strc_descriptions_start_title[#1]#*#=%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference={#1},\c!title={#2},\c!bookmark=,\c!list=,\c!referencetext=]%
+ \csname\??constructionstarthandler\currentconstructionhandler\endcsname}
+
+\protected\def\strc_descriptions_start_reference[#1]%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference={#1},\c!title=,\c!bookmark=,\c!list=,\c!referencetext=]%
+ \csname\??constructionstarthandler\currentconstructionhandler\endcsname}
+
+\protected\def\strc_descriptions_stop
+ {\csname\??constructionstophandler\currentconstructionhandler\endcsname}
+
+% \description [reference] text \par
+% \description [reference] {title} text \par
+
+\tolerant\protected\def\strc_descriptions_command[#1]#*[#2]%
+ {\begingroup
+ \strc_constructions_initialize{#1}%
+ \ifhastok={#2}%
+ \expandafter\strc_descriptions_command_setups
+ \orelse\ifconditional\c_strc_constructions_title_state
+ \expandafter\strc_descriptions_command_title
+ \else
+ \expandafter\strc_descriptions_command_reference
+ \fi[#2]}
+
+\protected\def\strc_descriptions_command_setups[#1]%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference=,\c!title=,\c!bookmark=,\c!list=,\c!referencetext=,#1]%
+ \csname\??constructioncommandhandler\currentconstructionhandler\endcsname}
+
+\protected\tolerant\def\strc_descriptions_command_title[#1]#:#*#=%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference={#1},\c!title={#2},\c!bookmark=,\c!list=,\c!referencetext=]%
+ \csname\??constructioncommandhandler\currentconstructionhandler\endcsname}
+
+\protected\def\strc_descriptions_command_reference[#1]%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference={#1},\c!title=,\c!bookmark=,\c!list=,\c!referencetext=]%
+ \csname\??constructioncommandhandler\currentconstructionhandler\endcsname}
+
+%D Handlers:
+
+\letcsname\??constructionmainhandler \v!description\expandafter\endcsname\csname\??constructionmainhandler \v!construction\endcsname
+\letcsname\??constructioncommandhandler\v!description\expandafter\endcsname\csname\??constructioncommandhandler\v!construction\endcsname
+\letcsname\??constructiontexthandler \v!description\expandafter\endcsname\csname\??constructiontexthandler \v!construction\endcsname
+
+\protected\setvalue{\??constructioncommandhandler\v!description}%
+ {\dowithpar
+ {\csname\??constructionstarthandler\v!construction\endcsname}%
+ {\csname\??constructionstophandler \v!construction\endcsname\endgroup}}
+
+\protected\setvalue{\??constructionstarthandler\v!description}%
+ {\dogotopar
+ {\csname\??constructionstarthandler\v!construction\endcsname}}
+
+\protected\setvalue{\??constructionstophandler\v!description}%
+ {\csname\??constructionstophandler\v!construction\endcsname
+ \endgroup}
+
+%D For historic reasons we have (from now on undocumented):
+
+\definedescription
+ [tab]
+ [\c!alternative=\v!left,
+ \c!headstyle=\v!normal,
+ \c!headcolor=,
+ \c!style=\v!normal,
+ \c!color=,
+ \c!width=\v!broad,
+ \c!sample=\kern4\emwidth, % was \hskip but that no longer is okay
+ \c!before=,
+ \c!after=]
+
+\permanent\protected\def\setuptab{\setupdescription[tab]} % for old times
+
+%D Maybe we should remove this definition.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-doc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-doc.mkxl
new file mode 100644
index 00000000000..50b71d9d2dc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-doc.mkxl
@@ -0,0 +1,85 @@
+%D \module
+%D [ file=strc-doc,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Document Structure,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Document Structure}
+
+\registerctxluafile{strc-doc}{}
+
+\unprotect
+
+%D This will move:
+
+\mutable\let\currentstructureattribute \empty
+\mutable\let\currentstructurebackreference \empty
+\mutable\let\currentstructurebookmark \empty
+\mutable\let\currentstructurecatcodes \empty
+\mutable\let\currentstructurecoding \empty
+\mutable\let\currentstructurecoupling \empty
+\mutable\let\currentstructureexpansion \empty
+\mutable\let\currentstructureincrementnumber \empty
+\mutable\let\currentstructurelabel \empty
+\mutable\let\currentstructurelevel \empty
+\mutable\let\currentstructurelist \empty
+\mutable\let\currentstructurelistattribute \empty
+\mutable\let\currentstructuremarking \empty
+\mutable\let\currentstructurename \empty
+\mutable\let\currentstructureownnumber \empty
+\mutable\let\currentstructureplaceholder \empty
+\mutable\let\currentstructurereference \empty
+\mutable\let\currentstructurereferenceattribute \empty
+\mutable\let\currentstructurereferenceprefix \empty
+\mutable\let\currentstructuresaveinlist \empty
+\mutable\let\currentstructureshownumber \empty
+\mutable\let\currentstructuretitle \empty
+\mutable\let\currentstructurexmlsetup \empty
+
+\mutable\let\currentstructurecomponentattribute \empty
+\mutable\let\currentstructurecomponentbookmark \empty
+\mutable\let\currentstructurecomponentcatcodes \empty
+\mutable\let\currentstructurecomponentcoding \empty
+\mutable\let\currentstructurecomponentexpansion \empty
+\mutable\let\currentstructurecomponentlabel \empty
+\mutable\let\currentstructurecomponentlevel \empty
+\mutable\let\currentstructurecomponentlist \empty
+\mutable\let\currentstructurecomponentmarking \empty
+\mutable\let\currentstructurecomponentname \empty
+\mutable\let\currentstructurecomponentreference \empty
+\mutable\let\currentstructurecomponentreferenceprefix\empty
+\mutable\let\currentstructurecomponentsynchronize \empty
+\mutable\let\currentstructurecomponenttitle \empty
+\mutable\let\currentstructurecomponentxmlsetup \empty
+
+\permanent\protected\def\setstructuresynchronization#1% todo: use ctxcontext
+ {\clf_setinternalreference
+ prefix {\currentstructurereferenceprefix}%
+ reference {\currentstructurereference}
+ internal \locationcount
+ view {\interactionparameter\c!focus}%
+ \relax
+ \enforced\xdef\currentstructureattribute
+ {\the\lastdestinationattribute}%
+ \enforced\xdef\currentstructuresynchronize
+ {\currentstructuresynchronize
+ \strc_lists_inject_enhance{#1}}}
+
+\permanent\protected\def\setstructurecomponentsynchronization#1% todo: use ctxcontext
+ {\clf_setinternalreference
+ prefix {\currentstructurecomponentreferenceprefix}%
+ reference {\currentstructurecomponentreference}
+ internal \locationcount
+ view {\interactionparameter\c!focus}%
+ \relax
+ \enforced\xdef\currentstructurecomponentattribute {\the\lastdestinationattribute}%
+ \enforced\xdef\currentstructurecomponentsynchronize{\strc_lists_inject_enhance{#1}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-enu.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-enu.mklx
new file mode 100644
index 00000000000..4bcdb6c4f32
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-enu.mklx
@@ -0,0 +1,372 @@
+%D \module
+%D [ file=strc-enu, % moved from strc-des
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Enumerations,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Enumerations}
+
+\unprotect
+
+% command -> headcommand
+
+%D Enumerations are just super descriptions. Therefore we implement
+%D them as an extension. After all, the keys don't clash. In order
+%D to be able to differentiate between specific settings we use
+%D some flags. Unfortunately we have some historic differences
+%D in default values. Therefore we push an extra step in the
+%D parent chain (we might do the same for regular descriptions).
+
+%D In order to be more flexible with theorems Aditya Mahajan added
+%D support for titles and endsymbols. At the same time we added more
+%D flexible support for inheriting numbers.
+%D
+%D \startbuffer
+%D \defineenumeration[one]
+%D \defineenumeration[two] [one] % clone one
+%D \defineenumeration[three] [counter=one,style=slanted] % only use counter of one
+%D \defineenumeration[four] [three] % clone three
+%D \defineenumeration[five] [three] [counter=five] % clone three and use own counter
+%D \defineenumeration[six] [three] [counter=four] % clone tree and use counter four (undefined)
+%D
+%D \startone test test 6 \stopone
+%D \starttwo test test 7 \stoptwo
+%D \startthree test test 8 \stopthree
+%D \startfour test test 9 \stopfour
+%D \startfive test test 2 \stopfive
+%D \startsix test test 10 \stopsix
+%D \stopbuffer
+%D
+%D \typebuffer \start \getbuffer \stop
+%D
+%D \starttyping
+%D \setupenumerations[one] [prefix=yes,prefixsegments=section]
+%D \setupenumerations[two] [prefix=yes,prefixsegments=section]
+%D \setupenumerations[three][prefix=yes,prefixsegments=section]
+%D \setupenumerations[four] [prefix=yes,prefixsegments=chapter:section]
+%D \setupenumerations[five] [prefix=yes,prefixsegments=chapter:section]
+%D \setupenumerations[six] [prefix=yes,prefixsegments=chapter:section]
+%D \stoptyping
+
+% list and titles are experimental
+%
+% \definedescription[test] [location=left,hang=4,headalign={right},distance=1em,list=test]
+% \defineenumeration[lemma][title=yes,right=:,textdistance=1em, location=top, titlestyle=\bs,list=lemma]
+% \defineenumeration[ammel][title=yes,right=:,textdistance=.5em,location=left,titlestyle=\it,width=9em]
+%
+% \placelist[enumeration:lemma]
+% \placelist[description:test][width=0pt]
+%
+% \starttest {something something something} \input zapf \stoptest
+% \startlemma {with a title of a certain length} \input tufte \stoplemma
+% \startammel {with a title} \input zapf \stopammel
+%
+% \defineenumeration[lemma][...]
+% \defineenumeration[titledlemma][lemma][title=yes,right=:,text=lemma,list=lemma]
+
+\installcorenamespace{enumeration}
+
+\installcommandhandler \??enumeration {enumeration} \??enumeration
+
+\installcounterassociation{enumeration}
+
+\let\setupenumerations\setupenumeration
+
+\setupenumeration % check with old
+ [\c!alternative=\v!top,
+ \c!headstyle=\v!bold,
+ \c!titlestyle=\v!bold,
+ %\c!style=,
+ %\c!color=,
+ %\c!headcolor=,
+ %\c!titlecolor=,
+ \c!width=8\emwidth,
+ %\c!distance=\zeropoint,
+ \c!distance=\emwidth,
+ \c!titledistance=.5\emwidth,
+ %\c!hang=,
+ %\c!sample=,
+ %\c!align=,
+ %\c!headalign=,
+ \c!margin=\v!no,
+ \c!before=\blank,
+ \c!inbetween=\blank,
+ \c!after=\blank,
+ \c!indentnext=\v!yes,
+ \c!indenting=\v!never,
+ \c!titleleft=(,
+ \c!titleright=),
+ %\c!closesymbol=,
+ \c!closecommand=\wordright,
+ \c!display=\v!yes,
+ %\c!command=,
+ \c!titlecommand=,
+ \c!expansion=\v!no,
+ %\c!xmlsetup=,
+ %\s!catcodes=,
+ \c!way=\v!by\v!text,
+ \c!prefix=\v!no,
+ \c!prefixconnector=.,
+ %\c!starter=,
+ %\c!stopper=,
+ \c!text=\currentenumeration,
+ \c!number=\v!yes, % else description
+ \c!start=0,
+ \c!state=\v!start,
+ \c!levels=4]
+
+% to be considered:
+%
+% \let\??construction\??enumeration
+%
+% with push/pop (also at definition time)
+
+\protected\def\strc_define_commands_enumeration#tag#level#parent%
+ {\doifelsenothing{#parent}
+ {\normalexpanded{\defineconstruction[#tag][\s!handler=\v!enumeration,\c!level=#level]}%
+ \setevalue{\??enumeration#tag:\s!parent}{\??enumeration}}%
+ {\normalexpanded{\defineconstruction[#tag][#parent][\s!handler=\v!enumeration,\c!level=#level]}%
+ \setevalue{\??enumeration#tag:\s!parent}{\??enumeration#parent}}%
+ \frozen\instance\setuevalue{\e!next #tag}{\strc_enumerations_next {#tag}{\number#level}}% obsolete
+ \frozen\instance\setuevalue{\c!reset#tag}{\strc_enumerations_reset {#tag}{\number#level}}% obsolete
+ %frozen\instance\setuevalue{\c!set #tag}{\strc_enumerations_set {#tag}{\number#level}}% obsolete
+ \frozen\instance\setuevalue {#tag}{\strc_enumerations_command[#tag]}% we could pass level here as well (faster)
+ \frozen\instance\setuevalue{\e!start#tag}{\strc_enumerations_start [#tag]}% we could pass level here as well (faster)
+ \frozen\instance\setuevalue{\e!stop #tag}{\strc_enumerations_stop }}
+
+\let\m_strc_enumeration_sub\empty
+
+\appendtoks
+ \let\m_strc_enumeration_sub\empty
+ \ifempty\currentenumerationparent
+ % clone => parent | subclone => clone | subsubclone => subclone
+ \strc_define_commands_enumeration
+ {\m_strc_enumeration_sub\currentenumeration}%
+ \plusone
+ \empty
+ \edef\p_levels{\enumerationparameter\c!levels}%
+ \dostepwiserecurse\plustwo\p_levels\plusone
+ {\strc_define_commands_enumeration
+ {\v!sub\m_strc_enumeration_sub\currentenumeration}%
+ \recurselevel
+ {\m_strc_enumeration_sub\currentenumeration}%
+ \edef\m_strc_enumeration_sub{\v!sub\m_strc_enumeration_sub}}%
+ \else
+ % clone => parent | subclone => subparent | subsubclone => subsubparent
+ \edef\p_levels{\enumerationparameter\c!levels}%
+ \dorecurse\p_levels
+ {\strc_define_commands_enumeration
+ {\m_strc_enumeration_sub\currentenumeration}%
+ \recurselevel
+ {\m_strc_enumeration_sub\currentenumerationparent}%
+ \edef\m_strc_enumeration_sub{\v!sub\m_strc_enumeration_sub}}%
+ \fi
+ \edef\p_counter{\enumerationparameter\s!counter}% can inherit from parent
+ \ifempty\p_counter %
+ \let\p_counter\currentenumeration
+ \fi
+ \doifelsecounter\p_counter\donothing{\strc_enumerations_define_counter\p_counter}%
+ \letenumerationparameter\s!counter\p_counter
+ %\strc_enumerations_setup_counter\currentenumeration
+\to \everydefineenumeration
+
+\let\p_strc_constructions_title \empty
+\let\p_strc_constructions_number\empty
+
+\protected\setvalue{\??constructioninitializer\v!enumeration}%
+ {\let \currentenumeration \currentconstruction
+ \enforced\let\constructionparameter \enumerationparameter
+ \enforced\let\constructionnamespace \??enumeration
+ \enforced\let\detokenizedconstructionparameter\detokenizedenumerationparameter
+ \enforced\let\letconstructionparameter \letenumerationparameter
+ \enforced\let\useconstructionstyleandcolor \useenumerationstyleandcolor
+ \enforced\let\setupcurrentconstruction \setupcurrentenumeration
+ \edef\p_strc_constructions_number{\constructionparameter\c!number}%
+ \ifx\p_strc_constructions_number\v!yes
+ \settrue\c_strc_constructions_number_state
+ \iftrialtypesetting
+ \strc_counters_save\currentconstructionnumber
+ \fi
+ \strc_counters_increment_sub\currentconstructionnumber\currentconstructionlevel
+ \else
+ \setfalse\c_strc_constructions_number_state
+ \fi
+ \edef\p_strc_constructions_title{\constructionparameter\c!title}%
+ \ifx\p_strc_constructions_title\v!yes
+ \settrue\c_strc_constructions_title_state
+ \else
+ \setfalse\c_strc_constructions_title_state
+ \fi}
+
+\protected\setvalue{\??constructionfinalizer\v!enumeration}%
+ {\ifconditional\c_strc_constructions_number_state
+ \iftrialtypesetting
+ \strc_counters_restore\currentconstructionnumber
+ \fi
+ \fi}
+
+%D Interfaces:
+
+\let\strc_enumerations_command\strc_descriptions_command
+\let\strc_enumerations_start \strc_descriptions_start
+\let\strc_enumerations_stop \strc_descriptions_stop
+
+\protected\def\strc_enumerations_next {\strc_constructions_next_indeed \namedenumerationparameter} % #1#2
+\protected\def\strc_enumerations_reset{\strc_constructions_reset_indeed\namedenumerationparameter} % #1#2
+%protected\def\strc_enumerations_set {\strc_constructions_set_indeed \namedenumerationparameter} % #1#2
+
+%D Counters (maybe we can share this code): (not ok yet, messy with notes)
+
+% Maybe we should move counters to the construction level as more derived mechanisms
+% use it \unknown\ so don't depend on names here!
+
+\def\strc_enumerations_define_counter#tag% todo: move inline
+ {\definecounter[#tag]%
+ \registerenumerationcounter{#tag}}
+
+\appendtoks
+ \synchronizeenumerationcounters
+\to \everysetupenumeration
+
+\appendtoks
+ \synchronizeenumerationcounters
+\to \everydefineenumeration
+
+%D Initializer:
+
+%D The handlers.
+
+\newtoks\everyenumeration
+
+\letcsname\??constructionmainhandler \v!enumeration\expandafter\endcsname\csname\??constructionmainhandler \v!description\endcsname
+\letcsname\??constructioncommandhandler\v!enumeration\expandafter\endcsname\csname\??constructioncommandhandler\v!description\endcsname
+\letcsname\??constructionstarthandler \v!enumeration\expandafter\endcsname\csname\??constructionstarthandler \v!description\endcsname
+\letcsname\??constructionstophandler \v!enumeration\expandafter\endcsname\csname\??constructionstophandler \v!description\endcsname
+
+\protected\setvalue{\??constructiontexthandler\v!enumeration}%
+ {\begingroup
+ \useconstructionstyleandcolor\c!headstyle\c!headcolor
+ \strc_enumerations_text
+ \endgroup}
+
+\protected\def\strc_enumerations_text
+ {\the\everyenumeration % will become obsolete
+ \ifconditional\c_strc_constructions_number_state
+ \ifx\currentconstructionreference\!!plustoken
+ \strc_enumerations_full_number_symbol
+ \orelse\ifx\currentconstructionreference\!!minustoken
+ \strc_enumerations_full_number_nop
+ \else
+ \strc_enumerations_full_number_coupling
+ \fi
+ \else
+ \strc_enumerations_full_number_nop
+ \fi}
+
+\protected\def\strc_enumerations_inject_text_with_space
+ {\edef\p_text{\constructionparameter\c!text}%
+ \ifempty\p_text \else
+ \p_text
+ \removeunwantedspaces
+ \nobreakspace
+ \fi}
+
+\protected\def\strc_enumerations_inject_extra_text
+ {\ifconditional\c_strc_constructions_title_state
+ \clf_doifelselisthastitle{\currentconstructionmain}\numexpr\currentconstructionlistentry\relax
+ \strc_enumerations_inject_extra_text_indeed
+ \donothing
+ \fi}
+
+\protected\def\strc_enumerations_inject_text
+ {\constructionparameter\c!numbercommand
+ {\constructionparameter\c!text}}
+
+% head ( number title )
+
+\protected\def\strc_enumerations_inject_extra_text_indeed
+ {\begingroup
+ \hskip\constructionparameter\c!titledistance % we listen to the headstyle
+ \useconstructionstyleandcolor\c!titlestyle\c!titlecolor
+ \constructionparameter\c!titlecommand
+ {\constructionparameter\c!titleleft
+ \clf_savedlisttitle{\currentconstructionmain}\currentconstructionlistentry\relax
+ \constructionparameter\c!titleright}%
+ \endgroup}
+
+\protected\def\strc_enumerations_inject_symbol % todo check
+ {\constructionparameter\c!left
+ \constructionparameter\c!symbol
+ \constructionparameter\c!right}
+
+\protected\def\strc_enumerations_inject_number
+ {\constructionparameter\c!left
+ \constructionparameter\c!starter
+ \clf_savedlistprefixednumber{\currentconstructionmain}\currentconstructionlistentry\relax
+ \constructionparameter\c!stopper
+ \constructionparameter\c!right}
+
+\protected\def\strc_enumerations_inject_text_and_number
+ {\constructionparameter\c!numbercommand
+ {\strc_enumerations_inject_text_with_space
+ \strc_enumerations_inject_number}}
+
+\protected\def\strc_enumerations_inject_text_and_symbol
+ {\constructionparameter\c!numbercommand
+ {\strc_enumerations_inject_text_with_space
+ \strc_enumerations_inject_symbol}}
+
+\protected\def\strc_enumerations_full_number_yes
+ {\constructionparameter\c!headcommand
+ {\begstrut
+ \strc_enumerations_inject_text_and_number
+ \strc_enumerations_inject_extra_text
+ \endstrut}}
+
+\protected\def\strc_enumerations_full_number_nop
+ {\constructionparameter\c!headcommand
+ {\begstrut
+ \strc_enumerations_inject_text
+ \strc_enumerations_inject_extra_text
+ \endstrut}}
+
+\protected\def\strc_enumerations_full_number_symbol
+ {\constructionparameter\c!headcommand
+ {\begstrut
+ \strc_enumerations_inject_text_and_symbol
+ \strc_enumerations_inject_extra_text
+ \endstrut}}
+
+% to be used in for instance:
+%
+% coupling=questions .... coupling=answers
+%
+% we will provide some more control, like skipping etc or reference relates
+
+% to do / to be checked
+
+\protected\def\strc_enumerations_full_number_coupling
+ {\iflocation
+ \strc_enumerations_full_number_yes
+ \else
+ \iftrialtypesetting \else
+ % \doenumerationregistercoupling
+ \fi
+ \strc_enumerations_full_number_yes
+ \edef\p_coupling{\constructionparameter\c!coupling}%
+ \ifempty\p_coupling \else
+ \symbolreference[order(construction:\p_coupling:\the\locationorder)]%
+ \fi
+ \fi}
+
+\protected\def\strc_enumerations_skip_number_coupling[#tag]% e.g. for questions with no answer
+ {\clf_setnextreferenceorder{construction}{#tag}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-flt.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-flt.mklx
new file mode 100644
index 00000000000..8f96701f5df
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-flt.mklx
@@ -0,0 +1,2609 @@
+%D \module
+%D \module
+%D [ file=strc-flt,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Float Numbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module will be redone with conditionals and everythings
+
+\writestatus{loading}{ConTeXt Structure Macros / Float Numbering}
+
+\registerctxluafile{strc-flt}{}
+
+\unprotect
+
+% todo: a keyword for this (and then a settings->hash for speed)
+%
+% \setuplayout[width=middle,backspace=3cm]
+%
+% \appendtoks
+% \settrue\inhibitmargindata
+% \to \everyinsidefloat
+%
+% \starttext
+% \dorecurse{20}{
+% \par \inleft{\red\infofont<#1>} \par
+% \placefigure[leftmargin]{}{\framed[height=1cm,width=2cm]{}}
+% % \placefigure{#1}{\framed[height=1cm,width=2cm]{}}
+% \par line #1.1 \par line #1.2 \par
+% }
+% \stoptext
+
+% todo: delay caption creation and make setups for each method instead
+% so that we can have a list of methods and redo them as we can
+% keep the list or even better: recreate it
+%
+% todo: strc_floats_analyze_variables_two could trigger a setup
+% and we could have nofmethods of them
+%
+% todo: move variables from page-flt to strc-flt
+%
+% todo: p_name etc
+%
+% todo: less globals!
+%
+% todo: do all options in lua
+
+%D This module is being converted into a mkvi one.
+%D
+%D - rename macros
+%D - get rid of dead code
+%D - less gobal mess
+%D - more mkiv-ish
+
+\installcorenamespace{float}
+\installcorenamespace{floatbuilder}
+\installcorenamespace{floatcaption}
+\installcorenamespace{floatframed}
+
+\installframedcommandhandler \??float {float} \??float
+\installframedcommandhandler \??floatcaption {floatcaption} \??floatcaption
+\installframedcommandhandler \??floatframed {floatframed} \??floatframed
+
+\aliased\let\setupfloats \setupfloat
+\aliased\let\setupcaption \setupfloatcaption
+\aliased\let\setupcaptions\setupfloatcaption
+
+\permanent\protected\def\dohandlenextfloatindent
+ {\useindentnextparameter\floatparameter
+ \dorechecknextindentation}
+
+\setupcaptions
+ [\c!location=\v!bottom,
+ \c!grid=,
+ \c!before=, % not used (yet)
+ \c!inbetween={\blank[\v!medium]},
+ \c!after=, % not used (yet)
+ \c!spacebefore=,
+ \c!spaceinbetween=, % replaces fuzzy inbetween dual usage
+ \c!spaceafter=,
+ \c!width=\v!fit,
+ \c!minwidth=\v!fit, % id est: the width of the floatbox in some cases
+ \c!headstyle=\v!bold,
+ \c!headcolor=,
+ \c!leftmargin=\zeropoint,
+ \c!rightmargin=\zeropoint,
+ \c!outermargin=\zeropoint,
+ \c!innermargin=\zeropoint,
+ \c!setups=,
+ \c!style=\v!normal,
+ \c!color=,
+ \c!textstyle=,
+ \c!textcolor=,
+ \c!align=,
+ \c!number=\v!yes,
+ \c!offset=\v!overlay,
+ \c!frame=\v!off,
+ % \c!expansion=,
+ % \c!prefix=,
+ % \c!prefixconnector=,
+ % \c!way=,
+ % \c!prefixsegments=,
+ % \c!way=,
+ % \c!blockway=,
+ % \c!sectionnumber=,
+ % \c!separator=,
+ % \c!starter=,
+ % \c!stopper=,
+ \c!suffixseparator=, % currently rather hard coded
+ \c!suffix=\floatcaptionsuffix,
+ \c!distance=\emwidth, % plus .5\emwidth minus .25\emwidth
+ \c!conversion=\v!numbers,
+ \c!maxwidth=\hsize,
+ \c!command=]
+
+% we can comment some of these
+
+\setupfloats
+ [\c!location=\v!middle,
+ \c!width=8\lineheight,
+ \c!height=6\lineheight,
+ \c!offset=\v!overlay,
+ \c!frame=\v!off,
+ \c!strut=\v!no,
+ \c!radius=.5\bodyfontsize,
+ \c!corner=\v!rectangular,
+ \c!grid=,
+ %\c!background=,
+ %\c!backgroundcolor=,
+ \c!backgroundoffset=\!!zeropoint,
+ %\c!topframe=,
+ %\c!bottomframe=,
+ %\c!leftframe=,
+ %\c!rightframe=,
+ \c!frameoffset=\!!zeropoint,
+ %\c!before=,
+ %\c!after=,
+ \c!spacebefore=\v!big,
+ \c!spaceafter=\v!big,
+ \c!sidespacebefore=\rootfloatparameter\c!spacebefore,
+ \c!sidespaceafter=\rootfloatparameter\c!spaceafter,
+ \c!sidespaceinbetween=\rootfloatparameter\c!spacebefore,
+ \c!spacebeforeside=, % extra, not part of, can be used to add whitespace before text
+ \c!spaceafterside=, % idem
+ \c!sidealign=\v!normal,
+ \c!textmethod=\ifgridsnapping2\else0\fi, % 0=raw 1=safe (.99pg) 2=tight (-1pt) % THIS WILL CHANGE
+ \c!sidemethod=\ifgridsnapping2\else1\fi, % 0=raw 1=safe (.99pg) 2=tight (-1pt) % THIS WILL CHANGE
+ \c!indentnext=\v!no,
+ \c!margin=\emwidth,
+ \c!method=1,
+ \c!cache=\v!yes, % when no, then intermediate flush
+ \c!leftmargin=\zeropoint, % displacement in 'normal floats'
+ \c!rightmargin=\zeropoint, % idem
+ \c!innermargin=\zeropoint, % idem
+ \c!outermargin=\zeropoint, % idem
+ \c!leftmargindistance=\zeropoint,
+ \c!rightmargindistance=\floatparameter\c!leftmargindistance,
+ \c!step=\v!small, % the flush side float step (big, medium, small : always depth)
+ \c!ntop=2,
+ \c!nbottom=0,
+ \c!nlines=4, % used?
+ \c!topoffset=\zeropoint,
+ \c!bottomoffset=\zeropoint,
+ \c!freeregion=\v!yes,
+ %\c!local=,
+ %\c!bottombefore=, % e.g. \vfill
+ %\c!bottomafter=,
+ %\c!default=, % default location
+ \c!sidethreshold=.5\strutdp, % set to "old" to check with old method
+ \c!numbering=\v!yes,
+ \c!compress=\v!yes, % when possible pack floats when flushing
+ \c!compressdistance=\emwidth]
+
+\setupfloatframed
+ [\c!frame=\v!off,
+ \c!offset=\v!overlay,
+ \c!strut=\v!no]
+
+\appendtoks
+ \doifelse{\floatparameter\c!compress}\v!yes\settrue\setfalse\c_page_floats_pack_flushed
+ \d_page_floats_compress_distance\floatparameter\c!compressdistance\relax
+\to \everysetupfloat
+
+\mutable\let\currentfloatattribute \empty
+\mutable\let\currentfloatcounter \empty
+\mutable\let\currentfloatgroup \empty
+\mutable\let\currentfloatnumber \empty
+\mutable\let\currentfloatsynchronize\empty
+
+\let\m_strc_floats_previous_number \empty % we can also just push/pop
+
+%D We need to do it again here:
+
+\setupfloat
+ [\c!compress=\v!yes, % when possible pack floats when flushing
+ \c!compressdistance=\emwidth]
+
+%D Individial settings:
+
+\installcounterassociation{floatcaption}
+
+\appendtoks
+ \let\currentfloat\currentfloatcaption % ?
+ \synchronizefloatcaptioncounters
+\to \everysetupfloatcaption
+
+\appendtoks
+ \let\currentfloat\currentfloatcaption % ?
+ \synchronizefloatcaptioncounters
+\to \everydefinefloatcaption
+
+%D Definitions:
+
+\let\strc_floats_define_saved\definefloat
+
+\permanent\tolerant\protected\overloaded\def\definefloat[#1]#*[#2]#*[#3]% name+plural+parent | name+parent+settings
+ {\ifarguments\or
+ \strc_floats_define_c[#1][#1]%
+ \or
+ \doifelsecommandhandler\??float{#2}%
+ {\strc_floats_define_a[#1][#1][#2]}%
+ {\strc_floats_define_c[#1][#2]}%
+ \orelse\ifhastok={#3}%
+ \strc_floats_define_b[#1][#2][#3]%
+ \else
+ \strc_floats_define_a[#1][#2][#3]%
+ \fi}
+
+\def\strc_floats_define_a[#1][#2][#3]% name names parent
+ {\definefloatcaption[#1][#3]%
+ \definefloatframed[#1][#3]%
+ \definecounter[#1][#3]%
+ \definelist[#1][#3]%
+ \copylabeltext[#1=#3]%
+ \strc_floats_define_saved[#1][#3]%
+ \strc_floats_define_commands{#1}{#2}}
+
+\def\strc_floats_define_b[#1][#2][#3]% name parent settings
+ {\definefloatcaption[#1][#2]%
+ \definefloatframed[#1][#2]%
+ \definecounter[#1][#2]%
+ \definelist[#1][#2]%
+ \copylabeltext[#1=#2]%
+ \strc_floats_define_saved[#1][#2][#3]%
+ \strc_floats_define_commands{#1}{#1}}
+
+\def\strc_floats_define_c[#1][#2]% name names
+ {\registerfloatcaptioncounter{#1}%
+ \definefloatcaption[#1]%
+ \definefloatframed[#1]%
+ \definecounter[#1]%
+ \definelist[#1]%
+ \presetlabeltext[#1=\Word{#1}~]%
+ \presetheadtext[#2=\Word{#2}]%
+ \strc_floats_define_saved[#1]%
+ \strc_floats_define_commands{#1}{#2}}
+
+\def\strc_floats_define_commands#1#2%
+ {\frozen\instance\setuevalue{\e!place\e!listof#2}{\strc_lists_place[#1]}% call will change
+ \frozen\instance\setuevalue{\e!complete\e!listof#2}{\strc_lists_complete[#1][#2]}% call will change
+ \frozen\instance\setuevalue{\e!place#1}{\placefloat[#1]}%
+ \frozen\instance\setuevalue{\e!start\e!place#1}{\startplacefloat[#1]}%
+ \frozen\instance\setuevalue{\e!stop\e!place#1}{\stopplacefloat}%
+ \frozen\instance\setuevalue{\e!start#1\e!text}{\strc_floats_start_text[#1]}%
+ \frozen\instance\setuevalue{\e!stop#1\e!text}{\strc_floats_stop_text}%
+ % these will become obsolete:
+ \frozen\instance\setuevalue{\e!reserve#1}{\strc_floats_reserve[#1]}%
+ \frozen\instance\setuevalue{\e!start\e!reserve#1\e!text}{\strc_floats_start_reserve_text[#1]}%
+ \frozen\instance\setuevalue{\e!stop\e!reserve#1\e!text}{\strc_floats_stop_reserve_text}}
+
+%D Fallback float body:
+
+\protected\def\strc_floats_place_empty_box % \inheritedfloatframed
+ {\framed
+ [\c!frame=\v!on,
+ \c!width=\rootfloatparameter\c!width,
+ \c!height=\rootfloatparameter\c!height,
+ \c!location=\v!normal,
+ \c!offset=\rootfloatparameter\c!offset]%
+ {\getmessage\m!floatblocks{12}\empty}}
+
+%D Data. We can generalize this to lists.
+
+\newif\ifnofloatcaption
+\newif\ifnofloatnumber
+\newif\ifemptyfloatcaption
+
+\installstructurelistprocessor\s!float{\usestructurelistprocessor{number+title}}
+
+\permanent\protected\def\thecurrentfloatnumbersuffix
+ {\doifsomething{\floatcaptionparameter\c!suffix}
+ {\floatcaptionparameter\c!suffixseparator
+ \floatcaptionparameter\c!suffix
+ \floatcaptionparameter\c!suffixstopper}}
+
+\permanent\protected\def\thecurrentfloatnumber
+ {\ifnofloatcaption \orelse \ifnofloatnumber \orelse \ifrelax\currentfloatnumber \else
+ \namedtaggedlabeltexts
+ \t!floatlabel \currentfloat
+ \t!floatnumber\currentfloat
+ {\begstrut
+ \floatcaptionparameter\c!numbercommand
+ {\clf_savedlistprefixednumber{\currentfloat}\currentfloatnumber\relax
+ \thecurrentfloatnumbersuffix}%
+ \endstrut}%
+ \fi}
+
+\permanent\protected\def\thecurrentfloatcaption
+ {\ifnofloatcaption \orelse \ifemptyfloatcaption \orelse \ifrelax\currentfloatnumber \else
+ \dostarttagged\t!floattext\empty
+ \begstrut
+ \floatcaptionparameter\c!textcommand
+ {\clf_savedlisttitle{\currentfloat}\numexpr\currentfloatnumber\relax}%
+ \endstrut
+ \dostoptagged
+ \fi}
+
+%D Captions.
+
+\mutable\let\floatcaptionarrangement\empty
+\mutable\let\floatcaptionattribute \empty
+\mutable\let\floatcaptiondirectives \empty
+\mutable\let\floatcaptionlocation \empty
+\mutable\let\floatcaptionnumber \empty
+\mutable\let\floatcaptionsuffix \empty
+
+% For a while these were placeholders:
+%
+%D \starttyping
+%D \protected\def\placefloatcaption{\dodoubleempty\strc_floats_place_caption}
+%D \protected\def\setfloatcaption {\dodoubleempty\strc_floats_set_caption}
+%D
+%D \def\strc_floats_place_caption[#tag][#reference]#caption{[not supported]}
+%D \def\strc_floats_set_caption [#tag][#reference]#caption{[not supported]}
+%D
+%D \protected\def\placefloatcaptiontext [#tag]{[not suported yet]}
+%D \protected\def\placefloatcaptionnumber [#tag]{[not suported yet]}
+%D \protected\def\placefloatcaptionreference[#tag]{[not suported yet]}
+%D \stoptyping
+%D
+%D because in \MKII\ we had:
+%D
+%D \starttyping
+%D \let\placefloatlabel \placefloatcaption
+%D \let\placefloatlabeltext \placefloatcaptiontext
+%D \let\placefloatlabelreference\placefloatcaptionreference
+%D \stoptyping
+%D
+%D But as it was never advertised we don't provide it in \MKIV. However, at some
+%D point HvdM wanted this:
+%D
+%D \starttyping
+%D \placefigure {labeltext-1} {\externalfigure[figure-1]}
+%D \placefloatcaption[figure][title={labeltext-2}] \externalfigure[figure-2]
+%D \placefigure {labeltext-3} {\externalfigure[figure-3]}
+%D \stoptyping
+%D
+%D So there you have it:
+
+\permanent\tolerant\protected\def\placefloatcaption[#category]#spacer[#settings]#spacer[#userdata]%
+ {\ifarguments\or\else % >= 2
+ % we need at least a category and title
+ \dontleavehmode
+ \bgroup
+ \edef\currentfloat{#category}%
+ \let\currentfloatcaption\currentfloat
+ \resetfloatcaptionparameter\c!reference
+ \resetfloatcaptionparameter\c!title
+ \resetfloatcaptionparameter\c!marking
+ \resetfloatcaptionparameter\c!list
+ \resetfloatcaptionparameter\c!bookmark
+ \resetfloatcaptionparameter\c!referencetext
+ \setupcurrentfloatcaption[#settings]%
+ \edef\currentfloatcounter{\namedcounterparameter\currentfloat\s!name}%
+ \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi
+ \strc_counters_increment\currentfloatcounter
+ \strc_counters_register_component
+ \s!float
+ \setupcurrentfloatcaption
+ \floatcaptionparameter
+ \detokenizedfloatcaptionparameter
+ \relax
+ \relax
+ \relax
+ [\s!name=\currentfloat,
+ \s!counter=\currentfloatcounter,%
+ \s!hascaption=\v!yes,%
+ \s!hasnumber=\v!yes,%
+ \s!hastitle=\v!yes]%
+ [#userdata]%
+ \glet\m_strc_floats_previous_number\m_strc_counters_last_registered_index
+ \glet\currentfloatnumber \m_strc_counters_last_registered_index
+ \glet\currentfloatattribute \m_strc_counters_last_registered_attribute
+ \glet\currentfloatsynchronize \m_strc_counters_last_registered_synchronize
+ %
+ % \iflocation
+ % \c_attr_destination\currentfloatattribute\relax
+ % \fi
+ \currentfloatsynchronize
+ \strc_floats_make_complete_caption
+ %
+ \iftrialtypesetting\strc_counters_restore\currentfloatcounter\fi
+ \egroup
+ \fi}
+
+\newbox \b_strc_floats_caption
+\newbox \b_strc_floats_content
+\newdimen\d_strc_floats_caption_height
+\newdimen\d_strc_floats_caption_depth
+
+\let\m_strc_floats_last_caption_tag\empty
+
+\def\strc_floats_make_complete_caption
+ {\doifsomething{\floatcaptionparameter\c!spacebefore}{\blank[\floatcaptionparameter\c!spacebefore]}%
+ \strc_floats_make_complete_caption_before
+ \synchronizedisplaydirection % temp hack, till we have a proper model
+ \noindent
+ \gdef\m_strc_floats_last_caption_tag{\strut\thecurrentfloatnumber}% was xdef ... needs checking
+ \begingroup
+ \ifnofloatcaption
+ \global\d_strc_floats_caption_height\zeropoint
+ \global\d_strc_floats_caption_depth \zeropoint
+ \else
+ \usefloatcaptionstyleandcolor\c!style\c!color
+ \clf_doifelselisthastitle{\currentfloat}\numexpr\currentfloatnumber\relax
+ \donothing
+ \emptyfloatcaptiontrue
+ \ifnofloatnumber \orelse \ifnofloatcaption \else
+ \ifemptyfloatcaption
+ \hbox{\usefloatcaptionstyleandcolor\c!headstyle\c!headcolor\thecurrentfloatnumber}%
+ \else
+ \doifelsenothing{\floatcaptionparameter\c!spaceinbetween}
+ {\scratchskip\floatcaptionparameter\c!distance\relax
+ \setbox\scratchbox\hbox
+ {\usefloatcaptionstyleandcolor\c!headstyle\c!headcolor
+ \thecurrentfloatnumber
+ \floatcaptionparameter\c!headseparator
+ \dotfskip\scratchskip}%
+ \doifelse{\floatcaptionparameter\c!hang}\v!yes
+ {\leftskip\wd\scratchbox
+ \llap{\box\scratchbox}}
+ {\unhbox\scratchbox}%
+ \emergencystretch.5\scratchskip}
+ {\hbox{\usefloatcaptionstyleandcolor\c!headstyle\c!headcolor\thecurrentfloatnumber}%
+ \blank[\floatcaptionparameter\c!spaceinbetween]}%
+ \fi
+ \fi
+ \usefloatcaptionstyleandcolor\c!textstyle\c!textcolor
+ \global\d_strc_floats_caption_height\strutheight
+ \global\d_strc_floats_caption_depth \strutdepth
+ \thecurrentfloatcaption\endgraf
+ \fi
+ \endgroup
+ \strc_floats_make_complete_caption_after
+ \doifsomething{\floatcaptionparameter\c!spaceafter}{\blank[\floatcaptionparameter\c!spaceafter]}}
+
+%let\strc_floats_make_complete_caption_before\relax
+\let\strc_floats_make_complete_caption_after \relax
+
+\def\strc_floats_make_complete_caption_before
+ {\doifelseframed\floatcaptionparameter\strc_floats_make_complete_caption_before_indeed\relax}
+
+\def\strc_floats_make_complete_caption_before_indeed
+ {\edef\m_strc_align{\floatcaptionparameter\c!align}%
+ \edef\m_strc_strut{\floatcaptionparameter\c!strut}%
+ \letfloatcaptionparameter\c!align\v!normal
+ \letfloatcaptionparameter\c!strut\v!no
+ \inheritedfloatcaptionframed
+ \bgroup
+ \letfloatcaptionparameter\c!align\m_strc_align
+ \letfloatcaptionparameter\c!strut\m_strc_strut
+ \let\strc_floats_make_complete_caption_after\egroup}
+
+% \definefloat [figure-1] [figure]
+% \definefloat [figure-2] [figure]
+% \setupfloat [figure-1] [location=left,leftmargin=10mm]
+% \setupfloat [figure-2] [location=left,leftmargin=-5mm]
+% \setupcaption [figure-1] [align=flushleft]
+% \setupcaption [figure-2] [align=flushleft,leftmargin=15mm]
+%
+% \startsetups somefigure
+% \ifdim\wd\nextbox>\textwidth
+% \placefloat[figure-2][][]{}{\box\nextbox}
+% \else
+% \placefloat[figure-1][][]{}{\box\nextbox}
+% \fi
+% \stopsetups
+%
+% \protected\def\setupswithbox[#1]{\dowithnextbox{\setups[#1]}\vbox}
+%
+% test \setupswithbox[somefigure]{\framed[width=3cm] {}} test
+% test \setupswithbox[somefigure]{\framed[width=\dimexpr\textwidth+3cm\relax]{}} test
+
+% temporary removed ... was not applied systematically
+%
+% \def\dosetcaptionthings
+% {\usesetupsparameter\floatcaptionparameter}
+
+\def\strc_floats_check_caption_content
+ {\ifnofloatcaption\else
+ \setbox\b_strc_floats_caption\hbox
+ {\settrialtypesetting
+ \notesenabledfalse
+ \strc_floats_make_complete_caption}%
+ % new, \placefigure{\xmlfirst{#1}{somecaption}}{} passes earlier empty check
+ % so here we misuse the scratch box; actually this means that the previous
+ % test can go away (some day, when i redo this module)
+ \ifzeropt\wd\b_strc_floats_caption
+ \global\emptyfloatcaptiontrue
+ \ifnofloatnumber
+ \global\nofloatcaptiontrue
+ \fi
+ \else
+ \global\emptyfloatcaptionfalse
+ \setbox\b_strc_floats_caption\hpack{\hskip\leftskip\box\b_strc_floats_caption}%
+ \fi
+ \fi}
+
+%D We can do this ...
+%D
+%D \starttyping
+%D \newcount\c_strc_floats_n
+%D
+%D \definedataset[\s!float][\c!delay=\v!yes]
+%D
+%D \let\strc_float_realpage\realpageno
+%D
+%D \def\strc_float_save_data
+%D {\setdataset[\s!float][\number\c_strc_floats_n][]}
+%D
+%D \def\strc_float_load_data % precedes save !
+%D {\global\advance\c_strc_floats_n\plusone
+%D \xdef\strc_float_realpage{\datasetvariable\s!float{\number\c_strc_floats_n}\s!page}%
+%D \ifempty\strc_float_realpage
+%D \glet\strc_float_realpage\realpageno % \realfolio
+%D \fi}
+%D \stoptyping
+%D
+%D ... but this is more efficient:
+
+\definepagestate[\s!float][\c!delay=\v!yes]
+
+\newcount\c_strc_floats_n
+
+\let\strc_float_realpage\realpageno
+
+\def\strc_float_save_data
+ {\setpagestate[\s!float][\number\c_strc_floats_n]}
+
+\def\strc_float_load_data % precedes save !
+ {\global\advance\c_strc_floats_n\plusone
+ \xdef\strc_float_realpage{\pagestaterealpage\s!float{\number\c_strc_floats_n}}%
+ \ifempty\strc_float_realpage
+ \glet\strc_float_realpage\realpageno % \realfolio
+ \fi}
+
+%D test case:
+%D
+%D \starttyping
+%D \setupfloat[figure][criterium=\marginwidth,fallback=bottom]
+%D \dorecurse{3}{
+%D \chapter{test}
+%D \placefigure[bottom]{1}{\framed{bottom}}
+%D test
+%D \placetable[bottom]{1}{\framed{table}}
+%D test
+%D \placetable{2}{\framed{table}}
+%D test
+%D \placefigure[left]{2}{\framed{left but way too wide}}
+%D \input tufte
+%D \placefigure[left]{3}{\framed{left but ok}}
+%D \input tufte }
+%D \stoptyping
+
+% A complication is that we may have to handle a pagebreak first, which in turn may
+% issue a (postponed) float. Therefore we may not trust on variable assignments
+% before we're really dealing with the float. Some day I'll root out the global
+% settings.
+
+\aliased\let\lastplacedfloat\empty
+
+\def\strc_floats_set_current_tag#tag%
+ {\edef\currentfloat{#tag}%
+ \ifempty\currentfloat
+ \let\currentfloat\v!figure % a bit of a hack
+ \fi
+ \doifelsecommandhandler\??float\currentfloat
+ \donothing
+ {\writestatus\m!floatblocks{unknown float type '\currentfloat'}%
+ \let\currentfloat\v!figure}% also a hack
+ \enforced\glet\lastplacedfloat\currentfloat
+ \let\m_strc_floats_saved_userdata\empty
+ \let\currentfloatcaption\currentfloat}
+
+\mutable\let\askedfloatmethod \empty
+\mutable\let\askedfloatoptions\empty
+
+\def\strc_floats_reset_variables
+ {\global\emptyfloatcaptionfalse
+ \global\nofloatcaptionfalse
+ \global\nofloatnumberfalse
+ \glet\askedfloatmethod \empty
+ \glet\askedfloatoptions\empty}
+
+% place
+
+\mutable\let\floatlabel \empty
+\mutable\let\floatcolumn \empty
+\mutable\let\floatrow \empty
+\mutable\let\floatlocation \empty
+\mutable\let\floatlocationmethod\empty
+
+\def\strc_floats_analyze_location
+ {% more will be moved here
+ \let\floatlabel \empty
+ \let\floatcolumn\empty
+ \let\floatrow \empty
+ %
+ \edef\floatcaptionlocation{\floatcaptionparameter\c!location}%
+ %
+ \setfloatmethodvariables\floatlocation}
+
+\newtoks\c_floats_every_table_float
+
+\appendtoks
+ \edef\floatlocation{\v!force,\v!always,\floatlocation}%
+ \setupfloat[\c!spacebefore=\v!none,\c!spaceafter=\v!none]%
+\to \c_floats_every_table_float
+
+\ifdefined\dotagregisterfloat \else \aliased\let\dotagregisterfloat\gobbletwoarguments \fi
+
+\tolerant\def\strc_floats_place[#tag]#spacer[#location]#spacer[#reference]#:#caption%
+ {\strc_floats_set_current_tag{#tag}%
+ \strc_floats_reset_variables
+ \xdef\askedfloatoptions{#location}%
+ \edef\floatlocation{#location}%
+ \ifempty\floatlocation
+ \edef\floatlocation{\floatparameter\c!default}% beware of a clash between alignment locations
+ \fi
+ \ifintable
+ \the\c_floats_every_table_float
+ \fi
+ \strc_floats_analyze_location
+ % todo: use \lets
+ \setupcurrentfloatcaption[\c!reference={#reference},\c!title={#caption},\c!marking=,\c!list=,\c!bookmark=,\c!referencetext=]%
+ \doifelseinset\v!split\floatlocation\strc_floats_place_next_box_split\strc_floats_place_next_box_normal}
+
+\permanent\protected\def\placefloat
+ {\flushnotes
+ \page_otr_command_flush_side_floats % here !
+ \strc_floats_begin_group
+ \strc_floats_place}
+
+% start-stop
+
+% \startplacefigure[title=oeps][subtitle=whatever]
+% \framed[width=10cm,height=5cm]{\floatuserdataparameter{subtitle}}
+% \stopplacefigure
+
+\installcorenamespace{floatuserdata}
+
+\installsetuponlycommandhandler \??floatuserdata {floatuserdata}
+
+\let\m_strc_floats_saved_userdata\empty % todo: reset this in non start|stop cases
+
+%D We abuse the settings to pick up some float parameters too which makes it
+%D messy.
+
+\permanent\tolerant\protected\def\startplacefloat[#tag]#spacer[#settings]#spacer[#userdata]%
+ {\flushnotes
+ \page_otr_command_flush_side_floats % here !
+ \strc_floats_begin_group
+ \strc_floats_set_current_tag{#tag}%
+ \strc_floats_reset_variables
+ % save
+ \edef\m_location {\floatcaptionparameter\c!location}%
+ \edef\m_topoffset {\floatcaptionparameter\c!topoffset}%
+ \edef\m_bottomoffset{\floatcaptionparameter\c!bottomoffset}%
+ \edef\m_freeregion {\floatcaptionparameter\c!freeregion}%
+ % preset
+ \resetfloatcaptionparameter \c!location
+ \setexpandedfloatcaptionparameter\c!topoffset {\floatparameter\c!topoffset}%
+ \setexpandedfloatcaptionparameter\c!bottomoffset{\floatparameter\c!bottomoffset}%
+ \setexpandedfloatcaptionparameter\c!freeregion {\floatparameter\c!freeregion}%
+ \resetfloatcaptionparameter \c!reference
+ \resetfloatcaptionparameter \c!title
+ \resetfloatcaptionparameter \c!marking
+ \resetfloatcaptionparameter \c!list
+ \resetfloatcaptionparameter \c!bookmark
+ \resetfloatcaptionparameter \c!referencetext
+ % pickup
+ \ifparameter#settings\or
+ \setupcurrentfloatcaption[#settings]%
+ \fi
+ \ifparameter#userdata\or
+ \setupcurrentfloatuserdata[#userdata]%
+ \def\m_strc_floats_saved_userdata{#userdata}%
+ \else
+ \let\m_strc_floats_saved_userdata\empty
+ \fi
+ % check
+ \edef\floatlocation{\floatcaptionparameter\c!location}%
+ \ifempty\floatlocation
+ \edef\floatlocation{\floatparameter\c!default}%
+ \fi
+ % inherit
+ \setexpandedfloatparameter\c!topoffset {\floatcaptionparameter\c!topoffset}%
+ \setexpandedfloatparameter\c!bottomoffset{\floatcaptionparameter\c!bottomoffset}%
+ \setexpandedfloatparameter\c!freeregion {\floatcaptionparameter\c!freeregion}%
+ % restore
+ \letfloatcaptionparameter\c!location \m_location
+ \letfloatcaptionparameter\c!topoffset \m_topoffset
+ \letfloatcaptionparameter\c!bottomoffset\m_bottomoffset
+ \letfloatcaptionparameter\c!freeregion \m_freeregion
+ %
+ \strc_floats_analyze_location
+ \doifelseinset\v!split\floatlocation\strc_floats_place_next_box_split\strc_floats_place_next_box_normal
+ \bgroup
+ \ignorespaces}
+
+\permanent\protected\def\stopplacefloat
+ {\removeunwantedspaces
+ \egroup}
+
+% reserve
+
+\protected\def\strc_floats_reserve
+ {\flushnotes
+ \page_otr_command_flush_side_floats % here !
+ \strc_floats_begin_group
+ \strc_floats_place}
+
+\def\strc_floats_reserve_box#settings%
+ {\begingroup
+ \setupcurrentfloat[\c!frame=\v!on,#settings]%
+ \inheritedfloatframed{}%
+ \endgroup}
+
+% text
+
+\protected\def\strc_floats_start_text
+ {\flushnotes % Here indeed?
+ \page_otr_command_flush_side_floats % Here indeed?
+ \strc_floats_begin_text_group
+ \strc_floats_start_text_indeed}
+
+\tolerant\protected\def\strc_floats_start_text_indeed[#tag]#spacer[#location]#spacer[#reference]%
+ {\strc_floats_place[#tag][\v!text,#location,\v!left][#reference]}
+
+\protected\def\strc_floats_stop_text
+ {\strc_floats_stop_text_indeed}
+
+% reserved text
+
+\tolerant\protected\def\strc_floats_start_reserve_text[#tag]#spacer[#settings]#spacer[#location]#spacer[#reference]#:#caption%%
+ {\flushnotes
+ \page_otr_command_flush_side_floats
+ \strc_floats_begin_text_group
+ \strc_floats_place[#tag][\v!text,#location,\v!left][#reference]{#caption}{\strc_floats_reserve_box{#settings}}}
+
+\protected\def\strc_floats_stop_reserve_text
+ {\strc_floats_stop_text_indeed}
+
+% special hack
+
+\def\strc_floats_begin_group {\begingroup}
+\def\strc_floats_end_group {\carryoverpar\endgroup} % hm
+\def\strc_floats_end_split_group {\endgroup}
+\def\strc_floats_begin_text_group{\begingroup\let\strc_floats_end_group\relax}
+\def\strc_floats_end_text_group {\endgroup}
+
+% implementation
+
+%setnewconstant\c_page_one_float_method \zerocount % 0=raw 1=safe (.99) 2=tight (-1pt) / belongs in page-one
+\setnewconstant\c_strc_floats_rotation \zerocount % 0 90 180 270
+\newconditional\c_strc_floats_par_float
+
+\ifdefined\page_margin_strc_floats_before \else \let\page_margin_strc_floats_before \relax \fi
+\ifdefined\page_margin_strc_floats_set_hsize \else \let\page_margin_strc_floats_set_hsize\relax \fi
+
+\immutable\def\flushfloatslist
+ {\v!left,\v!right,\v!inner,\v!outer,%
+ \v!backspace,\v!cutspace,%
+ \v!inleft,\v!inright,\v!inmargin,%
+ \v!leftmargin,\v!rightmargin,\v!leftedge,\v!rightedge,%
+ \v!innermargin,\v!outermargin,\v!inneredge,\v!outeredge,%
+ \v!text,\v!opposite}% \v!page
+
+\protected\def\strc_floats_place_next_box_split
+ {\let\splitfloatfinalizer\strc_floats_end_split_group
+ \let\strc_floats_end_group\relax
+ \splitfloat{\strc_floats_place_next_box_normal}}
+
+\protected\def\strc_floats_place_next_box_normal
+ {\ifconditional\c_page_floats_some_waiting
+ % this was \checkwaitingfloats spread all over
+ \doifelseinset\v!always\floatlocation
+ {\showmessage\m!floatblocks5\empty}
+ {\doifelsecommon\floatlocation\flushfloatslist\page_otr_command_flush_floats\donothing}%
+ % but which should be done before using box \floatbox
+ \fi
+ \page_margin_strc_floats_before % todo: each float handler gets a before
+ \global\insidefloattrue
+ \dostarttaggedchained\t!float\currentfloat\??float
+ \page_margin_strc_floats_set_hsize % todo: each float handler gets a set_hsize
+ \the\everyinsidefloat
+ \strc_floats_analyze_variables_one
+ \dostarttagged\t!floatcontent\empty
+ \dowithnextboxcontent
+ {\strc_floats_set_local_hsize
+ \floatparameter\c!inner
+ \postponenotes} % new
+ {\page_postprocessors_linenumbers_box\nextbox % for aditya
+ \dostoptagged
+ \strc_floats_finish_placement}
+ \vbox}
+
+%D \starttyping
+%D \definefloat
+%D [one] [figure]
+%D [default=right,
+%D rightmargindistance=-20cm,
+%D criterium=129pt,
+%D fallback=rightmargin]
+%D
+%D \definefloat
+%D [two] [figure]
+%D [default=right,
+%D rightmargindistance=-20cm,
+%D criterium=129pt,
+%D fallback=three]
+%D
+%D \definefloat
+%D [three] [figure]
+%D [default=rightmargin,
+%D rightmargindistance=0cm]
+%D
+%D \placefloat[one]{}{\blackrule[width=30pt]} \samplefile{tufte}
+%D \placefloat[one]{}{\blackrule[width=60pt]} \samplefile{tufte}
+%D \placefloat[one]{}{\blackrule[width=90pt]} \samplefile{tufte}
+%D \placefloat[one]{}{\blackrule[width=130pt]} \samplefile{tufte}
+%D \placefloat[two]{}{\blackrule[width=130pt]} \samplefile{tufte}
+%D \stoptyping
+
+\def\strc_floats_finish_placement
+ {\doifsomething{\floatparameter\c!criterium}
+ {\ifdim\wd\nextbox>\floatparameter\c!criterium\relax
+ \edef\forcedfloatmethod{\floatparameter\c!fallback}%
+ \ifempty\forcedfloatmethod \else
+ \doifelsecommandhandler\??float\forcedfloatmethod
+ {\let\currentfloat\forcedfloatmethod
+ \edef\floatlocation{\floatparameter\c!default}%
+ \let\forcedfloatmethod\floatlocation}
+ \donothing
+ \fi
+ \ifempty\forcedfloatmethod
+ \let\forcedfloatmethod\v!here
+ \fi
+ \fi}%
+ \strc_floats_check_extra_actions
+ \strc_floats_analyze_variables_two
+ \strc_floats_place_packaged_boxes
+ \dotagregisterfloat\askedfloatoptions\askedfloatmethod
+ \dostoptagged % tricky .... needs checking
+ % we need to carry over the par because of side floats
+ \global\d_page_sides_downshift \zeropoint
+ \global\d_page_sides_extrashift\zeropoint
+ \ifconditional\c_strc_floats_par_float
+ \doifinset\v!reset\floatlocation\page_sides_forget_floats
+ \doinhibitblank
+ \fi
+ \strc_floats_end_group}
+
+% nicer is a bunch of states and one loop that sets those states
+
+\newdimen\d_strc_floats_top
+\newdimen\d_strc_floats_bottom
+\newdimen\d_strc_floats_overflow
+
+% \def\strc_floats_calculate_skip#target#skip%
+% {\begingroup
+% \edef\askedfloatskip{\rootfloatparameter#skip}%
+% \ifempty\askedfloatskip
+% \global#target\zeropoint
+% \orelse\ifx\askedfloatskip\v!none
+% \global#target\zeropoint
+% \else
+% \setbox\scratchbox\vbox{\whitespace\blank[\askedfloatskip]}% todo: move whitespace inside blank
+% \global#target\ht\scratchbox
+% \fi
+% \endgroup}
+
+\def\strc_floats_calculate_skip#target#skip%
+ {\begingroup
+ \edef\p_blank{\rootfloatparameter#skip}%
+ \ifx\p_blank\v!nowhite
+ \edef\p_blank{-\v!white}%
+ \fi
+ \prerollblank[\p_blank]%
+ \global#target\prerolledblank
+ \endgroup}
+
+\def\strc_floats_analyze_variables_two
+ {\ifinsidecolumns
+ \global\setfalse\c_strc_floats_par_float
+ \else
+ \doifelsecommon\floatlocation\flushfloatslist
+ {\global\settrue \c_strc_floats_par_float}%
+ {\global\setfalse\c_strc_floats_par_float}%
+ \fi
+ % variable initializations
+ \global\d_page_sides_shift \zeropoint
+ \global\d_page_sides_maximum \zeropoint
+ \global\c_page_sides_align \zerocount
+ \global\c_page_sides_tolerance \zerocount
+ \global\c_page_sides_skipmode \zerocount
+ \global\c_strc_floats_rotation \zerocount
+ \global\d_page_sides_margin \floatparameter\c!margin
+ \global\d_page_sides_leftshift \floatparameter\c!leftmargindistance
+ \global\d_page_sides_rightshift \floatparameter\c!rightmargindistance
+ \global\d_page_sides_topoffset \floatparameter\c!topoffset
+ \global\d_page_sides_bottomoffset\floatparameter\c!bottomoffset
+ \global\c_page_sides_method \floatparameter\c!sidemethod
+ \global\c_page_one_float_method \floatparameter\c!textmethod
+ \global\c_page_floats_n_of_top \rootfloatparameter\c!ntop
+ \global\c_page_floats_n_of_bottom\rootfloatparameter\c!nbottom
+ \global\d_strc_floats_overflow \zeropoint
+ \ifconditional\c_strc_floats_par_float
+ \global\d_strc_floats_top \zeropoint
+ \global\d_strc_floats_bottom \zeropoint
+ \strc_floats_calculate_skip\d_page_sides_topskip \c!sidespacebefore
+ \strc_floats_calculate_skip\d_page_sides_bottomskip\c!sidespaceafter
+ \strc_floats_calculate_skip\d_page_sides_midskip \c!sidespaceinbetween
+ \strc_floats_calculate_skip\d_strc_floats_top \c!spacebeforeside
+ \strc_floats_calculate_skip\d_strc_floats_bottom \c!spaceafterside
+ \else
+ \global\d_page_sides_topskip \zeropoint
+ \global\d_page_sides_bottomskip \zeropoint
+ \strc_floats_calculate_skip\d_strc_floats_top \c!spacebefore
+ \strc_floats_calculate_skip\d_strc_floats_bottom\c!spaceafter
+ \fi
+ % keyword handling
+ \ifconditional\c_strc_floats_par_float
+ \processaction
+ [\floatparameter\c!sidealign]
+ [\v!height=>\global\c_page_sides_align\plusone ,%
+ \v!line=>\global\c_page_sides_align\plustwo ,% (***)
+ \v!depth=>\global\c_page_sides_align\plusthree,%
+ \v!grid=>\global\c_page_sides_align\plusfour ,%
+ \v!halfline=>\global\c_page_sides_align\plusfive ]%
+ \ifcase\c_page_sides_align\relax % todo: optie v!lokaal => \else
+ \doifinset\v!height \floatlocation{\global\c_page_sides_align\plusone }%
+ \doifinset\v!line \floatlocation{\global\c_page_sides_align\plustwo }%
+ \doifinset\v!depth \floatlocation{\global\c_page_sides_align\plusthree}%
+ \doifinset\v!grid \floatlocation{\global\c_page_sides_align\plusfour }%
+ \doifinset\v!halfline\floatlocation{\global\c_page_sides_align\plusfive }% meant for 'none'
+ \fi
+ \doifinset\v!high \floatlocation{\global\c_page_sides_skipmode \plusone }%
+ \doifinset\v!low \floatlocation{\global\c_page_sides_skipmode \plustwo }%
+ \doifinset\v!fit \floatlocation{\global\c_page_sides_skipmode \plusthree}%
+ \doifinset\v!tolerant \floatlocation{\global\c_page_sides_tolerance\plusone }%
+ \doifinset\v!verytolerant\floatlocation{\global\c_page_sides_tolerance\plustwo }%
+ \else
+ \processallactionsinset
+ [\floatlocation]%
+ [ 90=>\global\c_strc_floats_rotation\commalistelement\relax,%
+ 180=>\global\c_strc_floats_rotation\commalistelement\relax,%
+ 270=>\global\c_strc_floats_rotation\commalistelement\relax]%
+ \fi
+ \doifelseinset\v!nonumber\floatlocation
+ {\global\nofloatnumbertrue}%
+ {\doifelse{\floatcaptionparameter\c!number}\v!yes
+ {\global\nofloatnumberfalse}%
+ {\global\nofloatnumbertrue}}%
+ \doifelseinset\v!none\floatlocation
+ {\global\nofloatcaptiontrue}%
+ {\global\nofloatcaptionfalse}%
+ \doif{\floatcaptionparameter\c!number}\v!none % new
+ {\global\nofloatcaptiontrue}%
+ \doifinset\v!effective\floatlocation
+ {\letfloatparameter \c!leftmargin \effectiveleftskip
+ \letfloatparameter \c!rightmargin\effectiverightskip
+ \letfloatcaptionparameter\c!leftmargin \effectiveleftskip
+ \letfloatcaptionparameter\c!rightmargin\effectiverightskip}%
+ \ifemptyfloatcaption \ifnofloatnumber
+ \global\nofloatcaptiontrue
+ \fi \fi}
+
+% documenteren in details
+
+\def\strc_floats_analyze_variables_one
+ {\doifelse{\floatparameter\c!local}\v!yes\settrue\setfalse\c_page_floats_center_box_global % fout keyword
+ \ifconditional\c_page_floats_center_box_global
+ \settrue\c_page_floats_center_box_local
+ \else
+ \doifelseinset\v!local\floatlocation\settrue\setfalse\c_page_floats_center_box_local
+ \fi
+ \doifelse{\floatparameter\c!freeregion}\v!yes
+ \settrue\setfalse\c_strc_floats_mark_as_free
+ \doifnotcommon{\v!always,\v!here,\v!force}\floatlocation % ! ! ! ! ! !
+ {\setfalse\c_page_floats_center_box_global
+ \setfalse\c_page_floats_center_box_local}}
+
+\permanent\def\naturalfloatheight{\the\naturalfloatwd}
+\permanent\def\naturalfloatwidth {\the\naturalfloatht}
+\permanent\def\naturalfloatdepth {\the\naturalfloatdp}
+
+\permanent\def\floatcaptionheight{\the\floatcaptionwd}
+\permanent\def\floatcaptionwidth {\the\floatcaptionht}
+\permanent\def\floatcaptiondepth {\the\floatcaptiondp}
+
+\newdimen\naturalfloatwd
+\newdimen\naturalfloatht
+\newdimen\naturalfloatdp
+
+\newdimen\floatcaptionwd
+\newdimen\floatcaptionht
+\newdimen\floatcaptiondp
+
+\def\strc_floats_set_natural_dimensions#box%
+ {\global\naturalfloatwd\wd#box\relax
+ \global\naturalfloatht\ht#box\relax
+ \global\naturalfloatdp\dp#box\relax}
+
+\def\strc_floats_set_caption_dimensions#box%
+ {\global\floatcaptionwd\wd#box\relax
+ \global\floatcaptionht\ht#box\relax
+ \global\floatcaptiondp\dp#box\relax}
+
+\permanent\def\doifelsemainfloatbody
+ {\unless\ifinsidesplitfloat
+ \expandafter\firstoftwoarguments
+ \orelse\ifconditional\splitfloatfirstdone
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doifmainfloatbodyelse\doifelsemainfloatbody
+
+% todo: optional user pars
+
+\def\floatcaptionattribute
+ {\iflocation
+ \ifempty\currentfloatattribute
+ % safeguard, can be samepage too
+ \orelse\ifnofloatcaption
+ \orelse\ifinsidesplitfloat
+ \ifconditional\splitfloatfirstdone
+ \else
+ attr \destinationattribute \currentfloatattribute
+ \fi
+ \else
+ attr \destinationattribute \currentfloatattribute
+ \fi
+ \fi}
+
+% \def\floatcaptionattribute
+% {\iflocation
+% \ifempty\currentfloatattribute
+% % safeguard, can be samepage too
+% \orelse\ifnofloatcaption
+% % nothing
+% \orunless\ifinsidesplitfloat
+% attr \destinationattribute \currentfloatattribute
+% \orunless\ifconditional\splitfloatfirstdone
+% attr \destinationattribute \currentfloatattribute
+% \fi
+% \fi}
+
+\newconditional\usesamefloatnumber
+
+% \startplacefigure[location=here,reference=first, title=first, group=alpha,groupsuffix=.a]
+% \externalfigure[dummy][height=2cm]
+% \stopplacefigure
+% \startplacefigure[location=here,reference=second,title=second,group=alpha,groupsuffix=.b]
+% \externalfigure[dummy][height=2cm]
+% \stopplacefigure
+%
+% uses:
+
+\def\strc_floats_group_index
+ {\numexpr\clf_listgroupindex{\currentfloat}{\currentfloatgroup}\relax}
+
+\def\strc_floats_place_packaged_boxes
+ {\expandafter\strc_floats_place_packaged_boxes_indeed\expandafter{\m_strc_floats_saved_userdata}}
+
+\def\strc_floats_place_packaged_boxes_indeed#userdata%
+ {\bgroup
+ \ifconditional\usesamefloatnumber
+ \glet\currentfloatnumber \m_strc_floats_previous_number
+ \glet\currentfloatattribute \empty
+ \glet\currentfloatsynchronize\relax
+ \else
+ \edef\currentfloatcounter{\namedcounterparameter\currentfloat\s!name}%
+ \edef\currentfloatgroup {\floatcaptionparameter\c!group}%
+ \ifnofloatnumber
+ \orelse\ifnofloatcaption
+ \orelse\ifempty\currentfloatgroup
+ % independent
+ \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi
+ \strc_counters_increment\currentfloatcounter
+ \orelse\ifcase\strc_floats_group_index
+ % first in group
+ \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi
+ \strc_counters_increment\currentfloatcounter
+ \else
+ % next in group
+ \fi
+ % *1* as an alternative we could set counter parameters here if needed
+ \strc_counters_register_component
+ \s!float
+ \setupcurrentfloatcaption
+ \floatcaptionparameter
+ \detokenizedfloatcaptionparameter
+ \relax
+ \relax
+ \relax
+ [\s!name=\currentfloat,% was c!name
+ \s!counter=\currentfloatcounter,%
+ \s!hascaption=\ifnofloatcaption \v!no\else\v!yes\fi,%
+ \s!hasnumber=\ifnofloatnumber \v!no\else\v!yes\fi,%
+ \s!hastitle=\ifemptyfloatcaption\v!no\else\v!yes\fi]%
+ [#userdata]%
+ \glet\m_strc_floats_previous_number\m_strc_counters_last_registered_index
+ \glet\currentfloatnumber \m_strc_counters_last_registered_index
+ \glet\currentfloatattribute \m_strc_counters_last_registered_attribute
+ \glet\currentfloatsynchronize \m_strc_counters_last_registered_synchronize
+ \fi
+ %
+ \iftrialtypesetting\else\global\setfalse\usesamefloatnumber\fi % one shot
+ % check float box
+ \strc_floats_set_natural_dimensions\nextbox
+ \global\setbox\floatbox\vbox{\floatparameter\c!command{\box\nextbox}}% can be anything so no pack
+ \strc_floats_set_natural_dimensions\floatbox
+ \ifzeropt\htdp\floatbox
+ \showmessage\m!floatblocks{11}\empty
+ \global\setbox\floatbox\vpack
+ {\dostarttagged\t!floatcontent\empty
+ \strc_floats_place_empty_box
+ \dostoptagged}%
+ \fi
+ % deal with lack of caption
+ \global\setbox\floatbox\vpack \floatcaptionattribute
+ {\doifelsemainfloatbody\currentfloatsynchronize\donothing
+ \unvbox\floatbox
+ \ifnofloatcaption
+ \vss
+ \fi}% gets rid of the depth (unless tabulate)
+ \iftrialtypesetting\strc_counters_restore\currentfloatcounter\fi
+ \egroup
+ % place the float
+ \strc_floats_set_box
+ \strc_floats_get_box
+ \global\insidefloatfalse}
+
+\newdimen\availablefloatwidth
+\newdimen\availablefloatheight
+
+\def\strc_floats_set_local_hsize
+ {\ifconditional\c_page_floats_center_box_local
+ % also available check here?
+ \seteffectivehsize
+ \hsize\localhsize
+ \else
+ \doifinset\v!margin\floatlocation % brr, really needed! see wm will be redone
+ {\hsize\namedmarginblockparameter\empty\c!width}%
+ \fi
+ \edef\p_availablewidth {\floatparameter\c!availablewidth }%
+ \edef\p_availableheight{\floatparameter\c!availableheight}%
+ \availablefloatwidth \ifempty\p_availablewidth \hsize\else\p_availablewidth \relax\fi
+ \availablefloatheight\ifempty\p_availableheight\vsize\else\p_availableheight\relax\fi}
+
+\ifdefined\everyinsidefloat \else \newevery \everyinsidefloat \relax \fi
+
+\appendtoks
+ \everyinsidefloat\emptytoks % in case it's called earlier
+ \strc_float_load_data
+\to \everyinsidefloat
+
+\permanent\def\doifelserightpagefloat
+ {\unless\ifdoublesided
+ \expandafter\firstoftwoarguments
+ \orelse\ifsinglesided
+ \expandafter\firstoftwoarguments
+ \orelse\ifodd\purenumber\strc_float_realpage\space
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\def\doifelseoddpagefloat
+ {\ifodd\purenumber\strc_float_realpage\space
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifrightpagefloatelse\doifelserightpagefloat
+\aliased\let\doifoddpagefloatelse \doifelseoddpagefloat
+
+\appendtoks
+ \enforced\let\rightorleftpageaction\doifelserightpagefloat
+\to \everyinsidefloat
+
+% \let\movesidefloat\gobbleoneargument
+
+% new : \place...[leftmargin,-2*line]; we need to catch fxtb:2*3
+% watch out: line alone aligns on the line ! ! !
+
+\permanent\protected\def\movesidefloat[#settings]% (-)n*line|x=,y=
+ {\global\d_page_sides_downshift \zeropoint
+ \global\d_page_sides_extrashift\zeropoint
+ \ifhastok={#settings}%
+ \begingroup
+ \setupcurrentfloat[\c!x=\zeropoint,\c!y=\zeropoint,#settings]%
+ \ifgridsnapping
+ \getnoflines{\floatparameter\c!y}%
+ \global\d_page_sides_downshift\noflines\lineheight
+ \else
+ \global\d_page_sides_downshift\floatparameter\c!y
+ \fi
+ \global\d_page_sides_extrashift\floatparameter\c!x
+ \endgroup
+ \else
+ \movedownsidefloat[#settings]%
+ \fi}
+
+\installcorenamespace{floatmovement}
+
+\setvalue{\??floatmovement \v!line}{\strc_floats_move_down_line+}
+\setvalue{\??floatmovement+\v!line}{\strc_floats_move_down_line+}
+\setvalue{\??floatmovement-\v!line}{\strc_floats_move_down_line-}
+\setvalue{\??floatmovement \v!hang}{\strc_floats_move_down_hang\plusone}
+\setvalue{\??floatmovement+\v!hang}{\strc_floats_move_down_hang\plusone}
+\setvalue{\??floatmovement-\v!hang}{\strc_floats_move_down_hang\minusone}
+
+\setvalue{\??floatmovement-2*\v!line}{\strc_floats_move_down_line{-2}}
+\setvalue{\??floatmovement+2*\v!line}{\strc_floats_move_down_line{2}}
+\setvalue{\??floatmovement 2*\v!line}{\strc_floats_move_down_line{2}}
+
+\permanent\protected\def\installfloatmovement#1#2{\setvalue{\??floatmovement#1}{#2}}
+
+\def\strc_floats_move_down#setting%
+ {\begincsname\??floatmovement#setting\endcsname}
+
+\def\strc_floats_move_down_line#sign%
+ {\ifscratchconditionone \else
+ \global\d_page_sides_downshift\zeropoint
+ \scratchconditiononetrue
+ \fi
+ \global\advance\d_page_sides_downshift#sign\lineheight}
+
+\def\strc_floats_move_down_hang#lines%
+ {\ifscratchconditiontwo \else
+ \global\c_page_sides_n_of_lines\zerocount
+ \scratchconditiontwotrue
+ \fi
+ \global\advance\c_page_sides_n_of_lines#lines\relax}
+
+\permanent\protected\def\movedownsidefloat[#settings]% already in core
+ {\unless\ifhastok:{#settings}%
+ \begingroup
+ \scratchconditiononefalse
+ \scratchconditiontwofalse
+ \normalexpanded{\dorepeatwithcommand[#settings]}\strc_floats_move_down
+ \endgroup
+ \fi}
+
+\permanent\protected\def\hangsidefloat[#number]%
+ {\global\c_page_sides_n_of_lines#number\relax}
+
+\mutable\let\extrafloatlocation\empty
+
+\def\strc_floats_set_extra_action#rightpagelocation#leftpagelocation%
+ {\rightorleftpageaction
+ {\let\extrafloatlocation#rightpagelocation}%
+ {\let\extrafloatlocation#leftpagelocation}}
+
+
+\installcorenamespace{extrafloataction}
+
+\setvalue{\??extrafloataction \v!inner}#1{\strc_floats_set_extra_action\v!left \v!right}
+\setvalue{\??extrafloataction \v!outer}#1{\strc_floats_set_extra_action\v!right \v!left}
+\setvalue{\??extrafloataction\v!innermargin}#1{\strc_floats_set_extra_action\v!leftmargin \v!rightmargin}
+\setvalue{\??extrafloataction\v!outermargin}#1{\strc_floats_set_extra_action\v!rightmargin\v!leftmargin}
+\setvalue{\??extrafloataction \v!inneredge}#1{\strc_floats_set_extra_action\v!leftedge \v!rightedge}
+\setvalue{\??extrafloataction \v!outeredge}#1{\strc_floats_set_extra_action\v!rightedge \v!leftedge}
+\setvalue{\??extrafloataction \v!backspace}#1{\strc_floats_set_extra_action\v!backspace \v!cutspace}
+\setvalue{\??extrafloataction \v!cutspace}#1{\strc_floats_set_extra_action\v!cutspace \v!backspace}
+%setvalue{\??extrafloataction \v!margin}#1{\strc_floats_set_extra_action\v!cutspace \v!backspace}
+\setvalue{\??extrafloataction \v!left}#1{\strc_floats_set_extra_action\v!left \v!left}
+\setvalue{\??extrafloataction \v!right}#1{\strc_floats_set_extra_action\v!right \v!right}
+\setvalue{\??extrafloataction \v!line}#1{} % only -n*line is handled (see ***)
+\setvalue{\??extrafloataction \s!unknown}#1{\movedownsidefloat[#1]}
+
+\def\strc_floats_check_extra_actions % less tracingthis way ....
+ {\doifnotinset\v!text\floatlocation % fuzzy, text overloads left, since then it's a directive
+ {\let\extrafloatlocation\empty
+ % \d_page_sides_downshift will be reset afterwards, and can
+ % already be set at this point
+ \processcommacommand[\floatlocation]\strc_floats_check_extra_actions_step
+ \ifempty\extrafloatlocation \else
+ \edef\floatlocation{\extrafloatlocation,\floatlocation}%
+ \setfloatmethodvariables\floatlocation
+ \fi}}
+
+\def\strc_floats_check_extra_actions_step#step%
+ {\csname\??extrafloataction
+ \ifcsname\??extrafloataction#step\endcsname#step\else\s!unknown\fi
+ \endcsname{#step}}
+
+% pas op, maxbreedte niet instellen als plaats=links/rechts
+
+\def\strc_floats_set_local_dimensions
+ {\global\d_page_sides_shift \zeropoint % duplicate
+ \global\d_page_sides_maximum\zeropoint\relax % duplicate
+ \ifzeropt\d_page_sides_downshift\else
+ \global\setbox\floatbox\vpack
+ {\vskip\d_page_sides_downshift
+ \nointerlineskip
+ \box\floatbox}%
+ \fi
+ \edef\p_minwidth{\floatparameter\c!minwidth}%
+ \ifempty\p_minwidth
+ % nothing
+ \else
+ \scratchwidth\p_minwidth\relax
+ \ifdim\wd\floatbox<\scratchwidth
+ \strc_floats_realign_floatbox_horizontal_two
+ \fi
+ \fi
+ % we can also support edges .. in that case no common but a fast loop
+ \doifelseinset\v!hanging\floatlocation
+ {\doifelsecommon{\v!inleft,\v!leftmargin}\floatlocation
+ {\let\p_maxwidth\leftmarginwidth}%
+ {\doifelsecommon{\v!inright,\v!rightmargin}\floatlocation
+ {\let\p_maxwidth\rightmarginwidth}%
+ {\edef\p_maxwidth{\floatparameter\c!maxwidth}}}}%
+ {\edef\p_maxwidth{\floatparameter\c!maxwidth}}%
+ \ifempty\p_maxwidth
+ % nothing
+ \else
+ \scratchwidth\p_maxwidth\relax
+ \ifdim\wd\floatbox>\scratchwidth
+ \doifelsecommon{\v!inright,\v!rightmargin,\v!rightedge,\v!inleft,\v!leftmargin,\v!leftedge}\floatlocation
+ {\global\d_page_sides_maximum\scratchwidth}
+ {\doifelsecommon{\v!right,\v!left}\floatlocation
+ \strc_floats_realign_floatbox_horizontal_one
+ \strc_floats_realign_floatbox_horizontal_two}%
+ \fi
+ \fi}
+
+\def\strc_floats_realign_floatbox_horizontal_one
+ {\global\setbox\floatbox\hpack to \scratchwidth
+ {\doifnotinset\v!right\floatlocation\hss
+ \box\floatbox
+ \doifnotinset\v!left\floatlocation\hss}}
+
+\def\strc_floats_realign_floatbox_horizontal_two % why is this
+ {\global\setbox\floatbox\hpack to \scratchwidth
+ {\doifnot{\floatparameter\c!location}\v!left\hss
+ \box\floatbox
+ \doifnot{\floatparameter\c!location}\v!right\hss}}
+
+\permanent\protected\def\placefloats
+ {\page_otr_command_flush_floats}
+
+\permanent\protected\def\betweenfloatblanko % assumes that spaceafter is present
+ {\blank[\rootfloatparameter\c!spacebefore]} % or v!back,....
+
+% keep as old 1
+%
+% \protected\def\doplacefloatbox % used elsewhere
+% {%\forgetall % NO
+% \whitespace
+% \blank[\rootfloatparameter\c!spacebefore]
+% \page_otr_command_flush_float_box
+% \blank[\rootfloatparameter\c!spaceafter]}
+%
+% keep as old 2
+%
+% \protected\def\doplacefloatbox % used elsewhere
+% {%\forgetall % NO
+% \whitespace
+% \blank[\rootfloatparameter\c!spacebefore]
+% \nointerlineskip
+% \flushnotes % new per 2014-05-29 : todo: move them up in the mvl
+% \nointerlineskip
+% \page_otr_command_flush_float_box
+% \nointerlineskip
+% \blank[\rootfloatparameter\c!spaceafter]}
+
+\def\strc_floats_apply_skip#1%
+ {\edef\m_space{\rootfloatparameter#1}%
+ \ifempty\m_space\orelse\ifx\m_space\v!none\else
+ \directvspacing\m_space
+ \fi}
+
+\permanent\protected\def\doplacefloatbox % used elsewhere
+ {%\forgetall % NO
+ \whitespace
+ \strc_floats_apply_skip\c!spacebefore
+ \nointerlineskip
+ \flushnotes % new per 2014-05-29 : todo: move them up in the mvl
+ \nointerlineskip
+ \page_otr_command_flush_float_box
+ % \nointerlineskip % interferes with depth of caption
+ \strc_floats_apply_skip\c!spaceafter}
+
+% test case:
+%
+% \placefigure[page,none]{}{\blackrule[width=\textwidth,height=0.9\textheight,color=green]}
+% \placefigure[page,none]{}{\blackrule[width=\textwidth,height=1.0\textheight,color=green]}
+% \placefigure[page,none]{}{\blackrule[width=\textwidth,height=1.1\textheight,color=green]}
+
+% the [#1] will go away
+
+\def\page_one_place_float_text % this macro should be defined elsewhere
+ {%\checkwaitingfloats\floatlocationmethod
+ % todo: check if #1 is indeed \floatlocation or maybe more
+ \global\floatwidth \wd\floatbox
+ \global\floatheight \ht\floatbox % forget about the depth
+ \global\floattextwidth\dimexpr\hsize-\floatwidth-\rootfloatparameter\c!margin\relax
+ \edef\floatlocation{\floatlocationmethod}% to be sure .. why
+ \doifelseinset\v!tall\floatlocationmethod
+ {\floattextheight\dimexpr\pagegoal-\pagetotal-\bigskipamount\relax % ugly, this bigskip
+ \ifdim\floattextheight>\textheight
+ \floattextheight\textheight
+ \fi
+ \boxmaxdepth\zeropoint\relax % toegevoegd
+ \ifdim\floattextheight<\floatheight
+ \floattextheight\floatheight
+ \fi
+ \setbox\floattext\vbox to \floattextheight}
+ {\setbox\floattext\vbox}%
+ \bgroup
+ \forgetall
+ \setupblank
+ \setupwhitespace % new, also needed for footnotes
+ \blank[\v!disable]
+ \hsize\floattextwidth
+ \ignorespaces}
+
+\def\strc_floats_stop_text_indeed % todo
+ {\egroup
+ \doifnotinset\v!tall\floatlocation
+ {\floattextheight\ifdim\ht\floattext<\floatheight\floatheight\else\ht\floattext\fi}%
+ \setbox\floatbox\vpack to \floattextheight
+ {\hsize\floatwidth
+ \doifelseinset\v!both\floatlocation
+ {\doifelseinset\v!low\floatlocation
+ {\vfill\box\floatbox}
+ {\doifelseinset\v!middle\floatlocation
+ {\vfill\box\floatbox\vfill}
+ {\box\floatbox\vfill}}}
+ {\box\floatbox\vfill}}%
+ \setbox\floattext\vpack to \floattextheight
+ {\hsize\floattextwidth
+ \doifelseinset\v!low\floatlocation
+ {\vfill
+ \box\floattext
+ \doifinset\c!offset\floatlocation{\whitespace\blank}}
+ {\doifelseinset\v!middle\floatlocation
+ {\vfill
+ \box\floattext
+ \vfill}
+ {\doifinset\v!offset\floatlocation{\whitespace\blank}%
+ \box\floattext
+ \vfill}}}%
+ \doifelseinset\v!right\floatlocation
+ {\setbox\floatbox\hpack to \hsize
+ {\box\floattext
+ \hfill
+ \box\floatbox}}
+ {\setbox\floatbox\hpack to \hsize
+ {\box\floatbox
+ \hfill
+ \box\floattext}}%
+ \baselinecorrection
+ \whitespace
+ \blank[\rootfloatparameter\c!spacebefore]%
+ \doifnotinset\v!tall\floatlocation
+ {\dp\floatbox\openstrutdepth}% dp\strutbox}% % toegevoegd
+ \box\floatbox
+ \dostoptagged
+ \blank[\rootfloatparameter\c!spaceafter]%
+ \strc_floats_end_text_group
+ \page_floats_report_total}
+
+\permanent\def\borderedfloatbox
+ {\begingroup
+ \setupcurrentfloat[\c!location=\v!normal,\c!width=\v!fit,\c!height=\v!fit]%
+ \inheritedfloatframed{\box\floatbox}%
+ \endgroup}
+
+% minwidth=fit,width=max : no overshoot, as wide as graphic
+
+% keep these as reference:
+%
+% \def\strc_floats_align_content_indeed
+% {\alignstrutmode\zerocount
+% \doifnotcommon\floatcaptionlocation{\v!outermargin,\v!innermargin,\v!leftmargin,\v!rightmargin}
+% {\shiftalignedline
+% {\floatparameter\c!leftmargin }{\floatparameter\c!rightmargin}%
+% {\floatparameter\c!innermargin}{\floatparameter\c!outermargin}}%
+% \alignedline{\floatparameter\c!location}\v!middle}
+%
+% \def\strc_floats_align_caption_indeed
+% {\alignstrutmode\zerocount
+% \shiftalignedline
+% {\floatcaptionparameter\c!leftmargin }{\floatcaptionparameter\c!rightmargin}%
+% {\floatcaptionparameter\c!innermargin}{\floatcaptionparameter\c!outermargin}%
+% \alignedline{\floatparameter\c!location}\v!middle}
+%
+% Test case:
+%
+% \setupfloats[location=left]
+% \setupfloatcaption[width=max]
+%
+% \startfloatcombination
+% \placefigure{}{}
+% \placefigure{}{}
+% \stopfloatcombination
+
+%D In a floatcombination we ignore the margins .. if that is ever needed we need another
+%D state (instead of local).
+
+\def\strc_floats_align_indeed
+ {\alignedline{\floatparameter\c!location}\v!middle}
+
+\def\strc_floats_shift_indeed#1%
+ {\shiftalignedline{#1\c!leftmargin}{#1\c!rightmargin}{#1\c!innermargin}{#1\c!outermargin}}
+
+\def\strc_floats_align_content_indeed
+ {\alignstrutmode\zerocount
+ \ifx\forcedfloatmethod\v!local \else
+ \doifnotcommon\floatcaptionlocation{\v!outermargin,\v!innermargin,\v!leftmargin,\v!rightmargin}
+ {\strc_floats_shift_indeed\floatparameter}%
+ \expandafter\strc_floats_align_indeed
+ \fi}
+
+\def\strc_floats_align_caption_indeed
+ {\alignstrutmode\zerocount
+ \ifx\forcedfloatmethod\v!local
+ \expandafter\strc_floats_align_indeed_local
+ \else
+ \strc_floats_shift_indeed\floatcaptionparameter
+ \expandafter\strc_floats_align_indeed
+ \fi}
+
+% \def\strc_floats_align_indeed_local#1%
+% {\begingroup
+% \hsize\wd\floatbox
+% \strc_floats_align_indeed{#1}%
+% \endgroup}
+
+\let\strc_floats_align_indeed_local\firstofoneargument
+
+\newdimen\d_strc_floats_content
+\newdimen\d_strc_float_temp_height
+\newdimen\d_strc_float_temp_width
+
+\newconditional\c_floats_adapt_to_caption_width
+\newconditional\c_floats_store_minimal_package
+
+\newdimen\d_strc_floats_caption_hsize
+\newdimen\d_strc_floats_caption_min_width
+\newdimen\d_strc_floats_caption_overshoot
+
+% These are expandable, for diagnostic purposes only, but might eventually go away:
+
+\permanent\def\captionhsize {\the\d_strc_floats_caption_hsize}
+\permanent\def\captionminwidth {\the\d_strc_floats_caption_min_width}
+\permanent\def\captionovershoot{\the\d_strc_floats_caption_overshoot}
+
+\let\strc_floats_align_content\relax
+\let\strc_floats_align_caption\relax
+
+\def\strc_floats_set_caption_criteria % can become parameter (but what name)
+ {\d_strc_floats_caption_min_width15\bodyfontsize
+ \d_strc_floats_caption_overshoot 2\emwidth}
+
+\let\strc_floats_mark_pag_as_free\relax
+
+\def\strc_floats_set_page_variant
+ {\bgroup
+ \strc_floats_set_local_hsize
+ \ifcase\c_strc_floats_rotation\else
+ % Swapping fails in overloadmode and is also wrong (as it then is persistent due
+ % to global being inherited).
+ %\swapdimens\hsize\vsize
+ \scratchdimen\hsize
+ \hsize\vsize
+ \vsize\scratchdimen
+ \fi
+ \forgetall
+ \postponenotes
+ \dontcomplain
+ \setbox\b_strc_floats_content\vpack{\borderedfloatbox}%
+ \let\strc_floats_align_content\strc_floats_align_content_indeed
+ \let\strc_floats_align_caption\strc_floats_align_caption_indeed
+ \strc_floats_check_caption_content
+ \d_strc_floats_content\wd\b_strc_floats_content
+ \ifcase\floatparameter\c!method
+ % nothing
+ \or
+ % automatic
+ \ifnofloatcaption
+ \strc_floats_prepare_no_caption
+ \strc_floats_set_caption_dimensions\voidbox
+ %\page_backgrounds_add_local_to_box\floatbox % was \doglobal but not needed
+ \else
+ % todo: installable maken, variant/method=auto vs macro
+ \strc_floats_prepare_page_caption
+ %\page_backgrounds_add_local_to_box\b_strc_floats_content
+ \setbox\b_strc_floats_caption\hbox % text
+ {\floatcaptionparameter\c!command{\box\b_strc_floats_caption}}%
+ \strc_floats_set_caption_dimensions\b_strc_floats_caption
+ %\moveboxontogrid\b_strc_floats_caption{\floatcaptionparameter\c!grid}\d_strc_floats_caption_height
+ %\page_backgrounds_add_local_to_box\b_strc_floats_caption
+ \strc_floats_build_box
+ \fi
+ \or
+ % semi automatic
+ \or
+ % manual
+ \fi
+ \ifconditional\c_floats_store_minimal_package
+ % nothing
+ \orelse\ifcase\c_strc_floats_rotation
+ \doifnotinset\v!margin\floatlocation % brr, really needed! see wm
+ {\postcenterfloatbox\d_strc_floats_content
+ \strc_floats_mark_pag_as_free}%
+ % mark as free not done here
+ \else
+ \global\setbox\floatbox\vpack
+ {\rotate[\c!rotation=\number\c_strc_floats_rotation]{\box\floatbox}}%
+ \strc_floats_mark_pag_as_free
+ \fi
+ \egroup}
+
+\def\strc_floats_prepare_no_caption
+ {\global\setbox\floatbox\vpack % pas op als wd groter dan hsize
+ {\ifinsidecolumns\ifdim\wd\b_strc_floats_content>\hsize
+ \let\strc_floats_align_content\relax
+ \fi\fi
+ \strc_floats_align_content{\copy\b_strc_floats_content}}}
+
+\def\strc_floats_prepare_page_caption
+ {\edef\p_strc_floats_caption_width {\floatcaptionparameter\c!width}%
+ \edef\p_strc_floats_caption_minwidth{\floatcaptionparameter\c!minwidth}%
+ \edef\p_strc_floats_caption_align {\floatcaptionparameter\c!align}%
+ \dostarttagged\t!floatcaption\empty
+ \doifcommonelse\floatcaptionlocation{\v!top,\v!bottom}
+ {\strc_floats_prepare_page_caption_top_bottom}
+ {\ifx\p_strc_floats_caption_width\v!fit
+ \strc_floats_prepare_side_auto_caption
+ \orelse\ifx\p_strc_floats_caption_width\v!max
+ \strc_floats_prepare_side_auto_caption
+ \else
+ \strc_floats_prepare_side_width_caption
+ \fi}%
+ \dostoptagged}
+
+\def\strc_floats_prepare_page_caption_top_bottom
+ {\ifx\p_strc_floats_caption_width\v!fit
+ \strc_floats_prepare_page_caption_top_bottom_fit_max
+ \orelse\ifx\p_strc_floats_caption_width\v!max
+ \strc_floats_prepare_page_caption_top_bottom_fit_max
+ \else
+ \strc_floats_prepare_side_width_caption % new, special effects (see icare)
+ \fi}
+
+\def\strc_floats_prepare_page_caption_top_bottom_fit_max
+ {\unless\ifx\p_strc_floats_caption_minwidth\v!fit
+ \strc_floats_prepare_stack_caption_fixed
+ \orelse\ifx\p_strc_floats_caption_width\v!max
+ \strc_floats_prepare_stack_caption_max
+ \orelse\ifdim\wd\b_strc_floats_caption>\wd\b_strc_floats_content % wider caption
+ \ifx\p_strc_floats_caption_width\v!fit
+ \strc_floats_prepare_stack_caption_auto
+ \else
+ \strc_floats_prepare_stack_caption_width
+ \fi
+ \else
+ \strc_floats_prepare_stack_caption_min
+ \fi}
+
+\def\strc_floats_caption_set_align
+ {\edef\m_align{\v!reset\ifempty\p_strc_floats_caption_align\else,\fi\p_strc_floats_caption_align}%
+ \doifinset\v!tolerant \floatcaptionlocation{\edef\m_align{\m_align,\v!tolerant}}%
+ \doifinset\v!verytolerant\floatcaptionlocation{\edef\m_align{\m_align,\v!verytolerant}}%
+ \doifinset\v!stretch \floatcaptionlocation{\edef\m_align{\m_align,\v!stretch}}%
+ \setupalign[\m_align]}
+
+\def\strc_floats_prepare_side_auto_caption
+ {\scratchdimen\dimexpr\hsize-\wd\b_strc_floats_content-\floatparameter\c!margin\relax
+ \ifdim\wd\b_strc_floats_caption>\scratchdimen
+ \ifdim\wd\b_strc_floats_caption<1.3\scratchdimen
+ \scratchdimen0.8\scratchdimen
+ \fi
+ \fi
+ \setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\scratchdimen
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_side_width_caption
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\floatcaptionparameter\c!width
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_stack_caption_fixed
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\floatcaptionparameter\c!minwidth % special effects
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_stack_caption_max
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\wd\b_strc_floats_content
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_stack_caption_width
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\floatcaptionparameter\c!width
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_stack_caption_min
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\wd\b_strc_floats_content
+ \ifempty\p_strc_floats_caption_align
+ \raggedcenter % on purpose overloads align !
+ \fi
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_stack_caption_auto
+ {\ifempty\p_strc_floats_caption_align \else
+ \doifnotinset\v!middle\p_strc_floats_caption_align{\d_strc_floats_caption_overshoot\zeropoint}%
+ \fi
+ \d_strc_floats_caption_hsize\wd\b_strc_floats_content
+ \scratchwidth\floatcaptionparameter\c!maxwidth\relax
+ \ifconditional\c_floats_adapt_to_caption_width
+ \d_strc_floats_caption_min_width\zeropoint
+ \d_strc_floats_caption_overshoot\zeropoint
+ \fi
+ \ifdim\d_strc_floats_caption_hsize>\scratchwidth
+ % float is wider than \hsize
+ \setbox\b_strc_floats_caption\vbox
+ {\settrialtypesetting
+ \strc_floats_caption_set_align
+ \hsize\scratchwidth
+ \notesenabledfalse
+ \strc_floats_make_complete_caption}%
+ \ifdim\ht\scratchbox>\lineheight % more lines
+ \setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\dimexpr\d_strc_floats_caption_hsize-\d_strc_floats_caption_overshoot\relax
+ \ifdim\hsize<\d_strc_floats_caption_min_width\relax
+ \hsize\scratchwidth
+ \fi
+ \strc_floats_make_complete_caption}%
+ \else
+ \setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\scratchwidth
+ \strc_floats_make_complete_caption}%
+ \fi
+ \else
+ % float is smaller of equal to \hsize
+ \ifdim\d_strc_floats_caption_hsize<\d_strc_floats_caption_min_width\relax
+ \d_strc_floats_caption_hsize\d_strc_floats_caption_min_width % float smaller than min width
+ % \ifconditional\c_floats_adapt_to_caption_width
+ % \setbox\b_strc_floats_content\hpack to \d_strc_floats_caption_hsize{\hss\box\b_strc_floats_content\hss}%
+ % \fi
+ \fi
+ \setbox\scratchbox\vbox % test with overshoot
+ {\settrialtypesetting
+ \scratchdimen\dimexpr\d_strc_floats_caption_hsize+\d_strc_floats_caption_overshoot+3\emwidth\relax % 3em is an average word length
+ \ifdim\scratchdimen<\hsize
+ \hsize\scratchdimen
+ \fi
+ \notesenabledfalse
+ \strc_floats_make_complete_caption}%
+ \ifdim\ht\scratchbox>\lineheight
+ % at least an average word longer than a line
+ \setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \scratchdimen\dimexpr\d_strc_floats_caption_hsize+\d_strc_floats_caption_overshoot\relax
+ \ifdim\scratchdimen<\hsize
+ \hsize\scratchdimen
+ \fi
+ \strc_floats_make_complete_caption}%
+ \orelse\ifempty\p_strc_floats_caption_align
+ \setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\d_strc_floats_caption_hsize
+ \raggedcenter % overloads
+ \strc_floats_make_complete_caption}%
+ \else
+ \setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\d_strc_floats_caption_hsize
+ \strc_floats_make_complete_caption}%
+ \fi
+ \fi}
+
+\def\strc_floats_between_stack
+ {\endgraf
+ \nointerlineskip
+ \floatcaptionparameter\c!inbetween
+ \endgraf}
+
+\def\strc_floats_build_box_default % done
+ {\strc_floats_align_content{\box\b_strc_floats_content}}
+
+\def\strc_floats_build_box_next_right#1%
+ {\ifconditional\c_strc_floats_par_float \hpack \else \expandafter \strc_floats_align_content \fi % skip, no pack
+ {\d_strc_float_temp_height\ht\b_strc_floats_content
+ \box\b_strc_floats_content
+ \doifnotinset\v!hang\floatcaptionlocation
+ {\dotfskip{\floatcaptionparameter\c!distance}}%
+ \vbox to\d_strc_float_temp_height{#1}}}
+
+\def\strc_floats_build_box_next_left#1%
+ {\ifconditional\c_strc_floats_par_float \hpack \else \expandafter \strc_floats_align_content \fi % skip, no pack
+ {\d_strc_float_temp_height\ht\b_strc_floats_content
+ \vbox to\d_strc_float_temp_height{#1}%
+ \doifnotinset\v!hang\floatcaptionlocation
+ {\dotfskip{\floatcaptionparameter\c!distance}}%
+ \box\b_strc_floats_content}}
+
+\def\strc_floats_build_box_next_outer
+ {\doifelserightpagefloat\strc_floats_build_box_next_right\strc_floats_build_box_next_left}
+
+\def\strc_floats_build_box_next_inner
+ {\doifelserightpagefloat\strc_floats_build_box_next_left\strc_floats_build_box_next_right}
+
+\def\strc_floats_build_box_next_right_hang#1%
+ {\ifconditional\c_strc_floats_par_float \hpack \else \expandafter \strc_floats_align_content \fi
+ {\d_strc_float_temp_height\ht\b_strc_floats_content
+ \box\b_strc_floats_content
+ \vbox to\d_strc_float_temp_height{#1}}}
+
+\def\strc_floats_build_box_next_left_hang#1%
+ {\ifconditional\c_strc_floats_par_float \hpack \else \expandafter \strc_floats_align_content \fi
+ {\d_strc_float_temp_height\ht\b_strc_floats_content
+ \vbox to\d_strc_float_temp_height{#1}%
+ \box\b_strc_floats_content}}
+
+\def\strc_floats_build_box_next_right_margin_indeed#1#2%
+ {\ifconditional\c_strc_floats_par_float
+ \hpack\bgroup
+ \d_strc_float_temp_height\ht\b_strc_floats_content
+ \box\b_strc_floats_content
+ \hsmash{\hskip#1\vbox to\d_strc_float_temp_height{#2}}%
+ \egroup
+ \else
+ \begingroup
+ \d_strc_float_temp_height\ht\b_strc_floats_content
+ \setbox\scratchboxone\vbox{#2}%
+ \ifdim\htdp\scratchboxone>\htdp\b_strc_floats_content
+ \global\d_strc_floats_overflow\dimexpr\htdp\scratchboxone-\htdp\b_strc_floats_content\relax
+ \fi
+ \ht\scratchboxone\d_strc_float_temp_height
+ \everyrightofalignedline{\hsmash{\hskip#1\box\scratchboxone}}%
+ \strc_floats_align_content{\box\b_strc_floats_content}%
+ \endgroup
+ \fi}
+
+\def\strc_floats_build_box_next_left_margin_indeed#1#2%
+ {\ifconditional\c_strc_floats_par_float
+ \hpack\bgroup
+ \d_strc_float_temp_height\ht\b_strc_floats_content
+ \hsmash{\hskip-\dimexpr#1+\wd\b_strc_floats_caption\relax\vbox to\d_strc_float_temp_height{#2}}%
+ \box\b_strc_floats_content
+ \egroup
+ \else
+ \begingroup
+ \d_strc_float_temp_height\ht\b_strc_floats_content
+ \setbox\scratchboxone\vbox{#2}%
+ \ifdim\htdp\scratchboxone>\htdp\b_strc_floats_content
+ \global\d_strc_floats_overflow\dimexpr\htdp\scratchboxone-\htdp\b_strc_floats_content\relax
+ \fi
+ \ht\scratchboxone\d_strc_float_temp_height
+ \everyleftofalignedline{\hsmash{\hskip-\dimexpr#1+\wd\scratchboxone\relax\box\scratchboxone}}%
+ \strc_floats_align_content{\box\b_strc_floats_content}%
+ \endgroup
+ \fi}
+
+\def\strc_floats_build_box_next_right_margin
+ {\strc_floats_build_box_next_right_margin_indeed\rightmargindistance}
+
+\def\strc_floats_build_box_next_left_margin
+ {\strc_floats_build_box_next_left_margin_indeed \leftmargindistance}
+
+\def\strc_floats_build_box_next_outer_margin
+ {\doifelserightpagefloat
+ {\strc_floats_build_box_next_right_margin_indeed\rightmargindistance}
+ {\strc_floats_build_box_next_left_margin_indeed \rightmargindistance}}
+
+\def\strc_floats_build_box_next_inner_margin
+ {\doifelserightpagefloat
+ {\strc_floats_build_box_next_left_margin_indeed \leftmargindistance}
+ {\strc_floats_build_box_next_right_margin_indeed\leftmargindistance}}
+
+\def\strc_floats_build_box_next % beware, we first check on left/rightmargin because there can be left/right also
+ {\let\next\strc_floats_build_box_next_left
+ \processallactionsinset[\floatcaptionlocation]
+ [ \v!outermargin=>\let\next\strc_floats_build_box_next_outer_margin,
+ \v!innermargin=>\let\next\strc_floats_build_box_next_inner_margin,
+ \v!leftmargin=>\let\next\strc_floats_build_box_next_left_margin,
+ \v!rightmargin=>\let\next\strc_floats_build_box_next_right_margin,
+ \v!lefthanging=>\let\next\strc_floats_build_box_next_left_hang,
+ \v!righthanging=>\let\next\strc_floats_build_box_next_right_hang,
+ \v!outer=>\let\next\strc_floats_build_box_next_outer,
+ \v!inner=>\let\next\strc_floats_build_box_next_inner,
+ \v!left=>\let\next\strc_floats_build_box_next_left,
+ \v!right=>\let\next\strc_floats_build_box_next_right]%
+ \next}
+
+\def\strc_floats_build_box_side
+ {\ifconditional\c_strc_floats_par_float
+ \let\next\strc_floats_build_box_high
+ \else
+ \let\next\strc_floats_build_box_middle
+ \processallactionsinset[\floatcaptionlocation]
+ [ \v!low=>\let\next\strc_floats_build_box_low,
+ \v!middle=>\let\next\strc_floats_build_box_middle,
+ \v!high=>\let\next\strc_floats_build_box_high]%
+ \fi
+ \next}
+
+\def\strc_floats_flush_right_caption_hang
+ {\hsmash{\rlap{\dotfskip{\floatcaptionparameter\c!distance}\box\b_strc_floats_caption}}}
+
+\def\strc_floats_flush_left_caption_hang
+ {\hsmash{\llap{\box\b_strc_floats_caption\dotfskip{\floatcaptionparameter\c!distance}}}}
+
+\def\strc_floats_flush_caption_hang
+ {\doifelseinset\v!righthanging\floatcaptionlocation
+ {\strc_floats_flush_right_caption_hang}
+ {\doifelseinset\v!lefthanging\floatcaptionlocation
+ {\strc_floats_flush_left_caption_hang}
+ {\doifelseinset\v!hang\floatcaptionlocation
+ {\doifelseinset\v!outer\floatcaptionlocation
+ {\doifelserightpagefloat{\strc_floats_flush_right_caption_hang}{\strc_floats_flush_left_caption_hang}}
+ {\doifelseinset\v!right\floatcaptiondirectives
+ {\strc_floats_flush_right_caption_hang}
+ {\strc_floats_flush_left_caption_hang}}}
+ {\box\b_strc_floats_caption}}}} % is this ok
+
+\def\strc_floats_build_box_high
+ {\strc_floats_build_box_next{\strc_floats_between_stack\strc_floats_flush_caption_hang\vfill}}
+
+\def\strc_floats_build_box_low
+ {\strc_floats_build_box_next{\vfill\strc_floats_flush_caption_hang\strc_floats_between_stack}}
+
+\def\strc_floats_build_box_middle
+ {\strc_floats_build_box_next{\vfill\box\b_strc_floats_caption\vfill}}
+
+% \definefloat
+% [lefty][lefties][figure]
+% \setupfloat
+% [lefty]
+% [default=left,
+% rightmargindistance=-2cm,
+% leftmargindistance=-2cm]
+% \setupcaption
+% [lefty]
+% [location={bottom,overlay}]
+%
+% \starttext
+% \placelefty{}{} \input tufte \input tufte
+% \placelefty{}{} \input tufte \input tufte
+% \stoptext
+
+\def\strc_floats_build_box_top_stack_normal_overlay
+ {\vbox to \ht\b_strc_floats_content{\vss\strc_floats_build_box_top_stack_normal_content}}
+
+\def\strc_floats_build_box_top_stack_normal_content
+ {\d_strc_float_temp_width\wd\b_strc_floats_content
+ \ifconditional\c_strc_floats_par_float
+ \hpack{\strc_floats_locate_side_float{\box\b_strc_floats_caption}}%
+ \strc_floats_between_stack
+ \hpack{\hbox{\box\b_strc_floats_content}}%
+ \else
+ \page_otr_command_set_float_hsize
+ \hpack{\strc_floats_locate_text_float{\box\b_strc_floats_caption}}
+ \strc_floats_between_stack
+ \hpack{\strc_floats_align_content{\box\b_strc_floats_content}}%
+ \fi}
+
+\def\strc_floats_build_box_bottom_stack_normal_overlay
+ {\vbox to \ht\b_strc_floats_content{\strc_floats_build_box_bottom_stack_normal_content\vss}}
+
+\def\strc_floats_build_box_bottom_stack_normal_content
+ {\d_strc_float_temp_width\wd\b_strc_floats_content
+ \ifconditional\c_strc_floats_par_float
+ \hpack{\hpack{\box\b_strc_floats_content}}%
+ \strc_floats_between_stack
+ \hpack{\strc_floats_locate_side_float{\box\b_strc_floats_caption}}%
+ \else
+ \page_otr_command_set_float_hsize
+ \hpack{\strc_floats_align_content{\box\b_strc_floats_content}}%
+ \strc_floats_between_stack
+ \hpack{\strc_floats_locate_text_float{\box\b_strc_floats_caption}}%
+ \fi}
+
+\def\strc_floats_build_box_top_stack_normal
+ {\doifelseinset\v!overlay{\floatcaptionparameter\c!location}
+ \strc_floats_build_box_top_stack_normal_overlay
+ \strc_floats_build_box_top_stack_normal_content}
+
+\def\strc_floats_build_box_bottom_stack_normal
+ {\doifinset\v!overlay{\floatcaptionparameter\c!location}
+ \strc_floats_build_box_bottom_stack_normal_overlay
+ \strc_floats_build_box_bottom_stack_normal_content}
+
+\def\strc_floats_build_box_top_stack_grid
+ {\dp\b_strc_floats_caption\strutdepth
+ \setbox\scratchbox\vbox
+ {\d_strc_float_temp_width\wd\b_strc_floats_content
+ \hsize\d_strc_float_temp_width
+ \ifconditional\c_strc_floats_par_float
+ \strc_floats_locate_side_float{\box\b_strc_floats_caption}%
+ \vss\strc_floats_between_stack
+ \hpack{\box\b_strc_floats_content}%
+ \else
+ \page_otr_command_set_float_hsize
+ \strc_floats_locate_text_float{\box\b_strc_floats_caption}%
+ \vss\strc_floats_between_stack
+ \strc_floats_align_content{\box\b_strc_floats_content}%
+ \fi}%
+ \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
+ \vpack to \noflines\lineheight{\unvbox\scratchbox}}
+
+\def\strc_floats_build_box_bottom_stack_grid
+ {\dp\b_strc_floats_caption\strutdepth
+ \setbox\scratchbox\vbox
+ {\d_strc_float_temp_width\wd\b_strc_floats_content
+ \hsize\d_strc_float_temp_width
+ \ifconditional\c_strc_floats_par_float
+ \hpack{\box\b_strc_floats_content}%
+ \vss\strc_floats_between_stack
+ \strc_floats_locate_side_float{\box\b_strc_floats_caption}%
+ \else
+ \page_otr_command_set_float_hsize
+ \strc_floats_align_content{\box\b_strc_floats_content}%
+ \vss\strc_floats_between_stack
+ \strc_floats_locate_text_float{\box\b_strc_floats_caption}%
+ \fi}%
+ \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
+ \vpack to \noflines\lineheight{\unvbox\scratchbox}}
+
+\def\strc_floats_build_box_top_stack_stretch
+ {\dp\b_strc_floats_caption\strutdepth
+ \setbox\scratchbox\vpack
+ {\strc_floats_align_caption{\copy\b_strc_floats_caption}%
+ \strc_floats_align_content{\copy\b_strc_floats_content}}%
+ \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
+ \vbox to \noflines\lineheight % pack ?
+ {\d_strc_float_temp_width\wd\b_strc_floats_content
+ \hsize\d_strc_float_temp_width
+ \ifconditional\c_strc_floats_par_float
+ \strc_floats_locate_side_float{\box\b_strc_floats_caption}%
+ \vss\strc_floats_between_stack\vss
+ \hpack{\box\b_strc_floats_content}%
+ \else
+ \page_otr_command_set_float_hsize
+ \strc_floats_locate_text_float{\box\b_strc_floats_caption}%
+ \vss\strc_floats_between_stack\vss
+ \strc_floats_align_content{\box\b_strc_floats_content}%
+ \fi}}
+
+\def\strc_floats_build_box_bottom_stack_stretch
+ {\dp\b_strc_floats_caption\strutdepth
+ \setbox\scratchbox\vpack
+ {\strc_floats_align_content{\copy\b_strc_floats_content}%
+ \strc_floats_align_caption{\copy\b_strc_floats_caption}}%
+ \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
+ \vbox to \noflines\lineheight
+ {\d_strc_float_temp_width\wd\b_strc_floats_content
+ \hsize\d_strc_float_temp_width
+ \ifconditional\c_strc_floats_par_float
+ \hpack{\box\b_strc_floats_content}%
+ \vss\strc_floats_between_stack\vss
+ \strc_floats_locate_side_float{\box\b_strc_floats_caption}%
+ \else
+ \page_otr_command_set_float_hsize
+ \strc_floats_align_content{\box\b_strc_floats_content}%
+ \vss\strc_floats_between_stack\vss
+ \strc_floats_locate_text_float{\box\b_strc_floats_caption}%
+ \fi}}
+
+\def\strc_floats_build_box_top
+ {\let\next\strc_floats_build_box_top_stack_normal
+ \processfirstactioninset[\floatcaptionparameter\c!location]
+ [ \v!grid=>\let\next\strc_floats_build_box_top_stack_grid,
+ \v!lines=>\let\next\strc_floats_build_box_top_stack_stretch]% was \v!grid but interfered
+ \next}
+
+\def\strc_floats_build_box_bottom
+ {\let\next\strc_floats_build_box_bottom_stack_normal
+ \processfirstactioninset[\floatcaptionparameter\c!location]
+ [ \v!grid=>\let\next\strc_floats_build_box_bottom_stack_grid,
+ \v!lines=>\let\next\strc_floats_build_box_bottom_stack_stretch]% was \v!grid but interfered
+ \next}
+
+\def\strc_floats_relocate_caption_right#1{\strc_floats_align_caption{\hbox to \d_strc_float_temp_width{\hss#1}}}
+\def\strc_floats_relocate_caption_left #1{\strc_floats_align_caption{\hbox to \d_strc_float_temp_width{#1\hss}}}
+
+\permanent\protected\def\installfloatboxbuilder#1#2{\setvalue{\??floatbuilder#1}{#2}}
+
+\def\strc_floats_build_box
+ {\strc_floats_build_box_before
+ \global\setbox\floatbox\vbox % pack ? probably not
+ {\strc_floats_set_local_hsize
+ \forgetall
+ \ifconditional\c_floats_store_minimal_package
+ \strc_floats_build_box_separate_make
+ \else
+ % \let\floatcaptionarrangement\s!default
+ \let\floatcaptionarrangement\v!bottom % for Alan
+ \processcommacommand[\floatcaptionparameter\c!location]\strc_floats_build_box_step
+ \ifcsname\??floatbuilder\floatcaptionarrangement\endcsname
+ \lastnamedcs
+ \else
+ \strc_floats_build_box_default
+ \fi
+ \fi}%
+ \strc_floats_build_box_after}
+
+% \let\strc_floats_build_box_before\relax
+% \let\strc_floats_build_box_after \relax
+
+\def\strc_floats_build_box_before
+ {\let\currentfloatframed\currentfloat
+ \floatwidth\wd
+ \ifdim\wd\b_strc_floats_content>\wd\b_strc_floats_caption
+ \b_strc_floats_content\else\b_strc_floats_caption
+ \fi}
+
+\def\strc_floats_build_box_after
+ {\doifelseframed\floatframedparameter\strc_floats_build_box_after_indeed\relax}
+
+\def\strc_floats_build_box_after_indeed
+ {\global\setbox\floatbox\hpack
+ {\edef\m_width{\floatframedparameter\c!width}%
+ \ifx\m_width\v!fit
+ \let\m_width\floatwidth
+ \orelse\ifx\m_width\v!broad
+ \let\m_width\v!fit
+ \fi
+ \letfloatframedparameter\c!strut\v!no
+ \letfloatframedparameter\c!width\m_width
+ \inheritedfloatframedframed
+ {\box\floatbox}}}
+
+% special purpose: used in floatcombinations
+%
+% todo : keep float content and caption separated in local
+
+\newbox\b_strc_floats_separate_content
+\newbox\b_strc_floats_separate_caption
+
+\def\strc_floats_build_box_separate_set
+ {\settrue\c_floats_adapt_to_caption_width
+ \settrue\c_floats_store_minimal_package}
+
+% \def\strc_floats_build_box_separate_make
+% {\offinterlineskip
+% \vpack to \onepoint{\box\b_strc_floats_content}\break
+% \vpack to \onepoint{\box\b_strc_floats_caption}}
+
+%D Remark for \LMTX: we don't want to migrate inserts here so we unpack
+%D (could be a flag). When migrated, the inserts end up in the vertical
+%D list and we no longer have just a box (but inserts and lines again).
+
+\def\strc_floats_build_box_separate_make
+ {\savebox\??localfloatstack{\number\numexpr\c_strc_localfloats_n+1\relax:a}{\box\b_strc_floats_content}%
+ \savebox\??localfloatstack{\number\numexpr\c_strc_localfloats_n+1\relax:b}{\box\b_strc_floats_caption}}
+
+\protected\def\strc_floats_build_box_separate_split#1%
+ {\global\setbox\b_strc_floats_separate_content\vpack{\foundbox\??localfloatstack{\number#1:a}}%
+ \global\setbox\b_strc_floats_separate_caption\tpack{\foundbox\??localfloatstack{\number#1:b}}}
+
+% \def\strc_floats_build_box_step#1%
+% {\doifdefined{\??floatbuilder#1}{\def\floatcaptionarrangement{#1}\quitcommalist}}
+
+\def\strc_floats_build_box_step#1%
+ {\ifcsname\??floatbuilder#1\endcsname
+ \def\floatcaptionarrangement{#1}% \let\floatcaptionarrangement\commalistelement
+ \quitcommalist
+ \fi}
+
+\def\strc_floats_locate_text_float
+ {\let\next\strc_floats_align_caption
+ \processallactionsinset[\floatcaptionparameter\c!location]
+ [ \v!left=>\let\next\strc_floats_relocate_caption_left,
+ \v!right=>\let\next\strc_floats_relocate_caption_right,
+ \v!inner=>\doifelserightpagefloat{\let\next\strc_floats_relocate_caption_left }{\let\next\strc_floats_relocate_caption_right},
+ \v!outer=>\doifelserightpagefloat{\let\next\strc_floats_relocate_caption_right}{\let\next\strc_floats_relocate_caption_left }]%
+ \next}
+
+\installfloatboxbuilder \v!none \strc_floats_build_box_default
+\installfloatboxbuilder \s!default \strc_floats_build_box_default
+\installfloatboxbuilder \v!high \strc_floats_build_box_high
+\installfloatboxbuilder \v!low \strc_floats_build_box_low
+\installfloatboxbuilder \v!middle \strc_floats_build_box_middle
+
+\installfloatboxbuilder \v!rightmargin \strc_floats_build_box_side % added 2016-08-23
+\installfloatboxbuilder \v!leftmargin \strc_floats_build_box_side % added 2016-08-23
+\installfloatboxbuilder \v!innermargin \strc_floats_build_box_side % added 2016-08-23
+\installfloatboxbuilder \v!outermargin \strc_floats_build_box_side % added 2016-08-23
+
+\installfloatboxbuilder \v!left \strc_floats_build_box_side
+\installfloatboxbuilder \v!right \strc_floats_build_box_side
+\installfloatboxbuilder \v!inner \strc_floats_build_box_side % added 2016-08-23
+\installfloatboxbuilder \v!outer \strc_floats_build_box_side % added 2016-08-23
+
+\installfloatboxbuilder \v!lefthanging \strc_floats_build_box_side % added 2016-08-23
+\installfloatboxbuilder \v!righthanging \strc_floats_build_box_side % added 2016-08-23
+\installfloatboxbuilder \v!hang \strc_floats_build_box_side % added 2016-08-23
+
+\installfloatboxbuilder \v!top \strc_floats_build_box_top
+\installfloatboxbuilder \v!bottom \strc_floats_build_box_bottom
+
+% \setuplayout[grid=yes] \showgrid \setupcaptions[style=smallbodyfont,location=grid,inbetween=]
+%
+% \starttext
+% test \placefigure{} {\externalfigure[cow.pdf][frame=on,grid=yes]} test \page
+% test \placefigure{\input zapf\relax}{\externalfigure[cow.pdf][frame=on,grid=yes]} test \page
+% test \placefigure{} {\externalfigure[cow.pdf][frame=on,grid=depth]} test \page
+% test \placefigure{\input zapf\relax}{\externalfigure[cow.pdf][frame=on,grid=depth]} test \page
+% \stoptext
+
+% This might move to page-flt:
+
+\newif\ifpostponecolumnfloats \postponecolumnfloatsfalse % don't change
+
+\setnewconstant\postcenterfloatmethod\plusone
+
+\permanent\def\postcenterfloatbox#1%
+ {\scratchdimen
+ \ifcase\postcenterfloatmethod
+ #1% \wd\floatbox
+ \orelse\ifinsidecolumns
+ \ifpostponecolumnfloats\makeupwidth\else#1\fi
+ \orelse\ifdim#1>\hsize
+ \hsize
+ \else
+ \wd\floatbox
+ \fi
+ \global\setbox\floatbox\hbox to \scratchdimen
+ % {\hfill\box\floatbox\hfill}} % geen \hss, gaat mis in kolommen !
+ % {\hss \box\floatbox\hss }} % wel \hss, anders mis in colset
+ {\ifconditional\c_page_floats_center_box_global
+ \donetrue
+ \orelse\ifconditional\c_page_floats_center_box_local
+ \donetrue
+ \else
+ \donefalse
+ \fi
+ \ifdim\scratchdimen>\effectivehsize
+ \donefalse
+ \fi
+ \hss\ifdone\hskip\effectiveleftskip\fi
+ \box\floatbox
+ \ifdone\hskip\effectiverightskip\fi\hss}}
+
+\def\strc_floats_set_paragraph_variant
+ {\bgroup
+ \forgetall
+ \postponenotes
+ \dontcomplain
+ \setbox\b_strc_floats_content\vbox{\borderedfloatbox}% \vpack >?
+ %\page_backgrounds_add_local_to_box\b_strc_floats_content
+ \ifnofloatcaption
+ \global\setbox\floatbox\vpack{\box\b_strc_floats_content}%
+ \else
+ \strc_floats_check_caption_content
+ \strc_floats_prepare_side_caption
+ \setbox\b_strc_floats_caption\hbox{\floatcaptionparameter\c!command{\box\b_strc_floats_caption}}% \hpack ?
+ %\moveboxontogrid\b_strc_floats_caption{\floatcaptionparameter\c!grid}\d_strc_floats_caption_height
+ %\page_backgrounds_add_local_to_box\b_strc_floats_caption
+ \strc_floats_build_side_box
+ \fi
+ \egroup}
+
+\def\strc_floats_prepare_side_caption
+ {\dostarttagged\t!floatcaption\empty
+ \edef\p_strc_floats_caption_width{\floatcaptionparameter\c!width}%
+ \edef\p_strc_floats_caption_align{\floatcaptionparameter\c!align}%
+ \ifx\p_strc_floats_caption_width\v!max
+ \strc_floats_prepare_side_caption_max
+ \orelse\ifx\p_strc_floats_caption_width\v!fit
+ \strc_floats_prepare_side_caption_fit
+ \else
+ \strc_floats_prepare_side_caption_width
+ \fi
+ \dostoptagged}
+
+% these could be \??floatpreparesidecaption
+
+% \setupfloat[figure][location=left]
+% \setupcaption[figure][width=max]
+%
+% \placefigure{my figure caption my figure caption}{\framed[width=4cm,height=1cm]{}} \input tufte
+% \placefigure{my figure caption} {\framed[width=4cm,height=1cm]{}} \input tufte
+
+\def\strc_floats_prepare_side_caption_max
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\wd\b_strc_floats_content
+ \strc_floats_make_complete_caption}}
+
+\def\strc_floats_prepare_side_caption_fit % or center when smaller
+ {\ifdim\wd\b_strc_floats_caption>\wd\b_strc_floats_content\relax
+ \setbox\b_strc_floats_caption\vbox
+ {\forgetall % needed?
+ \strc_floats_caption_set_align
+ \hsize\wd\b_strc_floats_content
+ \strc_floats_make_complete_caption}%
+ \else
+ % maybe we should listen to the align option here (now side floats need the max option
+ \setbox\b_strc_floats_caption\hpack to \wd\b_strc_floats_content
+ {\hss\hbox{\strc_floats_make_complete_caption}\hss}%
+ \fi}
+
+\def\strc_floats_prepare_side_caption_width
+ {\setbox\b_strc_floats_caption\vbox
+ {\strc_floats_caption_set_align
+ \hsize\p_strc_floats_caption_width % \wd\b_strc_floats_content
+ \strc_floats_make_complete_caption}}
+
+% % maybe (but then also prepare_page that way):
+%
+% \installcorenamespace{floatpreparesidecaption}
+%
+% \def\strc_floats_prepare_side_caption
+% {\dostarttagged\t!floatcaption\empty
+% \edef\p_strc_floats_caption_width{\floatcaptionparameter\c!width}%
+% \edef\p_strc_floats_caption_align{\floatcaptionparameter\c!align}%
+% \expandnamespacemacro\??floatpreparesidecaption\p_strc_floats_caption_width\s!unknown
+% \dostoptagged}
+%
+% \setvalue{\??floatpreparesidecaption\v!max}%
+% {\setbox\b_strc_floats_caption\vbox
+% {\strc_floats_caption_set_align
+% \hsize\wd\b_strc_floats_content
+% \strc_floats_make_complete_caption}}
+%
+% \setvalue{\??floatpreparesidecaption\v!fit}%
+% {\ifdim\wd\b_strc_floats_caption>\wd\b_strc_floats_content\relax
+% \setbox\b_strc_floats_caption\vbox
+% {\forgetall % needed?
+% \hsize\wd\b_strc_floats_content
+% \strc_floats_make_complete_caption}%
+% \else
+% \setbox\b_strc_floats_caption\hbox to \wd\b_strc_floats_content
+% {\hss\hbox{\strc_floats_make_complete_caption}\hss}%
+% \fi}
+%
+% \setvalue{\??floatpreparesidecaption\s!unknown}%
+% {\setbox\b_strc_floats_caption\vbox
+% {\strc_floats_caption_set_align
+% \hsize\p_strc_floats_caption_width % \wd\b_strc_floats_content
+% \strc_floats_make_complete_caption}}
+
+\def\strc_floats_locate_side_float#1%
+ {\begingroup
+ \alignstrutmode\zerocount
+ \hsize\d_strc_float_temp_width \forgetall
+ \alignedline{\floatparameter\c!location}\v!middle{#1}%
+ \endgroup}
+
+\def\strc_floats_build_side_box
+ {\let\strc_floats_align_content\relax
+ \let\strc_floats_align_caption\relax
+ \strc_floats_build_box}
+
+\def\strc_floats_set_box % todo : \global\setbox, currently messy
+ {\ifvisible
+ \par
+ \edef\floatcaptiondirectives{\floatparameter\c!location,\floatcaptionparameter\c!location}%
+ \strc_floats_set_caption_criteria
+ \ifconditional\c_strc_floats_par_float
+ \strc_floats_set_paragraph_variant
+ \else
+ \strc_floats_set_page_variant
+ \fi
+ \strc_floats_set_local_dimensions
+ \global\advance\totalnoffloats\plusone
+ \ifconditional\c_floats_store_minimal_package \else
+ \setbox\floatbox\hpack{\strc_float_save_data\box\floatbox}% still needed? we will do renumbering differently
+ \fi
+ \global\floatheight\htdp\floatbox
+ \global\floatwidth\wd\floatbox
+ \ifconditional\c_floats_store_minimal_package \else
+ \doifnotinset\v!margin\floatlocation % gaat namelijk nog fout
+ {\setbox\floatbox\vpack
+ {\parindent\zeropoint
+ \box\floatbox}}%
+ \fi
+ \wd\floatbox\floatwidth
+ \ifdim\dimexpr\floatheight+\lineheight\relax<\textheight \else
+ \global\floatheight\dimexpr\textheight-\lineheight\relax
+ \ht\floatbox\floatheight
+ \dp\floatbox\zeropoint
+ \showmessage\m!floatblocks{10}{\the\totalnoffloats}%
+ \fi
+ \fi}
+
+% \def\dooutput{\sidefloatoutput} % redefinition of \dooutput
+
+\definefloat
+ [\v!figure]
+ [\v!figures]
+
+\definefloat
+ [\v!table]
+ [\v!tables]
+
+\setupfloat
+ [\v!table]
+ [\c!frame=\v!off]
+
+\definefloat
+ [\v!intermezzo]
+ [\v!intermezzi]
+
+\definefloat
+ [\v!graphic]
+ [\v!graphics]
+
+% float strategy, replaces some of the above macros
+
+\installcorenamespace{floatmethods}
+
+\mutable\let\floatmethod \empty % set by lua
+\mutable\let\floatlabel \empty % set by lua
+\mutable\let\floatcolumn \empty % set by lua
+\mutable\let\floatrow \empty % set by lua
+\mutable\let\floatspecification\empty
+
+\mutable\let\forcedfloatmethod \empty % set by lua and floatcombinations
+
+\permanent\protected\def\setfloatmethodvariables#1% \floatmethod \floatlabel \floatrow \floatcolumn
+ {\clf_analysefloatmethod{#1}} % move definition to lua end
+
+\permanent\protected\def\somesomewherefloat[#1]%
+ {\page_floats_save_somewhere_float\s!somewhere{#1}}
+
+\def\strc_floats_get_box
+ {\ifvisible
+ \ifcsname\??floatmethods\currentoutputroutine:\floatmethod\endcsname \else
+ \let\floatmethod\v!here
+ \fi
+ \ifempty\forcedfloatmethod \else
+ \let\floatmethod\forcedfloatmethod
+ \fi
+ \let\askedfloatmethod\floatmethod
+ \ifexporting \ifx\askedfloatmethod\v!here \else
+ \showmessage\m!floatblocks{15}{\askedfloatmethod,\v!here}%
+ \let\floatlocation\v!here
+ \fi \fi
+ % [] will go
+ \edef\floatlocationmethod{\floatmethod,\floatlocation}%
+ \csname\??floatmethods\currentoutputroutine:\floatmethod\endcsname
+ \fi}
+
+\installcorenamespace{floatsettings}
+
+\permanent\protected\def\installfloatmethod#1#2#3% routine keyword handler
+ {\setvalue{\??floatmethods#1:#2}{#3}}
+
+\permanent\protected\def\handlefloatmethod#1%
+ {\csname\??floatmethods\currentoutputroutine:#1\endcsname}
+
+% \protected\def\installfloatmethod#1#2#3% routine keyword handler
+% {\ifcsname\??floatsettings#1:#2\endcsname \else
+% \expandafter\newtoks\csname\??floatsettings#1:#2\endcsname
+% \fi
+% \setvalue{\??floatmethods#1:#2}{#3}}
+%
+% \protected\def\startfloatmethodsettings#1#2 #3\stopfloatmethodsettings
+% {\csname\??floatsettings#1:#2\endcsname\expandafter{\the\csname\??floatsettings#1:#2\endcsname#3}}
+%
+% \let\stopfloatmethodsettings\relax
+%
+% \protected\def\applyfloatmethodsettings#1#2{\the\??floatsettings#1:#2\endcsname}
+
+\definesystemconstant{tblr}
+\definesystemconstant{lrtb}
+\definesystemconstant{tbrl}
+\definesystemconstant{rltb}
+\definesystemconstant{btlr}
+\definesystemconstant{lrbt}
+\definesystemconstant{btrl}
+\definesystemconstant{rlbt}
+\definesystemconstant{fxtb}
+\definesystemconstant{fxbt}
+\definesystemconstant{fixd}
+
+% can move to page-one:
+
+\installfloatmethod \s!singlecolumn \v!here \page_one_place_float_here
+\installfloatmethod \s!singlecolumn \v!force \page_one_place_float_force
+\installfloatmethod \s!singlecolumn \v!left \page_one_place_float_left
+\installfloatmethod \s!singlecolumn \v!right \page_one_place_float_right
+\installfloatmethod \s!singlecolumn \v!text \page_one_place_float_text
+\installfloatmethod \s!singlecolumn \v!top \page_one_place_float_top
+\installfloatmethod \s!singlecolumn \v!bottom \page_one_place_float_bottom
+\installfloatmethod \s!singlecolumn \v!auto \page_one_place_float_auto
+\installfloatmethod \s!singlecolumn \v!margin \page_one_place_float_margin
+\installfloatmethod \s!singlecolumn \v!opposite \page_one_place_float_face
+\installfloatmethod \s!singlecolumn \v!page \page_one_place_float_page
+\installfloatmethod \s!singlecolumn \v!leftpage \page_one_place_float_leftpage
+\installfloatmethod \s!singlecolumn \v!rightpage \page_one_place_float_rightpage
+\installfloatmethod \s!singlecolumn \v!inmargin \page_one_place_float_inmargin
+\installfloatmethod \s!singlecolumn \v!inleft \page_one_place_float_leftmargin
+\installfloatmethod \s!singlecolumn \v!inright \page_one_place_float_rightmargin
+\installfloatmethod \s!singlecolumn \v!leftmargin \page_one_place_float_leftmargin
+\installfloatmethod \s!singlecolumn \v!rightmargin \page_one_place_float_rightmargin
+\installfloatmethod \s!singlecolumn \v!leftedge \page_one_place_float_leftedge
+\installfloatmethod \s!singlecolumn \v!rightedge \page_one_place_float_rightedge
+\installfloatmethod \s!singlecolumn \v!somewhere \page_one_place_float_somewhere
+\installfloatmethod \s!singlecolumn \v!backspace \page_one_place_float_backspace
+\installfloatmethod \s!singlecolumn \v!cutspace \page_one_place_float_cutspace
+\installfloatmethod \s!singlecolumn \s!tblr \page_one_place_float_top
+\installfloatmethod \s!singlecolumn \s!lrtb \page_one_place_float_top
+\installfloatmethod \s!singlecolumn \s!tbrl \page_one_place_float_top
+\installfloatmethod \s!singlecolumn \s!fxtb \page_one_place_float_top
+\installfloatmethod \s!singlecolumn \s!rltb \page_one_place_float_top
+\installfloatmethod \s!singlecolumn \s!btlr \page_one_place_float_bottom
+\installfloatmethod \s!singlecolumn \s!lrbt \page_one_place_float_bottom
+\installfloatmethod \s!singlecolumn \s!btrl \page_one_place_float_bottom
+\installfloatmethod \s!singlecolumn \s!rlbt \page_one_place_float_bottom
+\installfloatmethod \s!singlecolumn \s!fxbt \page_one_place_float_bottom
+\installfloatmethod \s!singlecolumn \s!fixd \page_one_place_float_force
+
+%D Local floats:
+
+\installcorenamespace{localfloats}
+\installcorenamespace{localfloatstack}
+
+\installsetuponlycommandhandler \??localfloats {localfloats}
+
+\setuplocalfloats
+ [%\c!before=\blank,
+ %\c!after=\blank,
+ \c!inbetween=\blank]
+
+\initializeboxstack\??localfloatstack
+
+\newcount\c_strc_localfloats_n \aliased\let\noflocalfloats\c_strc_localfloats_n
+
+\permanent\protected\def\resetlocalfloats
+ {\global\c_strc_localfloats_n\zerocount
+ \initializeboxstack\??localfloatstack}
+
+\permanent\protected\def\somelocalfloat
+ {\global\advance\c_strc_localfloats_n\plusone
+ \savebox\??localfloatstack{\number\c_strc_localfloats_n}{\box\floatbox}}
+
+\permanent\protected\def\getlocalfloats
+ {\dorecurse\c_strc_localfloats_n
+ {\ifnum\recurselevel=\plusone % 1\relax
+ \directlocalfloatsparameter\c!before
+ \else
+ \directlocalfloatsparameter\c!inbetween
+ \fi
+ \dontleavehmode\hpack{\foundbox\??localfloatstack\recurselevel}% \restorebox...
+ \ifnum\recurselevel=\c_strc_localfloats_n\relax
+ \directlocalfloatsparameter\c!after
+ \fi}}
+
+\permanent\protected\def\flushlocalfloats
+ {\getlocalfloats
+ \resetlocalfloats}
+
+% \protected\def\getlocalfloat#1%
+% {\normalexpanded{\foundbox{\??localfloatstack}{\number#1}}}% \vbox{\restorebox...}
+
+\permanent\protected\def\getlocalfloat#1%
+ {\foundbox\??localfloatstack{\number#1}} % \vbox{\restorebox...}
+
+\permanent\protected\def\forcelocalfloats
+ {\let\forcedfloatmethod\v!local}
+
+\installfloatmethod \s!singlecolumn \v!local \somelocalfloat
+\installfloatmethod \s!multicolumn \v!local \somelocalfloat
+\installfloatmethod \s!mixedcolumn \v!local \somelocalfloat
+\installfloatmethod \s!columnset \v!local \somelocalfloat
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-ind.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-ind.mkxl
new file mode 100644
index 00000000000..31d7eb5755a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-ind.mkxl
@@ -0,0 +1,128 @@
+%D \module
+%D [ file=strc-ind, % was part of strc-des,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Indented Text,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Indented Text}
+
+\unprotect
+
+%D \macros
+%D {defineindentedtext,
+%D setupindentedtext}
+%D
+%D Ok, we keep it but with a different command as it all looks too
+%D much like indentation and indenting. We also assume start/stop
+%D usage or some explicit par.
+
+\installcorenamespace{indentedtext}
+
+\installcommandhandler \??indentedtext {indentedtext} \??indentedtext
+
+%D \startbuffer
+%D \defineindentedtext[one][text=one]
+%D \defineindentedtext[two][text=two]
+%D
+%D \one test test \par
+%D \subone test test \par
+%D \subtwo test test \par
+%D \subsubone test test \par
+%D
+%D \startone
+%D test test
+%D \startone
+%D test test
+%D \startone
+%D test test
+%D \stopone
+%D \starttwo
+%D test test
+%D \startone
+%D test test
+%D \stopone
+%D \stoptwo
+%D \stopone
+%D \stopone
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\newcount\c_strc_indentedtexts_nesting
+\newdimen\d_strc_indentedtexts_width
+\newdimen\d_strc_indentedtexts_distance
+
+\appendtoks
+ \frozen\instance\setuevalue {\e!start\currentindentedtext}{\strc_indentedtexts_start{\currentindentedtext}\c_strc_indentedtexts_nesting}%
+ \frozen\instance\setuevalue {\e!stop \currentindentedtext}{\strc_indentedtexts_stop}%
+ % to be avoided ... might go away
+ \frozen\instance\setuevalue {\currentindentedtext}{\strc_indentedtexts_direct{\currentindentedtext}{0}}%
+ \frozen\instance\setuevalue {\v!sub\currentindentedtext}{\strc_indentedtexts_direct{\currentindentedtext}{1}}%
+ \frozen\instance\setuevalue{\v!sub\v!sub\currentindentedtext}{\strc_indentedtexts_direct{\currentindentedtext}{2}}%
+\to \everydefineindentedtext
+
+\protected\def\strc_indentedtexts_start#1#2% we need to get rid of \spr
+ {\par
+ \begingroup
+ \edef\currentindentedtext{#1}%
+ \c_strc_indentedtexts_nesting#2\relax
+ \indentedtextparameter\c!before
+ \d_strc_indentedtexts_distance\indentedtextparameter\c!distance\relax
+ \doifnothing{\indentedtextparameter\c!sample}
+ {\setindentedtextparameter\c!sample{\indentedtextparameter\c!text}}%
+ \assignwidth
+ {\indentedtextparameter\c!width}
+ {\d_strc_indentedtexts_width}
+ {\useindentedtextstyleandcolor\c!headstyle\c!headcolor
+ \indentedtextparameter\c!sample
+ \spr{\indentedtextparameter\c!separator}}
+ {\d_strc_indentedtexts_distance}%
+ \advance\d_strc_indentedtexts_width \d_strc_indentedtexts_distance
+ \setbox\scratchbox\hbox to \d_strc_indentedtexts_width
+ {\useindentedtextstyleandcolor\c!headstyle\c!headcolor
+ \strut
+ \indentedtextparameter\c!text
+ \hss
+ \spr{\indentedtextparameter\c!separator}%
+ \hskip\d_strc_indentedtexts_distance}%
+ \parindent\zeropoint
+ \hskip\c_strc_indentedtexts_nesting\d_strc_indentedtexts_width
+ \advance\c_strc_indentedtexts_nesting\plusone
+ \dontleavehmode\box\scratchbox
+ \hangindent\c_strc_indentedtexts_nesting\d_strc_indentedtexts_width
+ \useindentedtextstyleandcolor\c!style\c!color}
+
+\protected\def\strc_indentedtexts_stop
+ {\indentedtextparameter\c!after
+ \par
+ \endgroup}
+
+\permanent\tolerant\protected\def\startindentedtext[#1]%
+ {\strc_indentedtexts_start{#1}\c_strc_indentedtexts_nesting}
+
+\permanent\let\stopindentedtext\strc_indentedtexts_stop
+
+\protected\def\strc_indentedtexts_direct#1#2#3\par % no longer clever grabpar trickery
+ {\strc_indentedtexts_start{#1}{#2}#3\strc_indentedtexts_stop}
+
+\setupindentedtext
+ [\c!style=\v!normal,
+ \c!headstyle=\v!normal,
+ %\c!color=,
+ %\c!headcolor=,
+ \c!width=\v!fit,
+ \c!text=\unknown,
+ %\c!sample=,
+ \c!before=\blank,
+ \c!after=\blank,
+ \c!distance=1em,
+ \c!separator={ :}]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-ini.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-ini.mklx
new file mode 100644
index 00000000000..3897c1d2ac9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-ini.mklx
@@ -0,0 +1,81 @@
+%D \module
+%D [ file=strc-flt,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Initialization \& Helpers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Initialization & Helpers}
+
+\registerctxluafile{strc-ini}{}
+
+\unprotect
+
+% segments: 0:100 2:3 chapter:subsection 3 (=self+2) (alternative: sectionset)
+
+% section : [sectionnumber(s)]
+% sectionseparatorset (default) sectionconversionset (default) sectionstopper () sectionset sectionsegments
+
+% lists : [sectionnumber(s)] [text] [prefix(es)[separator][pagenumber(s)]
+% sectionseparatorset (default) sectionconversionset (default) sectionstopper sectionset sectionsegments
+% prefixseparatorset (default) prefixconversionset (default) prefixstopper (.) prefixset prefixsegments
+% pageseparatorset (default) pageconversionset (default) pagestopper () pagesegments
+% prefix (no)
+
+% counter : [prefix(es)[separator]][number(s)]
+% prefixseparatorset (default) prefixconversionset (default) prefixstopper (.) prefixset prefixsegments
+% numberseparatorset (default) numberconversionset (default) numberstopper () numbersegments
+% prefix (no)
+
+% pagenumber: [prefix(es)[separator]][pagenumber(s)]
+% prefixseparatorset (default) prefixconversionset (default) prefixstopper (.) prefixset prefixsegments
+% pageseparatorset (default) pageconversionset (default) pagestopper ()
+% prefix (no)
+
+% text mark reference list
+% section P P P P
+% float P.N P.N P.N
+% itemize P.N P.N
+% enumerate P.N P.N P.N
+% formula P.N P.N
+% footnote P.N P.N P.N
+% number P.N P.N
+
+% number prefix section page
+
+% [text|marking|reference|list]:[number|prefix|pagenumber|pageprefix]:[separatorset|conversionset|conversion|stopper|set|segments|resetset|order]
+
+% figure caption : text:number:* text:prefix:* -> \setupcaption[figure][...]; stores defaults
+% figure list : list:number:* list:prefix:* list:pagenumber:* list:pageprefix:* -> \setuplist[figure][...]; takes stored defaults for number and pagenumber]
+% figure reference: reference:number:* reference:prefix:* reference:pagenumber:* reference:pageprefix:* -> \setupreference[figure]]...]; takes stored defaults
+
+% This module deals with structure: section headers, list and numbering and
+% eventually cross referencing. These components are rather interwoven and
+% therefore an inbetween layer is used. Eventually this will replace the
+% corresponding code in core-sec, core-lst, core-num and core-ref.
+
+% We collect operations that deal with things like formatting on each level of a
+% number in sets. This is all handles at the \LUA\ end. References to such sets
+% travel with the multipass information.
+
+\permanent\tolerant\protected\def\defineresetset [#name]#spacer[#set]#spacer[#default]{\clf_definestructureset{structure:resets} {#name}{\detokenize{#set}}{\detokenize{#default}}true\relax}
+\permanent\tolerant\protected\def\defineseparatorset [#name]#spacer[#set]#spacer[#default]{\clf_definestructureset{structure:separators} {#name}{\detokenize{#set}}{\detokenize{#default}}false\relax}
+\permanent\tolerant\protected\def\defineconversionset[#name]#spacer[#set]#spacer[#default]{\clf_definestructureset{structure:conversions}{#name}{\detokenize{#set}}{\detokenize{#default}}false\relax}
+\permanent\tolerant\protected\def\defineprefixset [#name]#spacer[#set]#spacer[#default]{\clf_definestructureset{structure:prefixes} {#name}{\detokenize{#set}}{\detokenize{#default}}false\relax}
+
+\aliased\let\definestructureresetset \defineresetset
+\aliased\let\definestructureseparatorset \defineseparatorset
+\aliased\let\definestructureconversionset\defineconversionset
+\aliased\let\definestructureprefixset \defineprefixset
+
+% \defineseparatorset [weird][!,?,*][:] % tex content
+% \defineconversionset[weird][numbers,characters,romannumerals][numbers] % symbolic names
+% \defineresetset [weird][0,0,1][0] % numbers
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-itm.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-itm.mklx
new file mode 100644
index 00000000000..efead3d83a7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-itm.mklx
@@ -0,0 +1,1933 @@
+%D \module
+%D [ file=strc-itm,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Itemgroups,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Itemgroups}
+
+\registerctxluafile{strc-itm}{}
+
+%D As we analyze/register widths and such we could as well push and pop the numbers
+%D at the \LUA\ end (which saves a few calls).
+%D
+%D Cleaning up this module happened around the time when Kate Bush came up with the
+%D nicest numbered list of words: 50 Words For Snow. It's therefore no surprise that
+%D I had that cd running several times when updating this code. One of the
+%D highlights of 2011.
+%D
+%D This module needs to be rewritten but that is tricky with respect to
+%D compatibilitity. Basically each major variant (regular, text, columns,
+%D horizontal, etc) needs to be on its own.
+
+% todo: check breaks
+% todo: check grouping
+% todo: fixedconversion
+
+% \startitemize[n,packed]
+% \item test \item test \item test
+% \stopitemize
+%
+% \startitemize[n,packed,reverse]
+% \item test \item test \item test
+% \stopitemize
+%
+% \startitemize[n,packed,reverse] \item test \item test \stopitemize
+% \startitemize[continue]
+% \item test \startitemize[n,packed] \item test \item test \stopitemize
+% \item test
+% \item test
+% \stopitemize
+% \startitemize[continue] \item test \stopitemize
+%
+% \startitemize[n,packed] \item test \item test \stopitemize
+% \startitemize[continue] \item test \stopitemize
+% \startitemize[continue] \item test \stopitemize
+%
+% \setupwhitespace[big]
+% \starttext
+% test \startitemize[joinedup] \item test \item test \stopitemize test \par
+% test \startitemize[joinedup,nowhite] \item test \item test \stopitemize test \par
+% test \startitemize[joinedup,nowhite,before] \item test \item test \stopitemize test \par
+% test \startitemize[joinedup,nowhite,after] \item test \item test \stopitemize test \par
+% \stoptext
+%
+% test / example
+%
+% \startnarrower[left] \startcolumns[n=3] \startitemize
+% \item \input ward \item \input ward \item \input ward
+% \stopitemize \stopcolumns\stopnarrower \blank
+%
+% \startnarrower[left] \startitemize[columns,three]
+% \item \input ward \item \input ward \item \input ward
+% \stopitemize \stopnarrower \blank
+%
+% \setupitemize[leftmargin=1.5em] \startitemize[columns,three]
+% \item \input ward \item \input ward \item \input ward
+% \stopitemize \blank
+%
+% beware, we don't group (yet) as we want to keep the left/right skip
+%
+% \startitemize
+% \startitem \stopitem
+% some intermediate text that will properly indent
+% \startitem \stopitem
+% \stopitem
+%
+% so we need to keep that property
+%
+% \startitemize
+% \starthead {xx} test \stophead
+% \startitem test \stopitem
+% \startitem test \stopitem
+% \stopitemize
+%
+% Sometimes the user demands get pretty weird:
+%
+% \startitemize
+% \item test
+% \item test
+% \headsym{xx} test \par test
+% \stopitemize
+%
+% aligned items
+%
+% \startitemize[n,fit,broad][itemalign=flushright]
+% \dorecurse{100}{\item The first item.}
+% \stopitemize
+%
+% \setupitemgroup[itemize][each][fit]
+% \setupitemgroup[itemize][each][distance=.5em,factor=1,itemalign=flushright]
+%
+% \startitemize[n]
+% \dorecurse{100}{\item The first item.}
+% \stopitemize
+%
+% \defineitemgroup[gbitemize]
+% \setupitemgroup[gbitemize][each][headstyle=bold]
+%
+% \startgbitemize
+% \txt{italian} some italians like this kind of cross||breed between
+% an itemize and a description
+% \txt{sicilians} i wonder how many sicilian mathematicians do a thesis
+% on the math involved in predicting the next big bang of the vulcano
+% \stopgbitemize
+%
+% \startitemize[n,repeat]
+% \noitem \startitemize[a] \item Item 1.a. \item Item 1.b. \stopitemize
+% \noitem \startitemize[a] \item Item 2.a. \item Item 2.b. \stopitemize
+% \stopitemize
+%
+% \startitemize[n,repeat][width=0pt]
+% \noitem \startitemize[a][width=2em] \item Item 1.a. \item Item 1.b. \stopitemize
+% \noitem \startitemize[a][width=2em] \item Item 2.a. \item Item 2.b. \stopitemize
+% \stopitemize
+%
+% \startbuffer
+% \item
+% \startitemize[n]
+% \item item 1.1
+% \item item 1.2
+% \startitemize[n] \item item 1.2.1 \item item 1.2.2 \stopitemize
+% \item item 1.3
+% \stopitemize
+% \item
+% \startitemize[n] \item item 2.1 \item item 2.2 \stopitemize
+% \item item 3
+% \startitemize[n] \item item 3.1 \item item 3.2 \stopitemize
+% \item
+% \startitemize[n] \item item 4.1 \item item 4.2 \stopitemize
+% \stopbuffer
+%
+% \startitemize[n,repeat,6*broad,packed] \getbuffer \stopitemize \blank[3*big]
+% \startitemize[n,repeat,packed] \getbuffer \stopitemize \blank[3*big]
+% \setupitemize[each][atmargin][width=3em]
+% \startitemize[n,repeat,packed] \getbuffer \stopitemize
+%
+% todo: assume startitem ... stopitem and do an autostopitem .. cleaner for
+% elements
+%
+% \startitemize[text][space=medium]
+% \item one \item two \item three
+% \stopitemize
+%
+% For Giuseppe "Oblomov" Bilotta, inspired on a suggestion by Taco
+% Hoekwater.
+%
+% \def\MyItemCommand#1{{\bf#1}\quad}
+% \setupitemgroup[itemize][command=\MyItemCommand]
+%
+% \startitemize
+% \item {test} is this okay?
+% \item {test} is this okay?
+% \item {test} is this okay?
+% \stopitemize
+
+\unprotect
+
+\newconditional\c_strc_itemgroups_sub
+\newconditional\c_strc_itemgroups_head
+\newconditional\c_strc_itemgroups_intro
+\newconditional\c_strc_itemgroups_randomize
+\newconditional\c_strc_itemgroups_horizontal
+\newconditional\c_strc_itemgroups_collecting
+\newconditional\c_strc_itemgroups_auto_intro
+\newconditional\c_strc_itemgroups_pack
+\newconditional\c_strc_itemgroups_paragraph
+\newconditional\c_strc_itemgroups_text
+\newconditional\c_strc_itemgroups_text_saved
+\newconditional\c_strc_itemgroups_first
+\newconditional\c_strc_itemgroups_before
+\newconditional\c_strc_itemgroups_after
+\newconditional\c_strc_itemgroups_nowhite
+\newconditional\c_strc_itemgroups_joined
+\newconditional\c_strc_itemgroups_reverse
+\newconditional\c_strc_itemgroups_continue
+\newconditional\c_strc_itemgroups_fitting
+\newconditional\c_strc_itemgroups_indented_first
+\newconditional\c_strc_itemgroups_inline
+\newconditional\c_strc_itemgroups_columns
+\newconditional\c_strc_itemgroups_concat
+\newconditional\c_strc_itemgroups_txt
+\newconditional\c_strc_itemgroups_extra
+\newconditional\c_strc_itemgroups_repeat
+\newconditional\c_strc_itemgroups_inline_head
+\newconditional\c_strc_itemgroups_symbol
+
+% 0 = before/after
+% 1 = between unless before
+% 2 = between
+
+\newconstant \c_strc_itemgroups_spacing_mode \c_strc_itemgroups_spacing_mode\plustwo
+\newconditional\c_strc_itemgroups_optimize \settrue\c_strc_itemgroups_optimize
+\newconditional\c_strc_itemgroups_auto_concat \settrue\c_strc_itemgroups_auto_concat
+
+\newsignal \d_strc_itemgroups_signal
+
+\newbox \b_strc_itemgroups
+
+\newdimen \d_strc_itemgroups_list_width
+\newdimen \d_strc_itemgroups_asked_width
+
+\newdimen \d_strc_itemgroups_max_width % multipass
+\newcount \c_strc_itemgroups_max_items % multipass
+
+\newcount \c_strc_itemgroups_n_of_items
+\newcount \c_strc_itemgroups_nesting
+\newcount \c_strc_itemgroups_column_depth
+
+\def \v_strc_itemgroups_counter {itemgroup:\currentparentitemgroup}
+\let \m_strc_itemgroups_repeat_start \empty
+\def \v_strc_itemgroups_unknown_symbol {?}
+\let \m_strc_itemgroups_indenting \empty
+\let \m_strc_itemgroups_destination \empty
+
+\mutable\let \currentitemlevel \!!zerocount % public
+\mutable\def \currentnofitems {\the\c_strc_itemgroups_max_items}
+\mutable\def \currentitemnumber {\strc_counters_raw_sub\v_strc_itemgroups_counter\currentitemlevel} % public
+
+\newtoks \itemgroupcommands % maybe public
+
+\mutable\def \currentitemgroupsymbol {n} % here we cannot use a _ in the name
+\mutable\let \currentitemgroupconversionset \empty % here we cannot use a _ in the name
+\mutable\let \currentitemgroupsegments \empty
+
+\def\strc_itemgroups_register_status
+ {\clf_registeritemgroup{\currentparentitemgroup}\c_strc_itemgroups_nesting\c_strc_itemgroups_n_of_items\dimexpr\itemgroupparameter\c!maxwidth\relax}
+
+\def\strc_itemgroups_check_n_of_items % we could do this at the lua end and save a call (i.e. will be dimen and counter)
+ {\clf_analyzeitemgroup{\currentparentitemgroup}\c_strc_itemgroups_nesting\relax
+ \edef\currentnofitems{\the\c_strc_itemgroups_max_items}}
+
+% todo: \dodosetreference -> \strc_counters_register_component (to be checked)
+
+\def\strc_itemgroups_insert_reference % we will make a decent number helper
+ {\iftrialtypesetting \orelse \ifempty\currentitemreference \else
+ \strc_itemgroups_insert_reference_indeed
+ \fi}
+
+\def\strc_itemgroups_insert_extra_reference
+ {\iftrialtypesetting \orelse \ifempty\currentitemreference \else
+ \normalexpanded{\textreference[\currentitemreference]{\strc_itemgroups_extra_symbol}}%
+ \fi}
+
+\def\strc_itemgroups_insert_reference_indeed % maybe we need a 'frozen counter' numberdata blob / quick hack .. .mive this to strc-ref
+ {% needs testing, gave problems:
+ \setnextinternalreference
+ % no need to collect nodes in \b_strc_destination_nodes here ... maybe at some point
+ \strc_references_start_destination_nodes
+ % this is somewhat over the top ... we should use the counter's reference
+ \clf_setdestinationattribute
+ {%
+ metadata {%
+ kind {item}% was item, why?
+ \ifx\currentreferencecoding\s!xml
+ xmlroot {\xmldocument}% only useful when text
+ \fi
+ catcodes \catcodetable
+ }%
+ references {%
+ internal \locationcount % no: this spoils references
+ % block {\currentsectionblock}%
+ view {\interactionparameter\c!focus}%
+ prefix {\referenceprefix}%
+ reference {\currentitemreference}%
+ }%
+ prefixdata {%
+ prefix {\namedcounterparameter\v_strc_itemgroups_counter\c!prefix}%
+ separatorset {\namedcounterparameter\v_strc_itemgroups_counter\c!prefixseparatorset}%
+ conversion {\namedcounterparameter\v_strc_itemgroups_counter\c!prefixconversion}%
+ conversionset {\namedcounterparameter\v_strc_itemgroups_counter\c!prefixconversionset}%
+ set {\namedcounterparameter\v_strc_itemgroups_counter\c!prefixset}%
+ segments {\namedcounterparameter\v_strc_itemgroups_counter\c!prefixsegments}%
+ % segments {\askedprefixsegments}%
+ connector {\namedcounterparameter\v_strc_itemgroups_counter\c!prefixconnector}%
+ }%
+ numberdata {%
+ numbers {\v_strc_itemgroups_counter}%
+ separatorset {\namedcounterparameter\v_strc_itemgroups_counter\c!numberseparatorset}%
+ % conversion {\namedcounterparameter\v_strc_itemgroups_counter\c!numberconversion}%
+ % conversionset {\namedcounterparameter\v_strc_itemgroups_counter\c!numberconversionset}%
+ % fixedconversion {\currentitemgroupconversionset}%
+ conversionset {fixed::\currentitemgroupconversionset}% temp hack
+ %
+ % for the moment no stopper, we need to make references configurable first
+ % stopper {\namedcounterparameter\v_strc_itemgroups_counter\c!numberstopper}%
+ segments {\namedcounterparameter\v_strc_itemgroups_counter\c!numbersegments}%
+ }%
+ }%
+ \relax
+ \strc_references_stop_destination_nodes
+ \xdef\currentdestinationattribute{\number\lastdestinationattribute}%
+ % will become an option:
+ \ifnum\lastdestinationattribute>\zerocount
+ \dontleavehmode\hbox attr \destinationattribute\lastdestinationattribute\bgroup
+ \strc_references_flush_destination_nodes
+ \egroup
+ \fi}
+
+%D Defining and setup:
+
+\installcorenamespace{itemgroup}
+\installcorenamespace{itemgroupoption}
+\installcorenamespace{itemgroupsetting}
+\installcorenamespace{itemgroupkeyword}
+\installcorenamespace{itemgroupalign}
+\installcorenamespace{itemgrouplocal}
+\installcorenamespace{itemgroupglobal}
+\installcorenamespace{itemgroupdistance}
+\installcorenamespace{itemgroupstack}
+\installcorenamespace{itemgroupfirst}
+\installcorenamespace{itemgroupstart}
+
+\installcommandhandler \??itemgroup {itemgroup} \??itemgroup
+
+\aliased\let\setupitemgroups\setupitemgroup
+
+\appendtoks
+ \frozen\instance\setuevalue{\e!start\currentitemgroup}{\startitemgroup[\currentitemgroup]}%
+ \frozen\instance\setuevalue{\e!stop \currentitemgroup}{\stopitemgroup}%
+ \frozen\instance\setuevalue{\e!setup\currentitemgroup\e!endsetup}{\setupitemgroup[\currentitemgroup]}% obsolete
+ \let\currentparentitemgroup\currentitemgroup
+ \definecounter[\v_strc_itemgroups_counter]%
+\to \everydefineitemgroup
+
+%D Global states
+
+\def\strc_itemgroups_store_continue_state#options#settings%
+ {\xdefcsname\??itemgroupoption \currentitemgroup\endcsname{\strc_itemgroups_process_options{#options}}%
+ \gdefcsname\??itemgroupsetting\currentitemgroup\endcsname{\setupcurrentitemgroup[#settings]}}
+
+\def\strc_itemgroups_fetch_continue_state
+ {\csname\??itemgroupoption \currentitemgroup\endcsname
+ \csname\??itemgroupsetting\currentitemgroup\endcsname}
+
+\def\strc_itemgroups_reset_continue_state
+ {\gletcsname\??itemgroupoption \currentitemgroup\endcsname\relax
+ \gletcsname\??itemgroupsetting\currentitemgroup\endcsname\relax}
+
+% These will become keywords. We will also add a feature to keep the while set
+% together.
+
+\definevspacing[\v!item @0] [penalty:0] % allow
+\definevspacing[\v!item @10000] [penalty:10000] % no
+\definevspacing[\v!item @-5] [penalty:-5]
+\definevspacing[\v!item @5] [penalty:5]
+\definevspacing[\v!item @500] [penalty:500] % discourage .. too low, 5000 is better
+
+\def\strc_itemgroups_insert_break_when_needed#break%
+ {\ifconditional\c_strc_itemgroups_optimize
+ \ifconditional\c_strc_itemgroups_text \else
+ #break\relax
+ \fi
+ \fi}
+
+\def\strc_itemgroups_insert_breakallow {\strc_itemgroups_insert_break_when_needed\strc_itemgroups_insert_breakallow_indeed}
+\def\strc_itemgroups_insert_breakno {\strc_itemgroups_insert_break_when_needed\strc_itemgroups_insert_breakno_indeed }
+\def\strc_itemgroups_insert_break {\strc_itemgroups_insert_break_when_needed\strc_itemgroups_insert_break_indeed }
+\def\strc_itemgroups_insert_nobreak {\strc_itemgroups_insert_break_when_needed\strc_itemgroups_insert_nobreak_indeed }
+
+\def\strc_itemgroups_insert_breakallow_indeed{\vspacing[\v!item @0]}
+\def\strc_itemgroups_insert_breakno_indeed {\vspacing[\v!item @10000]}
+\def\strc_itemgroups_insert_break_indeed {\flushnotes
+ \vspacing[\v!item @-5]}
+\def\strc_itemgroups_insert_nobreak_indeed {\flushnotes
+ \ifinsidecolumns % todo
+ \vspacing[\v!item @5]%
+ \else
+ \vspacing[\v!item @500]%
+ \fi}
+
+\protected\def\strc_itemgroups_process_options#options%
+ {\processcommacommand[#options]\strc_itemgroups_process_option} % expansion of options is handy for xml
+
+% \installcommalistprocessorcommand \strc_itemgroups_process_option_list \strc_itemgroups_process_option
+%
+% \protected\def\strc_itemgroups_process_options#options%
+% {\normalexpanded{\strc_itemgroups_process_option_list[#options]}} % expansion of options is handy for xml
+
+\mutable\let\itemgroupfirst \empty
+\mutable\let\itemgroupsecond\empty
+
+\def\strc_itemgroups_process_option#option%
+ {\edef\scratchstringone{#option}%
+ \ifempty\scratchstringone\else
+ \splitatasterisk\scratchstringone\itemgroupfirst\itemgroupsecond
+ \ifempty\itemgroupsecond
+ \let\itemgroupsecond\itemgroupfirst
+ \let\itemgroupfirst\!!plusone
+ \fi
+ \ifcsname\??itemgroupkeyword\itemgroupsecond\endcsname
+ \lastnamedcs
+ \else
+ \strc_itemgroups_set_symbol\scratchstringone
+ \fi
+ \fi}
+
+\def\strc_itemgroups_process_set_option_pack
+ {\ifcase\c_strc_itemgroups_nesting \else
+ \settrue\c_strc_itemgroups_pack
+ \fi}
+
+\def\strc_itemgroups_process_set_option_unpack
+ {\ifcase\c_strc_itemgroups_nesting\else
+ \setfalse\c_strc_itemgroups_pack
+ \fi}
+
+\setvalue{\??itemgroupkeyword\!!zerocount }{} % ignore 0
+\setvalue{\??itemgroupkeyword\v!packed }{\strc_itemgroups_process_set_option_pack}
+\setvalue{\??itemgroupkeyword\v!unpacked }{\strc_itemgroups_process_set_option_unpack}
+\setvalue{\??itemgroupkeyword\v!intro }{\settrue\c_strc_itemgroups_intro} % here? not set to false
+\setvalue{\??itemgroupkeyword\v!autointro }{\settrue\c_strc_itemgroups_auto_intro}
+\setvalue{\??itemgroupkeyword\v!broad }{\ifempty\itemgroupfirst
+ \let\itemgroupfirst\!!plusone
+ \fi
+ \letitemgroupparameter\c!factor\itemgroupfirst}
+\setvalue{\??itemgroupkeyword\v!text }{\settrue\c_strc_itemgroups_text
+ \settrue\c_strc_itemgroups_inline
+ \settrue\c_strc_itemgroups_joined
+ \strc_itemgroups_process_set_option_pack}
+\setvalue{\??itemgroupkeyword\v!before }{\settrue\c_strc_itemgroups_before}
+\setvalue{\??itemgroupkeyword\v!after }{\settrue\c_strc_itemgroups_after}
+\setvalue{\??itemgroupkeyword\v!nowhite }{\settrue\c_strc_itemgroups_nowhite}
+\setvalue{\??itemgroupkeyword\v!margin }{\setitemgroupparameter\c!width{-2em}} % signal
+\setvalue{\??itemgroupkeyword\v!inmargin }{\setitemgroupparameter\c!width{-2em}} % signal
+\setvalue{\??itemgroupkeyword\v!atmargin }{\ifnum\c_strc_itemgroups_nesting>\plusone
+ \setitemgroupparameter\c!width{0em}%
+ \fi} % signal
+\setvalue{\??itemgroupkeyword\v!intext }{\settrue\c_strc_itemgroups_inline}
+\setvalue{\??itemgroupkeyword\v!headintext }{\settrue\c_strc_itemgroups_inline_head}
+\setvalue{\??itemgroupkeyword\v!loose }{\setfalse\c_strc_itemgroups_optimize}
+\setvalue{\??itemgroupkeyword\v!fit }{\settrue\c_strc_itemgroups_fitting}
+\setvalue{\??itemgroupkeyword\v!nofit }{\setfalse\c_strc_itemgroups_fitting}
+\setvalue{\??itemgroupkeyword\v!paragraph }{\settrue\c_strc_itemgroups_paragraph
+ \strc_itemgroups_process_set_option_pack}
+\setvalue{\??itemgroupkeyword\v!joinedup }{\settrue\c_strc_itemgroups_joined
+ \strc_itemgroups_process_set_option_pack}
+\setvalue{\??itemgroupkeyword\v!notjoinedup}{\setfalse\c_strc_itemgroups_joined}
+\setvalue{\??itemgroupkeyword\v!serried }{\edef\itemgroupfirst{-\ifempty\itemgroupfirst1\else\itemgroupfirst\fi}%
+ \letitemgroupparameter\c!factor\itemgroupfirst}
+\setvalue{\??itemgroupkeyword\v!stopper }{\letitemgroupparameter\c!placestopper\v!yes} % keep {}
+\setvalue{\??itemgroupkeyword\v!nostopper }{\letitemgroupparameter\c!placestopper\v!no} % keep {}
+\setvalue{\??itemgroupkeyword\v!repeat }{\settrue\c_strc_itemgroups_repeat}
+\setvalue{\??itemgroupkeyword\v!norepeat }{\setfalse\c_strc_itemgroups_repeat}
+\setvalue{\??itemgroupkeyword\v!reverse }{\settrue\c_strc_itemgroups_reverse}
+\setvalue{\??itemgroupkeyword\v!columns }{\settrue\c_strc_itemgroups_columns}
+\setvalue{\??itemgroupkeyword\v!one }{\letitemgroupparameter\c!n\plusone}
+\setvalue{\??itemgroupkeyword\v!two }{\letitemgroupparameter\c!n\plustwo}
+\setvalue{\??itemgroupkeyword\v!three }{\letitemgroupparameter\c!n\plusthree}
+\setvalue{\??itemgroupkeyword\v!four }{\letitemgroupparameter\c!n\plusfour}
+\setvalue{\??itemgroupkeyword\v!five }{\letitemgroupparameter\c!n\plusfive}
+\setvalue{\??itemgroupkeyword\v!six }{\letitemgroupparameter\c!n\plussix}
+\setvalue{\??itemgroupkeyword\v!seven }{\letitemgroupparameter\c!n\plusseven}
+\setvalue{\??itemgroupkeyword\v!eight }{\letitemgroupparameter\c!n\pluseight}
+\setvalue{\??itemgroupkeyword\v!nine }{\letitemgroupparameter\c!n\plusnine}
+\setvalue{\??itemgroupkeyword\v!standard }{\setitemgroupparameter\c!width {1.5\emwidth}%
+ \setitemgroupparameter\c!distance {.5\emwidth}%
+ %\letitemgroupparameter\c!factor \!!zerocount
+ \letitemgroupparameter\c!factor {\zerocount}%
+ \setitemgroupparameter\c!packcriterium{\zerocount}%
+ \letitemgroupparameter\c!inner \empty
+ \letitemgroupparameter\c!beforehead \empty
+ \letitemgroupparameter\c!afterhead \blank
+ \letitemgroupparameter\c!before \blank
+ \letitemgroupparameter\c!inbetween \blank
+ \letitemgroupparameter\c!after \blank}
+
+\def\strc_itemgroups_initialize_local
+ {\setfalse\c_strc_itemgroups_inline
+ \setfalse\c_strc_itemgroups_concat
+ \setfalse\c_strc_itemgroups_txt
+ %
+ \setfalse\c_strc_itemgroups_reverse
+ \setfalse\c_strc_itemgroups_intro
+ \setfalse\c_strc_itemgroups_auto_intro
+ \setfalse\c_strc_itemgroups_before
+ \setfalse\c_strc_itemgroups_after
+ \setfalse\c_strc_itemgroups_nowhite
+ \setfalse\c_strc_itemgroups_randomize
+ \setfalse\c_strc_itemgroups_horizontal
+ \setfalse\c_strc_itemgroups_collecting
+ \setfalse\c_strc_itemgroups_intro
+ \setfalse\c_strc_itemgroups_continue
+ % this will be a constant
+ \setfalse\c_strc_itemgroups_head
+ \setfalse\c_strc_itemgroups_sub
+ \setfalse\c_strc_itemgroups_symbol
+ \setfalse\c_strc_itemgroups_columns
+ % to be checked
+ \let\m_strc_itemgroups_destination\empty
+ \let\strc_itemgroups_used_symbol \empty % ** start value
+ \let\strc_itemgroups_margin_symbol\empty
+ \let\strc_itemgroups_extra_symbol \empty
+ %
+ \global\letitemgroupparameter\c!maxwidth\!!zeropoint
+ }
+
+\setvalue{\??itemgroupfirst\v!intro }{\settrue\c_strc_itemgroups_intro}
+\setvalue{\??itemgroupfirst\v!continue }{\settrue\c_strc_itemgroups_continue}
+\setvalue{\??itemgroupfirst\v!random }{\settrue\c_strc_itemgroups_randomize
+ \settrue\c_strc_itemgroups_collecting}
+\setvalue{\??itemgroupkeyword\v!horizontal}{\settrue\c_strc_itemgroups_horizontal
+ \settrue\c_strc_itemgroups_collecting
+ \settrue\c_strc_itemgroups_inline
+ \settrue\c_strc_itemgroups_joined
+ \strc_itemgroups_process_set_option_pack}
+
+\def\strc_itemgroups_preset_stage_one#options%
+ {\processcommacommand[#options]\strc_itemgroups_preset_stage_one_indeed}
+
+% \installcommalistprocessorcommand \strc_itemgroups_preset_stage_one_list \strc_itemgroups_preset_stage_one_indeed
+%
+% \def\strc_itemgroups_preset_stage_one#options%
+% {\normalexpanded{\strc_itemgroups_preset_stage_one_list[#options]}}
+
+\def\strc_itemgroups_preset_stage_one_indeed#option%
+ {\ifcsname\??itemgroupfirst#option\endcsname
+ \lastnamedcs
+ \fi}
+
+\ifdefined\dotagsetitemgroup \else \aliased\let\dotagsetitemgroup\relax \fi
+\ifdefined\dotagsetitem \else \aliased\let\dotagsetitem \gobbleoneargument \fi
+
+\def\strc_itemgroups_tag_start_group
+ {\dostarttaggedchained\t!itemgroup\currentparentitemgroup\??itemgroup
+ \dotagsetitemgroup}
+
+\def\strc_itemgroups_tag_stop_group
+ {\dostoptagged}
+
+\def\strc_itemgroups_before_command
+ {\ifconditional\c_strc_itemgroups_nowhite
+ \ifconditional\c_strc_itemgroups_before
+ \ifcase\c_strc_itemgroups_nesting\or\itemgroupparameter\c!before\fi
+ \else
+ \nowhitespace
+ \fi
+ \orelse\ifconditional\c_strc_itemgroups_joined
+ % \empty
+ \else
+ \itemgroupparameter\c!before
+ \fi}
+
+\def\strc_itemgroups_after_command
+ {\ifconditional\c_strc_itemgroups_nowhite
+ \ifconditional\c_strc_itemgroups_after
+ \ifcase\c_strc_itemgroups_nesting\or\itemgroupparameter\c!after\fi
+ \else
+ \nowhitespace
+ \fi
+ \orelse\ifconditional\c_strc_itemgroups_joined
+ % \empty
+ \else
+ \itemgroupparameter\c!after
+ \fi}
+
+\def\strc_itemgroups_between_command
+ {\ifconditional\c_strc_itemgroups_nowhite
+ \nowhitespace
+ \orelse\ifconditional\c_strc_itemgroups_joined
+ % \empty
+ \else
+ \itemgroupparameter\c!inbetween
+ \fi}
+
+\def\strc_itemgroups_before_head_command
+ {\ifconditional\c_strc_itemgroups_nowhite
+ \nowhitespace
+ \orelse\ifconditional\c_strc_itemgroups_joined
+ % \empty
+ \else
+ \itemgroupparameter\c!beforehead
+ \fi}
+
+\def\strc_itemgroups_after_head_command
+ {\ifconditional\c_strc_itemgroups_nowhite
+ \nowhitespace
+ \orelse\ifconditional\c_strc_itemgroups_joined
+ % \empty
+ \else
+ \itemgroupparameter\c!afterhead
+ \fi}
+
+% compatible setup command:
+
+\let\strc_itemgroups_normal_setup\setupitemgroup
+
+\permanent\overloaded\tolerant\protected\def\setupitemgroup[#category]#spacer[#levels]#spacer[#options]#spacer[#settings]% category level|each options|settings settings|options
+ {\ifarguments
+ % nothing to set up
+ \or
+ \edef\strc_itemgroups_setup_what{#levels}%
+ \strc_itemgroups_normal_setup[#category]% == settings
+ \or
+ \edef\strc_itemgroups_setup_what{#levels}%
+ \strc_itemgroups_setup_each{#category}{#levels}%
+ \or
+ \edef\strc_itemgroups_setup_what{#levels}%
+ \ifx\strc_itemgroups_setup_what\v!each
+ \strc_itemgroups_setup_each{#category}{#options}%
+ \else
+ \strc_itemgroups_setup_list{#levels}{#category}{#options}%
+ \fi
+ \or
+ \edef\strc_itemgroups_setup_what{#levels}%
+ \ifx\strc_itemgroups_setup_what\v!each
+ \strc_itemgroups_setup_each{#category}{#options}%
+ \strc_itemgroups_setup_each{#category}{#settings}%
+ \else
+ \strc_itemgroups_setup_list{#levels}{#category}{#options}%
+ \strc_itemgroups_setup_list{#levels}{#category}{#settings}%
+ \fi
+ \fi}
+
+% can be made a bit faster
+
+% \def\strc_itemgroups_setup_each#category#whatever%
+% {\doifelseassignment{#whatever}
+% {\strc_itemgroups_normal_setup[#category][#whatever]}
+% {\strc_itemgroups_normal_setup[#category][\c!option={#whatever}]}}
+
+\def\strc_itemgroups_setup_each#category#whatever%
+ {\ifcondition\validassignment{#whatever}%
+ \strc_itemgroups_normal_setup[#category][#whatever]%
+ \else
+ \strc_itemgroups_normal_setup[#category][\c!option={#whatever}]%
+ \fi}
+
+\def\strc_itemgroups_setup_list_level_a#category#whatever#level%
+ {\strc_itemgroups_normal_setup[#category:#level][#whatever]}
+
+\def\strc_itemgroups_setup_list_level_b#category#whatever#level%
+ {\strc_itemgroups_normal_setup[#category:#level][\c!option={#whatever}]}
+
+% \def\strc_itemgroups_setup_list#subcategories#category#whatever%
+% {\doifelseassignment{#whatever}
+% {\processcommalist[#subcategories]{\strc_itemgroups_setup_list_level_a{#category}{#whatever}}}
+% {\processcommalist[#subcategories]{\strc_itemgroups_setup_list_level_b{#category}{#whatever}}}}
+
+\def\strc_itemgroups_setup_list#subcategories#category#whatever%
+ {\ifcondition\validassignment{#whatever}%
+ \processcommalist[#subcategories]{\strc_itemgroups_setup_list_level_a{#category}{#whatever}}%
+ \else
+ \processcommalist[#subcategories]{\strc_itemgroups_setup_list_level_b{#category}{#whatever}}%
+ \fi}
+
+\def\strc_itemgroups_increment_item_counter
+ {\ifconditional\c_strc_itemgroups_sub \else
+ \ifconditional\c_strc_itemgroups_symbol \else % \ifempty\strc_itemgroups_extra_symbol
+ \strc_counters_increment_sub\v_strc_itemgroups_counter\currentitemlevel
+ \fi
+ \fi}
+
+\protected\def\strc_itemgroups_insert_item_counter
+ {\ifconditional\c_strc_itemgroups_repeat
+ \ifcase\c_strc_itemgroups_nesting
+ % skip
+ \or
+ \strc_itemgroups_insert_item_counter_indeed % this could become an option
+ \else
+ \strc_itemgroups_insert_item_counter_indeed
+ \fi
+ \else
+ \strc_itemgroups_insert_item_counter_indeed
+ \fi}
+
+\installcorenamespace{itemstopper}
+
+\setvalue{\??itemstopper\v!yes }{\itemgroupparameter\c!stopper}
+\setvalue{\??itemstopper\v!display}{\ifconditional\c_strc_itemgroups_inline\else\itemgroupparameter\c!stopper\fi}
+\setvalue{\??itemstopper\v!inline }{\ifconditional\c_strc_itemgroups_inline\itemgroupparameter\c!stopper\fi}
+
+\def\strc_itemgroups_insert_item_counter_indeed % quite slow ... every time this setup .. but it
+ {\begingroup % can be optimized ... best move some to strc-num
+ \setupcounter
+ [\v_strc_itemgroups_counter]%
+ [\c!prefix=\itemgroupparameter\c!prefix,
+ \c!prefixstopper=\itemgroupparameter\c!prefixstopper,
+ \c!prefixseparatorset=\itemgroupparameter\c!prefixseparatorset,
+ \c!prefixconversion=\itemgroupparameter\c!prefixconversion,
+ \c!prefixconversionset=\itemgroupparameter\c!prefixseparatorset,
+ \c!prefixset=\itemgroupparameter\c!prefixset,
+ \c!prefixsegments=\itemgroupparameter\c!prefixsegments,
+ \c!prefixconnector=\itemgroupparameter\c!prefixconnector,
+ \c!criterium=\itemgroupparameter\c!criterium,
+ \c!numberorder=\ifconditional\c_strc_itemgroups_reverse\v!reverse\else\v!normal\fi,
+ \c!numberstopper=\begincsname\??itemstopper\itemgroupparameter\c!placestopper\endcsname,
+ \c!numberconversionset=\v_strc_itemgroups_counter,
+ \c!numbersegments=\currentitemgroupsegments]%
+ % there will be a fixedconversion key
+ \normalexpanded{\defineconversionset[\v_strc_itemgroups_counter][\currentitemgroupconversionset][\currentitemgroupsymbol]}%
+ %\normalexpanded{\strc_sets_define_conversion_set[\v_strc_itemgroups_counter][\currentitemgroupconversionset][\currentitemgroupsymbol]}%
+ %
+ \convertedcounter[\v_strc_itemgroups_counter]% [\number\currentitemlevel]%
+ \strc_itemgroups_insert_reference
+ \endgroup}
+
+% needs testing (maybe it need to happen later)
+%
+% \appendtoks
+% \setupcounter
+% [\v_strc_itemgroups_counter]%
+% [\c!prefix=\itemgroupparameter\c!prefix,
+% \c!prefixstopper=\itemgroupparameter\c!prefixstopper,
+% \c!prefixseparatorset=\itemgroupparameter\c!prefixseparatorset,
+% \c!prefixconversion=\itemgroupparameter\c!prefixconversion,
+% \c!prefixconversionset=\itemgroupparameter\c!prefixseparatorset,
+% \c!prefixset=\itemgroupparameter\c!prefixset,
+% \c!prefixsegments=\itemgroupparameter\c!prefixsegments,
+% \c!prefixconnector=\itemgroupparameter\c!prefixconnector,
+% \c!criterium=\itemgroupparameter\c!criterium,
+% \c!numberorder=\ifconditional\c_strc_itemgroups_reverse\v!reverse\else\v!normal\fi,
+% \c!numberstopper=\expdoif{\itemgroupparameter\c!placestopper}\v!yes{\itemgroupparameter\c!stopper},
+% \c!numberconversionset=\v_strc_itemgroups_counter,
+% \c!numbersegments=\currentitemgroupsegments]%
+% \to\itemgroupcommands
+%
+% \def\strc_itemgroups_insert_item_counter_indeed % quite slow ... every time this setup .. but it
+% {\begingroup % can be optimized ... best move some to strc-num
+% % there will be a fixedconversion key
+% %\normalexpanded{\defineconversionset[\v_strc_itemgroups_counter][\currentitemgroupconversionset][\currentitemgroupsymbol]}%
+% \normalexpanded{\strc_sets_define_conversion_set[\v_strc_itemgroups_counter][\currentitemgroupconversionset][\currentitemgroupsymbol]}%
+% %
+% \convertedcounter[\v_strc_itemgroups_counter]% [\number\currentitemlevel]%
+% \strc_itemgroups_insert_reference
+% \endgroup}
+
+\let\strc_itemgroups_temp_symbol \empty
+\let\strc_itemgroups_used_symbol \empty
+\let\strc_itemgroups_asked_symbol \empty
+\let\strc_itemgroups_margin_symbol\empty
+\let\strc_itemgroups_extra_symbol \empty
+
+\def\strc_itemgroups_set_symbol#symbol%
+ {\edef\strc_itemgroups_temp_symbol{#symbol}%
+ \ifempty\strc_itemgroups_temp_symbol \else
+ \doifelsesymboldefined\strc_itemgroups_temp_symbol
+ \strc_itemgroups_set_symbol_symbol
+ {\doifelseconversiondefined\strc_itemgroups_temp_symbol
+ \strc_itemgroups_set_symbol_conversion
+ \donothing}%
+ \fi}
+
+\def\strc_itemgroups_set_symbol_symbol
+ {\let\currentitemgroupsymbol\strc_itemgroups_temp_symbol
+ \strc_itemgroups_store_global_symbol\currentitemgroupsymbol
+ \strc_itemgroups_store_local_symbol\v_strc_itemgroups_unknown_symbol
+ \let\strc_itemgroups_used_symbol\strc_itemgroups_set_symbol_symbol_item
+ \let\strc_itemgroups_asked_symbol\empty}
+
+\def\strc_itemgroups_set_symbol_conversion
+ {\let\currentitemgroupsymbol\strc_itemgroups_temp_symbol
+ \strc_itemgroups_store_global_symbol\currentitemgroupsymbol
+ \strc_itemgroups_store_local_symbol\strc_itemgroups_insert_item_counter
+ \let\strc_itemgroups_used_symbol\strc_itemgroups_set_symbol_conversion_item
+ \let\strc_itemgroups_asked_symbol\empty}
+
+\def\strc_itemgroups_set_symbol_symbol_item
+ {\symbol[\currentitemgroupsymbol]}
+
+\def\strc_itemgroups_set_symbol_conversion_item
+ {\ifconditional\c_strc_itemgroups_text
+ % maybe block stopper here, but one can as well clone an itemgroup then
+ \itemgroupparameter\c!lefttext
+ \strc_itemgroups_fetch_local_symbol
+ \itemgroupparameter\c!righttext
+ \else
+ \itemgroupparameter\c!left
+ \strc_itemgroups_fetch_local_symbol
+ \itemgroupparameter\c!right
+ \fi}
+
+\def\strc_itemgroups_calculate_list_width#level%
+ {\let\m_strc_itemgroups_saved\currentitemgroup
+ \edef\currentitemgroup{\currentparentitemgroup:\number#level}%
+ \ifdim\d_strc_itemgroups_max_width>\zeropoint
+ \d_strc_itemgroups_list_width\d_strc_itemgroups_max_width
+ \orelse\ifnum\itemgroupparameter\c!factor>\zerocount
+ \d_strc_itemgroups_list_width\itemgroupparameter\c!step\relax
+ \ifzeropt\d_strc_itemgroups_list_width \d_strc_itemgroups_list_width=.5em\fi
+ \multiply\d_strc_itemgroups_list_width \itemgroupparameter\c!factor
+ \advance\d_strc_itemgroups_list_width\itemgroupparameter\c!width\relax
+ \else
+ \d_strc_itemgroups_list_width\itemgroupparameter\c!width\relax
+ \fi
+ \advance\d_strc_itemgroups_list_width\itemgroupparameter\c!distance\relax
+ \let\currentitemgroup\m_strc_itemgroups_saved}
+
+\let\p_itemgroups_options \empty
+\let\p_itemgroups_extra_options\empty
+
+\permanent\tolerant\protected\def\startitemgroup[#category]#spacer[#options]#spacer[#settings]%
+ {\begingroup % (1)
+ \nofarguments\lastarguments
+ \def\currentitemgroup{#category}% no nested mixing of itemgroups
+ \let\currentparentitemgroup\currentitemgroup
+ \global\advance\c_strc_itemgroups_nesting\plusone
+ \def\currentitemlevel{\number\c_strc_itemgroups_nesting}%
+ \normalexpanded{\chaintocurrentitemgroup{\currentparentitemgroup:\currentitemlevel}}%
+ \edef\currentitemgroup{\currentparentitemgroup:\currentitemlevel}%
+ %
+ \edef\p_itemgroups_options{\itemgroupparameter\c!option}%
+ \let\p_itemgroups_extra_options\empty
+ \ifcase\nofarguments
+ \strc_itemgroups_start_indeed{}%
+ \or
+ \strc_itemgroups_start_indeed{}%
+ \or
+ \ifhastok={#options}%
+ \strc_itemgroups_start_indeed{#options}%
+ \else
+ \edef\p_itemgroups_extra_options{#options}%
+ \strc_itemgroups_start_indeed{}%
+ \fi
+ \or
+ \edef\p_itemgroups_extra_options{#options}%
+ \strc_itemgroups_start_indeed{#settings}%
+ \fi}
+
+\def\strc_itemgroups_start_indeed#settings%
+ {\ifempty\p_itemgroups_options
+ \let\p_itemgroups_options\p_itemgroups_extra_options
+ \orelse\ifempty\p_itemgroups_extra_options
+ % ok
+ \else
+ \edef\p_itemgroups_options{\p_itemgroups_options,\p_itemgroups_extra_options}%
+ \fi
+ \begingroup % (2)
+ \the\itemgroupcommands
+ \setconditional\c_strc_itemgroups_text_saved\c_strc_itemgroups_text
+ \strc_itemgroups_initialize_local
+ \strc_itemgroups_preset_stage_one\p_itemgroups_options
+%
+% \ifhmode
+% \ifconditional\c_strc_itemgroups_auto_concat
+% \ifdim\lastskip=\d_strc_itemgroups_signal
+% \settrue\c_strc_itemgroups_concat
+% \fi
+% \fi
+% \ifconditional\c_strc_itemgroups_text_saved \else
+% \ifconditional\c_strc_itemgroups_text \else
+% \par
+% \fi
+% \fi
+% \fi
+%
+ \iftrialtypesetting
+ \strc_counters_save\v_strc_itemgroups_counter
+ \fi
+ \c_strc_itemgroups_n_of_items\zerocount
+ \strc_itemgroups_check_n_of_items
+ \ifempty\p_itemgroups_options
+ \strc_itemgroups_setup_symbol_default
+ \strc_itemgroups_reset_continue_state
+ \setupcurrentitemgroup[#settings]%
+ \else
+ \strc_itemgroups_process_options\p_itemgroups_options
+ \setupcurrentitemgroup[#settings]%
+ \ifconditional\c_strc_itemgroups_continue
+ \strc_itemgroups_setup_symbol_continue
+ \strc_itemgroups_fetch_continue_state
+ \else
+ \strc_itemgroups_setup_symbol_asked
+ \strc_itemgroups_store_continue_state\p_itemgroups_options{#settings}%
+ \fi
+ \fi
+ %
+ \ifhmode
+ \ifconditional\c_strc_itemgroups_auto_concat
+ \ifdim\lastskip=\d_strc_itemgroups_signal
+ \settrue\c_strc_itemgroups_concat
+ \fi
+ \fi
+ \ifconditional\c_strc_itemgroups_text_saved \else
+ \ifconditional\c_strc_itemgroups_text \else
+ \par
+ \fi
+ \fi
+ \fi
+ %
+ \ifconditional\c_strc_itemgroups_pack
+ \ifnum\currentnofitems>\itemgroupparameter\c!packcriterium\relax\else
+ \setfalse\c_strc_itemgroups_pack
+ \fi
+ \fi
+ %
+ \ifnum\c_strc_itemgroups_nesting=\plusone % NIEUW
+ \doadaptleftskip {\itemgroupparameter\c!margin}%
+ \doadaptleftskip {\itemgroupparameter\c!leftmargin}%
+ \doadaptrightskip{\itemgroupparameter\c!rightmargin}%
+ \fi
+ %
+ \doadaptleftskip {\itemgroupparameter\c!leftmargindistance}%
+ \doadaptrightskip{\itemgroupparameter\c!rightmargindistance}%
+ %
+ \usealignparameter\itemgroupparameter
+ \edef\m_strc_itemgroups_indenting{\itemgroupparameter\c!indenting}%
+ \ifnum\c_strc_itemgroups_nesting>\zerocount
+ \settrue\c_strc_itemgroups_first
+ \ifconditional\c_strc_itemgroups_continue \else
+ \strc_counters_restart_sub\v_strc_itemgroups_counter\currentitemlevel{\numexpr\itemgroupparameter\c!start-\plusone\relax}%
+ \fi
+ \fi
+ \ifempty\strc_itemgroups_used_symbol
+ \strc_itemgroups_set_symbol\strc_itemgroups_asked_symbol % ** default value
+ \ifempty\strc_itemgroups_used_symbol
+ \let\currentitemgroupsymbol\currentitemlevel % ** fall back
+ \fi
+ \fi
+ \ifconditional\c_strc_itemgroups_auto_intro\ifnum\prevgraf<\plusthree
+ \settrue\c_strc_itemgroups_intro
+ \fi\fi
+ \ifconditional\c_strc_itemgroups_paragraph
+ \ifnum\c_strc_itemgroups_nesting>\plusone
+ \letitemgroupparameter\c!inbetween\empty
+ \fi
+ \fi
+ \ifconditional\c_strc_itemgroups_pack
+ \letitemgroupparameter\c!inbetween\empty
+ \fi
+ \ifconditional\c_strc_itemgroups_columns
+ \ifinsidecolumns\orelse\ifcase\c_strc_itemgroups_column_depth
+ \global\c_strc_itemgroups_column_depth\c_strc_itemgroups_nesting % global ?
+ \strc_itemgroups_before_command
+ \strc_itemgroups_tag_start_group
+ \strc_itemgroups_start_columns
+ \fi
+ \fi
+ \ifconditional\c_strc_itemgroups_fitting
+ \ifdim\d_strc_itemgroups_max_width>\zeropoint
+ \letitemgroupparameter\c!width\d_strc_itemgroups_max_width
+ \fi
+ \fi
+ \strc_itemgroups_calculate_list_width\c_strc_itemgroups_nesting
+ \ifdim\d_strc_itemgroups_list_width>\zeropoint\relax
+ \ifconditional\c_strc_itemgroups_inline\else
+ \frozen\advance\leftskip\d_strc_itemgroups_list_width\relax
+ \fi
+ \fi
+ \ifempty\m_strc_itemgroups_repeat_start
+ \let\currentitemgroupconversionset \currentitemgroupsymbol
+ \edef\currentitemgroupsegments {\currentitemlevel}%
+ \else
+ \edef\currentitemgroupconversionset{\currentitemgroupconversionset,\currentitemgroupsymbol}%
+ \edef\currentitemgroupsegments {\m_strc_itemgroups_repeat_start:\currentitemlevel}%
+ \fi
+ \d_strc_itemgroups_asked_width\itemgroupparameter\c!width\relax
+ \startcollectitems}
+
+\let\startcollectitems\relax
+\let\stopcollectitems \relax
+
+\letvalue{\??itemgroupalign\v!flushleft }\relax
+\letvalue{\??itemgroupalign\v!right }\relax
+\letvalue{\??itemgroupalign\v!flushright}\hfill
+\letvalue{\??itemgroupalign\v!left }\hfill
+\letvalue{\??itemgroupalign\v!middle }\hfil
+\letvalue{\??itemgroupalign\v!center }\hfil
+
+\def\strc_itemgroups_left_sym_filler
+ {\csname\??itemgroupalign\itemgroupparameter\c!symalign\endcsname}
+
+% symbols + states
+
+\def\strc_itemgroups_store_global_symbol#symbol%
+ {\letgvalue{\??itemgroupglobal\currentitemgroup:\currentitemlevel}#symbol}
+
+\def\strc_itemgroups_store_local_symbol#symbol%
+ {\letgvalue{\??itemgrouplocal\currentitemgroup:\currentitemlevel}#symbol}
+
+\def\strc_itemgroups_fetch_global_symbol
+ {\csname\??itemgroupglobal\currentitemgroup:\currentitemlevel\endcsname}
+
+\def\strc_itemgroups_fetch_local_symbol
+ {\csname\??itemgrouplocal\currentitemgroup:\currentitemlevel\endcsname}
+
+\def\strc_itemgroups_setup_symbol_default
+ {\edef\strc_itemgroups_asked_symbol{\itemgroupparameter\c!symbol}%
+ \strc_itemgroups_store_global_symbol\empty}
+
+\def\strc_itemgroups_setup_symbol_continue
+ {\ifcsname\??itemgroupglobal\currentitemgroup:\currentitemlevel\endcsname
+ \let\strc_itemgroups_asked_symbol\strc_itemgroups_fetch_global_symbol
+ \else
+ \let\strc_itemgroups_asked_symbol\currentitemlevel
+ \fi}
+
+\def\strc_itemgroups_setup_symbol_asked
+ {\edef\strc_itemgroups_asked_symbol{\itemgroupparameter\c!symbol}}
+
+\ifdefined\strc_itemgroups_start_columns
+
+ % already defined in page-mix
+
+\else
+
+ % will be redefined in page-mix
+
+ \ifdefined\startcolumns \else
+ \protected\def\startcolumns[#settings]{}
+ \protected\def\stopcolumns {}
+ \fi
+
+ \def\strc_itemgroups_start_columns
+ {\startcolumns
+ [\c!n=\itemgroupparameter\c!n,
+ \c!height=,
+ \c!rule=\v!off,
+ \c!balance=\v!yes,
+ \c!align=\v!no]}
+
+ \def\strc_itemgroups_stop_columns
+ {\stopcolumns}
+
+\fi
+
+\permanent\protected\def\stopitemgroup
+ {\stopcollectitems
+ \ifconditional\c_strc_itemgroups_text
+ \removeunwantedspaces
+ \space
+ \ignorespaces
+ \else
+ \par
+ \fi
+ \strc_itemgroups_register_status
+ \ifconditional\c_strc_itemgroups_first \else
+ \dostoptagged
+ \dostoptagged
+ % \endgroup % (3)
+ \fi
+ \ifnum\c_strc_itemgroups_column_depth=\c_strc_itemgroups_nesting\relax
+ \strc_itemgroups_stop_columns
+ \global\c_strc_itemgroups_column_depth\zerocount % global ?
+ \strc_itemgroups_tag_stop_group
+ \strc_itemgroups_after_command
+ \dontrechecknextindentation
+ \orelse\ifnum\c_strc_itemgroups_nesting=\plusone
+ \strc_itemgroups_insert_breakallow
+ \strc_itemgroups_tag_stop_group
+ \strc_itemgroups_after_command
+ \useindentnextparameter\itemgroupparameter
+ \else
+ % nieuw, not yet nobreak handling
+ \strc_itemgroups_tag_stop_group
+ \ifcase\c_strc_itemgroups_spacing_mode
+ \strc_itemgroups_after_command
+ \or
+ \strc_itemgroups_after_command
+ \fi
+ \dontrechecknextindentation
+ \fi
+ % new test, needed in sidefloats (surfaced in volker's proceedings)
+ \iftrialtypesetting
+ \strc_counters_restore\v_strc_itemgroups_counter % could happen in LUA
+ \fi
+ \global\advance\c_strc_itemgroups_nesting\minusone
+ \xdef\currentitemlevel{\number\c_strc_itemgroups_nesting}%
+ %\ifconditional\c_strc_itemgroups_text
+ \endgroup % (2)
+ \endgroup % (1)
+ %\else
+ % \endgroup % (2)
+ % \endgroup % (1)
+ % \par % hm, already done, and dangerous as \c_strc_itemgroups_text is already forgotten
+ %\fi
+ \dorechecknextindentation}
+
+% The items.
+
+\def\strc_itemgroups_start_item_first
+ {\setfalse\c_strc_itemgroups_first
+ % \begingroup % (3)
+ \ifcase\c_strc_itemgroups_nesting
+ % 0
+ \or
+ \strc_itemgroups_start_item_first_one % 1
+ \else
+ \strc_itemgroups_start_item_first_two % 2+
+ \fi}
+
+\def\strc_itemgroups_start_item_first_one
+ {\ifcase\c_strc_itemgroups_column_depth
+ \ifconditional\c_strc_itemgroups_intro\strc_itemgroups_insert_breakno\fi
+ \strc_itemgroups_before_command
+ \strc_itemgroups_tag_start_group
+ \ifconditional\c_strc_itemgroups_intro\strc_itemgroups_insert_breakno\fi
+ \fi}
+
+\newcount\c_strc_itemgroups_prev_level
+
+\def\strc_itemgroups_inbetween_command
+ {\nameditemgroupparameter{\currentparentitemgroup:\number\c_strc_itemgroups_prev_level}\c!inbetween}
+
+\def\strc_itemgroups_start_item_first_two
+ {\ifconditional\c_strc_itemgroups_paragraph\else
+ \c_strc_itemgroups_prev_level\numexpr\c_strc_itemgroups_nesting-\plusone\relax
+ \ifcase\c_strc_itemgroups_spacing_mode
+ \strc_itemgroups_before_command
+ \or
+ \doifelsenothing\strc_itemgroups_before_command % should possibly expand to empty
+ \strc_itemgroups_inbetween_command
+ \strc_itemgroups_before_command
+ \else
+ \strc_itemgroups_inbetween_command
+ \fi
+ \strc_itemgroups_tag_start_group
+ \fi}
+
+\def\strc_itemgroups_start_item_next
+ {\dostoptagged % ok? what do we stop here?
+ \dostoptagged % ok? what do we stop here?
+ \ifconditional\c_strc_itemgroups_text
+ \ifhmode
+ % WS: make the distance between items customizable, think about better default values -> see itemize-1.tex
+ \strc_itemgroups_set_text_item_distance% HH: moved out and made configurable (sort of)
+ \removeunwantedspaces
+ \hskip\m_strc_itemgroups_text_distance\relax
+ \fi
+ \else
+ \strc_itemgroups_between_command
+ \fi}
+
+% c_strc_itemgroups_concat:
+%
+% the problem is that we use leftskip so concat cannot reliable take the height into
+% account; it's .. rather tricky when white space in there anyway (due to \par) .. so
+% we rely on a special blank method
+%
+% \startitemize[n]
+% \item bla
+% \item \startitemize[a]
+% \item bla $\displaystyle\int^{x^{y^4}}$ \item bla
+% \stopitemize
+% \stopitemize
+
+\mutable\let\currentitemreference\empty
+
+\tolerant\permanent\protected\def\startitemgroupitem[#reference]% we can reuse more
+ {\def\currentitemreference{#reference}%
+ \ifconditional\c_strc_itemgroups_text
+ % begin of item
+ \else
+ \par
+ \fi
+ \ifconditional\c_strc_itemgroups_concat
+ \strc_itemgroups_insert_breakno
+ \fi
+ \strc_itemgroups_increment_item_counter
+ \ifconditional\c_strc_itemgroups_first
+ \strc_itemgroups_start_item_first
+ \else
+ \strc_itemgroups_start_item_next
+ \fi
+ \ifconditional\c_strc_itemgroups_concat
+ \spac_overlay_lines % see spac-ver.mkvi ... a typical potential problem
+ \setfalse\c_strc_itemgroups_concat
+ \fi
+ \dostarttagged\t!item\empty
+ % \dotagsetitem\empty
+ \dostarttagged\t!itemtag\empty
+ \strc_itemgroups_insert_item
+ \dostoptagged
+ \ifconditional\c_strc_itemgroups_pack
+ \setupwhitespace[\v!none]%
+ \fi
+ \itemgroupparameter\c!inner % will become obsolete (better use setups)
+ \strc_itemgroups_margin_symbol
+ \let\strc_itemgroups_margin_symbol\relax
+ \dostarttagged\t!itemcontent\empty
+ \begstrut % \strut
+ \nobreak % else problems with intext items
+ \seteffectivehsize % NEW !
+ \hskip\d_strc_itemgroups_signal % concat
+ \itemgroupparameter\c!command}
+
+\protected\permanent\def\stopitemgroupitem
+ {\ifhmode
+ \endstrut % new per 2017-12-15
+ \fi
+ \ifconditional\c_strc_itemgroups_text
+ % nothing
+ \else
+ \endgraf
+ \fi}
+
+\tolerant\permanent\protected\def\startitemgrouphead[#reference]%
+ {\ifconditional\c_strc_itemgroups_first \else
+ \strc_itemgroups_insert_breakallow
+ \fi
+ \ifconditional\c_strc_itemgroups_pack \else
+ \strc_itemgroups_before_head_command
+ \fi
+ \ifconditional\c_strc_itemgroups_first
+ \ifconditional\c_strc_itemgroups_intro \else
+ \ifcase\c_strc_itemgroups_nesting
+ \strc_itemgroups_insert_breakallow
+ \fi
+ \fi
+ \fi
+ %\strc_itemgroups_start_item[#reference]%
+ \startitemgroupitem[#reference]%
+ \pickupgroupedcommand
+ \strc_itemgroups_start_head_indeed
+ \strc_itemgroups_stop_head_indeed
+ \strc_itemgroups_head_body_indeed}
+
+\protected\permanent\def\stopitemgrouphead
+ {\dostoptagged
+ \stopitemgroupitem}
+
+\protected\def\strc_itemgroups_start_head_indeed
+ {\settrue\c_strc_itemgroups_head
+ \dotagsetitem\s!head% % weird place
+ \dostarttagged\t!itemhead\empty
+ \useitemgroupstyleandcolor\c!headstyle\c!headcolor\ignorespaces}
+
+\protected\def\strc_itemgroups_stop_head_indeed
+ {\removeunwantedspaces
+ \dostoptagged
+ \ifconditional\c_strc_itemgroups_inline_head
+ \space
+ \ignorespaces
+ \else
+ \ifconditional\c_strc_itemgroups_text
+ \space
+ \ignorespaces
+ \else
+ \par
+ \fi
+ \strc_itemgroups_insert_breakno
+ \ifconditional\c_strc_itemgroups_pack\else\strc_itemgroups_after_head_command\fi
+ \strc_itemgroups_insert_breakno
+ \fi}
+
+\protected\def\strc_itemgroups_head_body_indeed
+ {\dostarttagged\t!itembody\empty
+ \noindentation
+ \ignorespaces}
+
+% Simple commands.
+
+\protected\def\strc_itemgroups_start_do_item
+ {\startitemgroupitem}
+
+\protected\def\strc_itemgroups_start_no_item
+ {\let\currentitemreference\empty
+ \strc_itemgroups_increment_item_counter
+ %\advance\c_strc_itemgroups_n_of_items\plusone
+ \setbox\b_strc_itemgroups\emptyhbox
+ \strc_itemgroups_check_for_repeated
+ \ignorespaces}
+
+\protected\def\strc_itemgroups_start_button[#destination]%
+ {\edef\m_strc_itemgroups_destination{#destination}%
+ \startitemgroupitem}
+
+\protected\def\strc_itemgroups_start_symbol#text%
+ {\def\strc_itemgroups_extra_symbol{#text}%
+ \settrue\c_strc_itemgroups_symbol
+ \startitemgroupitem
+ \dotagsetitem\s!symbol}
+
+\protected\def\strc_itemgroups_start_dummy
+ {\strc_itemgroups_start_symbol
+ %\strut\strut} % two ?
+ \begstrut}
+
+\protected\def\strc_itemgroups_start_subitem
+ {\settrue\c_strc_itemgroups_sub
+ \startitemgroupitem
+ \dotagsetitem\s!sub}
+
+\protected\def\strc_itemgroups_start_edge#text%
+ {\strc_itemgroups_start_symbol
+ {\strc_itemgroups_calculate_list_width\c_strc_itemgroups_nesting
+ \dostarttagged\t!ignore\empty % for the moment, maybe an attribute
+ \hbox to \d_strc_itemgroups_list_width
+ {#text\hskip\itemgroupparameter\c!distance}%
+ \dostoptagged}}
+
+\protected\def\strc_itemgroups_start_margin#text%
+ {\def\strc_itemgroups_margin_symbol % brrr
+ {\dostarttagged\t!ignore\empty % for the moment, maybe an attribute
+ \llap
+ {\begingroup
+ \useitemgroupstyleandcolor\c!marstyle\c!marcolor
+ #text% keep em/ex local
+ \endgroup
+ \hskip\dimexpr\leftskip+\leftmargindistance\relax}%
+ \dostoptagged}%
+ \startitemgroupitem
+ \dotagsetitem\s!margin}
+
+\protected\def\strc_itemgroups_start_text#text%
+ {\def\strc_itemgroups_extra_symbol{#text}%
+ \settrue\c_strc_itemgroups_symbol
+ \settrue\c_strc_itemgroups_txt
+ \startitemgroupitem}
+
+% \protected\def\strc_itemgroups_start_head
+% {\settrue\c_strc_itemgroups_head
+% \startitemgrouphead}
+
+\protected\def\strc_itemgroups_start_items
+ {\dosingleempty\strc_itemgroups_start_items_indeed}
+
+\protected\def\strc_itemgroups_start_items_indeed[#whatever]% something got lost
+ {\strc_itemgroups_start_edge
+ {\dorecurse{0\itemgroupparameter\c!items}{\strc_itemgroups_used_symbol\hss}%
+ \unskip}}
+
+\protected\permanent\def\startspecialitemgroupitem[#name]% todo: frozen
+ {\ifcsname\??itemgroupstart#name\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\strc_itemgroups_start_do_item
+ \fi}
+
+\protected\permanent\def\stopspecialitemgroupitem % todo: frozen
+ {\stopitemgroupitem}
+
+\letvalue{\??itemgroupstart\v!item}\strc_itemgroups_start_do_item
+\letvalue{\??itemgroupstart\v!sub }\strc_itemgroups_start_subitem
+\letvalue{\??itemgroupstart\v!sym }\strc_itemgroups_start_symbol
+\letvalue{\??itemgroupstart\v!ran }\strc_itemgroups_start_edge
+\letvalue{\??itemgroupstart\v!its }\strc_itemgroups_start_items
+\letvalue{\??itemgroupstart\v!mar }\strc_itemgroups_start_margin
+\letvalue{\??itemgroupstart\v!txt }\strc_itemgroups_start_text
+
+\def\strc_itemgroups_optimize_breaks
+ {\ifcase\c_strc_itemgroups_column_depth
+ \ifconditional\c_strc_itemgroups_optimize
+ \ifcase\c_strc_itemgroups_max_items
+ \orelse\ifnum\c_strc_itemgroups_max_items=\plusthree
+ \ifnum\c_strc_itemgroups_n_of_items>\plusone
+ \strc_itemgroups_insert_nobreak
+ \fi
+ \orelse\ifnum\c_strc_itemgroups_max_items>\plusthree
+ \ifnum\c_strc_itemgroups_n_of_items=\plustwo
+ \ifconditional\c_strc_itemgroups_intro
+ \strc_itemgroups_insert_breakno
+ \else
+ \strc_itemgroups_insert_nobreak
+ \fi
+ \orelse\ifnum\c_strc_itemgroups_max_items=\c_strc_itemgroups_n_of_items\relax
+ \strc_itemgroups_insert_nobreak
+ \orelse\ifnum\c_strc_itemgroups_n_of_items>\plustwo
+ \strc_itemgroups_insert_break
+ \else
+ \ifconditional\c_strc_itemgroups_intro\else\strc_itemgroups_insert_break\fi
+ \fi
+ \fi
+ \fi
+ \fi}
+
+\def\strc_itemgroups_handle_text_item
+ {\scratchdimen\wd\b_strc_itemgroups
+ \advance \scratchdimen \itemgroupparameter\c!distance\relax
+ \ifdim\scratchdimen>\d_strc_itemgroups_list_width
+ \advance\scratchdimen -\d_strc_itemgroups_list_width
+ \else
+ \scratchdimen\zeropoint
+ \fi
+ \llap{\hbox to \d_strc_itemgroups_list_width{\ifconditional\c_strc_itemgroups_sub\llap{+\enspace}\fi\box\b_strc_itemgroups\hss}}% was: \hfill
+ \hskip\scratchdimen}
+
+\def\strc_itemgroups_handle_lapped_item_positive
+ {\llap
+ {\dontcomplain
+ \hbox to \d_strc_itemgroups_list_width
+ {\ifconditional\c_strc_itemgroups_sub
+ \dostarttagged\t!ignore\empty
+ \llap{+\enspace}%
+ \dostoptagged
+ \fi
+ \strc_itemgroups_left_sym_filler
+ \box\b_strc_itemgroups % can already have a forced widt, only factor handled here
+ \hfil
+ \hskip\itemgroupparameter\c!distance}}}
+
+\def\strc_itemgroups_handle_lapped_item_negative
+ {\llap
+ {\ifconditional\c_strc_itemgroups_sub
+ \dostarttagged\t!ignore\empty
+ \llap{+\enspace}%
+ \dostoptagged
+ \fi
+ \box\b_strc_itemgroups
+ \hskip\leftmargindistance}}
+
+\def\strc_itemgroups_handle_groups_text_item
+ {\hbox
+ {\ifconditional\c_strc_itemgroups_sub
+ \dostarttagged\t!ignore\empty
+ +\enspace
+ \dostoptagged
+ \fi
+ \box\b_strc_itemgroups
+ \hskip\interwordspace}%
+ \nobreak}
+
+\def\strc_itemgroups_handle_groups_inline_item
+ {\hbox to \d_strc_itemgroups_list_width
+ {\ifconditional\c_strc_itemgroups_sub
+ \dostarttagged\t!ignore\empty
+ \llap{+\enspace}%
+ \dostoptagged
+ \fi
+ \box\b_strc_itemgroups
+ \hss}} % was: \hfill
+
+\protected\def\strc_itemgroups_start_head_sym#text%
+ {\def\strc_itemgroups_extra_symbol{#text}%
+ \settrue\c_strc_itemgroups_symbol
+ \settrue\c_strc_itemgroups_head
+ %\strc_itemgroups_start_head}
+ \startitemgrouphead}
+
+\def\strc_itemgroups_make_symbol_box
+ {\setbox\b_strc_itemgroups\autodirhbox
+ {\ifconditional\c_strc_itemgroups_head
+ \ifconditional\c_strc_itemgroups_symbol
+ \strc_itemgroups_insert_extra_reference
+ \useitemgroupstyleandcolor\c!symstyle\c!symcolor
+ \strc_itemgroups_extra_symbol
+ \else
+ \useitemgroupstyleandcolor\c!headstyle\c!headcolor
+ \strc_itemgroups_used_symbol
+ \fi
+ \else
+ \ifconditional\c_strc_itemgroups_symbol
+ \strc_itemgroups_insert_extra_reference
+ \useitemgroupstyleandcolor\c!symstyle\c!symcolor
+ \strc_itemgroups_extra_symbol
+ \else
+ \useitemgroupstyleandcolor\c!style\c!color
+ \strc_itemgroups_used_symbol
+ \fi
+ \fi}%
+ \let\strc_itemgroups_extra_symbol\empty
+ \setfalse\c_strc_itemgroups_symbol}
+
+\def\strc_itemgroups_make_fitting_box
+ {\ifdim\wd\b_strc_itemgroups>\itemgroupparameter\c!maxwidth\relax
+ \normalexpanded{\global\setitemgroupparameter{\c!maxwidth}{\the\wd\b_strc_itemgroups}}%
+ \fi
+ \ifdim\d_strc_itemgroups_max_width>\zeropoint
+ \setbox\b_strc_itemgroups\simplealignedbox
+ {\dimexpr\d_strc_itemgroups_max_width+\itemgroupparameter\c!distance\relax}
+ {\itemgroupparameter\c!itemalign}
+ {\box\b_strc_itemgroups\hskip\itemgroupparameter\c!distance}%
+ \fi}
+
+\def\strc_itemgroups_make_aligned_box
+ {\doifsomething{\itemgroupparameter\c!itemalign}
+ {\setbox\b_strc_itemgroups\simplealignedbox
+ {\dimexpr\d_strc_itemgroups_asked_width+\itemgroupparameter\c!distance\relax}
+ {\itemgroupparameter\c!itemalign}
+ {\box\b_strc_itemgroups\hskip\itemgroupparameter\c!distance}}}
+
+\def\strc_itemgroups_make_destination_box
+ {\iftrialtypesetting \orelse \ifempty\m_strc_itemgroups_destination \else
+ \setbox\b_strc_itemgroups\hbox{\directgotobox{\box\b_strc_itemgroups}[\m_strc_itemgroups_destination]}%
+ \fi}
+
+\def\strc_itemgroups_check_indenting
+ {\setfalse\c_strc_itemgroups_indented_first
+ \ifempty\m_strc_itemgroups_indenting \else
+ \normalexpanded{\setupindenting[\v!reset,\v!yes,\m_strc_itemgroups_indenting]}%
+ \ifconditional\c_spac_indentation_indent_first % better is to have a mode
+ \doifnot{\itemgroupparameter\c!alignsymbol}\v!yes
+ {\settrue\c_strc_itemgroups_indented_first}%
+ \fi
+ \fi}
+
+\def\strc_itemgroups_check_for_repeated
+ {\ifconditional\c_strc_itemgroups_repeat
+ \ifempty\m_strc_itemgroups_repeat_start
+ \edef\m_strc_itemgroups_repeat_start{\currentitemlevel}%
+ \fi
+ \else
+ \let\m_strc_itemgroups_repeat_start\empty
+ \fi}
+
+\def\strc_itemgroups_insert_item
+ {\ifconditional\c_strc_itemgroups_text % again?
+ % begin of item
+ \else
+ \par % done twice?
+ \fi
+ \advance\c_strc_itemgroups_n_of_items\plusone
+ \strc_itemgroups_optimize_breaks
+ \strc_itemgroups_check_indenting
+ \dontleavehmode
+ \ifconditional\c_strc_itemgroups_indented_first
+ \hskip-\parindent
+ \fi
+ \strc_itemgroups_make_symbol_box
+ \strc_itemgroups_make_destination_box
+ \ifconditional\c_strc_itemgroups_fitting
+ \strc_itemgroups_make_fitting_box
+ \orelse\ifdim\d_strc_itemgroups_asked_width>\zeropoint
+ \strc_itemgroups_make_aligned_box
+ \fi
+ \let\m_strc_itemgroups_destination\empty
+ \ht\b_strc_itemgroups\strutheight % just in case a symbols is not yet available in
+ \dp\b_strc_itemgroups\strutdepth % the current run (more a mkii mp side artifact)
+ \strc_itemgroups_check_for_repeated
+ \ifdim\d_strc_itemgroups_asked_width<\zeropoint\relax
+ \strc_itemgroups_handle_lapped_item_negative
+ \else
+ \ifzeropt\d_strc_itemgroups_asked_width
+ \strc_itemgroups_calculate_list_width\plusone
+ \else
+ \strc_itemgroups_calculate_list_width\c_strc_itemgroups_nesting
+ \fi
+ \ifconditional\c_strc_itemgroups_text
+ \strc_itemgroups_handle_groups_text_item
+ \orelse\ifconditional\c_strc_itemgroups_inline
+ \strc_itemgroups_handle_groups_inline_item
+ \orelse\ifconditional\c_strc_itemgroups_txt
+ \strc_itemgroups_handle_text_item
+ \else
+ \strc_itemgroups_handle_lapped_item_positive
+ \fi
+ \fi
+ \setfalse\c_strc_itemgroups_head
+ \setfalse\c_strc_itemgroups_sub
+ \ifconditional\c_strc_itemgroups_indented_first
+ \hskip\parindent
+ \fi
+ \ignorespaces}
+
+\def\strc_itemgroups_start_item_nop
+ {\let\currentitemreference\empty
+ \strc_itemgroups_increment_item_counter
+ \advance\c_strc_itemgroups_n_of_items\plusone
+ \setbox\b_strc_itemgroups\hbox
+ {\useitemgroupstyleandcolor\c!style\c!color
+ \strc_itemgroups_used_symbol}%
+ \strc_itemgroups_check_for_repeated
+ \ignorespaces}
+
+\setvalue{\??itemgroupdistance\v!none}%
+ {\let\m_strc_itemgroups_text_distance\zeropoint}
+
+\setvalue{\??itemgroupdistance\v!space}%
+ {\def\m_strc_itemgroups_text_distance{\interwordspace\s!plus\interwordstretch\s!minus\interwordshrink}}
+
+\setvalue\??itemgroupdistance % catches empty value
+ {\let\m_strc_itemgroups_text_distance\zeropoint}
+
+\protected\def\strc_itemgroups_set_text_item_distance
+ {\edef\m_strc_itemgroups_text_distance{\itemgroupparameter\c!textdistance}%
+ \ifempty\m_strc_itemgroups_text_distance
+ %
+ \orelse\ifcsname\??itemgroupdistance\m_strc_itemgroups_text_distance\endcsname
+ \lastnamedcs
+ \else
+ \strc_itemgroups_set_text_item_distance_indeed
+ \fi}
+
+\def\strc_itemgroups_set_text_item_distance_indeed
+ {\assignvalue
+ \m_strc_itemgroups_text_distance
+ \m_strc_itemgroups_text_distance
+ {.5\interwordspace\s!plus.5\emwidth}%
+ {\interwordspace \s!plus \emwidth}%
+ {\emwidth \s!plus \interwordstretch\s!minus\interwordshrink}}
+
+% \protected\def\strc_itemgroups_default_command
+% {\EveryPar{\ignorespaces}% needed ?
+% \ignorespaces}
+
+\protected\def\strc_itemgroups_default_command
+ {\ignorespaces}
+
+%D Special case:
+
+\protected\def\strc_itemgroups_head#text\par
+ {\startitemgrouphead{#text}}
+
+%D The local commands:
+
+\ifdefined\item \else \let\item \relax \fi % defined later
+\ifdefined\sub \else \aliased\let\sub \relax \fi
+\ifdefined\sym \else \aliased\let\sym \relax \fi
+\ifdefined\ran \else \aliased\let\ran \relax \fi
+\ifdefined\its \else \aliased\let\its \relax \fi
+\ifdefined\mar \else \aliased\let\mar \relax \fi
+\ifdefined\noitem \else \aliased\let\noitem \relax \fi
+\ifdefined\itm \else \aliased\let\itm \relax \fi
+\ifdefined\but \else \aliased\let\but \relax \fi
+%ifdefined\nop \else \let\nop \relax \fi % used often by myself
+\ifdefined\txt \else \aliased\let\txt \relax \fi
+\ifdefined\head \else \let\head \relax \fi % defined later
+\ifdefined\headsym \else \aliased\let\headsym \relax \fi
+\ifdefined\startitem \else \let\startitem \relax \fi % defined later
+\ifdefined\stopitem \else \aliased\let\stopitem \relax \fi
+\ifdefined\starthead \else \aliased\let\starthead \relax \fi
+\ifdefined\stophead \else \aliased\let\stophead \relax \fi
+\ifdefined\startspecialitem \else \aliased\let\startspecialitem\relax \fi
+\ifdefined\stopspecialitem \else \aliased\let\stopspecialitem \relax \fi
+
+
+\appendtoks
+ \enforced\let\item \strc_itemgroups_start_do_item
+ \enforced\let\sub \strc_itemgroups_start_subitem
+ \enforced\let\sym \strc_itemgroups_start_symbol
+ \enforced\let\ran \strc_itemgroups_start_edge
+ \enforced\let\its \strc_itemgroups_start_items
+ \enforced\let\mar \strc_itemgroups_start_margin
+ \enforced\let\noitem \strc_itemgroups_start_no_item
+ \enforced\let\itm \strc_itemgroups_start_do_item
+ \enforced\let\but \strc_itemgroups_start_button
+ \enforced\let\nop \strc_itemgroups_start_dummy
+ \enforced\let\txt \strc_itemgroups_start_text
+ \enforced\let\head \strc_itemgroups_head
+ \enforced\let\headsym \strc_itemgroups_start_head_sym
+ \enforced\let\startitem \startitemgroupitem
+ \enforced\let\stopitem \stopitemgroupitem
+ \enforced\let\starthead \startitemgrouphead
+ \enforced\let\stophead \stopitemgrouphead
+ \enforced\let\startspecialitem\startspecialitemgroupitem
+ \enforced\let\stopspecialitem \stopspecialitemgroupitem
+\to \itemgroupcommands
+
+\ifx\currentinterface \s!english \else
+
+ \def\next#1#2{\ifcsname#1\endcsname\else\enforced\aliased\letcsname#1\endcsname#2\fi}
+
+ \etoksapp\itemgroupcommands{%
+ \next{\v!item }\strc_itemgroups_start_do_item
+ \next{\v!sub }\strc_itemgroups_start_subitem
+ \next{\v!sym }\strc_itemgroups_start_symbol
+ \next{\v!ran }\strc_itemgroups_start_edge
+ \next{\v!head }\strc_itemgroups_head
+ \next{\v!its }\strc_itemgroups_start_items
+ \next{\v!mar }\strc_itemgroups_start_margin
+ \next{\v!txt }\strc_itemgroups_start_text
+ \next{\e!start\v!item}\startitemgroupitem
+ \next{\e!stop \v!item}\stopitemgroupitem
+ \next{\e!start\v!head}\startitemgrouphead
+ \next{\e!stop \v!head}\stopitemgrouphead
+ }
+
+\fi
+
+\relaxvalueifundefined \v!item
+\relaxvalueifundefined \v!sub
+\relaxvalueifundefined \v!sym
+\relaxvalueifundefined \v!ran
+\relaxvalueifundefined \v!head
+\relaxvalueifundefined \v!its
+\relaxvalueifundefined \v!mar
+\relaxvalueifundefined \v!txt
+\relaxvalueifundefined {\e!start\v!item}
+\relaxvalueifundefined {\e!stop \v!item}
+\relaxvalueifundefined {\e!start\v!head}
+\relaxvalueifundefined {\e!stop \v!head}
+
+%D A nice example of a plugin:
+%D
+%D \startbuffer
+%D \startitemize[a,random,packed]
+%D \startitem first \stopitem \startitem second \stopitem
+%D \startitem third \stopitem \startitem fourth \stopitem
+%D \stopitemize
+%D
+%D \startitemize[a,random,packed]
+%D \startitem first \stopitem \startitem second \stopitem
+%D \startitem third \stopitem \startitem fourth \stopitem
+%D \stopitemize
+%D
+%D \startitemize[a,packed]
+%D \startitem first \stopitem \startitem second \stopitem
+%D \startitem third \stopitem \startitem fourth \stopitem
+%D \stopitemize
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+% better collectitems als conditional and a real plugin mechanism (some day)
+
+\newcount\c_strc_itemgroups_collected_stored
+\newcount\c_strc_itemgroups_collected_done
+\newcount\c_strc_itemgroups_collected_current
+
+\let\strc_itemgroups_collected_store\relax
+
+\let\m_strc_itemgroups_collected\empty
+
+\normalexpanded{\def\strc_itemgroups_collected_store#1\csname\e!stop\v!item\endcsname}%
+ {\advance\c_strc_itemgroups_collected_stored\plusone
+ \setvalue{\??itemgroupstack\number\c_strc_itemgroups_collected_stored}{\startitemgroupitem#1\stopitemgroupitem}}
+
+\def\strc_itemgroups_collected_flush_randomize
+ {\m_strc_itemgroups_collected}
+
+\def\strc_itemgroups_collected_flush_horizontal
+ {\begingroup
+ \scratchcounter\itemgroupparameter\c!n\relax
+ \ifnum\c_strc_itemgroups_collected_done>\plusone
+ \ifcase\modulonumber\scratchcounter\c_strc_itemgroups_collected_done\relax
+ % compensate for rounding errors
+ \hfill % \hskip\zeropoint plus \scaledpoint minus \scaledpoint
+ \allowbreak
+ \fi
+ \fi
+ \noindent % no \dontleavehmode
+ \hbox to \dimexpr\availablehsize/\scratchcounter\relax{\m_strc_itemgroups_collected\hss}%
+ \endgroup}
+
+\def\strc_itemgroups_collected_flush_indeed
+ {\m_strc_itemgroups_collected
+ \letcsname\??itemgroupstack\number\c_strc_itemgroups_collected_current\endcsname\undefined}
+
+\def\strc_itemgroups_collected_flush
+ {\ifconditional\c_strc_itemgroups_randomize
+ \getrandomcount\c_strc_itemgroups_collected_current\plusone\c_strc_itemgroups_collected_stored
+ \else
+ \advance\c_strc_itemgroups_collected_current\plusone
+ \fi
+ \ifcsname\??itemgroupstack\number\c_strc_itemgroups_collected_current\endcsname
+ \edef\m_strc_itemgroups_collected{\lastnamedcs}%
+ \ifconditional\c_strc_itemgroups_horizontal
+ \strc_itemgroups_collected_flush_horizontal
+ \else
+ \strc_itemgroups_collected_flush_indeed
+ \fi
+ \advance\c_strc_itemgroups_collected_done\plusone
+ \fi
+ \ifnum\c_strc_itemgroups_collected_done<\c_strc_itemgroups_collected_stored
+ \expandafter\strc_itemgroups_collected_flush
+ \fi}
+
+\protected\permanent\def\stopcollectitems % todo: frozen
+ {\ifconditional\c_strc_itemgroups_collecting
+ \c_strc_itemgroups_collected_done \zerocount
+ \c_strc_itemgroups_collected_current\zerocount
+ \ifnum\c_strc_itemgroups_collected_stored>\zerocount
+ \ifconditional\c_strc_itemgroups_horizontal
+ \strc_itemgroups_before_command
+ \setfalse\c_strc_itemgroups_first
+ \strc_itemgroups_collected_flush
+ %\strc_itemgroups_after_command % triggered elsewhere
+ \else
+ \strc_itemgroups_collected_flush
+ \fi
+ \fi
+ \fi}
+
+\protected\permanent\def\startcollectitems % todo: frozen
+ {\ifconditional\c_strc_itemgroups_collecting
+ \c_strc_itemgroups_collected_stored\zerocount
+ \enforced\letcsname\e!start\v!item\endcsname\strc_itemgroups_collected_store
+ \fi}
+
+%D Left-overs:
+
+\permanent\protected\def\item % todo: frozen
+ {\strc_itemgroups_item_alone}
+
+\tolerant\def\strc_itemgroups_item_alone[#category]#:#text\par
+ {\doifelsesomething{#category}{\startitemgroup[#category]}{\startitemgroup[\v!itemize]}%
+ \startitem#text\stopitem
+ \stopitemgroup}
+
+\permanent\protected\def\head % todo: frozen
+ {\strc_itemgroups_head_alone}
+
+\def\strc_itemgroups_head_alone[#category]#head\par#body\par
+ {\doifelsesomething{#category}{\startitemgroup[#category]}{\startitemgroup[\v!itemize]}%
+ \starthead{#head}#body\stophead
+ \stopitemgroup}
+
+\pushoverloadmode
+
+\permanent\protected\defcsname\e!start\v!item\endcsname
+ {\startitemgroup[\v!itemize]%
+ \startitemgroupitem
+ \enforced\letcsname\e!stop\v!item\endcsname\strc_itemgroups_stop_item}
+
+\aliased\letcsname\e!stop\v!item\endcsname\relax
+
+\permanent\protected\def\strc_itemgroups_stop_item
+ {\stopitemgroupitem
+ \stopitemgroup}
+
+\popoverloadmode
+
+% \def\sym#text%
+% {\noindent
+% \begingroup
+% \setbox\scratchbox\hbox{\settrialtypesetting#text}%
+% \setbox\scratchbox\hbox\ifdim\wd\scratchbox<1em to 1.5\else spread 1\fi em{#text\hfil}%
+% \normalexpanded{\box\scratchbox\endgroup\hangindent\the\wd\scratchbox}%
+% \ignorespaces}
+
+%D End of plugin.
+
+%D Something for tables:
+
+\permanent\tolerant\protected\def\itemtag[#reference]% we can reuse more
+ {% what if empty
+ \def\currentitemreference{#reference}%
+ \iftrialtypesetting
+ \strc_counters_save\v_strc_itemgroups_counter
+ \strc_itemgroups_increment_item_counter
+ \strc_itemgroups_insert_item_tag_indeed
+ \strc_counters_restore\v_strc_itemgroups_counter
+ \else
+ \strc_itemgroups_increment_item_counter
+ \strc_itemgroups_insert_item_tag_indeed
+ \fi}
+
+\def\strc_itemgroups_insert_item_tag_indeed
+ {\letitemgroupparameter\c!distance\zeropoint
+ %letitemgroupparameter\c!width\zeropoint
+ \strc_itemgroups_make_symbol_box
+ \strc_itemgroups_make_destination_box
+ \let\m_strc_itemgroups_destination\empty
+ \ht\b_strc_itemgroups\strutheight
+ \dp\b_strc_itemgroups\strutdepth
+ \box\b_strc_itemgroups
+ \ignorespaces}
+
+%D Done again.
+
+\setupitemgroup
+ [\c!margin=\zeropoint,
+ \c!leftmargin=\zeropoint,
+ \c!rightmargin=\zeropoint,
+ \c!leftmargindistance=\zeropoint,
+ \c!rightmargindistance=\zeropoint,
+ \c!indentnext=\v!yes,
+ \c!width=1.5\emwidth,
+ \c!factor=0,
+ %\c!distance=.5em, % is now:
+ \c!step=.5\emwidth, % deals with broad
+ \c!distance=\zeropoint,
+ %\c!align=\v!normal, % definitely not \v!normal !
+ %\c!symalign=,
+ %\c!color=,
+ %\c!indenting=, % untouched if empty
+ %\c!style=,
+ \c!marstyle=\v!type,
+ %\c!symstyle=,
+ %\c!headstyle=,
+ %\c!marcolor=,
+ %\c!symcolor=,
+ %\c!headcolor=,
+ %\c!beforehead=,
+ \c!symcolor=\itemgroupparameter\c!color, % new per 2012.01.17
+ \c!symstyle=\itemgroupparameter\c!style, % new per 2012.01.17
+ \c!afterhead=\blank,
+ \c!before=\blank,
+ \c!inbetween=\blank,
+ \c!after=\blank,
+ %\c!stopper=.,
+ \c!placestopper=\v!yes,
+ \c!stopper=.,
+ %\c!inner=,
+ \c!n=2,
+ \c!items=4,
+ \c!levels=10,
+ \c!lefttext=(,
+ \c!righttext=),
+ \c!start=1,
+ \c!packcriterium=\zerocount,
+ \c!criterium=\v!all, % permits 0 and negative numbers
+ %\c!option=,
+ \c!textdistance=\v!space, % none big medium small <dimension>
+ \c!command=\strc_itemgroups_default_command,
+ \c!indenting=\v!next,
+ %\c!alignsymbol=v!no,
+ \c!symbol=\currentitemlevel,
+ \c!prefix=\v!no,
+ %\c!prefixstopper=.,
+ %\c!prefixseparatorset=,
+ %\c!prefixconversion=,
+ %\c!prefixconversionset=,
+ %\c!prefixset=,
+ %\c!prefixsegments=1:100,
+ \c!prefixconnector=.,
+ \c!numberseparatorset=,
+ \c!numberconversionset=,
+ \c!numberstopper=.,
+ \c!numbersegments=1]
+
+\defineitemgroup
+ [\v!itemize]
+
+%D This is really ugly I should get rid of the global mess, whuch is a side
+%D effect of the simple \type {\item} interface that we're stuck with for
+%D compatibility reasons. See footnotes for usage. It's probablynot robust
+%D for mixed itemgroups so in the end I need to get rid of the globals. One
+%D problem is that counters are set.
+
+\defineitemgroup
+ [\v!itemize:\v!note]
+
+\def\strc_itemgroups_push
+ {\edef\strc_itemgroups_pop
+ {\xdef\noexpand\currentitemlevel{\currentitemlevel}%
+ \global\c_strc_itemgroups_nesting \the\c_strc_itemgroups_nesting \relax
+ \global\c_strc_itemgroups_column_depth\the\c_strc_itemgroups_column_depth\relax}%
+ \global\c_strc_itemgroups_column_depth\zerocount
+ \global\c_strc_itemgroups_nesting \zerocount
+ \glet\currentitemlevel\!!zerocount
+ \enforced\permanent\protected\def\startitemize{\startitemgroup[\v!itemize:\v!note]}%
+ \enforced\permanent\protected\def\stopitemize {\stopitemgroup}}
+
+\let\strc_itemgroups_pop\relax
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-lab.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-lab.mkxl
new file mode 100644
index 00000000000..3794fdca935
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-lab.mkxl
@@ -0,0 +1,199 @@
+%D \module
+%D [ file=strc-lab, % was part of strc-des,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Labels,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Labels}
+
+\unprotect
+
+% todo: reset and increment will go
+
+%D Labels are implemented somewhat inefficient but have the advantage that they
+%D can be controlled like other constructions. Who knows when that comes in
+%D handy?
+
+\installcorenamespace{labels}
+
+\installcommandhandler \??labels {label} \??labels % plural hash, else clash with label (or rename that one)
+
+\installcounterassociation{label}
+
+\let\setuplabels\setuplabel
+
+\setuplabel
+ [\c!alternative=\v!intext,
+ \c!headstyle=\v!bold,
+ \c!titlestyle=\v!bold,
+ \c!distance=\zeropoint,
+ \c!width=\v!fit,
+ \c!titledistance=.5\emwidth,
+ \c!margin=\v!no,
+ \c!titleleft=(,
+ \c!titleright=),
+ \c!display=\v!no,
+ \c!titlecommand=,
+ \c!expansion=\v!no,
+ \c!way=\v!by\v!text,
+ \c!prefix=\v!no,
+ \c!prefixconnector=.,
+ \c!text=\currentlabel,
+ \c!number=\v!yes, % else description
+ \c!start=0,
+ \c!state=\v!start,
+ \c!levels=3]
+
+\protected\def\strc_define_commands_label#1#2#3% current level parent
+ {\doifelsenothing{#3}
+ {\normalexpanded{\defineconstruction[#1][\s!handler=\v!label,\c!level=#2]}%
+ \edefcsname\??label#1:\s!parent\endcsname{\??label}}%
+ {\normalexpanded{\defineconstruction[#1][#3][\s!handler=\v!label,\c!level=#2]}%
+ \edefcsname\??label#1:\s!parent\endcsname{\??label#3}}%
+ \ifconditional\c_strc_constructions_define_commands
+ \frozen\instance\protected\edefcsname\e!next #1\endcsname{\strc_labels_next {#1}{\number#2}}% obsolete
+ \frozen\instance\protected\edefcsname\v!reset#1\endcsname{\strc_labels_reset{#1}{\number#2}}% obsolete % should be \e!reset anyway
+ %frozen\instance\setuevalue{\c!set #1}{\strc_labels_set {#1}{\number#2}}% obsolete
+ \ifcsname\v!current#1\endcsname
+ % we play safe
+ \else
+ \protected\edefcsname\v!current#1\endcsname{\strc_labels_current{#1}}% % obsolete % should be \e!current anyway
+ \fi
+ \frozen\instance\protected\edefcsname#1\endcsname{\strc_labels_command[#1]}%
+ \fi}
+
+% todo: \strc_labels_command for user
+
+\let\m_strc_labels_sub\empty
+
+\appendtoks
+ \let\m_strc_labels_sub\empty
+ \ifempty\currentlabelparent
+ % clone => parent | subclone => clone | subsubclone => subclone
+ \strc_define_commands_label
+ {\m_strc_labels_sub\currentlabel}%
+ \plusone
+ \empty
+ \edef\p_levels{\labelparameter\c!levels}%
+ \dostepwiserecurse\plustwo\p_levels\plusone
+ {\strc_define_commands_label
+ {\v!sub\m_strc_labels_sub\currentlabel}%
+ \recurselevel
+ {\m_strc_labels_sub\currentlabel}%
+ \edef\m_strc_labels_sub{\v!sub\m_strc_labels_sub}}%
+ \else
+ % clone => parent | subclone => subparent | subsubclone => subsubparent
+ \edef\p_levels{\labelparameter\c!levels}%
+ \dorecurse\p_levels
+ {\strc_define_commands_label
+ {\m_strc_labels_sub\currentlabel}%
+ \recurselevel
+ {\m_strc_labels_sub\currentlabelparent}%
+ \edef\m_strc_labels_sub{\v!sub\m_strc_labels_sub}}%
+ \fi
+ \edef\p_counter{\labelparameter\s!counter}% can inherit from parent
+ \ifempty\p_counter %
+ \let\p_counter\currentlabel
+ \fi
+ \doifelsecounter\p_counter\donothing{\strc_labels_define_counter\p_counter}%
+ \letlabelparameter\s!counter\p_counter
+\to \everydefinelabel
+
+\let\p_strc_constructions_title \empty
+\let\p_strc_constructions_number\empty
+
+\newconditional\c_strc_constructions_number_keep
+
+\setvalue{\??constructioninitializer\v!label}%
+ {\let \currentlabel \currentconstruction
+ \enforced\let\constructionparameter \labelparameter
+ \enforced\let\constructionnamespace \??label
+ \enforced\let\detokenizedconstructionparameter\detokenizedlabelparameter
+ \enforced\let\letconstructionparameter \letlabelparameter
+ \enforced\let\useconstructionstyleandcolor \uselabelstyleandcolor
+ \enforced\let\setupcurrentconstruction \setupcurrentlabel
+ % shared with enumerations
+ \edef\p_strc_constructions_number{\constructionparameter\c!number}%
+ \ifx\p_strc_constructions_number\v!yes
+ \settrue\c_strc_constructions_number_state
+ \iftrialtypesetting
+ \strc_counters_save\currentconstructionnumber
+ \fi
+ \ifconditional\c_strc_constructions_number_keep \else
+ \strc_counters_increment_sub\currentconstructionnumber\currentconstructionlevel
+ \fi
+ \else
+ \setfalse\c_strc_constructions_number_state
+ \fi
+ \edef\p_strc_constructions_title{\constructionparameter\c!title}%
+ \ifx\p_strc_constructions_title\v!yes
+ \settrue\c_strc_constructions_title_state
+ \else
+ \setfalse\c_strc_constructions_title_state
+ \fi}
+
+\setvalue{\??constructionfinalizer\v!label}%
+ {\ifconditional\c_strc_constructions_number_state
+ \iftrialtypesetting
+ \strc_counters_restore\currentconstructionnumber
+ \fi
+ \fi}
+
+%D Interfaces:
+
+\protected\def\strc_labels_command{\setfalse\c_strc_constructions_number_keep\strc_descriptions_command}
+\protected\def\strc_labels_current{\settrue \c_strc_constructions_number_keep\strc_descriptions_command}
+
+\protected\def\strc_labels_next {\strc_constructions_next_indeed \namedlabelparameter} % #1#2
+\protected\def\strc_labels_reset {\strc_constructions_reset_indeed\namedlabelparameter} % #1#2
+%protected\def\strc_labels_set {\strc_constructions_set_indeed \namedlabelparameter} % #1#2
+
+% similar to enumerations
+
+\def\strc_labels_define_counter#1%
+ {\definecounter[#1]%
+ \registerlabelcounter{#1}}
+
+\appendtoks
+ \synchronizelabelcounters
+\to \everysetuplabel
+
+\appendtoks
+ \synchronizelabelcounters
+\to \everydefinelabel
+
+% no start stop here
+
+\letcsname\??constructionmainhandler\v!label\expandafter\endcsname\csname\??constructionmainhandler\v!description\endcsname
+
+\protected\setevalue{\??constructioncommandhandler\v!label}%
+ {\csname\??constructionstarthandler\v!construction\endcsname
+ \csname\??constructionstophandler \v!construction\endcsname
+ \endgroup}
+
+\protected\setvalue{\??constructiontexthandler\v!label}%
+ {\begingroup
+ \useconstructionstyleandcolor\c!headstyle\c!headcolor
+ \strc_labels_text
+ \endgroup}
+
+\let\strc_labels_text\strc_enumerations_text
+
+% inline variant
+
+\defineconstructionalternative
+ [\v!intext]
+ [\c!renderingsetup=\??constructionrenderings:\v!intext]
+
+\startsetups[\??constructionrenderings:\v!intext]
+ \dontleavehmode\box\constructionheadbox
+\stopsetups
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-lev.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-lev.mklx
new file mode 100644
index 00000000000..08688ee04df
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-lev.mklx
@@ -0,0 +1,98 @@
+%D \module
+%D [ file=strc-lev,
+%D version=2010.09.23,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Automatic Levels,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Automatic Levels}
+
+%D This module is the result of a discussion between Alan Braslau and
+%D me. I used to have a private (\MKII) module for this but the new
+%D structure code in \MKIV\ is more suitable for this so now we have
+%D it as core functionality. For the moment this an experiment that
+%D Alan and I conduct so it might evolve.
+
+\registerctxluafile{strc-lev}{}
+
+\unprotect
+
+\permanent\tolerant\protected\def\definesectionlevels[#category]#spacer[#list]%
+ {\clf_definesectionlevels{#category}{#list}} % we could scan/use public at the lua end
+
+\permanent\tolerant\protected\def\startsectionlevel[#category]%
+ {\doifelseassignment{#category}%
+ {\clf_startsectionlevel\plusone{\v!default}{\currentnamedsection}[#category]}%
+ {\clf_startsectionlevel\plusone {#category}{\currentnamedsection}}}
+
+\permanent\protected\def\stopsectionlevel
+ {\clf_stopsectionlevel} % we could use public at the lua end
+
+\permanent\tolerant\protected\def\startsubjectlevel[#category]%
+ {\doifelseassignment{#category}%
+ {\clf_startsectionlevel\plustwo{\v!default}{\currentnamedsection}[#category]}%
+ {\clf_startsectionlevel\plustwo {#category}{\currentnamedsection}}}
+
+\permanent\protected\def\stopsubjectlevel
+ {\clf_stopsectionlevel} % we could use public at the lua end
+
+\permanent\tolerant\protected\def\nostarthead[#category]#spacer[#settings]#spacer[#userdata]%
+ {\blank
+ \noindentation{\tttf[start missing section level #category]}
+ \blank}
+
+\permanent\tolerant\protected\def\nostophead[#category]%
+ {\blank
+ \noindentation{\tttf[stop missing section level #category]}
+ \blank}
+
+\definesectionlevels
+ [\v!default]
+ [{\v!chapter,\v!title},
+ {\v!section,\v!subject},
+ {\v!subsection,\v!subsubject},
+ {\v!subsubsection,\v!subsubsubject},
+ {\v!subsubsubsection,\v!subsubsubject},
+ {\v!subsubsubsubsection,\v!subsubsubject}]
+
+\aliased\let\definestructurelevels\definesectionlevels
+\aliased\let\startstructurelevel \startsectionlevel
+\aliased\let\stopstructurelevel \stopsectionlevel
+
+\protect \endinput
+
+% \starttext
+%
+% \definehead[xxxxxxsection][subsubsection]
+% \setuphead [xxxxxxsection][color=red]
+%
+% \definesectionlevels[main][chapter,section,subsection,subsubsection]
+% \definesectionlevels[next][chapter,section,subsection,xxxxxxsection]
+%
+% \startsectionlevel [main] [title=first top level]
+%
+% \startsectionlevel [main] [title=first lower level]
+% \startsectionlevel [main] [title=second lower level]
+% \startsectionlevel [main] [title=third lower level]
+% test
+% \stopsectionlevel
+% \stopsectionlevel
+% \stopsectionlevel
+%
+% \startsectionlevel [main] [title=first lower level]
+% \startsectionlevel [main] [title=second lower level]
+% \startsectionlevel [next] [title=third lower level]
+% test
+% \stopsectionlevel
+% \stopsectionlevel
+% \stopsectionlevel
+%
+% \stopsectionlevel
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-lnt.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-lnt.mklx
new file mode 100644
index 00000000000..f389442d83c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-lnt.mklx
@@ -0,0 +1,361 @@
+%D \module
+%D [ file=strc-lnt,
+%D version=2002.05.10,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Line Notes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: mkvi #
+
+\writestatus{loading}{ConTeXt Structure Macros / Line Notes}
+
+%D This module loads on top of the footnote and line numbering macros.
+
+\unprotect
+
+\installcorenamespace{linenote}
+
+\aliased\let\setuplinenote\setupnote
+
+\newcount\c_strc_linenotes
+\newtoks\everydefinelinenote
+
+\permanent\tolerant\protected\def\definelinenote[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ % error
+ \orelse\ifcsname\??linenote#1\endcsname
+ % there might be files that define the default 'linenote'
+ \ifparameters\or\or
+ \setupnote[#1][#2]%
+ \or
+ \setupnote[#1][#3]%
+ \fi
+ \else
+ \ifparameters\or
+ \definenote[#1]%
+ \or
+ \definenote[#1][#2]%
+ \or
+ \definenote[#1][#2][#3]%
+ \fi
+ \pushmacro\currentnote
+ \edef\currentnote{#1}
+ \letcsname\??linenote\currentnote\expandafter\endcsname\csname\currentnote\endcsname % use copy command
+ \frozen\instance\setuevalue {\currentnote}{\strc_linenotes_direct{\currentnote}}%
+ \frozen\instance\setuevalue{\e!start\currentnote}{\strc_linenotes_start {\currentnote}}%
+ \frozen\instance\setuevalue{\e!stop \currentnote}{\strc_linenotes_stop }%
+ \the\everydefinelinenote
+ \popmacro\currentnote
+ \fi}
+
+\protected\def\strc_linenotes_direct#1#2%
+ {\global\advance\c_strc_linenotes\plusone
+ \strc_linenotes_indeed{#1}{\the\c_strc_linenotes}{#2}%
+ \strc_linenotes_traced\empty
+ \normalexpanded{\someline[\the\c_strc_linenotes]}}
+
+\protected\def\strc_linenotes_start#1[#2]#3%
+ {\global\advance\c_strc_linenotes\plusone
+ \keepunwantedspaces
+ \strc_linenotes_indeed{#1}{#2}{#3}%
+ \strc_linenotes_traced{#2}%
+ \startline[#2]}
+
+\protected\def\strc_linenotes_stop[#1]%
+ {\stopline[#1]}
+
+\let\m_page_lines_previous_to \relax
+\let\m_page_lines_previous_from\relax
+
+\let\m_page_lines_current_to \relax
+\let\m_page_lines_current_from \relax
+
+\newconditional\c_page_lines_current_to
+\newconditional\c_page_lines_current_from
+
+\installcorenamespace{linenotespreviousfrom}
+\installcorenamespace{linenotespreviousto}
+
+\letvalue\??linenotespreviousfrom\empty
+\letvalue\??linenotespreviousto \empty
+
+% maybe do this in lua
+
+\mutable\let\currentlinenotereference\empty
+
+\def\page_lines_in_from{\in[lr:b:\currentlinenotereference]}
+\def\page_lines_in_to {\in[lr:e:\currentlinenotereference]}
+
+\protected\def\strc_linenotes_range_normal#1% order
+ {\doifelsereferencefound{lr:b:\currentlinenotereference}\settrue\setfalse\c_page_lines_current_from
+ \ifconditional\c_page_lines_current_from
+ \xdef\m_page_lines_current_from{\currentreferencelinenumber}%
+ \doifelsereferencefound{lr:e:\currentlinenotereference}\settrue\setfalse\c_page_lines_current_to
+ \ifconditional\c_page_lines_current_to
+ \xdef\m_page_lines_current_to{\currentreferencelinenumber}%
+ \page_lines_in_from
+ \ifx\m_page_lines_current_from\m_page_lines_current_to \else
+ \endash
+ \page_lines_in_to
+ \fi
+ \else
+ \page_lines_in_from
+ \fi
+ \else
+ \page_lines_in_from
+ \fi}
+
+\protected\def\strc_linenotes_range_sparse#1% order
+ {\doifelsereferencefound{lr:b:\currentlinenotereference}\settrue\setfalse\c_page_lines_current_from
+ \ifconditional\c_page_lines_current_from
+ \xdef\m_page_lines_current_from{\currentreferencelinenumber}%
+ \doifelsereferencefound{lr:e:\currentlinenotereference}\settrue\setfalse\c_page_lines_current_to
+ \ifconditional\c_page_lines_current_to
+ \xdef\m_page_lines_current_to{\currentreferencelinenumber}%
+ \ifx\m_page_lines_previous_from\m_page_lines_current_from
+ \ifx\m_page_lines_previous_to\m_page_lines_current_to \else
+ \page_lines_in_from
+ \ifx\m_page_lines_current_from\m_page_lines_current_to\else\endash\page_lines_in_to\fi
+ \fi
+ \else
+ \page_lines_in_from
+ \ifx\m_page_lines_current_from\m_page_lines_current_to\else\endash\page_lines_in_to\fi
+ \fi
+ \else
+ \page_lines_in_from
+ \fi
+ \else
+ \ifx\m_page_lines_previous_from\m_page_lines_current_from \else
+ \page_lines_in_from
+ \fi
+ \fi}
+
+\let\currentlinenotereference\empty
+
+\mutable\let\linenotelinenumber\relax
+
+\protected\def\strc_linenotes_indeed#1#2#3%
+ {\begingroup
+ % we keep things local so we can use it as regular note too
+ \edef\currentnotation{#1}%
+ \edef\currentlinenotereference{#2}%
+ \xdef\m_page_lines_previous_from{\begincsname\??linenotespreviousfrom\currentnotation\endcsname}%
+ \xdef\m_page_lines_previous_to {\begincsname\??linenotespreviousto \currentnotation\endcsname}%
+ \strc_linenotes_check_compression
+ \let\currentnote\currentnotation
+ \letnotationparameter\c!numbercommand\linenotelinenumber % todo: deep hook, beware: should be mutable
+ \letnoteparameter \c!textcommand \gobbleoneargument % todo: deep hook
+ \csname\??linenote\currentnotation\endcsname{#3}%
+ \gletcsname\??linenotespreviousfrom\currentnotation\endcsname\m_page_lines_current_from
+ \gletcsname\??linenotespreviousto \currentnotation\endcsname\m_page_lines_current_to
+ \endgroup}
+
+% compression
+
+\installcorenamespace{linenotescompressmethod}
+
+% compress=yes|no
+% compressmethod=separator|stopper
+
+\setvalue{\??linenotescompressmethod\v!separator}%
+ {\edef\p_compressseparator{\noteparameter\c!compressseparator}%
+ \scratchskip\noteparameter\c!compressdistance\relax
+ \ifempty\p_compressseparator
+ \hskip\scratchskip
+ \else
+ \hskip.5\scratchskip
+ \begingroup\p_compressseparator\endgroup
+ \hskip.5\scratchskip
+ \fi}
+
+\setvalue{\??linenotescompressmethod\v!stopper}%
+ {\edef\p_compressstopper{\noteparameter\c!compressstopper}%
+ \scratchskip\noteparameter\c!compressdistance\relax
+ \ifempty\p_compressstopper
+ \hskip\scratchskip
+ \else
+ \begingroup\p_compressstopper\endgroup
+ \hskip.5\scratchskip
+ \fi}
+
+\setvalue{\??linenotescompressmethod\v!space}%
+ {\hskip\noteparameter\c!compressdistance\relax}
+
+\def\strc_linenotes_check_compression
+ {\edef\p_linenotes_compress {\noteparameter\c!compress}%
+ \edef\p_linenotes_compressmethod{\noteparameter\c!compressmethod}%
+ \ifx\p_linenotes_compress\v!yes
+ \let\linenotelinenumber\strc_linenotes_range_sparse
+ \else
+ \let\linenotelinenumber\strc_linenotes_range_normal
+ \fi
+ \ifcsname\??linenotescompressmethod\p_linenotes_compressmethod\endcsname \else
+ \let\p_linenotes_compressmethod\v!space
+ \fi}
+
+\def\strc_linenotes_inbetween % \ifcsname\??linenote\currentnote\expandafter\endcsname
+ {\begincsname\??linenotescompressmethod\p_linenotes_compressmethod\endcsname}
+
+\def\strc_notes_compress_distance{\emwidth \s!plus .5\emwidth \s!minus .25\emwidth}
+
+\setupnotes
+ [%c\compress=\v!no,
+ \c!compressdistance=\strc_notes_compress_distance,
+ \c!compressseparator=\symbol{\v!compressseparator},
+ \c!compressstopper=\symbol{\v!compressstopper}]
+
+\appendtoks
+ \letnoteparameter\c!inbetween\strc_linenotes_inbetween
+\to \everydefinelinenote
+
+% where to hook this one in? resetcounter has no hook:
+
+\permanent\protected\def\doresetlinenotecompression#1% \strc_linenotes_reset_previous
+ {\gletcsname\??linenotespreviousfrom#1\endcsname\empty
+ \gletcsname\??linenotespreviousto #1\endcsname\empty}
+
+\definesymbol
+ [\v!compressseparator]
+ [\hbox{\vl\thinspace\vl}] % \space removed
+
+\definesymbol
+ [\v!compressstopper]
+ [,]
+
+% \setupnotations
+% [%c\compress=\v!no,
+% \c!compressseparator=\symbol\v!compressseparator]
+
+\let\strc_linenotes_traced\gobbleoneargument
+
+\def\strc_linenotes_traced_indeed#1%
+ {\hpack to \zeropoint
+ {\forgetall
+ \hsize\zeropoint
+ \hss
+ \vpack to \strutheight{\llap{\red\infofont\setstrut\the\c_strc_linenotes}\vss}%
+ {\color[blue]{\vl}}%
+ \vpack to \strutheight{\rlap{\red\infofont\setstrut#1}\vss}%
+ \hss}%
+ \prewordbreak}
+
+\permanent\protected\def\tracelinenotes
+ {\let\strc_linenotes_traced\strc_linenotes_traced_indeed}
+
+% We predefine one, namely \type {\linenote} cum suis.
+
+\definelinenote[\v!linenote]
+
+%D Use these when not properly nested:
+
+\aliased\let\fromlinenote\startlinenote
+\aliased\let\tolinenote \stoplinenote
+
+% beware: line numbers are added later on so grouping setups is a bad idea
+%
+% \startbuffer[test]
+% \startlinenumbering[100]
+% test \linenote {oeps 1} test test test test test test
+% test \startlinenote [well] {oeps X} test test test test test test
+% test \linenote {oeps 2} test test test test test test
+% test \linenote {oeps 3} test test test test test test
+% test \linenote {oeps 4} test test test test test test
+% test \linenote {oeps 5} test test test test test test
+% test \stoplinenote [well] test test test test test test
+% \stoplinenumbering
+% \stopbuffer
+%
+% \typebuffer[test] \getbuffer[test] \page
+%
+% \startbuffer[setup]
+% \setuplinenumbering
+% [align=flushleft]
+% \stopbuffer
+%
+% \typebuffer[setup] \getbuffer[setup,test] \page
+%
+% \startbuffer[setup]
+% \setuplinenumbering
+% [width=4em,
+% distance=1em,
+% align=flushright]
+% \stopbuffer
+%
+% \typebuffer[setup] \getbuffer[setup,test] \page
+%
+% \startbuffer[setup]
+% \setuplinenumbering
+% [width=4em,
+% align=flushleft]
+% \stopbuffer
+%
+% \typebuffer[setup] \getbuffer[setup,test] \page
+%
+% \startbuffer[setup]
+% \setuplinenumbering
+% [width=2em,
+% distance=.5em,
+% align=middle]
+% \stopbuffer
+%
+% \typebuffer[setup] \getbuffer[setup,test] \page
+%
+% \startbuffer[setup]
+% \setuplinenumbering
+% [conversion=romannumerals,
+% start=1,
+% step=1,
+% location=text,
+% style=slanted,
+% color=blue,
+% width=1.5em]
+% \stopbuffer
+%
+% \typebuffer[setup] \getbuffer[setup] \startnarrower\getbuffer[test]\stopnarrower \page
+%
+% \startbuffer[setup]
+% \setuplinenumbering
+% [width=4em,
+% left=--,
+% right=--,
+% align=middle]
+% \stopbuffer
+%
+% \typebuffer[setup] \getbuffer[setup,test] \page
+%
+% \startbuffer[setup-1]
+% \setuplinenumbering
+% [style=\bfxx,
+% command=\WatchThis]
+% \stopbuffer
+%
+% \startbuffer[setup-2]
+% \def\WatchThis#1%
+% {\ifodd\linenumber
+% \definecolor[linecolor][red]%
+% \else
+% \definecolor[linecolor][green]%
+% \fi
+% \inframed
+% [offset=1pt,frame=off,background=color,backgroundcolor=linecolor]
+% {#1}}
+% \stopbuffer
+%
+% \typebuffer[setup-1,setup-2] \getbuffer[setup-1,setup-2,test] \page
+%
+% \startbuffer[setup-1]
+% \setuplinenumbering
+% [location=inright,
+% style=\bfxx,
+% command=\WatchThis]
+% \stopbuffer
+%
+% \typebuffer[setup-1] \getbuffer[setup-1,setup-2,test] \page
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-lst.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-lst.mklx
new file mode 100644
index 00000000000..049a0370e9f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-lst.mklx
@@ -0,0 +1,1574 @@
+%D \module
+%D [ file=strc-lst,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Lists,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Lists}
+
+\registerctxluafile{strc-lst}{}
+
+% clean up in progress ...
+%
+% also (long term) todo:
+%
+% autocrossdocument
+% auto refs to lists (chain)
+%
+% TODO: strut=yes|no
+%
+% \lists -> strc_lists
+
+\unprotect
+
+%D Lists are mostly used for tables of contents but are in fact a rather generic
+%D feature of \CONTEXT. We seperate between storage and rendering and the current
+%D implementation is a reworked version of all that was added in steps. As lists
+%D are used frequently compatibility is an important aspect. A couple of rendering
+%D alternatives are provided here but more are possible.
+
+\installcorenamespace{list}
+
+\installframedcommandhandler \??list {list} \??list
+
+\aliased\let\setuplists\setuplist % yes or no
+
+\setuplist
+ [\c!height=\v!broad,
+ \c!depth=\v!broad,
+ \c!offset=.25\emwidth,
+ \c!state=\v!start,
+ \c!coupling=\v!off,
+ \c!criterium=\v!local,
+ \c!reference=,% was number which was sort of obsolete
+ \c!width=3\emwidth,
+ %\c!maxwidth=,
+ \c!distance=\zeropoint,
+ \c!margin=\zeropoint,
+ \c!alternative=\c!b,
+ \c!style=\v!normal,
+ %\c!color=,
+ \c!textstyle=\listparameter\c!style, % \currentliststyleparameter (but then we need to set it in every ...)
+ \c!numberstyle=\listparameter\c!style, % \currentliststyleparameter
+ \c!pagestyle=\listparameter\c!style, % \currentliststyleparameter
+ \c!textcolor=\listparameter\c!color, % \currentlistcolorparameter (but then we need to set it in every ...)
+ \c!numbercolor=\listparameter\c!color, % \currentlistcolorparameter
+ \c!pagecolor=\listparameter\c!color, % \currentlistcolorparameter
+ \c!numbercommand=\firstofoneargument,
+ \c!textcommand=\firstofoneargument,
+ \c!pagecommand=\firstofoneargument,
+ \c!pagenumber=\v!yes, % better: 'first'
+ \c!headnumber=\v!yes, % better: 'second'
+% \c!sectionnumber=\listparameter\c!headnumber, % use this instead
+ \c!interaction=\v!all, % was \v!sectionnumber, % or make this headnumber (or accept both)
+ \c!label=\v!no,
+ %\c!extras=,
+ %\c!aligntitle=,
+ %\c!before=,
+ %\c!after=,
+ %\c!inbetween=,
+ %\c!symbol=,
+ %\c!expansion=,
+ \c!limittext=\languageparameter\c!limittext] % not used currently
+
+%D Helpers:
+
+\permanent\protected\def\usenestedliststyleandcolor#style#color% will change
+ {\useliststyleandcolor#style#color%
+ % how about style
+ \ifempty\currentcolorparameter \else
+ \resetinteractionparameter\c!color
+ \resetinteractionparameter\c!contrastcolor
+ \fi}
+
+\permanent\protected\def\doifelselist#tag% can also move to \installcommandhandler
+ {\ifcsname\namedlisthash{#tag}\s!parent\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiflistelse\doifelselist
+
+%D Regular list entries are bound to a specific location in order to get the right
+%D pagenumber etc.\ associated. When pushing something inbetween (in mkiv) it ends
+%D up directtly in the list. This is the default because otherwise users will wonder
+%D why spacing might get messed up (due to an unseen but present node). It is
+%D possible to force a location by explicitly setting \type {location} to \type
+%D {here}.
+%D
+%D Another way to force a certain order is to set the \type {order} variable when
+%D placing a list. The \type {command} option only pushes commands into the right
+%D order, and \type {all} orders all entries (which might be too much). In this case
+%D no specific location is needed with the inbetween method. Maybe additional
+%D mechanisms show up some day. See \type {inbetween-001.tex} for an example.
+
+% command : location=none
+% userdata : location=none
+% simple : location=here
+
+\installcorenamespace {listlocations}
+
+% \permanent\protected\def\doifelseinlistentry#1%
+% {\ifcsname\??listlocations#1\endcsname
+% \ifnum\lastnamedcs=\structurelistlocation\relax
+% \doubleexpandafter\firstoftwoarguments
+% \else
+% \doubleexpandafter\secondoftwoarguments
+% \fi
+% \else
+% \expandafter\secondoftwoarguments
+% \fi}
+
+\permanent\protected\def\doifelseinlistentry#1%
+ {\unless\ifcsname\??listlocations#1\endcsname
+ \expandafter\secondoftwoarguments
+ \orelse\ifnum\lastnamedcs=\structurelistlocation\relax
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifelseincurrentlistentry
+ {\doifelseinlistentry\currentlist}
+
+\permanent\protected\def\structurelistinject[#tag]%
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \doifelse{\listparameter\c!state}\v!start\strc_lists_inject_yes\strc_lists_inject_nop}
+
+\tolerant\protected\def\strc_lists_inject_nop[#settings]#spacer[#userdata]%
+ {\endgroup}
+
+\def\strc_lists_inject_enhance#listindex%
+ {\expandafter\clf_deferredenhancelist\number#listindex\relax}
+
+\tolerant\protected\def\strc_lists_inject_yes[#settings]#spacer[#userdata]% can be used directly
+ {\setupcurrentlist[\c!type=userdata,\c!location=\v!none,#settings]% grouped (use \let...
+ \edef\p_location{\listparameter\c!location}%
+ \setnextinternalreference
+ \scratchcounter\clf_addtolist
+ references {
+ internal \locationcount
+ % block {\currentsectionblock}
+ % section structures.sections.currentid()
+ % location {\p_location}
+ }
+ metadata {
+ kind {\listparameter\c!type}
+ name {\currentlist}
+ % level structures.sections.currentlevel()
+ catcodes \catcodetable
+ }
+ userdata {\detokenize\expandafter{\normalexpanded{#userdata}}}
+ \relax
+ \edef\currentlistnumber{\the\scratchcounter}%
+ \setxvalue{\??listlocations\currentlist}{\the\locationcount}%
+ \ifx\p_location\v!here
+ % this branch injects nodes !
+ \strc_lists_inject_enhance{\currentlistnumber}%
+ \clf_setinternalreference
+ internal \locationcount
+ view {\interactionparameter\c!focus}%
+ \relax % this will change
+ \xdef\currentstructurelistattribute{\the\lastdestinationattribute}%
+ \dontleavehmode\hpack attr \destinationattribute \lastdestinationattribute{}% todo
+ \else
+ % and this one doesn't
+ \clf_enhancelist\currentlistnumber\relax
+ \fi
+ \endgroup}
+
+% todo: make like \strc_references_direct_full_user ... with {}{}{}
+
+\protected\def\strc_lists_inject_direct[#tag]% [#settings][#userdata]
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \strc_lists_inject_yes} % [#settings][#userdata]
+
+\permanent\protected\def\writebetweenlist[#tag]%
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \doifelse{\namedlistparameter{#tag}\c!state}\v!start
+ \strc_lists_write_between_yes
+ \strc_lists_write_between_nop}
+
+\tolerant\def\strc_lists_write_between_yes[#settings]#:#command%
+ {\strc_lists_inject_yes[#settings,\c!type=\s!command][command={#command}]}
+
+\tolerant\def\strc_lists_write_between_nop[#settings]#:#command%
+ {\endgroup}
+
+\permanent\protected\def\writedatatolist[#tag]%
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \doifelse{\namedlistparameter{#tag}\c!state}\v!start
+ \strc_lists_write_data_to_yes
+ \strc_lists_write_data_to_nop}
+
+\tolerant\def\strc_lists_write_data_to_yes[#settings]#spacer[#userdata]%
+ {\ifparameter#userdata\or % no need to have an extra step ... used seldom
+ \strc_lists_inject_yes[#settings,\c!type=\s!userdata][#userdata]%
+ \else
+ \strc_lists_inject_yes[\c!type=\s!userdata][#settings]%
+ \fi}
+
+\tolerant\def\strc_lists_write_data_to_nop[#settings]#spacer[#userdata]%
+ {\endgroup}
+
+\permanent\protected\def\writetolist[#tag]%
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \doifelse{\namedlistparameter{#tag}\c!state}\v!start
+ \strc_lists_write_to_yes
+ \strc_lists_write_to_nop}
+
+\tolerant\def\strc_lists_write_to_yes[#settings]#:#first#second% no \s!first because we don't expand user settings
+ {\strc_lists_inject_yes[\c!location=\v!here,#settings,\c!type=\s!simple][first={#first},second={#second}]}
+
+\tolerant\def\strc_lists_write_to_nop[#settings]#:#first#second%
+ {\endgroup} % \strc_lists_inject_nop[][]
+
+%D When placing a list either one or a set can be giving. This makes it possible to
+%D flush for instance an nested (or merged) table of contents. Keep in mind that
+%D placing a list is what we do most (think of tables of contents, figures, etc.\
+%D but other usage is also possible in which case low level commands have to be
+%D used.
+
+\newtoks\everystructurelist
+
+\permanent\tolerant\protected\def\placelist[#taglist]#spacer[#settings]%
+ {\begingroup
+ \startpacked[\v!blank]%
+ \edef\m_list {#taglist}%
+ \edef\m_first{\firststructureelementinlist{#taglist}}%
+ \ifx\m_list\m_first
+ % use settings of first
+ \else
+ % use settings of root
+ \let\m_first\empty
+ \fi
+ \strc_lists_place_indeed\m_first\m_list{#settings}%
+ \stoppacked
+ \endgroup}
+
+\permanent\tolerant\protected\def\placerawlist[#tag]#spacer[#settings]% just one list
+ {\strc_lists_place_indeed\empty{#tag}{#settings}}
+
+\aliased\let\strc_lists_place\placelist % used in strc-flt
+
+\def\strc_lists_place_indeed#tag#list#settings%
+ {\begingroup
+ \the\t_lists_every_renderingcleanup % \let\currentlistentrylocation\empty
+ \edef\currentlist{#tag}%
+ \setupcurrentlist[#settings]%
+ \the\everystructurelist
+ % \doif{\listparameter\c!coupling}\v!on{\startlistreferences{#tag}}%
+ \strc_lists_place_current % maybe inline
+ {#list}%
+ {\listparameter\c!criterium}%
+ {\listparameter\c!reference}%
+ {\listparameter\c!extras}%
+ {\listparameter\c!order}%
+ % \stoplistreferences
+ \par % todo: only when vertical list mode
+ \endgroup
+ \strc_lists_set_mode}
+
+\def\strc_lists_set_mode
+ {\ifcase\structurelistsize\relax
+ \resetsystemmode\v!list
+ \else
+ \setsystemmode \v!list
+ \fi}
+
+%D Complete lists are just lists but with a title. They were originally introduced
+%D to minimize the number for commands in a document source but nowadays that is
+%D less an issue in the sense that the extra few lines are neglectable to the rest.
+
+\ifdefined\startnamedsection \else \let\startnamedsection\relax \fi
+\ifdefined\stopnamedsection \else \let\stopnamedsection \relax \fi
+\ifdefined\headtext \else \let\headtext \relax \fi
+
+\permanent\tolerant\protected\def\completelist[#tag]#spacer[#settings]%
+ {\normalexpanded{\startnamedsection[\v!title][\c!title=\headtext{#tag},\c!reference=#tag]}% {} around ref ?
+ \strc_lists_place[#tag][#settings]%
+ \stopnamedsection}
+
+\permanent\tolerant\protected\def\strc_lists_complete[#singular]#spacer[#plural]#spacer[#settings]% used in strc-flt
+ {\normalexpanded{\startnamedsection[\v!title][\c!title=\headtext{#plural},\c!reference=#singular]}% {} around ref ?
+ \strc_lists_place[#singular][#settings]%
+ \stopnamedsection}
+
+\permanent\tolerant\def\strc_lists_combined_complete[#tag]#spacer[#settings]%
+ {\normalexpanded{\startnamedsection[\v!title][\c!title={\headtext{#tag}},\c!reference=#tag]}%
+ \placecombinedlist[#tag][#settings]%
+ \stopnamedsection}
+
+%D Combined list provide a nice level of abstraction.
+%D
+%D \starttyping
+%D \definecombinedlist[whatever][a,b,c][settings]
+%D \stoptyping
+
+\permanent\tolerant\protected\def\definecombinedlist[#tag]#spacer[#list]#spacer[#settings]%
+ {\definelist[#tag][\c!criterium=\v!local,\c!reference=,\c!alternative=,\c!list={#list},#settings]% inherits from root
+ \frozen\instance\setvalue{\e!setup#tag\e!endsetup}{\setupcombinedlist[#tag]}%
+ \frozen\instance\setvalue{\e!place#tag}{\placecombinedlist[#tag]}%
+ \frozen\instance\setvalue{\e!complete#tag}{\strc_lists_combined_complete[#tag]}}
+
+\permanent\tolerant\protected\def\setupcombinedlist[#tag]#spacer[#settings]%
+ {\ifarguments\or\or
+ \setuplist[#tag][#settings]% we don't want to mess up the parent
+ \fi}
+
+\permanent\tolerant\protected\def\placecombinedlist[#tag]#spacer[#settings]% i.e. no list set in settings
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \setupcurrentlist[#settings]%
+ \edef\m_strc_list_alternative{\listparameter\c!alternative}% we only inherit alternative
+ \strc_lists_place_indeed{#tag}{\listparameter\c!list}{#settings}%
+ \endgroup}
+
+%D Given that some variables are set, we can ask for some properties of
+%D an entry.
+
+\mutable\def\currentstructurelistnumber{0} % injection
+\mutable\def\currentlistmethod {entry} % typesetting
+\mutable\def\currentlistindex {0} % typesetting (maybe also a real counter)
+
+\permanent\protected\def\savedlistnumber #1#2{\clf_savedlistnumber {#1}\numexpr#2\relax}
+\permanent\protected\def\savedlisttitle #1#2{\clf_savedlisttitle {#1}\numexpr#2\relax}
+\permanent\protected\def\savedlistprefixednumber#1#2{\clf_savedlistprefixednumber{#1}\numexpr#2\relax}
+
+\newconditional\c_lists_show_realpage
+
+\installcorenamespace {listpagenumber}
+
+\setvalue{\??listpagenumber\v!always }{\settrue\c_lists_show_page\settrue\c_lists_has_page}
+\setvalue{\??listpagenumber\v!yes }{\settrue\c_lists_show_page}
+\setvalue{\??listpagenumber\s!realpage}{\settrue\c_lists_show_page\settrue\c_lists_show_realpage}
+
+\def\strc_lists_process_pagenumber#1%
+ {\begincsname\??listpagenumber#1\endcsname}
+
+\permanent\def\structurelistlocation
+ {\clf_listlocation\numexpr\currentlistindex\relax}
+
+\permanent\def\structurelistrealpagenumber
+ {\clf_listrealpage{\currentlist}\numexpr\currentlistindex\relax}
+
+\permanent\protected\def\structurelistpagenumber
+ {\dostarttagged\t!listpage\empty
+ \ifconditional\c_lists_show_realpage
+ \clf_listrealpage{\currentlist}\numexpr\currentlistindex\relax
+ \else
+ \clf_listprefixedpage
+ {\currentlist}%
+ \currentlistindex
+ {%
+ separatorset {\listparameter\c!pageprefixseparatorset}%
+ conversionset {\listparameter\c!pageprefixconversionset}%
+ set {\listparameter\c!pageprefixset}%
+ segments {\listparameter\c!pageprefixsegments}%
+ connector {\listparameter\c!pageprefixconnector}%
+ }%
+ {%
+ prefix {\listparameter\c!pageprefix}%
+ conversionset {\listparameter\c!pageconversionset}%
+ starter {\listparameter\c!pagestarter}%
+ stopper {\listparameter\c!pagestopper}%
+ }%
+ \relax
+ \fi
+ \dostoptagged}
+
+\permanent\protected\def\structurelistuservariable#name%
+ {\dostarttagged\t!listdata{#name}%
+ \clf_listuserdata{\currentlist}\currentlistindex{#name}%
+ \dostoptagged}
+
+\permanent\def\rawstructurelistuservariable#name%
+ {\clf_listuserdata{\currentlist}\currentlistindex{#name}}
+
+\permanent\protected\def\structurelistfirst {\structurelistuservariable\s!first } % s!
+\permanent\protected\def\structurelistsecond{\structurelistuservariable\s!second} % s!
+
+\permanent\def\rawstructurelistfirst {\rawstructurelistuservariable\s!first } % s! % was \protected
+\permanent\def\rawstructurelistsecond{\rawstructurelistuservariable\s!second} % s! % was \protected
+
+\permanent\protected\def\doifelsestructurelisthaspage
+ {\clf_doifelselisthaspage{\currentlist}\numexpr\currentlistindex\relax}
+
+\permanent\protected\def\doifelsestructurelisthasnumber
+ {\clf_doifelselisthasnumber{\currentlist}\numexpr\currentlistindex\relax}
+
+\aliased\let\doifstructurelisthaspageelse \doifelsestructurelisthaspage
+\aliased\let\doifstructurelisthasnumberelse\doifelsestructurelisthasnumber
+
+\permanent\protected\def\structurelistgenerictitle
+ {\dostarttagged\t!listcontent\empty
+ \clf_listtitle{\currentlist}\currentlistindex\relax
+ \dostoptagged}
+
+\permanent\protected\def\structurelistgenericnumber % tricky, we need to delay tagging as we have nested lua calls
+ {\dostarttagged\t!listtag\empty
+ \clf_listprefixednumber
+ {\currentlist}%
+ \currentlistindex
+ {%
+ prefix {\listparameter\c!prefix}%
+ separatorset {\listparameter\c!prefixseparatorset}%
+ conversionset {\listparameter\c!prefixconversionset}%
+ starter {\listparameter\c!prefixstarter}%
+ stopper {\listparameter\c!prefixstopper}%
+ set {\listparameter\c!prefixset}%
+ segments {\listparameter\c!prefixsegments}%
+ connector {\listparameter\c!prefixconnector}%
+ }%
+ {%
+ separatorset {\listparameter\c!numberseparatorset}%
+ conversionset {\listparameter\c!numberconversionset}%
+ starter {\listparameter\c!numberstarter}%
+ stopper {\listparameter\c!numberstopper}%
+ segments {\listparameter\c!numbersegments}%
+ }%
+ \relax
+ \dostoptagged}
+
+% TODO: pass extra tag name (contents, figures, bibliography ...)
+
+\protected\def\strc_lists_place_current#list#criterium#reference#extras#order% beware, not a user command
+ {\dostarttaggedchained\t!list\empty\??list
+ \clf_processlist
+ names {#list}
+ criterium {#criterium}
+ reference {#reference}
+ extras {#extras}
+ order {#order}
+ \relax
+ \dostoptagged}
+
+\protected\def\strc_lists_analyze#list#criterium#reference%
+ {\clf_analyzelist
+ names {#list}
+ criterium {#criterium}
+ reference {#reference}
+ \relax}
+
+\permanent\def\firststructureelementinlist#list% expandable
+ {\firstinset{#list}}
+
+\permanent\def\structurelistsize
+ {\clf_listsize}
+
+%D Depending on what kind of list we have (e.g.\ a section related one) processors
+%D can be defined.
+
+% push pop test:
+%
+% \starttext
+% \placelist[chapter] [after={\placelist[section][criterium=local]}]
+% \chapter{One} \section{Alpha} \section{Beta}
+% \chapter{Two} \section{First} \section{Second}
+% \stoptext
+
+\installcorenamespace{structurelistprocessor} % the topmost list handler
+\installcorenamespace{listextra} % control of that handler
+
+\installcommandhandler \??listextra {listextra} \??listextra
+
+\definelistextra % example
+ [\v!page]
+ [\c!before={\showmessage\m!system{14}{\currentlist/\currentlistindex}\page}]
+
+\permanent\protected\def\installstructurelistprocessor#tag#meaning%
+ {\expandafter\normaldef\csname\??structurelistprocessor#tag\endcsname{#meaning}}
+
+\permanent\def\usestructurelistprocessor#tag%
+ {\csname\??structurelistprocessor#tag\endcsname}
+
+\aliased\let\dotaglistlocation\relax
+
+\def\strc_lists_entry_process_default
+ {no list method}
+
+\def\strc_lists_entry_process % assume things to be set up
+ {\listextraparameter\c!before
+ \dostarttagged\t!listitem\currentlist
+ \dotaglistlocation
+ \ifcsname\??structurelistprocessor\currentlist:\currentlistmethod\endcsname\lastnamedcs\orelse
+ \ifcsname\??structurelistprocessor\currentlistmethod \endcsname\lastnamedcs\orelse
+ \ifcsname\??structurelistprocessor\currentlist \endcsname\lastnamedcs\else
+ \strc_lists_entry_process_default\fi
+ \dostoptagged
+ \listextraparameter\c!after}
+
+\permanent\protected\def\strclistsentryprocess#tag#method#index#extra% This one is called at the lua end!
+ {\clf_pushlist#index\relax
+ %\let\currentlistentrylocation\empty
+ \edef\currentlist {#tag}%
+ \edef\currentlistmethod{#method}%
+ \edef\currentlistindex {#index}%
+ \edef\currentlistextra {#extra}%
+ \strc_lists_entry_process
+ \clf_poplist}
+
+% lists that have a number/title are kind of generic and can share code
+
+\installstructurelistprocessor\s!default
+ {\strc_lists_entry_process_default}
+
+\installstructurelistprocessor\s!simple
+ {\let\currentlistentrynumber \structurelistfirst
+ \let\currentlistentrytitle \structurelistsecond
+ \let\currentlistentrypagenumber\structurelistpagenumber
+ \strc_lists_apply_renderingsetup}
+
+\installstructurelistprocessor\s!command
+ {\clf_listuserdata{\currentlist}\currentlistindex{\s!command}}
+
+\installstructurelistprocessor{section}
+ {\let\currentlistentrynumber \structurelistgenericnumber
+ \let\currentlistentrytitle \structurelistgenerictitle
+ \let\currentlistentrypagenumber\structurelistpagenumber
+ \strc_lists_apply_renderingsetup}
+
+\installstructurelistprocessor{number+title}
+ {\let\currentlistentrynumber \structurelistgenericnumber
+ \let\currentlistentrytitle \structurelistgenerictitle
+ \let\currentlistentrypagenumber\structurelistpagenumber
+ \strc_lists_apply_renderingsetup}
+
+% example of usage elsewhere:
+%
+% \installstructcurelistprocessor{pubs:userdata}
+% {\clf_listuserdata{\currentlist}\currentlistindex{bibref}}
+
+%D List symbols are used in interactive documents where no numbers are used but
+%D nevertheless structure is present. Beware, the list symbol macro gets an argument
+%D passed, i.e. when this argument is not picked up, the symbol becomes a kind of
+%D prefix. It's not really a user command (and might even get protected).
+
+\permanent\protected\def\listsymbol[#tag]#number%
+ {\begingroup
+ \edef\currentlist{#tag}%
+ \def\currentlistentrynumber{#number}% no edef else tag problems
+ \currentlistsymbol
+ \endgroup}
+
+%D For historical reasons we're stuck to symbols, so in order to generalize, we have
+%D to hook it into the symbol handle. One way to deal with this is to use a
+%D different key and as it makes sense to use setups instead of def's we use a new
+%D key \quote {renderingsetup} which is the name of a setup.
+
+\def\strc_lists_assign_dimen#dimension#key#default%
+ {\edef\m_strc_list_dimen{\listparameter#key}%
+ \doifelseinset\m_strc_list_dimen{\v!fit,\v!broad}{#dimension#default}{#dimension\m_strc_list_dimen}\relax}
+
+\definesymbol[\v!list][\v!none ][\strc_lists_symbol_none]
+\definesymbol[\v!list][\v!one ][\strc_lists_symbol_one]
+\definesymbol[\v!list][\v!two ][\strc_lists_symbol_two]
+\definesymbol[\v!list][\v!three ][\strc_lists_symbol_three]
+\definesymbol[\v!list][\s!default][\strc_lists_symbol_default]
+\definesymbol[\v!list][\s!unknown][\strc_lists_symbol_unknown]
+
+\permanent\protected\def\currentlistsymbol
+ {\edef\p_symbol{\listparameter\c!symbol}%
+ \doifelseinsymbolset\v!list\p_symbol
+ {\directsymbol\v!list\p_symbol}
+ {\directsymbol\v!list\s!default}}
+
+\protected\def\strc_lists_symbol_none
+ {\strc_lists_assign_dimen\scratchwidth\c!width{1.5\emwidth}%
+ \hpack to \scratchwidth{}}
+
+\protected\def\strc_lists_symbol_one
+ {\strut\symbol[bullet]}
+
+\protected\def\strc_lists_symbol_two
+ {\vrule\s!width\emwidth\s!height\exheight\s!depth\zeropoint\relax}
+
+\protected\def\strc_lists_symbol_three
+ {\begingroup
+ \strc_lists_assign_dimen\scratchwidth \c!width {1.5\emwidth}%
+ \strc_lists_assign_dimen\scratchheight\c!height\exheight
+ \strc_lists_assign_dimen\scratchdepth \c!depth \zeropoint
+ \vrule\s!width\scratchwidth\s!height\scratchheight\s!depth\scratchdepth
+ \endgroup}
+
+\protected\def\strc_lists_symbol_unknown
+ {\listparameter\c!symbol}
+
+\installcorenamespace{listsymbollabels}
+
+\def\strc_lists_symbol_label_unknown
+ {\leftlabeltext\currentlistlabel
+ \listparameter\c!starter
+ \currentlistentrynumber
+ \listparameter\c!stopper
+ \rightlabeltext\currentlistlabel}
+
+\mutable\let\currentlistnumber\!!zerocount
+\mutable\let\currentlistlabel \empty
+
+\protected\def\strc_lists_symbol_default
+ {\dontleavehmode
+ \strut
+ \begingroup
+ \edef\currentlistlabel{\listparameter\c!label}% can be used in label
+ \ifcsname\??listsymbollabels\currentlistlabel\endcsname
+ \lastnamedcs
+ \else
+ \strc_lists_symbol_label_unknown
+ \fi
+ \endgroup}
+
+\letvalue{\??listsymbollabels\s!unknown}\strc_lists_symbol_default
+
+\setvalue{\??listsymbollabels}% default (empty)
+ {\listparameter\c!starter
+ \currentlistentrynumber
+ \listparameter\c!stopper}
+
+\setvalue{\??listsymbollabels\v!no}% also default
+ {\listparameter\c!starter
+ \currentlistentrynumber
+ \listparameter\c!stopper}
+
+\setvalue{\??listsymbollabels\v!none}% real minimal (as suggested by WS)
+ {\currentlistentrynumber}
+
+\setvalue{\??listsymbollabels\v!yes}% auto (use value stored in tuc file)
+ {\edef\currentlistlabel{\clf_listlabel\currentlistindex{\currentlistlabel}}%
+ \leftlabeltext\currentlistlabel
+ \listparameter\c!starter
+ \currentlistentrynumber
+ \listparameter\c!stopper
+ \rightlabeltext\currentlistlabel}
+
+% a : nr - tit - pag
+% b : nr - tit - fill - pag
+% c : nr - tit - dots - pag
+% d : inline
+% e : interaction
+% f : interaction
+% g : interaction
+
+\installcorenamespace{listalternative} % specific ways of rendering a list
+\installcorenamespace{listrenderings} % a namespace for setups (rather local)
+
+\installcommandhandler \??listalternative {listalternative} \??listalternative
+
+% Commands are bound to specific list instances as often these are quite special
+% and don't apply to multiple. So, being strict saves us resets.
+
+% \installcorenamespace{listfiller}
+%
+% \protected\def\installlistfiller#1#2%
+% {\setuvalue{\??listfiller#1}{#2}}
+%
+% \protected\def\listfiller#1%
+% {\begincsname\??listfiller#1\endcsname}
+%
+% \protected\def\currentlistfiller
+% {\begingroup
+% \edef\p_filler{\listalternativeparameter\c!filler}%
+% \ifcsname\??listfiller\p_filler\endcsname
+% \lastnamedcs
+% \else
+% \p_filler
+% \fi
+% \endgroup}
+%
+% \installlistfiller\v!sym % original one
+% {\begingroup
+% \scratchdimen.5\emwidth
+% \hskip\scratchdimen
+% \gleaders
+% \hbox to \scratchdimen
+% {\hss
+% \uselistalternativestyleandcolor\c!symstyle\c!symcolor
+% \listalternativeparameter\c!symbol
+% \hss}%
+% \hfill
+% \hskip\scratchdimen
+% \endgroup}
+%
+% \installlistfiller\v!symbol % new one (make that default?)
+% {\begingroup
+% \scratchdimen.5\emwidth
+% \hskip\scratchdimen
+% \gleaders
+% \hbox spread .5\scratchdimen
+% {\hss
+% \uselistalternativestyleandcolor\c!symstyle\c!symcolor
+% \listalternativeparameter\c!symbol
+% \hss}%
+% \hfill
+% \hskip\scratchdimen
+% \endgroup}
+%
+% \installlistfiller\v!width
+% {\hfill}
+%
+% \installlistfiller\v!space
+% {\hskip.25\emwidth\relax}
+
+\setuplistalternative
+ [\c!command=\directlistparameter\c!command,
+ \c!symbol=.]
+
+\permanent\protected\def\currentlistfiller
+ {\checkedfiller{\listalternativeparameter\c!filler}}
+
+\definelistalternative
+ [a]
+ [\c!distance=0pt,
+ \c!width=2em,
+ \c!stretch=10em,
+ \c!filler=\hskip.25em\relax,
+ \c!renderingsetup=\??listrenderings:abc]
+
+\definelistalternative
+ [b]
+ [\c!distance=5em,
+ \c!width=2em,
+ \c!stretch=10em,
+ \c!filler=\hfill,
+ \c!renderingsetup=\??listrenderings:abc]
+
+\definelistalternative
+ [c]
+ [\c!distance=5em,
+ \c!width=0pt,
+ \c!stretch=10em,
+ \c!filler=\hskip.5em\gleaders\hbox to .5\emwidth{\hss.\hss}\hfill\hskip.5em\relax,
+ \c!renderingsetup=\??listrenderings:abc]
+
+\definelistalternative
+ [d]
+ [\c!renderingsetup=\??listrenderings:d]
+
+\definelistalternative
+ [e]
+ [\c!renderingsetup=\??listrenderings:e]
+
+\definelistalternative
+ [f]
+ [\c!renderingsetup=\??listrenderings:f]
+
+\definelistalternative
+ [g]
+ [\c!renderingsetup=\??listrenderings:g]
+
+\definelistalternative
+ [\v!command]
+ [\c!renderingsetup=\??listrenderings:command]
+
+\definelistalternative
+ [\v!none]
+ [\c!renderingsetup=\??listrenderings:none]
+
+\definelistalternative
+ [\v!vertical]
+ [\c!before=\ifvmode\nointerlineskip\fi,
+ \c!after=\ifvmode\nointerlineskip\fi\endgraf\allowbreak,
+ \c!renderingsetup=\??listrenderings:generic]
+
+\definelistalternative
+ [\v!horizontal]
+ [\c!before=\noindent,
+ \c!after=,
+ \c!renderingsetup=\??listrenderings:generic]
+
+% \setuplist
+% [section]
+% [alternative=MyListItem,
+% after=\blank,
+% before=\blank]
+%
+% \definelistplacement[MyListItem][command]#1#2#3{(#1) (#2) (#3)}
+% \definelistplacement[MyListItem][command]{\whatever}
+%
+% this is a compatibility command, best use the regular defined with command=
+% either set in the alternative or in the list
+
+\installcorenamespace{listelementcommand} % the old plugin model
+
+\permanent\tolerant\protected\def\definelistplacement[#tag]#spacer[#method]%
+ {\edef\p_method{#method}%
+ \ifempty\p_method
+ \let\p_method\v!command
+ \fi
+ \normalexpanded{\definelistalternative[#tag][\p_method]}[\c!command=\strc_lists_placement_command]%
+ \doifelsenextbgroup
+ {\strc_lists_define_placement_yes{#tag}}
+ {\strc_lists_define_placement_nop{#tag}}}
+
+% indirect definition: <three ignore arguments>: {\bla}
+
+\def\strc_lists_define_placement_yes#tag%
+ {\protected\defcsname\??listelementcommand#tag\endcsname##1##2##3}
+
+% direct definition: <three arguments>{\bla}
+
+\def\strc_lists_define_placement_nop#tag%
+ {\protected\defcsname\??listelementcommand#tag\endcsname}
+
+\def\strc_lists_placement_command
+ {\csname\??listelementcommand\currentlistalternative\endcsname}
+
+%D The rendering macros.
+
+\newbox \b_strc_lists_number
+\newbox \b_strc_lists_text
+\newbox \b_strc_lists_page
+
+\newtoks \t_lists_every_renderingsetup
+\newtoks \t_lists_every_renderingtext
+\newtoks \t_lists_every_renderingsynchronize
+\newtoks \t_lists_every_renderingcleanup
+
+\newconditional\c_lists_has_number
+\newconditional\c_lists_has_page
+\newconditional\c_lists_show_number
+\newconditional\c_lists_show_page
+
+\mutable\let\currentlistentrylocation \empty % watch the 'entry' in the name
+\mutable\let\currentlistentrynumber \empty % watch the 'entry' in the name
+\mutable\let\currentlistentrytitle \empty % watch the 'entry' in the name
+\mutable\let\currentlistentrypagenumber\empty % watch the 'entry' in the name
+
+\appendtoks
+ \dontcomplain
+ % \letinteractionparameter\c!width\zeropoint % a weird one
+\to \t_lists_every_renderingsetup
+
+\appendtoks
+ % better is to use a special list entry but we keep this for compatibility
+ \enforced\let\\\space
+ % so expanding this token register has to come *after* the font switch
+ \dontconvertfont % (**) this has to become an option (see publ)
+\to \t_lists_every_renderingtext
+
+\appendtoks
+ % because we want to avoid redundant lua calls we expand the
+ % location beforehand
+ \ifempty\currentlistentrylocation
+ \enforced\edef\currentlistentrylocation{\structurelistlocation}% needs attention
+ \fi
+ % because these tests happen often and because we're dealing with
+ % rather complex composed data we have special conditionals; keep
+ % in mind that testing for empty fails do to tagging being applied
+% \edef\p_pagenumber{\listparameter\c!pagenumber}%
+% \ifx\p_pagenumber\v!always
+% \settrue\c_lists_has_page
+% \settrue\c_lists_show_page
+% \else
+% \doifelsestructurelisthaspage\settrue\setfalse\c_lists_has_page
+% \ifx\p_pagenumber\v!yes
+% \settrue\c_lists_show_page
+% \else
+% \setfalse\c_lists_show_page
+% \fi
+% \fi
+ \setfalse\c_lists_show_page % necessary?
+ \processcommacommand[\listparameter\c!pagenumber]\strc_lists_process_pagenumber
+ \ifconditional\c_lists_has_page \else
+ \doifelsestructurelisthaspage\settrue\setfalse\c_lists_has_page
+ \fi
+ % always forces number placement (in bib we use a forced number)
+ \edef\p_headnumber{\listparameter\c!headnumber}%
+ \ifx\p_headnumber\v!always
+ \settrue\c_lists_has_number
+ \settrue\c_lists_show_number
+ \else
+ \doifelsestructurelisthasnumber\settrue\setfalse\c_lists_has_number
+ \ifx\p_headnumber\v!yes
+ \settrue\c_lists_show_number
+ \else
+ \setfalse\c_lists_show_number
+ \fi
+ \fi
+ \strc_lists_interaction_check
+\to \t_lists_every_renderingsetup
+
+\appendtoks
+ \strc_references_flush_destination_nodes
+\to \t_lists_every_renderingsynchronize
+
+\appendtoks
+ % as we don't want any interference we clear some variables
+ % afterwards
+ \let\currentlistentrylocation \empty
+ \let\currentlistentrynumber \empty
+ \let\currentlistentrytitle \empty
+ \let\currentlistentrypagenumber\empty
+ \setfalse\c_lists_has_page
+ \setfalse\c_lists_has_number
+ \setfalse\c_lists_show_page
+ \setfalse\c_lists_show_realpage
+ \setfalse\c_lists_show_number
+\to \t_lists_every_renderingcleanup
+
+\let\m_strc_list_alternative\empty % combined
+
+\protected\def\strc_lists_apply_renderingsetup
+ {\the\t_lists_every_renderingsetup
+ % now we group
+ \begingroup
+ \ifempty\m_strc_list_alternative
+ \edef\currentlistalternative{\listparameter\c!alternative}%
+ \else
+ \let\currentlistalternative\m_strc_list_alternative
+ \fi
+ \directsetup{\listalternativeparameter\c!renderingsetup}\relax
+ \endgroup
+ % till here, where we reset locals
+ \the\t_lists_every_renderingcleanup}
+
+% todo: provide packager via attributes
+
+\doinstallinjector\s!list
+
+\installcorenamespace{listalternativemethods} % the general wrapper of a rendering
+
+\startsetups[\??listrenderings:none]
+ % nothing, nb we use the [] syntax here because we end with a \cs
+\stopsetups
+
+\permanent\protected\def\currentlistentrytitlesynchronize
+ {\the\t_lists_every_renderingsynchronize}
+
+\permanent\protected\def\currentlistentrytitlerendered
+ {\currentlistentrytitlesynchronize\currentlistentrytitle}
+
+\startsetups[\??listrenderings:command]
+ \edef\p_command{\listalternativeparameter\c!command}%
+ \ifempty\p_command
+ [\currentlist: \currentlistentrynumber\space -- \currentlistentrytitle\space -- \currentlistentrypagenumber]%
+ \else
+ \p_command
+ \currentlistentrynumber
+ \currentlistentrytitlerendered % {\currentlistentrytitlesynchronize\currentlistentrytitle}
+ \currentlistentrypagenumber
+ \fi
+\stopsetups
+
+% \startsetups[\??listrenderings:\v!vertical]
+% \directsetup{\??listrenderings:generic}
+% \stopsetups
+
+% \startsetups[\??listrenderings:\v!horizontal]
+% \directsetup{\??listrenderings:generic}
+% \stopsetups
+
+\startsetups[\??listrenderings:generic]
+ \typo_injectors_check_list
+ \listparameter\c!before % can be \hskip
+ \edef\p_command{\listalternativeparameter\c!command}
+ \ifempty\p_command
+ \listalternativeparameter\c!before
+ \vbox {
+ \forgetall
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all {
+ \ifconditional\c_lists_show_number
+ % \ifconditional\c_lists_has_page
+ \hbox \strc_lists_get_reference_attribute\v!number {
+ \strc_lists_set_style_color\c!numberstyle\c!numbercolor\v!number
+ \listparameter\c!numbercommand\currentlistsymbol
+ }
+ % \fi
+ \fi
+ \hbox \strc_lists_get_reference_attribute\v!text {
+ \strc_lists_set_style_color\c!textstyle\c!textcolor\v!text
+ \the\t_lists_every_renderingtext
+ \the\t_lists_every_renderingsynchronize
+ \listparameter\c!textcommand\currentlistentrytitle
+ }
+ \ifconditional\c_lists_show_page
+ \ifconditional\c_lists_has_page
+ \hbox \strc_lists_get_reference_attribute\v!pagenumber {
+ \strc_lists_set_style_color\c!pagestyle\c!pagecolor\v!pagenumber
+ \listparameter\c!pagecommand\currentlistentrypagenumber
+ }
+ \fi
+ \fi
+ }
+ }
+ \listalternativeparameter\c!after
+ \else
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute {
+ \p_command\currentlistentrynumber\currentlistentrytitle\currentlistentrypagenumber
+ }
+ \fi
+ \listparameter\c!after
+\stopsetups
+
+% to be documented: align, hang
+
+\startsetups[\??listrenderings:abc]
+ \endgraf % are we grouped?
+ \typo_injectors_check_list
+ % \advance % yes or no ... \rightskip is also honored
+ \leftskip\listparameter\c!margin % after \endgraf !
+ \listparameter\c!before
+ \endgraf
+ \edef\p_width{\listparameter\c!width}
+ \scratchdistance\listparameter\c!distance\relax
+ \ifx\p_width\v!fit
+ \scratchwidth\zeropoint
+ \orelse\ifconditional\c_lists_has_number
+ \scratchwidth\p_width
+ \else
+ \edef\p_aligntitle{\listparameter\c!aligntitle}
+ \ifx\p_aligntitle\v!yes
+ \scratchwidth\zeropoint
+ \scratchdistance\zeropoint
+ \else
+ \scratchwidth\p_width
+ \fi
+ \fi
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute {
+ \setlocalhsize
+ \hsize\localhsize
+ \hbox to \hsize {
+ \forgetall
+ \strc_lists_set_style_color\c!style\c!color\v!all
+ \scratchhsize\hsize
+ \ifconditional\c_lists_has_number
+ \ifconditional\c_lists_show_number
+ \setbox\b_strc_lists_number
+% \hbox
+% \strc_lists_get_reference_attribute\v!number
+% \ifdim\scratchwidth>\zeropoint to \scratchwidth \fi
+ \simplealignedboxplus
+ \scratchwidth
+ {\listparameter\c!numberalign}
+ {\strc_lists_get_reference_attribute\v!number}
+ {
+ \strc_lists_set_style_color\c!numberstyle\c!numbercolor\v!number
+ \listparameter\c!numbercommand\currentlistsymbol
+% \hfill
+ }
+ \else
+ \setbox\b_strc_lists_number\emptyhbox
+ \fi
+ \else
+ \scratchwidth\zeropoint
+ \scratchdistance\zeropoint
+ \setbox\b_strc_lists_number\emptyhbox
+ \fi
+ \ifconditional\c_lists_has_page
+ \ifconditional\c_lists_show_page
+ \setbox\b_strc_lists_page\hpack {
+ \scratchdimen\listalternativeparameter\c!width
+ \hbox \strc_lists_get_reference_attribute\v!pagenumber \ifdim\scratchdimen>\zeropoint to \scratchdimen\fi {
+ \hfill
+ \strc_lists_set_style_color\c!pagestyle\c!pagecolor\v!pagenumber
+ \strut
+ \listparameter\c!pagecommand\currentlistentrypagenumber
+ }
+ }
+ \else
+ \setbox\b_strc_lists_page\emptyhbox
+ \fi
+ \else
+ \setbox\b_strc_lists_page\emptyhbox
+ \fi
+ \vbox {
+ \hsize\scratchhsize
+ \usealignparameter\listparameter
+ \ifdim\scratchwidth<\hsize
+ % we have leftskip so we'd better just skip back instead of messing
+ % with hang*
+ \edef\p_hang{\listparameter\c!hang}
+ \hangindent\dimexpr\wd\b_strc_lists_number+\scratchdistance\relax
+ \hangafter\ifx\p_hang\v!no\zerocount\else\plusone\fi
+ \scratchdimen\listalternativeparameter\c!distance\relax
+ \ifzeropt\wd\b_strc_lists_page\orelse\ifdim\scratchdimen>\zeropoint\relax
+ \rightskip\scratchdimen\s!plus\listalternativeparameter\c!stretch\relax
+ \parfillskip-\rightskip
+ \fi
+ \else
+ \scratchdistance\zeropoint
+ \fi
+ \parindent\zeropoint
+ \dontleavehmode % this nils hang: i need to figure out why
+ % % topaligned
+ %
+ % \scratchdimen\wd\b_strc_lists_number
+ % \setbox\b_strc_lists_number\hbox to \hsize{\box\b_strc_lists_number\hss\box\b_strc_lists_page}%
+ % \wd\b_strc_lists_number\scratchdimen
+ %
+ \box\b_strc_lists_number
+ \hskip\scratchdistance\relax
+ \begingroup
+ \strc_lists_set_reference_attribute\v!text
+ \strc_lists_set_style_color\c!textstyle\c!textcolor\v!text
+ \the\t_lists_every_renderingtext
+ \the\t_lists_every_renderingsynchronize
+ \setstrut % needs checking, new here
+ \begstrut
+ \strc_lists_limitated_text\currentlistentrytitle
+ \endstrut
+ \endgroup
+ \ifzeropt\wd\b_strc_lists_page\else
+ \nobreak
+ \currentlistfiller
+ \box\b_strc_lists_page
+ \fi
+ }
+ \hss
+ }
+ }% new
+ \endgraf % new, else problems with nointerlinespace and prevdepth
+ \nointerlineskip % anders verkeerde spatiering bij multi-line
+ \endgraf
+ \allowbreak
+ \listparameter\c!after
+\stopsetups
+
+% % example from the context list
+%
+% \setuphead [part] [page=right,placehead=yes]
+% \setuplist [chapter] [alternative=d,before=\blank,after=\blank]
+% \setuplist [part] [before=\blank,after=\blank]
+%
+% \starttext
+% \startnarrower[2*right] \placecontent \stopnarrower
+% \blank[4*big]
+% \startsetups chapter
+% \blank \startnarrower[3*middle] \placecontent[criterium=local] \stopnarrower
+% \stopsetups
+% \placelist[part][criterium=text,after=\setups{chapter}]
+%
+% \part{First part} \chapter{Chapter one} \chapter{Chapter two}
+% \chapter{Chapter three} \chapter{Chapter four} \chapter{Chapter five}
+% \part{Second part} \chapter{Chapter one} \chapter{Chapter two}
+% \chapter{Chapter three} \chapter{Chapter four} \chapter{Chapter five}
+% \part{Third part} \chapter{Chapter one} \chapter{Chapter two}
+% \chapter{Chapter three} \chapter{Chapter four} \chapter{Chapter five}
+% \stoptext
+
+\startsetups[\??listrenderings:d]
+ \ifvmode
+ \advance\leftskip\listparameter\c!margin
+ \fi
+ \begingroup
+ \ifvmode
+ \noindent
+ \fi
+ \begingroup
+ \strc_lists_set_reference_attribute\v!all
+ \strc_lists_set_style_color\c!style\c!color\v!all
+ \strc_lists_get_destination_attribute
+ \begingroup
+ \ifconditional\c_lists_show_number
+ \donetrue
+ \ifconditional\c_lists_has_number \else
+ \edef\p_symbol{\listparameter\c!symbol}
+ \ifempty\p_symbol
+ \donefalse
+ \fi
+ \fi
+ \ifdone
+ \begingroup
+ \strc_lists_set_reference_attribute\v!number
+ \strc_lists_set_style_color\c!numberstyle\c!numbercolor\v!number
+ \listparameter\c!left
+ \listparameter\c!numbercommand\currentlistsymbol
+ \listparameter\c!right
+ \endgroup
+ \kern.5\emwidth\relax
+ \nobreak
+ \fi
+ \fi
+ \endgroup
+ \begingroup
+ \strc_lists_set_reference_attribute\v!text
+ \strc_lists_set_style_color\c!textstyle\c!textcolor\v!text
+ \the\t_lists_every_renderingtext
+ \the\t_lists_every_renderingsynchronize
+ \setstrut % needs checking, new here
+ \begstrut
+ \strc_lists_limitated_text\currentlistentrytitle
+ \endstrut
+ \endgroup
+ \begingroup
+ \ifconditional\c_lists_has_page
+ \ifconditional\c_lists_show_page
+ \nobreak
+ \hskip.75\emwidth\relax
+ \nobreak
+ \strc_lists_set_reference_attribute\v!pagenumber
+ \strc_lists_set_style_color\c!pagestyle\c!pagecolor\v!pagenumber
+ \strut
+ \listparameter\c!pagecommand\currentlistentrypagenumber
+ \fi
+ \fi
+ \endgroup
+ \scratchdistance\listparameter\c!distance\relax
+ \ifdim\scratchdistance<\emwidth
+ \hskip\emwidth\s!plus\emwidth\s!minus.25\emwidth\relax
+ \else
+ \hskip\scratchdistance\s!plus.5\scratchdistance\s!minus.25\scratchdistance\relax
+ \fi
+ \endgroup
+ \endgroup
+\stopsetups
+
+\startsetups[\??listrenderings:e]
+ \typo_injectors_check_list
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute {
+ \letlistparameter\c!depth\zeropoint
+ \letlistparameter\c!color\empty
+ \inheritedlistframed {
+ \letinteractionparameter\c!strut\v!no % still needed?
+ \strc_lists_set_style_color\c!style\c!color\v!all
+ \the\t_lists_every_renderingtext
+ \the\t_lists_every_renderingsynchronize
+ \setstrut
+ \begstrut
+ \strc_lists_limitated_text\currentlistentrytitle
+ \endstrut
+ }
+ }
+ \par
+ \listparameter\c!inbetween
+\stopsetups
+
+\startsetups[\??listrenderings:f]
+ \typo_injectors_check_list
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute {
+ \dosetraggedhbox{\listparameter\c!align}%
+ \raggedbox {
+ \strc_lists_set_style_color\c!style\c!color\v!all
+ \the\t_lists_every_renderingtext
+ \the\t_lists_every_renderingsynchronize
+ \setstrut
+ \begstrut
+ \strc_lists_limitated_text\currentlistentrytitle
+ \endstrut
+ }
+ }
+ \par
+ \listparameter\c!inbetween
+\stopsetups
+
+\startsetups[\??listrenderings:g]
+ \typo_injectors_check_list
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute {
+ \midaligned {
+ \strc_lists_set_style_color\c!style\c!color\v!all
+ \the\t_lists_every_renderingtext
+ \the\t_lists_every_renderingsynchronize
+ \setstrut
+ \begstrut
+ \strc_lists_limitated_text\currentlistentrytitle
+ \endstrut
+ }
+ }
+ \par
+ \listparameter\c!inbetween
+\stopsetups
+
+%D This is a new one, similar to vertical and horizontal but better suited when
+%D no command is set (WS):
+
+\definelistalternative
+ [\v!interactive]
+ [\c!renderingsetup=\??listrenderings:interactive,
+ \c!before=\endgraf, % new per 2014-11-08
+ \c!after=\endgraf] % new per 2014-11-08
+
+\startsetups[\??listrenderings:interactive]
+ \edef\p_command{\listalternativeparameter\c!command}%
+ \typo_injectors_check_list
+ \listparameter\c!before
+ \noindent % otherwise annotations are mirrored up
+ \typo_injectors_mark_list
+ \hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute {
+ \ifempty\p_command
+ [
+ \currentlist:\space
+ \currentlistentrynumber
+ \space\emdash\space
+ \currentlistentrytitle
+ \space\emdash\space
+ \currentlistentrypagenumber
+ ]
+ \else
+ \p_command\currentlistentrynumber\currentlistentrytitle\currentlistentrypagenumber
+ \fi
+ }
+ \listparameter\c!after
+\stopsetups
+
+%D One special for publications (as Alan loves to hangindent). No fonts and such
+%D (for now). No interaction either as that is dealt with elsewhere.
+%D
+%D \currentlistsymbol
+%D \currentlistentrynumber
+%D \currentlistentrytitle
+%D \currentlistentrypagenumber % not really used
+
+\definelistalternative
+ [\v!paragraph]
+ [\c!filler=\v!space,
+ \c!renderingsetup=\??listrenderings:\v!paragraph]
+
+\startsetups[\??listrenderings:\v!paragraph]
+ \endgraf % are we grouped?
+ \typo_injectors_check_list % ?
+ \listparameter\c!before
+ \endgraf
+ \begingroup
+ \forgetall
+ \noindent
+ \parindent\zeropoint
+ \edef\p_width{\listparameter\c!width}%
+ \edef\p_distance{\listparameter\c!distance}% we are nice for bib users
+ \edef\p_margin{\listparameter\c!margin}% we are nice for bib users
+ \ifx\p_distance\v!none
+ \scratchdistance\zeropoint
+ \else
+ \scratchdistance\p_distance
+ \fi
+ \ifx\p_margin\v!none
+ \scratchoffset\zeropoint
+ \else
+ \scratchoffset\p_margin
+ \fi
+ \ifx\p_width\v!fit
+ \scratchwidth\zeropoint
+ \leftskip\scratchoffset
+ \else
+ \scratchwidth\p_width
+ \ifzeropt\scratchoffset
+ \leftskip\dimexpr\scratchwidth+\scratchdistance\relax
+ \else
+ \leftskip\scratchoffset
+ \fi
+ \fi
+ \usealignparameter\listparameter
+ \hskip-\leftskip
+ \ifconditional\c_lists_has_number
+ \ifconditional\c_lists_show_number
+ \setbox\scratchbox
+ \simplealignedbox\scratchwidth{\listparameter\c!numberalign}
+ \bgroup
+ \useliststyleandcolor\c!numberstyle\c!numbercolor
+ \currentlistsymbol
+ \egroup
+ \ifdim\wd\scratchbox>\zeropoint
+ \box\scratchbox
+ \hskip\scratchdistance\relax
+ \fi
+ \fi
+ \fi
+ \begingroup
+ \useliststyleandcolor\c!textstyle\c!textcolor
+ \setstrut
+ \begstrut
+ \currentlistentrytitle
+ \endstrut
+ \endgroup
+ \ifconditional\c_lists_has_page
+ \ifconditional\c_lists_show_page
+ \nobreak
+ \currentlistfiller
+ \begingroup
+ \useliststyleandcolor\c!pagestyle\c!pagecolor
+ \currentlistentrypagenumber
+ \endgroup
+ \fi
+ \fi
+ \endgraf
+ \endgroup
+ \allowbreak
+ \listparameter\c!after
+\stopsetups
+
+%D List elements are packaged in such a way that we can click on them in an
+%D interactive document. Here are a few helpers.
+
+\newconstant\a_strc_lists_reference
+\newconstant\a_strc_lists_destination
+
+\installcorenamespace{listinteractions}
+
+\letvalue{\??listinteractions\v!number }\v!number
+\letvalue{\??listinteractions\v!sectionnumber}\v!number
+\letvalue{\??listinteractions\v!text }\v!text
+\letvalue{\??listinteractions\v!title }\v!text
+\letvalue{\??listinteractions\v!page }\v!pagenumber
+\letvalue{\??listinteractions\v!pagenumber }\v!pagenumber
+\letvalue{\??listinteractions\v!all }\v!all
+\letvalue{\??listinteractions\v!yes }\v!all
+
+\permanent\def\listboxproperties {\strc_lists_get_reference_attribute}
+\permanent\def\listrenderingsetup {\the\t_lists_every_renderingtext}
+\permanent\def\listrenderingsynchronize{\the\t_lists_every_renderingsynchronize}
+
+\protected\def\strc_lists_interaction_check
+ {\iflocation
+ \strc_lists_interaction_check_yes
+ \else
+ \strc_lists_interaction_check_nop
+ \fi}
+
+\def\strc_lists_interaction_check_yes_yes
+ {\edef\p_interaction_forward{\listparameter\c!interaction}%
+ \ifcsname\??listinteractions\p_interaction_forward\endcsname
+ %\expandafter\let\expandafter\p_interaction_forward\csname\??listinteractions\p_interaction_forward\endcsname
+ \expandafter\let\expandafter\p_interaction_forward\lastnamedcs
+ \strc_references_get_simple_reference{internal(\currentlistentrylocation)}%
+ \a_strc_lists_reference\currentreferenceattribute
+ \else
+ \a_strc_lists_reference\attributeunsetvalue
+ \fi
+ \ifnum\a_strc_lists_reference=\attributeunsetvalue
+ \let\strc_lists_get_reference_attribute\gobbleoneargument
+ \let\strc_lists_set_reference_attribute\gobbleoneargument
+ \let\strc_lists_set_style_color \strc_lists_set_style_color_normal
+ \else
+ \let\strc_lists_get_reference_attribute\strc_lists_get_reference_attribute_indeed
+ \let\strc_lists_set_reference_attribute\strc_lists_set_reference_attribute_indeed
+ \let\strc_lists_set_style_color \strc_lists_set_style_color_special
+ \fi
+ \edef\p_interaction_backward{\namedheadparameter\currentlist\c!interaction}% \namedheadparameter !
+ \ifx\p_interaction_backward\v!list
+ \strc_references_set_simple_reference{*\currentlistentrylocation}%
+ \a_strc_lists_destination\currentdestinationattribute
+ \else
+ \a_strc_lists_destination\attributeunsetvalue
+ \fi
+ \ifnum\a_strc_lists_destination=\attributeunsetvalue
+ \let\strc_lists_get_destination_attribute\empty
+ \let\strc_lists_set_destination_attribute\empty
+ \else
+ \let\strc_lists_get_destination_attribute\strc_lists_get_destination_attribute_indeed
+ \let\strc_lists_set_destination_attribute\strc_lists_set_destination_attribute_indeed
+ \fi}
+
+\def\strc_lists_interaction_check_yes_nop
+ {\a_strc_lists_reference \attributeunsetvalue
+ \a_strc_lists_destination\attributeunsetvalue
+ \let\strc_lists_get_reference_attribute\gobbleoneargument
+ \let\strc_lists_set_reference_attribute\gobbleoneargument
+ \let\strc_lists_get_destination_attribute\empty
+ \let\strc_lists_set_destination_attribute\empty
+ \let\strc_lists_set_style_color\strc_lists_set_style_color_normal}
+
+\def\strc_lists_interaction_check_yes
+ {\ifempty\currentlistentrylocation
+ \strc_lists_interaction_check_yes_nop
+ \orelse\ifnum\currentlistentrylocation=\zerocount
+ \strc_lists_interaction_check_yes_nop
+ \else
+ \strc_lists_interaction_check_yes_yes
+ \fi}
+
+\def\strc_lists_interaction_check_nop
+ {\let\strc_lists_get_reference_attribute \gobbleoneargument
+ \let\strc_lists_set_reference_attribute \gobbleoneargument
+ \let\strc_lists_get_destination_attribute\empty
+ \let\strc_lists_set_destination_attribute\empty
+ \let\strc_lists_set_style_color \strc_lists_set_style_color_normal}
+
+\strc_lists_interaction_check_nop
+
+\def\strc_lists_get_reference_attribute_indeed#element%
+ {\ifx#element\p_interaction_forward
+ attr \referenceattribute\a_strc_lists_reference
+ \fi}
+
+\def\strc_lists_set_reference_attribute_indeed#element%
+ {\ifx#element\p_interaction_forward
+ \c_attr_reference\a_strc_lists_reference
+ \fi}
+
+\def\strc_lists_get_destination_attribute_indeed
+ {attr \destinationattribute\number\a_strc_lists_destination}
+
+\def\strc_lists_set_destination_attribute_indeed
+ {\c_attr_destination\a_strc_lists_destination}
+
+\protected\def\strc_lists_set_style_color_normal#style#color#element%
+ {\useliststyleandcolor#style#color}
+
+\protected\def\strc_lists_set_style_color_special#style#color#element%
+ {\useliststyleandcolor#style#color%
+ \ifempty\currentcolorparameter
+ \ifx#element\p_interaction_forward
+ \setlocationcolor
+ \fi
+ % \else
+ % \resetinteractionparameter\c!color
+ % \resetinteractionparameter\c!contrastcolor
+ \fi}
+
+\let\strc_lists_set_style_color\strc_lists_set_style_color_normal
+
+%D A helper:
+
+\def\strc_lists_limitated_text#text%
+ {\edef\p_maxwidth{\listparameter\c!maxwidth}%
+ \ifempty\p_maxwidth
+ \listparameter\c!textcommand{#text}%
+ \else
+ \listparameter\c!textcommand{\limitatetext{#text}\p_maxwidth{\splitsymbol{\listparameter\c!limittext}}}%
+ \fi}
+
+% public helpers
+
+\permanent\protected\def\startcurrentlistentrywrapper
+ {\hbox \strc_lists_get_reference_attribute\v!all \strc_lists_get_destination_attribute\bgroup}
+
+\aliased\let\stopcurrentlistentrywrapper\egroup
+
+\permanent\def\currentlistentryreferenceattribute {\strc_lists_get_reference_attribute} % this definition can change
+\permanent\def\currentlistentrydestinationattribute{\strc_lists_get_destination_attribute} % this definition can change
+\permanent\def\currentlistentrylimitedtext {\strc_lists_limitated_text} % this definition can change
+
+% todo:
+
+\permanent\def\utilitylistlength{\listlength} % also in strc-reg (downward compatible name)
+
+\mutable\let\listlength\!!zerocount % also in strc-reg
+
+\permanent\tolerant\protected\def\determinelistcharacteristics[#list]#spacer[#settings]%
+ {\begingroup
+ \edef\currentlist{\firststructureelementinlist{#list}}%
+ \ifempty\currentlist
+ \endgroup
+ \let\listlength\!!zerocount
+ \else
+ \setupcurrentlist[#settings]%
+ \strc_lists_analyze{#list}{\listparameter\c!criterium}{\listparameter\c!reference}%
+ \normalexpanded{\endgroup\noexpand\edef\noexpand\listlength{\structurelistsize}}%
+ \fi
+ \strc_lists_set_mode}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-mar.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-mar.mkxl
new file mode 100644
index 00000000000..eb095a2a572
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-mar.mkxl
@@ -0,0 +1,136 @@
+%D \module
+%D [ file=strc-mar,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Markings,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Markings}
+
+\registerctxluafile{strc-mar}{}
+
+\unprotect
+
+%D Synchronizing marks is a rather tricky and messy business. When setting a mark, a
+%D node is added to the list in order for to \TEX\ be able to figure out the 3
+%D current marks when a page is made (last mark on previous page, first on current
+%D page, last on current page; in \LUATEX\ we might at one point have the first on
+%D the next page as well).
+%D
+%D Resetting a mark is not easy. An empty one will not erase the last one on the
+%D previous page for instance. In \LUATEX\ we can clear a marks state register with
+%D \type {\clearmarks} but since this is an immediate operation it might have
+%D unwanted side effects when \TEX\ has collected several pages of text and
+%D finishing off these pages uses marks.
+%D
+%D In \MKIV\ we provide an alternative model that permits some more control over the
+%D way marks are used. It is not entirely compatible with \MKII\ or previous \MKIV\
+%D implementations but in practice this is not a real problem. It's also easier now
+%D to extend this mechanism.
+
+\definesystemattribute [marks] [global]
+
+\installcorenamespace{marking}
+
+\installcommandhandler \??marking {marking} \??marking
+
+\newconditional\inhibitgetmarking % will become private
+\newconditional\inhibitsetmarking % will become private
+
+\newtoks \everymarking
+
+\appendtoks
+ \clf_definemarking{\currentmarking}{\currentmarkingparent}%
+\to \everydefinemarking
+
+\permanent\protected\tolerant\def\relatemarking [#1]#*[#2]{\clf_relatemarking{#1}{#2}}
+\permanent\protected\tolerant\def\resetmarking [#1]{\clf_resetmarking{#1}}
+\permanent\protected\tolerant\def\synchronizemarking[#1]#*[#2]#*[#3]{\ifvoid#2\else\clf_synchronizemarking{#1}#2{#3}\fi}
+\permanent \def\doifelsemarking #1{\clf_doifelsemarking{#1}} % no \noexpanded
+
+\def\strc_markings_synchronize#1#2#3{\ifvoid#2\else\clf_synchronizemarking{#1}#2{#3}\fi} % called in page-ini
+
+\permanent\protected\tolerant\def\setmarking[#1]#:#2%
+ {\ifconditional\inhibitsetmarking\else
+ \doifelse{\namedmarkingparameter{#1}\c!expansion}\v!yes
+ {\clf_setmarking{#1}{#2}}%
+ {\clf_setmarking{#1}{\detokenize{#2}}}%
+ \fi}
+
+\aliased\let\marking \setmarking
+\aliased\let\doifmarkingelse\doifelsemarking
+
+% defaults
+
+\setupmarking
+ [\c!expansion=\v!no,
+ \c!separator=\space\emdash\space,
+ \c!filtercommand=\firstofoneargument,
+ \c!state=\v!start]
+
+% fetching, regular interface
+
+\permanent\protected\def\getmarking
+ {\ifconditional\inhibitgetmarking
+ \expandafter\strc_markings_get_nop
+ \else
+ \expandafter\strc_markings_get_yes
+ \fi}
+
+\tolerant\def\strc_markings_get_nop[#-]#*[#-]#*[#-]%
+ {}
+
+\tolerant\def\strc_markings_get_yes[#1]#*[#2]#*[#3]%
+ {\ifcstok{\namedmarkingparameter{#1}\c!state}\v!start
+ \begingroup
+ \setsystemmode\v!marking
+ \the\everymarking
+ \ifparameter#3\or
+ \clf_getmarking{#1}{#2}{#3}%
+ \else
+ \clf_getmarking{#1}{\v!page}{#2}%
+ \fi
+ \endgroup
+ \fi}
+
+% the fetchers are fully expandable: [name][method]
+
+\permanent\tolerant\def\fetchonemark[#1]#*[#2]{\ifconditional\inhibitgetmarking\else\clf_fetchonemark {#1}{\v!page}{#2}\fi}
+\permanent\tolerant\def\fetchtwomarks [#1]{\ifconditional\inhibitgetmarking\else\clf_fetchtwomarks{#1}{\v!page}\fi}
+\permanent\tolerant\def\fetchallmarks [#1]{\ifconditional\inhibitgetmarking\else\clf_fetchallmarks{#1}{\v!page}\fi}
+
+\aliased\let\fetchmark\fetchonemark
+
+% also fully expandable but here we have: [name][range][method]
+
+\permanent\tolerant\def\fetchonemarking[#1]#*[#2]#*[#3]{\ifconditional\inhibitgetmarking\else\clf_fetchonemark {#1}{#2}{#3}\fi}
+\permanent\tolerant\def\fetchtwomarkings [#1]#*[#2]{\ifconditional\inhibitgetmarking\else\clf_fetchtwomarks{#1}{#2}\fi}
+\permanent\tolerant\def\fetchallmarkings [#1]#*[#2]{\ifconditional\inhibitgetmarking\else\clf_fetchallmarks{#1}{#2}\fi}
+
+\aliased\let\fetchmarking\fetchonemarking
+
+\permanent\def\markingseparator#1{\namedmarkingparameter{#1}\c!separator}
+\permanent\def\markingcommand #1{\namedmarkingparameter{#1}\c!filtercommand}
+
+%D Experimental:
+%D
+%D \starttyping
+%D \definemarking[boxmark]
+%D
+%D \setbox0\ruledvbox{
+%D \marking[boxmark]{tufte} \input tufte \par
+%D \marking[boxmark]{ward} \input ward \par
+%D }
+%D
+%D \synchronizemarking[zerobox][0] \box0
+%D
+%D marks: (\getmarking[boxmark][zerobox][first],\getmarking[boxmark][zerobox][last])
+%D \stoptyping
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-mat.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-mat.mkxl
new file mode 100644
index 00000000000..0b90eecba27
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-mat.mkxl
@@ -0,0 +1,1317 @@
+%D \module
+%D [ file=strc-mat,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Math Numbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Math Numbering}
+
+\registerctxluafile{strc-mat}{}
+
+% -- we have potential for captions
+% -- this module will use the commandhandler
+% -- key/value pairs will be added (I have no time now)
+
+\unprotect
+
+\setupformulas
+ [%\c!way=,
+ %\c!blockway=,
+ %\c!sectionnumber=,
+ %\c!conversion=\v!numbers,
+ %\c!numberstyle=,
+ %\c!numbercolor=,
+ %\c!numbercommand=,
+ %\c!margin=,
+ %\c!align=,
+ %\c!separator=,
+ \c!grid=\v!math,
+ \c!location=\v!right,
+ \c!left=(,
+ \c!right=),
+ \c!expansion=\v!yes, % maybe automatically
+ \c!spacebefore=\v!big,
+ \c!spaceafter=\formulaparameter\c!spacebefore,
+ \c!width=\hsize,
+ \c!leftmargin=\zeropoint,
+ \c!rightmargin=\zeropoint,
+ \c!indentnext=\v!no,
+ \c!alternative=\s!default,
+ \c!strut=\v!no,
+ \c!numberstrut=\v!yes, % \v!no \v!yes \v!always
+ \c!distance=2\emwidth]
+
+\setupformulaframed
+ [%c!location=<auto set>,
+ %c!width=<auto set>,
+ %c!align=<auto set>,
+ \c!offset=.5\exheight]
+
+\ifdefined\matheqnogapstep
+ % we're ok, now we have that quad in the distance which is
+ % more consistent and not depending on the text font in math
+ \matheqnogapstep\zerocount
+\else
+ % we will keep this for a while
+ \setupformulas[\c!distance=\emwidth]
+\fi
+
+% \ifdefined\mathdisplayskipmode
+% \mathdisplayskipmode\plustwo % only when not zero / needs adapted space handler
+% \fi
+
+% \mathdisplayskipmode\plusthree
+%
+% \hbox\bgroup
+% \setbox0\vbox\bgroup
+% xxxxxxxxx\par
+% \vskip-\baselineskip
+% $$a^2_2$$\par
+% xxxxxxxxx\par
+% \egroup
+% \box0
+% \vbox\bgroup
+% xxxxxxxxx\par
+% \vskip-\baselineskip
+% $$a^2$$\par
+% xxxxxxxxx\par
+% \egroup
+% \vbox\bgroup
+% xxxxxxxxx
+% \vskip-\baselineskip
+% $$a_2$$
+% xxxxxxxxx
+% \egroup
+% \egroup
+%
+% \hbox\bgroup
+% \setbox0\vbox\bgroup
+% xxxxxxxxx\par
+% \ctxlua{tex.prevdepth=-1000 *65536}
+% $$a^2_2$$\par
+% xxxxxxxxx\par
+% \egroup
+% \box0
+% \vbox\bgroup
+% xxxxxxxxx\par
+% \ctxlua{tex.prevdepth=-1000 *65536}
+% $$a^2$$\par
+% xxxxxxxxx\par
+% \egroup
+% \vbox\bgroup
+% xxxxxxxxx
+% \ctxlua{tex.prevdepth=-1000 *65536}
+% $$a_2$$
+% xxxxxxxxx
+% \egroup
+% \egroup
+
+\setupsubformulas % subformulas could be last in chain
+ [\c!indentnext=\formulaparameter\c!indentnext]
+
+\definecounter % one ?
+ [\v!formula]
+
+\defineconversionset
+ [\v!formula]
+ [numbers,characters] % no \v! ?
+
+\installcounterassociation{formula} \registerformulacounter\v!formula % currently we only have one
+
+\appendtoks
+ \synchronizeformulacounters
+\to \everysetupformula
+
+% \appendtoks
+% \synchronizeformulacounters
+% \to \everydefineformula
+
+\setupformulas
+ [\c!numberconversionset=\v!formula] % why forgotten
+
+\appendtoks
+ \normalexpanded{\definelist[\currentformula]}% is expansion needed?
+ \frozen\instance\protected\edefcsname\e!start\currentformula\v!formula\endcsname{\strc_formulas_start_formula{\currentformula}}%
+ \frozen\instance\protected\edefcsname\e!stop \currentformula\v!formula\endcsname{\strc_formulas_stop_formula}%
+\to \everydefineformula
+
+\definelist[\v!formula]
+
+\permanent\defcsname\e!start\v!formula\endcsname{\strc_formulas_start_formula{}}
+\permanent\defcsname\e!stop \v!formula\endcsname{\strc_formulas_stop_formula}
+
+\let\strc_formulas_start_formula\relax % defined later
+\let\strc_formulas_stop_formula \relax % defined later
+
+\permanent\tolerant\protected\def\defineformulaalternative[#1]#*[#2]#*[#3]%
+ {\frozen\instance\defcsname\e!start#1\v!formula\endcsname{#2}%
+ \frozen\instance\defcsname\e!stop #1\v!formula\endcsname{#3}}
+
+% sp = single line paragraph sd = single line display
+% mp = multi line paragraph md = multy line display
+
+\defineformulaalternative[\s!default][\startdisplaymath][\stopdisplaymath]
+\defineformulaalternative[\s!single] [\startdisplaymath][\stopdisplaymath]
+\defineformulaalternative[\s!multi] [\startdisplaymath][\stopdisplaymath]
+
+\defineformula
+ [sp]
+ [\c!spacebefore=\v!none,
+ \c!spaceafter=\v!none,
+ \c!indentnext=\v!no,
+ \c!alternative=\s!single]
+
+\defineformula
+ [sd]
+ [\c!spacebefore=\v!none,
+ \c!spaceafter=\v!none,
+ \c!indentnext=\v!yes,
+ \c!alternative=\s!single]
+
+\defineformula
+ [mp]
+ [\c!indentnext=\v!no,
+ \c!alternative=\s!multi]
+
+\defineformula
+ [md]
+ [\c!indentnext=\v!yes,
+ \c!alternative=\s!multi]
+
+\newtoks\everyresetformulas
+
+\appendtoks
+ \let\currentformula\empty % to be checked:
+\to \everyresetformulas
+
+% implementation
+
+\protected\def\strc_formulas_store_number#1#2#3#4#5% ref, todo:str, \sync % todo: title etc (like float)
+ {\settrue\c_strc_formulas_handle_number
+ \strc_counters_register_component
+ {formula}%
+ \setupcurrentformula \formulaparameter \detokenizedformulaparameter
+ \relax \relax \relax
+ [\c!name=\v!formula,\s!counter=\v!formula,%
+ \s!hascaption=\v!yes,\s!hastitle=\v!yes,\s!hasnumber=\v!yes,%\s!haslevel=#6,%
+ \c!reference=#1,\c!title=\namedformulaentry,\c!bookmark=]%
+ [#2]%
+ \glet\namedformulaentry\empty % \relax
+ \glet#3\m_strc_counters_last_registered_index
+ \glet#4\m_strc_counters_last_registered_synchronize
+ \glet#5\m_strc_counters_last_registered_attribute}
+
+% modes: 0=unset, 1=forced, 2=none, 3=reference
+
+\newconstant\c_strc_formulas_place_number_mode
+\newconstant\c_strc_formulas_number_mode
+\newconstant\c_strc_formulas_sub_number_mode
+\newconstant\c_strc_formulas_nested_number_mode
+
+\appendtoks
+ \c_strc_formulas_place_number_mode \zerocount
+ \c_strc_formulas_number_mode \zerocount
+ \c_strc_formulas_sub_number_mode \zerocount
+ \c_strc_formulas_nested_number_mode\zerocount
+\to \everyresetformulas
+
+\newconditional\c_strc_formulas_handle_number
+\newconditional\c_strc_formulas_inside_place
+\newconditional\c_strc_formulas_inside_place_sub
+\newconditional\c_strc_formulas_inside_formulas
+\newconditional\c_strc_formulas_inside_formulas_sub
+
+\appendtoks
+ \global\setfalse\c_strc_formulas_inside_place
+ \global\setfalse\c_strc_formulas_inside_place_sub
+\to \everyresetformulas
+
+\def\strc_formulas_place_number_noneed
+ {\doif{\formulaparameter\c!numberstrut}\v!always\strut}
+
+\def\strc_formulas_place_numbering % place formula
+ {\settrue\c_strc_formulas_handle_number
+ \strc_formulas_check_reference\c_strc_formulas_place_number_mode\currentplaceformulareference
+ \ifnum\c_strc_formulas_place_number_mode=\plustwo
+ \glet\strc_formulas_place_number\strc_formulas_place_number_noneed
+ \else
+ \glet\strc_formulas_place_number\strc_formulas_place_number_indeed
+ \fi
+ \glet\strc_formulas_place_number_nested\strc_formulas_place_number_nested_indeed}
+
+\def\strc_formulas_handle_number % formulas
+ {\strc_formulas_check_reference\c_strc_formulas_number_mode\currentformulasreference}
+
+\def\strc_formulas_handle_sub_number_indeed % sub formulas
+ {\strc_formulas_check_reference\c_strc_formulas_sub_number_mode\currentsubformulasreference
+ \strc_counters_increment\v!formula
+ \strc_formulas_store_number
+ \currentsubformulasreference
+ \empty
+ \currentsubformulasnumber
+ \currentsubformulassynchronize
+ \currentsubformulasattribute}
+
+\def\strc_formulas_handle_sub_number % sub formulas
+ {\iftrialtypesetting
+ \strc_counters_save\v!formula
+ \strc_formulas_handle_sub_number_indeed
+ \strc_counters_restore\v!formula
+ \else
+ \strc_formulas_handle_sub_number_indeed
+ \fi}
+
+\let\strc_formulas_reference_trace\relax
+\let\strc_formulas_reference_show \relax
+
+\permanent\protected\def\placecurrentformulanumber
+ {\begingroup
+ \rm % determines the distance and main font
+ \edef\p_location{\formulaparameter\c!location}%
+ \ifx\p_location\v!right
+ \hskip\formulaparameter\c!distance
+ \fi
+ \begingroup
+ \useformulastyleandcolor\c!numberstyle\c!numbercolor
+ \formulaparameter\c!numbercommand
+ {\edef\p_strut{\formulaparameter\c!numberstrut}%
+ \ifx\p_strut\v!always
+ \strut
+ \orelse\ifx\p_strut\v!yes
+ \strut
+ \fi
+ \formulaparameter\c!left
+ \namedtaggedlabeltexts
+ \t!formulalabel \v!formula
+ \t!formulanumber\v!formula
+ {\ignorespaces\strc_formulas_place_current_number\removeunwantedspaces}%
+ \formulaparameter\c!right}%
+ \endgroup
+ \ifx\p_location\v!left
+ \hskip\formulaparameter\c!distance
+ \fi
+ \endgroup}
+
+\protected\def\strc_formulas_place_current_number
+ {\ifempty\namedformulaentry
+ \strc_formulas_handle_current_references
+ \labeltexts\currentformula{\convertedcounter[\v!formula][]}%
+ \else
+ \expandafter % hm, the next one reset \namedformulaentry
+ \strc_formulas_handle_current_references
+ \namedformulaentry
+ \fi}
+
+\permanent\def\theformuladestinationattribute#1%
+ {\iflocation\ifx#1\relax\orelse\ifempty#1\else
+ \c_attr_destination#1%
+ \glet#1\relax
+ \fi\fi}
+
+\mutable\let\currentplaceformulaattribute\relax
+\mutable\let\currentformulaattribute \relax
+\mutable\let\currentsubformulaattribute \relax
+\mutable\let\currentformulasattribute \relax
+
+\mutable\let\currentplaceformulanumber\relax
+\mutable\let\currentformulanumber \relax
+\mutable\let\currentsubformulanumber \relax
+\mutable\let\currentformulasnumber \relax
+
+\mutable\let\currentformulasreference \empty
+\mutable\let\currentformulareference \empty
+\mutable\let\currentsubformulareference \empty
+\mutable\let\currentnestedformulanumber \empty
+\mutable\let\currentnestedformulareference\empty
+\mutable\let\currentnestedformulaattribute\empty
+
+\appendtoks
+ \glet\currentformulasreference \empty
+ \glet\currentformulareference \empty
+ \glet\currentsubformulareference \empty
+ \glet\currentnestedformulareference\empty
+\to \everyresetformulas
+
+\mutable\let\currentformulassuffix \empty
+\mutable\let\currentformulasuffix \empty
+\mutable\let\currentsubformulasuffix \empty
+\mutable\let\currentnestedformulasuffix\empty
+
+\appendtoks
+ \glet\currentformulassuffix \empty
+ \glet\currentformulasuffix \empty
+ \glet\currentsubformulasuffix \empty
+ \glet\currentnestedformulasuffix\empty
+\to \everyresetformulas
+
+\mutable\let\currentplaceformulasynchronize \relax
+\mutable\let\currentformulassynchronize \relax
+\mutable\let\currentsubformulasynchronize \relax
+\mutable\let\currentnestedformulasynchronize\relax
+
+\appendtoks
+ \glet\currentplaceformulasynchronize \relax
+ \glet\currentformulassynchronize \relax
+ \glet\currentsubformulassynchronize \relax
+ \glet\currentnestedformulasynchronize\relax
+\to \everyresetformulas
+
+\mutable\let\currentsubformulasattribute \empty
+\mutable\let\currentsubformulasnumber \empty
+\mutable\let\currentsubformulasreference \empty
+\mutable\let\currentsubformulassynchronize\empty
+
+% currently we do the number, some day we will do the (sub) formula
+
+\def\strc_formulas_handle_current_references
+ {\strc_formulas_reference_show
+ \ifnum\c_strc_formulas_place_number_mode=\plusthree
+ \strc_formulas_store_number
+ \currentplaceformulareference
+ \empty
+ \currentplaceformulanumber
+ \currentplaceformulasynchronize
+ \currentplaceformulaattribute
+ \currentplaceformulasynchronize
+ \glet\currentplaceformulasynchronize\relax
+ \theformuladestinationattribute\currentplaceformulaattribute
+ \fi
+ \ifnum\c_strc_formulas_number_mode=\plusthree
+ \strc_formulas_store_number
+ \currentformulasreference
+ \empty
+ \currentformulasnumber
+ \currentformulassynchronize
+ \currentformulasattribute
+ \currentformulassynchronize
+ \glet\currentformulassynchronize\relax
+ \theformuladestinationattribute\currentformulasattribute
+ \fi
+ \ifnum\c_strc_formulas_sub_number_mode=\plusthree
+ \currentsubformulassynchronize
+ \glet\currentsubformulassynchronize\relax
+ \fi
+ \ifnum\c_strc_formulas_nested_number_mode=\plusthree
+ \strc_formulas_store_number
+ \currentnestedformulareference
+ \empty
+ \currentnestedformulanumber
+ \currentnestedformulasynchronize
+ \currentnestedformulaattribute
+ \currentnestedformulasynchronize
+ \glet\currentnestedformulasynchronize\relax
+ \theformuladestinationattribute\currentnestedformulaattribute
+ \fi}
+
+% needs checking ... too many:
+
+\def\strc_formulas_handle_numbering_indeed
+ {\ifempty\namedformulaentry
+ \strc_counters_increment\v!formula
+ \doiftext\currentplaceformulasuffix{\strc_counters_setown_sub\v!formula\plustwo\currentplaceformulasuffix}%
+ \fi
+ \placecurrentformulanumber}
+
+\def\strc_formulas_handle_numbering
+ {\iftrialtypesetting
+ \strc_counters_save\v!formula
+ \strc_formulas_handle_numbering_indeed
+ \strc_counters_restore\v!formula
+ \else
+ \strc_formulas_handle_numbering_indeed
+ \fi}
+
+\def\strc_formulas_handle_sub_numbering_indeed
+ {\let\strc_formulas_handle_sub_numbering\relax % else error: see math/numbering-001.tex
+ \doifelsetext\currentsubformulasuffix
+ {\strc_counters_setown_sub\v!formula\plustwo\currentsubformulasuffix}
+ {\strc_counters_increment_sub\v!formula\plustwo}%
+ \placecurrentformulanumber}
+
+\def\strc_formulas_handle_sub_numbering
+ {\iftrialtypesetting
+ \strc_counters_save\v!formula
+ \strc_formulas_handle_sub_numbering_indeed
+ \strc_counters_restore\v!formula
+ \else
+ \strc_formulas_handle_sub_numbering_indeed
+ \fi}
+
+\def\strc_formulas_number_indeed
+ {\ifconditional\c_strc_formulas_handle_number
+ \hbox\bgroup
+ % main counter
+ \ifconditional\c_strc_formulas_inside_formulas_sub
+ % nothing
+ \else
+ \ifcase\c_strc_formulas_number_mode
+ \ifcase\c_strc_formulas_place_number_mode
+ \strc_formulas_handle_numbering
+ \or
+ \strc_formulas_handle_numbering
+ \or
+ % nothing
+ \or
+ \strc_formulas_handle_numbering
+ \fi
+ \or
+ \strc_formulas_handle_numbering
+ \or
+ % nothing
+ \or
+ \strc_formulas_handle_numbering
+ \fi
+ \fi
+ % subcounter
+ \ifconditional\c_strc_formulas_inside_formulas_sub
+ \ifcase\c_strc_formulas_sub_number_mode
+ \strc_formulas_handle_sub_numbering % was nothing
+ \or
+ \strc_formulas_handle_sub_numbering
+ \or
+ % nothing
+ \or
+ \strc_formulas_handle_sub_numbering
+ \fi
+ \fi
+ \strc_formulas_reference_trace
+ \egroup
+ \fi}
+
+\installstructurelistprocessor\v!formula % to be checked ...
+ {\let\currentlistentrynumber \structurelistgenericnumber
+ \let\currentlistentrytitle \structurelistgenerictitle
+ \let\currentlistentrypagenumber\structurelistpagenumber
+ \strc_lists_apply_renderingsetup}
+
+\newif\ifinformula
+
+%D We need a hook into the plain math alignment macros
+%D
+%D \starttyping
+%D \displaylines
+%D \eqalignno
+%D \eqalignno
+%D \stoptyping
+%D
+%D Otherwise we get a missing \type {$$} error reported.
+
+\pushoverloadmode
+
+\aliased\let\reqno\eqno % no longer valid as we just nil it
+
+\aliased\let\math_native_leqno\leqno
+\aliased\let\math_native_reqno\reqno
+
+\permanent\protected\def\normaleqno#1{\writestatus\m!system{no native (l)eqno equation number support}}
+
+\aliased\let\normalleqno\normaleqno
+\aliased\let\normalreqno\normaleqno
+
+\aliased\let\leqno\normaleqno
+\aliased\let\reqno\normaleqno
+\aliased\let\eqno \normaleqno
+
+\popoverloadmode
+
+%D \macros
+%D {startsubformulas}
+
+% \placeformula
+% \startsubformulas[Maxwell]
+% \startformulas
+% \startformula \startalign
+% \NC \nabla\cdot\bf E \NC = \frac{\rho}{\varepsilon_0} \NR[Maxwell 1]
+% \NC \nabla\times\bf E \NC = - \frac{\partial\bf B}{\partial t} \NR[Maxwell II]
+% \stopalign \stopformula
+% \startformula \startalign
+% \NC \nabla\cdot \bf B \NC = 0 \NR[Maxwell III]
+% \NC \nabla\times\bf B \NC = \mu_0{\bf j}+\varepsilon_0\mu_0\frac{\partial\bf E}{\partial t} \NR[Maxwell IV]
+% \stopalign \stopformula
+% \stopformulas
+% \stopsubformulas
+%
+% Maxwell : \in [Maxwell] and II : \in [Maxwell II]
+
+%D Tricky stuff:
+
+\abovedisplayskip \zeropoint
+\abovedisplayshortskip \zeropoint % evt. 0pt minus 3pt
+\belowdisplayskip \zeropoint
+\belowdisplayshortskip \zeropoint % evt. 0pt minus 3pt
+
+\predisplaypenalty \zerocount
+\postdisplaypenalty \zerocount % -5000 goes wrong, see penalty at \section
+\mathdisplayskipmode \plusthree % because align also adds
+
+% \predisplaygapfactor \zerocount % default is 2000
+
+\protected\def\strc_formulas_forget_display_skips
+ {\mathdisplayskipmode \plusthree
+ \abovedisplayskip \zeropoint
+ \belowdisplayskip \zeropoint
+ \abovedisplayshortskip\zeropoint
+ \belowdisplayshortskip\zeropoint}
+
+\newdimen\d_strc_formulas_display_skip_left
+\newdimen\d_strc_formulas_display_skip_right
+\newdimen\d_strc_formulas_display_margin_left
+\newdimen\d_strc_formulas_display_margin_right
+\newdimen\d_strc_formulas_display_pre_threshold
+\newdimen\d_strc_formulas_display_width
+
+\newconstant\c_strc_formulas_mode % this will go away
+\newconstant\c_strc_formulas_space_model
+
+\newconstant\c_strc_math_vertical % experiment
+
+\c_strc_formulas_mode \plustwo % 0=native 1=simple (old) 2=align (new)
+\c_strc_formulas_space_model\plusthree % replaces \plusone, we might use \plusfour in the future
+
+\newconditional\c_strc_formulas_tight
+
+\newbox\b_strc_formulas_number
+\newbox\b_strc_formulas_content
+
+\def\strc_formulas_flush_content_and_number
+ {\noindentation
+ % \dontleavehmode
+ \kern\d_strc_formulas_display_margin_left
+ \ifcase\wd\b_strc_formulas_number
+ \hbox to \displaywidth \bgroup
+ \hfill
+ \box\b_strc_formulas_content
+ \hfill
+ \egroup
+ \orelse\ifdim\dimexpr\wd\b_strc_formulas_content+\wd\b_strc_formulas_number\relax>\displaywidth
+ \vbox \bgroup
+ \hsize\displaywidth
+ \box\b_strc_formulas_content
+ \par
+ \ifx\p_location\v!left
+ \box\b_strc_formulas_number\hfill
+ \else
+ \hfill\box\b_strc_formulas_number
+ \fi
+ \egroup
+ \else
+ \hbox to \displaywidth \bgroup
+ \ifx\p_location\v!left
+ \rlap{\box\b_strc_formulas_number}%
+ \hfill\box\b_strc_formulas_content\hfill
+ \else
+ \hfill\box\b_strc_formulas_content\hfill
+ \llap{\box\b_strc_formulas_number}%
+ \fi
+ \egroup
+ \fi}
+
+\installcorenamespace{mathdisplayspacemodel}
+
+\setvalue{\??mathdisplayspacemodel\v!before:1}% old
+ {\ifx\p_spacebefore\v!none
+ % nothing
+ \else
+ \directvspacing\p_spacebefore
+ \fi}
+
+\setvalue{\??mathdisplayspacemodel\v!after:1}% old
+ {\prevdepth .5\strutdp
+ \edef\p_spaceafter{\formulaparameter\c!spaceafter}%
+ \ifx\p_spaceafter\v!none
+ % nothing
+ \else
+ \directvspacing\p_spaceafter
+ \fi}
+
+\setvalue{\??mathdisplayspacemodel\v!before:2}% old
+ {\ifx\p_spacebefore\v!none
+ % nothing
+ \else
+ \directvspacing\p_spacebefore
+ \fi
+ \prevdepth-\maxdimen} % texbook pagina 79-80
+
+\setvalue{\??mathdisplayspacemodel\v!after:2}% old
+ {\prevdepth\lineheight
+ \edef\p_spaceafter{\formulaparameter\c!spaceafter}%
+ \ifx\p_spaceafter\v!none
+ % nothing
+ \else
+ \directvspacing\p_spaceafter
+ \fi}
+
+\setvalue{\??mathdisplayspacemodel\v!before:3}%
+ {% not ok, try \stopformula\par\startformula vs \stopformula\startformula
+ \let\m_spacebefore\empty
+ \ifvmode
+ \ifdim\lastskip>\zeropoint\else
+ \ifdim\prevdepth<\zeropoint\else
+ \ifdim\prevdepth<\strutdp
+ % maybe add a tracing option here
+ \ifgridsnapping
+ \let\m_spacebefore\v!depth
+ \else
+ \edef\m_spacebefore{\the\dimexpr\strutdp-\prevdepth\relax}%
+ \fi
+ \fi
+ \fi
+ \fi
+ \nointerlineskip
+ \fi
+ \ifempty\m_spacebefore
+ \ifx\p_spacebefore\v!none
+ % nothing
+ \orelse\ifempty\p_spacebefore
+ \directvspacing\currentvspacing
+ \else
+ \directvspacing{\p_spacebefore,\the\scratchdimen}%
+ \fi
+ \else
+ \ifx\p_spacebefore\v!none
+ \directvspacing{\m_spacebefore}%
+ \orelse\ifempty\p_spacebefore
+ \directvspacing{\m_spacebefore,\currentvspacing}%
+ \else
+ \directvspacing{\m_spacebefore,\p_spacebefore}%
+ \fi
+ \fi}
+
+\setvalue{\??mathdisplayspacemodel\v!after:3}%
+ {\prevdepth\strutdp % \directvspacing\v!depth
+ \ifx\p_spaceafter\v!none
+ % nothing
+ \orelse\ifempty\p_spaceafter
+ \directvspacing\currentvspacing
+ \else
+ \directvspacing\p_spaceafter
+ \fi}
+
+\newconditional\c_math_model_four_indeed
+
+\setvalue{\??mathdisplayspacemodel\v!before:4}%
+ {% not ok, try \stopformula\par\startformula vs \stopformula\startformula
+ \ifvmode
+ \ifinner
+ \csname\??mathdisplayspacemodel\v!before:3\endcsname
+ \else
+ \settrue\c_math_model_four_indeed
+ \forcestrutdepth
+ \nointerlineskip
+ \ifx\p_spacebefore\v!none
+ % nothing
+ \orelse\ifempty\p_spacebefore
+ \directvspacing\currentvspacing
+ \else
+ \directvspacing{\p_spacebefore,\the\scratchdimen}%
+ \fi
+ \fi
+ \else
+ \csname\??mathdisplayspacemodel\v!before:3\endcsname
+ \fi}
+
+\setvalue{\??mathdisplayspacemodel\v!after:4}%
+ {\ifconditional\c_math_model_four_indeed
+ \setfalse\c_math_model_four_indeed
+ \forcestrutdepth
+ \else
+ \prevdepth\strutdp % \directvspacing\v!depth
+ \fi
+ \ifx\p_spaceafter\v!none
+ % nothing
+ \orelse\ifempty\p_spaceafter
+ \directvspacing\currentvspacing
+ \else
+ \directvspacing\p_spaceafter
+ \fi}
+
+\permanent\protected\def\setdisplaymathspacemodel[#1]%
+ {\ifcsname\??mathdisplayspacemodel\v!before:\number#1\endcsname
+ \c_strc_formulas_space_model#1\relax
+ \fi}
+
+% \newtoks\everybeforedisplay
+% \appendtoks\page_sides_check_floats_indeed\to\everybeforedisplay
+
+\permanent\protected\def\beforedisplayspace
+ {\ifhmode
+ \par
+ \fi
+ \ifvmode
+ \edef\p_spacebefore{\formulaparameter\c!spacebefore}%
+ \begincsname\??mathdisplayspacemodel\v!before:\number\c_strc_formulas_space_model\endcsname
+ \fi
+ \ifhmode
+ \par
+ \fi
+ \page_sides_check_floats_indeed} % probably needs more
+
+\permanent\protected\def\afterdisplayspace
+ {\ifhmode
+ \par
+ \fi
+ \ifvmode
+ \edef\p_spaceafter{\formulaparameter\c!spaceafter}%
+ \begincsname\??mathdisplayspacemodel\v!after:\number\c_strc_formulas_space_model\endcsname
+ \fi
+ \ifhmode
+ \par
+ \fi}
+
+\permanent\protected\def\setdisplaydimensions
+ {\displayindent\dimexpr
+ \d_strc_formulas_display_skip_left
+ +\d_strc_formulas_display_margin_left
+ \relax
+ \displaywidth\d_strc_formulas_display_width
+ %\setlocalhsize
+ %\displaywidth\localhsize
+ \ifdim\hangindent>\zeropoint
+ \advance\displayindent\hangindent
+ \else
+ \advance\displaywidth\hangindent
+ \fi
+ \advance\displaywidth\dimexpr
+ -\displayindent
+ -\d_strc_formulas_display_skip_right
+ -\d_strc_formulas_display_margin_right
+ \relax
+ \hsize\displaywidth} % new, else overfull in itemize
+
+\protected\def\strc_formulas_start_formula#1%
+ {\dodoubleempty\strc_formulas_start_formula_indeed[#1]}
+
+% \newskip\formulastrutht
+% \newskip\formulastrutdp
+
+%D \startbuffer
+%D \startformula[9pt] x = 1 \stopformula
+%D \startformula[7pt] x = 1 \stopformula
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\setvalue{\??formulaoption\v!packed}%
+ {\c_strc_formulas_space_model\zerocount}
+
+\setvalue{\??formulaoption\v!tight}%
+ {\settrue\c_strc_formulas_tight}
+
+\setvalue{\??formulaoption\v!middle}%
+ {\d_strc_formulas_display_skip_left \zeropoint
+ \d_strc_formulas_display_skip_right\zeropoint}
+
+\setvalue{\??formulaoption\v!depth}%
+ {\c_strc_formulas_space_model\plusfour}
+
+\setvalue{\??formulaoption\v!line}%
+ {\ifgridsnapping
+ \setformulaparameter\c!grid{\v!math:\v!line}%
+ \fi}
+
+\setvalue{\??formulaoption\v!halfline}%
+ {\ifgridsnapping
+ \setformulaparameter\c!grid{\v!math:\v!halfline}%
+ \fi}
+
+\setvalue{\??formulaoption-\v!line}%
+ {\ifgridsnapping
+ \setformulaparameter\c!grid{\v!math:-\v!line}%
+ \fi}
+
+\setvalue{\??formulaoption-\v!halfline}%
+ {\ifgridsnapping
+ \setformulaparameter\c!grid{\v!math:-\v!halfline}%
+ \fi}
+
+% when we have 1.0.6 we wil use \mathpenaltiesmode
+%
+% \prebinoppenalty -100
+% \prerelpenalty -100
+
+\def\strc_math_set_split
+ {\edef\p_split{\formulaparameter\c!split}%
+ \ifx\p_split\v!yes
+ \global\c_strc_math_vertical\plusone
+ \orelse\ifx\p_split\v!page
+ \global\c_strc_math_vertical\plustwo
+ \else
+ \global\c_strc_math_vertical\zerocount
+ \fi
+ \ifcase\c_strc_math_vertical
+ % \mathpenaltiesmode \zerocount
+ \clf_setmathpenalties\zerocount
+ \clf_resetmathhang
+ \else
+ % \mathpenaltiesmode \plusone
+ \clf_setmathpenalties\plusone
+ \edef\p_hang{\formulaparameter\c!hang}%
+ \ifx\p_hang\v!none
+ \global\setfalse\c_strc_math_indent
+ \clf_resetmathhang
+ \else
+ \global\settrue\c_strc_math_indent
+ \clf_setmathhang {%
+ method {\p_hang}%
+ distance \formulaparameter\c!distance
+ }%
+ \fi
+ \fi}
+
+\setupformula
+ [\c!split=\v!no,
+ \c!distance=\zeropoint,
+ %\c!interlinespace=1.5\lineheight,
+ \c!interlinespace=,
+ \c!hang=\v!none]
+
+% for the moment (when testing) we use a penalty 1
+
+\protected\def\strc_math_align_here{\ifmmode\penalty\plusone\fi}%
+\protected\def\strc_math_break_here{\ifmmode\hfill\break \fi}%
+
+\ifdefined\alignhere \else \aliased\let\alignhere\relax \fi
+\ifdefined\breakhere \else \aliased\let\breakhere\relax \fi
+
+\appendtoks
+ \enforced\let\alignhere\strc_math_align_here
+ \enforced\let\breakhere\strc_math_break_here
+\to \everymathematics
+
+\protected\def\strc_formulas_start_formula_indeed[#1][#2]% setting leftskip adaption is slow !
+ {\ifhmode
+ \par
+ \fi
+ \bgroup % HERE
+ \iftrialtypesetting\else
+ \global\advance\c_strc_formulas_n\plusone
+ \fi
+ \def\currentformula{#1}%
+ \strc_math_set_split
+ \dostarttaggedchained\t!formula\currentformula\??formula
+ \setfalse\c_strc_formulas_tight
+ \d_strc_formulas_display_skip_left \leftskip
+ \d_strc_formulas_display_skip_right \rightskip
+ \d_strc_formulas_display_width \formulaparameter\c!width\relax
+ \d_strc_formulas_display_margin_left \formulaparameter\c!leftmargin\relax
+ \d_strc_formulas_display_margin_right\formulaparameter\c!rightmargin\relax
+ \iftok{#2}\emptytoks
+ \edef\p_option{\formulaparameter\c!option}%
+ \orelse\ifhastok={#2}% this is new, so that we can also set the grid
+ \setupcurrentformula[#2]%
+ \edef\p_option{\formulaparameter\c!option}%
+ \else
+ \edef\p_option{\formulaparameter\c!option}%
+ \edef\p_option{\ifempty\p_option\else\p_option,\fi#2}%
+ \fi
+ \ifempty\p_option \else
+ \rawprocesscommacommand[\p_option]\strc_formulas_option
+ \fi
+ \edef\p_margin{\formulaparameter\c!margin}%
+ \ifempty\p_margin \else
+ \dosetleftskipadaption\p_margin
+ \d_strc_formulas_display_margin_left\leftskipadaption
+ \fi
+ \let\strc_formulas_start_formula\strc_formulas_start_formula_nested
+ \strc_formulas_forget_display_skips
+ \the\everybeforedisplayformula
+ \csname\e!start\formulaparameter\c!alternative\v!formula\endcsname}
+
+\protected\def\strc_formulas_start_formula_nested#1%
+ {\bgroup
+ \let\strc_formulas_stop_formula\strc_formulas_stop_formula_nested
+ \dostarttagged\t!subformula\empty}
+
+\protected\def\strc_formulas_stop_formula_nested
+ {\dostoptagged
+ \egroup}
+
+% tagging of formulanumbers is not ok (we get two display maths blobs)
+
+\newcount\c_strc_formulas_n
+
+\ifdefined\dotagregisterformula \else \aliased\let\dotagregisterformula\gobbleoneargument \fi
+
+\protected\def\strc_formulas_stop_formula
+ {\strc_formulas_place_number % in case it hasn't happened yet
+ \strc_formulas_flush_number % in case we are in native mode
+ \dostarttagged\t!formulacontent\empty
+ \dotagregisterformula\c_strc_formulas_n
+ \csname\e!stop\formulaparameter\c!alternative\v!formula\endcsname
+ \dostoptagged
+ \dostoptagged
+ \nonoindentation
+ \useindentnextparameter\formulaparameter
+ \egroup
+ \hangafter\minusone % added for side floats
+ \hangindent\zeropoint % added for side floats
+ \setfalse\c_strc_formulas_handle_number
+ \the\everyresetformulas
+ \dorechecknextindentation} % here ?
+
+% experiment:
+
+\def\strc_formulas_set_grid_snapping
+ {\edef\p_grid{\formulaparameter\c!grid}%
+ \ifempty\p_grid \else
+ \spac_grids_snap_value_auto\p_grid
+ \fi}
+
+\appendtoks
+ \ifgridsnapping
+ \strc_formulas_set_grid_snapping
+ \fi
+\to \everybeforedisplayformula
+
+% \protected\def\switchtoformulabodyfont
+% {\switchtobodyfont}
+
+\tolerant\permanent\protected\def\formula[#1];#2% todo: tagged
+ {\begingroup
+ \ifparameters#1\else
+ \rawprocesscommacommand[#1]\strc_formulas_option
+ \fi
+ \mathematics{#2}%
+ \endgroup}
+
+\aliased\letcsname\v!formula\endcsname\formula
+
+%D \starttyping
+%D % test \par % no preceding hlist
+%D % $$x$$ % preceding hlist
+%D % \noindent $$x$$ % no preceding hlist
+%D \startformula x \stopformula % now has \noindent (in mkii we messed with baselineskip)
+%D \stoptyping
+
+\permanent\protected\def\startdisplaymath
+ {\ifhmode
+ \par
+ \fi
+ \bgroup
+ \informulatrue
+ \beforedisplayspace
+ \setdisplaydimensions
+ \ifcase\c_strc_formulas_mode
+ \noindent % prevents that tex injects empty line (when using native display mechanism)
+ \Ucheckedstartdisplaymath
+ \the\everydisplay % new (probably too much)
+ \or
+ \setbox\b_strc_formulas_content\hbox\bgroup
+ \normalUstartmath
+ \displaystyle
+ \the\everydisplay % new (probably too much)
+ \else
+ \expandafter\startinnermath
+ \fi
+ \begingroup} % less interference with upcoming a \over b
+
+\permanent\protected\def\stopdisplaymath
+ {\endgroup % less interference with upcoming a \over b
+ \ifcase\c_strc_formulas_mode
+ \Ucheckedstopdisplaymath
+ \or
+ \normalUstopmath
+ \egroup
+ \strc_formulas_flush_content_and_number
+ \else
+ \expandafter\stopinnermath
+ \fi
+ \afterdisplayspace
+ \egroup}
+
+% already defined
+%
+% \let\startinnermath\empty
+% \let\stopinnermath \empty
+
+% \defineformulaalternative[multi][\begindmath][\enddmath]
+%
+% \fakewords{20}{40}\epar
+% \placeformula {a} $$ \fakespacingformula $$
+% \fakewords{20}{40}\epar
+% \placeformula {b} \startformule \fakespacingformula \stopformule
+% \placeformula {b} \startformule \fakespacingformula \stopformule
+% \fakewords{20}{40}\epar
+% \placeformula {c} \startmdformule \fakespacingformula \stopmdformule
+% \placeformula {c} \startmdformule \fakespacingformula \stopmdformule
+% \fakewords{20}{40}\epar
+% \placeformula {d} \startmpformule \fakespacingformula \stopmpformule
+% \placeformula {d} \startmpformule \fakespacingformula \stopmpformule
+% \fakewords{20}{40}\epar
+% \placeformula {e} \startsdformule \fakespacingformula \stopsdformule
+% \placeformula {e} \startsdformule \fakespacingformula \stopsdformule
+% \fakewords{20}{40}\epar
+% \placeformula {f} \startspformule \fakespacingformula \stopspformule
+% \placeformula {f} \startspformule \fakespacingformula \stopspformule
+% \fakewords{20}{40}
+
+\permanent\protected\def\startsubformulas
+ {\dosingleempty\strc_formulas_start_sub_formulas}
+
+\def\strc_formulas_start_sub_formulas[#1]%
+ {\edef\currentsubformulasreference{#1}%
+ \global\settrue\c_strc_formulas_inside_formulas_sub
+ \strc_formulas_handle_sub_number}
+
+\permanent\protected\def\stopsubformulas
+ {\nonoindentation
+ \useindentnextparameter\subformulaparameter
+ \the\everyresetformulas % to be checked
+ \global\setfalse\c_strc_formulas_inside_formulas_sub
+ \dorechecknextindentation} % here ?
+
+%D Named subformulas (to be redone)
+
+\permanent\protected\def\startnamedsubformulas
+ {\dosingleempty\strc_formulas_start_named_sub_formulas}
+
+\def\strc_formulas_start_named_sub_formulas[#1]#2%
+ {\setformulalistentry{#2}%
+ \startsubformulas[#1]}
+
+\permanent\protected\def\stopnamedsubformulas
+ {\stopsubformulas}
+
+%D Experimental goodie:
+%D
+%D \startbuffer
+%D \placelist[formula][criterium=text] \blank[2*big]
+%D \placenamedformula[one]{first} \startformula a = 1 \stopformula \endgraf
+%D \placeformula \startformula a = 2 \stopformula \endgraf
+%D \placenamedformula {second} \startformula a = 3 \stopformula \endgraf
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\protected\def\startformulas{\strc_formulas_start_formulas}
+
+\permanent\letcsname\e!stop\v!formulas\endcsname\relax
+
+\protected\def\strc_formulas_nested_formula_start
+ {\hbox to \displaywidth \bgroup
+ \hsize\displaywidth
+ \hss
+ %\Ustartmath
+ \dostarttagged\t!formulacontent\empty
+ \csname\e!start\formulaparameter\c!alternative\v!formula\endcsname}
+
+\protected\def\strc_formulas_nested_formula_stop
+ {\csname\e!stop\formulaparameter\c!alternative\v!formula\endcsname
+ \dostoptagged
+ %\Ustopmath
+ \hss
+ \egroup
+ \hss}
+
+\normalexpanded{\tolerant\def\noexpand\strc_formulas_start_formulas[#1]#:#2\csname\e!stop\v!formulas\endcsname}%
+ {\startformula
+ \dostarttagged\t!formulaset\empty
+ \global\settrue\c_strc_formulas_inside_formulas
+ \edef\currentformulasreference{#1}%
+ \strc_formulas_handle_number
+ \let\currentformula\empty
+ \strc_formulas_forget_display_skips
+ \enforced\protected\def\startformula
+ {\advance\scratchcounter\plusone
+ \expandafter\gobbleuntil\csname\e!stop\v!formula\endcsname}%
+ \scratchcounter\zerocount
+ #2% preroll
+ \hbox to \displaywidth \bgroup
+ \divide\displaywidth\scratchcounter
+ \hss
+ \enforced\let\startformula\strc_formulas_nested_formula_start
+ \enforced\let\stopformula \strc_formulas_nested_formula_stop
+ #2%
+ \egroup
+ \global\setfalse\c_strc_formulas_inside_formulas
+ \dostoptagged
+ \stopformula
+ \the\everyresetformulas
+ \hangafter\minusone % added for side floats
+ \hangindent\zeropoint} % added for side floats
+
+% place
+
+\def\m_strc_formulas_flag_inhibit{-}
+\def\m_strc_formulas_flag_force {+}
+
+\def\strc_formulas_check_reference#1#2%
+ {#1\unless\ifempty\namedformulaentry % \relax % new 29/8/2010
+ \plusthree
+ \orelse\ifempty#2%
+ \zerocount
+ \orelse\ifx#2\m_strc_formulas_flag_force
+ \plusone
+ \orelse\ifx#2\m_strc_formulas_flag_inhibit
+ \plustwo
+ \else
+ \plusthree
+ \fi}
+
+\permanent\protected\def\formulanumber
+ {\strc_formulas_number} % for the moment
+
+\tolerant\protected\def\strc_formulas_number[#1]%
+ {\def\currentformulareference{#1}%
+ \strc_formulas_place_number_in_box}
+
+\permanent\protected\def\placeformula {\global\settrue\c_strc_formulas_inside_place \strc_formulas_place}
+\permanent\protected\def\placesubformula{\global\settrue\c_strc_formulas_inside_place_sub\strc_formulas_place}
+
+\tolerant\protected\def\strc_formulas_place[#1]%
+ {\def\currentplaceformulareference{#1}%
+ \let\currentplaceformulasuffix\empty
+ \doifelsenextbgroup\strc_formulas_place_yes\strc_formulas_place_nop} % [ref]{}
+
+\protected\def\strc_formulas_place_yes#1%
+ {\def\currentplaceformulasuffix{#1}%
+ \strc_formulas_place_nop}
+
+\protected\def\strc_formulas_place_nop
+ {\doifelsenextchar$\strc_formulas_place_pickup\strc_formulas_place_indeed} % [ref]$$ [ref]\start
+
+\protected\def\strc_formulas_place_indeed
+ {\strc_formulas_place_numbering}
+
+\protected\def\strc_formulas_place_pickup$$#1$$%
+ {\strc_formulas_place_numbering
+ \strc_formulas_start_formula{}#1\strc_formulas_stop_formula}
+
+% \let\startplaceformula\placeformula
+% \let\stopplaceformula \relax
+
+% \startplaceformula \startformula e=mc^2 \stopformula \stopplaceformula
+% \startplaceformula[-] \startformula e=mc^2 \stopformula \stopplaceformula
+% \startplaceformula[x] \startformula e=mc^2 \stopformula \stopplaceformula
+% \startplaceformula[reference=foo] \startformula e=mc^2 \stopformula \stopplaceformula
+% \startplaceformula[title=whatever] \startformula e=mc^2 \stopformula \stopplaceformula
+% \startplaceformula[suffix=x] \startformula e=mc^2 \stopformula \stopplaceformula
+
+\mutable\let\currentplaceformulatitle \empty
+\mutable\let\currentplaceformulareference\empty
+\mutable\let\currentplaceformulasuffix \empty
+
+\permanent\tolerant\protected\def\startplaceformula[#1]%
+ {\begingroup
+ \global\settrue\c_strc_formulas_inside_place
+ \ifparameter#1\or
+ \expandafter\strc_formulas_start_place_yes
+ \else
+ \expandafter\strc_formulas_start_place_nop
+ \fi[#1]}
+
+\def\strc_formulas_start_place_yes[#1]%
+ {\doifassignmentelse{#1}\strc_formulas_start_place_parameters\strc_formulas_start_place_reference[#1]}
+
+\def\strc_formulas_start_place_nop[#1]%
+ {\let\currentplaceformulareference\empty
+ \let\currentplaceformulasuffix \empty
+ \strc_formulas_place_nop}
+
+\def\strc_formulas_start_place_reference[#1]%
+ {\edef\currentplaceformulareference{#1}%
+ \let\currentplaceformulasuffix\empty
+ %\doifelsenextbgroup\strc_formulas_place_yes\strc_formulas_place_nop} % [ref]{}
+ \strc_formulas_place_nop}
+
+\def\strc_formulas_start_place_parameters[#1]%
+ {\letdummyparameter\c!title \empty
+ \letdummyparameter\c!reference\empty
+ \letdummyparameter\c!suffix \empty
+ \getdummyparameters[#1]%
+ \edef\currentplaceformulatitle {\dummyparameter\c!title}%
+ \edef\currentplaceformulareference{\dummyparameter\c!reference}%
+ \edef\currentplaceformulasuffix {\dummyparameter\c!suffix}%
+ \ifempty\currentplaceformulatitle\else
+ \normalexpanded{\setformulalistentry{\currentplaceformulatitle}}%
+ \fi
+ \doifelsenextbgroup\strc_formulas_place_yes\strc_formulas_place_nop} % [ref]{}
+
+\permanent\protected\def\stopplaceformula
+ {\relax
+ \endgroup}
+
+% to be checked
+
+\let\strc_formulas_place_number \relax
+\let\strc_formulas_place_number_nested\gobbletwoarguments
+
+\def\strc_formulas_place_number_nested_indeed#1#2%
+ {\def\currentnestedformulareference{#1}%
+ \def\currentnestedformulasuffix{#2}%
+ \strc_formulas_check_reference\c_strc_formulas_nested_number_mode\currentnestedformulareference
+ \ifcase\c_strc_formulas_nested_number_mode
+ % nothing
+ \or
+ \glet\strc_formulas_place_number\relax
+ \expandafter\strc_formulas_number % hm, looks ahead for []
+ \or
+ % nothing
+ \or
+ \glet\strc_formulas_place_number\relax
+ \expandafter\strc_formulas_number % hm, looks ahead for []
+ \fi}
+
+\def\strc_formulas_place_number_indeed
+ {\strc_formulas_place_number_in_box}
+
+\def\strc_formulas_place_number_in_box
+ {\dostarttagged\t!formulacaption\empty
+ \global\setbox\b_strc_formulas_number\naturalhbox{\strc_formulas_number_indeed}%
+ \dostoptagged}
+
+\def\strc_formulas_flush_number
+ {\ifcase\c_strc_formulas_mode
+ \ifzeropt\wd\b_strc_formulas_number
+ % nothing to be done
+ \orelse\ifx\p_location\v!left
+ \math_native_leqno{\box\b_strc_formulas_number}%
+ \else
+ \math_native_reqno{\box\b_strc_formulas_number}%
+ \fi
+ \fi}
+
+% todo
+
+\permanent\tolerant\protected\def\placenamedformula[#1]%
+ {\ifarguments
+ \expandafter\strc_formulase_place_named_nop
+ \else
+ \expandafter\strc_formulase_place_named_yes
+ \fi[#1]}
+
+\def\strc_formulase_place_named_yes[#1]#2%
+ {\setformulalistentry{#2}%
+ \placeformula[#1]}
+
+\def\strc_formulase_place_named_nop[#1]#2%
+ {\setformulalistentry{#2}%
+ \placeformula}
+
+\mutable\let\namedformulaentry\empty % \relax % this will become a key/value so that we can do bookmarks
+
+\permanent\protected\def\setformulalistentry#1%
+ {\gdef\namedformulaentry{#1}}
+
+\protect \endinput
+
+% \abovedisplayshortskip0pt \belowdisplayshortskip0pt \abovedisplayskip0pt \belowdisplayskip0pt \forgetall
+%
+% test \par $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+% test \par $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+% test plus \par \prevdepth \maxdimen $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+% test minus \par \prevdepth-\maxdimen $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+%
+% \parskip\baselineskip
+%
+% test \par $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+% test \par $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+% test plus \par \prevdepth \maxdimen $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
+% test minus \par \prevdepth-\maxdimen $$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$$ \par test \par
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-not.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-not.mklx
new file mode 100644
index 00000000000..5ac57137422
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-not.mklx
@@ -0,0 +1,1971 @@
+%D \module
+%D [ file=strc-not,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Note Handling,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Note Handling}
+
+\registerctxluafile{strc-not}{}
+
+\unprotect
+
+% todo: finish local rendering
+% todo: more p_strc_notations_* (outside trial loop)
+% todo: see if we can now use \insertpenalties (>0 == some left)
+
+\ifdefined\dotagsetnotesymbol \else \aliased\let\dotagsetnotesymbol\relax \fi
+\ifdefined\dotagsetnotation \else \aliased\let\dotagsetnotation \relax \fi
+
+\permanent\protected\def\unvboxed {\ifvmode\unvbox \else\box \fi} % will change or used more often
+\permanent\protected\def\unvcopied{\ifvmode\unvcopy\else\copy\fi} % will change or used more often
+
+%D \LMTX\ testcase:
+%D
+%D \starttyping
+%D \hbox {\hbox{\footnote{HH1}\footnote{HH2}}} \par
+%D \hbox {\hbox{x\footnote{XHH1}x\footnote{XHH2}}} \par
+%D \vbox {\setbox0\hbox{\footnote{VH1}\footnote{VH2}}\box0} \par
+%D \vbox {\hbox{\footnote{VH1}xxx\footnote{VH2}}} \par
+%D \vbox {\hbox{\footnote{VH1}\footnote{VH2}}
+%D \hbox{\footnote{VH1}\footnote{VH2}}} \par
+%D \vbox {\hbox{x\footnote{XVH1}x\footnote{XVH2}}} \par
+%D \vbox {\vbox{\footnote{VV1}\footnote{VV2}}} \par % two lines
+%D \vbox {\vbox{x\footnote{XVV1}x\footnote{XVV2}}} \par
+%D \stoptyping
+
+%D Notes have two handlers: notations and notes. Although notations can be defined
+%D independently it makes not much sense. In principle we can treat notes as lists
+%D but they are currently done as a variant on enumerations. I will provide a list
+%D variant as well. One complication is that for page related notes inserts are used
+%D and therefore notes are typeset immediately and descriptions are better suited
+%D for that. For instance endnotes can as well be typeset using lists.
+
+% textcommand/textstyle/textcolor : set by note commandhandler and used for inline number
+
+%D \macros
+%D {setupnote,setupnotation}
+%D
+%D We can influence footnote typesetting with the setup command:
+%D
+%D \showsetup{setupnotation}
+%D \showsetup{setupnote}
+%D
+%D The definition command indicate that we can frame the footnote area. The
+%D footnotes themselves are treated as descriptions.
+%D
+%D \showsetup{definenote}
+%D
+%D It's sort of a custom to precede footnotes by a horizontal rule and although
+%D fancy rules like
+%D
+%D \starttyping
+%D \hbox to 10em{\hskip-3em\dotfill}
+%D \stoptyping
+%D
+%D Are quite ligitimate, we default to a simple one 20\% of the text width.
+
+% \c!headstyle=\noteparameter\c!style,
+% \c!headcolor=\noteparameter\c!color,
+
+\installcorenamespace{notation}
+
+\installcommandhandler \??notation {notation} \??notation
+
+\installcounterassociation{notation}
+
+\aliased\let\setupnotations\setupnotation
+
+\definesymbol[\v!note:\v!previouspage][\llap{\low{\symbol[\v!previouspage]}}]
+\definesymbol[\v!note:\v!nextpage ][\llap{\low{\symbol[\v!nextpage ]}}]
+
+\setupnotations % check with old
+ [\c!alternative=\v!note,
+ %\c!headstyle=,
+ %\c!titlestyle=,
+ %\c!style=,
+ %\c!color=,
+ %\c!headcolor=,
+ %\c!titlecolor=,
+ \c!numbercommand=\high,
+ %\c!symbolcommand=\lowllap, % experiment
+ %\c!indicator=\v!no, % was \v!yes in mkii for page notes
+ \c!width=\v!fit,
+ \c!titledistance=.5em,
+ \c!distance=.5em,
+ %\c!hang=,
+ %\c!sample=,
+ %\c!align=,
+ %\c!headalign=,
+ \c!margin=\v!no,
+ \c!before=,
+ \c!inbetween=,
+ \c!after=,
+ \c!indentnext=\v!yes,
+ \c!indenting=\v!never,
+ \c!titleleft=(,
+ \c!titleright=),
+ %\c!closesymbol=,
+ \c!closecommand=\wordright,
+ \c!display=\v!yes,
+ %\c!command=,
+ \c!titlecommand=,
+ \c!expansion=\v!no,
+ %\c!xmlsetup=,
+ %\s!catcodes=,
+ \c!way=\v!by\v!text,
+ \c!prefix=\v!no,
+ \c!prefixconnector=.,
+ %\c!starter=,
+ %\c!stopper=,
+ \c!number=\v!yes,
+ \c!text=,
+ \c!start=0,
+ \c!state=\v!start,
+ \c!levels=3]
+
+%D The code here is mostly the same as enumerations but we want to keep them
+%D isolated and at some point we might differentiate.
+
+% \installcorenamespace{noteclass}
+
+\protected\def\strc_define_commands_notation#tag#level#parent%
+ {\doifelsenothing{#parent}
+ {\normalexpanded{\defineconstruction[#tag][\s!handler=\v!notation,\c!level=#level]}%
+ \setevalue{\??notation#tag:\s!parent}{\??notation}}%
+ {\normalexpanded{\defineconstruction[#tag][#parent][\s!handler=\v!notation,\c!level=#level]}%
+ \setevalue{\??note #tag:\s!parent}{\??note#parent}% see later for \s!note
+ \setevalue{\??notation#tag:\s!parent}{\??notation#parent}}%
+ \instance\setuevalue{\e!next #tag}{\strc_notations_next {#tag}{\number#level}}% obsolete
+ \instance\setuevalue{\c!reset#tag}{\strc_notations_reset {#tag}{\number#level}}% obsolete
+ \instance\setuevalue {#tag}{\strc_notations_command[#tag]}%
+ \instance\setuevalue{\e!start#tag}{\strc_notations_start [#tag]}%
+ \instance\setuevalue{\e!stop #tag}{\strc_notations_stop }}
+
+\let\m_strc_notation_sub\empty
+
+\appendtoks
+ \let\m_strc_notation_sub\empty
+ \ifempty\currentnotationparent
+ % clone => parent | subclone => clone | subsubclone => subclone
+ \strc_define_commands_notation
+ {\m_strc_notation_sub\currentnotation}%
+ \plusone
+ \empty
+ \edef\p_levels{\notationparameter\c!levels}%
+ \dostepwiserecurse\plustwo\p_levels\plusone
+ {\strc_define_commands_notation
+ {\v!sub\m_strc_notation_sub\currentnotation}%
+ \recurselevel
+ {\m_strc_notation_sub\currentnotation}%
+ \edef\m_strc_notation_sub{\v!sub\m_strc_notation_sub}}%
+ \definelist[\currentnotation]% goodie
+ \else
+ % clone => parent | subclone => subparent | subsubclone => subsubparent
+ \edef\p_levels{\notationparameter\c!levels}%
+ \dorecurse\p_levels
+ {\strc_define_commands_notation
+ {\m_strc_notation_sub\currentnotation}%
+ \recurselevel
+ {\m_strc_notation_sub\currentnotationparent}%
+ \edef\m_strc_notation_sub{\v!sub\m_strc_notation_sub}}%
+ \definelist[\currentnotation][\currentnotationparent]% goodie
+ \fi
+ \edef\p_counter{\notationparameter\s!counter}% can inherit from parent
+ \ifempty\p_counter
+ \let\p_counter\currentnotation
+ \fi
+ \doifelsecounter\p_counter\donothing{\strc_notes_define_counter\p_counter}%
+ \letnotationparameter\s!counter\p_counter
+ %\strc_notes_setup_counter\currentnotation
+\to \everydefinenotation
+
+\let\p_strc_constructions_title \empty
+\let\p_strc_constructions_number\empty
+
+\protected\setvalue{\??constructioninitializer\v!notation}%
+ {\let \currentnotation \currentconstruction
+ \enforced\let\constructionparameter \notationparameter
+ \enforced\let\constructionnamespace \??notation
+ \enforced\let\detokenizedconstructionparameter\detokenizednotationparameter
+ \enforced\let\letconstructionparameter \letnotationparameter
+ \enforced\let\useconstructionstyleandcolor \usenotationstyleandcolor
+ \enforced\let\setupcurrentconstruction \setupcurrentnotation
+ \edef\p_strc_constructions_number{\constructionparameter\c!number}%
+ \ifx\p_strc_constructions_number\v!yes
+ \settrue\c_strc_constructions_number_state
+ \iftrialtypesetting
+ \strc_counters_save\currentconstructionnumber
+ \fi
+ %\strc_counters_increment_sub\currentconstructionnumber\currentconstructionlevel
+ \else
+ \setfalse\c_strc_constructions_number_state
+ \fi
+ \edef\p_strc_constructions_title{\constructionparameter\c!title}%
+ \ifx\p_strc_constructions_title\v!yes
+ \settrue\c_strc_constructions_title_state
+ \else
+ \setfalse\c_strc_constructions_title_state
+ \fi}
+
+\protected\setvalue{\??constructionfinalizer\v!notation}%
+ {\ifconditional\c_strc_constructions_number_state
+ \iftrialtypesetting
+ \strc_counters_restore\currentconstructionnumber
+ \fi
+ \fi}
+
+%D Notations (we simply needed a word that relates to notes and is and sounds like
+%D description and enumeration) are a special case in the sense that they are
+%D stored, rendered elsewhere and referered to from where they are specified. For
+%D that reason they have a different set op main commands.
+
+% \notation[ref]{title}
+% \notation[reference=,title=]
+% \startnotation[ref] title \stopnotation
+
+\protected\def\strc_notations_next {\strc_constructions_next_indeed \namednotationparameter} % #1#2
+\protected\def\strc_notations_reset{\strc_constructions_reset_indeed\namednotationparameter} % #1#2
+%protected\def\strc_notations_set {\strc_constructions_set_indeed \namednotationparameter} % #1#2
+
+\newconditional\c_strc_notations_anchored_next
+
+\def\strc_notations_finalize
+ {\ifconditional\c_strc_notations_anchored_next
+ \expandafter\strc_notations_finalize_next
+ \else
+ \expandafter\strc_notations_finalize_previous
+ \fi}
+
+\def\strc_notations_finalize_previous
+ {\normalexpanded{\endgroup\noteparameter\c!next}}
+
+\def\strc_notations_finalize_next
+ {\endgroup\ignorespaces}
+
+\tolerant\protected\def\strc_notations_command[#1]#*[#2]%
+ {\begingroup
+ \edef\currentnote{#1}%
+ \strc_constructions_initialize{#1}%
+ \strc_notes_synchronize
+ \edef\p_next{\noteparameter\c!anchor}%
+ \ifx\p_next\v!next % for now only two states
+ \settrue \c_strc_notations_anchored_next
+ \else
+ \setfalse\c_strc_notations_anchored_next
+ \fi
+ \ifnotesenabled
+ \strc_counters_increment_sub\currentconstructionnumber\currentconstructionlevel
+ \fi
+ \ifhastok={#2}%
+ \expandafter\strc_notations_command_setups
+ \else
+ \expandafter\strc_notations_command_reference
+ \fi[#2]}
+
+\def\strc_notations_command_setups[#1]%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference=,\c!title=,\c!bookmark=,\c!list=,\c!referencetext=,#1]%
+ \strc_notations_wrapup}
+
+\tolerant\def\strc_notations_command_reference[#1]#*#=%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference={#1},\c!title={#2},\c!bookmark=,\c!list=,\c!referencetext=]%
+ \strc_notations_wrapup}
+
+\def\strc_notations_wrapup
+ {\csname\??constructionnotehandler\currentconstructionhandler\endcsname
+ \strc_constructions_finalize
+ \strc_notations_finalize}
+
+\tolerant\protected\def\strc_notations_start[#1]#*[#2]%
+ {\begingroup
+ \edef\currentnote{#1}%
+ \strc_constructions_initialize{#1}%
+ \strc_notes_synchronize
+ \ifnotesenabled
+ \strc_counters_increment_sub\currentconstructionnumber\currentconstructionlevel
+ \fi
+ \ifhastok={#2}%
+ \expandafter\strc_notations_start_setups
+ \else
+ \expandafter\strc_notations_start_reference
+ \fi[#2]}
+
+\let\strc_notations_pickup_yes\relax
+
+\protected\def\strc_notations_start_setups[#1]%
+ {\normalexpanded{\def\noexpand\strc_notations_pickup_yes##/\csname\e!stop\currentconstruction\endcsname{\strc_notations_start_setups_indeed[#1]{##1}}}%
+ \strc_notations_pickup_yes}
+
+\protected\def\strc_notations_start_setups_indeed[#1]#*#2%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference=,\c!title={#2},\c!bookmark=,\c!list=,\c!referencetext=,#1]%
+ \strc_notations_wrapup}
+
+\protected\def\strc_notations_start_reference[#1]%
+ {\normalexpanded{\def\noexpand\strc_notations_pickup_yes##/\csname\e!stop\currentconstruction\endcsname{\strc_notations_start_reference_indeed[#1]{##1}}}%
+ \strc_notations_pickup_yes}
+
+\protected\def\strc_notations_start_reference_indeed[#1]#*#2%
+ {\strc_constructions_register[][\c!label={\descriptionparameter\c!text},\c!reference={#1},\c!title={#2},\c!bookmark=,\c!list=,\c!referencetext=]%
+ \strc_notations_wrapup}
+
+\protected\def\strc_notations_stop
+ {}
+
+%D A complication is that we need to set up rather specific properties of e.g.
+%D footnotes. It is for this reason that we introduce an extra level of
+%D indirectness. This way notations don't bark on undefined macros when used in
+%D combination.
+
+\protected\setvalue{\??constructionnotehandler\v!notation}%
+ {\csname\??constructionnotehandler\currentconstructionhandler:\constructionparameter\c!type\endcsname}
+
+\protected\setvalue{\??constructionnotehandler\v!notation:}% empty case
+ {[\currentconstructionhandler:\currentconstruction]}
+
+%D Here is a simple renderer for notes
+
+\defineconstructionalternative
+ [\v!note]
+ [\c!renderingsetup=\??constructionrenderings:\v!note]
+
+\startsetups[\??constructionrenderings:\v!note]
+ \noindent
+ \leftboundary % experiment, to be done in more places
+ \llap{\box\constructionheadbox\hskip\constructionparameter\c!distance}%
+ \useconstructionstyleandcolor\c!style\c!color
+ \ignorespaces
+\stopsetups
+
+%D We now implement the note definer.
+
+\installcorenamespace{note}
+
+\installframedcommandhandler \??note {note} \??note
+
+\let\setupnotes\setupnote
+
+% These only concern the inline symbol/number and wrapping of the whole list.
+
+\setupnotes % not all make sense here
+ [\c!location=\v!page,
+ %\c!conversion=,
+ \c!rule=\v!on,
+ \c!before=\blank,
+ \c!bodyfont=\v!small,
+ \c!anchor=, % can also be v!next
+ %\c!style=,
+ %\c!color=,
+ %\c!after=,
+ %\c!rulecolor=,
+ %\c!rulecommand=,
+ \c!rulethickness=\linewidth,
+ \c!frame=\v!off,
+ \c!distance=.125em, % in the text between symbols
+ % \c!textseparator={,}, % optional separator
+ \c!columndistance=1em,
+ % \c!margindistance=.5em,
+ \c!align=, % also use instead of \c!tolerance
+ \c!split=\v!tolerant,
+ \c!width=\makeupwidth, % \ifdim\hsize<\makeupwidth\hsize\else\makeupwidth\fi
+ \c!height=\textheight,
+ \c!command=, % \noteparameter\c!numbercommand, % (command in enumeration) too messy, called twice
+ \c!separator=,%
+ \c!textcommand=\high,
+ \c!textstyle=\tx,
+ %\c!textcolor=,
+ \c!interaction=\v!yes,
+ %\c!factor=,
+ %\c!scope=, % \v!text \v!page
+ \c!prefixconnector=.,
+ %\c!next=\autoinsertnextspace,
+ \c!prefix=\v!no,
+ %\c!continue=\v!no,
+ \c!paragraph=\v!no,
+ \c!inbetween=\hskip\emwidth,
+ \c!symbol=\mathematics{*},
+ \c!n=1]
+
+\setupnotes
+ [\c!expansion=\v!no,
+ \c!xmlsetup=,
+ \s!catcodes=]
+
+%D Insertions are part of notes.
+
+% \installcorenamespace{noteinsertion}
+
+\permanent\def\currentnoteinsertion {\noteparameter\s!insert}
+\permanent\def\currentnoteinsertionnumber{\namedinsertionnumber{\noteparameter\s!insert}}
+
+\appendtoks
+ \ifempty\currentnoteparent
+ \doifelseinsertion\currentnote
+ \donothing
+ {\defineinsertion[\currentnote]% could be an option
+ \normalexpanded{\t_strc_notes{\the\t_strc_notes\noexpand\strc_notes_process_list{\currentnote}}}}%
+ \letnoteparameter\s!insert\currentnote
+ \definenotation[\currentnote][\c!type=\v!note]%
+ \else
+ \setexpandednoteparameter\s!insert{\namednoteparameter\currentnoteparent\s!insert}%
+ \definenotation[\currentnote][\currentnoteparent][\c!type=\v!note]%
+ \fi
+ \clf_definenote
+ {\currentnote}%
+ {insert}%
+ \currentnoteinsertionnumber
+ \relax
+\to \everydefinenote
+
+% maybe we will share this at some point:
+
+\def\strc_notes_define_counter#tag% todo: move inline
+ {\definecounter[#tag]%
+ \registerenumerationcounter{#tag}}
+
+\appendtoks
+ \synchronizenotationcounters
+\to \everysetupnotation
+
+\appendtoks
+ \synchronizenotationcounters
+\to \everydefinenotation
+
+% so far
+
+%letcsname\??constructionstarthandler \v!notation\expandafter\endcsname\csname\??constructionstarthandler \v!enumeration\endcsname
+\letcsname\??constructionstarthandler \v!notation\expandafter\endcsname\csname\??constructionstarthandler \v!construction\endcsname % no par mess
+\letcsname\??constructionstophandler \v!notation\expandafter\endcsname\csname\??constructionstophandler \v!enumeration \endcsname
+\letcsname\??constructioncommandhandler\v!notation\expandafter\endcsname\csname\??constructioncommandhandler\v!enumeration \endcsname
+\letcsname\??constructiontexthandler \v!notation\expandafter\endcsname\csname\??constructiontexthandler \v!enumeration \endcsname
+
+\protected\setvalue{\??constructionmainhandler\v!notation}#following%
+ {\iftrialtypesetting \else
+ \begingroup
+ \currentconstructionsynchronize
+ \c_attr_destination\currentconstructionattribute\relax % todo, whole text
+ \signalcharacter
+ \endgroup
+ \fi#following}
+
+\protected\setvalue{\??constructionnotehandler\v!notation:\v!note}% in the running text
+ {\ifnotesenabled
+ % do be done elsewhere
+ %
+ %let\currentnote\currentconstructionmain
+ \let\currentnote\currentconstruction % else wrong inheritance
+ %
+ \iftrialtypesetting
+ \strc_notes_inject_dummy
+ \else
+ \begingroup
+ \edef\currentnotenumber{\clf_storenote{\currentnote}\currentconstructionlistentry}%
+ \settrue\processingnote
+ \ifconditional\c_strc_notes_skip
+ \glet\lastnotesymbol\strc_notes_inject_symbol_nop
+ \else
+ \iftypesettinglines % otherwise problems with \type <crlf> {xxx}
+ \ignorelines % makes footnotes work in \startlines ... \stoplines
+ \fi
+ \ifconditional\c_strc_notes_symbol
+ \strc_notes_inject_symbol_yes
+ \else
+ \unskip\unskip
+ \glet\lastnotesymbol\strc_notes_inject_symbol_yes
+ \fi
+ \fi
+ \ifconditional\postponingnotes % todo: per note class
+ \global\settrue\postponednote
+ \orelse\ifconditional\inlocalnotes % todo: per note class
+ \global\settrue\postponednote
+ \orelse\ifconditional\c_strc_notes_delayed
+ % probably end notes
+ \else
+ \handlenoteinsert\currentnote\currentnotenumber % either an insert or just delayed
+ \fi
+ \endgroup
+ \fi
+ \fi
+ \ifconditional\c_strc_notes_skip
+ \global\setfalse\c_strc_notes_skip
+ \else
+ \kern\d_strc_notes_signal\relax % \relax is needed to honor spaces
+ \fi}
+
+%D Interaction in notes is somewhat complex due to the way notes get flushed. In
+%D principle it is more or less the same as lists but where in lists we pack whole
+%D entries, in notes this doesn't happen. Okay, in retrospect we could have made
+%D descriptions lists but that will be a backward compatibility mess. At some point
+%D a completely new mechanism might show up, but not now. Also, as notes are inserts
+%D there is some extra mess to be kept in mind and it's easier to maintain two
+%D mechanisms than to combine too much.
+%D
+%D Interaction is also complicated because we want to provide several variants. For
+%D simple reference there is no need for anything special, as page references will
+%D do and we store them in the list anyway. But if we want a destination with
+%D dimensions we will use an additional destination because we can have only one
+%D with the same name and we always have the number as one.
+
+% interaction:
+%
+% all : text and number
+% number|yes: only number
+% text : only text
+%
+% \dogetsimple : injects
+
+\installcorenamespace{noteinteractioninline}
+\installcorenamespace{noteinteractiondisplay}
+
+\newconstant\a_strc_notes_symbol_reference
+\newconstant\a_strc_notes_number_reference
+\newconstant\a_strc_notes_text_reference
+\newconstant\a_strc_notes_text_destination
+
+\let\strc_notes_get_reference_attribute_symbol \empty
+\let\strc_notes_get_destination_attribute_symbol\empty
+
+\def\strc_notes_interaction_check_inline
+ {\edef\p_interaction{\noteparameter\c!interaction}%
+ \csname\??noteinteractioninline
+ \ifcsname\??noteinteractioninline\p_interaction\endcsname\p_interaction\else\v!no\fi
+ \endcsname}
+
+\def\strc_notes_interaction_check_display
+ {\edef\p_interaction{\noteparameter\c!interaction}%
+ \csname\??noteinteractiondisplay
+ \ifcsname\??noteinteractiondisplay\p_interaction\endcsname\p_interaction\else\v!no\fi
+ \endcsname}
+
+\mutable\let\currentnotenumber\!!zerocount
+
+\let\strc_notes_get_reference_attribute_symbol \empty
+\let\strc_notes_get_destination_attribute_symbol\empty
+
+\let\strc_notes_set_reference_attribute_number\donothing
+\let\strc_notes_set_reference_attribute_text \donothing
+\let\strc_notes_set_destination_attribute_text\donothing
+
+\let\strc_notes_set_style_color_inline \relax
+\let\strc_notes_set_style_color_display\relax
+
+% inline
+
+\let\m_strc_notes_internal\relax
+
+\def\strc_notes_prepare_inline_references_nop
+ {\let\strc_notes_get_reference_attribute_symbol\empty
+ \let\strc_notes_get_destination_attribute_symbol\empty
+ \let\strc_notes_set_style_color_inline\strc_notes_set_style_color_inline_nop}
+
+\def\strc_notes_prepare_inline_references_yes
+ {\edef\m_strc_notes_internal{\clf_noteinternal{\currentnote}\currentnotenumber}%
+ \strc_references_set_simple_reference{*\m_strc_notes_internal}% destination
+ \strc_references_get_simple_reference{internal(\m_strc_notes_internal)}% reference
+ \edef\strc_notes_get_destination_attribute_symbol{attr\destinationattribute\currentdestinationattribute}%
+ \edef\strc_notes_get_reference_attribute_symbol{attr\referenceattribute\currentreferenceattribute}%
+ \let\strc_notes_set_style_color_inline\strc_notes_set_style_color_inline_yes}
+
+\letvalue{\??noteinteractioninline\v!no }\strc_notes_prepare_inline_references_nop
+\letvalue{\??noteinteractioninline\v!all }\strc_notes_prepare_inline_references_yes
+\letvalue{\??noteinteractioninline\v!number}\strc_notes_prepare_inline_references_yes
+\letvalue{\??noteinteractioninline\v!text }\strc_notes_prepare_inline_references_yes
+\letvalue{\??noteinteractioninline\v!yes }\strc_notes_prepare_inline_references_yes
+
+% display (for 'all' we need unique text and number attributes so we resolve twice
+% as we otherwise don't get the number one which is lapped in the margin so we need
+% to explicitly visit it)
+
+\def\strc_notes_prepare_display_references_nop
+ {\let\strc_notes_set_reference_attribute_number\donothing
+ \let\strc_notes_set_reference_attribute_text\donothing
+ \let\strc_notes_set_destination_attribute_text\donothing
+ \let\strc_notes_set_style_color_display\strc_notes_set_style_color_display_nop}
+
+\def\strc_notes_prepare_display_references_yes_number
+ {\edef\m_strc_notes_internal{\clf_noteinternal{\currentnote}\currentnotenumber}%
+ \ifcase\m_strc_notes_internal\relax
+ \strc_notes_prepare_display_references_nop
+ \else
+ \let\strc_notes_set_reference_attribute_text\donothing
+ \strc_references_get_simple_reference{*\m_strc_notes_internal}% reference
+ \edef\strc_notes_set_reference_attribute_number{\c_attr_reference\currentreferenceattribute}%
+ \let\strc_notes_set_style_color_display\strc_notes_set_style_color_display_yes
+ \fi}
+
+\def\strc_notes_prepare_display_references_yes_text
+ {\edef\m_strc_notes_internal{\clf_noteinternal{\currentnote}\currentnotenumber}%
+ \ifcase\m_strc_notes_internal\relax
+ \strc_notes_prepare_display_references_nop
+ \else
+ \strc_references_get_simple_reference{*\m_strc_notes_internal}% reference
+ \edef\strc_notes_set_reference_attribute_text{\c_attr_reference\currentreferenceattribute}%
+ \let\strc_notes_set_reference_attribute_number\donothing
+ \let\strc_notes_set_style_color_display\strc_notes_set_style_color_display_yes
+ \fi}
+
+\def\strc_notes_prepare_display_references_yes_all
+ {\edef\m_strc_notes_internal{\clf_noteinternal{\currentnote}\currentnotenumber}%
+ \ifcase\m_strc_notes_internal\relax
+ \strc_notes_prepare_display_references_nop
+ \else
+ \strc_references_get_simple_reference{*\m_strc_notes_internal}% reference
+ \edef\strc_notes_set_reference_attribute_text{\c_attr_reference\currentreferenceattribute}%
+ \strc_references_get_simple_reference{*\m_strc_notes_internal}% reference
+ \edef\strc_notes_set_reference_attribute_number{\c_attr_reference\currentreferenceattribute}%
+ \let\strc_notes_set_style_color_display\strc_notes_set_style_color_display_yes
+ \fi}
+
+\letvalue{\??noteinteractiondisplay\v!no }\strc_notes_prepare_display_references_nop
+\letvalue{\??noteinteractiondisplay\v!all }\strc_notes_prepare_display_references_yes_all
+\letvalue{\??noteinteractiondisplay\v!number}\strc_notes_prepare_display_references_yes_number
+\letvalue{\??noteinteractiondisplay\v!text }\strc_notes_prepare_display_references_yes_text
+\letvalue{\??noteinteractiondisplay\v!yes }\strc_notes_prepare_display_references_yes_number
+
+\mutable\let\strc_notes_set_style_color_inline_nop \usenotestyleandcolor
+\mutable\let\strc_notes_set_style_color_display_nop\usenotationstyleandcolor
+
+\protected\def\strc_notes_set_style_color_inline_yes#style#color%
+ {\usenotestyleandcolor#style#color%
+ \iflocation\strc_notes_set_style_color_special\fi}
+
+\protected\def\strc_notes_set_style_color_display_yes#style#color%
+ {\usenotationstyleandcolor#style#color%
+ \iflocation\strc_notes_set_style_color_special\fi}
+
+\def\strc_notes_set_style_color_special
+ {\iftrialtypesetting
+ % keep
+ \orelse\ifempty\currentcolorparameter
+ \scratchcounter\clf_notedeltapage{\currentnote}\currentnotenumber\relax % todo calculate once
+ \setlocationcolorspecified\scratchcounter
+ \fi}
+
+\setvalue{\??constructiontexthandler\v!notation}%
+ {\begingroup
+ % we need to retrigger the reference as otherwise it gets lost because we don't do nested
+ % references with the same id ... maybe some day if we can figure out a nice heuristic ...
+ % the problem is that normally it's no issue but here we lap into the margin, so maybe that's
+ % a criterium
+ % \strc_notes_interaction_check_display
+ \strc_notes_set_reference_attribute_number
+ \dotagsetnotation
+ \strc_notes_set_style_color_display\c!headstyle\c!headcolor
+ \strc_enumerations_text
+ \endgroup}
+
+% in mkii the pointer only showed up in pagewise notes
+
+\protected\def\strc_notes_inject_pointer % todo calculate once
+ {\ifcase\clf_notedeltapage{\currentnote}\currentnotenumber\relax\relax
+ % unknown
+ \or
+ % same page
+ \or
+ \noteparameter\c!symbolcommand{\symbol[\v!note:\v!nextpage]}%
+ \or
+ \noteparameter\c!symbolcommand{\symbol[\v!note:\v!previouspage]}%
+ \fi}
+
+\protected\def\strc_notes_inject_symbol_yes
+ {\strc_notes_inject_symbol_indeed\conditionaltrue}
+
+\protected\def\strc_notes_inject_symbol_nop
+ {\strc_notes_inject_symbol_indeed\conditionalfalse}
+
+\definemargindata
+ [strc_notes_destination_margin]
+ [\v!left]
+ [\c!margin=\zeropoint,
+ \c!width=\zeropoint,
+ \c!style=,
+ \c!color=]
+
+\protected\def\strc_notes_inject_symbol_indeed#synchronize%
+ {\ifconditional\c_strc_notations_anchored_next\else
+ \removeunwantedspaces
+ \doifelseitalic\/\donothing % Charles IV \footnote{the fourth}
+ \fi
+ \ifdim\lastkern=\d_strc_notes_signal
+ % \kern\noteparameter\c!distance % yes or no note font? or main text
+ \strc_notes_inject_separator
+ \fi
+ \nobreak
+ \begingroup
+ \strc_notes_interaction_check_inline
+ \strc_notes_set_style_color_inline\c!textstyle\c!textcolor
+ \hbox \strc_notes_get_reference_attribute_symbol \strc_notes_get_destination_attribute_symbol \bgroup
+ % \hbox \strc_notes_get_reference_attribute_symbol \bgroup \strc_notes_destination_hack
+ \strc_references_flush_destination_nodes % a bit late but ok
+ \dostarttagged\t!descriptionsymbol\currentnote
+ \dotagsetnotesymbol
+ \noteparameter\c!textcommand{\clf_noteprefixednumber{\currentnote}\currentnotenumber\relax}%
+ % the next one can cycle so we need to make sure it has no advance width
+ \doif{\noteparameter\c!indicator}\v!yes\strc_notes_inject_pointer
+ \dostoptagged
+ \egroup
+ \endgroup
+ \glet\lastnotesymbol\empty}
+
+\protected\def\strc_notes_inject_dummy % temp hack
+ {\removeunwantedspaces
+ \doifelseitalic\/\donothing % Charles IV \footnote{the fourth}
+ \ifdim\lastkern=\d_strc_notes_signal
+ % \kern\noteparameter\c!distance % yes or no note font? or main text
+ \strc_notes_inject_separator
+ \fi
+ \nobreak
+ \hpack to .5\emwidth{}%
+ \glet\lastnotesymbol\empty}
+
+\protected\def\strc_notes_inject_separator % patch by WS due to request on list
+ {\edef\p_textseparator{\noteparameter\c!textseparator}%
+ \ifempty\p_textseparator
+ \kern\noteparameter\c!distance
+ \else
+ % skip or kern
+ \nobreak
+ \hbox\bgroup
+ \strc_notes_interaction_check_inline
+ \strc_notes_set_style_color_inline\c!textstyle\c!textcolor
+ \noteparameter\c!textcommand{\p_textseparator}%
+ \kern\noteparameter\c!distance
+ \egroup
+ \nobreak
+ \fi}
+
+% this needs a further cleanup ... soon as it's a slow mechanism
+%
+% -- set breakpoint in descriptions
+% -- reset after trialtypesetting
+% -- that way we can trick the symbol space
+
+% removed:
+%
+% \pushsomestates
+%
+% core-ins -> obsolete
+%
+% saveinsertiondata
+% restoreinsertiondata
+% saveinsertionbox
+% eraseinsertionbackup
+% restoreinsertionbackup
+
+% \def\savenotedata {} % \writestatus{todo}{save note data}}
+% \def\restorenotedata {} % \writestatus{todo}{restore note data}}
+% \def\savenotecontent {} % \writestatus{todo}{save note content}}
+% \def\restorenotecontent{} % \writestatus{todo}{restore note content}}
+% \def\erasenotebackup {} % \writestatus{todo}{erase note backup}}
+
+% page-set:
+
+% \def\enablenotes {\writestatus{todo}{enable notes}}
+% \def\disablenotes {\writestatus{todo}{disable notes}}
+% \def\savenotes {\writestatus{todo}{save notes}}
+% \def\flushsavednotes{\writestatus{todo}{flush notes}}
+
+% experiment: (compare scope=text and scope=page)
+%
+% \definenote[mynote][way=bytext,location=text,width=\leftmarginwidth,scope=page,rule=,before=,after=,factor=0]
+% \setuptexttexts[margin][\vbox to \textheight{\placenotes[mynote]\vfill}][]
+
+%D Footnotes are can be characterized by three components:
+%D
+%D \startitemize[packed]
+%D \item a small number \footnote {a footnote number} or symbol {\setupfootnotes
+%D [conversion=set 2]\footnote {a footnote}}
+%D \item and a similar mark at the bottom of the page
+%D \item followed by some additional text
+%D \stopitemize
+%D
+%D Because footnotes are declared at the location of their reference they can be
+%D seen as a special kind of floating bodies. Their placement is postponed but has
+%D to be taken into account in the pagebreak calculations. This kind of calculations
+%D are forced by using \type{\insert}s and dealing with all cases is not trivial.
+
+%D \macros
+%D {notesenabled}
+%D
+%D We need a couple of states because at some moments we don't want to mess around
+%D with inserts at all. Take for instance a table of contents. And so we can
+%D temporary disable footnotes by saying
+%D
+%D \starttyping
+%D \notesenabledfalse
+%D \stoptyping
+
+\newif\ifnotesenabled \notesenabledtrue
+
+% better mark a note .. once flushed no more flushing
+
+%appendtoks \notesenabledfalse \to \everymarking
+\appendtoks \notesenabledfalse \to \everybeforepagebody
+\appendtoks \notesenabledfalse \to \everystructurelist % quick hack
+\appendtoks \notesenabledfalse \to \everysimplifycommands % quick hack
+\appendtoks \notesenabledfalse \to \everypreroll % quick hack
+
+%D Often we need to process the whole set of notes and to make that fast, we use a
+%D token register:
+
+% we have several synchronizers:
+%
+% - after a definition
+% - after a specific setup
+% - after a general setup (inheritance of dimensions)
+% - just before a note is typeset
+
+\newtoks\t_strc_notes
+
+\let\strc_notes_process_list\gobbleoneargument
+
+\protected\def\strc_notes_process#action% argument is a \macro that uses \currentnote
+ {\def\strc_notes_process_list##1{\edef\currentnote{##1}\let\currentdescription\currentnote#action}%
+ \the\t_strc_notes}
+
+\newtoks\everychecknote % just before a note is typeset
+\newtoks\everysynchronizenote % after a general setup has happened
+
+\appendtoks
+ \ifempty\currentnote \else
+ \setupnotations[\currentnote][]% also a synchronize
+ \fi
+\to \everysynchronizenote
+
+\def\strc_notes_synchronize
+ {\the\everysynchronizenote} % we can speed this one up if needed by avoiding the commalist
+
+\appendtoks
+ \strc_notes_process\strc_notes_synchronize
+\to \everysetupnoteroot
+
+\appendtoks
+ \the\everysynchronizenote
+\to \everydefinenote
+
+% \starttext
+% text \startfootnote Test.\stopfootnote
+% test \footnote{xxxx} \subfootnote{xxxx}
+% test \footnote{xxxx} \subfootnote{xxxx}
+% \stoptext
+
+\installcorenamespace{notecommand}
+\installcorenamespace{notealign}
+\installcorenamespace{notepenalty}
+\installcorenamespace{noterule}
+
+\permanent\protected\def\currentnoterulecommand{\begincsname\??notecommand\currentnote\endcsname}
+\permanent\protected\def\currentnoterulealign {\begincsname\??notealign \currentnote\endcsname}
+
+\permanent\protected\def\currentnoterulecommandcommand{\noteparameter\c!rulecommand}
+\permanent\protected\def\currentnoterulecommandnormal {\normalnoterule} % no let as it can be changed afterwards
+
+\permanent\protected\def\letcurrentnoterulecommand{\enforced\letcsname\??notecommand\currentnote\endcsname}
+\permanent\protected\def\letcurrentnoterulealign {\enforced\letcsname\??notealign \currentnote\endcsname}
+
+\appendtoks
+ \enforced\letcsname\??notecommand\currentnote\endcsname\currentnoterulecommandnormal
+ \enforced\letcsname\??notealign \currentnote\endcsname\lefttoright
+\to \everysynchronizenote
+
+\def\strc_notes_set_rule
+ {\letcurrentnoterulecommand\relax % so we default to nothing
+ \letcurrentnoterulealign \relax
+ \processcommacommand[\noteparameter\c!rule]\strc_notes_set_rule_step}
+
+\def\strc_notes_set_rule_step#alternative%
+ {\begincsname\??noterule#alternative\endcsname}
+
+\def\strc_notes_set_rule_autodir
+ {\doifelserighttoleftinbox\currentnoteinsertionnumber\righttoleft\lefttoright}
+
+\setvalue{\??noterule \v!command}{\letcurrentnoterulecommand\currentnoterulecommandcommand}
+\setvalue{\??noterule \v!on}{\letcurrentnoterulecommand\currentnoterulecommandnormal}
+\setvalue{\??noterule \v!normal}{\letcurrentnoterulecommand\currentnoterulecommandnormal}
+\setvalue{\??noterule \v!left}{\letcurrentnoterulecommand\currentnoterulecommandnormal
+ \letcurrentnoterulealign \lefttoright}
+\setvalue{\??noterule \v!right}{\letcurrentnoterulecommand\currentnoterulecommandnormal
+ \letcurrentnoterulealign \righttoleft}
+\setvalue{\??noterule\v!paragraph}{\letcurrentnoterulecommand\currentnoterulecommandnormal
+ \letcurrentnoterulealign \strc_notes_set_rule_autodir}
+\setvalue{\??noterule \v!off}{\letcurrentnoterulecommand\relax}
+
+\appendtoks
+ \strc_notes_set_rule
+\to \everysynchronizenote
+
+\permanent\def\currentnotepenalty
+ {\ifcsname\??notepenalty\noteparameter\c!split\endcsname
+ \lastnamedcs
+ \else
+ \numexpr\noteparameter\c!split\relax
+ \fi}
+
+\setnewconstant\notepenaltytolerant \zerocount
+\setnewconstant\notepenaltystrict 9999
+\setnewconstant\notepenaltyverystrict\maxdimen
+
+\letvalue{\??notepenalty\v!tolerant }\notepenaltytolerant
+\letvalue{\??notepenalty\v!strict }\notepenaltystrict
+\letvalue{\??notepenalty\v!verystrict}\notepenaltyverystrict
+\letvalue{\??notepenalty }\notepenaltytolerant
+
+%D The following switch can be used to disable limiting the height of the footnote
+%D area, something that is needed in multi column balancing. Use this switch with
+%D care.
+
+\newif\ifnotelimit \notelimittrue % shared
+
+\def\strc_notes_set_factor
+ {\edef\p_factor{\noteparameter\c!factor}%
+ \ifempty\p_factor \else
+ \ifnum\p_factor<\zerocount \else
+% \global
+ \count\currentnoteinsertionnumber\p_factor % new: global
+ \fi
+ \fi}
+
+\appendtoks
+ \strc_notes_set_factor
+\to \everysynchronizenote
+
+% locations:
+
+\installcorenamespace{notelocationvariant}
+\installcorenamespace{notepositionvariant}
+\installcorenamespace{notedelayedvariant}
+\installcorenamespace{notelocation}
+
+\newconditional\c_strc_notes_delayed
+
+\protected\def\strc_notes_set_delayed_yes{\settrue \c_strc_notes_delayed}
+\protected\def\strc_notes_set_delayed_nop{\setfalse\c_strc_notes_delayed}
+
+\setvalue{\??notelocation\v!page }{\letcsname\??notedelayedvariant \currentnote\endcsname\strc_notes_set_delayed_nop
+ \letcsname\??notelocationvariant\currentnote\endcsname\strc_notes_set_location_page}
+\setvalue{\??notelocation\v!columns }{\letcsname\??notedelayedvariant \currentnote\endcsname\strc_notes_set_delayed_nop
+ \letcsname\??notelocationvariant\currentnote\endcsname\strc_notes_set_location_columns}
+\setvalue{\??notelocation\v!lastcolumn }{\letcsname\??notedelayedvariant \currentnote\endcsname\strc_notes_set_delayed_nop
+ \letcsname\??notelocationvariant\currentnote\endcsname\strc_notes_set_location_lastcolumn}
+\setvalue{\??notelocation\v!firstcolumn}{\letcsname\??notedelayedvariant \currentnote\endcsname\strc_notes_set_delayed_nop
+ \letcsname\??notelocationvariant\currentnote\endcsname\strc_notes_set_location_firstcolumn}
+\setvalue{\??notelocation\v!none }{\letcsname\??notedelayedvariant \currentnote\endcsname\strc_notes_set_delayed_yes
+ \letcsname\??notelocationvariant\currentnote\endcsname\strc_notes_set_location_none}
+\setvalue{\??notelocation\v!text }{\letcsname\??notedelayedvariant \currentnote\endcsname\strc_notes_set_delayed_yes
+ \letcsname\??notelocationvariant\currentnote\endcsname\strc_notes_set_location_text}
+\setvalue{\??notelocation\v!high }{\letcsname\??notepositionvariant\currentnote\endcsname\strc_notes_set_position_high}
+\setvalue{\??notelocation\v!bottom }{\letcsname\??notepositionvariant\currentnote\endcsname\strc_notes_set_position_bottom}
+
+\setvalue{\??notedelayedvariant \??notedelayedvariant }{\strc_notes_set_delayed_nop} % not let
+\setvalue{\??notepositionvariant\??notepositionvariant}{\strc_notes_set_position_bottom} % not let
+\setvalue{\??notelocationvariant\??notelocationvariant}{\strc_notes_set_location_page} % not let
+
+\protected\def\strc_notes_set_delayed
+ {\csname\??notedelayedvariant
+ \ifcsname\??notedelayedvariant\currentnote\endcsname
+ \currentnote
+ \else
+ \??notedelayedvariant
+ \fi
+ \endcsname}
+
+\protected\def\strc_notes_set_position
+ {\csname\??notepositionvariant
+ \ifcsname\??notepositionvariant\currentnote\endcsname
+ \currentnote
+ \else
+ \??notepositionvariant
+ \fi
+ \endcsname}
+
+\protected\def\strc_notes_set_location
+ {\csname\??notelocationvariant
+ \ifcsname\??notelocationvariant\currentnote\endcsname
+ \currentnote
+ \else
+ \??notelocationvariant
+ \fi
+ \endcsname}
+
+\protected\def\strc_notes_set_variants
+ {\normalexpanded{\rawprocesscommalist[\noteparameter\c!location]\strc_notes_set_location_step}}
+
+\protected\def\strc_notes_set_location_step#alternative% the insert related one
+ {\begincsname\??notelocation#alternative\endcsname}
+
+\appendtoks
+ \strc_notes_set_variants
+ \strc_notes_set_delayed
+\to \everysynchronizenote
+
+\newskip \s_strc_notes_distance % we need to implement stretch
+\newcount\c_strc_notes_columns
+
+\newskip \s_strc_notes_before
+\newskip \s_strc_notes_inbetween
+\newconditional\c_strc_notes_first_flushed
+
+\appendtoks
+ \edef\p_spacebefore{\rootnoteparameter\c!spacebefore}%
+ \ifempty\p_spacebefore
+ \global\s_strc_notes_before\zeropoint
+ \else
+ \setbox\scratchbox\vbox{\blank[\p_spacebefore]\global\s_strc_notes_before\lastskip}%
+ \fi
+ \edef\p_spaceinbetween{\rootnoteparameter\c!spaceinbetween}%
+ \ifempty\p_spaceinbetween
+ \global\s_strc_notes_inbetween\zeropoint
+ \else
+ \setbox\scratchbox\vbox{\blank[\p_spaceinbetween]\global\s_strc_notes_inbetween\lastskip}%
+ \fi
+\to \everysynchronizenote
+
+\def\strc_notes_set_distance
+ {\begingroup
+ \restoreglobalbodyfont
+ \setbox\scratchbox\vbox % no reuse as it can mirror
+ {\forgetall
+ \dontcomplain
+ \noteparameter\c!before
+ \placenoterule
+ \strut
+ \noteparameter\c!after}%
+ % also dp now
+ \scratchdimen\dimexpr\htdp\scratchbox-\lineheight\relax
+ \ifgridsnapping
+ \getnoflines\scratchdimen
+ \scratchdimen\noflines\lineheight
+ \fi
+ \expandafter\endgroup\expandafter
+ \s_strc_notes_distance\the\scratchdimen\relax}
+
+\def\strc_notes_set_columns
+ {\c_strc_notes_columns\noteparameter\c!n\relax
+ \ifcase\c_strc_notes_columns
+ \c_strc_notes_columns\plusone
+ \fi}
+
+\def\strc_notes_set_location_page
+ {\setfalse\c_strc_notes_delayed
+ \strc_notes_set_distance
+ \strc_notes_set_columns
+ \page_inserts_set_location\currentnoteinsertion\v!page % \setupinsertion[\currentnote][\c!location=\v!page]%
+ \global\count\currentnoteinsertionnumber\numexpr(\plusthousand/\c_strc_notes_columns)*\nofmulticolumns\relax
+ \global\dimen\currentnoteinsertionnumber\ifnotelimit\dimexpr\noteparameter\c!height*\c_strc_notes_columns\relax\else\maxdimen\fi
+ \global\skip \currentnoteinsertionnumber\s_strc_notes_distance}
+
+\def\strc_notes_set_location_columns
+ {\setfalse\c_strc_notes_delayed
+ \strc_notes_set_distance
+ \strc_notes_set_columns
+ % \ifnum\currentnofcolumns=\zerocount
+ \ifnum\nofcolumns=\zerocount
+ \c_strc_notes_columns\plusone
+ \fi
+ \page_inserts_set_location\currentnoteinsertion\v!columns % \setupinsertion[\currentnote][\c!location=\v!columns]%
+ \global\count\currentnoteinsertionnumber\numexpr\plusthousand/\c_strc_notes_columns\relax
+ \global\dimen\currentnoteinsertionnumber\ifnotelimit\dimexpr\noteparameter\c!height*\c_strc_notes_columns\relax\else\maxdimen\fi
+ \global\skip \currentnoteinsertionnumber\s_strc_notes_distance}
+
+\def\strc_notes_set_location_somecolumn#whatcolumn%
+ {\setfalse\c_strc_notes_delayed
+ \strc_notes_set_distance
+ \strc_notes_set_columns
+ \page_inserts_set_location\currentnoteinsertion#whatcolumn% \setupinsertion[\currentnote][\c!location=#whatcolumn]%
+ \global\count\currentnoteinsertionnumber\plusthousand
+ \global\dimen\currentnoteinsertionnumber\ifnotelimit\noteparameter\c!height\else\maxdimen\fi
+ \global\skip \currentnoteinsertionnumber\s_strc_notes_distance}
+
+\def\strc_notes_set_location_firstcolumn{\strc_notes_set_location_somecolumn\v!firstcolumn}
+\def\strc_notes_set_location_lastcolumn {\strc_notes_set_location_somecolumn\v!lastcolumn }
+
+\def\strc_notes_set_location_text % we don't use inserts anyway (e.g. endnotes)
+ {\settrue\c_strc_notes_delayed
+ \clf_setnotestate{\currentnote}{store}%
+ \page_inserts_set_location\currentnoteinsertion\v!text % \setupinsertion[\currentnote][\c!location=\v!text]%
+ \global\count\currentnoteinsertionnumber\zerocount
+ \global\dimen\currentnoteinsertionnumber\maxdimen
+ \global\skip \currentnoteinsertionnumber\zeropoint}
+
+\let\strc_notes_set_location_none\strc_notes_set_location_text
+
+\def\strc_notes_set_properties
+ {\strc_notes_set_columns
+ \strc_notes_set_distance
+ \strc_notes_set_location
+ \strc_notes_set_delayed}
+
+\let\strc_notes_set_position_high\relax
+
+\def\strc_notes_set_position_bottom
+ {\settrue\c_notes_bottom_present}
+
+\appendtoks
+ \strc_notes_set_properties
+\to \everysynchronizenote
+
+%D A fast checker for bottom notes being used:
+
+\newconditional\c_notes_bottom_present
+
+\def\strc_notes_check_if_bottom_present_indeed % in otr !
+ {\ifvoid\currentnoteinsertionnumber\else
+ \strc_notes_set_position
+ \fi}
+
+\def\strc_notes_check_if_bottom_present_step
+ {\ifconditional\c_notes_bottom_present\else\strc_notes_check_if_bottom_present_indeed\fi}
+
+\def\strc_notes_check_if_bottom_present
+ {\setfalse\c_notes_bottom_present
+ \strc_notes_process\strc_notes_check_if_bottom_present_step}
+
+% Example of using factor:
+%
+% \definenote[mynote][way=bypage,location=text,width=\marginwidth,rule=,before=,factor=0]
+% \setuplayout[backspace=5cm,margin=3cm,margindistance=.5cm,width=middle]
+% \setuptexttexts[margin][\vbox to \textheight{\placenotes[mynote]\vfill}][]
+% \starttext
+% \dorecurse{10}{test \mynote{one one one one one one} \input zapf \mynote{one one one one one one} }
+% \stoptext
+
+%D The noterule can be a graphic and therefore calling this setup macro at every
+%D skipswitch is tricky (many many MP runs). Let's just reserve a few points, that
+%D probably match those of the stretch component. A bit messy:
+
+\permanent\protected\def\placenoterule
+ {\begingroup
+ \currentnoterulealign
+ \currentnoterulecommand
+ \par
+ \endgroup}
+
+\permanent\protected\def\normalnoterule
+ {\ifvmode
+ \dontleavehmode \blackrule
+ [\c!color=\noteparameter\c!rulecolor,
+ \c!width=.2\hsize,
+ \c!height=\noteparameter\c!rulethickness,
+ \c!depth=\zeropoint]%
+ \endgraf
+ \kern\strutdepth
+ \fi}
+
+%D The formatting depends on the width of the table, so we have to set \type {n} to
+%D zero.
+%D
+%D \starttyping
+%D \startbuffer
+%D \bTABLE
+%D \bTR \bTD one \footnote{\dorecurse{10}{abcd }} \eTD \bTD two \eTD \eTR
+%D \bTR \bTD three fout five six seven eight nine \eTD \bTD ten \eTD \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \startlocalfootnotes[n=0,location={text,none}]
+%D \placelegend[n=2]{\getbuffer}{\placelocalfootnotes}
+%D \stoplocalfootnotes
+%D \stoptyping
+
+%D \macros
+%D {footnote}
+%D
+%D A footnote can have a reference as optional argument and therefore its formal
+%D specification looks like:
+%D
+%D \showsetup{footnote}
+%D
+%D This command has one optional command: the reference. By saying \type{[-]} the
+%D number is omitted. The footnote command is not that sensitive to spacing, so it's
+%D quite legal to say:
+%D
+%D \startbuffer
+%D Users of \CONTEXT\ must keep both feet \footnote {Given they have two.} on the
+%D ground and not get confused \footnote {Or even crazy.} by all those obscure
+%D \footnote {But fortunately readable.} parameters.
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D When setting the \type{conversion} to \type{set 2} we get
+%D something like:
+%D
+%D \bgroup
+%D \startnarrower
+%D \setupfootnotes[conversion=set 1]
+%D \getbuffer
+%D \stopnarrower
+%D \egroup
+%D
+%D Typesetting footnotes is, at least for the moment, disabled when reshaping boxes.
+%D The additional macro \type {\footnotetext} and the associated \type {\note} macro
+%D were implemented at request of users on the mailing list and a suggestion by taco
+%D to split of the symbol placement. I decided to merge this functionality with the
+%D existing \type {\note} functionality.
+
+\newconditional\c_strc_notes_symbol \settrue\c_strc_notes_symbol % not used
+\newconditional\c_strc_notes_skip
+
+\permanent\protected\def\setnote [#tag]{\csname#tag\endcsname}
+\permanent\protected\def\setnotetext[#tag]{\global\settrue\c_strc_notes_skip\csname#tag\endcsname}
+
+\permanent\protected\def\handlenoteinsert#tag#id%
+ {\begingroup
+ \edef\currentnote{#tag}%
+ \strc_constructions_initialize{#tag}%
+ \strc_notes_synchronize
+ \the\everybeforenoteinsert
+ \insert\currentnoteinsertionnumber\bgroup
+ \the\everyinsidenoteinsert\relax
+ \usesetupsparameter\noteparameter % experimental
+ \useinterlinespaceparameter\noteparameter
+ \doifelse{\noteparameter\c!paragraph}\v!yes
+ {\nointerlineskip
+ \startvboxtohboxseparator
+ \noteparameter\c!inbetween
+ \stopvboxtohboxseparator
+ \startvboxtohbox
+ \handlenoteitself{#tag}{#id}%
+ \stopvboxtohbox}
+ {\handlenoteitself{#tag}{#id}}%
+ \egroup
+ \the\everyafternoteinsert
+ \endgroup}
+
+\permanent\protected\def\betweennoteitself#tag% used ?
+ {\edef\currentnote{#tag}%
+ \doif{\noteparameter\c!paragraph}\v!yes{\noteparameter\c!inbetween}}
+
+\permanent\protected\def\handlenoteitself#tag#id%
+ {\edef\currentnotenumber{#id}%
+ \edef\currentnote{#tag}%
+ \strc_constructions_initialize{#tag}%
+ \strc_notes_synchronize
+ \edef\currentconstructionlistentry{\clf_notelistindex{\currentnote}#id}% index in list cache
+ % as we can have collected notes (e.g. in tables) we need to recover
+ % \currentdescriptionattribute and \currentdescriptionsynchronize
+ %
+ \reinstatecachedconstructionnumberentry\currentconstructionlistentry % we could store the number in the entry (e.g. needed when local notes in table)
+ %
+ \dontcomplain
+ %begingroup
+ \strc_notes_interaction_check_display
+ \strc_notes_set_reference_attribute_text
+ \strc_constructions_stored_start
+ \begstrut
+ \strc_references_flush_destination_nodes
+ \strc_notes_set_destination_attribute_text
+ \strc_notes_inject_text\relax
+ \ifvmode\obeydepth\else\endstrut\fi % \obeydepth is new per 2015-01-10
+ \strc_constructions_stored_stop
+ %endgroup
+ }
+
+\protected\def\strc_notes_inject_text % hm main?
+ {\clf_savedlisttitle{\currentconstructionmain}\currentconstructionlistentry\relax}
+
+\newsignal\d_strc_notes_signal
+
+\newconditional\processingnote
+\newconditional\postponednote
+
+\newtoks\everybeforenoteinsert
+\newtoks\everyinsidenoteinsert
+\newtoks\everyafternoteinsert
+
+\permanent\protected\def\doifelseinnote
+ {\ifconditional\processingnote
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\appendtoks
+ \enforced\let\flushnotes \relax
+ \enforced\let\postponenotes\relax
+ \forgetall
+ \resetallattributes % new, we don't want color bleed into notes
+ \inheritmaintextcolor % but we do want to obey the textcolor
+\to \everybeforenoteinsert
+
+\def\strc_notes_set_penalties
+ {% stored in insert node
+ \floatingpenalty \currentnotepenalty
+ % used when typesetting
+ \interlinepenalty\plushundred % plain value
+ % used when we need to split in columns
+ \ifnum\noteparameter\c!n>\plusone
+ \penalty\zerocount % otherwise no split in columns, maybe just always (tex just adds it to accumulated)
+ \fi}
+
+\appendtoks
+ \strc_notes_set_penalties
+ \forgetall % again
+ \strc_notes_set_bodyfont
+ \redoconvertfont % to undo \undo calls in in headings etc
+ \splittopskip\strutht % not actually needed here
+ \splitmaxdepth\strutdp % not actually needed here
+ %
+ % not:
+ %
+ % \leftmargindistance \noteparameter\c!margindistance
+ % \rightmargindistance\leftmargindistance
+ % \ifnum\noteparameter\c!n=\zerocount % no ifcase new 31-07-99 ; always ?
+ % \doifnotinset{\noteparameter\c!width}{\v!fit,\v!broad}\setnotehsize % ?
+ % \fi
+ %
+ \pickupattributes
+\to \everyinsidenoteinsert
+
+%D Nasty, the might be more:
+
+\appendtoks \strc_itemgroups_push \to \everybeforenoteinsert
+\appendtoks \strc_itemgroups_pop \to \everyafternoteinsert
+
+% maybe but better use [scope=local] here
+%
+% \appendtoks
+% \setfalse\inhibitmargindata
+% \to \everyinsidenoteinsert
+
+\setupnotes
+ [\c!width=\v!auto]
+
+\permanent\protected\def\setnotehsize
+ {\ifinsidemulticolumns
+ \setnoteparameter\c!width{\makeupwidth}%
+ \else
+ \edef\p_width{\noteparameter\c!width}%
+ \ifx\p_width\v!auto
+ % \ifinsidecolumns
+ \setnoteparameter\c!width{\ifdim\hsize<\makeupwidth\hsize\else\makeupwidth\fi}%
+ % \else
+ % \setnoteparameter\c!width{\makeupwidth}%
+ % \fi
+ \orelse\ifempty\p_width
+ \setnoteparameter\c!width{\hsize}%
+ \fi
+ \fi
+ \hsize\noteparameter\c!width\relax}
+
+\appendtoks
+ % only needed when columns (could be three \set...)
+ \setnotehsize
+ \setsimplecolumnshsize[\c!distance=\noteparameter\c!columndistance,\c!n=\noteparameter\c!n,\c!width=\noteparameter\c!width]%
+\to \everyinsidenoteinsert
+
+%D Normally footnotes are saved as inserts that are called upon as soon as the
+%D pagebody is constructed. The footnote insertion routine looks just like the
+%D \PLAIN\ \TEX\ one, except that we check for the end note state.
+
+% testcase for split bottom alignment see (a) below
+%
+% \dorecurse{6}{\input tufte\footnote{\input ward \input tufte \relax}}
+
+\newconditional\c_strc_notes_first_placed
+
+\permanent\protected\def\placenoteinserts
+ {\setfalse\c_strc_notes_first_placed
+ \strc_notes_process\strc_notes_place_inserts}
+
+\def\strc_notes_place_inserts
+ {\strc_notes_set_delayed % \strc_notes_synchronize % we need to know if it's delayed
+ \ifconditional\c_strc_notes_delayed \else
+ \ifdim\ht\currentnoteinsertionnumber>\zeropoint % or a faster delayed test
+ \strc_notes_place_inserts_indeed
+ \settrue\c_strc_notes_first_placed
+ \fi
+ \fi}
+
+\def\strc_notes_place_inserts_indeed
+ {\relax
+ \ifdim\ht\currentnoteinsertionnumber>\zeropoint
+ \endgraf
+ \ifvmode
+ \whitespace
+ \ifconditional\c_strc_notes_first_placed
+ \edef\p_spaceinbetween{\noteparameter\c!spaceinbetween}%
+ \ifempty\p_spaceinbetween\else
+ \blank[\p_spaceinbetween]%
+ \fi
+ \else
+ \edef\p_spacebefore{\noteparameter\c!spacebefore}%
+ \ifempty\p_spacebefore\else
+ \blank[\p_spacebefore]%
+ \fi
+ \fi
+ \noteparameter\c!before
+ \fi
+ \placenoterule
+ \bgroup
+ \strc_notes_set_bodyfont
+ \setbox\scratchbox\hbox
+ {\strc_notes_flush_inserts}%
+ \page_postprocessors_linenumbers_deepbox\scratchbox
+ \setbox\scratchbox\hbox
+ {\setupcurrentnote
+ [\c!location=,
+ \c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!strut=\v!no,
+ \c!offset=\v!overlay]%
+ \inheritednoteframed
+ {\ifzeropt\dp\scratchbox % this hack is needed because \vadjust
+ \hpack{\lower\strutdp\box\scratchbox}% % in margin number placement
+ \else % hides the (always) present depth
+ \box\scratchbox
+ \fi}}%
+ \setbox\scratchbox\hpack{\lower\strutdepth\box\scratchbox}%
+ \dp\scratchbox\strutdepth % so we know that it has the note bodyfont depth
+ \ifvmode
+ \nointerlineskip % else sometimes empty line
+ \fi
+ \box\scratchbox
+ \egroup
+ \endgraf
+ \ifvmode
+ \noteparameter\c!after
+ \fi
+ \fi}
+
+\def\strc_notes_flush_inserts
+ {\ifcase\noteparameter\c!n\relax
+ % should not happen
+ \or
+ \strc_notes_flush_inserts_normal
+ \else
+ \strc_notes_flush_inserts_columns
+ \fi}
+
+\def\strc_notes_flush_inserts_normal
+ {\strc_notes_flush_global
+ \obeydepth} % (a) added , since split footnotes will not align properly
+
+\def\strc_notes_flush_inserts_columns
+ {\begingroup
+ \setnotehsize % probably still not ok for columns
+ \startsimplecolumns[\c!distance=\noteparameter\c!columndistance,\c!n=\noteparameter\c!n,\c!width=\noteparameter\c!width]%
+ \strc_notes_flush_global
+ \stopsimplecolumns
+ \endgroup}
+
+% idea: tag with attr and then just flush them again
+
+\def\strc_notes_flush_global
+ {\begingroup
+ \useinterlinespaceparameter\noteparameter
+ \doifelse{\noteparameter\c!paragraph}\v!yes
+ {\leftorrightvbox % cf mail from ws to list
+ {\starthboxestohbox
+ \iftrialtypesetting\unvcopy\else\unvbox\fi\currentnoteinsertionnumber
+ \stophboxestohbox}}
+ {\iftrialtypesetting\unvcopied\else\unvboxed\fi\currentnoteinsertionnumber}%
+ \endgroup}
+
+%D Supporting end notes is surprisingly easy. Even better, we can combine this
+%D feature with solving the common \TEX\ problem of disappearing inserts when
+%D they're called for in deeply nested boxes. The general case looks like:
+%D
+%D \starttyping
+%D \postponenotes
+%D \.box{whatever we want with footnotes}
+%D \flushnotes
+%D \stoptyping
+%D
+%D This alternative can be used in headings, captions, tables etc. The latter one
+%D sometimes calls for notes local to the table, which can be realized by saying
+%D
+%D \starttyping
+%D \setlocalfootnotes
+%D some kind of table with local footnotes
+%D \placelocalfootnotes
+%D \stoptyping
+%D
+%D Postponing is accomplished by simply redefining the (local) insert operation. A
+%D not too robust method uses the \type{\insert} primitive when possible. This
+%D method fails in situations where it's not entirely clear in what mode \TEX\ is.
+%D Therefore the auto method can is to be overruled when needed.
+
+\newconditional\postponingnotes
+
+% we need a proper state: normal, postponing, flushing
+
+\permanent\protected\def\postponenotes % will be locally overloaded
+ {\ifcase\insertionmigrationmode
+ \ifconditional\postponingnotes\else
+ \global\settrue\postponingnotes
+ \enforced\glet\flushnotes\doflushnotes
+ \clf_postponenotes
+ \fi
+ \fi}
+
+\aliased\let\flushnotes\relax
+
+% also \ifcase\insertionmigrationmode here, needs testing:
+
+\permanent\protected\def\startpostponingnotes % experimental, page-mix
+ {\ifconditional\postponingnotes\else
+ \global\settrue\postponingnotes
+ %\glet\flushnotes\doflushnotes
+ \clf_postponenotes
+ \fi}
+
+\permanent\protected\def\stoppostponingnotes % experimental, page-mix
+ {\doflushnotes}
+
+\permanent\protected\def\doflushnotes
+ {\ifconditional\postponingnotes
+ \begingroup
+ \enforced\let\flushnotes \relax
+ \enforced\let\postponenotes\relax
+ \ifconditional\postponednote
+ \ifhmode
+ % needed for tagging ... otherwise we get some weird node free error
+ \signalcharacter
+ \fi
+ \fi
+ \clf_flushpostponednotes% this also resets the states !
+ \global\setfalse\postponednote
+ \global\setfalse\postponingnotes
+ \enforced\glet\flushnotes\relax
+ \endgroup
+ \fi}
+
+%D \macros
+%D {startlocalfootnotes,placelocalfootnotes}
+%D
+%D The next two macros can be used in for instance tables, as we'll demonstrate
+%D later on.
+%D
+%D \showsetup{startlocalfootnotes}
+%D \showsetup{placelocalfootnotes}
+
+% todo: compatibility mode: when first arg is assignment or missing, then all
+
+\newtoks\everyplacelocalnotes
+
+\appendtoks
+ \enforced\let\flushnotes \relax
+ \enforced\let\postponenotes\relax
+\to \everyplacelocalnotes
+
+\newconditional\inlocalnotes
+
+\mutable\let\localnoteslist\empty
+
+\permanent\tolerant\protected\def\startlocalnotes[#list]% grouping ? (we used to have a second argument ... settings)
+ {\settrue\inlocalnotes
+ \def\localnoteslist{#list}%
+ \processcommacommand[\localnoteslist]\strc_notes_local_start_step}
+
+\permanent\protected\def\stoplocalnotes
+ {\processcommacommand[\localnoteslist]\strc_notes_local_stop_step
+ \setfalse\inlocalnotes}
+
+\let\p_strc_notes_continue\empty
+
+\def\strc_notes_local_start_step#tag%
+ {\p_strc_notes_continue{\noteparameter\c!continue}%
+ \ifx\p_strc_notes_continue\v!yes \else
+ \strc_counters_save{#tag}%
+ \strc_counters_reset{#tag}%
+ \fi
+ \clf_savenote{#tag}{store}}
+
+\def\strc_notes_local_stop_step#tag%
+ {\p_strc_notes_continue{\noteparameter\c!continue}%
+ \ifx\p_strc_notes_continue\v!yes \else
+ \strc_counters_restore{#tag}%
+ \fi
+ \clf_restorenote{#tag}}
+
+\permanent\tolerant\protected\def\placelocalnotes[#tag]#spacer[#settings]%
+ {\doif{\clf_getnotestate{#tag}}{store}{\strc_notes_local_place_indeed{#settings}{#tag}}}
+
+\def\strc_notes_local_place_indeed#settings#tag%
+ {\begingroup
+ \edef\currentnote{#tag}% is already set?
+ \the\everyplacelocalnotes
+ % beware, we cannot trust setting \currentnote here
+ \setupcurrentnote[#settings]% later we set height etc for framed
+ \strc_notes_place_local_alternative
+ \strc_notes_set_properties % restore globals (if needed)
+ \endgroup
+ }% TODO: just restore properties \the\everychecknote} % we need to restore the old state
+
+%D These commands can be used like:
+%D
+%D \startbuffer
+%D \startlocalnotes[footnote]
+%D \placetable
+%D {Some Table}
+%D \placeontopofeachother
+%D {\starttable[|l|r|]
+%D \HL
+%D \VL Nota\footnote{Bene} \VL Bene\footnote{Nota} \VL\SR
+%D \VL Bene\footnote{Nota} \VL Nota\footnote{Bene} \VL\SR
+%D \HL
+%D \stoptable}
+%D {\setupnotation[footnote][alternative={serried},distance=.5em,after=\hskip1em]%
+%D \placelocalnotes[footnote]}
+%D \stoplocalnotes
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Because this table placement macro expect box content, and thanks to the grouping
+%D of the local footnotes, we don't need additional braces.
+%D
+%D \getbuffer
+
+%D \macros
+%D {placefootnotes}
+%D
+%D We still have no decent command for placing footnotes somewhere else than at the
+%D bottom of the page (for which no user action is needed). Footnotes (endnotes) can
+%D be placed by using
+%D
+%D \showsetup{placefootnotes}
+
+\permanent\protected\def\placebottomnotes
+ {\strc_notes_process\strc_notes_place_inserts}
+
+\permanent\tolerant\protected\def\placenotes[#list]#spacer[#settings]%
+ {\processcommalist[#list]{\strc_notes_place_indeed{#settings}}}
+
+\def\strc_notes_place_indeed#settings#tag% settings note
+ {\edef\currentnote{#tag}% grouping ?
+ \doifelse{\clf_getnotestate{#tag}}{store}
+ \strc_notes_local_place_indeed
+ \strc_notes_global_place_indeed
+ {#settings}{#tag}}
+
+\def\strc_notes_global_place_indeed#settings#tag%
+ {\begingroup
+ \setupnote[#tag][#settings]%
+ \strc_notes_place_inserts
+ \endgroup
+ \the\everysetupnote} % to be checked .. synchronize
+
+%D Placement
+
+\installcorenamespace{notealternative}
+
+\permanent\protected\def\installnotealternative#alternative#command%
+ {\setvalue{\??notealternative#alternative}{#command}}
+
+\permanent\protected\def\doifnotescollected#tag%
+ {\clf_doifnotecontent{#tag}}
+
+\def\strc_notes_place_local_alternative % will be a setup (wrapper)
+ {\doifnotescollected\currentnote
+ {\endgraf
+ \ifvmode
+ \whitespace
+ \noteparameter\c!before
+ \fi
+ \begingroup
+ \strc_notes_set_bodyfont
+ \csname\??notealternative\noteparameter\c!alternative\endcsname
+ \endgroup
+ \ifvmode
+ \noteparameter\c!after
+ \fi}}
+
+%D A stupid alternative is also provided:
+%D
+%D \starttyping
+%D \setupfootnotes[location=text,alternative=none]
+%D \stoptyping
+
+% setups ?
+
+\permanent\def\flushlocalnotes#tag{\clf_flushnotes{#tag}{store}{\noteparameter\c!criterium}}
+
+\installnotealternative \v!none
+ {\flushlocalnotes\currentnote}
+
+\installnotealternative \empty
+ {\flushlocalnotes\currentnote}
+
+\installnotealternative \v!grid % test if n > 0
+ {\begingroup
+ \setupcurrentnote[\c!location=]%
+ \snaptogrid\hbox
+ {\inheritednoteframed
+ {\flushlocalnotes\currentnote}}%
+ \endgroup}
+
+\installnotealternative \v!fixed % test if n > 0
+ {\begingroup
+ \setupcurrentnote[\c!location=]%
+ \inheritednoteframed
+ {\flushlocalnotes\currentnote}%
+ \endgroup}
+
+\installnotealternative \v!columns % redundant
+ {\begingroup
+ \setupcurrentnote[\c!location=]%
+ \inheritednoteframed
+ {\doifelsedimension{\noteparameter\c!width}\donothing{\setexpandednoteparameter\c!width{\the\hsize}}%
+ \startsimplecolumns[\c!distance=\noteparameter\c!columndistance,\c!n=\noteparameter\c!n,\c!width=\noteparameter\c!width]%
+ \flushlocalnotes\currentnote
+ \stopsimplecolumns}%
+ \endgroup}
+
+% 0:page 1:firstcolumn 2:lastcolumn
+
+\newconstant\c_strc_notes_page_location
+
+\protected\def\strc_notes_check_locations
+ {\edef\p_strc_notes_location{\rootnoteparameter\c!location}%
+ \c_strc_notes_page_location
+ \ifx\p_strc_notes_location\v!firstcolumn\plusone \else
+ \ifx\p_strc_notes_location\v!lastcolumn \plustwo \else
+ \zerocount\fi\fi}
+
+\appendtoks
+ \strc_notes_check_locations
+\to \everysynchronizenote
+
+% still semi public (but will change)
+
+\newif\ifnotespresent
+
+\permanent\protected\def\checknotepresence
+ {\notespresentfalse
+ \strc_notes_process\strc_notes_check_presence}
+
+\def\strc_notes_check_presence
+ {\ifdim\ht\currentnoteinsertionnumber>\zeropoint
+ \notespresenttrue
+ \fi}
+
+%D \macros
+%D {fakenotes}
+
+ % used in page-mul
+
+ % \ifdefined\currentnofcolumns\else \def\currentnofcolumns{\nofcolumns} \fi
+
+ \permanent\protected\def\fakenotes
+ {\ifhmode\endgraf\fi\ifvmode
+ \calculatetotalclevernoteheight
+ \ifdim\totalnoteheight>\zeropoint \kern\totalnoteheight \fi
+ \fi}
+
+ \permanent\protected\def\fakepagenotes
+ {\ifhmode\endgraf\fi\ifvmode
+ \calculatetotalpagenoteheight
+ \ifdim\totalnoteheight>\zeropoint \kern\totalnoteheight \fi
+ \fi}
+
+ % used in page-not but not yet ok
+
+ \newdimen\totalnoteheight
+
+ \permanent\protected\def\additionaltotalnoteheight#insert% temp hacks anyway
+ {\dimexpr
+ \ifdim\ht#insert>\zeropoint
+ \ifcase\count#insert %
+ \zeropoint
+ \else % todo: divide by count
+ \ht#insert+\skip#insert% hm, no stretch but a dimen anyway
+ \fi
+ \else
+ \zeropoint
+ \fi
+ \relax}
+
+ \permanent\def\docalculatetotalnoteheight
+ {\ifcase\c_strc_notes_page_location % tricky here ! ! ! to be sorted out ! ! !
+ \advance\totalnoteheight\normalexpanded{\additionaltotalnoteheight\currentnoteinsertionnumber}%
+ \fi}
+
+ \permanent\def\docalculatetotalclevernoteheight
+ {\ifcase\c_strc_notes_page_location \else % tricky here ! ! ! to be sorted out ! ! !
+ \advance\totalnoteheight\normalexpanded{\additionaltotalnoteheight\currentnoteinsertionnumber}%
+ \fi}
+
+ \permanent\def\docalculatetotalpagenoteheight
+ {\advance\totalnoteheight\normalexpanded{\additionaltotalnoteheight\currentnoteinsertionnumber}}
+
+ \permanent\def\calculatetotalnoteheight {\totalnoteheight\zeropoint\strc_notes_process\docalculatetotalnoteheight}
+ \permanent\def\calculatetotalclevernoteheight{\totalnoteheight\zeropoint\strc_notes_process\docalculatetotalclevernoteheight}
+ \permanent\def\calculatetotalpagenoteheight {\totalnoteheight\zeropoint\strc_notes_process\docalculatetotalpagenoteheight}
+
+%D Now how can this mechanism be hooked into \CONTEXT\ without explictly postponing
+%D footnotes? The solution turned out to be rather simple:
+%D
+%D \starttyping
+%D \everypar {...\flushnotes...}
+%D \neverypar {...\postponenotes}
+%D \stoptyping
+%D
+%D We can use \type {\neverypar} because in most commands sensitive to footnote
+%D gobbling we disable \type {\everypar} in favor for \type {\neverypar}. In fact,
+%D this footnote implementation is the first to use this scheme.
+
+%D This is a nasty and new secondary footnote flusher. It can be hooked into \type
+%D {\everypar} like:
+%D
+%D \starttyping
+%D \appendtoks \synchronizenotes \to \everypar
+%D \stoptyping
+
+% \let\synchronizenotes\relax
+
+%D When typesetting footnotes, we have to return to the footnote specific bodyfont
+%D size, which is in most cases derived from the global document bodyfont size. In
+%D the previous macros we already used a footnote specific font setting macro.
+
+\def\strc_notes_set_bodyfont
+ {\let\strc_notes_set_bodyfont\relax
+ \restoreglobalbodyfont
+ \usebodyfontparameter\noteparameter
+ \usealignparameter\noteparameter}
+
+%D The footnote mechanism defaults to a traditional one column way of showing them.
+%D By default we precede them by a small line.
+
+% end notes in the margin:
+%
+% \setuptexttexts
+% [margin]
+% [] [{\directsetup{notabene}}]
+%
+% \startsetups notabene
+% \vbox to \textheight \bgroup
+% \setupalign[tolerant]
+% \topskipcorrection
+% \placenotes[endnote][before=,after=]
+% \vfilll
+% \egroup
+% \stopsetups
+
+\definenote [\v!footnote]
+\definenote [\v!endnote ] [\c!location=\v!none] % else no break
+
+%D Compatibility macros:
+
+\permanent\protected\def\setupfootnotedefinition{\setupnotation [\v!footnote]}
+\permanent\protected\def\setupfootnotes {\setupnote [\v!footnote]}
+\permanent \def\footnotetext {\setnotetext [\v!footnote]}
+\permanent\protected\def\placefootnotes {\strc_notes_place_footnotes [\v!footnote]}
+\permanent\protected\def\placelocalfootnotes {\strc_notes_place_local_footnotes[\v!footnote]}
+\permanent\protected\def\startlocalfootnotes {\startlocalnotes [\v!footnote]} % alleen footnote
+\permanent\protected\def\stoplocalfootnotes {\stoplocalnotes }
+
+\tolerant\def\strc_notes_place_footnotes[#list]#spacer[#settings]%
+ {\ifarguments\or
+ \placenotes[#list][\c!height=\textheight]%
+ \or
+ \placenotes[#list][#settings,\c!height=\textheight]%
+ \fi}
+
+\tolerant\def\strc_notes_place_local_footnotes[#list]#spacer[#settings]%
+ {\ifarguments\or
+ \placelocalnotes[#list][\c!height=\textheight]%
+ \or
+ \placelocalnotes[#list][#settings,\c!height=\textheight]%
+ \fi}
+
+%D Goodies:
+%D
+%D \starttyping
+%D \dorecurse {100} {
+%D test \footnote{\doifnoteonsamepageelse[footnote]{ibidem}{aaa}}
+%D }
+%D \stoptyping
+
+\permanent\def\doifelsenoteonsamepage#tag{\clf_doifnoteonsamepageasprevious{#tag}}
+
+\aliased\let\doifnoteonsamepageelse\doifelsenoteonsamepage
+
+%D New trickery:
+
+%D \macros
+%D {note}
+%D
+%D Refering to a note is accomplished by the rather short command:
+%D
+%D \showsetup{note}
+%D
+%D This command is implemented rather straightforward as:
+
+\installcorenamespace{notesymbol}
+
+\mutable\let\lastnotesymbol\empty % todo: per class
+
+\permanent\tolerant\protected\def\notesymbol[#tag]#spacer[#reference]%
+ {\ifnotesenabled
+ \dontleavehmode
+ \begingroup
+ \edef\currentnote{#tag}%
+ \usenotestyleandcolor\c!textstyle\c!textcolor
+ \iftok{#reference}\emptytoks
+ \noteparameter\c!textcommand\lastnotesymbol % check if command double
+ \else
+ \unskip
+ \noteparameter\c!textcommand{\in[#reference]}% command here?
+ \fi
+ \endgroup
+ \fi}
+
+\permanent\tolerant\protected\def\note[#tag]#spacer[#reference]%
+ {\iftok{#reference}\emptytoks
+ \notesymbol[\v!footnote][#tag]%
+ \else
+ \notesymbol[#tag][#reference]%
+ \fi}
+
+% will be redone if needed
+%
+% \def\ownnotesymbol#1% #1 gets number passed
+% {\executeifdefined{\??notesymbol\currentnote}\empty}
+%
+% \protected\def\setnotesymbol[#1]#2#3%
+% {\prewordbreak % prevent lookback
+% \setgvalue{\??notesymbol#1}{#3}
+% \strc_notes_inject_symbol}
+%
+% \protected\def\ownnote[#1]#2#3#4%
+% {\setnotesymbol[#1]{#2}{#3}%
+% \setnotetext [#1]{#4}}
+%
+% \defineconversion
+% [ownnote]
+% [\ownnotesymbol]
+
+% tricky:
+%
+% \enabletrackers[nodes.areas]
+% \enabletrackers[nodes.references]
+% \enabletrackers[nodes.destinations]
+%
+% \setupnotes[interaction=all,rule=no]
+% \setupinteraction[state=start,focus=standard]
+%
+% \starttext
+% \goto{\input tufte\relax}[page(2)] \par
+% \ruledhbox{\gotobox{\vtop{\input tufte\relax}}[page(2)]} \par
+% \ruledhbox{\gotobox{\vbox{\input tufte\relax}}[page(2)]} \par
+% % \completecontent
+% % \chapter{Chapter}
+% % \dorecurse{5}{\input knuth}
+% a\footnote{\input tufte\par\input ward\relax}
+% \stoptext
+
+%D Bonus:
+
+\appendtoks
+ \setsystemmode\currentnote
+\to \everysynchronizenote
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-num.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-num.mkxl
new file mode 100644
index 00000000000..02674bce29c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-num.mkxl
@@ -0,0 +1,691 @@
+%D \module
+%D [ file=strc-num,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Basic Numbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Basic Numbering}
+
+\registerctxluafile{strc-num}{}
+
+\unprotect
+
+\startcontextdefinitioncode
+
+% work in progress
+% to be checked: can we use the command handler code here?
+% all settings will move to lua
+
+\installcorenamespace{counter}
+
+\installcommandhandler \??counter {counter} \??counter
+
+\aliased\let\setupcounters \setupcounter
+\aliased\let\setupstructurecounting\setupcounter % will disappear
+
+\setupcounter
+ [\c!way=\v!by\v!chapter,
+% \c!blockway=,
+% \c!prefixstarter=,
+% \c!prefixstopper=,
+ \c!prefixconnector=.,
+ \c!prefixsegments=\autostructureprefixsegments\rootcounterparameter,
+ \c!start=\zerocount,
+ \c!prefix=\v!yes,
+ \c!state=\v!start]
+
+\permanent\def\autostructureprefixsegments#1% todo: \c!prefixsegments=\v!auto
+ {2:\thenamedheadlevel{\clf_way{#1\c!way}}}
+
+\appendtoks
+ \resetcounterparameter\s!counter
+\to \everypresetcounter
+
+\appendtoks
+ \ifempty\currentcounterparent
+ \edef\p_start{\counterparameter\c!start}%
+ \clf_definecounter
+ name {\currentcounter}%
+ start \ifempty\p_start\zerocount\else\numexpr\p_start\relax\fi
+ counter {\counterparameter\s!counter}%
+ method {\counterparameter\c!method}%
+ \relax
+ \letcounterparameter\s!name\currentcounter
+ \else
+ % \letcounterparameter\s!name\currentcounterparent % we need a chained clone
+ \setexpandedcounterparameter\s!name{\namedcounterparameter\currentcounterparent\s!name}%
+ \fi
+ \strc_counters_check_setup
+\to \everydefinecounter
+
+\appendtoks
+ \ifempty\currentcounter \else
+ \edef\p_number{\counterparameter\c!number}%
+ \ifempty\p_number \else
+ \clf_setcounter{\counterparameter\s!name}\numexpr\p_number\relax
+ \letcounterparameter\c!number\empty
+ \fi
+ \edef\p_start{\counterparameter\c!start}%
+ \setexpandedcounterparameter\c!start{\ifempty\p_start0\else\number\p_start\fi}%
+ \strc_counters_check_setup
+ \fi
+\to \everysetupcounter
+
+% % % %
+
+\permanent\def\strc_counters_way #1{\clf_way{\namedcounterparameter{#1}\c!way}}
+\permanent\def\thenamedcounterlevel#1{\xthenamedheadlevel{\clf_way{\namedcounterparameter{#1}\c!way}}}
+
+\mutable\let\currentcounterlevel\!!zerocount % yes or no public ?
+
+\protected\def\strc_counters_check_setup
+ {\edef\p_name{\directcounterparameter\s!name}%
+ \ifx\currentcounter\p_name
+ \edef\currentcounterlevel{\thenamedcounterlevel\currentcounter}%
+ \edef\p_start{\counterparameter\c!start}%
+ \clf_checkcountersetup
+ {\currentcounter}%
+ \numexpr\currentcounterlevel\relax
+ \numexpr\ifempty\p_start\zerocount\else\p_start\fi\relax % bug in scanner
+ {\counterparameter\c!state}%
+ \fi}
+
+\permanent\protected\def\doifelsecounter #1{\clf_doifelsecounter{\namedcounterparameter{#1}\s!name}}
+\permanent\protected\def\doifcounter #1{\clf_doifcounter {\namedcounterparameter{#1}\s!name}}
+\permanent\protected\def\doifnotcounter #1{\clf_doifnotcounter {\namedcounterparameter{#1}\s!name}}
+
+\aliased\let\doifcounterelse\doifelsecounter
+
+\permanent\tolerant\protected\def\setcounter[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \clf_setcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax
+ \else
+ \clf_setsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\numexpr#3\relax
+ \fi}
+
+\permanent\tolerant\protected\def\setcounterown[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \clf_setowncounter {\namedcounterparameter{#1}\s!name}{#2}%
+ \else
+ \clf_setownsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax{#3}%
+ \fi}
+
+\permanent\tolerant\protected\def\restartcounter[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \clf_restartcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax
+ \else
+ \clf_restartsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\numexpr#3\relax
+ \fi}
+
+\permanent\tolerant\protected\def\resetcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_resetcounter {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_resetsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax
+ \fi}
+
+\permanent\tolerant\protected\def\incrementcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \strc_counters_increment_sub{#1}\plusone
+ \else
+ \strc_counters_increment_sub{#1}{#2}%
+ \fi}
+
+\permanent\tolerant\protected\def\decrementcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_decrementcounter {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_decrementsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax
+ \fi}
+
+\permanent\tolerant\protected\def\rawcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_countervalue {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_subcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or
+ \fi}
+
+\permanent\tolerant\protected\def\lastcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_lastcountervalue {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_lastsubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or
+ \fi}
+
+\permanent\tolerant\protected\def\firstcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_firstcountervalue {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_firstsubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or
+ \fi}
+
+\permanent\tolerant\protected\def\prevcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_previouscountervalue {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_previoussubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or
+ \fi}
+
+\permanent\tolerant\protected\def\nextcounter[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_nextcountervalue {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_nextsubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or
+ \fi}
+
+\permanent\tolerant\protected\def\countersubs[#1]#*[#2]%
+ {\ifarguments\or
+ \clf_subcountervalues {\namedcounterparameter{#1}\s!name}%
+ \else
+ \clf_subsubcountervalues{\namedcounterparameter{#1}\s!name}\numexpr#2\relax\or
+ \fi}
+
+\permanent\tolerant\protected\def\savecounter[#1]%
+ {\ifarguments\or
+ \clf_savecounter{\namedcounterparameter{#1}\s!name}%
+ \fi}
+
+\permanent\tolerant\protected\def\restorecounter[#1]%
+ {\ifarguments\or
+ \clf_restorecounter{\namedcounterparameter{#1}\s!name}%
+ \fi}
+
+\protected\def\strc_counters_set #1#2{\clf_setcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\protected\def\strc_counters_setown #1#2{\clf_setowncounter {\namedcounterparameter{#1}\s!name}{#2}}
+\protected\def\strc_counters_restart #1#2{\clf_restartcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\protected\def\strc_counters_reset #1{\clf_resetcounter {\namedcounterparameter{#1}\s!name}}
+\protected\def\strc_counters_decrement #1{\clf_decrementcounter {\namedcounterparameter{#1}\s!name}}
+
+ \def\strc_counters_raw #1{\clf_countervalue {\namedcounterparameter{#1}\s!name}}
+ \def\strc_counters_last #1{\clf_lastcountervalue {\namedcounterparameter{#1}\s!name}}
+ \def\strc_counters_first #1{\clf_firstcountervalue {\namedcounterparameter{#1}\s!name}}
+ \def\strc_counters_next #1{\clf_nextcountervalue {\namedcounterparameter{#1}\s!name}}
+ \def\strc_counters_prev #1{\clf_previouscountervalue {\namedcounterparameter{#1}\s!name}}
+ \def\strc_counters_subs #1{\clf_subcountervalues {\namedcounterparameter{#1}\s!name}}
+
+\protected\def\strc_counters_set_sub #1#2#3{\clf_setsubcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax\numexpr#3\relax}
+\protected\def\strc_counters_setown_sub #1#2#3{\clf_setownsubcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax{#3}}
+\protected\def\strc_counters_restart_sub #1#2#3{\clf_restartsubcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax\numexpr#3\relax}
+\protected\def\strc_counters_reset_sub #1#2{\clf_resetsubcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\protected\def\strc_counters_decrement_sub #1#2{\clf_decrementsubcounter {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+
+ \def\strc_counters_raw_sub #1#2{\clf_subcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax} % maybe raw
+ \def\strc_counters_last_sub #1#2{\clf_lastsubcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+ \def\strc_counters_first_sub #1#2{\clf_firstsubcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+ \def\strc_counters_next_sub #1#2{\clf_nextsubcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+ \def\strc_counters_prev_sub #1#2{\clf_previoussubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+ \def\strc_counters_subs_sub #1#2{\clf_subsubcountervalues {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+
+\protected\def\strc_counters_save #1{\clf_savecounter {\namedcounterparameter{#1}\s!name}}
+\protected\def\strc_counters_restore #1{\clf_restorecounter {\namedcounterparameter{#1}\s!name}}
+
+\protected\def\strc_counters_incremented #1{\clf_incrementedcounter {\namedcounterparameter{#1}\s!name}}
+\protected\def\strc_counters_decremented #1{\clf_decrementedcounter {\namedcounterparameter{#1}\s!name}}
+
+\permanent\protected\def\showcounter [#1]{\clf_showcounter {\namedcounterparameter{#1}\s!name}}
+\permanent\protected\def\incrementedcounter[#1]{\clf_incrementedcounter{\namedcounterparameter{#1}\s!name}} % no \dosingleargument
+\permanent\protected\def\decrementedcounter[#1]{\clf_decrementedcounter{\namedcounterparameter{#1}\s!name}} % no \dosingleargument
+
+% public variants ... beware, for old cases, from now on the value variants are the
+% ones that are expandable
+
+\permanent\def\rawcountervalue [#1]{\clf_countervalue {\namedcounterparameter{#1}\s!name}}
+\permanent\def\lastcountervalue [#1]{\clf_lastcountervalue {\namedcounterparameter{#1}\s!name}}
+\permanent\def\firstcountervalue[#1]{\clf_firstcountervalue {\namedcounterparameter{#1}\s!name}}
+\permanent\def\nextcountervalue [#1]{\clf_nextcountervalue {\namedcounterparameter{#1}\s!name}}
+\permanent\def\prevcountervalue [#1]{\clf_previouscountervalue{\namedcounterparameter{#1}\s!name}}
+
+\permanent\def\rawsubcountervalue [#1]#*[#2]{\clf_subcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\permanent\def\lastsubcountervalue [#1]#*[#2]{\clf_lastsubcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\permanent\def\firstsubcountervalue[#1]#*[#2]{\clf_firstsubcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\permanent\def\nextsubcountervalue [#1]#*[#2]{\clf_nextsubcountervalue {\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+\permanent\def\prevsubcountervalue [#1]#*[#2]{\clf_previoussubcountervalue{\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+
+% The bypage check needs a multipass reference and therefore we only check for it when we increment
+% and know that some content will be placed. We could also check for spreads.
+
+\protected\def\strc_counters_increment_sub#1#2%
+ {% this will be combined into one lua call
+ \edef\m_strc_counters_way{\strc_counters_way{#1}}%
+ \ifx\m_strc_counters_way\v!page
+ \checkpagechange{#1}%
+ \ifpagechanged
+ \strc_counters_reset{#1}%
+ \fi
+ \fi
+ \clf_incrementsubcounter{\namedcounterparameter{#1}\s!name}\numexpr#2\relax}
+
+\protected\def\strc_counters_increment#1%
+ {\strc_counters_increment_sub{#1}\plusone}
+
+% so far for the hack
+
+\permanent\tolerant\protected\def\convertedcounter[#1]#*[#2]%
+ {\begingroup
+ \edef\currentcounter{#1}%
+ \ifparameter#2\or\setupcurrentcounter[#2]\fi
+ \clf_prefixedconverted
+ {\counterparameter\s!name}
+ {
+ prefix {\counterparameter\c!prefix}
+ separatorset {\counterparameter\c!prefixseparatorset}
+ conversion {\counterparameter\c!prefixconversion}
+ conversionset {\counterparameter\c!prefixconversionset}
+ starter {\counterparameter\c!prefixstarter}
+ stopper {\counterparameter\c!prefixstopper}
+ set {\counterparameter\c!prefixset}
+ segments {\counterparameter\c!prefixsegments}
+ connector {\counterparameter\c!prefixconnector}
+ }
+ {
+ order {\counterparameter\c!numberorder}
+ separatorset {\counterparameter\c!numberseparatorset}
+ conversion {\counterparameter\c!numberconversion}
+ conversionset {\counterparameter\c!numberconversionset}
+ starter {\counterparameter\c!numberstarter}
+ stopper {\counterparameter\c!numberstopper}
+ segments {\counterparameter\c!numbersegments}
+ type {\counterparameter\c!type}
+ criterium {\counterparameter\c!criterium}
+ }
+ \relax
+ \endgroup}
+
+\permanent\def\directconvertedcounter#1#2% name, type
+ {\clf_prefixedconverted
+ {\namedcounterparameter{#1}\s!name}
+ {
+ prefix {\namedcounterparameter{#1}\c!prefix}
+ separatorset {\namedcounterparameter{#1}\c!prefixseparatorset}
+ conversion {\namedcounterparameter{#1}\c!prefixconversion}
+ conversionset {\namedcounterparameter{#1}\c!prefixconversionset}
+ % starter {\namedcounterparameter{#1}\c!prefixstarter}
+ % stopper {\namedcounterparameter{#1}\c!prefixstopper}
+ set {\namedcounterparameter{#1}\c!prefixset}
+ segments {\namedcounterparameter{#1}\c!prefixsegments}
+ connector {\namedcounterparameter{#1}\c!prefixconnector}
+ }
+ {
+ order {\namedcounterparameter{#1}\c!numberorder}
+ separatorset {\namedcounterparameter{#1}\c!numberseparatorset}
+ conversion {\namedcounterparameter{#1}\c!numberconversion}
+ conversionset {\namedcounterparameter{#1}\c!numberconversionset}
+ starter {\namedcounterparameter{#1}\c!numberstarter}
+ stopper {\namedcounterparameter{#1}\c!numberstopper}
+ segments {\namedcounterparameter{#1}\c!numbersegments}
+ type {#2}
+ }
+ \relax}
+
+\permanent\tolerant\protected\def\convertedsubcounter[#1]#*[#2]#*[#3]% #2 can be n or n:m
+ {\ifarguments\or
+ \convertedcounter[#1][]%
+ \else
+ \convertedcounter[#1][\c!numbersegments=#2,#3]%
+ \fi}
+
+\permanent\protected\def\doifdefinedcounter {\doifcommandhandler \??counter}
+\permanent\protected\def\doifundefinedcounter {\doifnotcommandhandler \??counter}
+\permanent\protected\def\doifelsedefinedcounter{\doifelsecommandhandler\??counter}
+
+\aliased\let\doifdefinedcounterelse\doifelsedefinedcounter
+
+%D What follows is a compatibility layer. Do we need this?
+
+\aliased\let \numberparameter \namedcounterparameter % {name}\c!key
+
+\aliased\let \definenumber \definecounter % [name]
+\aliased\let \setupnumber \setupcounter % [name][setups]
+
+\aliased\let \setnumber \setcounter % [name]{value}
+\aliased\let \resetnumber \resetcounter % [name]
+\aliased\let \savenumber \savecounter % [name]
+\aliased\let \restorenumber \restorecounter % [name]
+\aliased\let \incrementnumber \incrementcounter % [name]
+\aliased\let \decrementnumber \decrementcounter % [name]
+\aliased\let \rawnumber \rawcounter % [name]
+\aliased\let \getnumber \convertedcounter % [name]
+\aliased\let \convertednumber \convertedcounter % [name]
+
+\aliased\let \doifdefinednumber \doifdefinedcounter % {number}{true}
+\aliased\let \doifelsedefinednumber \doifelsedefinedcounter % {number}{true}{false}
+\aliased\let \doifdefinednumberelse \doifelsedefinedcounter % {number}{true}{false}
+
+\aliased\let \setupnumbering \setupcounter
+
+%D Helpers:
+
+% call:
+%
+% \strc_counters_register_component
+% \currentfloat
+% \getfloatparameters \floatparameter \detokenizedfloatparameter
+% \hascaption \hastitle \hasnumber
+% [settings][userdata]
+%
+% sets:
+%
+% \m_strc_counters_last_registered_index
+% \m_strc_counters_last_registered_attribute
+% \m_strc_counters_last_registered_synchronize
+
+% currentstructurecomponent => \strc_current_ or just \m_strc_
+
+\mutable\let\currentname \empty
+\mutable\let\currentcounter\empty
+
+\protected\def\strc_counters_register_component#1#2#3#4#5#6#7[#8][#9]% maybe also nolist
+ {\begingroup
+ %
+ #2[\s!haslevel=1,#8]%
+ \edef\p_haslevel {#3\s!haslevel}%
+ \edef\p_hascaption{#3\s!hascaption}%
+ \edef\p_hastitle {#3\s!hastitle}%
+ \edef\p_hasnumber {#3\s!hasnumber}%
+ %
+ \edef\p_prefixsegments{#3\c!prefixsegments}%
+ \ifx\p_prefixsegments\v!auto
+ \edef\p_prefixsegments{\autostructureprefixsegments#3}%
+ \fi
+ %
+ \edef\currentname{#3\s!name}%
+ \ifempty\currentname
+ \edef\currentname{#1}%
+ \fi
+ \edef\currentcounter{#3\s!counter}%
+ \ifempty\currentcounter
+ \let\currentcounter\currentname
+ \fi
+ %
+ \ifx\p_hascaption\v!yes
+ \strc_counters_register_component_list{#1}{#3}{#4}{#9}%
+ \orelse\ifempty\currentstructurecomponentreference
+ \strc_counters_register_component_none
+ \else
+ \strc_counters_register_component_page{#3}%
+ \fi
+ \endgroup}
+
+\def\strc_counters_register_component_none
+ {\glet\m_strc_counters_last_registered_index \relax
+ \glet\m_strc_counters_last_registered_attribute \attributeunsetvalue
+ \glet\m_strc_counters_last_registered_synchronize\relax}
+
+\def\strc_counters_register_component_check_prefix
+ {\ifempty\currentstructurecomponentreferenceprefix
+ \let\currentstructurecomponentreferenceprefix\currentstructurereferenceprefix
+ \fi
+ \ifempty\currentstructurecomponentreferenceprefix
+ \let\currentstructurecomponentreferenceprefix\referenceprefix
+ \fi
+ \ifdefined\currentstructurecomponentreferenceprefix\else
+ \let\currentstructurecomponentreferenceprefix\empty
+ \fi}
+
+\def\strc_counters_register_component_page#1%
+ {\xdef\currentstructurecomponentreference {#1\c!reference}%
+ \xdef\currentstructurecomponentreferenceprefix{#1\c!referenceprefix}%
+ \strc_counters_register_component_check_prefix
+ \setnextinternalreference
+ \clf_setdestinationattribute
+ {%
+ references {%
+ internal \locationcount
+ % block {\currentsectionblock}% move to lua
+ view {\interactionparameter\c!focus}%
+ prefix {\currentstructurecomponentreferenceprefix}%
+ reference {\currentstructurecomponentreference}%
+ }%
+ metadata {%
+ kind {\s!page}%
+ }%
+ }%
+ \relax
+ \xdef\m_strc_counters_last_registered_attribute {\the\lastdestinationattribute}%
+ \glet\m_strc_counters_last_registered_index \relax
+ \glet\m_strc_counters_last_registered_synchronize\relax}
+
+\def\strc_counters_register_component_list#1#2#3#4%
+ {\xdef\currentstructurecomponentname {#2\s!name}%
+ \xdef\currentstructurecomponentlevel {#2\c!level}%
+ \edef\currentstructurecomponentexpansion {#2\c!expansion}%
+ \xdef\currentstructurecomponentxmlsetup {#2\c!xmlsetup}%
+ \xdef\currentstructurecomponentcatcodes {#2\s!catcodes}%
+ \xdef\currentstructurecomponentlabel {#2\c!label}%
+ \xdef\currentstructurecomponentreference {#2\c!reference}%
+ \xdef\currentstructurecomponentreferenceprefix{#2\c!referenceprefix}%
+ \strc_counters_register_component_check_prefix
+ \ifx\currentstructurecomponentexpansion\s!xml
+ \xmlstartraw
+ \xdef\currentstructurecomponenttitle {#2\c!title}%
+ \xdef\currentstructurecomponentbookmark{#2\c!bookmark}%
+ \xdef\currentstructurecomponentmarking {#2\c!marking}%
+ \xdef\currentstructurecomponentlist {#2\c!list}%
+ \xmlstopraw
+ \ifempty\currentstructurecomponentlist
+ \glet\currentstructurecomponentlist\currentstructurecomponenttitle
+ \fi
+ \glet\currentstructurecomponentcoding\s!xml
+ \else
+ \ifx\currentstructurecomponentexpansion\v!yes
+ \xdef\currentstructurecomponenttitle {#2\c!title}%
+ \xdef\currentstructurecomponentbookmark{#2\c!bookmark}%
+ \xdef\currentstructurecomponentmarking {#2\c!marking}%
+ \xdef\currentstructurecomponentlist {#2\c!list}%
+ \else
+ \xdef\currentstructurecomponenttitle {#3\c!title}%
+ \xdef\currentstructurecomponentbookmark{#3\c!bookmark}%
+ \xdef\currentstructurecomponentmarking {#3\c!marking}%
+ \xdef\currentstructurecomponentlist {#3\c!list}%
+ \iflocation \ifempty\currentstructurecomponentbookmark
+ \begingroup
+ \simplifycommands
+ \xdef\currentstructurecomponentbookmark{\detokenize\expandafter{\normalexpanded{#2\c!title}}}%
+ \endgroup
+ \fi \fi
+ \fi
+ \ifempty\currentstructurecomponentlist
+ \glet\currentstructurecomponentlist\currentstructurecomponenttitle
+ \fi
+ \glet\currentstructurecomponentcoding\s!tex
+ \fi
+ %
+ \setnextinternalreference
+ \scratchcounter\clf_addtolist %{
+ metadata {
+ kind {#1}
+ name {\currentname}
+ % level structures.sections.currentlevel()
+ catcodes \ifempty\currentstructurecomponentcatcodes\catcodetable\else\csname\currentstructurecomponentcatcodes\endcsname\fi
+ coding {\currentstructurecomponentcoding}
+ \ifx\currentstructurecomponentcoding\s!xml
+ xmlroot {\xmldocument}
+ \fi
+ \ifempty\currentstructurecomponentxmlsetup \else
+ xmlsetup {\currentstructurexmlsetup}
+ \fi
+ }
+ references {
+ internal \locationcount
+ % block {\currentsectionblock}
+ reference {\currentstructurecomponentreference}
+ prefix {\currentstructurecomponentreferenceprefix}
+ % section structures.sections.currentid()
+ view {\interactionparameter\c!focus}%
+ }
+ titledata {
+ label {\detokenize\expandafter{\currentstructurecomponentlabel}}
+ title {\detokenize\expandafter{\currentstructurecomponenttitle}}
+ \ifx\currentstructurecomponentbookmark\currentstructurecomponenttitle \else
+ bookmark {\detokenize\expandafter{\currentstructurecomponentbookmark}}
+ \fi
+ \ifx\currentstructurecomponentmarking\currentstructurecomponenttitle \else
+ marking {\detokenize\expandafter{\currentstructurecomponentmarking}}
+ \fi
+ \ifx\currentstructurecomponentlist\currentstructurecomponenttitle \else
+ list {\detokenize\expandafter{\currentstructurecomponentlist}}
+ \fi
+ }
+ \ifx\p_hasnumber\v!yes
+ prefixdata {
+ prefix {#2\c!prefix}
+ separatorset {#2\c!prefixseparatorset}
+ conversion {#2\c!prefixconversion}
+ conversionset {#2\c!prefixconversionset}
+ set {#2\c!prefixset}
+ % segments {#2\c!prefixsegments}
+ segments {\p_prefixsegments}
+ connector {#2\c!prefixconnector}
+ }
+ numberdata { % more helpers here, like compact elsewhere
+ numbers {\currentcounter}
+ group {#2\c!group}
+ groupsuffix {#2\c!groupsuffix}
+ counter {\currentcounter}
+ separatorset {#2\c!numberseparatorset}
+ conversion {#2\c!numberconversion}
+ conversionset {#2\c!numberconversionset}
+ starter {#2\c!numberstarter}
+ stopper {#2\c!numberstopper}
+ segments {#2\c!numbersegments}
+ }
+ \fi
+ userdata {\detokenize{#4}}
+ %}
+ \relax
+ \xdef\m_strc_counters_last_registered_index{\the\scratchcounter}%
+ \setstructurecomponentsynchronization\m_strc_counters_last_registered_index
+ \glet\m_strc_counters_last_registered_attribute \currentstructurecomponentattribute
+ \glet\m_strc_counters_last_registered_synchronize\currentstructurecomponentsynchronize}
+
+\let\m_strc_counters_last_registered_index \relax
+\let\m_strc_counters_last_registered_attribute \relax
+\let\m_strc_counters_last_registered_synchronize\relax
+
+% This can be improved as we don't need to pas all these variables
+% each time (we can set them up once).
+
+\protected\def\strc_counter_preset_using_parameter#1#2% \setupcommand \someparameter
+ {#1%
+ [\c!way =#2\c!way,
+ \c!prefix =#2\c!prefix,
+ \c!prefixseparatorset =#2\c!prefixseparatorset,
+ \c!prefixconversion =#2\c!prefixconversion,
+ \c!prefixconversionset=#2\c!prefixconversionset,
+ \c!prefixstarter =#2\c!prefixstarter,
+ \c!prefixstopper =#2\c!prefixstopper,
+ \c!prefixsegments =#2\c!prefixsegments,
+ \c!prefixset =#2\c!prefixset,
+ \c!prefixconnector =#2\c!prefixconnector,
+ \c!numberseparatorset =#2\c!numberseparatorset,
+ \c!numberconversion =#2\c!numberconversion,
+ \c!numberconversionset=#2\c!numberconversionset,
+ \c!numberstarter =#2\c!numberstarter,
+ \c!numberstopper =#2\c!numberstopper,
+ \c!numbersegments =#2\c!numbersegments]}
+
+\protected\def\strc_counter_setup_using_parameter#1#2% name \someparameter
+ {\edef\currentcounter{#1}%
+ %
+ \setcounterparameter \c!start{#2\c!start}%
+ \setcounterparameter \c!state{#2\c!state}% % beware, "" == start
+ \setcounterparameter \c!way{#2\c!way}%
+ %
+ \setcounterparameter \c!prefix{#2\c!prefix}%
+ \setcounterparameter \c!prefixseparatorset{#2\c!prefixseparatorset}%
+ \setcounterparameter \c!prefixconversion{#2\c!prefixconversion}%
+ \setcounterparameter\c!prefixconversionset{#2\c!prefixconversionset}%
+ \setcounterparameter \c!prefixstarter{#2\c!prefixstarter}%
+ \setcounterparameter \c!prefixstopper{#2\c!prefixstopper}%
+ \setcounterparameter \c!prefixset{#2\c!prefixset}%
+ \setcounterparameter \c!prefixsegments{#2\c!prefixsegments}%
+ \setcounterparameter \c!prefixset{#2\c!prefixset}%
+ \setcounterparameter \c!prefixconnector{#2\c!prefixconnector}%
+ %
+ \setcounterparameter \c!numberseparatorset{#2\c!numberseparatorset}%
+ \setcounterparameter \c!numberconversion{#2\c!numberconversion}%
+ \setcounterparameter\c!numberconversionset{#2\c!numberconversionset}%
+ \setcounterparameter \c!numberstarter{#2\c!numberstarter}%
+ \setcounterparameter \c!numberstopper{#2\c!numberstopper}%
+ \setcounterparameter \c!numbersegments{#2\c!numbersegments}%
+ %
+ \the\everysetupcounter}
+
+\protected\def\mult_interfaces_counter_association#1#2#3#4#5#6#7% tag current setup parameter list sync register
+ {\strc_counter_preset_using_parameter#3\rootcounterparameter
+ \newtoks#5%
+ \permanent\protected\def #6{\ifempty#2\the#5\else\strc_counter_setup_using_parameter#2#4\fi}% sync
+ \permanent\protected\def#7##1{\normalexpanded{#5{\the#5\strc_counter_setup_using_parameter{##1}\noexpand#4}}}} % register
+
+\permanent\protected\def\installcounterassociation#1% => synchronize#1counters register#1counter
+ {\normalexpanded
+ {\mult_interfaces_counter_association
+ {#1}% not \??xx but xx
+ \expandafter\noexpand\csname current#1\endcsname
+ \expandafter\noexpand\csname setup#1\endcsname
+ \expandafter\noexpand\csname #1parameter\endcsname
+ \expandafter\noexpand\csname counter_association_list_#1\endcsname
+ \expandafter\noexpand\csname synchronize#1counters\endcsname
+ \expandafter\noexpand\csname register#1counter\endcsname}}
+
+% needs testing:
+%
+% \protected\def\strc_counter_setup_push#1#2#3% \someparameter \directsomeparameter \setexpandedsomeparameter
+% {\let\savedcounterparameter \counterparameter
+% \let\saveddirectcounterparameter \directcounterparameter
+% \let\savedsetexpandedcounterparameter\setexpandedcounterparameter
+% % remap
+% \let\counterparameter #1%
+% \let\directcounterparameter #2%
+% \let\setexpandedcounterparameter #3}
+%
+% \protected\def\strc_counter_setup_pop
+% {\let\counterparameter \savedcounterparameter
+% \let\directcounterparameter \saveddirectcounterparameter
+% \let\setexpandedcounterparameter \savedsetexpandedcounterparameter}
+%
+% \protected\def\mult_interfaces_counter_association#1#2#3#4#5#6#7% tag current setup parameter list sync register
+% {\strc_counter_preset_using_parameter#3\rootcounterparameter
+% \newtoks#5%
+% \protected\def#6% sync
+% {\strc_counter_setup_push
+% \ifempty#2%
+% \the#5%
+% \else
+% \let\currentcounter\empty
+% \the\everysetupcounter
+% \fi
+% \strc_counter_setup_pop}%
+% \protected\def#7##1% register
+% {\normalexpanded{#5{\the#5\edef\noexpand\currentcounter{##1}\noexpand\the\everysetupcounter}}}}
+
+%D For good old times (easier to explain in manuals):
+%D
+%D \starttyping
+%D \setcounter[userpage][9]
+%D \setupcounter[userpage][number=9]
+%D \setupuserpagenumber[number=9]
+%D \stoptyping
+
+% needs testing: we might have conflicts with mixins
+%
+% \appendtoks
+% \ifempty\currentcounter \else
+% \edef\p_number{\counterparameter\c!number}%
+% \ifempty\p_number \else
+% \strc_counters_set\currentcounter\p_number
+% \letcounterparameter\c!number\empty
+% \fi
+% \fi
+% \to \everysetupcounter
+
+\stopcontextdefinitioncode
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-pag.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-pag.mkxl
new file mode 100644
index 00000000000..c27fd942953
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-pag.mkxl
@@ -0,0 +1,487 @@
+%D \module
+%D [ file=strc-pag,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Pagenumbering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Pagenumbering}
+
+\registerctxluafile{strc-pag}{}
+
+\unprotect
+
+\startcontextdefinitioncode
+
+% Allocation:
+
+\ifdefined\realpageno \else \permanent\countdef\realpageno \zerocount \fi \realpageno \plusone
+\ifdefined\userpageno \else \permanent\countdef\userpageno \plusone \fi \userpageno \plusone
+\ifdefined\subpageno \else \permanent\countdef\subpageno \plustwo \fi \subpageno \plusone % was \zerocount but that doesn't work well with bytext
+\ifdefined\arrangeno \else \permanent\countdef\arrangeno \plusthree \fi \arrangeno \zerocount % !
+\ifdefined\pagenoshift \else \permanent\countdef\pagenoshift\plusfour \fi \pagenoshift\zerocount % !
+\ifdefined\lastpageno \else \permanent\countdef\lastpageno \plusfive \fi \lastpageno \zerocount % !
+
+\ifdefined\pageno \else \aliased\let\pageno\userpageno \fi
+
+\permanent\def\realfolio{\the\realpageno}
+\permanent\def\userfolio{\the\userpageno}
+\permanent\def\subfolio {\the\subpageno }
+\permanent\def\lastfolio{\the\lastpageno}
+
+\newtoks\everyinitializepagecounters
+
+\permanent\protected\def\initializepagecounters
+ {\the\everyinitializepagecounters}
+
+\appendtoks
+ \initializepagecounters
+\to \everyjob
+
+% Page numbers are kind of independent of each other and therefore they
+% all get their own counter. After all, it's easier to combine them in
+% a pseudo counterset than to deal with a complex set itself.
+
+% \defineprefixset [mine][section-1,section-2]
+% \defineseparatorset[mine][:]
+%
+% \setupuserpagenumber
+% [way=bypart,
+% prefix=yes,
+% prefixset=mine,
+% prefixseparatorset=mine]
+
+% \defineconversionset[frontpart:pagenumber][][romannumerals]
+% \defineconversionset[bodypart:pagenumber] [][numbers]
+%
+% \setupuserpagenumber[way=byblock]
+% \setupuserpagenumber[way=bychapter]
+% \setupuserpagenumber[numberconversionset=pagenumber]
+%
+% \starttext
+% \startfrontmatter
+% \completecontent[criterium=all]
+% \chapter{tufte} \section{one} \input tufte \page \section{two} \input tufte \page
+% \chapter{tufte} \section{one} \input tufte \page \section{two} \input tufte \page
+% \stopfrontmatter
+% \startbodymatter
+% \chapter{knuth} \section{one} \input knuth \page \section{two} \input knuth \page \section{three} \input knuth \page
+% \chapter{knuth} \section{one} \input knuth \page \section{two} \input knuth \page \section{three} \input knuth \page
+% \stopbodymatter
+% \stoptext
+
+\definecounter[\s!realpage][\c!prefix=\v!no,\c!start=\plusone,\c!prefixsegments=,\s!counter=realpageno,\c!method=\v!page]
+\definecounter[\s!userpage][\c!prefix=\v!no,\c!start=\plusone,\c!prefixsegments=,\s!counter=userpageno,\c!method=\v!page]
+\definecounter[\s!subpage] [\c!prefix=\v!no,\c!start=\plusone,\c!prefixsegments=,\s!counter=subpageno, \c!method=\v!page]
+
+\newtoks\everysetuprealpagenumber % todo: set state: none, start, stop, reset
+\newtoks\everysetupuserpagenumber % todo: set state: none, start, stop, reset
+\newtoks\everysetupsubpagenumber % todo: set state: none, start, stop, reset
+
+\let\m_strc_pagenumbers_state_old\zerocount
+\let\m_strc_pagenumbers_state_new\zerocount
+
+\def\strc_pagenumbers_save_state#1{\edef\m_strc_pagenumbers_state_old{\namedcounterparameter#1\c!state}}
+
+\permanent\tolerant\protected\def\setuprealpagenumber[#1]{\strc_pagenumbers_save_state\s!realpage\setupcounter[\s!realpage][#1]\the\everysetuprealpagenumber}
+\permanent\tolerant\protected\def\setupuserpagenumber[#1]{\strc_pagenumbers_save_state\s!userpage\setupcounter[\s!userpage][#1]\the\everysetupuserpagenumber}
+\permanent\tolerant\protected\def\setupsubpagenumber [#1]{\strc_pagenumbers_save_state\s!subpage \setupcounter[\s!subpage ][#1]\the\everysetupsubpagenumber }
+
+\permanent\protected\def\resetrealpagenumber {} % not permitted
+\permanent\protected\def\resetuserpagenumber {\strc_counters_reset\s!userpage}
+\permanent\protected\def\resetsubpagenumber {\strc_counters_reset\s!subpage}
+
+\appendtoks
+ \strc_counters_set\s!realpage\realpageno
+ \strc_counters_set\s!userpage\userpageno
+ \strc_counters_set\s!subpage \subpageno
+ \lastpageno\lastcountervalue[\s!realpage]\relax
+\to \everyinitializepagecounters
+
+\aliased\let\setuppagenumber\setupuserpagenumber
+\aliased\let\resetpagenumber\resetuserpagenumber
+
+% invisible =
+
+\def\strc_pagenumbers_page_state_save % \normalexpanded?
+ {\clf_savepagedata
+ {
+ prefix {\namedcounterparameter\s!userpage\c!prefix}
+ separatorset {\namedcounterparameter\s!userpage\c!prefixseparatorset}
+ conversion {\namedcounterparameter\s!userpage\c!prefixconversion}
+ conversionset {\namedcounterparameter\s!userpage\c!prefixconversionset}
+ set {\namedcounterparameter\s!userpage\c!prefixset}
+ segments {\namedcounterparameter\s!userpage\c!prefixsegments}
+ connector {\namedcounterparameter\s!userpage\c!prefixconnector}
+ }{
+ conversion {\namedcounterparameter\s!userpage\c!numberconversion}
+ conversionset {\namedcounterparameter\s!userpage\c!numberconversionset}
+ starter {\namedcounterparameter\s!userpage\c!numberstarter}
+ stopper {\namedcounterparameter\s!userpage\c!numberstopper}
+ }{
+ viewerprefix {\namedcounterparameter\s!userpage\c!viewerprefix}
+ state {\namedcounterparameter\s!userpage\c!state}
+ }%
+ \relax}
+
+\prependtoks
+ \strc_pagenumbers_page_state_save
+\to \everyshipout
+
+\installcorenamespace{pagestatestack} % no level yet
+
+\protected\def\strc_pagenumbers_page_state_push{\xdefcsname\??pagestatestack\c!state\endcsname{\namedcounterparameter\s!userpage\c!state}}
+\protected\def\strc_pagenumbers_page_state_pop {\normalexpanded{\setuppagenumber[\c!state=\csname\??pagestatestack\c!state\endcsname]}}
+
+\setuppagenumber
+ [\c!way=\v!by\v!text,
+ \c!prefix=\v!no,
+ \c!prefixset=\v!part,
+ \c!prefixconnector=\endash,
+ \c!state=\v!start]
+
+\setupsubpagenumber
+ [\c!way=\v!by\v!part,
+ \c!state=\v!start] % was stop but start looks better in logging
+
+% Counters
+
+%permanent\def\firstrealpagenumber{\convertedcounter[\s!realpage][\c!type=\v!first]}
+%permanent\def\firstuserpagenumber{\convertedcounter[\s!userpage][\c!type=\v!first]}
+%permanent\def\firstsubpagenumber {\convertedcounter[\s!subpage ][\c!type=\v!first]}
+
+%permanent\def\lastrealpagenumber {\convertedcounter[\s!realpage][\c!type=\v!last]}
+%permanent\def\lastuserpagenumber {\convertedcounter[\s!userpage][\c!type=\v!last]}
+%permanent\def\lastsubpagenumber {\convertedcounter[\s!subpage ][\c!type=\v!last]}
+
+%permanent\def\prevrealpagenumber {\convertedcounter[\s!realpage][\c!type=\v!previous]}
+%permanent\def\prevuserpagenumber {\convertedcounter[\s!userpage][\c!type=\v!previous]}
+%permanent\def\prevsubpagenumber {\convertedcounter[\s!subpage ][\c!type=\v!previous]}
+
+%permanent\def\nextrealpagenumber {\convertedcounter[\s!realpage][\c!type=\v!next]}
+%permanent\def\nextuserpagenumber {\convertedcounter[\s!userpage][\c!type=\v!next]}
+%permanent\def\nextsubpagenumber {\convertedcounter[\s!subpage ][\c!type=\v!next]}
+
+\permanent\def\firstrealpage{\strc_counters_first\s!realpage}
+\permanent\def\firstuserpage{\strc_counters_first\s!userpage}
+\permanent\def\firstsubpage {\strc_counters_first\s!subpage }
+
+\permanent\def\prevrealpage {\strc_counters_prev \s!realpage}
+\permanent\def\prevuserpage {\strc_counters_prev \s!userpage}
+\permanent\def\prevsubpage {\strc_counters_prev \s!subpage }
+
+\permanent\def\nextrealpage {\strc_counters_next \s!realpage}
+\permanent\def\nextuserpage {\strc_counters_next \s!userpage}
+\permanent\def\nextsubpage {\strc_counters_next \s!subpage }
+
+\permanent\def\lastrealpage {\strc_counters_last \s!realpage}
+\permanent\def\lastuserpage {\strc_counters_last \s!userpage}
+\permanent\def\lastsubpage {\strc_counters_last \s!subpage }
+
+\aliased\let\firstpage\firstrealpage
+\aliased\let\prevpage \prevrealpage
+\aliased\let\nextpage \nextrealpage
+\aliased\let\lastpage \lastrealpage
+
+% Compatibility counters:
+
+\permanent\def\nofrealpages {\lastrealpage} \permanent\def\totalnumberofpages{\lastrealpage}
+\permanent\def\nofuserpages {\lastuserpage} \permanent\def\lastpagenumber {\lastuserpage}
+\permanent\def\nofsubpages {\lastsubpage }
+
+% Renderers:
+
+\permanent\def\pagenumber {\strc_counters_raw\s!userpage}
+\permanent\def\prefixedpagenumber {\directconvertedcounter\s!userpage\empty} % \userpagenumber
+
+\permanent\def\realpagenumber {\directconvertedcounter\s!realpage\empty}
+\permanent\def\userpagenumber {\directconvertedcounter\s!userpage\empty}
+\permanent\def\subpagenumber {\directconvertedcounter\s!subpage \empty}
+
+\permanent\def\firstrealpagenumber{\directconvertedcounter\s!realpage\v!first}
+\permanent\def\firstuserpagenumber{\directconvertedcounter\s!userpage\v!first}
+\permanent\def\firstsubpagenumber {\directconvertedcounter\s!subpage \v!first}
+
+\permanent\def\lastrealpagenumber {\directconvertedcounter\s!realpage\v!last}
+\permanent\def\lastuserpagenumber {\directconvertedcounter\s!userpage\v!last}
+\permanent\def\lastsubpagenumber {\directconvertedcounter\s!subpage \v!last}
+
+\permanent\def\prevrealpagenumber {\directconvertedcounter\s!realpage\v!previous}
+\permanent\def\prevuserpagenumber {\directconvertedcounter\s!userpage\v!previous}
+\permanent\def\prevsubpagenumber {\directconvertedcounter\s!subpage \v!previous}
+
+\permanent\def\nextrealpagenumber {\directconvertedcounter\s!realpage\v!next}
+\permanent\def\nextuserpagenumber {\directconvertedcounter\s!userpage\v!next}
+\permanent\def\nextsubpagenumber {\directconvertedcounter\s!subpage \v!next}
+
+\protected\def\strc_pagenumbers_decrement_counters % only at the end
+ {\strc_counters_decrement\s!realpage
+ \strc_counters_decrement\s!userpage
+ \strc_counters_decrement\s!subpage}
+
+\protected\def\strc_pagenumbers_increment_counters
+ {\incrementpagenumber
+ \incrementsubpagenumber}
+
+\appendtoks
+ \strc_pagenumbers_decrement_counters
+\to \everygoodbye
+
+\newcount\c_strc_subpage_first_real \c_strc_subpage_first_real\plusone
+
+\appendtoks
+ \ifcase\subpageno\relax
+ \global\c_strc_subpage_first_real\realpageno
+ \or
+ \global\c_strc_subpage_first_real\realpageno
+ \fi
+\to \everybeforepagebody
+
+\permanent\def\therealsubpageno#1% new helper
+ {\the\numexpr\c_strc_subpage_first_real+#1+\minusone\relax}
+
+% Equivalents (compatibility):
+%
+% todo: maybe leave lastpage etc lua calls
+
+\permanent\def\realpage{\the\realpageno}
+\permanent\def\userpage{\the\userpageno}
+\permanent\def\subpage {\the\subpageno}
+
+% Hooks:
+
+\permanent\def\currentpage{\the\realpageno}% rather useless
+
+\appendtoks
+ \ifnum\realpageno>\lastpage \enforced\glet\lastpage\lastrealpage \fi
+\to \everyinitializepagecounters
+
+% States:
+
+\newconditional\layoutisdoublesided % already defined: \newif\ifdoublesided
+\newconditional\layoutissinglesided % already defined: \newif\ifsinglesided
+
+% Realpage and subpage numbers:
+
+\permanent\protected\def\setnextrealpageno{\global\realpageno\strc_counters_incremented\s!realpage\relax}
+\permanent\protected\def\setnextsubpageno {\global\subpageno \strc_counters_incremented\s!subpage \relax}
+
+% Page numbers: (can move to lua) ... inconsistent names
+
+\installcorenamespace{pagenumberinc}
+\installcorenamespace{pagenumberdec}
+
+\protected\def\strc_pagenumbers_decrement_userpage{\global\userpageno\strc_counters_decremented\s!userpage\relax}
+\protected\def\strc_pagenumbers_increment_userpage{\global\userpageno\strc_counters_incremented\s!userpage\relax}
+
+\permanent\protected\def\decrementsubpagenumber{\global\subpageno \strc_counters_decremented\s!subpage \relax}
+\permanent\protected\def\incrementsubpagenumber{\global\subpageno \strc_counters_incremented\s!subpage \relax}
+
+\protected\def\strc_pagenumbers_synchronize_userpage{\global\c_strc_pagenumbers_state_userpage\plustwo} % start and visible
+
+\permanent\protected\def\decrementpagenumber{\csname\??pagenumberdec\namedcounterparameter\s!userpage\c!state\endcsname}
+\permanent\protected\def\incrementpagenumber{\csname\??pagenumberinc\namedcounterparameter\s!userpage\c!state\endcsname}
+
+\letvalue{\??pagenumberdec\v!start}\strc_pagenumbers_decrement_userpage
+\letvalue{\??pagenumberdec\v!none }\strc_pagenumbers_decrement_userpage
+\letvalue{\??pagenumberdec\v!empty}\strc_pagenumbers_decrement_userpage
+
+\letvalue{\??pagenumberinc\v!start}\strc_pagenumbers_increment_userpage
+\letvalue{\??pagenumberinc\v!none }\strc_pagenumbers_increment_userpage
+\setvalue{\??pagenumberinc\v!empty}{\strc_pagenumbers_increment_userpage\strc_pagenumbers_synchronize_userpage}
+\letvalue{\??pagenumberinc\v!keep }\strc_pagenumbers_synchronize_userpage
+
+% Setup general page numbering
+
+\installcorenamespace{pagenumbering}
+
+\installdirectcommandhandler \??pagenumbering {pagenumbering}
+
+% some day ifsinglesided and ifdoublesided will become obsolete
+
+\newtoks\everysidedswitch
+
+\appendtoks
+ \singlesidedfalse \setfalse\layoutisdoublesided
+ \doublesidedfalse \setfalse\layoutissinglesided
+ \resetsystemmode\v!singlesided
+ \resetsystemmode\v!doublesided
+ \processallactionsinset
+ [\directpagenumberingparameter\c!alternative]%
+ [ \v!singlesided=>\setsystemmode\v!singlesided\singlesidedtrue\settrue\layoutissinglesided,
+ \v!doublesided=>\setsystemmode\v!doublesided\doublesidedtrue\settrue\layoutisdoublesided]%
+ \the\everysidedswitch
+ \pageduplexmode
+ \ifsinglesided
+ \ifdoublesided\plustwo\else\zerocount\fi
+ \else
+ \ifdoublesided\plusone\else\zerocount\fi
+ \fi
+ \page_backgrounds_recalculate
+ \strc_pagenumbers_set_location
+\to \everysetuppagenumbering
+
+\ifdefined \page_backgrounds_recalculate \else
+ \let\page_backgrounds_recalculate\relax
+\fi
+
+\ifdefined \strc_pagenumbers_set_location \else
+ \let\strc_pagenumbers_set_location\relax
+\fi
+
+\protected\def\strc_pagenumbers_flush_final_page
+ {\edef\p_strc_pagenumbers_page{\directpagenumberingparameter\c!page}%
+ \ifempty\p_strc_pagenumbers_page \else
+ \ifx\p_strc_pagenumbers_page\v!no \else
+ \page[\p_strc_pagenumbers_page]%
+ \fi
+ \fi}
+
+% The numbered location handler is there because we need to be downward
+% compatible. So, in fact there can be multiple handlers active at the
+% same time, but only the current one does something.
+
+% Rendering:
+
+\protected\def\strc_pagenumbers_place_location
+ {\ifnum\c_strc_pagenumbers_state_userpage=\plustwo
+ \ifnum\c_strc_pagenumbers_state=\plusone
+ \doif{\directpagenumberingparameter\c!strut}\v!yes\strut
+ \begingroup
+ \usepagenumberingstyleandcolor\c!style\c!color
+ \directpagenumberingparameter\c!command
+ {\directpagenumberingparameter\c!left
+ \labeltexts\v!pagenumber\prefixedpagenumber
+ \directpagenumberingparameter\c!right}%
+ \endgroup
+ \fi
+ \fi}
+
+\permanent\protected\def\completepagenumber
+ {\ifnum\c_strc_pagenumbers_state_userpage=\plustwo
+ \ifnum\c_strc_pagenumbers_state=\plusone
+ \directpagenumberingparameter\c!left
+ \labeltexts\v!pagenumber\prefixedpagenumber
+ \directpagenumberingparameter\c!right
+ \fi
+ \fi}
+
+\permanent\protected\def\placepagenumber
+ {\ifnum\c_strc_pagenumbers_state_userpage=\plustwo
+ \ifnum\c_strc_pagenumbers_state=\plusone
+ \labeltexts\v!pagenumber\pagenumber
+ \fi
+ \fi}
+
+\permanent\protected\def\referencepagenumber[#1]%
+ {\doifelsenothing{#1}{?}{}}
+
+% The numbered location handler is there because we need to be downward
+% compatible. So, in fact there can be multiple handlers active at the
+% same time, but only the current one does something.
+
+\setnewconstant\c_strc_pagenumbers_state_realpage\plustwo % counter state : 0=stop, 1=start, 2=start and visible
+\setnewconstant\c_strc_pagenumbers_state_userpage\plustwo % counter state : 0=stop, 1=start, 2=start and visible
+\setnewconstant\c_strc_pagenumbers_state_subpage \plustwo % counter state : 0=stop, 1=start, 2=start and visible
+\setnewconstant\c_strc_pagenumbers_state \plusone % general number: 0=invisible, 1=visible
+
+\protected\def\strc_pagenumbers_check_state_change#1#2%
+ {\edef\m_strc_pagenumbers_state_new{\namedcounterparameter#1\c!state}%
+ \ifx\m_strc_pagenumbers_state_new\m_strc_pagenumbers_state_old \else
+ #2\ifx\m_strc_pagenumbers_state_new\v!start\plustwo\else\zerocount\fi
+ \fi}
+
+\appendtoks % todo: set state: none, start, stop, reset
+ \strc_pagenumbers_check_state_change\s!realpage\c_strc_pagenumbers_state_realpage
+\to \everysetuprealpagenumber
+
+\appendtoks % todo: set state: none, start, stop, reset
+ \strc_pagenumbers_check_state_change\s!userpage\c_strc_pagenumbers_state_userpage
+\to \everysetupuserpagenumber
+
+\appendtoks % todo: set state: none, start, stop, reset
+ \strc_pagenumbers_check_state_change\s!subpage\c_strc_pagenumbers_state_subpage
+\to \everysetupsubpagenumber
+
+\appendtoks % todo: set state: none, start, stop, reset
+ \doifelse{\directpagenumberingparameter\c!state}\v!start
+ {\c_strc_pagenumbers_state\plusone }%
+ {\c_strc_pagenumbers_state\zerocount}%
+\to \everysetuppagenumbering
+
+% Done
+
+% \c!way=\v!by\v!part
+% \c!text=
+% \v!chapter\v!number=\v!no
+% \v!part\v!number=\v!yes
+% \c!numberseparator=--
+% \c!conversion=\v!numbers
+
+\setuppagenumbering
+ [\c!alternative=\v!singlesided,
+ \c!location={\v!header,\v!middle},
+ \c!width=, % in geval van \v!marginedge
+ \c!left=,
+ \c!right=,
+ \c!page=\v!last,
+ \c!textseparator=\tfskip,
+ \c!state=\v!start,
+ \c!command=,
+ \c!strut=\v!yes,
+ \c!style=, % empty, otherwise conflict
+ \c!color=]
+
+% just for downward compatbility
+
+\appendtoks
+ \edef\scratchstringone{\namedcounterparameter\s!userpage\c!number}%
+ \ifempty\scratchstringone \else
+ \normalexpanded{\setuppagenumber[\c!start=\scratchstringone,\c!number=]}%
+ \userpageno\strc_counters_raw\s!userpage
+ \fi
+\to \everysetupuserpagenumber % todo: set state: none, start, stop, reset
+
+\appendtoks
+ \edef\scratchstringone{\namedcounterparameter\s!subpage\c!number}%
+ \ifempty\scratchstringone \else
+ \normalexpanded{\setupsubpagenumber[\c!start=\scratchstringone,\c!number=]}%
+ \subpageno\strc_counters_raw\s!subpage\relax
+ \fi
+\to \everysetupsubpagenumber % todo: set state: none, start, stop, reset
+
+% \setuplayout[width=300pt,backspace=4cm]
+% \setuppagenumbering [alternative=doublesided]
+% \setupuserpagenumber[start=2]
+% \starttext \dorecurse{20}{\input knuth \par} \stoptext
+
+\protected\def\strc_pagenumbers_check_change_shift
+ {\userpageno\strc_counters_raw\s!userpage\relax
+ \ifnum\realpageno=\plusone
+ \ifodd\userpageno
+ \else
+ \global\pagenoshift\plusone
+ \fi
+ \fi}
+
+\appendtoks % todo: set state: none, start, stop, reset
+ % this makes starting at an even page possible
+ \strc_pagenumbers_check_change_shift
+\to \everysetupuserpagenumber
+
+\appendtoks % todo: set state: none, start, stop, reset
+ % this makes starting at an even page possible
+ \strc_pagenumbers_check_change_shift
+\to \everysetuppagenumbering
+
+\initializepagecounters
+
+\stopcontextdefinitioncode
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-ref.mklx b/Master/texmf-dist/tex/context/base/mkxl/strc-ref.mklx
new file mode 100644
index 00000000000..f3a90ed6764
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-ref.mklx
@@ -0,0 +1,2217 @@
+%D \module
+%D [ file=strc-ref,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Cross Referencing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: (1) configure references, (2) rendering => with presets
+%
+% \defineconversionset[default][Character,number,Romannumerals,Character][number]
+% \defineseparatorset [default][.,.,--][.]
+% \setuphead[subsection][sectionstopper=),sectionsegments=4:4]
+% \setupreferencestructureprefix[default][prefixsegments=2:4]
+% \setupreferencestructureprefix[figure][default][prefixsegments=3:4]
+% \chapter {One}
+% \section {One}
+% \subsection[sec:test]{Two}
+% See \in[sec:test] and \in[fig:xx] and \in[fig:yy]
+% \placefigure[here][fig:xx]{}{}
+% \placefigure[here][fig:yy]{}{}
+
+\writestatus{loading}{ConTeXt Structure Macros / Cross Referencing}
+
+\registerctxluafile{strc-rsc}{}
+\registerctxluafile{strc-ref}{}
+\registerctxluafile{node-ref}{autosuffix,optimize}
+
+\unprotect
+
+% todo : unknown/illegal reference no arg
+% todo : +n pages check on 'samepage' (contrastcolor)
+% todo : multiple text in reference
+
+% Makes more sense to build action data first, especially now openaction etc are
+% supported.
+%
+% \definespecial\doexecuteactionchain w h
+% \definespecial\dosetgotolocation
+% \definespecial\dosetexecuteJScode
+% ...
+
+%D This module deals with referencing. In \CONTEXT\ referencing is one of the core
+%D features, although at a first glance probably nobody will notice. This is good,
+%D because referencing should be as hidden as possible.
+%D
+%D Before we start implementing functionality we provide a way to set up this
+%D mechanism.
+%D
+%D \showsetup{setupreferencing}
+%D
+%D In interactive documents verbose references don't always make sense (what is a
+%D page number in an unnumbered document). By setting the \type{interaction}
+%D variable, one can influences the way interactive references are set.
+
+\installcorenamespace{referencing}
+
+\installdirectcommandhandler \??referencing {referencing} % \??referencing
+
+\mutable\let\referenceprefix \empty
+
+\mutable\let\currentdestinationattribute \empty
+
+\mutable\let\currentreferenceattribute \empty
+\mutable\let\currentreferencecoding \empty
+\mutable\let\currentreferencecontent \empty
+\mutable\let\currentreferencedata \empty
+\mutable\let\currentreferenceexpansion \empty
+\mutable\let\currentreferenceformatautocase\empty
+\mutable\let\currentreferenceformatlabel \empty
+\mutable\let\currentreferenceformatsetups \empty
+\mutable\let\currentreferenceformattype \empty
+\mutable\let\currentreferencekind \empty
+\mutable\let\currentreferencelabels \empty
+%mutable\let\currentreferencenumber \empty
+\mutable\let\currentreferenceorder \empty
+%mutable\let\currentreferencepage \empty
+%mutable\let\currentreferencerealpage \empty
+%mutable\let\currentreferencetext \empty
+%mutable\let\currentreferencetitle \empty
+\mutable\let\currentreferenceuserdata \empty
+
+
+\newif\ifreferencing \referencingtrue
+
+\appendtoks
+ \edef\p_state{\referencingparameter\c!state}%
+ \ifx\p_state\v!start
+ \referencingtrue
+ \else
+ \referencingfalse
+ \fi
+\to \everysetupreferencing
+
+%D In paper documents, referencing comes down to cross referencing, but in
+%D their interactive counterparts, is also involves navigation. Many features
+%D implemented here are therefore closely related to navigation.
+%D
+%D Many \CONTEXT\ commands can optionally be fed with a reference. Such a
+%D reference, when called upon, returns the number of a figure, table, chapter
+%D etc, a piece of text, or a pagenumber.
+%D
+%D There are three ways of defining a reference:
+%D
+%D \starttyping
+%D \pagereference[here]
+%D \textreference[here]{some text}
+%D \stoptyping
+%D
+%D the third alternative combines them in:
+%D
+%D \starttyping
+%D \reference[here]{some text}
+%D \stoptyping
+
+\permanent\protected\def\showreferences{\enabletrackers[nodes.references.show,nodes.destinations.show]}
+
+%D These are implemented in a low level form as:
+
+\permanent\tolerant\protected\def\textreference [#labels]{\strc_references_set_named_reference\s!text{#labels}{}}
+\permanent\tolerant\protected\def\pagereference [#labels]{\strc_references_set_named_reference\s!page{#labels}{}{}}
+\permanent\tolerant\protected\def\reference [#labels]{\strc_references_set_named_reference\s!full{#labels}{}}
+\permanent\tolerant\protected\def\setreference [#labels]#spacer[#settings]{\strc_references_set_named_reference\s!user{#labels}{#settings}{}}
+
+\permanent\protected\def\dosetdirectpagereference#1{\strc_references_set_named_reference\s!page{#1}{}{}} % low level, maybe use _
+
+\permanent\protected\def\usereferenceparameter#1% faster local variant
+ {\edef\m_strc_references_asked{#1\c!reference}%
+ \ifempty\m_strc_references_asked\else
+ \dosetdirectpagereference\m_strc_references_asked
+ \fi}
+
+%D Actually there is not much difference between a text and a full reference, but
+%D it's the concept that counts. The low level implementation is:
+
+\newcount\lastreferenceattribute
+\newcount\lastdestinationattribute
+
+\def\strc_references_finish#prefix#reference#internal% gets expanded anyway
+ {\normalexpanded{\clf_deferredenhancereference{#prefix}{#reference}}}
+
+\permanent\let\dofinishreference\strc_references_finish % used at lua end
+
+%D This is somewhat tricky: we want to keep the reference with the following word but
+%D that word should also hyphenate. We need to find a better way.
+
+% 0 = nothing
+% 1 = bind to following word
+
+\setnewconstant\c_strc_references_bind_state\plusone
+
+\def\strc_references_inject_before
+ {}
+
+\def\strc_references_inject_after
+ {\ifcase\c_strc_references_bind_state
+ % nothing
+ \or
+ \prewordbreak % to be tested: \removeunwantedspaces\permithyphenation
+ \fi}
+
+
+\protected\def\strc_references_set_named_reference
+ {\ifreferencing
+ \expandafter\strc_references_set_named_reference_indeed
+ \else
+ \expandafter\gobblefourarguments
+ \fi}
+
+\newbox\b_strc_destination_nodes
+
+\protected\def\strc_references_flush_destination_nodes
+ {\ifvoid\b_strc_destination_nodes \else
+ \unhbox\b_strc_destination_nodes
+ \fi}
+
+\def\strc_references_placeholder
+ {\ifempty\dotaggedplaceholder\else
+ \c_attr_destination\lastdestinationattribute
+ \dotaggedplaceholder
+ \fi}
+
+\protected\def\strc_references_destination_point_yes
+ {\strc_references_inject_before % new
+ \dostarttagged\t!reference\empty
+ \dontleavehmode\hbox attr \destinationattribute\lastdestinationattribute\bgroup
+ \strc_references_flush_destination_nodes
+ \strc_references_placeholder
+ \egroup
+ \dostoptagged
+ \strc_references_inject_after}
+
+\protected\def\strc_references_destination_point_nop
+ {\strc_references_inject_before % new
+ \dostarttagged\t!reference\empty
+ \dontleavehmode\hbox \bgroup
+ \strc_references_flush_destination_nodes
+ \strc_references_placeholder
+ \egroup
+ \dostoptagged
+ \strc_references_inject_after}
+
+\protected\def\strc_references_start_destination_nodes % messy but we need the delay
+ {\setbox\b_strc_destination_nodes\hbox\bgroup} % also sets lastdestinationattribute
+
+\protected\def\strc_references_stop_destination_nodes
+ {\normalexpanded{\egroup\lastdestinationattribute\the\lastdestinationattribute\relax}}
+
+\protected\def\strc_references_set_named_reference_indeed#kind#labels#userdata#text% labels userdata text -> todo: userdata
+ {\ifreferencing
+ % we could have a more efficient one for page references but for the moment
+ % we don't care too much
+ \edef\currentreferencekind {#kind}%
+ \edef\currentreferencelabels {#labels}%
+ \edef\currentreferenceuserdata {#userdata}%
+ \edef\currentreferenceexpansion{\referencingparameter\c!expansion}% {\referenceparameter\c!expansion}
+ \ifempty\currentreferencelabels
+ \lastdestinationattribute\attributeunsetvalue
+ \else
+ \ifx\currentreferenceexpansion\s!xml
+ \xmlstartraw
+ \xdef\currentreferencedata{#text}% data, no text else conflict
+ \xmlstopraw
+ \glet\currentreferencecoding\s!xml
+ \else
+ \ifx\currentreferenceexpansion\v!yes
+ \xdef\currentreferencedata{#text}%
+ \else
+ \xdef\currentreferencedata{\detokenize{#text}}%
+ \fi
+ \glet\currentreferencecoding\s!tex
+ \fi
+ % beware, the structures.references.set writes a
+ \setnextinternalreference
+ \strc_references_start_destination_nodes
+ \clf_setdestinationattribute
+ {%
+ references {%
+ internal \locationcount
+ % block {\currentsectionblock}%
+ view {\interactionparameter\c!focus}%
+ \ifempty\referenceprefix\else
+ prefix {\referenceprefix}%
+ \fi
+ reference {\currentreferencelabels}%
+ }%
+ metadata {%
+ kind {\currentreferencekind}%
+ \ifx\currentreferencekind\s!page\else
+ \ifx\currentreferencecoding\s!xml
+ xmlroot {\xmldocument}%
+ \fi
+ catcodes \catcodetable
+ \fi
+ }%
+ \ifempty\currentreferencedata\else
+ entries {%
+ text {\currentreferencedata}%
+ }%
+ \fi
+ \ifempty\currentreferenceuserdata\else
+ userdata {\detokenize{#userdata}}%
+ \fi
+ }%
+ \relax
+ \strc_references_stop_destination_nodes
+ \fi
+ \else
+ \setbox\b_strc_destination_nodes\emptyhbox
+ \lastdestinationattribute\attributeunsetvalue
+ \fi
+ % will become obsolete:
+ \xdef\currentdestinationattribute{\number\lastdestinationattribute}%
+ % will become an option:
+ \ifnum\lastdestinationattribute>\zerocount
+ \strc_references_destination_point_yes
+ \orelse\ifvoid\b_strc_destination_nodes\else
+ \strc_references_destination_point_nop
+ \fi}
+
+\def\strc_references_set_page_only_destination_attribute#labels% could in fact be fully expandable
+ {\ifreferencing
+ \edef\currentreferencelabels{#labels}%
+ \ifempty\currentreferencelabels
+ \setbox\b_strc_destination_nodes\emptyhbox
+ \lastdestinationattribute\attributeunsetvalue
+ \else
+ \strc_references_start_destination_nodes
+ \setnextinternalreference
+ \clf_setdestinationattribute
+ {%
+ references {%
+ internal \locationcount
+ % block {\currentsectionblock}%
+ view {\interactionparameter\c!focus}%
+ \ifempty\referenceprefix\else
+ prefix {\referenceprefix}%
+ \fi
+ reference {\currentreferencelabels}%
+ }%
+ metadata {%
+ kind {page}%
+ }%
+ }%
+ \relax
+ \strc_references_stop_destination_nodes
+ \fi
+ \else
+ \setbox\b_strc_destination_nodes\emptyhbox
+ \lastdestinationattribute\attributeunsetvalue
+ \fi}
+
+\protected\def\strc_references_direct_full_user#user#labels#text%
+ {\ifreferencing
+ \strc_references_start_destination_nodes
+ \setnextinternalreference
+ \edef\m_strc_references_user{#user}%
+ \edef\m_strc_references_text{#text}%
+ \clf_setdestinationattribute
+ {%
+ references {%
+ internal \locationcount
+ % block {\currentsectionblock}%
+ view {\interactionparameter\c!focus}%
+ \ifempty\referenceprefix\else
+ prefix {\referenceprefix}%
+ \fi
+ reference {#labels}%
+ }%
+ metadata {%
+ kind {\s!full}%
+ }%
+ \ifempty\m_strc_references_text \else
+ entries {%
+ text {\m_strc_references_text}%
+ }%
+ \fi
+ \ifempty\m_strc_references_user \else
+ userdata {\m_strc_references_user}% \detokenize\expandafter{\normalexpanded{...}}
+ \fi
+ }%
+ \relax
+ \strc_references_stop_destination_nodes
+ \else
+ \setbox\b_strc_destination_nodes\emptyhbox
+ \lastdestinationattribute\attributeunsetvalue
+ \fi
+ % will become obsolete:
+ \xdef\currentdestinationattribute{\number\lastdestinationattribute}%
+ % will become an option:
+ \ifnum\lastdestinationattribute>\zerocount
+ \strc_references_destination_point_yes
+ \orelse\ifvoid\b_strc_destination_nodes\else
+ \strc_references_destination_point_nop
+ \fi}
+
+\protected\def\strc_references_direct_full
+ {\strc_references_direct_full_user\empty}
+
+\permanent\let\dodirectfullreference\strc_references_direct_full % for at lua end (no longer)
+
+\def\strc_references_set_page_only_destination_box_attribute#cs#labels%
+ {\strc_references_set_page_only_destination_attribute{#labels}%
+ \ifnum\lastdestinationattribute>\zerocount
+ \edef#cs{attr \destinationattribute\number\lastdestinationattribute}%
+ \else
+ \let#cs\empty
+ \fi}
+
+%D It's about time to clean up references .. stable enough now.
+
+\permanent\protected\def\boxreference[#1]%
+ {\begingroup
+ \dowithnextbox
+ {\strc_references_set_page_only_destination_attribute{#1}%
+ \hpack % \hbox
+ \ifnum\lastdestinationattribute=\attributeunsetvalue\else attr \destinationattribute \lastdestinationattribute \fi
+ {\box\b_strc_destination_nodes\box\nextbox}%
+ \endgroup}}
+
+\permanent\protected\def\hboxreference[#1]{\boxreference[#1]\hbox}
+\permanent\protected\def\vboxreference[#1]{\boxreference[#1]\vbox}
+
+\mutable\def\defaultreferencepage#text{[[[#text]]]}
+\mutable\def\defaultreferencetext#text{[[[#text]]]}
+
+%D For internal usage:
+
+\def\strc_references_set_simple_reference#label%
+ {\iflocation
+ \strc_references_start_destination_nodes
+ \setnextinternalreference
+ \clf_setdestinationattribute
+ {%
+ references {%
+ view {\interactionparameter\c!focus}%
+ \ifempty\referenceprefix\else
+ prefix {\referenceprefix}%
+ \fi
+ reference {#label}%
+ internal \locationcount
+ }%
+ metadata {%
+ kind {\s!page}%
+ }%
+ }%
+ \relax
+ \strc_references_stop_destination_nodes
+ \xdef\currentdestinationattribute{\number\lastdestinationattribute}%
+ \else
+ \setbox\b_strc_destination_nodes\emptyhbox
+ \xdef\currentdestinationattribute{\number\attributeunsetvalue}%
+ \fi}
+
+\def\strc_references_set_simple_internal_reference#label% no prefix
+ {\iflocation
+ \strc_references_start_destination_nodes
+ \setnextinternalreference
+ \clf_setdestinationattribute
+ {%
+ references {%
+ view {\interactionparameter\c!focus}%
+ reference {#label}%
+ internal \locationcount
+ }%
+ metadata {%
+ kind {\s!page}%
+ }%
+ }%
+ \relax
+ \strc_references_stop_destination_nodes
+ \xdef\currentdestinationattribute{\number\lastdestinationattribute}%
+ \else
+ \setbox\b_strc_destination_nodes\emptyhbox
+ \xdef\currentdestinationattribute{\number\attributeunsetvalue}%
+ \fi}
+
+\def\strc_references_get_simple_reference#label%
+ {\iflocation
+ \clf_injectreference
+ {\referenceprefix}%
+ {#label}%
+ {%
+ height \ht\strutbox
+ depth \dp\strutbox
+ \extrareferencearguments
+ }%
+ \relax
+ \xdef\currentreferenceattribute{\number\lastreferenceattribute}%
+ \else
+ \xdef\currentreferenceattribute{\number\attributeunsetvalue}%
+ \fi}
+
+%D \macros
+%D {contentreference}
+%D
+%D \starttyping
+%D \setupinteraction
+%D [state=start,
+%D focus=standard]
+%D
+%D \setupheader
+%D [state=stop]
+%D
+%D See page \goto{page 2}[page2] \page
+%D
+%D \contentreference
+%D [page2]
+%D [offset=1cm,toffset=2cm,frame=on]
+%D {\externalfigure[cow.pdf][factor=fit]}
+%D
+%D \stoptyping
+
+\permanent\tolerant\protected\def\contentreference[#1]#*[#2]%
+ {\ifarguments
+ \expandafter\gobbletwoarguments
+ \or
+ \expandafter\strc_references_content_pickup_nop
+ \else
+ \expandafter\strc_references_content_pickup_yes
+ \fi{#1}{#2}}
+
+\def\strc_references_content_pickup_yes#1#2%
+ {\hbox\bgroup\dowithnextbox{\strc_references_content_yes_finish{#1}{#2}}\hbox}
+
+\def\strc_references_content_pickup_nop#1#2%
+ {\hbox\bgroup\dowithnextbox{\strc_references_content_nop_finish{#1}{#2}}\hbox}
+
+\def\strc_references_content_yes_finish#1#2%
+ {\scratchwidth \wd\nextbox
+ \scratchheight\ht\nextbox
+ \scratchdepth \dp\nextbox
+ \setbox\nextbox\hpack
+ {\framed[\c!frame=\v!off,#2]{\box\nextbox}}%
+ \strc_references_set_simple_reference{#1}%
+ \setbox\nextbox\hpack attr \destinationattribute \currentdestinationattribute % \hpack ?
+ {\strc_references_flush_destination_nodes
+ \box\nextbox}%
+ \setbox\nextbox\hpack{\box\nextbox}%
+ \wd\nextbox\scratchwidth
+ \ht\nextbox\scratchheight
+ \dp\nextbox\scratchdepth
+ \box\nextbox
+ \egroup}
+
+\def\strc_references_content_nop_finish#1#2%
+ {\strc_references_set_simple_reference{#1}%
+ \hpack attr \destinationattribute \currentdestinationattribute % \hpack ?
+ {\strc_references_flush_destination_nodes
+ \box\nextbox}%
+ \egroup}
+
+%D \macros
+%D {everyreference}
+%D
+%D For rather tricky purposes, one can assign sanitizing macros to \type
+%D {\everyreference} (no longer that relevant).
+
+\newevery \everyreference \relax
+
+%D This is really needed, since for instance Polish has a different alphabet and
+%D needs accented entries in registers.
+
+\appendtoks
+ \cleanupfeatures
+\to \everyreference
+
+%D We did not yet discuss prefixing. Especially in interactive documents, it's not
+%D always easy to keep track of duplicate references. The prefix mechanism, which we
+%D will describe later on, solves this problem. By (automatically) adding a prefix
+%D one keeps references local, but the global ones in view. To enable this feature,
+%D we explictly split the prefix from the reference.
+
+\let\referenceprefix\empty
+
+%D For a long time the only way to access an external file was to use the file
+%D prefix (\type {somefile::}. However, when you split up a document, redefining the
+%D references may be such a pain, that another approach is feasible. By setting the
+%D \type {autofile} variable to \type {yes} or \type {page}, you can access the
+%D reference directly.
+%D
+%D \starttabulate[||||]
+%D \NC filename::tag \NC page(filename::pnum) \NC tag \NC\NR
+%D \NC $\star$ \NC \NC \NC\NR
+%D \NC $\star$ \NC $\star$ \NC $\star$ \NC\NR
+%D \NC \NC $\star$ \NC \NC\NR
+%D \stoptabulate
+
+\permanent\protected\def\usereferences[#filename]{} % obsolete
+
+%D As mentioned we will also use the cross reference mechanism for navigational
+%D purposes. The main reason for this is that we want to treat both categories
+%D alike:
+%D
+%D \starttyping
+%D \goto{go back}[PreviousJump]
+%D \goto{colofon}[colofon page]
+%D \stoptyping
+%D
+%D Here \type{PreviousJump} is handled by the viewer, while the
+%D \type{colofon page} reference is, apart from hyperlinking, a
+%D rather normal reference.
+%D
+%D We already saw that cross refences are written to and read from a file. The pure
+%D navigational ones don't need to be written to file, but both for fast processing
+%D and transparant integration, they are saved internally as a sort of reference. We
+%D can easily distinguish such system references from real cross reference ones by
+%D their tag.
+%D
+%D We also use the odd/even characteristic to determine the page state.
+
+\mutable\let\currentrealreference \empty
+\mutable\let\currentpagereference \empty
+\mutable\let\currenttextreference \empty
+\mutable\let\currentsubtextreference \empty
+\mutable\let\currentsubsubtextreference\empty
+
+\newcount\referencehastexstate % set in backend
+
+% referencepagestate:
+%
+% 0 = no page ref, 1=same page, 2=before, 3=after
+
+%D Cross references appear as numbers (figure~1.1, chapter~2) or pagenumbers
+%D (page~2, page 3--2), and are called with \type {\in} and \type {\at}. In
+%D interactive documents we also have \type {\goto}, \type {\button} and alike.
+%D These are more versatile and look like:
+%D
+%D \starttyping
+%D \goto[reference]
+%D \goto[outer reference::]
+%D \goto[outer reference::inner reference]
+%D \goto[operation(argument)]
+%D \goto[operation(action{argument,argument})]
+%D \goto[action]
+%D \goto[action{argument}]
+%D \stoptyping
+%D
+%D The first one is a normal reference, the second and third are references to a
+%D file or \URL. The brace delimited references for instance refer to a \JAVASCRIPT.
+%D The last example shows that we can pass arguments to the actions.
+%D
+%D Now we've come to the testing step. As we can see below, this macro does bit more
+%D than testing: it also resolves the reference. This means that whenever we test
+%D for the existance of a reference at an outer level, we have all the relevant
+%D properties of that reference avaliable inside the true branche~(\type {#2}).
+%D
+%D The prefix has to do with localizing references. When a prefix is set, looking
+%D for a reference comes to looking for the prefixed one, and when not found,
+%D looking for the non prefixed one. Consider for instance the prefix set to \type
+%D {sidetrack}.
+%D
+%D \starttyping
+%D \pagereference[important]
+%D \pagereference[unimportant]
+%D \setupreferencing[prefix=sidetrack]
+%D \pagereference[important]
+%D \stoptyping
+%D
+%D results in saving (writing) the references
+%D
+%D \starttyping
+%D ...{}{important}
+%D ...{}{unimportant}
+%D ...{sidetrack}{important}...
+%D \stoptyping
+%D
+%D Now when we call for \type{unimportant}, we will indeed get the pagenumber
+%D associated to this reference. But when we call for \type{important}, while the
+%D prefix is still set, we will get the pagenumber bound to the prefixed one.
+%D
+%D {\em Some day, when processing time and memory are no longer
+%D performance factors, we will introduce multi||level
+%D prefixes.}
+%D
+%D Before we start analyzing, I introduce a general definition macro. Consider:
+%D
+%D \starttyping
+%D \goto{do}[JS(My_Script{"test",123}),titlepage]
+%D \stoptyping
+%D
+%D This can also be achieved by:
+%D
+%D \starttyping
+%D \definereference[startup][JS(My_Script{"test",123}),titlepage]
+%D \goto{do}[startup]
+%D \stoptyping
+%D
+%D Now is this is a handy feature or not?
+%D
+%D \showsetup{definereference}
+%D
+%D We can trace references by setting the next switch to true.
+
+\permanent\tolerant\protected\def\definereference[#name]#spacer[#specification]%
+ {\clf_definereference{\referenceprefix}{#name}{\detokenize{#specification}}}
+
+\permanent\protected\def\resetreference[#name]%
+ {\clf_resetreference{\referenceprefix}{#name}}
+
+\permanent\def\setpagereference#name#specification% hm,. low level ?
+ {\clf_definereference{}{#name}{\v!page(\detokenize{#specification}}} % is detokenize needed here?
+
+%D Chained references are defined as:
+%D
+%D \starttyping
+%D \goto{somewhere}[JS(somescript),nextpage,JS(anotherscript)]
+%D \stoptyping
+%D
+%D Actually supporting chains is up to the special driver. Here we only provide the
+%D hooks.
+
+%D \macros
+%D {highlighthyperlinks}
+%D
+%D The next switch can be used to make user hyperlinks are not highlighted when
+%D clicked on.
+
+\newconditional\highlighthyperlinks \settrue\highlighthyperlinks
+
+%D \macros
+%D {gotonewwindow}
+%D
+%D To make the {\em goto previous jump} feature more convenient when using more than
+%D one file, it makes sense to force the viewer to open a new window for each file
+%D opened.
+
+\newconditional\gotonewwindow \setfalse\gotonewwindow
+
+\permanent\def\expandtexincurrentreference % will happen in lua some time
+ {\ifcase\referencehastexstate\else\clf_expandcurrentreference\fi}
+
+\permanent\def\expandreferenceoperation#tag#content{\clf_setreferenceoperation#tag{#content}}
+\permanent\def\expandreferencearguments#tag#content{\clf_setreferencearguments#tag{#content}}
+
+\permanent\def\doifelsereferencefound#label#yes#nop%
+ {\clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}%
+ {\expandtexincurrentreference
+ #yes}%
+ {#nop}}
+
+\aliased\let\doifreferencefoundelse\doifelsereferencefound
+
+%D The tester only splits the reference in components but does not look into them.
+%D The following macro does a preroll and determines for instance the current real
+%D reference pagenumber. The \type {\currentrealreference} macro does the same so
+%D unless one wants to use the pagestate the next macro seldom needs to be called.
+%D
+%D The inner case is simple. Only two cases have to be taken
+%D care of:
+%D
+%D \starttyping
+%D \goto{some text}[reference]
+%D \goto{some text}[prefix:reference]
+%D \stoptyping
+%D
+%D References to other files however are treated strict or tolerant, depending on
+%D their loading and availability:
+%D
+%D \starttyping
+%D \useexternaldocument[somefile][filename][a nice description]
+%D
+%D \goto{checked reference}[somefile::reference]
+%D \goto{unchecked reference}[somefile::]
+%D \goto{unchecked reference}[anotherfile::reference]
+%D \stoptyping
+%D
+%D An unknown reference is reported on the screen, in the log file and, when
+%D enabled, in the left margin of the text.
+
+\mutable\let\unknownreference\gobbleoneargument
+
+%D When a reference is not found, we typeset a placeholder (two glyphs are often
+%D enough to represent the reference text).
+
+% \askedreference defined in lua
+
+\mutable\def\dummyreference{{\tttf ??}}
+\mutable\def\emptyreference{{\tttf !!}}
+
+\installtextracker
+ {structures.referencing.show}
+ {\let\strc_references_dummy\strc_references_dummy_yes}
+ {\let\strc_references_dummy\strc_references_dummy_nop}
+
+\def\strc_references_dummy_nop
+ {\dummyreference}
+
+\def\strc_references_dummy_yes
+ {\dummyreference
+ \margindata
+ [\referencingparameter\c!labelalternative]% alternative?
+ [\c!style=\referencingparameter\c!labelstyle,
+ \c!color=\referencingparameter\c!labelcolor]
+ {\askedreference}}
+
+\let\strc_references_dummy\strc_references_dummy_nop
+
+\setupreferencing
+ [\c!labelalternative=\v!inleft,
+ \c!labelcolor=darkred,
+ \c!labelstyle=\smallinfofont]
+
+%D To prevent repetitive messages concerning a reference being defined, we set such
+%D an unknown reference to an empty one after the first encounter.
+%D
+%D Apart from cross references supplied by the user, \CONTEXT\ generates cross
+%D references itself. Most of them are not saved as a reference, but stored with
+%D their source, for instance a list or an index entry. Such automatically
+%D generated, for the user invisible, references are called {\em internal
+%D references}. The user supplied ones are labeled as {\em external references}.
+%D
+%D A second important characteristic is that when we want to support different
+%D backends (viewers), we need to support named destinations as well as page
+%D numbers. I invite readers to take a glance at the special driver modules to
+%D understand the fine points of this. As a result we will deal with {\em locations}
+%D as well as {\em real page numbers}. We explictly call this pagenumber a real one,
+%D because it is independant of the page numbering scheme used in the document.
+%D
+%D One of the reasons for \CONTEXT\ being the first \TEX\ base macropackage to
+%D support sophisticated interactive \PDF\ files, lays in the mere fact that real
+%D page numbers are available in most two pass data, like references, list data and
+%D index entries.
+%D
+%D We will speak of \type {thisis...} when we are marking a location, and
+%D \type {goto...} when we point to such a location. The latter one can be seen as a
+%D hyperlink to the former one. In the next macros one we use constructs like:
+%D
+%D \starttyping
+%D \dostart...
+%D \dostop...
+%D \stoptyping
+%D
+%D The flag \type {\iflocation} signals if we're in interactive mode.
+
+\ifdefined\buttonheight \else \newdimen\buttonheight \fi
+\ifdefined\buttonwidth \else \newdimen\buttonwidth \fi
+
+%D Internal references can best be set using the next few macros. Setting such
+%D references to unique values is completely up to the macros that call them.
+%D
+%D \starttyping
+%D \thisissomeinternal{tag}{identifier}
+%D \gotosomeinternal {tag}{identifier}{pagenumber}{text}
+%D \stoptyping
+%D
+%D We could do this in \LUA\ \unknown
+
+\newif \iflocation
+\newcount\locationcount
+\newcount\locationorder
+\newbox \locationbox
+
+\permanent\def\nextinternalreference {\the\locationcount}
+\permanent\def\nextinternalorderreference{\the\locationorder}
+
+\permanent\def\setnextinternalreference
+ {\global\advance\locationcount\plusone}
+
+\permanent\def\setnextinternalreferences#kind#name% plural
+ {\clf_setnextinternalreference{#kind}{#name}}
+
+\permanent\def\getinternalorderreference#kind#name%
+ {\clf_currentreferenceorder{#kind}{#name}}
+
+\permanent\def\thisissomeinternal#kind#name% only for old time sake, will go away
+ {\begingroup
+ \clf_setinternalreference
+ reference {#kind:#name}% no view
+ \relax
+ \hpack attr \destinationattribute\lastdestinationattribute{}%
+ \endgroup}
+
+\installcorenamespace{savedinternalreference}
+
+\letvalue{\??savedinternalreference\s!default}\!!zerocount
+
+\permanent\protected\def\storeinternalreference#1#2%
+ {\xdefcsname\??savedinternalreference\currentstructurename\endcsname{\number#2}}
+
+\newconditional\preferpagereferences
+
+\permanent\def\gotosomeinternal#kind#name#target#text%
+ {\ifconditional\preferpagereferences
+ \directgoto{#text}[page(#target)]%
+ \else
+ \directgoto{#text}[#kind:#name]%
+ \fi}
+
+\permanent\def\gotonextinternal#text#target%
+ {\directgoto{#text}[internal(#target)]}
+
+%D In this module we define three system references: one for handling navigational,
+%D viewer specific, commands, another for jumping to special pages, like the first
+%D or last one, and a third reference for linking tree like lists, like tables of
+%D contents. The latter two adapt themselves to the current state.
+%D
+%D An example of an action is:
+%D
+%D \starttyping
+%D \goto{some action}[PreviousJump]
+%D \stoptyping
+%D
+%D as well as:
+%D
+%D \starttyping
+%D \goto{some text}[\v!action(PreviousJump]
+%D \stoptyping
+%D
+%D One can also activate an automatic prefix mechanism. By setting the
+%D \type {\prefix} variable to \type {+}, the prefix is incremented, when set to
+%D \type {-} or empty, the prefix is reset. Other values become the prefix.
+
+\newcount\prefixcounter
+
+\newconditional\autocrossfilereferences
+
+\appendtoks
+ \edef\p_autofile{\referencingparameter\c!autofile}%
+ \ifx\p_autofile\v!yes
+ \settrue \autocrossfilereferences
+ \else
+ \setfalse\autocrossfilereferences
+ \fi
+\to \everysetupreferencing
+
+\appendtoks
+ \edef\p_export{\referencingparameter\c!export}%
+ \ifx\p_export\v!yes
+ \clf_exportreferences
+ \fi
+\to \everygoodbye
+
+\permanent\protected\def\setupglobalreferenceprefix[#prefix]%
+ {\xdef\referenceprefix{#prefix}}
+
+\permanent\protected\def\globalpushreferenceprefix#prefix%
+ {\xdef\referenceprefix{\clf_pushreferenceprefix{#prefix}}}
+
+\permanent\protected\def\globalpopreferenceprefix
+ {\xdef\referenceprefix{\clf_popreferenceprefix}}
+
+\permanent\protected\def\pushreferenceprefix#prefix%
+ {\edef\referenceprefix{\clf_pushreferenceprefix{#prefix}}}
+
+\permanent\protected\def\popreferenceprefix
+ {\edef\referenceprefix{\clf_popreferenceprefix}}
+
+\def\m_strc_references_prefix_yes{+}
+\def\m_strc_references_prefix_nop{-}
+
+\permanent\protected\def\setupreferenceprefix[#prefix]%
+ {\edef\p_prefix{#prefix}%
+ \ifempty\p_prefix
+ \let\referenceprefix\empty
+ \orelse\ifx\p_prefix\m_strc_references_prefix_yes
+ \letreferencingparameter\c!prefix\s!unknown
+ \global\advance\prefixcounter\plusone
+ \edef\referenceprefix{\the\prefixcounter}%
+ \orelse\ifx\p_prefix\m_strc_references_prefix_nop
+ \letreferencingparameter\c!prefix\s!unknown
+ \let\referenceprefix\empty
+ \orelse\ifx\p_prefix\s!unknown
+ % forget about it
+ \else
+ \let\referenceprefix\p_prefix
+ \fi}
+
+\appendtoks
+ \setupreferenceprefix[\referencingparameter\c!prefix]
+\to \everysetupreferencing
+
+%D We can typeset a reference using \type {\in}, \type {\at} and \type {\about} and
+%D goto specific locations using \type {\goto}. The last one does not make that much
+%D sense in a paper document. To complicate things, \PLAIN\ \TEX\ also implements an
+%D \type {\in} but fortunately that one only makes sense in math mode.
+%D
+%D Typesetting the reference is a bit more complicated than one would at first sight
+%D expect. This is due to the fact that we distinguish three (five) alternative
+%D calls:
+%D
+%D \placefigure
+%D [here][three calls]
+%D {Three alternatives reference calls.}
+%D {\startcombination[1*3]
+%D {\framed{\type{ \in }}} {a}
+%D {\framed{\type{ \at }}} {b}
+%D {\framed{\type{\goto}}} {c}
+%D \stopcombination}
+%D
+%D \startbuffer
+%D \in figure[fig:three calls]
+%D \in{figure}[fig:three calls]
+%D \in figure a[fig:three calls]
+%D \in{figure}{a}[fig:three calls]
+%D figure~\in[fig:three calls]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This turns up as:
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D The dual \type {{}} results in a split reference. In a document meant for paper,
+%D one is tempted to use the last (most straightforward) alternative. When a
+%D document is also meant voor electronic distribution, the former alternatives have
+%D preference, because everything between the \type {\in} and~\type {[} becomes
+%D active (and when asked for, typeset in a different color and typeface).
+
+\appendtoks
+ % likely math
+ \ifdefined\in \aliased\let\normalmathin \in \permanent\protected\def\in {\mathortext\normalmathin \strc_references_in } \else \aliased\let\in \strc_references_in \fi
+ \ifdefined\over \aliased\let\normalmathover \over \permanent\protected\def\over {\mathortext\normalmathover \strc_references_about} \else \aliased\let\over \strc_references_about \fi
+ %
+ % \ifdefined\at \aliased\let\normalmathat \at \permanent\protected\def\at {\mathortext\normalmathat \strc_references_at } \else \aliased\let\at \strc_references_at \fi
+ % \ifdefined\about \aliased\let\normalmathabout\about \permanent\protected\def\about{\mathortext\normalmathabout\strc_references_about} \else \aliased\let\about\strc_references_about \fi
+ % \ifdefined\from \aliased\let\normalmathfrom \from \permanent\protected\def\from {\mathortext\normalmathfrom \strc_references_from } \else \aliased\let\from \strc_references_from \fi
+ \aliased\let\at \strc_references_at
+ \aliased\let\about\strc_references_about
+ \aliased\let\from \strc_references_from
+\to \everydump
+
+\permanent \def\filterreference #key{\clf_filterreference{#key}} % no checking, expandable
+\permanent\protected\def\getreferenceentry#key{\clf_filterreference{#key}} % no checking, not expandable
+
+\permanent\def\currentreferencenumber {\clf_filterreference{number}}
+\permanent\def\currentreferencepage {\clf_filterreference{page}}
+\permanent\def\currentreferencetitle {\clf_filterreference{title}}
+\permanent\def\currentreferencetext {\clf_filterreference{text}}
+%permanent\def\currentreferencedefault {\clf_filterreference{default}}
+\permanent\def\currentreferencerealpage{\clf_filterreference{realpage}}
+
+%D The most straightforward way of retrieving references is using \type {\ref}.
+
+\permanent\tolerant\protected\def\getreference[#key]#spacer[#label]% #key = number page title text default realpage ...
+ {\ifarguments\or
+ \strc_references_dummy
+ \else
+ \doifelsereferencefound{#label}{\clf_filterreference{#key}}\strc_references_dummy
+ \fi}
+
+\aliased\let\ref\getreference
+
+%D Special cases:
+
+\permanent\protected\def\strc_references_about[#label]%
+ {\dontleavehmode
+ \begingroup
+ \enforced\let\crlf\space
+ \enforced\let\\\space
+ \postponenotes % might go
+ \referencingparameter\c!left
+ \doifelsereferencefound{#label}
+ {\goto{\limitatetext\currentreferencetitle{\referencingparameter\c!width}\unknown}[#label]}% not so efficient (dup lookup)
+ {}% todo
+ \flushnotes % might go
+ \referencingparameter\c!right
+ \endgroup}
+
+%D The previously discussed setup macro lets us specify the representation of
+%D references. A symbol reference does not show the specific data, like the number
+%D of a figure, but shows one of: \hbox {$^\goforwardcharacter$
+%D $^\gobackwardcharacter$ $^\gonowherecharacter$}, depending on the direction to
+%D go.
+%D
+%D \starttyping
+%D ... \somewhere{backward text}{forward text}[someref] ...
+%D ... \atpage[someref] ...
+%D \stoptyping
+
+% standard detail
+%
+% 0 = unknown unknown
+% 1 = same on same page
+% 2 = before preceding page
+% 3 = after following page
+%
+% 4 = above above on same page
+% 5 = below below on same page
+
+% todo: optimize for use in pagebody
+% todo: maybe make it optional
+
+% \setuppagenumbering[alternative=doublesided]
+% \setupreferencing [doublesided=no] % yes is default
+%
+% \somewhere{backward}{foreward}[label]
+% \someplace{preceding}{backward}{current}{foreward}{following}[label]
+% \atpage[#label]
+% \doifcheckedpagestate{label}{preceding}{backward}{current}{foreward}{following}{otherwise}
+%
+% \dorecurse {20} {
+% \placefigure[here][fig:#1]{}{\externalfigure[dummy]}
+% \dorecurse {20} {
+% ##1: \atpage[fig:##1] /
+% \doifcheckedpagestate
+% {fig:##1}
+% {preceding}{backward}{current}{foreward}{following}
+% {otherwise}
+% }
+% }
+
+\newcount \nofreferencestates
+\newconditional\pagestatespread
+
+\appendtoks
+ \doifelse{\referencingparameter\c!doublesided}\v!yes\settrue\setfalse\pagestatespread
+\to \everysetupreferencing
+
+\setupreferencing
+ [\c!doublesided=\v!yes]
+
+\permanent\def\referencepagestate
+ {\numexpr\clf_referencepagestate
+ {rst::\number\nofreferencestates}%
+ \relax}
+
+\permanent\def\referencepagedetail
+ {\numexpr\clf_referencepagedetail
+ {rst::\number\nofreferencestates}%
+ true %
+ \ifconditional\pagestatespread false\ifdoublesided true\else false\fi\fi
+ \relax}
+
+\permanent\def\referencerealpage {\clf_referencerealpage} % todo: no need for wrapping
+\permanent\def\referencecolumnnumber{\clf_referencecolumn} % todo: no need for wrapping
+
+% So we need : instead of \ but that's only lmtx:
+%
+% \def\referencecolumnnumber
+% {\numexpr\dimexpr\clf_referenceposx-\cutspace\relax:\dimexpr\makeupwidth/\nofcolumns\relax+\plusone\relax}
+%
+% Tacos patch of the older one, kept here as illustration
+%
+% \def\referencecolumnnumber
+% {\numexpr
+% \dimexpr\clf_referenceposx-\cutspace-\makeupwidth/(2*\nofcolumns)\relax
+% /\dimexpr \makeupwidth/ \nofcolumns \relax
+% +\plusone
+% \relax}
+
+
+\permanent\protected\def\tracedpagestate
+ {{\blue\tttf(\ifcase\referencepagedetail unknown\or same\or previous\or next\or above\or below\else unknown\fi)}}
+
+\permanent\protected\def\markreferencepage
+ {\dontleavehmode\begingroup
+ \iftrialtypesetting
+ % issue warning that not stable
+ \else
+ % needs checking ... but probably never in trialmode
+ \global\advance\nofreferencestates\plusone
+ \xypos{rst::\number\nofreferencestates}%
+ % \tracedpagestate
+ \fi
+ \endgroup}
+
+\permanent\protected\def\doifcheckedpagestate#label% #preceding#backward#current#foreward#following#otherwise%
+ {\doifelsereferencefound{#label}\strc_references_handle_page_state_yes\strc_references_handle_page_state_nop}
+
+\let\strc_references_handle_page_state_nop\sixthofsixarguments
+
+\def\strc_references_handle_page_state_yes
+ {\markreferencepage
+ \ifcase\referencepagedetail
+ \expandafter\sixthofsixarguments \or
+ \expandafter\thirdofsixarguments \or
+ \expandafter\firstofsixarguments \or
+ \expandafter\fifthofsixarguments \or
+ \expandafter\secondofsixarguments\or
+ \expandafter\fourthofsixarguments\else
+ \expandafter\sixthofsixarguments \fi}
+
+\permanent\protected\def\referencesymbol
+ {\hpack\bgroup
+ \strut
+ \markreferencepage
+ \high
+ {\setupsymbolset[\interactionparameter\c!symbolset]%
+ \symbol[\ifcase\referencepagedetail\v!somewhere\or\v!nowhere\or\v!previous\or\v!next\or\v!previous\or\v!next\else\v!somewhere\fi]}%
+ \egroup}
+
+%D Hereafter the \type {\ignorespaces} binds the state node to next character (more likely
+%D than a preceding one) and one can always add an explicit space.
+
+\permanent\protected\def\somewhere#backward#foreward#dummy[#label]% #dummy gobbles space around #foreward
+ {\doifcheckedpagestate{#label}%
+ {\goto{#backward}[#label]}%
+ {\goto{#backward}[#label]}%
+ {\ignorespaces}%
+ {\goto{#foreward}[#label]}%
+ {\goto{#foreward}[#label]}%
+ {#label}}%
+
+\permanent\protected\def\someplace#preceding#backward#current#foreward#following#dummy[#label]% #dummy gobbles space around #foreward
+ {\doifcheckedpagestate{#label}%
+ {\doifelsenothing{#preceding}{\goto{#preceding}[#label]}\ignorespaces}%
+ {\doifelsenothing {#backward}{\goto {#backward}[#label]}\ignorespaces}%
+ {\doifelsenothing {#current}{\goto {#current}[#label]}\ignorespaces}%
+ {\doifelsenothing {#foreward}{\goto {#foreward}[#label]}\ignorespaces}%
+ {\doifelsenothing{#following}{\goto{#following}[#label]}\ignorespaces}%
+ {#label}}
+
+\permanent\protected\def\atpage[#label]% todo
+ {\doifcheckedpagestate{#label}%
+ {\goto{\labeltext\v!precedingpage}[#label]}%
+ {\goto{\labeltext\v!hencefore}[#label]}%
+ {\ignorespaces}%
+ {\goto{\labeltext\v!hereafter}[#label]}%
+ {\goto{\labeltext\v!followingpage}[#label]}%
+ {\goto{\labeltexts\v!page\strc_references_dummy}[#label]}}
+
+% Someone requested this but in retrospect didn't need it so we keep it as example.
+% Beware: a node is injected which is why we add ignorespaces!
+%
+% \protected\def\strc_references_conditional#action#text[#condition]#dummy[#label]%
+% {\doifcheckedpagestate{#label}%
+% {\doifelse{#condition}\v!precedingpage{#action{#text}[#label]}\ignorespaces}%
+% {\doifelse{#condition}\v!hencefore {#action{#text}[#label]}\ignorespaces}%
+% {\doifelse{#condition}\v!current {#action{#text}[#label]}\ignorespaces}%
+% {\doifelse{#condition}\v!hereafter {#action{#text}[#label]}\ignorespaces}%
+% {\doifelse{#condition}\v!followingpage{#action{#text}[#label]}\ignorespaces}%
+% {#label}}
+%
+% \protected\def\conditionalat {\strc_references_conditional\at}
+% \protected\def\conditionalin {\strc_references_conditional\in}
+% \protected\def\conditionalabout{\strc_references_conditional\about}
+
+%D The other alternatives just conform their names: only the label, only the text, or the
+%D label and the text.
+
+% \dounknownreference -> \dummyreference
+
+\permanent\def\symbolreference[#label]% for old times sake
+ {\goto{\referencesymbol}[#label]}
+
+% \referencecontentmode 0=all 1=label 2=text 3=symbol
+
+\newtoks\leftreferencetoks
+\newtoks\rightreferencetoks
+\newtoks\defaultleftreferencetoks
+\newtoks\defaultrightreferencetoks
+
+\permanent\let\rightofreferencecontent\empty
+\permanent\let\leftofreference \empty
+\permanent\let\rightofreference \empty
+
+\permanent\protected\def\leftofreferencecontent
+ {\removeunwantedspaces
+ \nonbreakablespace
+ \ignorespaces}
+
+\installcorenamespace{referencinginteraction}
+
+\def\strc_references_interaction_all
+ {\the\leftreferencetoks
+ \doifelsesometoks\leftreferencetoks \leftofreferencecontent \donothing
+ \leftofreference
+ \doifelsesometoks\leftreferencetoks\onlynonbreakablespace\relax % new, replace space by nonbreakable if present
+ \currentreferencecontent
+ \rightofreference
+ \doifelsesometoks\rightreferencetoks\rightofreferencecontent\donothing
+ \the\rightreferencetoks}
+
+\letvalue{\??referencinginteraction\v!all}\strc_references_interaction_all
+
+\setvalue{\??referencinginteraction\v!label}%
+ {\leftofreference
+ \the\leftreferencetoks
+ \the\rightreferencetoks
+ \rightofreference}
+
+\setvalue{\??referencinginteraction\v!text}%
+ {\leftofreference
+ \currentreferencecontent
+ \rightofreference}
+
+\setvalue{\??referencinginteraction\v!symbol}%
+ {\referencesymbol}
+
+\permanent\def\referencesequence
+ {\ifcsname\??referencinginteraction\referencingparameter\c!interaction\endcsname
+ \expandafter\lastnamedcs
+ \else
+ \expandafter\strc_references_interaction_all
+ \fi}
+
+\newtoks\everyresetinatreference
+
+\appendtoks
+ \enforced\glet\leftofreference \relax
+ \enforced\glet\rightofreference\relax
+\to \everyresetinatreference
+
+\def\strc_references_start_goto
+ {\dontleavehmode
+ \begingroup}
+
+\def\strc_references_stop_goto
+ {\the\everyresetinatreference
+ \endgroup}
+
+% NEEDS TESTING:
+
+\tolerant\def\strc_references_pickup_goto#=#=#:#*[#3]%
+ {\leftreferencetoks
+ \ifparameter#1\or
+ {#1}%
+ \else
+ \defaultleftreferencetoks
+ \enforced\let\leftofreferencecontent\empty
+ \fi
+ \rightreferencetoks
+ \ifparameter#2\or
+ {#2}%
+ \else
+ \defaultrightreferencetoks
+ \enforced\let\rightofreferencecontent\empty
+ \fi
+ % inefficient: double resolve
+ \doifelsereferencefound{#3} % we need to resolve the text
+ {\goto{\referencesequence}[#3]}
+ {\let\currentreferencecontent\strc_references_dummy
+ \goto{\referencesequence}[#3]}%
+ \strc_references_stop_goto}
+
+\permanent\protected\def\strc_references_in
+ {\strc_references_start_goto
+ \let\currentreferencecontent\currentreferencedefault
+ \strc_references_pickup_goto}
+
+\permanent\protected\def\strc_references_at
+ {\strc_references_start_goto
+ \let\currentreferencecontent\currentreferencepage
+ \strc_references_pickup_goto}
+
+%D \macros
+%D {definereferenceformat}
+%D
+%D The next few macros were made for for David Arnold and Taco Hoekwater. They can
+%D be used for predefining reference texts, and thereby stimulate efficiency.
+%D
+%D \starttyping
+%D \definereferenceformat[informula] [left=(,right=),text=formula]
+%D \definereferenceformat[informulas] [left=(,right=),text=formulas]
+%D \definereferenceformat[andformula] [left=(,right=),text=and]
+%D \definereferenceformat[andformulas][left=(,right=),text=and]
+%D
+%D \informula [b] and \informula [for:c]
+%D the \informula {formulas}[b] \informula {and} [for:c]
+%D the \informulas {formulas}[b] \informula {and} [for:c]
+%D the \informulas [b] \informula {en} [for:c]
+%D the \informulas [b] \andformula [for:c]
+%D \stoptyping
+%D
+%D Instead of a text, one can specify a label, which should be defined with \type
+%D {\setuplabeltext}.
+%D
+%D Watch out: the second argument is somewhat special and mostly meant for a suffix
+%D to a number:
+%D
+%D \startbuffer
+%D \definereferenceformat [intesta] [left=(,right=),text=Whatever~]
+%D \definereferenceformat [intestb] [left=(,right=),label=figure]
+%D
+%D \placeformula[x]\startformula a \stopformula
+%D
+%D \starttabulate[|||||]
+%D \NC \in [x] \NC \in {left}[x] \NC \in {}{right}[x] \NC \in {left}{right}[x] \NC \NR
+%D \NC \intesta[x] \NC \intesta{left}[x] \NC \intesta{}{right}[x] \NC \intesta{left}{right}[x] \NC \NR
+%D \NC \intestb[x] \NC \intestb{left}[x] \NC \intestb{}{right}[x] \NC \intestb{left}{right}[x] \NC \NR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+% to be done: interfaced
+
+\installcorenamespace{referenceformat}
+
+\installcommandhandler \??referenceformat {referenceformat} \??referenceformat
+
+\appendtoks
+ \setuevalue\currentreferenceformat{\strc_references_apply_format{\currentreferenceformat}}%
+\to \everydefinereferenceformat
+
+\setupreferenceformat
+ [\c!left=,
+ \c!right=,
+ \c!text=,
+ \c!label=,
+ \c!autocase=\v!no,
+ \c!style=,
+ \c!type=default, % to be done: interfaced
+ \c!setups=,
+ \c!color=]
+
+\protected\def\strc_references_apply_format#name%
+ {\strc_references_start_goto
+ \edef\currentreferenceformat{#name}%
+ \enforced\gdef\leftofreference {\referenceformatparameter\c!left }%
+ \enforced\gdef\rightofreference {\referenceformatparameter\c!right }%
+ \edef\currentreferenceformatlabel {\referenceformatparameter\c!label }%
+ \edef\currentreferenceformattype {\referenceformatparameter\c!type }%
+ \edef\currentreferenceformatsetups {\referenceformatparameter\c!setups }%
+ \edef\currentreferenceformatautocase{\referenceformatparameter\c!autocase}%
+ \usereferenceformatstyleandcolor\c!style\c!color
+ \ifempty\currentstyleparameter \else
+ \resetinteractionparameter\c!style
+ \fi
+ \ifempty\currentcolorparameter \else
+ \resetinteractionparameter\c!contrastcolor
+ \resetinteractionparameter\c!color
+ \fi
+ \ifx\currentreferenceformatlabel\wildcardsymbol
+ \edef\currentreferenceformatlabel{\autoreferencelabeltext}%
+ \fi
+ \ifx\currentreferenceformatautocase\v!yes
+ \setcharactercleaning[1]%
+ \fi
+ \ifempty\currentreferenceformatlabel
+ \defaultleftreferencetoks {\referenceformatparameter\c!text}%
+ \defaultrightreferencetoks\emptytoks
+ \else
+ \defaultleftreferencetoks {\leftlabeltext \currentreferenceformatlabel}%
+ \defaultrightreferencetoks{\rightlabeltext\currentreferenceformatlabel}%
+ \fi
+ \ifempty\currentreferenceformattype
+ \def\currentreferenceformattype{default}%
+ \fi
+ %
+ \ifempty\currentreferenceformatsetups
+ \def\currentreferencecontent{\filterreference\currentreferenceformattype}%
+ \else
+ \def\currentreferencecontent{\directsetup\currentreferenceformatsetups}%
+ \fi
+ %
+ \enforced\let\leftofreferencecontent \empty
+ \enforced\let\rightofreferencecontent\empty
+ \strc_references_pickup_goto}
+
+\permanent\protected\def\autoreferencelabeltext
+ {\clf_getcurrentreferencemetadata{name}}
+
+% \starttext
+% \definereferenceformat[inxx] [left=(,right=),text=txt]
+% \setupinteraction[state=start]
+% \chapter[one]{xx}
+% [\goto{state}[file(mk-last-state)]]
+% [\goto{state} [file(mk-last-state)]]
+% [\at{page} [one]]
+% [\at{page}[one]]
+% [\at{page}{okay}[one]]
+% [\inxx{a}{b}[one]]
+% \stoptext
+
+% \startsetups referenceformat:numberplustext
+% \filterreference{number}, \filterreference{title}
+% \stopsetups
+%
+% \definereferenceformat[hellup][text=Hellup ,setups=referenceformat:numberplustext]
+
+%D In interactive documents going to a specific location is not bound to cross
+%D references. The \type {\goto} commands can be used to let users access another
+%D part of the document. In this respect, interactive tables of contents and
+%D registers can be considered goto's. Because in fact a \type {\goto} is just a
+%D reference without reference specific data, the previous macros are implemented
+%D using the goto functionality.
+%D
+%D \showsetup{goto}
+%D
+%D One important characteristic is that the first argument of \type {\goto} (and
+%D therefore \type {\at} and \type {\in} is split at spaces. This means that,
+%D although hyphenation is prevented, long references can cross line endings.
+
+% \starttext
+% \setupinteraction[state=start]
+% [\goto{state}[file(mk-last-state)]]
+% [\goto{state} [file(mk-last-state)]]
+% \stoptext
+
+\newconditional\uselocationstrut \settrue\uselocationstrut
+
+\permanent\def\extrareferencearguments
+ {highlight \luaconditional\highlighthyperlinks\space
+ newwindow \luaconditional\gotonewwindow\space
+ layer {\currentviewerlayer}}
+
+\permanent\protected\def\directgoto
+ {\ifconditional\uselocationstrut
+ \expandafter\strc_references_direct_goto
+ \else
+ \expandafter\strc_references_direct_goto_htdp
+ \fi}
+
+\permanent\protected\def\goto
+ {\ifconditional\uselocationstrut
+ \expandafter\strc_references_goto
+ \else
+ \expandafter\strc_references_goto_htdp
+ \fi}
+
+% The unbox trick is needed in order to permit \par inside a reference. Otherwise
+% the reference attribute migrates to the outer boxes.
+
+\newcount\lastsavedreferenceattribute
+
+\newbox\referencebox
+
+\permanent\def\revivesavedreferenceattribute % sometimes handy as no test etc needed
+ {\c_attr_reference\lastsavedreferenceattribute}
+
+\def\strc_references_direct_goto#content[#label]% no test for valid references
+ {\dontleavehmode
+ \begingroup
+ \c_attr_reference\attributeunsetvalue
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \iflocation
+ \clf_injectreference
+ {\referenceprefix}%
+ {#label}%
+ {%
+ height \ht\strutbox
+ depth \dp\strutbox
+ \extrareferencearguments
+ }%
+ \relax
+ \setlocationattributes
+ \setstrut % can be option
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \c_attr_reference\lastreferenceattribute
+ \dostarttagged\t!link\empty % not here
+ #content%
+ \dostoptagged
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\def\strc_references_direct_goto_htdp#content[#label]% no test for valid references
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_injectreference
+ {\referenceprefix}%
+ {#label}%
+ {%
+ height \dimexpr\interactionparameter\c!height\relax
+ depth \dimexpr\interactionparameter\c!depth \relax
+ \extrareferencearguments
+ }%
+ \relax
+ \setlocationattributes
+ \c_attr_reference\lastreferenceattribute
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ #content%
+ \dostoptagged
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\def\strc_references_goto#content#dummy[#label]% #dummy gobbles spaces
+ {\dontleavehmode
+ \begingroup
+ %\setbox\referencebox\hbox\bgroup % experiment, might change again to non \par support
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}%
+ {\expandtexincurrentreference
+ \clf_injectcurrentreferencehtdp
+ \ht\strutbox
+ \dp\strutbox
+ \relax
+ \setlocationattributes
+ \setstrut % can be option
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \c_attr_reference\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ #content%
+ \dostoptagged}%
+ {#content}%
+ \else
+ #content%
+ \fi
+ %\egroup\unhbox\referencebox}
+ \endgroup}
+
+\def\strc_references_goto_internal#content#dummy[#internal]% #dummy gobbles spaces
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \setstrut % can be option
+ \strc_references_get_simple_reference{#internal}%
+ \global\lastsavedreferenceattribute\currentreferenceattribute
+ \c_attr_reference\currentreferenceattribute
+ \setlocationattributes
+ \dostarttagged\t!link\empty
+ #content%
+ \dostoptagged
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\startgoto[#label]%
+ {\dontleavehmode
+ \begingroup
+ \iflocation
+ \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}%
+ {\expandafter\strc_references_start_goto_yes}%
+ {\expandafter\strc_references_start_goto_nop}%
+ \else
+ \expandafter\strc_references_start_goto_nop
+ \fi}
+
+\aliased\let\stopgoto\relax
+
+\permanent\protected\def\strc_references_start_goto_nop
+ {\enforced\let\stopgoto\strc_references_stop_goto_nop}
+
+\permanent\protected\def\strc_references_stop_goto_nop
+ {\endgroup}
+
+\protected\def\strc_references_start_goto_yes
+ {\expandtexincurrentreference
+ \clf_injectcurrentreferencehtdp
+ \ht\strutbox
+ \dp\strutbox
+ \relax
+ \setlocationattributes
+ \setstrut % can be option
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \c_attr_reference\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ \let\stopgoto\strc_references_stop_goto_yes}
+
+\protected\def\strc_references_stop_goto_yes
+ {\dostoptagged
+ \endgroup}
+
+\def\strc_references_goto_htdp#content#dummy[#label]% dummy gobbles spaces
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}%
+ {\expandtexincurrentreference
+ \clf_injectcurrentreferencehtdp
+ \dimexpr\interactionparameter\c!height\relax
+ \dimexpr\interactionparameter\c!depth \relax
+ \relax
+ \setlocationattributes
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \c_attr_reference\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ #content%
+ \dostoptagged}%
+ {#content}%
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\directgotobox#content[#label]% no test for valid references
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_injectreference
+ {\referenceprefix}%
+ {#label}%
+ {\extrareferencearguments}%
+ \relax
+ \setlocationattributes
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ \hbox attr \referenceattribute \lastreferenceattribute {#content}%
+ \dostoptagged
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\directgotospecbox#resolver#content[#label]% no test for valid references
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_injectreference
+ {\referenceprefix}%
+ {#label}%
+ {\extrareferencearguments}%
+ \relax
+ \setlocationcolorspec{#resolver}% no consequence for strut
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ \hbox attr \referenceattribute \lastreferenceattribute {#content}%
+ \dostoptagged
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\directgotodumbbox#content[#label]% no test for valid references
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_injectreference
+ {\referenceprefix}%
+ {#label}%
+ {\extrareferencearguments}%
+ \relax
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ \hbox attr \referenceattribute \lastreferenceattribute {#content}%
+ \dostoptagged
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\gotobox#content[#label]%
+ {\dontleavehmode
+ \begingroup
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \iflocation
+ \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}%
+ {\expandtexincurrentreference
+ \clf_injectcurrentreference
+ \setlocationattributes
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \dostarttagged\t!link\empty
+ \hbox attr \referenceattribute \lastreferenceattribute {#content}%
+ \dostoptagged}%
+ {#content}%
+ \else
+ #content%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\gotowdhtbox#width#height[#label]% fast variant for overlays
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\emptyhbox
+ \wd\scratchbox#width%
+ \ht\scratchbox#height%
+ \global\lastsavedreferenceattribute\attributeunsetvalue
+ \c_attr_reference\attributeunsetvalue
+ \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}%
+ {\clf_injectcurrentreference
+ \global\lastsavedreferenceattribute\lastreferenceattribute
+ \hpack attr \referenceattribute \lastreferenceattribute {\box\scratchbox}}
+ {\box\scratchbox}%
+ \endgroup}
+
+%D An reference to another document can be specified as a file or as an \URL. Both
+%D are handled by the same mechanism and can be issued by saying something like:
+%D
+%D \starttyping
+%D \goto[dictionary::the letter a]
+%D \stoptyping
+%D
+%D One can imagine that many references to such a dictionary are made, so in most
+%D cases such a document reference in an indirect one.
+%D
+%D \showsetup{useexternaldocument}
+%D
+%D For example:
+%D
+%D \starttyping
+%D \useexternaldocument
+%D [dictionary][engldict]
+%D [The Famous English Dictionary]
+%D \stoptyping
+%D
+%D The next macro implements these relations, and also take care of loading the
+%D document specific references.
+%D
+%D The \URL\ alternative takes four arguments:
+%D
+%D \showsetup{useURL}
+%D
+%D like:
+%D
+%D \starttyping
+%D \useURL
+%D [dictionary][http://www.publisher.com/public][engldict]
+%D [The Famous English Dictionary]
+%D \stoptyping
+%D
+%D Several specifications are possible:
+%D
+%D \starttyping
+%D \useURL [id] [url] [file] [description]
+%D \useURL [id] [url] [file]
+%D \useURL [id] [url]
+%D \stoptyping
+%D
+%D This time we don't load the references when no file is specified. This is logical
+%D when one keeps in mind that a valid \URL\ can also be a mail address.
+
+\permanent\tolerant\protected\def\useurl[#label]#spacer[#url]#spacer[#file]#spacer[#description]%
+ {\clf_useurl{#label}{\detokenize{#url}}{\detokenize{#file}}{\detokenize{#description}}}
+
+\permanent\tolerant\protected\def\usefile[#label]#spacer[#file]#spacer[#description]%
+ {\clf_usefile{#label}{\detokenize{#file}}{\detokenize{#description}}}
+
+\aliased\let\useURL \useurl
+\aliased\let\useexternaldocument\usefile
+
+\permanent\def\doifelseurldefined #label{\clf_doifelseurldefined {#label}} % todo: no wrap
+\permanent\def\doifelsefiledefined#label{\clf_doifelsefiledefined{#label}} % todo: no wrap
+
+\aliased\let\doifurldefinedelse \doifelseurldefined
+\aliased\let\doiffiledefinedelse\doifelsefiledefined
+
+%D \macros
+%D {url,setupurl}
+%D
+%D We also have: \type {\url} for directly calling the description. So we can say:
+%D
+%D \starttyping
+%D \useURL [one] [http://www.test.nl]
+%D \useURL [two] [http://www.test.nl] [] [Some Site]
+%D
+%D \url[one] or \from[two] or \goto{Whatever Site}[URL(two)]
+%D \stoptyping
+%D
+%D An \URL\ can be set up with
+%D
+%D \showsetup{setupurl}
+
+\installcorenamespace{url}
+
+\installdirectcommandhandler \??url {url}
+
+\setupurl
+ [\c!style=\v!type,
+ \c!color=]
+
+\permanent\protected\def\url[#label]% move \hyphenatedurl to lua end (is already lua)
+ {\dontleavehmode
+ \begingroup
+ \useurlstyleandcolor\c!style\c!color
+ \hyphenatedurl{\clf_geturl{#label}}%
+ \endgroup}
+
+%D This macro is hooked into a support macro, and thereby \URL's break ok, according
+%D to the setting of a switch,
+%D
+%D \startbuffer
+%D \useURL
+%D [test]
+%D [sentence_sentence~sentence//sentence:sentence.sentence]
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Such an \URL\ is, depending on the settings, hyphenated as:
+%D
+%D \getbuffer
+
+%D When defining the external source of information, one can also specify a suitable
+%D name (the last argument). This name can be called upon with:
+%D
+%D \showsetup{from}
+%D
+%D We keep this for compatibility reasons, hence the hackery.
+
+\permanent\tolerant\protected\def\strc_references_from[#label]%
+ {\dontleavehmode
+ \goto{\clf_from{#label}}[fileorurl(#label)]}
+
+\permanent\def\dofromurldescription#content% called at the lua end
+ {#content}
+
+\permanent\def\dofromurlliteral#content% called at the lua end
+ {\useurlstyleandcolor\c!style\c!color
+ \hyphenatedurl{#content}}
+
+\aliased\let\dofromfiledescription\dofromurldescription
+\aliased\let\dofromfileliteral \dofromurlliteral % maybe some day setupfile that inherits from url
+
+%D We also support:
+%D
+%D \starttyping
+%D \goto{some text}[file(identifier{location}]
+%D \stoptyping
+%D
+%D which is completely equivalent with
+%D
+%D \starttyping
+%D \goto{some text}[identifier::location]
+%D \stoptyping
+
+%D A special case of references are those to programs. These, very system dependant
+%D references are implemented by abusing some of the previous macros.
+%D
+%D \showsetup{setupprograms}
+%D \showsetup{defineprogram}
+%D \showsetup{program} % changed functionality !
+%D
+%D The latter gives access to the description of the program,
+%D being the last argument to the definition command.
+
+% also lua, like urls and files
+
+\installcorenamespace{programs}
+
+\installdirectcommandhandler \??programs {programs}
+
+\permanent\tolerant\protected\def\defineprogram[#name]#spacer[#program]#spacer[#description]%
+ {\clf_defineprogram{#name}{#program}{#description}}
+
+\permanent\protected\def\program[#name]% incompatible, more consistent, hardy used anyway
+ {\dontleavehmode
+ \begingroup
+ \useprogramsstyleandcolor\c!style\c!color
+ \clf_getprogram{#name}%
+ \endgroup}
+
+%D As we can see, we directly use the special reference mechanism, which means that
+%D
+%D \starttyping
+%D \goto{some text}[program(name{args})]
+%D \stoptyping
+%D
+%D is valid.
+
+%D The next macro provides access to the actual pagenumbers. When documenting and
+%D sanitizing the original reference macros, I decided to keep the present meaning
+%D as well as to make this meaning available as a special reference method. So now
+%D one can use:
+%D
+%D \starttyping
+%D \gotopage{some text}[location]
+%D \gotopage{some text}[number]
+%D \gotopage{some text}[file::number]
+%D \stoptyping
+%D
+%D as well as:
+%D
+%D \starttyping
+%D \goto{some text}[page(location)]
+%D \goto{some text}[page(number)]
+%D \goto{some text}[file::page(number)]
+%D \stoptyping
+%D
+%D Here location is a keyword like \type{nextpage}.
+%D
+%D \showsetup{gotopage}
+
+\permanent\tolerant\protected\def\definepage[#name]#spacer[#target]%
+ {\definereference[#name][page(#target)]}
+
+\permanent\protected\def\gotopage#text[#target]%
+ {\goto{#text}[\v!page(#target)]}
+
+%D The previous definitions are somewhat obsolete so we don't use it here.
+
+%D We can cross link documents by using:
+%D
+%D \showsetup{coupledocument}
+%D
+%D like:
+%D
+%D \starttyping
+%D \coupledocument[print][somefile][chapter,section]
+%D \stoptyping
+%D
+%D After which when applicable, we have available the references:
+%D
+%D \starttyping
+%D \goto{print version}[print::chapter]
+%D \stoptyping
+%D
+%D and alike. The title placement definition macros have a key \type {file}, which
+%D is interpreted as the file to jump to, that is, when one clicks on the title.
+
+\permanent\tolerant\protected\def\coupledocument[#name]#spacer[#file]#spacer[#sections]#spacer[#description]%
+ {} % this will be done differently (when it's needed)
+
+%D \macros
+%D {dotextprefix}
+%D
+%D In previous macros we used \type {\dotextprefix} to generate a space between
+%D a label and a number.
+%D
+%D \starttyping
+%D \dotextprefix{text}
+%D \stoptyping
+%D
+%D Only when \type {text} is not empty, a space is inserted.
+
+\permanent\protected\def\dotextprefix#text%
+ {\begingroup
+ \setbox\scratchbox\hbox{#text}% to be solved some day
+ \ifdim\wd\scratchbox>\zeropoint
+ \unhbox\scratchbox
+ \edef\p_separator{\referencingparameter\c!separator}%
+ \ifempty\p_separator \else
+ \removeunwantedspaces % remove is new
+ \p_separator
+ \fi
+ \else
+ \unhbox\scratchbox
+ \fi
+ \endgroup}
+
+%D In the next settings we see some variables that were not used here and that
+%D concern the way the pagenumbers refered to are typeset.
+
+\setupreferencing
+ [\c!state=\v!start,
+ \c!autofile=\v!no,
+ \v!part\c!number=\v!yes,
+ \v!chapter\c!number=\v!no,
+ \c!interaction=\v!all,
+ \c!convertfile=\v!no,
+ %\c!strut=\v!no, % some day an option
+ \c!prefix=,
+ \c!width=.75\makeupwidth,
+ \c!left=\quotation\bgroup,
+ \c!right=\egroup,
+ \c!global=\v!no,
+ \c!expansion=\v!no,
+ \c!separator=\nonbreakablespace,
+ \c!export=\v!no]
+
+\setupprograms
+ [\c!directory=,
+ \c!style=\v!type,
+ \c!color=]
+
+\definereference [\v!CloseDocument ] [action(close)]
+\definereference [\v!ExitViewer ] [action(exit)]
+\definereference [\v!FirstPage ] [action(first)]
+\definereference [\v!LastPage ] [action(last)]
+\definereference [\v!NextJump ] [action(forward)]
+\definereference [\v!NextPage ] [action(next)]
+\definereference [\v!PauseMovie ] [action(pausemovie)]
+\definereference [\v!PauseSound ] [action(pausesound)]
+\definereference [\v!PauseRendering ] [action(pauserendering)]
+\definereference [\v!PreviousJump ] [action(backward)]
+\definereference [\v!PreviousPage ] [action(previous)]
+\definereference [\v!PrintDocument ] [action(print)]
+\definereference [\v!SaveForm ] [action(exportform)]
+\definereference [\v!LoadForm ] [action(importform)]
+\definereference [\v!ResetForm ] [action(resetform)]
+\definereference [\v!ResumeMovie ] [action(resumemovie)]
+\definereference [\v!ResumeSound ] [action(resumesound)]
+\definereference [\v!ResumeRendering ] [action(resumerendering)]
+\definereference [\v!SaveDocument ] [action(save)]
+\definereference [\v!SaveNamedDocument] [action(savenamed)]
+\definereference [\v!OpenNamedDocument] [action(opennamed)]
+\definereference [\v!SearchDocument ] [action(search)]
+\definereference [\v!SearchAgain ] [action(searchagain)]
+\definereference [\v!StartMovie ] [action(startmovie)]
+\definereference [\v!StartSound ] [action(startsound)]
+\definereference [\v!StartRendering ] [action(startrendering)]
+\definereference [\v!StopMovie ] [action(stopmovie)]
+\definereference [\v!StopSound ] [action(stopsound)]
+\definereference [\v!StopRendering ] [action(stoprendering)]
+\definereference [\v!SubmitForm ] [action(submitform)]
+\definereference [\v!ToggleViewer ] [action(toggle)]
+\definereference [\v!ViewerHelp ] [action(help)]
+\definereference [\v!HideField ] [action(hide)]
+\definereference [\v!ShowField ] [action(show)]
+\definereference [\v!GotoPage ] [action(gotopage)]
+\definereference [\v!Query ] [action(query)]
+\definereference [\v!QueryAgain ] [action(queryagain)]
+\definereference [\v!FitWidth ] [action(fitwidth)]
+\definereference [\v!FitHeight ] [action(fitheight)]
+\definereference [\v!ShowThumbs ] [action(thumbnails)]
+\definereference [\v!ShowBookmarks ] [action(bookmarks)]
+
+\definereference [\v!HideLayer ] [action(hidelayer)]
+\definereference [\v!VideLayer ] [action(videlayer)]
+\definereference [\v!ToggleLayer ] [action(togglelayer)]
+
+\definereference [\v!firstpage] [page(firstpage)]
+\definereference [\v!previouspage] [page(previouspage)]
+\definereference [\v!nextpage] [page(nextpage)]
+\definereference [\v!lastpage] [page(lastpage)]
+\definereference [\v!forward] [page(forward)]
+\definereference [\v!backward] [page(backward)]
+\definereference [\v!firstsubpage] [page(firstsubpage)]
+\definereference [\v!previoussubpage] [page(previoussubpage)]
+\definereference [\v!nextsubpage] [page(nextsubpage)]
+\definereference [\v!lastsubpage] [page(lastsubpage)]
+
+% we can do this but only when later in resolve (else problems with \chapter[first]{...}
+%
+% \definereference [\v!first] [page(firstpage)]
+% \definereference [\v!previous] [page(prevpage)]
+% \definereference [\v!next] [page(nextpage)]
+% \definereference [\v!last] [page(lastpage)]
+% \definereference [\v!first\v!sub] [page(firstsubpage)]
+% \definereference [\v!previous\v!sub] [page(prevsubpage)]
+% \definereference [\v!next\v!sub] [page(nextsubpage)]
+% \definereference [\v!last\v!sub] [page(lastsubpage)]
+
+%D We cannot set up buttons (not yet, this one calls a menu macro):
+
+%D New (and experimental):
+
+% \starttext
+% \chapter{test}
+% \placefigure[here][xx:1]{}{\framed{one}} \placefigure[here][xx:2]{}{\framed{three}}
+% \placetable [here][xx:3]{}{\framed{two}} \placetable [here][xx:4]{}{\framed{four}}
+% \start
+% \in{fig}[xx:1] and \in{fig}[xx:2] \in{tab}[xx:3] and \in{tab}[xx:4]
+% \stop \blank \start
+% \setupreferencestructureprefix[default][prefix=no]
+% \in{fig}[xx:1] and \in{fig}[xx:2] \in{tab}[xx:3] and \in{tab}[xx:4]
+% \stop \blank \start
+% \setupreferencestructureprefix[float][default][prefix=no]
+% \in{fig}[xx:1] and \in{fig}[xx:2] \in{tab}[xx:3] and \in{tab}[xx:4]
+% \stop \blank \start
+% \setupreferencestructureprefix[figure][default][prefix=no]
+% \in{fig}[xx:1] and \in{fig}[xx:2] \in{tab}[xx:3] and \in{tab}[xx:4]
+% \stop \blank
+% \stoptext
+
+% todo: parameterhandler
+
+\installcorenamespace{referencingprefix}
+
+\permanent\def\getreferencestructureprefix#kind#name#category% name will change
+ {{%
+ prefix {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefix}%
+ separatorset {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixseparatorset}%
+ conversion {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixconversion}%
+ conversionset {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixconversionset}%
+ starter {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixstarter}%
+ stopper {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixstopper}%
+ set {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixset}%
+ segments {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixsegments}%
+ connector {\referencestructureprefixparameter{#kind}{#name}{#category}\c!prefixconnector}%
+ }%
+ {%
+ separatorset {\referencestructureprefixparameter{#kind}{#name}{#category}\c!numberseparatorset}%
+ conversion {\referencestructureprefixparameter{#kind}{#name}{#category}\c!numberconversion}%
+ conversionset {\referencestructureprefixparameter{#kind}{#name}{#category}\c!numberconversionset}%
+ starter {\referencestructureprefixparameter{#kind}{#name}{#category}\c!numberstarter}%
+ stopper {\referencestructureprefixparameter{#kind}{#name}{#category}\c!numberstopper}%
+ segments {\referencestructureprefixparameter{#kind}{#name}{#category}\c!numbersegments}%
+ }}
+
+\permanent\tolerant\protected\def\setupreferencestructureprefix[#kind]#spacer[#category]#spacer[#settings]%
+ {\ifarguments\or\or
+ \getparameters[\??referencingprefix:#kind][#category]%
+ \else
+ \getparameters[\??referencingprefix#kind:#category][#settings]%
+ \fi}
+
+\permanent\def\referencestructureprefixparameter#kind#name#category#parameter%
+ {\ifcsname\??referencingprefix#name:#category#parameter\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??referencingprefix#kind:#category#parameter\endcsname
+ \lastnamedcs
+ \orelse\ifcsname\??referencingprefix:#category#parameter\endcsname
+ \lastnamedcs
+ \fi}
+
+\permanent\def\currentreferencedefault % for some reason we need to explicitly expand
+ {\normalexpanded{\noexpand\clf_filterdefaultreference
+ {\s!default}%
+ \noexpand\getreferencestructureprefix\clf_getcurrentprefixspec{\s!default}% returns #kind#name#category
+ \relax}}
+
+%D Not all support is visible by looking at the \TEX\ code; here is one of those:^
+%D
+%D \starttyping
+%D \startinteractionmenu[right]
+%D \startbut [section(first {chapter})] first chapter \stopbut
+%D \startbut [section(previous{chapter})] previous chapter \stopbut
+%D \startbut [section(next {chapter})] next chapter \stopbut
+%D \startbut [section(last {chapter})] last chapter \stopbut
+%D \blank[2*big]
+%D \startbut [section(first {section})] first section \stopbut
+%D \startbut [section(previous{section})] previous section \stopbut
+%D \startbut [section(next {section})] next section \stopbut
+%D \startbut [section(last {section})] last section \stopbut
+%D \stopinteractionmenu
+%D \stoptyping
+
+%D Relatively new:
+%D
+%D \starttyping
+%D \chapter{The never ending story}
+%D
+%D \section{An ending story}
+%D
+%D \in{chapter}[match(complex bibliographies)]
+%D \in{chapter}[match(never ending)]
+%D \in{chapter}[match(ending)]
+%D \in{chapter}[match(chapter:never ending)]
+%D \in{chapter}[match(chapter:ending)]
+%D \in{section}[match(section:ending)]
+%D \in{figure}[match(float:mess)]
+%D \in{figure}[match(figure:mess)]
+%D \in{figure (not found)}[match(section:mess)]
+%D \in{figure (not found)}[match(section:xxxx)]
+%D \in{figure}[match(mess)]
+%D
+%D \placefigure{What a mess}{}
+%D
+%D \chapter{About complex bibliographies}
+%D
+%D \in{chapter}[match(complex bibliographies)]
+%D \in{chapter}[match(never ending)]
+%D \in{figure}[match(mess)]
+%D \stoptyping
+
+\protect \endinput
+
+% tricky:
+%
+% \enabletrackers[nodes.references]
+% \setupinteraction[state=start]
+% \def\KnuthTest{\input knuth }
+% \def\KnuthTest{\input tufte }
+% \def\TufteTest{\input tufte }
+% \defineoverlay[xxx][\overlaybutton{page(3)}]
+% \setupbackgrounds[text][background=xxx]
+% \starttext
+% test {\red \KnuthTest} test \par
+% \button{test}[page(1)] \par
+% \goto{page 2 \TeX}[page(2)] \goto{page 2 \TeX}[page(2)] \goto{\TufteTest}[page(2)] test \page
+% test \goto{page 3}[page(3)] \goto{\TufteTest\space\par\TufteTest}[page(4)] test \page
+% \goto{page 1}[page(1)] \goto{\TufteTest\space test}[page(1)] \page
+% \goto{page 1}[page(1)] \goto{\KnuthTest\space test}[page(1)] \page
+% test \goto{page 1}[page(1)] {\goto{\KnuthTest\space test}[page(1)]} test
+% \goto{page 1}[page(1)] \goto{\TufteTest}[page(1)] test \page
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-reg.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-reg.mkxl
new file mode 100644
index 00000000000..22a6614b785
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-reg.mkxl
@@ -0,0 +1,1276 @@
+%D \module
+%D [ file=strc-reg,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Registers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Registers}
+
+\registerctxluafile{strc-reg}{}
+
+\unprotect
+
+\startcontextdefinitioncode
+
+% todo: tag:: becomes rendering
+% todo: language, character, linked, location
+% todo: fonts etc at sublevels (already defined)
+
+% \starttext
+% \placeregister[index]
+% \chapter{a} \index{93} \index{456} \index{***} \index{*} \index{@}
+% test \index{aa} test \startregister[index][x]{bb} test \page test \page test \page test \stopregister[index][x]
+% test \index{aa} test \setregisterentry[index][label=x,entries=bb] test \page test \page test \page test \finishregisterentry[index][label=x]
+% test \index{aa} test \setregisterentry[index][label=y] test \page test \page test \page test \finishregisterentry[index][label=y,entries=yy]
+% \stoptext
+
+% \index {entry}
+% \index[key] {entry}
+% \index[pageclass::] {entry}
+% \index[pageclass::key]{entry}
+% \index {textclass::entry}
+% \index[key] {textclass::entry}
+% \index[pageclass::] {textclass::entry}
+% \index[pageclass::key]{textclass::entry}
+
+% nice example:
+%
+% \setupregister[index]
+% [n=1,
+% pageleft=\hfilll,
+% pageright=\frozen\parfillleftskip 0pt plus 1fill\frozen\parfillrightskip 0pt\par]
+%
+% \starttext
+%
+% {\showmakeup \placeindex \page}
+%
+% % \dorecurse{10}{\dontleavehmode \index{AAA}\page}
+% \dorecurse{100}{\dontleavehmode\index{AAA}\page}
+
+% tzt variant with n entries, parameters and userdata (altnum)
+
+\installcorenamespace{register}
+
+\installcommandhandler\??register {register} \??register
+
+\aliased\let\strc_registers_setup_saved\setupregister
+
+% maybe we should drop the plural form
+
+\let\strc_registers_setup_step\relax
+
+\permanent\tolerant\protected\overloaded\def\setupregister[#1]#*[#2]#*[#3]%
+ {\ifarguments\or
+ \strc_registers_setup_saved[#1]%
+ \or
+ \strc_registers_setup_saved[#1][#2]%
+ \or
+ \def\strc_registers_setup_step##1{\strc_registers_setup_saved[#1:##1][#3]}%
+ \processcommalist[#2]\strc_registers_setup_step
+ \fi}
+
+\permanent\tolerant\protected\def\setupregisters[#1]%
+ {\strc_registers_setup_saved[#1]\relax}
+
+\setupregister
+ [\c!n=2,
+ \c!balance=\v!yes, % \v!no komt niet zo vaak voor
+ \c!align=\v!flushleft,
+ \c!tolerance=\v!stretch,
+ \c!before=\blank,
+ %\c!after=,
+ %\c!symbol=,
+ \c!compress=\v!no,
+ \c!interaction=\v!pagenumber,
+ \c!alternative=\v!a,
+ \c!distance=\emwidth,
+ \c!style=\v!bold,
+ \c!pagestyle=\v!slanted,
+ \c!indicator=\v!yes,
+ \c!criterium=\v!all,
+ \c!check=\v!yes, % check for weird see usage
+ %\c!command=,
+ \c!referencing=\v!on,
+ \c!location=\v!middle,
+ %\c!maxwidth=,
+ \c!number=\v!no,
+ \c!unknownreference=\v!empty,
+ \c!prefix=\v!both,
+ %\c!expansion=,
+ %\c!xmlsetup=,
+ \c!pagenumber=\v!yes,
+ \c!pageprefixconnector=\endash,
+ \c!pagesegments=2:2,
+ \c!file=\jobname,
+ %\c!deeptextcommand=, % undefined by default !
+ \c!method=, % no default as we have them in the module, maybe some day in lang-*
+ \c!numberorder=\v!numbers, % \v!characters
+ \s!language=\currentmainlanguage]%
+
+% yes or no shared ?
+
+\setupregister
+ [\c!label=,
+ \c!entries=,
+ \c!alternative=]
+
+\definemixedcolumns
+ [\v!register]
+ [\c!define=\v!no,
+ \c!n=\registerparameter\c!n,
+ \c!balance=\registerparameter\c!balance,
+ \c!align=\registerparameter\c!align,
+ \c!tolerance=\registerparameter\c!tolerance]
+
+%D \starttyping
+%D \setupregister[index][1][textcolor=darkred]
+%D \setupregister[index][2][textcolor=darkgreen,textstyle=bold]
+%D
+%D \placeregister[index][n=1] \blank[3*big]
+%D
+%D test \index{test+one} test \index{test+two} more \index{more}
+%D \stoptyping
+
+\mutable\let\currentregistercharacter \empty
+\mutable\let\currentregistercoding \empty
+\mutable\let\currentregisterentries \empty
+\mutable\let\currentregisterentriesa \empty
+\mutable\let\currentregisterentriesb \empty
+\mutable\let\currentregisterentriesc \empty
+\mutable\let\currentregisterexpansion \empty
+\mutable\let\currentregisterkeys \empty
+\mutable\let\currentregisterkeysa \empty
+\mutable\let\currentregisterkeysb \empty
+\mutable\let\currentregisterkeysc \empty
+\mutable\let\currentregisterlabel \empty
+\mutable\let\currentregistermaxwidth \empty
+\mutable\let\currentregistername \empty
+\mutable\let\currentregisternumber \empty
+\mutable\let\currentregisterownnumber \empty
+\mutable\let\currentregisterpageindex \empty
+\mutable\let\currentregisterpagesymbol \empty
+\mutable\let\currentregistersectionindex\empty
+\mutable\let\currentregisterseeindex \empty
+\mutable\let\currentregisterseeword \empty
+\mutable\let\currentregistersynchronize \empty
+\mutable\let\currentregisterxmlsetup \empty
+\mutable\let\currentregisterprocessors \empty
+\mutable\let\currentregisterprocessorsa \empty
+\mutable\let\currentregisterprocessorsb \empty
+\mutable\let\currentregisterprocessorsc \empty
+
+\newconditional\c_strc_registers_defining
+\setnewconstant\c_strc_registers_maxlevel \plusfive
+
+\ifdefined\Word \else \protected\def\Word#1{#1} \fi
+
+\appendtoks
+ \ifconditional\c_strc_registers_defining \else % todo: dosingle ...
+ \settrue\c_strc_registers_defining
+ \definemixedcolumns[\currentregister][\v!register]% first as otherwise it overloads start/stop
+ \clf_defineregister{\currentregister}{\registerparameter\c!referencemethod}%
+ \normalexpanded{\presetheadtext[\currentregister=\Word{\currentregister}]}%
+ \frozen\instance\setuevalue{\currentregister}{\strc_registers_insert_entry[\currentregister]}%
+ \frozen\instance\setuevalue{\e!see\currentregister}{\strc_registers_insert_see[\currentregister]}%
+ %frozen\instance\setuevalue{\e!coupled\currentregister}{\dolinkedregister{\currentregister}}%
+ % historic ballast
+ \frozen\instance\setuevalue{\e!place\currentregister}{\placeregister[\currentregister]}%
+ \frozen\instance\setuevalue{\e!complete\currentregister}{\completeregister[\currentregister]}%
+ \frozen\instance\setuevalue{\e!setup\currentregister\e!endsetup}{\setupregister[\currentregister]}%
+ \dorecurse\c_strc_registers_maxlevel{% weird, expanded should not be needed
+ \normalexpanded{\defineregister[\currentregister:\recurselevel][\currentregister]}%
+ %\defineregister[\currentregister:\recurselevel][\currentregister]%
+ \letregisterparameter{\c!entries:\recurselevel}\empty % needed as we use detokenize (ok, we can
+ \letregisterparameter{\c!keys :\recurselevel}\empty % avoid it, but it's faster too)
+ }%
+ %
+ \setfalse\c_strc_registers_defining
+ \fi
+\to \everydefineregister
+
+\appendtoks
+ \clf_setregistermethod{\currentregister}{\registerparameter\c!referencemethod}%
+\to \everysetupregister
+
+%D Registering:
+
+\glet\currentregistername \empty
+\glet\currentregisternumber\!!zerocount
+
+\def\strc_registers_register_page_entry
+ {\iftrialtypesetting
+ \expandafter\gobblethreearguments
+ \else
+ \expandafter\strc_registers_register_page_entry_indeed
+ \fi}
+
+\def\strc_registers_register_page_expand_xml_entries
+ {\xmlstartraw
+ \xdef\currentregisterentriesa{\registerparameter{\c!entries:1}}%
+ \xdef\currentregisterentriesb{\registerparameter{\c!entries:2}}%
+ \xdef\currentregisterentriesc{\registerparameter{\c!entries:3}}%
+ \xmlstopraw
+ \glet\currentregistercoding\s!xml}
+
+\def\strc_registers_register_page_expand_yes_entries
+ {\xdef\currentregisterentriesa{\registerparameter{\c!entries:1}}%
+ \xdef\currentregisterentriesb{\registerparameter{\c!entries:2}}%
+ \xdef\currentregisterentriesc{\registerparameter{\c!entries:3}}%
+ \glet\currentregistercoding\s!tex}
+
+\def\strc_registers_register_page_expand_nop_entries
+ {\xdef\currentregisterentriesa{\detokenizedregisterparameter{\c!entries:1}}%
+ \xdef\currentregisterentriesb{\detokenizedregisterparameter{\c!entries:2}}%
+ \xdef\currentregisterentriesc{\detokenizedregisterparameter{\c!entries:3}}%
+ \glet\currentregistercoding\s!tex}
+
+\def\strc_registers_register_page_expand_xml
+ {\xmlstartraw
+ \xdef\currentregisterentries{\registerparameter\c!entries}%
+ \xmlstopraw
+ \glet\currentregistercoding\s!xml}
+
+\def\strc_registers_register_page_expand_yes
+ {\xdef\currentregisterentries{\registerparameter\c!entries}%
+ \glet\currentregistercoding\s!tex}
+
+\def\strc_registers_register_page_expand_nop
+ {\xdef\currentregisterentries{\detokenizedregisterparameter\c!entries}%
+ \glet\currentregistercoding\s!tex}
+
+\def\strc_registers_register_page_expand_xml_keys
+ {\xmlstartraw
+ \xdef\currentregisterkeysa{\registerparameter{\c!keys:1}}%
+ \xdef\currentregisterkeysb{\registerparameter{\c!keys:2}}%
+ \xdef\currentregisterkeysc{\registerparameter{\c!keys:3}}%
+ \xmlstopraw}
+
+\def\strc_registers_register_page_expand_yes_keys
+ {\xdef\currentregisterkeysa{\registerparameter{\c!keys:1}}%
+ \xdef\currentregisterkeysb{\registerparameter{\c!keys:2}}%
+ \xdef\currentregisterkeysc{\registerparameter{\c!keys:3}}}
+
+\def\strc_registers_register_page_expand_processors
+ {\xdef\currentregisterprocessorsa{\registerparameter{\c!processors:1}}%
+ \xdef\currentregisterprocessorsb{\registerparameter{\c!processors:2}}%
+ \xdef\currentregisterprocessorsc{\registerparameter{\c!processors:3}}}
+
+\def\strc_registers_register_page_entry_indeed#1#2#3% register data userdata
+ {\begingroup
+ \edef\currentregister{#1}%
+ %\setupcurrentregister[\c!entries=,\c!label=,\c!keys=,\c!alternative=,#2]%
+ \setupcurrentregister[#2]%
+ \edef\currentregisterlabel {\registerparameter\c!label}%
+ \edef\currentregisterexpansion {\registerparameter\c!expansion}%
+ \edef\currentregisterownnumber {\registerparameter\c!ownnumber}%
+ \xdef\currentregisterkeys {\registerparameter\c!keys}%
+ \xdef\currentregisterentries {\registerparameter\c!entries}%
+ \xdef\currentregisterprocessors{\registerparameter\c!processors}%
+ \xdef\currentregisterxmlsetup {\registerparameter\c!xmlsetup}%
+ \ifempty\currentregisterentries
+ \ifx\currentregisterexpansion\s!xml
+ \strc_registers_register_page_expand_xml_entries
+ \orelse\ifx\currentregisterexpansion\v!yes
+ \strc_registers_register_page_expand_yes_entries
+ \else
+ \strc_registers_register_page_expand_nop_entries
+ \fi
+ \else
+ \ifx\currentregisterexpansion\s!xml
+ \strc_registers_register_page_expand_xml
+ \orelse\ifx\currentregisterexpansion\v!yes
+ \strc_registers_register_page_expand_yes
+ \else
+ \strc_registers_register_page_expand_nop
+ \fi
+ \fi
+ \ifempty\currentregisterkeys
+ \ifx\currentregistercoding\s!xml
+ \strc_registers_register_page_expand_xml_keys
+ \else
+ \strc_registers_register_page_expand_yes_keys
+ \fi
+ \fi
+ \strc_registers_register_page_expand_processors
+ \setnextinternalreference
+ % we could consider storing register entries in a list which we
+ % could then sort
+ \glet\currentregistername\currentregister
+ \xdef\currentregisternumber{\clf_storeregister % 'own' should not be in metadata
+ metadata {%
+ name {\currentregister}%
+ coding {\currentregistercoding}%
+ \ifx\currentregisterownnumber\v!yes
+ own {\registerparameter\c!alternative}% can be used instead of pagenumber
+ \fi
+ \ifx\currentreferencecoding\s!xml
+ xmlroot {\xmldocument} % only useful when text
+ \fi
+ \ifempty\currentregisterxmlsetup \else
+ xmlsetup {\currentregisterxmlsetup}%
+ \fi
+ }%
+ references {%
+ \ifempty\currentregisterlabel \else
+ label {\currentregisterlabel}%
+ \fi
+% view {\interactionparameter\c!focus}%
+ }%
+ entries {%
+ % we need a special one for xml, this is just a single one
+ \ifempty\currentregisterentries
+ entries {
+ {\currentregisterentriesa}%
+ {\currentregisterentriesb}%
+ {\currentregisterentriesc}%
+ }
+ \else
+ entry {\currentregisterentries}%
+ \fi
+ \ifempty\currentregisterkeys
+ keys {
+ {\currentregisterkeysa}%
+ {\currentregisterkeysb}%
+ {\currentregisterkeysc}%
+ }
+ \else
+ key {\currentregisterkeys}%
+ \fi
+ \ifempty\currentregisterprocessors
+ processors {
+ {\currentregisterprocessorsa}%
+ {\currentregisterprocessorsb}%
+ {\currentregisterprocessorsc}%
+ }
+ \else
+ processor {\currentregisterprocessors}%
+ \fi
+ }%
+ userdata {\detokenize\expandafter{\normalexpanded{#3}}}
+ }%
+ \clf_setinternalreference
+ internal \locationcount
+ view {\interactionparameter\c!focus}%
+ \relax % this will change
+ \ifx\currentregisterownnumber\v!yes
+ \glet\currentregistersynchronize\relax
+ \else
+ \xdef\currentregistersynchronize{\clf_deferredenhanceregister{\currentregister}\number\currentregisternumber}%
+ \fi
+ \currentregistersynchronize % here?
+ % needs thinking ... bla\index{bla}. will break before the . but adding a
+ % penalty is also no solution
+ \dostarttagged\t!registerlocation\currentregister
+ \c_attr_destination\lastdestinationattribute
+ \signalcharacter % no \strut as it will be removed during cleanup
+ \dotagregisterlocation
+ \dostoptagged
+ \endgroup}
+
+\permanent\protected\def\dosetfastregisterentry#1#2#3#4#5% register entry key processor processor
+ {\begingroup
+ \edef\currentregister{#1}%
+ \setnextinternalreference
+ \glet\currentregistername\currentregister
+ \xdef\currentregisternumber{\clf_storeregister
+ {%
+ metadata {%
+ name {\currentregister}%
+ }
+ entries {%
+ entry {#2}%
+ key {#3}%
+ }%
+ processors {%
+ entry {#4}%
+ page {#5}%
+ }%
+ }%
+ }%
+ % overlap with the above
+ % \clf_setinternalreference
+ % internal \locationcount
+ % view {\interactionparameter\c!focus}%
+ \relax % this will change
+ \xdef\currentregistersynchronize{\clf_deferredenhanceregister{\currentregister}\number\currentregisternumber}%
+ \currentregistersynchronize % here?
+ \dostarttagged\t!registerlocation\currentregister
+ \c_attr_destination\lastdestinationattribute \signalcharacter % no \strut as it will be removed during cleanup
+ \dotagregisterlocation
+ \dostoptagged
+ \endgroup}
+
+\aliased\let\dotagregisterlocation\relax % experiment
+
+\tolerant\protected\def\strc_registers_insert_entry[#1]#*[#2]%
+ {\def\currentregister{#1}%
+ \edef\p_ownnumber{\registerparameter\c!ownnumber}%
+ \ifx\p_ownnumber\v!yes
+ \expandafter\strc_registers_insert_entry_yes
+ \else
+ \expandafter\strc_registers_insert_entry_nop
+ \fi{#2}}
+
+% \def\strc_registers_insert_entry_nop#1#2%
+% {\doflushatpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!entries={#2}}{}}}
+%
+% \def\strc_registers_insert_entry_yes#1#2#3%
+% {\doflushatpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!alternative=#2,\c!entries={#3}}{}}}
+%
+% less tokens passed (nicer for tracing) .. could become installable
+
+\def\strc_registers_insert_entry_nop
+ {\ifvmode
+ \expandafter\strc_registers_insert_entry_nop_par
+ \else
+ \expandafter\strc_registers_insert_entry_nop_txt
+ \fi}
+
+\def\strc_registers_insert_entry_yes
+ {\ifvmode
+ \expandafter\strc_registers_insert_entry_yes_par
+ \else
+ \expandafter\strc_registers_insert_entry_yes_txt
+ \fi}
+
+\def\strc_registers_insert_entry_nop_par#1#2%
+ {\flushatnextpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!entries={#2}}{}}}
+
+\def\strc_registers_insert_entry_yes_par#1#2#3%
+ {\flushatnextpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!alternative=#2,\c!entries={#3}}{}}}
+
+\def\strc_registers_insert_entry_nop_txt#1#2%
+ {\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!entries={#2}}{}}
+
+\def\strc_registers_insert_entry_yes_txt#1#2#3%
+ {\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!alternative=#2,\c!entries={#3}}{}}
+
+%D This is one of the few commands where a stop has arguments.
+
+\permanent\tolerant\protected\def\startregister[#1]#*[#2]#*[#3]#*[#4]#*#:#5%
+ {\ifparameter#4\or
+ % #1=register #2=tag #3=own #4=sortkey #5=entry
+ \doflushatpar{\strc_registers_register_page_entry{#1}{\c!label=#2,\c!alternative=#3,\c!keys={#4},\c!entries={#5}}{}}%
+ \else
+ % #1=register #2=tag #3=sortkey #5=entry
+ \doflushatpar{\strc_registers_register_page_entry{#1}{\c!label=#2,\c!keys={#3},\c!entries={#5}}{}}%
+ \fi}
+
+\permanent\tolerant\protected\def\stopregister[#1]#*[#2]%
+ {\normalexpanded{\ctxlatecommand{extendregister("#1","#2")}}}
+
+% a synonym, so that we can nest with overlap without syntax check problems
+
+\aliased\let\openregisterrange \startregister
+\aliased\let\closeregisterrange\stopregister
+
+% not yet document, not sure if this will stay:
+
+\permanent\tolerant\protected\def\setregisterentry[#1]#*[#2]#*[#3]%
+ {\doflushatpar{\strc_registers_register_page_entry{#1}{#2}{#3}}}
+
+\permanent\tolerant\protected\def\finishregisterentry[#1]#*[#2]#*[#3]%
+ {\strc_registers_finish_entry_indeed{#1}{#2}{#3}}
+
+\def\strc_registers_finish_entry_indeed#1#2#3% register data userdata
+ {\begingroup
+ \edef\currentregister{#1}%
+ %\setupcurrentregister[\c!entries=,\c!label=,\c!keys=,\c!alternative=,#2]% todo: fast setter
+ \resetregisterparameter\c!entries
+ \resetregisterparameter\c!label
+ \resetregisterparameter\c!keys
+ \resetregisterparameter\c!alternative
+ \setupcurrentregister[#2]%
+ \edef\currentregisterlabel {\registerparameter\c!label}%
+ \edef\currentregisterexpansion{\registerparameter\c!expansion}%
+ \edef\currentregisterownnumber{\registerparameter\c!ownnumber}%
+ \xdef\currentregisterkeys {\registerparameter\c!keys}%
+ \ifx\currentregisterexpansion\s!xml
+ \xmlstartraw
+ \xdef\currentregisterentries{\registerparameter\c!entries}%
+ \xmlstopraw
+ \glet\currentregistercoding\s!xml
+ \else
+ \ifx\currentregisterexpansion\v!yes
+ \xdef\currentregisterentries{\registerparameter\c!entries}%
+ \else
+ \xdef\currentregisterentries{\detokenizedregisterparameter\c!entries}%
+ \fi
+ \glet\currentregistercoding\s!tex
+ \fi
+ % I hate this kind of mess ... but it's a user request.
+ \ifempty\currentregisterentries
+ \normalexpanded{\ctxcommand{extendregister("\currentregister","\currentregisterlabel", {
+ metadata = {
+ \ifx\currentregisterownnumber\v!yes
+ own = "\registerparameter\c!alternative", % can be used instead of pagenumber
+ \fi
+ },
+ userdata = \!!bs\detokenize{#3}\!!es
+ })%
+ }}%
+ \else
+ \normalexpanded{\ctxcommand{extendregister("\currentregister","\currentregisterlabel", {
+ metadata = {
+ % catcodes = \the\catcodetable,
+ coding = "\currentregistercoding",
+ \ifx\currentregisterownnumber\v!yes
+ own = "\registerparameter\c!alternative", % can be used instead of pagenumber
+ \fi
+ },
+ entries = {
+ % we need a special one for xml, this is just a single one
+ \!!bs\currentregisterentries\!!es,
+ \!!bs\currentregisterkeys\!!es
+ },
+ userdata = \!!bs\detokenize{#3}\!!es
+ })
+ }}%
+ \fi
+ \endgroup}
+
+% The following variants are meant for (for instance xml). There is some
+% overlap with previously defined macros.
+%
+% \starttext
+% \setstructurepageregister[index][entries=alpha]a
+% \setstructurepageregister[index][entries=gamma]g
+% \setstructurepageregister[index][entries=beta]b
+% \setstructurepageregister[index][entries:1=alpha,keys:1=z]a
+% \setstructurepageregister[index][entries:1=gamma,keys:1=x]g
+% \setstructurepageregister[index][entries:1=beta, keys:1=y]b
+% \index{alpha}a
+% \index{gamma}g
+% \index{beta}b
+% \placeregister[index][n=1]
+% \stoptext
+
+% some overlap with previous
+
+\permanent\tolerant\protected\def\setstructurepageregister[#1]#*[#2]#*[#3]% [register][settings][userdata]
+ {\doflushatpar{\strc_registers_register_page_entry{#1}{#2}{#3}}}
+
+\permanent\tolerant\protected\def\startstructurepageregister[#1]#*[#2]#*[#3]#*[#4]% [register][tag][settings][userdata]
+ {\doflushatpar{\strc_registers_register_page_entry{#1}{\c!label=#2,#3}{#4}}}
+
+\permanent\tolerant\protected\def\stopstructurepageregister[#1]#*[#2]%
+ {\normalexpanded{\ctxlatecommand{structures.registers.extend("#1","#2")}}}
+
+\aliased\let\openstructurepageregisterrange \startstructurepageregister
+\aliased\let\closestructurepageregisterrange\stopstructurepageregister
+
+% So far.
+
+\tolerant\protected\def\strc_registers_insert_see[#1]#*[#2]#*#:#3#4%
+ {\doflushatpar{\strc_registers_insert_see_indeed{#1}{#2}{#3}{#4}}}
+
+\def\strc_registers_insert_see_indeed#1#2#3#4% register key entry seeword
+ {\begingroup
+ \edef\currentregister{#1}%
+ \edef\currentregisterexpansion{\registerparameter\c!expansion}%
+ \ifx\currentregisterexpansion\s!xml
+ \xmlstartraw
+ \xdef\currentregisterentries{\detokenize{#3}}% not ok yet
+ \xdef\currentregisterseeword{\detokenize{#4}}% not ok yet
+ \xmlstopraw
+ \glet\currentregistercoding\s!xml
+ \else
+ \ifx\currentregisterexpansion\v!yes
+ \xdef\currentregisterentries{#3}% not ok yet
+ \xdef\currentregisterseeword{#4}% not ok yet
+ \else
+ \xdef\currentregisterentries{\detokenize{#3}}% not ok yet
+ \xdef\currentregisterseeword{\detokenize{#4}}% not ok yet
+ \fi
+ \glet\currentregistercoding\s!tex
+ \fi
+ \setnextinternalreference
+ % we could consider storing register entries in list
+ \edef\temp{\clf_storeregister{% \temp grabs the nofentries
+ metadata {%
+ kind {see}%
+ name {\currentregister}%
+ }%
+ references {%
+% view {\interactionparameter\c!focus}%
+ }%
+ entries {%
+ % we need a special one for xml, this is just a single one
+ entry {\currentregisterentries}%
+ key {#2}%
+ }%
+ seeword {%
+ text {\currentregisterseeword}%
+ }%
+ }}%
+ \clf_setinternalreference
+ internal \locationcount
+ view {\interactionparameter\c!focus}%
+ \relax % this will change
+ \dostarttagged\t!registerlocation\currentregister
+ \c_attr_destination\lastdestinationattribute \signalcharacter % no \strut as it will be removed during cleanup
+ \dotagregisterlocation
+ \dostoptagged
+ \endgroup}
+
+%D Rendering:
+
+% todo: c!language ipv s!language
+
+\permanent\def\utilityregisterlength{\listlength} % also in strc-lst (downward compatible name)
+
+% \mutable\let\listlength\!!zerocount % also in strc-lst
+
+\permanent\tolerant\protected\def\determineregistercharacteristics[#1]#*[#2]%
+ {\begingroup
+ \setupregister[#1][#2]%
+ \edef\currentregister{\firstinset{#1}}%
+ \normalexpanded{\endgroup\noexpand\xdef\noexpand\listlength{\clf_analyzeregister
+ {\currentregister}%
+ {%
+ language {\registerparameter\s!language}%
+ method {\registerparameter\c!method}%
+ numberorder {\registerparameter\c!numberorder}%
+ compress {\registerparameter\c!compress}%
+ criterium {\registerparameter\c!criterium}%
+ pagenumber \ifempty\registerpageseparatorsymbol false\else true\fi
+ }%
+ }}%
+ \ifcase\utilityregisterlength\relax
+ \resetsystemmode\v!register
+ \else
+ \setsystemmode \v!register
+ \fi}
+
+\newtoks\everyplaceregister
+
+\appendtoks
+ \dontcomplain
+\to \everyplaceregister
+
+\newconditional\c_strc_registers_text_interaction
+\newconditional\c_strc_registers_page_done
+
+\permanent\tolerant\protected\def\placeregister[#1]#*[#2]%
+ {\ifarguments\else
+ \begingroup
+ %\forgetall
+ \setupregister[#1][#2]% can be a list
+ \edef\currentregister{\firstinset{#1}}%
+ \the\everyplaceregister
+ \ifnum\namedmixedcolumnsparameter\currentregister\c!n>\plusone
+ \startmixedcolumns[\currentregister]%
+ \strc_registers_place_indeed{#1}%
+ \stopmixedcolumns
+ \else
+ \strc_registers_place_indeed{#1}%
+ \fi
+ \endgroup
+ \fi}
+
+\def\strc_registers_place_indeed#1%
+ {\doifelse{\registerparameter\c!interaction}\v!text
+ \settrue\setfalse\c_strc_registers_text_interaction
+ \clf_processregister
+ {#1}%
+ {%
+ language {\registerparameter\s!language}%
+ method {\registerparameter\c!method}%
+ numberorder {\registerparameter\c!numberorder}%
+ check {\registerparameter\c!check}%
+ compress {\registerparameter\c!compress}%
+ criterium {\registerparameter\c!criterium}%
+ pagemethod {\registerparameter\c!pagemethod}%
+ pagenumber \ifempty\registerpageseparatorsymbol false\else true\fi
+ }{%
+ separatorset {\registerparameter\c!pageprefixseparatorset}%
+ conversionset {\registerparameter\c!pageprefixconversionset}%
+ starter {\registerparameter\c!pageprefixstarter}%
+ stopper {\registerparameter\c!pageprefixstopper}%
+ set {\registerparameter\c!pageprefixset}%
+ segments {\registerparameter\c!pageprefixsegments}%
+ connector {\registerparameter\c!pageprefixconnector}%
+ }{%
+ prefix {\registerparameter\c!pageprefix}%
+ separatorset {\registerparameter\c!pageseparatorset}%
+ conversionset {\registerparameter\c!pageconversionset}%
+ starter {\registerparameter\c!pagestarter}%
+ stopper {\registerparameter\c!pagestopper}%
+ segments {\registerparameter\c!pagesegments}%
+ }%
+ \relax}
+
+% \def\strc_registers_limited_entry#1%
+% {\limitatetext{#1}\currentregistermaxwidth\unknown}%
+
+\def\strc_registers_limited_entry#1%
+ {\limitated left \currentregistermaxwidth sentinel {\unknown} text {#1} freeze true\relax}
+
+\aliased\let\limitedregisterentry\firstofoneargument
+
+\appendtoks
+ \edef\currentregistermaxwidth{\registerparameter\c!maxwidth}%
+ \ifempty\currentregistermaxwidth
+ \enforced\let\limitedregisterentry\firstofoneargument
+ \else
+ \enforced\let\limitedregisterentry\strc_registers_limited_entry
+ \fi
+\to \everyplaceregister
+
+\permanent\tolerant\protected\def\completeregister[#1]#*[#2]%
+ {\ifarguments\or
+ \begingroup
+ \edef\currentregister{\firstinset{#1}}%
+ \normalexpanded{\startnamedsection[\v!chapter][\c!title={\headtext{\currentregister}},reference=\currentregister]}%
+ \placeregister[#1][#2]%
+ \page[\v!yes]%
+ \stopnamedsection
+ \endgroup
+ \fi}
+
+% test case for collapsing (experimental, for Steffen Wolfrum)
+%
+% \starttext
+% \placeregister[index][compress=no] \blank[2*big]
+% \placeregister[index][compress=yes] \blank[2*big]
+% \placeregister[index][compress=all] \page
+% \dorecurse{10}{test 1:!\index{test} test \page}
+% \dorecurse{5} {test 2:\recurselevel \page}
+% \dorecurse{10}{test 3:!\index{test} test \page}
+% \dorecurse{5} {test 4:\recurselevel \page}
+% \dorecurse{1} {test 5:!\index{test} test \page}
+% \dorecurse{5} {test 6:\recurselevel \page}
+% \dorecurse{10}{test 7:!\index{test} test \page}
+% \dorecurse{5} {test 8:\recurselevel \page}
+% oeps \index{oeps}
+% xxxx \index{xxxx}
+% todo \index{todo}
+% \stoptext
+
+%D Character rendering (sections):
+
+\installcorenamespace{registerindicator}
+
+\permanent\def\defaultregistercharacter#1%
+ {\edef\currentregistercharacter{#1}%
+ \ifempty\currentregistercharacter
+ % skip
+ \orelse\ifx\currentregistercharacter\s!unknown
+ % skip
+ \else
+ \edef\p_indicator{\registerparameter\c!indicator}%
+ \ifx\p_indicator\v!yes
+ \strc_registers_place_character_yes
+ \else
+ \strc_registers_place_character_nop
+ \fi
+ \fi}
+
+\def\strc_registers_place_character_yes
+ {\expandnamespaceparameter\??registerindicator\registerparameter\c!alternative\v!a{\currentregistercharacter}}
+
+\def\strc_registers_place_character_nop
+ {\registerparameter\c!before
+ \goodbreak}
+
+% a = <before> <goodbreak> <character> <par> <after> <nobreak>
+
+\def\strc_registers_indicator_a#1#2%
+ {\registerparameter\c!before
+ % bugged, why does leftskip gets set: \vskip\lineheight\goodbreak\vskip-\lineheight
+ \typo_injectors_check_register
+ \begingroup
+ \useregisterstyleandcolor\c!style\c!color
+ \dontleavehmode
+ \typo_injectors_mark_register
+ \strut
+ \iflocation
+ \dosetdirectpagereference{\currentregister:\v!section:#1}%
+ \fi
+ \registerparameter\c!command{#2}%
+ \endgroup
+ \blank[\v!samepage]%
+ \registerparameter\c!after
+ \par
+ \nobreak}
+
+% b = <goodbreak> <before> <character> <after> <nobreak>
+
+\def\strc_registers_indicator_b#1#2%
+ {\registerparameter\c!before
+ \typo_injectors_check_register
+ \begingroup
+ \useregisterstyleandcolor\c!style\c!color
+ \dontleavehmode
+ \typo_injectors_mark_register
+ \strut
+ \iflocation
+ \dosetdirectpagereference{\currentregister:\v!section:#1}%
+ \fi
+ \registerparameter\c!command{#2}%
+ \endgroup
+ \registerparameter\c!after
+ \nobreak}
+
+\setvalue{\??registerindicator a}#1{\strc_registers_indicator_a{#1}{#1}}
+\setvalue{\??registerindicator A}#1{\strc_registers_indicator_a{#1}{\WORD{#1}}}
+\setvalue{\??registerindicator b}#1{\strc_registers_indicator_b{#1}{#1}}
+\setvalue{\??registerindicator B}#1{\strc_registers_indicator_b{#1}{\WORD{#1}}}
+
+%D The following macros are the interface to the rendering. These are
+%D generated by \LUA. This might change.
+
+% \showinjector
+% \setinjector[register][2][\column]
+%
+% \starttext
+% first \index{first}
+% second \index{second}
+% third \index{third}
+% fourth \index{fourth}
+% \placeregister[index]
+% \stoptext
+
+\doinstallinjector\s!register
+
+%D Beware, we get funny side effects when a dangling \index precedes an
+%D placeindex as then flushing takes place inside the index. Took me hours
+%D to notice that.
+
+\newconstant\c_strc_registers_page_state % 0=nothing 1=page 2=see
+\newdimen \d_strc_registers_distance
+
+\permanent\protected\def\startregisteroutput
+ {\endgraf
+ \begingroup
+ \d_strc_registers_distance\registerparameter\c!distance\relax
+ \dostarttaggedchained\t!register\currentregister\??register
+ \forgeteverypar
+ \forgetparindent
+ \forgetparskip}
+
+\permanent\protected\def\stopregisteroutput
+ {\endgraf
+ \dostoptagged
+ \endgroup}
+
+\newdimen\d_strc_registers_hangindent
+\newcount\c_strc_registers_hangafter
+
+\permanent\protected\def\usenestedregisterstyleandcolor#1#2% will change
+ {\useregisterstyleandcolor#1#2%
+ % how about style
+ \ifconditional\c_strc_registers_text_interaction
+ \ifempty\currentcolorparameter \else
+ \resetinteractionparameter\c!color
+ \resetinteractionparameter\c!contrastcolor
+ \fi
+ \fi}
+
+\permanent\protected\def\startregisterentries#1% depth
+ {\endgraf
+ \begingroup
+ \scratchcounter\ifnum#1>\c_strc_registers_maxlevel\c_strc_registers_maxlevel\else#1\fi\relax
+ \dostarttagged\t!registerentries\empty
+% \let\savedcurrentregister\currentregister
+ \pushmacro\currentregister
+ \edef\currentregister{\currentregister:\number\scratchcounter}%
+ \usenestedregisterstyleandcolor\c!textstyle\c!textcolor
+ \ifnum\scratchcounter>\plusone
+ \advance\leftskip\d_strc_registers_distance\relax
+ \fi
+ \d_strc_registers_hangindent\registerparameter\c!distance\relax
+ \c_strc_registers_hangafter \plusone
+ \blank[\v!samepage]%
+% \let\currentregister\savedcurrentregister}
+ \popmacro\currentregister}
+
+\permanent\protected\def\stopregisterentries
+ {\endgraf
+ \dostoptagged
+ \endgroup}
+
+\permanent\protected\def\startregisterentry#1% todo: level
+ {\typo_injectors_check_register
+ \begingroup
+ \dostarttagged\t!registerentry\empty
+ \global\setconstant\c_strc_registers_page_state\zerocount
+ \hangindent\d_strc_registers_hangindent
+ \hangafter \c_strc_registers_hangafter
+ \typo_injectors_mark_register}
+
+\permanent\protected\def\stopregisterentry
+ {\endgraf
+ \global\setconstant\c_strc_registers_page_state\zerocount
+ \dostoptagged
+ \endgroup}
+
+\permanent\protected\def\startregistersection#1% title
+ {\dostarttagged\t!registersection\empty
+ \dostarttagged\t!registertag\empty
+ \registercharacter{#1}\endgraf
+ \dostoptagged}
+
+\permanent\protected\def\stopregistersection
+ {\dostoptagged
+ \endgraf}
+
+\permanent\protected\def\startregisterpages
+ {\begingroup
+ \dostarttagged\t!registerpages\empty
+ \useregisterstyleandcolor\c!pagestyle\c!pagecolor
+ \registerparameter\c!pageleft}
+
+\permanent\protected\def\stopregisterpages
+ {\registerparameter\c!pageright
+ \dostoptagged
+ \endgroup}
+
+\permanent\protected\def\startregisterseewords
+ {\begingroup
+ \dostarttagged\t!registerpage\empty
+ \useregisterstyleandcolor\c!pagestyle\c!pagecolor}
+
+\permanent\protected\def\stopregisterseewords
+ {\dostoptagged
+ \endgroup}
+
+\permanent\protected\def\registerpageseparator % todo: , configurable
+ {\ifcase\c_strc_registers_page_state
+ \hskip\d_strc_registers_distance\relax
+ \or
+ \dostarttagged\t!registerseparator\empty
+ \registerpageseparatorsymbol % page
+ \dostoptagged
+ \or
+ \dostarttagged\t!registerseparator\empty
+ \registerpageseparatorsymbol % see
+ \dostoptagged
+ \fi}
+
+\permanent\protected\def\registeronepagerangeseparator
+ {|\endash|} % todo use \prewordbreak
+
+% \protected\def\withregisterpagecommand#1#2#3#4%
+% {\def\currentregisterpageindex{#2}%
+% \iflocation
+% \goto{\applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}}[internal(#2)]%
+% \else
+% \applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}%
+% \fi}
+
+\permanent\protected\def\withregisterpagecommand#1#2#3#4%
+ {\ifcase#3\relax
+ {\tt [entry\space not\space flushed]}%
+ \else
+ \def\currentregisterpageindex{#2}%
+ \iflocation
+ \strc_references_goto_internal{\applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}}[internal(#2)]%
+ \else
+ \applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}%
+ \fi
+ \fi}
+
+\permanent\protected\def\pushcurrentregister#1%
+ {\let\m_current_register\currentregister
+ \edef\currentregister{#1}}
+
+\permanent\protected\def\popcurrentregister
+ {\let\currentregister\m_current_register}
+
+\permanent\protected\def\registeronepage#1#2#3#4#5% #1:class #2:processor content
+ {\pushcurrentregister{#1}%
+ \edef\p_pagenumber{\registerparameter\c!pagenumber}%
+ \ifx\p_pagenumber\v!no\else
+ \registerpageseparator
+ \global\setconstant\c_strc_registers_page_state\plusone
+ \dostarttagged\t!registerpage\empty
+ \withregisterpagecommand{#2}{#3}{#4}{#5}%
+ \dostoptagged
+ \fi
+ \popcurrentregister}
+
+\newconditional\c_strc_registers_following
+
+\appendtoks
+ \edef\p_compress{\registerparameter\c!compress}%
+ \ifx\p_compress\v!text
+ \settrue\c_strc_registers_following
+ \letregisterparameter\c!compress\v!yes
+ \else
+ \setfalse\c_strc_registers_following
+ \fi
+\to \everyplaceregister
+
+\permanent\protected\def\registerpagerange#1#2#3#4#5#6#7#8% #1:class #2:processor content, content todo: -- configurable
+ {\pushcurrentregister{#1}%
+ \edef\p_pagenumber{\registerparameter\c!pagenumber}%
+ \ifx\p_pagenumber\v!no\else
+ \registerpageseparator
+ \global\setconstant\c_strc_registers_page_state\plusone
+ \dostarttagged\t!registerpagerange\empty
+ \dostarttagged\t!registerfrompage\empty
+ \withregisterpagecommand{#2}{#3}{#4}{#5}%
+ \dostoptagged
+ \ifconditional\c_strc_registers_following
+ \ifnum#3=\numexpr#6-1\relax
+ \labeltext{following:\s!singular}%
+ \else
+ \labeltext{following:\s!plural}%
+ \fi
+ \else
+ \registeronepagerangeseparator
+ \dostarttagged\t!registertopage\empty
+ \withregisterpagecommand{#2}{#6}{#7}{#8}%
+ \fi
+ \dostoptagged
+ \dostoptagged
+ \fi
+ \popcurrentregister}
+
+\permanent\protected\def\defaultregisterentry#1#2#3#4#5% #1:class #2:processor #3:internal #4:seeindex #5:word
+ {\pushcurrentregister{#1}%
+ \def\currentregisterpageindex{#3}%
+ \iflocation
+ \def\currentregisterseeindex{#4}%
+ \ifconditional\c_strc_registers_text_interaction
+ \strc_references_goto_internal{\setlocationcolor\doapplyregisterentrycommand{#2}{#5}}[internal(#3)]%
+ \else
+ \doapplyregisterentrycommand{#2}{#5}%
+ \fi
+ \else
+ \let\currentregisterseeindex\empty
+ \doapplyregisterentrycommand{#2}{#5}%
+ \fi
+ \popcurrentregister}
+
+\permanent\protected\def\doapplyregisterentrycommand#1#2% processor text
+ {\dostarttagged\t!registercontent\empty
+ \ifempty\currentregisterseeindex \else
+ \dontleavehmode
+ \dosetdirectpagereference{seeindex:\currentregisterseeindex}% maybe some day we will support an area
+ \fi
+ \applyprocessor{#1}{\registerparameter\c!textcommand{\limitedregisterentry{\registerparameter\c!deeptextcommand{#2}}}}%
+ \dostoptagged}
+
+\permanent\protected\def\doapplyregisterseecommand#1#2%
+ {\ifempty\currentregisterseeindex
+ \applyprocessor{#1}{#2}%
+ \orelse\iflocation
+ \strc_references_goto_internal{\applyprocessor{#1}{#2}}[seeindex:\currentregisterseeindex]%
+ \else
+ \applyprocessor{#1}{#2}%
+ \fi}
+
+\permanent\protected\def\defaultregisterseeword#1#2#3#4#5#6#7% class i n #3:processor #4:internal #5:seeindex #6:word
+ {\pushcurrentregister{#1}%
+ \ifnum#2=\plusone
+ \registerpageseparator
+ \fi
+ \global\setconstant\c_strc_registers_page_state\plustwo
+ \def\currentregisterpageindex{#5}%
+ \dostarttagged\t!registersee\empty
+ \settrue\c_strc_registers_page_done
+ \iflocation
+ \def\currentregisterseeindex{#6}%
+ \else
+ \let\currentregisterseeindex\empty
+ \fi
+ \ifnum#2=\plusone
+ \labeltexts\v!see{\doapplyregisterseecommand{#4}{#7}}%
+ \orelse\ifnum#2=#3\relax
+ \labeltexts\v!and{\doapplyregisterseecommand{#4}{#7}}%
+ \else
+ ,\space\doapplyregisterseecommand{#4}{#7}%
+ \fi
+ \dostoptagged
+ \popcurrentregister}
+
+\permanent\protected\def\doapplyregistersectioncommand#1#2%
+ {\ifempty\currentregistersectionindex
+ \applyprocessor{#1}{#2}%
+ \orelse\iflocation
+ \strc_references_goto_internal{\applyprocessor{#1}{#2}}[sectionindex:\currentregistersectionindex]%
+ \else
+ \applyprocessor{#1}{#2}%
+ \fi}
+
+\permanent\protected\def\defaultregistersection#1#2#3#4#5#6#7% class i n #4:processor #5:internal #6:sectionindex #7:word
+ {\pushcurrentregister{#1}%
+ \ifnum#2=\plusone
+ \registerpageseparator
+ \fi
+ \global\setconstant\c_strc_registers_page_state\plustwo
+ \def\currentregisterpageindex{#5}%
+ \dostarttagged\t!registersection\empty
+ \settrue\c_strc_registers_page_done
+ \iflocation
+ \def\currentregistersectionindex{#6}%
+ \else
+ \let\currentregistersectionindex\empty
+ \fi
+ \ifnum#2=\plusone\else
+ ,\space
+ \fi
+ \doapplyregistersectioncommand{#4}{#7}%
+ \dostoptagged
+ \popcurrentregister}
+
+\aliased\let\registersection \defaultregistersection
+\aliased\let\registerseeword \defaultregisterseeword
+\aliased\let\registerentry \defaultregisterentry
+\aliased\let\registercharacter\defaultregistercharacter
+
+%D Experimental:
+%D
+%D \starttyping
+%D \setupregister
+%D [index]
+%D [pagesegments=1:4,
+%D pagemethod=section]
+%D
+%D \starttext
+%D
+%D \chapter {one} \section {alpha}
+%D
+%D x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
+%D x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
+%D
+%D \chapter {one} \section {alpha}
+%D
+%D x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
+%D x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
+%D
+%D \placeindex[n=1]
+%D
+%D \stoptext
+%D \stoptyping
+
+%D A few specific rendering variants:
+
+% \def\doregisterpagelocation#1#2%
+% {\nextregisterpage
+% \hbox to 1em{\hss\doregisterpagehowto{#1}{#2}\hss}}
+
+% todo: \installregisterpagehandler
+
+% \def\MyRegisterPageCommand#1%
+% {#1\currentregisterpageuserdata{whatever}}
+%
+% \starttext
+% \setregisterentry[index][entries=aaa][whatever=f.] test \index{bbb} test
+% \placeregister[index][n=1,pagecommand=\MyRegisterPageCommand]
+% \stoptext
+
+\permanent\def\registerpageuserdata #1#2{\clf_registeruserdata#1{#2}}
+\permanent\def\currentregisterpageuserdata {\registerpageuserdata\currentregisterpageindex} % {#1}
+
+% not yet ok : new internal handler names
+
+\aliased\let\registerpageseparatorsymbol\empty
+
+\permanent\protected\def\registerpagebuttonsymbol
+ {\vrule\s!width\emwidth\s!height\exheight\s!depth\zeropoint\relax}
+
+\installcorenamespace{registersymbol}
+
+\setvalue{\??registersymbol n}%
+ {\enforced\frozen\def\registerpageseparatorsymbol{,\space}}
+
+\setvalue{\??registersymbol a}%
+ {\enforced\frozen\def\registerpageseparatorsymbol{,\space}} % now done via conversion
+
+\setvalue{\??registersymbol\v!none}%
+ {\enforced\frozen\let\registerpageseparatorsymbol\empty
+ \enforced\frozen\let\registeronepage\gobblefivearguments
+ \enforced\frozen\let\registerpagerange\gobbleeightarguments}
+
+\setvalue{\??registersymbol 1}%
+ {\enforced\frozen\let\registerpageseparatorsymbol\space
+ \enforced\frozen\def\registeronepage{\symbol[1]\gobblefivearguments}%
+ \enforced\frozen\def\registerpagerange{\symbol[1]\gobbleeightarguments}}
+
+\setvalue{\??registersymbol 2}%
+ {\enforced\frozen\let\registerpageseparatorsymbol\space
+ \enforced\frozen\def\registeronepage{\registerpagebuttonsymbol\gobblefivearguments}%
+ \enforced\frozen\def\registerpagerange{\registerpagebuttonsymbol\gobbleeightarguments}}
+
+\protected\def\strc_registers_set_page_rendering
+ {\doifelse{\registerparameter\c!pagenumber}\v!no
+ {\enforced\frozen\let \currentregisterpagesymbol\v!none}
+ {\enforced\frozen\edef\currentregisterpagesymbol{\registerparameter\c!symbol}}%
+ \ifempty\currentregisterpagesymbol
+ \csname\??registersymbol n\endcsname
+ \orelse\ifcsname\??registersymbol\currentregisterpagesymbol\endcsname
+ \csname\??registersymbol\currentregisterpagesymbol\endcsname
+ \else
+ \enforced\frozen\let\registerpageseparatorsymbol\space
+ \enforced\frozen\def\registeronepage{\registerparameter\c!symbol\gobblefivearguments}%
+ \enforced\frozen\def\registerpagerange{\registerparameter\c!symbol\gobbleeightarguments}%
+ \fi}
+
+\appendtoks
+ \strc_registers_set_page_rendering
+\to \everyplaceregister
+
+%D The linked register code will be reimplemented (not that hard) when it's needed
+%D again and/or when I'm bored.
+
+\permanent \def\findregisterinternal#1#2#3{\clf_findregisterinternal{#1}{#2}#3\relax}
+\permanent\protected\def\pageofinternal #1{\clf_pageofinternal#1\relax}
+
+\permanent\protected\def\linkedregisterentrylink#1#2#3#4% tag where before after
+ {\iflocation
+ \scratchcounter\findregisterinternal{#1}{#2}\currentregisternumber\relax\relax
+ \ifcase\scratchcounter\else
+ #3\relax
+ \goto{\symbol[#2]}[internal(\the\scratchcounter)]%
+ #4\relax
+ \fi
+ \else
+ % \scratchcounter\findregisterinternal{#1}{#2}\currentregisternumber\relax\relax
+ % \ifcase\scratchcounter\else
+ % #3\relax
+ % \pageofinternal\scratchcounter
+ % #4\relax
+ % \fi
+ \fi}
+
+\permanent\protected\def\linkedregisterentry#1%
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox{#1}%
+ \linkedregisterentrylink\currentregistername\v!previous\relax\nobreakspace
+ \unhbox\scratchbox
+ \linkedregisterentrylink\currentregistername\v!next\nobreakspace\relax
+ \endgroup}
+
+\permanent\protected\def\registerpacked#1#2%
+ {\iflocation
+ \hskip\d_strc_registers_distance\relax
+ \nobreak
+ \ifnum#1=#2\relax
+ \goto{\symbol[\v!somewhere]}[internal(#1)]%
+ \else
+ \goto{\symbol[\v!first]}[internal(#1)]%
+ \nobreakspace
+ \goto{\symbol[\v!last]}[internal(#2)]%
+ \fi
+ \fi}
+
+%D Default index:
+
+\defineregister
+ [\v!index]
+% [\v!indices]
+
+\stopcontextdefinitioncode
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-ren.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-ren.mkxl
new file mode 100644
index 00000000000..774f6f64049
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-ren.mkxl
@@ -0,0 +1,820 @@
+%D \module
+%D [ file=strc-ren,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Section Rendering,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Section Rendering}
+
+\unprotect
+
+\newbox\b_strc_rendering_head
+
+\newdimen\d_strc_rendering_local_leftoffset
+\newdimen\d_strc_rendering_local_rightoffset
+
+% Martin Kolarik's problem:
+%
+% \setuphead[section][command=\doTitle]
+% \def\doTitle#1#2{\ruledvbox{\forgetall \hsize=4cm \ruledhbox{\ruledvtop{#1}\ruledvtop{#2}}}}
+% \section{test test test test test test test test test test test test test test test test test}
+
+% \newtoks\everyheadstart % not used currently
+
+\protected\def\strc_rendering_initialize_style_and_color
+ {\ifconditional\headisdisplay
+ \expandafter\strc_rendering_initialize_style_and_color_display
+ \else
+ \expandafter\strc_rendering_initialize_style_and_color_inline
+ \fi}
+
+\protected\def\strc_rendering_initialize_style_and_color_display#1#2%
+ {\dontconvertfont
+ \edef\p_strc_rendering_interlinespace{\headparameter\c!interlinespace}%
+ \ifempty\p_strc_rendering_interlinespace
+ % here the interline space is only set when style sets no space
+ \setfalse\fontattributeisset % use the currentfontparameter state instead
+ \setfalse\interlinespaceisset
+ \useheadstyleandcolor\c!style\c!color\relax
+ \ifconditional\fontattributeisset \ifconditional\interlinespaceisset \else
+ \setupinterlinespace
+ \fi \fi
+ \setfalse\fontattributeisset
+ \useheadstyleandcolor#1#2\relax
+ \ifconditional\fontattributeisset \ifconditional\interlinespaceisset \else
+ \setupinterlinespace
+ \fi \fi
+ \else
+ % here the set interline space overloads any other set space in the style
+ \setfalse\fontattributeisset
+ \useheadstyleandcolor\c!style\c!color\relax
+ \ifconditional\fontattributeisset
+ \dosetupcheckedinterlinespace\p_strc_rendering_interlinespace
+ \fi
+ \setfalse\fontattributeisset
+ \useheadstyleandcolor#1#2\relax
+ \ifconditional\fontattributeisset
+ \dosetupcheckedinterlinespace\p_strc_rendering_interlinespace
+ \fi
+ \fi}
+
+\protected\def\strc_rendering_initialize_style_and_color_inline#1#2%
+ {\dontconvertfont
+ \setfalse\fontattributeisset
+ \useheadstyleandcolor\c!style\c!color\relax
+ \ifconditional\fontattributeisset
+ \updateraggedskips % \setupspacing
+ \fi
+ \setfalse\fontattributeisset
+ \useheadstyleandcolor#1#2\relax
+ \ifconditional\fontattributeisset
+ \updateraggedskips % \setupspacing
+ \fi}
+
+\let\currentstructurereferenceattribute\attributeunsetvalue
+
+\permanent\def\headreferenceattributes
+ {\iflocation
+ % \ctxlua{structures.lists.taglocation(\the\locationcount)}% maybe ... tags entry as used
+ attr \destinationattribute \currentstructureattribute
+ attr \referenceattribute \currentstructurereferenceattribute
+ % attr \internalattribute \locationcount
+ \fi}
+
+\permanent\def\setinlineheadreferenceattributes
+ {\ifconditional\headisdisplay\orelse\iflocation
+ \c_attr_destination\currentstructureattribute
+ \c_attr_reference \currentstructurereferenceattribute
+ % \c_attr_internal \locationcount
+ \fi}
+
+\permanent\protected\def\docheckheadreference
+ {\edef\currentheadinteraction{\headparameter\c!interaction}%
+ \ifx\currentheadinteraction\v!list
+ % setuphead[<section>][interaction=list,...]
+ \strc_references_get_simple_reference{*\the\locationcount}%
+ \let\currentstructurereferenceattribute\currentreferenceattribute
+ \orelse\ifx\currentheadinteraction\v!reference
+ % setuphead[<section>][interaction=reference,...] start<section>[backreference=abc,...]
+ \edef\currentheadbackreference{\structurevariable\c!backreference}% weird, was references.backreference
+ \ifempty\currentheadbackreference \else
+ \strc_references_get_simple_reference\currentheadbackreference
+ \let\currentstructurereferenceattribute\currentreferenceattribute
+ \fi
+ \else
+ % maybe auto: backreference when given, else list
+ \fi}
+
+% a bit messy ... empty in place instead of self .. might change (or use special
+% whatsig (invisible user one)
+
+\protected\def\strc_rendering_place_head_text
+ {\strc_rendering_start_placement
+ \setheadmarking
+ \strc_sectioning_reset_head_number_content
+ \ifconditional\c_strc_sectioning_empty
+ \setbox\b_strc_rendering_head\hpack \headreferenceattributes to \zeropoint{\strut}%
+ \else
+ \docheckheadreference
+ \setbox\b_strc_rendering_head\hbox \headreferenceattributes
+ {\spac_grids_set_local_snapping{\headparameter\c!internalgrid}%
+ \strc_sectioning_reset_head_number_content
+ \useheadstyleparameter\c!style
+ \setinlineheadreferenceattributes
+ \strc_rendering_inject_text}%
+ \fi
+ \strc_rendering_stop_placement}
+
+\protected\def\strc_rendering_place_head_number_and_text
+ {\strc_rendering_start_placement
+ \setheadmarking
+ \doifelsetext\getheadnumber
+ \strc_sectioning_set_head_number_content
+ \strc_sectioning_reset_head_number_content
+ \ifconditional\c_strc_sectioning_empty
+ \setbox\b_strc_rendering_head\hpack \headreferenceattributes to \zeropoint{\strut}%
+ \else % = needed
+ \docheckheadreference
+ \setbox\b_strc_rendering_head\hbox \headreferenceattributes
+ {\spac_grids_set_local_snapping{\headparameter\c!internalgrid}%
+ \useheadstyleparameter\c!style
+ \setinlineheadreferenceattributes
+ \strc_rendering_inject_number_and_text}%
+ \fi
+ \strc_rendering_stop_placement}
+
+\protected\def\strc_rendering_place_head_empty
+ {\hpack\headreferenceattributes{\getheadsyncs}}
+
+%D \starttyping
+%D \def\StretchedBox#1%
+%D {\framed
+%D [frame=off,offset=.5em,align=middle,width=broad]
+%D {\sc\def\stretchedspaceamount{.3em}\stretchednormalcase{#1}}}
+%D
+%D \definehead[MySubject][subject]
+%D \setuphead [MySubject][deeptextcommand=\StretchedBox]
+%D
+%D \MySubject{feeling stretched feeling stretched feeling stretched feeling stretched}
+%D \stoptyping
+
+% helpers
+
+\permanent\protected\def\headhbox{\hbox\headreferenceattributes}
+\permanent\protected\def\headvbox{\vbox\headreferenceattributes}
+
+\permanent\protected\def\startlocalheadsetup{\bgroup\strc_rendering_initialize_spacing}
+\permanent\protected\def\stoplocalheadsetup {\egroup}
+
+\aliased\let\headsetupspacing\relax
+\aliased\let\localheadsetup \relax
+
+\permanent\protected\def\strc_rendering_initialize_spacing
+ {\forgetall % local !
+ \edef\p_align{\headparameter\c!align}%
+ \ifempty\p_align \else
+ \setupalign[\p_align]%
+ \fi
+ \edef\p_tolerance{\headparameter\c!tolerance}%
+ \ifempty\p_tolerance \else
+ \setuptolerance[\p_tolerance]%
+ \fi
+ \edef\p_strut{\headparameter\c!strut}%
+ \ifx\p_strut\v!no
+ \setnostrut
+ \fi
+ \enforced\let\\\strc_rendering_shortcut_backslash}
+
+\protected\def\strc_rendering_shortcut_backslash
+ {\crlf
+ \strut
+ \ignorespaces}
+
+\def\strc_rendering_start_placement
+ {\bgroup
+ \setsystemmode\currenthead
+ \strc_rendering_initialize_alternatives
+ \strc_rendering_initialize_dimensions
+ \strc_rendering_initialize_line_state
+ \reseteverypar % needed indeed
+ \noindent % ipv \whitespace elders, na \forgetall !
+ \bgroup
+ \synctexpushline
+ \edef\p_aligntitle{\headparameter\c!aligntitle}%
+ \ifx\p_aligntitle\v!yes
+ \strc_rendering_initialize_hsize_local
+ \orelse\ifx\p_aligntitle\v!float
+ \strc_rendering_initialize_hsize_local
+ \else
+ \strc_rendering_initialize_hsize_global
+ \fi
+ \setfalse\inhibitmargindata % brrrr is set in forgetall
+ \dontcomplain
+ \postponenotes
+ \strc_rendering_initialize_interaction
+ % delayed:
+ \enforced\let\localheadsetup \strc_rendering_initialize_spacing % historic name, kept around because some styles use it
+ \enforced\let\headsetupspacing\strc_rendering_initialize_spacing} % official name
+
+\def\strc_rendering_initialize_interaction
+ {\resetinteractionparameter\c!style
+ \resetinteractionparameter\c!color
+ \resetinteractionparameter\c!contrastcolor}
+
+% \setuphead[chapter] [style=\bfd,after=,hang=line] % fit broad 2
+% \setuphead[section] [style=\bfc,after=,hang=line]
+% \setuphead[subsection] [style=\bfb,after=,hang=line]
+% \setuphead[subsubsection] [style=\bfa,after=,hang=line]
+% \setuphead[subsubsubsection][style=\bf ,after=,hang=line]
+%
+% \chapter {Test} \input tufte \page
+% \section {Test} \input tufte \page
+% \subsection {Test} \input tufte \page
+% \subsubsection {Test} \input tufte \page
+% \subsubsubsection{Test} \input tufte \page
+%
+% \chapter {Test\\Test} \input tufte \page
+% \section {Test\\Test} \input tufte \page
+% \subsection {Test\\Test} \input tufte \page
+% \subsubsection {Test\\Test} \input tufte \page
+% \subsubsubsection{Test\\Test} \input tufte \page
+
+\newdimen\d_strc_rendering_hang_height
+\newcount\n_strc_rendering_hang_lines
+
+\newdimen\d_strc_rendering_local_height
+\newdimen\d_strc_rendering_local_depth
+\newdimen\d_strc_rendering_local_lineheight
+
+\def\strc_rendering_initialize_line_state
+ {\global\d_strc_rendering_local_height\strutht
+ \global\d_strc_rendering_local_depth\strutdp
+ \global\d_strc_rendering_local_lineheight\lineheight}
+
+\def\strc_rendering_check_hang
+ {\begingroup
+ \openlineheight\d_strc_rendering_local_lineheight
+ \d_strc_rendering_hang_height\htdp\b_strc_rendering_head
+ \getnoflines\d_strc_rendering_hang_height
+ \normalexpanded{\endgroup\n_strc_rendering_hang_lines\the\numexpr\noflines-\plusone\relax}% brrr
+ \setbox\b_strc_rendering_head\hpack{\lower\n_strc_rendering_hang_lines\d_strc_rendering_hang_height\box\b_strc_rendering_head}%
+ \d_strc_rendering_hang_height\dimexpr\htdp\b_strc_rendering_head-\d_strc_rendering_local_height+\strutdp\relax
+ \ht\b_strc_rendering_head\strutht
+ \dp\b_strc_rendering_head\strutdp
+ \d_strc_rendering_local_depth\strutdp}
+
+\installcorenamespace{headplacementcheckhang}
+
+\setvalue{\??headplacementcheckhang\v!line }{\strc_rendering_check_hang
+ \n_strc_rendering_hang_lines\zerocount}
+\setvalue{\??headplacementcheckhang\v!broad }{\strc_rendering_check_hang
+ \getnoflines\d_strc_rendering_hang_height}
+\setvalue{\??headplacementcheckhang\v!fit }{\strc_rendering_check_hang
+ \getrawnoflines\d_strc_rendering_hang_height}
+\setvalue{\??headplacementcheckhang\v!none }{\n_strc_rendering_hang_lines\zerocount}
+\setvalue{\??headplacementcheckhang }{\n_strc_rendering_hang_lines\zerocount}
+\setvalue{\??headplacementcheckhang\s!unknown}{\strc_rendering_check_hang
+ \n_strc_rendering_hang_lines\numexpr\headparameter\c!hang-\plusone\relax}
+
+\def\strc_rendering_initialize_line_hang
+ {\ifconditional\headisdisplay
+ \expandnamespaceparameter\??headplacementcheckhang\headparameter\c!hang\s!unknown
+ \relax
+ \else
+ \n_strc_rendering_hang_lines \zerocount
+ \d_strc_rendering_hang_height\zeropoint
+ \fi}
+
+\def\strc_rendering_initialize_hsize_local
+ {\global\d_strc_rendering_local_leftoffset \leftskip
+ \global\d_strc_rendering_local_rightoffset\rightskip
+ % \forgetall
+ % \leftskip \d_strc_rendering_local_leftoffset % no stretch
+ % \rightskip\d_strc_rendering_local_rightoffset % no stretch
+ % \setlocalhsize
+ % \hsize\localhsize
+ % \forgetbothskips}
+ \scratchwidth\availablehsize
+ \forgetall
+ \hsize\scratchwidth}
+
+\def\strc_rendering_initialize_hsize_global
+ {\global\d_strc_rendering_local_leftoffset \zeropoint
+ \global\d_strc_rendering_local_rightoffset\zeropoint
+ \forgetall}
+
+% \def\strc_sectioning_stay_on_this_line
+% {\directcheckedvspacing{-\v!line,\v!samepage,\v!nowhite}%
+% \directcheckedvspacing\v!disable}
+%
+% we now use \ignoreparskip, so:
+
+\def\strc_sectioning_stay_on_this_line
+ {\directcheckedvspacing{-\v!line,\v!samepage}%
+ \directcheckedvspacing\v!disable}
+
+\def\strc_rendering_stop_placement
+ {\n_strc_rendering_hang_lines\zerocount
+ \ifconditional\headisdisplay
+ \strc_rendering_initialize_line_hang
+ % kind of special, we want to snap heads also according to local specs local
+ \setbox\b_strc_rendering_head\hbox
+ {\hskip\dimexpr\d_strc_rendering_local_leftoffset+\headparameter\c!margin\relax
+ \box\b_strc_rendering_head
+ \getheadsyncs % a latelua why not in the box
+ }%
+ \ifgridsnapping
+ \applygridmethod
+ {\headparameter\c!grid}%
+ {\ifconditional\headisdisplay
+ \strc_rendering_initialize_style_and_color_display\c!textstyle\c!textcolor
+ \fi}%
+ {\box\b_strc_rendering_head}
+ \else
+ \box\b_strc_rendering_head
+ \fi
+ \flushnotes % new, not really needed
+ \endgraf
+ \ifvmode
+ \ifnum\n_strc_rendering_hang_lines>\zerocount
+ \dorecurse\n_strc_rendering_hang_lines{\nointerlineskip\dosomebreak\nobreak\strut\endgraf}% to be checked
+ \fi
+ \nointerlineskip
+ \dosomebreak\nobreak
+ \fi
+% \getheadsyncs % a latelua why not in the box
+ \else
+ % somehow this goes ok even when we push in the margin probably because we gobble pars
+ % in the process of collecting index entries etc
+ \strut
+ \flushnotes % new, here since we're in par mode
+ \unhbox\b_strc_rendering_head
+ \getheadsyncs % a latelua
+ \ifconditional\headissomewhere
+ \strc_sectioning_stay_on_this_line % test case: alternative=margintext and \startparagraph ..
+ \else
+ %\hskip\headnumberdistance\s!plus\headnumberdistance\s!minus.25\dimexpr\headnumberdistance\relax
+ \hskip\headtextdistance\relax
+ \strc_sectioning_inject_continuous_signal
+ \fi
+ \fi
+ \ifconditional\headisdisplay
+ \ifvmode
+ \ifgridsnapping % important, font related depth, see comment
+ \prevdepth\strutdp
+ \else
+ \prevdepth\d_strc_rendering_local_depth
+ \fi
+ \fi
+ \fi
+ \synctexpopline
+ \egroup
+ \egroup
+ \ifconditional\headisdisplay
+ \useindentnextparameter\headparameter
+ \orelse\ifconditional\headissomewhere
+ \ignoreparskip
+ \noindentation
+ \else
+ \ignoreparskip
+ \fi}
+
+% nice testcase
+%
+% \setupheads[aligntitle=yes]
+%
+% \startnarrower
+% \subject{\dorecurse{100}{x }}
+% \section{\dorecurse{100}{x }}
+% \input tufte \par
+% \setupheads[alternative=inmargin]
+% \subject{\dorecurse{100}{x }}
+% \section{\dorecurse{100}{x }}
+% \input tufte \par
+% \stopnarrower
+
+% \dodefineheadplacement[sectiona][vertical]{#1->#2}
+% \dodefineheadplacement[sectionb][vertical]#1#2{#1->#2}
+%
+% \setuphead[section][alternative=sectiona]
+% \setuphead[subsection][alternative=sectionb]
+
+% \startsetups[\??headrenderings:\v!vertical:\v!sectiona]
+% ... there will be a more public namespace
+% \stopsetups
+
+\installcorenamespace{headplacementalternative}
+\installcorenamespace{headrenderings}
+\installcorenamespace{headalternative}
+
+\installcommandhandler \??headalternative {headalternative} \??headalternative % or just \??head
+
+\setupheadalternative
+ [%\c!width=\headparameter\c!width,
+ %\c!distance=\headparameter\c!distance,
+ \c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\currentheadalternative]
+
+\let\currentheadalternative \v!normal
+\let\currentheadrenderingsetup \empty
+\let\currentheadrenderingalternative\v!vertical
+
+\permanent\tolerant\protected\def\defineheadplacement[#1]#*[#2]%
+ {\doifelsenextbgroup
+ {\strc_rendering_define_placement_yes[#1][#2]}%
+ {\strc_rendering_define_placement_nop[#1][#2]}}
+
+\def\strc_rendering_define_placement_yes[#1][#2]%
+ {\defineheadalternative[#1][\c!alternative=#2,\c!renderingsetup=\??headrenderings:\v!command]%
+ \setuvalue{\??headplacementalternative#1}##1##2}
+
+\def\strc_rendering_define_placement_nop[#1][#2]%
+ {\defineheadalternative[#1][\c!alternative=#2,\c!renderingsetup=\??headrenderings:\v!command]%
+ \setuvalue{\??headplacementalternative#1}}
+
+% these can be used in setups:
+%
+% \headnumbercontent
+% \headtextcontent
+%
+% \headwidth
+% \headtextwidth
+% \headnumberdistance
+% \headnumberwidth
+% \headsetupspacing
+%
+% \headshownumber
+% \headisdisplay
+
+\let\headnumbercontent\empty
+\let\headtextcontent \empty
+
+\newdimen\headwidth
+\newdimen\headtextwidth
+\newskip \headtextdistance
+\newdimen\headnumberdistance
+\newdimen\headnumberwidth
+
+% \newconditional\headshownumber % defined already
+% \newconditional\headisdisplay % defined already
+
+\protected\def\strc_rendering_initialize_alternatives
+ {\edef\currentheadalternative{\headparameter\c!alternative}%
+ \ifcsname\currentheadalternativehash\s!parent\endcsname \else
+ \let\currentheadalternative\v!normal % cf. mkii
+ \fi
+ \edef\currentheadrenderingsetup{\headalternativeparameter\c!renderingsetup}%
+ \edef\currentheadrenderingalternative{\headalternativeparameter\c!alternative}%
+ \ifempty\currentheadrenderingalternative
+ \let\currentheadrenderingalternative\v!vertical
+ \fi
+ \ifx\currentheadrenderingalternative\v!horizontal
+ \global\setfalse\headisdisplay % global
+ \global\setfalse\headissomewhere % global
+ \orelse\ifx\currentheadrenderingalternative\v!somewhere
+ \global\setfalse\headisdisplay % global
+ \global\settrue \headissomewhere % global
+ \else
+ \global\settrue \headisdisplay % global
+ \global\setfalse\headissomewhere % global
+ \fi}
+
+\protected\def\strc_rendering_initialize_dimensions
+ {\headwidth \headparameter\c!width \relax % \zeropoint == unset
+ \headnumberwidth \headparameter\c!numberwidth \relax % \zeropoint == unset
+ \headnumberdistance\headparameter\c!distance \relax
+ \headtextdistance \headparameter\c!textdistance\relax
+ \headtextwidth \headparameter\c!textwidth \relax} % \zeropoint == unset
+
+\permanent\protected\def\headtextcontent
+ {\begingroup
+ \strc_rendering_initialize_style_and_color\c!textstyle\c!textcolor
+ \headparameter\c!commandbefore\relax
+ \ifcsname\currentheadhash\c!deeptextcommand\endcsname
+ %\expandafter\let\expandafter\deepstructuretitlecommand\csname\currentheadhash\c!deeptextcommand\endcsname
+ \expandafter\let\expandafter\deepstructuretitlecommand\lastnamedcs
+ \fi
+ \ifconditional\headisdisplay
+ % struts can be nilled with \setnostrut
+ \headparameter\c!textcommand{\setstrut\begstrut\getheadtitle\endstrut}%
+ \global\d_strc_rendering_local_height\strutht
+ \global\d_strc_rendering_local_depth\strutdp
+ \global\d_strc_rendering_local_lineheight\lineheight
+ \headparameter\c!commandafter\relax
+ \endgraf
+ \else
+ \headparameter\c!textcommand{\getheadtitle}%
+ \headparameter\c!commandafter\relax
+ \fi
+ \endgroup}
+
+\permanent\protected\def\headnumbercontent
+ {\begingroup
+ \strc_rendering_initialize_style_and_color\c!numberstyle\c!numbercolor
+ \ifcsname\currentheadhash\c!deepnumbercommand\endcsname
+ %\expandafter\let\expandafter\deepstructurenumbercommand\csname\currentheadhash\c!deepnumbercommand\endcsname
+ \expandafter\let\expandafter\deepstructurenumbercommand\lastnamedcs
+ \fi
+ \ifconditional\headisdisplay
+ % can be nilled with \setnostrut
+ \headparameter\c!numbercommand{\setstrut\begstrut\getheadnumber\endstrut}%
+ \else
+ \headparameter\c!numbercommand{\getheadnumber}%
+ \fi
+ \endgroup}
+
+\permanent\protected\def\fakedheadnumber{\vphantom{0}} % needed for mathplus
+
+% \permanent\protected\def\fakeheadnumbercontent
+% {\hbox to \zeropoint{\let\getheadnumber\fakedheadnumber\headnumbercontent}}
+
+\permanent\protected\def\fakeheadnumbercontent
+ {\edef\p_hidenumber{\headparameter\c!hidenumber}%
+ \ifx\p_hidenumber\v!yes\else
+ \hbox to \zeropoint{\let\getheadnumber\fakedheadnumber\headnumbercontent}%
+ \fi}
+
+\permanent\protected\def\strc_rendering_inject_number_and_text
+ {\edef\p_command{\headparameter\c!command}% assumes \protected definition
+ \ifempty\p_command
+ \directsetup\currentheadrenderingsetup
+ \else
+ \p_command\headnumbercontent\headtextcontent
+ \fi}
+
+\protected\def\strc_rendering_inject_text
+ {\edef\p_command{\headparameter\c!command}% assumes \protected definition
+ \ifempty\p_command
+ \directsetup\currentheadrenderingsetup
+ \else
+ \p_command\empty\headtextcontent
+ \fi}
+
+\startsetups[\??headrenderings:\v!command]
+ \csname\??headplacementalternative\currentheadalternative\endcsname \headnumbercontent \headtextcontent
+\stopsetups
+
+% obsolete
+%
+% \def\normalplacehead % hooks into \c!command
+% {\csname\??headplacementalternative\ifcsname\??headplacementalternative\currentheadalternative\endcsname\currentheadalternative\else\v!normal\fi\endcsname}
+
+\defineheadalternative
+ [\v!paragraph]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!paragraph]
+
+\startsetups[\??headrenderings:\v!paragraph]
+ \vbox {
+ \headsetupspacing
+ \begstrut
+ \ifconditional\headshownumber % \ifheadnumbercontent
+ \headnumbercontent
+ \hskip\headnumberdistance
+ \fi
+ \headtextcontent
+ }
+\stopsetups
+
+% \setuphead
+% [chapter]
+% [numberwidth=2cm,hang=line,after={\blank[3*line]}]
+%
+% \chapter{Oeps oeps oeps} \input tufte \section{Oeps}
+% \chapter{Oeps oeps oeps} \section{Oeps} \input tufte
+
+\defineheadalternative
+ [\v!normal]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!normal]
+
+\startsetups[\??headrenderings:\v!normal]
+ \vbox {
+ \headsetupspacing
+ \ifconditional\headshownumber
+ \ifzeropt\headwidth \else
+ \ifzeropt\headnumberwidth
+ \ifzeropt\headtextwidth \else
+ \headnumberwidth\dimexpr\headwidth-\headtextwidth\relax
+ \fi
+ \else
+ \ifzeropt\headtextwidth
+ \headtextwidth\dimexpr\headwidth-\headnumberwidth\relax
+ \fi
+ \fi
+ \hsize\headwidth
+ \fi
+ \ifzeropt\headnumberwidth \else
+ \headnumberdistance\zeropoint
+ \fi
+ \setbox\scratchbox\hbox \ifzeropt\headnumberwidth\else to \headnumberwidth\fi{\headnumbercontent}
+ \scratchdimen\dimexpr\wd\scratchbox+\headnumberdistance\relax
+ \ifzeropt\headtextwidth \else
+ \hsize\dimexpr\scratchdimen+\headtextwidth\relax
+ \fi
+ \hangindent\scratchdimen
+ \hangafter \plusone
+ \noindent
+ \box\scratchbox
+ \hskip\headnumberdistance
+ \else
+ \ifzeropt\headtextwidth
+ \ifzeropt\headwidth \else
+ \hsize\headwidth
+ \fi
+ \else
+ \hsize\headtextwidth
+ \fi
+ \noindent
+ \fakeheadnumbercontent % will also be done in the other ones (force consistency with numbered)
+ \fi
+ \headtextcontent
+ }
+\stopsetups
+
+\defineheadalternative
+ [\v!inmargin]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!inmargin]
+
+\startsetups[\??headrenderings:\v!inmargin]
+ \vbox {
+ \headsetupspacing
+ \dontleavehmode % in case there is no strut, else side effects with llap
+ \begstrut % use one \strut here!
+ \ifconditional\headshownumber
+ \doifelsesomething {\headparameter\c!location} {
+ % kind of new
+ \margindata [\headparameter\c!location] {
+ \headnumbercontent
+ }
+ } {
+ % normal backward compatible variant
+ \llap {
+ \signalrightpage
+ \hbox {
+ \hfill
+ \headnumbercontent
+ \doifelserightpage{
+ \scratchdistance\leftmargindistance
+ } {
+ \scratchdistance\rightmargindistance
+ }
+ \hskip\dimexpr\d_strc_rendering_local_leftoffset+\scratchdistance\relax
+ }
+ }
+ }
+ \else
+ \fakeheadnumbercontent % will also be done in the other ones (force consistency with numbered)
+ \fi
+ \headtextcontent
+ }
+\stopsetups
+
+\defineheadalternative
+ [\v!margin]
+ [\v!inmargin]
+
+% \startsetups[\??headrenderings:\v!vertical:\v!margin]
+% \directsetup{\??headrenderings:\v!vertical:\v!inmargin}
+% \stopsetups
+
+%D This one is for head based numbering usage: foo 1.2 and so:
+
+\defineheadalternative
+ [\v!reverse]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!reverse]
+
+\startsetups[\??headrenderings:\v!reverse]
+ \vbox {
+ \headsetupspacing
+ \noindent
+ \begstrut
+ \setfalse\headisdisplay % so a kind of mix
+ \headtextcontent
+ \ifconditional\headshownumber
+ \kern\headnumberdistance
+ \headnumbercontent
+ \else
+ \fakeheadnumbercontent
+ \fi
+ \endstrut
+ }
+\stopsetups
+
+\defineheadalternative
+ [\v!middle]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!middle]
+
+\startsetups[\??headrenderings:\v!middle]
+ \vbox {
+ \headsetupspacing
+ \veryraggedcenter
+ \enforced\let\\\endgraf
+ \enforced\let\crlf\endgraf
+ \ifconditional\headshownumber
+ \strut
+ \headnumbercontent
+ \par
+ \else
+ \fakeheadnumbercontent
+ \fi
+ \begstrut
+ \headtextcontent
+ \endstrut
+ }
+\stopsetups
+
+\defineheadalternative
+ [\v!text]
+ [\c!alternative=\v!horizontal,
+ \c!renderingsetup=\??headrenderings:\v!text]
+
+\startsetups[\??headrenderings:\v!text]
+ \begingroup
+ \headsetupspacing % no stretch in distance
+ \ifconditional\headshownumber
+ \headnumbercontent
+ \kern\headnumberdistance
+ \fi
+ \begstrut
+ \headtextcontent
+ \endstrut
+ \endgroup
+\stopsetups
+
+% onder/boven lijnt het nummer op de onderste/bovenste regel uit van een meerregelige kop
+
+\defineheadalternative
+ [\v!bottom]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!bottom]
+
+\startsetups[\??headrenderings:\v!bottom]
+ \ifconditional\headshownumber
+ \setbox\scratchboxone\hbox {
+ \headnumbercontent
+ }
+ \setbox\scratchboxtwo\vbox {
+ \headsetupspacing
+ \advance\hsize-\wd\scratchboxone\relax
+ \headtextcontent
+ }
+ \hpack {
+ \box\scratchboxone
+ \hskip\headnumberdistance
+ \box\scratchboxtwo
+ }
+ \else
+ \vbox {
+ \headsetupspacing
+ \noindent
+ \headtextcontent
+ }
+ \fi
+\stopsetups
+
+\defineheadalternative
+ [\v!top]
+ [\c!alternative=\v!vertical,
+ \c!renderingsetup=\??headrenderings:\v!top]
+
+\startsetups[\??headrenderings:\v!top]
+ \ifconditional\headshownumber
+ \setbox\scratchboxone\hbox {
+ \headnumbercontent
+ }
+ \setbox\scratchboxtwo\vtop {
+ \headsetupspacing
+ \advance\hsize-\wd\scratchboxone\relax
+ \headtextcontent
+ }
+ \hpack {
+ \box\scratchboxone
+ \hskip\headnumberdistance
+ \box\scratchboxtwo
+ }
+ \else
+ \vtop{
+ \headsetupspacing
+ \noindent
+ \headtextcontent
+ }
+ \fi
+\stopsetups
+
+% see typo-mar.mkiv:
+%
+% \defineheadalternative
+% [\v!margintext]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-sbe.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-sbe.mkxl
new file mode 100644
index 00000000000..d3141d28b66
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-sbe.mkxl
@@ -0,0 +1,137 @@
+%D \module
+%D [ file=strc-sbe,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Section Block Environments,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Section Block Environments}
+
+\unprotect
+
+% \def\ChapterEntry#1#2#3%
+% {chapter : \hbox to \hsize{\strut\bf#2\hss#3}\endgraf\placelist[section]}
+%
+% \startfrontmatter % optional
+% \placelist[chapter][alternative=command,command=\ChapterEntry,criterium=text] \page
+% \stopfrontmatter % optional
+%
+% \startbodymatter % optional
+% \chapter{first} \section{one} test \section{two} test \page
+% \chapter{second} \section{alpha} test \section{beta} test \page
+% \stopbodymatter % optional
+
+\installcorenamespace{sectionblock}
+
+\installcommandhandler \??sectionblock {sectionblock} \??sectionblock
+
+\setupsectionblock
+ [\c!number=\v!yes,
+ \c!page=\v!right] % otherwise marks are reset too soon
+
+\newtoks\everybeforesectionblock
+\newtoks\everyaftersectionblock
+
+\let\strc_sectionblock_define_normal\definesectionblock
+
+\permanent\overloaded\tolerant\protected\def\definesectionblock[#1]#*[#2]#*[#3]% singular plural settings
+ {\strc_sectionblock_define_normal[#1][#3]%
+ %\expandafter\newif\csname if#2\endcsname % obsolete
+ \strc_sectionblock_set_environment{#1}\empty
+ \frozen\setuvalue{\e!start#2}{\startsectionblock[#1]}%
+ \frozen\setuvalue{\e!stop #2}{\stopsectionblock}}
+
+\ifdefined \resetallstructuremarks \else
+ \let\resetallstructuremarks\relax
+\fi
+
+\def\strc_sectionblock_handle_page
+ {\edef\p_strc_sectionblock_page{\sectionblockparameter\c!page}%
+ \ifempty\p_strc_sectionblock_page \else
+ \page[\p_strc_sectionblock_page]%
+ \fi}
+
+\appendtoks
+ \strc_sectionblock_handle_page
+ \resetallstructuremarks
+ \strc_sectionblock_get_environment\currentsectionblock
+ \sectionblockparameter\c!before % don't move
+ \dostarttagged\t!division\currentsectionblock % no parents
+\to \everybeforesectionblock
+
+\appendtoks
+ \sectionblockparameter\c!after % don't move
+ \strc_sectionblock_handle_page
+ \dostoptagged
+ \resetallstructuremarks
+\to \everyaftersectionblock
+
+\permanent\tolerant\protected\def\setsectionblock[#1]#*[#2]% used to set the default
+ {\edef\currentsectionblock{#1}% from now on we assume a value
+ \setupcurrentsectionblock[#2]%
+ \clf_setsectionblock{#1}
+ bookmark {\sectionblockparameter\c!bookmark}%
+ \relax}
+
+\mutable\let\currentsectionblock\empty % was \s!unknown
+
+\permanent\tolerant\protected\def\startsectionblock[#1]#*[#2]%
+ {%\ctxlua{structures.counters.check(0)}% we assume sane usage of \page, as this is the only workable place (in push)
+ \begingroup
+ \edef\currentsectionblock{#1}% from now on we assume a value
+ \setupcurrentsectionblock[#2]%
+ \clf_pushsectionblock{#1}
+ bookmark {\sectionblockparameter\c!bookmark}%
+ \relax
+ %\csname #1true\endcsname % obsolete
+ \setsystemmode\currentsectionblock
+ \the\everybeforesectionblock\relax
+ \showmessage\m!structures1\currentsectionblock}
+
+\permanent\protected\def\stopsectionblock
+ {\showmessage\m!structures2\currentsectionblock
+ \the\everyaftersectionblock\relax
+ \clf_popsectionblock
+ \endgroup}
+
+%D \starttyping
+%D \startsectionblockenvironment[frontpart]
+%D \setupnumber[userpage][numberconversion=romannumerals,start=1]
+%D \stopsectionblockenvironment
+%D
+%D \startsectionblockenvironment[bodypart]
+%D \setupnumber[userpage][numberconversion=numbers,start=1]
+%D \stopsectionblockenvironment
+%D
+%D \startsectionblockenvironment[backpart]
+%D \setupnumber[userpage][numberconversion=numbers,start=1]
+%D \stopsectionblockenvironment
+%D
+%D \starttext
+%D \startfrontmatter \chapter{test} \stopfrontmatter
+%D \startbodymatter \chapter{test} \stopbodymatter
+%D \startappendices \chapter{test} \stopappendices
+%D \stoptext
+%D \stoptyping
+
+\installcorenamespace{sectionblockenvironment}
+
+\protected\def\strc_sectionblock_set_environment#1#2%
+ {\setvalue{\??sectionblockenvironment#1}{#2}}
+
+\protected\def\strc_sectionblock_get_environment#1%
+ {\csname\??sectionblockenvironment#1\endcsname}
+
+\permanent\tolerant\protected\setvalue{\e!start\v!sectionblockenvironment}[#1]% evt \pushendofline \popendofline
+ {\grabuntil{\e!stop\v!sectionblockenvironment}{\strc_sectionblock_set_environment{#1}}}
+
+\aliased\letvalue{\e!stop\v!sectionblockenvironment}\relax
+
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-sec.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-sec.mkxl
new file mode 100644
index 00000000000..7c4a8fc249d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-sec.mkxl
@@ -0,0 +1,1322 @@
+%D \module
+%D [ file=strc-sec,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Sectioning,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D In retrospect I should have gone futher and move more to the
+%D \LUA\ end but here we are now. Maybe some day.
+
+\writestatus{loading}{ConTeXt Structure Macros / Sectioning}
+
+\unprotect
+
+\startcontextdefinitioncode
+
+\installcorenamespace{structure}
+
+\installdirectcommandhandler \??structure {structure} % unchecked, so we need to initialize used parameters
+
+\setupstructure % not a user command so we might need to change the name
+ [\c!number=,
+ \c!level=,
+ \c!name=,
+ \c!title=,
+ \c!bookmark=, % no real need, default to empty anyway
+ \c!marking=, % no real need, default to empty anyway
+ \c!list=, % no real need, default to empty anyway
+ \c!referencetext=, % no real need, default to empty anyway
+ \c!label=, % no real need, default to empty anyway
+ \c!coupling=,
+ \c!ownnumber=,
+ % \c!interaction=\v!list,
+ \c!sectionseparatorset=\s!default,
+ \c!sectionconversionset=\s!default,
+ \c!sectionstopper=,
+ \c!sectionstarter=,
+ \c!sectionsegments=,
+ \c!sectionresetset=,
+ \c!reference=,
+ \c!backreference=,
+ \c!expansion=\v!no,
+ \c!xmlsetup=,
+ \s!catcodes=,
+ \c!saveinlist=\v!yes]
+
+% maybe flags for list, bm, mark
+
+\def\m_strc_references_prefix_yes{+}
+\def\m_strc_references_prefix_nop{-}
+
+\mutable\let\currentstructurereferenceprefix\empty
+\mutable\let\currentstructurereferencetext \empty
+
+\installglobalmacrostack\currentstructurereferenceprefix
+
+\def\strc_sectioning_set_reference_prefix
+ {\ifempty\currentstructurereferenceprefix
+ % nothing
+ \orelse\ifx\currentstructurereferenceprefix\m_strc_references_prefix_yes
+ \global\advance\prefixcounter \plusone % temp here
+ \setupglobalreferenceprefix[\the\prefixcounter]%
+ \orelse\ifx\currentstructurereferenceprefix\m_strc_references_prefix_nop
+ \setupglobalreferenceprefix[]%
+ \else
+ \setupglobalreferenceprefix[\currentstructurereferenceprefix]%
+ \fi
+ \glet\currentstructurereferenceprefix\referenceprefix}
+
+% why xdef ?
+
+\setupstructure
+ [\c!label={\headparameter{\currentsectionblock\c!label}},
+ \c!incrementnumber=\ifconditional\c_strc_sectioning_increment\v!yes\else\v!no\fi, % not that needed
+ \c!saveinlist=\ifconditional\c_strc_sectioning_to_list\v!yes\else\v!no\fi,
+ \c!level=\currentheadlevel,
+ \c!number=\ifconditional\c_strc_sectioning_increment\ifconditional\headshownumber\v!yes\else\v!no\fi\else\v!no\fi,
+ \c!expansion=\headparameter\c!expansion,
+ \c!xmlsetup=\headparameter\c!xmlsetup,
+ \s!catcodes=\headparameter\s!catcodes,
+ \c!sectionresetset=\headparameter\c!sectionresetset,
+ \c!sectionseparatorset=\headparameter\c!sectionseparatorset,
+ \c!sectionconversionset=\headparameter\c!sectionconversionset,
+ \c!sectionconversion=\headparameter\c!conversion, % just for compatibility
+ \c!sectionstarter=\headparameter\c!sectionstarter,
+ \c!sectionstopper=\headparameter\c!sectionstopper,
+ \c!sectionset=\headparameter\c!sectionset,
+ \c!sectionsegments=\headparameter\c!sectionsegments,
+ \c!reference=\headparameter\c!reference,
+ \c!referenceprefix=\headparameter\c!referenceprefix,
+ \c!criterium=\headparameter\c!criterium]
+
+% see lists/neat-001.tex for usage of:
+
+\permanent\def\namedstructureheadlocation#1% expandable, maybe [#1]
+ {\csname\??savedinternalreference\ifcsname\??savedinternalreference#1\endcsname#1\else\s!default\fi\endcsname}
+
+\mutable\let\currentheadbackreference \empty
+\mutable\let\currentheaddefault \empty
+\mutable\let\currentheadincrement \empty
+\mutable\let\currentheadinteraction \empty
+\mutable\let\currentheadlabeltag \empty
+\mutable\let\currentheadnumber \!!zerocount
+\mutable\let\currentheadrenderingalternative\empty
+\mutable\let\currentheadrenderingsetup \empty
+\mutable\let\currentheadtext \empty
+
+% zeros:
+%
+% \setuphead[subsection][criterium=all]
+%
+% \dorecurse{3} {
+% \chapter{Blabla} \subsection{bla 1 1} \subsection{bla 1 2}
+% \section{bla 2} \subsection{bla 2 1} \subsection{bla 2 2}
+% }
+
+\protected\def\strc_sectioning_register#1#2#3% #1=interfaced-settings, #2=optional user data (not yet supported)
+ {\begingroup
+ \setupstructure[\c!name={#1},#2]%
+ \xdef\currentstructurename {\structureparameter\c!name}%
+ \xdef\currentstructurecoupling {\structureparameter\c!coupling}%
+ \xdef\currentstructureownnumber {\structureparameter\c!ownnumber}% optional own number
+ \xdef\currentstructurelevel {\structureparameter\c!level}%
+ \edef\currentstructureexpansion {\structureparameter\c!expansion}%
+ \xdef\currentstructurexmlsetup {\structureparameter\c!xmlsetup}%
+ \xdef\currentstructurecatcodes {\structureparameter\s!catcodes}%
+ \xdef\currentstructurelabel {\structureparameter\c!label}%
+ \xdef\currentstructurereference {\structureparameter\c!reference}%
+ \xdef\currentstructurereferenceprefix{\structureparameter\c!referenceprefix}%
+ \xdef\currentstructurebackreference {\structureparameter\c!backreference}%
+ \xdef\currentstructureshownumber {\structureparameter\c!number}%
+ \xdef\currentstructuresaveinlist {\structureparameter\c!saveinlist}%
+ \xdef\currentstructureincrementnumber{\structureparameter\c!incrementnumber}%
+ \xdef\currentstructureplaceholder {\structureparameter\c!placeholder}%
+ \ifx\currentstructureexpansion\s!xml
+ \xmlstartraw
+ \xdef\currentstructuretitle {\structureparameter\c!title}%
+ \xdef\currentstructurebookmark {\structureparameter\c!bookmark}%
+ \xdef\currentstructuremarking {\structureparameter\c!marking}%
+ \xdef\currentstructurelist {\structureparameter\c!list}%
+ \xdef\currentstructurereferencetext{\structureparameter\c!referencetext}%
+ \xmlstopraw
+ \iflocation \ifempty\currentstructurebookmark
+ \xdef\currentstructurebookmark{\prerolltostring{\currentstructuretitle}}%
+ \fi \fi
+ \ifempty\currentstructurelist
+ \glet\currentstructurelist\currentstructuretitle
+ \fi
+ \glet\currentstructurecoding\s!xml
+ \else
+ \ifx\currentstructureexpansion\v!yes
+ \xdef\currentstructuretitle {\structureparameter\c!title}%
+ \xdef\currentstructurebookmark {\structureparameter\c!bookmark}%
+ \xdef\currentstructuremarking {\structureparameter\c!marking}%
+ \xdef\currentstructurelist {\structureparameter\c!list}%
+ \xdef\currentstructurereferencetext{\structureparameter\c!referencetext}%
+ \iflocation \ifempty\currentstructurebookmark
+ \xdef\currentstructurebookmark{\prerolltostring{\currentstructuretitle}}%
+ \fi \fi
+ \else
+ \xdef\currentstructuretitle {\detokenizedstructureparameter\c!title}%
+ \xdef\currentstructurebookmark {\detokenizedstructureparameter\c!bookmark}%
+ \xdef\currentstructuremarking {\detokenizedstructureparameter\c!marking}%
+ \xdef\currentstructurelist {\detokenizedstructureparameter\c!list}%
+ \xdef\currentstructurereferencetext{\detokenizedstructureparameter\c!referencetext}%
+ \iflocation \ifempty\currentstructurebookmark
+ \xdef\currentstructurebookmark{\prerolltostring{\structureparameter\c!title}}%
+ \fi \fi
+ \fi
+ \ifempty\currentstructurelist
+ \glet\currentstructurelist\currentstructuretitle
+ \fi
+ \glet\currentstructurecoding\s!tex
+ \fi
+ \iflocation \ifempty\currentstructurebookmark \orelse \ifx\currentstructurebookmark\currentstructuretitle \else
+ %\showmessage\m!structures3{{\currentstructurebookmark}}% comma interference
+ \writestatus{\m!structures}{bookmark:\space\currentstructurebookmark}%
+ \fi \fi
+ \setnextinternalreference
+ \storeinternalreference\currentstructurename{\the\locationcount}%
+ \strc_sectioning_set_reference_prefix
+ \clf_setsectionentry
+ references {
+ internal \locationcount
+ % block {\currentsectionblock}
+ prefix {\currentstructurereferenceprefix}
+ reference {\currentstructurereference}
+ backreference {\currentstructurebackreference}
+ }
+ directives {
+ resetset {\structureparameter\c!sectionresetset}
+ }
+ metadata {
+ kind {section}
+ name {\currentstructurename}
+ catcodes \ifempty\currentstructurecatcodes\catcodetable\else\csname\currentstructurecatcodes\endcsname\fi\space
+ coding {\currentstructurecoding}
+ \ifx\currentstructurecoding\s!xml
+ xmlroot {\xmldocument}
+ \fi
+ \ifempty\currentstructurexmlsetup \else
+ xmlsetup {\currentstructurexmlsetup}
+ \fi
+ \ifx\currentstructuresaveinlist\v!no
+ nolist \space true\space
+ \fi
+ \ifx\currentstructureincrementnumber\v!yes
+ increment {\currentstructureincrementnumber}
+ \fi
+ }
+ titledata {
+ label {\detokenize\expandafter{\currentstructurelabel}}
+ title {\detokenize\expandafter{\currentstructuretitle}}
+ \ifx\currentstructurebookmark\currentstructuretitle \else
+ bookmark {\detokenize\expandafter{\currentstructurebookmark}}
+ \fi
+ \ifx\currentstructuremarking\currentstructuretitle \else
+ marking {\detokenize\expandafter{\currentstructuremarking}}
+ \fi
+ \ifx\currentstructuresaveinlist\v!no \else
+ \ifx\currentstructurelist\currentstructuretitle \else
+ list {\detokenize\expandafter{\currentstructurelist}}
+ \fi
+ \fi
+ \ifx\currentstructurereferencetext\currentstructuretitle \else
+ reference {\detokenize\expandafter{\currentstructurereferencetext}}
+ \fi
+ }
+ numberdata {
+ % block {\currentsectionblock}
+ \ifx\currentstructureshownumber\v!no
+ hidenumber \space true\space % space needed for parser
+ \fi
+ separatorset {\structureparameter\c!sectionseparatorset}
+ conversionset {\structureparameter\c!sectionconversionset}
+ conversion {\structureparameter\c!sectionconversion}
+ starter {\structureparameter\c!sectionstarter}
+ stopper {\structureparameter\c!sectionstopper}
+ set {\structureparameter\c!sectionset}
+ segments {\structureparameter\c!sectionsegments}
+ ownnumber {\currentstructureownnumber}
+ language {\currentlanguage}% for the moment, needed for bookmarks conversion
+ criterium {\structureparameter\c!criterium}
+ }
+ userdata {\detokenize{#3}}% will be converted to table at the lua end
+ \relax
+ \xdef\currentstructurelistnumber{\clf_currentsectiontolist}%
+ % \currentstructuresynchronize has to be called someplace, since it introduces a node
+ \setstructuresynchronization\currentstructurelistnumber
+ \endgroup}
+
+\mutable\let\currentsectioncountervalue \!!zerocount % redefined later
+\mutable\let\previoussectioncountervalue\!!zerocount % redefined later
+
+% We can access the (stored) data with the following macros.
+%
+% \def\MyHeadCommand #1#2{\framed{#1}\framed{#2 / \structureuservariable{subtitle}}}
+% \def\MyListCommand#1#2#3{\externalfigure[\structurelistuservariable{figure}][height=5mm]#2}
+%
+% \setuphead[chapter][command=\MyHeadCommand]
+% \setuplist[chapter][alternative=command,command=\MyListCommand]
+%
+% \starttext
+% \setupheadertexts[chapter]
+% \setupinteraction[state=start]
+% \placebookmarks[chapter]
+% \placelist[chapter]
+% \startchapter[ownnumber=10,title=Ton,list=Hans,marking=Kees,bookmark=Bram][figure=cow.pdf,subtitle=oeps]
+% \stopchapter
+% \stoptext
+
+% todo: #1 => "#1" ... adapt lua code for name and number
+
+\permanent\def\structurenumber {\clf_structurenumber}
+\permanent\def\structuretitle {\clf_structuretitle}
+\permanent\def\structurevariable #1{\clf_structurevariable {#1}}
+\permanent\def\structureuservariable #1{\clf_structureuservariable {#1}}
+\permanent\def\structurecatcodedget #1{\clf_structurecatcodedget {#1}} % bad name
+\permanent\def\structuregivencatcodedget #1#2{\clf_structuregivencatcodedget {#1}#2 } % bad name
+\permanent\def\structureautocatcodedget #1#2{\clf_structureautocatcodedget {#1}{#2}}
+\permanent\def\namedstructurevariable #1#2{\clf_namedstructurevariable {#1}{#2}}
+\permanent\def\namedstructureuservariable#1#2{\clf_namedstructureuservariable{#1}{#2}}
+
+% compatibility issue:
+%
+% \def\setfullsectionnumber #1{}
+% \def\preparefullnumber #1{}
+% \def\fullsectionnumber {1--1--1}
+% \def\makesectionnumber [#1]{}
+% \def\makesectionformat {}
+% \def\sectionformat {1--1-1-1-1-1-1}
+% \def\composedsectionnumber{}
+% \def\@@kolist{}
+
+% \setuphead[section] [separator=\separatorlist{?,!,*}]
+% \setuphead[subsection][separator=\separatorlist{??,!!,**}]
+%
+% \let\spr\separatorlist % this will enable this feature
+%
+% \setuphead[section] [separator={?,!,*}]
+% \setuphead[subsection][separator={??,!!,**}]
+%
+% \setupheads[separator={A,B,C,D,E,F}]
+% \chapter{test}
+% \section{test} \subsection{test} \subsection{test}
+% \section{test} \subsection{test} \subsection{test}
+
+% lua interface / names and interface might change
+
+% \newconditional\c_strc_rendering_continuous % not used (mkii ?)
+
+\permanent\def\setstructurelevel #1#2{\clf_setstructurelevel {#1}{#2}} % name, level|parent
+\permanent\def\getstructurelevel #1{\clf_getstructurelevel {#1}} % name
+\permanent\def\setstructurenumber #1#2{\clf_setstructurenumber #1{#2}} % level, number (+/-)
+\permanent\def\getstructurenumber #1{\clf_getstructurenumber \numexpr#1\relax} % level
+\permanent\def\getsomestructurenumber #1#2{\clf_getsomestructurenumber #1{#2}} % level, what
+\permanent\def\getfullstructurenumber #1{\clf_getfullstructurenumber \numexpr#1\relax} % level
+\permanent\def\getsomefullstructurenumber#1#2{\clf_getsomefullstructurenumber #1{#2}} % level, what
+\permanent\def\getspecificstructuretitle #1{\clf_getspecificstructuretitle {#1}{\headparameter\s!catcodes}}
+
+% structure heads (like \startchapter)
+
+% \c!deeptextcommand, \c!deepnumbercommand: undefined !
+% \c!before \c!after \c!distance
+% \c!page \c!header \c!text \c!footer=,
+% \c!numbercommand \c!textcommand \c!ownnumber \c!number
+% \c!file \c!grid \c!margintext
+% \c!expansion \c!xmlsetup \s!catcode
+
+\installcorenamespace{head}
+\installcorenamespace{headlevel}
+\installcorenamespace{headincrement}
+\installcorenamespace{headplace}
+\installcorenamespace{headmarkyes}
+\installcorenamespace{headmarknop}
+
+\installcommandhandler \??head {head} \??head
+
+\installmacrostack\currenthead
+\installmacrostack\currentheadparent
+
+\setuphead [%
+ %\c!after=,
+ %\c!align=,
+ %\c!aligntitle=,
+ \c!alternative=\v!normal,
+ %\c!before=,
+ %\c!color=,
+ %\c!command=,
+ \c!continue=\v!yes,
+ %\c!coupling=,
+ %\c!deepnumbercommand=,
+ %\c!deeptextcommand=,
+ %\c!default=,
+ \c!distance=\zeropoint,
+ \c!textdistance=\zeropoint,
+ \c!textwidth=\zeropoint, % signal too
+ \c!numberwidth=\zeropoint, % signal too
+ \c!width=\zeropoint, % signal too
+ \c!expansion=\v!no,
+ %\c!file=,
+ %\c!footer=,
+ %\c!grid=,
+ \c!hang=\v!none,
+ %\c!header=,
+ \c!incrementnumber=\v!yes,
+ \c!indentnext=\v!no,
+ %\c!label=,
+ %\c!limittext=\languageparameter\c!limittext,
+ \c!margin=\zeropoint,
+ %\c!margintext=,
+ \c!number=\v!yes,
+ \c!numbercolor=\headparameter\c!color,
+ \c!textcolor=\headparameter\c!color,
+ \c!numberstyle=\headparameter\c!style,
+ \c!textstyle=\headparameter\c!style,
+ %\c!numbercommand=,
+ %\c!textcommand=,
+ \c!ownnumber=\v!no,
+ %\c!page=,
+ \c!placehead=\v!yes,
+ \c!sectionconversionset=\s!default,
+ \c!sectionnumber=\v!yes,
+ %\c!sectionsegments=,
+ \c!sectionseparatorset=\s!default,
+ \c!sectionset=\v!all,
+ \c!interlinespace=,
+ %\c!sectionstopper=,
+ %\c!sectionstarter=,
+ %\c!strut=,
+ %\c!style=,
+ %\c!text=,
+ %\c!tolerance=,
+ %\c!beforesection=\directsetup{document:\currenthead:start}, % these might become defaults i.e. acpect document: namespace
+ %\c!insidesection=\directsetup{document:\currenthead:inside}, % these might become defaults i.e. acpect document: namespace
+ %\c!aftersection=\directsetup{document:\currenthead:stop}, % these might become defaults i.e. acpect document: namespace
+ ]
+
+\aliased\let\setupheads\setuphead % will go
+
+\appendtoks
+ \ifempty\currentheadparent
+ \edef\currentheaddefault{\headparameter\c!default}%
+ \edef\currentheadsection{\headparameter\c!section}%
+ \ifx\currenthead\currentheaddefault
+ \let\currentheadparent\currentheadsection
+ \orelse\ifempty\currentheaddefault
+ \let\currentheadparent\currentheadsection
+ \else
+ \let\currentheadparent\currentheaddefault
+ \fi
+ \normalexpanded {%
+ \setheadparameter{\c!label}{\currenthead}%
+ \setheadparameter{\c!coupling}{\currenthead}%
+ \setheadparameter{\s!parent}{\??head\currentheadparent}%
+ \definemarking[\currenthead] [\currentheadsection]%
+ \definemarking[\currenthead\v!number][\currentheadsection]%
+ \setupmarking [\currenthead] [\c!filtercommand=\noexpand\sectionheadmarkingtitle {\currenthead}]%
+ \setupmarking [\currenthead\v!number][\c!filtercommand=\noexpand\sectionheadmarkingnumber{\currenthead}]%
+ }%
+ \doifelselist\currenthead\donothing
+ {\definelist[\currenthead][\c!prefix=\v!no]}%
+ % we can't do this now for backward compatibility reasons
+ % \doifelselist\currenthead\donothing
+ % {\normalexpanded{\definelist[\currenthead][\currentheadparent][\c!prefix=\v!no]}}%
+ \else
+ \normalexpanded {%
+ \setheadparameter{\c!label}{\currenthead}%
+ \setheadparameter{\c!coupling}{\currentheadparent}%
+ \definemarking[\currenthead] [\currentheadparent]%
+ \definemarking[\currenthead\v!number][\currentheadparent\c!number]%
+ }%
+ \doifelselist\currenthead\donothing
+ {\normalexpanded{\definelist[\currenthead][\currentheadparent][\c!prefix=\v!no]}}%
+ \fi
+ \presetlabeltext[\currenthead=]%
+ \the\everysetuphead
+\to \everydefinehead
+
+\newtoks\everyredefinehead
+
+\appendtoks
+ \the\everyredefinehead
+\to \everydefinehead
+
+\appendtoks
+ \setstructurelevel\currenthead{\thenamedheadlevel\currenthead}%
+\to \everyredefinehead
+
+\mutable\let\currentsectionheadcoupling\empty
+\mutable\let\currentsectionheadsection \empty
+\mutable\let\currentsectionlevel \empty
+
+\appendtoks
+ % beware, this is a global register
+ \begingroup
+ \edef\currentsectionheadcoupling{\sectionheadcoupling\currenthead}%
+ \edef\currentsectionheadsection {\sectionheadsection \currentsectionheadcoupling}%
+ \edef\currentsectionlevel {\sectionlevel \currentsectionheadsection}%
+ \clf_registersection {\currenthead} {
+ coupling {\currentsectionheadcoupling}
+ section {\currentsectionheadsection}
+ level \space \currentsectionlevel \space % space needed for parser
+ parent {\currentheadparent}
+ }%
+ \endgroup
+\to \everyredefinehead
+
+\appendtoks
+ \frozen\instance\protected\edefcsname\e!start\currenthead\endcsname{\strc_sectioning_start[\currenthead]}%
+ \frozen\instance\protected\edefcsname\e!stop \currenthead\endcsname{\strc_sectioning_stop [\currenthead]}%
+\to \everydefinehead
+
+% so \subject as well as \section will need two commands when ownnumber
+% is used (one can disable it anyway for subject) .. this is not downward
+% compatible but better
+
+\appendtoks
+ \ifempty\currenthead \else
+ \doifelse{\headparameter\c!ownnumber}\v!yes
+ {\instance\protected\edefcsname\currenthead\endcsname{\strc_sectioning_handle_own[\currenthead]}}
+ {\instance\protected\edefcsname\currenthead\endcsname{\strc_sectioning_handle_nop[\currenthead]}}%
+ \fi
+\to \everysetuphead
+
+\permanent\protected\def\doredefinehead#1#2% called at lua end
+ {\push_macro_currenthead
+ \push_macro_currentheadparent
+ \edef\currenthead{#1}%
+ \edef\currentheadparent{#2}%
+ \the\everyredefinehead\relax
+ \pop_macro_currentheadparent
+ \pop_macro_currenthead}
+
+\mutable\let\currentnamedsection\empty
+
+\installmacrostack\currentnamedsection
+
+% structure sections (the parents of chapter etc)
+
+\aliased\let\firstsectionname\empty
+\aliased\let\lastsectionname \empty
+
+%aliased\let\resetallstructuremarks \relax
+%aliased\let\resetcurrentstructuremarks \relax
+\aliased\let\resetcurrentstructuremarkswithpage\relax
+
+\permanent\protected\def\resetallstructuremarks {\resetmarking[\firstsectionname]} % will become option (was \v!section-1)
+\permanent\protected\def\resetcurrentstructuremarks {\resetmarking[\lastsectionname]} % will become option
+%permanent\protected\def\resetcurrentstructuremarkswithpage{\resetmarking[\lastsectionname]} % will become option
+
+% We could use a commandhandler here but sections are somewhat special in the
+% sense that we have two ways of chaining: the main section (levels) as well
+% as rendering (head).
+
+% -2 = text
+% -1 = manual
+% 0 = block
+% +1 = structurelevel 1 .. n
+
+\newcount\maxstructuredepth
+
+\permanent\def\sectionlevel#1%
+ {\csname\??headlevel\ifcsname\??headlevel#1\endcsname#1\else\v!none\fi\endcsname}
+
+\permanent\def\namedsectionlevel#1#2% direct indirect
+ {\csname\??headlevel
+ \ifcsname\??headlevel#1\endcsname
+ #1%
+ \orelse\ifcsname\??headlevel#2\endcsname
+ #2%
+ \else
+ \v!none
+ \fi
+ \endcsname}
+
+\permanent\def\xthenamedheadlevel#1%
+ {\namedsectionlevel{#1}{\sectionheadsection{\sectionheadcoupling{#1}}}}
+
+\defcsname\??headlevel\v!block\endcsname{0}
+\defcsname\??headlevel\v!none \endcsname{-1}
+\defcsname\??headlevel\v!text \endcsname{-2}
+\defcsname\??headlevel\v!head \endcsname{-3}
+
+\newtoks\everydefinesection
+
+\mutable\let\currentsection\empty % historic alias
+
+\permanent\protected\def\definesection[#1]%
+ {\ifcsname\??headlevel#1\endcsname \else
+ \edef\currenthead{#1}%
+ \let\currentsection\currenthead % just an alias
+ \global\advance\maxstructuredepth\plusone
+ \setevalue{\??headlevel#1}{\the\maxstructuredepth}%
+ \setstructurelevel{#1}{\sectionlevel{#1}}%
+ \normalexpanded{\setheadparameter{\s!parent}{\??head\lastsectionname}}% TO BE CHECKED, WE HAVE A HELPER
+ \the\everydefinesection
+ % so far for these default inheritances
+ \definemarking[#1]%
+ \ifnum\maxstructuredepth>\plusone
+ \normalexpanded{\relatemarking[#1][\lastsectionname]}% so, the parent will reset the child
+ \fi
+ \enforced\permanent\xdef\lastsectionname{#1}%
+ \ifempty\firstsectionname
+ \enforced\glet\firstsectionname\lastsectionname
+ \fi
+ \fi}
+
+\permanent\tolerant\protected\def\setupsection[#1]#*[#2]#*[#3]%
+ {\ifparameter#1\else
+ \push_macro_currenthead
+ \edef\currenthead{\ifcsname\??headlevel#1\endcsname#1\else\sectionheadsection{#1}\fi}%
+ \ifparameter#3\or
+ \edef\currenthead{\currenthead#2}% not used at any more in mkiv (sets now)
+ \setupcurrenthead[#3]%
+ \else
+ \setupcurrenthead[#2]%
+ \fi
+ \pop_macro_currenthead
+ \fi}
+
+% we share the parameters as sections are roots of heads so eventually we can
+% consider \definesection -> \definehead with one argument
+
+\appendtoks
+ % This is a rather practical default that we don't want to
+ % be part of the parent chain lookup mechanism; it's also
+ % mkii compatible. Somewhat weird that it's part of the
+ % top level structure but it will be flattened anyway.
+ % \let\currenthead\currentsection % hm
+ \setheadparameter\c!textstyle {\directheadparameter\c!style}%
+ \setheadparameter\c!textcolor {\directheadparameter\c!color}%
+ \setheadparameter\c!numberstyle{\directheadparameter\c!style}%
+ \setheadparameter\c!numbercolor{\directheadparameter\c!color}%
+\to \everydefinesection
+
+% head -> head
+
+\permanent\def\sectionheadmarkingtitle #1#2{\clf_markingtitle {#1}{#2}} % can be done at lua end
+\permanent\def\sectionheadmarkingnumber#1#2{\clf_markingnumber{#1}{#2}} % can be done at lua end
+
+\permanent\def\sectionheadcoupling#1{\namedheadparameter{#1}\c!coupling}
+\permanent\def\sectionheadsection #1{\namedheadparameter{#1}\c!section}
+
+% head construction
+
+\newconditional\currentstructureown
+
+\newtoks\everybeforehead % hook, todo: before/after keys
+\newtoks\everyafterhead % hook, todo: before/after keys
+
+\permanent\tolerant\protected\def\strc_sectioning_handle_own[#1]#*[#2]#:#*#=#*#=% [ref] {nr} {title}
+ {\settrue\currentstructureown
+ \triggerautostructurelevel
+ \strc_sectioning_handle{#1}{\c!reference={#2},\c!ownnumber={#3},\c!title={#4}}{}} % name ref nr title --
+
+\permanent\tolerant\protected\def\strc_sectioning_handle_nop[#1]#*[#2]% [ref] {title} / for taco: [key=value] variant
+ {\setfalse\currentstructureown
+ \triggerautostructurelevel
+ \ifhastok={#2}%
+ \expandafter\strc_sectioning_handle_nop_indeed_yes
+ \else
+ \expandafter\strc_sectioning_handle_nop_indeed_nop
+ \fi
+ {#1}{#2}}
+
+\def\strc_sectioning_handle_nop_indeed_yes#1#2%
+ {\strc_sectioning_handle{#1}{#2}{}}
+
+\def\strc_sectioning_handle_nop_indeed_nop#1#2#3%
+ {\strc_sectioning_handle{#1}{\c!reference={#2},\c!title={#3}}{}} % name ref nr title --
+
+\permanent\tolerant\protected\def\strc_sectioning_start[#1]#*[#2]#*[#3]% for the moment no grouping, too annoying with page breaks
+ {\push_macro_currentnamedsection
+ \push_macro_currentstructurereferenceprefix
+ \edef\currentnamedsection{#1}%
+ \setfalse\currentstructureown
+ %\globalpushmacro\currenthead % this does not work out well
+ \xdef\currenthead{#1}%
+ \setsystemmode\currenthead % new, also here now
+ \headparameter\c!beforesection % beware, no users vars set yet
+ \the\everybeforehead
+ \strc_sectioning_handle{#1}{#2}{#3}% name -- -- -- userdata (we might move the tagged to here)
+ % potential: \bgroup (can be optional: grouped = yes)
+ \headparameter\c!insidesection}
+
+\permanent\protected\def\strc_sectioning_stop[#1]% !!! also used at lua end
+ {\dostoptagged
+ \dostoptagged
+ % potential: \egroup
+ %\globalpopmacro\currenthead % so we do a hard recover
+ \xdef\currenthead{#1}% recover
+ \headparameter\c!aftersection
+ \the\everyafterhead
+ \resetsystemmode\currenthead
+ \pop_macro_currentstructurereferenceprefix
+ \pop_macro_currentnamedsection} % new, also here now
+
+\aliased\let\dostarthead\strc_sectioning_start % used at lua end
+\aliased\let\dostophead \strc_sectioning_stop % used at lua end
+
+% todo: add grouping but where: before/after trickery .. probably inside because one can always add
+% grouping to the before/after settings
+
+\aliased\let\startnamedsection\strc_sectioning_start
+
+\permanent\protected\def\stopnamedsection
+ {\normalexpanded{\strc_sectioning_stop[\currentnamedsection]}}
+
+% \newconditional\structurereversesectionnumbers % todo: key/val
+
+\newconditional\c_strc_sectioning_to_list
+\newconditional\c_strc_sectioning_increment
+\newconditional\c_strc_sectioning_place
+\newconditional\c_strc_sectioning_empty
+\newconditional\c_strc_sectioning_hidden
+\newconditional\c_strc_sectioning_section
+
+\newconditional\headshownumber % public
+\newconditional\headisdisplay % public
+\newconditional\headissomewhere % public
+
+\defcsname\??headincrement\v!yes \endcsname{\settrue \c_strc_sectioning_increment\settrue \c_strc_sectioning_to_list}
+\defcsname\??headincrement\v!no \endcsname{\setfalse\c_strc_sectioning_increment\setfalse\c_strc_sectioning_to_list}
+\defcsname\??headincrement\v!list \endcsname{\setfalse\c_strc_sectioning_increment\settrue \c_strc_sectioning_to_list}
+\defcsname\??headincrement\s!empty\endcsname{\settrue \c_strc_sectioning_increment\settrue \c_strc_sectioning_to_list}
+
+\protected\def\strc_sectioning_initialize_increment
+ {\edef\currentheadincrement{\headparameter\c!incrementnumber}%
+ \ifcsname\??headincrement\currentheadincrement\endcsname
+ \lastnamedcs
+ \else
+ \settrue \c_strc_sectioning_increment\settrue \c_strc_sectioning_to_list
+ % \filterheadnumber
+ \fi}
+
+\permanent\protected\def\filterheadnumber
+ {\settrue\c_strc_sectioning_increment
+ \settrue\c_strc_sectioning_to_list
+ \ifempty\currentproduct
+ % todo : filter from other toc (number, file, title)
+ % use : \currentheadincrement as spec
+ \fi}
+
+\defcsname\??headplace\v!yes\endcsname
+ {\setfalse\c_strc_sectioning_empty
+ \settrue \c_strc_sectioning_place
+ \setfalse\c_strc_sectioning_hidden
+ \setfalse\c_strc_sectioning_section}
+
+\defcsname\??headplace\v!empty\endcsname
+ {\settrue \c_strc_sectioning_empty
+ \settrue \c_strc_sectioning_place
+ \setfalse\c_strc_sectioning_hidden
+ \setfalse\c_strc_sectioning_section}
+
+\defcsname\??headplace\v!no\endcsname
+ {\settrue \c_strc_sectioning_empty
+ \setfalse\c_strc_sectioning_place
+ \setfalse\c_strc_sectioning_hidden
+ \setfalse\c_strc_sectioning_section}
+
+\defcsname\??headplace\v!hidden\endcsname
+ {\settrue \c_strc_sectioning_empty
+ \setfalse\c_strc_sectioning_place
+ \settrue \c_strc_sectioning_hidden
+ \setfalse\c_strc_sectioning_section}
+
+\defcsname\??headplace\v!section\endcsname
+ {\settrue \c_strc_sectioning_empty
+ \setfalse\c_strc_sectioning_place
+ \settrue \c_strc_sectioning_hidden
+ \settrue \c_strc_sectioning_section}
+
+\protected\def\strc_sectioning_initialize_placement
+ {\expandnamespaceparameter\??headplace\headparameter\c!placehead\v!yes}
+
+\newmode\v!sectionnumber
+
+\def\strc_sectioning_set_head_number_content % was: \dosetstructureheadnumbercontent
+ {\setsystemmode\v!sectionnumber
+ \settrue\headshownumber} % why ?
+
+\def\strc_sectioning_reset_head_number_content % was: \doresetstructureheadnumbercontent
+ {\resetsystemmode\v!sectionnumber
+ \setfalse\headshownumber} % why ?
+
+\protected\def\strc_sectioning_initialize_number
+ {\edef\p_number{\sectionblockparameter\c!number}%
+ \ifx\p_number\v!yes
+ \edef\p_number{\headparameter\c!number}%
+ \ifx\p_number\v!yes
+ \settrue\headshownumber
+ \else
+ \setfalse\headshownumber
+ \fi
+ \else
+ \setfalse\headshownumber
+ \fi}
+
+% Beware, we do need some node for anchoring marks and normally a zwnj will
+% do but it interferes so we deal with it at the \LUA\ end.
+
+\newtoks\everyheadsynchronization
+
+% \appendtoks
+% \currentstructuresynchronize
+% \to \everyheadsynchronization
+
+\aliased\let\currentstructuresynchronize\donothing
+
+\appendtoks
+ \currentstructuresynchronize
+ \enforced\glet\currentstructuresynchronize\donothing
+\to \everyheadsynchronization
+
+\permanent\protected\def\theheadsynchronization % public
+ {% no, interferes: \signalcharacter
+ \the\everyheadsynchronization}
+
+% BEWARE: \marking[section]{my text} does not work as we use list indices instead
+% so we need a 'keep track of raw set option' (or maybe a funny internal prefix)
+
+\permanent\protected\def\setheadmarking % li:: so that we can use \marking[section]{Taco needed this}
+ {\strc_sectioning_delayed_flush
+ \normalexpanded{\setmarking[\currenthead]{li::\currentstructurelistnumber}}}
+
+\mutable\let\deepstructurenumbercommand\relax
+\mutable\let\deepstructuretitlecommand \relax
+
+\permanent\protected\def\fullheadnumber
+ {\edef\currentheadlabeltag{\currentsectionblock\c!label}%
+ \dostarttagged\t!sectionnumber\empty
+ \labeltexts
+ {\headparameter\currentheadlabeltag}
+ {\ifrelax\deepstructurenumbercommand
+ \structurenumber
+ \else
+ \normalexpanded{\noexpand\deepstructurenumbercommand{\structurenumber}}%
+ \fi}%
+ \dostoptagged}
+
+\permanent\protected\def\fullheadtitle
+ {\dostarttagged\t!sectiontitle\empty
+ \ifrelax\deepstructuretitlecommand
+ \structuretitle
+ \else
+ \normalexpanded{\noexpand\deepstructuretitlecommand{\structuretitle}}%
+ \fi
+ \dostoptagged}
+
+\mutable\let\currenthead \empty
+\mutable\let\currentheadcoupling\empty
+\mutable\let\currentheadsection \empty
+\mutable\let\currentheadlevel \!!zerocount
+\mutable\let\currentheadcounter \!!zerocount
+
+\let\strc_show_used\relax
+
+\installtextracker
+ {structures.showused}
+ {\let\strc_show_used\clf_showstructure}
+ {\let\strc_show_used\relax}
+
+\appendtoks
+ \strc_show_used
+\to \everystoptext
+
+\protected\def\strc_sectioning_report{\clf_reportstructure}
+
+\ifdefined\strc_rendering_initialize_style_and_color \else
+
+ \protected\def\strc_rendering_initialize_style_and_color#1#2%
+ {\dontconvertfont
+ \useheadstyleandcolor\c!style\c!color
+ \useheadstyleandcolor#1#2%
+ \setupinterlinespace}
+
+\fi
+
+\permanent\tolerant\protected\def\placeheadtext[#1]%
+ {\dontleavehmode
+ \begingroup
+ \protected\def\\{\space}% messy here, but the default (and needs to be grouped)
+ \global\settrue\headisdisplay % triggers interlinespace checking
+ \edef\currenthead{#1}% maybe only when #1 is given
+ \strc_rendering_initialize_style_and_color\c!textstyle\c!textcolor
+ \relax
+ \getspecificstructuretitle{\thenamedheadlevel{#1}}%
+ \endgraf
+ \endgroup}
+
+\permanent\tolerant\protected\def\placeheadnumber[#1]%
+ {\dontleavehmode
+ \begingroup
+ \global\settrue\headisdisplay % triggers interlinespace checking
+ \edef\currenthead{#1}% maybe only when #1 is given
+ \strc_rendering_initialize_style_and_color\c!numberstyle\c!numbercolor
+ \relax
+ \getfullstructurenumber{\thenamedheadlevel{#1}}%
+ \endgraf
+ \endgroup}
+
+\ifdefined\triggerautostructurelevel \else \let\triggerautostructurelevel\relax \fi
+
+\newtoks\everybeforesectionheadhandle
+\newtoks\everyaftersectionheadhandle
+
+\mutable\let\getheadnumber\empty
+\mutable\let\getheadtitle \empty
+\mutable\let\getheadsyncs \empty
+
+\def\strc_sectioning_handle#1#2#3% name data userdata (we can move #1 to the caller)
+ {\xdef\currenthead {#1}%
+ \xdef\currentheadcoupling{\sectionheadcoupling\currenthead}%
+ \xdef\currentheadsection {\sectionheadsection \currentheadcoupling}%
+ \xdef\currentheadlevel {\sectionlevel \currentheadsection}%
+ %
+ %\writestatus\m!system{setup: \currenthead,\currentheadcoupling,\currentheadsection,\currentheadlevel}%
+ %
+ \strc_sectioning_initialize_autolevel
+ \strc_sectioning_initialize_increment
+ \strc_sectioning_initialize_placement
+ \strc_sectioning_initialize_number
+ %
+ \the\everybeforesectionheadhandle
+ %
+ % todo: also mark (for header)
+ %
+ % we might remove the lower level
+ %
+ % not here, after optional \page: \strc_sectioning_register{#1}{#2}{#3}%
+ %
+% \xdef\currentheadcounter{\currentsectioncountervalue}% lua call
+ %
+ % \currentstructuresynchronize % will move
+ %
+ \let\getheadnumber\empty
+ \let\getheadtitle \empty
+ \let\getheadsyncs \empty
+ \ifconditional\c_strc_sectioning_increment
+ \ifconditional\c_strc_sectioning_place
+ \strc_sectioning_before_yes
+ \strc_sectioning_register{#1}{#2}{#3}% after optional \page
+ \strc_sectioning_report
+ \dostarttagged\t!sectioncaption\empty
+ \let\getheadsyncs\theheadsynchronization
+ \let\getheadtitle\fullheadtitle
+ \ifconditional\headshownumber
+ \let\getheadnumber\fullheadnumber
+ \strc_rendering_place_head_number_and_text
+ \else
+ \strc_rendering_place_head_text
+ \fi
+ \dostoptagged
+ \strc_sectioning_after_yes
+ \orelse\ifconditional\c_strc_sectioning_hidden
+ \strc_sectioning_register{#1}{#2}{#3}% after optional \page
+ \strc_sectioning_report
+ \dostarttagged\t!sectioncaption\empty
+ \let\getheadsyncs\theheadsynchronization
+ \ifconditional\c_strc_sectioning_section
+ \strc_rendering_place_head_section
+ \else
+ \strc_rendering_place_head_hidden % only something when tracing
+ \fi
+ \dostoptagged
+ \else
+ \strc_sectioning_before_nop % toegevoegd ivm subpaginanr / tug sheets
+ \strc_sectioning_register{#1}{#2}{#3}% after optional \page
+ \strc_sectioning_report
+ \dostarttagged\t!sectioncaption\empty
+ \let\getheadsyncs\theheadsynchronization
+ \strc_rendering_place_head_empty % just flush 'm
+ \dostoptagged
+ \strc_sectioning_after_nop
+ \fi
+ \else
+ \ifconditional\c_strc_sectioning_place
+ \strc_sectioning_before_yes
+ \strc_sectioning_register{#1}{#2}{#3}% after optional \page
+ \strc_sectioning_report
+ \dostarttagged\t!sectioncaption\empty
+ \let\getheadsyncs\theheadsynchronization
+ \let\getheadtitle\fullheadtitle
+ \strc_rendering_place_head_text
+ \dostoptagged
+ \strc_sectioning_after_yes
+ \orelse\ifconditional\c_strc_sectioning_hidden
+ \strc_sectioning_register{#1}{#2}{#3}% after optional \page
+ \strc_sectioning_report
+ \let\getheadsyncs\theheadsynchronization
+ \dostarttagged\t!sectioncaption\empty
+ \ifconditional\c_strc_sectioning_section
+ \strc_rendering_place_head_section
+ \else
+ \strc_rendering_place_head_hidden % only something when tracing
+ \fi
+ \dostoptagged
+ \else
+ % do nothing / should be vbox to 0pt
+ \strc_sectioning_before_nop
+ \strc_sectioning_register{#1}{#2}{#3}% after optional \page
+ \strc_sectioning_report
+ \dostarttagged\t!sectioncaption\empty
+ \let\getheadsyncs\theheadsynchronization
+ \strc_rendering_place_head_empty % just flush 'm
+ \dostoptagged
+ \strc_sectioning_after_nop
+ \fi
+ \fi
+ %
+ \the\everyaftersectionheadhandle
+ %
+ \setfalse\c_strc_sectioning_ignore_page
+ % ignorespaces prevents spaces creeping in when after=\dontleavehmode
+ \dostarttagged\t!sectioncontent\empty
+ \ifempty\currentstructureplaceholder
+ \ifconditional\headisdisplay
+ \doubleexpandafter\ignorespaces
+ \else
+ \doubleexpandafter\ignorepars
+ \fi
+ \else
+ \expandafter\strc_sectioning_placeholder
+ \fi}
+
+%D \starttyping
+%D \startsubject[placeholder=todo,title=one]
+%D whatever one
+%D \stopsubject
+%D \stoptyping
+
+\def\strc_sectioning_placeholder
+ {\placeholder[\currentstructureplaceholder]%
+ \gobblenested{\e!start\currenthead}{\e!stop\currenthead}{\e!stop\currenthead}}
+
+% typesetting (the getters are public)
+
+\protected\def\strc_rendering_place_head_number_and_text
+ {\setheadmarking
+ \getheadnumber/\getheadtitle
+ \getheadsyncs}
+
+\protected\def\strc_rendering_place_head_text
+ {\setheadmarking
+ \getheadtitle
+ \getheadsyncs}
+
+\protected\def\strc_rendering_place_head_empty
+ {\setheadmarking
+ \getheadsyncs}
+
+\installcorenamespace{hiddenheadattr}
+\installcorenamespace{hiddenheadsync}
+
+% todo: when in the page builder we need to resolve the marking immediately
+% because otherwise we have an async
+
+\newbox\b_sectioning_delayed
+
+\def\strc_sectioning_delayed_flush
+ {\ifvoid\b_sectioning_delayed\else
+ \smashedbox\b_sectioning_delayed
+ \fi}
+
+\protected\def\strc_rendering_place_head_section % see hidden below
+ {\global\setbox\b_sectioning_delayed\hpack\bgroup
+ \setmarking[\currentheadcoupling]{li::\currentstructurelistnumber}%
+ \hpack\headreferenceattributes{}% also does the mark
+ \theheadsynchronization
+ \egroup}
+
+\protected\def\strc_rendering_place_head_hidden % maybe trialtypesetting check
+ {\xdefcsname\??hiddenheadattr\currenthead\endcsname
+ {\headreferenceattributes}% can be used when making a box
+ \xdefcsname\??hiddenheadsync\currenthead\endcsname
+ {\noexpand\letgvalue{\??hiddenheadsync\currenthead}\relax
+ \noexpand\setmarking[\currentheadcoupling]{li::\currentstructurelistnumber}%
+ \hpack\headreferenceattributes{}% otherwise no destination ... maybe tag ref as hidden and fall back on page reference
+ \theheadsynchronization}} % and it's a node anyway
+
+\permanent\def\synchronizehead #1{\begincsname\??hiddenheadsync#1\endcsname}
+\permanent\def\theheadreferenceattributes#1{\begincsname\??hiddenheadattr#1\endcsname}
+
+\permanent\protected\def\placerawheaddata [#1]{\synchronizehead{#1}}
+\permanent\protected\def\placerawheadtext [#1]{\getspecificstructuretitle{\thenamedheadlevel{#1}}}
+\permanent\protected\def\placerawheadnumber[#1]{\getfullstructurenumber{\thenamedheadlevel{#1}}}
+
+\permanent\protected\def\repeathead[#1]%
+ {\begingroup
+ \setupinteraction[\c!state=\v!stop]%
+ \def\currenthead{#1}
+ \strc_sectioning_initialize_placement
+ \strc_sectioning_initialize_number
+ \dostarttagged\t!sectioncaption\empty
+ \let\getheadsyncs \relax
+ \def\getheadtitle {\getmarking[#1]}
+ \def\getheadnumber{\getmarking[#1\v!number]}
+ \strc_sectioning_before_yes
+ \ifconditional\headshownumber
+ \strc_rendering_place_head_number_and_text
+ \else
+ \strc_rendering_place_head_text
+ \fi
+ \dostoptagged
+ \strc_sectioning_after_yes
+ \endgroup}
+
+% \setuphead[chapter][placehead=hidden]
+% \chapter {test}
+%
+% %(\synchronizehead{chapter}) % \getheadsyncs
+% %(\getfullstructurenumber{\thenamedheadlevel{chapter}})
+% %(\getspecificstructuretitle{\thenamedheadlevel{chapter}})
+%
+% (\placerawheaddata [chapter])
+% (\placerawheadnumber[chapter])
+% (\placerawheadtext [chapter])
+
+% pagebreaks
+
+\letcsname\??headmarknop\v!page \endcsname\donothing
+\defcsname\??headmarknop\v!reset \endcsname{\resetcurrentstructuremarks}
+\letcsname\??headmarknop\s!unknown\endcsname\donothing
+
+\letcsname\??headmarkyes\v!page \endcsname\donothing % to be checked: {\resetcurrentstructuremarks}
+\defcsname\??headmarkyes\v!reset \endcsname{\resetcurrentstructuremarks}
+\letcsname\??headmarkyes\s!unknown\endcsname\donothing
+
+\def\strc_sectioning_check_layout
+ {\edef\p_page{\headparameter\c!page}%
+ \ifempty\p_page
+ \strc_sectioning_check_layout_nop
+ \else
+ \strc_sectioning_check_layout_yes
+ \fi}
+
+\def\strc_sectioning_check_layout_nop
+ {\expandnamespaceparameter\??headmarknop\headparameter\c!marking\s!unknown}
+
+\def\strc_sectioning_check_layout_yes
+ {\page[\p_page]%
+ \expandnamespaceparameter\??headmarkyes\headparameter\c!marking\s!unknown
+ \edef\p_header{\headparameter\c!header}%
+ \ifempty\p_header \else
+ \doifelselayouttextline\v!header{\normalexpanded{\setuplayouttext[\v!header][\c!state=\p_header]}}\donothing
+ \fi
+ \edef\p_text{\headparameter\c!text}%
+ \ifempty\p_text \else
+ \doifelselayouttextline\v!text {\normalexpanded{\setuplayouttext[\v!text ][\c!state=\p_text ]}}\donothing
+ \fi
+ \edef\p_footer{\headparameter\c!footer}%
+ \ifempty\p_footer \else
+ \doifelselayouttextline\v!footer{\normalexpanded{\setuplayouttext[\v!footer][\c!state=\p_footer]}}\donothing
+ \fi}
+
+\newcount \c_strc_sectioning_preceding_level \c_strc_sectioning_preceding_level\plusone
+\newconditional\c_strc_sectioning_auto_break \settrue\c_strc_sectioning_auto_break
+\newconditional\c_strc_sectioning_ignore_page
+\newsignal \d_strc_sectioning_continuous_signal
+
+\protected\def\strc_sectioning_inject_continuous_signal
+ {\ifhmode
+ \hskip\d_strc_sectioning_continuous_signal\relax
+ \fi}
+
+% \let\dotagsectionlevel\relax
+
+\def\strc_sectioning_before_yes
+ {\strc_sectioning_check_before\strc_sectioning_handle_page_yes
+ \headparameter\c!inbetween
+ \dostarttaggedchained\t!section\currenthead\??head
+% \dotagsectionlevel
+ }
+
+\def\strc_sectioning_before_nop
+ {\strc_sectioning_check_before\strc_sectioning_handle_page_nop
+ \headparameter\c!inbetween
+ \dostarttagged\currenthead\empty} % this is a weird one .. needs checking
+
+\def\strc_sectioning_empty_correction
+ {\ifconditional\c_strc_sectioning_empty
+ % this needs checking
+ \penalty\plustenthousand
+ \vskip-\lineheight
+ \kern\zeropoint
+ \prevdepth\strutdepth
+ \fi}
+
+\def\strc_sectioning_after_nop
+ {}
+
+\def\strc_sectioning_check_before#1%
+ {\ifhmode
+ \scratchcounter\lastpenalty
+ \unpenalty % no beauty in this
+ \ifdim\lastskip=\d_strc_sectioning_continuous_signal
+ % no page break
+ \ifconditional\c_strc_sectioning_ignore_page
+ \setfalse\c_strc_sectioning_ignore_page
+ \else
+ \global\c_strc_sectioning_preceding_level\currentheadlevel
+ \nobreak
+ \fi
+ % \global\settrue\c_strc_rendering_continuous
+ \else
+ \penalty\scratchcounter
+ % \global\setfalse\c_strc_rendering_continuous
+ #1%
+ \fi
+ \else
+ % \global\setfalse\c_strc_rendering_continuous
+ #1%
+ \fi}
+
+%permanent\def\currentsectioncountervalue {\clf_depthnumber\numexpr\thenamedheadlevel\currenthead\relax}
+%permanent\def\previoussectioncountervalue{\clf_depthnumber\numexpr\thenamedheadlevel\currenthead+\minusone\relax}
+
+\permanent\def\currentsectioncountervalue {\clf_depthnumber\numexpr\thenamedheadlevel\currenthead\relax}
+%permanent\def\previoussectioncountervalue{\clf_depthnumber\numexpr\thenamedheadlevel\currenthead+\minusone\relax}
+\permanent\def\previoussectioncountervalue{\clf_depthnumber\numexpr\thenamedheadlevel\previoushead\relax}
+
+\mutable\let\previoushead\empty
+
+\def\strc_sectioning_handle_page_nop
+ {\edef\p_continue{\headparameter\c!continue}%
+ \ifx\p_continue\v!yes
+ \ifx\currenthead\previoushead % not really needed
+ \strc_sectioning_check_layout
+ \orelse\ifnum\previoussectioncountervalue=\zerocount
+ \strc_sectioning_check_layout
+ \orelse\ifnum\currentsectioncountervalue>\zerocount
+ \strc_sectioning_check_layout
+ \fi
+ \else
+ \strc_sectioning_check_layout
+ \fi
+ \glet\previoushead\currenthead}
+
+
+\def\strc_sectioning_handle_page_yes
+ {\ifconditional\c_strc_sectioning_ignore_page
+ \setfalse\c_strc_sectioning_ignore_page
+ \else
+ % beware, these numbers are not yet know here
+ \strc_sectioning_handle_page_nop
+ \edef\p_aligntitle{\headparameter\c!aligntitle}%
+ \ifx\p_aligntitle\v!float
+ \ifconditional\c_strc_sectioning_auto_break
+ \spac_vspacing_same_page\currentheadlevel\zerocount
+ \fi
+ \headparameter\c!before\relax
+ \indent
+ \else
+ \page_otr_command_flush_side_floats
+ \ifconditional\c_strc_sectioning_auto_break
+ \spac_vspacing_same_page\currentheadlevel\zerocount
+ \fi
+ \headparameter\c!before\relax
+ \fi
+ \global\c_strc_sectioning_preceding_level\currentheadlevel
+ \fi
+ \glet\previoushead\currenthead}
+
+\def\strc_sectioning_depth_correction
+ {\ifvmode
+ \edef\p_depthcorrection{\headparameter\c!depthcorrection}%
+ \ifx\p_depthcorrection\v!strut
+ \prevdepth\strutdepth
+ \fi
+ \fi}
+
+\def\strc_sectioning_after_yes
+ {\ifconditional\headisdisplay
+ \ifconditional\c_strc_sectioning_auto_break
+ \spac_vspacing_same_page\currentheadlevel\plusone
+ \fi
+ \strc_sectioning_empty_correction
+ \headparameter\c!after
+ \strc_sectioning_depth_correction
+ \fi}
+
+\protected\def\strc_sectioning_prevent_page_break % see strc-con
+ {\ifconditional\c_strc_sectioning_auto_break
+ \spac_vspacing_same_page\currentheadlevel\plustwo
+ \fi}
+
+
+% We do support negative numbers but it can have side effects that we won't catch:
+%
+% \chapter{some} \setupheadnumber[chapter][3] \chapter{more}
+% \setupheadnumber[section][8] \section{b} \section{c} \setupheadnumber[section][-1] \section{d}
+
+\permanent\def\thenamedheadlevel#1%
+ {\sectionlevel{\sectionheadsection{\sectionheadcoupling{#1}}}}
+
+\permanent\tolerant\protected\def\setupheadnumber[#1]#*[#2]% todo: reset if at other level
+ {\setstructurenumber{\thenamedheadlevel{#1}}{\number#2}}
+
+\permanent\protected\def\determineheadnumber[#1]%
+ {\xdef\currentheadnumber{\getstructurenumber{\thenamedheadlevel{#1}}}}
+
+% The previous macro is been replaced by the expandable:
+
+\permanent\def\namedheadnumber #1{\getstructurenumber {\thenamedheadlevel{#1}}}
+\permanent\def\somenamedheadnumber#1#2{\getsomestructurenumber{\thenamedheadlevel{#1}}{#2}}
+
+\permanent\tolerant\protected\def\headnumber[#1]#*[#2]% simple case is just a number
+ {\getsomefullstructurenumber{\ifparameter#1\or\thenamedheadlevel{#1}\fi}{#2}}
+
+\permanent\tolerant\protected\def\someheadnumber[#1]#*[#2]%
+ {\dontleavehmode
+ \begingroup
+ \edef\currenthead{#1}%
+ \getsomefullstructurenumber{\thenamedheadlevel{#1}}{#2}%
+ \endgroup}
+
+% \mutable\def\currentheadtext{obsolete,\space use marks}
+
+% list references, will be redone in lua when we need it
+
+% \let\startlistreferences\relax
+% \let\stoplistreferences \relax
+
+% experimental
+
+\newconditional\c_strc_sectioning_auto_levels
+
+\appendtoks
+ \settrue\c_strc_sectioning_auto_levels
+\to \everyenableelements
+
+\protected\def\strc_sectioning_initialize_autolevel
+ {\ifconditional\c_strc_sectioning_auto_levels
+ \clf_autonextstructurelevel\currentheadlevel\relax
+ \global\setfalse\c_strc_sectioning_auto_levels
+ \fi}
+
+\permanent\protected\def\triggerautostructurelevel
+ {\global\settrue\c_strc_sectioning_auto_levels}
+
+\permanent\protected\def\finalizeautostructurelevels
+ {\clf_autofinishstructurelevels}
+
+\permanent\protected\def\finalizeautostructurelevel
+ {\dostoptagged
+ \dostoptagged}
+
+\appendtoks
+ \finalizeautostructurelevels
+\to \everystoptext
+
+\stopcontextdefinitioncode
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-syn.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-syn.mkxl
new file mode 100644
index 00000000000..df573a7e995
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-syn.mkxl
@@ -0,0 +1,680 @@
+%D \module
+%D [ file=strc-syn,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Synonyms and Sorting,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Synonyms and Sorting}
+
+\registerctxluafile{strc-syn}{}
+
+%D Although we could nowadays build this on top of regular lists we keep this
+%D more efficient variant around. Eventually we can add some options to lists
+%D that also provide such functionality but at the cost of much more overhead.
+%D
+%D We show a usage of both synonyms and sorts, which are deep down variants of
+%D so called simple lists. A definition looks like this:
+%D
+%D \startbuffer
+%D \definesynonyms
+%D [myabbreviation]
+%D
+%D \setupsynonyms
+%D [myabbreviation]
+%D [headstyle=bold,
+%D headcolor=darkred,
+%D synonymstyle=boldslanted,
+%D synonymcolor=darkblue,
+%D textstyle=slanted,
+%D textcolor=darkgreen,
+%D style=normal,
+%D color=darkyellow]
+%D
+%D \definesorting
+%D [mylogo]
+%D
+%D \setupsorting
+%D [mylogo]
+%D [style=bold,
+%D color=darkmagenta]
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D More complex definitions involves commands to call up meanings and such. The
+%D use of the defined commands is as follows: \
+%D
+%D \startbuffer
+%D \myabbreviation [FIRST] {TheFirst} {The First Words}
+%D \myabbreviation [SECOND] {TheSecond} {The Second Words}
+%D \myabbreviation [THIRD] {TheThird} {The Third Words}
+%D
+%D \mylogo [FOURTH] {TheFourth}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D By default a synonym is just typeset and flagges as being used, so that in
+%D a list it wil be shows with its meaning. You can however also expand the
+%D meaning automatically at first use:
+%D
+%D \startbuffer
+%D \setupsynonyms[myabbreviation][alternative=first]
+%D
+%D We have \FIRST, \SECOND\ and also \THIRD\ but no \FOURTH.
+%D
+%D We have \FIRST, \SECOND\ and also \THIRD\ but no \FOURTH.
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D We can change the order, as demonstrated in:
+%D
+%D \startbuffer
+%D \resetshownsynonyms[myabbreviation]
+%D
+%D \setupsynonyms[myabbreviation][alternative=last]
+%D
+%D We have \FIRST\ and \THIRD\ or \FOURTH.
+%D
+%D We have \FIRST\ and \THIRD\ or \FOURTH.
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D A list is called up with:
+%D
+%D \startbuffer
+%D \placelistofsynonyms[myabbreviation]
+%D
+%D \placelistofsorts[mylogo]
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D The lists are constructions (like descriptions are) and can be set up
+%D likewise.
+
+% todo: add 'define only' option to descriptions, then add sorting (also based on key)
+% and call to definition -> replaces this module
+
+\unprotect
+
+% split but common in lua
+
+\permanent\protected\def\preprocessexpansion#1#2#3#4% do this at the lua end if still needed
+ {\ifx#1\s!xml
+ \xmlstartraw
+ \xdef#2{#4}%
+ \xmlstopraw
+ \glet#3\s!xml
+ \else
+ \ifx#1\v!yes
+ \xdef#2{#4}%
+ \else
+ \xdef#2{\detokenize{#4}}%
+ \fi
+ \glet#3\s!tex
+ \fi}
+
+%D We now use a simple list variant:
+
+\installcorenamespace {simplelist}
+
+\installcommandhandler \??simplelist {simplelist} \??simplelist
+
+\aliased\let\setupsimplelists\setupsimplelist
+
+\setupsimplelists[%
+ %c!title=,
+ %c!text=,
+ %
+ %c!style=,
+ %c!color=,
+ %c!command=,
+ %c!align=,
+ %
+ %c!headstyle=,
+ %c!headcolor=,
+ %c!headalign=,
+ %
+ %c!titlestyle=,
+ %c!titlecolor=,
+ %c!titlecommand=,
+ %c!titleleft=,
+ %c!titleright=,
+ %
+ %c!closesymbol=,
+ %c!closecommand=,
+ %
+ \c!alternative=\v!left,
+ \c!display=\v!yes,
+ \c!width=7\emwidth,
+ \c!distance=\emwidth,
+ \c!titledistance=.5\emwidth,
+ %c!hang=,
+ %c!sample=,
+ \c!margin=\v!no,
+ \c!before=\startpacked,
+ \c!inbetween=\blank,
+ \c!after=\stoppacked,
+ %c!indentnext=,
+ %c!indenting=,
+ %
+ \c!expansion=\v!no,
+ %c!xmlsetup=,
+ %s!catcodes=,
+ \s!language=\currentmainlanguage,
+]
+
+\appendtoks
+ \setfalse\c_strc_constructions_define_commands
+ \ifempty\currentsimplelistparent
+ \defineconstruction[\currentsimplelist][\s!handler=\v!simplelist,\c!level=1]%
+ \else
+ \defineconstruction[\currentsimplelist][\currentsimplelistparent][\s!handler=\v!simplelist,\c!level=1]%
+ \fi
+ \settrue\c_strc_constructions_define_commands
+\to \everydefinesimplelist
+
+\protected\defcsname\??constructioninitializer\v!simplelist\endcsname
+ {\let \currentsimplelist \currentconstruction
+ \enforced\let\constructionparameter \simplelistparameter
+ \enforced\let\constructionnamespace \??simplelist
+ \enforced\let\detokenizedconstructionparameter\detokenizedsimplelistparameter
+ \enforced\let\letconstructionparameter \letsimplelistparameter
+ \enforced\let\useconstructionstyleandcolor \usesimpleliststyleandcolor
+ \enforced\let\setupcurrentconstruction \setupcurrentsimplelist}
+
+\protected\defcsname\??constructionfinalizer\v!simplelist\endcsname
+ {}
+
+\protected\defcsname\??constructiontexthandler\v!simplelist\endcsname
+ {\begingroup
+ \useconstructionstyleandcolor\c!headstyle\c!headcolor
+ \the\everyconstruction
+ \constructionparameter\c!headcommand
+ {\strut
+ \currentsimplelistentry}%
+ \endgroup}
+
+% And we build on top of this.
+
+\ifdefined\dotagsynonym \else \aliased\let\dotagsynonym\relax \fi
+\ifdefined\dotagsorting \else \aliased\let\dotagsorting\relax \fi
+
+\definesimplelist
+ [\v!synonym]
+ [\c!state=\v!start,
+ %\c!synonymstyle=,
+ %\c!textstyle=,
+ %\c!headstyle=,
+ %\c!headcolor=,
+ %\c!criterium=,
+ \c!location=\v!left,
+ \c!width=5\emwidth,
+ \c!distance=\zeropoint,
+ %\c!sample=,
+ %\c!hang=,
+ %\c!align=,
+ %\c!before=,
+ %\c!inbetween=,
+ %\c!after=,
+ \c!indentnext=\v!no,
+ %\c!expansion=,
+ \c!method=]
+
+\aliased\let\setupsynonyms\setupsimplelist
+
+% these might become private
+
+\mutable\let\currentsynonymoption \empty
+\mutable\let\currentsynonymcoding \empty
+\mutable\let\currentsynonymexpansion\empty
+\mutable\let\currentsynonym \empty
+\mutable\let\currentsynonymtag \empty
+\mutable\let\currentsynonymtext \empty
+
+\permanent\tolerant\protected\def\definesynonyms[#1]#*[#2]#*[#3]#*[#4]% name plural \meaning \use
+ {\ifnum\lastarguments=\plusfour
+ \protected\instance\def#4##1{\strc_synonyms_insert{#1}{##1}}% name tag
+ \frozen\instance\protected\defcsname#1\endcsname{\definesynonym[\v!no][#1]}% \name
+ \else
+ \frozen\instance\protected\defcsname#1\endcsname{\definesynonym[\v!yes][#1]}% \name
+ \fi
+ \ifparameter#3\or
+ \frozen\instance\protected\def#3##1{\strc_synonyms_insert_meaning{#1}{##1}}% \meaning
+ \fi
+ \edef\currentsynonym{#1}%
+ %
+ %\checksynonymparent
+ %\setupcurrentsynonym[\s!single={#1},\s!multi={#2}]%
+ \setfalse\c_strc_constructions_define_commands
+ \definesimplelist
+ [\currentsynonym]%
+ [\v!sorting]
+ [\s!single={#1},%
+ \s!multi={#2}]%
+ \settrue\c_strc_constructions_define_commands
+ \ifparameter#2\or
+ %
+ \presetheadtext[#2=\Word{#2}]% changes the \if...argument
+ %
+ % These will go away. Actually the overloads catched this empty case!
+ %
+ \frozen\instance\protected\defcsname\e!setup #2\e!endsetup\endcsname{\setupsynonyms[#1]}% obsolete definition
+ \frozen\instance\protected\defcsname\e!place \e!listof#2\endcsname{\placelistofsynonyms[#1]}% accepts extra argument
+ \frozen\instance\protected\defcsname\e!complete\e!listof#2\endcsname{\completelistofsynonyms[#1]}%
+ \fi}
+
+\permanent\tolerant\protected\def\definesynonym[#1]#*[#2]#*[#3]%
+ {\begingroup
+ \edef\currentsynonymoption{#1}%
+ \edef\currentsynonym{#2}%
+ \edef\currentsynonymtag{#3}%
+ \strc_synonyms_define_entry}
+
+\def\strc_synonyms_define_entry#1#2%
+ {\let\currentsimplelist\currentsynonym
+ \ifempty\currentsynonymtag
+ \edef\currentsynonymtag{#1}%
+ \fi
+ \ifempty\currentsynonymtag \else
+ % this is not that efficient, esp when we load a big list
+ \edef\currentsynonymexpansion{\simplelistparameter\c!expansion}%
+ \preprocessexpansion\currentsynonymexpansion\m_synonyms_text \currentsynonymcoding{#1}%
+ \preprocessexpansion\currentsynonymexpansion\m_synonyms_meaning\currentsynonymcoding{#2}%
+ % currently they all end up in the tuc file
+ \clf_registersynonym
+ {\currentsynonym}%
+ {synonym}%
+ {%
+ metadata {%
+ catcodes \catcodetable
+ coding {\currentsynonymcoding}%
+ \ifx\currentsynonymcoding\s!xml
+ xmlroot {\xmldocument}%
+ \fi
+ }%
+ definition {%
+ tag {\currentsynonymtag}%
+ synonym {\m_synonyms_text}%
+ meaning {\m_synonyms_meaning}%
+ % used false
+ }%
+ }%
+ \relax
+ \ifx\currentsynonymoption\v!yes
+ \instance\setuxvalue\currentsynonymtag{\strc_synonyms_insert{\currentsynonym}{\currentsynonymtag}}%
+ \fi
+ \fi
+ \endgroup}
+
+\permanent\tolerant\protected\def\registersynonym [#1]#*[#2]{\clf_registerusedsynonym{#1}{#2}}
+\permanent \protected\def\currentsynonymname {\clf_synonymname{\currentsimplelist}{\currentsynonymtag}}
+\permanent \protected\def\currentsynonymmeaning {\clf_synonymmeaning{\currentsimplelist}{\currentsynonymtag}}
+\permanent \protected\def\doifelsecurrentsynonymused {\clf_doifelsesynonymused{\currentsimplelist}{\currentsynonymtag}}
+\permanent \protected\def\doifelsecurrentsynonymshown{\clf_doifelsesynonymshown{\currentsimplelist}{\currentsynonymtag}}
+\permanent \protected\def\resetusedsynonyms [#1]{\clf_resetusedsynonyms{#1}}
+\permanent \protected\def\resetshownsynonyms [#1]{\clf_resetshownsynonyms{#1}}
+
+\aliased\let\rawsynonymname \clf_synonymname
+\aliased\let\rawsynonymmeaning\clf_synonymmeaning
+
+\installcorenamespace{simplelistalternative} % specific ways of rendering a list
+\installcorenamespace{simplelistrenderings} % a namespace for setups (rather local)
+
+\installcommandhandler \??simplelistalternative {simplelistalternative} \??simplelistalternative
+
+\setupsimplelist
+ [\v!synonym]
+ [\c!alternative=\v!normal]
+
+\protected\def\strc_synonyms_insert_meaning#1#2% name tag
+ {\dontleavehmode % otherwise we don't get it right at the beginning of a par
+ \begingroup
+ \def\currentsimplelist{#1}%
+ \def\currentsynonymtag{#2}%
+ \fastsetup{\??simplelistrenderings::\v!text}%
+ \endgroup}
+
+\protected\def\strc_synonyms_insert#1#2% name tag
+ {\dontleavehmode % otherwise we don't get it right at the beginning of a par
+ \begingroup
+ \edef\currentsimplelist{#1}%
+ \let\currentsynonym\currentsimplelist % for a while
+ \def\currentsynonymtag{#2}%
+ \edef\currentsimplelistalternative{\simplelistparameter\c!alternative}%
+ \doifnotcommandhandler\??simplelistalternative\currentsimplelistalternative
+ {\let\currentsimplelistalternative\v!normal}%
+ \fastsetup{\??simplelistrenderings:\v!synonym:\currentsimplelistalternative}%
+ \normalexpanded{\endgroup\simplelistparameter\c!next}}
+
+% \setupsimplelistalternative
+% [\c!command=\directsimplelistparameter\c!command]
+
+\definesimplelistalternative
+ [\v!normal]
+ [\c!inbetween=\space,
+ \c!left=(,
+ \c!right=)]
+
+\definesimplelistalternative
+ [\v!first]
+ [\v!normal]
+
+\definesimplelistalternative
+ [\v!last]
+ [\v!normal]
+
+\startsetups[\??simplelistrenderings::\v!synonym]
+ \begingroup
+ \dostarttaggedchained\t!synonym\currentsynonym\??simplelist
+ \dotagsynonym
+ \usesimpleliststyleandcolor\c!synonymstyle\c!synonymcolor
+ \simplelistparameter\c!synonymcommand{\currentsynonymname}%
+ \dostoptagged
+ \endgroup
+\stopsetups
+
+\startsetups[\??simplelistrenderings::\v!text]
+ \begingroup
+ \usehyphensparameter\simplelistparameter
+ \usesimpleliststyleandcolor\c!textstyle\c!textcolor
+ \simplelistparameter\c!textcommand{\currentsynonymmeaning}%
+ \endgroup
+\stopsetups
+
+\startsetups[\??simplelistrenderings:\v!synonym:\v!normal]
+ \fastsetup{\??simplelistrenderings::\v!synonym}
+\stopsetups
+
+\startsetups[\??simplelistrenderings:\v!synonym:\v!first]
+ \fastsetup{\??simplelistrenderings::\v!synonym}
+ \doifelsecurrentsynonymshown \donothing {
+ \simplelistalternativeparameter\c!inbetween
+ \simplelistalternativeparameter\c!left
+ \fastsetup{\??simplelistrenderings::\v!text}
+ \simplelistalternativeparameter\c!right
+ }
+\stopsetups
+
+\startsetups[\??simplelistrenderings:\v!synonym:\v!last]
+ \doifelsecurrentsynonymshown {
+ \fastsetup{\??simplelistrenderings::\v!synonym}
+ } {
+ \fastsetup{\??simplelistrenderings::\v!text}
+ \simplelistalternativeparameter\c!inbetween
+ \simplelistalternativeparameter\c!left
+ \fastsetup{\??simplelistrenderings::\v!synonym}
+ \simplelistalternativeparameter\c!right
+ }
+\stopsetups
+
+\aliased\let\synonymentry\relax
+
+\permanent\tolerant\protected\def\placelistofsynonyms[#1]#*[#2]%
+ {\begingroup
+ \edef\currentsimplelist{#1}%
+ \doifelsecommandhandler\??simplelist\currentsimplelist
+ {\strc_constructions_initialize{#1}%
+ \setupcurrentsimplelist[#2]%
+ \enforced\let\synonymentry\strc_synonym_normal
+ % so we can hook tabulate into before and after
+ \normalexpanded{\simplelistparameter\c!before
+ \noexpand\clf_processsynonyms
+ {#1}%
+ {%
+ criterium {\simplelistparameter\c!criterium}%
+ language {\simplelistparameter\s!language}%
+ method {\simplelistparameter\c!method}%
+ }%
+ \relax
+ \simplelistparameter\c!after}%
+ \relax}%
+ {}% todo: message that invalid
+ \endgroup}
+
+\permanent\tolerant\protected\def\completelistofsynonyms[#1]#*[#2]%
+ {\begingroup
+ \edef\currentsimplelist{#1}%
+ \doifelsecommandhandler\??simplelist\currentsimplelist
+ {\normalexpanded{\startnamedsection[\v!chapter][\c!title={\headtext{\simplelistparameter\s!multi}},\c!reference=#1]}%
+ \placelistofsynonyms[#1][#2]%
+ \page
+ \stopnamedsection}%
+ {}% todo: message that invalid
+ \endgroup}
+
+\mutable\let\currentsimplelistentry\empty
+
+\protected\def\strc_synonym_normal#1#2#3#4%
+ {\begingroup
+ \def\currentsimplelistentry{#3}%
+ \csname\??constructionstarthandler\v!construction\endcsname
+ #4%
+ \csname\??constructionstophandler\v!construction\endcsname
+ \endgroup}
+
+%D Sorting (a simplified version of synonym).
+
+\definesimplelist
+ [\v!sorting]
+ [\c!state=\v!start,
+ %\c!command=, % we test for defined !
+ %\c!criterium=,
+ %\c!style=,
+ \c!before=\startpacked,
+ \c!after=\stoppacked,
+ %\c!expansion=,
+ \c!method=]
+
+\aliased\let\setupsorting\setupsimplelist
+
+% if #3=\relax or \v!none, then no command but still protected
+
+% these might become private
+
+\mutable\let\currentsortingoption \empty
+\mutable\let\currentsortingcoding \empty
+\mutable\let\currentsortingexpansion\empty
+\mutable\let\currentsorting \empty
+\mutable\let\currentsortingtag \empty
+\mutable\let\currentsortingtext \empty
+
+\permanent\tolerant\protected\def\definesorting[#1]#*[#2]#*[#3]%
+ {\ifnum\lastarguments=\plusthree
+ \doifnot{#3}\v!none
+ {\ifx#3\relax \else
+ \protected\instance\def#3##1{\strc_sorting_insert{#1}{##1}}%
+ \fi}%
+ \frozen\instance\protected\defcsname#1\endcsname{\definesort[\v!no][#1]}%
+ \else
+ \frozen\instance\protected\defcsname#1\endcsname{\definesort[\v!yes][#1]}%
+ \fi
+ \edef\currentsorting{#1}%
+ \setfalse\c_strc_constructions_define_commands
+ \definesimplelist
+ [\currentsorting]%
+ [\v!sorting]
+ [\s!single={#1},%
+ \s!multi={#2}]%
+ \settrue\c_strc_constructions_define_commands
+ \ifparameter#2\or
+ %
+ \presetheadtext[#2=\Word{#2}]% changes the \if...argument
+ %
+ % These will go away. Actually the overloads catched this empty case!
+ %
+ %
+ \presetheadtext[#2=\Word{#2}]%
+ %
+ \frozen\instance\protected\defcsname\e!setup #2\e!endsetup\endcsname{\setupsorting[#1]}% obsolete definition
+ \frozen\instance\protected\defcsname\e!place \e!listof#2\endcsname{\placelistofsorts[#1]}%
+ \frozen\instance\protected\defcsname\e!complete\e!listof#2\endcsname{\completelistofsorts[#1]}%
+ \fi}
+
+\permanent\tolerant\protected\def\definesort[#1]#*[#2]#*[#3]%
+ {\begingroup
+ \edef\currentsortingoption{#1}%
+ \edef\currentsorting{#2}%
+ \edef\currentsortingtag{#3}%
+ \strc_sorting_define_entry}
+
+\def\strc_sorting_define_entry#1%
+ {\let\currentsimplelist\currentsimplelist
+ \ifempty\currentsortingtag
+ \edef\currentsortingtag{#1}%
+ \fi
+ \ifempty\currentsortingtag \else
+ \edef\currentsortingexpansion{\simplelistparameter\c!expansion}%
+ \preprocessexpansion\currentsortingexpansion\currentsortingtext\currentsortingcoding{#1}%
+ \clf_registersynonym
+ {\currentsorting}%
+ {sorting}%
+ {%
+ metadata {%
+ catcodes \catcodetable
+ coding {\currentsortingcoding}%
+ \ifx\currentsortingcoding\s!xml
+ xmlroot {\xmldocument}%
+ \fi
+ }%
+ definition {%
+ tag {\currentsortingtag}%
+ synonym {\currentsortingtext}%
+ % used false
+ }%
+ }%
+ \relax
+ \ifx\currentsortingoption\v!yes
+ \instance\setuxvalue\currentsortingtag{\strc_sorting_insert{\currentsorting}{\currentsortingtag}}%
+ \fi
+ \fi
+ \endgroup}
+
+\permanent\protected\def\currentsortingname {\clf_synonymname {\currentsimplelist}{\currentsortingtag}}
+\permanent\protected\def\doifelsecurrentsortingused {\clf_doifelsesynonymused {\currentsimplelist}{\currentsortingtag}}
+\permanent\protected\def\resetusedsortings [#1]{\clf_resetusedsynonyms {#1}}
+
+\setupsimplelist
+ [\v!sorting]
+ [\c!alternative=\v!normal]
+
+\protected\def\strc_sorting_insert#1#2% name tag
+ {\dontleavehmode % otherwise we don't get it right at the beginning of a par
+ \begingroup
+ % no kap currently, of .. we need to map cap onto WORD
+ \edef\currentsorting{#1}%
+ \edef\currentsortingtag{#2}%
+ \let\currentsimplelist\currentsorting
+ \edef\currentsimplelistalternative{\simplelistparameter\c!alternative}%
+ \doifnotcommandhandler\??simplelistalternative\currentsimplelistalternative
+ {\let\currentsimplelistalternative\v!normal}%
+ \fastsetup{\??simplelistrenderings:\v!sorting:\currentsimplelistalternative}%
+ \normalexpanded{\endgroup\simplelistparameter\c!next}}
+
+% or:
+%
+% \doifelsesetups{\??simplelistrenderings:\v!sorting:\currentsimplelistalternative}
+% {\fastsetup{\??simplelistrenderings:\v!sorting:\currentsimplelistalternative}}
+% {\fastsetup{\??simplelistrenderings:\v!sorting:\v!normal}}
+
+\startsetups [\??simplelistrenderings:\v!sorting:\v!normal]
+ \fastsetup{\??simplelistrenderings::\v!sorting}%
+\stopsetups
+
+\startsetups [\??simplelistrenderings::\v!sorting]
+ \begingroup
+ \dostarttaggedchained\t!sorting\currentsorting\??simplelist
+ \dotagsorting
+ \usesimpleliststyleandcolor\c!style\c!color
+ \currentsortingname
+ \dostoptagged
+ \endgroup
+\stopsetups
+
+\permanent\tolerant\protected\def\registersort[#1]#*[#2]%
+ {\clf_registerusedsynonym{#1}{#2}}
+
+% before after
+%
+% maybe just 'commandset' and then combine
+
+\permanent\tolerant\protected\def\placelistofsorts[#1]#*[#2]%
+ {\begingroup
+ \edef\currentsimplelist{#1}%
+ \strc_constructions_initialize{#1}%
+ \setupcurrentsimplelist[#2]%
+ \edef\p_simplelist_command{\simplelistparameter\c!command}%
+ \ifempty\p_simplelist_command
+ \enforced\let\synonymentry\strc_sorting_normal
+ \else
+ \enforced\let\synonymentry\strc_sorting_command
+ \fi
+ % so we can hook tabulate into before and after
+ \normalexpanded{\simplelistparameter\c!before
+ \noexpand\clf_processsynonyms
+ {#1}%
+ {%
+ criterium {\simplelistparameter\c!criterium}%
+ language {\simplelistparameter\s!language}%
+ method {\simplelistparameter\c!method}%
+ }%
+ \relax
+ \simplelistparameter\c!after}%
+ \endgroup}
+
+\permanent\tolerant\protected\def\completelistofsorts[#1]#*[#2]%
+ {\begingroup
+ \edef\currentsimplelist{#1}%
+ \normalexpanded{\startnamedsection[\v!chapter][\c!title={\headtext{\simplelistparameter\s!multi}},\c!reference=#1]}%
+ \placelistofsorts[#1][#2]%
+ \page
+ \stopnamedsection
+ \endgroup}
+
+\def\strc_sorting_command#1#2#3#4% #4 is meaning but empty here
+ {\p_simplelist_command{#1}{#2}{#3}}
+
+\def\strc_sorting_normal#1#2#3#4% #4 is meaning but empty here
+ {\begingroup
+ \usesimpleliststyleandcolor\c!style\c!color
+ #3%
+ \endgroup
+ \par}
+
+%D Presets.
+
+% To be considered:
+%
+% \setupsimplelist
+% [\v!sorting]
+% [\c!headstyle=\simplelistparameter\c!synonymstyle,
+% \c!headcolor=\simplelistparameter\c!synonymcolor,
+% \c!style=\simplelistparameter\c!textstyle,
+% \c!color=\simplelistparameter\c!textcolor]
+
+\definesynonyms
+ [\v!abbreviation]
+ [\v!abbreviations]
+ [\infull]
+
+\setupsynonyms
+ [\v!abbreviation]
+ [\c!hyphens=\v!no, % new, not yet in setup definitions
+ \c!textstyle=\v!capital]
+
+\definesorting
+ [\v!logo]
+ [\v!logos]
+% [\logogram] % no
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-tag.lmt b/Master/texmf-dist/tex/context/base/mkxl/strc-tag.lmt
new file mode 100644
index 00000000000..96510f24eac
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-tag.lmt
@@ -0,0 +1,615 @@
+if not modules then modules = { } end modules ['strc-tag'] = {
+ version = 1.001,
+ comment = "companion to strc-tag.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is rather experimental code. Tagging happens on the fly and there are two analysers
+-- involved: the pdf backend tagger and the exporter. They share data but there are subtle
+-- differences. Each tag carries a specification and these can be accessed by attribute (the
+-- end of the chain tag) or by so called fullname which is a tagname combined with a number.
+
+local type, next = type, next
+local insert, remove, unpack, concat, merge = table.insert, table.remove, table.unpack, table.concat, table.merge
+local find, topattern, format = string.find, string.topattern, string.format
+local lpegmatch, P, S, C, Cc = lpeg.match, lpeg.P, lpeg.S, lpeg.C, lpeg.Cc
+local allocate = utilities.storage.allocate
+local settings_to_hash = utilities.parsers.settings_to_hash
+local setmetatableindex = table.setmetatableindex
+
+local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end)
+
+local report_tags = logs.reporter("structure","tags")
+
+local attributes = attributes
+local structures = structures
+local implement = interfaces.implement
+
+local a_tagged = attributes.private('tagged')
+
+local unsetvalue = attributes.unsetvalue
+local codeinjections = backends.codeinjections
+
+local texgetattribute = tex.getattribute
+local texsetattribute = tex.setattribute
+
+local taglist = allocate() -- access by attribute
+local specifications = allocate() -- access by fulltag
+local labels = allocate()
+local stack = { }
+local chain = { }
+local ids = { }
+local enabled = false
+local tagcontext = { }
+local tagpatterns = { }
+local lasttags = { }
+local stacksize = 0
+local metadata = nil -- applied to the next element
+local documentdata = { }
+local extradata = false
+
+local tags = structures.tags
+tags.taglist = taglist -- can best be hidden
+tags.labels = labels
+tags.patterns = tagpatterns
+tags.specifications = specifications
+
+function tags.current()
+ if stacksize > 0 then
+ return stack[stacksize] -- maybe copy or proxy
+ end
+end
+
+-- Tags are internally stored as:
+--
+-- tag>number tag>number tag>number
+
+local p_splitter = C((1-S(">"))^1) * P(">") * C(P(1)^1)
+tagpatterns.splitter = p_splitter
+
+local properties = allocate { -- todo: more "record = true" to improve formatting
+
+ document = { pdf = "Div", nature = "display" },
+
+ division = { pdf = "Div", nature = "display" },
+ paragraph = { pdf = "P", nature = "mixed" },
+ p = { pdf = "P", nature = "mixed" },
+ construct = { pdf = "Span", nature = "inline" },
+ highlight = { pdf = "Span", nature = "inline" },
+
+ section = { pdf = "Sect", nature = "display" },
+ sectioncaption = { pdf = "Div", nature = "display", record = true },
+ sectiontitle = { pdf = "H", nature = "mixed" },
+ sectionnumber = { pdf = "H", nature = "mixed" },
+ sectioncontent = { pdf = "Div", nature = "display" },
+
+ itemgroup = { pdf = "L", nature = "display" },
+ item = { pdf = "LI", nature = "display" },
+ itemtag = { pdf = "Lbl", nature = "mixed" },
+ itemcontent = { pdf = "LBody", nature = "mixed" },
+ itemhead = { pdf = "Div", nature = "display" },
+ itembody = { pdf = "Div", nature = "display" },
+
+ description = { pdf = "Div", nature = "display" },
+ descriptiontag = { pdf = "Div", nature = "mixed" },
+ descriptioncontent = { pdf = "Div", nature = "mixed" },
+ descriptionsymbol = { pdf = "Span", nature = "inline" }, -- note reference
+
+ verbatimblock = { pdf = "Code", nature = "display" },
+ verbatimlines = { pdf = "Code", nature = "display" },
+ verbatimline = { pdf = "Code", nature = "mixed" },
+ verbatim = { pdf = "Code", nature = "inline" },
+
+ lines = { pdf = "Code", nature = "display" },
+ line = { pdf = "Code", nature = "mixed" },
+ linenumber = { pdf = "Span", nature = "inline" },
+
+ synonym = { pdf = "Span", nature = "inline" },
+ sorting = { pdf = "Span", nature = "inline" },
+
+ register = { pdf = "Div", nature = "display" },
+ registerlocation = { pdf = "Span", nature = "inline" },
+ registersection = { pdf = "Div", nature = "display" },
+ registertag = { pdf = "Span", nature = "mixed" },
+ registerentries = { pdf = "Div", nature = "display" },
+ registerentry = { pdf = "Div", nature = "display" },
+ registercontent = { pdf = "Span", nature = "mixed" },
+ registersee = { pdf = "Span", nature = "mixed" },
+ registerpages = { pdf = "Span", nature = "mixed" },
+ registerpage = { pdf = "Span", nature = "mixed" },
+ registerseparator = { pdf = "Span", nature = "inline" },
+ registerpagerange = { pdf = "Span", nature = "mixed" },
+
+ table = { pdf = "Table", nature = "display" },
+ tablerow = { pdf = "TR", nature = "display" },
+ tablecell = { pdf = "TD", nature = "mixed" },
+ tableheadcell = { pdf = "TH", nature = "mixed" },
+ tablehead = { pdf = "THEAD", nature = "display" },
+ tablebody = { pdf = "TBODY", nature = "display" },
+ tablefoot = { pdf = "TFOOT", nature = "display" },
+
+ tabulate = { pdf = "Table", nature = "display" },
+ tabulaterow = { pdf = "TR", nature = "display" },
+ tabulatecell = { pdf = "TD", nature = "mixed" },
+ tabulateheadcell = { pdf = "TH", nature = "mixed" },
+ tabulatehead = { pdf = "THEAD", nature = "display" },
+ tabulatebody = { pdf = "TBODY", nature = "display" },
+ tabulatefoot = { pdf = "TFOOT", nature = "display" },
+
+ list = { pdf = "TOC", nature = "display" },
+ listitem = { pdf = "TOCI", nature = "display" },
+ listtag = { pdf = "Lbl", nature = "mixed" },
+ listcontent = { pdf = "P", nature = "mixed" },
+ listdata = { pdf = "P", nature = "mixed" },
+ listpage = { pdf = "Reference", nature = "mixed" },
+ listtext = { pdf = "Span", nature = "inline" },
+
+ delimitedblock = { pdf = "BlockQuote", nature = "display" },
+ delimited = { pdf = "Quote", nature = "inline" },
+ delimitedcontent = { pdf = "Span", nature = "inline" },
+ delimitedsymbol = { pdf = "Span", nature = "inline" },
+ subsentence = { pdf = "Span", nature = "inline" },
+ subsentencecontent = { pdf = "Span", nature = "inline" },
+ subsentencesymbol = { pdf = "Span", nature = "inline" },
+
+ label = { pdf = "Span", nature = "mixed" },
+ number = { pdf = "Span", nature = "mixed" },
+
+ float = { pdf = "Div", nature = "display" }, -- Figure
+ floatcaption = { pdf = "Caption", nature = "mixed" },
+ floatlabel = { pdf = "Span", nature = "inline" },
+ floatnumber = { pdf = "Span", nature = "inline" },
+ floattext = { pdf = "Span", nature = "mixed" },
+ floatcontent = { pdf = "P", nature = "mixed" },
+
+ image = { pdf = "P", nature = "mixed" },
+ mpgraphic = { pdf = "P", nature = "mixed" },
+
+ formulaset = { pdf = "Div", nature = "display" },
+ formula = { pdf = "Div", nature = "display" }, -- Formula
+ formulacaption = { pdf = "Span", nature = "mixed" },
+ formulalabel = { pdf = "Span", nature = "mixed" },
+ formulanumber = { pdf = "Span", nature = "mixed" },
+ formulacontent = { pdf = "P", nature = "display" },
+ subformula = { pdf = "Div", nature = "display" },
+
+ link = { pdf = "Link", nature = "inline" },
+ reference = { pdf = "Span", nature = "inline" },
+
+ margintextblock = { pdf = "Span", nature = "inline" },
+ margintext = { pdf = "Span", nature = "inline" },
+ marginanchor = { pdf = "Span", nature = "inline" },
+
+ math = { pdf = "Div", nature = "inline" }, -- no display
+ mn = { pdf = "Span", nature = "mixed" },
+ mi = { pdf = "Span", nature = "mixed" },
+ mo = { pdf = "Span", nature = "mixed" },
+ ms = { pdf = "Span", nature = "mixed" },
+ mrow = { pdf = "Span", nature = "display" },
+ msubsup = { pdf = "Span", nature = "display" },
+ msub = { pdf = "Span", nature = "display" },
+ msup = { pdf = "Span", nature = "display" },
+ merror = { pdf = "Span", nature = "mixed" },
+ munderover = { pdf = "Span", nature = "display" },
+ munder = { pdf = "Span", nature = "display" },
+ mover = { pdf = "Span", nature = "display" },
+ mtext = { pdf = "Span", nature = "mixed" },
+ mfrac = { pdf = "Span", nature = "display" },
+ mroot = { pdf = "Span", nature = "display" },
+ msqrt = { pdf = "Span", nature = "display" },
+ mfenced = { pdf = "Span", nature = "display" },
+ maction = { pdf = "Span", nature = "display" },
+
+ mstacker = { pdf = "Span", nature = "display" }, -- these are only internally used
+ mstackertop = { pdf = "Span", nature = "display" }, -- these are only internally used
+ mstackerbot = { pdf = "Span", nature = "display" }, -- these are only internally used
+ mstackermid = { pdf = "Span", nature = "display" }, -- these are only internally used
+
+ mtable = { pdf = "Table", nature = "display" }, -- might change
+ mtr = { pdf = "TR", nature = "display" }, -- might change
+ mtd = { pdf = "TD", nature = "display" }, -- might change
+
+ ignore = { pdf = "Span", nature = "mixed" }, -- used internally
+ private = { pdf = "Span", nature = "mixed" }, -- for users (like LS) when they need it
+ metadata = { pdf = "Div", nature = "display" },
+ metavariable = { pdf = "Span", nature = "mixed" },
+
+ mid = { pdf = "Span", nature = "inline" },
+ sub = { pdf = "Span", nature = "inline" },
+ sup = { pdf = "Span", nature = "inline" },
+ subsup = { pdf = "Span", nature = "inline" },
+
+ combination = { pdf = "Span", nature = "display" },
+ combinationpair = { pdf = "Span", nature = "display" },
+ combinationcontent = { pdf = "Span", nature = "mixed" },
+ combinationcaption = { pdf = "Span", nature = "mixed" },
+
+ publications = { pdf = "Div", nature = "display" },
+ publication = { pdf = "Div", nature = "mixed" },
+ pubfld = { pdf = "Span", nature = "inline" },
+
+ block = { pdf = "Div", nature = "display" },
+ userdata = { pdf = "Div", nature = "display" },
+
+}
+
+tags.properties = properties
+
+local patterns = setmetatableindex(function(t,tag)
+ local v = topattern("^" .. tag .. ">")
+ t[tag] = v
+ return v
+end)
+
+function tags.locatedtag(tag)
+ local attribute = texgetattribute(a_tagged)
+ if attribute >= 0 then
+ local specification = taglist[attribute]
+ if specification then
+ local taglist = specification.taglist
+ local pattern = patterns[tag]
+ for i=#taglist,1,-1 do
+ local t = taglist[i]
+ if find(t,pattern) then
+ return t
+ end
+ end
+ end
+ else
+ -- enabled but not auto
+ end
+ return false -- handy as bogus index
+end
+
+function structures.atlocation(str) -- not used
+ local specification = taglist[texgetattribute(a_tagged)]
+ if specification then
+ local list = specification.taglist
+ if list then
+ local pattern = patterns[str]
+ for i=#list,1,-1 do
+ if find(list[i],pattern) then
+ return true
+ end
+ end
+ end
+ end
+end
+
+function tags.setproperty(tag,key,value)
+ local p = properties[tag]
+ if p then
+ p[key] = value
+ else
+ properties[tag] = { [key] = value }
+ end
+end
+
+function tags.setaspect(key,value)
+ local tag = chain[stacksize]
+ if tag then
+ local p = properties[tag]
+ if p then
+ p[key] = value
+ else
+ properties[tag] = { [key] = value }
+ end
+ end
+end
+
+function tags.registermetadata(data)
+ local d = settings_to_hash(data)
+ if #chain > 1 then
+ if metadata then
+ merge(metadata,d)
+ else
+ metadata = d
+ end
+ else
+ merge(documentdata,d)
+ end
+end
+
+function tags.getmetadata()
+ return documentdata or { }
+end
+
+function tags.registerextradata(name,serializer)
+ if type(serializer) == "function" then
+ if extradata then
+ extradata[name] = serializer
+ else
+ extradata = { [name] = serializer }
+ end
+ end
+end
+
+function tags.getextradata()
+ return extradata
+end
+
+function tags.start(tag,specification)
+ if not enabled then
+ codeinjections.enabletags()
+ enabled = true
+ end
+ --
+ labels[tag] = tag -- can go away
+ --
+ local attribute = #taglist + 1
+ local tagindex = (ids[tag] or 0) + 1
+ --
+ local completetag = tag .. ">" .. tagindex
+ --
+ ids[tag] = tagindex
+ lasttags[tag] = tagindex
+ stacksize = stacksize + 1
+ --
+ chain[stacksize] = completetag
+ stack[stacksize] = attribute
+ tagcontext[tag] = completetag
+ --
+ local tagnesting = { unpack(chain,1,stacksize) } -- a copy so we can add actualtext
+ --
+ if specification then
+ specification.attribute = attribute
+ specification.tagindex = tagindex
+ specification.taglist = tagnesting
+ specification.tagname = tag
+ if metadata then
+ specification.metadata = metadata
+ metadata = nil
+ end
+ local userdata = specification.userdata
+ if userdata == "" then
+ specification.userdata = nil
+ elseif type(userdata) == "string" then
+ specification.userdata = settings_to_hash(userdata)
+ end
+ local detail = specification.detail
+ if detail == "" then
+ specification.detail = nil
+ end
+ local parents = specification.parents
+ if parents == "" then
+ specification.parents = nil
+ end
+ else
+ specification = {
+ attribute = attribute,
+ tagindex = tagindex,
+ taglist = tagnesting,
+ tagname = tag,
+ metadata = metadata,
+ }
+ metadata = nil
+ end
+ --
+ taglist[attribute] = specification
+ specifications[completetag] = specification
+ --
+ if completetag == "document>1" then
+ specification.metadata = documentdata
+ end
+ --
+ texsetattribute(a_tagged,attribute)
+ return attribute
+end
+
+function tags.restart(attribute)
+ stacksize = stacksize + 1
+ if type(attribute) == "number" then
+ local taglist = taglist[attribute].taglist
+ chain[stacksize] = taglist[#taglist]
+ else
+ chain[stacksize] = attribute -- a string
+ attribute = #taglist + 1
+ taglist[attribute] = { taglist = { unpack(chain,1,stacksize) } }
+ end
+ stack[stacksize] = attribute
+ texsetattribute(a_tagged,attribute)
+ return attribute
+end
+
+function tags.stop()
+ if stacksize > 0 then
+ stacksize = stacksize - 1
+ end
+ local t = stack[stacksize]
+ if not t then
+ if trace_tags then
+ report_tags("ignoring end tag, previous chain: %s",stacksize > 0 and concat(chain," ",1,stacksize) or "none")
+ end
+ t = unsetvalue
+ end
+ texsetattribute(a_tagged,t)
+ return t
+end
+
+function tags.getid(tag,detail)
+ return ids[tag] or "?"
+end
+
+function tags.last(tag)
+ return lasttags[tag] -- or false
+end
+
+function tags.lastinchain(tag)
+ if tag and tag ~= "" then
+ return tagcontext[tag]
+ else
+ return chain[stacksize]
+ end
+end
+
+local strip = C((1-S(">"))^1)
+
+function tags.elementtag()
+ local fulltag = chain[stacksize]
+ if fulltag then
+ return lpegmatch(strip,fulltag)
+ end
+end
+
+function tags.strip(fulltag)
+ return lpegmatch(strip,fulltag)
+end
+
+function tags.setuserproperties(tag,list)
+ if not list or list == "" then
+ tag, list = chain[stacksize], tag
+ else
+ tag = tagcontext[tag]
+ end
+ if tag then -- an attribute now
+ local l = settings_to_hash(list)
+ local s = specifications[tag]
+ if s then
+ local u = s.userdata
+ if u then
+ for k, v in next, l do
+ u[k] = v
+ end
+ else
+ s.userdata = l
+ end
+ else
+ -- error
+ end
+ end
+end
+
+function tags.handler(head) -- we need a dummy
+ return head, false
+end
+
+statistics.register("structure elements", function()
+ if enabled then
+ if stacksize > 0 then
+ return format("%s element chains identified, open chain: %s ",#taglist,concat(chain," => ",1,stacksize))
+ else
+ return format("%s element chains identified",#taglist)
+ end
+ end
+end)
+
+directives.register("backend.addtags", function(v)
+ if not enabled then
+ codeinjections.enabletags()
+ enabled = true
+ end
+end)
+
+-- interface
+
+local starttag = tags.start
+
+implement {
+ name = "strc_tags_start",
+ public = true,
+ protected = true,
+ actions = starttag,
+ arguments = "argument",
+}
+
+implement {
+ name = "strc_tags_stop",
+ public = true,
+ protected = true,
+ actions = tags.stop,
+}
+
+implement {
+ name = "strc_tags_start_userdata",
+ public = true,
+ protected = true,
+ actions = function(tag,userdata) starttag(tag,{ userdata = userdata }) end,
+ arguments = { "optional", "optional" },
+}
+
+implement {
+ name = "strc_tags_start_detail",
+ public = true,
+ protected = true,
+ actions = function(tag,detail) starttag(tag,{ detail = detail }) end,
+ arguments = "2 arguments",
+}
+
+implement {
+ name = "strc_tags_start_ignore",
+ public = true,
+ protected = true,
+ actions = function(detail) starttag("ignore",{ detail = detail }) end,
+ arguments = { "argument" },
+}
+
+implement {
+ name = "strc_tags_start_chained",
+ public = true,
+ protected = true,
+ actions = function(tag,detail,parents) starttag(tag,{ detail = detail, parents = parents }) end,
+ arguments = "3 arguments",
+}
+
+implement {
+ name = "strc_tags_set_aspect",
+ public = true,
+ protected = true,
+ actions = tags.setaspect,
+ arguments = "2 arguments"
+}
+
+implement {
+ name = "settagproperty",
+ actions = tags.setproperty,
+ arguments = "3 arguments"
+}
+
+implement {
+ name = "setelementbackendtag",
+ public = true,
+ protected = true,
+ actions = tags.setproperty,
+ arguments = { "optional", "'backend'", "optional" },
+}
+
+implement {
+ name = "setelementnature",
+ public = true,
+ protected = true,
+ actions = tags.setproperty,
+ arguments = { "optional", "'nature'", "optional" },
+}
+
+implement {
+ name = "strc_tags_get_element_tag",
+ public = true,
+ protected = true,
+ actions = { tags.elementtag, context }
+}
+
+implement {
+ name = "strc_tags_set_element_user_properties",
+ public = true,
+ protected = true,
+ actions = tags.setuserproperties,
+ arguments = { "optional", "optional" },
+}
+
+implement {
+ name = "doifelseinelement",
+ public = true,
+ protected = true,
+ actions = { structures.atlocation, commands.testcase },
+ arguments = "argument",
+}
+
+implement {
+ name = "settaggedmetadata",
+ public = true,
+ protected = true,
+ actions = tags.registermetadata,
+ arguments = "optional",
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-tag.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-tag.mkxl
new file mode 100644
index 00000000000..c2597074750
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-tag.mkxl
@@ -0,0 +1,531 @@
+%D \module
+%D [ file=strc-tag,
+%D version=2010.07.16,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Tags,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% labels: no language needed
+% key/values and other names might change (and probably will)
+
+\writestatus{loading}{ConTeXt Structure Macros / Tags}
+
+\registerctxluafile{strc-tag}{autosuffix}
+
+\unprotect
+
+%D Eventually these labels will either move to the modules where they're used, or
+%D they will en dup in mult-tag.
+
+\definetagconstant{document} % Div
+
+\definetagconstant{division} % Div
+\definetagconstant{paragraph} % P
+\definetagconstant{p} % P
+\definetagconstant{construct} % Span
+\definetagconstant{highlight} % Span
+
+\definetagconstant{section} % Sect
+\definetagconstant{sectioncaption} % Div
+\definetagconstant{sectiontitle} % H
+\definetagconstant{sectionnumber} % H
+\definetagconstant{sectioncontent} % Div
+
+\definetagconstant{itemgroup} % L
+\definetagconstant{item} % Li
+\definetagconstant{itemtag} % Lbl
+\definetagconstant{itemcontent} % LBody
+\definetagconstant{itemhead} % Div
+\definetagconstant{itembody} % Div
+
+\definetagconstant{description} % Li
+\definetagconstant{descriptiontag} % Lbl
+\definetagconstant{descriptioncontent} % LBody
+\definetagconstant{descriptionsymbol} % Span
+
+\aliastagconstant{construction} {description}
+\aliastagconstant{constructiontag} {descriptiontag}
+\aliastagconstant{constructioncontent}{descriptioncontent}
+\aliastagconstant{constructionsymbol} {descriptionsymbol}
+
+\definetagconstant{verbatimblock} % Code
+\definetagconstant{verbatimlines} % Code
+\definetagconstant{verbatimline} % Code
+\definetagconstant{verbatim} % Code
+
+\definetagconstant{lines} % Code
+\definetagconstant{line} % Code
+\definetagconstant{linenumber} % Span
+
+\definetagconstant{sorting} % Span
+\definetagconstant{synonym} % Span
+
+\definetagconstant{register} % Div
+\definetagconstant{registerlocation} % Span
+\definetagconstant{registersection} % Div
+\definetagconstant{registertag} % Span
+\definetagconstant{registerentries} % Div
+\definetagconstant{registerentry} % Span
+\definetagconstant{registercontent} % Span
+\definetagconstant{registersee} % Span
+\definetagconstant{registerpages} % Span
+\definetagconstant{registerpage} % Span
+\definetagconstant{registerpagerange} % Span
+\definetagconstant{registerfrompage} % Span
+\definetagconstant{registertopage} % Span
+\definetagconstant{registerseparator} % Span
+
+\definetagconstant{table} % Table
+\definetagconstant{tablerow} % TR
+\definetagconstant{tablecell} % TD
+\definetagconstant{tableheadcell} % TH
+\definetagconstant{tablehead} % THEAD
+\definetagconstant{tablebody} % TBODY
+\definetagconstant{tablefoot} % TFOOT
+
+\definetagconstant{tabulate} % Table
+\definetagconstant{tabulaterow} % TR
+\definetagconstant{tabulatecell} % TD
+\definetagconstant{tabulateheadcell} % TH
+\definetagconstant{tabulatehead} % THEAD
+\definetagconstant{tabulatebody} % TBODY
+\definetagconstant{tabulatefoot} % TFOOT
+
+\definetagconstant{math} % math
+\definetagconstant{mtable} % Table
+\definetagconstant{mtr} % TR
+\definetagconstant{mtd} % TD
+\definetagconstant{maction} %
+\definetagconstant{mstacker}
+\definetagconstant{mstackertop}
+\definetagconstant{mstackermid}
+\definetagconstant{mstackerbot}
+
+\aliastagconstant{mtablerow} {mtr}
+\aliastagconstant{mtablecell}{mtd}
+
+\definetagconstant{munderover} % special cases
+\definetagconstant{munder} % special cases
+\definetagconstant{mover} % special cases
+
+\definetagconstant{list} % TOC
+\definetagconstant{listitem} % TOCI
+\definetagconstant{listtag} % Lbl
+\definetagconstant{listcontent} % P
+\definetagconstant{listdata} % P
+\definetagconstant{listpage} % Reference
+\definetagconstant{listtext} % Span
+
+\definetagconstant{delimited} % BlockQuote
+%definetagconstant{delimited} % Quote
+\definetagconstant{delimitedsymbol} % Span
+\definetagconstant{delimitedcontent} % Span
+
+\aliastagconstant{delimitedblock}{delimited}
+
+\definetagconstant{subsentence} % Span
+\definetagconstant{subsentencecontent} % Span
+\definetagconstant{subsentencesymbol} % Span
+
+\definetagconstant{float} % Div
+\definetagconstant{floatcaption} % Caption
+\definetagconstant{floatlabel} % Span
+\definetagconstant{floattext} % Span
+\definetagconstant{floatnumber} % Span
+\definetagconstant{floatcontent} % P
+
+\definetagconstant{image} % P
+
+\definetagconstant{mpgraphic} % P
+
+\definetagconstant{formulaset} % Div
+\definetagconstant{formula} % Div
+\definetagconstant{formulacaption} % Span
+\definetagconstant{formulalabel} % Span
+\definetagconstant{formulanumber} % P
+\definetagconstant{formulacontent} % P
+\definetagconstant{subformula} % Div
+
+\definetagconstant{link} % Link
+\definetagconstant{reference} % Span
+
+\definetagconstant{margintext} % Span
+\definetagconstant{margintextblock} % Div
+\definetagconstant{marginanchor} % Span
+
+% we might opt for verbose variants so this is experimental:
+
+\definetagconstant{label} % Span
+\definetagconstant{number} % Span
+
+\definetagconstant{ignore} % Span
+\definetagconstant{private} % Span
+
+\definetagconstant{mid} % Span
+\definetagconstant{sub} % Span
+\definetagconstant{sup} % Span
+\definetagconstant{subsup} % Span
+
+\definetagconstant{unit} % Span
+\definetagconstant{quantity} % Span
+%definetagconstant{number} % Span
+
+\definetagconstant{combination} % Span
+\definetagconstant{combinationpair} % Span
+\definetagconstant{combinationcontent} % Span
+\definetagconstant{combinationcaption} % Span
+
+\definetagconstant{publications} % Span
+\definetagconstant{publication} % Span
+\definetagconstant{pubfld} % Span
+
+\definetagconstant{block} % Div
+\definetagconstant{userdata} % Div
+
+% \setuptaglabeltext
+% [en]
+% [\t!document=document]
+
+% the real code
+
+\definesystemattribute[tagged][public]
+\definesystemattribute[image] [public]
+
+% \setelementbackendtag [#1][#2] % define at the lua end
+% \setelementnature [#1][#2] % define at the lua end
+
+\permanent\protected\def\ignoretagsinexport[#1]{\clf_ignoretagsinexport{#1}} % todo: public implementor
+
+\installcorenamespace{tagging}
+
+\installsetuponlycommandhandler \??tagging {tagging}
+
+\def\strc_tags_report_hyphen#1%
+ {\writestatus\m!languages{setting #1 to U+00AD}}
+
+\protected\def\strc_tags_patch_hyphen
+ {% for the moment here
+ \ifnum\languageparameter\s!lefthyphenchar>\zerocount
+ \setuplanguage[\s!default][\s!lefthyphenchar="AD]%
+ \strc_tags_report_hyphen\s!lefthyphenchar
+ \fi
+ \ifnum\languageparameter\s!righthyphenchar>\zerocount
+ \setuplanguage[\s!default][\s!righthyphenchar="AD]%
+ \strc_tags_report_hyphen\s!righthyphenchar
+ \fi
+ \let\strc_tags_report_hyphen\gobbleoneargument}
+
+% It makes no sense to have labels ... maybe some day as a last 'replace' in the export
+% which might be more efficient then ... okay, we now cannot overload but who cares.
+
+% \strc_tags_start_userdata % defined at the lua end
+% \strc_tags_stop % defined at the lua end
+% \strc_tags_set_aspect % defined at the lua end
+% \strc_tags_get_element_tag % defined at the lua end
+% \strc_tags_set_element_user_properties % defined at the lua end
+
+\permanent\protected\def\strc_tags_element_start_yes
+ {\iftrialtypesetting
+ \expandafter\gobbletwooptionals
+ \else
+ \expandafter\strc_tags_start_userdata
+ \fi}
+
+\permanent\protected\def\strc_tags_element_stop_yes
+ {\iftrialtypesetting
+ % nothing
+ \else
+ \expandafter\strc_tags_stop
+ \fi}
+
+\protected\def\strc_tags_enable_elements
+ {\strc_tags_patch_hyphen
+ \enforced\let\startelement \strc_tags_element_start_yes
+ \enforced\let\stopelement \strc_tags_element_stop_yes
+ \enforced\let\dosettagproperty\strc_tags_set_aspect}
+
+\protected\def\strc_tags_disable_elements
+ {\enforced\let\startelement \gobbletwooptionals
+ \enforced\let\stopelement \relax
+ \enforced\let\dosettagproperty\gobbletwoarguments}
+
+% beware: making these unexpanded spoils tables (noalign problem)
+
+\def\strc_tags_enabled_start_no_detail
+ {\iftrialtypesetting
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\strc_tags_start
+ \fi}
+
+\def\strc_tags_enabled_start_detail
+ {\iftrialtypesetting
+ \expandafter\gobbletwoarguments
+ \else
+ \expandafter\strc_tags_start_detail
+ \fi}
+
+\def\strc_tags_enabled_start_chained
+ {\iftrialtypesetting
+ \expandafter\gobblethreearguments
+ \else
+ \expandafter\strc_tags_start_chained_indeed
+ \fi}
+
+\def\strc_tags_enabled_start_ignore
+ {\iftrialtypesetting
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\strc_tags_start_ignore
+ \fi}
+
+\def\strc_tags_enabled_stop
+ {\iftrialtypesetting
+ % do nothing
+ \else
+ \expandafter\strc_tags_stop
+ \fi}
+
+\def\strc_tags_start_chained_indeed#1#2#3{\strc_tags_start_chained{#1}{#2}{\getcurrentparentchain#3{#2}}}
+
+\newconditional\c_strc_tags_enabled
+
+\aliased\let\dotaggedplaceholder\empty
+
+\immutable\chardef\strc_tags_placeholder_char\zerocount % "FFFC
+
+\protected\def\strc_tags_enable_indeed
+ {\enforced\let\dotaggedplaceholder \strc_tags_placeholder_char
+ \enforced\let\dostarttagged \strc_tags_enabled_start_detail
+ \enforced\let\dostarttaggednodetail\strc_tags_enabled_start_no_detail
+ \enforced\let\dostarttaggedchained \strc_tags_enabled_start_chained
+ \enforced\let\dostoptagged \strc_tags_enabled_stop
+ \enforced\let\dostartignoretagging \strc_tags_enabled_start_ignore
+ \enforced\let\dostopignoretagging \strc_tags_stop}
+
+\protected\def\strc_tags_enable
+ {% once enable one is toast
+ \global\settrue\c_strc_tags_enabled
+ % and gets:
+ \strc_tags_enable_indeed}
+
+\protected\def\strc_tags_disable
+ {\ifconditional\c_strc_tags_enabled
+ % so now all are artifacts
+ \enforced\let\dotaggedplaceholder \strc_tags_placeholder_char
+ \enforced\let\dostarttagged \gobbletwoarguments
+ \enforced\let\dostarttaggednodetail\gobbleoneargument
+ \enforced\let\dostarttaggedchained \gobblethreearguments
+ \enforced\let\dostoptagged \donothing
+ \else
+ % initial
+ \enforced\let\dotaggedplaceholder \empty
+ \enforced\let\dostarttagged \gobbletwoarguments
+ \enforced\let\dostarttaggednodetail\gobbleoneargument
+ \enforced\let\dostarttaggedchained \gobblethreearguments
+ \enforced\let\dostoptagged \donothing
+ \enforced\let\dostartignoretagging \donothing
+ \enforced\let\dostopignoretagging \donothing
+ \fi}
+
+% for luigi (beware: fully expandable):
+
+\protected\def\strc_tags_setup_element_user_properties
+ {\iftrialtypesetting
+ \expandafter\gobbletwooptionals
+ \else
+ \expandafter\strc_tags_set_element_user_properties
+ \fi}
+
+\protected\def\strc_tags_enable_properties
+ {\enforced\let\getelementtag \strc_tags_get_element_tag
+ \enforced\let\setupelementuserproperties\strc_tags_set_element_user_properties}
+
+\protected\def\strc_tags_disable_properties
+ {\enforced\let\getelementtag \donothing
+ \enforced\let\setupelementuserproperties\gobbletwooptionals}
+
+%D The triggers:
+
+\newtoks\everyenableelements
+\newtoks\everydisableelements
+
+\appendtoks
+ \strc_tags_enable_elements
+ \strc_tags_enable_properties
+ \doifelse{\taggingparameter\c!method}\v!auto\strc_tags_enable\strc_tags_disable
+\to \everyenableelements
+
+\appendtoks
+ \strc_tags_disable_elements
+ \strc_tags_disable_properties
+ \strc_tags_disable
+\to \everydisableelements
+
+\appendtoks
+ \doifelse{\taggingparameter\c!state}\v!start{\the\everyenableelements}{\the\everydisableelements}%
+\to \everysetuptagging
+
+\permanent\protected\def\forgettagging
+ {\c_attr_tagged\attributeunsetvalue}
+
+\setuptagging
+ [\c!state=\v!stop,
+ \c!method=\v!auto]
+
+% Cf suggestion by Wolfgang we now have named paragraphs. Watch out, the content
+% is grouped but only when we have an instance.
+%
+% \defineparagraph[red] [color=red]
+% \defineparagraph[bold][style=bold]
+%
+% \startparagraph \input ward \stopparagraph
+% \startparagraph[red] \input ward \stopparagraph
+% \startparagraph[bold] \input ward \stopparagraph
+
+\installcorenamespace {paragraph}
+\installcommandhandler \??paragraph {paragraph} \??paragraph
+
+\setupparagraph % someday maybe also strut (beg/end) and align
+ [\c!color=,
+ \c!style=]
+
+\ifdefined\dotagparagraph \else \aliased\let\dotagparagraph\gobbleoneargument \fi
+
+\permanent\tolerant\protected\def\startparagraph[#1]#*[#2]%
+ {\endgraf % we end before the group
+ \begingroup
+ \ifarguments
+ \let\currentparagraph\empty
+ \or
+ \ifhastok={#1}%
+ \let\currentparagraph\empty
+ \setupcurrentparagraph[#1]
+ \else
+ \edef\currentparagraph{#1}%
+ \fi
+ \or
+ \edef\currentparagraph{#1}%
+ \setupcurrentparagraph[#2]%
+ \fi
+ \useparagraphstyleandcolor\c!style\c!color
+ \usealignparameter\paragraphparameter
+ \usesetupsparameter\paragraphparameter
+ \dostarttagged\t!paragraph\currentparagraph
+ \dotagparagraph{\paragraphparameter\c!align}}
+
+\permanent\protected\def\stopparagraph
+ {\dostoptagged
+ \endgraf % we end inside the group
+ \endgroup}
+
+\aliased\let\startpar\startparagraph
+\aliased\let\stoppar \stopparagraph
+
+\def\strc_tags_document_start_indeed
+ {\glet\strc_tags_document_start_indeed\relax
+ \dostarttagged\t!document\empty}
+
+\def\strc_tags_document_stop_indeed
+ {\glet\strc_tags_document_stop_indeed\relax
+ \dostoptagged}
+
+\appendtoks
+ \strc_tags_document_start_indeed % here because otherwise products don't get a root (starttext before env)
+\to \everyenableelements
+
+% \appendtoks
+% \strc_tags_document_start_indeed
+% \to \everystarttext
+
+\appendtoks
+ \strc_tags_document_stop_indeed
+\to \everystoptext
+
+\appendtoks
+ \strc_tags_disable_elements
+ \strc_tags_disable
+\to \everybeforepagebody
+
+% This doesn't work well either, so instead we handle the ornaments in the tagging
+% in a different way (see attr -> false code).
+
+% \appendtoks
+% \dostartignoretagging
+% \to \everybeforepagebody
+%
+% \appendtoks
+% \dostopignoretagging
+% \to \everyafterpagebody
+
+% \doifelseinelement{structure:section} {yes} {no}
+% \doifelseinelement{structure:chapter} {yes} {no}
+% \doifelseinelement{division:*-structure:chapter} {yes} {no}
+
+\aliased\let\doifinelementelse\doifelseinelement % define at the lua end
+
+\permanent\protected\def\taggedlabeltexts#1#2#3% experimental: label, numberdetail, numbercontent
+ {\begingroup
+ \dostarttagged\t!label{#1}%
+ \labeltexts{#1}%
+ {\dostoptagged
+ \dostarttagged\t!number{#2}%
+ #3%
+ \dostoptagged
+ \dostarttagged\t!label{#1}}%
+ \dostoptagged
+ \endgroup}
+
+\permanent\protected\def\namedtaggedlabeltexts#1#2#3#4#5% experimental: labeltag label numbertag numberdetail numbercontent
+ {\begingroup
+ \dostarttagged{#1}{#2}%
+ \labeltexts{#2}%
+ {\dostoptagged
+ \dostarttagged{#3}{#4}%
+ #5%
+ \dostoptagged
+ \dostarttagged{#1}{#2}}%
+ \dostoptagged
+ \endgroup}
+
+%D Metadata is added after the following structure element so here we get some as
+%D child of the document root and some as child of the chapter element.
+%D
+%D \settaggedmetadata[title=Hello World!,author=Hans Hagen]
+%D
+%D \starttyping
+%D \starttext
+%D \startelement[ignore]
+%D \input tufte
+%D \stopelement
+%D \par \input ward \par
+%D \settaggedmetadata[whatever=Again and Again]
+%D \startchapter[title=test]
+%D \input ward
+%D \stopchapter
+%D \stoptext
+%D \stoptyping
+
+% \settaggedmetadata[#1] % define at the lua end
+
+%D An overload:
+
+\pushoverloadmode
+
+\aliased\let\strc_tagged_saved_bpar\bpar
+\aliased\let\strc_tagged_saved_epar\epar
+
+\enforced\permanent\protected\def\bpar{\dostarttagged\t!paragraph\empty\strc_tagged_saved_bpar}
+\enforced\permanent\protected\def\epar{\strc_tagged_saved_epar\dostoptagged}
+
+\popoverloadmode
+
+% \permanent\def\untagged{attr \taggedattribute\attributeunsetvalue}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-tnt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-tnt.mkxl
new file mode 100644
index 00000000000..4129117f06a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-tnt.mkxl
@@ -0,0 +1,117 @@
+%D \module
+%D [ file=strc-tnt,
+%D version=2019.05.30, % based on older code
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Text Notes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D \macros
+%D {definetextnote,setuptextnote}
+%D
+%D \startbuffer
+%D \definetextnote
+%D [textnote]
+%D
+%D \startbuffer
+%D Test test test \textnote [n=5] {alpha}. test test test test tets test test
+%D \textnote [n=10] {beta}. Test test test test tets test test \textnote [n=12]
+%D {gamma}. Test test test test tets test test \textnote [n=24] {delta}. Test test
+%D test test test test \textnote {epsilon} test test \textnote [n=*] {zeta}.
+%D \stopbuffer
+%D
+%D \blank {\setuptextnote[empty=yes] \getbuffer\par} \blank
+%D \blank {\setuptextnote[empty=number] \getbuffer\par} \blank
+%D \blank {\setuptextnote[empty=none] \getbuffer\par} \blank
+%D \blank { \getbuffer\par} \blank
+%D
+%D \blank[2*big]
+%D
+%D \placenotes[textnote:note][criterium=text]
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\installcorenamespace{textnote}
+
+\installcommandhandler \??textnote {textnote} \??textnote
+
+\definebar
+ [\v!textnote:\v!underbar]
+ [\v!underbar]
+
+\definenote
+ [\v!textnote:\v!note]
+
+\setuptextnote
+ [\c!rule=\v!textnote:\v!underbar,
+ \c!note=\v!textnote:\v!note,
+ \c!n=10] % * will use the real space
+
+\appendtoks
+ \frozen\instance\setuevalue{\currenttextnote}{\educ_textnote[\currenttextnote]}%
+\to \everydefinetextnote
+
+\tolerant\protected\def\educ_textnote[#1]#*[#2]#:#3%
+ {\dontleavehmode
+ \begingroup
+ \def\currenttextnote{#1}%
+ \ifparameter#2\or\setupcurrenttextnote[#2]\fi
+ \edef\p_n{\textnoteparameter\c!n}%
+ \edef\p_empty{\textnoteparameter\c!empty}%
+ \edef\currentbar{\textnoteparameter\c!rule}%
+ \edef\currentnote{\textnoteparameter\c!note}%
+ \ifx\p_n\wildcardsymbol
+ \donefalse
+ \ifx\p_empty\v!yes
+ \donetrue
+ \orelse\ifx\p_empty\v!number
+ \donetrue
+ \orelse\ifx\p_empty\v!none
+ \donetrue
+ \fi
+ \ifdone
+ \setupbar[\currentbar][\c!empty=\v!yes]%
+ \fi
+ \inlinebar[\currentbar]\bgroup
+ \wordboundary#3%
+ \ifx\p_empty\v!yes
+ \setnotetext[\currentnote]{#3}%
+ \orelse\ifx\p_empty\v!number
+ \runninghbox{\resetbar\setnote[\currentnote]{#3}}%
+ \orelse\ifx\p_empty\v!none
+ \setupnote[\currentnote][\c!location=\v!none]%
+ \runninghbox{\resetbar\setnote[\currentnote]{#3}}%
+ \fi
+ \egroup
+ \else
+ \inlinebar[\currentbar]\bgroup
+ \scratchcounter\numexpr\p_n/\plustwo\relax
+ \ifx\p_empty\v!yes
+ \interwordspacesbefore\scratchcounter
+ \setnotetext[\currentnote]{#3}%
+ \interwordspacesafter\scratchcounter
+ \orelse\ifx\p_empty\v!number
+ \interwordspacesbefore\scratchcounter
+ \zwnj\runninghbox{\resetbar\setnote[\currentnote]{#3}}\zwnj
+ \interwordspacesafter\scratchcounter
+ \orelse\ifx\p_empty\v!none
+ \setupnote[\currentnote][\c!location=\v!none]%
+ \interwordspacesbefore\scratchcounter
+ \zwnj\runninghbox{\resetbar\setnote[\currentnote]{#3}}\zwnj
+ \interwordspacesafter\scratchcounter
+ \else
+ #3%
+ \fi
+ \egroup
+ \fi
+ \endgroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/strc-usr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/strc-usr.mkxl
new file mode 100644
index 00000000000..28d398194f1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/strc-usr.mkxl
@@ -0,0 +1,169 @@
+%D \module
+%D [ file=strc-bkm,
+%D version=2009.04.01,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Bookmarks,
+%D author=Wolfgang Schuster,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Structure Macros / Userdata}
+
+\registerctxluafile{strc-usr}{}
+
+%D It's a bit like blocks that also use buffers but more lightweight and with
+%D inplace settings.
+%D
+%D \starttyping
+%D \defineuserdata [test] [style=italic]
+%D
+%D \samplefile{klein}
+%D
+%D \startuserdata [before=\blank,after=\blank,color=red]
+%D \samplefile{greenfield}
+%D \stopuserdata
+%D
+%D \samplefile{sapolsky}
+%D
+%D \startuserdata [test]
+%D \samplefile{bryson}
+%D \stopuserdata
+%D
+%D \samplefile{jojomayer}
+%D
+%D \startuserdata [test] [before=\blank,after=\blank,color=red]
+%D \samplefile{linden}
+%D \stopuserdata
+%D
+%D \samplefile{montgomery}
+%D \stoptyping
+%D
+%D Or from \LUA:
+%D
+%D \starttyping
+%D \startluacode
+%D context.startuserdata({color="blue"})
+%D context.samplefile("klein")
+%D context.stopuserdata()
+%D \stopluacode
+%D \stoptyping
+%D
+%D An example of an alternative:
+%D
+%D \starttyping
+%D \defineuserdataalternative [epigraph] [renderingsetup=userdata:epigraph]
+%D
+%D \startsetups [userdata:epigraph]
+%D \startframedtext [location=right,frame=off,align={flushleft,broad},style=\tfx,offset=.25ex,width=.5\textwidth]
+%D \begstrut\inlinebuffer[userdata]\endstrut
+%D \hairline
+%D \wordright{\userdataparameter{author}}
+%D \stopframedtext
+%D \stopsetups
+%D
+%D \defineuserdata
+%D [epigraph]
+%D [alternative=epigraph]
+%D
+%D \startuserdata [epigraph] [author={Sean B. Carrol}]
+%D The fraction of fossil olfactory receptor genes is significantly higher in
+%D all species with full color vision. This suggests that the evolution of
+%D trichromatic vision --- which allows these primates to detect food, mates,
+%D and danger with visual cues --- has reduced their reliance on the sense of
+%D smell.
+%D \stopuserdata
+%D
+%D \startuserdata [epigraph] [author={Sean B. Carrol}]
+%D \samplefile{carrol}
+%D \stopuserdata
+%D \stoptyping
+
+\unprotect
+
+\installnamespace {userdata}
+\installnamespace {userdataalternative}
+\installnamespace {userdatarenderings}
+
+\installcommandhandler \????userdata {userdata} \????userdata
+\installcommandhandler \????userdataalternative {userdataalternative} \????userdataalternative
+
+\permanent\protected\def\startuserdata
+ {\begingroup
+ \let\currentuserdata\empty
+ \doifelsenextoptionalcs\userdata_start_delayed\userdata_start_indeed}
+
+% This variant works only when the userdata instance exists while the assignment check
+% can also be used with undefined instances which falls back to the global settings.
+%
+% \def\userdata_start_delayed[#1]%
+% {\ifcsname\nameduserdatahash{\detokenize\expandafter{\normalexpanded{#1}}}\s!parent\endcsname
+% \expandafter\userdata_start_delayed_name
+% \else
+% \expandafter\userdata_start_delayed_parameters
+% \fi[#1]}
+
+\def\userdata_start_delayed[#1]%
+ {\doifelseassignmentcs{#1}%
+ \userdata_start_delayed_parameters
+ \userdata_start_delayed_name
+ [#1]}
+
+\def\userdata_start_delayed_parameters[#1]%
+ {\setupcurrentuserdata[#1]%
+ \userdata_start_indeed}
+
+\def\userdata_start_delayed_name[#1]%
+ {\edef\currentuserdata{#1}%
+ \checkuserdataparent
+ \doifelsenextoptionalcs\userdata_start_delayed_parameters\userdata_start_indeed}
+
+\def\userdata_start_indeed
+ {\grabbufferdatadirect\s!userdata{\csstring\startuserdata}{\csstring\stopuserdata}}
+
+\permanent\protected\def\stopuserdata
+ {\userdataparameter\c!before % HH: moved, so we obey the outer spacing
+ \dostarttagged\t!userdata\currentuserdata % HH: added, maybe move up ?
+ \begingroup
+ \useuserdatastyleandcolor\c!style\c!color
+ \usealignparameter\userdataparameter % HH: added
+ \edef\currentuserdataalternative{\userdataparameter\c!alternative}%
+ \ifcsname\currentuserdataalternativehash\s!parent\endcsname \else
+ \let\currentuserdataalternative\s!default
+ \fi
+ \usesetupsparameter\userdataparameter
+ \edef\p_renderingsetup{\userdataalternativeparameter\c!renderingsetup}%
+ \directsetup\p_renderingsetup
+ \endgroup
+ \dostoptagged
+ \userdataparameter\c!after % HH: moved
+ \endgroup}
+
+\permanent\protected\def\getuserdata
+ {\getbufferdata[\s!userdata]}
+
+\permanent\protected\def\getinlineuserdata
+ {\inlinebuffer[\s!userdata]}
+
+\defineuserdataalternative
+ [\s!default]
+ [\c!renderingsetup=\????userdatarenderings:\s!default]
+
+% \startsetups[\????userdatarenderings:\s!default]
+% \userdataparameter\c!before
+% \usesetupsparameter\userdataparameter
+% \getbufferdata[\s!userdata]
+% \userdataparameter\c!after
+% \stopsetups
+
+\startsetups[\????userdatarenderings:\s!default]
+ \getuserdata
+\stopsetups
+
+\setupuserdata
+ [\c!alternative=\s!default]
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/supp-box.lmt b/Master/texmf-dist/tex/context/base/mkxl/supp-box.lmt
new file mode 100644
index 00000000000..657e3addb97
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/supp-box.lmt
@@ -0,0 +1,1395 @@
+if not modules then modules = { } end modules ['supp-box'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to supp-box.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this is preliminary code, use insert_before etc
+
+local report_hyphenation = logs.reporter("languages","hyphenation")
+
+local tonumber, next, type = tonumber, next, type
+
+local lpegmatch = lpeg.match
+
+local tex = tex
+local context = context
+local nodes = nodes
+
+local implement = interfaces.implement
+
+local nodecodes = nodes.nodecodes
+
+local disc_code = nodecodes.disc
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local penalty_code = nodecodes.penalty
+local glyph_code = nodecodes.glyph
+local par_code = nodecodes.par
+
+local indent_code = nodes.listcodes.indent
+
+local hmode_code = tex.modelevels.horizontal
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+local tonode = nuts.tonode
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getdisc = nuts.getdisc
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local getattribute = nuts.getattribute
+local getbox = nuts.getbox
+local getdirection = nuts.getdirection
+local getwidth = nuts.getwidth
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+local getwhd = nuts.getwhd
+local takebox = nuts.takebox
+
+local setlink = nuts.setlink
+local setboth = nuts.setboth
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local setbox = nuts.setbox
+local setlist = nuts.setlist
+local setdisc = nuts.setdisc
+local setwidth = nuts.setwidth
+local setheight = nuts.setheight
+local setdepth = nuts.setdepth
+local setshift = nuts.setshift
+local setsplit = nuts.setsplit
+local setattrlist = nuts.setattrlist
+local setwhd = nuts.setwhd
+local setglue = nuts.setglue
+
+local flush_node = nuts.flush_node
+local flush_list = nuts.flush_list
+local copy_node = nuts.copy
+local copy_list = nuts.copy_list
+local find_tail = nuts.tail
+local getdimensions = nuts.dimensions
+local hpack = nuts.hpack
+local vpack = nuts.vpack
+local traverse_id = nuts.traverse_id
+local traverse = nuts.traverse
+local free = nuts.free
+local findtail = nuts.tail
+local reverse = nuts.reverse
+local effective_glue= nuts.effective_glue
+
+local nextdisc = nuts.traversers.disc
+local nextdir = nuts.traversers.dir
+local nexthlist = nuts.traversers.hlist
+
+local listtoutf = nodes.listtoutf
+
+local nodepool = nuts.pool
+local new_penalty = nodepool.penalty
+local new_hlist = nodepool.hlist
+local new_glue = nodepool.glue
+
+local setlistcolor = nodes.tracers.colors.setlist
+
+local texget = tex.get
+local texgetbox = tex.getbox
+local texsetdimen = tex.setdimen
+local texgetnest = tex.getnest
+
+local function hyphenatedlist(head,usecolor)
+ local current = head and tonut(head)
+ while current do
+ local id = getid(current)
+ local prev, next = getboth(current)
+ if id == disc_code then
+ local pre, post, replace = getdisc(current)
+ if not usecolor then
+ -- nothing fancy done
+ elseif pre and post then
+ setlistcolor(pre,"darkmagenta")
+ setlistcolor(post,"darkcyan")
+ elseif pre then
+ setlistcolor(pre,"darkyellow")
+ elseif post then
+ setlistcolor(post,"darkyellow")
+ end
+ if replace then
+ flush_list(replace)
+ end
+ setdisc(current)
+ if pre then
+ setlink(prev,new_penalty(10000),pre)
+ setlink(find_tail(pre),current)
+ end
+ if post then
+ setlink(current,new_penalty(10000),post)
+ setlink(find_tail(post),next)
+ end
+ elseif id == vlist_code or id == hlist_code then
+ hyphenatedlist(getlist(current))
+ end
+ current = next
+ end
+end
+
+implement {
+ name = "hyphenatedlist",
+ arguments = { "integer", "boolean" },
+ actions = function(n,color)
+ local b = texgetbox(n)
+ if b then
+ hyphenatedlist(b.list,color)
+ end
+ end
+}
+
+local function checkedlist(list)
+ if type(list) == "number" then
+ return getlist(getbox(tonut(list)))
+ else
+ return tonut(list)
+ end
+end
+
+implement {
+ name = "showhyphenatedinlist",
+ arguments = "integer",
+ actions = function(n)
+ -- we just hyphenate (as we pass a hpack) .. a bit too much casting but ...
+ local l = languages.hyphenators.handler(tonode(checkedlist(n)))
+ report_hyphenation("show: %s",listtoutf(l,false,true))
+ end
+}
+
+local function applytochars(current,doaction,noaction,nested)
+ while current do
+ local id = getid(current)
+ if nested and (id == hlist_code or id == vlist_code) then
+ context.beginhbox()
+ applytochars(getlist(current),doaction,noaction,nested)
+ context.endhbox()
+ elseif id ~= glyph_code then
+ noaction(tonode(copy_node(current)))
+ else
+ doaction(tonode(copy_node(current)))
+ end
+ current = getnext(current)
+ end
+end
+
+local function applytowords(current,doaction,noaction,nested)
+ local start
+ while current do
+ local id = getid(current)
+ if id == glue_code then
+ if start then
+ doaction(tonode(copy_list(start,current)))
+ start = nil
+ end
+ noaction(tonode(copy_node(current)))
+ elseif nested and (id == hlist_code or id == vlist_code) then
+ context.beginhbox()
+ applytowords(getlist(current),doaction,noaction,nested)
+ context.egroup()
+ elseif not start then
+ start = current
+ end
+ current = getnext(current)
+ end
+ if start then
+ doaction(tonode(copy_list(start)))
+ end
+end
+
+local methods = {
+ char = applytochars,
+ characters = applytochars,
+ word = applytowords,
+ words = applytowords,
+}
+
+implement {
+ name = "applytobox",
+ arguments = {
+ {
+ { "box", "integer" },
+ { "command" },
+ { "method" },
+ { "nested", "boolean" },
+ }
+ },
+ actions = function(specification)
+ local list = checkedlist(specification.box)
+ local action = methods[specification.method or "char"]
+ if list and action then
+ action(list,context[specification.command or "ruledhbox"],context,specification.nested)
+ end
+ end
+}
+
+local split_char = lpeg.Ct(lpeg.C(1)^0)
+local split_word = lpeg.tsplitat(lpeg.patterns.space)
+local split_line = lpeg.tsplitat(lpeg.patterns.eol)
+
+local function processsplit(specification)
+ local str = specification.data or ""
+ local command = specification.command or "ruledhbox"
+ local method = specification.method or "word"
+ local spaced = specification.spaced
+ if command then
+ command = context[command]
+ end
+ if method == "char" or method == "character" then
+ if spaced then
+ spaced = context.space
+ end
+ local words = lpegmatch(split_char,str)
+ for i=1,#words do
+ local word = words[i]
+ if word == " " then
+ if spaced then
+ spaced()
+ end
+ elseif command then
+ command(word)
+ else
+ context(word)
+ end
+ end
+ elseif method == "word" then
+ if spaced then
+ spaced = context.space
+ end
+ local words = lpegmatch(split_word,str)
+ for i=1,#words do
+ local word = words[i]
+ if spaced and i > 1 then
+ spaced()
+ end
+ if command then
+ command(word)
+ else
+ context(word)
+ end
+ end
+ elseif method == "line" then
+ if spaced then
+ spaced = context.par
+ end
+ local words = lpegmatch(split_line,str)
+ for i=1,#words do
+ local word = words[i]
+ if spaced and i > 1 then
+ spaced()
+ end
+ if command then
+ command(word)
+ else
+ context(word)
+ end
+ end
+ else
+ context(str)
+ end
+end
+
+implement {
+ name = "processsplit",
+ actions = processsplit,
+ arguments = {
+ {
+ { "data" },
+ { "command" },
+ { "method" },
+ { "spaced", "boolean" },
+ }
+ }
+}
+
+local a_vboxtohboxseparator = attributes.private("vboxtohboxseparator")
+
+implement {
+ name = "vboxlisttohbox",
+ arguments = { "integer", "integer", "dimen" },
+ actions = function(original,target,inbetween)
+ local current = getlist(getbox(original))
+ local head = nil
+ local tail = nil
+ while current do
+ local id = getid(current)
+ local next = getnext(current)
+ if id == hlist_code then
+ local list = getlist(current)
+ if head then
+ if inbetween > 0 then
+ local n = new_glue(0,0,inbetween)
+ setlink(tail,n)
+ tail = n
+ end
+ setlink(tail,list)
+ else
+ head = list
+ end
+ tail = find_tail(list)
+ -- remove last separator
+ if getid(tail) == hlist_code and getattribute(tail,a_vboxtohboxseparator) == 1 then
+ local temp = tail
+ local prev = getprev(tail)
+ if next then
+ local list = getlist(tail)
+ setlink(prev,list)
+ setlist(tail)
+ tail = find_tail(list)
+ else
+ tail = prev
+ end
+ flush_node(temp)
+ end
+ -- done
+ setnext(tail)
+ setlist(current)
+ end
+ current = next
+ end
+ local result = new_hlist()
+ setlist(result,head)
+ setbox(target,result)
+ -- setbox(target,new_hlist(head))
+ end
+}
+
+implement {
+ name = "hboxtovbox",
+ arguments = "integer",
+ actions = function(n)
+ local b = getbox(n)
+ local factor = texget("baselineskip",false) / texget("hsize")
+ setdepth(b,0)
+ setheight(b,getwidth(b) * factor)
+ end
+}
+
+implement {
+ name = "boxtostring",
+ arguments = "integer",
+ public = true,
+ actions = function(n)
+ context.verbatim(nodes.toutf(texgetbox(n).list)) -- helper is defined later
+ end
+}
+
+local function getnaturaldimensions(n)
+ local w = 0
+ local h = 0
+ local d = 0
+ local l = getlist(getbox(n))
+ if l then
+ w, h, d = getdimensions(l)
+ end
+ texsetdimen("lastnaturalboxwd",w)
+ texsetdimen("lastnaturalboxht",h)
+ texsetdimen("lastnaturalboxdp",d)
+ return w, h, d
+end
+
+implement {
+ name = "getnaturaldimensions",
+ arguments = "integer",
+ actions = getnaturaldimensions
+}
+
+implement {
+ name = "naturalwd",
+ arguments = "integer",
+ actions = function(n)
+ getnaturaldimensions(n)
+ context.lastnaturalboxwd(false)
+ end
+}
+
+implement {
+ name = "getnaturalwd",
+ arguments = "integer",
+ actions = function(n)
+ local w = 0
+ local h = 0
+ local d = 0
+ local l = getlist(getbox(n))
+ if l then
+ w, h, d = getdimensions(l)
+ end
+ context("\\dimexpr%i\\scaledpoint\\relax",w)
+ end
+}
+
+local function setboxtonaturalwd(n)
+ local old = takebox(n)
+ local new = hpack(getlist(old))
+ setlist(old,nil)
+ flush_node(old)
+ setbox(n,new)
+end
+
+implement {
+ name = "setnaturalwd",
+ arguments = "integer",
+ actions = setboxtonaturalwd
+}
+
+nodes.setboxtonaturalwd = setboxtonaturalwd
+
+local doifelse = commands.doifelse
+
+do
+
+ local dirvalues = nodes.dirvalues
+ local lefttoright_code = dirvalues.lefttoright
+ local righttoleft_code = dirvalues.righttoleft
+
+ local function firstdirinbox(n)
+ local b = getbox(n)
+ if b then
+ local l = getlist(b)
+ if l then
+ for d in nextdir, l do
+ return getdirection(d)
+ end
+ for h in nexthlist, l do
+ return getdirection(h)
+ end
+ end
+ end
+ return lefttoright_code
+ end
+
+ nodes.firstdirinbox = firstdirinbox
+
+ implement {
+ name = "doifelserighttoleftinbox",
+ arguments = "integer",
+ actions = function(n)
+ doifelse(firstdirinbox(n) == righttoleft_code)
+ end
+ }
+
+end
+
+-- new (handy for mp) .. might move to its own module
+
+do
+
+ local tonut = nodes.tonut
+ local takebox = nuts.takebox
+ local flush_list = nuts.flush_list
+ local copy_list = nuts.copy_list
+ local getwhd = nuts.getwhd
+ local setbox = nuts.setbox
+ local new_hlist = nuts.pool.hlist
+
+ local values = tokens.values
+ local dimension_value = values.dimension
+
+ local boxes = { }
+ nodes.boxes = boxes
+ local cache = table.setmetatableindex("table")
+ local report = logs.reporter("boxes","cache")
+ local trace = false
+
+ trackers.register("nodes.boxes",function(v) trace = v end)
+
+ function boxes.save(category,name,b)
+ name = tonumber(name) or name
+ local b = takebox(b)
+ if trace then
+ report("category %a, name %a, %s (%s)",category,name,"save",b and "content" or "empty")
+ end
+ cache[category][name] = b or false
+ end
+
+ function boxes.savenode(category,name,n)
+ name = tonumber(name) or name
+ if trace then
+ report("category %a, name %a, %s (%s)",category,name,"save",n and "content" or "empty")
+ end
+ cache[category][name] = tonut(n) or false
+ end
+
+ function boxes.found(category,name)
+ name = tonumber(name) or name
+ return cache[category][name] and true or false
+ end
+
+ function boxes.direct(category,name,copy)
+ name = tonumber(name) or name
+ local c = cache[category]
+ local b = c[name]
+ if not b then
+ -- do nothing, maybe trace
+ elseif copy then
+ b = copy_list(b)
+ else
+ c[name] = false
+ end
+ if trace then
+ report("category %a, name %a, %s (%s)",category,name,"direct",b and "content" or "empty")
+ end
+ if b then
+ return tonode(b)
+ end
+ end
+
+ function boxes.restore(category,name,box,copy)
+ name = tonumber(name) or name
+ local c = cache[category]
+ local b = takebox(box)
+ if b then
+ flush_list(b)
+ end
+ local b = c[name]
+ if not b then
+ -- do nothing, maybe trace
+ elseif copy then
+ b = copy_list(b)
+ else
+ c[name] = false
+ end
+ if trace then
+ report("category %a, name %a, %s (%s)",category,name,"restore",b and "content" or "empty")
+ end
+ setbox(box,b or nil)
+ end
+
+ local function dimensions(category,name)
+ name = tonumber(name) or name
+ local b = cache[category][name]
+ if b then
+ return getwhd(b)
+ else
+ return 0, 0, 0
+ end
+ end
+
+ boxes.dimensions = dimensions
+
+ function boxes.reset(category,name)
+ name = tonumber(name) or name
+ local c = cache[category]
+ if name and name ~= "" then
+ local b = c[name]
+ if b then
+ flush_list(b)
+ c[name] = false
+ end
+ if trace then
+ report("category %a, name %a, reset",category,name)
+ end
+ else
+ for k, b in next, c do
+ if b then
+ flush_list(b)
+ end
+ end
+ cache[category] = { }
+ if trace then
+ report("category %a, reset",category)
+ end
+ end
+ end
+
+ implement {
+ name = "putboxincache",
+ public = true,
+ protected = true,
+ arguments = { "string", "string", "integer" },
+ actions = boxes.save,
+ }
+
+ implement {
+ name = "getboxfromcache",
+ public = true,
+ protected = true,
+ arguments = { "string", "string", "integer" },
+ actions = boxes.restore,
+ }
+
+ implement {
+ name = "directboxfromcache",
+ public = true,
+ protected = true,
+ arguments = "2 strings",
+ actions = { boxes.direct, context },
+ -- actions = function(category,name) local b = boxes.direct(category,name) if b then context(b) end end,
+ }
+
+ implement {
+ name = "directcopyboxfromcache",
+ public = true,
+ protected = true,
+ arguments = { "string", "string", true },
+ actions = { boxes.direct, context },
+ -- actions = function(category,name) local b = boxes.direct(category,name,true) if b then context(b) end end,
+ }
+
+ implement {
+ name = "copyboxfromcache",
+ public = true,
+ protected = true,
+ arguments = { "string", "string", "integer", true },
+ actions = boxes.restore,
+ }
+
+ implement {
+ name = "doifelseboxincache",
+ public = true,
+ protected = true,
+ arguments = "2 strings",
+ actions = { boxes.found, doifelse },
+ }
+
+ implement {
+ name = "resetboxesincache",
+ public = true,
+ protected = true,
+ arguments = "string",
+ actions = boxes.reset,
+ }
+
+ -- we can share this ...
+
+ implement {
+ name = "getboxwdfromcache",
+ public = true,
+ protected = true,
+ usage = "value",
+ arguments = "2 strings",
+ actions = function(category,name)
+ local w, h, d = dimensions(category,name)
+ return dimension_value, w
+ end,
+ }
+
+ implement {
+ name = "getboxhtfromcache",
+ arguments = "2 strings",
+ public = true,
+ protected = true,
+ usage = "value",
+ actions = function(category,name)
+ local w, h, d = dimensions(category,name)
+ return dimension_value, h
+ end,
+ }
+
+ implement {
+ name = "getboxdpfromcache",
+ arguments = "2 strings",
+ public = true,
+ protected = true,
+ usage = "value",
+ actions = function(category,name)
+ local w, h, d = dimensions(category,name)
+ return dimension_value, d
+ end,
+ }
+
+ implement {
+ name = "putnextboxincache",
+ public = true,
+ protected = true,
+ arguments = { "string", "string", "box" },
+ actions = function(category,name,b)
+ name = tonumber(name) or name
+ b = tonut(b)
+ if trace then
+ report("category %a, name %a, %s (%s)",category,name,"save",b and "content" or "empty")
+ end
+ cache[category][name] = b or false
+ end
+ }
+
+end
+
+implement {
+ name = "lastlinewidth",
+ actions = function()
+ local head = tex.lists.page_head
+ -- list dimensions returns 3 value but we take the first
+ context(head and getdimensions(getlist(find_tail(tonut(tex.lists.page_head)))) or 0)
+ end
+}
+
+implement {
+ name = "shiftbox",
+ arguments = { "integer", "dimension" },
+ actions = function(n,d)
+ setshift(getbox(n),d)
+ end,
+}
+
+implement { name = "vpackbox", arguments = "integer", actions = function(n) setbox(n,(vpack(takebox(n)))) end }
+implement { name = "hpackbox", arguments = "integer", actions = function(n) setbox(n,(hpack(takebox(n)))) end }
+
+implement { name = "vpackedbox", arguments = "integer", actions = function(n) context(vpack(takebox(n))) end }
+implement { name = "hpackedbox", arguments = "integer", actions = function(n) context(hpack(takebox(n))) end }
+
+implement {
+ name = "scangivendimensions",
+ public = true,
+ protected = true,
+ arguments = {
+ {
+ { "width", "dimension" },
+ { "height", "dimension" },
+ { "depth", "dimension" },
+ },
+ },
+ actions = function(t)
+ texsetdimen("givenwidth", t.width or 0)
+ texsetdimen("givenheight",t.height or 0)
+ texsetdimen("givendepth", t.depth or 0)
+ end,
+}
+
+local function stripglue(list)
+ local done = false
+ local first = list
+ while first do
+ local id = getid(first)
+ if id == glue_code or id == penalty_code then
+ first = getnext(first)
+ else
+ break
+ end
+ end
+ if first and first ~= list then
+ -- we have discardables
+ setsplit(getprev(first),first)
+ flush_list(list)
+ list = first
+ done = true
+ end
+ if list then
+ local tail = findtail(list)
+ local last = tail
+ while last do
+ local id = getid(last)
+ if id == glue_code or id == penalty_code then
+ last = getprev(last)
+ else
+ break
+ end
+ end
+ if last ~= tail then
+ -- we have discardables
+ flush_list(getnext(last))
+ setnext(last)
+ done = true
+ end
+ end
+ return list, done
+end
+
+local function limitate(t) -- don't pack the result !
+ local text = t.text
+ if text then
+ text = tonut(text)
+ else
+ return
+ end
+ local sentinel = t.sentinel
+ if sentinel then
+ sentinel = tonut(sentinel)
+ local s = getlist(sentinel)
+ setlist(sentinel)
+ free(sentinel)
+ sentinel = s
+ else
+ return tonode(text)
+ end
+ local width = getwidth(text)
+ local list = getlist(text)
+ local done = false
+ if t.strip then
+ list, done = stripglue(list)
+ if not list then
+ setlist(text)
+ setwidth(text,0)
+ return text
+ elseif done then
+ width = getdimensions(list)
+ setlist(text,list)
+ end
+ end
+ local left = t.left or 0
+ local right = t.right or 0
+ local total = left + right
+ if total < width then
+ local last = nil
+ local first = nil
+ local maxleft = left
+ local maxright = right
+ local swidth = getwidth(sentinel)
+ if maxright > 0 then
+ maxleft = maxleft - swidth/2
+ maxright = maxright - swidth/2
+ else
+ maxleft = maxleft - swidth
+ end
+ for n in traverse_id(glue_code,list) do
+ local width = getdimensions(list,n)
+ if width > maxleft then
+ if not last then
+ last = n
+ end
+ break
+ else
+ last = n
+ end
+ end
+ if last and maxright > 0 then
+ for n in traverse_id(glue_code,last) do
+ local width = getdimensions(n)
+ if width < maxright then
+ first = n
+ break
+ else
+ first = n
+ end
+ end
+ end
+ if last then
+ local rest = getnext(last)
+ if rest then
+ local tail = findtail(sentinel)
+ if first and getid(first) == glue_code and getid(tail) == glue_code then
+ setwidth(first,0)
+ end
+ if last and getid(last) == glue_code and getid(sentinel) == glue_code then
+ setwidth(last,0)
+ end
+ if first and first ~= last then
+ local prev = getprev(first)
+ if prev then
+ setnext(prev)
+ end
+ setlink(tail,first)
+ end
+ setlink(last,sentinel)
+ setprev(rest)
+ flush_list(rest)
+ end
+ end
+ end
+ setlist(text)
+ free(text)
+
+ if t.freeze then
+ local l = hpack(list,total,"exactly")
+ for n in traverse_id(glue_code,list) do
+ setglue(n,(effective_glue(n,l)))
+ end
+ setlist(l)
+ flush_node(l)
+ end
+
+ return tonode(list)
+end
+
+implement {
+ name = "limitated",
+ public = true,
+ protected = true,
+ arguments = {
+ {
+ { "left", "dimension" },
+ { "right", "dimension" },
+ { "text", "hbox" },
+ { "sentinel", "hbox" },
+ { "strip", "boolean" },
+ { "freeze", "boolean" },
+ }
+ },
+ actions = function(t)
+ context.dontleavehmode()
+ context(limitate(t))
+ end,
+}
+
+-- only in lmtx:
+
+implement {
+ name = "widthuptohere",
+ public = true,
+ usage = "value",
+ actions = function()
+ local n = texgetnest()
+ local w = 0
+ if n.mode == hmode_code then
+ local h = hpack(getnext(tonut(n.head)))
+ w = getwidth(h)
+ setlist(h)
+ free(h)
+ end
+ return tokens.values.dimension, w
+ end,
+}
+
+implement {
+ name = "doifelseindented",
+ public = true,
+ protected = true,
+ actions = function()
+ local n = texgetnest()
+ local b = false
+ -- can be a helper !
+ if n.mode == hmode_code then
+ n = tonut(n.head)
+ while n do
+ n = getnext(n)
+ if n then
+ local id = getid(n)
+ if id == hlist_code then
+ if getsubtype(n) == indent_code then
+ b = getwidth(n) > 0
+ break
+ end
+ elseif id ~= par_code then
+ break
+ end
+ end
+ end
+ end
+ commands.doifelse(b)
+ end,
+}
+
+implement {
+ name = "noflinesinbox",
+ public = true,
+ protected = false,
+ arguments = "integer",
+ actions = function(n)
+ local c = 0
+ local b = getbox(n)
+ if b then
+ b = getlist(b)
+ if b then
+ for n, id in traverse(b) do
+ if id == hlist_code or id == vlist_code then
+ c = c + 1
+ end
+ end
+ end
+ end
+ context(c)
+ end,
+}
+
+do
+
+ local takebox = tex.takebox
+
+ implement {
+ name = "thebox",
+ public = true,
+ arguments = "integer",
+ actions = function(n)
+ context(takebox(n))
+ end
+ }
+
+end
+
+-- only in lmtx
+
+implement {
+ name = "reversevboxcontent",
+ protected = true,
+ public = true,
+ arguments = "integer",
+ actions = function(n)
+ local b = getbox(n)
+ local l = b and getid(b) == vlist_code and getlist(b)
+ if l and getnext(l) then
+ setlist(b,reverse(l)) -- no re-vpack here!
+ end
+ end
+}
+
+-- only in lmtx
+
+do
+
+ local scaninteger = tokens.scanners.integer
+ local scanbox = tokens.scanners.box
+ local scandimen = tokens.scanners.dimen
+
+ local setsubtype = nuts.setsubtype
+ local removenode = nuts.remove
+ local getnormalizedline = nuts.getnormalizedline -- we can optimize this
+ local getdimensions = nuts.dimensions
+ local getrangedimensions = nuts.rangedimensions
+
+ local setprop = nuts.setprop
+ local getprop = nuts.getprop
+
+ local listcodes = nodes.listcodes
+ local line_code = listcodes.line
+ local equation_code = listcodes.equation
+ local unknown_code = listcodes.unknown
+
+ local values = tokens.values
+ local dimension_value = values.dimension
+ local cardinal_value = values.cardinal
+ local direct_value = values.direct
+
+ -- todo: make helper that formats
+
+ local reporterror = logs.texerrormessage
+
+ -- The first variant did a linear lookup but for large boxes and lots of
+ -- analysis that is not nice. Okay, in practice performance is quite ok
+ -- (milliseconds for thousands of access) but still ... the next is nicer
+ -- and it's part of the experimental fun stuff anyway.
+
+ local function countlines(box)
+ local prop = getprop(box,"boxlines")
+ if not prop then
+ local line = 0
+ local list = getlist(box)
+ prop = { }
+ if list then
+ for n, subtype in nexthlist, list do
+ if subtype == line_code then -- or subtype == equation_code then
+ line = line + 1
+ prop[line] = n
+ end
+ end
+ end
+ setprop(box,"boxlines",prop)
+ end
+ return prop
+ end
+
+ local function boxlinecount(what)
+ local n = scaninteger()
+ local box = getbox(n)
+ if what == "value" then
+ return cardinal_value, box and #countlines(box) or 0
+ end
+ end
+
+ local function findline()
+ local n = scaninteger()
+ local line = scaninteger()
+ local box = getbox(n)
+ if box then
+ local prop = getprop(box,"boxlines")
+ if not prop then
+ prop = countlines(box)
+ end
+ local found = prop[line]
+ if found then
+ local props = getprop(found,"lineproperties")
+ if not props then
+ props = getnormalizedline(found)
+ props.width, props.height, props.depth = getwhd(found)
+ setprop(found,"lineproperties",props)
+ end
+ return props, line, found, box
+ end
+ end
+ reporterror("no line %i in box %i",line,n)
+ end
+
+ local function findrange()
+ local n = scaninteger()
+ local first = scaninteger()
+ local last = scaninteger()
+ local box = getbox(n)
+ if box then
+ local prop = getprop(box,"boxlines")
+ if not prop then
+ prop = countlines(box)
+ end
+ if first > 0 and last <= #prop then
+ for i = first, last do
+ local found = prop[i]
+ local props = getprop(found,"lineproperties")
+ if not props then
+ props = getnormalizedline(found)
+ props.width, props.height, props.depth = getwhd(found)
+ setprop(found,"lineproperties",props)
+ end
+ end
+ return prop, first, last, box
+ end
+ end
+ reporterror("no lines %i - %i in box %i",first,last,n)
+ end
+
+ local function getline(props,line,found,box,value)
+ local p, n = getboth(found)
+ local temp = new_hlist()
+ setsubtype(temp,getsubtype(found))
+ setwhd(temp,getwhd(found))
+ if found == getlist(box) then
+ setlink(temp,n)
+ setlist(box,temp)
+ else
+ setlink(p,temp,n)
+ end
+ getprop(box,"boxlines")[line] = temp
+ setboth(found)
+ setsubtype(found, unknown_code)
+ if value then
+ return direct_value, found
+ else
+ context(tonode(found))
+ end
+ end
+
+ local function copyline(props,line,found,box,value)
+ found = copy_node(found)
+ setsubtype(found, unknown_code)
+ if value then
+ return direct_value, found
+ else
+ context(tonode(found))
+ end
+ end
+
+ local function setline(props,line,found,box)
+ local p, n = getboth(found)
+ local temp = scanbox()
+ if temp then
+ temp = tonut(temp)
+ if found == getlist(box) then
+ setlink(temp,n)
+ setlist(box,temp)
+ else
+ setlink(p,temp,n)
+ end
+ flush_node(found)
+ getprop(box,"boxlines")[line] = temp
+ end
+ end
+
+ local function naturaldimensions(p,l,found)
+ if not p.naturalwidth then
+ p.naturalwidth, p.naturalheight, p.naturaldepth = getdimensions(getlist(found))
+ end
+ return p
+ end
+
+ local function rangedimensions(p,f,l,box)
+ local w, h, d = getrangedimensions(box,p[f],getnext(p[l]),true)
+ return { width = w, height = h, depth = d }
+ end
+
+ local getters_one = {
+
+ ["wd"] = function(p,l,found) return dimension_value, p.width end,
+ ["ht"] = function(p,l,found) return dimension_value, p.height end,
+ ["dp"] = function(p,l,found) return dimension_value, p.depth end,
+ ["ls"] = function(p,l,found) return dimension_value, p.leftskip end,
+ ["rs"] = function(p,l,found) return dimension_value, p.rightskip end,
+ ["lh"] = function(p,l,found) return dimension_value, p.lefthangskip end,
+ ["rh"] = function(p,l,found) return dimension_value, p.righthangskip end,
+ ["lp"] = function(p,l,found) return dimension_value, p.parfillleftskip end,
+ ["rp"] = function(p,l,found) return dimension_value, p.parfillrightskip end,
+ ["in"] = function(p,l,found) return dimension_value, p.indent end,
+
+ ["nw"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturalwidth end,
+ ["nh"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturalheigth end,
+ ["nd"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturaldepth end,
+
+ ["get"] = function(p,l,found,box) return getline(p,l,found,box,true) end,
+ }
+
+ local getters_two = {
+ ["wd"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).width end,
+ ["ht"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).height end,
+ ["dp"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).depth end,
+ }
+
+ local setters_one = {
+ ["wd"] = function(p,l,found) return setwidth (found,scandimen(false,false,true)) end,
+ ["ht"] = function(p,l,found) return setheight(found,scandimen(false,false,true)) end,
+ ["dp"] = function(p,l,found) return setdepth (found,scandimen(false,false,true)) end,
+ ["set"] = setline,
+ ["get"] = getline,
+ ["copy"] = copyline,
+ }
+
+ local function boxline(name,what)
+ local props, line, found, box = findline()
+ if not found then
+ --
+ elseif what == "value" then
+ local getter = getters_one[name]
+ if getter then
+ return getter(props,line,found,box)
+ end
+ else
+ local setter = setters_one[name]
+ if setter then
+ return setter(props,line,found,box)
+ end
+ end
+ end
+
+ --
+
+ local function boxrange(name,what)
+ local prop, first, last, box = findrange()
+ if not prop then
+ --
+ elseif what == "value" then
+ local getter = getters_two[name]
+ if getter then
+ return getter(prop,first,last,box)
+ end
+ else
+ local setter = setters_two[name]
+ if setter then
+ return setter(prop,first,last,box)
+ end
+ end
+ end
+
+ local function define_one(name,action)
+ implement {
+ name = name,
+ public = true,
+ usage = "value",
+ actions = function(what) return boxline(action,what) end,
+ }
+ end
+
+ local function define_two(name,action)
+ implement {
+ name = name,
+ public = true,
+ usage = "value",
+ actions = function(what) return boxrange(action,what) end,
+ }
+ end
+
+ implement {
+ name = "boxlines",
+ public = true,
+ usage = "value",
+ actions = boxlinecount,
+ }
+
+ define_one("boxline", "get")
+ define_one("setboxline", "set")
+ define_one("copyboxline", "copy")
+ define_one("boxlineht", "ht")
+ define_one("boxlinedp", "dp")
+ define_one("boxlinewd", "wd")
+ define_one("boxlinels", "ls")
+ define_one("boxliners", "rs")
+ define_one("boxlinelh", "lh")
+ define_one("boxlinerh", "rh")
+ define_one("boxlinelp", "lp")
+ define_one("boxlinerp", "rp")
+ define_one("boxlinein", "in")
+ define_one("boxlinenw", "nw")
+ define_one("boxlinenh", "nh")
+ define_one("boxlinend", "nd")
+
+ define_two("boxrangewd", "wd")
+ define_two("boxrangeht", "ht")
+ define_two("boxrangedp", "dp")
+
+end
+
+do
+
+ local getbox = tex.getbox
+ local setfield = nodes.setfield
+ local getfield = nodes.getfield
+ local flush = nodes.flush
+ local copynode = nodes.copy
+
+ local function get(n,field,copy)
+ local b = getbox(n)
+ if b then
+ local p = getfield(b,field)
+ if copy then
+ p = copynode(p)
+ else
+ setfield(b,field)
+ end
+ context(p) -- todo: proper return
+ end
+ end
+
+ local function set(n,l,field)
+ local b = getbox(n)
+ if b then
+ setfield(b,field,l)
+ else
+ flush(l)
+ end
+ end
+
+ implement {
+ name = "prelistbox",
+ public = true,
+ usage = "value",
+ arguments = { "integer", '"pre"' },
+ actions = get,
+ }
+
+ implement {
+ name = "postlistbox",
+ public = true,
+ usage = "value",
+ arguments = { "integer", '"post"' },
+ actions = get,
+ }
+
+ implement {
+ name = "prelistcopy",
+ public = true,
+ usage = "value",
+ arguments = { "integer", '"pre"', true },
+ actions = get,
+ }
+
+ implement {
+ name = "postlistcopy",
+ public = true,
+ usage = "value",
+ arguments = { "integer", '"post"', true },
+ actions = get,
+ }
+
+ implement {
+ name = "setprelistbox",
+ public = true,
+ usage = "value",
+ arguments = { "integer", "box", '"pre"' },
+ actions = set,
+ }
+
+ implement {
+ name = "setpostlistbox",
+ public = true,
+ usage = "value",
+ arguments = { "integer", "box", '"post"' },
+ actions = set,
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/supp-box.mkxl b/Master/texmf-dist/tex/context/base/mkxl/supp-box.mkxl
new file mode 100644
index 00000000000..e52ea90b81c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/supp-box.mkxl
@@ -0,0 +1,2926 @@
+%D \module
+%D [ file=supp-box,
+%D version=1995.10.10,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Boxes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Support Macros / Boxes}
+
+\unprotect
+
+\registerctxluafile{supp-box}{autosuffix,optimize}
+
+%D And some dimensions:
+
+\newdimen\givenwidth
+\newdimen\givenheight
+\newdimen\givendepth
+
+% \fixupboxesmode\plusone % gone: is now the default
+
+%D This module implements some box manipulation macros. Some are quite simple, some
+%D are more advanced and when understood well, all can be of use.
+%D
+%D \macros
+%D {strutdp,strutht,strutwd}
+%D
+%D The next shortcuts save memory and keying. The width is normally zero points (if
+%D not, you're in trouble). These shortcuts can be used like a dimension, opposite
+%D to the core macros \type {\strutdepth} and alike, which are values.
+
+ \def\strutdp {\dp\strutbox} % overloaded in spac-ver
+ \def\strutht {\ht\strutbox} % overloaded in spac-ver
+\permanent\protected\def\strutwd {\wd\strutbox}
+\permanent\protected\def\struthtdp{\dimexpr\strutht+\strutdp\relax}
+\permanent\protected\def\strutgap {\dimexpr\strutht-\strutdp\relax}
+
+%D \macros
+%D {voidbox,nextbox}
+%D
+%D Let's start with an easy one. The next macro hides the ugly \type {@} in \type
+%D {\voidb@x}.
+
+\ifdefined\voidbox \else \newbox\voidbox \fi
+\ifdefined\nextbox \else \newbox\nextbox \fi
+
+%D \macros
+%D {nextdepth}
+%D
+%D Let's start with a rather simple declaration. Sometimes we need to save the \TEX\
+%D \DIMENSION\ \type{\prevdepth} and append it later on. The name \type {\nextdepth}
+%D suits this purpose well.
+
+\newdimen\nextdepth
+
+%D \macros
+%D {smashbox, smashboxed, smashedbox}
+%D
+%D Smashing is introduced in \PLAIN\ \TEX, and stands for reducing the dimensions of
+%D a box to zero. The most resolute one is presented first.
+
+% \permanent\protected\def\smashbox#1%
+% {\wd#1\zeropoint
+% \ht#1\zeropoint
+% \dp#1\zeropoint}
+%
+% \permanent\protected\def\smashboxed#1%
+% {\wd#1\zeropoint
+% \ht#1\zeropoint
+% \dp#1\zeropoint
+% \box#1\relax}
+
+%D \macros
+%D {hsmashbox,vsmashbox}
+%D
+%D Smashing can be used for overlaying boxes. Depending on the mode, horizontal or
+%D vertical, one can use:
+
+% \permanent\protected\def\hsmashbox#1%
+% {\wd#1\zeropoint}
+%
+% \permanent\protected\def\vsmashbox#1%
+% {\ht#1\zeropoint
+% \dp#1\zeropoint}
+
+%D The next implementation is less sensitive for spurious spaces.
+
+\newcount\c_boxes_register
+
+\permanent\protected\def\smashbox
+ {\afterassignment\syst_boxes_smash_boxes_register\c_boxes_register}
+
+\def\syst_boxes_smash_boxes_register
+ {\wd\c_boxes_register\zeropoint
+ \ht\c_boxes_register\zeropoint
+ \dp\c_boxes_register\zeropoint}
+
+\permanent\protected\def\hsmashbox
+ {\afterassignment\syst_boxes_hsmashed_boxes_register\c_boxes_register}
+
+\def\syst_boxes_hsmashed_boxes_register
+ {\wd\c_boxes_register\zeropoint}
+
+\permanent\protected\def\vsmashbox
+ {\afterassignment\syst_boxes_vsmashed_boxes_register\c_boxes_register}
+
+\def\syst_boxes_vsmashed_boxes_register
+ {\ht\c_boxes_register\zeropoint
+ \dp\c_boxes_register\zeropoint}
+
+\permanent\protected\def\smashedbox
+ {\afterassignment\syst_boxes_smashed_boxes_register\c_boxes_register}
+
+\protected\def\syst_boxes_smashed_boxes_register
+ {\wd\c_boxes_register\zeropoint
+ \ht\c_boxes_register\zeropoint
+ \dp\c_boxes_register\zeropoint
+ \box\c_boxes_register}
+
+\aliased\let\smashboxed\smashedbox
+
+%D \macros
+%D {hsmash,vsmash,
+%D hsmashed,vsmashed}
+%D
+%D While the previous macros expected a \BOX, the next act on a content. They are
+%D some subtle differences betreen the smash and smashed alternatives. The later
+%D ones reduce all dimensions to zero.
+
+\permanent\protected\def\hsmash {\bgroup\dowithnextboxcs\syst_boxes_hsmashed_nextbox\hbox}
+\permanent\protected\def\vsmash {\bgroup\dowithnextboxcs\syst_boxes_vsmashed_nextbox\vbox}
+\permanent\protected\def\hsmashed{\bgroup\dowithnextboxcs\syst_boxes_smashed_nextbox \hbox}
+\permanent\protected\def\vsmashed{\bgroup\dowithnextboxcs\syst_boxes_smashed_nextbox \vbox}
+
+\protected\def\syst_boxes_hsmashed_nextbox
+ {\wd\nextbox\zeropoint
+ \box\nextbox
+ \egroup}
+
+\protected\def\syst_boxes_vsmashed_nextbox
+ {\ht\nextbox\zeropoint
+ \dp\nextbox\zeropoint
+ \box\nextbox
+ \egroup}
+
+\protected\def\syst_boxes_smashed_nextbox
+ {\ht\nextbox\zeropoint
+ \dp\nextbox\zeropoint
+ \wd\nextbox\zeropoint
+ \box\nextbox
+ \egroup}
+
+%D \macros
+%D {smashedhbox,smashedvbox}
+%D
+%D Also handy (all dimensions zeroed):
+%D
+%D \starttyping
+%D \smashedhbox to ... {...}
+%D \smashedvbox to ... {...}
+%D \stoptyping
+
+\permanent\protected\def\smashedhbox{\hpack\bgroup\dowithnextboxcs\syst_boxes_smashed_nextbox\hbox}
+\permanent\protected\def\smashedvbox{\vpack\bgroup\dowithnextboxcs\syst_boxes_smashed_nextbox\vbox}
+
+%D First we define a helper. We use a \LUATEX\ feature in order to avoid
+%D mathpalettes.
+
+\newcount\c_boxes_math_style
+
+\protected\def\syst_boxes_math_set_nextbox#1%
+ {\c_boxes_math_style\normalmathstyle
+ \setbox\nextbox\hbox{\normalstartimath\mathsurround\zeropoint\triggermathstyle\c_boxes_math_style{#1}\normalstopimath}}
+
+%D \macros
+%D {smash}
+%D
+%D This smash alternative takes an optional arg [whdtb] as well as is potentially
+%D catcode safer. It is needed by the math module (although the \type {\leavevmode}
+%D is not added here).
+
+\permanent\protected\def\smash
+ {\begingroup
+ \futureexpandis[\syst_boxes_smash_yes\syst_boxes_smash_nop}
+
+\def\syst_boxes_smash_nop
+ {\edef\m_boxes_smash_options{hd}%
+ \futurelet\nexttoken\syst_boxes_smash_indeed}
+
+\def\syst_boxes_smash_yes[#1]%
+ {\edef\m_boxes_smash_options{#1}%
+ \futurelet\nexttoken\syst_boxes_smash_indeed}
+
+\def\syst_boxes_smash_indeed
+ {\ifmmode
+ \expandafter\syst_boxes_smash_math
+ \orelse\ifx\nexttoken\bgroup
+ \expandafter\syst_boxes_smash_hbox
+ \else
+ \expandafter\syst_boxes_smash_text
+ \fi}
+
+\def\syst_boxes_smash_math#1%
+ {\syst_boxes_math_set_nextbox{#1}%
+ \syst_boxes_smash_process}
+
+\def\syst_boxes_smash_hbox
+ {\dowithnextboxcs\syst_boxes_smash_process\hbox}
+
+\def\syst_boxes_smash_text#1%
+ {\setbox\nextbox\hbox{#1}%
+ \syst_boxes_smash_process}
+
+\def\syst_boxes_smash_process
+ {\expandafter\syst_boxes_smash_process_option\m_boxes_smash_options\relax
+ \box\nextbox
+ \endgroup}
+
+\installcorenamespace {smashoptions}
+
+\setvalue{\??smashoptions w}{\wd\nextbox\zeropoint}
+\setvalue{\??smashoptions h}{\ht\nextbox\zeropoint}
+\setvalue{\??smashoptions d}{\dp\nextbox\zeropoint}
+\setvalue{\??smashoptions t}{\ht\nextbox\zeropoint}
+\setvalue{\??smashoptions b}{\dp\nextbox\zeropoint}
+
+\def\syst_boxes_smash_process_option#1%
+ {\ifx#1\relax\else
+ \begincsname\??smashoptions#1\endcsname
+ \expandafter\syst_boxes_smash_process_option
+ \fi}
+
+\def\syst_boxes_lower_nextbox_dp
+ {\setbox\nextbox\hpack{\lower\dp\nextbox\box\nextbox}}
+
+%D \starttabulate[|l|l|]
+%D \NC w \NC \ruledhbox{\smash [w]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC h \NC \ruledhbox{\smash [h]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC d \NC \ruledhbox{\smash [d]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC tb \NC \ruledhbox{\smash [tb]{This is some great smashing, isn't it?}} \NC \NR
+%D \NC whd \NC \ruledhbox{\smash[whd]{This is some great smashing, isn't it?}} \NC \NR
+%D \stoptabulate
+
+%D \macros
+%D {phantom, hphantom, vphantom, mathstrut}
+%D
+%D The next implementation of \type {\phantom} cum suis does not grab an argument in
+%D the non||math case, which is better.
+%D
+%D Due to a complicated call to \type {\mathpallete} and thereby \type
+%D {\mathchoice}, the next macro looks ugly. We also take care of non||braced
+%D arguments.
+
+\permanent\protected\def\phantom {\begingroup\futurelet\nexttoken\syst_boxes_phantom_indeed }
+\permanent\protected\def\vphantom{\begingroup\futurelet\nexttoken\syst_boxes_phantom_indeed_v}
+\permanent\protected\def\hphantom{\begingroup\futurelet\nexttoken\syst_boxes_phantom_indeed_h}
+
+\def\syst_boxes_phantom_math #1{\syst_boxes_math_set_nextbox{#1}\syst_boxes_phantom_make }
+\def\syst_boxes_phantom_math_v#1{\syst_boxes_math_set_nextbox{#1}\syst_boxes_phantom_make_v}
+\def\syst_boxes_phantom_math_h#1{\syst_boxes_math_set_nextbox{#1}\syst_boxes_phantom_make_h}
+
+\def\syst_boxes_phantom_hbox {\dowithnextboxcs\syst_boxes_phantom_make \hbox} % always hbox
+\def\syst_boxes_phantom_hbox_v{\dowithnextboxcs\syst_boxes_phantom_make_v\hbox} % always hbox
+\def\syst_boxes_phantom_hbox_h{\dowithnextboxcs\syst_boxes_phantom_make_h\hbox} % always hbox
+
+\def\syst_boxes_phantom_text #1{\setbox\nextbox\hbox{#1}\syst_boxes_phantom_make } % always hbox
+\def\syst_boxes_phantom_text_v#1{\setbox\nextbox\hbox{#1}\syst_boxes_phantom_make_v} % always hbox
+\def\syst_boxes_phantom_text_h#1{\setbox\nextbox\hbox{#1}\syst_boxes_phantom_make_h} % always hbox
+
+\def\syst_boxes_phantom_indeed
+ {\ifmmode
+ \expandafter\syst_boxes_phantom_math
+ \orelse\ifx\nexttoken\bgroup
+ \expandafter\syst_boxes_phantom_hbox
+ \else
+ \expandafter\syst_boxes_phantom_text
+ \fi}
+
+\def\syst_boxes_phantom_indeed_v
+ {\ifmmode
+ \expandafter\syst_boxes_phantom_math_v
+ \orelse\ifx\nexttoken\bgroup
+ \expandafter\syst_boxes_phantom_hbox_v
+ \else
+ \expandafter\syst_boxes_phantom_text_v
+ \fi}
+
+\def\syst_boxes_phantom_indeed_h
+ {\ifmmode
+ \expandafter\syst_boxes_phantom_math_h
+ \orelse\ifx\nexttoken\bgroup
+ \expandafter\syst_boxes_phantom_hbox_h
+ \else
+ \expandafter\syst_boxes_phantom_text_h
+ \fi}
+
+\def\syst_boxes_phantom_make
+ {\setbox\scratchbox\emptyhbox
+ \ht\scratchbox\ht\nextbox
+ \dp\scratchbox\dp\nextbox
+ \wd\scratchbox\wd\nextbox
+ \box\scratchbox
+ \endgroup}
+
+\def\syst_boxes_phantom_make_v
+ {\setbox\scratchbox\emptyhbox
+ \ht\scratchbox\ht\nextbox
+ \dp\scratchbox\dp\nextbox
+ \box\scratchbox
+ \endgroup}
+
+\def\syst_boxes_phantom_make_h
+ {\setbox\scratchbox\emptyhbox
+ \wd\scratchbox\wd\nextbox
+ \box\scratchbox
+ \endgroup}
+
+%D We also define plain's \type {\mathstrut}.
+
+\permanent\protected\def\mathstrut{\vphantom(} % can be made faster by inlining
+
+%D \macros
+%D {getboxheight}
+%D
+%D Although often needed, \TEX\ does not support arithmics like:
+%D
+%D \starttyping
+%D \dimen0 = \ht0 + \dp0
+%D \stoptyping
+%D
+%D so we implemented:
+%D
+%D \starttyping
+%D \getboxheight ... \of \box...
+%D \stoptyping
+%D
+%D For instance,
+%D
+%D \starttyping
+%D \getboxheight \dimen0 \of \box0
+%D \getboxheight \someheight \of \box \tempbox
+%D \stoptyping
+%D
+%D The implementation is rather stupid:
+%D
+%D \starttyping
+%D \def\getboxheight#1\of#2\box#3%
+%D {#1\ht#3\advance#1\dp#3\relax}
+%D \stoptyping
+%D
+%D The next alternative is slightly more clever, since it accepts \type {{12}} as
+%D well as \type {12} as box number.
+
+\permanent\protected\def\getboxheight#1\of#2\box#3%
+ {\def\next{#1\htdp\c_boxes_register}%
+ \afterassignment\next\c_boxes_register=#3}
+
+%D For a long time the following three macros were part of the grid snapping core
+%D module, but it makes more sense to have them here so that users can see them.
+%D
+%D \macros
+%D {getnoflines, getroundednoflines, getrawnoflines}
+%D
+%D Het commando \type {\getnoflines} converteert een hoogte (dimensie) in een aantal
+%D regels en kent dit toe aan \type {\noflines}.
+%D
+%D \starttyping
+%D \getnoflines{dimensie}
+%D \stoptyping
+%D
+%D Er wordt gedeeld door \type {\openlineheight} en een hoogte van~0pt komt overeen
+%D met 0~regels. The raw alternative does not round. See the \MKII\ and \MKIV\ files
+%D for historic variants.
+
+\ifdefined\roundingeps \else \newdimen\roundingeps \roundingeps=10sp \fi
+
+\newcount\noflines
+\newdimen\noflinesheight
+
+\permanent\protected\def\getnoflines#1%
+ {\noflinesheight#1\relax
+ \ifzeropt\noflinesheight
+ \noflines\zerocount
+ \orelse\ifdim\noflinesheight>\zeropoint
+ \advance\noflinesheight-\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \advance\noflines\plusone
+ \else
+ \advance\noflinesheight\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \advance\noflines\minusone
+ \fi}
+
+\permanent\protected\def\getroundednoflines#1%
+ {\noflinesheight#1\relax
+ \ifzeropt\noflinesheight
+ \noflines\zerocount
+ \orelse\ifdim\noflinesheight>\zeropoint
+ \advance\noflinesheight\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \else
+ \advance\noflinesheight-\roundingeps
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \fi}
+
+\permanent\protected\def\getrawnoflines#1%
+ {\noflinesheight#1\relax
+ \ifzeropt\noflinesheight
+ \noflines\zerocount
+ \orelse\ifdim\noflinesheight>\zeropoint
+ \advance\noflinesheight\roundingeps
+ \advance\noflinesheight.5\openlineheight
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \else
+ \advance\noflinesheight-\roundingeps
+ \advance\noflinesheight-.5\openlineheight
+ \divide\noflinesheight\openlineheight
+ \noflines\noflinesheight
+ \fi}
+
+%D Let's proof that it works:
+%D
+%D \startbuffer
+%D \scratchdimen\dimexpr(3pt) \getnoflines\scratchdimen 1=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight) \getnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.1\lineheight) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.5\lineheight) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.9\lineheight) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3pt) \getnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3sp) \getnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight-3sp) \getnoflines\scratchdimen 10=\the\noflines \endgraf
+%D
+%D \scratchdimen\dimexpr(3pt) \getrawnoflines\scratchdimen 0=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.1\lineheight) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.5\lineheight) \getrawnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10.9\lineheight) \getrawnoflines\scratchdimen 11=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3pt) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight+3sp) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \scratchdimen\dimexpr(10\lineheight-3sp) \getrawnoflines\scratchdimen 10=\the\noflines \endgraf
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+%D \macros
+%D {determinenoflines}
+%D
+%D The next macro determines the number of lines and returns it it \type
+%D {\noflines}. The macro works reasonable well as long as the content can be
+%D unboxed.
+%D
+%D \starttyping
+%D \determinenoflines{test\\test}
+%D \determinenoflines{\bfd test\\test}
+%D \determinenoflines{\definedfont[Sans at 40pt]test\\test}
+%D \stoptyping
+
+% \def\syst_boxes_determine_noflines % can be mkiv'd
+% {\beginofshapebox
+% \unvbox\nextbox
+% \endofshapebox
+% \globalscratchcounter\zerocount
+% \reshapebox{\global\advance\globalscratchcounter\plusone}%
+% \expandafter\egroup\expandafter\noflines\the\globalscratchcounter\relax}
+
+\def\syst_boxes_determine_noflines
+ {\expandafter\egroup\expandafter\noflines\noflinesinbox\nextbox\relax}
+
+\permanent\protected\def\determinenoflines
+ {\bgroup
+ \forgetall
+ \enforced\let\crlf\endgraf
+ \enforced\let\\\endgraf
+ \dowithnextboxcs\syst_boxes_determine_noflines\vbox}
+
+%D \macros
+%D {doiftextelse, doiftext}
+%D
+%D When \type {\doifelse} cum suis hopelessly fail, for instance because we pass
+%D data, we can fall back on the next macro:
+%D
+%D \starttyping
+%D \doiftextelse {data} {then branch} {else branch}
+%D \doiftext {data} {then branch}
+%D \stoptyping
+
+\permanent\protected\def\doifelsetext#1%
+ {\begingroup
+ \setbox\scratchbox\hbox % no \hpack because we can have fallbacks
+ {\settrialtypesetting
+ \ignorespaces#1\removeunwantedspaces}%
+ \ifzeropt\wd\scratchbox
+ \endgroup\expandafter\secondoftwoarguments
+ \else
+ \endgroup\expandafter\firstoftwoarguments
+ \fi}
+
+\permanent\protected\def\doiftext#1%
+ {\begingroup
+ \setbox\scratchbox\hbox % no \hpack because we can have fallbacks
+ {\settrialtypesetting
+ \ignorespaces#1\removeunwantedspaces}%
+ \ifzeropt\wd\scratchbox
+ \endgroup\expandafter\gobbleoneargument
+ \else
+ \endgroup\expandafter\firstofoneargument
+ \fi}
+
+% more efficient but maybe fragile:
+%
+% \nospacing#1}%
+
+\aliased\let\doiftextelse\doifelsetext
+
+%D \macros
+%D {dowithnextbox,nextbox}
+%D
+%D Sometimes we want a macro to grab a box and do something on the content. One
+%D could pass an argument to a box, but this can violate the specific \CATCODES\ of
+%D its content and leads to unexpected results. The next macro treats the following
+%D braced text as the content of a box and manipulates it afterwards in a predefined
+%D way.
+%D
+%D The first argument specifies what to do with the content. This content is
+%D available in \type {\nextbox}. The second argument is one of \type {\hbox}, \type
+%D {\vbox} or \type {\vtop}. The third argument must be grouped with \type {\bgroup}
+%D and \type {\egroup}, \type {{...}} or can be a \type {\box} specification.
+%D
+%D In \CONTEXT\ this macro is used for picking up a box and treating it according to
+%D earlier specifications. We use for instance something like:
+%D
+%D \starttyping
+%D \def\getfloat%
+%D {\def\handlefloat{...\box\nextbox...}
+%D \dowithnextboxcs\handlefloat\vbox}
+%D \stoptyping
+%D
+%D instead of:
+%D
+%D \starttyping
+%D \def\getfloat#1%
+%D {...#1...}
+%D \stoptyping
+%D
+%D In this implementation the \type {\aftergroup} construction is needed because
+%D \type {\afterassignment} is executed inside the box.
+
+\let\syst_boxes_with_next_box\relax
+
+\permanent\protected\def\dowithnextbox#1%
+ {\def\syst_boxes_with_next_box{#1}%
+ \afterassignment\syst_boxes_with_next_box_indeed
+ \setbox\nextbox}
+
+\def\syst_boxes_with_next_box_indeed
+ {\aftergroup\syst_boxes_with_next_box}
+
+\permanent\protected\def\dowithnextboxcs#1%
+ {\let\syst_boxes_with_next_box#1%
+ \afterassignment\syst_boxes_with_next_box_indeed
+ \setbox\nextbox}
+
+%D So in fact we get:
+%D
+%D \starttyping
+%D \setbox\nextbox { \aftergroup\syst_boxes_with_next_box ... }
+%D \stoptyping
+%D
+%D or
+%D
+%D \starttyping
+%D \setbox\nextbox { ... } \syst_boxes_with_next_box
+%D \stoptyping
+%D
+%D A slower but more versatile implementation is:
+%D
+%D \starttyping
+%D \protected\def\dowithnextbox#1#2%
+%D {\def\syst_boxes_with_next_box{#1}%
+%D \ifx#2\hbox
+%D \afterassignment\syst_boxes_with_next_box_indeed
+%D \orelse\ifx#2\vbox
+%D \afterassignment\syst_boxes_with_next_box_indeed
+%D \orelse\ifx#2\vtop
+%D \afterassignment\syst_boxes_with_next_box_indeed
+%D \orelse\ifx#2\normalvcenter
+%D \afterassignment\syst_boxes_with_next_box_indeed
+%D \else
+%D \afterassignment\syst_boxes_with_next_box
+%D \fi
+%D \setbox\nextbox#2}
+%D \stoptyping
+%D
+%D This alternative also accepts \type {\box0} and alike, but we don't really need
+%D this functionality now.
+
+%D \macros
+%D {nextboxht,nextboxwd,nextboxdp,flushnextbox}
+%D
+%D The next couple of shortcuts saves us memory as well as \type {{}}'s in passing
+%D parameters.
+
+\permanent\def\nextboxht {\ht\nextbox}
+\permanent\def\nextboxwd {\wd\nextbox}
+\permanent\def\nextboxdp {\dp\nextbox}
+\permanent\def\nextboxhtdp{\htdp\nextbox}
+
+\permanent\protected\def\flushnextbox{\box\nextbox}
+
+%D \macros
+%D {dowithnextboxcontent}
+%D
+%D But, occasionally we do need to pass some local settings without wanting to use
+%D additional grouping. Therefore we provide:
+%D
+%D \starttyping
+%D \dowithnextboxcontent{inside}{after}{box content}
+%D \stoptyping
+%D
+%D {\em todo: Search source for potential usage!}
+
+% \protected\def\dowithnextboxcontent#1#2% inside, after
+% {\def\syst_boxes_with_next_box_one{#2}%
+% \def\syst_boxes_with_next_box_two{#1}%
+% \afterassignment\syst_boxes_with_next_box_content_indeed
+% \setbox\nextbox}
+%
+% \protected\def\dowithnextboxcontentcs#1#2% inside, after
+% {\let\syst_boxes_with_next_box_one#2%
+% \let\syst_boxes_with_next_box_two#1%
+% \afterassignment\syst_boxes_with_next_box_content_indeed
+% \setbox\nextbox}
+%
+% \def\syst_boxes_with_next_box_content_indeed
+% {\syst_boxes_with_next_box_two\aftergroup\syst_boxes_with_next_box_one}
+
+\permanent\protected\def\dowithnextboxcontent#1#2% inside, after
+ {\afterassigned{#1\aftergrouped{#2}}%
+ \setbox\nextbox}
+
+\permanent\protected\def\dowithnextboxcontentcs#1#2% inside, after
+ {\afterassigned{#1\aftergroup#2}%
+ \setbox\nextbox}
+
+%D \macros
+%D {llap, rlap, tlap, blap, clap}
+%D
+%D Some well known friends, but we implement them our own way. We want the macros to
+%D work in both math and text mode.
+
+\def\syst_boxes_do_rlap{\hpack to \zeropoint{\box\nextbox\hss}\endgroup}
+\def\syst_boxes_do_llap{\hpack to \zeropoint{\hss\box\nextbox}\endgroup}
+\def\syst_boxes_do_clap{\hpack to \zeropoint{\hss\box\nextbox\hss}\endgroup}
+\def\syst_boxes_do_tlap{\vpack to \zeropoint{\vss\box\nextbox}\endgroup}
+\def\syst_boxes_do_blap{\vpack to \zeropoint{\box\nextbox\vss}\endgroup}
+
+\def\syst_boxes_rlap {\begingroup\dowithnextboxcs\syst_boxes_do_rlap\hbox}
+\def\syst_boxes_llap {\begingroup\dowithnextboxcs\syst_boxes_do_llap\hbox}
+\def\syst_boxes_clap {\begingroup\dowithnextboxcs\syst_boxes_do_clap\hbox}
+\def\syst_boxes_math_clap{\mathpalette\syst_boxes_do_math_clap}
+\def\syst_boxes_math_llap{\mathpalette\syst_boxes_do_math_llap}
+\def\syst_boxes_math_rlap{\mathpalette\syst_boxes_do_math_rlap}
+
+\def\syst_boxes_do_math_clap#1#2{\syst_boxes_clap{\normalstartimath\mathsurround\zeropoint#1#2\normalstopimath}}
+\def\syst_boxes_do_math_llap#1#2{\syst_boxes_llap{\normalstartimath\mathsurround\zeropoint#1#2\normalstopimath}}
+\def\syst_boxes_do_math_rlap#1#2{\syst_boxes_rlap{\normalstartimath\mathsurround\zeropoint#1#2\normalstopimath}}
+
+\permanent\protected\def\rlap{\mathortext\syst_boxes_math_rlap\syst_boxes_rlap}
+\permanent\protected\def\llap{\mathortext\syst_boxes_math_llap\syst_boxes_llap}
+\permanent\protected\def\clap{\mathortext\syst_boxes_math_clap\syst_boxes_clap}
+\permanent\protected\def\tlap{\begingroup\dowithnextboxcs\syst_boxes_do_tlap\vbox}
+\permanent\protected\def\blap{\begingroup\dowithnextboxcs\syst_boxes_do_blap\vbox}
+
+%D in \LMTX\ this mechanism is obsolete! Use boxlines instead!
+
+%D \macros
+%D {beginofshapebox,
+%D reshapebox, doreshapebox,
+%D flushshapebox,
+%D innerflushshapebox,
+%D shapebox,
+%D ifreshapingbox}
+%D
+%D The next utility macro originates from some linenumbering mechanism. Due to
+%D \TEX's advanced way of typesetting paragraphs, it's not easy to do things on a
+%D line||by||line basis. This macro is able to reprocess a given box and can act
+%D upon its vertical boxed components, such as lines. The unwinding sequence in this
+%D macro is inspired by a \NTG\ workshop of David Salomon in June 1992.
+%D
+%D First we have to grab the piece of text we want to act upon. This is done by
+%D means of the duo macros:
+%D
+%D \starttyping
+%D \beginofshapebox
+%D a piece of text
+%D \endofshapebox
+%D \stoptyping
+%D
+%D When all texts is collected, we can call \type {\reshapebox} and do something
+%D with it's vertical components. We can make as much passes as needed. When we're
+%D done, the box can be unloaded with \type {\flushshapebox}. The only condition in
+%D this scheme is that \type {\reshapebox} must somehow unload the \BOX\ \type
+%D {\shapebox}.
+%D
+%D An important aspect is that the content is unrolled bottom||up. The next example
+%D illustrates this maybe unexpected characteristic.
+%D
+%D \startbuffer
+%D \beginofshapebox
+%D \em \input tufte
+%D \endofshapebox
+%D
+%D \newcounter\LineNumber
+%D
+%D \reshapebox
+%D {\doglobal\increment\LineNumber
+%D \hbox{\llap{\LineNumber\hskip2em}\box\shapebox}}
+%D
+%D \flushshapebox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D As we can see, when some kind of numbering is done, we have to add a second pass.
+%D
+%D \startbuffer
+%D \newcounter\LineNumber
+%D \newcounter\NumberOfLines
+%D
+%D \reshapebox
+%D {\doglobal\increment\NumberOfLines
+%D \box\shapebox}
+%D
+%D \reshapebox
+%D {\doglobal\increment\LineNumber
+%D \hbox
+%D {\llap{\LineNumber\ (\NumberOfLines)\hskip2em}%
+%D \box\shapebox}%
+%D \doglobal\decrement\NumberOfLines}
+%D
+%D \flushshapebox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D This example shows that the content of the box is still available after flushing.
+%D Another feature is that only the last reshaping counts. Multiple reshaping can be
+%D done by:
+%D
+%D \startbuffer
+%D \beginofshapebox
+%D \flushshapebox
+%D \endofshapebox
+%D
+%D \reshapebox
+%D {\doglobal\increment\LineNumber
+%D \hbox{\llap{$\star$\hskip1em}\box\shapebox}%
+%D \doglobal\decrement\NumberOfLines}
+%D
+%D \flushshapebox
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D The macros are surprisingly easy to follow and in fact introduce no new concepts.
+%D Nearly all books on \TEX\ show similar solutions for unwinding \BOXES.
+%D
+%D Some macros, like footnote ones, can be sensitive for reshaping, which can result
+%D in an endless loop. We therefore offer:
+%D
+%D \starttyping
+%D \ifreshapingbox
+%D \stoptyping
+%D
+%D Some \CONTEXT\ commands are protected this way. Anyhow, reshaping is aborted
+%D after 100 dead cycles.
+%D
+%D By the way, changing the height and depth of \BOX\ \type {\shapebox} results in
+%D bad spacing. This means that for instance linenumbers etc. should be given zero
+%D height and depth before being lapped into the margin. The previous examples
+%D ignore this side effect, but beware!
+%D
+%D This is ancient stuff and proably not used any more but we keep it around or
+%D maybe turn it into a module some day. It shows the kind of struggling that we
+%D needed before \LUATEX\ came around.
+
+\newif \ifsomeshapeleft
+\newif \ifreshapingbox
+\newif \ifreshapingfailed % may save redundant runs
+
+\newbox \shapebox
+\newcount \shapepenalty
+\newdimen \shapekern
+\newskip \shapeskip
+
+\newbox \newshapebox
+\newbox \oldshapebox
+\newbox \tmpshapebox
+
+\newcount \shapecounter
+
+\newevery \everyshapebox \relax
+
+\permanent\dimensiondef\shapesignal.12345678pt % or 12345sp
+
+\permanent\protected\def\reshapebox#1%
+ {\doreshapebox
+ {#1}%
+ {\penalty\shapepenalty}%
+ {\kern \shapekern }%
+ {\vskip \shapeskip }}
+
+\permanent\protected\def\doreshapebox#1#2#3#4% \shapebox, \shapepenalty, \shapekern, \shapeskip
+ {\global\reshapingfailedfalse
+ \ifzeropt\ht\oldshapebox
+ \setbox\newshapebox\emptyvbox
+ \else
+ \setbox\newshapebox\vbox % can be \vpack
+ {\unvcopy\oldshapebox
+ \setbox\newshapebox\emptybox
+ \shapecounter\zerocount
+ \doloop{\dodoreshapebox{#1}{#2}{#3}{#4}}}%
+ \setbox\newshapebox\box\tmpshapebox
+ \fi}
+
+% We will turn this into a \MKIV\ variant (we can use \type {\vpack} too).
+
+\permanent\protected\def\insertshapesignal
+ {\hpack to \shapesignal{\strut\hss}% plus \strut
+ \prevdepth\strutdp} % never \nointerlineskip
+
+\permanent\protected\def\restoreshapebox % compensates for the signal
+ {\global\setbox\tmpshapebox\vbox{\vskip-\lineheight\unvcopy\oldshapebox}}
+
+\permanent\def\dodoreshapebox#1#2#3#4% \shapebox, \shapepenalty, \shapekern, \shapeskip
+ {\ifnum\lastnodetype=\gluenodecode
+ \shapeskip\lastskip
+ \global\setbox\tmpshapebox\vbox{#4\unvbox\tmpshapebox}%
+ \unskip
+ \orelse\ifnum\lastnodetype=\kernnodecode
+ \shapekern\lastkern
+ \global\setbox\tmpshapebox\vbox{#3\unvbox\tmpshapebox}%
+ \unkern
+ \orelse\ifnum\lastnodetype=\penaltynodecode
+ \shapepenalty\lastpenalty
+ \global\setbox\tmpshapebox\vbox{#2\unvbox\tmpshapebox}%
+ \unpenalty
+ \orelse\ifnum\lastnodetype<\zeropoint
+ \exitloop
+ \else
+ \setbox\shapebox\lastbox
+ \ifvoid\shapebox
+ \orelse\ifdim\wd\shapebox=\shapesignal\relax
+ \exitloop
+ \else
+ \shapecounter\zerocount
+ \global\setbox\tmpshapebox\vbox{#1\unvbox\tmpshapebox}%
+ \fi
+ \fi
+ \ifnum\shapecounter>100 % can be less
+ \global\reshapingfailedtrue
+ \message{!!forced exit from shapebox \the\lastnodetype !!}%
+ \restoreshapebox
+ \exitloop
+ \else
+ \advance\shapecounter \plusone
+ \fi}
+
+\permanent\protected\def\beginofshapebox
+ {\setbox\oldshapebox\vbox
+ \bgroup
+ \reshapingboxtrue
+ \the\everyshapebox
+ \insertshapesignal}
+
+\permanent\protected\def\endofshapebox
+ {\endgraf
+ \egroup}
+
+\aliased\let\beginshapebox\beginofshapebox
+\aliased\let\endshapebox \endofshapebox
+
+\permanent\protected\def\flushshapebox
+ {\bgroup
+ \ifzeropt\ht\newshapebox
+ \else
+ % make \prevdepth legal
+ % \par before the next \vskip gives far worse results
+ \ifdim\parskip>\zeropoint\vskip\parskip\else\par\fi
+ % and take a look
+ \ifdim\prevdepth=-\thousandpoint
+ \prevdepth\zeropoint
+ \fi
+ \ifdim\prevdepth<\zeropoint\relax
+ % something like a line or a signal or ...
+ \donetrue
+ \orelse\ifinner
+ % not watertight and not ok
+ \donefalse
+ \orelse\ifdim\pagegoal=\maxdimen
+ \donetrue
+ \else
+ % give the previous line a normal depth
+ \donetrue
+ {\forgeteverypar\verticalstrut}\nobreak
+ \kern-\struttotal % geen \vskip
+ \kern-\parskip
+ % \vskip-\strutdp
+ \fi
+ \scratchdimen\dp\newshapebox
+ \unvbox\newshapebox
+ % \prevdepth=0pt and \dp\newshapebox depend on last line
+ \kern-\scratchdimen % ??
+ % now \prevdepth=0pt
+ \ifdone
+ \kern\strutdp
+ \prevdepth\strutdp
+ \fi
+ \fi
+ \egroup}
+
+%D In real inner situations we can use:
+%D
+%D \starttyping
+%D \flushinnershapebox
+%D \stoptyping
+%D
+%D This one is used in \type{\framed}.
+
+% The kern fails on for instance:
+%
+% \omlijnd[offset=0pt,hoogte=8mm,uitlijnen={rechts,laho}]{\bfa test}
+
+\permanent\protected\def\innerflushshapebox
+ {\ifzeropt\ht\newshapebox \else
+ \unvcopy\newshapebox\relax % unvcopy ! else spacing problem
+ % \kern-\dp\newshapebox\relax
+ \fi}
+
+%D For absolute control, one can use \type {\doreshapebox} directly. This macro
+%D takes four arguments, that take care of:
+%D
+%D \startitemize[n,packed]
+%D \item \type{\shapebox}
+%D \item \type{\shapepenalty}
+%D \item \type{\shapekern}
+%D \item \type{\shapeskip}
+%D \stopitemize
+
+%D \macros
+%D {shapedhbox}
+%D
+%D When constructing a new box, using the content of \type {\shapebox}, one can best
+%D use \type {\shapedhbox} instead of \type {\hbox}, since it manages the height and
+%D depth of the line.
+
+\permanent\protected\def\shapedhbox % lines with non strutted dimensions have
+ {\expanded{\dowithnextbox % interlineskip so if we want the original
+ {\ht\nextbox\the\ht\shapebox % spacing, we need to preserve the original
+ \dp\nextbox\the\dp\shapebox % height and depth which is definitely
+ \box\nextbox}} % needed if we apply struts to the 'new'
+ \hbox} % box or do something that changed ist size
+
+%D Till here obsolete.
+
+%D \macros
+%D {hyphenatedword,
+%D hyphenatedpar,
+%D hyphenatedfile,
+%D dohyphenateword}
+%D
+%D We no longer use the pure \TEX\ variant. In due time we will report some more
+%D advanced statistics.
+%D
+%D \starttyping
+%D \showhyphens{dohyphenatedword}
+%D \stoptyping
+
+\permanent\protected\def\doshowhyphenatednextbox
+ {\clf_showhyphenatedinlist\nextbox}
+
+\permanent\protected\def\showhyphens % hpack: so no processing (we hyphenate in lua)
+ {\dowithnextboxcs\doshowhyphenatednextbox\hpack}
+
+%D The following macros are seldom used but handy for tracing.
+%D
+%D \starttyping
+%D \hyphenatedword{dohyphenatedword}
+%D \hyphenatedpar {\dorecurse{10}{dohyphenatedword }}
+%D \hyphenatedfile{tufte}
+%D \stoptyping
+
+\def\syst_boxes_hyphenatednextbox {\clf_hyphenatedlist\nextbox false\relax\unhbox\nextbox}
+\def\syst_boxes_hyphenatednextboxcolor{\clf_hyphenatedlist\nextbox true \relax\unhbox\nextbox}
+
+\permanent\protected\def\hyphenatedword {\dowithnextboxcs\syst_boxes_hyphenatednextbox\hbox}
+\permanent\protected\def\hyphenatedpar {\dowithnextboxcs\syst_boxes_hyphenatednextbox\hbox}
+\permanent\protected\def\hyphenatedfile #1{\dowithnextboxcs\syst_boxes_hyphenatednextbox\hbox{\readfile{#1}\donothing\donothing}}
+\permanent\protected\def\hyphenatedcoloredword{\dowithnextboxcs\syst_boxes_hyphenatednextboxcolor\hbox}
+
+%D \macros
+%D {processtokens}
+%D
+%D We fully agree with (most) typographers that inter||letter spacing is only
+%D permitted in fancy titles, we provide a macro that can be used to do so. Because
+%D this is (definitely and fortunately) no feature of \TEX, we have to step through
+%D the token list ourselves.
+%D
+%D \starttyping
+%D \processtokens {before} {between} {after} {space} {tokens}
+%D \stoptyping
+%D
+%D An example of a call is:
+%D
+%D \startbuffer
+%D \processtokens {[} {+} {]} {\space} {hello world}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This results in:
+%D
+%D \getbuffer
+%D
+%D The list of tokens may contain spaces, while \type {\\}, \type {{}} and \type {\
+%D } are handled as space too.
+
+%D This can be done in a more modern way but for nostalgic reasons we keep it.
+
+\mutable\let\nextprocessedtoken\empty
+
+\mutable\let\before \relax % for now mutable
+%mutable\let\between\relax % is a math character
+\mutable\let\after \relax % for now mutable
+\mutable\let\white \relax % for now mutable
+
+\permanent\protected\def\processtokens#1#2#3#4#5%
+ {\begingroup
+ \enforced\permanent\def\space{ }%
+ \enforced\let\\\space
+ \enforced\def\before {#1}%
+ \enforced\def\between{#2}%
+ \enforced\def\after {#3}%
+ \enforced\def\white {#4}%
+ \enforced\let\m_syst_boxes_before\before
+ \syst_boxes_processtokens#5\s!e_o_t_token
+ \endgroup}
+
+\def\syst_boxes_processtokens% the space after = is essential
+ {\afterassignment\syst_boxes_do_processtokens\let\nextprocessedtoken= }
+
+\def\syst_boxes_redo_processedtoken
+ {\dowithnextbox
+ {\before{\copy\nextbox}% \before can use nextbox several times
+ \enforced\let\before\between
+ \syst_boxes_processtokens}
+ \hbox\bgroup}
+
+\def\syst_boxes_do_processtokens
+ {\ifx\nextprocessedtoken\s!e_o_t_token
+ \after
+ \orelse\ifx\nextprocessedtoken\bgroup
+ \expandafter\syst_boxes_redo_processedtoken
+ \else
+ \expandafter\if\space\nextprocessedtoken
+ \after\white
+ \enforced\let\before\m_syst_boxes_before
+ \else
+ \before\nextprocessedtoken
+ \enforced\let\before\between
+ \fi
+ \expandafter\syst_boxes_processtokens
+ \fi}
+
+%D \macros
+%D {doboundtext}
+%D
+%D Sometimes there is not enough room to show the complete (line of) text. In such a
+%D situation we can strip of some characters by using \type {\doboundtext}. When the
+%D text is wider than the given width, it's split and the third argument is
+%D appended. When the text to be checked is packed in a command, we'll have to use
+%D \type {\expandafter}.
+%D
+%D \starttyping
+%D \doboundtext{a very, probably to long, text}{3cm}{...}
+%D \stoptyping
+%D
+%D When calculating the room needed, we take the width of the third argument into
+%D account, which leads to a bit more complex macro than needed at first sight.
+%D
+%D Sort of obsolete I guess:
+
+\def\syst_boxes_boundtext#1%
+ {\setbox\scratchboxone\hbox{#1}%
+ \advance\scratchdimen -\wd\scratchboxone
+ \ifdim\scratchdimen>\zeropoint\relax#1\fi}
+
+\permanent\protected\def\doboundtext#1#2#3% still used?
+ {\hbox
+ {\setbox\scratchbox\hbox{#1}%
+ \scratchdimen#2\relax
+ \ifdim\wd\scratchbox>\scratchdimen
+ \setbox\scratchbox\hbox{#3}%
+ \advance\scratchdimen -\wd\scratchbox
+ \handletokens#1\with\syst_boxes_boundtext
+ \fi
+ \box\scratchbox}}
+
+%D \macros
+%D {limitatetext}
+%D
+%D A bit more beautiful alternative for the previous command is the next one. This
+%D command is more robust because we let \TEX\ do most of the job. The previous
+%D command works better on text that cannot be hyphenated.
+%D
+%D \starttyping
+%D \limitatetext {text} {width} {sentinel}
+%D \limitatetext {text} {-width} {prelude}
+%D \stoptyping
+%D
+%D When no width is given, the whole text comes available. The sentinel is optional.
+%D This is about the third version.
+
+\ifdefined\fakecompoundhyphen\else \let\fakecompoundhyphen\relax \fi
+\ifdefined\veryraggedright \else \def\veryraggedright{\raggedright} \fi
+
+%D See \MKIV\ file for the older implementation.
+
+\let\m_syst_boxes_left \empty
+\let\m_syst_boxes_right\empty
+
+\permanent\protected\def\limitatetext#1#2#3%
+ {% we could also split in \LUA
+ \splitatcomma{#2}\m_syst_boxes_left\m_syst_boxes_right
+ \limitated
+ \ifempty\m_syst_boxes_left\else
+ left \m_syst_boxes_left
+ \fi
+ \ifempty\m_syst_boxes_right\else
+ right \m_syst_boxes_right
+ \fi
+ strip true
+ sentinel {#3}
+ text {#1}
+ \relax}
+
+%D Undocumented bonus (see wiki):
+%D
+%D \starttyping
+%D \limitatefirstline{\input tufte\relax}{10cm}{\unknown}
+%D \stoptyping
+
+\permanent\protected\def\limitatefirstline#1#2#3%
+ {\hbox\bgroup\strut % \hpack
+ \setbox\scratchbox\hbox{\begstrut#1\endstrut}%
+ \ifdim\wd\scratchbox>#2\relax
+ \setbox\scratchbox\hbox{#3}%
+ \hsize#2\relax
+ \advance\hsize-\wd\scratchbox
+ \setbox\scratchbox\vbox{\forgetall\veryraggedright#1}%
+ \setbox\scratchbox\vsplit\scratchbox to \lineheight
+ \vbox
+ {\unvbox\scratchbox
+ \global\setbox\plusone\lastbox
+ \global\setbox\plusone\hbox{\strut\unhbox\plusone}%
+ \hbox % to #2 % \hpack
+ {\unless\ifdefined\clip
+ \box\plusone
+ \orelse\ifdim\wd\plusone>\hsize
+ \lower\strutdepth\hpack{\clip[\c!width=\hsize,\c!height=\lineheight]{\hpack{\raise\strutdepth\box\plusone}}}%
+ \else
+ \box\plusone
+ \fi
+ \removeunwantedspaces#3}}% \removeunwantedspaces\hss#3}}%
+ \else
+ #1%
+ \fi
+ \egroup}
+
+%D \macros
+%D {processisolatedwords,processisolatedchars}
+%D
+%D \startbuffer
+%D \processisolatedchars{some more words} \ruledhbox \par
+%D \processisolatedchars{and some $x + y = z$ math} \ruledhbox \par
+%D \processisolatedchars{and a \hbox{$x + y = z$}} \ruledhbox \par
+%D \processisolatedwords{some more words} \ruledhbox \par
+%D \processisolatedwords{and some $x + y = z$ math} \ruledhbox \par
+%D \processisolatedwords{and a \hbox{$x + y = z$}} \ruledhbox \par
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+% todo: provide variant with #1 picked up as box
+
+\permanent\protected\def\processisolatedchars#1#2%
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox{\settrialtypesetting#2{\savecurrentattributes{pic}}}%
+ \setbox\scratchbox\hbox{\restorecurrentattributes{pic}#1}%
+ \clf_applytobox
+ method {char}%
+ box \scratchbox
+ command {\csstring#2}%
+ nested true%
+ \relax
+ \endgroup}
+
+\permanent\protected\def\processisolatedwords#1#2%
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox{\settrialtypesetting#2{\savecurrentattributes{pic}}}%
+ \setbox\scratchbox\hbox{\restorecurrentattributes{pic}#1}%
+ \clf_applytobox
+ method {word}%
+ box \scratchbox
+ command {\csstring#2}%
+ nested true%
+ \relax
+ \endgroup}
+
+%D A variant:
+
+\permanent\protected\def\applytocharacters#1%
+ {\dontleavehmode
+ \dowithnextbox{\clf_applytobox
+ method {char}%
+ box \nextbox
+ command {\csstring#1}%
+ nested true%
+ \relax}%
+ \hbox}
+
+\permanent\protected\def\applytowords#1%
+ {\dontleavehmode
+ \dowithnextbox{\clf_applytobox
+ method {word}%
+ box \nextbox
+ command {\csstring#1}%
+ nested true%
+ \relax}%
+ \hbox}
+
+%D The old call:
+
+\permanent\protected\def\processwords#1%
+ {\processisolatedwords{#1}\processword}
+
+\mutable\let\processword\relax
+
+\permanent\protected\def\applytosplitstringchar#1#2%
+ {\dontleavehmode\clf_processsplit
+ data {#2}%
+ command {\csstring#1}%
+ method {char}%
+ \relax}
+
+\permanent\protected\def\applytosplitstringword#1#2%
+ {\dontleavehmode\clf_processsplit
+ data {#2}%
+ command {\csstring#1}%
+ method {word}%
+ \relax}
+
+\permanent\protected\def\applytosplitstringline#1#2%
+ {\dontleavehmode\clf_processsplit
+ data {#2}%
+ command {\csstring#1}%
+ method {line}%
+ \relax}
+
+\permanent\protected\def\applytosplitstringcharspaced#1#2%
+ {\dontleavehmode\clf_processsplit
+ data {#2}%
+ command {\csstring#1}%
+ method {char}%
+ spaced true%
+ \relax}
+
+\permanent\protected\def\applytosplitstringwordspaced#1#2%
+ {\dontleavehmode\clf_processsplit
+ data {#2}%
+ command {\csstring#1}%
+ method {word}%
+ spaced true%
+ \relax}
+
+\permanent\protected\def\applytosplitstringlinespaced#1#2%
+ {\dontleavehmode\clf_processsplit
+ data {#2}%
+ command {\csstring#1}%
+ method {line}%
+ spaced true%
+ \relax}
+
+%D \macros
+%D {sbox}
+%D
+%D This is a rather strange command. It grabs some box content and and limits the
+%D size to the height and depth of a \type {\strut}. The resulting bottom||alligned
+%D box can be used aside other ones, without disturbing the normal baseline
+%D distance.
+%D
+%D \startbuffer
+%D \ruledhbox to .5\hsize{\sbox{eerste\par tweede \par derde}}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Shows up as:
+%D
+%D \startexample
+%D \vskip3\baselineskip
+%D \getbuffer
+%D \stopexample
+%D
+%D Before displaying the result we added some skip, otherwise the first two lines
+%D would have ended up in the text. This macro can be useful when building
+%D complicated menus, headers and footers and|/|or margin material.
+
+\permanent\protected\def\sbox
+ {\vpack\bgroup
+ \dowithnextboxcs\syst_boxes_sbox_finish\vbox}
+
+\def\syst_boxes_sbox_finish
+ {\boxyoffset\nextbox-\strutdp
+ \dp\nextbox\strutdp
+ \ht\nextbox\strutht
+ \box\nextbox
+ \egroup}
+
+%D A variant on this:
+%D
+%D \starttyping
+%D xx \ruledhbox{\inlinedbox{\tfd test}} xx
+%D \stoptyping
+
+\permanent\protected\def\inlinedbox
+ {\bgroup
+ \dowithnextboxcs\syst_boxes_inlined_finish\hbox}
+
+\def\syst_boxes_inlined_finish
+ {\boxyoffset\nextbox-\dimexpr(\htdp\nextbox-\lineheight)/\plustwo+\strutdp\relax
+ \ht\nextbox\strutht
+ \dp\nextbox\strutdp
+ \box\nextbox
+ \egroup}
+
+%D \macros
+%D {struttedbox}
+%D
+%D This boxing macro limits the height and depth to those of a strut.
+
+\permanent\protected\def\struttedbox
+ {\hpack\bgroup
+ \dowithnextboxcs\syst_boxes_struttedbox_finish\hbox}
+
+\def\syst_boxes_struttedbox_finish
+ {\dp\nextbox\strutdepth
+ \ht\nextbox\strutheight
+ \box\nextbox
+ \egroup}
+
+%D \macros
+%D {topskippedbox}
+%D
+%D This macro compensates the difference between the topskip and strutheight. Watch
+%D how we preserve the depth when it equals strutdepth.
+
+\permanent\protected\def\topskippedbox
+ {\hpack\bgroup\dowithnextboxcs\syst_boxes_topskippedbox_finish\hbox}
+
+\def\syst_boxes_topskippedbox_finish
+ {\edef\m_boxes_tmp{\ifdim\strutdepth=\dp\nextbox\dp\nextbox\the\dp\nextbox\fi}%
+ \lower\topskip\hpack{\raise\strutheight\box\nextbox}%
+ \m_boxes_tmp
+ \egroup}
+
+%D \macros
+%D {centeredbox, centerednextbox}
+%D
+%D Here is another strange one. This one offers a sort of overlay with positive or
+%D negative offsets. This command can be used in well defined areas where no offset
+%D options are available. We first used it when building a button inside the margin
+%D footer, where the button should have a horizontal offset and should be centered
+%D with respect to the surrounding box. The last of the three examples we show below
+%D says:
+%D
+%D \starttyping
+%D \vsize=3cm
+%D \hsize=3cm
+%D \ruledvbox to \vsize
+%D {\centeredbox height .5cm width -1cm
+%D {\vrule width \hsize height \vsize}}}
+%D \stoptyping
+%D
+%D Here the \type {\ruledvbox} just shows the surrounding box and \type {\vrule} is
+%D used to show the centered box.
+%D
+%D \def\AnExample#1#2%
+%D {\vsize=3cm
+%D \hsize=3cm
+%D \ruledvbox to \vsize
+%D {\centeredbox height #1 width #2
+%D {\color[green]{\vrule width \hsize height \vsize}}}}
+%D
+%D \startlinecorrection
+%D \startcombination[3*1]
+%D {\AnExample {-1cm} {.5cm}} {}
+%D {\AnExample {.5cm} {-1cm}} {}
+%D {\AnExample {-1cm} {-.5cm}} {}
+%D \stopcombination
+%D \stoplinecorrection
+%D
+%D This command takes two optional arguments: \type {width} and \type {height}.
+%D Observing readers can see that we use \TEX's own scanner for grabbing these
+%D arguments: \type {#1#} reads everyting till the next brace and passes it to both
+%D rules. The setting of the box dimensions at the end is needed for special cases.
+%D The dimensions of the surrounding box are kept intact. This commands handles
+%D positive and negative dimensions (which is why we need two boxes with rules).
+
+\permanent\protected\def\centeredbox#1#% height +/-dimen width +/-dimen
+ {\bgroup
+ \dontcomplain
+ \forgetall
+ \scangivendimensions#1\relax
+ \advance\vsize\givenheight
+ \advance\hsize\givenwidth
+ \dowithnextboxcs\syst_boxes_centered_finish
+ \hbox}
+
+\def\syst_boxes_centered_finish
+ {\boxxoffset\nextbox.5\dimexpr
+ \hsize
+ -\wd\nextbox
+ -\givenwidth
+ \relax
+ \boxyoffset\nextbox.5\dimexpr
+ \vsize
+ -\ht\nextbox
+ +\dp\nextbox
+ -\givenheight
+ \relax
+ \wd\nextbox\dimexpr\hsize-\givenwidth \relax
+ \ht\nextbox\dimexpr\vsize-\givenheight\relax
+ \dp\nextbox\zeropoint
+ \box\nextbox
+ \egroup}
+
+%D For those who don't want to deal with \type {\hsize} and \type {\vsize}, we have:
+%D
+%D \starttyping
+%D \centerednextbox width 2bp height 2bp
+%D {\framed[width=100bp,height=100bp]{}}
+%D \stoptyping
+%D
+%D Do you see why we call this one \type {next}?
+
+\permanent\protected\def\centerednextbox#1#%
+ {\bgroup
+ \dowithnextbox
+ {\hsize\wd\nextbox
+ \vsize\ht\nextbox
+ \centeredbox#1{\box\nextbox}%
+ \egroup}
+ \hbox}
+
+%D \macros
+%D {centerbox}
+%D
+%D Centering on the available space is done by:
+%D
+%D \starttyping
+%D \centerbox <optional specs> {content}
+%D \stoptyping
+%D
+%D When omitted, the current \type {\hsize} and \type {\vsize} are used. Local
+%D dimensions are supported.
+
+\permanent\protected\def\centerbox#1#% optional height +/-dimen width +/-dimen
+ {\bgroup
+ \dowithnextbox
+ {\setlocalhsize
+ \setbox\scratchbox\hpack{\vrule\s!width \zeropoint#1}%
+ \ifzeropt\wd\scratchbox\else\hsize\wd\scratchbox\fi
+ \setbox\scratchbox\vpack{\hrule\s!height\zeropoint#1}%
+ \ifzeropt\ht\scratchbox\else\vsize\ht\scratchbox\fi
+ \vpack to \vsize{\vss\hpack to \hsize{\hss\box\nextbox\hss}\vss}%
+ \egroup}%
+ \hbox}
+
+%D \macros
+%D {setrigidcolumnhsize,rigidcolumnbalance,rigidcolumnlines}
+%D
+%D These macros are copied from the \TEX book, page~397, and extended by a macro
+%D that sets the \type {\hsize}.
+%D
+%D \starttyping
+%D \setrigidcolumnhsize {total width} {distance} {n}
+%D \rigidcolumnbalance {box}
+%D \stoptyping
+%D
+%D Both these macros are for instance used in typesetting footnotes. The following
+%D flags influence the process.
+
+\newif\ifalignrigidcolumns
+\newif\ifstretchrigidcolumns
+\newif\iftightrigidcolumns % if true: just a vbox, no depth/noflines/gridsnap corrrections
+
+\mutable\let\rigidcolumnlines\!!zerocount
+
+\newbox \rigidcolumnbox
+\newdimen\rigidhsize
+\newcount\rigidcolumns
+
+\permanent\protected\def\setrigidcolumnhsize#1#2#3% todo: \dimexpr
+ {\global\rigidhsize\hsize
+ \global\rigidcolumns#3\relax
+ \hsize#1\relax
+ \scratchdimen -#2\relax
+ \multiply\scratchdimen #3\relax
+ \advance\scratchdimen #2\relax
+ \advance\hsize \scratchdimen
+ \divide\hsize #3\relax}
+
+% ==
+%
+% \def\setrigidcolumnhsize#1#2#3%
+% {\global\rigidhsize\hsize
+% \rigidcolumns#3\relax
+% \hsize\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax}
+
+\permanent\protected\def\rigidcolumnbalance#1%
+ {\ifnum\rigidcolumns=1 % tzt ook h/d correctie
+ \ifinner\ifhmode\box\else\unvbox\fi\else\unvbox\fi#1\relax
+ \else
+ \vbox % \vpack
+ {\forgetall
+ \nopenalties
+ \dontcomplain
+ \setbox\rigidcolumnbox\vbox
+ {\line{}\goodbreak\unvbox#1\removebottomthings}%
+ \splittopskip\openstrutheight
+ \setbox\scratchbox\vsplit\rigidcolumnbox to \zeropoint
+ \ifcase\rigidcolumnlines\relax
+ % \iffalse
+ % % maybe some day an option
+ % \scratchskip\ht\rigidcolumnbox
+ % \advance\scratchskip\dp\rigidcolumnbox
+ % \getnoflines\scratchskip
+ % \ifodd\noflines
+ % \advance\noflines\plusone
+ % \fi
+ % \divide\noflines\rigidcolumns
+ %\else
+ \scratchdimen\ht\rigidcolumnbox
+ \divide\scratchdimen \rigidcolumns
+ \getnoflines\scratchdimen
+ %\fi
+ \else
+ \noflines\rigidcolumnlines % to be sure
+ \fi
+ \scratchdimen\noflines\lineheight
+ % new: we now loop so that we don't loose content
+ % since in practice we also use this macro for
+ % funny lineheights and border cases
+ \setbox0=\box\rigidcolumnbox
+ \doloop
+ {\setbox\rigidcolumnbox=\copy0
+ \setbox\scratchbox\hpack to \rigidhsize
+ {\dorecurse\rigidcolumns
+ {\setbox\scratchbox\vsplit\rigidcolumnbox to \scratchdimen
+ \dp\scratchbox\openstrutdepth
+ \setbox\scratchbox\vtop
+ \ifalignrigidcolumns to
+ \ifstretchrigidcolumns\vsize\else\scratchdimen\fi
+ \fi
+ {\unvbox\scratchbox}%
+ \wd\scratchbox\hsize
+ \box\scratchbox
+ \hfill}%
+ \hfillneg}%
+ \ifvoid\rigidcolumnbox\exitloop\else\advance\scratchdimen\lineheight\fi}%
+ \iftightrigidcolumns
+ \setbox\scratchbox\hpack{\raise\dp\scratchbox\box\scratchbox}%
+ \else
+ \advance\scratchdimen -\openstrutdepth
+ \setbox\scratchbox\hpack{\raise\scratchdimen\box\scratchbox}%
+ \dp\scratchbox\openstrutdepth
+ \ht\scratchbox\scratchdimen
+ \fi
+ \box\scratchbox}%
+ \fi}
+
+%D \macros
+%D {startvboxtohbox,stopvboxtohbox,convertvboxtohbox}
+%D
+%D Here is another of Knuth's dirty tricks, as presented on pages 398 and 399 of the
+%D \TEX book. These macros can be used like:
+%D
+%D \starttyping
+%D \vbox
+%D \bgroup
+%D \startvboxtohbox ... \stopvboxtohbox
+%D \startvboxtohbox ... \stopvboxtohbox
+%D \startvboxtohbox ... \stopvboxtohbox
+%D \egroup
+%D
+%D \vbox
+%D \bgroup
+%D \convertvboxtohbox
+%D \egroup
+%D \stoptyping
+%D
+%D These macros are used in reformatting footnotes, so they do what they're meant
+%D for.
+
+\newdimen\vboxtohboxslack
+\newdimen\hboxestohboxslack
+
+%D Create line and fake height of paragraph by messign with heights: a nice hack by
+%D DEK himself.
+
+%\protected\def\setvboxtohbox
+% {\bgroup
+% \ifdim\baselineskip<16pt \relax
+% \scratchdimen\baselineskip
+% \multiply\scratchdimen 1024
+% \else
+% \message{cropping \baselineskip to 16pt}%
+% \scratchdimen\maxdimen
+% \fi
+% \divide\scratchdimen \hsize
+% \multiply\scratchdimen 64
+% \xdef\vboxtohboxfactor{\withoutpt\the\scratchdimen}%
+% \egroup}
+%
+% \protected\def\startvboxtohbox
+% {\bgroup
+% \setvboxtohbox
+% \setbox\scratchbox\hbox\bgroup}
+%
+% \protected\def\stopvboxtohbox
+% {\ifcase\vboxtohboxslack\else\hskip\zeropoint\s!minus\vboxtohboxslack\fi
+% \egroup
+% \dp\scratchbox\zeropoint
+% \ht\scratchbox\vboxtohboxfactor\wd\scratchbox
+% \box\scratchbox
+% \egroup}
+
+% More modern:
+
+% \definesystemattribute[vboxtohboxseparator][public]
+
+%newbox\d_syst_boxes_vboxtohbox
+\newbox\d_syst_boxes_separator
+
+\permanent\protected\def\startvboxtohboxseparator
+ {\setbox\d_syst_boxes_separator\hbox attr \vboxtohboxseparatorattribute\plusone\bgroup}
+
+\permanent\protected\def\stopvboxtohboxseparator
+ {\egroup}
+
+\permanent\protected\def\startvboxtohbox
+ {\begingroup
+ \setbox\scratchbox\hbox\bgroup}
+
+\permanent\protected\def\stopvboxtohbox
+ {\ifvoid\d_syst_boxes_separator
+ \hskip\zeropoint\ifcase\vboxtohboxslack\else\s!minus\vboxtohboxslack\fi % we really need a skip
+ \else
+ \box\d_syst_boxes_separator
+ \fi
+ \egroup
+ \clf_hboxtovbox\scratchbox
+ \box\scratchbox
+ \endgroup}
+
+% A possible reconstruction:
+
+\permanent\protected\def\convertvboxtohbox
+ {\makehboxofhboxes
+ \setbox\scratchboxone\hpack{\unhbox\scratchboxone\removehboxes}% \hpack
+ \noindent\unhbox\scratchboxone\par}
+
+\permanent\protected\def\makehboxofhboxes
+ {\setbox\scratchboxone\emptyhbox
+ \loop % \doloop { .. \exitloop .. }
+ \setbox\scratchboxtwo\lastbox
+ \ifhbox\scratchboxtwo
+ \setbox\scratchboxone\hpack{\box\scratchboxtwo\unhbox\scratchboxone}%
+ \repeat}
+
+\permanent\protected\def\removehboxes
+ {\setbox\scratchboxone\lastbox
+ \ifhbox\scratchboxone
+ {\removehboxes}\unhbox\scratchboxone
+ \fi}
+
+% And one special for notes:
+
+\permanent\protected\def\starthboxestohbox
+ {\bgroup
+ \setbox\scratchbox\vbox\bgroup}
+
+\permanent\protected\def\stophboxestohbox
+ {\egroup
+ \clf_vboxlisttohbox\scratchbox\nextbox\dimexpr\hboxestohboxslack\relax
+ \dontleavehmode
+ \unhbox\nextbox
+ \removeunwantedspaces
+ \par
+ \egroup}
+
+%D \macros
+%D {unhhbox}
+%D
+%D The next macro is used in typesetting inline headings. Let's first look at the
+%D macro and then show an example.
+
+\newbox \unhhedbox
+\newbox \hhbox
+\newdimen \lasthhboxwidth
+\newskip \hhboxindent
+
+\permanent\protected\def\unhhbox#1\with#2%
+ {\bgroup
+ \nopenalties
+ \dontcomplain
+ \forgetall
+ \setbox\unhhedbox\vbox{\hskip\hhboxindent\strut\unhbox#1}% => \hsize
+ \doloop
+ {\setbox\hhbox\vsplit\unhhedbox to \lineheight
+ \ifvoid\unhhedbox
+ \setbox\hhbox\hbox{\strut\hboxofvbox\hhbox}% \hpack
+ \fi
+ \ht\hhbox\strutht
+ \dp\hhbox\strutdp
+ \ifzeropt\hhboxindent\else
+ \setbox\hhbox\hpack{\kern-\hhboxindent\box\hhbox}%
+ \hhboxindent\zeropoint
+ \fi
+ \global\lasthhboxwidth\wd\hhbox
+ #2\relax
+ \ifvoid\unhhedbox
+ \exitloop
+ \else
+ \hskip\zeropoint \s!plus \zeropoint
+ \fi}%
+ \egroup}
+
+\def\syst_boxes_hboxofvbox
+ {\setbox0\vpack{\unvbox\scratchcounter\global\setbox1\lastbox}%
+ \unhbox1
+ \egroup}
+
+\permanent\protected\def\hboxofvbox
+ {\bgroup
+ \afterassignment\syst_boxes_hboxofvbox
+ \scratchcounter=}
+
+%D This macro can be used to break a paragraph apart and treat each line seperately,
+%D for instance, making it clickable. The main complication is that we want to be
+%D able to continue the paragraph, something that's needed in the in line section
+%D headers.
+%D
+%D \startbuffer
+%D \setbox0=\hbox{\input tufte \relax}
+%D \setbox2=\hbox{\input knuth \relax}
+%D \unhhbox0\with{\ruledhbox{\box\hhbox}}
+%D \hskip1em plus 1em minus 1em
+%D \hhboxindent=\lasthhboxwidth
+%D \advance\hhboxindent by \lastskip
+%D \unhhbox2\with{\ruledhbox{\box\hhbox}}
+%D \stopbuffer
+%D
+%D \getbuffer
+%D
+%D This piece of text was typeset by saying:
+%D
+%D \typebuffer
+%D
+%D Not that nice a definition, but effective. Note the stretch we've build in the
+%D line that connects the two paragraphs.
+
+%D \macros
+%D {doifcontent}
+%D
+%D When processing depends on the availability of content, one can give the next
+%D macro a try.
+%D
+%D \starttyping
+%D \doifcontent{pre content}{post content}{no content}\somebox
+%D \stoptyping
+%D
+%D Where \type {\somebox} is either a \type {\hbox} or \type {\vbox}. If the
+%D dimension of this box suggest some content, the resulting box is unboxed and
+%D surrounded by the first two arguments, else the third arguments is executed.
+
+\permanent\protected\def\doifcontent#1#2#3%
+ {\dowithnextbox
+ {\ifhbox\nextbox
+ \ifdim\wd\nextbox>\zeropoint
+ #1\unhbox\nextbox#2\relax
+ \else
+ #3\relax
+ \fi
+ \else
+ \ifdim\ht\nextbox>\zeropoint
+ #1\unvbox\nextbox#2\relax
+ \else
+ #3\relax
+ \fi
+ \fi}}
+
+%D So when we say:
+%D
+%D \startbuffer
+%D \doifcontent{[}{]}{}\hbox{content sensitive typesetting}
+%D
+%D \doifcontent{}{\page}{}\vbox{content sensitive typesetting}
+%D
+%D \doifcontent{}{}{\message{Didn't you forget something?}}\hbox{}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D We get:
+%D
+%D \getbuffer
+%D
+%D Where the last call of course does not show up in this document, but definitely
+%D generates a confusing message.
+
+%D \macros
+%D {processboxes}
+%D
+%D The next macro gobble boxes and is for instance used for overlays. First we show
+%D the general handler.
+
+\newbox\processbox % public : this is the one where \nextbox's end up in
+
+\let\syst_boxes_process_indeed\relax
+
+\permanent\protected\def\processboxes#1%
+ {\bgroup
+ \def\syst_boxes_process_indeed{#1}% #1 can be redefined halfway
+ \setbox\processbox\emptybox
+ \doifelsenextbgroup\syst_boxes_process_yes\syst_boxes_process_nop}
+
+\def\syst_boxes_process_yes
+ {\dowithnextboxcs\syst_boxes_process_content\hbox}
+
+\def\syst_boxes_process_content
+ {\removeunwantedspaces
+ \syst_boxes_process_indeed % takes \nextbox makes \processbox
+ \doifelsenextbgroup\syst_boxes_process_yes\syst_boxes_process_nop}
+
+\protected\def\syst_boxes_process_nop
+ {\removeunwantedspaces
+ \box\processbox
+ \egroup}
+
+%D \macros
+%D {startoverlay}
+%D
+%D We can overlay boxes by saying:
+%D
+%D \startbuffer
+%D \startoverlay
+%D {\framed{hans}}
+%D {\framed[width=3cm]{ton}}
+%D {\framed[height=2cm]{oeps}}
+%D \stopoverlay
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D shows up as:
+%D
+%D \leavevmode\getbuffer
+
+\permanent\def\boxisempty#1%
+ {\ifdim\wd#1=\zeropoint
+ \ifdim\ht#1=\zeropoint
+ \ifdim\dp#1=\zeropoint
+ \zerocount
+ \else
+ \plusone
+ \fi
+ \else
+ \plusone
+ \fi
+ \else
+ \plusone
+ \fi}
+
+\def\syst_boxes_overlay_process
+ {\ifcase\boxisempty\nextbox\else
+ \syst_boxes_overlay_process_indeed
+ \fi}
+
+\def\syst_boxes_overlay_process_indeed
+ {%\removeunwantedspaces % already done
+ \scratchdepth\dp\ifdim\dp\nextbox>\dp\processbox\nextbox\else\processbox\fi
+ \ifdim\ht\nextbox>\ht\processbox
+ \setbox\processbox\vpack to \ht\nextbox {\dp\processbox\zeropoint\vss\box\processbox\vss}%
+ \else
+ \setbox\nextbox \vpack to \ht\processbox{\dp\nextbox \zeropoint\vss\box\nextbox \vss}%
+ \fi
+ \dp\nextbox \scratchdepth
+ \dp\processbox\scratchdepth
+ \scratchwidth\wd\ifdim\wd\nextbox>\wd\processbox\nextbox\else\processbox\fi
+ \setbox\processbox\hpack to \scratchwidth
+ {\hpack to \scratchwidth{\hss\box\processbox\hss}%
+ \kern-\scratchwidth
+ \hpack to \scratchwidth{\hss\box\nextbox \hss}}}
+
+\permanent\protected\def\startoverlay
+ {\bgroup
+ \enforced\aliased\let\stopoverlay\egroup
+ \processboxes\syst_boxes_overlay_process}
+
+\aliased\let\stopoverlay\relax
+
+%D \macros
+%D {fakebox}
+%D
+%D The next macro is a rather silly one, but saves space.
+%D
+%D \starttyping
+%D \hbox{\fakebox0}
+%D \stoptyping
+%D
+%D returns an empty box with the dimensions of the box specified, here being zero.
+
+\permanent\protected\def\fakebox
+ {\bgroup
+ \afterassignment\syst_boxes_fakebox_finish\scratchcounter}
+
+\def\syst_boxes_fakebox_finish
+ {\setbox\scratchbox\ifhbox\scratchcounter\emptyhbox\else\emptyvbox\fi
+ \wd\scratchbox\wd\scratchcounter
+ \ht\scratchbox\ht\scratchcounter
+ \dp\scratchbox\dp\scratchcounter
+ \box\scratchbox
+ \egroup}
+
+%D \macros
+%D {lbox,rbox,cbox,tbox,bbox}
+%D
+%D Here are some convenient alternative box types:
+%D
+%D \starttyping
+%D \lbox{text ...}
+%D \cbox{text ...}
+%D \rbox{text ...}
+%D \stoptyping
+%D
+%D Are similar to \type {\vbox}, which means that they also accept something like
+%D \type {to 3cm}, but align to the left, middle and right. These box types can be
+%D used to typeset paragraphs.
+
+\def\syst_boxes_lrc_process#1%
+ {\bgroup
+ \forgetall
+ \enforced\let\\\endgraf
+ #1%
+ \let\nexttoken}
+
+\permanent\protected\def\lbox#1#{\vbox#1\syst_boxes_lrc_process\raggedleft }
+\permanent\protected\def\cbox#1#{\vbox#1\syst_boxes_lrc_process\raggedcenter}
+\permanent\protected\def\rbox#1#{\vbox#1\syst_boxes_lrc_process\raggedright }
+
+\permanent\protected\def\ltop#1#{\vtop#1\syst_boxes_lrc_process\raggedleft }
+\permanent\protected\def\ctop#1#{\vtop#1\syst_boxes_lrc_process\raggedcenter}
+\permanent\protected\def\rtop#1#{\vtop#1\syst_boxes_lrc_process\raggedright }
+
+%D The alternatives \type {\tbox} and \type {\bbox} can be used to properly align
+%D boxes, like in:
+%D
+%D \setupexternalfigures[directory={../sample}]
+%D \startbuffer
+%D \starttable[|||]
+%D \HL
+%D \VL \tbox{\externalfigure[cow][height=3cm,frame=on]} \VL top aligned \VL\SR
+%D \HL
+%D \VL \bbox{\externalfigure[cow][height=3cm,frame=on]} \VL bottom aligned \VL\SR
+%D \HL
+%D \stoptable
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D The positioning depends on the strut settings:
+%D
+%D \getbuffer
+
+\permanent\protected\def\tbox{\hpack\bgroup\dowithnextboxcs\syst_boxes_tbox_finish\hbox}
+\permanent\protected\def\bbox{\hpack\bgroup\dowithnextboxcs\syst_boxes_bbox_finish\hbox}
+
+\def\syst_boxes_tbox_finish
+ {\scratchheight\ht\strutbox
+ \scratchdepth\dimexpr\htdp\nextbox-\scratchheight\relax
+ \ht\nextbox\scratchheight
+ \dp\nextbox\scratchdepth
+ \boxyoffset\nextbox-\scratchdepth
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_bbox_finish
+ {\scratchdepth\dp\strutbox
+ \scratchheight\dimexpr\htdp\nextbox-\scratchdepth\relax
+ \dp\nextbox\scratchdepth
+ \ht\nextbox\scratchheight
+ \boxyoffset\nextbox-\scratchdepth
+ \box\nextbox
+ \egroup}
+
+%D \macros
+%D {lhbox,mhbox,rhbox}
+%D
+%D A few more boxes.
+
+\def\syst_boxes_lhbox{\hpack to \hsize{\box\nextbox\hss }}
+\def\syst_boxes_mhbox{\hpack to \hsize{\hss\box\nextbox\hss}}
+\def\syst_boxes_rhbox{\hpack to \hsize{\hss\box\nextbox }}
+
+\permanent\protected\def\lhbox{\dowithnextboxcs\syst_boxes_lhbox\hbox}
+\permanent\protected\def\mhbox{\dowithnextboxcs\syst_boxes_mhbox\hbox}
+\permanent\protected\def\rhbox{\dowithnextboxcs\syst_boxes_rhbox\hbox}
+
+\aliased\let\lefthbox \lhbox
+\aliased\let\midhbox \mhbox
+\aliased\let\righthbox\rhbox
+
+%D \macros
+%D {boxofsize}
+%D
+%D Sometimes we need to construct a box with a height or width made up of several
+%D dimensions. Instead of cumbersome additions, we can use:
+%D
+%D \starttyping
+%D \boxofsize \vbox 10cm 3cm -5cm {the text to be typeset}
+%D \stoptyping
+%D
+%D This example demonstrates that one can use positive and negative values.
+%D Dimension registers are also accepted.
+
+\newdimen\sizeofbox
+
+\permanent\protected\def\boxofsize#1%
+ {\bgroup
+ \sizeofbox\zeropoint
+ \scratchdimen\zeropoint
+ \def\docommand
+ {\advance\sizeofbox\scratchdimen
+ \futurelet\nexttoken\dodocommand}%
+ \def\dodocommand
+ {\ifx\nexttoken\bgroup
+ \expanded{\egroup#1 to \the\sizeofbox}%
+ \else
+ \expandafter\afterassignment\expandafter\docommand\expandafter\scratchdimen
+ \fi}%
+ \docommand}
+
+%D Some new, still undocumented features:
+
+% limitatetext -> beter {text} als laatste !!
+%
+% \limitvbox
+% \limithbox
+
+\permanent\protected\def\limitatelines#1#2% size sentinel
+ {\dowithnextbox
+ {\scratchdimen#1\hsize
+ \ifdim\wd\nextbox>\scratchdimen
+ \setbox\nextbox\hbox
+ {\advance\scratchdimen -.1\hsize
+ \limitatetext{\unhbox\nextbox}{\scratchdimen}{\nobreak#2}}%
+ \fi
+ \unhbox\nextbox}
+ \hbox}
+
+\permanent\protected\def\fittoptobaselinegrid % weg hier
+ {\dowithnextbox
+ {\bgroup
+ \par
+ \scratchdimen\ht\nextbox
+ \ht\nextbox\strutht
+ \dp\nextbox\strutdp
+ \hpack{\box\nextbox}
+ \prevdepth\strutdp
+ \doloop
+ {\advance\scratchdimen -\lineheight
+ \ifdim\scratchdimen<\zeropoint
+ \exitloop
+ \else
+ \nobreak
+ \hpack{\strut}
+ \fi}
+ \egroup}
+ \vbox}
+
+%D Some more undocumented macros (used in m-chart).
+
+\newif\iftraceboxplacement % \traceboxplacementtrue
+
+\newbox\fakedboxcursor
+
+\setbox\fakedboxcursor\hpack
+ {\vrule\s!width\zeropoint\s!height\zeropoint\s!depth\zeropoint}
+
+\permanent\protected\def\boxcursor % overloaded in core-vis
+ {\iftraceboxplacement
+ \bgroup
+ \scratchdimen2\onepoint
+ \setbox\scratchbox\hpack to \zeropoint
+ {\hss
+ \vrule
+ \s!width \scratchdimen
+ \s!height\scratchdimen
+ \s!depth \scratchdimen
+ \hss}%
+ \smashedbox\scratchbox
+ \egroup
+ \else
+ \copy\fakedboxcursor
+ \fi}
+
+\permanent\protected\def\placedbox
+ {\iftraceboxplacement\ruledhbox\else\hbox\fi}
+
+\newdimen\boxoffset
+\newdimen\boxhdisplacement
+\newdimen\boxvdisplacement
+
+\permanent\protected\def\rightbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_rightbox_finish \placedbox}
+\permanent\protected\def\leftbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_leftbox_finish \placedbox}
+\permanent\protected\def\topbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_topbox_finish \placedbox}
+\permanent\protected\def\bottombox {\hpack\bgroup\dowithnextboxcs\syst_boxes_bottombox_finish \placedbox}
+\permanent\protected\def\lefttopbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_lefttopbox_finish \placedbox}
+\permanent\protected\def\righttopbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_righttopbox_finish \placedbox}
+\permanent\protected\def\leftbottombox {\hpack\bgroup\dowithnextboxcs\syst_boxes_leftbottombox_finish \placedbox}
+\permanent\protected\def\rightbottombox{\hpack\bgroup\dowithnextboxcs\syst_boxes_rightbottombox_finish\placedbox}
+
+\aliased\let\topleftbox \lefttopbox
+\aliased\let\toprightbox \righttopbox
+\aliased\let\bottomleftbox \leftbottombox
+\aliased\let\bottomrightbox\rightbottombox
+
+\def\syst_boxes_rightbox_finish
+ {\global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement.5\dimexpr\ht\nextbox-\dp\nextbox\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_leftbox_finish
+ {\global\boxhdisplacement\dimexpr-\wd\nextbox-\boxoffset\relax
+ \global\boxvdisplacement.5\dimexpr\ht\nextbox-\dp\nextbox\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_topbox_finish
+ {\global\boxhdisplacement-.5\wd\nextbox
+ \global\boxvdisplacement\dimexpr-\dp\nextbox-\boxoffset\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_bottombox_finish
+ {\global\boxhdisplacement-.5\wd\nextbox
+ \global\boxvdisplacement\dimexpr\ht\nextbox+\boxoffset\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_lefttopbox_finish
+ {\global\boxhdisplacement\dimexpr-\wd\nextbox-\boxoffset\relax
+ \global\boxvdisplacement\dimexpr-\dp\nextbox-\boxoffset\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_righttopbox_finish
+ {\global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement\dimexpr-\dp\nextbox-\boxoffset\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_leftbottombox_finish
+ {\global\boxhdisplacement\dimexpr-\wd\nextbox-\boxoffset\relax
+ \global\boxvdisplacement\dimexpr\ht\nextbox+\boxoffset\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_rightbottombox_finish
+ {\global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement\dimexpr\ht\nextbox+\boxoffset\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\permanent\protected\def\middlebox {\hpack\bgroup\dowithnextboxcs\syst_boxes_middlebox_finish \placedbox}
+\permanent\protected\def\baselinemiddlebox{\hpack\bgroup\dowithnextboxcs\syst_boxes_baselinemiddlebox_finish\placedbox}
+\permanent\protected\def\baselineleftbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_baselineleftbox_finish \placedbox}
+\permanent\protected\def\baselinerightbox {\hpack\bgroup\dowithnextboxcs\syst_boxes_baselinerightbox_finish \placedbox}
+
+\def\syst_boxes_middlebox_finish
+ {\global\boxhdisplacement-.5\wd\nextbox
+ \global\boxvdisplacement.5\dimexpr\ht\nextbox-\dp\nextbox\relax
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_baselinemiddlebox_finish
+ {\global\boxhdisplacement\dimexpr-.5\wd\nextbox-\boxoffset\relax
+ \global\boxvdisplacement-\boxoffset
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_baselineleftbox_finish
+ {\global\boxhdisplacement\dimexpr-\wd\nextbox-\boxoffset\relax
+ \global\boxvdisplacement-\boxoffset
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_baselinerightbox_finish
+ {\global\boxhdisplacement\boxoffset
+ \global\boxvdisplacement-\boxoffset
+ \boxcursor
+ \boxxmove\nextbox \boxhdisplacement
+ \boxymove\nextbox-\boxvdisplacement
+ \box\nextbox
+ \egroup}
+
+%D \macros
+%D {obox}
+%D
+%D Experimental, not yet frozen:
+
+\permanent\protected\def\lrtbbox#1#2#3#4% l r t b
+ {\bgroup
+ \dowithnextboxcontent
+ {\advance\hsize-#1\advance\hsize-#2\relax
+ \advance\vsize-#3\advance\vsize-#4\relax}
+ {\forgetall\vpack to \vsize{\vskip#3\hpack to \hsize{\hskip#1\box\nextbox\hss}\vss}\egroup}
+ \vbox}
+
+%D \macros
+%D {toplinebox}
+%D
+%D See core-tbl.tex for an example of its usage:
+
+\permanent\protected\def\toplinebox
+ {\dowithnextboxcs\syst_boxes_toplinebox_finish\tbox}
+
+\def\syst_boxes_toplinebox_finish
+ {\ifdim\dp\nextbox>\strutdepth
+ \scratchdimen\dp\nextbox
+ \advance\scratchdimen-\strutdepth
+ \getnoflines\scratchdimen
+ \struttedbox{\box\nextbox}%
+ \dorecurse\noflines\verticalstrut
+ \else
+ \box\nextbox
+ \fi}
+
+%D \macros
+%D {initializeboxstack,savebox,foundbox}
+%D
+%D At the cost of some memory, but saving box registers, we have implemented a box
+%D repository.
+%D
+%D \starttyping
+%D \initializeboxstack{one}
+%D
+%D \savebox{one}{a}{test a}
+%D \savebox{one}{p}{test p}
+%D \savebox{one}{q}{test q}
+%D
+%D \hbox{a:\foundbox{one}{a}} \par
+%D \hbox{q:\foundbox{one}{q}} \par
+%D \hbox{p:\foundbox{one}{p}} \par
+%D \hbox{x:\foundbox{one}{x}} \par
+%D \hbox{y:\foundbox{two}{a}} \par
+%D \stoptyping
+
+%D Kind of obsolete:
+
+\installcorenamespace {stackbox}
+\installcorenamespace {stacklst}
+
+\permanent\protected\def\setstackbox#1#2%
+ {\ifcsname\??stackbox#1:#2\endcsname\else
+ \expandafter\newbox\csname\??stackbox#1:#2\endcsname
+ \fi
+ \global\setbox\csname\??stackbox#1:#2\endcsname\vbox}
+
+\permanent\protected\def\initializeboxstack#1%
+ {\def\docommand##1{\setstackbox{#1}{##1}{}}%
+ \ifcsname\??stacklst#1\endcsname
+ \expandafter\processcommacommand\expandafter[\lastnamedcs]\docommand
+ \fi
+ \gletcsname\??stacklst#1\endcsname\empty}
+
+\permanent\protected\def\savebox#1#2% stack name
+ {% beware, \setxvalue defines the cs beforehand so we cannot use the test inside the { }
+ \ifcsname\??stacklst#1\endcsname
+ \xdefcsname\??stacklst#1\expandafter\endcsname\expandafter{\lastnamedcs,#2}%
+ \else
+ \xdefcsname\??stacklst#1\endcsname{#2}%
+ \fi
+ \setstackbox{#1}{#2}}
+
+\permanent\protected\def\flushbox#1#2% unwrapped
+ {\ifcsname\??stackbox#1:#2\endcsname
+ \box\lastnamedcs
+ \else
+ \emptybox
+ \fi}
+
+\permanent\protected\def\restorebox#1#2% unwrapped
+ {\ifcsname\??stackbox#1:#2\endcsname
+ \copy\lastnamedcs
+ \else
+ \emptybox
+ \fi}
+
+\permanent\protected\def\foundbox#1#2% wrapped
+ {\vpack
+ {\ifcsname\??stackbox#1:#2\endcsname
+ \copy\lastnamedcs
+ \fi}}
+
+\permanent\protected\def\doifelsebox#1#2%
+ {\unless\ifcsname\??stackbox#1:#2\endcsname
+ \expandafter\secondoftwoarguments
+ \orelse\ifvoid\lastnamedcs
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\aliased\let\doifboxelse\doifelsebox
+
+%D This one is cheaper (the above is no longer used that much):
+
+\installcorenamespace {boxstack}
+
+\newcount\c_syst_boxes_stack
+
+\mutable\let\b_syst_boxes_stack\relax
+
+\protected\def\syst_boxes_stack_allocate
+ {\newbox\b_syst_boxes_stack
+ \letcsname\??boxstack\number\c_syst_boxes_stack\endcsname\b_syst_boxes_stack}
+
+\protected\def\syst_boxes_push#1#2%
+ {\global\advance\c_syst_boxes_stack\plusone
+ \expandafter\let\expandafter\b_syst_boxes_stack\csname\??boxstack\number\c_syst_boxes_stack\endcsname
+ \ifrelax\b_syst_boxes_stack % cheaper then csname check as in most cases it's defined
+ \syst_boxes_stack_allocate
+ \fi
+ #1\setbox\b_syst_boxes_stack\box#2\relax}
+
+\protected\def\syst_boxes_pop#1#2%
+ {#1\setbox#2\box\csname\??boxstack\number\c_syst_boxes_stack\endcsname
+ \global\advance\c_syst_boxes_stack\minusone}
+
+\permanent\protected\def\localpushbox {\syst_boxes_push\relax}
+\permanent\protected\def\localpopbox {\syst_boxes_pop \relax}
+
+\permanent\protected\def\globalpushbox{\syst_boxes_push\global}
+\permanent\protected\def\globalpopbox {\syst_boxes_pop \global}
+
+%D And here is a more modern one (not yet in i-*):
+%D
+%D \starttyping
+%D \dorecurse {100} {
+%D \setbox\zerocount\hbox{test \recurselevel}
+%D \putboxincache{foo}{\recurselevel}\zerocount
+%D \copyboxfromcache{foo}{\recurselevel}\zerocount
+%D \iftrue
+%D \setbox\zerocount\hbox{\directboxfromcache{foo}{\recurselevel}}%
+%D \else
+%D \getboxfromcache{foo}{\recurselevel}\zerocount
+%D \fi
+%D }
+%D \resetboxesincache{foo}
+%D \stoptyping
+
+% \putboxincache % {category} {name} number
+% \getboxfromcache % {category} {name} number
+% \doifelseboxincache % {category} {name}
+% \copyboxfromcache % {category} {name} number
+% \directboxfromcache % {category} {name}
+% \directcopyboxfromcache % {category} {name}
+% \resetboxesincache % {category}
+% \putnextboxincache % {category} {name} box
+% \getboxwdfromcache % {category} {name}
+% \getboxhtfromcache % {category} {name}
+% \getboxdpfromcache % {category} {name}
+
+%D \macros
+%D {removedepth, obeydepth}
+%D
+%D While \type {\removedepth} removes the preceding depth, \type {\obeydepth} makes
+%D sure we have depth. Both macros leave the \type {\prevdepth} untouched.
+
+\permanent\protected\def\removedepth
+ {\ifvmode
+ \ifdim\prevdepth>\zeropoint
+ \kern-\prevdepth
+ \fi
+ \fi}
+
+\permanent\protected\def\obeydepth
+ {\par % watch out for changes in math formulas
+ \ifvmode\ifdim\prevdepth<\zeropoint\orelse\ifdim\prevdepth<\strutdp
+ \kern\dimexpr\strutdp-\prevdepth\relax
+ \prevdepth\strutdp
+ \fi\fi}
+
+\permanent\protected\def\undepthed
+ {\dowithnextbox{\dp\nextbox\zeropoint\box\nextbox}\hbox}
+
+%D \macros
+%D {removebottomthings, removelastskip}
+%D
+%D A funny (but rather stupid) one, plus a redefinition.
+
+\permanent\protected\def\removebottomthings
+ {\dorecurse\plusfive{\unskip\unkern\unpenalty}}
+
+\permanent\protected\def\removelastskip % \ifvmode the plain tex one \fi, overloaded to \permanent in spac-ver
+ {\ifvmode\ifzeropt\lastskip\else\vskip-\lastskip\fi\fi}
+
+%D \macros
+%D {makestrutofbox}
+%D
+%D This macro sets the dimensions of a box to those of a strut. Sort of obsolete so
+%D it will go away.
+
+\permanent\protected\def\makestrutofbox % not used
+ {\afterassignment\syst_boxes_makestrutofbox\c_boxes_register}
+
+\def\syst_boxes_makestrutofbox
+ {\ht\c_boxes_register\strutht
+ \dp\c_boxes_register\strutdp
+ \wd\c_boxes_register\zeropoint}
+
+%D \macros
+%D {raisebox,lowerbox}
+%D
+%D Some more box stuff, related to positioning (under construction). Nice stuff for
+%D a tips and tricks maps article.
+%D
+%D \starttyping
+%D \raisebox{100pt}\hbox{test}
+%D \hsmash{\raisebox{100pt}\hbox{test}}
+%D \stoptyping
+
+\permanent\protected\def\raisebox#1{\bgroup\afterassignment\syst_boxes_raise_indeed\scratchdimen#1} % so both 10pt and {10pt} is accepted
+\permanent\protected\def\lowerbox#1{\bgroup\afterassignment\syst_boxes_lower_indeed\scratchdimen#1} % so both 10pt and {10pt} is accepted
+
+\def\syst_boxes_raise_indeed{\dowithnextboxcs\syst_boxes_raise_finish}
+\def\syst_boxes_lower_indeed{\dowithnextboxcs\syst_boxes_lower_finish}
+
+\def\syst_boxes_raise_finish
+ {\boxyoffset\nextbox\scratchdimen
+ \ht\nextbox\strutht
+ \dp\nextbox\strutdp
+ \box\nextbox
+ \egroup}
+
+\def\syst_boxes_lower_finish
+ {\boxyoffset\nextbox-\scratchdimen
+ \ht\nextbox\strutht
+ \dp\nextbox\strutdp
+ \box\nextbox
+ \egroup}
+
+% vcenter in text, we kunnen vcenter overloaden
+
+\permanent\protected\def\halfwaybox
+ {\hpack\bgroup
+ \dowithnextboxcs\syst_boxes_halfwaybox_finish\hbox}
+
+\def\syst_boxes_halfwaybox_finish
+ {\dp\nextbox\zeropoint
+ \lower.5\ht\nextbox\box\nextbox
+ \egroup}
+
+\permanent\protected\def\depthonlybox
+ {\tpack\bgroup
+ \dowithnextboxcs\syst_boxes_depthonlybox_finish\vbox}
+
+\def\syst_boxes_depthonlybox_finish
+ {\hsize\wd\nextbox
+ \kern\zeropoint\box\nextbox
+ \egroup}
+
+%D New:
+
+\permanent\def\setdimentoatleast#1#2{\ifdim#1>\zeropoint\else#1=#2\fi}
+\permanent\def\setdimentoatmost #1#2{\ifdim#1>#2\relax \else#1=#2\fi}
+
+%D And even rawer:
+
+\aliased\let\naturalvcenter\normalvtop % will go away
+
+% \appendtoks \let\vcenter\normalvcenter \to \everymathematics
+
+%D \macros
+%D {frozenhbox}
+%D
+%D A not so well unhboxable box can be made with:
+
+\permanent\protected\def\frozenhbox
+ {\hpack\bgroup
+ \dowithnextboxcs\syst_boxes_frozenhbox_finish\hbox}
+
+\def\syst_boxes_frozenhbox_finish
+ {\hpack{\hpack{\box\nextbox}}%
+ \egroup}
+
+%D \macros
+%D {setboxllx,setboxlly,gsetboxllx,gsetboxlly,getboxllx,getboxlly}
+%D
+%D A prelude to an extended \TEX\ feature:
+
+\installcorenamespace {box_x}
+\installcorenamespace {box_y}
+
+\permanent\protected\def\setboxllx #1#2{\edefcsname\??box_x\number#1\endcsname{\the\dimexpr#2\relax}}
+\permanent\protected\def\setboxlly #1#2{\edefcsname\??box_y\number#1\endcsname{\the\dimexpr#2\relax}}
+
+\permanent\protected\def\gsetboxllx#1#2{\xdefcsname\??box_x\number#1\endcsname{\the\dimexpr#2\relax}}
+\permanent\protected\def\gsetboxlly#1#2{\xdefcsname\??box_y\number#1\endcsname{\the\dimexpr#2\relax}}
+
+\permanent\def\getboxllx#1{\ifcsname\??box_x\number#1\endcsname\lastnamedcs\else\zeropoint\fi}
+\permanent\def\getboxlly#1{\ifcsname\??box_y\number#1\endcsname\lastnamedcs\else\zeropoint\fi}
+
+\permanent\def\directgetboxllx#1{\csname\??box_x\number#1\endcsname} % use when sure existence
+\permanent\def\directgetboxlly#1{\csname\??box_y\number#1\endcsname} % use when sure existence
+
+%D \macros
+%D {shownextbox}
+%D
+%D Handy for tracing
+%D
+%D \starttyping
+%D \shownextbox\vbox{test}
+%D \shownextbox\vbox{test\endgraf}
+%D \shownextbox\vbox{test\endgraf\strut\endgraf}
+%D \shownextbox\vbox{test\endgraf\thinrule}
+%D \shownextbox\vbox{\setupwhitespace[big]test\endgraf\thinrule}
+%D \stoptyping
+
+\permanent\protected\def\shownextbox % seldom used
+ {\dowithnextbox
+ {\bgroup
+ \showboxbreadth\maxdimen
+ \showboxdepth \maxdimen
+ \scratchcounter\interactionmode
+ \batchmode
+ \showbox\nextbox
+ \box\nextbox
+ \interactionmode\scratchcounter
+ \egroup}}
+
+\permanent\protected\def\spreadhbox#1% rebuilds \hbox{<box><hss><box><hss><box>}
+ {\bgroup
+ \ifhbox#1\relax
+ \setbox\scratchboxtwo\emptybox
+ \unhbox#1%
+ \doloop
+ {\unpenalty\unskip\unpenalty\unskip\unpenalty\unskip
+ \setbox\scratchboxone\lastbox
+ \ifvoid\scratchboxone
+ \exitloop
+ \else
+ \setbox\scratchboxtwo\hbox
+ {\ifhbox\scratchboxone \spreadhbox\scratchboxone\else\box\scratchboxone\fi
+ \ifvoid\scratchboxtwo \else\hss\unhbox\scratchboxtwo\fi}%
+ \fi}%
+ \ifvoid\scratchboxtwo\else\unhbox\scratchboxtwo\fi
+ \else
+ \box#1%
+ \fi
+ \egroup}
+
+% Why not ...
+
+\showboxbreadth\plusthousand
+\showboxdepth \plusthousand
+
+%D Moved from cont-new:
+%D
+%D \starttyping
+%D \minimalhbox 100pt {test}
+%D \stoptyping
+
+\permanent\protected\def\minimalhbox#1#%
+ {\dowithnextbox
+ {\bgroup
+ \setbox\scratchbox\hpack#1{\hss}%
+ \ifdim\wd\nextbox<\wd\scratchbox\wd\nextbox\wd\scratchbox\fi
+ \box\nextbox
+ \egroup}
+ \hbox}
+
+%D A bit dirty:
+
+% \permanent\protected\def\nodestostring#1#2% more tolerant for #2=\cs
+% {\begingroup
+% \setbox\nextbox\hbox{#2}%
+% \normalexpanded{\endgroup\edef\noexpand#1{\boxtostring\nextbox}}}
+%
+% \permanent\def\tostring
+% {\beginlocalcontrol
+% \dowithnextbox{\endlocalcontrol\boxtostring\nextbox}\hbox}
+%
+% \permanent\def\tostring
+% {\beginlocalcontrol\begingroup
+% \dowithnextbox{\normalexpanded{\endgroup\endlocalcontrol\boxtostring\nextbox}}\hbox}
+
+\def\syst_boxes_contenttostring
+ {\normalexpanded{\endgroup\endlocalcontrol\boxtostring\nextbox}}
+
+\permanent\def\contenttostring
+ {\beginlocalcontrol\begingroup
+ \dowithnextboxcs\syst_boxes_contenttostring\hbox}
+
+\newtoks\everypreroll
+
+\let\prerolltostring\firstofoneargument % we need to bypass initializations
+
+\appendtoks
+ \permanent\def\prerolltostring
+ {\beginlocalcontrol\begingroup
+ \the\everypreroll
+ \dowithnextboxcs\syst_boxes_contenttostring\hbox}
+\to \everydump
+
+% depricated:
+
+\permanent\protected\def\nodestostring#1#2% more tolerant for #2=\cs
+ {\edef#2{\contenttostring{#1}}}
+
+%D Even more dirty:
+
+\aliased\let\hyphenatedhbox\hbox % was a macro in mkii
+
+%D We can do this:
+%D
+%D \starttyping
+%D \setbox0\hbox to 10cm{foo} \setbox2\hbox{\unhbox0} \the\wd2
+%D \stoptyping
+%D
+%D But this saves a copy (and hpack pass):
+%D
+%D \starttyping
+%D \setbox0\hbox to 10cm{foo} \the\naturalwd0
+%D \stoptyping
+
+\newdimen\lastnaturalboxwd
+\newdimen\lastnaturalboxht
+\newdimen\lastnaturalboxdp
+
+%D We can define these public in lua:
+
+\aliased\let\getnaturaldimensions\clf_getnaturaldimensions % sets three dimensions
+\aliased\let\naturalwd \clf_naturalwd % calculates and returns wd
+
+\aliased\let\getnaturalwd\clf_getnaturalwd % no intermediate
+\aliased\let\setnaturalwd\clf_setnaturalwd % no intermediate
+
+\permanent\protected\def\doifelserighttoleftinbox{\clf_doifelserighttoleftinbox}
+
+\aliased\let\doifrighttoleftinboxelse\doifelserighttoleftinbox
+
+%D New, used in high/low:
+
+\definesystemattribute [runningtext] [public]
+
+\permanent\protected\def\runninghbox{\hbox attr \runningtextattribute \fontid\font} % not yet in i-*
+
+%D To complement lua (yet undocumented):
+
+\permanent\protected\def\beginhbox{\hbox\bgroup} \aliased\let\endhbox\egroup
+\permanent\protected\def\beginvbox{\vbox\bgroup} \aliased\let\endvbox\egroup
+\permanent\protected\def\beginvtop{\vtop\bgroup} \aliased\let\endvtop\egroup
+
+\permanent\protected\def\sethboxregister#1{\setbox#1\hbox}
+\permanent\protected\def\setvboxregister#1{\setbox#1\vbox}
+\permanent\protected\def\setvtopregister#1{\setbox#1\vtop}
+
+\permanent\protected\def\flushboxregister#1{\box\numexpr#1\relax}
+
+\permanent\protected\def\starthboxregister#1{\setbox#1\hbox\bgroup} \aliased\let\stophboxregister\egroup
+\permanent\protected\def\startvboxregister#1{\setbox#1\vbox\bgroup} \aliased\let\stopvboxregister\egroup
+\permanent\protected\def\startvtopregister#1{\setbox#1\vtop\bgroup} \aliased\let\stopvtopregister\egroup
+
+% %D For whatever third party package needs it:
+% %D
+% %D \starttyping
+% %D \newlocalbox\BoxOne
+% %D \newlocalbox\BoxTwo
+% %D
+% %D \setbox\BoxOne\hbox{Box One}
+% %D \setbox\BoxTwo\hbox{Box Two}
+% %D
+% %D [\box\BoxTwo] [\box\BoxOne]
+% %D \stoptyping
+%
+% \installcorenamespace{localbox}
+%
+% \permanent\protected\def\newlocalbox#1%
+% {\expandafter\let\expandafter#1\csname\??localbox\string#1\endcsname
+% \ifrelax#1%
+% \syst_aux_new_localbox#1%
+% \fi}
+%
+% \def\syst_aux_new_localbox#1%
+% {\expandafter\newbox\csname\??localbox\string#1\endcsname
+% \newlocalbox#1}
+
+%D Who knows when this comes in handy:
+
+\permanent\protected\def\lastlinewidth{\dimexpr\clf_lastlinewidth\scaledpoint\relax}
+
+%D Keep as reference:
+
+% \protected\def\tightvbox{\dowithnextbox{\dp\nextbox\zeropoint\box\nextbox}\vbox}
+% \protected\def\tightvtop{\dowithnextbox{\ht\nextbox\zeropoint\box\nextbox}\vtop}
+
+%D This one keeps dimensions and sets the shift field (and so it's more for testing
+%D than for real usage):
+
+\permanent\protected\def\shiftbox {\clf_shiftbox}
+\permanent\protected\def\vpackbox {\clf_vpackbox}
+\permanent\protected\def\hpackbox {\clf_hpackbox}
+\permanent\protected\def\vpackedbox{\clf_vpackedbox}
+\permanent\protected\def\hpackedbox{\clf_hpackedbox}
+
+%D This one has been moved from a 2 decade old file. It makes something boxed
+%D sit on the baseline.
+
+\permanent\protected\def\linebox
+ {\hpack\bgroup\dowithnextbox
+ {\scratchdimen\dimexpr\dimexpr\htdp\nextbox-\lineheight\relax/2+\dp\strutbox\relax
+ \setbox\nextbox\hpack{\lower\scratchdimen\box\nextbox}%
+ \ht\nextbox\ht\strutbox
+ \dp\nextbox\dp\strutbox
+ \box\nextbox
+ \egroup}
+ \hbox}
+
+%D \macros
+%D {widthuptohere}
+%D
+%D Implemented at the \LUA\ end:
+%D
+%D \startbuffer
+%D widthuptohere:\the\widthuptohere\crlf
+%D widthuptohere : \the\widthuptohere (space without stretch or shrink!)
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+%D
+%D Implemented elsewhere:
+%D
+%D \starttyping
+%D \boxlines <box>
+%D \boxline <box> <line>
+%D \copyboxline <box> <line>
+%D \setboxline <box> <line>
+%D \boxlineht <box> <line> [<dimen>]
+%D \boxlinedp <box> <line> [<dimen>]
+%D \boxlinewd <box> <line> [<dimen>]
+%D \boxlinels <box> <line>
+%D \boxliners <box> <line>
+%D \boxlinelh <box> <line>
+%D \boxlinerh <box> <line>
+%D \boxlinelp <box> <line>
+%D \boxlinerp <box> <line>
+%D \boxlinein <box> <line>
+%D \boxrangeht <box> <first line> <last line>
+%D \boxrangedp <box> <first line> <last line>
+%D \boxrangewd <box> <first line> <last line>
+%D \stoptyping
+
+% To be discussed with ws.
+%
+% \installcorenamespace{namedboxes}
+%
+% \protected\def\newnamedbox#1%
+% {\ifcsname\??namedboxes#1\endcsname\else
+% \expandafter\newbox\csname\??namedboxes#1\endcsname
+% \fi}
+%
+% \protected\def\namedbox#1{\csname\??namedboxes#1\endcsname}
+
+%D Implemented elsewhere:
+%D
+%D \startbuffer
+%D \setbox0\hbox{test \footnote{test}} (\prelistbox0) (\postlistbox0)
+%D \setprelistbox0\hbox{BEFORE} \setpostlistbox0\hbox{AFTER}
+%D \box0
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+%D
+%D \starttyping
+%D \prelistbox <box>
+%D \postlistbox <box>
+%D \prelistcopy <box>
+%D \postlistcopy <box>
+%D \setprelistbox <box> <hbox|vbox|vtop|somebox>
+%D \setpostlistbox <box> <hbox|vbox|vtop|somebox>
+%D \stoptyping
+
+\protect \endinput
+
+% a bit of test code:
+
+% \hbox \bgroup
+% \ruledvbox {\hbox{\strut gans}}
+% \ruledvbox to \lineheight {\hbox{\strut gans}}
+% \ruledvbox to \lineheight {\hbox {gans}}
+% \ruledvbox to \strutheight{\hbox {gans}}
+% \ruledvbox to \strutheight{\hbox{\strut gans}}
+% \ruledvbox to \strutheight{\vss\hbox{gans}}
+% \egroup
+
+% to be considered
+
+% \startluacode
+%
+% local spacer = lpeg.patterns.spacer
+%
+% function commands.withwords(command,str)
+% if str then
+% command = command or "ruledhbox"
+% local done = false
+% local function apply(s)
+% if done then
+% context.space()
+% done = true
+% else
+% context.dontleavehmode()
+% end
+% context[command](s)
+% end
+% lpeg.match(lpeg.splitter(spacer,apply),str)
+% end
+%
+% end
+%
+% \stopluacode
+%
+% \unprotect
+%
+% \protected\def\withwordsinstring#1#2% command str
+% {\ctxcommand{withwords(\!!bs#1\!!es,\!!bs#2\!!es)}}
+%
+% \protected\def\withwordsinfile#1#2% command name
+% {\ctxcommand{withwords(\!!bs#1\!!es,io.loaddata(resolvers.findfile("#2")))}}
+%
+% \protect
+%
+% \starttext
+%
+% \defineframed[colored][foregroundcolor=red,foregroundstyle=\bfc\underbar,location=low]
+%
+% \withwordsinstring{colored}{bla bla}
+% \withwordsinfile{colored}{ward.tex}
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/supp-dir.mkxl b/Master/texmf-dist/tex/context/base/mkxl/supp-dir.mkxl
new file mode 100644
index 00000000000..d2500d236b1
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/supp-dir.mkxl
@@ -0,0 +1,44 @@
+%D \module
+%D [ file=supp-dir,
+%D version=2004.11.11,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Directional Things,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\aliased\let\directionlefttoright\zerocount
+\aliased\let\directionrighttoleft\plusone
+
+\immutable\edef\??bdir{\ifdefined\bodydir bdir\else direction\fi}
+
+\permanent\protected\edef\naturalhbox {\hbox \??bdir\directionlefttoright}
+\permanent\protected\edef\naturalvbox {\vbox \??bdir\directionlefttoright}
+\permanent\protected\edef\naturalvtop {\vtop \??bdir\directionlefttoright}
+\permanent\protected\edef\naturalhpack {\hpack \??bdir\directionlefttoright}
+\permanent\protected\edef\naturalvpack {\vpack \??bdir\directionlefttoright}
+\permanent\protected\edef\naturaltpack {\tpack \??bdir\directionlefttoright}
+
+\permanent\protected\edef\reversehbox {\hbox \??bdir\directionrighttoleft}
+\permanent\protected\edef\reversevbox {\vbox \??bdir\directionrighttoleft}
+\permanent\protected\edef\reversevtop {\vtop \??bdir\directionrighttoleft}
+\permanent\protected\edef\reversehpack {\hpack \??bdir\directionrighttoleft}
+\permanent\protected\edef\reversevpack {\vpack \??bdir\directionrighttoleft}
+\permanent\protected\edef\reversetpack {\tpack \??bdir\directionrighttoleft}
+
+% \expanded{\defineactivecharacter \number"2000E} {\textdirection\directionrighttoleft\relax}
+% \expanded{\defineactivecharacter \number"2000F} {\textdirection\directionlefttoright\relax}
+
+\def\syst_direction_string#1{\ifcase#1=\plusone r2l\else l2r\fi}
+
+\permanent\protected\def\showdirsinmargin
+ {\normalexpanded{\inleft{\naturalhbox
+ {\ttxx[\syst_direction_string\pardirection,\syst_direction_string\textdirection]}}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/supp-mat.mkxl b/Master/texmf-dist/tex/context/base/mkxl/supp-mat.mkxl
new file mode 100644
index 00000000000..5822e959dd3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/supp-mat.mkxl
@@ -0,0 +1,180 @@
+%D \module
+%D [ file=supp-mat,
+%D version=1998.09.10,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Math,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D For practical reasons, I decided to move some math things to a support module.
+%D There is nothing spectacular here. It may move back to math-ini.
+
+\writestatus{loading}{ConTeXt Support Macros / Math}
+
+\unprotect
+
+%D \macros
+%D {mathematics, math, nomathematics, startmathmode}
+%D
+%D The \type {$} can be both an begin and end math character. This can lead to
+%D confusing and errorprone situations when macros insert \type {$}. When for
+%D instance we have defined:
+%D
+%D \starttyping
+%D \def\MyPlus{$\,+\,$}
+%D \stoptyping
+%D
+%D the sequence \type{$x^2 \MyPlus y^2 = r^2$} will expand to:
+%D
+%D \starttyping
+%D $x^2 $\,+\,$ y^2 = r^2$
+%D \stoptyping
+%D
+%D Here the \type {\,} are given outside math mode and \TEX\ will definitely
+%D complain about this. A more save definition would have been:
+%D
+%D \starttyping
+%D \def\MyPlus{\mathematics{\,+\,}}
+%D \stoptyping
+%D
+%D Which is implemented (in \type{syst-ini.mkiv}) as:
+
+\permanent\protected\def\Ucheckedstartdisplaymath
+ {\ifinner
+ \ifhmode
+ \normalUstartmath
+ \let\Ucheckedstopdisplaymath\normalUstopmath
+ \else
+ \normalUstartdisplaymath
+ \let\Ucheckedstopdisplaymath\normalUstopdisplaymath
+ \fi
+ \else
+ \normalUstartdisplaymath
+ \let\Ucheckedstopdisplaymath\normalUstopdisplaymath
+ \fi}
+
+\aliased\let\Ucheckedstopdisplaymath\relax
+
+% \let\normalsuper\Usuperscript % obsolete
+% \let\normalsuber\Usubscript % obsolete
+
+\aliased\let\startimath\Ustartmath
+\aliased\let\stopimath \Ustopmath
+\aliased\let\startdmath\Ustartdisplaymath % \Ucheckedstartdisplaymath
+\aliased\let\stopdmath \Ustopdisplaymath % \Ucheckedstopdisplaymath
+
+\permanent\protected\def\mathematics#1{\relax \ifmmode#1\else\normalstartimath#1\normalstopimath\fi}
+\permanent\protected\def\displaymath#1{\noindent \ifmmode#1\else\normalstartdmath#1\normalstopdmath\fi}
+\permanent\protected\def\inlinemath #1{\dontleavehmode\ifmmode#1\else\normalstartimath#1\normalstopimath\fi}
+\permanent\protected\def\textmath #1{\dontleavehmode\ifmmode#1\else\begingroup\everymath\emptytoks\normalstartimath#1\normalstopimath\endgroup\fi} % \mathsurround\zeropoint
+
+\aliased\let\stopmathmode\relax
+
+\permanent\protected\def\startmathmode % nested variant
+ {\relax\ifmmode
+ \begingroup
+ \enforced\let\stopmathmode\endgroup
+ \else
+ \normalstartimath
+ \enforced\let\stopmathmode\normalstopimath
+ \fi}
+
+\permanent\protected\def\nomathematics#1%
+ {\relax\ifmmode\hbox{#1}\else#1\fi}
+
+\aliased\let\m \mathematics % \mathematics will be overloaded later
+\aliased\let\math\mathematics % \mathematics will be overloaded later
+
+%D \macros
+%D {displaymathematics,inlinemathematics,automathematics}
+%D
+%D An example of usage of the following can be found in the MathML module:
+
+\ifdefined\strc_formulas_start_formula \else
+ \def\strc_formulas_start_formula{\normalstartdmath}
+ \def\strc_formulas_stop_formula {\normalstopdmath }
+\fi
+
+\permanent\protected\def\displaymathematics#1{\relax\ifmmode#1\else\strc_formulas_start_formula{}#1\strc_formulas_stop_formula\fi}
+\permanent\protected\def\inlinemathematics {\dontleavehmode\mathematics}
+%permanent\protected\def\automathematics {\relax\ifhmode\expandafter\inlinemathematics\else\expandafter\displaymathematics\fi}
+
+% better, esp when used in bTABLE ... eTABLE
+
+\permanent\protected\def\automathematics
+ {\relax
+ \ifhmode
+ \expandafter\inlinemathematics
+ \orelse\ifintable
+ \expandafter\inlinemathematics
+ \else
+ \expandafter\displaymathematics
+ \fi}
+
+%D \macros
+%D {dimension, nodimension}
+%D
+%D The next few macros are used for typesetting dimensions in such a way that
+%D spacing is acceptable. I won't spend much words on these macros, because they
+%D will be overloaded in the units module.
+%D
+%D REPLACED (keep commented):
+
+\newsignal\dimensionsignal
+
+\mutable\def\dimensiontypeface {\tf}
+\mutable\def\dimensionhalfspace {\,}
+
+\aliased\let\dodimensionsignal\relax
+
+\permanent\protected\def\dimension#1%
+ {\pushmacro\dodimensionsignal
+ \enforced\gdef\dodimensionsignal{\kern\dimensionsignal}%
+ \ifzeropt\lastskip
+ \ifzeropt\lastkern
+ \ifmmode
+ \mathematics{\dimensionhalfspace\dimensionhalfspace\dimensiontypeface#1}%
+ \else
+ \mathematics{\dimensiontypeface#1}%
+ \fi
+ \orelse\ifdim\lastkern=\dimensionsignal
+ \mathematics{\dimensionhalfspace\dimensiontypeface#1}%
+ \else
+ \unkern\mathematics{\dimensionhalfspace\dimensionhalfspace\dimensiontypeface#1}%
+ \fi
+ \else
+ \unskip\mathematics{\dimensionhalfspace\dimensionhalfspace\dimensiontypeface#1}%
+ \fi
+ \dodimensionsignal
+ \popmacro\dodimensionsignal}
+
+\permanent\protected\def\nodimension#1%
+ {\unskip#1\enforced\glet\dodimensionsignal\relax}
+
+% experiment, not yet to be used
+
+% \protected\def\displaybreak
+% {\ifhmode
+% \removeunwantedspaces
+% \ifcase\raggedstatus\hfill\fi
+% \strut\penalty-9999 % \break fails on case (3)
+% \fi}
+%
+% \def\startdisplay{\displaybreak\ignorespaces\startpacked}
+% \def\stopdisplay {\stoppacked\displaybreak\ignorespaces}
+
+\aliased\let\superscript \Usuperscript
+\aliased\let\subscript \Usubscript
+\aliased\let\nosuperscript \Unosuperscript
+\aliased\let\nosubscript \Unosubscript
+\aliased\let\superprescript \Usuperprescript
+\aliased\let\subprescript \Usubprescript
+\aliased\let\nosuperprescript\Unosuperprescript
+\aliased\let\nosubsprecript \Unosubprescript
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/supp-ran.mkxl b/Master/texmf-dist/tex/context/base/mkxl/supp-ran.mkxl
new file mode 100644
index 00000000000..7d212fdfd01
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/supp-ran.mkxl
@@ -0,0 +1,52 @@
+%D \module
+%D [ file=supp-ran,
+%D version=2008-10-31,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Random Number Generation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Support Macros / Random Number Generation}
+
+%D This module is downward compatible in the sense that we've kept the user
+%D interface (which uses intermediate variables).
+%D
+%D \starttyping
+%D \type{new}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \setrandomseed{24}\type{set}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \setrandomseed{24}\type{set}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \setrandomseed{24}\type{set}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \pushrandomseed \type{new}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \poprandomseed \type{old}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \type{new}: \dorecurse{10}{\randomnumber{1}{100} }\par
+%D \stoptyping
+
+\registerctxluafile{supp-ran}{}
+
+\unprotect
+
+\permanent\protected\def\getrandomcount #1#2#3{#1=\clf_getrandomnumber\numexpr#2\relax\numexpr#3\relax\relax}
+\permanent\protected\def\getrandomdimen #1#2#3{#1=\clf_getrandomdimen\dimexpr#2\relax\dimexpr#3\relax\scaledpoint\relax}
+\permanent\protected\def\getrandomnumber#1#2#3{\edef#1{\clf_getrandomnumber\numexpr#2\relax\numexpr#3\relax}}
+\permanent\protected\def\getrandomfloat #1#2#3{\edef#1{\clf_getrandomfloat\dimexpr#2\relax\dimexpr#3\relax}}
+\permanent\protected\def\setrandomseed #1{\clf_setrandomseed\numexpr#1\relax}
+\permanent\protected\def\getrandomseed #1{\edef#1{\clf_getrandomseed}}
+\permanent\protected\def\newrandomseed {\clf_getrandomseed}
+%permanent\protected\def\pushrandomseed {\clf_pushrandomseed}
+%permanent\protected\def\poprandomseed {\clf_poprandomseed}
+%permanent\protected\def\reuserandomseed {\clf_reuserandomseed} % within push/pop
+
+\aliased\let\freezerandomseed \pushrandomseed
+\aliased\let\defrostrandomseed\poprandomseed
+
+\setnewconstant \mpscaledmax 4095 % for old times sake
+
+\permanent\def\randomnumber #1#2{\clf_getrandomnumber\numexpr#1\relax\numexpr#2\relax}
+\permanent\def\mprandomnumber {\clf_getrandomnumber\zerocount\mpscaledmax}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/symb-emj.lmt b/Master/texmf-dist/tex/context/base/mkxl/symb-emj.lmt
new file mode 100644
index 00000000000..b043d835df8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/symb-emj.lmt
@@ -0,0 +1,76 @@
+if not modules then modules = { } end modules ['symb-emj'] = {
+ version = 1.001,
+ comment = "companion to symb-emj.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- processors.hpack_filter does it all
+
+local symbols = fonts.symbols
+
+local resolvedemoji = characters.emoji.resolve
+local processfeatures = fonts.handlers.otf.featuresprocessor
+local injectspacing = nodes.injections.handler
+local protectglyphs = nodes.handlers.protectglyphs
+local tonodes = nodes.tonodes
+local currentfont = font.current
+
+local nuts = nodes.nuts
+local tonode = nuts.tonode
+local tonut = nuts.tonut
+local remove_node = nuts.remove
+local isglyph = nuts.isglyph
+local getnext = nuts.getnext
+
+local function removemodifiers(head)
+ local head = tonut(head)
+ local current = head
+ while current do
+ local char = isglyph(current)
+ if char and (char == 0x200D or (char >= 0x1F3FB and char <= 0x1F3FF)) then
+ head, current = remove_node(head,current,true)
+ else
+ current = getnext(current)
+ end
+ end
+ return tonode(head)
+end
+
+-- fast enough, no need to memoize, maybe use attributes
+
+local function checkedemoji(name,id)
+ local str = resolvedemoji(name)
+ if str then
+ if not id then
+ id = currentfont()
+ end
+ local head = tonodes(str,id,nil,nil,true) -- use current attributes
+ head = processfeatures(head,id,false)
+ if head then
+ head = injectspacing(head)
+ protectglyphs(head)
+ return removemodifiers(head)
+ end
+ end
+end
+
+symbols.emoji = {
+ resolved = resolvedemoji,
+ checked = checkedemoji,
+}
+
+interfaces.implement {
+ name = "resolvedemoji",
+ actions = { resolvedemoji, context.escaped },
+ arguments = "string",
+}
+
+interfaces.implement {
+ name = "checkedemoji",
+ actions = { checkedemoji, context },
+ arguments = "string",
+}
+
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/symb-emj.mkxl b/Master/texmf-dist/tex/context/base/mkxl/symb-emj.mkxl
new file mode 100644
index 00000000000..e64646a529e
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/symb-emj.mkxl
@@ -0,0 +1,26 @@
+%D \module
+%D [ file=symb-emj,
+%D version=2017.04.21,
+%D title=\CONTEXT\ Symbol Libraries,
+%D subtitle=Emoji,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Symbol Libraries / Emoji}
+
+\registerctxluafile{symb-emj}{autosuffix}
+
+\unprotect
+
+\permanent \def\expandedemoji#1{\clf_resolvedemoji{#1}}
+\permanent\protected\def\resolvedemoji#1{\clf_resolvedemoji{#1}}
+\permanent\protected\def\checkedemoji #1{\clf_checkedemoji {#1}}
+\permanent\protected\def\emoji #1{\dontleavehmode{\setdirectsymbolicfont{emoji}\clf_resolvedemoji{#1}}}
+\permanent\protected\def\robustemoji #1{\dontleavehmode{\setdirectsymbolicfont{emoji}\clf_checkedemoji {#1}}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/symb-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/symb-ini.lmt
new file mode 100644
index 00000000000..9837afcf325
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/symb-ini.lmt
@@ -0,0 +1,59 @@
+if not modules then modules = { } end modules ['symb-ini'] = {
+ version = 1.001,
+ comment = "companion to symb-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local context = context
+local variables = interfaces.variables
+
+fonts = fonts or { } -- brrrr
+
+local symbols = fonts.symbols or { }
+fonts.symbols = symbols
+
+local listitem = utilities.parsers.listitem
+local uselibrary = resolvers.uselibrary
+
+local report_symbols = logs.reporter ("fonts","symbols")
+local status_symbols = logs.messenger("fonts","symbols")
+
+local patterns = {
+ "symb-imp-%s.mkxl",
+ "symb-imp-%s.mkiv",
+ "symb-imp-%s.tex",
+ -- obsolete:
+ "symb-%s.mkiv",
+ "symb-%s.tex"
+}
+
+local function action(name,foundname)
+ commands.loadlibrary(name,foundname,false)
+ status_symbols("library %a loaded",name)
+end
+
+local function failure(name)
+ report_symbols("library %a is unknown",name)
+end
+
+function symbols.uselibrary(name)
+ if name ~= variables.reset then
+ for name in listitem(name) do
+ uselibrary {
+ name = name,
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = true,
+ }
+ end
+ end
+end
+
+interfaces.implement {
+ name = "usesymbols",
+ actions = symbols.uselibrary,
+ arguments = "string",
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/symb-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/symb-ini.mkxl
new file mode 100644
index 00000000000..c29b073606d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/symb-ini.mkxl
@@ -0,0 +1,340 @@
+%D \module
+%D [ file=symb-ini,
+%D version=1998.07.20,
+%D title=\CONTEXT\ Symbol Libraries,
+%D subtitle=Basic Symbols Commands,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The macros described here used to be part of the \type {core-con} module. I
+%D decided to move them here when symbolsets saw the light. Let their light shine.
+
+\writestatus{loading}{ConTeXt Symbol Libraries / Initialization}
+
+\registerctxluafile{symb-ini}{autosuffix}
+
+\unprotect
+
+%D \macros
+%D {definesymbol, symbol}
+%D
+%D Converting numbers or levels into a character, romannumeral, symbol or something
+%D else, is supported by many \CONTEXT\ commands. Therefore we need a mechanism for
+%D linking such numbers to their counterparts.
+%D
+%D First we take care of symbols. These are for instance used in enumerations and
+%D itemizations. We have:
+%D
+%D \showsetup{definesymbol}
+%D \showsetup{symbol}
+%D
+%D Symbols are simply linked to a tag. Such tags can be numbers or strings.
+%D
+%D \starttyping
+%D \definesymbol [1] [$\bullet$]
+%D \definesymbol [level 5] [$\star$]
+%D \stoptyping
+
+\installcorenamespace{symbol}
+\installcorenamespace{symbols}
+\installcorenamespace{symbolset}
+\installcorenamespace{symboldefault}
+
+%D For now we only have one option.
+
+\installparameterhandler\??symbols {symbols}
+\installsetuphandler \??symbols {symbols}
+
+\appendtoks
+ \doifelse{\symbolsparameter\c!stylealternative}\v!math
+ \settrue\setfalse\prefermathovertextchar
+\to \everysetupsymbols
+
+\setupsymbols
+ [\c!stylealternative=\v!text]
+
+\mutable\let\currentsymbol \empty
+\mutable\let\currentsymbolset\empty
+
+\newtoks\t_symb_setups
+
+\let\m_symb_current_set\empty
+
+\newconditional\c_symb_found
+
+\newtoks\everysymbol
+
+%D We don't use the commandhandler as symbols have their own subsystem for resolving
+%D values.
+
+\permanent\tolerant\protected\def\definesymbol[#1]#*[#2]#*[#3]%
+ {\ifparameter#3\or
+ \defcsname\??symbol#1:#2\endcsname{#3}%
+ \doifsomething{#1}{\addvalue{\??symbolset#1}{#2}}%
+ \else
+ \defcsname\??symbol\m_symb_current_set:#1\endcsname{#2}%
+ \addvalue{\??symbolset\m_symb_current_set}{#1}%
+ \fi}
+
+\permanent\protected\def\doifelseinsymbolset#1#2{\ifcsname\??symbol#1:#2\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}
+\permanent\protected\def\doifinsymbolset #1#2{\ifcsname\??symbol#1:#2\endcsname\expandafter\firstofoneargument \else\expandafter\gobbleoneargument \fi}
+\permanent\protected\def\doifelsesymbolset #1{\ifcsname\??symbolset#1\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}
+
+\aliased\let\doifinsymbolsetelse\doifelseinsymbolset
+\aliased\let\doifsymbolsetelse \doifelsesymbolset
+
+\letvalue{\??symbolset}\empty
+
+%def\symbolset#1{\csname\??symbolset\ifcsname\??symbolset#1\endcsname#1\fi\endcsname} % no [#1], to be used in commalists etc
+
+\permanent\def\symbolset#1{\begincsname\??symbolset#1\endcsname} % no [#1], to be used in commalists etc
+
+%D Since symbols are used frequently in interactive documents, we speed up this one.
+%D Well, that was history, since now we simplified things a bit, because the low
+%D level macros have been sped up every now and then.
+
+% We support both:
+%
+% Test test \symbol[whatever]\ test \symbol[whatever].
+% Test test \symbol{whatever} test \symbol{whatever}.
+
+\permanent\tolerant\protected\def\symbol[#1]#*[#2]%
+ {\ifarguments
+ \expandafter\symb_place_argument
+ \or
+ \symb_place_argument{#1}%
+ \or
+ \dontleavehmode
+ \edef\currentsymbol{#2}%
+ \ifcsname\??symbol#1:#2\endcsname
+ \symb_place_indeed{#1:#2}% maybe use \lastnamescs
+ \orelse\ifcsname\??symboldefault#1\endcsname
+ \symb_place_named{#1}% maybe use \lastnamescs
+ \else
+ \symb_place_normal
+ \fi
+ \fi}
+
+\def\symb_place_argument#1%
+ {\dontleavehmode
+ \edef\currentsymbol{#1}%
+ \ifcsname\??symbol\currentsymbolset:#1\endcsname
+ \symb_place_indeed{\currentsymbolset:#1}%
+ \else
+ \symb_place_normal
+ \fi}
+
+\def\symb_place_normal
+ {\setfalse\c_symb_found
+ \the\t_symb_setups
+ \ifconditional\c_symb_found \else
+ \symb_place_retry\currentsymbol % hm, isn't this redundant?
+ \fi}
+
+\def\symb_place_indeed#1% \relax's prevent lookahead problems
+ {\settrue\c_symb_found
+ \begingroup
+ \the\everysymbol
+ \csname\??symbol#1\endcsname\relax
+ \endgroup}
+
+\letvalue{\??symbol}\firstofoneargument
+
+\permanent\def\directsymbol#1#2% no \relax, there can be an argument, see lists
+ {\begincsname\??symbol#1:#2\endcsname}
+
+\protected\def\symb_fetch
+ {\ifconditional\c_symb_found
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\symb_fetch_indeed
+ \fi}
+
+\def\symb_fetch_indeed#1%
+ {\ifcsname\??symbol#1:\currentsymbol\endcsname
+ \symb_place_indeed{#1:\currentsymbol}%
+ \orelse\ifcsname\??symboldefault#1\endcsname
+ \symb_place_named{#1}%
+ \fi}
+
+\def\symb_place_named#1% \relax's prevent lookahead problems
+ {\begingroup
+ \setbox\scratchbox\hbox\bgroup
+ \the\everysymbol
+ \getglyphstyled
+ {\csname\??symboldefault#1\endcsname}%
+ {\tochar{n:\currentsymbol}}%
+ \relax
+ \egroup
+ \ifdim\wd\scratchbox>\zeropoint
+ \unhbox\scratchbox
+ \endgroup
+ \xdefcsname\??symbol#1:\currentsymbol\endcsname{\symb_place_named_indeed{#1}{\currentsymbol}}%
+ \settrue\c_symb_found
+ \else
+ \endgroup
+ \fi}
+
+\protected\def\symb_place_named_indeed#1#2% \relax's prevent lookahead problems
+ {\settrue\c_symb_found
+ \begingroup
+ \the\everysymbol
+ \getglyphstyled
+ {\csname\??symboldefault#1\endcsname}%
+ {\tochar{n:#2}}%
+ \relax
+ \endgroup}
+
+\def\symb_place_retry#1%
+ {\ifcsname\??symbol:#1\endcsname
+ \symb_place_indeed{:#1}%
+ \else
+ #1%
+ \fi}
+
+%D \macros
+%D {definefiguresymbol}
+%D
+%D To simplify defining figure symbols, we offer:
+%D
+%D \showsetup{definefiguresymbol}
+%D
+%D By default, such symbols scale along the current bodyfont size or running font
+%D size (which is better).
+
+\ifdefined\externalfigure \else \def\externalfigure[#1][#2]{#1} \fi
+
+\mutable\def\defaultsymbolfactor{10}
+\mutable\def\defaultsymbolheight{1.25ex}
+
+\permanent\tolerant\protected\def\figuresymbol[#1]#*[#2]%
+ {\externalfigure[#1][\c!reset=\v!yes,\c!symbol=\v!yes,\c!height=\defaultsymbolheight,#2]}
+
+\permanent\tolerant\protected\def\definefiguresymbol[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\else
+ \definesymbol[#1][{\figuresymbol[#2][#3]}]%
+ \fi}
+
+%D \macros
+%D {doifsymboldefinedelse}
+%D
+%D A handy private one:
+
+\def\symb_fetch_first
+ {\ifconditional\c_symb_found
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\symb_fetch_first_indeed
+ \fi}
+
+\def\symb_fetch_first_indeed#1%
+ {\doifinsymbolset{#1}\currentsymbol{\settrue\c_symb_found}}
+
+\def\symb_fetch_second#1%
+ {\doifinsymbolset\empty\currentsymbol{\settrue\c_symb_found}}
+
+\permanent\protected\def\doifelsesymboldefined#1%
+ {\begingroup
+ \edef\currentsymbol{#1}%
+ \let\symb_fetch\symb_fetch_first
+ \setfalse\c_symb_found
+ \the\t_symb_setups
+ \ifconditional\c_symb_found
+ \endgroup\expandafter\firstoftwoarguments
+ \else
+ \symb_fetch_second\currentsymbol
+ \ifconditional\c_symb_found
+ \endgroup\doubleexpandafter\firstoftwoarguments
+ \else
+ \endgroup\doubleexpandafter\secondoftwoarguments
+ \fi
+ \fi}
+
+\aliased\let\doifsymboldefinedelse\doifelsesymboldefined
+
+%D \macros
+%D {setupsymbolset,startsymbolset}
+%D
+%D From these macro definitions one can deduce that symbols can be grouped in
+%D symbol sets:
+%D
+%D \starttyping
+%D \startsymbolset [navigation 1]
+%D \definefiguresymbol [Next] [mp-symb.1]
+%D \definefiguresymbol [Prev] [mp-symb.2]
+%D \stopsymbolset
+%D \stoptyping
+%D
+%D Such a symbol can be typeset with:
+%D
+%D \starttyping
+%D \setupsymbolset[navigation 1]\symbol[Next]
+%D \stoptyping
+%D
+%D or simply:
+%D
+%D \starttyping
+%D \symbol[navigation 1][Next]
+%D \stoptyping
+%D
+%D Formally:
+%D
+%D \showsetup{setupsymbolset}
+%D \showsetup{startsymbolset}
+
+\installcorenamespace{symbolsets}
+
+\permanent\tolerant\protected\def\startsymbolset[#1]#*[#2]%
+ {\pushmacro\m_symb_current_set
+ \def\m_symb_current_set{#1}%
+ \ifarguments\or\or
+ \getdummyparameters[\s!font=,#2]%
+ \edef\p_font{\dummyparameter\s!font}%
+ \ifempty\p_font\else
+ \letcsname\??symboldefault#1\endcsname\p_font
+ \fi
+ \fi}
+
+\permanent\protected\def\stopsymbolset
+ {\popmacro\m_symb_current_set}
+
+\permanent\protected\def\setupsymbolset[#1]%
+ {\edef\currentsymbolset{#1}%
+ \ifcsname\??symbolsets\currentsymbolset\endcsname \else
+ \symb_setup_symbol_set
+ \fi}
+
+\def\symb_setup_symbol_set
+ {\normalexpanded{\t_symb_setups{\symb_fetch{\currentsymbolset}\the\t_symb_setups}}%
+ \letcsname\??symbolsets\currentsymbolset\endcsname\empty} % speedup
+
+\permanent\protected\def\resetsymbolset
+ {\t_symb_setups\emptytoks}
+
+\permanent\protected\def\forcesymbolset[#1]% still needed? why not just usesymbolset
+ {\normalexpanded{\t_symb_setups{\symb_fetch{#1}}}}
+
+%D \macros
+%D {showsymbolset}
+%D
+%D \showsetup{showsymbolset}
+
+\fetchmodulecommand \showsymbolset \f!symb_run
+
+%D \macros
+%D {usesymbols}
+%D
+%D \showsetup{usesymbols}
+
+\permanent\protected\def\usesymbols[#1]{\clf_usesymbols{#1}}
+
+%D As longs as symbols are linked to levels or numbers, we can also use the
+%D conversion mechanism, but in for instance the itemization macros, we prefer
+%D symbols because they can more easier be (partially) redefined.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-aux.lmt b/Master/texmf-dist/tex/context/base/mkxl/syst-aux.lmt
new file mode 100644
index 00000000000..726fd4d326a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-aux.lmt
@@ -0,0 +1,782 @@
+ if not modules then modules = { } end modules ['syst-aux'] = {
+ version = 1.001,
+ comment = "companion to syst-aux.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- slower than lpeg:
+--
+-- utfmatch(str,"(.?)(.*)$")
+-- utf.sub(str,1,1)
+
+local tonumber, next, type = tonumber, next, type
+local utfsub = utf.sub
+local P, S, R, C, Cc, Cs, Carg, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Carg, lpeg.match
+local find, formatters = string.find, string.formatters
+
+local context = context
+local implement = interfaces.implement
+local setmacro = interfaces.setmacro
+local setcatcode = tex.setcatcode
+local texget = tex.get
+local utf8character = lpeg.patterns.utf8character
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_set = utilities.parsers.settings_to_set
+
+local pattern = C(utf8character^-1) * C(P(1)^0)
+
+implement {
+ name = "getfirstcharacter",
+ arguments = "string",
+ actions = function(str)
+ local first, rest = lpegmatch(pattern,str)
+ setmacro("firstcharacter",first)
+ setmacro("remainingcharacters",rest)
+ end
+}
+
+implement {
+ name = "thefirstcharacter",
+ arguments = "string",
+ actions = function(str)
+ local first, rest = lpegmatch(pattern,str)
+ context(first)
+ end
+}
+
+implement {
+ name = "theremainingcharacters",
+ arguments = "string",
+ actions = function(str)
+ local first, rest = lpegmatch(pattern,str)
+ context(rest)
+ end
+}
+
+local pattern = C(utf8character^-1)
+local ctx_doifelse = commands.doifelse
+
+implement {
+ name = "doifelsefirstchar",
+ arguments = "2 strings",
+ actions = function(str,chr)
+ ctx_doifelse(lpegmatch(pattern,str) == chr)
+ end
+}
+
+implement {
+ name = "getsubstring",
+ arguments = "3 strings",
+ actions = function(str,first,last)
+ context(utfsub(str,tonumber(first),tonumber(last)))
+ end
+}
+
+-- function commands.addtocommalist(list,item)
+-- if list == "" then
+-- context(item)
+-- else
+-- context("%s,%s",list,item) -- using tex.print is some 10% faster
+-- end
+-- end
+--
+-- function commands.removefromcommalist(list,item)
+-- if list == "" then
+-- context(item)
+-- else
+-- -- okay, using a proper lpeg is probably faster
+-- -- we could also check for #l = 1
+-- local l = settings_to_array(list)
+-- local t, n = { }
+-- for i=1,#l do
+-- if l[i] ~= item then
+-- n = n + 1
+-- t[n] = item
+-- end
+-- end
+-- if n == 0 then
+-- context(item)
+-- else
+-- context(concat(list,","))
+-- end
+-- end
+-- end
+
+local pattern = (C((1-P("%"))^1) * Carg(1)) / function(n,d)
+ return formatters["%.0fsp"](d * tonumber(n)/100) end * P("%") * P(-1) -- .0 ?
+
+-- percentageof("10%",65536*10)
+
+implement {
+ name = "percentageof",
+ arguments = { "string", "dimen" },
+ actions = function(str,dim)
+ context(lpegmatch(pattern,str,1,dim) or str)
+ end
+}
+
+-- \gdef\setpercentdimen#1#2%
+-- {#1=\ctxcommand{percentageof("#2",\number#1)}\relax}
+
+local space = P(" ") / ""
+local spaces = P(" ")^0 / ""
+local nohash = 1 - P("#")
+local digit = R("09")
+local double = P("##") / "#"
+local single = P("#")
+local sentinel = spaces * (nohash^1 / "\\%0")
+local whatever = S("+-/*_^=:") + digit
+local sargument = (single * digit)^1
+local dargument = (double * digit)^1
+local swhatever = (single * whatever)^1
+local dwhatever = (double * whatever)^1
+
+-- different ones:
+
+local space = P(" ")
+local spaces = space^0
+
+-- see syst-aux.lua for historic variants
+
+local global = nil
+local protected = nil
+local permanent = nil
+local expanded = nil
+local mutable = nil
+local immutable = nil
+local optional = nil
+local tolerant = nil
+local instance = nil
+local frozen = nil
+local csname = nil
+local rest = nil
+
+local function catcodes_s()
+ setcatcode(32,10) -- space
+ setcatcode(13, 5) -- endofline
+end
+
+local function catcodes_n()
+ setcatcode(32, 9) -- ignore
+ setcatcode(13, 9) -- ignore
+end
+
+local function oldoption(s)
+ if optional > 1 then
+ optional = optional - 1
+ return s .. "#*"
+ else
+ return s
+ end
+end
+
+local option = (
+ P("single") * Cc(1)
+ + P("double") * Cc(2)
+ + P("triple") * Cc(3)
+ + P("quadruple") * Cc(4)
+ + P("quintuple") * Cc(5)
+ + P("sixtuple")
+ ) * (P("empty") + P("argument"))
+
+local pattern = (
+ (
+ spaces * (
+ ( P("spaces") * space / catcodes_s )
+ + ( P("nospaces") * space / catcodes_n )
+ + ( P("global") * space / function() global = true end)
+ + ( P("protected") * space / function() protected = true end)
+ + ( P("permanent") * space / function() permanent = true end)
+ + ( P("expanded") * space / function() expanded = true end)
+ + ( P("tolerant") * space / function() tolerant = true end)
+ + ( P("instance") * space / function() instance = true end)
+ + ( P("frozen") * space / function() frozen = true end)
+ + ( P("mutable") * space / function() mutable = true end)
+ + ( P("immutable") * space / function() immutable = true end)
+ + ( P("unexpanded") * space / function() protected = true end)
+ + ( option * space / function(s) tolerant = true
+ optional = s end)
+ )
+ )^0
+ * spaces * ( C((1-S(" #["))^1) )
+ * spaces * Cs(
+ (Cs(P("[") * dargument * P("]")) / oldoption + dwhatever)^1 * sentinel^-1 * double^-1
+ + (Cs(P("[") * sargument * P("]")) / oldoption + swhatever)^1 * sentinel^-1 * single^-1
+ + sentinel^-1 * (double+single)^-1
+ )
+)
+
+local ctx_dostarttexdefinition = context.dostarttexdefinition
+
+local function texdefinition_one(str)
+ global = false
+ protected = false
+ permanent = false
+ expanded = false
+ mutable = false
+ immutable = false
+ optional = 0
+ tolerant = false
+ instance = false
+ frozen = false
+ csname, rest = lpegmatch(pattern,str)
+ ctx_dostarttexdefinition()
+end
+
+local function texdefinition_two()
+ context (
+ (tolerant and [[\tolerant]] or "") ..
+ (frozen and [[\frozen]] or "") ..
+ (protected and [[\protected]] or "") ..
+ (permanent and [[\permanent]] or "") ..
+ (instance and [[\instance]] or "") ..
+ (mutable and [[\mutable]] or "") ..
+ (immutable and [[\immutable]] or "") ..
+-- [[\expandafter]] .. (global and (expanded and [[\xdef]] or [[\gdef]]) or (expanded and [[\edef]] or [[\def]])) ..
+-- [[\csname ]] .. csname .. [[\endcsname ]] ..
+ (global and (expanded and [[\xdefcsname ]] or [[\gdefcsname ]]) or (expanded and [[\edefcsname ]] or [[\defcsname ]])) ..
+ csname .. [[\endcsname ]] ..
+ rest
+ )
+end
+
+implement { name = "texdefinition_one", actions = texdefinition_one, scope = "private", arguments = "string" }
+implement { name = "texdefinition_two", actions = texdefinition_two, scope = "private" }
+
+do
+
+ -- Quite probably we don't yet have characters loaded so we delay some
+ -- aliases.
+
+ local _lower_, _upper_, _strip_
+
+ _lower_ = function(s)
+ if characters and characters.lower then
+ _lower_ = characters.lower
+ return _lower_(s)
+ end
+ return string.lower(s)
+ end
+
+ _upper_ = function(s)
+ if characters and characters.upper then
+ _upper_ = characters.upper
+ return _upper_(s)
+ end
+ return string.upper(s)
+ end
+
+ _strip_ = function(s)
+ -- or utf.strip
+ if string.strip then
+ _strip_ = string.strip
+ return _strip_(s)
+ end
+ return s
+ end
+
+ local function lower(s) context(_lower_(s)) end
+ local function upper(s) context(_upper_(s)) end
+ local function strip(s) context(_strip_(s)) end
+
+ implement { name = "upper", arguments = "string", actions = upper }
+ implement { name = "lower", arguments = "string", actions = lower }
+ implement { name = "strip", arguments = "string", actions = strip }
+
+end
+
+implement {
+ name = "converteddimen",
+ arguments = { "dimen", "string" },
+ actions = function(dimen,unit)
+ context(number.todimen(dimen,unit or "pt","%0.5f")) -- no unit appended (%F)
+ end
+}
+
+-- where, not really the best spot for this:
+
+implement {
+ name = "immediatemessage",
+ public = true,
+ arguments = { "'message'", "string" },
+ actions = logs.status
+}
+
+implement {
+ name = "writestring",
+ public = true,
+ protected = true,
+ arguments = "string",
+ actions = function (s)
+ logs.writer(s,"\n")
+ end,
+}
+
+implement {
+ name = "writeline",
+ public = true,
+ protected = true,
+ actions = logs.newline,
+}
+
+implement {
+ name = "resettimer",
+ actions = function()
+ statistics.resettiming("whatever")
+ statistics.starttiming("whatever")
+ end
+}
+
+implement {
+ name = "benchmarktimer",
+ actions = function()
+ statistics.benchmarktimer("whatever")
+ end
+}
+
+implement {
+ name = "elapsedtime",
+ actions = function()
+ statistics.stoptiming("whatever")
+ context(statistics.elapsedtime("whatever"))
+ end
+}
+
+implement {
+ name = "elapsedsteptime",
+ arguments = "integer",
+ actions = function(n)
+ statistics.stoptiming("whatever")
+ local t = statistics.elapsed("whatever")/(n > 0 and n or 1)
+ if t > 0 then
+ context("%0.9f",t)
+ else
+ context(0)
+ end
+ end
+}
+
+local accuracy = table.setmetatableindex(function(t,k)
+ local v = formatters["%0." ..k .. "f"]
+ t[k] = v
+ return v
+end)
+
+implement {
+ name = "rounded",
+ arguments = "integer",
+ actions = function(n,m) context(accuracy[n](m)) end
+}
+
+-- not faster but just less tracing:
+
+local ctx_protected_cs = context.protected.cs -- more efficient
+
+local ctx_firstoftwoarguments = ctx_protected_cs.firstoftwoarguments
+local ctx_secondoftwoarguments = ctx_protected_cs.secondoftwoarguments
+local ctx_firstofoneargument = ctx_protected_cs.firstofoneargument
+local ctx_gobbleoneargument = ctx_protected_cs.gobbleoneargument
+
+context.firstoftwoarguments = ctx_firstoftwoarguments
+context.secondoftwoarguments = ctx_secondoftwoarguments
+context.firstofoneargument = ctx_firstofoneargument
+context.gobbleoneargument = ctx_gobbleoneargument
+
+local ctx_iftrue = context.iftrue
+local ctx_iffalse = context.iffalse
+
+local hash = utilities.parsers.hashes.settings_to_set
+
+local function doifelsecommon(a,b)
+ if a == b then
+ setmacro("commalistelement",a)
+ if a == "" then
+ ctx_secondoftwoarguments()
+ else
+ ctx_firstoftwoarguments()
+ end
+ return
+ end
+ local ba = find(a,",",1,true)
+ local bb = find(b,",",1,true)
+ if ba and bb then
+ local ha = hash[a]
+ local hb = hash[b]
+ -- local ha = settings_to_set(a)
+ -- local hb = settings_to_set(b)
+ for k in next, ha do
+ if hb[k] then
+ setmacro("commalistelement",k)
+ ctx_firstoftwoarguments()
+ return
+ end
+ end
+ elseif ba then
+ if hash[a][b] then
+ -- if settings_to_set(a)[b] then
+ setmacro("commalistelement",b)
+ ctx_firstoftwoarguments()
+ return
+ end
+ elseif bb then
+ if hash[b][a] then
+ -- if settings_to_set(b)[a] then
+ setmacro("commalistelement",a)
+ ctx_firstoftwoarguments()
+ return
+ end
+ end
+ setmacro("commalistelement","")
+ ctx_secondoftwoarguments()
+end
+
+local function doifcommon(a,b)
+ if a == b then
+ setmacro("commalistelement",a)
+ if a == "" then
+ ctx_gobbleoneargument()
+ else
+ ctx_firstofoneargument()
+ end
+ return
+ end
+ local ba = find(a,",",1,true)
+ local bb = find(b,",",1,true)
+ if ba and bb then
+ local ha = hash[a]
+ local hb = hash[b]
+ -- local ha = settings_to_set(a)
+ -- local hb = settings_to_set(b)
+ for k in next, ha do
+ if hb[k] then
+ setmacro("commalistelement",k)
+ ctx_firstofoneargument()
+ return
+ end
+ end
+ elseif ba then
+ if hash[a][b] then
+ -- if settings_to_set(a)[b] then
+ setmacro("commalistelement",b)
+ ctx_firstofoneargument()
+ return
+ end
+ elseif bb then
+ if hash[b][a] then
+ -- if settings_to_set(b)[a] then
+ setmacro("commalistelement",a)
+ ctx_firstofoneargument()
+ return
+ end
+ end
+ setmacro("commalistelement","")
+ ctx_gobbleoneargument()
+end
+
+local function doifnotcommon(a,b)
+ if a == b then
+ setmacro("commalistelement",a)
+ if a == "" then
+ ctx_firstofoneargument()
+ else
+ ctx_gobbleoneargument()
+ end
+ return
+ end
+ local ba = find(a,",",1,true)
+ local bb = find(b,",",1,true)
+ if ba and bb then
+ local ha = hash[a]
+ local hb = hash[b]
+ -- local ha = settings_to_set(a)
+ -- local hb = settings_to_set(b)
+ for k in next, ha do
+ if hb[k] then
+ setmacro("commalistelement",k)
+ ctx_gobbleoneargument()
+ return
+ end
+ end
+ elseif ba then
+ if hash[a][b] then
+ -- if settings_to_set(a)[b] then
+ setmacro("commalistelement",b)
+ ctx_gobbleoneargument()
+ return
+ end
+ elseif bb then
+ if hash[b][a] then
+ -- if settings_to_set(b)[a] then
+ setmacro("commalistelement",a)
+ ctx_gobbleoneargument()
+ return
+ end
+ end
+ setmacro("commalistelement","")
+ ctx_firstofoneargument()
+end
+
+-- local function hascommonargumentcondition(a,b)
+-- if a == b then
+-- setmacro("commalistelement",a)
+-- if a == "" then
+-- ctx_iffalse()
+-- else
+-- ctx_iftrue()
+-- end
+-- return
+-- end
+-- local ba = find(a,",",1,true)
+-- local bb = find(b,",",1,true)
+-- if ba and bb then
+-- local ha = hash[a]
+-- local hb = hash[b]
+-- for k in next, ha do
+-- if hb[k] then
+-- setmacro("commalistelement",k)
+-- ctx_iftrue()
+-- return
+-- end
+-- end
+-- elseif ba then
+-- if hash[a][b] then
+-- setmacro("commalistelement",b)
+-- ctx_iftrue()
+-- return
+-- end
+-- elseif bb then
+-- if hash[b][a] then
+-- setmacro("commalistelement",a)
+-- ctx_iftrue()
+-- return
+-- end
+-- end
+-- setmacro("commalistelement","")
+-- ctx_iffalse()
+-- end
+
+local function doifelseinset(a,b)
+ if a == b then
+ setmacro("commalistelement",a)
+ if a == "" then
+ ctx_secondoftwoarguments()
+ else
+ ctx_firstoftwoarguments()
+ end
+ return
+ end
+ local bb = find(b,",",1,true)
+ if bb then
+ if hash[b][a] then
+ -- if settings_to_set(b)[a] then
+ setmacro("commalistelement",a)
+ ctx_firstoftwoarguments()
+ return
+ end
+ end
+ setmacro("commalistelement","")
+ ctx_secondoftwoarguments()
+end
+
+local function doifinset(a,b)
+ if a == b then
+ setmacro("commalistelement",a)
+ if a == "" then
+ ctx_gobbleoneargument()
+ else
+ ctx_firstofoneargument()
+ end
+ return
+ end
+ local bb = find(b,",",1,true)
+ if bb then
+ if hash[b][a] then
+ -- if settings_to_set(b)[a] then
+ setmacro("commalistelement",a)
+ ctx_firstofoneargument()
+ return
+ end
+ end
+ setmacro("commalistelement","")
+ ctx_gobbleoneargument()
+end
+
+local function doifnotinset(a,b)
+ if a == b then
+ setmacro("commalistelement",a)
+ if a == "" then
+ ctx_firstofoneargument()
+ else
+ ctx_gobbleoneargument()
+ end
+ return
+ end
+ local bb = find(b,",",1,true)
+ if bb then
+ if hash[b][a] then
+ -- if settings_to_set(b)[a] then
+ setmacro("commalistelement",a)
+ ctx_gobbleoneargument()
+ return
+ end
+ end
+ setmacro("commalistelement","")
+ ctx_firstofoneargument()
+end
+
+implement {
+ name = "doifelsecommon",
+ actions = doifelsecommon,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "doifcommon",
+ actions = doifcommon,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "doifnotcommon",
+ actions = doifnotcommon,
+ arguments = "2 strings",
+}
+
+-- implement {
+-- name = "hascommonargumentcondition",
+-- actions = hascommonargumentcondition,
+-- arguments = "2 strings",
+-- arguments = { "argument", "argument" },
+-- }
+
+implement {
+ name = "doifelseinset",
+ actions = doifelseinset,
+ arguments = "2 strings",
+-- arguments = { "argument", "argument" },
+}
+
+implement {
+ name = "doifinset",
+ actions = doifinset,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "doifnotinset",
+ actions = doifnotinset,
+ arguments = "2 strings",
+}
+
+-- done elsewhere:
+--
+-- local function firstinset(a)
+-- local aa = hash[a]
+-- context(aa and aa[1] or a)
+-- end
+--
+-- implement {
+-- name = "firstinset",
+-- actions = firstinset,
+-- arguments = "string",
+-- private = false,
+-- }
+
+-- implement {
+-- name = "stringcompare",
+-- arguments = "2 strings",
+-- actions = function(a,b)
+-- context((a == b and 0) or (a > b and 1) or -1)
+-- end
+-- }
+--
+-- implement {
+-- name = "doifelsestringafter",
+-- arguments = "2 strings",
+-- actions = function(a,b)
+-- ctx_doifelse((a == b and 0) or (a > b and 1) or -1)
+-- end
+-- }
+--
+-- implement {
+-- name = "doifelsestringbefore",
+-- arguments = "2 strings",
+-- actions = function(a,b)
+-- ctx_doifelse((a == b and 0) or (a < b and -1) or 1)
+-- end
+-- }
+
+-- implement { -- not faster than addtocommalist
+-- name = "additemtolist", -- unique
+-- arguments = "2 strings",
+-- actions = function(l,s)
+-- if l == "" or s == l then
+-- -- s = s
+-- elseif find("," .. l .. ",","," .. s .. ",") then
+-- s = l
+-- else
+-- s = l .. "," .. s
+-- end
+-- context(s)
+-- end
+-- }
+
+local bp = number.dimenfactors.bp
+
+implement {
+ name = "tobigpoints",
+ actions = function(d) context("%.5F",bp * d) end,
+ arguments = "dimension",
+}
+
+implement {
+ name = "towholebigpoints",
+ actions = function(d) context("%r",bp * d) end,
+ arguments = "dimension",
+}
+
+-- for now here:
+
+local function getshape(s)
+ local t = texget(s)
+ local n = t and #t or 0
+ context(n)
+ if n > 0 then
+ for i=1,n do
+ local ti = t[i]
+ if type(ti) == "table" then
+ context(" %isp %isp",ti[1],ti[2])
+ else
+ context(" %i",ti)
+ end
+ end
+ end
+end
+
+implement {
+ name = "getparshape",
+ public = true,
+ actions = function() getshape("parshape") end,
+}
+implement {
+ name = "getclubpenalties",
+ public = true,
+ actions = function() getshape("clubpenalties") end,
+}
+implement {
+ name = "getinterlinepenalties",
+ public = true,
+ actions = function() getshape("interlinepenalties") end,
+ }
+implement {
+ name = "getdisplaywidowpenalties",
+ public = true,
+ actions = function() getshape("displaywidowpenalties") end,
+}
+implement {
+ name = "getwidowpenalties",
+ public = true,
+ actions = function() getshape("widowpenalties") end,
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-aux.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-aux.mkxl
new file mode 100644
index 00000000000..e4cecfe58e2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-aux.mkxl
@@ -0,0 +1,6671 @@
+%D \module
+%D [ file=syst-aux, % merge of syst-gen cum suis
+%D version=1996.03.20,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=General,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D In the process of being adapted to \LMTX\ flags so a bit of a mess now. Also
+%D our playground.
+
+\registerctxluafile{syst-aux}{autosuffix}
+\registerctxluafile{syst-mac}{autosuffix}
+
+%D This file is a follow up in \type {syst-aux.mkii} and \type {syst-aux.mkiv} where
+%D you can find some more pure \TEX\ or \LUATEX\ variants. There are quite some
+%D helpers here and many no longer are used but we keep them around because they
+%D have always been there. However, the implementation in \LMTX\ can be somewhat
+%D different that in \MKIV, because it's also a kind of a playground for new
+%D features. Of course nostalgia also plays a role.
+%D
+%D For the original code we refer to \type {syst-aux.mkii} and its follow up \type
+%D {syst-aux.mkiv}. These also have historic versions. As usual, the upgrade went
+%D through stages. First I wrote variants usign the new \type {\ignorearguments}
+%D feature, then I decided to implement a few more conditionals and new fancy code
+%D could be ditched before it was even published. I kept some as examples.
+%D
+%D Of course I couldn't do this without Wolfgang looking over my shoulder. Changing
+%D core code is kind of tricky.
+
+\unprotect
+
+%D These are for local helpers, the names are historic:
+
+\mutable\let\docommand \relax
+\mutable\let\dodocommand\relax
+
+%D \macros
+%D {unexpanded}
+%D
+%D Because we use this module only in \MKIV, we have removed the old protection
+%D code.
+%D
+%D \starttyping
+%D \protected\def\somecommand{... ... ...}
+%D \stoptyping
+%D
+%D This overloads the \ETEX\ primitive but as we already had an \MKII\ solution we
+%D keep the same name for a similar mechanism. So, effectively we have two ways to
+%D protect a macro.
+%D
+%D In \LMTX\ we try to avoid this and use \type {\protected} instead, which is
+%D possible because over time we got rid of using the \CONTEXT\ macro with that
+%D name.
+
+\pushoverloadmode
+
+\aliased\let\unexpanded\normalprotected
+
+\popoverloadmode
+
+%D We're definitely in \LMTX\ mode here.
+
+\aliased \let\startlmtxmode\relax
+\aliased \let\stoplmtxmode \relax
+\permanent\def\startmkivmode#-\stopmkivmode{}
+\permanent\let\stopmkivmode \relax
+
+%D As we don't have namespace definers yet, we use a special one. Later we will
+%D do a better job.
+
+\ifdefined\c_syst_helpers_n_of_namespaces
+
+ % lets plug in a better error message
+
+\else
+
+ \newcount\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes
+
+ \def\v_interfaces_prefix_template_system{\number \c_syst_helpers_n_of_namespaces>>}
+ %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet
+
+\fi
+
+\permanent\protected\def\installsystemnamespace#1% maybe move this to syst-ini
+ {\ifcsname ??#1\endcsname
+ \writestatus\m!system{duplicate system namespace '#1'}\wait
+ \else
+ \global\advance\c_syst_helpers_n_of_namespaces\plusone
+ \immutable\edefcsname ??#1\endcsname{\v_interfaces_prefix_template_system}%
+ \fi}
+
+%D \macros
+%D {normalspace}
+%D
+%D There is already \type{\space} but just to be sure we also provide this one:
+
+\permanent\def\normalspace{ }
+
+\newif\if!!donea \newif\if!!doneb \newif\if!!donec % soon obsolete in lmtx
+\newif\if!!doned \newif\if!!donee \newif\if!!donef % soon obsolete in lmtx
+
+\immutable\def\!!zerocount {0} % alongside \zerocount
+\immutable\def\!!minusone {-1} % ...
+\immutable\def\!!plusone {1} % ...
+\immutable\def\!!plustwo {2} % ...
+\immutable\def\!!plusthree {3} % ...
+\immutable\def\!!plusfour {4} % ...
+\immutable\def\!!plusfive {5} % ...
+\immutable\def\!!plussix {6} % ...
+\immutable\def\!!plusseven {7} % ...
+\immutable\def\!!pluseight {8} % ...
+\immutable\def\!!plusnine {9} % alongside \plusnine
+
+\setnewconstant \uprotationangle 0
+\setnewconstant\rightrotationangle 90
+\setnewconstant \downrotationangle 180
+\setnewconstant \leftrotationangle 270
+
+%D \macros
+%D {s!,c!,e!,p!,v!,@@,??}
+%D
+%D To save memory, we use constants (sometimes called variables). Redefining these
+%D constants can have disastrous results. Of course, expanding them costs time, so
+%D that's the price to pay. More are defined elsewhere. Of course we later pay a
+%D price when they need to be expanded.
+
+% \def\v!prefix! {v!}
+% \def\c!prefix! {c!}
+% \def\s!prefix! {s!}
+%
+% \def\s!next {next}
+% \def\s!default {default}
+% \def\s!dummy {dummy}
+% \def\s!unknown {unknown}
+%
+% \def\s!do {do}
+% \def\s!dodo {dodo}
+%
+% \def\s!complex {complex}
+% \def\s!simple {simple}
+%
+% \def\s!start{start}
+% \def\s!stop {stop}
+
+% \immutable\def\s!empty {empty}
+
+%D Sometimes we pass macros as arguments to commands that don't expand them before
+%D interpretation. Such commands can be enclosed with \type {\expanded}, like:
+%D
+%D \starttyping
+%D \expanded{\setupsomething[\alfa]}
+%D \stoptyping
+%D
+%D Such situations occur for instance when \type {\alfa} is a commalist or when data
+%D stored in macros is fed to index of list commands. If needed, one should use
+%D \type {\noexpand} inside the argument. Later on we will meet some more clever
+%D alternatives to this command. Beware, only the simple one has \type {\noexpand}
+%D before its argument.
+
+\let\m_syst_helpers_expanded\empty
+
+\pushoverloadmode
+
+\permanent\protected\def\expanded#1%
+ {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded}
+
+\popoverloadmode
+
+\permanent\protected\def\startexpanded#1\stopexpanded
+ {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded}
+
+\aliased\let\stopexpanded\relax
+
+%D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when
+%D possible. After all, we can make not expandable macros now. The name clash is an
+%D unfortunate fact and in retrospect I should nto have agreed to a primitive having
+%D that same name.
+
+% We cannot use the next variant as first we need to adapt \type {##}'s in callers:
+%
+% \def\expanded#1%
+% {\normalexpanded{\noexpand#1}}
+%
+% \def\startexpanded#1\stopexpanded
+% {\normalexpanded{#1}}
+
+%D \macros
+%D {gobbleoneargument,gobble...arguments}
+%D
+%D The next set of macros just do nothing, except that they get rid of a number of
+%D arguments. These zero references prevent intermediate storage. In principle that
+%D is more efficient but it probably won't be noticed. It's anyway cheaper on memory
+%D access. We use versions that don't even store the arguments.
+
+%permanent\def\gobbleoneargument #-{}
+\permanent\def\gobbletwoarguments #-#-{}
+\permanent\def\gobblethreearguments #-#-#-{}
+\permanent\def\gobblefourarguments #-#-#-#-{}
+\permanent\def\gobblefivearguments #-#-#-#-{}
+\permanent\def\gobblesixarguments #-#-#-#-{}
+\permanent\def\gobblesevenarguments #-#-#-#-#-{}
+\permanent\def\gobbleeightarguments #-#-#-#-#-#-{}
+\permanent\def\gobbleninearguments #-#-#-#-#-#-#-{}
+
+\permanent\tolerant\def\gobbleoneoptional [#-]{}
+\permanent\tolerant\def\gobbletwooptionals [#-]#*[#-]{}
+\permanent\tolerant\def\gobblethreeoptionals[#-]#*[#-]#*[#-]{}
+\permanent\tolerant\def\gobblefouroptionals [#-]#*[#-]#*[#-]#*[#-]{}
+\permanent\tolerant\def\gobblefiveoptionals [#-]#*[#-]#*[#-]#*[#-]#*[#-]{}
+
+%D Reserved macros for tests:
+
+\aliased\let\donothing\empty
+
+\let\m_syst_string_one \empty
+\let\m_syst_string_two \empty
+\let\m_syst_string_three\empty
+\let\m_syst_string_four \empty
+
+\let\m_syst_action_yes \empty
+\let\m_syst_action_nop \empty
+
+%D \macros
+%D {doifnextcharelse}
+%D
+%D This macro has a long history. Among the things we had to deal with was ignoring
+%D blank spaces but in the meantime we can use some \LUATEX\ trickery. Older versions
+%D use more code and can be find in the \MKIV\ and \MKII\ files.
+
+% \mutable\let\next \relax
+% \mutable\let\nextnext \relax % kind of obsolete
+% \mutable\let\nextnextnext \relax % kind of obsolete
+% \mutable\let\nexttoken \relax
+
+\permanent\protected\def\doifelsenextchar#1#2#3% #1 should not be {} !
+ {\def\m_syst_action_yes{#2}%
+ \def\m_syst_action_nop{#3}%
+ \futureexpandis#1\m_syst_action_yes\m_syst_action_nop}
+
+\permanent\protected\def\doifelsenextcharcs % #1#2#3% #1 should not be {} !
+ {\futureexpandis}
+
+\aliased\let\doifnextcharelse \doifelsenextchar
+\aliased\let\doifnextcharcselse\doifelsenextcharcs
+
+%D Because we will mostly use this macro for testing if the next character is \type
+%D {[}, we also make a slightly faster variant as it is not uncommon to have tens of
+%D thousands of calls to this test in a run. Of course it also is more convenient to
+%D read a trace then. Here we use a lookahead primitive that ignores (blank) spaces
+%D which makes for less tracing clutter than the original definition. It's also
+%D faster bit that will probably go unnoticed. Of course, hard||core \TEX ies whose
+%D reputations depends on understanding obscure macro definitions will love the more
+%D low level variants.
+
+\permanent\protected\def\doifelsenextoptional#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \futureexpandis[\m_syst_action_yes\m_syst_action_nop}
+
+\permanent\protected\def\doifelsenextoptionalcs
+ {\futureexpandis[}
+
+\aliased\let\doifnextoptionalelse \doifelsenextoptional
+\aliased\let\doifnextoptionalcselse\doifelsenextoptionalcs
+
+\permanent\protected\def\doifelsenextbgroup#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \futureexpandis\bgroup\m_syst_action_yes\m_syst_action_nop}
+
+\permanent\protected\def\doifelsenextbgroupcs % #1#2
+ {\futureexpandis\bgroup}
+
+\aliased\let\doifnextbgroupelse \doifelsenextbgroup
+\aliased\let\doifnextbgroupcselse\doifelsenextbgroupcs
+
+\permanent\protected\def\doifelsenextparenthesis#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \futureexpandis(\m_syst_action_yes\m_syst_action_nop}
+
+\aliased\let\doifnextparenthesiselse\doifelsenextparenthesis
+
+%D The next one is handy in predictable situations:
+
+\permanent\protected\def\doifelsefastoptionalcheck#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \futureexpandis[\m_syst_action_yes\m_syst_action_nop}
+
+\permanent\protected\def\doifelsefastoptionalcheckcs
+ {\futureexpandis[}
+
+\aliased\let\doiffastoptionalcheckelse \doifelsefastoptionalcheck
+\aliased\let\doiffastoptionalcheckcselse\doifelsefastoptionalcheckcs
+
+%D Here's one for skipping spaces and pars, handy for:
+%D
+%D \starttyping
+%D \hbox
+%D
+%D {a few lines later}
+%D \stoptyping
+%D
+%D like:
+%D
+%D \starttyping
+%D \def\somecommand{\dowithnextbox{\box\nextbox}\ruledhbox}
+%D
+%D \assumelongusagecs\somecommand
+%D
+%D \bgroup
+%D oeps
+%D \egroup
+%D \stoptyping
+%D
+%D The original kind of clumsy but working version is now replaced by a simple
+%D macro. And it can be even less code in \LUAMETATEX:
+
+\aliased\let\assumelongusagecs\expandafterpars % so we can replace it
+
+%D It's used to skip over empty lines in some constructs that we like to set up
+%D spacy. We already permit par tokens (and equivalents) in math and some other
+%D places visa engine features. And, \type {\long} stuff has been dropped for a long
+%D time already (it is still optional in \lUATEX). But the \quote {long} in the
+%D name kind of stuck.
+
+%D \macros
+%D {blankspace}
+%D
+%D Here is how this works. The \type {\let} primitive first picks up the to be let
+%D name. Then it scans for an optional equal and when that is found it will skip the
+%D next space, which is why we need an extra one to achieve our goal. Such a \type
+%D {\blankspace} has the meaning \typ {blank space}. A typical \TEX ie definition:
+
+\normalexpanded{\permanent\let\noexpand\blankspace=\space\space}
+
+%D \macros
+%D {setvalue,setgvalue,setevalue,setxvalue,
+%D letvalue,letgvalue,getvalue,resetvalue,
+%D undefinevalue,ignorevalue}
+%D
+%D \TEX's primitive \type {\csname} can be used to construct all kind of commands
+%D that cannot be defined with \type {\def} and \type {\let}. Every macro programmer
+%D sooner or later wants macros like these.
+%D
+%D \starttyping
+%D \setvalue {name}{...} = \def\name{...}
+%D \setgvalue {name}{...} = \gdef\name{...}
+%D \setevalue {name}{...} = \edef\name{...}
+%D \setxvalue {name}{...} = \xdef\name{...}
+%D \letvalue {name}=\... = \let\name=\...
+%D \letgvalue {name}=\... = \glet\name=\...
+%D \getvalue {name} = \name
+%D \resetvalue {name} = \def\name{}
+%D \stoptyping
+%D
+%D As we will see, \CONTEXT\ uses these commands many times, which is mainly due to
+%D its object oriented and parameter driven character.
+
+\permanent\def\setvalue #1{\defcsname #1\endcsname}
+\permanent\def\setgvalue #1{\gdefcsname#1\endcsname}
+\permanent\def\setevalue #1{\edefcsname#1\endcsname}
+\permanent\def\setxvalue #1{\xdefcsname#1\endcsname}
+\permanent\def\getvalue #1{\csname#1\endcsname} % maybe: \begincsname#1\endcsname
+\permanent\def\letvalue #1{\letcsname #1\endcsname}
+\permanent\def\letgvalue #1{\gletcsname#1\endcsname}
+\permanent\def\resetvalue #1{\letcsname#1\endcsname\empty}
+\permanent\def\undefinevalue#1{\letcsname#1\endcsname\undefined}
+\permanent\def\ignorevalue#1#2{\letcsname#1\endcsname\empty}
+
+\permanent\def\setuvalue #1{\protected\defcsname #1\endcsname}
+\permanent\def\setuevalue #1{\protected\edefcsname#1\endcsname}
+\permanent\def\setugvalue #1{\protected\gdefcsname#1\endcsname}
+\permanent\def\setuxvalue #1{\protected\xdefcsname#1\endcsname}
+
+\permanent\protected\def\getuvalue#1{\csname#1\endcsname}
+
+%D \macros
+%D {globallet,glet}
+%D
+%D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two tokens will
+%D save us some $300\times4=1200$ bytes of format file on a 32~bit system. Not that
+%D it matters much today. But nowadays we can just alias to a primitive:
+
+\aliased\let\globallet\glet
+
+%D \macros
+%D {doifundefined,doifdefined,
+%D doifundefinedelse,doifdefinedelse,
+%D doifalldefinedelse}
+%D
+%D The standard way of testing if a macro is defined is comparing its meaning with
+%D another undefined one, usually \type {\undefined}. To garantee correct working of
+%D the next set of macros, \type {\undefined} may never be defined!
+%D
+%D \starttyping
+%D \doifundefined {string} {...}
+%D \doifdefined {string} {...}
+%D \doifundefinedelse {string} {then ...} {else ...}
+%D \doifdefinedelse {string} {then ...} {else ...}
+%D \doifalldefinedelse {commalist} {then ...} {else ...}
+%D \stoptyping
+%D
+%D Every macroname that \TEX\ builds gets an entry in the hash table, which is of
+%D limited size. It is expected that \ETEX\ will offer a less memory||consuming
+%D alternative.
+%D
+%D Although it will probably never be a big problem, it is good to be aware of the
+%D difference between testing on a macro name to be build by using \type {\csname} and
+%D \type {\endcsname} and testing the \type {\name} directly.
+%D
+%D \starttyping
+%D \expandafter\ifrelax\csname NameA\endcsname ... \else ... \fi
+%D
+%D \ifundefined\NameB ... \else ... \fi
+%D \stoptyping
+%D
+%D Suppression of errors while constructing a control sequence is one of the (few)
+%D things that I remember being discussed in the perspective of \ETEX\ but it was
+%D rejected because it was not considered useful. Well, in \LUATEX\ we can surpress
+%D it and that is what we do in \MKIV. We're probably the only macro package that
+%D needs it. That kind of configuration happens elsewhere. These macros are (mostly
+%D for historic reasons) fully expandable.
+
+\permanent\def\doifelseundefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments
+ \fi}
+
+\permanent\def\doifelsedefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\def\doifundefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\gobbleoneargument\else\expandafter\firstofoneargument
+ \fi}
+
+\permanent\def\doifdefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\firstofoneargument\else\expandafter\gobbleoneargument
+ \fi}
+
+\aliased\let\doifundefinedelse\doifelseundefined
+\aliased\let\doifdefinedelse \doifelsedefined
+
+%D \macros
+%D {letbeundefined}
+%D
+%D Testing for being undefined comes down to testing on \type {\relax} when we use
+%D \type {\csname}, but when using \type {\ifx}, we test on being \type
+%D {\undefined}! In \ETEX\ we have \type {\ifcsname} and that way of testing on
+%D existance is not the same as the one described here. Therefore we introduce:
+
+\permanent\protected\def\letbeundefined#1%
+ {\letcsname#1\endcsname\undefined}
+
+\permanent\protected\def\localundefine#1% conditional
+ {\ifcsname#1\endcsname\letcsname#1\endcsname\undefined\fi}
+
+\permanent\protected\def\globalundefine#1% conditional
+ {\ifcsname#1\endcsname\gletcsname#1\endcsname\undefined\fi}
+
+%D Beware, being \type {\undefined} in \ETEX\ means that the macro {\em is} defined!
+%D
+%D When we were developing the scientific units module, we encountered different
+%D behavior in text and math mode, which was due to this grouping subtilities. We
+%D therefore decided to use \type {\begingroup} instead of \type {\bgroup}.
+
+\permanent\protected\def\doifelsealldefined#1%
+ {\begingroup
+ \donetrue % we could use a reserved one and avoid the group
+ \processcommalist[#1]\syst_helpers_do_if_all_defined_else
+ \ifdone
+ \endgroup\expandafter\firstoftwoarguments
+ \else
+ \endgroup\expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifalldefinedelse\doifelsealldefined
+
+\def\syst_helpers_do_if_all_defined_else#1%
+ {\ifcsname#1\endcsname\else
+ \donefalse
+ \expandafter\quitcommalist % added
+ \fi}
+
+%D \macros
+%D {doif,doifelse,doifnot}
+%D
+%D Programming in \TEX\ differs from programming in procedural languages like
+%D \MODULA. This means that one --- well, let me speek for myself --- tries to do
+%D the things in the well known way. Therefore the next set of \type {ifthenelse}
+%D commands were between the first ones we needed. A few years later, the opposite
+%D became true: when programming in \MODULA, I sometimes miss handy things like
+%D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to
+%D structure, \TEX\ taught me to think recursive.
+%D
+%D \starttyping
+%D \doif {string1} {string2} {...}
+%D \doifnot {string1} {string2} {...}
+%D \doifelse {string1} {string2} {then ...}{else ...}
+%D \stoptyping
+%D
+%D Again, we use some of the new primitives in \LUAMETATEX. Using straightforward
+%D \type {\edef}'s and \type {\ifx} comparison works as well, but this saves tokens
+%D and, more important, tracing clutter.
+
+\permanent\protected\def\doifelse#1#2%
+ {\iftok{#1}{#2}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doif#1#2%
+ {\iftok{#1}{#2}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnot#1#2%
+ {\iftok{#1}{#2}%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D \macros
+%D {doifempty,doifemptyelse,doifnotempty}
+%D
+%D We complete our set of conditionals with:
+%D
+%D \starttyping
+%D \doifempty {string} {...}
+%D \doifnotempty {string} {...}
+%D \doifemptyelse {string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D This time, the string is not expanded, but we use the dedicated empty checker
+%D here.
+
+\permanent\protected\def\doifelseempty#1%
+ {\def\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifemptyelse\doifelseempty
+
+\permanent\protected\def\doifempty#1%
+ {\def\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotempty#1%
+ {\def\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D \macros
+%D {doifinset,doifnotinset,doifinsetelse}
+%D
+%D We can check if a string is present in a comma separated set of strings.
+%D Depending on the result, some action is taken.
+%D
+%D \starttyping
+%D \doifinset {string} {string,...} {...}
+%D \doifnotinset {string} {string,...} {...}
+%D \doifinsetelse {string} {string,...} {then ...} {else ...}
+%D \stoptyping
+
+% !0nop=\doifinsetelse{ccc}{,}{yes}{nop}
+% !0nop=\doifinsetelse{ccc}{,,}{yes}{nop}
+% !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop}
+
+% !1nop=\doifinsetelse{}{}{yes}{nop}
+% !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
+% !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop}
+% !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop}
+% !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
+% !6nop=\doifinsetelse{}{}{yes}{nop}
+% !7nop=\doifinsetelse{}{aaa}{yes}{nop}
+% !8nop=\doifinsetelse{aaa}{}{yes}{nop}
+
+% !1=\doifinset{}{}{yes}
+% !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
+% !3=\doifinset{aaa}{bbb}{yes}
+% !4yes=\doifinset{aaa}{aaa}{yes}
+% !5=\doifinset{}{}{yes}
+% !6=\doifinset{aaa}{}{yes}
+
+% !1yes=\doifnotinset{}{}{yes}
+% !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
+% !3yes=\doifnotinset{aaa}{bbb}{yes}
+% !4=\doifnotinset{aaa}{aaa}{yes}
+% !5yes=\doifnotinset{}{}{yes}
+% !6yes=\doifnotinset{aaa}{}{yes}
+
+%D These are normally only used for keywords, i.e.\ strings so we can delegate
+%D the work to \LUA:
+
+%protected\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}}
+%protected\def\doifinset #1#2{\clf_doifinset {#1}{#2}}
+%protected\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}}
+% % \let\firstinset \clf_firstinset
+
+% These don't accept spaces after commas:
+%
+% \protected\def\doifelseinset#1#2%
+% {\ifhasxtoks{,#1,}{,#2,}%
+% \expandafter\firstoftwoarguments
+% \else
+% \expandafter\secondoftwoarguments
+% \fi}
+
+% \protected\def\doifinset#1#2%
+% {\ifhasxtoks{,#1,}{,#2,}%
+% \expandafter\firstofoneargument
+% \else
+% \expandafter\gobbleoneargument
+% \fi}
+
+% \protected\def\doifnotinset#1#2%
+% {\ifhasxtoks{,#1,}{,#2,}%
+% \expandafter\gobbleoneargument
+% \else
+% \expandafter\firstofoneargument
+% \fi}
+
+% But these do:
+
+\immutable\edef\a!comma{\expandtoken \ignorecatcode \commaasciicode}
+\immutable\edef\a!space{\expandtoken \ignorecatcode \spaceasciicode}
+
+\normalexpanded {
+
+ \permanent \protected \def \noexpand \doifelseinset#1#2%
+ {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}%
+ \noexpand\expandafter\noexpand\firstoftwoarguments
+ \noexpand\else
+ \noexpand\expandafter\noexpand\secondoftwoarguments
+ \noexpand\fi}
+
+ \permanent \protected \def \noexpand \doifinset#1#2%
+ {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}%
+ \noexpand\expandafter\noexpand\firstofoneargument
+ \noexpand\else
+ \noexpand\expandafter\noexpand\gobbleoneargument
+ \noexpand\fi}
+
+ \permanent \protected \def \noexpand \doifnotinset#1#2%
+ {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}%
+ \noexpand\expandafter\noexpand\gobbleoneargument
+ \noexpand\else
+ \noexpand\expandafter\noexpand\firstofoneargument
+ \noexpand\fi}
+
+}
+
+%D Done.
+
+\aliased\let\doifinsetelse\doifelseinset
+
+%D \macros
+%D {doifcommon,doifnotcommon,doifcommonelse}
+%D
+%D Probably the most time consuming tests are those that test for overlap in sets
+%D of strings.
+%D
+%D \starttyping
+%D \doifcommon {string,...} {string,...} {...}
+%D \doifnotcommon {string,...} {string,...} {...}
+%D \doifcommonelse {string,...} {string,...} {then ...} {else ...}
+%D \stoptyping
+
+% !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop}
+% !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop}
+% !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop}
+% !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop}
+% !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop}
+% !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop}
+% !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop}
+% !8nop=\doifcommonelse{}{}{yes}{nop}
+
+% !9nop=\doifcommonelse{,,}{,,}{yes}{nop}
+% !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop}
+
+\permanent\protected\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}} % todo: define in lua
+\permanent\protected\def\doifcommon #1#2{\clf_doifcommon {#1}{#2}} % todo: define in lua
+\permanent\protected\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}} % todo: define in lua
+
+\aliased\let\doifcommonelse\doifelsecommon
+
+%D \macros
+%D {processcommalist,processcommacommand,quitcommalist,
+%D processcommalistwithparameters}
+%D
+%D We've already seen some macros that take care of comma separated lists. Such
+%D list can be processed with
+%D
+%D \starttyping
+%D \processcommalist[string,string,...]\commando
+%D \stoptyping
+%D
+%D The user supplied command \type{\commando} receives one argument: the string.
+%D This command permits nesting and spaces after commas are skipped. Empty sets
+%D are no problem.
+%D
+%D \startbuffer
+%D \def\dosomething#1{(#1)}
+%D
+%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par
+%D 2: \processcommalist [{a,b,c,d,e,f}] \dosomething \par
+%D 3: \processcommalist [{a,b,c},d,e,f] \dosomething \par
+%D 4: \processcommalist [a,b,{c,d,e},f] \dosomething \par
+%D 5: \processcommalist [a{b,c},d,e,f] \dosomething \par
+%D 6: \processcommalist [{a,b}c,d,e,f] \dosomething \par
+%D 7: \processcommalist [] \dosomething \par
+%D 8: \processcommalist [{[}] \dosomething \par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Or expanded:
+%D
+%D \blank \getbuffer \blank
+%D
+%D The original definitions can be found elsewhere and need a bit more code. In case of
+%D issues, consult the \MKIV\ variant or previous \LMTX\ variants in the repository.
+%D
+%D When a list is saved in a macro, we can use a construction like:
+%D
+%D \starttyping
+%D \expandafter\processcommalist\expandafter[\list]\command
+%D \stoptyping
+%D
+%D Such solutions suit most situations, but we wanted a bit more.
+%D
+%D \starttyping
+%D \processcommacommand[string,\stringset,string]\commando
+%D \stoptyping
+%D
+%D where \type{\stringset} is a predefined set, like:
+%D
+%D \starttyping
+%D \def\first{aap,noot,mies}
+%D \def\second{laatste}
+%D
+%D \processcommacommand[\first]\message
+%D \processcommacommand[\first,second,third]\message
+%D \processcommacommand[\first,between,\second]\message
+%D \stoptyping
+%D
+%D Commands that are part of the list are expanded, so the use of this macro has its
+%D limits. We use a new \LUAMETATEX\ feature that intercepts invalid macro arguments
+%D by quitting when \type {\ignorearguments} is seen. That's why we act on the
+%D arguments state. Again it permits leaner and meaner macro definitions with a bit
+%D less clutter in tracing.
+
+\mutable\let\commalistcommand\empty
+
+\protected\def\syst_helpers_process_comma_item#+,%
+ {\ifarguments
+ \expandafter\syst_helpers_process_comma_item_gobble
+ \orelse\ifparameter#1\or
+ \commalistcommand{#1}%
+ \expandafter\syst_helpers_process_comma_item_next
+ \else
+ \expandafter\syst_helpers_process_comma_item_gobble
+ \fi}
+
+% \def\syst_helpers_process_comma_item_next
+% {\expandafterspaces\syst_helpers_process_comma_item}
+
+% \protected\def\processcommalist[#1]#2%
+% {\pushmacro\commalistcommand
+% \def\commalistcommand{#2}%
+% \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments
+% \popmacro\commalistcommand}
+
+% \protected\def\processcommacommand[#1]#2%
+% {\pushmacro\commalistcommand
+% \def\commalistcommand{#2}%
+% \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments
+% \popmacro\commalistcommand}
+
+% \permanent\protected\def\processcommalist[#*#+]#2%
+% {\pushmacro\commalistcommand
+% \def\commalistcommand{#2}%
+% \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments
+% \popmacro\commalistcommand}
+%
+% \permanent\protected\def\processcommacommand[#*#+]#2%
+% {\pushmacro\commalistcommand
+% \def\commalistcommand{#2}%
+% \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments
+% \popmacro\commalistcommand}
+
+\tolerant\protected\def\syst_helpers_process_comma_item#*#1,%
+ {\ifarguments\or
+ \commalistcommand{#1}%
+ \expandafter\syst_helpers_process_comma_item_next
+ \fi}
+
+\def\syst_helpers_process_comma_item_next
+ {\expandafterspaces\syst_helpers_process_comma_item}
+
+\permanent\protected\def\processcommalist[#1]#2%
+ {\pushmacro\commalistcommand
+ \def\commalistcommand{#2}%
+ \syst_helpers_process_comma_item#1\ignorearguments\ignorearguments\ignorearguments
+ \popmacro\commalistcommand}
+
+\permanent\protected\def\processcommacommand[#1]#2%
+ {\pushmacro\commalistcommand
+ \def\commalistcommand{#2}%
+ \normalexpanded{\syst_helpers_process_comma_item#1}\ignorearguments\ignorearguments\ignorearguments
+ \popmacro\commalistcommand}
+
+% \let\syst_helpers_process_comma_item_next_a \syst_helpers_process_comma_item_next
+% \def\syst_helpers_process_comma_item_next_b#0\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a}
+% \def\syst_helpers_process_comma_item_next_c#0\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b}
+% \def\syst_helpers_process_comma_item_gobble#0\ignorearguments{}
+
+\let\syst_helpers_process_comma_item_next_a \syst_helpers_process_comma_item_next
+\def\syst_helpers_process_comma_item_next_b#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a}
+\def\syst_helpers_process_comma_item_next_c#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b}
+\def\syst_helpers_process_comma_item_gobble#-\ignorearguments{}
+
+\permanent\protected\def\quitcommalist {\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b}
+\permanent\protected\def\quitprevcommalist{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_c}
+
+%D \startbuffer
+%D \def\foo#1{(#1)}
+%D <\processcommalist[a,b,c,d]\foo>
+%D
+%D \def\foo#1{(#1)}
+%D \def\oof#1{<\processcommalist[#1]\foo>}
+%D <\processcommalist[{a,b},{c,d}]\oof>
+%D
+%D \def\foo#1{(#1)\quitcommalist}
+%D <\processcommalist[a,b,c,d]\foo>
+%D
+%D \def\foo#1{(#1)}
+%D \def\oof#1{<\processcommalist[#1]\foo\quitcommalist>}
+%D <\processcommalist[{a,b},{c,d}]\oof>
+%D
+%D \def\foo#1{(#1)\quitcommalist}
+%D \def\oof#1{<\processcommalist[#1]\foo>}
+%D <\processcommalist[{a,b},{c,d},{e,f}]\oof>
+%D
+%D \def\foo#1{(#1)\quitprevcommalist}
+%D \def\oof#1{<\processcommalist[#1]\foo>}
+%D <\processcommalist[{a,b},{c,d},{e,f}]\oof>
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+%D The argument to \type{\command} is not delimited. Because we often use \type {[]}
+%D as delimiters, we also have:
+%D
+%D \starttyping
+%D \processcommalistwithparameters[string,string,...]\command
+%D \stoptyping
+%D
+%D where \type{\command} looks like:
+%D
+%D \starttyping
+%D \def\command[#1]{... #1 ...}
+%D \stoptyping
+
+\let\syst_helpers_do_process_comma_list_with_parameters\gobbleoneargument
+
+\permanent\protected\def\processcommalistwithparameters[#1]#2%
+ {\def\syst_helpers_do_process_comma_list_with_parameters##1{#2[##1]}%
+ \processcommalist[#1]\syst_helpers_do_process_comma_list_with_parameters}
+
+%D \macros
+%D {startprocesscommalist,startprocesscommacommand}
+%D
+%D Two more:
+
+\let\syst_helpers_comma_list_step\relax
+
+\mutable\let\currentcommalistitem\empty
+
+\permanent\protected\def\startprocesscommalist[#1]#2\stopprocesscommalist
+ {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
+ \processcommalist[#1]\syst_helpers_comma_list_step}
+
+\permanent\protected\def\startprocesscommacommand[#1]#2\stopprocesscommacommand
+ {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step}
+
+\aliased\let\stopprocesscommalist \relax
+\aliased\let\stopprocesscommacommand\relax
+
+%D \macros
+%D {processaction,
+%D processfirstactioninset,
+%D processallactionsinset}
+%D
+%D \CONTEXT\ makes extensive use of a sort of case or switch command. Depending of
+%D the presence of one or more provided items, some actions is taken. These macros
+%D can be nested without problems.
+%D
+%D \starttyping
+%D \processaction [x] [x=>a,y=>b,z=>c]
+%D \processfirstactioninset [x,y,z] [x=>a,y=>b,z=>c]
+%D \processallactionsinset [x,y,z] [x=>a,y=>b,z=>c]
+%D \stoptyping
+%D
+%D We can supply both a \type {default} action and an action to be undertaken when
+%D an \type {unknown} value is met:
+%D
+%D \starttyping
+%D \processallactionsinset
+%D [x,y,z]
+%D [ a=>\a,
+%D b=>\b,
+%D c=>\c,
+%D default=>\default,
+%D unknown=>\unknown{... \commalistelement ...}]
+%D \stoptyping
+%D
+%D When \type {#1} is empty, this macro scans list \type {#2} for the keyword \type
+%D {default} and executed the related action if present. When \type {#1} is non
+%D empty and not in the list, the action related to \type {unknown} is executed.
+%D Both keywords must be at the end of list \type{#2}. Afterwards, the actually
+%D found keyword is available in \type {\commalistelement}. An advanced example of
+%D the use of this macro can be found in \PPCHTEX, where we completely rely on \TEX\
+%D for interpreting user supplied keywords like \type {SB}, \type {SB1..6}, \type
+%D {SB125} etc.
+%D
+%D In the meantime we follow a different approach, often somewhat more heavy on the
+%D number of control sequences used, but that is no lomger a real issue. The code
+%D has been simplified and nwo uses the macro stack mechanism. If needed I can make
+%D this more hip and a bit faster now but \unknown\ it's seldom used nowadays as we
+%D have better ways now.
+
+\mutable\let\commalistelement\empty
+
+\let\m_syst_string_one\empty
+\let\m_syst_string_two\empty
+
+\let\syst_helpers_do_compare_process_action\relax
+
+\protected\def\syst_helpers_do_compare_process_action_a[#1=>#2][#3]%
+ {\edef\m_syst_string_two{#1}%
+ \ifx\m_syst_string_two\s!default
+ \let\commalistelement\empty
+ #2%
+ \fi}
+
+\protected\def\syst_helpers_do_compare_process_action_b[#1=>#2][#3]%
+ {\edef\m_syst_string_two{#1}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitcommalist
+ \orelse\ifx\m_syst_string_two\s!unknown
+ \def\commalistelement{#3}% beware of loops
+ #2%
+ \fi}
+
+\permanent\protected\def\processaction[#1]#2[%
+ {\edef\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one
+ \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_a
+ \else
+ \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_b
+ \fi
+ \edef\syst_helpers_do_process_action##1{\syst_helpers_do_compare_process_action[##1][#1]}% expands #1
+ \processnextcommalist\syst_helpers_do_process_action[}
+
+\protected\def\syst_helpers_do_compare_process_action_c[#1=>#2][#3]%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#3}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitprevcommalist
+ \else
+ \edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\s!unknown
+ \def\commalistelement{#3}%
+ #2%
+ \fi
+ \fi}
+
+\permanent\protected\def\processfirstactioninset[#1]%
+ {\edef\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one
+ \expandafter\processaction
+ \else
+ \expandafter\syst_helpers_process_first_action_in_set_indeed
+ \fi
+ [#1]}
+
+\let\syst_helpers_do_process_action \gobbleoneargument
+\let\syst_helpers_do_do_process_action\gobbleoneargument
+
+\tolerant\protected\def\syst_helpers_process_first_action_in_set_indeed[#1]#*[#2]%
+ {\def\syst_helpers_do_process_action##1%
+ {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_c[####1][##1]}%
+ \processcommalist[#2]\syst_helpers_do_do_process_action}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action}
+
+\protected\def\syst_helpers_do_compare_process_action_d[#1=>#2][#3]%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#3}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitcommalist
+ \else
+ \edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\s!unknown
+ \def\commalistelement{#3}%
+ #2%
+ \fi
+ \fi}
+
+\let\syst_process_action_in_set_all\relax
+
+\tolerant\protected\def\syst_helpers_process_all_actions_in_set_indeed[#1]#*[#2]%
+ {\globalpushmacro\syst_process_action_in_set_all
+ \def\syst_process_action_in_set##1%
+ {\def\syst_process_action_in_set_one####1{\syst_helpers_do_compare_process_action_d[####1][##1]}%
+ \processcommalist[#2]\syst_process_action_in_set_one}%
+ \processcommacommand[#1]\syst_process_action_in_set
+ \globalpopmacro\syst_process_action_in_set_all}
+
+\permanent\protected\def\processallactionsinset[#1]%
+ {\edef\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one
+ \expandafter\processaction
+ \else
+ \expandafter\syst_helpers_process_all_actions_in_set_indeed
+ \fi[#1]}
+
+%D These macros use:
+
+% \protected\def\processnextcommalist#1[#2#3]%
+% {\pushmacro\commalistcommand
+% \def\commalistcommand{#1}%
+% \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments
+% \popmacro\commalistcommand}
+
+\permanent\protected\def\processnextcommalist#1[#2#3]%
+ {\pushmacro\commalistcommand
+ \def\commalistcommand{#1}%
+ \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments
+ \popmacro\commalistcommand}
+
+%D \macros
+%D {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter}
+%D
+%D Sometimes the action to be undertaken depends on the next character. This macro
+%D get this character and puts it in \type {\firstcharacter}.
+%D
+%D \starttyping
+%D \getfirstcharacter {string}
+%D \stoptyping
+%D
+%D A two step expansion is used to prevent problems with complicated arguments, for
+%D instance arguments that consist of two or more expandable tokens.
+
+\mutable\let\firstcharacter \empty
+\mutable\let\remainingcharacters\empty
+
+\permanent\protected\def\getfirstcharacter #1{\clf_getfirstcharacter{#1}}
+\permanent\protected\def\doifelsefirstchar #1#2{\clf_doifelsefirstchar{#1}{#2}}
+\permanent\protected\def\thefirstcharacter #1{\clf_thefirstcharacter{#1}}
+\permanent\protected\def\theremainingcharacters#1{\clf_theremainingcharacters{#1}}
+
+\aliased\let\doiffirstcharelse\doifelsefirstchar
+
+%D \macros
+%D {doifinstringelse, doifincsnameelse}
+%D
+%D We can check for the presence of a substring in a given sequence of characters.
+%D
+%D \starttyping
+%D \doifinstringelse {substring} {string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D \startbuffer
+%D \doifinstringelse{abc}{foo bar abc}{Y}{N}=Y\par
+%D \doifinstringelse{abc}{foo bar cab}{Y}{N}=N\par
+%D \doifinstring {abc}{foo bar abc}{Y}=Y\par
+%D \doifinstring {abc}{foo bar cab}{Y}\par
+%D \doifnotinstring {abc}{foo bar abc}{Y}\par
+%D \doifnotinstring {abc}{foo bar cab}{Y}=Y\par
+%D \doifinstringelse{}{foo bar abc}{Y}{N}=N\par
+%D \doifinstring {}{foo bar abc}{N}\par
+%D \doifnotinstring {}{foo bar abc}{Y}=Y\par
+%D \doifinstringelse{x}{}{Y}{N}=N\par
+%D \doifinstring {x}{}{N}\par
+%D \doifnotinstring {x}{}{Y}=Y\par
+%D \doifinstringelse{}{}{Y}{N}=N\par
+%D \doifinstring {}{}{N}\par
+%D \doifnotinstring {}{}{Y}=Y\par
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+%D I keep the following as example code:
+
+% \let\syst_helpers_do_do_if_in_string_else\relax
+% \let\syst_helpers_do_do_if_in_string \relax
+% \let\syst_helpers_do_do_if_not_in_string \relax
+%
+% \let\m_syst_sub_string \empty
+%
+% \protected\def\doifelseinstring#1%
+% {\edef\m_syst_sub_string{#1}% expand #1 here
+% \ifempty\m_syst_sub_string
+% \expandafter\thirdofthreearguments
+% \else
+% \expandafter\syst_helpers_do_if_in_string_else
+% \fi}
+%
+% \let\doifinstringelse\doifelseinstring
+%
+% \protected\def\syst_helpers_do_if_in_string_else#1% ##2 can be {abc}
+% {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string_else##1\m_syst_sub_string##2}%
+% {\ifarguments
+% \or
+% \expandafter\syst_helpers_do_if_in_string_else_nop
+% \or
+% \expandafter\syst_helpers_do_if_in_string_else_yes
+% \fi}%
+% \normalexpanded{\syst_helpers_do_do_if_in_string_else#1}\s!e_o_t_token\ignorearguments\ignorearguments}
+%
+% \protected\def\syst_helpers_do_if_in_string_else_delimited#1% ##2 can be {abc}
+% {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string_else##1,\m_syst_sub_string,##2}%
+% {\ifarguments
+% \or
+% \expandafter\syst_helpers_do_if_in_string_else_nop
+% \or
+% \expandafter\syst_helpers_do_if_in_string_else_yes
+% \fi}%
+% \normalexpanded{\syst_helpers_do_do_if_in_string_else,#1,}\s!e_o_t_token\ignorearguments\ignorearguments}
+%
+% \protected\def\doifinstring#1%
+% {\edef\m_syst_sub_string{#1}% expand #1 here
+% \ifempty\m_syst_sub_string
+% \expandafter\gobbletwoarguments
+% \else
+% \expandafter\syst_helpers_do_if_in_string
+% \fi}
+%
+% \protected\def\syst_helpers_do_if_in_string#1% ##2 can be {abc}
+% {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string##1\m_syst_sub_string##2}%
+% {\ifarguments
+% \or
+% \expandafter\syst_helpers_do_if_in_string_nop
+% \or
+% \expandafter\syst_helpers_do_if_in_string_yes
+% \fi}%
+% \normalexpanded{\syst_helpers_do_do_if_in_string#1}\s!e_o_t_token\ignorearguments\ignorearguments}
+%
+% \protected\def\doifnotinstring#1%
+% {\edef\m_syst_sub_string{#1}% expand #1 here
+% \ifempty\m_syst_sub_string
+% \expandafter\secondoftwoarguments
+% \else
+% \expandafter\syst_helpers_do_if_not_in_string
+% \fi}
+%
+% \protected\def\syst_helpers_do_if_not_in_string#1% ##2 can be {abc}
+% {\normalexpanded{\protected\def\syst_helpers_do_do_if_not_in_string##1\m_syst_sub_string##2}%
+% {\ifarguments
+% \or
+% \expandafter\syst_helpers_do_if_not_in_string_nop
+% \or
+% \expandafter\syst_helpers_do_if_not_in_string_yes
+% \fi}%
+% \normalexpanded{\syst_helpers_do_do_if_not_in_string#1}\s!e_o_t_token\ignorearguments\ignorearguments}
+%
+% \def\syst_helpers_do_if_in_string_else_yes#0\ignorearguments\ignorearguments#2#0{#2}
+% \def\syst_helpers_do_if_in_string_else_nop#0\ignorearguments#0#3{#3}
+% \def\syst_helpers_do_if_in_string_yes #0\ignorearguments\ignorearguments#2{#2}
+% \def\syst_helpers_do_if_in_string_nop #0\ignorearguments#0{}
+% \def\syst_helpers_do_if_not_in_string_yes #0\ignorearguments\ignorearguments#0{}
+% \def\syst_helpers_do_if_not_in_string_nop #0\ignorearguments#2{#2}
+
+\permanent\protected\def\doifelseinstring#1#2%
+ {\ifhasxtoks{#1}{#2}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifinstring#1#2%
+ {\ifhasxtoks{#1}{#2}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotinstring#1#2%
+ {\ifhasxtoks{#1}{#2}%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\aliased\let\doifinstringelse\doifelseinstring
+
+%D The next one one of those variants that we used when speed was more of an issue
+%D that today. Now we just expand the lot. We just use an alias now:
+
+\aliased\let\doifelseincsname\doifelseinstring
+\aliased\let\doifincsnameelse\doifinstringelse
+
+%D \macros
+%D {doifnumberelse,doifnumber,doifnotnumber}
+%D
+%D The next macro executes a command depending of the outcome of a test on numerals.
+%D We now use a \LUATEX\ feature that permits a more robust checking, but you might
+%D want to take a look at the originals. It's typically one of these new features
+%D that probably only \CONTEXT\ will use, which is probably true for more such
+%D features that no one ever asked for (but they are pretty generic in nature
+%D anyway).
+
+\permanent\def\doifelsenumber#1%
+ {\ifchknum#1\or
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\def\doifnumber#1%
+ {\ifchknum#1\or
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\def\doifnotnumber#1%
+ {\ifchknum#1\or
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\aliased\let\doifnumberelse\doifelsenumber
+
+%D \macros
+%D {setpercentdimen}
+%D
+%D \starttyping
+%D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent}
+%D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt}
+%D \scratchdimen \percentdimen \hsize {10\letterpercent}
+%D \stoptyping
+
+% todo: use the push back dimen trickery
+
+\permanent\def\percentdimen#1#2% dimen percentage (with %)
+ {\dimexpr\clf_percentageof{#2}\dimexpr#1\relax}
+
+\permanent\protected\def\setpercentdimen#1#2% dimen percentage (with %)
+ {#1=\clf_percentageof{#2}\dimexpr#1\relax}
+
+%D \macros
+%D {makerawcommalist,
+%D rawdoinsetelse,
+%D rawprocesscommalist,
+%D rawprocessaction}
+%D
+%D Some of the commands mentioned earlier are effective but slow. When one is
+%D desperately in need of faster alternatives and when the conditions are
+%D predictable safe, the \type {\raw} alternatives come into focus. A major drawback
+%D is that they do not take \type {\c!constants} into account, simply because no
+%D expansion is done. This is no problem with \type {\rawprocesscommalist}, because
+%D this macro does not compare anything. Expandable macros are permitted as search
+%D string.
+%D
+%D \starttyping
+%D \makerawcommalist[string,string,...]\stringlist
+%D \rawdoifelseinset{string}{string,...}{...}{...}
+%D \rawprocesscommalist[string,string,...]\commando
+%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c]
+%D \stoptyping
+%D
+%D Spaces embedded in the list, for instance after commas, spoil the search process.
+%D The gain in speed depends on the length of the argument (the longer the argument,
+%D the less we gain). The question is: do we still need these raw variants?
+
+\let\syst_helpers_do_make_raw_comma_list\gobbleoneargument
+
+\permanent\protected\def\makerawcommalist[#1]#2% use \processnext ... here
+ {\scratchtoks\emptytoks
+ \def\syst_helpers_do_make_raw_comma_list##1{\iftok\scratchtoks\emptytoks\scratchtoks{##1}\else\toksapp\scratchtoks{,##1}\fi}%
+ \processcommalist[#1]\syst_helpers_do_make_raw_comma_list
+ \edef#2{\the\scratchtoks}}
+
+% beware: in mkiv { } were lost so it was not compatible with the non raw
+
+\aliased\let\rawprocesscommalist \processcommalist % can go
+\aliased\let\rawprocesscommacommand\processcommacommand % can go
+
+%D Here is one without nesting .. still needed? Only used for modes (no nesting
+%D there).
+
+\mutable\let\fastcommalistcommand\relax
+
+\protected\def\syst_helpers_process_fast_comma_item#1,%
+ {\ifarguments
+ \expandafter\syst_helpers_process_comma_item_gobble
+ \or
+ \fastcommalistcommand{#1}%
+ \expandafter\syst_helpers_process_fast_comma_item_next
+ \fi}
+
+\protected\def\syst_helpers_process_fast_comma_item_next
+ {\expandafterspaces\syst_helpers_process_fast_comma_item}
+
+\permanent\protected\def\fastprocesscommalist[#1]#2%
+ {\let\fastcommalistcommand#2%
+ \expandafterspaces\syst_helpers_process_fast_comma_item#1\ignorearguments\ignorearguments\ignorearguments}
+
+\permanent\protected\def\fastprocesscommacommand[#1]#2%
+ {\let\fastcommalistcommand#2%
+ \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_fast_comma_item#1}\ignorearguments\ignorearguments\ignorearguments}
+
+% \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}}
+% \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}}
+
+\def\syst_helpers_do_if_else_in_set#1%
+ {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\rawdoifelseinset#1%
+ {\edef\m_syst_sub_string{#1}% expand #1 here
+ \ifempty\m_syst_sub_string
+ \expandafter\thirdofthreearguments
+ \else
+ \expandafter\syst_helpers_do_if_else_in_set
+ \fi}
+
+\aliased\let\rawdoifinsetelse\rawdoifelseinset
+
+\def\syst_helpers_do_if_in_set#1%
+ {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\rawdoifinset#1% or just alias this one
+ {\edef\m_syst_sub_string{#1}% expand #1 here
+ \ifx\m_syst_sub_string\m_syst_two_commas
+ \expandafter\gobbletwoarguments
+ \else
+ \expandafter\syst_helpers_do_if_in_set
+ \fi}
+
+%D Some more raw material:
+
+% probably never used
+%
+% \def\syst_helpers_raw_process_action#1=>#2,%
+% {\ifarguments
+% %\expandafter\syst_helpers_raw_process_action_gobble
+% \or
+% \expandafter\syst_helpers_raw_process_action_gobble
+% \or
+% \edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_one\m_syst_string_two
+% \def\m_syst_helpers_process_action{#2}%
+% \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_gobble
+% \else
+% \ifx\s!unknown\m_syst_string_two
+% \def\m_syst_helpers_process_action_unknown{#2}%
+% \fi
+% \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_next
+% \fi
+% \fi}
+%
+% \def\syst_helpers_raw_process_action_gobble#-\ignorearguments
+% {}
+%
+% \def\syst_helpers_raw_process_action_next
+% {\expandafterspaces\syst_helpers_raw_process_action}
+%
+% \permanent\protected\def\xrawprocessaction[#1]#*[#2]%
+% {\edef\m_syst_string_one{#1}%
+% \ifempty\m_syst_string_one
+% \let\m_syst_string_one\s!default
+% \fi
+% \let\m_syst_helpers_process_action\relax
+% \let\m_syst_helpers_process_action_unknown\relax
+% \syst_helpers_raw_process_action#2\ignorearguments\ignorearguments\ignorearguments
+% \ifrelax\m_syst_helpers_process_action
+% \m_syst_helpers_process_action_unknown
+% \else
+% \m_syst_helpers_process_action
+% \fi}
+
+%D When we process the list \type {a,b,c,d,e}, the raw routine takes over 30\% less
+%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which
+%D use \type {\futurelet} perform worse. Part of the speedup is due to the \type
+%D {\let} and \type {\expandafter} in the test.
+%D
+%D \macros
+%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue,
+%D dogetvalue}
+%D
+%D When we are going to do assignments, we have to take multi||linguality into account.
+%D For the moment we keep things simple and single||lingual.
+%D
+%D \starttyping
+%D \dosetvalue {label} {variable} {value}
+%D \dosetevalue {label} {variable} {value}
+%D \dosetgvalue {label} {variable} {value}
+%D \docopyvalue {to label} {from label} {variable}
+%D \doresetvalue {label} {variable}
+%D \stoptyping
+%D
+%D These macros are in fact auxiliary ones and are not meant for use outside the
+%D assignment macros.
+
+\def\dosetvalue #1#2{\defcsname #1#2\endcsname} % takes #3
+\def\dosetevalue #1#2{\edefcsname #1#2\endcsname} % takes #3
+\def\dosetgvalue #1#2{\global\edefcsname#1#2\endcsname} % takes #3
+\def\doresetvalue #1#2{\letcsname #1#2\endcsname\empty}
+\def\doignorevalue#1#2#3{\letcsname #1#2\endcsname\empty}
+\def\docopyvalue #1#2#3{\defcsname #1#3\endcsname{\csname#2#3\endcsname}}
+
+%D \macros
+%D {doassign,undoassign,doassignempty}
+%D
+%D Assignments are the backbone of \CONTEXT. Abhorred by the concept of style file
+%D hacking, we took a considerable effort in building a parameterized system.
+%D Unfortunately there is a price to pay in terms of speed. Compared to other
+%D packages and taking the functionality of \CONTEXT\ into account, the total size
+%D of the format file is still very acceptable. Now how are these assignments done.
+%D
+%D Assignments can be realized with:
+%D
+%D \starttyping
+%D \doassign[label][variable=value]
+%D \undoassign[label][variable=value]
+%D \stoptyping
+%D
+%D and:
+%D
+%D \starttyping
+%D \doassignempty[label][variable=value]
+%D \stoptyping
+%D
+%D Assignments like \type{\doassign} are compatible with:
+%D
+%D \starttyping
+%D \def\labelvariable{value}
+%D \stoptyping
+%D
+%D We do check for the presence of an \type{=} and loudly complain of it's missed. We
+%D will redefine this macro later on, when a more advanced message mechanism is
+%D implemented.
+
+\protected\def\showassignerror#1#2%
+ {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}}
+
+\permanent\protected\def\doassignempty[#1][#2=#3]% kind of obsolete
+ {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi}
+
+%D \macros
+%D {getparameters,geteparameters,getgparameters,
+%D forgetparameters}
+%D
+%D Using the assignment commands directly is not our ideal of user friendly interfacing,
+%D so we take some further steps.
+%D
+%D \starttyping
+%D \getparameters [label] [...=...,...=...]
+%D \forgetparameters [label] [...=...,...=...]
+%D \stoptyping
+%D
+%D Again, the label identifies the category a variable belongs to. The second argument
+%D can be a comma separated list of assignments.
+%D
+%D \starttyping
+%D \getparameters
+%D [demo]
+%D [alfa=1,
+%D beta=2]
+%D \stoptyping
+%D
+%D is equivalent to
+%D
+%D \starttyping
+%D \def\demoalfa{1}
+%D \def\demobeta{2}
+%D \stoptyping
+%D
+%D
+%D In the pre||multi||lingual stadium \CONTEXT\ took the next approach. With
+%D
+%D \starttyping
+%D \def\??demo {@@demo}
+%D \def\!!alfa {alfa}
+%D \def\!!beta {beta}
+%D \stoptyping
+%D
+%D calling
+%D
+%D \starttyping
+%D \getparameters
+%D [\??demo]
+%D [\!!alfa=1,
+%D \!!beta=2]
+%D \stoptyping
+%D
+%D lead to:
+%D
+%D \starttyping
+%D \def\@@demoalfa{1}
+%D \def\@@demobeta{2}
+%D \stoptyping
+%D
+%D Because we want to be able to distinguish the \type{!!} pre||tagged user supplied
+%D variables from internal counterparts, we will introduce a slightly different tag
+%D in the multi||lingual modules. There we will use \type{c!} or \type{v!},
+%D depending on the context.
+%D
+%D By calling \type{doassign} directly, we save ourselves some argument passing
+%D and gain some speed. Whatever optimizations we do, this command will always be
+%D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's
+%D funny to see that this alternative saw the light so lately --- can be used to do
+%D expanded assigments.
+
+% \mutable\let\currentvalue\empty % only mkii
+
+\permanent\protected\def\getparameters {\dogetparameters\dosetvalue}
+\permanent\protected\def\geteparameters {\dogetparameters\dosetevalue}
+\permanent\protected\def\getgparameters {\dogetparameters\dosetgvalue}
+\permanent\protected\def\getxparameters {\dogetparameters\dosetxvalue}
+\permanent\protected\def\forgetparameters{\dogetparameters\doignorevalue}
+
+\aliased\let\getexpandedparameters\geteparameters
+
+\def\syst_helpers_grab_parameter_error#1%
+ {\showassignerror{#1}{\the\inputlineno\space(\m_syst_parameter_n)}}
+
+\def\syst_helpers_grab_parameter#1,%
+ {\ifarguments
+ % done
+ \else
+ \syst_helpers_grab_parameter_okay#1,\ignorearguments
+ \expandafter\syst_helpers_grab_parameter_next
+ \fi}
+
+\def\syst_helpers_grab_parameter_okay#1=#2,%
+ {\ifarguments
+ \or
+ \syst_helpers_grab_parameter_error{#1}%
+ \or
+ \m_syst_parameter_s\m_syst_parameter_n{#1}{#2}%
+ \fi}
+
+\def\syst_helpers_grab_parameter_next
+ {\expandafterspaces\syst_helpers_grab_parameter}
+
+\permanent\protected\def\dogetparameters#1[#2]#*[#3]%
+ {\def\m_syst_parameter_n{#2}%
+ \let\m_syst_parameter_s#1%
+ \expandafterspaces\syst_helpers_grab_parameter#3\ignorearguments\ignorearguments}
+
+%D \macros
+%D {getemptyparameters}
+%D
+%D Sometimes we explicitly want variables to default to an empty string, so we
+%D welcome:
+%D
+%D \starttyping
+%D \getemptyparameters [label] [...=...,...=...]
+%D \stoptyping
+
+\let\syst_helpers_get_empty_parameters\gobbleoneargument
+
+\permanent\protected\def\getemptyparameters[#1]#*[#2]%
+ {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}%
+ \processcommalist[#2]\syst_helpers_get_empty_parameters}
+
+%D We now just alias these as there is no need for a speedup. These have not been used
+%D for a long time in core code.
+
+\aliased\let\doassign \getparameters % obsolete, these will go
+\aliased\let\doeassign \geteparameters % obsolete, these will go
+\aliased\let\undoassign\forgetparameters % obsolete, these will go
+
+%D \macros
+%D {processassignmentlist,processassignmentcommand,
+%D startprocessassignmentlist,startprocessassignmentcommand}
+%D
+%D For Wolfgang:
+%D
+%D \starttyping
+%D \def\showpair#1#2{key:#1, value:#2\par}
+%D \processassignmentlist[a=1,b=2]\showpair
+%D \stoptyping
+%D
+%D We can optimize this one if needed but it's not a core macro so hardly worth the
+%D trouble and tokens.
+
+\mutable\let\currentassignmentlistcommand\gobbletwoarguments
+\mutable\let\currentassignmentlistkey \empty
+\mutable\let\currentassignmentlistvalue \empty
+
+\let\syst_helpers_process_assign_entry\gobbleoneargument
+
+\permanent\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value]
+ {\def\syst_helpers_process_assign_entry##1{#2}% {##2}{##3} % namespace is ignored
+ \dogetparameters\syst_helpers_process_assign_entry[][#1]}
+
+\permanent\protected\def\processassignmentcommand[#1]%
+ {\normalexpanded{\processassignmentlist[#1]}}
+
+\permanent\protected\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist
+ {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
+ \processassignmentlist[#1]\currentassignmentlistcommand}
+
+\aliased\let\stopprocessassignmentlist\relax
+
+\permanent\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand
+ {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
+ \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand}
+
+\aliased\let\stopprocessassignmentcommand\relax
+
+%D \macros
+%D {currentvalue}
+%D
+%D Just in case a \type{\getparameter} argument itself ends up inside a \type
+%D {\write} or other expandable location, our new macro needs a default value.
+%D
+%D \starttyping
+%D \getparameters[xxx][aaa=bbb]\par
+%D \getparameters[xxx][=bbb]\par
+%D \getparameters[xxx][aaa=]\par
+%D \getparameters[xxx][=]\par
+%D \getparameters[xxx][aaa]\par
+%D \stoptyping
+
+%D \macros
+%D {copyparameters}
+%D
+%D Some \CONTEXT\ commands take their default setups from others. All commands that
+%D are able to provide backgounds or rules around some content, for instance default
+%D to the standard command for ruled boxes. Is situations like this we can use:
+%D
+%D \starttyping
+%D \copyparameters [to-label] [from-label] [name1,name2,...]
+%D \stoptyping
+%D
+%D For instance
+%D
+%D \starttyping
+%D \copyparameters
+%D [internal][external]
+%D [alfa,beta]
+%D \stoptyping
+%D
+%D Leads to:
+%D
+%D \starttyping
+%D \def\internalalfa {\externalalfa}
+%D \def\internalbeta {\externalbeta}
+%D \stoptyping
+%D
+%D By using \type {\docopyvalue} we've prepared this command for use in a
+%D multi||lingual environment.
+
+\let\syst_helpers_copy_parameter\relax
+
+\permanent\protected\def\copyparameters[#1]#*[#2]#*[#3]%
+ {\doifnot{#1}{#2}
+ {\def\syst_helpers_copy_parameter{\docopyvalue{#1}{#2}}% ##1
+ \processcommalist[#3]\syst_helpers_copy_parameter}}
+
+% %D \macros
+% %D {ifparameters,checkparameters}
+% %D
+% %D A slightly different one is \type {\checkparameters}, which also checks on the
+% %D presence of a~\type {=}.
+% %D
+% %D The boolean \type {\ifparameters} can be used afterwards. Combining both in one
+% %D \type {\if}||macro would lead to problems with nested \type {\if}'s.
+% %D
+% %D \starttyping
+% %D \checkparameters[argument]
+% %D \stoptyping
+%
+% \newif\ifparameters
+%
+% \protected\def\checkparameters[#1]%
+% {\ifhastok={#1}\parameterstrue\else\parametersfalse\fi}
+
+%D \macros
+%D {getfromcommalist,getfromcommacommand,
+%D commalistelement,
+%D getcommalistsize,getcommacommandsize}
+%D
+%D It's possible to get an element from a commalist or a command representing
+%D a commalist.
+%D
+%D \starttyping
+%D \getfromcommalist [string] [n]
+%D \getfromcommacommand [string,\strings,string,...] [n]
+%D \stoptyping
+%D
+%D The difference betwee the two of them is the same as the difference between
+%D \type {\processcomma...}. The found string is stored in \type
+%D {\commalistelement}.
+%D
+%D We can calculate the size of a comma separated list by using:
+%D
+%D \starttyping
+%D \getcommalistsize [string,string,...]
+%D \getcommacommandsize [string,\strings,string,...]
+%D \stoptyping
+%D
+%D Afterwards, the length is available in the macro \type {\commalistsize}
+%D (not a \COUNTER).
+
+\newcount\commalistcounter
+
+\mutable\def\commalistsize{0}
+
+\def\syst_helpers_get_comma_list_size#0,% no #- as we need to count
+ {\ifarguments\or
+ \advance\commalistcounter\plusone
+ \expandafter\syst_helpers_get_comma_list_size
+ \fi}
+
+\permanent\protected\def\getcommalistsize[%
+ {\futureexpand]\syst_helpers_get_comma_list_size_nop\syst_helpers_get_comma_list_size_yes}
+
+\def\syst_helpers_get_comma_list_size_yes#+]%
+ {\commalistcounter\zerocount
+ \syst_helpers_get_comma_list_size #1,\ignorearguments\ignorearguments
+ \edef\commalistsize{\the\commalistcounter}}
+
+\def\syst_helpers_get_comma_list_size_nop]%
+ {\commalistcounter\zerocount
+ \let\commalistsize\!!zerocount}
+
+\permanent\protected\def\getcommacommandsize[#1]%
+ {\normalexpanded{\getcommalistsize[#1]}}
+
+%D Filters:
+
+% \def\syst_helpers_gobble_comma_list#0\ignorearguments{}
+
+\def\syst_helpers_gobble_comma_list#-\ignorearguments{}
+
+\def\syst_helpers_get_from_comma_list#1,%
+ {\ifarguments \or
+ \advance\commalistcounter \minusone
+ \ifcase\commalistcounter
+ \def\commalistelement{#1}%
+ \expandafter\expandafter\expandafter\syst_helpers_gobble_comma_list
+ \else
+ \expandafter\expandafter\expandafter\syst_helpers_get_from_comma_list_next
+ \fi
+ \fi}
+
+\def\syst_helpers_get_from_comma_list_next
+ {\expandafterspaces\syst_helpers_get_from_comma_list}
+
+\permanent\protected\def\getfromcommalist[#1]#*[#2]%
+ {\let\commalistelement\empty
+ \commalistcounter#2\relax
+ \expandafterspaces\syst_helpers_get_from_comma_list#1\ignorearguments\ignorearguments}
+
+\permanent\protected\def\getfromcommacommand[#1]%
+ {\normalexpanded{\getfromcommalist[#1]}}
+
+%D Watertight (and efficient) solutions are hard to find, due to the handling of
+%D braces during parameters passing and scanning. Nevertheless:
+%D
+%D \startbuffer
+%D \def\dosomething#1{(#1=\commalistsize) }
+%D
+%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1
+%D \getcommalistsize [{a,b,c,d,e,f}] \dosomething 1
+%D \getcommalistsize [{a,b,c},d,e,f] \dosomething 4
+%D \getcommalistsize [a,b,{c,d,e},f] \dosomething 4
+%D \getcommalistsize [a{b,c},d,e,f] \dosomething 4
+%D \getcommalistsize [{a,b}c,d,e,f] \dosomething 4
+%D \getcommalistsize [] \dosomething 0
+%D \getcommalistsize [{[}] \dosomething 1
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D reports:
+%D
+%D \getbuffer
+
+%D \macros
+%D {dogetcommalistelement,dogetcommacommandelement}
+%D
+%D For low level (fast) purposes, we can also use the next alternative, which can
+%D handle 9~elements at most.
+%D
+%D \starttyping
+%D \dogetcommalistelement1\from a,b,c\to\commalistelement
+%D \stoptyping
+
+\def\syst_helpers_get_comma_list_element#1,#2,#3,#4,#5,#6,#7,#8,#9,%
+ {\ifcase\scratchcounter\or#1\or#2\or#3\or#4\or#5\or#6\or#7\or#8\or#9\fi
+ \syst_helpers_gobble_comma_list}
+
+\permanent\protected\def\dogetcommacommandelement#1\from#2\to#3%
+ {\scratchcounter#1\relax
+ \edef#3{\normalexpanded{\syst_helpers_get_comma_list_element#2\ignorearguments\ignorearguments}}}
+
+%D \macros
+%D {dosingleargument,dodoubleargument,dotripleargument,
+%D doquadrupleargument,doquintupleargument,dosixtupleargument,
+%D doseventupleargument}
+%D
+%D When working with delimited arguments, spaces and lineendings can interfere. The
+%D next set of macros uses \TEX' internal scanner for grabbing everything between
+%D arguments. Forgive me the funny names.
+%D
+%D \starttyping
+%D \dosingleargument \command = \command[#1]
+%D \dodoubleargument \command = \command[#1][#2]
+%D \dotripleargument \command = \command[#1][#2][#3]
+%D \doquadrupleargument \command = \command[#1][#2][#3][#4]
+%D \doquintupleargument \command = \command[#1][#2][#3][#4][#5]
+%D \dosixtupleargument \command = \command[#1][#2][#3][#4][#5][#6]
+%D \doseventupleargument\command = \command[#1][#2][#3][#4][#5][#6][#7]
+%D \stoptyping
+%D
+%D These macros can be used in the following way:
+%D
+%D \starttyping
+%D \def\dosetupsomething[#1][#2]%
+%D {... #1 ... #2 ...}
+%D
+%D \protected\def\setupsomething
+%D {\dodoubleargument\dosetupsomething}
+%D \stoptyping
+%D
+%D The implementation can be surprisingly simple and needs no further explanation,
+%D like:
+%D
+%D \starttyping
+%D \def\dosingleargument#1[#2]%
+%D {#1[#2]}
+%D \def\dotripleargument#1[#2]#3[#4]#5[#6]%
+%D {#1[#2][#4][#6]}
+%D \def\doquintupleargument#1%
+%D {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]%
+%D {#1[##1][##3][##5][##7][##9]}%
+%D \dodoquintupleargument}
+%D \stoptyping
+%D
+%D Because \TEX\ accepts 9~arguments at most, we have to use two||step solution when
+%D getting five or more arguments.
+%D
+%D When developing more and more of the real \CONTEXT, we started using some
+%D alternatives that provided empty arguments (in fact optional ones) whenever the
+%D user failed to supply them. Because this more complicated macros enable us to do
+%D some checking, we reimplemented the non||empty ones.
+
+%D \macros
+%D {iffirstagument,ifsecondargument,ifthirdargument,
+%D iffourthargument,iffifthargument,ifsixthargument,
+%D ifseventhargument}
+%D
+%D We use some signals for telling the calling macros if all wanted arguments are
+%D indeed supplied by the user.
+
+\newcount\nofarguments
+
+\newif\iffirstargument
+\newif\ifsecondargument
+\newif\ifthirdargument
+\newif\iffourthargument
+\newif\iffifthargument
+\newif\ifsixthargument
+\newif\ifseventhargument
+
+%D \macros
+%D {dosingleempty,dodoubleempty,dotripleempty,
+%D doquadrupleempty,doquintupleempty,dosixtupeempty,
+%D doseventupleempty}
+%D
+%D The empty argument supplying macros mentioned before, look like:
+%D
+%D \starttyping
+%D \dosingleempty \command
+%D \dodoubleempty \command
+%D \dotripleempty \command
+%D \doquadrupleempty \command
+%D \doquintupleempty \command
+%D \dosixtuple_empty \command
+%D \doseventupleempty\command
+%D \stoptyping
+%D
+%D So \type{\dodoubleempty} leads to:
+%D
+%D \starttyping
+%D \command[#1][#2]
+%D \command[#1][]
+%D \command[][]
+%D \stoptyping
+%D
+%D Depending of the generousity of the user. Afterwards one can use the \type
+%D {\if...argument} boolean. For novice: watch the stepwise doubling of \type {#}'s.
+
+%D NB : experimental versions in cont-exp.mkiv
+
+%D Common:
+
+\newtoks\t_syst_aux
+
+% \def\syst_helpers_single_empty_one_yes {\firstargumenttrue \the\t_syst_aux}
+% \def\syst_helpers_double_empty_two_yes {\secondargumenttrue \the\t_syst_aux}
+% \def\syst_helpers_triple_empty_three_yes {\thirdargumenttrue \the\t_syst_aux}
+% \def\syst_helpers_quadruple_empty_four_yes {\fourthargumenttrue \the\t_syst_aux}
+% \def\syst_helpers_quintuple_empty_five_yes {\fifthargumenttrue \the\t_syst_aux}
+% \def\syst_helpers_sixtuple_empty_six_yes {\sixthargumenttrue \the\t_syst_aux}
+% \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\the\t_syst_aux}
+%
+% %D Single:
+%
+% \protected\def\dosingleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_single_empty_one_yes\syst_helpers_single_empty_one_nop}
+%
+% \def\syst_helpers_single_empty_one_nop
+% {\firstargumentfalse
+% \the\t_syst_aux[]}
+%
+% %D Double
+%
+% \protected\def\dodoubleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_double_empty_one_yes\syst_helpers_double_empty_one_nop}
+%
+% % \def\syst_helpers_double_empty_one_yes[#1]%
+% % {\firstargumenttrue
+% % \toksapp\t_syst_aux{[{#1}]}%
+% % \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop}
+% %
+% % nicer is:
+%
+% \def\syst_helpers_double_empty_one_yes[#+]%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop}
+%
+% \def\syst_helpers_double_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \the\t_syst_aux[][]}
+%
+% \def\syst_helpers_double_empty_two_nop
+% {\secondargumentfalse
+% \the\t_syst_aux[]}
+%
+% % Triple
+%
+% \protected\def\dotripleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_triple_empty_one_yes\syst_helpers_triple_empty_one_nop}
+%
+% \def\syst_helpers_triple_empty_one_yes[#+]%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_triple_empty_two_yes\syst_helpers_triple_empty_two_nop}
+%
+% \def\syst_helpers_triple_empty_two_yes[#+]%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_triple_empty_three_yes\syst_helpers_triple_empty_three_nop}
+%
+% \def\syst_helpers_triple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \the\t_syst_aux[][][]}
+%
+% \def\syst_helpers_triple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \the\t_syst_aux[][]}
+%
+% \def\syst_helpers_triple_empty_three_nop
+% {\thirdargumentfalse
+% \the\t_syst_aux[]}
+%
+% %D Quadruple:
+%
+% \protected\def\doquadrupleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_quadruple_empty_one_yes\syst_helpers_quadruple_empty_one_nop}
+%
+% \def\syst_helpers_quadruple_empty_one_yes[#+]%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quadruple_empty_two_yes\syst_helpers_quadruple_empty_two_nop}
+%
+% \def\syst_helpers_quadruple_empty_two_yes[#+]%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quadruple_empty_three_yes\syst_helpers_quadruple_empty_three_nop}
+%
+% \def\syst_helpers_quadruple_empty_three_yes[#+]%
+% {\thirdargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quadruple_empty_four_yes\syst_helpers_quadruple_empty_four_nop}
+%
+% \def\syst_helpers_quadruple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \the\t_syst_aux[][][][]}
+%
+% \def\syst_helpers_quadruple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \the\t_syst_aux[][][]}
+%
+% \def\syst_helpers_quadruple_empty_three_nop
+% {\thirdargumentfalse
+% \fourthargumentfalse
+% \the\t_syst_aux[][]}
+%
+% \def\syst_helpers_quadruple_empty_four_nop
+% {\fourthargumentfalse
+% \the\t_syst_aux[]}
+%
+% %D Quintuple:
+%
+% \protected\def\doquintupleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_quintuple_empty_one_yes\syst_helpers_quintuple_empty_one_nop}
+%
+% \def\syst_helpers_quintuple_empty_one_yes[#+]%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quintuple_empty_two_yes\syst_helpers_quintuple_empty_two_nop}
+%
+% \def\syst_helpers_quintuple_empty_two_yes[#+]%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quintuple_empty_three_yes\syst_helpers_quintuple_empty_three_nop}
+%
+% \def\syst_helpers_quintuple_empty_three_yes[#+]%
+% {\thirdargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quintuple_empty_four_yes\syst_helpers_quintuple_empty_four_nop}
+%
+% \def\syst_helpers_quintuple_empty_four_yes[#+]%
+% {\fourthargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_quintuple_empty_five_yes\syst_helpers_quintuple_empty_five_nop}
+%
+% \def\syst_helpers_quintuple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux[][][][][]}
+%
+% \def\syst_helpers_quintuple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux[][][][]}
+%
+% \def\syst_helpers_quintuple_empty_three_nop
+% {\thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux[][][]}
+%
+% \def\syst_helpers_quintuple_empty_four_nop
+% {\fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux[][]}
+%
+% \def\syst_helpers_quintuple_empty_five_nop
+% {\fifthargumentfalse
+% \the\t_syst_aux[]}
+%
+% %D Sixtuple:
+%
+% \protected\def\dosixtupleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_sixtuple_empty_one_yes\syst_helpers_sixtuple_empty_one_nop}
+%
+% \def\syst_helpers_sixtuple_empty_one_yes[#+]%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_sixtuple_empty_two_yes\syst_helpers_sixtuple_empty_two_nop}
+%
+% \def\syst_helpers_sixtuple_empty_two_yes[#+]%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_sixtuple_empty_three_yes\syst_helpers_sixtuple_empty_three_nop}
+%
+% \def\syst_helpers_sixtuple_empty_three_yes[#+]%
+% {\thirdargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_sixtuple_empty_four_yes\syst_helpers_sixtuple_empty_four_nop}
+%
+% \def\syst_helpers_sixtuple_empty_four_yes[#+]%
+% {\fourthargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_sixtuple_empty_five_yes\syst_helpers_sixtuple_empty_five_nop}
+%
+% \def\syst_helpers_sixtuple_empty_five_yes[#+]%
+% {\fifthargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_sixtuple_empty_six_yes\syst_helpers_sixtuple_empty_six_nop}
+%
+% \def\syst_helpers_sixtuple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \the\t_syst_aux[][][][][][]}
+%
+% \def\syst_helpers_sixtuple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \the\t_syst_aux[][][][][]}
+%
+% \def\syst_helpers_sixtuple_empty_three_nop
+% {\thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \the\t_syst_aux[][][][]}
+%
+% \def\syst_helpers_sixtuple_empty_four_nop
+% {\fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \the\t_syst_aux[][][]}
+%
+% \def\syst_helpers_sixtuple_empty_five_nop
+% {\fifthargumentfalse
+% \sixthargumentfalse
+% \the\t_syst_aux[][]}
+%
+% \def\syst_helpers_sixtuple_empty_six_nop
+% {\sixthargumentfalse
+% \the\t_syst_aux[]}
+%
+% %D Seventuple:
+%
+% \protected\def\doseventupleempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand[\syst_helpers_seventuple_empty_one_yes\syst_helpers_seventuple_empty_one_nop}
+%
+% \def\syst_helpers_seventuple_empty_one_yes[#+]%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_seventuple_empty_two_yes\syst_helpers_seventuple_empty_two_nop}
+%
+% \def\syst_helpers_seventuple_empty_two_yes[#+]%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_seventuple_empty_three_yes\syst_helpers_seventuple_empty_three_nop}
+%
+% \def\syst_helpers_seventuple_empty_three_yes[#+]%
+% {\thirdargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_seventuple_empty_four_yes\syst_helpers_seventuple_empty_four_nop}
+%
+% \def\syst_helpers_seventuple_empty_four_yes[#+]%
+% {\fourthargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_seventuple_empty_five_yes\syst_helpers_seventuple_empty_five_nop}
+%
+% \def\syst_helpers_seventuple_empty_five_yes[#+]%
+% {\fifthargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_seventuple_empty_six_yes\syst_helpers_seventuple_empty_six_nop}
+%
+% \def\syst_helpers_seventuple_empty_six_yes[#+]%
+% {\sixthargumenttrue
+% \toksapp\t_syst_aux{[#1]}%
+% \futureexpand[\syst_helpers_seventuple_empty_seven_yes\syst_helpers_seventuple_empty_seven_nop}
+%
+% \def\syst_helpers_seventuple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \seventhargumentfalse
+% \the\t_syst_aux[][][][][][][]}
+%
+% \def\syst_helpers_seventuple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \seventhargumentfalse
+% \the\t_syst_aux[][][][][][]}
+%
+% \def\syst_helpers_seventuple_empty_three_nop
+% {\thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \seventhargumentfalse
+% \the\t_syst_aux[][][][][]}
+%
+% \def\syst_helpers_seventuple_empty_four_nop
+% {\fourthargumentfalse
+% \fifthargumentfalse
+% \sixthargumentfalse
+% \seventhargumentfalse
+% \the\t_syst_aux[][][][]}
+%
+% \def\syst_helpers_seventuple_empty_five_nop
+% {\fifthargumentfalse
+% \sixthargumentfalse
+% \seventhargumentfalse
+% \the\t_syst_aux[][][]}
+%
+% \def\syst_helpers_seventuple_empty_six_nop
+% {\sixthargumentfalse
+% \seventhargumentfalse
+% \the\t_syst_aux[][]}
+%
+% \def\syst_helpers_seventuple_empty_seven_nop
+% {\seventhargumentfalse
+% \the\t_syst_aux[]}
+
+%tolerant\def\syst_single_empty#1\relax[#+]%
+\tolerant\def\syst_single_empty#1\relax[#2]%
+ {\ifarguments
+ \or\firstargumentfalse
+ \or\firstargumenttrue
+ \fi
+ #1[#2]}
+
+%tolerant\def\syst_double_empty#1\relax[#2]#*[#3]%
+\tolerant\def\syst_double_empty#1\relax[#+]#*[#+]%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse
+ \or\firstargumenttrue \secondargumentfalse
+ \or\firstargumenttrue \secondargumenttrue
+ \fi
+ #1[#2][#3]}
+
+%tolerant\def\syst_triple_empty#1\relax[#2]#*[#3]#*[#4]%
+\tolerant\def\syst_triple_empty#1\relax[#+]#*[#+]#*[#+]%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue
+ \fi
+ #1[#2][#3][#4]}
+
+%tolerant\def\syst_quadruple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]%
+\tolerant\def\syst_quadruple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue
+ \fi
+ #1[#2][#3][#4][#5]}
+
+%\tolerant\def\syst_quintuple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]#*[#6]%
+\tolerant\def\syst_quintuple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]#*[#+]%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue
+ \fi
+ #1[#2][#3][#4][#5][#6]}
+
+%tolerant\def\syst_sixtuple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]#*[#6]#*[#7]%
+\tolerant\def\syst_sixtuple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]#*[#+]#*[#+]%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue
+ \fi
+ #1[#2][#3][#4][#5][#6][#7]}
+
+%tolerant\def\syst_seventuple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]#*[#6]#*[#7]#*[#8]%
+\tolerant\def\syst_seventuple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]#*[#+]#*[#+]#*[#+]%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse\seventhargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumenttrue
+ \fi
+ #1[#2][#3][#4][#5][#6][#7][#8]}
+
+\permanent\protected\def\dosingleempty #1{\syst_single_empty #1\relax}
+\permanent\protected\def\dodoubleempty #1{\syst_double_empty #1\relax}
+\permanent\protected\def\dotripleempty #1{\syst_triple_empty #1\relax}
+\permanent\protected\def\doquadrupleempty #1{\syst_quadruple_empty #1\relax}
+\permanent\protected\def\doquintupleempty #1{\syst_quintuple_empty #1\relax}
+\permanent\protected\def\dosixtupleempty #1{\syst_sixtuple_empty #1\relax}
+\permanent\protected\def\doseventupleempty#1{\syst_seventuple_empty#1\relax}
+
+%D Aliases:
+
+\aliased\let\dosingleargument \dosingleempty
+\aliased\let\dodoubleargument \dodoubleempty
+\aliased\let\dotripleargument \dotripleempty
+\aliased\let\doquadrupleargument \doquadrupleempty
+\aliased\let\doquintupleargument \doquintupleempty
+\aliased\let\dosixtupleargument \dosixtupleempty
+\aliased\let\doseventupleargument\doseventupleempty
+
+%D \macros
+%D {strippedcsname}
+%D
+%D The next macro can be very useful when using \type{\csname} like in:
+%D
+%D \starttyping
+%D \csname if\strippedcsname\something\endcsname
+%D \stoptyping
+%D
+%D This expands to \type{\ifsomething}.
+
+\aliased\let\strippedcsname\csstring
+
+%D \macros
+%D {complexorsimple,complexorsimpleempty}
+%D
+%D Setups can be optional. A command expecting a setup is prefixed by \type
+%D {\complex}, a command without one gets the prefix \type {\simple}. Commands like
+%D this can be defined by:
+%D
+%D \starttyping
+%D \complexorsimple\command
+%D \stoptyping
+%D
+%D When \type{\command} is followed by a \type{[setup]}, then
+%D
+%D \starttyping
+%D \complexcommand [setup]
+%D \stoptyping
+%D
+%D executes, else we get
+%D
+%D \starttyping
+%D \simplecommand
+%D \stoptyping
+%D
+%D An alternative for \type{\complexorsimple} is:
+%D
+%D \starttyping
+%D \complexorsimpleempty {command}
+%D \stoptyping
+%D
+%D Depending on the presence of \type{[setup]}, this one leads to one of:
+%D
+%D \starttyping
+%D \complexcommando [setup]
+%D \complexcommando []
+%D \stoptyping
+%D
+%D Many \CONTEXT\ commands started as complex or simple ones, but changed into more
+%D versatile (more object oriented) ones using the \type {\get..argument} commands.
+
+\permanent\protected\def\complexorsimple#1%
+ {\doifelsenextoptional
+ {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
+ {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}}
+
+\permanent\protected\def\complexorsimpleempty#1%
+ {\doifelsenextoptional
+ {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
+ {\firstargumentfalse\csname\s!complex\csstring#1\endcsname[]}}
+
+%D \macros
+%D {definecomplexorsimple,definecomplexorsimpleempty}
+%D
+%D The previous commands are used that often that we found it worthwile to offer two
+%D more alternatives. Watch the build in protection.
+
+\protected\def\syst_helpers_complex_or_simple#1#2%
+ {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}}
+
+\protected\def\syst_helpers_complex_or_simple_empty#1%
+ {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}}
+
+\permanent\protected\def\definecomplexorsimple#1%
+ {\protected\edef#1{\syst_helpers_complex_or_simple
+ \expandafter\noexpand\csname\s!complex\csstring#1\endcsname
+ \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}}
+
+\permanent\protected\def\definecomplexorsimpleempty#1%
+ {\protected\edef#1{\syst_helpers_complex_or_simple_empty
+ \expandafter\noexpand\csname\s!complex\csstring#1\endcsname}}
+
+%D These commands are called as:
+%D
+%D \starttyping
+%D \definecomplexorsimple\command
+%D \stoptyping
+%D
+%D Of course, we must have available
+%D
+%D \starttyping
+%D \def\complexcommand[#1]{...}
+%D \def\simplecommand {...}
+%D \stoptyping
+%D
+%D Using this construction saves a few string now and then.
+
+%D \macros
+%D {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty,
+%D doquadruplegroupempty, doquintuplegroupempty}
+%D
+%D We've already seen some commands that take care of
+%D optional arguments between \type{[]}. The next two commands
+%D handle the ones with \type{{}}. They are called as:
+%D
+%D \starttyping
+%D \dosinglegroupempty \ineedONEargument
+%D \dodoublegroupempty \ineedTWOarguments
+%D \dotriplegroupempty \ineedTHREEarguments
+%D \doquadruplegroupempty \ineedFOURarguments
+%D \doquintuplegroupempty \ineedFIVEarguments
+%D \stoptyping
+%D
+%D We can add additional definitions later when we have defined \type {\appendtoks}.
+
+\newconditional\c_syst_helpers_permit_spaces_between_groups
+
+\permanent\protected\def \permitspacesbetweengroups{\settrue \c_syst_helpers_permit_spaces_between_groups}
+\permanent\protected\def\dontpermitspacesbetweengroups{\setfalse\c_syst_helpers_permit_spaces_between_groups}
+
+\dontpermitspacesbetweengroups
+
+%D We can avoid the nasty if handling in \type {syst-gen} by splitting the lot in
+%D pieces so that we have no nested \type {\nextarguments} potentially being an
+%D \type {conditional} token. Okay, these macros are not called that often but it
+%D saves crap when tracing.
+
+% \protected\def\dosinglegroupempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_single_empty_one_yes\syst_helpers_single_group_empty_one_nop}
+%
+% \def\syst_helpers_single_group_empty_one_nop
+% {\firstargumentfalse
+% \the\t_syst_aux{}}
+%
+% \protected\def\dodoublegroupempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_double_empty_one_yes\syst_helpers_group_double_empty_one_nop}
+%
+% % \def\syst_helpers_group_double_empty_one_yes#1%
+% % {\firstargumenttrue
+% % \toksapp\t_syst_aux{{#1}}%
+% % \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop}
+% %
+% % nicer is:
+%
+% \def\syst_helpers_group_double_empty_one_yes#+%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop}
+%
+% \def\syst_helpers_group_double_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \the\t_syst_aux{}{}}
+%
+% \def\syst_helpers_group_double_empty_two_nop
+% {\secondargumentfalse
+% \the\t_syst_aux{}}
+%
+% \protected\def\dotriplegroupempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_triple_empty_one_yes\syst_helpers_group_triple_empty_one_nop}
+%
+% \def\syst_helpers_group_triple_empty_one_yes#+%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_triple_empty_two_yes\syst_helpers_group_triple_empty_two_nop}
+%
+% \def\syst_helpers_group_triple_empty_two_yes#+%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_triple_empty_three_yes\syst_helpers_group_triple_empty_three_nop}
+%
+% \def\syst_helpers_group_triple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \the\t_syst_aux{}{}{}}
+%
+% \def\syst_helpers_group_triple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \the\t_syst_aux{}{}}
+%
+% \def\syst_helpers_group_triple_empty_three_nop
+% {\thirdargumentfalse
+% \the\t_syst_aux{}}
+%
+% \protected\def\doquadruplegroupempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_one_yes\syst_helpers_group_quadruple_empty_one_nop}
+%
+% \def\syst_helpers_group_quadruple_empty_one_yes#+%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_two_yes\syst_helpers_group_quadruple_empty_two_nop}
+%
+% \def\syst_helpers_group_quadruple_empty_two_yes#+%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_three_yes\syst_helpers_group_quadruple_empty_three_nop}
+%
+% \def\syst_helpers_group_quadruple_empty_three_yes#+%
+% {\thirdargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_quadruple_empty_four_yes\syst_helpers_group_quadruple_empty_four_nop}
+%
+% \def\syst_helpers_group_quadruple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \the\t_syst_aux{}{}{}{}}
+%
+% \def\syst_helpers_group_quadruple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \the\t_syst_aux{}{}{}}
+%
+% \def\syst_helpers_group_quadruple_empty_three_nop
+% {\thirdargumentfalse
+% \fourthargumentfalse
+% \the\t_syst_aux{}{}}
+%
+% \def\syst_helpers_group_quadruple_empty_four_nop
+% {\fourthargumentfalse
+% \the\t_syst_aux{}}
+%
+% \protected\def\doquintuplegroupempty#1%
+% {\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_one_yes\syst_helpers_group_quintuple_empty_one_nop}
+%
+% \def\syst_helpers_group_quintuple_empty_one_yes#+%
+% {\firstargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_two_yes\syst_helpers_group_quintuple_empty_two_nop}
+%
+% \def\syst_helpers_group_quintuple_empty_two_yes#+%
+% {\secondargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_three_yes\syst_helpers_group_quintuple_empty_three_nop}
+%
+% \def\syst_helpers_group_quintuple_empty_three_yes#+%
+% {\thirdargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_four_yes\syst_helpers_group_quintuple_empty_four_nop}
+%
+% \def\syst_helpers_group_quintuple_empty_four_yes#+%
+% {\fourthargumenttrue
+% \toksapp\t_syst_aux{#1}%
+% \futureexpand\bgroup\syst_helpers_quintuple_empty_five_yes\syst_helpers_group_quintuple_empty_five_nop}
+%
+% \def\syst_helpers_group_quintuple_empty_one_nop
+% {\firstargumentfalse
+% \secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux{}{}{}{}{}}
+%
+% \def\syst_helpers_group_quintuple_empty_two_nop
+% {\secondargumentfalse
+% \thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux{}{}{}{}}
+%
+% \def\syst_helpers_group_quintuple_empty_three_nop
+% {\thirdargumentfalse
+% \fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux{}{}{}}
+%
+% \def\syst_helpers_group_quintuple_empty_four_nop
+% {\fourthargumentfalse
+% \fifthargumentfalse
+% \the\t_syst_aux{}{}}
+%
+% \def\syst_helpers_group_quintuple_empty_five_nop
+% {\fifthargumentfalse
+% \the\t_syst_aux{}}
+
+\tolerant\def\syst_single_group_empty#1\relax#_%
+ {\ifarguments
+ \or\firstargumentfalse
+ \or\firstargumenttrue
+ \fi
+ #1#2}
+
+\tolerant\def\syst_double_group_empty#1\relax#_#*#_%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse
+ \or\firstargumenttrue \secondargumentfalse
+ \or\firstargumenttrue \secondargumenttrue
+ \fi
+ #1#2#3}
+
+\tolerant\def\syst_triple_group_empty#1\relax#_#*#_#*#_%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue
+ \fi
+ #1#2#3#4}
+
+\tolerant\def\syst_quadruple_group_empty#1\relax#_#*#_#*#_#*#_%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue
+ \fi
+ #1#2#3#4#5}
+
+\tolerant\def\syst_quintuple_group_empty#1\relax#_#*#_#*#_#*#_#*#_%
+ {\ifarguments
+ \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse
+ \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue
+ \fi
+ #1#2#3#4#5#6}
+
+\permanent\protected\def\dosinglegroupempty #1{\syst_single_group_empty #1\relax}
+\permanent\protected\def\dodoublegroupempty #1{\syst_double_group_empty #1\relax}
+\permanent\protected\def\dotriplegroupempty #1{\syst_triple_group_empty #1\relax}
+\permanent\protected\def\doquadruplegroupempty #1{\syst_quadruple_group_empty #1\relax}
+\permanent\protected\def\doquintuplegroupempty #1{\syst_quintuple_group_empty #1\relax}
+
+%D These macros can explictly take care of spaces, which means that the next
+%D definition and calls are valid:
+%D
+%D \starttyping
+%D \def\test#1#2#3{[#1#2#3]}
+%D
+%D \dotriplegroupempty\test {a}{b}{c}
+%D \dotriplegroupempty\test {a}{b}
+%D \dotriplegroupempty\test {a}
+%D \dotriplegroupempty\test
+%D \dotriplegroupempty\test {a} {b} {c}
+%D \dotriplegroupempty\test {a} {b}
+%D \dotriplegroupempty\test
+%D {a}
+%D {b}
+%D \stoptyping
+%D
+%D And alike.
+
+%D \macros
+%D {firstofoneargument, firstoftwoarguments, firstofthreearguments
+%D secondoftwoarguments, secondofthreearguments,
+%D thirdofthreearguments}
+%D
+%D The next six macros (dedicated to Taco) can conveniently used to select
+%D arguments. Their names explain their functionality.
+
+\permanent\def\firstofoneargument #1{#1}
+
+\permanent\def\firstoftwoarguments #1#-{#1}
+\permanent\def\secondoftwoarguments #-#1{#1}
+
+\permanent\def\firstofthreearguments #1#-#-{#1}
+\permanent\def\secondofthreearguments #-#1#-{#1}
+\permanent\def\thirdofthreearguments #-#-#1{#1}
+
+\permanent\def\firstoffourarguments #1#-#-#-{#1}
+\permanent\def\secondoffourarguments #-#1#-#-{#1}
+\permanent\def\thirdoffourarguments #-#-#1#-{#1}
+\permanent\def\fourthoffourarguments #-#-#-#1{#1}
+
+\permanent\def\firstoffivearguments #1#-#-#-#-{#1}
+\permanent\def\secondoffivearguments #-#1#-#-#-{#1}
+\permanent\def\thirdoffivearguments #-#-#1#-#-{#1}
+\permanent\def\fourthoffivearguments #-#-#-#1#-{#1}
+\permanent\def\fifthoffivearguments #-#-#-#-#1{#1}
+
+\permanent\def\firstofsixarguments #1#-#-#-#-#-{#1}
+\permanent\def\secondofsixarguments#-#1#-#-#-#-{#1}
+\permanent\def\thirdofsixarguments #-#-#1#-#-#-{#1}
+\permanent\def\fourthofsixarguments#-#-#-#1#-#-{#1}
+\permanent\def\fifthofsixarguments #-#-#-#-#1#-{#1}
+\permanent\def\sixthofsixarguments #-#-#-#-#-#1{#1}
+
+\permanent\protected\def\firstofoneunexpanded #1{#1}
+
+\permanent\protected\def\firstoftwounexpanded #1#-{#1}
+\permanent\protected\def\secondoftwounexpanded #-#1{#1}
+
+\permanent\protected\def\firstofthreeunexpanded #1#-#-{#1}
+\permanent\protected\def\secondofthreeunexpanded#-#1#-{#1}
+\permanent\protected\def\thirdofthreeunexpanded #-#-#1{#1}
+
+%D \macros
+%D {globalletempty,letempty,
+%D letvalueempty,letgvalueempty,
+%D letvaluerelax,letgvaluerelax}
+%D
+%D Trivial:
+
+\permanent\protected\def\letempty #1{\let #1\empty}
+\permanent\protected\def\globalletempty#1{\glet#1\empty}
+
+\permanent\protected\def\letvalueempty #1{\letcsname #1\endcsname\empty}
+\permanent\protected\def\letgvalueempty#1{\gletcsname#1\endcsname\empty}
+\permanent\protected\def\letvaluerelax #1{\letcsname #1\endcsname\relax}
+\permanent\protected\def\letgvalurelax #1{\gletcsname#1\endcsname\relax}
+
+\permanent\protected\def\relaxvalueifundefined#1%
+ {\ifcsname#1\endcsname \else
+ \aliased\letcsname#1\endcsname\relax
+ \fi}
+
+%D \macros
+%D {wait}
+%D
+%D The next macro hardly needs explanation. Because no nesting is to be expected, we
+%D can reuse \type {\wait} within \type {\wait} itself.
+
+\permanent\protected\def\wait
+ {\begingroup
+ \read16 to \wait
+ \endgroup}
+
+% %D \macros
+% %D {writestring,writeline,writestatus}
+% %D
+% %D Maybe one didn't notice, but we've already introduced a macro for showing
+% %D messages. In the multi||lingual modules, we will also introduce a mechanism for
+% %D message passing. For the moment we stick to the core macros:
+% %D
+% %D \starttyping
+% %D \writestring {string}
+% %D \writeline
+% %D \writestatus {category} {message}
+% %D \stoptyping
+% %D
+% %D Messages are formatted. One can provide the maximum with of the identification
+% %D string with the macro \type {\statuswidth}.
+%
+% \setnewconstant\statuswidth 15
+% \setnewconstant\statuswrite 128 % \pluscxxviii
+%
+% \ifdefined\writestring \else
+%
+% \protected\def\writestring{\immediate\write\statuswrite}
+% \protected\def\writeline {\writestring{}}
+%
+% \fi
+%
+% \protected\def\normalwritestatus#1#2%
+% {\writestring{\expandafter\syst_helpers_split_status_yes\expandafter\statuswidth#1%
+% \space\space\space\space\space\space\space
+% \space\space\space\space\space\space\space
+% \space\space\space\space\space\space\end
+% \space:\space#2}}
+%
+% \def\syst_helpers_split_status_yes#1#2%
+% {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2%
+% \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}}
+%
+% \def\syst_helpers_split_status_nop#1\end
+% {}
+
+%D \macros
+%D {immediatemessage}
+%D
+%D A fully expandable message:
+
+% \immediatemessage % {} mandate / defined at the lua end
+
+%D \macros
+%D {rawgetparameters}
+%D
+%D A raw and dirty alternative for \type {\getparameters}; no checking is done!
+
+\def\syst_helpers_grab_raw_parameter#1=#2,%
+ {\ifarguments\or\or
+ \defcsname\m_syst_parameter_n#1\endcsname{#2}%
+ \expandafter\syst_helpers_grab_raw_parameter_next
+ \fi}
+
+\def\syst_helpers_grab_raw_parameter_next
+ {\expandafterspaces\syst_helpers_grab_raw_parameter}
+
+\permanent\protected\def\rawgetparameters#1[#2]#*[#3]%
+ {\def\m_syst_parameter_n{#2}%
+ %\expandafterspaces\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments}
+ \expandafter\expandafterspaces\expandafter\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments}
+
+%D \macros
+%D {doglobal,
+%D redoglobal,dodoglobal,resetglobal}
+%D
+%D The two macros \type {\redoglobal} and \type{\dodoglobal} are used in this and
+%D some other modules to enforce a user specified \type {\doglobal} action. The last
+%D and often only global assignment in a macro is done with \type {\dodoglobal}, but
+%D all preceding ones with \type {\redoglobal}. When using only alternatives, one
+%D can reset this mechanism with \type {\resetglobal}.
+
+\permanent\protected\def\resetglobal
+ {\enforced\let\redoglobal\relax
+ \enforced\let\dodoglobal\relax}
+
+\resetglobal
+
+\permanent\protected\def\doglobal
+ {\ifrelax\redoglobal
+ \enforced\let\redoglobal\global
+ \enforced\let\dodoglobal\syst_helpers_dodo_global
+ \fi}
+
+\def\syst_helpers_dodo_global
+ {\resetglobal\global}
+
+\let\syst_helpers_redo_global\relax
+
+\permanent\protected\def\saveglobal
+ {\let\syst_helpers_dodo_global\dodoglobal
+ \let\syst_helpers_redo_global\redoglobal}
+
+\permanent\protected\def\restoreglobal
+ {\enforced\let\redoglobal\syst_helpers_redo_global
+ \enforced\let\dodoglobal\syst_helpers_dodo_global}
+
+%D Whatever (will be overtoaded):
+
+\protected\def\define#1%
+ {\ifdefined#1%
+ \message{[\noexpand#1is already defined]}%
+ \protected\expandafter\def\expandafter\gobbleddefinition
+ \else
+ \protected\expandafter\def
+ \fi#1}
+
+\protected\def\redefine#1%
+ {\ifdefined#1%
+ \message{[\noexpand#1is redefined]}%
+ \fi
+ \protected\def#1}
+
+% \permanent\protected\def\definemacro#1%
+% {\ifdefined#1%
+% \message{[\noexpand#1is already defined]}%
+% \protected\expandafter\def\expandafter\gobbleddefinition
+% \else
+% \protected\expandafter\def
+% \fi#1}
+
+% \define\hans{hans}
+% \redefine\hans{hans}
+% \define\hans#1[]#2#3{hans}
+
+%D The next variant fits nicely in the setups syntax:
+%D
+%D \starttyping
+%D \starttexdefinition bagger [#1] #2
+%D oeps
+%D #1
+%D oeps
+%D \stoptexdefinition
+%D
+%D \bagger [a] {b}
+%D \stoptyping
+
+% \starttexdefinition test
+% oeps
+% \stoptexdefinition
+%
+% [\test]
+
+\bgroup \obeylines
+
+\permanent\protected\gdef\starttexdefinition%
+ {\bgroup%
+ \obeylines%
+ \syst_helpers_start_tex_definition}
+
+\gdef\syst_helpers_start_tex_definition#1
+ {\catcode\endoflineasciicode\ignorecatcode%
+ \clf_texdefinition_one{#1}}
+
+\aliased\glet\stoptexdefinition\relax
+
+\permanent\gdef\dostarttexdefinition#1\stoptexdefinition%
+ {\egroup%
+ \clf_texdefinition_two{#1}}
+
+\egroup
+
+% \protected\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace
+
+\permanent\protected\def\texdefinition#1{\begincsname#1\endcsname}
+
+%D \macros
+%D {newcounter,
+%D increment,decrement}
+%D
+%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, but fortunately we
+%D can store numbers in a macro. We can increment such pseudo \COUNTERS\ with \type
+%D {\increment}.
+%D
+%D \starttyping
+%D \increment(\counter,20)
+%D \increment(\counter,-4)
+%D \increment(\counter)
+%D \increment\counter
+%D \stoptyping
+%D
+%D After this sequence of commands, the value of \type {\counter} is 20, 16, 17
+%D and~18. Of course there is also the complementary command \type {\decrement}.
+%D
+%D Global assignments are possible too, using \type{\doglobal}:
+%D
+%D \starttyping
+%D \doglobal\increment\counter
+%D \stoptyping
+%D
+%D When \type {\counter} is undefined, it's value is initialized at~0. It is
+%D nevertheless better to define a \COUNTER\ explicitly. One reason could be that
+%D the \COUNTER\ can be part of a test with \type {\ifnum} and this conditional does
+%D not accept undefined macro's. The \COUNTER\ in our example can for instance be
+%D defined with:
+%D
+%D \starttyping
+%D \newcounter\counter
+%D \stoptyping
+%D
+%D The command \type {\newcounter} must not be confused with \type {\newcount}! Of
+%D course this mechanism is much slower than using \TEX's \COUNTERS\ directly. In
+%D practice \COUNTERS\ (and therefore our pseudo counters too) are seldom the
+%D bottleneck in the processing of a text. Apart from some other incompatilities we
+%D want to mention a pitfal when using \type {\ifnum}.
+%D
+%D \starttyping
+%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi
+%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi
+%D \stoptyping
+%D
+%D In the first test, \TEX\ continues it's search for the second number after
+%D reading \type {\pseudocounter}, while in the second test, it stops reading after
+%D having encountered a real one. Tests like the first one therefore can give
+%D unexpected results, for instance execution of \type {\doif} even if both numbers
+%D are unequal.
+
+\immutable\def\zerocountervalue{0}
+
+\permanent\protected\def\newcounter#1%
+ {\dodoglobal\let#1\zerocountervalue}
+
+%D Nowadays we don't mind a few more tokens if we can gain a bit of speed.
+
+\def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+\plusone \relax}}
+\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+\minusone\relax}}
+
+\def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+#2\relax}}
+\def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi-#2\relax}}
+
+\def\syst_helpers_do_do_increment(#1%
+ {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}%
+ \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}%
+ \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
+
+\def\syst_helpers_do_do_decrement(#1%
+ {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}%
+ \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}%
+ \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
+
+\permanent\protected\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment}
+\permanent\protected\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement}
+
+\permanent\protected\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}}
+\permanent\protected\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}}
+
+\permanent\protected\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname}
+\permanent\protected\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname}
+
+%D \macros
+%D {newsignal}
+%D
+%D When writing advanced macros, we cannot do without signaling. A signal is a small
+%D (invisible) kern or penalty that signals the next macro that something just
+%D happened. This macro can take any action depending on the previous signal.
+%D Signals must be unique and the next macro takes care of that.
+%D
+%D \starttyping
+%D \newsignal\somesignal
+%D \stoptyping
+%D
+%D Signals old dimensions and can be used in skips, kerns and tests like \type
+%D {\ifdim}.
+
+\newdimen\d_syst_maximum_signal % step is about 0.00025pt
+
+\permanent\protected\def\newsignal#1%
+ {\ifdefined#1\else
+ \advance\d_syst_maximum_signal2\scaledpoint % to be save in rounding
+ \immutable\dimensiondef#1\d_syst_maximum_signal
+ \fi}
+
+%D \macros
+%D {strippedcsname}
+%D
+%D The next macro can be very useful when using \type {\csname} like in:
+%D
+%D \starttyping
+%D \csname if\strippedcsname\something\endcsname
+%D \stoptyping
+
+\aliased\let\checkedstrippedcsname\csstring
+
+%D \macros
+%D {savenormalmeaning}
+%D
+%D We will use this one in:
+
+\permanent\protected\def\savenormalmeaning#1%
+ {\ifcsname normal\csstring#1\endcsname \else
+ \aliased\letcsname normal\csstring#1\endcsname#1%
+ \fi}
+
+%D \macros
+%D {dorecurse,recurselevel,recursedepth,
+%D dostepwiserecurse}
+%D
+%D \TEX\ does not offer us powerfull for||loop mechanisms. On the other hand its
+%D recursion engine is quite unique. We therefore identify the for||looping macros
+%D by this method. The most simple alternative is the one that only needs a number.
+%D
+%D \starttyping
+%D \dorecurse {n} {whatever we want}
+%D \stoptyping
+%D
+%D This macro can be nested without problems and therefore be used in situations
+%D where \PLAIN\ \TEX's \type {\loop} macro ungracefully fails. The current value of
+%D the counter is available in \type {\recurselevel}, before as well as after the
+%D \typ {whatever we wat} stuff.
+%D
+%D \starttyping
+%D \dorecurse % inner loop
+%D {10}
+%D {\recurselevel: % outer value
+%D \dorecurse % inner loop
+%D {\recurselevel} % outer value
+%D {\recurselevel} % inner value
+%D \dorecurse % inner loop
+%D {\recurselevel} % outer value
+%D {\recurselevel} % inner value
+%D \endgraf}
+%D \stoptyping
+%D
+%D In this example the first, second and fourth \type {\recurselevel} concern the
+%D outer loop, while the third and fifth one concern the inner loop. The depth of
+%D the nesting is available for inspection in \type {\recursedepth}.
+%D
+%D Both \type {\recurselevel} and \type {\recursedepth} are macros. The real
+%D \COUNTERS\ are hidden from the user because we don't want any interference.
+%D
+%D We now use the macro stack which is somewhat leaner and meaner and a little
+%D faster too.
+
+% left overs: too much \protected here
+
+\newcount\outerrecurse
+\newcount\innerrecurse
+
+\mutable\def\recursedepth{\the\outerrecurse}
+\mutable\def\recurselevel{0}
+
+\mutable\let\recurseaction\relax
+\mutable\let\recursestring\empty
+
+% \let\syst_helpers_stepwise_next\relax
+
+% \protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step
+% {\ifnum#1>#2\relax
+% \expandafter\syst_helpers_stepwise_recurse_nop
+% \else
+% \def\recurselevel{#1}%
+% \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter
+% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
+
+\protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step
+ {\ifnum#1>#2\relax
+ \expandafter\gobblefourarguments
+ \else
+ \def\recurselevel{#1}%
+% \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter
+% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
+ \doubleexpandafter\syst_helpers_stepwise_recurse_yes
+ \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
+
+\protected\def\syst_helpers_stepwise_recurse_yes
+ {\syst_helpers_recurse_content
+ \syst_helpers_stepwise_recurse}
+
+\protected\def\syst_helpers_stepwise_reverse#1#2#3% from to step
+ {\ifnum#1<#2\relax
+% \expandafter\syst_helpers_stepwise_recurse_nop
+ \expandafter\gobblefourarguments
+ \else
+ \def\recurselevel{#1}%
+ \innerrecurse#1\relax
+ \advance\innerrecurse#3\relax
+% \doubleexpandafter\syst_helpers_stepwise_reverse_yes\expandafter
+% \fi\expandafter{\the\innerrecurse}{#2}{#3}}
+ \doubleexpandafter\syst_helpers_stepwise_reverse_yes
+ \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
+
+\protected\def\syst_helpers_stepwise_reverse_yes
+ {\syst_helpers_recurse_content
+ \syst_helpers_stepwise_reverse}
+
+% \protected\def\syst_helpers_stepwise_exit
+% {\syst_helpers_stepwise_recurse_nop\relax}
+
+\permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse)
+ {\ifnum#1>\zerocount
+ #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}%
+ \fi}
+
+%D As we can see here, the simple command \type{\dorecurse} is a special case of the
+%D more general:
+%D
+%D \starttyping
+%D \dostepwiserecurse {from} {to} {step} {action}
+%D \stoptyping
+%D
+%D This commands accepts positive and negative steps. Illegal values are handles as
+%D good as possible and the macro accepts numbers and \COUNTERS.
+%D
+%D \starttyping
+%D \dostepwiserecurse {1} {10} {2} {...}
+%D \dostepwiserecurse {10} {1} {-2} {...}
+%D \stoptyping
+%D
+%D Because the simple case (n=1) is used often, we implement it more efficiently:
+
+\permanent\protected\def\dorecurse#1%
+ {\ifcase#1\relax
+ \expandafter\gobbletwoarguments
+ \or
+ \expandafter\syst_helpers_recurse_y
+ \else
+ \expandafter\syst_helpers_recurse_x
+ \fi{#1}}
+
+\protected\def\syst_helpers_recurse_indeed#1#2% from to
+% {\ifnum#1>#2 %
+ {\ifnum#1>#2\relax
+ \expandafter\syst_helpers_recurse_indeed_nop
+ \else
+ \def\recurselevel{#1}%
+ \innerrecurse#1\advance\innerrecurse\plusone
+ \doubleexpandafter\syst_helpers_recurse_indeed_yes
+ \fi\expandafter{\the\innerrecurse}{#2}}
+
+\protected\def\syst_helpers_recurse_indeed_yes
+ {\syst_helpers_recurse_content
+ \syst_helpers_recurse_indeed}
+
+\protected\def\syst_helpers_recurse_indeed_nop#0#0#0%
+ {}
+
+%D \macros
+%D {dowith}
+%D
+%D Here's a loop over whatever is in a list:
+%D
+%D \starttyping
+%D \dowith{a,b,c}{[#1]}
+%D \stoptyping
+
+\let\syst_helpers_with\gobbleoneargument
+
+\permanent\protected\def\dowith#1#2%
+ {\def\syst_helpers_with##1{#2}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_with}
+
+%D \macros
+%D {doloop,exitloop}
+%D
+%D Sometimes loops are not determined by counters, but by (a combinations of)
+%D conditions. We therefore implement a straightforward loop, which can only be left
+%D when we explictly exit it. Nesting is supported. First we present a more
+%D extensive alternative.
+%D
+%D \starttyping
+%D \doloop
+%D {Some kind of typesetting punishment \par
+%D \ifnum\pageno>100 \exitloop \fi}
+%D \stoptyping
+%D
+%D When needed, one can call for \type {\looplevel} and \type {\loopdepth}.
+
+\aliased\let\endofloop\donothing % maybe \syst_helpers_loop_end
+
+\permanent\protected\def\doloop#1%
+ {\global\advance\outerrecurse \plusone
+ \globalpushmacro\recurseaction
+ \globalpushmacro\recurselevel
+ \protected\gdef\recurseaction##1##2{#1}%
+ \enforced\let\endofloop\syst_helpers_loop
+ \syst_helpers_loop1}% no \plusone else \recurselevel wrong
+
+\protected\def\syst_helpers_loop#1%
+ {\def\recurselevel{#1}%
+ \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}}
+
+\protected\def\syst_helpers_loop_yes
+ {\syst_helpers_recurse_content
+ \endofloop}
+
+\protected\def\syst_helpers_loop_nop#0%
+ {\enforced\let\endofloop\syst_helpers_loop
+ \globalpopmacro\recurselevel
+ \globalpopmacro\recurseaction
+ \global\advance\outerrecurse\minusone}
+
+\permanent\protected\def\exitloop % \exitloop quits at end
+ {\enforced\let\endofloop\syst_helpers_loop_nop}
+
+\permanent\protected\def\exitloopnow#0\endofloop % \exitloopnow quits directly
+ {\syst_helpers_loop_nop}
+
+%D The loop is executed at least once, so beware of situations like:
+%D
+%D \starttyping
+%D \doloop {\exitloop some commands}
+%D \stoptyping
+%D
+%D It's just a matter of putting the text into the \type {\if} statement that should
+%D be there anyway, like in:
+%D
+%D \starttyping
+%D \doloop {\ifwhatever \exitloop \else some commands\fi}
+%D \stoptyping
+%D
+%D You can also quit a loop immediately, by using \type
+%D {\exitloopnow} instead. Beware, this is more sensitive
+%D for conditional errors.
+
+%D Krzysztof Leszczynski suggested to provide access to the level by means of a
+%D \type {#1}. I decided to pass the more frequently used level as \type {#1} and
+%D the less favoured depth as \type {#2}. The intended usage is:
+%D
+%D \starttyping
+%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]}
+%D
+%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test
+%D
+%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3]
+%D \stoptyping
+%D
+%D Since the hashed arguments are expanded, we don't need tricky expansion here.
+%D
+%D \starttyping
+%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}}
+%D \stoptyping
+
+\def\syst_helpers_recurse_content
+ {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}}
+
+\protected\def\syst_helpers_recurse_x#1#2%
+ {\global\advance\outerrecurse\plusone
+ \globalpushmacro\recurseaction
+ \globalpushmacro\recurselevel
+ \protected\gdef\recurseaction##1##2{#2}%
+ \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}%
+ \globalpopmacro\recurselevel
+ \globalpopmacro\recurseaction
+ \global\advance\outerrecurse\minusone}
+
+\protected\def\syst_helpers_recurse_y#1#2%
+ {\global\advance\outerrecurse\plusone
+ \globalpushmacro\recurseaction
+ \globalpushmacro\recurselevel
+ \let\recurselevel\!!plusone
+ \protected\gdef\recurseaction##1##2{#2}%
+ \syst_helpers_recurse_content
+ \globalpopmacro\recurselevel
+ \globalpopmacro\recurseaction
+ \global\advance\outerrecurse\minusone}
+
+% \protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
+% {\global\advance\outerrecurse \plusone
+% \globalpushmacro\recurseaction
+% \globalpushmacro\recurselevel
+% \protected\gdef\recurseaction##1##2{#4}%
+% \normalexpanded{\ifcmpnum#3\zerocount
+% \ifnum#1<#2\relax\relax % so we catch \number\numexpr xx without \relax's
+% \syst_helpers_stepwise_exit
+% \else
+% \syst_helpers_stepwise_reverse
+% \fi
+% \or
+% \syst_helpers_stepwise_exit
+% \or
+% \ifnum#2<#1\relax\relax % so we catch \number\numexpr xx without \relax's
+% \syst_helpers_stepwise_exit
+% \else
+% \syst_helpers_stepwise_recurse
+% \fi
+% \fi{\number#1}{\number#2}{\number#3}}%
+% \globalpopmacro\recurselevel
+% \globalpopmacro\recurseaction
+% \global\advance\outerrecurse\minusone}
+
+\permanent\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 ... todo: remove unused helpers
+ {\global\advance\outerrecurse \plusone
+ \globalpushmacro\recurseaction
+ \globalpushmacro\recurselevel
+ \protected\gdef\recurseaction##1##2{#4}%
+ \normalexpanded{\ifcmpnum#3\zerocount
+ \ifnum#1<#2\relax\relax % so we catch \number\numexpr xx without \relax's
+ \doubleexpandafter\gobbletwoarguments
+ \else
+ \tripleexpandafter\syst_helpers_stepwise_reverse
+ \fi
+ \or
+ \doubleexpandafter\gobbletwoarguments
+ \orelse\ifnum#2<#1\relax\relax % so we catch \number\numexpr xx without \relax's
+ \doubleexpandafter\gobbletwoarguments
+ \else
+ \doubleexpandafter\syst_helpers_stepwise_recurse
+ \fi\normalexpanded{{\number#1}{\number#2}{\number#3}}}%
+ \globalpopmacro\recurselevel
+ \globalpopmacro\recurseaction
+ \global\advance\outerrecurse\minusone}
+
+% \protected\def\syst_helpers_stepwise_recurse_nop#0#0#0#0%
+% {}
+
+\newcount\fastloopindex
+\newcount\fastloopfinal
+
+\let\m_syst_helpers_fast_loop_cs\relax
+
+\permanent\protected\def\dofastloopcs#1%
+ {\fastloopfinal#1\relax
+ \ifcase\fastloopfinal
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\syst_helpers_fast_loop_cs
+ \fi}
+
+\protected\def\syst_helpers_fast_loop_cs#1%
+ {\let\m_syst_helpers_fast_loop_cs#1%
+ \fastloopindex\plusone
+ \syst_helpers_fast_loop_cs_step}
+
+\protected\def\syst_helpers_fast_loop_cs_step
+ {\ifnum\fastloopindex>\fastloopfinal
+ \let\m_syst_helpers_fast_loop_cs\relax
+ \else
+ \m_syst_helpers_fast_loop_cs
+ \advance\fastloopindex\plusone
+ \expandafter\syst_helpers_fast_loop_cs_step
+ \fi}
+
+% Helper:
+
+\permanent\protected\def\resetrecurselevel{\let\recurselevel\!!zerocount}
+
+\let\recurselevel\!!zerocount
+
+% \appendtoks \resetrecurselevel \to \everydump
+
+%D \macros
+%D {doloopoverlist}
+%D
+%D \starttyping
+%D \doloopoverlist {red,green,blue} {
+%D \setuppalet[\recursestring]
+%D \doloopoverlist {light,normal,dark} {
+%D \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par
+%D }
+%D }
+%D \stoptyping
+%D
+%D or:
+%D
+%D \starttyping
+%D \doloopoverlist {red,green,blue} {
+%D \setuppalet[#1]
+%D \doloopoverlist {light,normal,dark} {
+%D \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par
+%D }
+%D }
+%D \stoptyping
+
+\permanent\protected\def\doloopoverlist#1#2%
+ {\global\advance\outerrecurse\plusone
+ \globalpushmacro\recurseaction
+ \globalpushmacro\recursestring
+ \protected\gdef\recurseaction##1{\edef\recursestring{##1}#2}%
+ \processcommacommand[#1]\recurseaction
+ \globalpopmacro\recursestring
+ \globalpopmacro\recurseaction
+ \global\advance\outerrecurse\minusone}
+
+%D This is some \LMTX\ experiment:
+%D
+%D Think of:
+%D
+%D \starttyping
+%D \doloopovermatch {(\letterpercent w+) *(\letterpercent w*)} {aa bb cc dd} {
+%D [
+%D \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#1} {(##1 ##2)}
+%D \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#2} {(##1 ##2)}
+%D ]
+%D }
+%D
+%D and:
+%D
+%D \stoptyping
+%D
+%D \starttyping
+%D \def\MyMacro#1{(#1)} \ctxluamatch \MyMacro {(.)} {abcd}
+%D \stoptyping
+
+\aliased\let\matchloopcommand\relax
+
+\permanent\protected\def\doloopovermatch#1#2#3%
+ {\pushmacro\matchloopcommand
+ \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9{#3}%
+ \ctxluamatch\matchloopcommand{#1}{#2}%
+ \popmacro\matchloopcommand}
+
+\permanent\def\doloopovermatched#1#2#3%
+ {\beginlocalcontrol
+ \pushmacro\matchloopcommand
+ \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9{#3}%
+ \endlocalcontrol
+ \the\ctxluamatch\matchloopcommand{#1}{#2}%
+ \beginlocalcontrol
+ \popmacro\matchloopcommand
+ \endlocalcontrol}
+
+%D \macros
+%D {newevery,everyline,EveryLine,EveryPar}
+%D
+%D Lets skip to something quite different. It's common use to use \type {\everypar}
+%D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats.
+%D This means that when user assignments to \type {\everypar} can interfere with
+%D those of the package. We therefore introduce \type {\EveryPar}.
+%D
+%D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline}
+%D primitive, we have to call for \type {\everyline} when we are working on a line
+%D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we
+%D restore the old situation.
+
+% \dorecurse{2}{
+% \expanded{\everypar{before \recurselevel\space}}
+% \EveryPar{x } [before \recurselevel\space x] \par
+% \EveryPar{y } [before \recurselevel\space y] \par
+% \EveryPar{} [before \recurselevel] \par
+% \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par
+% \EveryPar{y } \everypar{before } [before] \par
+% }
+
+\installsystemnamespace{extraevery}
+
+\permanent\protected\def\newevery#1#2%
+ {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
+ \ifrelax#2\orelse\ifdefined#2\else
+ \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
+ \frozen\protected\edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}%
+ \fi}
+
+\protected\def\syst_helpers_every#1#2%
+ {\removetoks\the#2\from#1%
+ \appendtoks\the#2\to #1%
+ #2}
+
+%D This one permits definitions like:
+
+\newevery \everypar \EveryPar % we get a warning which is ok
+\newevery \everyline \EveryLine
+
+%D and how about:
+
+\newtoks \neverypar
+
+\permanent\protected\def\forgeteverypar
+ {\everypar{\the\neverypar}}
+
+%D Which we're going to use indeed! When the second argument equals \type {\relax},
+%D the first token list is created unless it is already defined.
+%D
+%D Technically spoken we could have used the method we are going to present in the
+%D visual debugger. First we save the primitive \type{\everypar}:
+%D
+%D \starttyping
+%D \let\normaleverypar=\everypar
+%D \stoptyping
+%D
+%D Next we allocate a \TOKENLIST\ named \type{\everypar}, which means that
+%D \type{\everypar} is no longer a primitive but something like \type{\toks44}.
+%D
+%D \starttyping
+%D \newtoks\everypar
+%D \stoptyping
+%D
+%D Because \TEX\ now executes \type{\normaleverypar} instead of \type{\everypar}, we
+%D are ready to assign some tokens to this internally known and used \TOKENLIST.
+%D
+%D \starttyping
+%D \normaleverypar={all the things the system wants to do \the\everypar}
+%D \stoptyping
+%D
+%D Where the user can provide his own tokens to be expanded every time he expects
+%D them to expand.
+%D
+%D \starttyping
+%D \everypar={something the user wants to do}
+%D \stoptyping
+%D
+%D We don't use this method because it undoubtly leads to confusing situations,
+%D especially when other packages are used, but it's this kind of tricks that make
+%D \TEX\ so powerful.
+
+%D \macros
+%D {convertargument,convertcommand,convertvalue}
+%D
+%D Some persistent experimenting led us to the next macro. This macro converts a
+%D parameter or an expanded macro to it's textual meaning.
+%D
+%D \starttyping
+%D \convertargument ... \to \command
+%D \stoptyping
+%D
+%D For example,
+%D
+%D \starttyping
+%D \convertargument{one \two \three{four}}\to\ascii
+%D \stoptyping
+%D
+%D The resulting macro \type{\ascii} can be written to a file or the terminal
+%D without problems. In \CONTEXT\ we use this macro for generating registers and
+%D tables of contents.
+%D
+%D The second conversion alternative accepts a command:
+%D
+%D \starttyping
+%D \convertcommand\command\to\ascii
+%D \stoptyping
+%D
+%D Both commands accept the prefix \type{\doglobal} for global assignments.
+
+\permanent\protected\def\convertvalue#1\to
+ {\expandafter\convertcommand\csname#1\endcsname\to}
+
+\permanent\protected\def\defconvertedvalue#1#2% less sensitive for \to
+ {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname}
+
+%D \macros
+%D {doifassignmentelse}
+%D
+%D A lot of \CONTEXT\ commands take optional arguments, for instance:
+%D
+%D \starttyping
+%D \dothisorthat[alfa,beta]
+%D \dothisorthat[first=foo,second=bar]
+%D \dothisorthat[alfa,beta][first=foo,second=bar]
+%D \stoptyping
+%D
+%D Although a combined solution is possible, we prefer a seperation. The next
+%D command takes care of propper handling of such multi||faced commands.
+%D
+%D \starttyping
+%D \doifassignmentelse {...} {then ...} {else ...}
+%D \stoptyping
+%D
+%D So
+%D \startbuffer
+%D \doifelseassignment{a=b}{Y}{N}
+%D \doifelseassignment{a+b}{Y}{N}
+%D
+%D \ifcondition\validassignment {a=b}Y\else N\fi
+%D \ifcondition\novalidassignment{a=b}N\else Y\fi
+%D \ifcondition\novalidassignment {ab}Y\else N\fi
+%D \stopbuffer
+%D
+%D \typebuffer gives: \blank \getbuffer \blank
+
+\permanent\protected\def\doifelseassignment#1%
+ {\ifhastok={#1}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifelseassignmentcs#1#2#3%
+ {\ifhastok={#1}%
+ \expandafter#2%
+ \else
+ \expandafter#3%
+ \fi}
+
+\aliased\let\doifassignmentelse \doifelseassignment
+\aliased\let\doifassignmentelsecs\doifelseassignmentcs
+
+\newif\ifassignment
+
+\permanent\protected\def\docheckassignment#1%
+ {\ifhastok={#1}%
+ \assignmenttrue
+ \else
+ \assignmentfalse
+ \fi}
+
+\permanent\protected\def\validassignment #1{\ifhastok={#1}} % can become: {\ifhastok=} as we enforce {}
+\permanent\protected\def\novalidassignment#1{\ifnum\ifhastok={#1}\zerocount\else\plusone\fi=\plusone} % or use unless
+
+%D In \ETEX\ we can use \type {\detokenize} and gain some speed, but in general far
+%D less that 1\% for \type {\convertargument} and nil for \type {\convertcommand}.
+%D This macro is more robust than the pure \TEX\ one, something I found out when
+%D primitives like \type {\jobname} were fed (or something undefined).
+
+\permanent\protected\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}}
+\permanent\protected\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok
+
+\permanent\protected\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}}
+\permanent\protected\def\defconvertedcommand #1#2{\edef#1{\detokenize\expandafter{#2}}}
+\permanent\protected\def\edefconvertedargument#1#2{\edef#1{#2}%
+ \edef#1{\detokenize\expandafter{#1}}}
+\permanent\protected\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}}
+\permanent\protected\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}}
+\permanent\protected\def\xdefconvertedargument#1#2{\xdef#1{#2}%
+ \xdef#1{\detokenize\expandafter{#1}}}
+
+%D When you try to convert a primitive command, you'll find out that the \ETEX\
+%D method fails on for instance \type {\jobname} in the sense that it returns the
+%D filename instead of just \type {\jobname}. So far this does not give real
+%D problems.
+
+%D This is typically a macro that one comes to after reading the \TEX book
+%D carefully. Even then, the definite solution was found after rereading the \TEX
+%D book. The first implementation was:
+%D
+%D \starttyping
+%D \def\doconvertargument#1->#2\\\\{#2}
+%D \stoptyping
+%D
+%D The \type {-}, the delimiter \type {\\\\} and the the second argument are
+%D completely redundant.
+
+%D \macros
+%D {showvalue}
+%D
+%D Ahandy macro, for testing purposes only:
+
+\permanent\protected\def\showvalue#1%
+ {\ifcsname#1\endcsname
+ \expandafter\show\csname#1\endcsname
+ \else
+ \show\undefined
+ \fi}
+
+%D \macros
+%D {doifmeaningelse}
+%D
+%D We can use both commands in testing, but alas, not all meanings expand to
+%D something \type {->}. This is no problem in the \ETEX\ implementation, but since
+%D we want compatibility, we need:
+%D
+%D \starttyping
+%D \doifmeaningelse {\next} {\something} {true} {false}
+%D \stoptyping
+%D
+%D Watch the one level expansion of the second argument.
+
+\permanent\protected\def\doifelsemeaning#1#2%
+ {\edef\m_syst_string_one{\normalmeaning#1}%
+ \def \m_syst_string_two{#2}%
+ \edef\m_syst_string_two{\normalmeaning\m_syst_string_two}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifmeaningelse\doifelsemeaning
+
+%D \macros
+%D {doifsamestringselse,doifsamestring,doifnotsamestring}
+%D
+%D The next comparison macro converts the arguments into expanded strings. This
+%D command can be used to compare for instance \type {\jobname} with a name stored
+%D in a macro.
+%D
+%D \starttyping
+%D \doifelse {\jobname}{oeps}{YES}{NO}
+%D \doifsamestringelse{\jobname}{oeps}{YES}{NO}
+%D \stoptyping
+
+\def\syst_helpers_if_samestring_else#1#2#3#4%
+ {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}%
+ \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}%
+ \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi}
+
+\permanent\protected\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments}
+\permanent\protected\def\doifsamestring {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument }
+\permanent\protected\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument \firstofoneargument }
+
+\aliased\let\doifsamestringelse\doifelsesamestring
+
+%D These are obsolete in MTX:
+%D
+%D \starttyping
+%D \ConvertToConstant #1#2#3
+%D \CheckConstantAfter #1#2
+%D \ConvertConstantAfter #1#2#3
+%D \stoptyping
+
+%D \macros
+%D {assignifempty}
+%D
+%D We can assign a default value to an empty macro using:
+%D
+%D \starttyping
+%D \assignifempty \macros {default value}
+%D \stoptyping
+%D
+%D We don't explicitly test if the macro is defined.
+
+\permanent\protected\def\assignifempty#1#2%
+ {\iftok{#1}\emptytoks \def#1{#2}\fi}
+
+%D \macros
+%D {gobbleuntil,grabuntil,gobbleuntilrelax,
+%D processbetween,processuntil}
+%D
+%D In \TEX\ gobbling usually stand for skipping arguments, so here are our gobbling
+%D macros.
+%D
+%D In \CONTEXT\ we use a lot of \type {\start}||\type {\stop} like constructions.
+%D Sometimes, the \type {\stop} is used as a hard coded delimiter like in: %D
+%D \starttyping
+%D \protected\def\startcommand#1\stopcommand%
+%D {... #1 ...}
+%D \stoptyping
+%D
+%D In many cases the \type {\start}||\type {\stop} pair is defined at format
+%D generation time or during a job. This means that we cannot hardcode the \type
+%D {\stop} criterium. Only after completely understanding \type {\csname} and \type
+%D {\expandafter} I was able to to implement a solution, starting with:
+%D
+%D \starttyping
+%D \grabuntil{stop}\command
+%D \stoptyping
+%D
+%D This commands executes, after having encountered \type {\stop} the command \type
+%D {\command}. This command receives as argument the text preceding the \type
+%D {\stop}. This means that:
+%D
+%D \starttyping
+%D \protected\def\starthello%
+%D {\grabuntil{stophello}\message}
+%D
+%D \starthello Hello world!\stophello
+%D \stoptyping
+%D
+%D results in: \type{\message{Hello world!}}.
+
+\let\syst_helpers_grab_indeed\relax
+
+\protected\def\syst_helpers_grab#1#2%
+ {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed}
+
+\permanent\protected\def\grabuntil#1%
+ {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}}
+
+%D The next command build on this mechanism:
+%D
+%D \starttyping
+%D \processbetween{string}\command
+%D \stoptyping
+%D
+%D Here:
+%D
+%D \starttyping
+%D \processbetween{hello}\message
+%D \starthello Hello again!\stophello
+%D \stoptyping
+%D
+%D leads to: \type{\message{Hello again!}}. The command
+%D
+%D \starttyping
+%D \gobbleuntil{sequence}
+%D \stoptyping
+%D
+%D is related to these commands. This one simply throws away
+%D everything preceding \type{\command}.
+
+\let\syst_helpers_gobble_indeed\relax
+
+\permanent\protected\def\processbetween#1#2%
+ {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}}
+
+% \protected\def\gobbleuntil#1%
+% {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed}
+%
+% \protected\def\gobbleuntilrelax#1\relax
+% {}
+
+\permanent\protected\def\gobbleuntil#1%
+ {\def\syst_helpers_gobble_indeed##-#1{}\syst_helpers_gobble_indeed}
+
+\permanent\protected\def\gobbleuntilandfinalize#1%
+ {\def\syst_helpers_gobble_indeed##-#1{#1}\syst_helpers_gobble_indeed}
+
+\permanent\protected\def\gobbleuntilrelax#-\relax
+ {}
+
+%D The next one simply expands the pickup up tokens.
+%D
+%D \starttyping
+%D \processuntil{sequence}
+%D \stoptyping
+
+\let\syst_helpers_until_indeed\relax
+
+\permanent\protected\def\processuntil#1%
+ {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed}
+
+%D \macros
+%D {groupedcommand}
+%D
+%D Commands often manipulate argument as in:
+%D
+%D \starttyping
+%D \def\doezomaarwat#1{....#1....}
+%D \stoptyping
+%D
+%D A disadvantage of this approach is that the tokens that form \type{#1} are fixed
+%D the the moment the argument is read in. Normally this is no problem, but for
+%D instance verbatim environments adapt the \CATCODES\ of characters and therefore
+%D are not always happy with already fixed tokens.
+%D
+%D Another problem arises when the argument is grouped not by \type {{}} but by
+%D \type {\bgroup} and \type {\egroup}. Such an argument fails, because the \type
+%D {\bgroup} is een as the argument (which is quite normal).
+%D
+%D The next macro offers a solution for both unwanted situations:
+%D
+%D \starttyping
+%D \groupedcommand {before} {after}
+%D \stoptyping
+%D
+%D Which can be used like:
+%D
+%D \starttyping
+%D \def\cite%
+%D {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}}
+%D \stoptyping
+%D
+%D This command is equivalent to, but more 'robust' than:
+%D
+%D \starttyping
+%D \def\cite#1%
+%D {\rightquote\rightquote#1\leftquote\leftquote}
+%D \stoptyping
+%D
+%D \starttyping
+%D \def\rightword%
+%D {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}}
+%D
+%D .......... \rightword{the right way}
+%D \stoptyping
+%D
+%D Here \TEX\ typesets \type {\bf the right way} unbreakable at the end of the line.
+%D The solution mentioned before does not work here. We also handle
+%D
+%D \starttyping
+%D to be \bold{bold} or not, that's the question
+%D \stoptyping
+%D
+%D and
+%D
+%D \starttyping
+%D to be {\bold bold} or not, that's the question
+%D \stoptyping
+%D
+%D This alternative checks for a \type {\bgroup} token first. The internal
+%D alternative does not accept the box handling mentioned before, but further
+%D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to
+%D keep \type {\m_syst_helpers_handle_group_after} both into sight and local.
+
+\let\m_syst_helpers_handle_group_after \relax
+\let\m_syst_helpers_handle_group_before\relax
+
+\protected\def\syst_helpers_handle_group_nop
+ {\ifnum\currentgrouptype=\semisimplegroupcode
+ \expandafter\syst_helpers_handle_group_nop_a
+ \else
+ \expandafter\syst_helpers_handle_group_nop_b
+ \fi}
+
+\def\syst_helpers_handle_group_nop_a
+ {\begingroup
+ \aftergroup\m_syst_helpers_handle_group_a
+ \aftergroup\endgroup
+ \m_syst_helpers_handle_group_b}
+
+\def\syst_helpers_handle_group_nop_b
+ {\bgroup
+ \aftergroup\m_syst_helpers_handle_group_a
+ \aftergroup\egroup
+ \m_syst_helpers_handle_group_b}
+
+\protected\def\syst_helpers_handle_group_normal
+ {\bgroup
+ \afterassignment\m_syst_helpers_handle_group_normal_before
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_normal_before
+ {\bgroup
+ \m_syst_helpers_handle_group_b
+ \bgroup
+ \aftergroup\m_syst_helpers_handle_group_a
+ \aftergroup\egroup
+ \aftergroup\egroup}
+
+\protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference)
+ {\bgroup
+ \afterassignment\m_syst_helpers_handle_group_simple_before
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_simple_before
+ {\bgroup
+ \aftergroup\m_syst_helpers_handle_group_simple_after
+ \m_syst_helpers_handle_group_b}
+
+\def\m_syst_helpers_handle_group_simple_after
+ {\m_syst_helpers_handle_group_a
+ \egroup}%
+
+\protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference)
+ {\bgroup
+ \afterassignment\m_syst_helpers_handle_group_pickup_before
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_pickup_before
+ {\bgroup
+ \aftergroup\m_syst_helpers_handle_group_a
+ \aftergroup\egroup
+ \aftergroup\m_syst_helpers_handle_group_p
+ \m_syst_helpers_handle_group_b}
+
+\protected\def\syst_helpers_handle_group_nop_x
+ {\ifnum\currentgrouptype=\semisimplegroupcode
+ \begingroup
+ \aftergroup\endgroup
+ \else
+ \bgroup
+ \aftergroup\egroup
+ \fi
+ \m_syst_helpers_handle_group_b}
+
+\protected\def\syst_helpers_handle_group_normal_x
+ {\bgroup
+ \afterassignment\m_syst_helpers_handle_group_normal_before_x
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_normal_before_x
+ {\bgroup
+ \m_syst_helpers_handle_group_b
+ \bgroup
+ \aftergroup\egroup
+ \aftergroup\egroup}
+
+%D I considered it a nuisance that
+%D
+%D \starttyping
+%D \color[green]
+%D {as grass}
+%D \stoptyping
+%D
+%D was not interpreted as one would expect. This is due to the fact that \type
+%D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank
+%D space. So the final implementation became:
+
+\permanent\protected\def\groupedcommand#1#2%
+ {\def\m_syst_helpers_handle_group_b{#1}%
+ \def\m_syst_helpers_handle_group_a{#2}%
+ \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
+
+\permanent\protected\def\groupedcommandcs#1#2%
+ {\let\m_syst_helpers_handle_group_b#1%
+ \let\m_syst_helpers_handle_group_a#2%
+ \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
+
+\permanent\protected\def\simplegroupedcommand#1#2%
+ {\def\m_syst_helpers_handle_group_b{#1}%
+ \def\m_syst_helpers_handle_group_a{#2}%
+ \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop}
+
+\permanent\protected\def\pickupgroupedcommand#1#2#3%
+ {\def\m_syst_helpers_handle_group_b{#1}%
+ \def\m_syst_helpers_handle_group_a{#2}%
+ \def\m_syst_helpers_handle_group_p{#3}%
+ \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
+
+\permanent\protected\def\triggergroupedcommand#1%
+ {\def\m_syst_helpers_handle_group_b{#1}%
+ \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
+
+\permanent\protected\def\triggergroupedcommandcs#1%
+ {\let\m_syst_helpers_handle_group_b#1%
+ \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
+
+%D Users should be aware of the fact that grouping can interfere with ones paragraph
+%D settings that are executed after the paragraph is closed. One should therefore
+%D explictly close the paragraph with \type {\par}, else the settings will be
+%D forgotten and not applied. So it's:
+%D
+%D \starttyping
+%D \def\BoldRaggedCenter%
+%D {\groupedcommand{\raggedcenter\bf}{\par}}
+%D \stoptyping
+
+% %D \macros
+% %D {checkdefined}
+% %D
+% %D The bigger the system, the greater the change that user defined commands collide
+% %D with those that are part of the system. The next macro gives a warning when a
+% %D command is already defined. We considered blocking the definition, but this is
+% %D not always what we want.
+% %D
+% %D \starttyping
+% %D \checkdefined {category} {class} {command}
+% %D \stoptyping
+% %D
+% %D The user is warned with the suggestion to use \type {CAPITALS}. This suggestion
+% %D is feasible, because \CONTEXT only defines lowcased macros. These are obsolete:
+%
+% \protected\def\showdefinederror#1#2%
+% {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}}
+%
+% \protected\def\checkdefined#1#2#3%
+% {\doifdefined{#3}{\showdefinederror{#2}{#3}}}
+
+%D \macros
+%D {GotoPar,GetPar}
+%D
+%D Typesetting a paragraph in a special way can be done by first grabbing the
+%D contents of the paragraph and processing this contents grouped. The next macro
+%D for instance typesets a paragraph in boldface.
+%D
+%D \starttyping
+%D \def\remark#1\par%
+%D {\bgroup\bf#1\egroup}
+%D \stoptyping
+%D
+%D This macro has to be called like
+%D
+%D \starttyping
+%D \remark some text ... ending with \par
+%D \stoptyping
+%D
+%D Instead of \type {\par} we can of course use an empty line. When we started
+%D typesetting with \TEX, we already had produced lots of text in plain \ASCII. In
+%D producing such simple formatted texts, we adopted an open layout, and when
+%D switching to \TEX, we continued this open habit. Although \TEX\ permits a cramped
+%D and badly formatted source, it adds to confusion and sometimes introduces errors.
+%D So we prefer:
+%D
+%D \starttyping
+%D \remark
+%D
+%D some text ... ending with an empty line
+%D \stoptyping
+%D
+%D We are going to implement a mechanism that allows such open specifications. The
+%D definition of the macro handling \type {\remark} becomes:
+%D
+%D \starttyping
+%D \def\remark%
+%D {\BeforePar{\bgroup\bf}%
+%D \AfterPar{\egroup}%
+%D \GetPar}
+%D \stoptyping
+%D
+%D A macro like \type {\GetPar} can be defined in several ways. The recent version,
+%D the fourth one in a row, originally was far more complicated, but some
+%D functionality has been moved to other macros.
+%D
+%D We start with the more simple but in some cases more appropriate alternative is
+%D \type {\GotoPar}. This one leaves \type {\par} unchanged and is therefore more
+%D robust. On the other hand, \type {\AfterPar} is not supported.
+
+\let\syst_helpers_par_before\relax
+\let\syst_helpers_par_around\relax
+
+\permanent\protected\def\dowithpar#1#2%
+ {\globalpushmacro\syst_helpers_par_around
+ \def\syst_helpers_par_around##1\par{#1##1#2\globalpopmacro\syst_helpers_par_around}%
+ \expandafter\syst_helpers_par_around\ignorepars}
+
+\permanent\protected\def\dogotopar#1%
+ {\globalpushmacro\syst_helpers_par_before
+ \def\syst_helpers_par_before{#1\globalpopmacro\syst_helpers_par_before}%
+ \expandafter\syst_helpers_par_before\ignorepars}
+
+\aliased\let\dogotoparcs\dogotopar
+
+\permanent\protected\def\dogotoparstart
+ {\ignorepars}
+
+%D This is old and kind of obsolete:
+
+\newtoks\BeforePar
+\newtoks\AfterPar
+
+\permanent\protected\def\GetPar
+ {\expanded
+ {\dowithpar
+ {\the\BeforePar
+ \BeforePar\emptytoks}
+ {\the\AfterPar
+ \BeforePar\emptytoks
+ \AfterPar\emptytoks}}}
+
+\permanent\protected\def\GotoPar
+ {\expanded
+ {\dogotopar
+ {\the\BeforePar
+ \BeforePar\emptytoks}}}
+
+%D \macros
+%D {dowithpargument,dowithwargument}
+%D
+%D The next macros are a variation on \type {\GetPar}. When macros expect an
+%D argument, it interprets a grouped sequence of characters a one token. While this
+%D adds to robustness and less ambiguous situations, we sometimes want to be a bit
+%D more flexible, or at least want to be a bit more tolerant to user input.
+%D
+%D We start with a commands that acts on paragraphs. This
+%D command is called as:
+%D
+%D \starttyping
+%D \dowithpargument\command
+%D \dowithpargument{\command ... }
+%D \stoptyping
+%D
+%D In \CONTEXT\ we use this one to read in the titles of chapters, sections etc. The
+%D commands responsible for these activities accept several alternative ways of
+%D argument passing. In these examples, the \type {\par} can be omitted when an
+%D empty line is present.
+%D
+%D \starttyping
+%D \command{...}
+%D \command ... \par
+%D \command
+%D {...}
+%D \command
+%D ... \par
+%D \stoptyping
+
+\let\syst_helpers_next_par\relax
+\let\syst_helpers_next_arg\relax
+
+\permanent\protected\def\dowithpargument#1%
+ {\def\syst_helpers_next_par##1 \par{#1{##1}}%
+ \def\syst_helpers_next_arg##1{#1{##1}}%
+ \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}}
+
+%D The \type {p} in the previous command stands for paragraph. When we want to act
+%D upon words we can use the \type{w} alternative.
+%D
+%D \starttyping
+%D \dowithwargument\command
+%D \dowithwargument{... \command ...}
+%D \stoptyping
+%D
+%D The main difference bwteen two alternatives is in the handling of \type {\par}'s.
+%D This time the space token acts as a delimiter.
+%D
+%D \starttyping
+%D \command{...}
+%D \command ...
+%D \command
+%D {...}
+%D \command
+%D ...
+%D \stoptyping
+
+\let\syst_helpers_next_war\relax
+\let\syst_helpers_next_arg\relax
+
+\permanent\protected\def\dowithwargument#1%
+ {\def\syst_helpers_next_war##1 {#1{##1}}%
+ \def\syst_helpers_next_arg##1{#1{##1}}%
+ \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war}
+
+%D \macros
+%D {dorepeat,dorepeatwithcommand}
+%D
+%D When doing repetitive tasks, we stromgly advice to use \type {\dorecurse}. The
+%D next alternative however, suits better some of the \CONTEXT\ interface commands.
+%D
+%D \starttyping
+%D \dorepeat[n*\command]
+%D \stoptyping
+%D
+%D The value of the used \COUNTER\ can be called within
+%D \type{\command} by \type{\repeater}.
+%D
+%D A slightly different alternative is:
+%D
+%D \starttyping
+%D \dorepeatwithcommand[n*{...}]\command
+%D \stoptyping
+%D
+%D When we call for something like:
+%D
+%D \starttyping
+%D \dorepeatwithcommand[3*{Hello}]\message
+%D \stoptyping
+%D
+%D we get ourselves three \type {\message{Hello}} messages in a row. In both
+%D commands, the \type {n*} is optional. When this specification is missing, the
+%D command executes once.
+
+\permanent\protected\def\dorepeatwithcommand[#1]%
+ {\syst_helpers_repeat_with_command#1*\empty*\relax}
+
+\def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5%
+ {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi}
+
+\def\syst_helpers_repeat_with_command_indeed#1#2#3#4%
+ {\ifx#2\empty % redundant but gives cleaner extensions
+ #4{#1}%
+ \orelse\ifnum#1<\zerocount
+ %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}%
+ \dorecurse{-#1}{#4{-#2#3}}%
+ \orelse\ifx#2+%
+ \dorecurse{#1}{#4{#3}}%
+ \else
+ \dorecurse{#1}{#4{#2#3}}%
+ \fi}
+
+\def\syst_helpers_repeat_with_command_again[#1]#2%
+ {#2{#1}}
+
+%D The extension hook permits something like:
+%D
+%D \starttyping
+%D \bgroup
+%D
+%D \catcode`\*=\superscriptcatcode
+%D
+%D \gdef\syst_helpers_repeat_with_command_again[#1]%
+%D {\redodorepeatwithcommand#1*\empty*\relax}
+%D
+%D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5%
+%D {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5}
+%D
+%D \egroup
+%D \stoptyping
+%D
+%D although one may wonder if changing the catcode of \type {*} is wise.
+
+%D \macros
+%D {doifstringinstringelse}
+%D
+%D The next macro is meant for situations where both strings are macros. This save
+%D some unneeded expansion. But now we just alias.
+
+\aliased\let\doifelsestringinstring\doifelseinstring
+\aliased\let\doifstringinstringelse\doifelseinstring
+
+%D \macros
+%D {appendtoks,prependtoks,appendtoksonce,prependtoksonce,
+%D doifintokselse,flushtoks,dotoks}
+%D
+%D We use tokenlists sparsely within \CONTEXT, because the comma separated lists are
+%D more suitable for the user interface. Nevertheless we have:
+%D
+%D \starttyping
+%D (\doglobal) \appendtoks ... \to\tokenlist
+%D (\doglobal) \prependtoks ... \to\tokenlist
+%D (\doglobal) \flushtoks\tokenlist
+%D \dotoks\tokenlist
+%D \stoptyping
+%D
+%D These macros are clones of the ones implemented in page~378 of Knuth's \TEX book.
+
+\newtoks\t_syst_helpers_scratch
+\let \m_syst_helpers_scratch\empty
+
+\permanent\protected\def\appendtoks#1\to#2%
+ {\ifrelax\dodoglobal
+ \expandafter\toksapp
+ \else
+ \resetglobal
+ \expandafter\gtoksapp
+ \fi#2{#1}}
+
+\permanent\protected\def\prependtoks#1\to#2%
+ {\ifrelax\dodoglobal
+ \expandafter\tokspre
+ \else
+ \resetglobal
+ \expandafter\gtokspre
+ \fi#2{#1}}
+
+\permanent\protected\def\appendtoksonce#1\to#2%
+ {\ifhasxtoks{#1}#2\else
+ \appendtoks#1\to#2%
+ \fi}
+
+\permanent\protected\def\prependtoksonce#1\to#2%
+ {\ifhasxtoks{#1}{#2}\m_syst_helpers_scratch\else
+ \prependtoks#1\to#2%
+ \fi}
+
+%D The test macro:
+
+\permanent\protected\def\doifelseintoks#1#2% #1 en #2 zijn toks
+ {\ifhasxtoks#1#2%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifintokselse\doifelseintoks
+
+%D Moved from \type {lxml-ini.tex} to here. This one is for generators that collect
+%D stuff piecewise, which is sometimes hard on mechanisms that grab content using
+%D delimiters:
+%D
+%D \starttyping
+%D \startcollecting
+%D \startcollect \bTABLE \stopcollect
+%D \startcollect \bTR \stopcollect
+%D \startcollect \bTD \stopcollect
+%D \startcollect foo\stopcollect
+%D \startcollect \eTD \stopcollect
+%D \startcollect \bTD \stopcollect
+%D \startcollect bar\stopcollect
+%D \startcollect \eTD \stopcollect
+%D \startcollect \eTR \stopcollect
+%D \startcollect \eTABLE \stopcollect
+%D \stopcollecting
+%D \stoptyping
+
+\newtoks \collectingtoks
+
+\aliased\let\stopcollect \relax
+\aliased\let\stopexpandedcollect\relax
+
+\permanent\protected\def\startcollect #1\stopcollect {\toksapp \collectingtoks{#1}}
+\permanent\protected\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}}
+
+\permanent\protected\def\startcollecting{\collectingtoks\emptytoks}
+\permanent\protected\def\stopcollecting {\the\collectingtoks}
+
+\permanent\protected\def\collect {\toksapp \collectingtoks}
+\permanent\protected\def\collectexpanded{\etoksapp\collectingtoks}
+
+%D A nice one too:
+
+% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]}
+
+\let\syst_helpers_remove_toks\relax
+
+\permanent\protected\def\removetoks#1\from#2%
+ {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3^^^^0004%
+ {\def\m_syst_string_one{##3}%
+ \ifempty\m_syst_string_one#2{##1}\else#2{##1##2}\fi}%
+ \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty^^^^0004}
+
+%D Also:
+
+\permanent\protected\def\appendetoks#1\to#2%
+ {\ifrelax\dodoglobal
+ \expandafter\etoksapp
+ \else
+ \resetglobal
+ \expandafter\xtoksapp
+ \fi#2{#1}}
+
+\permanent\protected\def\prependetoks#1\to#2%
+ {\ifrelax\dodoglobal
+ \expandafter\etokspre
+ \else
+ \resetglobal
+ \expandafter\xtokspre
+ \fi#2{#1}}
+
+%D Hm.
+
+\permanent\protected\def\flushtoks#1% nb: can reassign to #1 again, hence the indirectness
+ {\t_syst_helpers_scratch#1\relax
+ \dodoglobal#1\emptytoks
+ \the\t_syst_helpers_scratch\relax}
+
+\aliased\let\dotoks\the
+
+%D \macros
+%D {beforesplitstring,aftersplitstring}
+%D
+%D These both commands split a string at a given point in two
+%D parts, so \type{x.y} becomes \type{x} or \type{y}.
+%D
+%D \starttyping
+%D \beforesplitstring test.tex\at.\to\filename
+%D \aftersplitstring test.tex\at.\to\extension
+%D \stoptyping
+%D
+%D The first routine looks (and is indeed) a bit simpler than the second one. The
+%D alternative looking more or less like the first one did not always give the
+%D results we needed. Both implementations show some insight in the manipulation of
+%D arguments.
+
+\let\syst_helpers_split_string\relax
+
+\permanent\protected\def\beforesplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string##1#2##0^^^^0004% no #- as we need to count
+ {\ifarguments
+ \let#3\empty
+ \or
+ \let#3\empty
+ \else
+ \def#3{##1}%
+ \fi}%
+ \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
+
+\permanent\protected\def\aftersplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count
+ {\ifarguments
+ \let#3\empty
+ \or
+ \def#3{#1}%
+ \else
+ \def#3{##2}%
+ \fi}%
+ \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
+
+%D \macros
+%D {splitstring,greedysplitstring}
+%D
+%D A bonus macro.
+
+\permanent\protected\def\splitstring#1\at#2\to#3\and#4%
+ {\def\syst_helpers_split_string##1#2##2^^^^0004%
+ {\ifarguments
+ \let#3\empty
+ \let#4\empty
+ \or
+ \def#3{#1}%
+ \let#4\empty
+ \else
+ \def#3{##1}%
+ \def#4{##2}%
+ \fi}%
+ \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
+
+\permanent\protected\def\greedysplitstring#1\at#2\to#3\and#4%
+ {\def\syst_helpers_split_string##1#2##2^^^^0004%
+ {\ifarguments
+ \let#3\empty
+ \let#4\empty
+ \or
+ \def#3{#1}%
+ \let#4\empty
+ \else
+ \def#3{##1}%
+ \def#4{##2}%
+ \def\syst_helpers_split_string####1#2####2^^^^0004%
+ {\ifarguments
+ \or
+ \else
+ \expandafter\def\expandafter#3\expandafter{#3####1}%
+ \def#4{####2}%
+ \syst_helpers_split_string####2^^^^0004\ignorearguments\ignorearguments
+ \fi}%
+ \syst_helpers_split_string##2^^^^0004\ignorearguments\ignorearguments
+ \fi}%
+ \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
+
+%D \macros
+%D {beforetestandsplitstring,
+%D aftertestandsplitstring,
+%D testandsplitstring}
+
+\aliased\let\beforetestandsplitstring\beforesplitstring
+
+\permanent\protected\def\aftertestandsplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count
+ {\ifarguments
+ \let#3\empty
+ \or
+ \let#3\empty
+ \else
+ \def#3{##2}%
+ \fi}%
+ \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
+
+\permanent\protected\def\testandsplitstring#1\at#2\to#3\and#4%
+ {\def\syst_helpers_split_string##1#2##2^^^^0004%
+ {\ifarguments
+ \let#3\empty
+ \let#4\empty
+ \or
+ \let#3\empty
+ \let#4\empty
+ \else
+ \def#3{##1}%
+ \def#4{##2}%
+ \fi}%
+ \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
+
+%D \macros
+%D {splitatperiod, splitatcomma, splitatasterisk, splitatcolon, splitatcolons}
+
+\protected\def\syst_helpers_splitatperiod #1.#2.#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
+\protected\def\syst_helpers_splitatcomma #1,#2,#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
+\protected\def\syst_helpers_splitatasterisk #1*#2*#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
+\protected\def\syst_helpers_splitatcolon #1:#2:#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
+\protected\def\syst_helpers_splitatcolons #1::#2::#-^^^^0004#3#4{\edef#3{#1}\edef#4{#2}}
+
+\permanent\protected\def\splitatperiod #1{\normalexpanded{\syst_helpers_splitatperiod #1}..^^^^0004}
+\permanent\protected\def\splitatcomma #1{\normalexpanded{\syst_helpers_splitatcomma #1},,^^^^0004} % not at ", "
+\permanent\protected\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**^^^^0004}
+\permanent\protected\def\splitatcolon #1{\normalexpanded{\syst_helpers_splitatcolon #1}::^^^^0004}
+\permanent\protected\def\splitatcolons #1{\normalexpanded{\syst_helpers_splitatcolons #1}::::^^^^0004}
+
+%D \macros
+%D {removesubstring}
+%D
+%D A first application of the two routines defined above is:
+%D
+%D \starttyping
+%D \removesubstring-\from first-last\to\nothyphenated
+%D \stoptyping
+%D
+%D Which in terms of \TEX\ looks like:
+
+\permanent\protected\def\removesubstring#1\from#2\to#3%
+ {\splitstring#2\to\m_syst_string_one\and\m_syst_string_two
+ \dodoglobal#3{\m_syst_string_one\m_syst_string_two}}
+
+%D \macros
+%D {appendtocommalist,prependtocommalist,
+%D addtocommalist,removefromcommalist}
+%D
+%D When working with comma separated lists, one sooner or later want the tools to
+%D append or remove items from such a list. When we add an item, we first check if
+%D it's already there. This means that every item in the list is unique.
+%D
+%D \starttyping
+%D \addtocommalist {alfa} \name
+%D \addtocommalist {beta} \name
+%D \addtocommalist {gamma} \name
+%D \removefromcommalist {beta} \name
+%D \stoptyping
+%D
+%D These commands can be prefixed with \type {\doglobal}. The implementation of the
+%D second command is more complecated, because we have to take leading spaces into
+%D account. Keep in mind that users may provide lists with spaces after the commas.
+%D When one item is left, we also have to get rid of trailing spaces.
+%D
+%D \starttyping
+%D \def\words{alfa, beta, gamma, delta}
+%D \def\words{alfa,beta,gamma,delta}
+%D \stoptyping
+%D
+%D Removing an item takes more time than adding one. A fast appending alternative,
+%D without any testing, is also provided:
+%D
+%D \starttyping
+%D \appendtocommalist {something} \name
+%D \prependtocommalist {something} \name
+%D \stoptyping
+%D
+%D This can be implemented as follows:
+%D
+%D \starttyping
+%D \def\appendtocommalist#1#2%
+%D {\ifx#2\empty
+%D \dodoglobal\edef#2{#1}%
+%D \else % no test on empty
+%D \dodoglobal\edef#2{#2,#1}%
+%D \fi}
+%D
+%D \def\prependtocommalist#1#2%
+%D {\ifx#2\empty
+%D \dodoglobal\edef#2{#1}%
+%D \else % no test on empty
+%D \dodoglobal\edef#2{#1,#2}%
+%D \fi}
+%D \stoptyping
+%D
+%D The faster alternatives are:
+
+\permanent\protected\def\appendtocommalist#1#2%
+ {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}
+
+\permanent\protected\def\prependtocommalist#1#2%
+ {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}
+
+\permanent\protected\def\addtocommalist#1#2% {item} \cs
+ {\rawdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}}
+
+\permanent\protected\def\pretocommalist#1#2% {item} \cs
+ {\rawdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}}
+
+\permanent\protected\def\robustdoifelseinset#1#2%
+ {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}%
+ \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}%
+ \rawdoifelseinset\m_syst_string_one\m_syst_string_two}
+
+\aliased\let\robustdoifinsetelse\robustdoifelseinset
+
+\permanent\protected\def\robustaddtocommalist#1#2% {item} \cs
+ {\robustdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}}
+
+\permanent\protected\def\robustpretocommalist#1#2% {item} \cs
+ {\robustdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}}
+
+\permanent\protected\def\xsplitstring#1#2% \cs {str}
+ {\def\syst_helpers_split_string##1,#2,##2,#2,##-\\%
+ {\edef\m_syst_string_one{\syst_cleanedup_commalist_b##1\empty\empty\relax}%
+ \edef\m_syst_string_two{\syst_cleanedup_commalist_a##2,,\relax}}%
+ \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\}
+
+\def\syst_cleanedup_commalist_b#1#2\relax{\if#1,\else#1\fi#2}
+\def\syst_cleanedup_commalist_a#1,,#-\relax{#1}
+
+\permanent\protected\def\removefromcommalist#1#2% to be sped up
+ {\rawdoifelseinset{#1}#2%
+ {\normalexpanded{\xsplitstring\noexpand#2{#1}}%
+ \dodoglobal\edef#2%
+ {\ifempty\m_syst_string_one
+ \m_syst_string_two
+ \else
+ \m_syst_string_one\ifempty\m_syst_string_two\else,\m_syst_string_two\fi
+ \fi}}
+ \resetglobal}
+
+%D \macros
+%D {substituteincommalist}
+%D
+%D Slow but seldom used, so for the moment we stick to this implementation.
+%D
+%D \starttyping
+%D \substituteincommalist{old}{new}{list}
+%D \stoptyping
+
+\def\syst_helpers_substitute_in_comma_list_step#1%
+ {\edef\m_syst_string_three{#1}%
+ \ifx\m_syst_string_one\m_syst_string_three
+ \ifempty\m_syst_string_two \else
+ \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi\m_syst_string_two}%
+ \fi
+ \else
+ \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi#1}%
+ \fi}
+
+\permanent\protected\def\substituteincommalist#1#2#3% old, new, list (slooow)
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#2}%
+ \let\m_syst_string_four\empty
+ \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step
+ \let#3\m_syst_string_four}
+
+%D \macros
+%D {replaceincommalist}
+%D
+%D The next macro can be used to replace an indexed element in a commalist:
+%D
+%D \starttyping
+%D \replaceincommalist\MyList{2}
+%D \stoptyping
+%D
+%D Element~2 will be replaced by the current meaning of the macro \type
+%D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}.
+%D The replacement honors grouped items, like in:
+%D
+%D \starttyping
+%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3}
+%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3}
+%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3}
+%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3}
+%D \stoptyping
+%D
+%D This macro was used in the bibtex code (and is probably no longer needed).
+
+\newcount\c_syst_helpers_comma_list_index
+\let \m_syst_helpers_comma_list_target\empty
+
+\mutable\let\newcommalistelement\empty
+
+\def\syst_helpers_replace_in_comma_list_step#1% we can use #+ here too
+ {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax
+ \ifempty\newcommalistelement\else
+ \ifempty\m_syst_helpers_comma_list_target
+ \let\m_syst_helpers_comma_list_target\newcommalistelement
+ \else
+ \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter
+ \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter
+ {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}%
+ \fi
+ \fi
+ \def\commalistelement{#1}%
+ \else
+ \ifempty\m_syst_helpers_comma_list_target
+ \ifx\nexttoken\bgroup % is known -)
+ \def\m_syst_helpers_comma_list_target{{#1}}%
+ \else
+ \def\m_syst_helpers_comma_list_target{#1}%
+ \fi
+ \else
+ \ifx\nexttoken\bgroup % is known -)
+ \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}%
+ \else
+ \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}%
+ \fi
+ \fi
+ \fi
+ \advance\commalistcounter\plusone}
+
+\permanent\protected\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1
+ {\c_syst_helpers_comma_list_index#2\relax
+ \let\m_syst_helpers_comma_list_target\empty
+ \let\commalistelement\empty
+ \commalistcounter\plusone
+ \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step
+ \dodoglobal\let#1\m_syst_helpers_comma_list_target}
+
+%D \macros
+%D {globalprocesscommalist}
+%D
+%D The commalist processing commands are characterized by the fact that the way they
+%D handle expansion as well as the fact that they can be nested. This makes them
+%D kind of useless for handling comma lists in alignments. In these situations the
+%D next macro can be of use.
+
+\let\m_syst_helpers_comma_list_command_global\empty
+
+\def\syst_helpers_comma_list_command_global_step#1,%
+ {\if]#1\else
+ \m_syst_helpers_comma_list_command_global{#1}%
+ \expandafter\syst_helpers_comma_list_command_global_step
+ \fi}
+
+\permanent\protected\def\globalprocesscommalist[#1]#2%
+ {\glet\m_syst_helpers_comma_list_command_global#2%
+ \expandafter\syst_helpers_comma_list_command_global_step#1,],}
+
+%D \macros
+%D {withoutpt,PtToCm,
+%D numberofpoints,dimensiontocount}
+%D
+%D We can convert point into centimeters with:
+%D
+%D \starttyping
+%D \PtToCm{dimension}
+%D \stoptyping
+
+% {\catcode`\.=\othercatcode
+% \catcode`\p=\othercatcode
+% \catcode`\t=\othercatcode
+% \gdef\WITHOUTPT#1pt{#1}}
+%
+% \def\withoutpt#1%
+% {\expandafter\WITHOUTPT#1}
+%
+% Okay, not really an impressive extension to the engine, but better than some macro
+% that is demonstrating deeper understanding of \TEX. It's anyway a trivial extension
+% anyway. There are actually many examples of very advanced macros and exposure of
+% how clever one is that could be done cheap in the engine. It's just that at the time
+% that \TEX\ was written, it made no sense to add a lot of that. After decades we know
+% what extras we need.
+
+\permanent\def\withoutpt#1{\thewithoutunit\dimexpr#1} % best use the primitive directly
+
+%D The capitals are needed because \type {p} and \type {t} have catcode~12, while
+%D macronames only permit tokens with the catcode~11. As a result we cannot use the
+%D \type {.group} primitives. Those who want to know more about this kind of
+%D manipulations, we advice to study the \TEX book in detail. Because this macro
+%D does not do any assignment, we can use it in the following way too.
+
+% \def\PtToCm#1%
+% {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
+
+\permanent\def\PtToCm#1{\thewithoutunit\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
+
+%D We also support:
+%D
+%D \starttyping
+%D \numberofpoints {dimension}
+%D \dimensiontocount {dimension} {\count}
+%D \stoptyping
+%D
+%D Both macros return a rounded number.
+
+% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt}
+% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt}
+
+\permanent\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax}
+\permanent\def\numberofpoints #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax}
+
+%D \macros
+%D {swapdimens,swapskips,swapcounts,swapmacros,
+%D globalswapdimens,globalswapcounts,globalswapmacros}
+%D
+%D Simple but effective are the next two macros. There name exactly states their
+%D purpose.
+
+\newdimen\d_syst_helpers_swapped
+\newskip \s_syst_helpers_swapped
+\newcount\c_syst_helpers_swapped
+\let \m_syst_helpers_swapped\relax
+
+% \protected\def\swapdimens#1#2{\d_syst_helpers_swapped#1#1#2#2\d_syst_helpers_swapped}
+% \protected\def\swapskips #1#2{\s_syst_helpers_swapped#1#1#2#2\s_syst_helpers_swapped}
+% \protected\def\swapcounts#1#2{\c_syst_helpers_swapped#1#1#2#2\c_syst_helpers_swapped}
+% \protected\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let#1#2\let#2\m_syst_helpers_swapped}
+
+\aliased\let\swapdimens\swapcsvalues
+\aliased\let\swapskips \swapcsvalues
+\aliased\let\swapcounts\swapcsvalues
+\aliased\let\swapmacros\swapcsvalues
+
+% \protected\def\globalswapdimens#1#2{\d_syst_helpers_swapped#1\global#1#2\global#2\d_syst_helpers_swapped}
+% \protected\def\globalswapskips #1#2{\s_syst_helpers_swapped#1\global#1#2\global#2\s_syst_helpers_swapped}
+% \protected\def\globalswapcounts#1#2{\c_syst_helpers_swapped#1\global#1#2\global#2\c_syst_helpers_swapped}
+% \protected\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\glet#1#2\glet#2\m_syst_helpers_swapped}
+
+\permanent\protected\def\globalswapdimens{\global\swapcsvalues}
+\permanent\protected\def\globalswapskips {\global\swapcsvalues}
+\permanent\protected\def\globalswapcounts{\global\swapcsvalues}
+\permanent\protected\def\globalswapmacros{\global\swapcsvalues}
+
+%D \macros
+%D {pushmacro,popmacro}
+%D
+%D Premature and a bit of beta, we offer:
+%D
+%D \starttyping
+%D \pushmacro\macro
+%D \popmacro\macro
+%D \stoptyping
+
+\permanent\let\pushmacro\localpushmacro
+\permanent\let\popmacro \localpopmacro
+
+%D \macros
+%D {setlocalhsize,distributedhsize}
+%D
+%D Sometimes we need to work with the \type{ \hsize} that is corrected for
+%D indentation and left and right skips. The corrected value is available in \type
+%D {\localhsize}, which needs to be calculated with \type {\setlocalhsize} first. %D
+%D
+%D \starttyping
+%D \setlocalhsize \hbox to \localhsize{...}
+%D \setlocalhsize[-1em] \hbox to \localhsize{...}
+%D \setlocalhsize[.5ex] \hbox to \localhsize{...}
+%D \stoptyping
+%D
+%D These examples show us that an optional can be used. The value provided is added
+%D to \type {\localhsize}.
+
+\newdimen\localhsize
+
+\permanent\protected\def\setlocalhsize % don't change !
+ {\doifelsenextoptional
+ \syst_helpers_set_local_hsize_yes
+ \syst_helpers_set_local_hsize_nop}
+
+\def\syst_helpers_set_local_hsize_nop
+ {\localhsize\availablehsize}
+
+\def\syst_helpers_set_local_hsize_yes[#1]%
+ {\syst_helpers_set_local_hsize_nop
+ \advance\localhsize#1\relax}
+
+\permanent\def\availablehsize
+ {\dimexpr
+ \hsize-\leftskip-\rightskip
+ \ifnum\hangafter<\zerocount
+ \ifdim\hangindent>\zeropoint-\else+\fi\hangindent
+ \fi
+ \relax}
+
+\permanent\def\distributedhsize#1#2#3%
+ {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax}
+
+\permanent\def\hsizefraction#1#2%
+ {\dimexpr#1/#2\relax}
+
+%D \macros
+%D {doifvalue,doifnotvalue,doifelsevalue,
+%D doifnothing,doifsomething,doifelsenothing,
+%D doifvaluenothing,doifvaluesomething,doifelsevaluenothing}
+%D
+%D These \type {\if} commands can be used to access macros (or variables) that are
+%D normally accessed by using \type {\getvalue}. Using these alternatives safes us
+%D three tokens per call. Anyone familiar with the not||values ones, can derive
+%D their meaning from the definitions.
+
+\permanent\protected\def\doifvalue#1#2%
+ {\iftok{\csname#1\endcsname}{#2}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifnotvalue#1#2%
+ {\iftok{\csname#1\endcsname}{#2}%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\doifelsevalue#1#2%
+ {\iftok{\csname#1\endcsname}{#2}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifnothing#1%
+ {\iftok{#1}\emptytoks
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifsomething#1%
+ {\iftok{#1}\emptytoks
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\doifelsenothing#1%
+ {\iftok{#1}\emptytoks
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifelsesomething#1%
+ {\iftok{#1}\emptytoks
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifvaluenothing#1%
+ {\iftok{\csname#1\endcsname}\emptytoks
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\protected\def\doifvaluesomething#1%
+ {\iftok{\csname#1\endcsname}\emptytoks
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\doifelsevaluenothing#1%
+ {\iftok{\csname#1\endcsname}\emptytoks
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifvalueelse \doifelsevalue
+\aliased\let\doifnothingelse \doifelsenothing
+\aliased\let\doifsomethingelse \doifelsesomething
+\aliased\let\doifvaluenothingelse\doifelsevaluenothing
+
+%D \macros
+%D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue}
+%D
+%D Also handy:
+
+\permanent\def\doifelseemptyvalue#1%
+ {\expandafter\ifempty\csname#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifemptyvalueelse\doifelseemptyvalue
+
+\permanent\def\doifemptyvalue#1%
+ {\expandafter\ifempty\csname#1\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\permanent\def\doifnotemptyvalue#1%
+ {\expandafter\ifempty\csname#1\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D \macros
+%D {doifallcommonelse}
+%D
+%D A complete match of two sets can be tested with \type {\doifallcommonelse}, where
+%D the first two arguments are sets.
+
+\let\syst_helpers_do_common_check_all\gobbleoneargument
+
+\def\syst_helpers_do_if_all_common_else#1#2#3#4% slow
+ {\def\syst_helpers_do_common_check_all##1%
+ {\doifnotinset{##1}{#4}\donefalse
+ \ifdone\else\expandafter\quitcommalist\fi}%
+ \donetrue
+ \processcommalist[#3]\syst_helpers_do_common_check_all
+ \ifdone\expandafter#1\else\expandafter#2\fi}
+
+\permanent\protected\def\doifelseallcommon{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments}
+\permanent\protected\def\doifallcommon {\syst_helpers_do_if_all_common_else\firstofoneargument \gobbleoneargument }
+\permanent\protected\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument \firstofoneargument }
+
+\aliased\let\doifallcommonelse\doifelseallcommon
+
+%D \macros
+%D {DOIF,DOIFELSE,DOIFNOT}
+%D
+%D \TEX\ is case sensitive. When comparing arguments, this feature sometimes is less
+%D desirable, for instance when we compare filenames. The next three alternatives
+%D upcase their arguments before comparing them.
+%D
+%D \starttyping
+%D \DOIF {string1} {string2} {...}
+%D \DOIFNOT {string1} {string2} {...}
+%D \DOIFELSE {string1} {string2} {then ...}{else ...}
+%D \stoptyping
+%D
+%D We have to use a two||step implementation, because the
+%D expansion has to take place outside \type{\uppercase}.
+%D
+%D These might end up as \LUA based helpers (i.e. consider these
+%D obsolete:
+
+\protected\def\syst_helpers_do_IF#1#2%
+ {\uppercase{\iftok{#1}{#2}}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\protected\def\syst_helpers_do_IF_NOT#1#2%
+ {\uppercase{\iftok{#1}{#2}}%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\protected\def\syst_helpers_do_IF_ELSE#1#2%
+ {\uppercase{\iftok{#1}{#2}}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\protected\def\syst_helpers_do_IF_INSTRING_ELSE#1#2%
+ {\uppercase{\doifelseinstring{#1}{#2}}}
+
+\permanent\protected\def\DOIF #1#2{\normalexpanded{\syst_helpers_do_IF {#1}{#2}}}% will become obsolete
+\permanent\protected\def\DOIFNOT #1#2{\normalexpanded{\syst_helpers_do_IF_NOT {#1}{#2}}}% will become obsolete
+\permanent\protected\def\DOIFELSE #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE {#1}{#2}}}% will become obsolete
+\permanent\protected\def\DOIFINSTRINGELSE #1#2{\normalexpanded{\syst_helpers_do_IF_INSTRING_ELSE{#1}{#2}}}% will become obsolete
+
+%D \macros
+%D {dosingleargumentwithset,
+%D dodoubleargumentwithset,dodoubleemptywithset,
+%D dotripleargumentwithset,dotripleemptywithset}
+%D
+%D These maybe too mysterious macros enable us to handle more than one setup at once.
+%D
+%D \starttyping
+%D \dosingleargumentwithset \command[#1]
+%D \dodoubleargumentwithset \command[#1][#2]
+%D \dotripleargumentwithset \command[#1][#2][#3]
+%D \dodoubleemptywithset \command[#1][#2]
+%D \dotripleemptywithset \command[#1][#2][#3]
+%D \stoptyping
+%D
+%D The first macro calls \type {\command[##1]} for each string in the set~\type
+%D {#1}. The second one calls for \typ {\command [##1][#2]} and the third, well one
+%D may guess. These commands support constructions like:
+%D
+%D \starttyping
+%D \def\dodefinesomething[#1][#2]%
+%D {\getparameters[\??xx#1][#2]}
+%D
+%D \protected\def\definesomething%
+%D {\dodoubleargumentwithset\dodefinesomething}
+%D \stoptyping
+%D
+%D Which accepts calls like:
+%D
+%D \starttyping
+%D \definesomething[alfa,beta,...][variable=...,...]
+%D \stoptyping
+
+\let\syst_helpers_with_set_step\relax % maybe push pop
+
+\permanent\tolerant\protected\def\dodoubleemptywithset#1#*[#2]#*[#3]%
+ {\doifsomething{#2}%
+ {\def\syst_helpers_with_set_step##1{#1[##1][#3]}%
+ \processcommalist[#2]\syst_helpers_with_set_step}}
+
+\permanent\tolerant\protected\def\dotripleemptywithset#1#*[#2]#*[#3]#*[#4]%
+ {\doifsomething{#2}%
+ {\def\syst_helpers_with_set_step##1{#1[##1][#3][#4]}%
+ \processcommalist[#2]\syst_helpers_with_set_step}}
+
+\aliased\let\dodoubleargumentwithset\dodoubleemptywithset
+\aliased\let\dotripleargumentwithset\dotripleemptywithset
+
+%D \macros
+%D {stripcharacters,stripspaces}
+%D
+%D The next command was needed first when we implemented the \CONTEXT\ interactivity
+%D macros. When we use labeled destinations, we often cannot use all the characters
+%D we want. We therefore strip some of the troublemakers, like spaces, from the
+%D labels before we write them to the \DVI||file, which passes them to for instance
+%D a \POSTSCRIPT\ file.
+%D
+%D \starttyping
+%D \stripspaces\from\one\to\two
+%D \stoptyping
+%D
+%D Both the old string \type {\one} and the new one \type {\two} are expanded. This
+%D command is a special case of:
+%D
+%D \starttyping
+%D \stripcharacter\char\from\one\to\two
+%D \stoptyping
+%D
+%D As we can see below, spaces following a control sequence are to enclosed in \type
+%D {{}}.
+
+\let\syst_helpers_strip_character \relax
+\let\m_syst_helpers_strip_character\empty
+
+\permanent\protected\def\stripcharacter#1\from#2\to#3%
+ {\def\syst_helpers_strip_character##1#1##2\end
+ {\edef\m_syst_helpers_strip_character{\m_syst_helpers_strip_character##1}%
+ \doifnotempty{##2}{\syst_helpers_strip_character##2\end}}%
+ \let\m_syst_helpers_strip_character\empty
+ \edef\m_syst_string_one{#2}%
+ \expandafter\syst_helpers_strip_character\m_syst_string_one#1\end
+ \dodoglobal\let#3\m_syst_helpers_strip_character}
+
+\permanent\protected\def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2
+ {\stripcharacter{ }\from#1\to#2}
+
+%D \macros
+%D {unspacestring}
+%D
+%D The next macro does the same but is more compatible with other macros, like \type
+%D {\convert...}.
+
+\permanent\protected\def\unspacestring#1\to#2%
+ {\stripcharacter{ }\from#1\to#2}
+
+%D \macros
+%D {executeifdefined}
+%D
+%D \CONTEXT\ uses one auxiliary file for all data concerning tables of contents,
+%D references, two||pass optimizations, sorted lists etc. This file is loaded as
+%D many times as needed. During such a pass we skip the commands thate are of no use
+%D at that moment. Because we don't want to come into trouble with undefined
+%D auxiliary commands, we call the macros in a way similar to \type {\getvalue}. The
+%D next macro take care of such executions and when not defined, gobbles the
+%D unwanted arguments.
+%D
+%D \starttyping
+%D \executeifdefined{name}\gobbleoneargument
+%D \stoptyping
+%D
+%D We can of course gobble more arguments using the appropriate gobbling command.
+
+\permanent\def\executeifdefined#1% #2 / never change this one again
+ {\ifcsname#1\endcsname
+ \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D This one also has the advantage that it is fully expandable and that it can be
+%D used after an assignment.
+
+%D \macros
+%D {doifsomespaceelse}
+%D
+%D The next command checks a string on the presence of a space and executed a
+%D command accordingly.
+%D
+%D \starttyping
+%D \doifsomespaceelse {tekst} {then ...} {else ...}
+%D \stoptyping
+%D
+%D We use this command in \CONTEXT\ for determing if an argument must be broken into
+%D words when made interactive. Watch the use of \type {\noexpand}.
+
+%D Is this one still needed?
+
+% \def\syst_helpers_if_some_space_else#1 #2#3^^^^0004{\if\noexpand#2@}
+%
+% \def\doifelsesomespace#1% % #2#3%
+% {\syst_helpers_if_some_space_else#1 @ @^^^^0004% #3\else#2\fi}
+% \expandafter\secondoftwoarguments
+% \else
+% \expandafter\firstoftwoarguments
+% \fi}
+
+\permanent\edef\doifelsesomespace#1%
+ {\noexpand\ifhastok\space{#1}%
+ \noexpand\expandafter\noexpand\firstoftwoarguments
+ \noexpand\else
+ \noexpand\expandafter\noexpand\secondoftwoarguments
+ \noexpand\fi}
+
+\aliased\let\doifsomespaceelse\doifelsesomespace
+
+%D \macros
+%D {processseparatedlist}
+%D
+%D Maybe a bit late, but here is a more general version of the \type
+%D {\processcommalist} command. This time we don't handle nesting but accept
+%D arbitrary seperators.
+%D
+%D \starttyping
+%D \processseparatedlist[list][separator]\command
+%D \stoptyping
+%D
+%D One can think of things like:
+%D
+%D \starttyping
+%D \processseparatedlist[alfa+beta+gamma][+]\message
+%D \stoptyping
+%D
+%D We want to handle all situations, like:
+%D
+%D \startbuffer
+%D \processseparatedlist[{aap noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \processseparatedlist[aap {noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \processseparatedlist[aap noot] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Therefore we smuggle a \type {\relax} in front of the argument, which we remove
+%D afterwards.
+
+\let\syst_helpers_process_separated_list_step\relax
+
+% \def\syst_helpers_process_separated_list#1]#*[#2]#3%
+% {\def\syst_helpers_process_separated_list_step##1##2#2%
+% {\def\m_syst_string_one{##2}% suggested by VZ
+% \if]##1%
+% \let\syst_helpers_process_separated_list_step\relax
+% \orelse\ifx\blankspace\m_syst_string_one
+% #3{##1}%
+% \orelse\if]##2%
+% \let\syst_helpers_process_separated_list_step\relax
+% \else
+% #3{##1##2}%
+% \fi
+% \syst_helpers_process_separated_list_step}%
+% \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2}
+
+% \def\syst_helpers_process_separated_list#1]#*[#2]#3%
+% {\def\syst_helpers_process_separated_list_step##1##2#2%
+% {\def\m_syst_string_one{##2}% suggested by VZ
+% \if]##1%
+% \orelse\ifx\blankspace\m_syst_string_one
+% #3{##1}%
+% \expandafter\syst_helpers_process_separated_list_step
+% \orelse\if]##2%
+% \else
+% #3{##1##2}%
+% \expandafter\syst_helpers_process_separated_list_step
+% \fi
+% }%
+% \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2}
+
+% \permanent\protected\def\processseparatedlist[%
+% {\syst_helpers_process_separated_list\relax}
+
+\permanent\protected\def\processseparatedlist[#+]#*[#2]#3%
+ {\tolerant\def\syst_helpers_process_separated_list_step##1#2%
+ {\ifarguments\or
+ #3{##1}%
+ \expandafter\syst_helpers_process_separated_list_step
+ \fi}%
+ \syst_helpers_process_separated_list_step#1\ignorearguments\ignorearguments}
+
+%D \macros
+%D {processlist}
+%D
+%D An even more general list processing macro is the following one:
+%D
+%D \starttyping
+%D \processlist{beginsym}{endsym}{separator}\docommand list
+%D \stoptyping
+%D
+%D This one supports arbitrary open and close symbols as well as user defined
+%D separators.
+%D
+%D \starttyping
+%D \processlist(){=>}\docommand(a=>b=>c=>d)
+%D \stoptyping
+
+\let\syst_helpers_process_any_list \relax
+\let\syst_helpers_process_any_list_indeed\relax
+\let\syst_helpers_process_any_list_step \relax
+
+\permanent\protected\def\processlist#1#2#3#4% no blank skipping !
+ {\def\syst_helpers_process_any_list_indeed##1#2%
+ {\def\syst_helpers_process_any_list_step####1####2#3%
+ {\ifx#2####1%
+ \let\syst_helpers_process_any_list_step\relax
+ \orelse\ifx#2####2%
+ \let\syst_helpers_process_any_list_step\relax
+ \else
+ #4{####1####2}%
+ \fi
+ \syst_helpers_process_any_list_step}%
+ \expandafter\syst_helpers_process_any_list_step\gobbleoneargument##1#3#2#3}%
+ \def\syst_helpers_process_any_list#1%
+ {\syst_helpers_process_any_list_indeed\relax}%
+ \syst_helpers_process_any_list}
+
+%D \macros
+%D {processassignlist}
+%D
+%D Is possible to combine an assignment list with one containing keywords.
+%D Assignments are treated accordingly, keywords are treated by \type {\command}.
+%D
+%D \starttyping
+%D \processassignlist[...=...,...=...,...]\commando
+%D \stoptyping
+%D
+%D This command can be integrated in \type {\getparameters}, but we decided best not
+%D to do so.
+
+% \protected\def\processassignlist#1[#2]#3%
+% {\def\syst_helpers_process_assign_list_assign[##1=##2=##3]%
+% {\doif{##3}\relax{#3{##1}}}%
+% \def\syst_helpers_process_assign_list_step##1%
+% {\syst_helpers_process_assign_list_assign[##1==\relax]}%
+% \processcommalist[#2]\syst_helpers_process_assign_list_step}
+
+\let\syst_helpers_process_assign_list_assign\gobbleoneoptional
+\let\syst_helpers_process_assign_list_step \gobbleoneargument
+
+\permanent\protected\def\processassignlist#1[#2]#3%
+ {\def\syst_helpers_process_assign_list_assign[##1=##-=##2]%
+ {\doif{##2}\relax{#3{##1}}}%
+ \def\syst_helpers_process_assign_list_step##1%
+ {\syst_helpers_process_assign_list_assign[##1==\relax]}%
+ \processcommalist[#2]\syst_helpers_process_assign_list_step}
+
+%D \macros
+%D {untextargument
+%D untexcommand}
+%D
+%D When manipulating data(bases) and for instance generating index entries, the next
+%D three macros can be of help:
+%D
+%D \starttyping
+%D \untextargument{...}\to\name
+%D \untexcommand {...}\to\name
+%D \stoptyping
+%D
+%D They remove braces and backslashes and give us something to sort.
+
+\let\m_syst_helpers_untexed\empty
+
+\permanent\protected\def\untexsomething
+ {\begingroup
+ \catcode\leftbraceasciicode \ignorecatcode
+ \catcode\rightbraceasciicode\ignorecatcode
+ \escapechar\minusone
+ \syst_helpers_untex_something}
+
+\def\syst_helpers_untex_something#1#2\to#3%
+ {\doglobal#1#2\to\m_syst_helpers_untexed
+ \endgroup
+ \let#3\m_syst_helpers_untexed}
+
+\permanent\protected\def\untexargument{\untexsomething\convertargument}
+\permanent\protected\def\untexcommand {\untexsomething\convertcommand}
+
+%D \macros
+%D {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints}
+%D
+%D One characteristic of \POSTSCRIPT\ and \PDF\ is that both used big points (\TEX's
+%D bp). The next macros convert points and scaled points into big points. The magic
+%D factor $72/72.27$ can be found in most \TEX\ related books.
+%D
+%D \starttyping
+%D \ScaledPointsToBigPoints {number} \target
+%D \ScaledPointsToWholeBigPoints {number} \target
+%D \stoptyping
+
+\aliased\let\tobigpoints \clf_tobigpoints % todo: permanent at lua end
+\aliased\let\towholebigpoints\clf_towholebigpoints % todo: permanent at lua end
+
+\permanent\protected\def\PointsToBigPoints #1#2{\edef#2{\tobigpoints #1}} % can be avoided
+\permanent\protected\def\PointsToWholeBigPoints #1#2{\edef#2{\towholebigpoints#1}} % can be avoided
+\permanent\protected\def\ScaledPointsToBigPoints #1#2{\edef#2{\tobigpoints #1\scaledpoint}} % obsolete
+\permanent\protected\def\ScaledPointsToWholeBigPoints#1#2{\edef#2{\towholebigpoints#1\scaledpoint}} % obsolete
+
+%D \macros
+%D {PointsToReal}
+%D
+%D Points can be stripped from their suffix by using \type {\withoutpt}. The next
+%D macro enveloppes this macro.
+%D
+%D \starttyping
+%D \PointsToReal {dimension} \target
+%D \stoptyping
+
+% \protected\def\PointsToReal#1#2%
+% {\edef#2{\withoutpt\the\dimexpr#1}}
+
+\permanent\protected\def\PointsToReal#1#2%
+ {\edef#2{\thewithoutunit\dimexpr#1}}
+
+%D \macros
+%D {dontleavehmode}
+%D
+%D Sometimes when we enter a paragraph with some command, the first token gets the
+%D whole first line. We can prevent this by saying:
+%D
+%D \starttyping
+%D \dontleavehmode
+%D \stoptyping
+%D
+%D This command is used in for instance the language module \type {lang-ini}. The
+%D first version was:
+%D
+%D \starttyping
+%D \def\dontleavehmode{\ifhmode\orelse\ifmmode\else$ $\fi}
+%D \stoptyping
+%D
+%D Next, Taco came with a better alternative (using mathsurround):
+%D
+%D \starttyping
+%D \def\dontleavehmode
+%D {\ifhmode\orelse\ifmmode\else
+%D {\mathsurround\zeropoint\everymath\emptytoks$ $}%
+%D \fi}
+%D \stoptyping
+%D
+%D And finaly we got the following alternative, one that avoids interfering grouping
+%D at the cost of a box.
+%D
+%D \starttyping
+%D \newbox\b_syst_helpers_dlh
+%D
+%D \protected\def\dontleavehmode
+%D {\ifhmode\orelse\ifmmode\else
+%D \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh
+%D \fi}
+%D \stoptyping
+%D
+%D But, as we run a recent version of \TEX, we can use the new primitive:
+
+\aliased\let\dontleavehmode\quitvmode
+
+%D \macros
+%D {utfupper, utflower, uppercasestring, lowercasestring}
+%D
+%D The names tell what they do:
+%D
+%D \starttyping
+%D \uppercasestring somestring\to\somestring
+%D \lowercasestring somestring\to\somestring
+%D \stoptyping
+%D
+%D The first argument may be a \type{\macro}.
+%D
+%D These macros are sort of obsolete as we never use uppercase this way. But
+%D nevertheless we provide them:
+
+\permanent\def\utfupper#1{\clf_upper{#1}} % expandable
+\permanent\def\utflower#1{\clf_lower{#1}} % expandable
+
+\permanent\protected\def\uppercasestring#1\to#2{\dodoglobal\edef#2{\clf_upper{#1}}}
+\permanent\protected\def\lowercasestring#1\to#2{\dodoglobal\edef#2{\clf_lower{#1}}}
+
+%D \macros
+%D {handletokens}
+%D
+%D With the next macro we enter a critical area of macro expansion. What we want is
+%D a macro that looks like:
+%D
+%D \starttyping
+%D \handletokens some tokens\with \somemacro
+%D \stoptyping
+%D
+%D A bonus example:
+%D
+%D \starttyping
+%D \hbox{\handletokens tekst en meer tekst\with\ruledhbox}
+%D
+%D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi}
+%D
+%D \hbox{\handletokens tekst en meer tekst\with\weetikveel}
+%D \stoptyping
+
+%D \macros
+%D {counttoken,counttokens}
+%D
+%D For the few occasions that we want to know the number of specific tokens in a
+%D string, we can use:
+%D
+%D \starttyping
+%D \counttoken token\in string\to \somecount
+%D \counttokens string\to \somecount
+%D \stoptyping
+%D
+%D This macro, that for instance is used in \type {cont-tab}, takes a real counter.
+%D The macro can be preceded by \type {\doglobal}.
+
+\def\syst_helpers_count_token#1% obeys {}
+ {\def\m_syst_string_three{#1}%
+ \ifx\m_syst_string_two\m_syst_string_three \else
+ \ifx\m_syst_string_one\m_syst_string_three
+ \advance\privatescratchcounter\plusone
+ \fi
+ \expandafter\syst_helpers_count_token
+ \fi}
+
+\permanent\protected\def\counttoken#1\in#2\to#3%
+ {\privatescratchcounter\zerocount
+ \def\m_syst_string_one{#1}%
+ \def\m_syst_string_two{\end}%
+ \syst_helpers_count_token#2\end
+ \dodoglobal#3\privatescratchcounter}
+
+\permanent\protected\def\counttokens#1\to#2%
+ {\privatescratchcounter\zerocount
+ \def\syst_helpers_count_token##1{\advance\privatescratchcounter\plusone}%
+ \handletokens#1\with\syst_helpers_count_token
+ \dodoglobal#2\privatescratchcounter}
+
+%D \macros
+%D {splitofftokens}
+%D
+%D Running this one not always gives the expected results. Consider for instance the
+%D macro for which I originally wrote this token handler.
+
+\let\syst_helpers_split_off_tokens\gobbleoneargument
+
+\permanent\protected\def\splitofftokens#1\from#2\to#3% slow but hardly used
+ {\ifnum#1>\zerocount
+ \privatescratchcounter#1\relax
+ \def\syst_helpers_split_off_tokens##1%
+ {\ifnum\privatescratchcounter>\zerocount
+ \advance\privatescratchcounter \minusone
+ \edef#3{#3##1}%
+ \fi}%
+ % \let#3\empty % #3 can be #2, so:
+ \expandafter\let\expandafter#3\expandafter\empty
+ \expandafter\handletokens#2\with\syst_helpers_split_off_tokens
+ \else
+ \edef#3{#2}%
+ \fi}
+
+%D This macro can be called like:
+%D
+%D \startbuffer[example]
+%D \splitofftokens10\from01234567 890123456789\to\test [\test]
+%D \stopbuffer
+%D
+%D up there. The reason for this is not that logical but follows from \TEX's
+%D However, the characters that we expect to find in \type {\test} just don't show
+%D sometimes mysterious way of expanding. Look at this:
+%D
+%D \startbuffer[next]
+%D \def\next{a} \edef\test{\next} [\test]
+%D \let\next=b \edef\test{\test\next} [\test]
+%D \let\next=c \edef\test{\next} [\test]
+%D \let\next=d \edef\test{\test\next} [\test]
+%D \let\next=e \expandafter\edef\expandafter\test\expandafter{\test\next} [\test]
+%D \stopbuffer
+%D
+%D \typebuffer[next]
+%D
+%D Careful reading shows that inside an \type {\edef} macro's that are \type {\let}
+%D are not expanded!
+%D
+%D \unprotect\getbuffer[next]\protect
+%D
+%D That's why we finally end up with a macro that looks ahead by using an
+%D assignment, this time by using \type {\futurelet}, and grabbing an argument as
+%D well. That way we can handle the sentinal, a blank space and grouped tokens.
+
+\mutable\let\nexthandledtoken\empty % part of public interface
+
+\let\syst_helpers_handle_tokens_command\relax
+
+\protected\def\syst_helpers_handle_tokens
+ {\futurelet\nexthandledtoken\syst_helpers_handle_tokens_indeed}
+
+\permanent\protected\def\handletokens#1\with#2%
+ {\gdef\syst_helpers_handle_tokens_command{#2}% permits more complex #2's
+ \syst_helpers_handle_tokens#1\end}
+
+\def\syst_helpers_handle_tokens_indeed
+ {\ifx\nexthandledtoken\blankspace
+ \expandafter\syst_helpers_handle_tokens_indeed_one
+ \orelse\ifx\nexthandledtoken\end
+ \expandafter\gobbletwoarguments % also gobble the \end
+ \else
+ \expandafter\syst_helpers_handle_tokens_indeed_two
+ \fi *}
+
+\def\syst_helpers_handle_tokens_indeed_one * %
+ {\syst_helpers_handle_tokens_command{ }\syst_helpers_handle_tokens}
+
+\def\syst_helpers_handle_tokens_indeed_two *#1%
+ {\syst_helpers_handle_tokens_command{#1}\syst_helpers_handle_tokens}
+
+%D This macro is tested on:
+%D
+%D \def\xxx#1{[#1]}
+%D
+%D \startlines
+%D \handletokens abc\with\xxx
+%D \handletokens a b c\with\xxx
+%D \handletokens a b c\with\xxx
+%D \handletokens a{bc}d\with\xxx
+%D \handletokens a\space bc \with\xxx
+%D \stoplines
+%D
+%D And our previous example shows up as:
+%D
+%D \getbuffer[example]
+
+%D \macros
+%D {iftrialtypesetting, ifvisible}
+%D
+%D The next boolean is at first sight a strange one. Sometimes one does a trial
+%D typesetting run, for instance to determine dimensions. Some mechanisms, like
+%D object inclusion, can fail on such trials. Temporary setting the next boolean to
+%D true, helps a lot. The second boolena can be used to inhibit processing
+%D completely.
+
+\newif\ifvisible \visibletrue
+
+\newtoks\everysettrialtypesetting
+\newtoks\everyresettrialtypesetting
+
+\permanent\protected\def\settrialtypesetting {\the\everysettrialtypesetting } % obeys grouping so
+\permanent\protected\def\resettrialtypesetting{\the\everyresettrialtypesetting} % this one is seldom needed
+
+\aliased\let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv !
+
+\appendtoks \enforced\aliased\let\iftrialtypesetting\iftrue \to \everysettrialtypesetting
+\appendtoks \enforced\aliased\let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting
+
+%D \macros
+%D {twodigitrounding}
+%D
+%D The next macro rounds a real number to two digits. They are probably no longer needed
+%D but we keep them around for a while.
+
+\permanent\def\integerrounding #1{\clf_rounded\zerocount\numexpr#1\relax}
+\permanent\def\onedigitrounding #1{\clf_rounded\plusone \numexpr#1\relax}
+\permanent\def\twodigitrounding #1{\clf_rounded\plustwo \numexpr#1\relax}
+\permanent\def\threedigitrounding#1{\clf_rounded\plusthree\numexpr#1\relax}
+
+%D \macros
+%D {processcontent}
+%D
+%D This macro is first used in the tabulation macros.
+%D
+%D \starttyping
+%D \protected\def\starthans%
+%D {\processcontent{stophans}\test{\message{\test}\wait}}
+%D \stoptyping
+
+\permanent\protected\def\processcontent#1%
+ {\begingroup\expandafter\syst_helpers_process_content\csname#1\endcsname}
+
+\protected\def\syst_helpers_process_content#1#2#3%
+ {\protected\def\syst_helpers_process_content##1#1%
+ {\endgroup\def#2{##1}#3}%
+ \syst_helpers_process_content}
+
+%D \macros
+%D {dogobblesingleempty, dogobbledoubleempty}
+%D
+%D These two macros savely grab and dispose two arguments.
+
+\permanent\tolerant\protected\def\dogobblesingleempty [#-]{}
+\permanent\tolerant\protected\def\dogobbledoubleempty[#-]#*[#-]{}
+
+\aliased\let\gobblesingleempty\dogobblesingleempty % also used
+\aliased\let\gobbledoubleempty\dogobbledoubleempty % also used
+
+%D \macros
+%D {setdimensionwithunit, freezedimensionwithunit}
+%D
+%D The next assignments are all valid:
+%D
+%D \starttyping
+%D \setdimensionwithunit\scratchdimen{10} {cm}
+%D \setdimensionwithunit\scratchdimen{10cm}{cm}
+%D \setdimensionwithunit\scratchdimen{10cm}{}
+%D \freezedimensionwithunit\SomeWidth{\textwidth}
+%D \freezedimensionwithunit\SomeDepth{\dp\strutbox}
+%D \stoptyping
+%D
+%D As an alternative for the next macro we can use a global assignment inside a box.
+%D The \type {\empty}'s permits gobbling while preventing spurious \type {\relax}'s.
+
+\permanent\protected\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick
+ {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty}
+
+\permanent\protected\def\freezedimensionwithunit#1#2%
+ {\setdimensionwithunit\privatescratchdimen#1{#2}\edef#1{\the\privatescratchdimen}}
+
+%D \macros
+%D {doifsometokselse, doifsometoks}
+%D
+%D Not that fast I guess, but here's a way to test for token registers being empty.
+
+\permanent\protected\def\doifelsesometoks#1%
+ {\iftok#1\emptytoks
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\permanent\protected\def\doifsometoks#1%
+ {\iftok#1\emptytoks
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\doifemptytoks#1%
+ {\iftok#1\emptytoks
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\aliased\let\doifsometokselse\doifelsesometoks
+
+%D \macros
+%D {startstrictinspectnextcharacter}
+%D
+%D This one is for the bibliography module (still?):
+
+\let\syst_helpers_strict_inspect_next_character[
+
+\def\syst_helpers_strict_inspect_next_character% no user macro !
+ {\ifx\nexttoken[%
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+\permanent\protected\def\strictdoifelsenextoptional#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \futurelet\nexttoken\syst_helpers_strict_inspect_next_character}
+
+\aliased\let\strictdoifnextoptionalelse\strictdoifelsenextoptional
+
+%D \macros
+%D {gobblespacetokens}
+%D
+%D This macro needs a speed-up!
+
+%\def\gobblespacetokens
+% {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do !
+
+\permanent\def\gobblespacetokens
+ {\afterassignment\nexttoken\let\nexttoken=}
+
+%D \macros
+%D {verbatimargument}
+%D
+%D As the name says, this macro converts its argument to a (rather safe) string.
+
+\aliased\let\verbatimstring\detokenize
+
+%D These are needed in ordinal number conversions:
+
+\permanent\def\lastdigit#1%
+ {\expandafter\thelastdigit\number#1\relax}
+
+\permanent\def\thelastdigit#1#2%
+ {\ifrelax#2#1\else\expandafter\thelastdigit\expandafter#2\fi}
+
+\permanent\def\lasttwodigits#1%
+ {\expandafter\thelasttwodigits\expandafter0\number#1\relax}
+
+\permanent\def\thelasttwodigits#1#2#3% 0 dig ... \relax
+ {\ifrelax#3#1#2\else\expandafter\thelasttwodigits\expandafter#2\expandafter#3\fi}
+
+%D \macros
+%D {serializecommalist}
+%D
+%D Concatenate commalists:
+
+\let\syst_helpers_serialize_comma_list_step\relax
+
+\mutable\let\serializedcommalist\empty
+
+\def\syst_helpers_serialize_comma_list_step#1%
+ {\edef\serializedcommalist{\serializedcommalist#1}}
+
+\permanent\protected\def\serializecommalist[#1]%
+ {\let\serializedcommalist\empty
+ \processcommacommand[#1]\syst_helpers_serialize_comma_list_step}
+
+%D \macros
+%D {purenumber}
+%D
+%D Sometimes we need control over when \TEX\ stops reading a number, especially in
+%D full expandable macros where using \type {\relax} would lead to disasters.
+%D
+%D \starttyping
+%D \ifodd\purenumber{...}\space ... \else ... \fi
+%D \stoptyping
+%D
+%D Here we use a space as number delimiter in combination with a space- and
+%D relax-less \type {\purenumber}. This macro works ok with \type {\the}, \type
+%D {\number} as well as \ETEX's \type {\numexpr}.
+
+\permanent\def\purenumber#1{\expandafter\firstofoneargument\expandafter{\number#1}}
+
+%D \macros
+%D {filterfromvalue, filterfromnext}
+%D
+%D \starttyping
+%D \setvalue{xx}{{A}{B}{C}}
+%D
+%D \filterfromvalue{xx}{3}{3}
+%D \filterfromvalue{xx}{3}{2}
+%D \filterfromvalue{xx}{3}{1}
+%D \stoptyping
+
+% can be sped up with ignored arguments
+
+\def\syst_filter_from_next_yes#1%
+ {\advance\scratchcounterone\plusone
+ \ifnum\scratchcounterone=\scratchcountertwo
+ \scratchtoks{#1}%
+ \fi
+ \ifnum\scratchcounterone=\scratchcounterthree
+ \expandafter\syst_filter_from_next_nop
+ \else
+ \expandafter\syst_filter_from_next_yes
+ \fi}
+
+\def\syst_filter_from_next_nop
+ {\expandafter\endgroup
+ \expandafter\endlocalcontrol
+ \the\scratchtoks}
+
+\permanent\def\filterfromnext#1#2% max n {..}{..}{..}{..}
+ {\beginlocalcontrol
+ \begingroup
+ \scratchcounterone \zerocount
+ \scratchcountertwo #2%
+ \scratchcounterthree #1%
+ \syst_filter_from_next_yes}
+
+\permanent\def\filterfromvalue#1#2#3% max n {..}{..}{..}{..}
+ {\beginlocalcontrol
+ \begingroup
+ \scratchcounterone \zerocount
+ \scratchcountertwo #3%
+ \scratchcounterthree #2%
+ \expandafter\expandafter\expandafter\syst_filter_from_next_yes\csname#1\endcsname}
+
+%D \macros
+%D {definemeasure}
+%D
+%D \starttyping
+%D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)]
+%D
+%D ... \measure{mywidth} ...
+%D \stoptyping
+
+\installsystemnamespace{measure}
+
+\permanent\tolerant\protected\def\definemeasure[#1]#*[#2]{\defcsname \??measure#1\endcsname{#2}}
+\permanent\tolerant\protected\def\freezemeasure[#1]#*[#2]{\edefcsname\??measure#1\endcsname{\the\dimexpr#2}}
+
+\permanent\protected\def\setmeasure #1#2{\defcsname \??measure#1\endcsname{#2}} % quick way
+\permanent\protected\def\setgmeasure#1#2{\gdefcsname\??measure#1\endcsname{#2}} % quick way
+\permanent\protected\def\setemeasure#1#2{\edefcsname\??measure#1\endcsname{\the\dimexpr#2}} % quick way
+\permanent\protected\def\setxmeasure#1#2{\xdefcsname\??measure#1\endcsname{\the\dimexpr#2}} % quick way
+
+\permanent\def\measure {\the\measured}
+\permanent\def\measured#1{\dimexpr\ifcsname\??measure#1\endcsname\lastnamedcs\else\zeropoint\fi\relax}
+
+% #2 could be omitted, but we want to support spaces
+%
+% \setmeasure {x} {1cm}
+% \setmeasure {xx} {1cm}
+% \setmeasure {xxx}{1cm}
+
+%D \macros
+%D {definequantity}
+%D
+%D These do the same but for numbers.
+
+\installsystemnamespace{quantity}
+
+\permanent\tolerant\protected\def\definequantity[#1]#*[#2]{\defcsname \??quantity#1\endcsname{#2}}
+\permanent\tolerant\protected\def\freezequantity[#1]#*[#2]{\edefcsname\??quantity#1\endcsname{\the\numexpr#2}}
+
+\permanent\protected\def\setquantity #1#2{\defcsname \??quantity#1\endcsname{#2}} % quick way
+\permanent\protected\def\setgquantity#1#2{\gdefcsname\??quantity#1\endcsname{#2}} % quick way
+\permanent\protected\def\setequantity#1#2{\edefcsname\??quantity#1\endcsname{\the\numexpr#2}} % quick way
+\permanent\protected\def\setxquantity#1#2{\xdefcsname\??quantity#1\endcsname{\the\numexpr#2}} % quick way
+
+\permanent\def\quantity {\the\quantitied}
+\permanent\def\quantitied #1{\numexpr\ifcsname\??quantity#1\endcsname\lastnamedcs\else\zeropoint\fi\relax}
+\permanent\def\directquantity#1{\the\numexpr#1\relax}
+
+% let\quantified\quantitied
+
+%D \macros
+%D {dividedsize}
+%D
+%D This one can be used inside a measure (used in m4all):
+%D
+%D \starttyping
+%D \definemeasure[columnwidth][\dividedsize\textwidth{1em}{3}]
+%D \stoptyping
+
+\permanent\def\dividedsize#1#2#3% size gap n
+ {\dimexpr
+ \ifnum\dimexpr#1\relax>\plusone
+ (\dimexpr#1\relax-\numexpr#3-\plusone\relax\dimexpr#2\relax)/#3\else#1%
+ \fi
+ \relax}
+
+%D \macros
+%D {doifdimensionelse}
+%D
+%D This is a dirty one: we simply append a unit and discard it when needed.
+
+\permanent\def\doifelsedimension#1%
+ {\ifchkdim#1\or
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifdimensionelse\doifelsedimension
+
+%D Ok, here's another one, slower but seldom used. This one scans the text.
+%D
+%D \starttabulate[|Tc|Tc|]
+%D \NC pt \NC \doifdimenstringelse {pt}{yes}{no} \NC \NR
+%D \NC 12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR
+%D \NC 1pt \NC \doifdimenstringelse {1pt}{yes}{no} \NC \NR
+%D \NC 12pt \NC \doifdimenstringelse {12pt}{yes}{no} \NC \NR
+%D \NC 12.0pt \NC \doifdimenstringelse {12.0pt}{yes}{no} \NC \NR
+%D \NC -.12pt \NC \doifdimenstringelse {-.12pt}{yes}{no} \NC \NR
+%D \NC .12pt \NC \doifdimenstringelse {.12pt}{yes}{no} \NC \NR
+%D \NC -12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR
+%D \NC -12.0pt \NC \doifdimenstringelse{-12.0pt}{yes}{no} \NC \NR
+%D \NC big \NC \doifdimenstringelse {big}{yes}{no} \NC \NR
+%D \NC 10 \NC \doifdimenstringelse {10}{yes}{no} \NC \NR
+%D \NC 1 \NC \doifdimenstringelse {1}{yes}{no} \NC \NR
+%D \stoptabulate
+
+\aliased\let\doifelsedimenstring\doifelsedimension
+\aliased\let\doifdimenstringelse\doifelsedimenstring
+
+%D \macros
+%D {comparedimension,comparedimensioneps}
+%D
+%D We no longer use the \MKIV\ dirty trick. These are obsolete anyway.
+
+\newdimen \roundingeps \roundingeps=10sp
+\newconstant\compresult
+
+\permanent\protected\def\comparedimension#1#2%
+ {\compresult
+ \ifcmpdim#1#2 % space wil be skipped
+ \zerocount
+ \or
+ \plusone
+ \else
+ \plustwo
+ \fi}
+
+\permanent\protected\def\comparedimensioneps#1#2% todo: use eps feature
+ {\compresult
+ \ifabsdim\dimexpr#1-#2\relax<\roundingeps
+ \zerocount
+ \orelse\ifdim#1<#2 % space wil be skipped
+ \plusone
+ \else
+ \plustwo
+ \fi}
+
+% pretty ugly but fast
+
+% \copycsname xxx\endcsname\csname ..\endcsname
+
+\permanent\protected\def\copycsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname}
+
+% \letcscsname \crap \csname ..\endcsname
+% \letcsnamecs \csname ..\endcsname\crap
+% \letcsnamecsname\csname ..\endcsname\csname ..\endcsname
+
+\permanent\protected\def\letcscsname {\expandafter\let\expandafter}
+\permanent\protected\def\letcsnamecs {\expandafter\let}
+\permanent\protected\def\letcsnamecsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter}
+
+% another one, add an item to a commalist
+
+\permanent\protected\def\addvalue#1#2% cs item
+ {\ifcsname#1\endcsname\else\letcsname#1\endcsname\empty\fi
+ \normalexpanded{\addtocommalist{#2}\expandafter\noexpand\csname#1\endcsname}}
+
+%D Are these ever used? Anyway, these variants are somewhat more efficient than
+%D the \MKIV variants for larger strings.
+
+% A variant:
+%
+% \permanent\def\unspaced#1%
+% {\localcontrolled{\begingroup\catcode\spaceasciicode\ignorecatcode}%
+% \tokenized{#1}%
+% \localcontrolled{\endgroup}}
+%
+% but we can also do this:
+
+\permanent\def\unspaced#1%
+ {\tokenized \s!catcodetable \ctdcatcodes {#1}}
+
+\permanent\protected\def\unspaceargument#1\to#2%
+ {\edef#2{\tokenized \s!catcodetable \ctdcatcodes {#1}}}
+
+\permanent\protected\def\unspaceafter#1#2%
+ {\expandafter#1\expandafter{\tokenized \s!catcodetable \ctdcatcodes {#2}}}
+
+\permanent\protected\def\doifelsehasspace#1%
+ {\expandafter\ifhastok\space{#1}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifhasspaceelse\doifelsehasspace
+
+% this will replace loadfile once and alike !!! todo
+
+\installsystemnamespace{flag}
+
+\permanent\protected\def\setflag #1{\dodoglobal\letcsname\??flag#1\endcsname\zerocount}
+\permanent\protected\def\resetflag#1{\dodoglobal\letcsname\??flag#1\endcsname\plusone}
+
+\permanent\def\flag#1{\csname\??flag#1\endcsname}
+
+\permanent\def\doifelseflagged#1%
+ {\expandafter\ifrelax\csname\??flag#1\endcsname
+ \expandafter\secondoftwoarguments
+ \orelse\ifcase\csname\??flag#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doifflaggedelse\doifelseflagged
+
+\permanent\def\doifnotflagged#1%
+ {\expandafter\ifrelax\csname\??flag#1\endcsname
+ \expandafter\firstofoneargument
+ \orelse\ifcase\csname\??flag#1\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\permanent\protected\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey
+ {\defcsname#1#3\expandafter\endcsname\expandafter{\csname#1#5\endcsname}}
+
+\def\syst_helpers_if_non_zero_positive_else#1#2\end % #3#4%
+ {\ifrelax#1%
+ \ifcase\privatescratchcounter
+ \endgroup
+ \doubleexpandafter\secondoftwoarguments
+ \else
+ \endgroup
+ \doubleexpandafter\firstoftwoarguments
+ \fi
+ \else
+ \endgroup
+ \expandafter\secondoftwoarguments
+ \fi}
+
+% used ?
+
+\permanent\def\doifelsenonzeropositive#1%
+ {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\privatescratchcounter=0#1\relax\empty\end}
+
+\aliased\let\doifnonzeropositiveelse\doifelsenonzeropositive
+
+%D Some old|-|time favourites:
+
+\permanent\protected\def\dosetrawvalue #1#2#3{\defcsname #1#2\endcsname{#3}}
+\permanent\protected\def\dosetrawevalue#1#2#3{\edefcsname #1#2\endcsname{#3}}
+\permanent\protected\def\dosetrawgvalue#1#2#3{\global\defcsname #1#2\endcsname{#3}}
+\permanent\protected\def\dosetrawxvalue#1#2#3{\global\edefcsname#1#2\endcsname{#3}}
+
+\permanent\protected\def\getrawparameters {\dogetparameters\dosetrawvalue }
+\permanent\protected\def\getraweparameters {\dogetparameters\dosetrawevalue}
+\permanent\protected\def\getrawgparameters {\dogetparameters\dosetrawgvalue}
+\permanent\protected\def\getrawxparameters {\dogetparameters\dosetrawxvalue}
+
+\permanent\protected\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete
+
+%D Sort of obsolete:
+
+\newcount\c_syst_helpers_mod
+
+\permanent\protected\def\dosetmodulo#1#2#3%
+ {\c_syst_helpers_mod#1\divide\c_syst_helpers_mod#2\multiply\c_syst_helpers_mod#2%
+ #3#1\advance#3-\c_syst_helpers_mod}
+
+\permanent\protected\def\dosetdivision#1#2#3%
+ {#3#1\divide#3 #2\relax}
+
+\permanent\protected\def\DoMod#1by#2to#3{\dosetmodulo {#1}{#2}{#3}}
+\permanent\protected\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}}
+
+\def\syst_helpers_unprotected#1\par
+ {#1\protect}
+
+\permanent\protected\def\unprotected
+ {\unprotect
+ \syst_helpers_unprotected}
+
+\aliased\let\resettimer \clf_resettimer % todo: at lua end
+\aliased\let\elapsedtime \clf_elapsedtime % todo: at lua end
+\aliased\let\elapsedseconds\elapsedtime
+
+\aliased\let\elapsedsteps\!!zerocount
+
+\permanent\protected\def\elapsedsteptime % unexpanded ! a bit useless but who knows ...
+ {\clf_elapsedsteptime\elapsedsteps\relax}
+
+\newcount\c_syst_helpers_test_feature_n
+\newcount\c_syst_helpers_test_feature_m
+
+\let\syst_helpers_test_feature_yes\relax
+\let\syst_helpers_test_feature_nop\relax
+
+\permanent\def\currentfeaturetest{\number\c_syst_helpers_test_feature_n}
+
+\permanent\protected\def\testfeature#1#2%
+ {\c_syst_helpers_test_feature_m#1\relax
+ \enforced\xdef\elapsedsteps{\number\c_syst_helpers_test_feature_m}%
+ \def\syst_helpers_test_feature_yes
+ {\advance\c_syst_helpers_test_feature_n\plusone
+ \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else
+ #2\expandafter\syst_helpers_test_feature_yes
+ \fi}%
+ \def\syst_helpers_test_feature_nop
+ {\advance\c_syst_helpers_test_feature_n\plusone
+ \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else
+ \expandafter\syst_helpers_test_feature_nop
+ \fi}%
+ \retestfeature}
+
+\permanent\protected\def\retestfeature % timer support is new per 10/5/2005
+ {\bgroup
+ \ifcase\interactionmode\enforced\let\wait\relax\fi
+ \clf_resettimer
+ \c_syst_helpers_test_feature_n\zerocount
+ \syst_helpers_test_feature_nop
+ \clf_benchmarktimer
+ \writestatus\m!system
+ {starting feature test: %
+ \number\c_syst_helpers_test_feature_m\space steps}%
+ \c_syst_helpers_test_feature_n\zerocount
+ \syst_helpers_test_feature_yes
+ \writestatus\m!system
+ {feature test done: %
+ \number\c_syst_helpers_test_feature_m\space steps, %
+ \clf_elapsedtime\space seconds, %
+ \clf_elapsedsteptime\elapsedsteps\space\space per step}%
+ \egroup}
+
+\permanent\protected\def\showtimer#1%
+ {\writestatus{runtime}{\elapsedseconds\space s / #1}}
+
+\permanent\protected\def\testfeatureonce#1#2%
+ {\begingroup
+ \enforced\let\wait\relax
+ \testfeature{#1}{#2}%
+ \endgroup}
+
+%D \macros
+%D {freezedimenmacro}
+%D
+%D This macro is use as:
+%D
+%D \starttyping
+%D \freezedimenmacro\leftmargindistance
+%D \stoptyping
+
+\permanent\protected\def\freezedimenmacro#1%
+ {\edef#1{\the\dimexpr#1}}
+
+%D The next macro negates a macro (dimension or number, or actually, whatever. It's
+%D a typical example of \type {\if} usage:
+%D
+%D \starttyping
+%D \if-\whatever \else-\whatever\fi => else => -whatever
+%D \if--\whatever\else-\whatever\fi => then => whatever
+%D \stoptyping
+%D
+%D Do we still need this? If not it will go away:
+
+\permanent\def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text
+
+% \def\gobbleassigndimen#1\\{}
+%
+% \permanent\def\assigndimen#1#2%
+% {\afterassignment\gobbleassigndimen#1=#2\zeropoint\\}
+
+%D Maybe ... toksapp should deal with this ...
+
+\permanent\protected\def\appendvalue #1#2%
+ {\defcsname#1\expandafter\expandafter\expandafter\endcsname\expandafter\expandafter\expandafter
+ {\begincsname#1\endcsname#2}}
+
+\permanent\protected\def\prependvalue #1#2%
+ {\edefcsname#1\endcsname
+ {\normalunexpanded{#2}\expandafter\expandafter\expandafter\empty\begincsname#1\endcsname}}
+
+\permanent\protected\def\appendgvalue #1#2{\global\appendvalue}
+\permanent\protected\def\prependgvalue#1#2{\global\prependvalue}
+
+%D \macros
+%D {dowithrange}
+%D
+%D This one is for Mojca Miklavec, who made me aware of the fact that \type
+%D {page-imp.tex} was not the best place to hide it.
+%D
+%D \startbuffer
+%D \def\DoSomething#1{ [item #1] }
+%D
+%D \processranges[1,4:5]\DoSomething \par
+%D \dowithrange {1,4:5}\DoSomething \par
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+\def\syst_helpers_with_range#1%
+ {\splitstring#1\at:\to\m_syst_helpers_range_from\and\m_syst_helpers_range_to
+ \ifempty\m_syst_helpers_range_to\let\m_syst_helpers_range_to\m_syst_helpers_range_from\fi
+ \dostepwiserecurse\m_syst_helpers_range_from\m_syst_helpers_range_to\plusone{\m_helpers_range_action{##1}}}%
+
+\permanent\protected\def\processranges[#1]#2% #1= n:m,p,q:r
+ {\def\m_helpers_range_action{#2}%
+ \processcommacommand[#1]\syst_helpers_with_range}
+
+\permanent\protected\def\dowithrange#1#2%
+ {\def\m_helpers_range_action{#2}%
+ \processcommacommand[#1]\syst_helpers_with_range}
+
+% \def\DoSomething#1{ [item #1] }
+% \dowithrange[1,4:5]\DoSomething
+
+%D \macros
+%D {ignoreimplicitspaces}
+%D
+%D \startbuffer
+%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignorespaces}
+%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b}
+%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignoreimplicitspaces}
+%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\protected\def\ignoreimplicitspaces
+ {\doifelsenextchar\relax\relax\relax}
+
+% %D \macros
+% %D {processwords}
+% %D
+% %D Not that sophisticated but sometimes users (like in metafun).
+%
+% %D This will be overloaded.
+%
+% \def\syst_helpers_process_word#1 #2\s!e_o_t_token
+% {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \s!e_o_t_token}}
+%
+% \def\processwords#1%
+% {\syst_helpers_process_word#1 \s!e_o_t_token}% no \unskip
+%
+% \let\processword\relax
+
+%D \macros
+%D {startnointerference}
+%D
+%D \starttyping
+%D \startnointerference
+%D all kind of code
+%D \stopnointerference
+%D \stoptyping
+
+\newbox\b_syst_helpers_no_interference
+
+\permanent\protected\def\startnointerference % not even grouped !
+ {\setbox\b_syst_helpers_no_interference\vbox
+ \bgroup}
+
+\permanent\protected\def\stopnointerference
+ {\egroup
+ \setbox\b_syst_helpers_no_interference\emptybox}
+
+%D A variant for \type {\executeifdefined}:
+
+\permanent\def\expandcheckedcsname#1#2% #2 is often a \xxxparameter so let's expand it once
+ {\normalexpanded{\noexpand\syst_helpers_expand_checked_csname{#1}{#2}}}
+
+\def\syst_helpers_expand_checked_csname#1#2#3%
+ {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname}
+
+%D Signal. Some fonts have a char0 rendering so we need to make sure that it is not
+%D set in the font! (This will be overloaded)
+
+\protected\def\signalcharacter{\char\zerocount} % \zwj
+
+%D A few secial variants of commands defined here. Some more will be moved here (e.g.
+%D from table modules.
+
+\permanent\def\dodirectdoubleempty#1#2% used in math (lookahead issues)
+ {\ifx#2[%
+ \expandafter\syst_helpers_direct_double_empty_one_yes
+ \else
+ \expandafter\syst_helpers_direct_double_empty_one_nop
+ \fi#1#2}
+
+\def\syst_helpers_direct_double_empty_one_yes#1[#2]#3%
+ {\ifx#3[\else\expandafter\syst_helpers_direct_double_empty_two_nop\fi#1[#2]#3}
+
+\def\syst_helpers_direct_double_empty_one_nop#1{#1[][]}
+\def\syst_helpers_direct_double_empty_two_nop#1[#2]{#1[#2][]}
+
+% %D Used in math definitions (in an \type {\edef}). This will be replaced.
+% %D
+% %D \startbuffer
+% %D [\docheckedpair{}]
+% %D [\docheckedpair{a}]
+% %D [\docheckedpair{a,b}]
+% %D [\docheckedpair{a,b,c}]
+% %D \stopbuffer
+% %D
+% %D \typebuffer \startlines \getbuffer \stoplines
+%
+% \permanent\def\docheckedpair#1%
+% {\syst_helpers_checked_pair#1,,\s!e_o_t_token}
+%
+% \def\syst_helpers_checked_pair#1,#2,#-\s!e_o_t_token
+% {#1,#2}
+
+%D Here are some nasty helpers. They can be used to fill often expanded token
+%D lists efficiently (see tabulate for an example).
+
+\permanent\def\constantnumber#1%
+ {\ifcase#1\zerocount
+ \or \plusone
+ \or \plustwo
+ \or \plusthree
+ \or \plusfour
+ \or \plusfive
+ \or \plussix
+ \or \plusseven
+ \or \pluseight
+ \or \plusnine
+ \or \plusten
+ \else \number#1\relax\fi}
+
+\permanent\def\constantnumberargument#1%
+ {\ifcase#1\zerocount
+ \or \plusone
+ \or \plustwo
+ \or \plusthree
+ \or \plusfour
+ \or \plusfive
+ \or \plussix
+ \or \plusseven
+ \or \pluseight
+ \or \plusnine
+ \or \plusten
+ \else {\number#1}\fi}
+
+\permanent\def\constantdimen#1%
+ {\ifdim#1=\zeropoint
+ \zeropoint
+ \else
+ \the#1\relax
+ \fi}
+
+\permanent\def\constantdimenargument#1%
+ {\ifdim#1=\zeropoint
+ \zeropoint
+ \else
+ {\the#1}%
+ \fi}
+
+\permanent\def\constantemptyargument#1%
+ {\ifempty#1%
+ \noexpand\empty
+ \else
+ {#1}%
+ \fi}
+
+%D \macros
+%D {getsubstring}
+%D \startbuffer
+%D
+%D \getsubstring{4}{}{Who Wants This}
+%D \getsubstring{4}{9}{Who Wants This}
+%D \getsubstring{9}{-2}{Who Wants This}
+%D \getsubstring{1}{5}{Who Wants This}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+% expandable:
+
+\permanent\def\getsubstring#1#2#3{\clf_getsubstring{#3}{#1}{#2}}
+
+%D Other dimensions than pt (used in mb-mp)
+
+\permanent\def\converteddimen#1#2{\clf_converteddimen\dimexpr#1\relax{#2}}
+
+%D Maybe (looks ugly):
+%D
+%D \starttyping
+%D \doifcase {foo}
+%D {bar} {BAR}
+%D {foo} {FOO}
+%D {default} {DEFAULT}
+%D
+%D \doifcase {foo}
+%D {bar} {BAR}
+%D {foo} {\doifcase {bar}
+%D {bar} {BAR}
+%D {foo} {FOO}
+%D {default} {DEFAULT}
+%D }
+%D {default} {DEFAULT}
+%D \stoptyping
+
+% \doifcase {\btxfoundname{author}}
+% {author} {\btxflush{author}}
+% {editor} {\texdefinition{btx:apa:editor-or-editors}}
+% {title} {\texdefinition{btx:apa:title-subtitle-type}}
+% {default} {\btxflush{author}}
+
+% \protected\def\doifcase#1%
+% {\edef\m_case_asked{#1}%
+% \syst_aux_case}
+%
+% \def\syst_aux_case#1%
+% {\edef\m_case_temp{#1}%
+% \ifx\m_case_temp\m_case_asked
+% \expandafter\syst_aux_case_yes
+% \orelse\ifx\m_case_temp\s!default
+% \expandafter\firstofoneargument
+% \else
+% \expandafter\syst_aux_case_nop
+% \fi}
+%
+% \def\syst_aux_skip#1#2%
+% {\edef\m_case_temp{#1}%
+% \ifx\m_case_temp\s!default
+% \expandafter\syst_aux_done
+% \else
+% \expandafter\syst_aux_skip
+% \fi}
+%
+% \def\syst_aux_case_yes#1%
+% {\def\syst_aux_done{#1}%
+% \syst_aux_skip}
+%
+% \def\syst_aux_case_nop#1%
+% {\syst_aux_case}
+
+%D \macros
+%D {ntimes}
+%D
+%D some repetition:
+%D
+%D \startbuffer
+%D \ntimes{*}{20}
+%D \stopbuffer
+%D
+%D \typebuffer \blank gives: \getbuffer \blank
+%D
+%D This is not real fast but quite okay:
+
+%def\ntimes#1#2{\ifnum#2>\zerocount#1\ntimes{#1}{\numexpr#2-\plusone\relax}\fi} % 1.72
+\permanent\def\ntimes#1#2{\clf_ntimes{#1}\numexpr#2\relax} % 0.33
+
+%D Experiment (sometimes looks nicer in code):
+
+\permanent\protected\def\sameargumentscondition#1#2%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two}
+
+\permanent\protected\def\emptyargumentcondition#1%
+ {\edef\m_syst_string_one{#1}%
+ \ifempty\m_syst_string_one}
+
+%D New (also serves as an example):
+%D
+%D \starttyping
+%D \commandflags\defineframed
+%D \stoptyping
+
+\permanent\def\commandflags#1%
+ {\beginlocalcontrol\begingroup\scratchtoks\emptytoks\donefalse
+ \ifflags#1= \frozenflagcode\etoksapp\scratchtoks{\ifdone \space\fi frozen}\donetrue\fi
+ \ifflags#1=\permanentflagcode\etoksapp\scratchtoks{\ifdone \space\fi permanent}\donetrue\fi
+ \ifflags#1=\immutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi immutable}\donetrue\fi
+ \ifflags#1=\primitiveflagcode\etoksapp\scratchtoks{\ifdone \space\fi primitive}\donetrue\fi
+ \ifflags#1= \mutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi mutable}\donetrue\fi
+ \ifflags#1=\noalignedflagcode\etoksapp\scratchtoks{\ifdone \space\fi noaligned}\donetrue\fi
+ \ifflags#1= \instanceflagcode\etoksapp\scratchtoks{\ifdone \space\fi instance}\donetrue\fi
+ %\ifflags#1= \reservedflagcode\etoksapp\scratchtoks{\ifdone \space\fi reserved}\donetrue\fi
+ \ifflags#1= \tolerantflagcode\etoksapp\scratchtoks{\ifdone \space\fi tolerant}\donetrue\fi
+ \ifflags#1=\protectedflagcode\etoksapp\scratchtoks{\ifdone \space\fi protected}\donetrue\fi
+ \expandafter\endgroup\expandafter\endlocalcontrol\the\scratchtoks}
+
+%D \macros
+%D {resetmacros}
+%D
+%D The next macro can be used to reset a macro:
+%D
+%D \starttyping
+%D \resetmacros[startfoo,\stopfoo]
+%D \stoptyping
+
+\permanent\protected\def\syst_reset_macro#1%
+ {\overloaded\letcsname\csstring#1\endcsname\undefined} % so only frozen (instances(
+
+\permanent\protected\def\resetmacros[#1]%
+ {\processcommalist[#1]\syst_reset_macro}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-cmp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-cmp.mkxl
new file mode 100644
index 00000000000..a5ebdd8e33d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-cmp.mkxl
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=syst-cmp,
+%D version=2018.12.05,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Compatibility,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Some code that has been laying around for generic compatibility is (and will be)
+%D moved here, for instance because tikz needs it. At some point I will make an
+%D indirect interface for generic code (if needed).
+
+\registerctxluafile{syst-cmp}{}
+
+\unprotect
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-con.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-con.mkxl
new file mode 100644
index 00000000000..ed4a4f3c5a7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-con.mkxl
@@ -0,0 +1,88 @@
+%D \module
+%D [ file=syst-con,
+%D version=2006.09.16, % real old stuff ... 2000.12.10
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Conversions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{syst-con}{}
+
+\unprotect
+
+%D \macros{lchexnumber,uchexnumber,lchexnumbers,uchexnumbers}
+%D
+%D In addition to the uppercase hex conversion, as needed in math families, we
+%D occasionally need a lowercase one.
+
+\permanent\def\lchexnumber #1{\clf_lchexnumber \numexpr#1\relax}
+\permanent\def\uchexnumber #1{\clf_uchexnumber \numexpr#1\relax}
+\permanent\def\lchexnumbers#1{\clf_lchexnumbers\numexpr#1\relax}
+\permanent\def\uchexnumbers#1{\clf_uchexnumbers\numexpr#1\relax}
+
+\aliased\let\hexnumber\uchexnumber
+
+%D \macros{octnumber}
+%D
+%D For \UNICODE\ remapping purposes, we need octal numbers.
+
+\permanent\def\octnumber#1{\clf_octnumber\numexpr#1\relax}
+
+%D \macros{hexstringtonumber,octstringtonumber}
+%D
+%D This macro converts a two character hexadecimal number into a decimal number,
+%D thereby taking care of lowercase characters as well.
+
+\permanent\def\hexstringtonumber#1{\clf_hexstringtonumber\numexpr#1\relax}
+\permanent\def\octstringtonumber#1{\clf_octstringtonumber\numexpr#1\relax}
+
+%D \macros{rawcharacter}
+%D
+%D This macro can be used to produce proper 8 bit characters that we sometimes need
+%D in backends and round||trips.
+
+\permanent\def\rawcharacter#1{\clf_rawcharacter\numexpr#1\relax}
+
+%D \macros{twodigits, threedigits}
+%D
+%D These macros provides two or three digits always:
+
+\permanent\def\twodigits #1{\ifnum #1<10 0\fi\number#1}
+\permanent\def\threedigits#1{\ifnum#1<100 \ifnum#1<10 0\fi0\fi\number#1}
+
+%D \macros{modulonumber}
+%D
+%D In the conversion macros described in \type {core-con} we need a wrap||around
+%D method. The following solution is provided by Taco.
+%D
+%D The \type {modulonumber} macro expands to the mathematical modulo of a positive
+%D integer. It is crucial for it's application that this macro is fully exandable.
+%D
+%D The expression inside the \type {\numexpr} itself is somewhat bizarre because
+%D \ETEX\ uses a rounding division instead of truncation. If \ETEX's division would
+%D have behaved like \TEX's normal\type {\divide}, then the expression could have
+%D been somewhat simpler, like \type {#2-(#2/#1)*#1}. This works just as well, but a
+%D bit more complex.
+
+%permanent\def\modulonumber#1#2{\the\numexpr#2-((((#2+(#1/2))/#1)-1)*#1)\relax}
+%permanent\def\modulonumber#1#2{\the\numexpr#2-(#2:#1)*#1\relax}
+
+%D \macros{setcalculatedsin,setcalculatedcos,setcalculatedtan}
+
+\permanent\protected\def\setcalculatedsin#1#2{\edef#1{\clf_sind#2}}
+\permanent\protected\def\setcalculatedcos#1#2{\edef#1{\clf_cosd#2}}
+\permanent\protected\def\setcalculatedtan#1#2{\edef#1{\clf_tand#2}}
+
+%D \macros{formatted,format}
+
+\permanent \def\formatted#1{\ctxcommand{format(#1)}} % not clf
+\permanent\protected\def\format #1{\ctxcommand{format(#1)}} % not clf
+
+%D The \type {\modulatednumber} and \type {\realnumber} macros have been removed.
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-fnt.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-fnt.mkxl
new file mode 100644
index 00000000000..e6029d80511
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-fnt.mkxl
@@ -0,0 +1,47 @@
+%D \module
+%D [ file=syst-fnt,
+%D version=2006.08.11,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Font Things,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% formal names cf the tb \& tbt
+
+\unprotect
+
+\immutable\protected\def\fontslantperpoint {\scaledfontdimen\plusone }
+\immutable\protected\def\fontinterwordspace {\scaledfontdimen\plustwo }
+\immutable\protected\def\fontinterwordstretch{\scaledfontdimen\plusthree}
+\immutable\protected\def\fontinterwordshrink {\scaledfontdimen\plusfour }
+\immutable\protected\def\fontexheight {\scaledfontdimen\plusfive }
+\immutable\protected\def\fontemwidth {\scaledfontdimen\plussix }
+\immutable\protected\def\fontextraspace {\scaledfontdimen\plusseven}
+
+\immutable\protected\def\slantperpoint {\scaledfontdimen\plusone \font}
+\immutable\protected\def\interwordspace {\scaledfontdimen\plustwo \font}
+\immutable\protected\def\interwordstretch {\scaledfontdimen\plusthree\font}
+\immutable\protected\def\interwordshrink {\scaledfontdimen\plusfour \font}
+\immutable\protected\def\exheight {\scaledfontdimen\plusfive \font}
+\immutable\protected\def\emwidth {\scaledfontdimen\plussix \font}
+\immutable\protected\def\extraspace {\scaledfontdimen\plusseven\font}
+
+\aliased\let\mathaxisheight\Umathaxis % takes style
+
+\permanent\def\currentspaceskip{\interwordspace\s!plus\interwordstretch\s!minus\interwordshrink\relax}
+
+\permanent\def\currentspacevalue{\the\interwordspace
+ \ifzeropt\interwordstretch\else{ plus }\the\interwordstretch\fi
+ \ifzeropt\interwordshrink \else{ minus }\the\interwordshrink \fi}
+
+\permanent\def\usedspacevalue{\ifzeropt\spaceskip\the\spaceskip\else\currentspacevalue\fi}
+
+\permanent\def\mathstacktotal{\dimexpr\Umathstacknumup\scriptstyle+\Umathstackdenomdown\scriptstyle\relax}
+\permanent\def\mathstackvgap {\Umathstackvgap\scriptstyle}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-ini.mkxl
new file mode 100644
index 00000000000..50304d1048a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-ini.mkxl
@@ -0,0 +1,1319 @@
+%D \module
+%D [ file=syst-ini,
+%D version=2008.11.04, % 2001.11.16, % 1999.03.17, % an oldie: 1995.10.10
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Bootstrapping \TEX,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D We used to load plain \TEX\ in a special way, but redefining a couple of
+%D primitives so that for instance font loading was ignored. For those interested,
+%D this loader is found in \type {syst-tex.tex}. Some of the comment's are Don
+%D Knuths and more of it can be found in the plain \TEX\ format.
+%D
+%D Characters can have special states, that can be triggered by setting their
+%D category coded. Some are preset, others are to be set as soon as possible,
+%D otherwise we cannot define any useful macros.
+%D
+%D First we define a bunch of constants. Normally we would \type {\setconstant}
+%D but we're prestine and have no macros defined yet. Abstraction also makes it
+%D possible to avoid the \type {^^} in the input.
+
+\immutable\integerdef\escapecatcode 0
+\immutable\integerdef\begingroupcatcode 1
+\immutable\integerdef\endgroupcatcode 2
+\immutable\integerdef\mathshiftcatcode 3
+\immutable\integerdef\alignmentcatcode 4
+\immutable\integerdef\endoflinecatcode 5
+\immutable\integerdef\parametercatcode 6
+\immutable\integerdef\superscriptcatcode 7
+\immutable\integerdef\subscriptcatcode 8
+\immutable\integerdef\ignorecatcode 9
+\immutable\integerdef\spacecatcode 10
+\immutable\integerdef\lettercatcode 11
+\immutable\integerdef\othercatcode 12 % finally obsolete: \let\other \othercatcode
+\immutable\integerdef\activecatcode 13 % finally obsolete: \let\active\activecatcode
+\immutable\integerdef\commentcatcode 14
+\immutable\integerdef\invalidcatcode 15
+
+%immutable\integerdef\zeroasciicode 0
+\immutable\integerdef\tabasciicode 9
+\immutable\integerdef\newlineasciicode 10 % don't confuse this one with \endoflineasciicode
+\immutable\integerdef\formfeedasciicode 12
+\immutable\integerdef\endoflineasciicode 13 % somewhat messy but this can be the active \par
+\immutable\integerdef\endoffileasciicode 26
+\immutable\integerdef\spaceasciicode 32
+\immutable\integerdef\exclamationmarkasciicode 33 % ! used in namespace protection
+\immutable\integerdef\doublequoteasciicode 34 % "
+\immutable\integerdef\hashasciicode 35
+\immutable\integerdef\dollarasciicode 36
+\immutable\integerdef\commentasciicode 37
+\immutable\integerdef\ampersandasciicode 38
+\immutable\integerdef\singlequoteasciicode 39 % '
+\immutable\integerdef\primeasciicode 39 % '
+\immutable\integerdef\leftparentasciicode 40
+\immutable\integerdef\rightparentasciicode 41
+\immutable\integerdef\commaasciicode 44
+\immutable\integerdef\hyphenasciicode 45
+\immutable\integerdef\periodasciicode 46
+\immutable\integerdef\forwardslashasciicode 47 % /
+\immutable\integerdef\colonasciicode 58
+\immutable\integerdef\lessthanasciicode 60 % < used as alternative verbatim {
+\immutable\integerdef\morethanasciicode 62 % > used as alternative verbatim }
+\immutable\integerdef\questionmarkasciicode 63 % ? used in namespace protection
+\immutable\integerdef\atsignasciicode 64 % @ used in namespace protection
+\immutable\integerdef\leftbracketasciicode 91
+\immutable\integerdef\backslashasciicode 92 % `\\
+\immutable\integerdef\rightbracketasciicode 93
+\immutable\integerdef\circumflexasciicode 94
+\immutable\integerdef\underscoreasciicode 95
+\immutable\integerdef\leftbraceasciicode 123 % `\{
+\immutable\integerdef\barasciicode 124 % `\|
+\immutable\integerdef\rightbraceasciicode 125 % `\}
+\immutable\integerdef\tildeasciicode 126 % `\~
+\immutable\integerdef\delasciicode 127
+
+\aliased\let\percentasciicode\commentasciicode
+
+%catcode\zeroasciicode \ignorecatcode % `\^^@ ascii null is ignored
+\catcode\tabasciicode \spacecatcode % `\^^I ascii tab is a blank space
+\catcode\formfeedasciicode \activecatcode % `\^^L ascii form-feed (active, set later)
+%catcode\endoflineasciicode \endoflinecatcode % `\^^M ascii return is end-line
+\catcode\endoffileasciicode \ignorecatcode % `\^^Z endoffile (ignored in ConTeXt)
+%catcode\spaceasciicode \spacecatcode % `\ ascii space is blank space
+\catcode\hashasciicode \parametercatcode % `\# hash mark is macro parameter character
+\catcode\dollarasciicode \mathshiftcatcode % `\$ dollar sign is math shift
+%catcode\commentasciicode \commentcatcode % `\% percent sign is comment character
+\catcode\ampersandasciicode \alignmentcatcode % `\& ampersand is alignment tab
+%catcode\backslashasciicode \escapecatcode % `\\ backslash is TeX escape character
+\catcode\circumflexasciicode \superscriptcatcode % `\^ circumflex and uparrow are for superscripts
+\catcode\underscoreasciicode \subscriptcatcode % `\_ underline and downarrow are for subscripts
+\catcode\leftbraceasciicode \begingroupcatcode % `\{ left brace is begin-group character
+\catcode\rightbraceasciicode \endgroupcatcode % `\} right brace is end-group character
+\catcode\tildeasciicode \activecatcode % `\~ tilde is active
+%catcode\delasciicode \invalidcatcode % `\^^? ascii delete is invalid
+
+%D First we need to initialization the primitives. Because \CONTEXT\ is already a
+%D pretty old macro package, we have a couple of issues with respect to primitives.
+%D The \ETEX\ engine added a few as did \PDFTEX. The \LUATEX\ engine added even
+%D more. This means that there can be a potential clash between primitives and
+%D existing macros. The most noticeable ones are:
+%D
+%D \starttyping
+%D \protected
+%D \expanded
+%D \unexpanded
+%D \stoptyping
+%D
+%D Because we had macros like that before the primitives showed up. The protection
+%D related macros were there before we even knew about extensions to the engine.
+%D When the expansion related ones were introduced, we originally came up with
+%D different names but due to requests we used the current names, somethng that in
+%D retrospect was a bad idea: they should have gotten different names in \LUATEX, if
+%D only because at that time only \CONTEXT\ was using them in rolling releases.
+%D Anyway, we're now stuck with this situation, and it means that one should use the
+%D \type {\normal...} variants in low level code:
+%D
+%D \starttyping
+%D \normalexpanded
+%D \normalprotected
+%D \normalunexpanded
+%D \stoptyping
+%D
+%D In the end not using different names in \LUATEX\ for these kind of backfires. It
+%D makes not much sense to fix this in \LUAMETATEX\ because we're now to long on the
+%D road. It is actually the reason why we have the option in \LUATEX\ to alias all
+%D primitives in one go using a prefix. Actually this trick could be used to recover
+%D a primitive meaning: just enable it with some prefix and \type {\let} the
+%D original to that. But \unknown\ we prevent that trick below.
+%D
+%D The code below differs from \LUATEX: in \LUAMETATEX\ all primitives are already
+%D available; it cannot limit itself to being \TEX\ or \ETEX. It could not do that
+%D anyway because there are differences (no backend, to mention one).
+
+\directlua {
+ tex.enableprimitives("normal",true) % we default to everything
+ function tex.enableprimitives() end % so we kind of protect what's there
+}
+
+\permanent\let\undefined\undefined
+
+\immutable\def\space{ }
+\immutable\def\empty{}
+\mutable \def\temp {}
+
+\immutable\letcharcode \formfeedasciicode \par % \def ^^L{\par} formfeed
+\immutable\letcharcode \tildeasciicode \ % tilde
+\immutable\letcharcode \spaceasciicode \space % space
+
+\immutable\defcsname\Uchar\tabasciicode \endcsname {\ } % \def\^^I{\ } tab
+\immutable\defcsname\Uchar\formfeedasciicode \endcsname {\par} % \def\^^L{\par} formfeed
+\immutable\defcsname\Uchar\endoflineasciicode\endcsname {\ } % \def\^^M{\ } return
+
+%D For now:
+
+\permanent\def\gobbleoneargument#-{} % will be defined later on anyway
+
+%D First we define a simplified version of the \CONTEXT\ protection mechanism. Later
+%D we will implement a better variant.
+
+\def\unprotect
+ {\edef\protect
+ {\catcode\atsignasciicode \the\catcode\atsignasciicode \relax
+ \catcode\exclamationmarkasciicode\the\catcode\exclamationmarkasciicode\relax
+ \catcode\questionmarkasciicode \the\catcode\questionmarkasciicode \relax
+ \catcode\underscoreasciicode \the\catcode\underscoreasciicode \relax
+ \let\protect\relax}%
+ \catcode\atsignasciicode \lettercatcode
+ \catcode\exclamationmarkasciicode\lettercatcode
+ \catcode\questionmarkasciicode \lettercatcode
+ \catcode\underscoreasciicode \lettercatcode}
+
+\let\protect\relax
+
+\unprotect
+
+%D Some pretty important definitions:
+
+\immutable\let\bgroup={
+\immutable\let\egroup=}
+
+%D \macros
+%D {normalbgroup,normalgroup}
+%D
+%D No comment.
+
+%D Allocation of registers is done slightly different than in plain \TEX. First of
+%D all we use different reserved counters. We also don't implement a family handler
+%D because users are not supposed to implement their own math. We reserve the lowest
+%D 31 registers for scratch purposes. Keep in mind that in the core engine some
+%D registers are reserved: counters 0 upto 9, and counter 255.
+%D
+%D As with plain \TEX\ we recommend that macro designers always use \type {\global}
+%D assignments with respect to registers numbered 1, 3, 5 \unknown\ 31, and always
+%D non||\type {\global} assignments with respect to registers 0, 2, 4, \unknown\ 30.
+%D This will prevent \quote {save stack buildup} that might otherwise occur.
+%D
+%D We reserve some registers for special (management) purposes:
+
+% 0 - 20 : scratch
+% 21 - 127 : internal
+% 128 - 254 : inserts
+% 255 : page
+% 256 - : user
+
+% use \chardef instead for min and max
+%
+% will be blocked: \newfamily \newlanguage
+
+\permanent\countdef \c_syst_min_allocated_register = 52 \c_syst_min_allocated_register = 256 % can change
+\permanent\countdef \c_syst_max_allocated_register = 53 \c_syst_max_allocated_register = 65535
+\permanent\countdef \c_syst_min_allocated_read = 54 \c_syst_min_allocated_read = 0
+\permanent\countdef \c_syst_max_allocated_read = 55 \c_syst_max_allocated_read = 1023
+\permanent\countdef \c_syst_min_allocated_language = 56 \c_syst_min_allocated_language = 0
+\permanent\countdef \c_syst_max_allocated_language = 57 \c_syst_max_allocated_language = 8191
+\permanent\countdef \c_syst_min_allocated_insert = 58 \c_syst_min_allocated_insert = 128
+\permanent\countdef \c_syst_max_allocated_insert = 59 \c_syst_max_allocated_insert = 254
+\permanent\countdef \c_syst_min_allocated_family = 60 \c_syst_min_allocated_family = 128
+\permanent\countdef \c_syst_max_allocated_family = 61 \c_syst_max_allocated_family = 255
+\permanent\countdef \c_syst_min_allocated_attribute = 62 \c_syst_min_allocated_attribute = 1024 % 0-1023 : private
+\permanent\countdef \c_syst_min_allocated_write = 63 \c_syst_min_allocated_write = 0
+\permanent\countdef \c_syst_max_allocated_write = 64 \c_syst_max_allocated_write = 1023
+
+\permanent\countdef \c_syst_last_allocated_count = 32 \c_syst_last_allocated_count = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_dimen = 33 \c_syst_last_allocated_dimen = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_skip = 34 \c_syst_last_allocated_skip = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_muskip = 35 \c_syst_last_allocated_muskip = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_box = 36 \c_syst_last_allocated_box = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_toks = 37 \c_syst_last_allocated_toks = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_read = 38 \c_syst_last_allocated_read = \c_syst_min_allocated_read
+\permanent\countdef \c_syst_last_allocated_write = 39 \c_syst_last_allocated_write = \c_syst_min_allocated_write
+\permanent\countdef \c_syst_last_allocated_marks = 40 \c_syst_last_allocated_marks = \c_syst_min_allocated_register
+\permanent\countdef \c_syst_last_allocated_language = 41 \c_syst_last_allocated_language = \c_syst_min_allocated_language % not used in context
+\permanent\countdef \c_syst_last_allocated_insertion = 42 \c_syst_last_allocated_insertion = \c_syst_min_allocated_insert
+\permanent\countdef \c_syst_last_allocated_family = 43 \c_syst_last_allocated_family = \c_syst_min_allocated_family % not used in context
+\permanent\countdef \c_syst_last_allocated_attribute = 44 \c_syst_last_allocated_attribute = \c_syst_min_allocated_attribute % not used in context
+
+\permanent\countdef \c_syst_min_counter_value = 125 \c_syst_min_counter_value = -"7FFFFFFF % beware, we use index 125 at the lua end
+\permanent\countdef \c_syst_max_counter_value = 126 \c_syst_max_counter_value = "7FFFFFFF % beware, we use index 126 at the lua end
+
+\immutable\integerdef\zerocount 0
+\immutable\integerdef\plusone 1
+\immutable\integerdef\minusone -1
+
+\immutable\integerdef\normalpagebox 255 % hardcoded in pdftex/xetex
+
+\immutable\integerdef\statuswrite \minusone
+
+%D So, effectively we start allocating from 256 and upwards. The inserts sit in the
+%D range 128 upto 254. Page numbers use the counters 0 upto 9 and the pagebox is
+%D 255. Users can use the scratch registers upto 31 without problem but all others
+%D are reserved.
+
+\permanent\let\wlog\gobbleoneargument % Let's get rid of this one.
+
+%D The allocators share a common helper macro.
+
+\permanent\protected\def\newcount {\syst_basics_allocate\c_syst_last_allocated_count \count \countdef \c_syst_max_allocated_register}
+\permanent\protected\def\newdimen {\syst_basics_allocate\c_syst_last_allocated_dimen \dimen \dimendef \c_syst_max_allocated_register}
+\permanent\protected\def\newskip {\syst_basics_allocate\c_syst_last_allocated_skip \skip \skipdef \c_syst_max_allocated_register}
+\permanent\protected\def\newmuskip {\syst_basics_allocate\c_syst_last_allocated_muskip \muskip \muskipdef \c_syst_max_allocated_register}
+\permanent\protected\def\newbox {\syst_basics_allocate\c_syst_last_allocated_box \box \integerdef\c_syst_max_allocated_register}
+\permanent\protected\def\newtoks {\syst_basics_allocate\c_syst_last_allocated_toks \toks \toksdef \c_syst_max_allocated_register}
+\permanent\protected\def\newread {\syst_basics_allocate\c_syst_last_allocated_read \read \integerdef\c_syst_max_allocated_read}
+\permanent\protected\def\newwrite {\syst_basics_allocate\c_syst_last_allocated_write \write \integerdef\c_syst_max_allocated_write}
+\permanent\protected\def\newmarks {\syst_basics_allocate\c_syst_last_allocated_marks \marks \integerdef\c_syst_max_allocated_register}
+\permanent\protected\def\newinsert {\syst_basics_allocate\c_syst_last_allocated_insertion\insert \chardef \c_syst_max_allocated_insert}
+\permanent\protected\def\newlanguage{\syst_basics_allocate\c_syst_last_allocated_language \language\integerdef\c_syst_max_allocated_language} % not needed in context
+\permanent\protected\def\newfamily {\syst_basics_allocate\c_syst_last_allocated_family \fam \integerdef\c_syst_max_allocated_family} % not needed in context
+
+\aliased\let\newfam\newfamily
+
+\firstvalidlanguage \plusone % so zero is ignored in hyphenation, this might become the default
+
+% Watch out, for the moment we disable the check for already being defined
+% later we will revert this but first all chardefs must be replaced.
+
+\permanent\protected\def\setconstant {}
+\permanent\protected\def\setconstantvalue#1#2{\csname#1\endcsname\numexpr#2\relax}
+
+\permanent\protected\def\newconstant#1%
+ {\ifrelax#1%
+ %\writestatus{!!!!!!!!!!}{\string #1 => RELAX}%
+ \unletfrozen#1%
+ \newcount#1%
+ \orelse\ifdefined#1%
+ %\writestatus{!!!!!!!!!!}{\string #1 => DEFINED}%
+ \else
+ \newcount#1%
+ \fi}
+
+\permanent\protected\def\setnewconstant#1{\newconstant#1#1}
+
+% %D The next definitions are really needed (in \CONTEXT):
+
+\newlinechar\newlineasciicode \permanent\edef\outputnewlinechar{\Uchar\newlineasciicode} % {^^J}
+
+%D One reason to start high with allocation is that it permits us to allocate
+%D consecutive ranges more easily, for instance if for \MPLIB\ we want to allocate a
+%D continuous range of boxes. It also permits us to do a proper upward allocation
+%D for inserts. The current code evolved from code that dealt with older engines but
+%D as all engines now provide many registers we removed all traces.
+
+\ifdefined\writestatus \else
+ %\protected\def\writestatus#1#2{\immediate\write\statuswrite{#1: #2}}
+ \frozen\protected\def\writestatus#1#2{\message{#1: #2}}
+\fi
+
+\def\syst_basics_allocate_yes#1#2#3#4#5% last class method max name
+ {\ifnum#1<#4\relax
+ \global\advance#1\plusone
+ \permanent\global#3#5=#1\relax
+ \else
+ \writestatus{warning}{no room for \string#2\space \string#5\space (max: \number#4)}%
+ \fi}
+
+\def\syst_basics_allocate_nop#1#2#3#4#5% last class method max name
+ {\writestatus{warning}{\string#2 \string#5 is already defined (\string\relax\space it first)}}
+
+\def\syst_basics_allocate#1#2#3#4#5% last class method max name
+ {\ifrelax#5%
+ \expandafter\syst_basics_allocate_yes
+ \orelse\ifdefined#5%
+ \expandafter\syst_basics_allocate_nop
+ \else
+ \expandafter\syst_basics_allocate_yes
+ \fi
+ #1#2#3#4#5}
+
+%D Since the number of chars exceed 256 now, we can use \type {\chardef} instead of
+%D the more limited \type {\mathchardef}.
+
+% \protected\def\newbox {\syst_basics_allocate\c_syst_last_allocated_box \box \chardef\c_syst_max_allocated_register}
+% \protected\def\newmarks{\syst_basics_allocate\c_syst_last_allocated_marks\marks\chardef\c_syst_max_allocated_register}
+
+%D Attributes are something very \LUATEX. In \CONTEXT\ you are not supposed to use
+%D the attributes directly but always allocate then first. For instance attribute~0
+%D is reserved for special purposes (this might change). Attributes in the range
+%D 128-1023 are private and should not be touched.
+
+\immutable\integerdef\attributeunsetvalue\c_syst_min_counter_value % used to be \minusone
+
+% \permanent\protected\def\newattribute{\syst_basics_allocate\c_syst_last_allocated_attribute\attribute\attributedef\c_syst_max_allocated_register}
+
+% %D Not used by \CONTEXT\ but for instance \PICTEX\ needs it. It's a trick to force
+% %D strings instead of tokens that take more memory.
+%
+% \permanent\protected\def\newhelp#1#2{\newtoks#1#1\expandafter{\detokenize{#2}}}
+
+%D \macros
+%D {scratchcounter,
+%D scratchdimen,scratchskip,scratchmuskip,
+%D scratchbox,
+%D scratchtoks}
+%D
+%D We now define a few scratch registers, so that successive loads at least have
+%D some available. The private ones are used in cases where we don't want to
+%D intrude on normal scratch ones.
+
+\newcount \scratchcounter \newcount \globalscratchcounter \newcount \privatescratchcounter
+\newdimen \scratchdimen \newdimen \globalscratchdimen \newdimen \privatescratchdimen
+\newskip \scratchskip \newskip \globalscratchskip \newskip \privatescratchskip
+\newmuskip\scratchmuskip \newmuskip\globalscratchmuskip \newmuskip\privatescratchmuskip
+\newtoks \scratchtoks \newtoks \globalscratchtoks \newtoks \privatescratchtoks
+\newbox \scratchbox \newbox \globalscratchbox \newbox \privatescratchbox
+
+\newcount\scratchcounterone \newcount\scratchcountertwo \newcount\scratchcounterthree
+\newdimen \scratchdimenone \newdimen \scratchdimentwo \newdimen \scratchdimenthree
+\newskip \scratchskipone \newskip \scratchskiptwo \newskip \scratchskipthree
+\newmuskip\scratchmuskipone \newmuskip\scratchmuskiptwo \newmuskip\scratchmuskipthree
+\newtoks \scratchtoksone \newtoks \scratchtokstwo \newtoks \scratchtoksthree
+\newbox \scratchboxone \newbox \scratchboxtwo \newbox \scratchboxthree
+
+\newcount\scratchcounterfour \newcount\scratchcounterfive \newcount\scratchcountersix
+\newdimen \scratchdimenfour \newdimen \scratchdimenfive \newdimen \scratchdimensix
+\newskip \scratchskipfour \newskip \scratchskipfive \newskip \scratchskipsix
+\newmuskip\scratchmuskipfour \newmuskip\scratchmuskipfive \newmuskip\scratchmuskipsix
+\newtoks \scratchtoksfour \newtoks \scratchtoksfive \newtoks \scratchtokssix
+\newbox \scratchboxfour \newbox \scratchboxfive \newbox \scratchboxsix
+
+\newcount\globalscratchcounterone
+\newcount\globalscratchcountertwo
+\newcount\globalscratchcounterthree
+
+\mutable\let\scratchmacro \relax
+\mutable\let\scratchmacroone\relax
+\mutable\let\scratchmacrotwo\relax
+
+%D \macros
+%D {tempstring, scratchstring*}
+
+\mutable\let\tempstring \empty
+
+\mutable\let\scratchstring \empty
+\mutable\let\scratchstringone\empty
+\mutable\let\scratchstringtwo\empty
+
+%D \macros
+%D {scratchwidth, scratchheight, scratchdepth, scratchoffset, scratchdistance}
+%D
+%D A few more scratch dimensions:
+
+\newdimen\scratchwidth
+\newdimen\scratchheight
+\newdimen\scratchdepth
+\newdimen\scratchtotal
+
+\newdimen\scratchoffset
+\newdimen\scratchleftoffset
+\newdimen\scratchrightoffset
+\newdimen\scratchtopoffset
+\newdimen\scratchbottomoffset
+
+\newdimen\scratchdistance
+
+\newdimen\scratchhsize
+\newdimen\scratchvsize
+
+\newdimen\scratchxoffset
+\newdimen\scratchyoffset
+\newdimen\scratchhoffset
+\newdimen\scratchvoffset
+
+\newdimen\scratchxposition
+\newdimen\scratchyposition
+
+\newcount\scratchnx
+\newcount\scratchny
+
+\newcount\scratchmx
+\newcount\scratchmy
+
+\newcount\scratchmin
+\newcount\scratchmax
+
+\newcount\scratchunicode
+
+\newdimen\scratchleftskip
+\newdimen\scratchrightskip
+\newdimen\scratchtopskip
+\newdimen\scratchbottomskip
+
+%D More allocations:
+
+% \newcount \maxcount \immutable\maxcount 2147483647
+
+% \newdimen \zeropoint \immutable\zeropoint 0pt
+% \newdimen \onepoint \immutable\onepoint 1pt
+% \newdimen \halfapoint \immutable\halfapoint 0.5pt
+% \newdimen \maxdimen \immutable\maxdimen 16383.99999pt % 1073741823sp
+% \newdimen \onebasepoint \immutable\onebasepoint 1bp
+% \newdimen \scaledpoint \immutable\scaledpoint 1sp
+% \newdimen \thousandpoint \immutable\thousandpoint 1000pt
+
+\immutable\integerdef \maxcount 2147483647
+
+\immutable\dimensiondef \zeropoint 0pt
+\immutable\dimensiondef \onepoint 1pt
+\immutable\dimensiondef \halfapoint 0.5pt
+\immutable\dimensiondef \maxdimen 16383.99999pt % 1073741823sp
+\immutable\dimensiondef \onebasepoint 1bp
+\immutable\dimensiondef \scaledpoint 1sp
+\immutable\dimensiondef \thousandpoint 1000pt
+
+\immutable\gluespecdef \zeroskip 0pt plus 0pt minus 0pt
+
+\immutable\mugluespecdef \zeromuskip 0mu
+\immutable\mugluespecdef \onemuskip 1mu
+\immutable\mugluespecdef \muquad 18mu
+
+\aliased\let\points \onepoint
+\aliased\let\halfpoint\halfapoint
+
+\newtoks \emptytoks
+
+%D And even more: (todo: countdefs 60+). Watch out, we need a count for a negative constant
+%D but can use chardefs for positive ones. Actually, because we have plenty of counters we
+%D can also use counts for those.
+
+%immutable\integerdef\minusone -1
+\immutable\integerdef\minustwo -2
+%immutable\integerdef\zerocount 0
+%immutable\integerdef\plusone 1
+\immutable\integerdef\plustwo 2
+\immutable\integerdef\plusthree 3
+\immutable\integerdef\plusfour 4
+\immutable\integerdef\plusfive 5
+\immutable\integerdef\plussix 6
+\immutable\integerdef\plusseven 7
+\immutable\integerdef\pluseight 8
+\immutable\integerdef\plusnine 9
+\immutable\integerdef\plusten 10
+\immutable\integerdef\pluseleven 11
+\immutable\integerdef\plustwelve 12
+\immutable\integerdef\plussixteen 16
+\immutable\integerdef\plusfifty 50
+\immutable\integerdef\plushundred 100
+\immutable\integerdef\plusonehundred 100
+\immutable\integerdef\plustwohundred 200
+\immutable\integerdef\plusfivehundred 500
+\immutable\integerdef\pluscxxvii 127
+\immutable\integerdef\pluscxxviii 128
+\immutable\integerdef\pluscclv 255
+\immutable\integerdef\pluscclvi 256
+\immutable\integerdef\plusthousand 1000
+\immutable\integerdef\plustenthousand 10000
+\immutable\integerdef\plustwentythousand 20000
+\immutable\integerdef\medcard 32768
+\immutable\integerdef\maxcard 65536 % pdftex has less mathchars
+\immutable\integerdef\maxcardminusone 65535
+
+%D \macros
+%D {doubleexpandafter,tripleexpandafter,expanded,startexpanded}
+%D
+%D A few handy shortcuts
+
+\permanent\let\singleexpandafter \expandafter
+\permanent\def\doubleexpandafter{\expandafter\expandafter\expandafter}
+\permanent\def\tripleexpandafter{\expandafter\doubleexpandafter\expandafter}
+
+%D We prefer the more readable variant than in plain \TEX. User should only
+%D use \type {\emptybox}:
+
+\newbox\voidbox % public
+
+\permanent\def\unvoidbox{\unhbox\voidbox}
+\permanent\def\emptybox {\box \voidbox} % used in initializations so no attributes
+\permanent\def\emptyvbox{\normalvpack{}} % no copy as we need to set attributes
+\permanent\def\emptyhbox{\normalhpack{}} % no copy as we need to set attributes
+
+\aliased\let\leavevmode\unvoidbox % we prefer to use \dontleavehmode
+
+%D \macros
+%D {dontcomplain}
+%D
+%D We need this one soon:
+
+\permanent\protected\def\dontcomplain
+ {\hbadness\plustenthousand
+ \vbadness\plustenthousand
+ \hfuzz \maxdimen
+ \vfuzz \maxdimen}
+
+%D Some expected plain variants follow. We don't reuse registers because we
+%D don't want clashes.
+
+\aliased\let\p@ \onepoint
+\aliased\let\m@ne \minusone
+\aliased\let\z@ \zeropoint
+\aliased\let\@ne \plusone
+\aliased\let\tw@ \plustwo
+\aliased\let\thr@@ \plusthree
+\aliased\let\sixt@@n\plussixteen
+\aliased\let\@cclv \pluscclv
+\aliased\let\@cclvi \pluscclvi
+\aliased\let\voidb@x\voidbox
+
+\newtoks \toks@ %scratchtoks
+
+%D We define \type {\newif} a la plain \TEX, but will redefine it later. As Knuth
+%D says:
+%D
+%D \startnarrower
+%D And here's a different sort of allocation: for example,
+%D
+%D \starttyping
+%D \newif\iffoo
+%D \stoptyping
+%D
+%D creates \type {\footrue}, \type {\foofalse} to go with \type {\iffoo}.
+%D \stopnarrower
+
+% \protected\def\newif#1%
+% {\count@\escapechar
+% \escapechar\minusone
+% \expandafter\expandafter\expandafter\def\new_if #1{true}{\let#1\iftrue }%
+% \expandafter\expandafter\expandafter\def\new_if#1{false}{\let#1\iffalse}%
+% \new_if#1{false}% the condition starts out false
+% \escapechar\count@}
+%
+% \def\new_if#1#2%
+% {\csname\expandafter\if@\string#1#2\endcsname}
+%
+% \bgroup % `if' is required
+% \uccode`1=`i \uccode`2=`f \uppercase{\gdef\if@12{}}
+% \egroup
+
+% We use \csstring so there is no need to push/pop escapechar.
+% We use different names so that we get a better error message.
+%
+% \protected\def\newif#1%
+% {\let\new_if_saved\newif
+% \let\newif\new_if_check
+% \expandafter\expandafter\expandafter\def\new_if_cs #1{true}{\let#1\iftrue }%
+% \expandafter\expandafter\expandafter\def\new_if_cs#1{false}{\let#1\iffalse}%
+% \new_if_cs#1{false}%
+% \let\newif\new_if_saved}
+%
+% \protected\def\new_if_cs#1#2%
+% {\csname\expandafter\newif\csstring#1#2\endcsname}
+%
+% We wrap all into one macro (the frozen stuff adds 10% runtime):
+
+\permanent\protected\def\newif#1%
+ {\permanent\protected\gdefcsname\expandafter\new_if_check\csstring#1true\endcsname {\overloaded\frozen\let#1\iftrue }%
+ \permanent\protected\gdefcsname\expandafter\new_if_check\csstring#1false\endcsname{\overloaded\frozen\let#1\iffalse}%
+ \csname\expandafter\new_if_check\csstring#1false\endcsname}
+
+\normalexpanded{\gdef\noexpand\new_if_check\string i\string f{}}
+
+%D Let's test this one:
+
+\newif\ifdone
+\newif\iffound
+
+\newif\ifscratchcondition
+\newif\ifscratchconditionone
+\newif\ifscratchconditiontwo
+
+\aliased\let\htdp\boxtotal
+
+%D A few shortcuts:
+
+% \permanent\protected\def\udef {\protected\def }
+% \permanent\protected\def\ugdef{\protected\gdef}
+% \permanent\protected\def\uedef{\protected\edef}
+% \permanent\protected\def\uxdef{\protected\xdef}
+
+% For now:
+
+\permanent\protected\def\defUmathtopaccent #1#2#3#4{\global\immutable\protected\def#1{\Umathaccent "#2 "#3 "#4 }}
+\permanent\protected\def\defUmathbotaccent #1#2#3#4{\global\immutable\protected\def#1{\Umathbotaccent "#2 "#3 "#4 }}
+\permanent\protected\def\defUdelimiterover #1#2#3#4{\global\immutable\protected\def#1{\Udelimiterover "#2 "#3 }}
+\permanent\protected\def\defUdelimiterunder #1#2#3#4{\global\immutable\protected\def#1{\Udelimiterunder "#2 "#3 }}
+\permanent\protected\def\defUdelimiter #1#2#3#4{\global\immutable\protected\def#1{\Udelimiter "#2 "#3 "#4 }}
+\permanent\protected\def\defUradical #1#2#3{\global\immutable\protected\def#1{\Uradical "#2 "#3 }}
+\permanent\protected\def\defUroot #1#2#3{\global\immutable\protected\def#1{\Uroot "#2 "#3 }}
+\permanent\protected\def\defUmathchar #1#2#3#4{\global\immutable\Umathchardef #1 "#2 "#3 "#4 }
+
+%D For a while we keep the following, as systems like tikz need it. Best not use
+%D that one \CONTEXT.
+
+\aliased\let\active\activecatcode
+
+%D Constants to be used with \type {\interactionmode}.
+
+\immutable\integerdef\batchmodecode \zerocount
+\immutable\integerdef\nonstopmodecode \plusone
+\immutable\integerdef\scrollmodecode \plustwo
+\immutable\integerdef\errorstopmodecode \plusthree
+
+%D Of course we want even bigger log files, so we copied this from the \ETEX\
+%D source files.
+%D
+%D When watching such logs, beware of nasty side effects of \type {\scantokens},
+%D as in:
+%D
+%D \starttyping
+%D \bgroup
+%D \lccode`a=12\lowercase{\xdef\whatever{a}}\egroup
+%D \def\whatever{test \whatever test}
+%D \scantokens\expandafter{\whatever}
+%D \egroup
+%D \stoptyping
+%D
+%D In \LUATEX\ we have ways around this.
+
+% no longer \errorstopmode cf. plain tex 3.141592653
+
+\permanent\protected\def\tracingall
+ {\tracingonline \plusone
+ \tracingcommands \plusthree
+ \tracingmacros \plustwo
+ \tracingoutput \plusone
+ \tracingpages \plusone
+ \tracingparagraphs\plusone
+ \tracingrestores \plusone
+ \tracinggroups \plusone
+ \tracingifs \plusone
+ \tracingnesting \plustwo
+ \tracingassigns \plustwo
+ \tracingmath \plusone
+ \tracingalignments\plusone
+ \showboxbreadth \maxcount
+ \showboxdepth \maxcount}
+
+\permanent\protected\def\loggingall
+ {\tracingall
+ \tracingonline \zerocount}
+
+\permanent\protected\def\tracingnone
+ {\showboxdepth \plusthree
+ \showboxbreadth \plusfive
+ \tracingalignments\zerocount
+ \tracingmath \zerocount
+ \tracingassigns \zerocount
+ \tracingnesting \zerocount
+ \tracingifs \zerocount
+ \tracinggroups \zerocount
+ \tracingrestores \zerocount
+ \tracingparagraphs\zerocount
+ \tracingpages \zerocount
+ \tracingoutput \zerocount
+ \tracingmacros \zerocount
+ \tracingcommands \zerocount
+ \tracingonline \zerocount}
+
+%D When we want to see a box we can as well show all of it.
+
+\showboxdepth \maxdimen
+\showboxbreadth\maxdimen
+
+%D Just for tracing purposes we set:
+
+\tracingstats\plusone
+
+%D This is only used when we load \CONTEXT. Nesting is not supported here.
+
+\permanent\def\pushoverloadmode
+ {\enforced\permanent\protected\edef\popoverloadmode{\overloadmode\the\overloadmode\enforced\let\popoverloadmode\relax}%
+ \overloadmode\zerocount}
+
+\aliased\let\popoverloadmode\relax
+
+\let\pushrunstate\relax % will be defined later
+\let\poprunstate \relax % will be defined later
+
+\newtoks\everydump
+
+\pushoverloadmode
+
+\permanent\protected\def\dump
+ {\the\everydump
+ \global\everydump\emptytoks
+ \pushoverloadmode
+ \enforced\glet\dump\relax
+ \popoverloadmode
+ %let\pushoverloadmode\relax
+ %let\popoverloadmode\relax
+ % \tracingall
+ \normaldump}
+
+\permanent\protected\def\input
+ {\normalinput}
+
+\popoverloadmode
+
+%D \macros
+%D {newconditional,
+%D settrue, setfalse,
+%D ifconditional,then}
+%D
+%D \TEX's lacks boolean variables, although the \PLAIN\ format implements \type
+%D {\newif}. The main disadvantage of this scheme is that it takes three hash table
+%D entries. A more memory saving alternative is presented here. A conditional is
+%D defined by:
+%D
+%D \starttyping
+%D \newconditional\doublesided
+%D \setfalse
+%D \stoptyping
+%D Setting a conditional is done by \type{\settrue} and
+%D \type{\setfalse}:
+%D
+%D \starttyping
+%D \settrue\doublesided
+%D \setfalse
+%D \stoptyping
+%D while testing is accomplished by:
+%D
+%D \starttyping
+%D \ifconditional\doublesided ... \else ... \fi
+%D \setfalse
+%D \stoptyping
+%D We cannot use the simple scheme:
+%D
+%D \starttyping
+%D \def\settrue #1{\let#1=\iftrue}
+%D \def\setfalse#1{\let#1=\iffalse}
+%D \stoptyping
+%D
+%D Such an implementation gives problems with nested conditionals. The next
+%D implementation is about as fast and just as straightforward:
+
+\aliased\let\conditionalfalse\plusone % maybe we will have a dedicated count/chardef
+\aliased\let\conditionaltrue \zerocount % maybe we will have a dedicated count/chardef
+
+% \permanent\protected\def\settrue #1{\integerdef#1\conditionaltrue }
+% \permanent\protected\def\setfalse#1{\integerdef#1\conditionalfalse}
+%
+% \permanent\protected\def\settruevalue #1{\expandafter\integerdef\csname#1\endcsname\conditionaltrue }
+% \permanent\protected\def\setfalsevalue#1{\expandafter\integerdef\csname#1\endcsname\conditionalfalse}
+
+\permanent\protected\def\settrue #1{\enforced\let#1\conditionaltrue }
+\permanent\protected\def\setfalse #1{\enforced\let#1\conditionalfalse}
+\permanent\protected\def\setconditional#1{\enforced\let#1}
+
+\permanent\protected\def\settruevalue #1{\enforced\letcsname#1\endcsname\conditionaltrue }
+\permanent\protected\def\setfalsevalue#1{\enforced\letcsname#1\endcsname\conditionalfalse}
+
+
+\aliased\let\newconditional\setfalse
+\aliased\let\ifconditional \ifcase
+
+\aliased\let\then\relax % so that we can say: \ifnum1>2\then -)
+
+\permanent\def\truecondition {\iftrue}
+\permanent\def\falsecondition{\iffalse}
+
+% This one has to be unprotected otherwise we get a files-ends-to-soon but it's ok
+% as conditions expand anyway.
+
+\permanent\def\quitcondition{\orelse\iffalse}
+
+%D \macros
+%D {newmacro,setnewmacro,newfraction}
+%D
+%D Let's be complete and also introduce some definers. These are not mandate
+%D but handy for grepping. We make these mutable because they are \type
+%D {\let} (the only advantage is that we often give them names with some
+%D prefix.
+
+\permanent\protected\def\newmacro #1{\mutable\let#1\empty}
+\permanent\protected\def\setnewmacro#1{\mutable\let#1}
+
+\permanent\protected\def\newfraction#1{\mutable\let#1\!!plusone}
+
+\def\!!zerocount{0} % later redefined
+\def\!!plusone {1} % later redefined
+
+%D It would be handy to have a primitive \type {\unless\ifcase} because then we
+%D could use nicer values. Anyhow, this conditional code used to be in the \type
+%D {syst-aux} module but is now promoted to here.
+
+%D \macros
+%D {ifzeropt}
+%D
+%D The next macro is both cosmetic and byte saving. It is pretty \type
+%D {\if}||safe too. It can be used in cases like:
+%D
+%D \starttyping
+%D \ifzeropt \somedimen ... \else ... \fi
+%D \stoptyping
+
+\aliased\let\ifzeropt\ifcase
+
+% these token list helpers might move to syst-aux.mkiv
+%
+% we assume a \cs. not toks0 or so
+%
+% \protected\def\appendtotoks #1#{\def\temp{#1}\afterassignment\doappendtotoks \scratchtoks=}
+% \protected\def\prependtotoks#1#{\def\temp{#1}\afterassignment\doprependtotoks\scratchtoks=}
+
+\newtoks\t_syst_toks_temp \let\m_syst_toks_temp\t_syst_toks_temp
+
+\permanent\protected\def\appendtotoks #1{\let\m_syst_toks_temp#1\afterassignment\syst_toks_append_l \t_syst_toks_temp=}
+\permanent\protected\def\prependtotoks#1{\let\m_syst_toks_temp#1\afterassignment\syst_toks_prepend_l\t_syst_toks_temp=}
+
+% \def\syst_toks_append {\m_syst_toks_temp\expandafter\expandafter\expandafter{\expandafter\the\expandafter\m_syst_toks_temp\the\t_syst_toks_temp}}}
+% \def\syst_toks_prepend{\m_syst_toks_temp\expandafter\expandafter\expandafter{\expandafter\the\expandafter\t_syst_toks_temp\the\m_syst_toks_temp}}}
+
+\permanent\protected\def\globalappendtotoks #1{\let\m_syst_toks_temp#1\afterassignment\syst_toks_append_g \t_syst_toks_temp=}
+\permanent\protected\def\globalprependtotoks#1{\let\m_syst_toks_temp#1\afterassignment\syst_toks_prepend_g\t_syst_toks_temp=}
+
+\def\syst_toks_append_l {\normalexpanded{\m_syst_toks_temp{\the\m_syst_toks_temp\the\t_syst_toks_temp}}}
+\def\syst_toks_prepend_l{\normalexpanded{\m_syst_toks_temp{\the\t_syst_toks_temp\the\m_syst_toks_temp}}}
+
+\def\syst_toks_append_g {\global\syst_toks_append_l }
+\def\syst_toks_prepend_g{\global\syst_toks_prepend_l}
+
+\permanent\protected\def\addtotoks #1{\let\m_syst_toks_temp#1\afterassignment\syst_toks_add_l\let\next}
+\permanent\protected\def\globaladdtotoks#1{\let\m_syst_toks_temp#1\afterassignment\syst_toks_add_g\let\next}
+
+\def\syst_toks_add_l{\m_syst_toks_temp\expandafter\bgroup\the\m_syst_toks_temp}
+\def\syst_toks_add_g{\global\syst_toks_add_l}
+
+\permanent\protected\def\addtotokscs #1#2{#1{\the#1#2}} % saves a few bytes
+\permanent\protected\def\globaladdtotokscs#1#2{\global#1{\the#1#2}} % saves a few bytes
+
+%D \macros
+%D {begcsname}
+%D
+%D Handy for \ETEX-only usage (avoids making \type {\relax}'s:
+
+% \def\begcsname#1\endcsname{\ifcsname#1\endcsname\csname#1\endcsname\fi}
+
+\aliased\let\begcsname\begincsname
+
+%D Now come a few macros that might be needed in successive loading. We redefine the
+%D \type {\par} primitive pretty soon so that we get the equivalents right.
+
+% too tricky: \par is use more often than a par starts so we have too much change
+% that we get assymetrical behaviour
+%
+% \newtoks\everyendpar
+%
+% \protected\def\endpar{\the\everyendpar\normalpar}
+% \protected\def\par {\endpar}
+%
+% \protected\def\reseteverypar
+% {\everypar \emptytoks
+% \everyendpar\emptytoks}
+
+\permanent\protected\def\reseteverypar
+ {\everypar\emptytoks}
+
+\aliased\let\endgraf\par % plain, a kind of funny name
+%aliased\let\endline\cr % plain, not needed and probably also confusing
+
+\permanent\protected\def\null{\hpack{}}
+
+%D The following two might be overloaded later on but some modules need then
+%D earlier. These functionality is reflected in the name and will not change.
+
+% \bgroup
+% \catcode`\^^M=\activecatcode%
+% \gdef\obeylines{\catcode`\^^M\activecatcode \let^^M\par}%
+% \glet^^M\par%
+% \egroup
+%
+% \bgroup
+% \gdef\obeyspaces{\catcode`\ \activecatcode}%
+% \obeyspaces\glet =\space%
+% \egroup
+
+\def\obeylines {\catcode\endoflineasciicode\activecatcode\letcharcode\endoflineasciicode\par } % check if these can be \permanent
+\def\obeyspaces{\catcode\spaceasciicode \activecatcode\letcharcode\spaceasciicode \space} % check if these can be \permanent
+
+% %D A constant:
+%
+% \let\endoflinetoken=^^M
+
+%D Also needed might be a simple loop structure and we borrow plain \TEX's one
+%D as it is often expected to be present and it is about the fastest you can
+%D get. Beware: this macro does not support nested loops. We use a namespace
+%D prefix \type {@@pln}.
+
+\let\syst_plain_body\relax
+
+\permanent\def\loop#1\repeat{\def\syst_plain_body{#1}\syst_plain_iterate} % might go
+
+%D The following makes \type {\loop} \unknown\ \type {\if} \unknown\ \type
+%D {\repeat} skippable (clever trick):
+
+\permanent\let\repeat\fi % so both \loop and \repeat are reserved words!
+
+%D The original (no \type {syst_plain_} there):
+%D
+%D \starttyping
+%D \def\syst_plain_iterate
+%D {\syst_plain_body
+%D \let\next\syst_plain_iterate
+%D \else
+%D \let\next\relax
+%D \fi
+%D \next}
+%D \stoptyping
+%D
+%D A more efficient alternative:
+%D
+%D \starttyping
+%D \def\syst_plain_iterate
+%D {\syst_plain_body
+%D \expandafter\syst_plain_iterate
+%D \else\expandafter\relax\fi}
+%D \stoptyping
+%D
+%D An even more efficient one:
+
+\def\syst_plain_iterate
+ {\syst_plain_body
+ \expandafter\syst_plain_iterate\else\fi}
+
+%D We don't define a real output routine yet but at least get rid of pages:
+
+\output{\shipout\box\normalpagebox}
+
+%D Although we don't add pagenumbers yet we alias the default register used
+%D for counting pages:
+
+\permanent\countdef\pageno\zerocount \pageno\plusone % first page is number 1
+
+%D Beside the raw counter \type {\pageno} the \type {\folio} macro provides
+%D the value.
+
+\permanent\def\folio{\the\pageno} % kind of expected and therefore reserved
+
+%D The following registers are kind of standard and (for the moment) we define
+%D them here. This might change.
+
+\newskip \bigskipamount \bigskipamount = 12pt plus 4pt minus 4pt
+\newskip \medskipamount \medskipamount = 6pt plus 2pt minus 2pt
+\newskip \smallskipamount \smallskipamount = 3pt plus 1pt minus 1pt
+
+\baselineskip = 12pt
+\lineskip = 1pt
+\lineskiplimit = 0pt
+
+%D Sometimes kerns make more sense than glue but we need to be in the
+%D right mode:
+
+\permanent\protected\def\vkern {\ifhmode\par \fi\kern}
+\permanent\protected\def\hkern {\ifvmode\dontleavehmode\fi\kern}
+
+\permanent\protected\def\vpenalty{\ifhmode\par \fi\penalty}
+\permanent\protected\def\hpenalty{\ifvmode\dontleavehmode\fi\penalty}
+
+%D Again a few kind-of-extensions the core. These come from plain \TEX\ but
+%D are probably not used in \CONTEXT.
+
+\newskip \hideskip \hideskip = -1000pt plus 1fill
+\newskip \centering \centering = 0pt plus 1000pt minus 1000pt
+
+\permanent\def\hidewidth % for alignment entries that can stick out
+ {\hskip\hideskip}
+
+\permanent\def\ialign % initialized \halign
+ {\everycr\emptytoks
+ \tabskip\zeroskip
+ \halign}
+
+\newcount \mscount
+
+\permanent\def\spanomit{\span\omit} % bypass error message
+
+\permanent\def\multispan#1%
+ {\omit
+ \mscount#1\relax
+ \loop
+ \ifnum\mscount>\plusone
+ \spanomit \advance\mscount\minusone
+ \repeat}
+
+\let\nopdfcompression \relax % later
+\let\onlypdfobjectcompression\relax % later
+\let\maximumpdfcompression \relax % later
+\let\normalpdfcompression \relax % later
+
+%D Basic status stuff.
+
+% \newif\ifproductionrun % already defined
+
+%D For those who expect this \unknown
+
+\ifdefined\fmtname \else \immutable\def\fmtname {ConTeXt Initial TeX} \fi
+\ifdefined\fmtversion \else \immutable\def\fmtversion{3.1415926} \fi
+
+%D A few bonus macros:
+
+%permanent\def\modulonumber#1#2{\the\numexpr#2-((((#2+(#1/2))/#1)-1)*#1)\relax}
+\permanent\def\modulonumber#1#2{\the\numexpr#2-(#2:#1)*#1\relax}
+\permanent\def\dividenumber#1#2{\the\numexpr(#2-(#1/2))/#1\relax}
+
+%D These could be set at the \LUA\ end instead:
+
+\immutable\edef\texenginename {\directlua{tex.print(LUATEXENGINE)}}
+\immutable\edef\texengineversion {\directlua{tex.print(LUATEXVERSION)}}
+\immutable\edef\texenginefunctionality{\directlua{tex.print(LUATEXFUNCTIONALITY)}}
+
+%D We have no reason not to enable this:
+
+\savingvdiscards\plusone
+
+%D We only can set this one via directives (system.synctex) and we only support
+%D the context variant. This will go away completely.
+
+\newcount\synctex % \let\normalsynctex\synctex
+
+%D We get rid of the funny \TEX\ offset defaults of one inch by setting them to zero.
+
+% \voffset\zeropoint \overloaded\let\voffset\relax \newdimen\voffset \let\normalvoffset\voffset
+% \hoffset\zeropoint \overloaded\let\hoffset\relax \newdimen\hoffset \let\normalhoffset\hoffset
+
+\matheqnogapstep\zerocount % for now
+
+%D Now we define a few helpers that we need in a very early stage. We have no
+%D message system yet but redundant definitions are fatal anyway.
+
+\newcount\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes
+
+\def\v_interfaces_prefix_template_system{\number \c_syst_helpers_n_of_namespaces>>}
+%def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet
+
+\protected\def\installsystemnamespace#1% will be redefined
+ {\ifcsname ??#1\endcsname
+ \immediate\write\statuswrite{fatal error: duplicate system namespace '#1'}%
+ \else
+ \global\advance\c_syst_helpers_n_of_namespaces\plusone
+ \immutable\edefcsname ??#1\endcsname{\v_interfaces_prefix_template_system}%
+ \fi}
+
+%D It makes more sense to have these here:
+
+\aliased\let\normalsuperscript \Usuperscript
+\aliased\let\normalsubscript \Usubscript
+\aliased\let\normalnosuperscript\Unosuperscript
+\aliased\let\normalnosubscript \Unosubscript
+\aliased\let\normalstartimath \Ustartmath
+\aliased\let\normalstopimath \Ustopmath
+\aliased\let\normalstartdmath \Ustartdisplaymath
+\aliased\let\normalstopdmath \Ustopdisplaymath
+
+%D Also better here:
+
+\immutable\def\wildcardsymbol{*}
+
+%D For a while we will keep these useless numbers as for instance tikz checks for them:
+
+\immutable\integerdef\eTeXversion 2
+\immutable\def \eTeXrevision {2}
+
+%D Experiment:
+
+\glyphoptions"80 % replaces \glyphdimensionsmode=1
+
+%D Just in case users use this:
+
+\aliased\let\immediateassign \immediate
+\aliased\let\immediateassigned\localcontrolled
+
+%D Needed:
+
+\mutable\let\nexttoken \relax
+\mutable\let\next \relax
+\mutable\let\nextnext \relax
+\mutable\let\nextnextnext\relax
+
+%D For now here: will get a proper solution
+
+% \pushoverloadmode
+% \mutable\let\par\par
+% \popoverloadmode
+
+%D Also here:
+
+\listcallbackmode\plusone
+
+%D Often used as cs key:
+
+\mutable\let\on \empty
+\mutable\let\of \empty
+\mutable\let\and \empty
+\mutable\let\with\empty
+\mutable\let\nop \empty % used often
+\mutable\let\yes \empty % used often
+
+%D Another experiment:
+
+\def\syst_suggested_alias#1#2% expandable
+ {\beginlocalcontrol
+ \writestatus{suggestion}{use \string#2 instead of \string#1}%
+ \enforced\glet#1#2%
+ \endlocalcontrol
+ #2}
+
+\permanent\protected\def\suggestedalias#1#2%
+ {\ifflags#2\permanent\permanent\fi % the rest is taken with the copy
+ \def#1{\syst_suggested_alias#1#2}}
+
+% A few traditional allocations (these might go):
+
+% \countdef \count@ 255 % hm, used in \newif .. todo: replace it there
+% \dimendef \dimen@ 0
+% \dimendef \dimen@i 1 % global only
+% \dimendef \dimen@ii 2
+
+\suggestedalias \count@ \scratchcounter
+\suggestedalias \dimen@ \scratchdimen
+\suggestedalias \dimen@i \scratchdimenone
+\suggestedalias \dimen@ii \scratchdimentwo
+
+%D The next feature is experimental and could go into another system module, but it
+%D feels more natural to have it here.
+%D
+%D \starttyping
+%D \begingroup
+%D \newlocaldimen\mydimena % \mydimena1\onepoint
+%D \newlocaldimen\mydimenb % \mydimenb2\onepoint
+%D \begingroup
+%D \newlocaldimen\mydimena % \mydimena3\onepoint
+%D \newlocaldimen\mydimenb % \mydimenb4\onepoint
+%D \newlocaldimen\mydimenc % \mydimenc5\onepoint
+%D \endgroup
+%D \endgroup
+%D
+%D \begingroup
+%D \setlocaldimen\mydimena 1\onepoint
+%D \setlocaldimen\mydimenb 2\onepoint
+%D \begingrou
+%D \setlocaldimen\mydimena 3\onepoint
+%D \setlocaldimen\mydimenb 4\onepoint
+%D \setlocaldimen\mydimenc 5\onepoint
+%D \endgroup
+%D \endgroup
+%D \stoptyping
+%D
+%D There is a little extra overhead in the overload protection but not that much.
+%D Instead of:
+
+% \newcount\c_syst_local_count \c_syst_local_count \c_syst_max_allocated_register
+% \newcount\c_syst_local_dimen \c_syst_local_dimen \c_syst_local_count
+% \newcount\c_syst_local_skip \c_syst_local_skip \c_syst_local_count
+% \newcount\c_syst_local_muskip\c_syst_local_muskip\c_syst_local_count
+% \newcount\c_syst_local_box \c_syst_local_box \c_syst_local_count
+% \newcount\c_syst_local_toks \c_syst_local_toks \c_syst_local_count
+
+%D We do this:
+
+\permanent\countdef\c_syst_local_count = 72 \c_syst_local_count = \c_syst_max_allocated_register
+\permanent\countdef\c_syst_local_dimen = 73 \c_syst_local_dimen = \c_syst_local_count
+\permanent\countdef\c_syst_local_skip = 74 \c_syst_local_skip = \c_syst_local_count
+\permanent\countdef\c_syst_local_muskip = 75 \c_syst_local_muskip = \c_syst_local_count
+\permanent\countdef\c_syst_local_box = 76 \c_syst_local_box = \c_syst_local_count
+\permanent\countdef\c_syst_local_toks = 77 \c_syst_local_toks = \c_syst_local_count
+\permanent\countdef\c_syst_local_read = 78 \c_syst_local_read = \c_syst_local_count
+\permanent\countdef\c_syst_local_write = 79 \c_syst_local_write = \c_syst_local_count
+
+% We should have used \c_syst_local_dimen = \numexpr \c_syst_max_allocated_register +
+% 1\relax, but we do this instead (it's no problem loosing a few registers).
+
+\c_syst_max_allocated_register \numexpr\c_syst_max_allocated_register:2\relax % floor
+
+\permanent\protected\def\syst_local_overflow#1#2{\writestatus\m!system{no room for local #1 \string#2}\wait}
+
+\permanent\protected\def\setnewlocaldimen#1%
+ {\ifnum\c_syst_local_dimen>\c_syst_max_allocated_register
+ \advance\c_syst_local_dimen\minusone\dimendef#1\c_syst_local_dimen
+ \else
+ \syst_local_overflow_dimen#1%
+ \fi
+ #1}
+
+\permanent\protected\def\setnewlocalcount#1%
+ {\ifnum\c_syst_local_count>\c_syst_max_allocated_register
+ \advance\c_syst_local_count\minusone
+ \overloaded\frozen\countdef#1\c_syst_local_count
+ \else
+ \syst_local_overflow_count#1%
+ \fi
+ #1}
+
+\permanent\protected\def\setnewlocalskip#1%
+ {\ifnum\c_syst_local_skip>\c_syst_max_allocated_register
+ \advance\c_syst_local_skip\minusone
+ \overloaded\frozen\skipdef#1\c_syst_local_skip
+ \else
+ \syst_local_overflow_skip#1%
+ \fi
+ #1}
+
+\permanent\protected\def\setnewlocalmuskip#1%
+ {\ifnum\c_syst_local_muskip>\c_syst_max_allocated_register
+ \advance\c_syst_local_muskip\minusone
+ \overloaded\frozen\muskipdef#1\c_syst_local_muskip
+ \else
+ \syst_local_overflow_muskip#1%
+ \fi
+ #1}
+
+\permanent\protected\def\setnewlocaltoks#1%
+ {\ifnum\c_syst_local_toks>\c_syst_max_allocated_register
+ \advance\c_syst_local_toks\minusone
+ \overloaded\frozen\toksdef#1\c_syst_local_toks
+ \else
+ \syst_local_overflow_toks#1%
+ \fi
+ #1}
+
+\permanent\protected\def\setnewlocalbox#1%
+ {\ifnum\c_syst_local_box>\c_syst_max_allocated_register
+ \advance\c_syst_local_box\minusone
+ \overloaded\frozen\integerdef#1\c_syst_local_box
+ \else
+ \syst_local_overflow_box#1%
+ \fi
+ \setbox#1}
+
+\permanent\protected\def\newlocalread#1% so no set
+ {\ifnum\c_syst_local_read>\c_syst_max_allocated_register
+ \advance\c_syst_local_read\minusone
+ \overloaded\frozen\integerdef#1\c_syst_local_read
+ \else
+ \syst_local_overflow_read#1%
+ \fi}
+
+\permanent\protected\def\newlocalwrite#1% so no set
+ {\ifnum\c_syst_local_write>\c_syst_max_allocated_register
+ \advance\c_syst_local_write\minusone
+ \overloaded\frozen\integerdef#1\c_syst_local_write
+ \else
+ \syst_local_overflow_write#1%
+ \fi}
+
+%D The error handlers:
+
+\permanent\protected\def\syst_local_overflow_count {\syst_local_overflow {count}}
+\permanent\protected\def\syst_local_overflow_dimen {\syst_local_overflow {dimen}}
+\permanent\protected\def\syst_local_overflow_skip {\syst_local_overflow {skip}}
+\permanent\protected\def\syst_local_overflow_muskip{\syst_local_overflow{muskip}}
+\permanent\protected\def\syst_local_overflow_toks {\syst_local_overflow {toks}}
+\permanent\protected\def\syst_local_overflow_box {\syst_local_overflow {box}}
+\permanent\protected\def\syst_local_overflow_read {\syst_local_overflow {read}}
+\permanent\protected\def\syst_local_overflow_write {\syst_local_overflow {write}}
+
+%D We sacrifice a little performance but save some tokens by not defineing these
+%D completely:
+
+\permanent\protected\def\newlocalcount #1{\setnewlocalcount #1\zerocount }
+\permanent\protected\def\newlocaldimen #1{\setnewlocaldimen #1\zeropoint }
+\permanent\protected\def\newlocalskip #1{\setnewlocalskip #1\zeroskip }
+\permanent\protected\def\newlocalmuskip#1{\setnewlocalmuskip#1\zeromuskip}
+\permanent\protected\def\newlocaltoks #1{\setnewlocaltoks #1\emptytoks }
+\permanent\protected\def\newlocalbox #1{\setnewlocalbox #1\emptybox }
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-lua.lmt b/Master/texmf-dist/tex/context/base/mkxl/syst-lua.lmt
new file mode 100644
index 00000000000..a89cea852bf
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-lua.lmt
@@ -0,0 +1,521 @@
+if not modules then modules = { } end modules ['syst-lua'] = {
+ version = 1.001,
+ comment = "companion to syst-lua.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local load, type, tonumber = load, type, tonumber
+local find, gsub = string.find, string.gsub
+local concat = table.concat
+local utfchar = utf.char
+local S, C, P, lpegmatch, lpegtsplitat = lpeg.S, lpeg.C, lpeg.P, lpeg.match, lpeg.tsplitat
+
+local xmath = xmath or math
+local xcomplex = xcomplex or { }
+
+local scan_next = token.scan_next or token.get_next
+local scan_cmdchr = token.scan_cmdchr_expanded
+local scan_token = token.scan_token
+
+local get_csname = token.get_csname
+----- get_macro = token.get_macro
+----- put_next = token.put_next
+
+local cmd = tokens.commands
+local letter_code = cmd.letter
+local other_char_code = cmd.other_char
+local spacer_code = cmd.spacer
+local other_char_code = cmd.other_char
+local relax_code = cmd.relax
+local register_int_code = cmd.register_int
+local internal_int_code = cmd.internal_int
+local register_dimen_code = cmd.register_dimen
+local internal_dimen_code = cmd.internal_dimen
+local register_glue_code = cmd.register_glue
+local internal_glue_code = cmd.internal_glue
+local register_toks_code = cmd.register_toks
+local internal_toks_code = cmd.internal_toks
+local char_given_code = cmd.char_given
+local math_given_code = cmd.math_given
+local xmath_given_code = cmd.xmath_given
+local some_item_code = cmd.some_item
+
+local getdimen = tex.getdimen
+local getglue = tex.getglue
+local getcount = tex.getcount
+local gettoks = tex.gettoks
+local gettex = tex.get
+
+local context = context
+commands = commands or { }
+local commands = commands
+local context = context
+local implement = interfaces.implement
+local dimenfactors = number.dimenfactors
+
+local ctx_protected_cs = context.protected.cs -- more efficient
+local ctx_firstoftwoarguments = context.firstoftwoarguments
+local ctx_secondoftwoarguments = context.secondoftwoarguments
+local ctx_firstofoneargument = context.firstofoneargument
+local ctx_gobbleoneargument = context.gobbleoneargument
+
+implement { -- will be overloaded later
+ name = "writestatus",
+ arguments = "2 arguments",
+ actions = logs.status,
+}
+
+function commands.doifelse(b)
+ if b then
+ ctx_firstoftwoarguments()
+ else
+ ctx_secondoftwoarguments()
+ end
+end
+
+function commands.doifelsesomething(b)
+ if b and b ~= "" then
+ ctx_firstoftwoarguments()
+ else
+ ctx_secondoftwoarguments()
+ end
+end
+
+function commands.doif(b)
+ if b then
+ ctx_firstofoneargument()
+ else
+ ctx_gobbleoneargument()
+ end
+end
+
+function commands.doifsomething(b)
+ if b and b ~= "" then
+ ctx_firstofoneargument()
+ else
+ ctx_gobbleoneargument()
+ end
+end
+
+function commands.doifnot(b)
+ if b then
+ ctx_gobbleoneargument()
+ else
+ ctx_firstofoneargument()
+ end
+end
+
+function commands.doifnotthing(b)
+ if b and b ~= "" then
+ ctx_gobbleoneargument()
+ else
+ ctx_firstofoneargument()
+ end
+end
+
+commands.testcase = commands.doifelse -- obsolete
+
+function commands.boolcase(b)
+ context(b and 1 or 0)
+end
+
+function commands.doifelsespaces(str)
+ if find(str,"^ +$") then
+ ctx_firstoftwoarguments()
+ else
+ ctx_secondoftwoarguments()
+ end
+end
+
+local pattern = lpeg.patterns.validdimen
+
+function commands.doifelsedimenstring(str)
+ if lpegmatch(pattern,str) then
+ ctx_firstoftwoarguments()
+ else
+ ctx_secondoftwoarguments()
+ end
+end
+
+local p_first = C((1-P(",")-P(-1))^0)
+
+implement {
+ name = "firstinset",
+ arguments = "string",
+ actions = function(str) context(lpegmatch(p_first,str or "")) end,
+ public = true,
+}
+
+implement {
+ name = "ntimes",
+ arguments = { "string", "integer" },
+ actions = { string.rep, context }
+}
+
+implement {
+ name = "execute",
+ arguments = "string",
+ actions = os.execute -- wrapped in sandbox
+}
+
+implement {
+ name = "doifelsesame",
+ arguments = "2 strings",
+ actions = function(a,b)
+ if a == b then
+ ctx_firstoftwoarguments()
+ else
+ ctx_secondoftwoarguments()
+ end
+ end
+}
+
+implement {
+ name = "doifsame",
+ arguments = "2 strings",
+ actions = function(a,b)
+ if a == b then
+ ctx_firstofoneargument()
+ else
+ ctx_gobbleoneargument()
+ end
+ end
+}
+
+implement {
+ name = "doifnotsame",
+ arguments = "2 strings",
+ actions = function(a,b)
+ if a == b then
+ ctx_gobbleoneargument()
+ else
+ ctx_firstofoneargument()
+ end
+ end
+}
+
+-- This is a bit of a joke as I never really needed floating point expressions (okay,
+-- maybe only with scaling because there one can get numbers that are too large for
+-- dimensions to deal with). Of course one can write a parser in \TEX\ speak but then
+-- one also needs to implement a bunch of functions. It doesn't pay of so we just
+-- stick to the next gimmick. It looks inefficient but performance is actually quite
+-- efficient.
+
+do
+
+ local result = { "return " }
+ local word = { }
+ local r = 1
+ local w = 0
+
+ local report = logs.reporter("system","expression")
+
+ local function unexpected(c)
+ report("unexpected token %a",c)
+ end
+
+ local function unexpected(c)
+ report("unexpected token %a",c)
+ end
+
+ local function expression()
+ local w = 0
+ local r = 1
+ while true do
+ local n, i = scan_cmdchr()
+ if n == letter_code then
+ w = w + 1 ; word[w] = utfchar(i)
+ else
+ if w > 0 then
+ -- we could use a metatable for all math, complex and factors
+ local s = concat(word,"",1,w)
+ local d = dimenfactors[s]
+ if d then
+ r = r + 1 ; result[r] = "*"
+ r = r + 1 ; result[r] = 1/d
+ else
+ if xmath[s] then
+ r = r + 1 ; result[r] = "xmath."
+ elseif xcomplex[s] then
+ r = r + 1 ; result[r] = "xcomplex."
+ end
+ r = r + 1 ; result[r] = s
+ end
+ w = 0
+ end
+ if n == other_char_code then
+ r = r + 1 ; result[r] = utfchar(i)
+ elseif n == spacer_code then
+ -- r = r + 1 ; result[r] = " "
+ elseif n == relax_code then
+ break
+ elseif n == register_int_code or n == internal_int_code then
+ r = r + 1 ; result[r] = getcount(i)
+ elseif n == register_dimen_code or n == internal_dimen_code then
+ r = r + 1 ; result[r] = getdimen(i)
+ elseif n == register_glue_code or n == n == register_dimen_code_glue_code then
+ r = r + 1 ; result[r] = getglue(i)
+ elseif n == register_toks_code or n == n == register_dimen_code_toks_code then
+ r = r + 1 ; result[r] = gettoks(i)
+ elseif n == char_given_code or n == math_given_code or n == xmath_given_code then
+ r = r + 1 ; result[r] = i
+ elseif n == some_item_code then
+ local n = get_csname(t)
+ if n then
+ local s = gettex(n)
+ if s then
+ r = r + 1 ; result[r] = s
+ else
+ unexpected(c)
+ end
+ else
+ unexpected(c)
+ end
+ -- elseif n == call_code then
+ -- local n = get_csname(t)
+ -- if n then
+ -- local s = get_macro(n)
+ -- if s then
+ -- r = r + 1 ; result[r] = s
+ -- else
+ -- unexpected(c)
+ -- end
+ -- else
+ -- unexpected(c)
+ -- end
+ -- elseif n == the_code or n == convert_code or n == lua_expandable_call_code then
+ -- put_next(t)
+ -- scan_token() -- expands
+ else
+ unexpected(c)
+ end
+ end
+ end
+ local code = concat(result,"",1,r)
+ local func = load(code)
+ if type(func) == "function" then
+ context(func())
+ else
+ report("invalid lua %a",code)
+ end
+ end
+
+ implement {
+ public = true,
+ name = "expression",
+ actions = expression,
+ }
+
+end
+
+do
+
+ -- This is some 20% slower than native but we only provide this for compatibility
+ -- reasons so we don't care that much about it. Eventually we can drop the built-in
+ -- method.
+
+ local channels = { }
+ local lastdone = { }
+
+ local findbinfile = resolvers.findbinfile
+ local loadbinfile = resolvers.loadbinfile
+ local opentexfile = resolvers.opentexfile
+
+ local scaninteger = tokens.scanners.integer
+ local scankeyword = tokens.scanners.keyword
+ local scanstring = tokens.scanners.string
+ local scancsname = tokens.scanners.csname
+
+ local setmacro = tokens.setters.macro
+ local rlncatcodes = tex.rlncatcodes
+ local texgetcount = tex.getcount
+
+ local bytes = string.bytes
+ local getcatcode = tex.getcatcode
+
+ local char, concat = string.char, table.concat
+
+ -- This uses the normal bin lookup method that we also use for other files,
+ -- and in principle there is no limit on the amount of files other than the
+ -- operating system imposes.
+
+ local t = { }
+ local l = 0
+
+ implement {
+ name = "openin",
+ public = true,
+ usage = "value",
+ actions = function()
+ local n = scaninteger()
+ scankeyword("=")
+ local s = scanstring(true)
+ local c = channels[n]
+ if c then
+ c:close()
+ end
+ local f = findbinfile(s,"tex")
+ if f then
+ channels[n] = opentexfile(f)
+ lastdone[n] = false
+ else
+ channels[n] = false
+ lastdone[n] = true
+ end
+ end,
+ }
+
+ implement {
+ name = "closein",
+ public = true,
+ usage = "value",
+ actions = function()
+ local n = scaninteger()
+ local c = channels[n]
+ if c then
+ c:close()
+ end
+ channels[n] = false
+ t = { }
+ end,
+ }
+
+ -- This is not the fastest routine but hardly used, occasionally for line by line
+ -- input in e.g. tikz which is not that fast anyway so I'll deal with it when it
+ -- really is a bottleneck.
+
+ local readerror = function(what,n)
+ tex.error(string.formatters["too many %s brace tokens in read from channel %i"](what,n))
+ end
+
+ interfaces.implement {
+ name = "read",
+ public = true,
+ usage = "value",
+ actions = function(prefix)
+ local n = scaninteger()
+ local c = channels[n]
+ if scankeyword("line") and c then
+ c:gotoline(scaninteger())
+ end
+ scankeyword("to")
+ local m = scancsname(true)
+ local g = 0
+ local s
+ l = 0
+ if c then
+ while true do
+ local s = c and c:reader()
+ if s then
+ l = l + 1
+ s = gsub(s," *$","")
+ t[l] = s
+ for c in bytes(s) do
+ local cc = getcatcode(c)
+ if cc == 1 then
+ g = g + 1
+ elseif cc == 2 then
+ g = g - 1
+ end
+ end
+ if g <= 0 then
+ break
+ end
+ else
+ break
+ end
+ end
+ if g > 0 then
+ readerror("left",n)
+ s = ""
+ elseif g < 0 then
+ readerror("right",n)
+ s = ""
+ elseif l == 0 then
+ if c:endoffile() then
+ lastdone[n] = true
+ s = "\\par"
+ else
+ s = ""
+ end
+ channels[n] = false
+ else
+ local e = texgetcount("endlinechar") -- we can have tex.endline if needed
+ if e < 0 or e > 127 then
+ e = ""
+ else
+ e = " "
+ l = l + 1
+ t[l] = ""
+ end
+ s = concat(t, e, 1, l)
+ end
+ else
+ s = ""
+ end
+ setmacro(m, s, prefix) -- checks for frozen
+ end,
+ }
+
+ -- This is an etex extension. All characters become catcode 12 but a space gets code
+ -- 10. The etex manual specifies a lineending of catcode 12 too. Should we strip spaces
+ -- at the end? A bit weird command that we never use anyway.
+
+ implement {
+ name = "readline",
+ public = true,
+ usage = "value",
+ actions = function(prefix)
+ local n = scaninteger()
+ local c = channels[n]
+ if scankeyword("line") and c then
+ c:gotoline(scaninteger())
+ end
+ scankeyword("to")
+ local m = scancsname(true)
+ local s = c and c:reader()
+ if s then
+ local e = texgetcount("endlinechar") -- we can have tex.endline if needed
+ if e > 0 then -- forget about 0
+ s = s .. char(e)
+ end
+ else
+ channels[n] = false
+ s = ""
+ end
+ setmacro(rlncatcodes, m, s, prefix) -- checks for frozen
+ end,
+ }
+
+ -- This one uses the special lua condition option which is kind of experimental
+ -- but seems to work fine.
+
+ local boolean_value = tokens.values.boolean
+
+ implement {
+ name = "ifeof",
+ public = true,
+ usage = "condition",
+ actions = function()
+ local n = scaninteger()
+ return boolean_value, not channels[n]
+ end,
+ }
+
+ -- This one doesn't belong here and it might become a real primitive if we need it
+ -- frequently. So, for the moment we keep it in this file.
+
+ local getnest = tex.getnest
+
+ implement {
+ name = "ifmvl",
+ public = true,
+ usage = "condition",
+ actions = function()
+ return boolean_value, getnest("ptr") == 0
+ end,
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-lua.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-lua.mkxl
new file mode 100644
index 00000000000..158682399a8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-lua.mkxl
@@ -0,0 +1,104 @@
+%D \module
+%D [ file=syst-lua,
+%D version=2008.01.25,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Helper macros based on \LUA,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\registerctxluafile{syst-lua}{autosuffix}
+
+\unprotect
+
+\permanent\def\expdoifelse#1#2{\clf_doifelsesame{#1}{#2}}
+\permanent\def\expdoif #1#2{\clf_doifsame {#1}{#2}}
+\permanent\def\expdoifnot #1#2{\clf_doifnotsame {#1}{#2}}
+
+%D Here is variant using a brainwave of the 12\high{+} hour \quotation {Long Road
+%D Out of Eden}\footnote {Eden being Backo\TeX\ 2018, where the virtues of the \type
+%D {\expanded} primitive were mentioned in talks.} trip. For now I don't really see
+%D other useful applications.
+
+\permanent\def\expandeddoifelse#1#2{\iftok{#1}{#2}\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}
+\permanent\def\expandeddoif #1#2{\iftok{#1}{#2}\expandafter\firstofoneargument \else\expandafter\gobbleoneargument \fi}
+\permanent\def\expandeddoifnot #1#2{\iftok{#1}{#2}\expandafter\gobbleoneargument \else\expandafter\firstofoneargument \fi}
+
+% \testfeatureonce{100000}{\doifelse{hello world}{here i am}{}} % 0.3
+% \testfeatureonce{100000}{\expandabledoifelse{hello world}{here i am}{}} % 1.5
+
+\permanent\def\expdoifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}}
+\permanent\def\expdoifelseinset #1#2{\clf_doifelseinset {#1}{#2}}
+
+\aliased\let\expdoifcommonelse\expdoifelsecommon
+\aliased\let\expdoifinsetelse \expdoifelseinset
+
+%D We define these here, just in case. This permits \typefile {self} otherwise
+%D nested b|/|e sep problems.
+
+\immutable\def\luastringsep{===}
+
+\immutable\edef\!!bs{[\luastringsep[}
+\immutable\edef\!!es{]\luastringsep]}
+
+\pushoverloadmode
+
+\permanent\protected\def\writestatus#1#2{\clf_writestatus{#1}{#2}}
+
+\popoverloadmode
+
+%D We can omit the tex.ctxcatcodes here as nowadays we seldom change the regime at
+%D the \TEX\ end:
+
+%def\luaexpr#1{\ctxlua{context (tostring(#1))}} % more efficient:
+\permanent\def\luaexpr#1{\ctxlua{tex.print(tostring(#1))}} % no use is shortcutting has to be compiled
+
+%D But as we only use write 16 we could as well do all in \LUA\ and ignore the rest.
+%D Okay, we still can do writes here but only when not blocked.
+
+\aliased\let\syst_write_execute\clf_execute % always {...}
+
+%D There is a probably rather useless \type {\expression} command that can mix \LUA\ numeric
+%D expressions with \TEX\ commands (for as far that makes sense) that works like this:
+%D
+%D \starttyping
+%D \mathchardef\foooo 1234
+%D \def \mymacro {123.45}
+%D \scratchdimen 123pt
+%D \scratchskip 123pt
+%D \scratchtoks {34}
+%D
+%D \testfeatureonce{10000}{\edef\foo{\expression
+%D round(
+%D 12.3 * \scratchdimen
+%D - (12.3 * \scratchskip) / 2
+%D + 2 ^ 3
+%D * (
+%D - sqrt(12)
+%D - 10pt
+%D - \foooo
+%D + \luatexversion
+%D - \mymacro
+%D + \scratchtoks
+%D + \the\scratchcounter
+%D + \number\scratchcounter
+%D )
+%D )
+%D \relax}}
+%D
+%D \expression string.format("\letterpercent f",\elapsedtime/50000) \relax
+%D \stoptyping
+%D
+%D The final \type {\relax} is mandate because we also accept spaces. Of course we
+%D could have avoided it by using outer parenthesis but this is cleaner anyway.
+%D
+%D The \LMTX\ version supports te \type {xmath} and \type {xcomplex} functions and
+%D therefore one can have expressions that don't work in \MKIV.
+
+% maybe: \let\texexpr\expression
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-mac.lmt b/Master/texmf-dist/tex/context/base/mkxl/syst-mac.lmt
new file mode 100644
index 00000000000..410a49f8b11
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-mac.lmt
@@ -0,0 +1,117 @@
+if not modules then modules = { } end modules ['syst-mac'] = {
+ version = 1.001,
+ comment = "companion to syst-aux.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is kind of tricky and might not work for all csnames but as long as we use
+-- it in a controlled way, we're okay. The engine implementation might be changed
+-- a bit (no need to go through strings, but fetching a cs index and passing that
+-- back also takes time).
+
+-- Another approach is to have the predefined stack operate use private stacks and
+-- then the pop doesn't need the cs. But ... we then also need to store stuff in
+-- the format so that complicates maters more than I'm willing to do.
+
+local insert, remove = table.insert, table.remove
+
+local push_macro = token.push_macro
+local pop_macro = token.pop_macro
+local scan_csname = token.scan_csname
+local create_token = token.create
+local gobble_token = token.gobble
+
+local context = context
+local implement = interfaces.implement
+
+local report = logs.reporter("system","macrostack")
+
+local stack = table.setmetatableindex("table")
+
+local function pushmacro(name,global)
+ local s = push_macro(name,global)
+ if s then
+ insert(stack[name],s)
+ else
+ report("no macro %a to push",name)
+ insert(stack[name],false)
+ end
+end
+
+local function popmacro(name)
+ local s = remove(stack[name])
+ if s then
+ pop_macro(s)
+ else
+ report("no macro %a to pop",name)
+ end
+end
+
+tokens.pushmacro = pushmacro
+tokens.popmacro = popmacro
+
+implement {
+ name = "localpushmacro",
+ public = true,
+ protected = true,
+ actions = function()
+ pushmacro(scan_csname())
+ end
+}
+
+implement {
+ name = "globalpushmacro",
+ public = true,
+ protected = true,
+ actions = function()
+ pushmacro(scan_csname(),true)
+ end
+}
+
+implement {
+ name = "localpopmacro",
+ public = true,
+ protected = true,
+ actions = function()
+ popmacro(scan_csname())
+ end
+}
+
+implement {
+ name = "globalpopmacro",
+ public = true,
+ protected = true,
+ actions = function()
+ popmacro(scan_csname())
+ end
+}
+
+implement {
+ name = "showmacrostack",
+ public = true,
+ protected = true,
+ actions = function()
+ local n = scan_csname()
+ local s = stack[n]
+ local m = #s
+ report("%s : %i stack slots used",n,m)
+ for i=1,m do
+ report("% 3i %S",i,s[i])
+ end
+ end
+}
+
+implement {
+ name = "gobblenested",
+ public = true,
+ protected = true,
+ arguments = "3 strings",
+ actions = function(start,stop,command)
+ gobble_token(create_token(start),create_token(stop))
+ if command then
+ context[command]()
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-pln.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-pln.mkxl
new file mode 100644
index 00000000000..38ec9c4c1c5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-pln.mkxl
@@ -0,0 +1,119 @@
+%D \module
+%D [ file=syst-pln,
+%D version=2001.11.16, % 1999.03.17, % an oldie: 1995.10.10
+%D title=\CONTEXT\ System Macros,
+%D subtitle=Efficient \PLAIN\ \TEX\ loading,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D This module set a couple of variables to the plain \TEX\ values. Later they might
+%D be overloaded. There is not much difference between \MKII\ and \MKIV\ but at some
+%D point they might diverge.
+
+%D Build||in numeric variables.
+
+\adjdemerits = 10000
+\binoppenalty = 700
+\brokenpenalty = 100
+\clubpenalty = 150
+%day = 0
+\defaulthyphenchar = `\-
+\defaultskewchar = -1
+\delimiterfactor = 901
+\displaywidowpenalty = 50
+\doublehyphendemerits = 10000
+%endlinechar = `\^^M
+\errorcontextlines = 5
+%escapechar = `\\
+\exhyphenpenalty = 50
+%fam = 0
+\finalhyphendemerits = 5000
+%floatingpenalty = 0
+%globaldefs = 0
+\hangafter = 1 % automatically set to 1 (no zero assignment)
+\hbadness = 1000
+%holdinginserts = 0
+\hyphenpenalty = 50
+%interlinepenalty = 0
+%language = 0
+\lefthyphenmin = 2
+\linepenalty = 10
+%looseness = 0
+%mag = 1000
+%maxdeadcycles = 25
+%month = 0
+%newlinechar = -1 % commented i.e. no plain value
+%outputpenalty = 0
+%pausing = 0
+%postdisplaypenalty = 0
+\predisplaypenalty = 10000
+\pretolerance = 100
+\relpenalty = 500
+\righthyphenmin = 3
+\showboxbreadth = 5
+\showboxdepth = 3
+%time = 0
+\tolerance = 200
+%tracingcommands = 0
+\tracinglostchars = 1
+%tracingmacros = 0
+%tracingonline = 0
+%tracingoutput = 0
+%tracingpages = 0
+%tracingparagraphs = 0
+%tracingrestores = 0
+%tracingstats = 0
+\uchyph = 1
+\vbadness = 1000
+\widowpenalty = 150
+%year = 0
+
+%D Build in dimension variables.
+
+\abovedisplayshortskip = 0pt plus 3pt
+\abovedisplayskip = 12pt plus 3pt minus 9pt
+%baselineskip = 0pt
+\belowdisplayshortskip = 7pt plus 3pt minus 4pt
+\belowdisplayskip = 12pt plus 3pt minus 9pt
+\boxmaxdepth = \maxdimen
+\delimitershortfall = 5pt
+%displayindent = 0pt
+%displaywidth = 0pt
+%hangindent = 0pt
+\hfuzz = 0.1pt
+%hoffset = 0pt
+\hsize = 6.5in
+%leftskip = 0pt
+%lineskip = 0pt
+%lineskiplimit = 0pt
+%mathsurround = 0pt
+\maxdepth = 4pt
+\medmuskip = 4mu plus 2mu minus 4mu
+\nulldelimiterspace = 1.2pt
+\overfullrule = 5pt
+\parfillskip = 0pt plus 1fil
+\parindent = 20pt
+\parskip = 0pt plus 1pt
+%predisplaysize = 0pt
+%rightskip = 0pt
+\scriptspace = 0.5pt
+%spaceskip = 0pt
+\splitmaxdepth = \maxdimen
+\splittopskip = 10pt
+%tabskip = 0pt
+\thickmuskip = 5mu plus 5mu
+\thinmuskip = 3mu
+\topskip = 10pt
+\vfuzz = 0.1pt
+%voffset = 0pt
+\vsize = 8.9in
+%xspaceskip = 0pt
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/syst-rtp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/syst-rtp.mkxl
new file mode 100644
index 00000000000..73aa59ba942
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/syst-rtp.mkxl
@@ -0,0 +1,18 @@
+%D \module
+%D [ file=syst-rtp,
+%D version=2006.10.13,
+%D title=\CONTEXT\ Core Macros,
+%D subtitle=Run Time Processes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\aliased\let\executesystemcommand\clf_execute % can be public implementor
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-com.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-com.mkxl
new file mode 100644
index 00000000000..bdae8711bb9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-com.mkxl
@@ -0,0 +1,96 @@
+%D \module
+%D [ file=tabl-com,
+%D version=2009.08.22,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Common Code,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / Common Code}
+
+\unprotect
+
+\pushoverloadmode
+
+% maybe frozen instead
+
+\permanent\let\AR\relax
+\permanent\let\BA\relax
+\permanent\let\BC\relax
+\permanent\let\BH\relax
+\permanent\let\BL\relax
+\permanent\let\BR\relax
+\permanent\let\CC\relax
+\permanent\let\CL\relax
+\permanent\let\CM\relax
+\permanent\let\CR\relax
+\permanent\let\DB\relax
+\permanent\let\DC\relax
+\permanent\let\DL\relax
+\permanent\let\DR\relax
+\permanent\let\DV\relax
+\permanent\let\EH\relax
+\permanent\let\EQ\relax
+\permanent\let\ER\relax
+\permanent\let\FB\relax
+\permanent\let\FL\relax
+\permanent\let\FR\relax
+\permanent\let\HC\relax
+\permanent\let\HL\relax
+\permanent\let\HQ\relax
+\permanent\let\HR\relax
+\permanent\let\LB\relax
+\permanent\let\LC\relax
+\permanent\let\LT\relax
+\permanent\let\LL\relax
+\permanent\let\LR\relax
+\permanent\let\MC\relax
+\permanent\let\ML\relax
+\permanent\let\MR\relax
+\permanent\let\NA\relax
+\permanent\let\NB\relax
+\permanent\let\NC\relax
+\permanent\let\ND\relax
+\permanent\let\NF\relax
+\permanent\let\NG\relax
+\permanent\let\NI\relax
+\permanent\let\NL\relax
+\permanent\let\NN\relax
+\permanent\let\NP\relax
+\permanent\let\NR\relax
+\permanent\let\PB\relax
+\permanent\let\RC\relax
+\permanent\let\RQ\relax
+\permanent\let\SL\relax
+\permanent\let\SM\relax
+\permanent\let\SR\relax
+\permanent\let\TB\relax
+\permanent\let\TL\relax
+\permanent\let\TR\relax
+\permanent\let\VL\relax
+\permanent\let\VC\relax
+\permanent\let\VD\relax
+\permanent\let\VN\relax
+\permanent\let\VS\relax
+\permanent\let\VT\relax
+\permanent\let\XC\relax
+
+\permanent\let\FC\relax
+\permanent\let\RB\relax
+\permanent\let\RT\relax
+
+\popoverloadmode
+
+\installmacrostack\BC
+\installmacrostack\EC
+\installmacrostack\EQ
+\installmacrostack\NC
+\installmacrostack\NN
+\installmacrostack\NR
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-frm.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-frm.mkxl
new file mode 100644
index 00000000000..ec530e7a4a5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-frm.mkxl
@@ -0,0 +1,263 @@
+%D \module
+%D [ file=tabl-frm,
+%D version=2017.04.11,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Framed Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D For Thomas Schmitz who needed 3000 pages long tables.
+
+\unprotect
+
+\writestatus{loading}{ConTeXt Table Macros / Framed Tables}
+
+\installcorenamespace{framedtable}
+\installcorenamespace{framedtablerow}
+\installcorenamespace{framedtablecolumn}
+
+\newcount\c_tabl_framed_c
+\newcount\c_tabl_framed_r
+\newdimen\d_tabl_framed_h
+\newdimen\d_tabl_framed_d
+\newdimen\b_tabl_framed
+
+\initializeboxstack\??framedtable
+
+\defineframed[\??framedtable]
+\defineframed[\??framedtablerow][\??framedtable]
+\defineframed[\??framedtablecolumn][\??framedtable]
+
+\setupframed
+ [\??framedtable]
+ [\c!distance=\zeropoint,
+ \c!before=,
+ \c!after=,
+ \c!inbetween=]
+
+\setupframed
+ [\??framedtablerow]
+ [\c!strut=\v!no,
+ \c!offset=\v!overlay]
+
+% \defineframedtable[foo]
+% \defineframedtable[bar][foo]
+
+\permanent\tolerant\protected\def\defineframedtable[#1]#*[#2]%
+ {\ifarguments\or
+ \defineframed[\??framedtable#1][\??framedtable]%
+ \or
+ \defineframed[\??framedtable#1][\??framedtable#2]%
+ \fi}
+
+% \setupframedtable[foo][...]
+
+\permanent\tolerant\protected\def\setupframedtable[#1]#*[#2]%
+ {\ifarguments\or
+ \setupframed[\??framedtable][#1]%
+ \or
+ \ifcsname\??framed:\??framedtable#1\endcsname \else
+ \defineframed[\??framedtable#1][\??framedtable]%
+ \fi
+ \setupframed[\??framedtable#1][#2]%
+ \fi}
+
+% \setupframedtable[1][...]
+% \setupframedtable[foo][1][...]
+
+\permanent\tolerant\protected\def\setupframedtablerow[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \ifcsname\??framed:\??framedtablerow:#1\endcsname \else
+ \defineframed[\??framedtablerow:#1][\??framedtable]%
+ \fi
+ \setupframed[\??framedtablerow:#1][#2]%
+ \or
+ \ifcsname\??framed:\??framedtable#1\endcsname \else
+ \defineframed[\??framedtable#1][\??framedtable]%
+ \fi
+ \ifcsname\??framed:\??framedtablerow#1:#2\endcsname \else
+ \defineframed[\??framedtablerow#1:#2][\??framedtable#1]%
+ \fi
+ \setupframed[\??framedtablerow#1:#2][#3]%
+ \fi}
+
+\permanent\tolerant\protected\def\setupframedtablecolumn[#1]#*[#2]#*[#3]%
+ {\ifarguments\or\or
+ \ifcsname\??framed:\??framedtablecolumn:#1\endcsname \else
+ \defineframed[\??framedtablecolumn:#1][\??framedtable]%
+ \fi
+ \setupframed[\??framedtablecolumn:#1][#2]%
+ \or
+ \ifcsname\??framed:\??framedtable#1\endcsname \else
+ \defineframed[\??framedtable#1][\??framedtable]%
+ \fi
+ \ifcsname\??framed:\??framedtablecolumn#1:#2\endcsname \else
+ \defineframed[\??framedtablecolumn#1:#2][\??framedtable#1]%
+ \fi
+ \setupframed[\??framedtablecolumn#1:#2][#3]%
+ \fi}
+
+\mutable\let\currentframedtable\empty
+
+\permanent\tolerant\protected\def\startframedtable[#1]#*[#2]%
+ {\begingroup
+ \forgetall
+ \ifhastok={#1}%
+ \let\currentframedtable\empty
+ \setupframed[\??framedtable][#1]%
+ \else
+ \edef\currentframedtable{#1}%
+ \setupframed[\??framedtable][#2]%
+ \fi
+ \edef\currentframed{\??framedtable\currentframedtable}%
+ \c_tabl_framed_r\zerocount
+ \d_tabl_framed_d\framedparameter\c!distance
+ \framedparameter\c!before}
+
+\permanent\protected\def\stopframedtable
+ {\framedparameter\c!after
+ \endgroup}
+
+% a two pass variant that deals with the height .. so no catcode changes here
+
+\tolerant\protected\def\pack_framed_start_framed_whatever[#1]%
+ {\pack_framed_initialize
+ \bgroup
+ \setupcurrentframed[#1]% here !
+ \pack_framed_process_indeed
+ \bgroup
+ \ignorespaces}
+
+% \permanent\protected\def\startframedrow
+% {\advance\c_tabl_framed_r\plusone
+% \c_tabl_framed_c\zerocount
+% \d_tabl_framed_h\zeropoint
+% \bgroup
+% \edef\currentframed{\number\c_tabl_framed_r}%
+% \edef\currentframed
+% {\??framedtablerow\currentframedtable
+% \ifcsname\??framedtablerow\currentframedtable:\currentframed\endcsname
+% :\currentframed
+% \orelse\ifcsname\??framedtablerow\currentframedtable:\v!each\endcsname
+% :\v!each
+% \fi}%
+% \pack_framed_start_framed_whatever}
+%
+% \permanent\protected\def\stopframedrow
+% {\dofastloopcs\c_tabl_framed_c\tabl_framed_flush_row
+% \stopframed
+% \nointerlineskip
+% \vskip\zeropoint\relax
+% \framedparameter\c!inbetween}
+
+\protected\def\tabl_framed_flush_row
+ {\vpack to \d_tabl_framed_h{\flushbox\??framedtable{\number\fastloopindex}\vfill}%
+ \ifzeropt\d_tabl_framed_d\else\kern\d_tabl_framed_d\fi}
+
+\newcount\c_tabl_framed_pass
+
+\permanent\let\stopframedrow\relax
+
+\permanent\protected\def\startframedrow#1\stopframedrow
+ {\advance\c_tabl_framed_r\plusone
+ \startframedrow_one#1\stopframedrow_one
+ \startframedrow_two#1\stopframedrow_two}
+
+\tolerant\def\startframedrow_one[#-]%
+ {\bgroup
+ \c_tabl_framed_pass\plusone
+ \c_tabl_framed_c\zerocount
+ \d_tabl_framed_h\zeropoint
+ \settrialtypesetting}
+
+\protected\def\stopframedrow_one
+ {\normalexpanded{\egroup\d_tabl_framed_h\the\d_tabl_framed_h\relax}}
+
+\def\startframedrow_two
+ {\bgroup
+ \c_tabl_framed_c\zerocount
+ \c_tabl_framed_pass\plustwo
+ \edef\currentframed{\number\c_tabl_framed_r}%
+ \edef\currentframed
+ {\??framedtablerow\currentframedtable
+ \ifcsname\??framedtablerow\currentframedtable:\currentframed\endcsname
+ :\currentframed
+ \orelse\ifcsname\??framedtablerow\currentframedtable:\v!each\endcsname
+ :\v!each
+ \fi}%
+ \pack_framed_start_framed_whatever}
+
+\protected\def\stopframedrow_two
+ {\dofastloopcs\c_tabl_framed_c\tabl_framed_flush_row
+ \stopframed
+ \nointerlineskip
+ \vskip\zeropoint\relax
+ \framedparameter\c!inbetween}
+
+\protected\def\tabl_framed_flush_row_two
+ {\vpack to \d_tabl_framed_h{\flushbox\??framedtable{\number\fastloopindex}\vfill}%
+ \ifzeropt\d_tabl_framed_d\else\kern\d_tabl_framed_d\fi}
+
+\permanent\protected\def\startframedcell
+ {\advance\c_tabl_framed_c\plusone
+ \setbox\b_tabl_framed\hpack\bgroup
+ %\bgroup
+ \edef\currentframed{\number\c_tabl_framed_c}%
+ \edef\currentframed
+ {\??framedtablecolumn\currentframedtable
+ \ifcsname\??framedtablecolumn\currentframedtable:\currentframed\endcsname
+ :\currentframed
+ \orelse\ifcsname\??framedtablecolumn\currentframedtable:\v!each\endcsname
+ :\v!each
+ \fi}%
+ \ifcase\c_tabl_framed_pass
+ \or
+ \letframedparameter\c!background\empty
+ \letframedparameter\c!frame\v!off
+ \or
+ \letframedparameter\c!height\d_tabl_framed_h
+ \fi
+ \pack_framed_start_framed_whatever}
+
+\permanent\protected\def\stopframedcell
+ {\stopframed
+ %\egroup
+ \ifcase\c_tabl_framed_pass
+ \or
+ \ifdim\ht\b_tabl_framed>\d_tabl_framed_h
+ \d_tabl_framed_h\ht\b_tabl_framed
+ \fi
+ \else
+ \savebox\??framedtable{\number\c_tabl_framed_c}{\box\b_tabl_framed}%
+ \fi}
+
+\protect \endinput
+
+\starttext
+
+\setupframedtablecolumn [1] [width=3cm,background=color,backgroundcolor=red]
+\setupframedtablecolumn [2] [width=4cm,background=color,backgroundcolor=green,align=normal]
+% \setupframedtablerow [each] [background=color,backgroundcolor=blue,strut=no]
+% \setupframedtablerow [each] [strut=no,offset=overlay]
+
+\startframedtable[inbetween=\kern-0.4pt,distance=-0.4pt]
+
+\testfeatureonce{10000}{
+% \testfeatureonce{10}{
+ \startframedrow
+ \startframedcell%[backgroundcolor=yellow]
+ test
+ \stopframedcell
+ \startframedcell
+ test \par test
+ \stopframedcell
+ \stopframedrow
+}
+\stopframedtable
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-ltb.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-ltb.mkxl
new file mode 100644
index 00000000000..ec4a373603b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-ltb.mkxl
@@ -0,0 +1,803 @@
+%D \module
+%D [ file=tabl-ltb,
+%D version=2002.10.31, % updated 2016.01.08
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Line Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module was made for some special project where we needed to typeset tables
+%D spanning spanning many pages horizontally and vertically, with repeated header
+%D lines and (entry) columns, tab tracking, color, etc. We do two passes over a
+%D table, which is why the table can go into a buffer or file. As said, tables can
+%D be real huge but performance is still quite okay (there is room for some speed
+%D up). The code has been adapted to \MKIV\ but the functionality is the same as in
+%D \MKII.
+
+% \BH \BC .. \EC \BC .. \EC \EH % append
+% \BR \BC .. \EC \BC .. \EC \ER
+%
+% or
+%
+% \NC .. \NC .. \NC \NR (todo: optional last \NC)
+
+% alternative:
+%
+% (1) direct run, save content in macro, but only if needed
+%
+% todo
+%
+% (2) buffered table content
+%
+% \startbuffer
+% \startlinetablehead
+% \stoplinetablehead
+% \startlinetablebody
+% \stoplinetablebody
+% \stopbuffer
+%
+% \processlinetablebuffer[buffer]
+%
+% in buffer : head and body
+%
+% (3) unbuffered run, multipass
+%
+% - run with starting width zero / prev run
+% - clip on prev run
+% - flush real widths
+
+\writestatus{loading}{ConTeXt Table Macros / Line Tables}
+
+\unprotect
+
+\installcorenamespace{linetable}
+\installcorenamespace{linetablepart}
+\installcorenamespace{linetablewidth}
+\installcorenamespace{linetableheight}
+\installcorenamespace{linetabledepth}
+
+% (For now) we share these three:
+
+\aliased\let\tabl_lines_initialize_box \tabl_tabulate_initialize_box
+\aliased\let\tabl_lines_initialize_boxes\tabl_tabulate_initialize_boxes
+\aliased\let\b_tabl_lines_current \b_tabl_tabulate_current
+
+\newconditional \c_tabl_lines_preroll
+\newconditional \c_tabl_lines_in_table
+
+\newdimen \d_tabl_lines_width
+\newdimen \d_tabl_lines_height
+%newdimen \d_tabl_lines_depth
+
+\newbox \b_tabl_lines_cell
+
+\newcount \c_tabl_lines_n_of_columns
+\newcount \c_tabl_lines_n_of_rows
+\newcount \c_tabl_lines_n_of_lines
+\newcount \c_tabl_lines_n_of_parts
+\newcount \c_tabl_lines_part \c_tabl_lines_part\plusone
+\newcount \c_tabl_lines_step \c_tabl_lines_step\plusone
+\newcount \c_tabl_lines_line
+\newcount \c_tabl_lines_row
+\newcount \c_tabl_lines_rows
+\newcount \c_tabl_lines_column
+\newcount \c_tabl_lines_subcol
+
+\newconstant \c_tabl_lines_hmode
+\newconstant \c_tabl_lines_page
+\newconstant \c_tabl_lines_repeat
+\newconstant \c_tabl_lines_split_state
+\newconstant \c_tabl_lines_head_state
+\newconstant \c_tabl_lines_mode
+
+\newtoks \t_tabl_lines_head
+
+\newconditional \linetableautoheight \settrue\linetableautoheight
+
+\tabl_lines_initialize_box\zerocount % holds repeater
+
+\pushoverloadmode
+
+\permanent\tolerant\protected\def\setuplinetable[#1]#*[#2]#*[#3]%
+ {\ifarguments\or
+ \getparameters[\??linetable][#1]%
+ \or
+ \getparameters[\??linetable#1:][#2]%
+ \or
+ \getparameters[\??linetable#1:#2][#3]%
+ \fi}
+
+\permanent\def\linetableparameter#1%
+ {\begincsname\??linetable#1\endcsname}
+
+\permanent\protected\def\doifelselinetablecparameter#1%
+ {\ifcsname\??linetable c:\number\c_tabl_lines_column#1\endcsname
+ \expandafter\firstoftwoarguments
+ \orelse\ifcsname\??linetable c:#1\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\aliased\let\doiflinetablecparameterelse\doifelselinetablecparameter
+
+\permanent\def\linetablecparameter#1%
+ {\begincsname
+ \??linetable
+ \ifcsname\??linetable c:\number\c_tabl_lines_column#1\endcsname
+ c:\number\c_tabl_lines_column
+ \orelse\ifcsname\??linetable c:#1\endcsname
+ c:%
+ \fi
+ #1%
+ \endcsname}
+
+\permanent\def\linetablerparameter#1% faster, leaner and meaner
+ {\begincsname
+ \??linetable
+ \ifnum\c_tabl_lines_row=\zerocount % geen ifcase
+ \ifcsname\??linetable r:\v!header#1\endcsname
+ r:\v!header
+ \orelse\ifcsname\??linetable r:0#1\endcsname
+ r:0%
+ \fi
+ \else
+ \ifcsname\??linetable r:\number\c_tabl_lines_row#1\endcsname
+ r:\number\c_tabl_lines_row
+ \orelse\ifcsname\??linetable r:\v!oddeven\c_tabl_lines_row#1\endcsname
+ r:\v!oddeven\c_tabl_lines_row
+ \fi
+ \fi
+ #1%
+ \endcsname}
+
+\protected\def\tabl_lines_set
+ {\edef\p_lines{\linetableparameter\c!lines}%
+ \ifx\p_lines\v!fit
+ \tabl_lines_set_indeed
+ \else
+ \global\c_tabl_lines_n_of_lines\p_lines
+ \fi}
+
+\protected\def\tabl_lines_set_indeed
+ {% whitespace already added by vertical strut
+ %\triggerpagebuilder
+ \scratchdimen
+ \ifdim\pagegoal<\maxdimen
+ \dimexpr\pagegoal-\pagetotal\relax
+ \else
+ \textheight
+ \fi
+ \getrawnoflines\scratchdimen
+ \global\c_tabl_lines_n_of_lines\noflines
+ \ifconditional\c_tabl_lines_preroll\orelse\ifnum\c_tabl_lines_n_of_lines<\plustwo
+ \page
+ \tabl_lines_set
+ \fi}
+
+\def\tabl_lines_step_cell
+ {\advance\scratchdimen\linetablecparameter\c!width
+ \global\advance \c_tabl_lines_column\plusone
+ \advance\scratchskip \linetablecparameter\c!distance}
+
+\permanent\tolerant\protected\def\startlinetablecell[#1]%
+ {\global\setbox\b_tabl_lines_cell\hbox\bgroup
+ \ifparameter#1\or
+ \getparameters[\??linetable c:\number\c_tabl_lines_column][#1]%
+ \fi
+ \global\c_tabl_lines_step\linetablecparameter\c!nx\relax
+ \ifcase\c_tabl_lines_step\or
+ \scratchdimen\linetablecparameter\c!width
+ \scratchskip \linetablecparameter\c!distance
+ \else
+ \scratchdimen \zeropoint
+ \scratchskip \zeropoint
+ \scratchcounter\c_tabl_lines_column
+ \dorecurse\c_tabl_lines_step\tabl_lines_step_cell
+ \global\c_tabl_lines_column\scratchcounter
+ \fi
+ \c_tabl_lines_mode
+ \ifconditional\c_tabl_lines_preroll
+ \ifdim\scratchdimen>\zeropoint \zerocount \else \plustwo \fi
+ \else
+ \zerocount
+ \fi
+ \ifcase\c_tabl_lines_mode
+ \ifcase\c_tabl_lines_hmode
+ % nothing
+ \or
+ % fit, keep it simple
+ \or
+ \c_tabl_lines_mode\plusone % line
+ \else
+ % some already calculated height
+ \fi
+ \fi
+ \setbox\scratchbox\hbox
+ \bgroup
+ \dontcomplain
+ \hskip\linetablecparameter\c!leftoffset\relax
+ % 0 = width, unknown height
+ % 1 = width, fixed height
+ % 2 = no width, auto hsize
+ \ifnum\c_tabl_lines_mode<\plustwo
+ \advance\scratchdimen-\linetablecparameter\c!leftoffset
+ \advance\scratchdimen-\linetablecparameter\c!rightoffset
+ \fi
+ \ifcase\c_tabl_lines_mode
+ \dosetraggedcommand{\linetablecparameter\c!align}%
+ \vtop \ifdim\d_tabl_lines_height>\zeropoint to\d_tabl_lines_height \fi \bgroup
+ \hsize\scratchdimen
+ \raggedcommand
+ \else
+ \setalignmentswitch{\linetablecparameter\c!align}%
+ \hbox \ifcase\c_tabl_lines_mode \or to\scratchdimen \fi \bgroup
+ \ifcase\alignmentswitch\hss\or\hss\fi
+ \fi
+ \dousestyleparameter{\linetablecparameter\c!style}%
+ \dousecolorparameter{\linetablecparameter\c!color}%
+ \begstrut \ignorespaces}
+
+\permanent\protected\def\stoplinetablecell
+ {\unskip \endstrut
+ \ifcase\c_tabl_lines_mode
+ \endgraf
+ \else
+ \ifcase\alignmentswitch\else\hss\fi
+ \fi
+ \egroup
+ \hskip\linetablecparameter\c!rightoffset
+ \egroup
+ \ifconditional\c_tabl_lines_preroll
+ \box\scratchbox
+ \else
+ \tabl_lines_wrap_up
+ \fi
+ \egroup}
+
+\def\tabl_lines_wrap_up
+ {\edef\p_background{\linetablecparameter\c!background}%
+ \ifx\p_background\v!color
+ \ifconditional\linetableautoheight
+ \tabl_lines_wrap_up_auto
+ \else
+ \tabl_lines_wrap_up_line
+ \fi
+ \else
+ \box\scratchbox
+ \fi}
+
+% \startuseMPgraphic{one}
+% path p, q ;
+% numeric r ;
+% r := RuleThickness ;
+% p := unitsquare xysized(RuleWidth,RuleHeight+RuleDepth) ;
+% q := p topenlarged -r bottomenlarged -r ;
+% draw q ;
+% setbounds currentpicture to p;
+% \stopuseMPgraphic
+%
+% \setuplinetable[r][odd] [type=mp,mp=one,backgroundcolor=gray,rulethickness=1pt]
+% \setuplinetable[r][even][type=mp,mp=one,backgroundcolor=green,rulethickness=1pt]
+%
+% \startlinetable
+% \dorecurse{10}{\NC aaa \NC bb \NC c \NC ddddd \NC eeee \NC ff \NC \NR}
+% \stoplinetable
+
+\def\tabl_lines_wrap_up_auto
+ {\edef\p_height{\linetablerparameter{x\c!height}}%
+ \edef\p_depth {\linetablerparameter{x\c!depth }}%
+ \hpack
+ {\blackrule
+ [ \c!color=\linetablecparameter\c!backgroundcolor,
+ \c!type=\linetablecparameter\c!type,
+ \c!mp=\linetablecparameter\c!mp,
+ \c!rulethickness=\linetablecparameter\c!rulethickness,
+ \c!height=\ifempty\p_height\ht\scratchbox\else\p_height\fi,
+ \c!depth=\ifempty\p_depth \dp\scratchbox\else\p_depth \fi,
+ \c!width=\wd\scratchbox]%
+ \hskip-\wd\scratchbox\box\scratchbox}}
+
+\def\tabl_lines_wrap_up_auto_r
+ {\hpack
+ {\blackrule
+ [ \c!color=\linetablerparameter\c!backgroundcolor,
+ \c!type=\linetablerparameter\c!type,
+ \c!mp=\linetablerparameter\c!mp,
+ \c!rulethickness=\linetablerparameter\c!rulethickness,
+ \c!height=\ht\scratchbox,
+ \c!depth=\dp\scratchbox,
+ \c!width=\wd\scratchbox]%
+ \hskip-\wd\scratchbox\box\scratchbox}}
+
+\def\tabl_lines_wrap_up_line
+ {\backgroundline[\linetablecparameter\c!backgroundcolor]{\box\scratchbox}}
+
+\def\tabl_lines_save_part
+ {\global\setbox\b_tabl_lines_current\c_tabl_lines_part
+ \ifcase\c_tabl_lines_part\relax
+ \box\scratchbox % just storing
+ \else
+ \vbox
+ {\ifvoid\b_tabl_lines_current\c_tabl_lines_part\else\unvbox\b_tabl_lines_current\c_tabl_lines_part\fi
+ \edef\p_background{\linetablerparameter\c!background}%
+ \ifx\p_background\v!color
+ \backgroundline[\linetablerparameter\c!backgroundcolor]{\box\scratchbox}%
+ \else
+ \edef\p_type{\linetablerparameter\c!type}%
+ \ifempty\p_type
+ \box\scratchbox
+ \else
+ \tabl_lines_wrap_up_auto_r
+ \fi
+ \fi
+ \endgraf
+ \linetablerparameter\c!after}%
+ \fi}
+
+\def\tabl_lines_flush_parts
+ {\global\advance\c_tabl_lines_line\plusone
+ \ifnum\c_tabl_lines_line<\c_tabl_lines_n_of_lines
+ % keep collecting
+ \else
+ \ifconditional\c_tabl_lines_preroll
+ % forget about them
+ \else
+ \dorecurse\c_tabl_lines_n_of_parts
+ {\c_tabl_lines_part\recurselevel
+ \dp\b_tabl_lines_current\c_tabl_lines_part\strutdepth
+ % noindent en endgraf needed else whitespace mess-up!
+ \whitespace % here not after verticalstrut
+ \ifdim\topskipgap=\zeropoint\else
+ \verticalstrut
+ \nobreak
+ \kern-\struttotal
+ \kern-\parskip
+ \nobreak
+ \nointerlineskip % fix topskip
+ \fi
+ \noindent\strut
+ \hpack to \hsize{\box\b_tabl_lines_current\c_tabl_lines_part\hss}%
+ \endgraf
+ \ifnum\c_tabl_lines_part<\c_tabl_lines_n_of_parts\relax
+ \linetableparameter\c!inbetween
+ \fi}%
+ \ifnum\c_tabl_lines_rows<\c_tabl_lines_n_of_rows
+ \linetableparameter\c!inbetween
+ \else
+ % after, later
+ \fi
+ \c_tabl_lines_head_state\plusthree
+ \global\setbox\b_tabl_lines_current\zerocount\emptybox % here
+ \fi
+ % reset \c_tabl_lines_row will be an option, currently
+ % starts at zero after split
+ \global\c_tabl_lines_row\zerocount
+ \global\c_tabl_lines_line\zerocount
+ \global\c_tabl_lines_page\zerocount
+ \global\d_tabl_lines_width\zeropoint
+ \tabl_lines_set
+ \fi}
+
+\def\tabl_lines_start_part
+ {\global\c_tabl_lines_subcol\zerocount
+ \setbox\scratchbox\hbox\bgroup
+ \dousestyleparameter{\linetablerparameter\c!style}%
+ \dousecolorparameter{\linetablerparameter\c!color}%
+ \ignorespaces}
+
+\def\tabl_lines_stop_part
+ {\ifnum\c_tabl_lines_part>\zerocount
+ \unskip \unskip % remove last intercolumn skip (distance+fill)
+ \fi
+ \egroup
+ \ifconditional\c_tabl_lines_preroll \else
+ \ifcase\c_tabl_lines_part
+ % we're collecting the repeater
+ \else
+ \ifdim\dimexpr\hsize-\wd\scratchbox\relax>\linetableparameter\c!stretch\else
+ \setbox\scratchbox\hpack to \hsize{\unhbox\scratchbox}%
+ \fi
+ \fi
+ \fi}
+
+\def\tabl_lines_check_part
+ {\global\advance\d_tabl_lines_width\wd\b_tabl_lines_cell
+ \global\advance\c_tabl_lines_column\c_tabl_lines_step
+ \global\advance\c_tabl_lines_subcol\c_tabl_lines_step
+ \relax
+ %\message{\the\c_tabl_lines_column,\the\c_tabl_lines_subcol}\wait
+ % from now on the column counter is already incremented
+ \ifcase\c_tabl_lines_split_state
+ \ifconditional\c_tabl_lines_preroll \else
+ \box\b_tabl_lines_cell
+ % the columncounter is one ahead !
+ \hskip\scratchskip
+ \fi
+ %%%
+ \donefalse
+ \ifcase\c_tabl_lines_repeat\else
+ % calculate ahead
+ \ifnum\c_tabl_lines_repeat=\numexpr\c_tabl_lines_column-\plustwo\relax
+ \donetrue % collecting repeater
+ \fi
+ \fi
+ %%%%
+ \ifdone
+ % collecting repeater
+ \else
+ \ifnum\c_tabl_lines_column>\csname\??linetablepart\number\c_tabl_lines_part\endcsname\relax
+ \donetrue
+ \fi
+ \fi
+ \ifdone
+ \tabl_lines_stop_part
+ \ifconditional\c_tabl_lines_preroll \else
+ \tabl_lines_save_part
+ \fi
+ \ifcase\c_tabl_lines_page \or
+ \global\c_tabl_lines_page \plustwo
+ \else
+ \global\c_tabl_lines_page \plusone
+ \fi
+ \global\advance\c_tabl_lines_part\plusone
+ \global\d_tabl_lines_width\wd\b_tabl_lines_current\zerocount
+ \tabl_lines_start_part
+ \fi
+ \else
+ \donefalse
+ \scratchconditiononefalse
+ \ifcase\c_tabl_lines_repeat\else
+ % calculate ahead
+ \ifnum\c_tabl_lines_repeat=\numexpr\c_tabl_lines_column-\plustwo\relax
+ \donetrue % collecting repeater
+ \fi
+ \fi
+ \ifdone
+ \scratchconditiononetrue
+ % collecting repeater
+ \orelse\ifdim\d_tabl_lines_width>\hsize
+ \donetrue
+ \else
+ \global\advance\d_tabl_lines_width\scratchskip
+ \ifdim\d_tabl_lines_width>\hsize % ?
+ \donetrue
+ \fi
+ \fi
+ \ifdone
+ \tabl_lines_stop_part
+ \tabl_lines_save_part
+ \ifcase\c_tabl_lines_page \or
+ \global\c_tabl_lines_page \plustwo
+ \else
+ \global\c_tabl_lines_page \plusone
+ \fi
+ \global\advance\c_tabl_lines_part\plusone
+ \ifnum\c_tabl_lines_part>\c_tabl_lines_n_of_parts
+ \global\c_tabl_lines_n_of_parts\c_tabl_lines_part
+ \tabl_lines_initialize_box\c_tabl_lines_part
+ \fi
+ \global\d_tabl_lines_width\wd\b_tabl_lines_cell
+ \tabl_lines_start_part
+ \ifscratchconditionone \orelse \ifcase\c_tabl_lines_repeat \else
+ % check for left/right page
+ \ifcase\c_tabl_lines_page\donetrue\or\donetrue\or\donefalse\fi\ifdone
+ % insert repeater
+ \global\advance\d_tabl_lines_width\wd\b_tabl_lines_current\zerocount
+ \ifconditional\c_tabl_lines_preroll\kern\wd\else\unhcopy\fi\b_tabl_lines_current\zerocount
+ \fi
+ \fi
+ \fi
+ \ifconditional\c_tabl_lines_preroll \else
+ \box\b_tabl_lines_cell
+ % the columncounter is one ahead !
+ %\dorecurse\c_tabl_lines_step{\strut\hfil}%
+ \strut
+ \hskip\scratchskip
+ \fi
+ \fi}
+
+\permanent\protected\def\startlinetablerun % to do: quit when nested
+ {\bgroup
+ \dontcomplain
+ \settrue\c_tabl_lines_in_table
+ % autowidth
+ \doif{\linetableparameter\c!maxwidth}\v!fit
+ {\setuplinetable[\c!maxwidth=\zeropoint]}%
+ \processaction
+ [\linetableparameter\c!stretch]
+ [ \v!no=>{\setuplinetable[\c!stretch=\maxdimen]},% no stretch
+ \v!yes=>{\setuplinetable[\c!stretch=\zeropoint]}]% max stretch
+ \c_tabl_lines_repeat\linetableparameter\c!nleft
+ \c_tabl_lines_split_state % =
+ \ifdim\linetableparameter\c!maxwidth>\zeropoint
+ \zerocount \else \plusone
+ \fi
+ % optional prevdepth correction
+ \ifconditional\c_tabl_lines_preroll
+ \global\c_tabl_lines_n_of_rows\zerocount
+ \else
+ \linetableparameter\c!before
+ \fi
+ \global\c_tabl_lines_rows\zerocount
+ \global\c_tabl_lines_n_of_columns\zerocount
+ \global\c_tabl_lines_n_of_parts\zerocount
+ \scratchcounter\zerocount
+ \def\docommand##1%
+ {\global\advance\c_tabl_lines_n_of_parts\plusone
+ \advance\scratchcounter##1%
+ \xdefcsname\??linetablepart\number\c_tabl_lines_n_of_parts\endcsname{\the\scratchcounter}}%
+ \processcommacommand[\linetableparameter\c!n]\docommand
+ \tabl_lines_initialize_boxes\c_tabl_lines_n_of_parts
+ \global\c_tabl_lines_part\ifcase\c_tabl_lines_repeat\plusone\else\zerocount\fi % repeater
+ \global\c_tabl_lines_step\plusone
+ \global\c_tabl_lines_line\zerocount
+ \global\c_tabl_lines_row \zerocount
+ \global\c_tabl_lines_column\zerocount
+ \global\c_tabl_lines_subcol\zerocount
+ \global\d_tabl_lines_width\zeropoint
+ \ifconditional\c_tabl_lines_preroll\orelse\ifdim\pagetotal>\zeropoint
+ \verticalstrut\kern-\struttotal
+ \fi
+ \tabl_lines_set
+ \tabl_lines_check_page
+ \enforced\let\BR\tabl_lines_BR
+ \enforced\let\ER\tabl_lines_ER
+ \enforced\let\BH\tabl_lines_BR
+ \enforced\let\EH\tabl_lines_ER
+ \enforced\let\BC\tabl_lines_BC
+ \enforced\let\EC\tabl_lines_EC
+ \enforced\let\NC\tabl_lines_NC
+ \enforced\let\NR\tabl_lines_NR
+ \tabl_lines_flush_head}
+
+\permanent\protected\def\stoplinetablerun
+ {\global\c_tabl_lines_line\maxcard
+ \c_tabl_lines_head_state\zerocount % blocked
+ \tabl_lines_flush_parts
+ \ifconditional\c_tabl_lines_preroll \else
+ \linetableparameter\c!after
+ \fi
+ \global\c_tabl_lines_part\zerocount
+ \global\c_tabl_lines_n_of_parts\zerocount
+ \egroup}
+
+\permanent\def\checklinecolumndimension#1#2#3%
+ {\global\edef#1\number#3\endcsname
+ {\expandafter\ifrelax\csname#1\number#3\endcsname
+ \the#2\b_tabl_lines_cell
+ \orelse\ifdim\csname#1\number#3\endcsname<#2\b_tabl_lines_cell
+ \the#2\b_tabl_lines_cell
+ \else
+ \csname#1\number#3\endcsname
+ \fi}}
+
+\def\tabl_lines_check_width {\checklinecolumndimension\??linetablewidth \wd\c_tabl_lines_column}
+\def\tabl_lines_check_height{\checklinecolumndimension\??linetableheight\ht\c_tabl_lines_row}
+\def\tabl_lines_check_depth {\checklinecolumndimension\??linetabledepth \dp\c_tabl_lines_row}
+
+\permanent\tolerant\protected\def\tabl_lines_BR[#1]% #1 not yet implemented
+ {\ifnum\c_tabl_lines_head_state=1\else
+ \global\advance\c_tabl_lines_row\plusone
+ \global\advance\c_tabl_lines_rows\plusone
+ \fi
+ \global\c_tabl_lines_column\plusone
+ \global\c_tabl_lines_subcol\plusone
+ \d_tabl_lines_height\zeropoint
+ \edef\p_height{\linetablerparameter\c!height}%
+ \ifempty\p_height
+ \c_tabl_lines_hmode \zerocount
+ \orelse\ifx\p_height\v!fit
+ \c_tabl_lines_hmode \plusone
+ \orelse\ifx\p_height\v!line
+ \c_tabl_lines_hmode \plustwo
+ \else
+ \d_tabl_lines_height\dimexpr\p_height-\strutdepth\relax
+ \fi
+ \tabl_lines_start_part}
+
+\permanent\protected\def\tabl_lines_BC
+ {\startlinetablecell}
+
+\permanent\protected\def\tabl_lines_EC
+ {\stoplinetablecell
+ \ifconditional\c_tabl_lines_preroll
+ \tabl_lines_check_width
+ \tabl_lines_check_height
+ \tabl_lines_check_depth
+ \fi
+ \tabl_lines_check_part}
+
+\permanent\protected\def\tabl_lines_ER
+ {% \stoplinetablecell
+ % no \box\b_tabl_lines_cell, i.e. dummy columnn, last \NC \NR
+ \tabl_lines_stop_part
+ \tabl_lines_save_part
+ \advance\c_tabl_lines_column \minusone
+ \ifnum\c_tabl_lines_column>\c_tabl_lines_n_of_columns
+ \global\c_tabl_lines_n_of_columns\c_tabl_lines_column
+ \fi
+ \tabl_lines_flush_parts
+ \global\c_tabl_lines_column\zerocount
+ \global\d_tabl_lines_width \zeropoint
+ \ifcase\c_tabl_lines_repeat
+ \global\c_tabl_lines_part\plusone
+ \else
+ \global\c_tabl_lines_part\zerocount % repeater
+ \fi
+ \tabl_lines_check_page
+ \tabl_lines_flush_head}
+
+\def\tabl_lines_check_page
+ {\global\c_tabl_lines_page\zerocount
+ \ifcase\c_tabl_lines_repeat\orelse\ifcase\c_tabl_lines_page
+ \doif{\linetableparameter\c!repeat}\v!no
+ {\global\c_tabl_lines_page\doifelseoddpage\plusone\plustwo}%
+ \fi}
+
+\def\tabl_lines_flush_head
+ {\ifcase\c_tabl_lines_head_state
+ % 0 blocked
+ \or
+ % 1 doing head
+ \or
+ % 2 head done
+ \or
+ % 3 trigger flush
+ \c_tabl_lines_head_state\plusone
+ \the\t_tabl_lines_head\relax
+ \c_tabl_lines_head_state\plustwo
+ \fi}
+
+\permanent\protected\def\tabl_lines_NC % first time special treatment
+ {\relax
+ \ifcase\c_tabl_lines_column
+ \tabl_lines_BR
+ \else
+ \tabl_lines_EC
+ \fi
+ \tabl_lines_BC} % beware, this will result in BR BC EC BC NR
+
+\permanent\protected\def\tabl_lines_NR
+ {\stoplinetablecell % dummy
+ \tabl_lines_ER}
+
+\permanent\protected\def\startlinetable
+ {\startlinetablerun}
+
+\permanent\protected\def\stoplinetable
+ {\stoplinetablerun}
+
+\permanent\protected\def\startlinetableanalysis
+ {\bgroup
+ \settrue\c_tabl_lines_preroll
+ \settrialtypesetting
+ \startlinetablerun}
+
+\permanent\protected\def\stoplinetableanalysis
+ {\stoplinetablerun
+ \egroup
+ \global\c_tabl_lines_n_of_rows\c_tabl_lines_rows
+ \dorecurse\c_tabl_lines_n_of_rows % global, from last run {\linetableparameter\c!n}
+ {\edefcsname\??linetable r:##1x\c!height\endcsname{\csname\??linetableheight##1\endcsname}%
+ \edefcsname\??linetable r:##1x\c!depth \endcsname{\csname\??linetabledepth ##1\endcsname}%
+ \gletcsname\??linetableheight##1\endcsname\zeropoint
+ \gletcsname\??linetabledepth ##1\endcsname\zeropoint}
+ \dorecurse\c_tabl_lines_n_of_columns % global, from last run {\linetableparameter\c!n}
+ {\edefcsname\??linetable c:##1\c!width\endcsname{\csname\??linetablewidth##1\endcsname}%
+ \gletcsname\??linetablewidth##1\endcsname\zeropoint}} % init next table
+
+% todo: store in box instead of macro
+
+\aliased\let\stoplinetablehead\relax
+
+\permanent\protected\def\startlinetablehead#1\stoplinetablehead
+ {\ifconditional\c_tabl_lines_in_table
+ \t_tabl_lines_head\emptytoks
+ \fi
+ \c_tabl_lines_head_state\plusthree % full
+ \t_tabl_lines_head{#1}%
+ \ifconditional\c_tabl_lines_in_table
+ \tabl_lines_flush_head
+ \fi}
+
+\permanent\protected\def\tabl_lines_BH
+ {\ifrelax\EC
+ % signal, grabbing lines
+ \else
+ \t_tabl_lines_head\emptytoks
+ \fi
+ \push_macro_BC
+ \push_macro_EC
+ \enforced\permanent\protected\def\BC##1\EC{\appendtoks##1\to\t_tabl_lines_head}%
+ \enforced\permanent \let\EC\relax} % signal
+
+\permanent\protected\def\tabl_lines_EH
+ {\pop_macro_EC
+ \pop_macro_BC
+ \expandafter\startlinetablehead\the\t_tabl_lines_head\stoplinetablehead}
+
+\permanent\let\startlinetablebody\relax
+\permanent\let\stoplinetablebody \relax
+
+\permanent\tolerant\protected\def\processlinetablebuffer[#1]%
+ {\bgroup
+ \enforced\let\startlinetable\relax
+ \enforced\let\stoplinetable \relax
+ \startlinetableanalysis\getbuffer[#1]\stoplinetableanalysis
+ \startlinetablerun \getbuffer[#1]\stoplinetablerun
+ \egroup}
+
+\permanent\tolerant\protected\def\processlinetablefile[#1]% maybe accept #1 as well as [#1]
+ {\bgroup
+ \enforced\let\startlinetable\relax
+ \enforced\let\stoplinetable \relax
+ \startlinetableanalysis\readfile{#1}\donothing\donothing\stoplinetableanalysis
+ \startlinetablerun \readfile{#1}\donothing\donothing\stoplinetablerun
+ \egroup}
+
+\popoverloadmode
+
+\setuplinetable
+ [\c!n=\maxcard,
+ \c!lines=\maxcard,
+ \c!nx=\plusone,
+ \c!nleft=\zerocount,
+ \c!repeat=\v!yes, % when nleft > 0, repeat on both pages
+ \c!before=,
+ \c!after=,
+ \c!inbetween=\page,
+ \c!distance=\zeropoint,
+ \c!stretch=\v!no,
+ \c!align=\c!right,
+ \c!leftoffset=.25\exheight,
+ \c!rightoffset=\linetableparameter\c!leftoffset,
+ \c!maxwidth=\zeropoint,
+ \c!width=5\emwidth,
+ \c!height=\v!fit, % \v!line = faster
+ \c!background=,
+ \c!backgroundcolor=,
+ \c!rulethickness=\linewidth]
+
+\protect
+
+\continueifinputfile{tabl-ltb.mkxl}
+
+\setuplinetable[n=6,m={2,2,2},lines=25] % m ?
+
+\setuplinetable[c][1] [width=2cm,background=color,backgroundcolor=red]
+\setuplinetable[c][4] [width=3cm,background=color,backgroundcolor=yellow]
+\setuplinetable[c][6] [width=3cm,background=color,backgroundcolor=magenta]
+\setuplinetable[r][odd] [background=color,backgroundcolor=gray]
+\setuplinetable[r][even][background=color,backgroundcolor=green]
+
+\starttext
+
+\showframe \showstruts
+
+\setuppagenumbering[alternative=doublesided]\page[left]
+
+\startlinetable
+\NC aaa\crlf aaa \NC bb \NC c \NC ddddd \NC eeee \NC ff \NC \NR
+\dorecurse{100}{\NC aaa \NC bb \NC c \NC ddddd \NC eeee \NC ff \NC \NR}
+\stoplinetable
+
+\startlinetable
+\NC[style=slanted,color=green,background=color,backgroundcolor=darkred,nx=2,uitlijnen=middle] xxx
+ \NC yy \NC ddddd \NC eeee \NC ff \NC \NR
+\dorecurse{100}{\NC aaa \NC bb \NC c \NC ddddd \NC eeee \NC ff \NC \NR}
+\stoplinetable
+
+% \startbuffer[lt]
+% \NC aaa\crlf aaa \NC bb \NC c \NC ddddd \NC ee \NC ff \NC \NR
+% \NC aaa\crlf aaa \NC b \NC cc \NC ddd \NC eeee \NC f \NC \NR
+% \stopbuffer
+%
+% \processlinetablebuffer[lt]
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-mis.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-mis.mkxl
new file mode 100644
index 00000000000..7b6f0ffac1f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-mis.mkxl
@@ -0,0 +1,299 @@
+%D \module
+%D [ file=tabl-mis,
+%D version=2012.06.28,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Miscellaneous,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / Miscellaneous}
+
+\unprotect
+
+%D \macros
+%D {somekindoftab,kindoftabposition}
+%D
+%D This macro can be used to create tabs:
+%D
+%D \starttyping
+%D \setupheadertexts[{\somekindoftab[alternative=horizontal]{\framed{\kindoftabposition}}}]
+%D \setuptexttexts [{\somekindoftab[alternative=vertical] {\framed{\kindoftabposition}}}]
+%D
+%D \starttext
+%D \showframe \dorecurse{10}{test\page}
+%D \stoptext
+%D \stoptyping
+%D
+%D (This rather old but updated code used to be in \type {core-mis.mkiv}.)
+
+\mutable\let\kindoftabposition\!!zerocount
+
+\permanent\tolerant\protected\def\somekindoftab[#1]%
+ {\bgroup
+ \getdummyparameters
+ [\c!alternative=\v!vertical,
+ \c!width=\textwidth,\c!height=\textheight,
+ \c!n=\lastpage,\c!m=\realpageno,
+ #1]%
+ \doifelse{\directdummyparameter\c!alternative}\v!vertical
+ {\typo_kindoftab_indeed\vbox\vskip\c!height}
+ {\typo_kindoftab_indeed\hbox\hskip\c!width }}
+
+\def\typo_kindoftab_indeed#1#2#3#4%
+ {#1 to \directdummyparameter#3 \bgroup
+ \forgetall
+ \scratchnx\directdummyparameter\c!n\relax
+ \scratchmx\directdummyparameter\c!m\relax
+ \edef\kindoftabposition{\the\scratchmx}%
+ \ifnum\scratchmx>\plusone
+ #2\zeropoint \s!plus \the\numexpr\scratchmx-\plusone \relax\s!fill\relax
+ \fi
+ #4% can use \kindoftabposition
+ \ifnum\scratchmx<\scratchnx\relax
+ #2\zeropoint \s!plus \the\numexpr\scratchnx-\scratchmx\relax\s!fill\relax
+ \fi
+ \egroup
+ \egroup}
+
+%D The following paragraphs mechanism is probably one of the oldest of \CONTEXT\ and
+%D mostly served as a table mechanism capable of dealing with paragraphs. Nowadays
+%D one can also use tabulate or natural tables.
+%D
+%D \startbuffer
+%D \defineparagraphs[sample][n=2,rule=on]
+%D
+%D \startsample
+%D first \nextsample
+%D second \nextsample
+%D third
+%D \stopsample
+%D
+%D \startsample
+%D \input tufte \nextsample
+%D \input ward \nextsample
+%D \input davis \nextsample
+%D \input zapf
+%D \stopsample
+%D
+%D \startparagraphs[sample]
+%D first \nextsample
+%D second \nextsample
+%D third
+%D \stopparagraphs
+%D
+%D \startparagraphs[sample]
+%D \startparagraphscell
+%D first
+%D \stopparagraphscell
+%D \startparagraphscell
+%D second
+%D \stopparagraphscell
+%D \startparagraphscell
+%D third
+%D \stopparagraphscell
+%D \stopparagraphs
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\installcorenamespace{paragraphs}
+
+\installframedcommandhandler \??paragraphs {paragraphs} \??paragraphs
+
+\setupparagraphs
+ [\c!n=3,
+ \c!offset=\zeropoint,
+ \c!before=\blank,
+ \c!after=\blank,
+ \c!distance=\emwidth,
+ \c!height=\v!fit,
+ \c!width=\availablehsize,
+ \c!rule=\v!off,
+ \c!command=,
+ \c!align=,
+ \c!tolerance=\v!tolerant, % obsolete
+ \c!rulethickness=\linewidth,
+ \c!rulecolor=,
+ \c!style=,
+ \c!color=,
+ \c!frame=\v!off,
+ \c!top=\vss,
+ \c!bottom=\vfill]
+
+\aliased\let\typo_paragraphs_setup_saved\setupparagraphs
+
+\let\typo_paragraphs_setup_step\gobbleoneargument
+
+\overloaded\permanent\tolerant\protected\def\setupparagraphs[#1]#*[#2]#*[#3]% we are downward compatible with [each] and [1,3]
+ {\ifarguments\or
+ \typo_paragraphs_setup_saved[#1]%
+ \or
+ \typo_paragraphs_setup_saved[#1][#2]%
+ \or
+ \doifelse{#2}\v!each
+ {\dorecurse{\namedparagraphsparameter{#1}\c!n}%
+ {\normalexpanded{\typo_paragraphs_setup_saved[#1:\recurselevel]}[#3]}}%
+ {\def\typo_paragraphs_setup_step##1{\typo_paragraphs_setup_saved[#1:##1][#3]}%
+ \processcommalist[#2]\typo_paragraphs_setup_step}%
+ \fi}
+
+\appendtoks
+ \frozen\instance\letcsname\e!next \currentparagraphs\endcsname\nextparagraphs
+ \frozen\instance\letcsname\currentparagraphs\endcsname\nextparagraphs
+ \frozen\instance\protected\edefcsname\e!start\currentparagraphs\endcsname{\startparagraphs[\currentparagraphs]}%
+ \frozen\instance\letcsname\e!stop \currentparagraphs\endcsname\stopparagraphs
+ %frozen\instance\setuevalue{\e!setup\currentparagraph\e!endsetup}{\typo_paragraphs_setup_saved[\currentparagraphs]}%
+ \dorecurse{\paragraphsparameter\c!n}%
+ {\normalexpanded{\typo_paragraphs_setup_saved[\currentparagraphs:\recurselevel][\c!width=,\s!parent=\??paragraphs\currentparagraphs]}}%
+ \typo_paragraphs_setup_saved[\currentparagraphs:1][\c!distance=\zeropoint]%
+\to \everydefineparagraphs
+
+\newcount\c_typo_paragraphs_n
+\newcount\c_typo_paragraphs_max
+\newdimen\d_typo_paragraphs_width
+\newdimen\d_typo_paragraphs_auto
+
+\let\typo_paragraphs_start_cell\relax
+\let\typo_paragraphs_stop_cell \relax
+
+\permanent\protected\def\startparagraphs[#1]% quite slow
+ {\bgroup % (1)
+ \edef\currentparagraphs{#1}%
+ % \paragraphsparameter\c!before
+ \edef\p_width{\paragraphsparameter\c!width}%
+ \ifempty\p_width
+ \d_typo_paragraphs_width\availablehsize
+ \else
+ \d_typo_paragraphs_width\p_width\relax
+ \fi
+ \advance\d_typo_paragraphs_width-2\dimexpr\paragraphsparameter\c!offset\relax
+ \c_typo_paragraphs_max\paragraphsparameter\c!n\relax
+ \d_typo_paragraphs_auto\d_typo_paragraphs_width\relax
+ \scratchcounter\zerocount
+ \dorecurse\c_typo_paragraphs_max
+ {\edef\p_width{\namedparagraphsparameter{\currentparagraphs:\recurselevel}\c!width}%
+ \ifempty\p_width
+ \advance\scratchcounter\plusone
+ \else
+ \advance\d_typo_paragraphs_auto-\p_width\relax
+ \fi
+ \ifnum\recurselevel>\plusone
+ \advance\d_typo_paragraphs_auto-\namedparagraphsparameter{\currentparagraphs:\recurselevel}\c!distance\relax
+ \fi}%
+ \ifnum\scratchcounter>\zerocount
+ \divide\d_typo_paragraphs_auto\scratchcounter
+ \else
+ \d_typo_paragraphs_auto\zeropoint
+ \fi
+ \parindent\zeropoint
+ \c_typo_paragraphs_n\zerocount
+ \enforced\let\\\nextparagraphs % downward compatible
+ \edef\p_offset{\paragraphsparameter\c!offset}%
+ \doifelsedimension\p_offset
+ {\scratchoffset\p_offset}%
+ {\scratchoffset\zeropoint}%
+ \setbox\scratchbox\hpack
+ \bgroup % (2)
+ \forgetall
+ \advance\hsize-2\scratchoffset
+ \let\typo_paragraphs_start_cell\typo_paragraphs_start_cell_indeed
+ \let\typo_paragraphs_stop_cell \typo_paragraphs_stop_cell_indeed
+ \typo_paragraphs_start_cell_indeed}
+
+\permanent\protected\def\stopparagraphs
+ {\stopparagraphscell
+ \egroup % (2)
+ \letparagraphsparameter\c!align\v!flushleft % normal
+ \paragraphsparameter\c!before
+ \dontleavehmode\inheritedparagraphsframed{\box\scratchbox}%
+ \paragraphsparameter\c!after
+ \egroup} % (1)
+
+\permanent\protected\def\nextparagraphs
+ {\stopparagraphscell
+ \startparagraphscell}
+
+\permanent\protected\def\startparagraphscell
+ {\typo_paragraphs_start_cell}
+
+\def\typo_paragraphs_start_cell_indeed
+ {\removeunwantedspaces
+ \advance\c_typo_paragraphs_n\plusone
+ \ifnum\c_typo_paragraphs_n>\c_typo_paragraphs_max
+ \expandafter\typo_paragraphs_start_cell_nop
+ \else
+ \expandafter\typo_paragraphs_start_cell_yes
+ \fi}
+
+\def\typo_paragraphs_start_cell_nop
+ {\begingroup
+ % message: too many cells in paragraphs
+ \let\typo_paragraphs_start_cell\relax
+ \let\typo_paragraphs_stop_cell\typo_paragraphs_stop_cell_indeed
+ \setbox\scratchbox\vbox\bgroup}
+
+\def\typo_paragraphs_start_cell_yes
+ {\begingroup
+ \let\typo_paragraphs_start_cell\relax
+ \let\typo_paragraphs_stop_cell\typo_paragraphs_stop_cell_indeed
+ \edef\currentparagraphs{\currentparagraphs:\the\c_typo_paragraphs_n}%
+ \ifnum\c_typo_paragraphs_n>\plusone
+ \typo_paragraphs_separator
+ \fi
+ \edef\p_height{\paragraphsparameter\c!height}%
+ \edef\p_width {\paragraphsparameter\c!width }%
+ \useparagraphsstyleandcolor\c!style\c!color
+ \setbox\scratchbox\vtop \ifempty\p_height \orelse\ifx\p_height\v!fit \else to \p_height \fi
+ \bgroup % (2)
+ \blank[\v!disable]%
+ \paragraphsparameter\c!top
+ \hsize\ifempty\p_width \d_typo_paragraphs_auto \else \p_width \fi \relax
+ \usealignparameter\paragraphsparameter
+ \paragraphsparameter\c!inner
+ \everypar{\begstrut\everypar\emptytoks}%
+ \ignorespaces
+ \paragraphsparameter\c!command}
+
+\permanent\protected\def\stopparagraphscell
+ {\typo_paragraphs_stop_cell
+ \let\typo_paragraphs_stop_cell\relax}
+
+\def\typo_paragraphs_stop_cell_indeed
+ {\ifnum\c_typo_paragraphs_n>\c_typo_paragraphs_max
+ \expandafter\typo_paragraphs_stop_cell_nop
+ \else
+ \expandafter\typo_paragraphs_stop_cell_yes
+ \fi}
+
+\def\typo_paragraphs_stop_cell_nop
+ {\egroup
+ \endgroup}
+
+\def\typo_paragraphs_stop_cell_yes
+ {\ifvmode
+ \removelastskip
+ \else
+ \removeunwantedspaces
+ \endstrut
+ \endgraf
+ \fi
+ \paragraphsparameter\c!bottom
+ \egroup % (2)
+ \dontleavehmode\hpack{\raise\strutheight\box\scratchbox}%
+ \endgroup}
+
+\def\typo_paragraphs_separator
+ {\scratchdistance\paragraphsparameter\c!distance
+ \doif{\paragraphsparameter\c!rule}\v!on
+ {\scratchwidth\paragraphsparameter\c!rulethickness
+ \scratchdistance\dimexpr(\scratchdistance-\scratchwidth)/2\relax
+ \hskip\scratchdistance
+ \color[\paragraphsparameter\c!rulecolor]{\vrule\s!width\scratchwidth}}%
+ \hskip\scratchdistance}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-ntb.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-ntb.mkxl
new file mode 100644
index 00000000000..5b30c449ca4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-ntb.mkxl
@@ -0,0 +1,2179 @@
+%D \module
+%D [ file=tabl-ntb,
+%D version=2000.04.18,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Natural Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module has a more modern variant in xtables but as we follow a bit different
+%D approach with settings there, this mechanism will stay. In fact each of them has
+%D its advantages. This module could be sped up a bit and made more efficient by
+%D delegating some housekeeping to \LUA\ but it's not worth the effort. The code
+%D could me made more readable but again, there is no real purpose in it. If needed
+%D I can squeeze out a few more percentages runtime.
+
+% columndistance 'optimized' ... needs checking
+%
+% we don't need the alignment mechanism .. we can just pack the row in a box
+
+\writestatus{loading}{ConTeXt Table Macros / Natural Tables}
+
+% sometimes this helps (with nc going wild): \setupTABLE[maxwidth=100cm]
+%
+% bug: width 3cm is not honored and column becomes too wide as given width is added
+% to distributed width
+%
+% \bTABLE
+% \bTR
+% \bTD test \eTD
+% \bTD \framed[height=3cm]{test} \eTD
+% \bTD[width=3cm] \dorecurse{30}{a } \eTD
+% \bTD \input ward \eTD
+% \eTR
+% \bTR
+% \bTD test \eTD
+% \bTD \framed[height=3cm]{test} \eTD
+% \bTD \dorecurse{30}{a } \eTD
+% \bTD \input ward \eTD
+% \eTR
+% \eTABLE
+
+% \protected\def\startrow {\bTR}
+% \protected\def\stoprow {\eTR}
+% \protected\def\startcell#1\stopcell{\bTD#1\eTD}
+% \let\stopcell \relax
+% \let\startcelltable \bTABLE
+% \let\stopcelltable \eTABLE
+
+% \starttext
+% \startcelltable
+% \startrow \startcell a \stopcell \stoprow
+% \startrow \startcell a \stopcell \stoprow
+% \startrow \startcell a \stopcell \stoprow
+% \startrow \startcell a \stopcell \stoprow
+% \stopcelltable
+% \stoptext
+
+%D As always, this is the n\high{th} version. Much time went in trying to speed up
+%D the many cell calculations, some optimizations were rejected in order not to
+%D complicate this module too much (and in order to prevail extensibility). In the
+%D meantime we've sacrified some speed for readability.
+
+\unprotect
+
+%D The next alternative also takes care of preceding and following white space.
+%D
+%D \startbuffer
+%D \bTABLE[left={(},right={)},top=\startnarrower,bottom=\stopnarrower]
+%D \bTR \bTD something \eTD \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\ifdefined\dotagTABLEcell \else \aliased\let\dotagTABLEcell \relax \fi % todo: namespace
+\ifdefined\dotagTABLEsignal \else \aliased\let\dotagTABLEsignal\relax \fi % todo: namespace
+
+\let\tabl_ntb_next_level\relax
+
+\newtoks\t_tabl_ntb_cell_start
+\newtoks\t_tabl_ntb_cell_stop
+
+\appendtoks
+ \naturaltablelocalparameter\c!left
+ \delayedbegstrut
+\to \t_tabl_ntb_cell_start
+
+\appendtoks
+ \delayedendstrut
+ \naturaltablelocalparameter\c!right
+\to \t_tabl_ntb_cell_stop
+
+\appendtoks
+ \flushpostponednodedata
+ % maybe: \the\neverypar
+\to \t_tabl_ntb_cell_start
+
+\protected\def\tabl_ntb_cell_start
+ {% \inhibitblank
+ \dotagTABLEcell
+ %\tabl_ntb_next_level
+ \font_styles_math_reset
+ \usenaturaltablelocalstyleandcolor\c!style\c!color
+ \everypar\t_tabl_ntb_cell_start
+ \font_styles_math_start}
+
+\protected\def\tabl_ntb_cell_stop
+ {\font_styles_math_stop
+ \ifhmode
+ \the\t_tabl_ntb_cell_stop
+ \par % added 13/4/2006
+ \orelse\ifdim\prevdepth<\zeropoint % =-1000pt ?
+ % not sure yet:\naturaltablelocalparameter\c!right
+ \vskip-\strutdp
+ \else
+ \removebottomthings
+ \fi}
+
+% maybe:
+%
+% \protected\def\tabl_ntb_cell_stop
+% {\ifhmode
+% \the\t_tabl_ntb_cell_stop
+% \par % added 13/4/2006
+% \else
+% % not sure yet:\naturaltablelocalparameter\c!right
+% \par
+% \ifhmode
+% % \removeunwantedspaces
+% \orelse\ifdim\prevdepth<\zeropoint % =-1000pt ?
+% \vskip-\strutdp
+% \else
+% \removebottomthings
+% \fi
+% \fi}
+
+\newcount\c_tabl_ntb_row
+\newcount\c_tabl_ntb_col
+\newcount\c_tabl_ntb_spn
+
+\newcount\c_tabl_ntb_nx
+\newcount\c_tabl_ntb_ny
+
+\setnewconstant\c_tabl_ntb_cell \plusone
+\setnewconstant\c_tabl_ntb_none \plustwo
+
+\newcount\c_tabl_ntb_current_row
+\newcount\c_tabl_ntb_current_col
+\newcount\c_tabl_ntb_current_row_one
+\newcount\c_tabl_ntb_current_col_one
+\newcount\c_tabl_ntb_current_row_two
+\newcount\c_tabl_ntb_current_col_two
+\newcount\c_tabl_ntb_current_row_three
+\newcount\c_tabl_ntb_current_col_three
+\newcount\c_tabl_ntb_current_row_four
+\newcount\c_tabl_ntb_current_col_four
+
+\newcount\c_tabl_ntb_running_col
+\newcount\c_tabl_ntb_maximum_row
+\newcount\c_tabl_ntb_maximum_col
+\newcount\c_tabl_ntb_maximum_row_span
+\newcount\c_tabl_ntb_maximum_col_span
+
+\newcount\c_tabl_ntb_encountered_col
+\newcount\c_tabl_ntb_encountered_max
+
+\newtoks\t_tabl_ntb
+\newtoks\t_tabl_ntb_row
+
+\newconstant\c_tabl_tbl_pass
+
+\newtoks\t_tabl_ntb_head
+\newtoks\t_tabl_ntb_next
+\newtoks\t_tabl_ntb_body
+\newtoks\t_tabl_ntb_foot
+
+\newcount\c_tabl_ntb_n_of_head_lines
+\newcount\c_tabl_ntb_n_of_next_lines
+\newcount\c_tabl_ntb_n_of_hdnx_lines
+
+\newdimen\d_tabl_ntb_height
+\newdimen\d_tabl_ntb_width
+
+\newdimen\d_tabl_ntb_leftmargindistance
+\newdimen\d_tabl_ntb_rightmargindistance
+\newdimen\d_tabl_ntb_columndistance
+\newdimen\d_tabl_ntb_maxwidth
+
+\newtoks\everyTABLEpass % public
+
+\newcount\tablecellrows % public (needs checking)
+\newcount\tablecellcolumns % public (needs checking)
+
+\newbox\b_tabl_ntb_final
+
+%D For tagging and export:
+
+\newconstant \c_tabl_ntb_head
+\newconstant \c_tabl_ntb_body
+\newconstant \c_tabl_ntb_foot
+\newconditional \c_tabl_ntb_okay
+
+%D We have already prepared the previous macros for nesting, so we only have to pop
+%D in the right ones:
+
+\newcount\c_tabl_level
+
+\installglobalmacrostack\m_tabl_ntb_saved_row
+\installglobalmacrostack\m_tabl_ntb_saved_col
+
+\protected\def\tabl_ntb_table_push
+ {\ifnum\m_tabl_tbl_level>\plusone
+ \tabl_ntb_parameters_reset
+ % we need a proper count push/pop
+ \xdef\m_tabl_ntb_saved_row{\the\c_tabl_ntb_row}\push_macro_m_tabl_ntb_saved_row
+ \xdef\m_tabl_ntb_saved_col{\the\c_tabl_ntb_col}\push_macro_m_tabl_ntb_saved_col
+ \else
+ \global\intabletrue
+ \fi}
+
+\protected\def\tabl_ntb_table_pop
+ {\ifnum\m_tabl_tbl_level>\plusone
+ \pop_macro_m_tabl_ntb_saved_row\global\c_tabl_ntb_row\m_tabl_ntb_saved_row
+ \pop_macro_m_tabl_ntb_saved_col\global\c_tabl_ntb_col\m_tabl_ntb_saved_col
+ \else
+ \global\intablefalse
+ \fi}
+
+\protected\def\tabl_ntb_next_level
+ {\advance\c_tabl_level\plusone
+ \edef\m_tabl_tbl_level{\the\c_tabl_level}}
+
+\protected\def\tabl_ntb_prev_level
+ {\advance\c_tabl_level\minusone
+ \edef\m_tabl_tbl_level{\the\c_tabl_level}}
+
+\tabl_ntb_next_level % go to level 1
+
+\installcorenamespace{naturaltable} % was tbl
+\installcorenamespace{naturaltablelocal} % was tbltbl
+
+\installdirectcommandhandler \??naturaltable {naturaltable} % \??naturaltable
+\installsimpleframedcommandhandler \??naturaltablelocal {naturaltablelocal} \??naturaltablelocal
+
+\permanent\protected\def\bTABLEnested{\tabl_ntb_next_level\bTABLE}
+\permanent\protected\def\eTABLEnested{\eTABLE\tabl_ntb_prev_level}
+
+\installcorenamespace{naturaltabletal}
+\installcorenamespace{naturaltablegal}
+\installcorenamespace{naturaltablenob}
+\installcorenamespace{naturaltabletag}
+\installcorenamespace{naturaltablecol}
+\installcorenamespace{naturaltablerow}
+\installcorenamespace{naturaltablewd}
+\installcorenamespace{naturaltableht}
+\installcorenamespace{naturaltabledp}
+\installcorenamespace{naturaltablewid}
+\installcorenamespace{naturaltablehei}
+\installcorenamespace{naturaltabledis}
+\installcorenamespace{naturaltableaut}
+\installcorenamespace{naturaltablebck}
+%installcorenamespace{naturaltablefwd} % forcedwidth
+\installcorenamespace{naturaltabletxt}
+\installcorenamespace{naturaltablespn}
+\installcorenamespace{naturaltableref}
+\installcorenamespace{naturaltableset}
+\installcorenamespace{naturaltablecell}
+\installcorenamespace{naturaltablesqueeze}
+\installcorenamespace{naturaltabletok}
+
+\letvalue{\??naturaltablesqueeze }\donefalse
+\letvalue{\??naturaltablesqueeze\v!fit }\donetrue
+\letvalue{\??naturaltablesqueeze\v!fixed}\donetrue
+\letvalue{\??naturaltablesqueeze\v!broad}\donetrue
+\letvalue{\??naturaltablesqueeze\v!local}\donetrue
+
+\def\tabl_ntb_let_gal{\gletcsname\??naturaltablegal\m_tabl_tbl_level\endcsname}
+\def\tabl_ntb_get_gal{\csname\??naturaltablegal\m_tabl_tbl_level\endcsname}
+
+\def\tabl_ntb_let_tal#1{\gletcsname\??naturaltabletal\m_tabl_tbl_level:\number#1\endcsname}
+\def\tabl_ntb_get_tal#1{\csname\??naturaltabletal\m_tabl_tbl_level:\number#1\endcsname}
+
+\def\tabl_ntb_set_nob#1{\letcsname\??naturaltablenob\m_tabl_tbl_level:\number#1\endcsname\plusone}
+\def\tabl_ntb_get_nob#1{\ifcsname\??naturaltablenob\m_tabl_tbl_level:\number#1\endcsname\plusone\else\zerocount\fi}
+
+%def\tabl_ntb_set_tag#1#2{\edefcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_set_col#1#2{\edefcsname\??naturaltablecol\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_set_row#1#2{\edefcsname\??naturaltablerow\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+\def\tabl_ntb_let_tag#1#2{\letcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_let_col#1#2{\letcsname\??naturaltablecol\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_let_row#1#2{\letcsname\??naturaltablerow\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+%def\tabl_ntb_set_wd#1#2{\xdefcsname\??naturaltablewd\m_tabl_tbl_level:\number#1:\number#2\endcsname} % global !
+\def\tabl_ntb_set_ht#1#2{\xdefcsname\??naturaltableht\m_tabl_tbl_level:\number#1:\number#2\endcsname} % global !
+
+%def\tabl_ntb_let_wd#1#2{\letcsname\??naturaltablewd\m_tabl_tbl_level:\number#1:\number#2\endcsname} % global !
+\def\tabl_ntb_let_ht#1#2{\letcsname\??naturaltableht\m_tabl_tbl_level:\number#1:\number#2\endcsname} % global !
+
+\def\tabl_ntb_get_tag#1#2{\csname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_get_col#1#2{\csname\??naturaltablecol\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_get_row#1#2{\csname\??naturaltablerow\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+%def\tabl_ntb_get_wd#1#2{\csname\??naturaltablewd\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_get_ht#1#2{\csname\??naturaltableht\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+\def\tabl_ntb_set_wid#1{\xdefcsname\??naturaltablewid\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+\def\tabl_ntb_set_hei#1{\xdefcsname\??naturaltablehei\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+\def\tabl_ntb_set_dis#1{\xdefcsname\??naturaltabledis\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+\def\tabl_ntb_set_aut#1{\xdefcsname\??naturaltableaut\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+
+\def\tabl_ntb_let_wid#1{\gletcsname\??naturaltablewid\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+\def\tabl_ntb_let_hei#1{\gletcsname\??naturaltablehei\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+\def\tabl_ntb_let_dis#1{\gletcsname\??naturaltabledis\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+\def\tabl_ntb_let_aut#1{\gletcsname\??naturaltableaut\m_tabl_tbl_level:\number#1\endcsname} % {#2} global !
+
+\def\tabl_ntb_get_wid#1{\ifcsname\??naturaltablewid\m_tabl_tbl_level:\number#1\endcsname\lastnamedcs\else\zeropoint\fi}
+\def\tabl_ntb_get_hei#1{\ifcsname\??naturaltablehei\m_tabl_tbl_level:\number#1\endcsname\lastnamedcs\else\zeropoint\fi}
+\def\tabl_ntb_get_dis#1{\ifcsname\??naturaltabledis\m_tabl_tbl_level:\number#1\endcsname\lastnamedcs\else\zeropoint\fi}
+\def\tabl_ntb_get_aut#1{\csname \??naturaltableaut\m_tabl_tbl_level:\number#1\endcsname}
+
+\def\tabl_ntb_let_bck#1#2{\global\expandafter\chardef\csname\??naturaltablebck\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+\def\tabl_ntb_get_bck#1#2{\csname\??naturaltablebck\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+\def\tabl_ntb_tag_pattern#1#2{\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2}
+\def\tabl_ntb_row_pattern#1#2{\??naturaltablerow\m_tabl_tbl_level:\number#1:\number#2}
+\def\tabl_ntb_col_pattern#1#2{\??naturaltablecol\m_tabl_tbl_level:\number#1:\number#2}
+
+\def\tabl_ntb_tag_doif #1#2{\ifcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname\expandafter\firstofoneargument \else\expandafter\gobbleoneargument \fi}
+\def\tabl_ntb_tag_doifnot #1#2{\ifcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname\expandafter\gobbleoneargument \else\expandafter\firstofoneargument \fi}
+\def\tabl_ntb_tag_doifelse#1#2{\ifcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}
+\def\tabl_ntb_row_doif #1#2{\ifcsname\??naturaltablerow\m_tabl_tbl_level:\number#1:\number#2\endcsname\expandafter\firstofoneargument \else\expandafter\gobbleoneargument \fi}
+\def\tabl_ntb_col_doif #1#2{\ifcsname\??naturaltablecol\m_tabl_tbl_level:\number#1:\number#2\endcsname\expandafter\firstofoneargument \else\expandafter\gobbleoneargument \fi}
+\def\tabl_ntb_col_doifnot #1#2{\ifcsname\??naturaltablecol\m_tabl_tbl_level:\number#1:\number#2\endcsname\expandafter\gobbleoneargument \else\expandafter\firstofoneargument \fi}
+
+%D If we ever run into memory issues we can do:
+%
+% \def\tabl_ntb_let_tag#1#2#3%
+% {\ifx#3\c_tabl_ntb_none\else
+% \letcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname#3%
+% \fi}
+%
+% \def\tabl_ntb_get_tag#1#2%
+% {\ifcsname\??naturaltabletag\m_tabl_tbl_level:\number#1:\number#2\endcsname
+% \lastnamedcs
+% \else
+% \c_tabl_ntb_none
+% \fi}
+
+\def\tabl_ntb_let_ref #1#2{\gletcsname\??naturaltableref\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+\def\tabl_ntb_set_ref #1#2{\xdefcsname\??naturaltableref\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+%def\tabl_ntb_get_ref #1#2{\ifcsname\??naturaltableref\m_tabl_tbl_level:\number#1:\number#2\endcsname\csname\??naturaltableref\m_tabl_tbl_level:\number#1:\number#2\endcsname\fi}
+\def\tabl_ntb_get_ref #1#2{\begincsname\??naturaltableref\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+\def\tabl_ntb_set_spn #1{\letcsname\??naturaltablespn\m_tabl_tbl_level:\number#1\endcsname \!!plusone}
+\def\tabl_ntb_spn_doifelse#1{\ifcase0\csname\??naturaltablespn\m_tabl_tbl_level:\number#1\endcsname\relax % could be inlined
+ \expandafter\secondoftwoarguments % unset
+ \else
+ \expandafter\firstoftwoarguments % a span
+ \fi}
+
+\protected\def\tabl_ntb_set_txt_process#1#2#3#4#5#6%
+ {\defcsname\??naturaltabletxt\m_tabl_tbl_level:\number#1:\number#2\endcsname
+ {\tabl_ntb_cell_process{#3}{#4}[#5]{\tabl_ntb_next_level#6\tabl_ntb_prev_level}}}
+
+\def\tabl_ntb_get_txt#1#2%
+ {\csname\??naturaltabletxt\m_tabl_tbl_level:\number#1:\number#2\endcsname}
+
+% to be changed:
+
+\newif\ifsqueezeTBLspan \squeezeTBLspantrue % spans one column cell over multi column par cells
+\newif\ifautosqueezeTBLspan \autosqueezeTBLspantrue % unless explicit widths are given
+\newif\ifautoTBLspread \autoTBLspreadfalse
+\newif\ifautoTBLhsize \autoTBLhsizetrue
+\newif\ifautoTBLrowspan \autoTBLrowspantrue
+\newif\ifautoTBLemptycell \autoTBLemptycelltrue
+\newif\ifautoTBLcheckwidth \autoTBLcheckwidthtrue
+\newif\ifappendTBLsetups \appendTBLsetupstrue
+\newif\ifenableTBLbreak \enableTBLbreakfalse
+\newif\ifmultipleTBLheads \multipleTBLheadsfalse
+\newif\iftightTBLrowspan \tightTBLrowspantrue
+\newif\iftightTBLcolspan \tightTBLcolspanfalse
+
+\newconditional \c_tabl_ntb_trace_widths
+
+\installtextracker
+ {tables.natural.widths}
+ {\settrue \c_tabl_ntb_trace_widths}
+ {\setfalse\c_tabl_ntb_trace_widths}
+
+% so far
+
+\protected\def\tabl_ntb_cell_process#1#2[#3]{}
+
+\permanent\protected\def\bTC#1\eTC{\bTD#1\eTD} \aliased\let\eTC\relax
+\permanent\protected\def\bTX#1\eTX{\bTD#1\eTD} \aliased\let\eTX\relax
+\permanent\protected\def\bTY#1\eTY{\bTR#1\eTR} \aliased\let\eTY\relax
+
+\let\tabl_ntb_parameters_get\setupcurrentnaturaltablelocal
+
+\let\tabl_ntb_setup_step \gobbleoneargument
+\let\tabl_ntb_setup_step_step\gobbleoneargument
+
+\permanent\tolerant\protected\def\setupTABLE[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ \expandafter\gobblethreeoptionals
+ \or
+ \expandafter\tabl_ntb_setup_one
+ \or
+ \expandafter\tabl_ntb_setup_two
+ \or
+ \expandafter\tabl_ntb_setup_three
+ \fi[#1][#2][#3]}
+
+\def\tabl_ntb_setup_one[#1][#2][#3]%
+ {\setupcurrentnaturaltablelocal[#1]}
+
+\def\tabl_ntb_setup_xy[#1][#2][#3]%
+ {\def\tabl_ntb_setup_step##1{\tabl_ntb_parameters_set[#1##1][#3]}%
+ \processcommalist[#2]\tabl_ntb_setup_step}
+
+\def\tabl_ntb_setup_un[#1][#2][#3]%
+ {\def\tabl_ntb_setup_step##1%
+ {\def\tabl_ntb_setup_step_step####1{\tabl_ntb_parameters_set[\c!x##1\c!y####1][#3]}%
+ \processcommalist[#2]\tabl_ntb_setup_step_step}%
+ \processcommalist[#1]\tabl_ntb_setup_step}
+
+\def\tabl_ntb_setup_each[#1][#2][#3]% ignores #3
+ {\tabl_ntb_parameters_set[#1\v!each][#2]}
+
+\def\tabl_ntb_setup_ux[#1][#2][#3]% ignores #3
+ {\def\tabl_ntb_setup_step##1{\tabl_ntb_parameters_set[\c!x##1][#2]}%
+ \processcommalist[#1]\tabl_ntb_setup_step}
+
+\installcorenamespace{naturaltablesetupthree}
+\installcorenamespace{naturaltablesetuptwo}
+
+\def\tabl_ntb_setup_three[#1]{\ifcsname\??naturaltablesetupthree#1\endcsname\expandafter\lastnamedcs\else\expandafter\tabl_ntb_setup_un\fi[#1]}
+\def\tabl_ntb_setup_two [#1]{\ifcsname\??naturaltablesetuptwo #1\endcsname\expandafter\lastnamedcs\else\expandafter\tabl_ntb_setup_ux\fi[#1]}
+
+\setvalue{\??naturaltablesetupthree \v!row}[#1]{\tabl_ntb_setup_xy [\c!y]}
+\setvalue{\??naturaltablesetupthree \v!column}[#1]{\tabl_ntb_setup_xy [\c!x]}
+\setvalue{\??naturaltablesetupthree \v!start}[#1]{\tabl_ntb_setup_xy [\v!start]}
+\setvalue{\??naturaltablesetupthree \v!header}[#1]{\tabl_ntb_setup_xy [\v!header]}
+
+\setvalue{\??naturaltablesetuptwo \v!row}[#1]{\tabl_ntb_setup_each[\c!y]}
+\setvalue{\??naturaltablesetuptwo \v!column}[#1]{\tabl_ntb_setup_each[\c!x]}
+\setvalue{\??naturaltablesetuptwo \v!start}[#1]{\tabl_ntb_setup_each[\v!start]}
+\setvalue{\??naturaltablesetuptwo \v!header}[#1]{\tabl_ntb_setup_each[\v!header]}
+
+\letvalue{\??naturaltablesetupthree\s!unknown}\tabl_ntb_setup_un
+\letvalue{\??naturaltablesetuptwo \s!unknown}\tabl_ntb_setup_ux
+
+\letcsnamecsname\csname\??naturaltablesetupthree r\endcsname\csname\??naturaltablesetupthree \v!row\endcsname
+\letcsnamecsname\csname\??naturaltablesetupthree c\endcsname\csname\??naturaltablesetupthree\v!column\endcsname
+\letcsnamecsname\csname\??naturaltablesetupthree y\endcsname\csname\??naturaltablesetupthree \v!row\endcsname
+\letcsnamecsname\csname\??naturaltablesetupthree x\endcsname\csname\??naturaltablesetupthree\v!column\endcsname
+
+\letcsnamecsname\csname\??naturaltablesetuptwo r\endcsname\csname\??naturaltablesetuptwo \v!row\endcsname
+\letcsnamecsname\csname\??naturaltablesetuptwo c\endcsname\csname\??naturaltablesetuptwo \v!column\endcsname
+\letcsnamecsname\csname\??naturaltablesetuptwo y\endcsname\csname\??naturaltablesetuptwo \v!row\endcsname
+\letcsnamecsname\csname\??naturaltablesetuptwo x\endcsname\csname\??naturaltablesetuptwo \v!column\endcsname
+
+\def\tabl_ntb_parameters_set[#1][#2]%
+ {\ifappendTBLsetups
+ \ifcsname\??naturaltableset\m_tabl_tbl_level:#1\endcsname
+ \enforced\def\tabl_ntb_parameters_get[##1]%
+ {\defcsname\??naturaltableset\m_tabl_tbl_level:#1\endcsname{\tabl_ntb_parameters_get[##1,#2]}}%
+ \csname\??naturaltableset\m_tabl_tbl_level:#1\endcsname
+ \enforced\let\tabl_ntb_parameters_get\setupcurrentnaturaltablelocal
+ \else
+ \defcsname\??naturaltableset\m_tabl_tbl_level:#1\endcsname{\tabl_ntb_parameters_get[#2]}%
+ \fi
+ \else
+ \defcsname\??naturaltableset\m_tabl_tbl_level:#1\endcsname{\tabl_ntb_parameters_get[#2]}%
+ \fi}
+
+% % \setupTABLE [y] [first][background=color,backgroundcolor=blue,frame=off,bottomframe=on,topframe=on,framecolor=white]
+% \setupTABLE [first][first][backgroundcorner=2,corner=10,frame=on]
+% \setupTABLE [last] [first][backgroundcorner=4,corner=12,frame=on]
+%
+% \setupTABLE [row] [each] [background=color,backgroundcolor=blue,frame=on,framecolor=white]
+% \setupTABLE [first][2] [corner=8]
+% \setupTABLE [last] [2] [corner=5]
+% \setupTABLE [first][last] [corner=7]
+% \setupTABLE [last] [last] [corner=6]
+%
+% \startTEXpage
+% \bTABLE[frame=off,align=middle]
+% \bTR \bTD one \eTD \bTD two \eTD \bTD three \eTD \eTR
+% \bTR \bTD first \eTD \bTD second \eTD \bTD third \eTD \eTR
+% \bTR \bTD alpha \eTD \bTD beta \eTD \bTD gamma \eTD \eTR
+% \eTABLE
+% \stopTEXpage
+%
+% \setupTABLE [first] [two][corner=2] % special case
+% \setupTABLE [last] [two][corner=4] % special case
+%
+% % % \setupTABLE [one] [first] ... special case of span
+%
+% \startTEXpage
+% \bTABLE[frame=off,align=middle]
+% \bTR \bTD one \eTD \bTD two \eTD \bTD three \eTD \eTR
+% \bTR \bTD first \eTD \bTD second \eTD \bTD third \eTD \eTR
+% \eTABLE
+% \stopTEXpage
+
+%D By default rowspans are tight but you can change that:
+%D
+%D \startbuffer
+%D \bTABLE
+%D \bTR[height=20pt] \bTH 1. col \eTH \bTH 2. col \eTH \eTR
+%D \bTR[height=20pt] \bTD 1 row in 1. col \eTD \bTD[nr=2] 2 rows in 2. col \eTD \eTR
+%D \bTR[height=20pt] \bTD[nr=2] 2 rows in 1. col\eTD \eTR
+%D \bTR[height=20pt] \bTD[nr=3] 3 rows in 2. col \eTD \eTR
+%D \bTR[height=20pt] \bTD 1 row in 1. col \eTD \eTR
+%D \bTR[height=20pt] \bTD 1 row in 1. col \eTD \eTR
+%D \eTABLE
+%D
+%D \bTABLE
+%D \bTR[height=20pt] \bTH 2. col \eTH \bTH 1. col \eTH \eTR
+%D \bTR[height=20pt] \bTD[nr=2] 2 rows in 2. col \eTD \bTD 1 row in 1. col \eTD \eTR
+%D \bTR[height=20pt] \bTD[nr=2] 2 rows in 1. col\eTD \eTR
+%D \bTR[height=20pt] \bTD[nr=3] 3 rows in 2. col \eTD \eTR
+%D \bTR[height=20pt] \bTD 1 row in 1. col \eTD \eTR
+%D \bTR[height=20pt] \bTD 1 row in 1. col \eTD \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D With \type {\tightTBLrowspanfalse} we get:
+%D
+%D \start \tightTBLrowspanfalse \getbuffer \stop
+
+\let\tabl_ntb_setup_section\relax
+
+\protected\def\tabl_ntb_setup_cell#1#2% cell over col over row
+ {\tabl_ntb_setup_section % already forgotten
+ \edef\m_tabl_ntb_positive_row{\number#1}%
+ \edef\m_tabl_ntb_positive_col{\number#2}%
+ \edef\m_tabl_ntb_negative_row{\the\numexpr-\c_tabl_ntb_maximum_row+#1+\minusone\relax}%
+ \edef\m_tabl_ntb_negative_col{\the\numexpr-\c_tabl_ntb_maximum_col+#2+\minusone\relax}%
+ % saves tokens (no speed gain)
+ \edef\m_tabl_ntb_prefix{\??naturaltableset\m_tabl_tbl_level:}%
+ % each each
+ \begincsname\m_tabl_ntb_prefix\c!x\v!each\c!y\v!each\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!y\v!each\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\v!each\endcsname
+ % odd even
+ \begincsname\m_tabl_ntb_prefix\c!y\v!oddeven\m_tabl_ntb_positive_row\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\v!oddeven\m_tabl_ntb_positive_col\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\v!oddeven\m_tabl_ntb_positive_col\c!y\v!oddeven\m_tabl_ntb_positive_row\endcsname
+ % row/col number combinations
+ \begincsname\m_tabl_ntb_prefix\c!y\m_tabl_ntb_positive_row\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!y\m_tabl_ntb_negative_row\endcsname
+ \naturaltablelocalparameter\c!extras
+ \letnaturaltablelocalparameter\c!extras\relax % new, see x-fo
+ \begincsname\m_tabl_ntb_prefix\c!x\m_tabl_ntb_positive_col\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\m_tabl_ntb_negative_col\endcsname
+ \naturaltablelocalparameter\c!extras
+ \letnaturaltablelocalparameter\c!extras\relax % new, see x-fo
+ % first/last combinations
+ \ifnum\m_tabl_ntb_positive_row=\plusone
+ \begincsname\m_tabl_ntb_prefix\c!y\v!first\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\m_tabl_ntb_positive_col\c!y\v!first\endcsname
+ \fi
+ \ifnum\m_tabl_ntb_positive_col=\plusone
+ \begincsname\m_tabl_ntb_prefix\c!x\v!first\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\v!first\c!y\m_tabl_ntb_positive_row\endcsname
+ \fi
+ \ifnum\m_tabl_ntb_positive_row=\c_tabl_ntb_maximum_row\relax
+ \begincsname\m_tabl_ntb_prefix\c!y\v!last\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\m_tabl_ntb_positive_col\c!y\v!last\endcsname
+ \fi
+ \ifnum\m_tabl_ntb_positive_col=\c_tabl_ntb_maximum_col\relax
+ \begincsname\m_tabl_ntb_prefix\c!x\v!last\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\v!last\c!y\m_tabl_ntb_positive_row\endcsname
+ \fi
+ \ifnum\m_tabl_ntb_positive_row=\c_tabl_ntb_maximum_row\relax \ifnum\m_tabl_ntb_positive_col=\c_tabl_ntb_maximum_col\relax
+ \begincsname\m_tabl_ntb_prefix\c!x\v!last\c!y\v!last\endcsname
+ \fi\fi
+ \ifnum\m_tabl_ntb_positive_row=\plusone \ifnum\m_tabl_ntb_positive_col=\plusone
+ \begincsname\m_tabl_ntb_prefix\c!x\v!first\c!y\v!first\endcsname
+ \fi\fi
+ \ifnum\m_tabl_ntb_positive_row=\plusone \ifnum\m_tabl_ntb_positive_col=\c_tabl_ntb_maximum_col\relax
+ \begincsname\m_tabl_ntb_prefix\c!x\v!last\c!y\v!first\endcsname
+ \fi\fi
+ \ifnum\m_tabl_ntb_positive_row=\c_tabl_ntb_maximum_row\relax \ifnum\m_tabl_ntb_positive_col=\plusone
+ \begincsname\m_tabl_ntb_prefix\c!x\v!first\c!y\v!last\endcsname
+ \fi\fi
+ % special case: two rows and last row : two&first and two&last (round corners)
+ \ifnum\c_tabl_ntb_maximum_row=\plustwo\relax
+ \ifnum\m_tabl_ntb_positive_row=\c_tabl_ntb_maximum_row\relax \ifnum\m_tabl_ntb_positive_col=\plusone
+ \begincsname\m_tabl_ntb_prefix\c!x\v!first\c!y\v!two\endcsname
+ \fi\fi
+ \ifnum\m_tabl_ntb_positive_row=\c_tabl_ntb_maximum_row\relax \ifnum\m_tabl_ntb_positive_col=\c_tabl_ntb_maximum_col\relax
+ \begincsname\m_tabl_ntb_prefix\c!x\v!last\c!y\v!two\endcsname
+ \fi\fi
+ \fi
+ \ifnum\tabl_ntb_get_col\m_tabl_ntb_positive_row\m_tabl_ntb_positive_col=\c_tabl_ntb_maximum_col\relax % top span over whole width
+ \ifnum\m_tabl_ntb_positive_row=\plusone
+ \begincsname\m_tabl_ntb_prefix\c!x\v!one\c!y\v!first\endcsname
+ \fi
+ \ifnum\m_tabl_ntb_positive_row=\c_tabl_ntb_maximum_row\relax
+ \begincsname\m_tabl_ntb_prefix\c!x\v!one\c!y\v!last\endcsname
+ \fi
+ \fi
+ % header things
+ \ifnum#1>\c_tabl_ntb_n_of_hdnx_lines\else
+ \begincsname\m_tabl_ntb_prefix\v!header\v!each\endcsname
+ \begincsname\m_tabl_ntb_prefix\v!header\m_tabl_ntb_positive_col\endcsname
+ \fi
+ % explicit cells
+ \begincsname\m_tabl_ntb_prefix\c!x\m_tabl_ntb_positive_col\c!y\m_tabl_ntb_positive_row\endcsname
+ \begincsname\m_tabl_ntb_prefix\c!x\m_tabl_ntb_negative_col\c!y\m_tabl_ntb_negative_row\endcsname
+ % local
+ \begincsname\m_tabl_ntb_prefix\c!y++\m_tabl_ntb_positive_row\endcsname
+ % done
+ \relax}
+
+% we cannot use +n (checking on number/last/first would slow down too much)
+%
+% \setupTABLE[r] [2][color=red]
+% \setupTABLE[r] [-2][color=red]
+% \setupTABLE[c] [2][color=green]
+% \setupTABLE[c] [-2][color=green]
+% \setupTABLE[4] [4][color=blue]
+% \setupTABLE[-4][-4][color=blue]
+%
+% \bTABLE
+% \dorecurse{10}{\bTR \dorecurse{6}{\bTD xxx \eTD} \eTR}
+% \eTABLE
+
+\let\m_tabl_ntb_before_split\empty
+\let\m_tabl_ntb_after_split \empty
+\let\m_tabl_ntb_same_page \empty
+
+% split + page:
+%
+% \bTABLE[split=yes]
+% \bTR \bTD left \eTD\bTD right \eTD\eTR
+% \bTR[after=\page] \bTD left \eTD\bTD right \eTD\eTR
+% \bTR \bTD left \eTD\bTD right \eTD\eTR
+% \eTABLE
+
+\tolerant\protected\def\tabl_ntb_tr[#1]%
+ {\c_tabl_ntb_running_col\zerocount
+ \c_tabl_ntb_encountered_col\zerocount
+ \advance\c_tabl_ntb_maximum_row\plusone
+ \ifparameter#1\or
+ \expandafter\tabl_ntb_tr_yes
+ \else
+ \expandafter\gobbleoneoptional
+ \fi[#1]}
+
+\def\tabl_ntb_tr_yes[#1]%
+ {\defcsname\??naturaltableset\m_tabl_tbl_level:\c!y++\the\c_tabl_ntb_maximum_row\endcsname{\setupcurrentnaturaltablelocal[#1]}}
+
+\def\m_tabl_ntb_default_nr{\naturaltableparameter\c!nr}
+\def\m_tabl_ntb_default_nc{\naturaltableparameter\c!nc}
+
+\tolerant\protected\def\tabl_ntb_td[#1]%
+ {\advance\c_tabl_ntb_encountered_col\plusone
+ \ifparameter#1\or
+ \expandafter\tabl_ntb_td_yes
+ \else
+ \expandafter\tabl_ntb_td_nop
+ \fi[#1]}
+
+\def\tabl_ntb_td_yes[#1]#2\eTD
+ {\letnaturaltableparameter\c!ny \m_tabl_ntb_default_nr
+ \letnaturaltableparameter\c!nx \m_tabl_ntb_default_nc
+ \letnaturaltableparameter\c!nc \plusone
+ \letnaturaltableparameter\c!nr \plusone
+ \letnaturaltableparameter\c!n \c_tabl_ntb_running_col
+ \letnaturaltableparameter\c!m \empty
+ \letnaturaltableparameter\c!action\empty % not that important
+ \setupcurrentnaturaltable[#1]%
+ %
+ \c_tabl_ntb_nx\naturaltableparameter\c!nx\relax
+ \c_tabl_ntb_ny\naturaltableparameter\c!ny\relax
+ % goto first cell n/m=cellnumber
+ \edef\m_tabl_ntb_n{\naturaltableparameter\c!n}%
+ \edef\m_tabl_ntb_m{\naturaltableparameter\c!m}%
+ %
+ \ifempty\m_tabl_ntb_n
+ \global\advance\c_tabl_ntb_spn\c_tabl_ntb_nx\relax
+ \orelse\ifnum\m_tabl_ntb_n=\c_tabl_ntb_running_col\else
+ \tabl_ntb_td_pass_n{#1}%
+ \fi
+ \ifempty\m_tabl_ntb_m \else
+ \ifnum\m_tabl_ntb_m=\c_tabl_ntb_running_col\else
+ \tabl_ntb_td_pass_m{#1}%
+ \fi
+ \fi
+ \doloop % skip over columns that result from earlier span
+ {\advance\c_tabl_ntb_running_col\plusone
+ \ifcsname\tabl_ntb_tag_pattern\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col\endcsname \else
+ \exitloop
+ \fi}%
+ % fill r*c cells and set span
+ \c_tabl_ntb_nx\naturaltableparameter\c!nx\relax
+ \c_tabl_ntb_ny\naturaltableparameter\c!ny\relax
+ \ifnum\c_tabl_ntb_nx=\plusone
+ \ifnum\c_tabl_ntb_ny=\plusone
+ \ifnum\c_tabl_ntb_running_col>\c_tabl_ntb_maximum_col\relax
+ \c_tabl_ntb_maximum_col\c_tabl_ntb_running_col
+ \fi
+ \else
+ \tabl_ntb_cell_preset
+ \fi
+ \else
+ \tabl_ntb_cell_preset
+ \fi
+ % set values
+ \tabl_ntb_let_tag\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col\c_tabl_ntb_cell
+ \tabl_ntb_set_col\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\the\c_tabl_ntb_nx}%
+ \tabl_ntb_set_row\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\the\c_tabl_ntb_ny}%
+ % the action key will change!
+ \tabl_ntb_set_ref\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\naturaltableparameter\c!action}%
+ % save text
+ \normalexpanded
+ {\tabl_ntb_set_txt_process\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\the\c_tabl_ntb_maximum_row}{\the\c_tabl_ntb_running_col}}%
+ {#1}{#2}%
+ \ifnum\c_tabl_ntb_encountered_col>\c_tabl_ntb_encountered_max
+ \c_tabl_ntb_encountered_max\c_tabl_ntb_encountered_col
+ \fi}
+
+\def\tabl_ntb_td_nop[#1]#2\eTD
+ {\global\advance\c_tabl_ntb_spn\plusone\relax
+ \doloop
+ {\advance\c_tabl_ntb_running_col\plusone
+ \ifcsname\tabl_ntb_tag_pattern\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col\endcsname \else
+ \exitloop
+ \fi}%
+ \c_tabl_ntb_nx\plusone
+ \c_tabl_ntb_ny\plusone
+ \ifnum\c_tabl_ntb_running_col>\c_tabl_ntb_maximum_col\relax
+ \c_tabl_ntb_maximum_col\c_tabl_ntb_running_col
+ \fi
+ \tabl_ntb_let_tag\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col\c_tabl_ntb_cell
+ \tabl_ntb_set_col\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\the\c_tabl_ntb_nx}%
+ \tabl_ntb_set_row\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\the\c_tabl_ntb_ny}%
+ \tabl_ntb_let_ref\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col\empty
+ \normalexpanded
+ {\tabl_ntb_set_txt_process\c_tabl_ntb_maximum_row\c_tabl_ntb_running_col{\the\c_tabl_ntb_maximum_row}{\the\c_tabl_ntb_running_col}}%
+ {#1}{#2}%
+ \ifnum\c_tabl_ntb_encountered_col>\c_tabl_ntb_encountered_max
+ \c_tabl_ntb_encountered_max\c_tabl_ntb_encountered_col
+ \fi}
+
+\def\tabl_ntb_td_pass_n#1%
+ {\scratchcounter\numexpr\m_tabl_ntb_n-\c_tabl_ntb_running_col+\minusone-\c_tabl_ntb_spn\relax
+ \ifnum\scratchcounter>\zerocount
+ \normalexpanded{\tabl_ntb_td[\c!nx=\the\scratchcounter,\c!n=,\c!m=,*sq=\v!no]}\eTD
+ \fi
+ \letnaturaltableparameter\c!ny\m_tabl_ntb_default_nr
+ \letnaturaltableparameter\c!nx\m_tabl_ntb_default_nc
+ \letnaturaltableparameter\c!nc\plusone
+ \letnaturaltableparameter\c!nr\plusone
+ \setupcurrentnaturaltable[#1]%
+ \letnaturaltableparameter\c!n \empty
+ \letnaturaltableparameter\c!m \empty}
+
+\def\tabl_ntb_td_pass_m#1%
+ {\scratchcounter\numexpr\m_tabl_ntb_m-\c_tabl_ntb_running_col+\minusone-\c_tabl_ntb_spn\relax
+ \dorecurse\scratchcounter{\normalexpanded{\tabl_ntb_td[\c!n=,\c!m=]}\eTD}%
+ % can be sped up
+ \letnaturaltableparameter\c!ny\m_tabl_ntb_default_nr
+ \letnaturaltableparameter\c!nx\m_tabl_ntb_default_nc
+ \letnaturaltableparameter\c!nc\plusone
+ \letnaturaltableparameter\c!nr\plusone
+ \setupcurrentnaturaltable[#1]%
+ \letnaturaltableparameter\c!n \empty
+ \letnaturaltableparameter\c!m \empty}
+
+\def\tabl_ntb_cell_preset
+ {\c_tabl_ntb_current_row\c_tabl_ntb_maximum_row
+ \c_tabl_ntb_current_col\c_tabl_ntb_running_col
+ \dorecurse\c_tabl_ntb_ny\tabl_ntb_cell_preset_rows
+ % check max column
+ \advance\c_tabl_ntb_current_col\minusone
+ \ifnum\c_tabl_ntb_current_col>\c_tabl_ntb_maximum_col\relax
+ \c_tabl_ntb_maximum_col\c_tabl_ntb_current_col
+ \fi}
+
+\def\tabl_ntb_cell_preset_rows
+ {\c_tabl_ntb_current_col\c_tabl_ntb_running_col
+ \tabl_ntb_set_col\c_tabl_ntb_current_row\c_tabl_ntb_current_col{\the\c_tabl_ntb_nx}%
+ \ifnum\c_tabl_ntb_nx>\c_tabl_ntb_maximum_row_span\relax
+ \c_tabl_ntb_maximum_row_span\c_tabl_ntb_nx
+ \fi
+ \dorecurse\c_tabl_ntb_nx\tabl_ntb_cell_preset_cells
+ \advance\c_tabl_ntb_current_row\plusone}
+
+\def\tabl_ntb_cell_preset_cells
+ {\tabl_ntb_let_tag\c_tabl_ntb_current_row\c_tabl_ntb_current_col\c_tabl_ntb_none
+ \advance\c_tabl_ntb_current_col\plusone}
+
+%D The usage of n and m:
+%D
+%D \startbuffer
+%D \bTABLE[width=3em]
+%D \bTR\bTD d1 \eTD\bTD[n=2] d2 \eTD\bTD[n=5] d5 \eTD\bTD[n=7] d7 \eTD\eTR
+%D \bTR\bTD f1 \eTD\bTD[n=4] f4 \eTD\bTD[n=5] f5 \eTD\bTD[n=7] f7 \eTD\eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \bTABLE[width=3em]
+%D \bTR\bTD d1 \eTD\bTD[m=2] d2 \eTD\bTD[m=5] d5 \eTD\bTD[m=7] d7 \eTD\eTR
+%D \bTR\bTD f1 \eTD\bTD[m=4] f4 \eTD\bTD[m=5] f5 \eTD\bTD[m=7] f7 \eTD\eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D \startbuffer
+%D \bTABLE[frame=on]
+%D \bTR \bTH[nc=3] One \eTH \bTH[m=4] Four \eTH\eTR
+%D \bTR \bTD a \eTD\bTD b \eTD\bTD c \eTD\bTD d \eTD\eTR
+%D \eTABLE
+%D
+%D \bTABLE[frame=on]
+%D \bTR \bTH[nr=2] One \eTH \bTH[m=3] Three \eTH\eTR
+%D \bTR \bTD[m=3] a \eTD\bTD b \eTD\bTD c \eTD\bTD d \eTD\eTR
+%D \bTR \bTD[m=3] a \eTD\bTD b \eTD\bTD c \eTD\bTD d \eTD\eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\tolerant\def\tabl_ntb_th[#1]#:#2\eTH
+ {\tabl_ntb_td[#1,\c!color=\naturaltablelocalparameter\c!headcolor,\c!style=\naturaltablelocalparameter\c!headstyle,\c!aligncharacter=\v!no]#2\eTD}
+
+\tolerant\def\tabl_ntb_tn[#1]#:#2\eTN
+ {\tabl_ntb_td[#1]\digits#2\relax\eTD}
+
+%D Vit Zyka needed the option to create a distance between columns, so I added
+%D support for individual column distances.
+%D
+%D \startbuffer
+%D % \setupTABLE[c][each][distance=2em]
+%D \setupTABLE[c][1][distance=2em]
+%D \setupTABLE[c][2][distance=3em]
+%D
+%D \bTABLE
+%D \bTR \bTD test \eTD \bTD test \eTD \bTD test \eTD \eTR
+%D \bTR \bTD[nx=2] test \eTD \bTD test \eTD \eTR
+%D \bTR \bTD test \eTD \bTD[nx=2] test \eTD \eTR
+%D \eTABLE
+%D
+%D \bTABLE[option=stretch]
+%D \bTR \bTD test \eTD \bTD test \eTD \bTD test \eTD \eTR
+%D \bTR \bTD[nx=2] test \eTD \bTD test \eTD \eTR
+%D \bTR \bTD test \eTD \bTD[nx=2] test \eTD \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \startlinecorrection \getbuffer \stoplinecorrection
+%D
+%D and he provided patches for the global left and right margin distances as well as
+%D the columndistance (although i changed the names -). Here is his testcase:
+%D
+%D \startbuffer
+%D \framed[offset=overlay]\bgroup
+%D \setupTABLE[column][2][align=left]%
+%D \setupTABLE[column][3][align=right]%
+%D \bTABLE[columndistance=2cm,leftmargindistance=.3cm,rightmargindistance=.5cm]
+%D \bTR \bTH[nc=3] Table head\eTH \eTR
+%D \bTR \bTD[nc=2] AB\eTD \bTD C\eTD \eTR
+%D \bTR \bTD[nc=2,align=left] AB\eTD \bTD C\eTD \eTR
+%D \bTR \bTD[nc=2,align=middle] AB\eTD \bTD C\eTD \eTR
+%D \bTR \bTD A\eTD \bTD B\eTD \bTD C\eTD \eTR
+%D \bTR \bTD Aa\eTD \bTD Bb\eTD \bTD Cccc\eTD \eTR
+%D \bTR \bTD[nc=3,align=middle] ABC\eTD \eTR
+%D \eTABLE
+%D \egroup
+%D \stopbuffer
+%D
+%D \typebuffer \startlinecorrection \getbuffer \stoplinecorrection
+
+% to be done: head <raw> foot, dus state var
+
+\aliased\let\eTABLEhead\relax
+\aliased\let\eTABLEnext\relax
+\aliased\let\eTABLEbody\relax
+\aliased\let\eTABLEfoot\relax
+
+\permanent\tolerant\protected\def\bTABLEhead[#1]#:#2\eTABLEhead{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_head}
+\permanent\tolerant\protected\def\bTABLEnext[#1]#:#2\eTABLEnext{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_next}
+\permanent\tolerant\protected\def\bTABLEbody[#1]#:#2\eTABLEbody{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_body}
+\permanent\tolerant\protected\def\bTABLEfoot[#1]#:#2\eTABLEfoot{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_foot}
+
+\def\tabl_ntb_section[#1]#2% also used in tabl-nte
+ {\protected\def\tabl_ntb_setup_section{\setupcurrentnaturaltablelocal[#1]}%
+ #2%
+ \let\tabl_ntb_setup_section\relax}
+
+\def\tabl_ntb_preset_parameters% each odd|even level / can be sped up but only once per table
+ {\begincsname\??naturaltableset\m_tabl_tbl_level:\v!start\v!each\endcsname
+ \begincsname\??naturaltableset\m_tabl_tbl_level:\v!start\v!oddeven\m_tabl_tbl_level\endcsname
+ \begincsname\??naturaltableset\m_tabl_tbl_level:\v!start\m_tabl_tbl_level\endcsname}
+
+\permanent\tolerant\protected\def\bTABLE[#1]%
+ {\tabl_ntb_table_push
+ % box not here
+ \bgroup
+ \pushpostponednodedata
+ \t_tabl_ntb_head\emptytoks
+ \t_tabl_ntb_next\emptytoks
+ \t_tabl_ntb_body\emptytoks
+ \t_tabl_ntb_foot\emptytoks
+ \ifhmode\kern\zeropoint\fi % blocks \removeunwantedspaces: check this on icare handelingsschema
+ \resetcharacteralign % new
+ \setupcurrentnaturaltablelocal[\c!align={\v!right,\v!broad,\v!high},#1]%
+ %
+ \d_tabl_ntb_leftmargindistance \naturaltablelocalparameter\c!leftmargindistance\relax
+ \d_tabl_ntb_rightmargindistance\naturaltablelocalparameter\c!rightmargindistance\relax
+ \d_tabl_ntb_columndistance \naturaltablelocalparameter\c!columndistance\relax
+ \d_tabl_ntb_maxwidth \naturaltablelocalparameter\c!maxwidth\relax
+ %
+ \usesetupsparameter\naturaltablelocalparameter
+ \doifelse{\naturaltablelocalparameter\c!textwidth}\v!local
+ {\hsize\availablehsize}
+ {\hsize\naturaltablelocalparameter\c!textwidth}%
+ \enableTBLbreakfalse
+ \multipleTBLheadsfalse
+ \autoTBLspreadfalse
+ \tightTBLcolspanfalse
+ \processaction
+ [\naturaltablelocalparameter\c!split]
+ [ \v!yes=>\enableTBLbreaktrue,
+ \v!repeat=>\enableTBLbreaktrue\multipleTBLheadstrue,
+ \v!auto=>\ifinsidesplitfloat\enableTBLbreaktrue\fi]
+ \processaction
+ [\naturaltablelocalparameter\c!header]
+ [\v!repeat=>\multipleTBLheadstrue]%
+ \tabl_ntb_preset_parameters
+ \processallactionsinset
+ [\naturaltablelocalparameter\c!option]
+ [\v!stretch=>\autoTBLspreadtrue,%
+ \v!tight=>\tightTBLcolspantrue]%
+ \linewidth\naturaltablelocalparameter\c!rulethickness % needs to be frozen
+ \dontcomplain
+ \c_tabl_ntb_running_col \zerocount
+ \c_tabl_ntb_maximum_col \zerocount
+ \c_tabl_ntb_maximum_row \zerocount
+ \c_tabl_ntb_maximum_row_span\plusone
+ \let\currentTABLErow \tabl_ntb_current_row
+ \let\currentTABLEcolumn\tabl_ntb_current_column
+ \let\nofTABLErows \tabl_ntb_n_of_rows
+ \let\nofTABLEcolumns \tabl_ntb_n_of_columns
+ \enforced\let\bTR\tabl_ntb_bTR
+ \enforced\let\bTD\tabl_ntb_bTD
+ \enforced\let\bTH\tabl_ntb_bTH
+ \enforced\let\bTN\tabl_ntb_bTN}
+
+\def\tabl_ntb_current_row {\m_tabl_ntb_positive_row}
+\def\tabl_ntb_current_column{\m_tabl_ntb_positive_col}
+\def\tabl_ntb_n_of_rows {\number\c_tabl_ntb_maximum_row}
+\def\tabl_ntb_n_of_columns {\number\c_tabl_ntb_maximum_col}
+
+\mutable\let\currentTABLErow \!!zerocount
+\mutable\let\currentTABLEcolumn\!!zerocount
+\mutable\let\nofTABLErows \!!zerocount
+\mutable\let\nofTABLEcolumns \!!zerocount
+
+% there is no gain in a \doifelsenextoptionalcs variant
+
+% todo with tolerant:
+
+\permanent\tolerant\protected\def\tabl_ntb_bTR{\tabl_ntb_tr} % also used in tabl-nte
+\permanent\tolerant\protected\def\tabl_ntb_bTD{\tabl_ntb_td} % also used in tabl-nte
+\permanent\tolerant\protected\def\tabl_ntb_bTH{\tabl_ntb_th} % also used in tabl-nte
+\permanent\tolerant\protected\def\tabl_ntb_bTN{\tabl_ntb_tn} % also used in tabl-nte
+
+% permits \expanded{\bTD ... \eTD}
+
+\permanent\let\bTR\relax \permanent\protected\def\eTR{\ignorespaces} % handy in case we use a macro to generate rows
+\permanent\let\bTD\relax \permanent\protected\def\eTD{\ignorespaces}
+\permanent\let\bTH\relax \permanent\protected\def\eTH{\ignorespaces}
+\permanent\let\bTN\relax \permanent\protected\def\eTN{\ignorespaces}
+
+\permanent\protected\def\eTABLE % beware, we need to get rid of spurious spaces when in hmode
+ {% tricky and dirty order -)
+ \setfalse\c_tabl_ntb_okay
+ % head
+ \doifelsesometoks\t_tabl_ntb_head % slow, better a flag
+ {\c_tabl_ntb_head\numexpr\c_tabl_ntb_maximum_row+\plusone\relax
+ \the\t_tabl_ntb_head
+ \c_tabl_ntb_n_of_head_lines\c_tabl_ntb_maximum_row\relax
+ \doifelsesometoks\t_tabl_ntb_next
+ {\the\t_tabl_ntb_next
+ \c_tabl_ntb_n_of_next_lines\numexpr\c_tabl_ntb_maximum_row-\c_tabl_ntb_n_of_head_lines\relax}%
+ {\c_tabl_ntb_n_of_next_lines\zerocount}% was 1
+ \c_tabl_ntb_n_of_hdnx_lines\c_tabl_ntb_maximum_row}
+ {\c_tabl_ntb_head\zerocount
+ \c_tabl_ntb_n_of_head_lines\zerocount % was 1
+ \c_tabl_ntb_n_of_next_lines\zerocount
+ \c_tabl_ntb_n_of_hdnx_lines\zerocount}%
+ % body
+ \c_tabl_ntb_body\numexpr\c_tabl_ntb_maximum_row+\plusone\relax
+ \the\t_tabl_ntb_body
+ % foot
+ \doifelsesometoks\t_tabl_ntb_foot
+ {\c_tabl_ntb_foot\numexpr\c_tabl_ntb_maximum_row+\plusone\relax
+ \the\t_tabl_ntb_foot}%
+ {\c_tabl_ntb_foot\zerocount}%
+ % done
+ \removeunwantedspaces % only if hmode
+ % finish cells
+ \tabl_ntb_loop_one
+ % to be sure
+ \tabl_ntb_loop_two
+ % check and do
+ \ifcase\c_tabl_ntb_maximum_col\else
+ \startTBLprocessing
+ \tabl_ntb_table_start
+ \dorecurse\c_tabl_ntb_maximum_row
+ {\tabl_ntb_row_start
+ \c_tabl_ntb_current_row\recurselevel\relax
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\c_tabl_ntb_current_col\recurselevel\relax
+ \normalexpanded{\tabl_ntb_cell{\the\c_tabl_ntb_current_row}{\the\c_tabl_ntb_current_col}}}%
+ \tabl_ntb_row_stop}%
+ \removeunwantedspaces % only if hmode
+ \tabl_ntb_table_stop
+ \stopTBLprocessing
+ % wrong ! ! ! better to have an auto-offset-overlay
+ % \ifnum\m_tabl_tbl_level>1
+ % \vskip-\strutdp
+ % \fi
+ \fi
+ % tracing
+ % \iftrue
+ % \blank \tttf
+ % \dorecurse\c_tabl_ntb_maximum_row
+ % {\c_tabl_ntb_current_row\recurselevel\relax
+ % \dorecurse\c_tabl_ntb_maximum_col
+ % {\c_tabl_ntb_current_col\recurselevel\relax
+ % [r=\the\c_tabl_ntb_current_row,c=\the\c_tabl_ntb_current_col,h=\the\dimexpr\tabl_ntb_get_ht\c_tabl_ntb_current_row\c_tabl_ntb_current_col,w=\the\dimexpr\tabl_ntb_get_wd\c_tabl_ntb_current_row\c_tabl_ntb_current_col]}%
+ % \par}%
+ % \blank
+ % \fi
+ \poppostponednodedata
+ \egroup
+ \tabl_ntb_table_pop}
+
+\def\tabl_ntb_loop_one
+ {\dorecurse\c_tabl_ntb_maximum_row{\tabl_ntb_loop_one_rows}}
+
+\def\tabl_ntb_loop_one_rows
+ {\c_tabl_ntb_current_row\recurselevel\relax
+ \dorecurse\c_tabl_ntb_maximum_col\tabl_ntb_loop_one_cells}
+
+\def\tabl_ntb_loop_one_cells
+ {\c_tabl_ntb_current_col\recurselevel\relax
+ \ifcsname\tabl_ntb_tag_pattern\c_tabl_ntb_current_row\c_tabl_ntb_current_col\endcsname \else
+ \tabl_ntb_loop_one_cells_indeed
+ \fi}
+
+\def\tabl_ntb_loop_one_cells_indeed
+ {\c_tabl_ntb_current_col_two\c_tabl_ntb_current_col
+ \c_tabl_ntb_current_row_two\c_tabl_ntb_current_row
+ \c_tabl_ntb_current_row_one\c_tabl_ntb_current_row
+ \doloop
+ {\c_tabl_ntb_current_col_one\c_tabl_ntb_current_col
+ \doloop
+ {\ifcsname\tabl_ntb_tag_pattern\c_tabl_ntb_current_row_one\c_tabl_ntb_current_col_one\endcsname
+ \exitloop
+ \else
+ \advance\c_tabl_ntb_current_col_one\plusone
+ \ifnum\c_tabl_ntb_current_col_one>\c_tabl_ntb_maximum_col\relax
+ \exitloop
+ \fi
+ \fi}%
+ \ifcsname\tabl_ntb_tag_pattern\c_tabl_ntb_current_row_one\c_tabl_ntb_current_col_one\endcsname
+ \exitloop
+ \else
+ \c_tabl_ntb_current_row_two\c_tabl_ntb_current_row_one
+ \c_tabl_ntb_current_col_two\c_tabl_ntb_current_col_one
+ \advance\c_tabl_ntb_current_row_one\plusone
+ \ifnum\c_tabl_ntb_current_row_one>\c_tabl_ntb_maximum_row
+ \exitloop
+ \fi
+ \fi}%
+ \ifnum\c_tabl_ntb_current_row_two>\c_tabl_ntb_maximum_row\c_tabl_ntb_current_row_two\c_tabl_ntb_maximum_row\fi
+ \ifnum\c_tabl_ntb_current_col_two>\c_tabl_ntb_maximum_col\c_tabl_ntb_current_col_two\c_tabl_ntb_maximum_col\fi
+ \c_tabl_ntb_current_row_two\numexpr\c_tabl_ntb_current_row_two-\c_tabl_ntb_current_row+\plusone\relax
+ \c_tabl_ntb_current_col_two\numexpr\c_tabl_ntb_current_col_two-\c_tabl_ntb_current_col+\plusone\relax
+ \c_tabl_ntb_current_row_one\c_tabl_ntb_current_row
+ \dorecurse\c_tabl_ntb_current_row_two
+ {\c_tabl_ntb_current_col_one\c_tabl_ntb_current_col
+ \tabl_ntb_set_col\c_tabl_ntb_current_row_one\c_tabl_ntb_current_col_one{\the\c_tabl_ntb_current_col_two}%
+ \dorecurse\c_tabl_ntb_current_col_two
+ {\tabl_ntb_let_tag\c_tabl_ntb_current_row_one\c_tabl_ntb_current_col_one\c_tabl_ntb_none
+ \advance\c_tabl_ntb_current_col_one\plusone}%
+ \advance\c_tabl_ntb_current_row_one\plusone}%
+ \tabl_ntb_let_tag\c_tabl_ntb_current_row\c_tabl_ntb_current_col\c_tabl_ntb_cell
+ \tabl_ntb_set_col\c_tabl_ntb_current_row\c_tabl_ntb_current_col{\the\c_tabl_ntb_current_col_two}%
+ \tabl_ntb_set_row\c_tabl_ntb_current_row\c_tabl_ntb_current_col{\the\c_tabl_ntb_current_row_two}%
+ \ifautoTBLemptycell
+ \normalexpanded
+ {\tabl_ntb_set_txt_process\c_tabl_ntb_current_row\c_tabl_ntb_current_col{\the\c_tabl_ntb_current_row}{\the\c_tabl_ntb_current_col}}%
+ {\c!option=\v!tight}{\strut\kern\scaledpoint}% the kern forces the tight
+ \fi}
+
+\def\tabl_ntb_loop_two
+ {\dorecurse\c_tabl_ntb_maximum_row\tabl_ntb_loop_two_rows}
+
+\def\tabl_ntb_loop_two_rows
+ {\c_tabl_ntb_current_row\recurselevel\relax
+ \dorecurse\c_tabl_ntb_maximum_col\tabl_ntb_loop_two_cells}
+
+\def\tabl_ntb_loop_two_cells
+ {\c_tabl_ntb_current_col\recurselevel\relax
+ \ifcsname\tabl_ntb_row_pattern\c_tabl_ntb_current_row\c_tabl_ntb_current_col\endcsname
+ \scratchcounter\numexpr\c_tabl_ntb_maximum_row-\c_tabl_ntb_current_row+\plusone\relax
+ \ifnum\tabl_ntb_get_row\c_tabl_ntb_current_row\c_tabl_ntb_current_col>\scratchcounter
+ \tabl_ntb_set_row\c_tabl_ntb_current_row\c_tabl_ntb_current_col{\the\scratchcounter}%
+ \fi
+ \fi
+ \tabl_ntb_let_ht\c_tabl_ntb_current_row\c_tabl_ntb_current_col\zeropoint
+ %tabl_ntb_let_wd\c_tabl_ntb_current_row\c_tabl_ntb_current_col\zeropoint
+ \ifcsname\tabl_ntb_col_pattern\c_tabl_ntb_current_row\c_tabl_ntb_current_col\endcsname \else
+ \tabl_ntb_let_col\c_tabl_ntb_current_row\c_tabl_ntb_current_col\zerocount
+ \fi
+ \ifcsname\tabl_ntb_tag_pattern\c_tabl_ntb_current_row\c_tabl_ntb_current_col\endcsname \else
+ \tabl_ntb_let_tag\c_tabl_ntb_current_row\c_tabl_ntb_current_col\c_tabl_ntb_none
+ \fi}
+
+\mutable\let\startTBLprocessing\relax % public
+\mutable\let\stopTBLprocessing \relax % public
+
+\newcount\c_tabl_prelocated_rows % \prelocateTBLrows{1000} may speed up large tables
+
+% \def\tabl_ntb_row_start{\t_tabl_ntb_row\emptytoks}
+% \def\tabl_ntb_row_stop {\normalexpanded{\t_tabl_ntb{\the\t_tabl_ntb\noexpand\tabl_ntb_row_align_start\the\t_tabl_ntb_row\tabl_ntb_row_align_stop}}}
+
+\def\tabl_ntb_row_start
+ {\t_tabl_ntb_row\emptytoks}
+
+\def\tabl_ntb_row_stop
+ {\ifenableTBLbreak
+ \tabl_ntb_row_stop_split
+ \else
+ \tabl_ntb_row_stop_boxed
+ \fi}
+
+\def\tabl_ntb_row_stop_boxed
+ {% \noindent % no, else double leftskip in narrower
+ \etoksapp\t_tabl_ntb
+ {% no need for init
+ \tabl_ntb_row_align_start
+ \the\t_tabl_ntb_row
+ \tabl_ntb_row_align_stop}}
+
+\def\tabl_ntb_row_stop_split
+ {\ifcsname\??naturaltableset\m_tabl_tbl_level:\c!y++\the\c_tabl_ntb_current_row\endcsname
+ \tabl_ntb_row_stop_split_yes
+ \else
+ \tabl_ntb_row_stop_split_nop
+ \fi}
+
+\def\tabl_ntb_row_stop_split_nop
+ {\etoksapp\t_tabl_ntb
+ {\tabl_ntb_row_align_reset
+ \tabl_ntb_row_align_start
+ \the\t_tabl_ntb_row
+ \tabl_ntb_row_align_stop}}
+
+\def\tabl_ntb_row_stop_split_yes
+ {\begingroup
+ \csname\??naturaltableset\m_tabl_tbl_level:\c!y++\the\c_tabl_ntb_current_row\endcsname
+ \xdef\m_tabl_ntb_before_split{\naturaltablelocalparameter\c!before}% to be checked
+ \xdef\m_tabl_ntb_after_split {\naturaltablelocalparameter\c!after}% to be checked
+ \xdef\m_tabl_ntb_same_page {\naturaltablelocalparameter\c!samepage}%
+ \endgroup
+ \etoksapp\t_tabl_ntb
+ {\tabl_ntb_row_align_set{\m_tabl_ntb_before_split}{\m_tabl_ntb_after_split}{\m_tabl_ntb_same_page}%
+ \tabl_ntb_row_align_start
+ \the\t_tabl_ntb_row
+ \tabl_ntb_row_align_stop}}
+
+\protected\def\tabl_ntb_row_align_set#1#2#3%
+ {\xdef\m_tabl_ntb_before_split{#1}%
+ \xdef\m_tabl_ntb_after_split {#2}%
+ \xdef\m_tabl_ntb_same_page {#3}}
+
+\protected\def\tabl_ntb_row_align_reset
+ {\glet\m_tabl_ntb_before_split\empty
+ \glet\m_tabl_ntb_after_split \empty
+ \glet\m_tabl_ntb_same_page \empty}
+
+\def\tabl_ntb_prelocate_error
+ {\writestatus\m!TABLE{fatal error: use \string\prelocateTBLrows\space to increase table memory (now: \the\c_tabl_prelocated_rows)}}
+
+% \prelocateTBLrows{1000} % may speed up large tables
+
+\permanent\protected\def\prelocateTBLrows#1% we start at zero so we have one to much, better play safe anyway
+ {\dostepwiserecurse\c_tabl_prelocated_rows{#1}\plusone
+ {\expandafter\newtoks\csname\??naturaltabletok\recurselevel\endcsname}%
+ \def\tabl_ntb_row_start
+ {\ifnum\c_tabl_ntb_row<\c_tabl_prelocated_rows\relax
+ \tabl_ntb_prelocate_okay
+ \else
+ \tabl_ntb_prelocate_error
+ \fi}%
+ \def\tabl_ntb_row_stop
+ {\etoksapp\t_tabl_ntb
+ {\tabl_ntb_row_align_start
+ \the\csname\??naturaltabletok\the\c_tabl_ntb_row\endcsname
+ \tabl_ntb_row_align_stop}}%
+ \global\c_tabl_prelocated_rows#1\relax}
+
+\def\tabl_ntb_prelocate_okay
+ {\expandafter\let\expandafter\t_tabl_ntb_row\csname\??naturaltabletok\the\c_tabl_ntb_row\endcsname\t_tabl_ntb_row\emptytoks}
+
+%D We use aligments to handle the empty (skipped) columns, so that we don't have to
+%D (re|)|calculate these.
+
+\let\m_tabl_ntb_saved_row\!!zerocount
+\let\m_tabl_ntb_saved_col\!!zerocount
+
+\def\tabl_ntb_start_tagged
+ {\scratchcounter\numexpr\c_tabl_ntb_row+\plusone\relax
+ \ifnum\scratchcounter=\c_tabl_ntb_head
+ \ifconditional\c_tabl_ntb_okay
+ \dostoptagged
+ \else
+ \settrue\c_tabl_ntb_okay
+ \fi
+ \dostarttagged\t!tablehead\empty
+ \orelse\ifnum\scratchcounter=\c_tabl_ntb_body
+ \ifconditional\c_tabl_ntb_okay
+ \dostoptagged
+ \else
+ \settrue\c_tabl_ntb_okay
+ \fi
+ \dostarttagged\t!tablebody\empty
+ \orelse\ifnum\scratchcounter=\c_tabl_ntb_foot
+ \ifconditional\c_tabl_ntb_okay
+ \dostoptagged
+ \else
+ \settrue\c_tabl_ntb_okay
+ \fi
+ \dostarttagged\t!tablefoot\empty
+ \fi
+ \dostarttagged\t!tablerow\empty}
+
+\def\tabl_ntb_stop_tagged
+ {\dostoptagged
+ \ifconditional\c_tabl_ntb_okay
+ \scratchcounter\numexpr\c_tabl_ntb_row+\plustwo\relax
+ \ifnum\scratchcounter=\c_tabl_ntb_body
+ \dostoptagged
+ \setfalse\c_tabl_ntb_okay
+ \orelse\ifnum\scratchcounter=\c_tabl_ntb_foot
+ \dostoptagged
+ \setfalse\c_tabl_ntb_okay
+ \orelse\ifnum\scratchcounter>\c_tabl_ntb_maximum_row
+ \dostoptagged
+ \setfalse\c_tabl_ntb_okay
+ \fi
+ \fi}
+
+\protected\def\tabl_ntb_row_align_start
+ {\global\advance\c_tabl_ntb_row\plusone
+ \global\c_tabl_ntb_col\plusone
+ \global\c_tabl_ntb_spn\zerocount
+ \tabl_ntb_row_align_start_inject
+ \ifconditional\c_strc_tags_enabled
+ \tabl_ntb_start_tagged
+ \fi
+ \hbox\bgroup
+ \kern\dimexpr\d_tabl_ntb_leftmargindistance\relax}
+
+\protected\def\tabl_ntb_row_align_stop
+ {\kern\dimexpr\d_tabl_ntb_rightmargindistance-\d_tabl_ntb_columndistance\relax
+ \egroup
+ \ifconditional\c_strc_tags_enabled
+ \tabl_ntb_stop_tagged
+ \fi
+ \tabl_ntb_row_align_stop_inject}
+
+\protected\def\tabl_ntb_before_page
+ {\ifx\m_tabl_ntb_same_page\v!before
+ % \blank[\v!samepage,\v!strong]%
+ \unpenalty
+ \nobreak
+ \orelse\ifx\m_tabl_ntb_same_page\v!both
+ % \blank[\v!samepage,\v!strong]%
+ \unpenalty
+ \nobreak
+ \fi}
+
+\protected\def\tabl_ntb_after_page
+ {\ifnum\c_tabl_ntb_row>\c_tabl_ntb_n_of_head_lines
+ \ifnum\tabl_ntb_get_nob\c_tabl_ntb_row=\zerocount
+ \unpenalty
+ \ifx\m_tabl_ntb_same_page\v!after
+ % \blank[\v!samepage,\v!strong]%
+ \nobreak
+ \orelse\ifx\m_tabl_ntb_same_page\v!both
+ % \blank[\v!samepage,\v!strong]%
+ \nobreak
+ \else
+ % \blank[\v!preference,\v!weak]%
+ \allowbreak
+ \fi
+ \fi
+ \else
+ % \blank[\v!preference,\v!weak]%
+ \allowbreak % else no proper head split off
+ \fi}
+
+\protected\def\tabl_ntb_inbetween
+ {\scratchcounter\numexpr\c_tabl_ntb_row+\plusone\relax
+ \ifnum\scratchcounter>\c_tabl_ntb_n_of_hdnx_lines\relax
+ \ifnum\scratchcounter<\c_tabl_ntb_maximum_row\relax
+ \edef\p_spaceinbetween{\naturaltablelocalparameter\c!spaceinbetween}%
+ \ifempty\p_spaceinbetween\else
+ \blank[\p_spaceinbetween]%
+ \fi
+ \fi
+ \fi}
+
+\protected\def\tabl_ntb_row_align_start_inject
+ {\bgroup % protect local vars
+ \m_tabl_ntb_before_split
+ \egroup
+ \ifenableTBLbreak
+ \tabl_ntb_before_page
+ \fi}
+
+\protected\def\tabl_ntb_row_align_stop_inject
+ {\par
+ \nointerlineskip
+ \ifenableTBLbreak
+ \tabl_ntb_after_page
+ \fi
+ \bgroup % protect local vars
+ \m_tabl_ntb_after_split
+ \egroup
+ \bgroup % protect local vars
+ \tabl_ntb_inbetween
+ \egroup}
+
+\def\tabl_ntb_flush_content
+ {\the\everyTABLEpass
+ \global\c_tabl_ntb_spn\zerocount
+ \global\c_tabl_ntb_col\zerocount
+ \global\c_tabl_ntb_row\zerocount
+ \global\advance\c_tabl_ntb_row\minusone
+ \dostarttaggedchained\t!table\empty\??naturaltable
+ %\registerparoptions % (*) triggers max hsize
+ \the\t_tabl_ntb
+ \dostoptagged}
+
+\protected\def\tabl_ntb_span#1%
+ {\hskip\tabl_ntb_get_dis\c_tabl_ntb_col
+ \dorecurse{#1}
+ {\hskip\tabl_ntb_get_wid\c_tabl_ntb_col\relax
+ \global\advance\c_tabl_ntb_col\plusone}}
+
+\protected\def\tabl_ntb_skip#1%
+ {\global\advance\c_tabl_ntb_col#1\relax}
+
+\protected\def\tabl_ntb_plus
+ {\global\advance\c_tabl_ntb_col\plusone
+ \kern\d_tabl_ntb_columndistance}
+
+\setvalue{\??naturaltablecell\the\c_tabl_ntb_none}#1#2%
+ {\scratchcounter\tabl_ntb_get_col{#1}{#2}\relax
+ \ifnum\scratchcounter>\zerocount
+ \etoksapp\t_tabl_ntb_row
+ {\tabl_ntb_span{\the\scratchcounter}}%
+ \fi}
+
+\setvalue{\??naturaltablecell\the\c_tabl_ntb_cell}#1#2%
+ {\toksapp\t_tabl_ntb_row{\tabl_ntb_pass #1 #2 }% space delimited -> less tokens
+ \scratchcounter\tabl_ntb_get_col{#1}{#2}\relax
+ \ifnum\scratchcounter>\zerocount
+ \etoksapp\t_tabl_ntb_row
+ {\ifnum\scratchcounter=\plusone
+ \tabl_ntb_plus
+ \else
+ \tabl_ntb_skip{\the\scratchcounter}%
+ \fi}%
+ \fi}
+
+\protected\def\tabl_ntb_cell#1#2%
+ {\csname\??naturaltablecell\the\tabl_ntb_get_tag{#1}{#2}\endcsname{#1}{#2}}
+
+\protected\def\tabl_ntb_table_start
+ {\global\c_tabl_ntb_spn\zerocount
+ \global\c_tabl_ntb_row\zerocount
+ \global\c_tabl_ntb_col\zerocount
+ \c_tabl_tbl_pass\zerocount
+ \t_tabl_ntb\emptytoks}
+
+\def\tabl_ntb_pass_one#1 #2 %
+ {\tabl_ntb_get_txt{#1}{#2}}%
+
+\def\tabl_ntb_pass_two#1 #2 % meer in cellD
+ {\d_tabl_ntb_width\zeropoint
+ \scratchcounter\c_tabl_ntb_col
+ \scratchcounterone\tabl_ntb_get_col{#1}{#2}\relax
+ \ifcase\scratchcounterone\or
+ \advance\d_tabl_ntb_width\dimexpr
+ \tabl_ntb_get_wid\scratchcounter
+ \relax
+ \advance\scratchcounter\plusone
+ \else
+ \dorecurse\scratchcounterone
+ {\advance\d_tabl_ntb_width\dimexpr
+ \tabl_ntb_get_wid\scratchcounter
+ \ifnum\recurselevel<\scratchcounterone
+ +\d_tabl_ntb_columndistance
+ +\tabl_ntb_get_dis\scratchcounter
+ \fi
+ \relax
+ \advance\scratchcounter\plusone}%
+ \fi
+ \setbox\scratchbox\hbox{\tabl_ntb_get_txt{#1}{#2}}%
+ \tabl_ntb_set_ht{#1}{#2}{\the\ht\scratchbox}%
+ %tabl_ntb_set_wd{#1}{#2}{\the\wd\scratchbox}%
+ \ifdim\ht\scratchbox>\tabl_ntb_get_hei{#1}\relax
+ \tabl_ntb_set_hei{#1}{\the\ht\scratchbox}%
+ \fi}%
+
+\def\tabl_ntb_pass_three#1 #2 %
+ {% height
+ \dostarttagged\t!tablecell\empty
+ \scratchcounterone\tabl_ntb_get_col{#1}{#2}\relax
+ \scratchcountertwo\tabl_ntb_get_row{#1}{#2}\relax
+ \scratchheight\tabl_ntb_get_ht {#1}{#2}\relax
+ \tablecellcolumns\scratchcounterone % used later so don't adapt these
+ \tablecellrows \scratchcountertwo % used later so don't adapt these
+ \d_tabl_ntb_height\zeropoint
+ \ifnum\scratchcounterone=\c_tabl_ntb_maximum_col\relax
+ % case: nc=maxcolumns
+ \else
+ \scratchcounter#1\relax
+ \dorecurse\scratchcountertwo
+ {\advance\d_tabl_ntb_height\tabl_ntb_get_hei\scratchcounter
+ \advance\scratchcounter\plusone}%
+ \ifdim\d_tabl_ntb_height<\scratchheight\relax
+ \d_tabl_ntb_height\scratchheight
+ \fi
+ \fi
+ % width
+ \d_tabl_ntb_width\zeropoint
+ \scratchcounter\c_tabl_ntb_col
+ \ifcase\scratchcounterone\or
+ \advance\d_tabl_ntb_width\dimexpr
+ \tabl_ntb_get_wid\scratchcounter
+ \relax
+ \advance\scratchcounter\plusone
+ \else
+ \dorecurse\scratchcounterone
+ {\advance\d_tabl_ntb_width\dimexpr
+ \tabl_ntb_get_wid\scratchcounter
+ \ifnum\recurselevel<\scratchcounterone
+ +\d_tabl_ntb_columndistance
+ +\tabl_ntb_get_dis\scratchcounter
+ \fi
+ \relax
+ \advance\scratchcounter\plusone}%
+ \fi
+ % cell
+ \setbox\scratchbox\hbox attr \taggedattribute \c_attr_tagged \bgroup
+ \dotagTABLEsignal % maybe we need to add some packaging in this case
+ \tabl_ntb_get_txt{#1}{#2}%
+ \egroup
+ \ifnum\scratchcounterone=\c_tabl_ntb_maximum_col\relax
+ % case: nc=maxcolumns
+ \else
+ \scratchdimen\tabl_ntb_get_hei{#1}%
+ \setbox\scratchbox\hpack
+ {\lower\ht\scratchbox\hpack{\raise\scratchdimen\box\scratchbox}}%
+ \ht\scratchbox\scratchdimen
+ \fi
+ \dp\scratchbox\zeropoint
+ \edef\scratchmacro{\tabl_ntb_get_ref{#1}{#2}}%
+ \ifempty\scratchmacro
+ \box\scratchbox
+ \else
+ \normalexpanded{\directgotobox{\box\scratchbox}[\scratchmacro]}% to be checked
+ \fi
+ \dostoptagged} % right spot
+
+\def\tabl_ntb_cell_finalize
+ {\ifx\localwidth\v!fit
+ % nothing
+ \orelse\ifx\localwidth\v!broad
+ % nothing
+ \orelse\ifempty\localwidth
+ % nothing (safeguard)
+ \else
+ \tabl_ntb_cell_finalize_indeed
+ \fi}
+
+\def\tabl_ntb_cell_finalize_indeed
+ {\scratchdimen\tabl_ntb_get_aut\c_tabl_ntb_col\relax
+ \ifdim\localwidth>\scratchdimen
+ \tabl_ntb_set_aut\c_tabl_ntb_col{\the\dimexpr\localwidth}%
+ \fi}
+
+\let\tabl_ntb_preroll\relax
+
+\def\tabl_ntb_table_get_max_width
+ {\scratchdimen\wd\scratchbox\relax}
+
+% enable dper 2018-02-22
+
+\def\tabl_ntb_table_get_max_width_step
+ {\advance\scratchdimen\tabl_ntb_get_wid\fastloopindex
+ \advance\scratchdimen\tabl_ntb_get_dis\fastloopindex}
+
+\def\tabl_ntb_table_get_max_width
+ {\scratchdimen\zeropoint
+ \dofastloopcs\c_tabl_ntb_maximum_col\tabl_ntb_table_get_max_width_step
+ \ifdim\scratchdimen<\wd\scratchbox\relax
+ \scratchdimen\wd\scratchbox\relax
+ \fi}
+
+\newdimen\d_tabl_ntb_saved_hsize
+
+\let\tabl_ntb_pass\relax
+
+\def\tabl_ntb_table_stop
+ {\forgetall % new, here see narrower-004.tex
+ %\setbox\scratchbox\hbox
+ % {\letnaturaltablelocalparameter\c!frame\v!off
+ % \letnaturaltablelocalparameter\c!background\empty
+ % \letnaturaltablelocalparameter\c!align\v!no
+ % \inheritednaturaltablelocalframed{\strut}}%
+ %\edef\minimalcellheight{\the\ht\scratchbox}% not used
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\tabl_ntb_let_aut\recurselevel\zeropoint
+ % new
+ \c_tabl_ntb_current_col_one\recurselevel\relax
+ \dorecurse\c_tabl_ntb_maximum_row
+ {%tabl_ntb_let_wd\recurselevel\c_tabl_ntb_current_col_one\zeropoint
+ \tabl_ntb_let_ht\recurselevel\c_tabl_ntb_current_col_one\zeropoint}%
+ % till here
+ \tabl_ntb_let_tal\recurselevel\zerocount
+ \tabl_ntb_let_wid\recurselevel\zeropoint
+ \tabl_ntb_let_dis\recurselevel\zeropoint}%
+ \dorecurse\c_tabl_ntb_maximum_row
+ {\tabl_ntb_let_hei\recurselevel\maxdimen}%
+ \tabl_ntb_let_gal\zerocount
+ \tabl_ntb_preroll\relax
+ \c_tabl_tbl_pass\plusone
+ \let\tabl_ntb_pass\tabl_ntb_pass_one
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_a
+ \setbox\scratchbox\vbox{\settrialtypesetting \tabl_ntb_flush_content}%
+ \ifcase\tabl_ntb_get_gal\or
+ % \c_tabl_tbl_pass\plusone
+ % \let\tabl_ntb_pass\tabl_ntb_pass_one
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_a_extra
+ \setbox\scratchbox\vbox{\settrialtypesetting \tabl_ntb_flush_content}%
+ \fi
+ \tabl_ntb_let_dis\c_tabl_ntb_maximum_col\zeropoint
+ %
+ \tabl_ntb_table_get_max_width % \scratchdimen\scratchbox
+ %
+ \ifautoTBLspread
+ % experimental, stretch non fixed cells to \hsize
+ \tabl_ntb_check_widths_one % trial run
+ \tabl_ntb_check_widths_two % real run
+ \tabl_ntb_stretch_widths
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_b
+ \setbox\scratchbox\vbox{\settrialtypesetting \tabl_ntb_flush_content}%
+ % \orelse\ifdim\wd\scratchbox>\hsize
+ \orelse\ifdim\scratchdimen>\hsize
+ \ifautoTBLhsize
+ \tabl_ntb_check_widths_one % trial run
+ \tabl_ntb_check_widths_two % real run
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_b
+ \setbox\scratchbox\vbox{\settrialtypesetting \tabl_ntb_flush_content}%
+ \fi
+ \orelse\ifautoTBLrowspan
+ \ifnum\c_tabl_ntb_maximum_row_span>\plusone % max ?
+ % added jan 2002 because nx=* did no longer work
+ \ifnum\c_tabl_ntb_encountered_max<\c_tabl_ntb_maximum_col
+ % added jun 2014 because someone had less columns than nx .. sigh / see *nx*
+ \writestatus\m!TABLE{missing\space\number\numexpr\c_tabl_ntb_maximum_col-\c_tabl_ntb_encountered_max\relax\space column(s), guessing widths}%
+ \fi
+ \d_tabl_ntb_saved_hsize\hsize
+ % \hsize\wd\scratchbox\relax % new per 17/04/2006
+ \hsize\scratchdimen\relax % new per 17/04/2006
+ \tabl_ntb_check_widths_one % trial run
+ \tabl_ntb_check_widths_two % real run
+ \hsize\d_tabl_ntb_saved_hsize
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_c
+ \setbox\scratchbox\vbox{\settrialtypesetting \tabl_ntb_flush_content}%
+ \fi
+ \fi
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_d
+ \c_tabl_tbl_pass\plustwo
+ \let\tabl_ntb_pass\tabl_ntb_pass_two
+ \setbox\scratchbox\vbox{\settrialtypesetting \tabl_ntb_flush_content}%
+ \tabl_ntb_check_heights_one
+ \tabl_ntb_check_heights_two
+ \let\tabl_ntb_cell_process\tabl_ntb_cell_process_e
+ \c_tabl_tbl_pass\plusthree
+ \let\tabl_ntb_pass\tabl_ntb_pass_three
+ \ifnum\m_tabl_tbl_level>\plusone
+ \tabl_ntb_split_nop
+ \orelse\ifenableTBLbreak
+ \tabl_ntb_split_yes
+ \else
+ \tabl_ntb_split_nop
+ \fi}
+
+\def\tabl_ntb_stretch_widths % more variants, e.g. a max to \dimend
+ {\ifcase\c_tabl_ntb_maximum_col\else % else division by zero
+ \scratchdimenfour\zeropoint
+ \scratchdimenfive\dimexpr
+ \hsize
+ -\d_tabl_ntb_leftmargindistance
+ -\d_tabl_ntb_rightmargindistance
+ +\d_tabl_ntb_columndistance
+ \relax
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\advance\scratchdimenfour\dimexpr
+ \tabl_ntb_get_wid\recurselevel
+ \relax
+ \advance\scratchdimenfive\dimexpr
+ -\tabl_ntb_get_dis\recurselevel
+ -\d_tabl_ntb_columndistance
+ \relax}%
+ \relax
+ % distribute width (stretch)
+ \ifdim\scratchdimenfour<\scratchdimenfive
+ \advance\scratchdimenfour-\scratchdimenfive
+ \divide\scratchdimenfour\c_tabl_ntb_maximum_col
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\tabl_ntb_set_wid\recurselevel{\the\dimexpr\tabl_ntb_get_wid\recurselevel-\scratchdimenfour\relax}}%
+ \fi
+ \fi}
+
+\def\tabl_ntb_split_nop
+ {\setbox\b_tabl_ntb_final\vbox{\tabl_ntb_flush_content}%
+ \postprocessTABLEbox\b_tabl_ntb_final
+ \beforeTABLEbox
+ % packaging prevents max hsized box
+ % \hbox{\registerparoptions\box\b_tabl_ntb_final}% (*) better here
+ % better :
+ \ifinsidefloat
+ % no \dontleavehmode else too wide, otherwise we get a \hsized box
+ \else
+ \registerparoptions % (*) better here (also does a \dontleavehmode)
+ \ifhmode\else\dontleavehmode\fi
+ \fi
+ \box\b_tabl_ntb_final
+ \afterTABLEbox}
+
+\def\tabl_ntb_split_yes
+ {\ifinsidesplitfloat
+ \donetrue
+ \orelse\ifinsidefloat
+ \donefalse
+ \else
+ \donetrue
+ \fi
+ \ifdone
+ \expandafter\tabl_ntb_split_box
+ \else
+ \expandafter\tabl_ntb_split_nop
+ \fi}
+
+\newbox\TABLEsplitbox % public, don't change
+
+\mutable\let\extratblsplitheight\zeropoint % additional space taken by before/afterTABLEsplitbox
+
+\def\tabl_ntb_split_box
+ {\resettsplit
+ \c_split_minimum_free_lines\plustwo
+ \d_split_minimum_free_space\dimexpr\extratblsplitheight+\naturaltablelocalparameter\c!splitoffset\relax
+ \t_split_before_result{\beforeTABLEsplitbox}%
+ \t_split_after_result{\afterTABLEsplitbox}%
+ \t_split_after{\m_tabl_ntb_after_split}%
+ \t_split_before{\m_tabl_ntb_before_split}% not used (yet)
+ \setbox\b_split_content\vbox{\tabl_ntb_flush_content}%
+ \ifmultipleTBLheads
+ \dorecurse\c_tabl_ntb_n_of_head_lines
+ {\setbox\scratchbox\vsplit\b_split_content to \lineheight
+ \setbox\b_split_head\vbox{\unvcopy\b_split_head\unvcopy\scratchbox}}% \vpack ?
+ \dorecurse\c_tabl_ntb_n_of_next_lines
+ {\setbox\scratchbox\vsplit\b_split_content to \lineheight
+ \setbox\b_split_next\vbox{\unvcopy\b_split_next\unvcopy\scratchbox}}% \vpack ?
+ \fi
+ \edef\p_spaceinbetween{\naturaltablelocalparameter\c!spaceinbetween}%
+ \ifempty\p_spaceinbetween\else
+ \blank[\p_spaceinbetween]%
+ \fi
+ \def\postprocesstsplit{\postprocessTABLEsplitbox{\box\b_split_result}}%
+ \handletsplit}
+
+% ! ! ! ! TODO: naast \postprocessTABLEsplitbox ook evt \postprocessTABLEbox voor niet split
+
+\mutable\let\postprocessTABLEsplitbox\gobbleoneargument % maybe just a parameter
+\mutable\let\postprocessTABLEbox \gobbleoneargument % maybe just a parameter
+
+\mutable\let\beforeTABLEsplitbox\relax % maybe just a parameter
+\mutable\let\afterTABLEsplitbox \relax % maybe just a parameter
+\mutable\let\beforeTABLEbox \relax % maybe just a parameter
+\mutable\let\afterTABLEbox \relax % maybe just a parameter
+
+\def\tabl_ntb_check_widths_one{\tabl_ntb_check_widths_indeed\zerocount} % 0 = trial run
+\def\tabl_ntb_check_widths_two{\tabl_ntb_check_widths_indeed\plusone } % 1 = real run
+
+\def\tabl_ntb_check_widths_indeed#1%
+ {\ifconditional\c_tabl_ntb_trace_widths\tabl_ntb_show_widths B#1\fi
+ \scratchcounterone\zerocount
+ \scratchdimenone\dimexpr
+ \hsize
+ -\d_tabl_ntb_leftmargindistance
+ -\d_tabl_ntb_rightmargindistance
+ -\d_tabl_ntb_columndistance
+ \relax
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\scratchdimen\tabl_ntb_get_aut\recurselevel\relax
+ \advance\scratchdimenone\dimexpr
+ -\tabl_ntb_get_dis\recurselevel
+ -\d_tabl_ntb_columndistance
+ \relax
+ \ifdim\scratchdimen>\zeropoint\relax
+ \advance\scratchdimenone -\scratchdimen
+ \else
+ \scratchdimen\tabl_ntb_get_wid\recurselevel\relax
+ \ifdim\scratchdimen>\d_tabl_ntb_maxwidth\relax
+ \ifcase#1\else\tabl_ntb_let_wid\recurselevel\zeropoint\fi
+ \advance\scratchcounterone \plusone
+ \orelse\ifdim\scratchdimen>\zeropoint\relax
+ \advance\scratchdimenone -\scratchdimen
+ \else
+ % eigenlijk moet dit alleen als de kolom wordt overspannen door een
+ % vorige, maw extra dubbele loop en status var
+ \ifnum\c_tabl_ntb_encountered_max=\c_tabl_ntb_maximum_col % *nx* bah
+ \advance\scratchcounterone \plusone % setting maxwidth to a large value also works
+ \fi
+ \fi
+ \fi}%
+ \ifconditional\c_tabl_ntb_trace_widths\tabl_ntb_show_widths M#1\fi
+ \ifcase\scratchcounterone \else \divide\scratchdimenone \scratchcounterone \fi
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\scratchdimen\tabl_ntb_get_wid\recurselevel\relax
+ \ifcase#1\relax
+ \ifdim\scratchdimen<\scratchdimenone % take natural width
+ \tabl_ntb_set_aut\recurselevel{\the\scratchdimen}%
+ \fi
+ \else
+ \ifzeropt\scratchdimen % auto set width
+ \tabl_ntb_set_wid\recurselevel{\the\scratchdimenone}%
+ \fi
+ \fi}%
+ \ifconditional\c_tabl_ntb_trace_widths\tabl_ntb_show_widths E#1\fi}
+
+% todo: use scratchcounters, not !! ones
+
+\def\tabl_ntb_check_heights_one_indeed
+ {\scratchcountertwo\tabl_ntb_get_row\c_tabl_ntb_current_row_three\c_tabl_ntb_current_col_three\relax
+ % check row span
+ \ifnum\scratchcountertwo>\plusone
+ % current height in row
+ \dimen0=\tabl_ntb_get_ht\c_tabl_ntb_current_row_three\c_tabl_ntb_current_col_three
+ % find nearest height in row
+ \dimen2=\zeropoint
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\ifnum\recurselevel=\c_tabl_ntb_current_col_three\else
+ \ifcsname\tabl_ntb_row_pattern\c_tabl_ntb_current_row_three\recurselevel\endcsname
+ \scratchcounterthree\tabl_ntb_get_row\c_tabl_ntb_current_row_three\recurselevel\relax
+ \ifnum\scratchcounterthree=\plusone
+ \dimen4=\tabl_ntb_get_ht\c_tabl_ntb_current_row_three\recurselevel\relax
+ \ifdim\dimen2<\dimen4
+ \dimen2=\dimen4
+ \fi
+ \fi
+ \fi
+ \fi}%
+ \c_tabl_ntb_current_row_four\c_tabl_ntb_current_row_three
+ % calculate cummulative height
+ \dimen4=\dimen2
+ \scratchcounterthree\c_tabl_ntb_current_row_three
+ \advance\scratchcounterthree\minusone
+ \dorecurse\scratchcountertwo
+ {\ifnum\c_tabl_ntb_current_row_four=\c_tabl_ntb_current_row_three\else
+ \advance\dimen4 \tabl_ntb_get_hei\c_tabl_ntb_current_row_four
+ \fi
+ \ifnum\recurselevel=\scratchcountertwo\else
+ \tabl_ntb_set_nob\scratchcounterthree
+ \advance\scratchcounterthree\plusone
+ \fi
+ \advance\c_tabl_ntb_current_row_four\plusone}%
+ % distribute overshoot equally
+ \ifdim\dimen2>\zeropoint % new: test on natural-003
+ \ifdim\dimen4<\dimen0
+ \advance\dimen0 -\dimen4
+ \divide\dimen0 \scratchcountertwo
+ \c_tabl_ntb_current_row_four\c_tabl_ntb_current_row_three
+ \tabl_ntb_set_hei\c_tabl_ntb_current_row_three{\the\dimen2}%
+ \dorecurse\scratchcountertwo
+ {\dorecurse\c_tabl_ntb_maximum_col
+ {\ifnum\recurselevel=\c_tabl_ntb_current_col_three\else
+ \scratchdimen\dimexpr\tabl_ntb_get_ht\c_tabl_ntb_current_row_four\recurselevel+\dimen0\relax
+ \tabl_ntb_set_ht\c_tabl_ntb_current_row_four\recurselevel{\the\scratchdimen}%
+ \ifdim\tabl_ntb_get_hei\c_tabl_ntb_current_row_four<\scratchdimen
+ \tabl_ntb_set_hei\c_tabl_ntb_current_row_four{\the\scratchdimen}%
+ \fi
+ \fi}%
+ \advance\c_tabl_ntb_current_row_four\plusone}%
+ \orelse\ifdim\dimen4>\dimen0
+ \iftightTBLrowspan
+ \tabl_ntb_set_hei\c_tabl_ntb_current_row_three{\the\dimen2}%
+ \fi
+ \fi
+ \fi
+ \fi}
+
+\def\tabl_ntb_check_heights_one
+ {\dorecurse\c_tabl_ntb_maximum_row
+ {\c_tabl_ntb_current_row_three\recurselevel\relax
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\c_tabl_ntb_current_col_three\recurselevel\relax
+ \ifcsname\tabl_ntb_row_pattern\c_tabl_ntb_current_row_three\c_tabl_ntb_current_col_three\endcsname
+ \tabl_ntb_check_heights_one_indeed
+ \fi}}}
+
+\def\tabl_ntb_check_heights_two
+ {}
+
+\def\tabl_ntb_show_widths#1#2%
+ {\begingroup
+ \scratchdimen\zeropoint
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\advance\scratchdimen\tabl_ntb_get_wid\recurselevel\relax}%
+ \writestatus\m!TABLE{#1 \ifcase#2trial\else real\fi: hsize: \the\hsize, total: \the\scratchdimen}%
+ \dorecurse\c_tabl_ntb_maximum_col
+ {\writestatus\m!TABLE{\space\space\recurselevel: \the\dimexpr\tabl_ntb_get_wid\recurselevel}}%
+ \endgroup}
+
+\def\tabl_ntb_char_align % called often
+ {\edef\p_characteralign{\naturaltablelocalparameter\c!aligncharacter}%
+ \ifx\p_characteralign\v!yes
+ \ifcase\c_tabl_tbl_pass\or
+ \tabl_ntb_let_tal\currentTABLEcolumn\plusone
+ \tabl_ntb_let_gal\plusone
+ \fi
+ \expandafter\tabl_ntb_char_align_indeed
+ \else
+ \expandafter\gobbletwoarguments
+ \fi}
+
+\def\tabl_ntb_char_align_indeed#1#2% row column
+ {\ifcase\c_tabl_tbl_pass \or
+ \setcharacteralign{#2}{\naturaltablelocalparameter\c!alignmentcharacter}% we could store the character in tal
+ \fi
+ \typo_charalign_adapt_font
+ \signalcharacteralign{#2}{#1}}
+
+\protected\def\tabl_ntb_cell_process_a_extra#1#2%
+ {\ifcase\tabl_ntb_get_tal{#2}\relax
+ \expandafter\tabl_ntb_cell_process_x
+ \else
+ \expandafter\tabl_ntb_cell_process_a
+ \fi{#1}{#2}}
+
+\protected\def\tabl_ntb_cell_process_x#1#2[#3]#4%
+ {}
+
+% problem: when span doesn't break we can have a span that is the sum of cells but
+% still to small .. chicken egg problem ... for that we should also have a smallest
+% width run
+%
+% nilling the background makes a run upto 25% faster
+
+\def\tabl_ntb_cell_process_a_check_span_one
+ {\ifautosqueezeTBLspan
+ \edef\p_width{\naturaltablelocalparameter\c!width}%
+ \ifcsname\??naturaltablesqueeze\p_width\endcsname\lastnamedcs\else\donefalse\fi
+ \else
+ \donetrue
+ \fi
+ \ifdone % brr, 0
+ \ifnum\scratchcounter>\plusone
+ \tabl_ntb_set_spn\c_tabl_ntb_col
+ \fi
+ \fi}
+
+\let\tabl_ntb_cell_process_a_check_span_two_yes\relax
+
+\def\tabl_ntb_cell_process_a_check_span_two_nop
+ {\ifdim\tabl_ntb_get_wid\c_tabl_ntb_col<\wd\scratchbox
+ \tabl_ntb_set_wid\c_tabl_ntb_col{\the\wd\scratchbox}%
+ \fi}
+
+\protected\def\tabl_ntb_cell_process_a#1#2[#3]#4% grouping added ! ! !
+ {\bgroup
+ \letnaturaltablelocalparameter\c!option\empty
+ \tabl_ntb_setup_cell{#1}{#2}%
+ \setupcurrentnaturaltablelocal[#3]%
+ \letnaturaltablelocalparameter\c!background\empty
+ \letnaturaltablelocalparameter\c!frame\v!off
+ \scratchcounter\tabl_ntb_get_col{#1}{#2}\relax
+ \setbox\scratchbox\hbox
+ {\scratchdimen\naturaltablelocalparameter\c!distance\relax
+ \ifdim\scratchdimen>\tabl_ntb_get_dis{#2}\relax
+ \tabl_ntb_set_dis{#2}{\the\scratchdimen}%
+ \fi
+ \anch_backgrounds_text_level_start
+ \inheritednaturaltablelocalframed{\tabl_ntb_cell_start\tabl_ntb_char_align{#1}{#2}#4\tabl_ntb_cell_stop\tabl_ntb_cell_finalize}%
+ \anch_backgrounds_text_level_stop
+ \ifcase\c_anch_backgrounds_text_count\else
+ \tabl_ntb_let_bck{#1}{#2}\c_anch_backgrounds_text_state
+ \fi
+ }%
+ \scratchdimen\tabl_ntb_get_wid\c_tabl_ntb_col\relax
+ \ifdim\wd\scratchbox>\scratchdimen
+ \ifsqueezeTBLspan
+ \tabl_ntb_cell_process_a_check_span_one
+ \fi
+ \tabl_ntb_spn_doifelse\c_tabl_ntb_col
+ \tabl_ntb_cell_process_a_check_span_two_yes
+ \tabl_ntb_cell_process_a_check_span_two_nop
+ \fi
+ \scratchcounter\numexpr\c_tabl_ntb_row+\plusone\relax
+ \scratchdimen\tabl_ntb_get_hei\scratchcounter\relax
+ \ifdim\ht\scratchbox<\scratchdimen
+ \tabl_ntb_set_hei\scratchcounter{\the\ht\scratchbox}% auto set
+ \fi
+ \tabl_ntb_set_ht{#1}{#2}{\the\ht\scratchbox}%
+ %tabl_ntb_set_wd{#1}{#2}{\the\wd\scratchbox}%
+ \ifautoTBLcheckwidth
+ \ifdim\wd\scratchbox<.75\hsize % fuzzy guess
+ \ifdim\ht\scratchbox>2\openlineheight % honor width since this
+ \scratchdimen\tabl_ntb_get_aut\c_tabl_ntb_col\relax % can be a figure or so
+ \ifzeropt\scratchdimen
+ % side effect: when width is set to 0pt,
+ % we can force a span that fits the sum of spans widths
+ \tabl_ntb_set_aut\c_tabl_ntb_col{\the\scratchdimen}%
+ \orelse\ifdim\wd\scratchbox>\scratchdimen
+ % unless span
+ \tabl_ntb_set_aut\c_tabl_ntb_col{\the\wd\scratchbox}%
+ % to be translated
+ \writestatus\m!TABLE{no auto width in (\number#1,\number#2)\space\the\wd\scratchbox/\the\hsize}%
+ \fi
+ \fi
+ \fi
+ \fi
+ \setbox\scratchboxone\emptyhbox
+ \wd\scratchboxone\wd\scratchbox
+ \ht\scratchboxone\ht\scratchbox
+ \dp\scratchboxone\dp\scratchbox
+ \box\scratchboxone
+ \egroup}
+
+\protected\def\tabl_ntb_cell_process_b_c#1#2#3[#4]#5%
+ {\setbox\scratchbox\hbox
+ {\tabl_ntb_setup_cell{#2}{#3}%
+ \setupcurrentnaturaltablelocal[#4,#1]%
+ \letnaturaltablelocalparameter\c!background\empty
+ \letnaturaltablelocalparameter\c!frame\v!off
+ \inheritednaturaltablelocalframed{\tabl_ntb_cell_start#5\tabl_ntb_cell_stop}}%
+ \setbox2\emptyhbox % todo: \scratchboxtwo
+ \wd2\wd\scratchbox
+ \ht2\ht\scratchbox
+ \dp2\dp\scratchbox
+ \ifautoTBLrowspan
+ \scratchcounter\numexpr\c_tabl_ntb_row+\plusone\relax
+ \ifcsname\tabl_ntb_row_pattern\scratchcounter\c_tabl_ntb_col\endcsname
+ \scratchdimen\tabl_ntb_get_hei\scratchcounter\relax
+ \ifnum\tabl_ntb_get_row\scratchcounter\c_tabl_ntb_col>\plusone
+ \ifdim\ht\scratchbox>\scratchdimen
+ \ht2\dimexpr-\scratchdimen-\ht\scratchbox\relax
+ \fi
+ \fi
+ \fi
+ \fi
+ \box2 }
+
+\protected\def\tabl_ntb_cell_process_b#1#2[#3]#4%
+ {\scratchdimen\tabl_ntb_get_aut\c_tabl_ntb_col\relax
+ \ifdim\scratchdimen>\zeropoint\relax
+ \else
+ \scratchdimen\tabl_ntb_get_wid\c_tabl_ntb_col\relax
+ \ifdim\scratchdimen>\zeropoint\relax
+ \ifnum\tabl_ntb_get_col{#1}{#2}=\c_tabl_ntb_maximum_col\relax
+ \scratchdimen\hsize
+ \fi
+ \fi
+ \fi
+ \normalexpanded{\tabl_ntb_cell_process_b_c{\ifdim\scratchdimen>\zeropoint \c!width=\the\scratchdimen\fi}}%
+ {#1}{#2}[#3]{\tabl_ntb_char_align{#1}{#2}#4}}
+
+\protected\def\tabl_ntb_cell_process_c
+ {\tabl_ntb_cell_process_b_c{}}
+
+\protected\def\tabl_ntb_cell_process_d#1#2[#3]#4%
+ {\tabl_ntb_setup_cell{#1}{#2}%
+ \bgroup
+ \setupcurrentnaturaltablelocal[#3]%
+ \letnaturaltablelocalparameter\c!background\empty
+ \letnaturaltablelocalparameter\c!frame\v!off
+ \setnaturaltablelocalparameter\c!width{\d_tabl_ntb_width}%
+ \inheritednaturaltablelocalframed{\tabl_ntb_cell_start\tabl_ntb_char_align{#1}{#2}#4\tabl_ntb_cell_stop}%
+ \egroup}
+
+\protected\def\tabl_ntb_cell_process_e#1#2[#3]#4%
+ {\tabl_ntb_setup_cell{#1}{#2}%
+ \setupcurrentnaturaltablelocal[#3]% to get the color right, the way we
+ \color % handle color here prevents interference due to whatsit nodes
+ [\naturaltablelocalparameter\c!color] % as well as permits local colors to take precedence
+ {\letnaturaltablelocalparameter\c!color\empty
+ \setnaturaltablelocalparameter\c!width{\d_tabl_ntb_width}%
+ \ifzeropt\d_tabl_ntb_height % case: nc=maxcolumns
+ \else
+ \setnaturaltablelocalparameter\c!height{\d_tabl_ntb_height}%
+ \fi
+ \ifcase\c_anch_backgrounds_text_count\else
+ \edef\p_region{\naturaltablelocalparameter\c!region}%
+ \ifempty\p_region\ifnum\tabl_ntb_get_bck{#1}{#2}>\zerocount
+ \letnaturaltablelocalparameter\c!region\v!yes
+ \fi\fi
+ \fi
+ \inheritednaturaltablelocalframed{\tabl_ntb_cell_start\tabl_ntb_char_align{#1}{#2}#4\tabl_ntb_cell_stop}}%
+ \hskip\tabl_ntb_get_dis{#2}}
+
+\newtoks\everyresetTABLEyes
+\newtoks\everyresetTABLEnop
+
+\appendtoks
+ \setupTABLE [%
+ %
+ % framed defaults
+ %
+ \c!width=\v!fit,%
+ \c!height=\v!fit,%
+ \c!lines=,%
+ \c!offset=.25\exheight,%
+ \c!empty=\v!no,%
+ \c!frame=\v!on,%
+ \c!topframe=,%
+ \c!bottomframe=,%
+ \c!leftframe=,%
+ \c!rightframe=,%
+ \c!radius=.5\bodyfontsize,%
+ \c!rulethickness=\linewidth,%
+ \c!corner=\v!rectangular,%
+ \c!depth=\zeropoint,%
+ \c!foregroundcolor=,%
+ \c!foregroundstyle=,%
+ \c!background=,%
+ \c!backgroundcolor=,%
+ \c!backgroundoffset=\v!frame,%
+ \c!framecolor=,%
+ \c!frameoffset=.5\linewidth,%
+ % \c!backgroundcorner=\framedparameter\c!corner,%
+ % \c!backgrounddepth=\framedparameter\c!depth,%
+ % \c!backgroundradius=\framedparameter\c!radius,%
+ % \c!framecorner=\framedparameter\c!corner,%
+ % \c!framedepth=\framedparameter\c!depth,%
+ % \c!frameradius=\framedparameter\c!radius,%
+ \c!component=,%
+ \c!region=,%
+ \c!align=,%
+ \c!bottom=\vss,%
+ \c!top=,%
+ \c!strut=\v!yes,%
+ \c!autostrut=\v!no,%
+ \c!location=\v!normal,%
+ \c!orientation=,%
+ \c!autowidth=\v!yes,%
+ \c!setups=,%
+ \c!loffset=\zeropoint,%
+ \c!roffset=\zeropoint,%
+ \c!toffset=\zeropoint,%
+ \c!boffset=\zeropoint,%
+ %
+ % table specific
+ %
+ \c!aligncharacter=\v!no,%
+ \c!alignmentcharacter={,},%
+ \c!color=,%
+ \c!columndistance=\zeropoint,% each column (whole table)
+ \c!distance=\zeropoint,% individual column
+ \c!headcolor=,%
+ \c!header=,%
+ \c!headstyle=\v!bold,%
+ \c!left=,%
+ \c!leftmargindistance=\zeropoint,% whole table
+ \c!maxwidth=8\emwidth,%
+ \c!option=,% \v!stretch
+ \c!right=,%
+ \c!rightmargindistance=\zeropoint,% whole table
+ \c!spaceinbetween=,%
+ \c!split=\v!auto,%
+ \c!splitoffset=\zeropoint,%
+ \c!style=,%
+ \c!textwidth=\v!local,% was \hsize
+ ]%
+\to \everyresetTABLEyes
+
+\appendtoks
+ \setupTABLE [%
+ \c!width=\v!fit,%
+ \c!height=\v!fit%
+ ]%
+\to \everyresetTABLEnop
+
+\the\everyresetTABLEyes
+
+% \bgroup
+% \setupTABLE[column][1][aligncharacter=yes, alignmentcharacter={,}]
+% \bTABLE
+% \bTR \bTD 1,2 \eTD \bTD 2 \eTD \eTR
+% \bTR \bTD 11,2 \eTD \bTD
+% {\setupTABLE[column][1][aligncharacter=yes, alignmentcharacter={,}]
+% \bTABLE
+% \bTR \bTD 1,2 \eTD \bTD 2 \eTD \eTR
+% \bTR \bTD 11,22 \eTD \bTD 2 \eTD \eTR
+% \bTR \bTD 11,2 \eTD \bTD 2 \eTD \eTR \eTABLE} \eTD \eTR
+% \bTR \bTD 11,22 \eTD \bTD 2 \eTD \eTR
+% \eTABLE
+% \egroup
+
+\newconditional\resetTABLEmode \settrue\resetTABLEmode
+
+\def\tabl_ntb_parameters_reset % we can use setters instead
+ {\ifnum\m_tabl_tbl_level>\plusone % in ieder geval
+ \ifconditional\resetTABLEmode
+ \the\everyresetTABLEyes
+ \else
+ \the\everyresetTABLEnop
+ \fi
+ \fi}
+
+% new (for Olivier Turlier)
+%
+% \defineTABLEsetup [xx] [foregroundcolor=red]
+%
+% \bTABLE
+% \bTR \bTD oeps \eTD \bTD oeps \eTD \eTR
+% \bTR \bTDs[xx] oeps \eTDs \bTD oeps \eTD \eTR
+% \bTRs[xx] \bTD oeps \eTD \bTD oeps \eTD \eTRs
+% \eTABLE
+
+\installcorenamespace{naturaltablesetup}
+
+\permanent\tolerant\protected\def\defineTABLEsetup[#1]#*[#2]%
+ {\ifarguments\else\defcsname\??naturaltablesetup#1\endcsname{#2}\fi}
+
+\permanent\let\eTDs\relax
+\permanent\let\eTRs\relax
+
+\permanent\protected\def\bTDs[#1]#2\eTDs
+ {\normalexpanded{\bTD[\begincsname\??naturaltablesetup#1\endcsname]}#2\eTD}
+
+\permanent\protected\def\bTRs[#1]#2\eTRs
+ {\normalexpanded{\bTR[\begincsname\??naturaltablesetup#1\endcsname]}#2\eTR}
+
+\protect \endinput
+
+% todo: mode: first|next (of niets)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-nte.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-nte.mkxl
new file mode 100644
index 00000000000..e79e2a00417
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-nte.mkxl
@@ -0,0 +1,107 @@
+%D \module
+%D [ file=core-nte,
+%D version=2009.03.08,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Natural Tables Extensions,
+%D author=Hans Hagen \& Wolfgang Schuster,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Module / Natural Tables Extensions}
+
+\unprotect
+
+%D This module is suggested by Wolfgang Schuster who also prototyped
+%D it and came up with the rationale:
+%D
+%D This module provides an easy way to use natural in a similiar
+%D way as the older table module (based on the \TABLE\ macros) and
+%D the newer tabulate module.
+%D
+%D You can see the advantage in the following table, once created
+%D with the new macros and once with the normal macros provided
+%D with the natural table module.
+%D
+%D Let us start with the original macros:
+%D
+%D \startbuffer
+%D \bTABLE
+%D \bTR
+%D \bTD Text 1 \eTD
+%D \bTD Text 2 \eTD
+%D \eTR
+%D \bTR
+%D \bTD Text 3 \eTD
+%D \bTD Text 4 \eTD
+%D \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Watch how the new macros use less code:
+%D
+%D \startbuffer
+%D \startTABLE
+%D \NC Text 1 \NC Text 2 \NC\NR
+%D \NC Text 3 \NC Text 4 \NC\NR
+%D \stopTABLE
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D The actual code differs from the prototype that it does not need
+%D to collect whole rows and parse them but looks ahead instead.
+
+\newconditional\c_tabl_nte_in_nc
+
+\permanent\tolerant\protected\def\startTABLE[#1]%
+ {\bgroup
+ \bTABLE[#1]%
+ \enforced\let\NC\tabl_nte_start_nc
+ \enforced\let\NR\tabl_nte_start_nr
+ \enforced\let\bTR\relax
+ \enforced\let\bTD\relax
+ \enforced\let\bTH\relax
+ \enforced\let\bTN\relax}
+
+\permanent\protected\def\stopTABLE
+ {\eTABLE
+ \egroup}
+
+\permanent\protected\def\tabl_nte_start_nr
+ {\eTR
+ \setfalse\c_tabl_nte_in_nc}
+
+\permanent\protected\def\tabl_nte_start_nc
+ {\futurelet\nexttoken\tabl_nte_start_nc_indeed}
+
+\def\tabl_nte_start_nc_indeed
+ {\ifx\nexttoken\tabl_nte_start_nr \else
+ \expandafter\tabl_nte_start_nc_finish
+ \fi}
+
+\def\tabl_nte_start_nc_finish#1\NC
+ {\ifconditional\c_tabl_nte_in_nc \else
+ \settrue\c_tabl_nte_in_nc
+ \tabl_ntb_bTR[]%
+ \fi
+ \tabl_ntb_bTD#1\eTD\NC}
+
+%D The related structure commands are also available:
+
+\permanent\let\stopTABLEhead\relax
+\permanent\let\stopTABLEnext\relax
+\permanent\let\stopTABLEbody\relax
+\permanent\let\stopTABLEfoot\relax
+
+\permanent\tolerant\protected\def\startTABLEhead[#1]#:#2\stopTABLEhead{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_head}
+\permanent\tolerant\protected\def\startTABLEnext[#1]#:#2\stopTABLEnext{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_next}
+\permanent\tolerant\protected\def\startTABLEbody[#1]#:#2\stopTABLEbody{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_body}
+\permanent\tolerant\protected\def\startTABLEfoot[#1]#:#2\stopTABLEfoot{\appendtoks\tabl_ntb_section[#1]{#2}\to\t_tabl_ntb_foot}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-tab.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-tab.mkxl
new file mode 100644
index 00000000000..547ff93ff5d
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-tab.mkxl
@@ -0,0 +1,2267 @@
+%D \module
+%D [ file=core-tab,
+%D version=1997.10.10,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=\TABLE\ Embedding,
+%D author=Hans Hagen with copied and patched code from MJ Wichura,
+%D date=\currentdate]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / TaBlE Embedding}
+
+% Todo: just for the fun of it ... use lmtx features.
+
+% Todo: a bitmore namespace protection ... although we want to keep some of the
+% original flavour.
+%
+% In \MKIV\ the old table macros are sort of obsolete. The color extensions have
+% been removed and some code is stripped. For practical reasons the \TABLE\ macros
+% that are used are embedded in this file.
+%
+% The following code is based on TABLE 1.0 by Michael J. Wichura (August 1988. We
+% used a patched version with many overloads and extensions. The documented (and
+% larger) source can be found in \type {thrd-tab.tex}.
+%
+% Some code has been stripped. Some color has been added. Some macros have been
+% renamed. Registers have been replaces. And probably much more can be cleaned up.
+% We also need to use \tabl_tab_ prefixes here.
+
+\unprotect
+
+\newconditional\c_tabl_table_spacing_left
+\newconditional\c_tabl_table_spacing_right
+
+\newdimen \d_tabl_table_line_thickness_unit
+\newdimen \d_tabl_table_strut_unit
+\newskip \s_tabl_table_inter_column_space_unit
+\newdimen \d_tabl_table_column_width_unit
+\newdimen \d_tabl_table_kern_unit
+
+\mutable\def\tablestrutheightfactor {8}
+\mutable\def\tablestrutdepthfactor {3}
+\mutable\def\tableintercolumnspacefactor {3}
+\mutable\def\tablecolumnwidthfactor {10}
+\mutable\def\tablevspacefactor {2}
+\mutable\def\tablekernfactor {1}
+\mutable\def\tablelinethicknessfactor {4}
+
+\newtoks\everytable
+\newtoks\everytableparbox
+
+\protected\def\tabl_table_begin_par_box#1%
+ {\setbox\scratchbox\vtop\bgroup % \setbox added
+ \hsize#1\relax
+ \dontcomplain
+ \tabl_table_restore_lineskips
+ \normalbaselines
+ \enforced\let~\fixedspace
+ \inhibitblank % \blank[\v!disable]% % added
+ \the\everytableparbox}
+
+\protected\def\tabl_table_end_par_box
+ {\removelastskip % itemize or so
+ \endgraf
+ \ifnum\prevgraf>\zerocount % we want at least
+ \verticalstrut \nowhitespace \vskip-\struttotal % one line of text
+ \egroup
+ \ifdim\dp\scratchbox>\lineheight % see (*) for an
+ \getnoflines{\dp\scratchbox}% % example of where
+ \dp\scratchbox\zeropoint % saving can go
+ \setbox\scratchbox % terrible wrong
+ \vtop to \noflines\lineheight{\box\scratchbox}%
+ \fi % esp between rows
+ \else % of paragraphs
+ \egroup
+ \fi
+ \box\scratchbox}
+
+\appendtoks
+ \parindent\zeropoint
+ \raggedright
+ \rightskip\zeropoint \s!plus 4em \relax
+\to \everytableparbox
+
+\newskip \tablelefttabskip
+\newskip \tablerighttabskip
+
+\newcount\!taColumnNumber
+\newcount\!taRecursionLevel % (Initially 0)
+
+\newdimen\!taDimenA % used by \Enlarge
+\newdimen\!taDimenB % used by \Enlarge
+\newdimen\!taDimenC % used by numeric.tex
+\newdimen\!taMinimumColumnWidth
+
+\newtoks \!taTableSpread
+\newtoks \!taPreamble
+\newtoks \!taDataColumnTemplate
+\newtoks \!taRuleColumnTemplate
+\newtoks \!taOldRuleColumnTemplate
+\newtoks \!taLeftGlue
+\newtoks \!taRightGlue
+
+\newskip \!taLastRegularTabskip
+
+\newif \if!taBeginFormat
+\newif \if!taOnceOnlyTabskip
+
+\def\!thToksEdef#1#2%
+ {\edef\tempstring{#2}%
+ #1\expandafter{\tempstring}%
+ \ignorespaces}
+
+\def\!thLoop#1\repeat
+ {\def\!thIterate{#1\expandafter\!thIterate\fi}%
+ \!thIterate
+ \let\!thIterate\relax}
+
+\def\tabl_table_begin_format
+ {\!taPreamble\emptytoks
+ \!taColumnNumber\zerocount
+ \scratchskip\s_tabl_table_inter_column_space_unit
+ \multiply\scratchskip\tableintercolumnspacefactor
+ \divide\scratchskip\plustwo
+ \!taRuleColumnTemplate\expandafter{\expandafter\tabskip\the\scratchskip}%
+ \!taLastRegularTabskip\scratchskip
+ \!taOnceOnlyTabskipfalse
+ \!taBeginFormattrue
+ \let\!tfRowOfWidths\empty
+ \doreadtableformatkeys}
+
+\def\!tfSetWidth
+ {\ifempty\!tfRowOfWidths % true if no prior "w" keys
+ \ifnum\!taColumnNumber>\zerocount % true if "w" key is to right of first "|"
+ \begingroup % RowOfWidths={\aligntab\omit || n copies of \aligntab\omit\aligntab#\omit}, where n = number of column to the left of this one
+ \scratchcounter\plusone
+ \aftergroup\edef
+ \aftergroup\!tfRowOfWidths
+ \aftergroup{%
+ \aftergroup\aligntab
+ \aftergroup\omit
+ \!thLoop
+ \ifnum \scratchcounter<\!taColumnNumber
+ \advance\scratchcounter\plusone
+ \aftergroup\!tfAOAO
+ \repeat
+ \aftergroup}%
+ \endgroup
+ \fi
+ \fi
+ \ifx[\tempstring % \!tgGetValue sets \tempstring = token after w
+ \expandafter\!tfSetWidthText
+ \else
+ \expandafter\!tfSetWidthValue
+ \fi}
+
+\def\!tfAOAO{\aligntab\omit\aligntab\omit}
+
+\def\!tfSetWidthText[#1]%
+ {\def\!tfWidthText{#1}%
+ \doreadtableformatkeys}
+
+\def\!tfSetWidthValue
+ {\!taMinimumColumnWidth
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue \tablecolumnwidthfactor \else \!tgValue \fi \d_tabl_table_column_width_unit
+ \else
+ \!tgValue
+ \fi
+ \let\!tfWidthText\empty % Override possible prior `w[sample entry]'
+ \doreadtableformatkeys}
+
+\def\!tfSetTabskip
+ {\ifnum\!tgCode=\plusone
+ \scratchskip\s_tabl_table_inter_column_space_unit
+ \multiply\scratchskip \ifempty\!tgValue\tableintercolumnspacefactor\else\!tgValue\fi
+ \else
+ \scratchskip\!tgValue
+ \fi
+ \divide\scratchskip\plustwo
+ \ifnum\!taColumnNumber=\zerocount
+ %\!thToksEdef\!taRuleColumnTemplate{\the\!taRuleColumnTemplate\tabskip\the\scratchskip}%
+ \normalexpanded{\!taRuleColumnTemplate{\the\!taRuleColumnTemplate\tabskip\the\scratchskip}}%
+ \else
+ %\!thToksEdef\!taDataColumnTemplate{\the\!taDataColumnTemplate\tabskip\the\scratchskip}%
+ \normalexpanded{\!taDataColumnTemplate{\the\!taDataColumnTemplate\tabskip\the\scratchskip}}%
+ \fi
+ \if!taOnceOnlyTabskip\else
+ \!taLastRegularTabskip\scratchskip % Remember this Tabskip, for possible
+ \fi % restoration after a subsequent"OnceOnly"
+ \doreadtableformatkeys}
+
+\def\!tfSetVrule
+ {\!thToksEdef\!taRuleColumnTemplate
+ {\hfil
+ \vrule
+ \noexpand\s!width
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \c_tabl_table_vrule_thickness_factor
+ \else
+ \!tgValue
+ \fi
+ \d_tabl_table_line_thickness_unit
+ \else
+ \!tgValue
+ \fi
+ \alignmark\alignmark\alignmark\alignmark
+ \hfil
+ \the\!taRuleColumnTemplate}%
+ \!tfAdjoinPriorColumn}
+
+\def\!tfSetAlternateVrule
+ {\afterassignment\!tfSetAlternateA\scratchtoks}
+
+\def\!tfSetAlternateA
+ {\!thToksEdef\!taRuleColumnTemplate{\the\scratchtoks\the\!taRuleColumnTemplate}%
+ \!tfAdjoinPriorColumn}
+
+\def\!tfAdjoinPriorColumn
+ {\ifnum\!taColumnNumber=\zerocount
+ \!taPreamble=\!taRuleColumnTemplate % New \tabskip may have been added
+ \else
+ \ifempty\!tfRowOfWidths\else % no "w" keys specified yet, not even this col
+ \!tfUpdateRowOfWidths
+ \fi
+ \!thToksEdef\!taDataColumnTemplate{\the\!taLeftGlue\the\!taDataColumnTemplate\the\!taRightGlue}%
+ \!thToksEdef\!taPreamble{\the\!taPreamble\aligntab\the\!taDataColumnTemplate\aligntab\the\!taRuleColumnTemplate}%
+ \fi
+ \advance\!taColumnNumber\plusone
+ \if!taOnceOnlyTabskip
+ \!thToksEdef\!taDataColumnTemplate{\alignmark\alignmark\alignmark\alignmark\tabskip\the\!taLastRegularTabskip}%
+ \else
+ \!taDataColumnTemplate{\alignmark\alignmark}%
+ \fi
+ \!taRuleColumnTemplate\emptytoks
+ \!taLeftGlue{\hfil}%
+ \!taRightGlue{\hfil}%
+ \!taMinimumColumnWidth\zeropoint
+ \let\!tfWidthText\empty
+ \!taOnceOnlyTabskipfalse
+ \doreadtableformatkeys}
+
+\def\!tfUpdateRowOfWidths
+ {\ifempty\!tfWidthText\else
+ \!tfComputeMinColWidth
+ \fi
+ \edef\!tfRowOfWidths
+ {\!tfRowOfWidths
+ \aligntab
+ \omit
+ \ifdim \!taMinimumColumnWidth>\zeropoint
+ \hskip \the\!taMinimumColumnWidth
+ \fi
+ \aligntab
+ \omit}}
+
+\def\!tfComputeMinColWidth
+ {\setbox\scratchbox\vbox
+ {\ialign{% Plain's initialized \halign; \tabskip=0pt \everycr={}
+ \span\the\!taDataColumnTemplate\cr
+ \!tfWidthText\cr}}%
+ \!taMinimumColumnWidth\wd\scratchbox}
+
+\def\!tfFinishFormat
+ {\!thToksEdef\!taPreamble{%
+ \alignmark\alignmark\alignmark\alignmark\tabskip\tablelefttabskip
+ \aligntab
+ \the\!taPreamble\tabskip\tablerighttabskip
+ \aligntab
+ \alignmark\alignmark\alignmark\alignmark\tabskip\zeropoint\cr}
+ \!taBeginFormatfalse
+ \!ttDoHalign}
+
+\def\tabl_table_reformat[#1]% will become local
+ {\omit
+ \!taDataColumnTemplate{\alignmark\alignmark}%
+ \!taLeftGlue\emptytoks
+ \!taRightGlue\emptytoks
+ \begingroup
+ \tabl_table_use_bar
+ \expanded{\endgroup\noexpand\doreadtableformatkeys#1]}}% appear in a \tabl_table_reformat cmd; this is here as a safeguard.
+
+\aliased\let\ReFormat\relax
+
+\appendtoks
+ \enforced\let\ReFormat\tabl_table_reformat
+\to \everytable
+
+\def\!tfEndReFormat
+ {\!tfReFormat}
+
+\appendtoks
+ \tabl_table_paralignment
+\to \everytableparbox
+
+\def\!tfReFormat#1%
+ {\the \!taLeftGlue
+ \vbox{\forgetall\ialign{\span\the\!taDataColumnTemplate\cr#1\cr}}%
+ \the \!taRightGlue
+ \kern\zeropoint} % prevents \unskip / really needed
+
+\def\!tgGetValue#1%
+ {\def\!tgReturn{#1}%
+ \futurelet\tempstring\!tgCheckForParen}
+
+\def\!tgCheckForParen%
+ {\ifx\tempstring (%
+ \expandafter \!tgDoParen
+ \else
+ \expandafter \!tgCheckForSpace
+ \fi}
+
+\def\!tgDoParen(#1)%
+ {\def\!tgCode{2}% will be expanded
+ \def\!tgValue{#1}%
+ \!tgReturn}
+
+\def\!tgCheckForSpace
+ {\def\!tgCode{1}%
+ \let\!tgValue\empty
+ \ifx\tempstring\!thSpaceToken
+ \expandafter\!tgReturn
+ \else
+ \expandafter\!tgCheckForDigit
+ \fi}
+
+\def\!tgCheckForDigit % less tokens: (could be an ifcsname)
+ {\donetrue
+ \ifx 0\tempstring \orelse \ifx 1\tempstring \orelse
+ \ifx 2\tempstring \orelse \ifx 3\tempstring \orelse
+ \ifx 4\tempstring \orelse \ifx 5\tempstring \orelse
+ \ifx 6\tempstring \orelse \ifx 7\tempstring \orelse
+ \ifx 8\tempstring \orelse \ifx 9\tempstring \else
+ \donefalse
+ \fi
+ \ifdone
+ \expandafter\!tgGetNumber
+ \else
+ \expandafter\!tgReturn
+ \fi}
+
+% \def\!tgCheckForDigit % does not work
+% {\relax\doifnumberelse\tempstring\!tgGetNumber\!tgReturn}
+
+\def\!tgGetNumber {\afterassignment\!tgGetNumberA\scratchcounter=}
+\def\!tgGetNumberA{\edef\!tgValue{\the\scratchcounter}\!tgReturn}
+
+\def\!tgSetUpParBox
+ {\normalexpanded
+ {\noexpand \doreadtableformatkeys
+ b{\tabl_table_begin_par_box
+ {\ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \tablecolumnwidthfactor
+ \else
+ \!tgValue
+ \fi
+ \d_tabl_table_column_width_unit
+ \else
+ \!tgValue
+ \fi}}%
+ a{\tabl_table_end_par_box}}}
+
+\def\!tgInsertKern
+ {\edef\tempstring
+ {\kern
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \tablekernfactor
+ \else
+ \!tgValue
+ \fi
+ \d_tabl_table_kern_unit
+ \else
+ \!tgValue
+ \fi}%
+ \edef\tempstring
+ {\noexpand\doreadtableformatkeys
+ \ifconditional\c_tabl_table_spacing_left
+ b{\tempstring}
+ \fi
+ \ifconditional\c_tabl_table_spacing_right
+ a{\tempstring}
+ \fi}%
+ \tempstring}
+
+\permanent\protected\def\newtableformatkey #1{\defcsname !tk<\string#1>\endcsname}
+\permanent \def\doreadtableformatkeys#1{\begincsname !tk<\string#1>\endcsname}
+
+% Key "b": b{TOKENS} adds TOKENS to the left of (before) the template
+
+\newtableformatkey b#1%
+ {\expandafter\!tkJoin\expandafter{\the\!taDataColumnTemplate}{#1}%
+ \doreadtableformatkeys}
+
+\def\!tkJoin#1#2%
+ {\!taDataColumnTemplate{#2#1}}%
+
+% Key "a": a{TOKENS} adds TOKENS to the right of (after) the template
+
+\newtableformatkey a#1%
+ {\toksapp\!taDataColumnTemplate{#1}%
+ \doreadtableformatkeys}
+
+% Key "\{": Enclose template in braces.
+
+\newtableformatkey \{%
+ {\!taDataColumnTemplate=\expandafter{\expandafter{\the\!taDataColumnTemplate}}%
+ \doreadtableformatkeys}
+
+% Key "*": "*{N}{KEY LETTERS}" is equivalent to specifying
+% <KEY LETTERS> N times.
+% KEY LETTERS may contain further * specifications
+
+\newtableformatkey *#1#2%
+ {\scratchcounter=#1\relax
+ \scratchtoks\emptytoks
+ \!thLoop
+ \ifnum\scratchcounter>\zerocount
+ \toksapp\scratchtoks{#2}%
+ \advance\scratchcounter\minusone
+ \repeat
+ \expandafter\doreadtableformatkeys\the\scratchtoks}
+
+% Key "\LeftGlue": Specifies the glue (usually \hfil, or nothing) to be
+% added to extreme left of the template to position a column
+
+\aliased\let\LeftGlue \relax
+\aliased\let\RightGlue\relax
+
+\newtableformatkey \LeftGlue#1%
+ {\!taLeftGlue{#1}%
+ \doreadtableformatkeys}
+
+\newtableformatkey \RightGlue#1%
+ {\!taRightGlue{#1}%
+ \doreadtableformatkeys}
+
+\newtableformatkey c%
+ {\prependtoks\raggedcenter\to\!taDataColumnTemplate
+ \doreadtableformatkeys \LeftGlue\hfil \RightGlue\hfil}
+
+\newtableformatkey l%
+ {\prependtoks\raggedright\to\!taDataColumnTemplate
+ \doreadtableformatkeys \LeftGlue\empty \RightGlue\hfil}
+
+\newtableformatkey r%
+ {\prependtoks\raggedleft\to\!taDataColumnTemplate
+ \doreadtableformatkeys \LeftGlue\hfil \RightGlue\empty}
+
+\newtableformatkey x%
+ {\prependtoks\notragged\to\!taDataColumnTemplate
+ \doreadtableformatkeys \LeftGlue\hfil \RightGlue\empty}
+
+% Key "k": Adds kerns to left and right of "#" This key and the two below use Plain
+% TeX's \if@h as if it were \if@left, and \if@v as if it were \if@right. Table
+% making goes on in a group, so even in the unlikely circumstance that a \phantom
+% is currently under construction, there's no problem.
+
+\newtableformatkey k%
+ {\settrue\c_tabl_table_spacing_left
+ \settrue\c_tabl_table_spacing_right
+ \!tgGetValue{\!tgInsertKern}}
+
+% Key "i": Adds a kern to the left of "#"
+
+\newtableformatkey i%
+ {\settrue\c_tabl_table_spacing_left
+ \setfalse\c_tabl_table_spacing_right
+ \!tgGetValue{\!tgInsertKern}}
+
+% Key "j": Adds a kern to the right of "#"
+
+\newtableformatkey j%
+ {\setfalse\c_tabl_table_spacing_left
+ \settrue\c_tabl_table_spacing_right
+ \!tgGetValue{\!tgInsertKern}}
+
+% Key "n": numeric item , non-math mode.
+
+\newtableformatkey n%
+ {\def\!tnStyle{}%
+ \futurelet\!tnext\!tnTestForBracket}
+
+% Key "N": numeric item, math mode.
+
+\newtableformatkey N%
+ {\def\!tnStyle{$}%
+ \futurelet\!tnext\!tnTestForBracket}
+
+% Key "m": Math mode.
+
+\newtableformatkey m%
+ {\doreadtableformatkeys b{\normalstartimath} a{\normalstopimath}}
+
+% Key "M": Displaymath mode.
+
+\newtableformatkey M%
+ {\doreadtableformatkeys \{ b{\normalstartimath\displaystyle} a{\normalstopimath}}
+
+% Key "\m": Template ${}#\hfil$
+
+\newtableformatkey \m%
+ {\doreadtableformatkeys l b{{}} m}
+
+% Key "\M": Template $\displaystyle{{}#\hfil}$
+
+\newtableformatkey \M%
+ {\doreadtableformatkeys l b{{}} M}
+
+% Key "f": Set font (E.g., f\it sets up italic font (assuming \it
+% has its usual meaning)
+
+\newtableformatkey f#1%
+ {\doreadtableformatkeys b{#1}}
+
+\newtableformatkey B{\doreadtableformatkeys f\bf} % Key "B": abbreviation for f\bf
+\newtableformatkey I{\doreadtableformatkeys f\it} % Key "I": abbreviation for f\it
+\newtableformatkey S{\doreadtableformatkeys f\sl} % Key "S": abbreviation for f\sl
+\newtableformatkey R{\doreadtableformatkeys f\rm} % Key "R": abbreviation for f\rm
+\newtableformatkey T{\doreadtableformatkeys f\tt} % Key "T": abbreviation for f\tt
+
+% Key "p": ParBox
+
+\newtableformatkey p%
+ {\!tgGetValue{\!tgSetUpParBox}}
+
+% Key "w": minimum column width
+
+\newtableformatkey w%
+ {\!tkTestForBeginFormat w{\!tgGetValue{\!tfSetWidth}}}
+
+% Key "s": Set tabskip for the inter-column space to the right of the current
+% column, and all subsequent spaces, until overriden by a new "s" or "o" key.
+
+\newtableformatkey s%
+ {\!taOnceOnlyTabskipfalse % in case same column has a prior "o" key
+ \!tkTestForBeginFormat t{\!tgGetValue{\!tfSetTabskip}}}
+
+% Key "o": Apply the \tabskip stated for this column ONLY to the inter-column space
+% just to the right of this column; restore the the previous \tabskip for
+% subsequent columns.
+
+\newtableformatkey o%
+ {\!taOnceOnlyTabskiptrue
+ \!tkTestForBeginFormat o{\!tgGetValue{\!tfSetTabskip}}}
+
+% Key "|": Standard rule column designator
+
+\newtableformatkey |%
+ {\!tkTestForBeginFormat |{\!tgGetValue{\!tfSetVrule}}}
+
+% Key "\|": Non-standard rule column designator
+
+\newtableformatkey \|%
+ {\!tkTestForBeginFormat \|{\!tfSetAlternateVrule}}
+
+% Key ".": PERIOD -- end of \tabl_table_begin_format section.
+
+\newtableformatkey .%
+ {\!tkTestForBeginFormat.{\!tfFinishFormat}}
+
+% Key "\doendtableformat": Equivalent to "."
+
+\aliased\let\doendtableformat\relax
+
+\newtableformatkey \doendtableformat
+ {\!tkTestForBeginFormat\doendtableformat{\!tfFinishFormat}}
+
+% Key "]": End of \tabl_table_reformat section
+
+\newtableformatkey ]%
+ {\!tkTestForReFormat ] \!tfEndReFormat}
+
+% TEST FOR BEGIN FORMAT{<Key>}{Intended Action}: This test is run on keys that can
+% only be used by \tabl_table_begin_format --- "s", "o", "|", "\|", "w", ".", and
+% "\doendtableformat".
+
+\def\!tkTestForBeginFormat#1#2%
+ {\if!taBeginFormat
+ \def\tempstring{#2}%
+ \expandafter\tempstring
+ \else
+ \toks0={#1}%
+ \toks2=\expandafter{\string\tabl_table_reformat}%
+ \expandafter\!tkImproperUse
+ \fi}
+
+% TEST FOR RE FORMAT{<Key>}{Intended Action}: This test is run on the key "]",
+% which can only be used by \tabl_table_reformat.
+
+\def\!tkTestForReFormat#1#2%
+ {\if!taBeginFormat
+ \toks0={#1}%
+ \toks2=\expandafter{\string\tabl_table_begin_format}%
+ \expandafter\!tkImproperUse
+ \else
+ \def\tempstring{#2}%
+ \expandafter\tempstring
+ \fi}
+
+% NOTE: THE SPACE BETWEEN A NUMERIC ENTRY AND THE FOLLOWING '|', '"', OR '\|' IS
+% MANDATORY. EMPTY NUMERIC ENTRIES ARE NOT ALLOWED: USE '{}' OR '\omit' INSTEAD.
+
+\def\!tnTestForBracket
+ {\ifx[\!tnext
+ \expandafter\!tnGetArgument
+ \else
+ \expandafter\!tnGetCode
+ \fi}
+
+% GET CODE: E.g. "4", or "4.0", "0.4", or "10.2"
+
+\def\!tnGetCode#1 %
+ {\!tnConvertCode #1..!}
+
+% CONVERT CODE: E.g. converts above to [0000], [0000.], [.0000], [0000000000.00]
+
+\def\!tnConvertCode #1.#2.#3!%
+ {\begingroup
+ \aftergroup\edef \aftergroup\tempstring \aftergroup{%
+ \aftergroup[%
+ \scratchcounter#1\relax
+ \!thLoop
+ \ifnum \scratchcounter>\zerocount
+ \advance\scratchcounter\minusone
+ \aftergroup0
+ \repeat
+ \def\tempstring{#3}%
+ \ifempty\tempstring\else
+ \aftergroup.
+ \scratchcounter#2\relax
+ \!thLoop
+ \ifnum \scratchcounter>\zerocount
+ \advance\scratchcounter\minusone
+ \aftergroup0
+ \repeat
+ \fi
+ \aftergroup]\aftergroup}%
+ \endgroup\relax
+ \expandafter\!tnGetArgument\tempstring}
+
+% GET ARGUMENT: [<sample left field> <optional .<sample right field>>
+
+\def\!tnGetArgument[#1]%
+ {\!tnMakeNumericTemplate\!tnStyle#1..!}
+
+% MAKE NUMERIC TEMPLATE
+
+\def\!tnMakeNumericTemplate#1#2.#3.#4!% #1=<empty> or $
+ {\def\tempstring{#4}%
+ \ifempty\tempstring
+ \!taDimenC\zeropoint
+ \else
+ \setbox\scratchbox\hbox{\mathsurround\zeropoint #1.#3#1}%
+ \!taDimenC\wd\scratchbox
+ \fi
+ \setbox\scratchbox\hbox{\mathsurround\zeropoint #1#2#1}%
+ \!thToksEdef\!taDataColumnTemplate
+ {\noexpand\!tnSetNumericItem{\the\wd\scratchbox}{\the\!taDimenC}{#1}%
+ \the\!taDataColumnTemplate}% Might have tabskip glue in here
+ \doreadtableformatkeys}
+
+% SET NUMERIC ITEM
+
+\def\!tnSetNumericItem #1#2#3#4 %
+ {\!tnSetNumericItemA {#1}{#2}{#3}#4..!}
+
+\def\!tnSetNumericItemA #1#2#3#4.#5.#6!%
+ {\def\tempstring{#6}%
+ \hbox to #1{\hss \mathsurround\zeropoint #3#4#3}%
+ \hbox to #2{\ifempty\tempstring\else\mathsurround\zeropoint #3.#5#3\fi\hss}}
+
+% extensions
+
+\newtableformatkey q%
+ {\let\!tqStyle\empty
+ \futurelet\!tnext\!tqTestForBracket}
+
+\newtableformatkey Q%
+ {\def\!tqStyle{$}%
+ \futurelet\!tnext\!tqTestForBracket}
+
+\def\!tqTestForBracket
+ {\ifx[\!tnext
+ \!thx\!tqGetArgument
+ \else
+ \!thx\!tqGetCode
+ \fi}
+
+\def\!tqGetCode#1 % note the blank
+ {\!tqConvertCode #1,,!}
+
+\def\!tqConvertCode #1,#2,#3!%
+ {\begingroup
+ \aftergroup\edef
+ \aftergroup\tempstring
+ \aftergroup{%
+ \aftergroup[%
+ \scratchcounter#1\relax
+ \!thLoop
+ \ifnum \scratchcounter>\zerocount
+ \advance\scratchcounter\minusone
+ \aftergroup0
+ \repeat
+ \def\tempstring{#3}%
+ \ifempty\tempstring\else
+ \aftergroup,
+ \scratchcounter#2\relax
+ \!thLoop
+ \ifnum\scratchcounter>\zerocount
+ \advance\scratchcounter\minusone
+ \aftergroup0
+ \repeat
+ \fi
+ \aftergroup]\aftergroup}%
+ \endgroup\relax
+ \!thx\!tqGetArgument\tempstring}
+
+\def\!tqGetArgument[#1]%
+ {\!tqMakeQuantityTemplate\!tqStyle#1,,!}
+
+\def\!tqMakeQuantityTemplate#1#2,#3,#4!% #1=<empty> or $
+ {\def\tempstring{#4}%
+ \ifempty\tempstring
+ \!taDimenC\zeropoint
+ \else
+ \setbox\scratchbox\hbox{\mathsurround\zeropoint #1,#3#1}%
+ \!taDimenC\wd\scratchbox
+ \fi
+ \setbox\scratchbox\hbox{\mathsurround\zeropoint #1#2#1}%
+ \!thToksEdef\!taDataColumnTemplate
+ {\noexpand\!tqSetQuantityItem{\the\wd\scratchbox}{\the\!taDimenC}{#1}%
+ \the\!taDataColumnTemplate}%
+ \doreadtableformatkeys}
+
+\def\!tqSetQuantityItem #1#2#3#4 %
+ {\!tqSetQuantityItemA{#1}{#2}{#3}#4,,!}
+
+\def\!tqSetQuantityItemA #1#2#3#4,#5,#6!%
+ {\def\tempstring{#6}%
+ \hbox to #1{\hss\mathsurround\zeropoint#3#4#3}%
+ \hbox to #2{\ifempty\tempstring\else\mathsurround\zeropoint#3,#5#3\fi\hss}}
+
+% \Enlarge<extra height><extra depth><original>
+% \enlarge<multiple for extra height><multiple for extra depth><original>
+
+\def\tabl_table_Enlarge#1#2%
+ {% 3rd argument is picked up later
+ % #1=extra height
+ % #2=extra depth
+ \!taDimenA=#1\relax
+ \!taDimenB=#2\relax
+ \let\!TsSpaceFactor\empty
+ \ifmmode
+ \expandafter\mathpalette
+ \expandafter\!TsEnlargeMath
+ \else
+ \expandafter\!TsEnlargeOther
+ \fi}
+
+\def\!TsEnlargeOther#1%
+ {\ifhmode
+ \setbox\scratchbox\hbox{#1\xdef\!TsSpaceFactor{\spacefactor\the\spacefactor}}%
+ \else
+ \setbox\scratchbox\hbox{#1}%
+ \fi
+ \!TsFinishEnlarge}
+
+\def\!TsEnlargeMath#1#2%
+ {\setbox\scratchbox\hbox{\normalstartimath\mathsurround\zeropoint#1{#2}\normalstopimath}%
+ \!TsFinishEnlarge}
+
+\def\!TsFinishEnlarge
+ {\ht\scratchbox\dimexpr\ht\scratchbox+\!taDimenA\relax
+ \dp\scratchbox\dimexpr\dp\scratchbox+\!taDimenB\relax
+ \box\scratchbox
+ \!TsSpaceFactor\relax}
+
+\def\tabl_table_enlarge#1#2% 3rd argument is picked up later
+ {\tabl_table_Enlarge{#1\d_tabl_table_strut_unit}{#2\d_tabl_table_strut_unit}}
+
+
+\aliased\let\enlarge\relax
+\aliased\let\Enlarge\relax
+
+\appendtoks
+ \enforced\let\enlarge\tabl_table_enlarge
+ \enforced\let\Enlarge\tabl_table_Enlarge
+\to \everytable
+
+% BEGIN TABLE
+
+\let\tabl_table_standard_end\relax
+
+\def\tabl_table_standard_begin[#1]% \!ttBeginTable (always argument)
+ {\if#1u% unboxed table
+ \ifmmode
+ \let\tabl_table_standard_end\relax % user had better be in display math mode and have only one table at the outer level
+ \else % user had better be in vertical mode
+ \bgroup
+ \let\tabl_table_standard_end\egroup
+ \fi
+ \else
+ \hbox\bgroup
+ \def\tabl_table_standard_end{\egroup\egroup}%
+ \if#1t%
+ \vtop
+ \orelse\if#1b%
+ \vbox
+ \else
+ \def\tabl_table_standard_end{\egroup\normalstopimath\egroup}%
+ \scratchtoks\everymath
+ \everymath\emptytoks
+ \normalstartimath
+ \everymath\scratchtoks
+ \vcenter
+ \fi
+ \bgroup % for the \vtop, \vbox, or \vcenter
+ \fi
+ \advance\!taRecursionLevel\plusone
+ \let\!ttRightGlue\relax
+ \everycr\emptytoks
+ \ifnum\!taRecursionLevel=\plusone
+ \the\everytable
+ \fi}
+
+\bgroup \catcode\tildeasciicode\activecatcode
+
+ \appendtoks
+ \catcode\barasciicode\activecatcode
+ \enforced\protected\def ~{\kern.5em}%
+ \enforced\protected\def\\{\ifhmode\space\else\par\fi}%
+ \to \everytable
+
+\egroup
+
+\let\!ttRightGlue\relax % This may be changed, in a group, by \JustCenter, etc
+
+% DO HALIGN: Invoked by END FORMAT (or the key ".")
+
+\let\tabl_table_restore_lineskips\relax
+
+\def\!ttDoHalign
+ {\edef\tabl_table_restore_lineskips
+ {\baselineskip \the\baselineskip
+ \lineskiplimit\the\lineskiplimit
+ \lineskip \the\lineskip
+ \tabskip \the\tabskip
+ \relax}%
+ \baselineskip \zeropoint
+ \lineskiplimit\zeropoint
+ \lineskip \zeropoint
+ \tabskip \zeropoint
+ \edef\p_tabl_table_textwidth{\directtablesparameter\c!textwidth}%
+ % we are not in sync so:
+ \synchronizedisplaydirection
+ \synchronizeinlinedirection
+ \halign
+ \usedirectionparameterreverse\directtablesparameter
+ \ifempty\p_tabl_table_textwidth \else to \ifx\p_tabl_table_textwidth\v!max \hsize \else \p_tabl_table_textwidth \fi\fi
+ %\the\!taTableSpread
+ \bgroup
+ \span
+ \the\!taPreamble
+ \ifempty\!tfRowOfWidths\else
+ \!tfRowOfWidths\cr
+ \fi}
+
+% END TABLE
+
+\def\tabl_table_normal_end
+ {\egroup % finishes the \halign
+ \tabl_table_standard_end} % closes off the table envirnoment set up by \tablestandardbegin
+
+\def\tabl_table_normal_line_ending
+ {\cr}
+
+\def\tabl_table_normal_line_format#1#2%
+ {\vrule
+ \s!width \zeropoint
+ \s!height\dimexpr\tablestrutheightfactor\d_tabl_table_strut_unit+#1\d_tabl_table_strut_unit\relax
+ \s!depth \dimexpr\tablestrutdepthfactor \d_tabl_table_strut_unit+#2\d_tabl_table_strut_unit\relax
+ \relax
+ \cr}
+
+% INSERT VRULE
+
+\newcount\c_tabl_table_n_of_vrules \c_tabl_table_n_of_vrules\plusone
+
+\let\m_tabl_table_vrule_color\empty
+\let\m_tabl_table_hrule_color\empty
+
+\def\tabl_table_insert_vrule
+ {\vrule\s!width
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \c_tabl_table_vrule_thickness_factor
+ \else
+ \!tgValue
+ \fi
+ \d_tabl_table_line_thickness_unit
+ \else
+ \!tgValue
+ \fi
+ \hskip.125\emwidth\relax}
+
+\def\tabl_table_normal_line_simple_bar
+ {\unskip\!ttRightGlue\aligntab\aligntab}
+
+\def\tabl_table_normal_line_complex_bar
+ {\unskip\!ttRightGlue\aligntab\omit
+ \hfil
+ \ifempty\m_tabl_table_vrule_color\else
+ \switchtocolor[\m_tabl_table_vrule_color]%
+ \fi
+ \ifcase\c_tabl_table_n_of_vrules\or
+ \tabl_table_insert_vrule
+ \unskip
+ \else
+ \dorecurse\c_tabl_table_n_of_vrules\tabl_table_insert_vrule
+ \global\c_tabl_table_n_of_vrules\plusone
+ \unskip
+ \fi
+ \glet\m_tabl_table_vrule_color\empty
+ \hfil
+ \aligntab}
+
+\def\tabl_table_normal_no_bar
+ {\unskip\!ttRightGlue\aligntab\omit\aligntab}
+
+\def\tabl_table_normal_single_rule
+ {\aligntab\tabl_table_normal_long_rule\aligntab}
+
+\def\tabl_table_normal_multi_rule
+ {\aligntab\tabl_table_use\c_tabl_table_drule_span\tabl_table_normal_long_rule\aligntab}
+
+% USE
+
+\def\tabl_table_use#1%
+ {\ifnum#1>\plusone
+ \omit
+ \global\setfalse\c_tabl_table_is_division % added
+ \scratchcounter\currenttablecolumn % added
+ \advance\scratchcounter #1% % added
+ \advance\scratchcounter \minusone % added
+ \def\next % added
+ {\global\advance\currenttablecolumn #1% % added
+ \global\advance\currenttablecolumn \minusone % added
+ \scratchcounter#1%
+ \advance\scratchcounter \minusone
+ \advance\scratchcounter \scratchcounter
+ \!thLoop
+ \ifnum\scratchcounter>\plusone
+ \spanomit \advance\scratchcounter\minusone
+ \repeat
+ \span}%
+ \else % added
+ \def\next % conflicts with possible next \omit % added
+ {\global\advance\currenttablecolumn \plusone}%% added
+ \fi
+ \next} % added
+
+\def\tabl_table_Use#1[%
+ {\tabl_table_use{#1}%
+ \tabl_table_reformat[}
+
+\aliased\let\use\relax
+\aliased\let\Use\relax
+
+\appendtoks
+ \enforced\let\use\tabl_table_use
+ \enforced\let\Use\tabl_table_Use
+\to \everytable
+
+% rules
+
+\def\tabl_table_normal_full_rule
+ {\noalign\bgroup
+ \!ttGetHalfRuleThickness
+ \scratchdistance\directtablesparameter\c!openup
+ \ifzeropt\scratchdistance\else\kern\scratchdistance\fi
+ \hrule\s!height\scratchdimen\s!depth\scratchdimen
+ \ifzeropt\scratchdistance\else\kern\scratchdistance\fi
+ \egroup}
+
+\def\tabl_table_normal_short_rule % was: \!ttShortHrule
+ {\omit
+ \!ttGetHalfRuleThickness
+ \ifempty\m_tabl_table_hrule_color\else
+ \switchtocolor[\m_tabl_table_hrule_color]% see *DL*
+ \fi
+ \leaders\hrule\s!height\scratchdimen\s!depth\scratchdimen\hfill
+ \emptyhbox
+ \ignorespaces}
+
+\def\tabl_table_normal_long_rule % was: \!ttLongHrule
+ {\omit\span
+ \omit\span
+ \tabl_table_normal_short_rule}
+
+\def\!ttGetHalfRuleThickness
+ {\scratchdimen\dimexpr
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \c_tabl_table_hrule_thickness_factor
+ \else
+ \!tgValue % user-specified integer
+ \fi
+ \d_tabl_table_line_thickness_unit
+ \else
+ \!tgValue % user-specified dimension
+ \fi
+ \divide\scratchdimen\plustwo}
+
+% \emptyhbox prevents \unskip
+
+\def\tabl_table_Left #1{#1\hfill\emptyhbox}
+\def\tabl_table_Center#1{\hfill#1\hfill\emptyhbox}
+\def\tabl_table_Right #1{\hfill#1}
+
+\def\tabl_table_OpenUp#1#2%
+ {\edef\tablestrutheightfactor{\withoutpt\the\dimexpr\tablestrutheightfactor\points+#1\points}%
+ \edef\tablestrutdepthfactor {\withoutpt\the\dimexpr\tablestrutdepthfactor \points+#2\points}}
+
+% SetTableToWidth -> textwidth=dimension [to dimension]
+% Expand -> textwidth=max [to \hsize]
+% WidenTableBy -> [spread #1]
+% \tablelefttabskip\zeropoint\s!plus1\s!fill
+% \tablerighttabskip\tablelefttabskip
+% LongLines -> [spread \hsize]
+
+\def\tabl_table_JustLeft {\omit\let\!ttRightGlue\hfill}
+\def\tabl_table_JustCenter{\omit\hfill\emptyhbox\let\!ttRightGlue\hfill}
+\def\tabl_table_JustRight {\omit\hfill\emptyhbox}
+
+\def\tabl_table_Smash
+ {\relax
+ \ifmmode
+ \expandafter\mathpalette
+ \expandafter\!thDoMathVCS
+ \else
+ \expandafter\!thDoVCS
+ \fi}
+
+\def\!thDoVCS#1%
+ {\setbox\zerocount\hbox{#1}%
+ \!thFinishVCS}
+
+\def\!thDoMathVCS#1#2%
+ {\setbox\zerocount\hbox{\normalstartimath\mathsurround\zeropoint#1{#2}\normalstopimath}%
+ \!thFinishVCS}
+
+\def\!thFinishVCS
+ {\vpack to\zeropoint{\vss\box\zerocount\vss}}
+
+\def\tabl_table_Raise
+ {\def\!thSign{+}%
+ \!tgGetValue\!thSetDimen}
+
+\def\tabl_table_Lower
+ {\def\!thSign{-}%
+ \!tgGetValue\!thSetDimen}
+
+\def\!thSetDimen
+ {\ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \!taDimenA\tablestrutheightfactor\d_tabl_table_strut_unit
+ \advance\!taDimenA\tablestrutdepthfactor\d_tabl_table_strut_unit
+ \divide\!taDimenA\plustwo
+ \else
+ \!taDimenA\!tgValue\d_tabl_table_strut_unit
+ \fi
+ \else
+ \!taDimenA\!tgValue
+ \fi
+ \!taDimenA\!thSign\!taDimenA\relax
+ \ifmmode
+ \expandafter\mathpalette
+ \expandafter\!thDoMathRaise
+ \else
+ \expandafter\!thDoSimpleRaise
+ \fi}
+
+\def\!thDoSimpleRaise#1%
+ {\setbox\zerocount\hbox{\raise \!taDimenA\hbox{#1}}%
+ \!thFinishRaise} % From Plain TeX: \ht0=0pt \dp0=0pt \box0
+
+\def\!thDoMathRaise#1#2%
+ {\setbox\zerocount\hbox{\raise \!taDimenA\hbox{\normalstartimath\mathsurround\zeropoint#1{#2}\normalstopimath}}%
+ \!thFinishRaise}
+
+\def\!thFinishRaise
+ {\ht\zerocount\zeropoint
+ \dp\zerocount\zeropoint
+ \box\zerocount}
+
+\def\tabl_table_BackSpace
+ {\!tgGetValue\!thKernBack}
+
+\def\!thKernBack
+ {\kern -
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \tablekernfactor
+ \else
+ \!tgValue % user-specified integer
+ \fi
+ \d_tabl_table_kern_unit
+ \else
+ \!tgValue % user-specified dimension
+ \fi
+ \ignorespaces}
+
+\def\tabl_table_Vspace
+ {\noalign
+ \bgroup
+ \!tgGetValue\!thVspace}
+
+\def\!thVspace
+ {\vskip
+ \ifnum\!tgCode=\plusone
+ \ifempty\!tgValue
+ \tablevspacefactor
+ \else
+ \!tgValue % user-specified integer
+ \fi
+ \d_tabl_table_strut_unit
+ \else
+ \!tgValue % user-specified skip
+ \fi
+ \egroup} % Ends the \noalign
+
+\aliased\let\JustLeft \relax
+\aliased\let\JustCenter \relax
+\aliased\let\JustRight \relax
+\aliased\let\Smash \relax
+\aliased\let\Raise \relax
+\aliased\let\Lower \relax
+\aliased\let\BackSpace \relax
+\aliased\let\Vspace \relax
+\aliased\let\OpenUp \relax
+\aliased\let\TableLeft \relax
+\aliased\let\TableCenter\relax
+\aliased\let\TableRight \relax
+
+\appendtoks
+ \enforced\let\JustLeft \tabl_table_JustLeft
+ \enforced\let\JustCenter \tabl_table_JustCenter
+ \enforced\let\JustRight \tabl_table_JustRight
+ \enforced\let\Smash \tabl_table_Smash
+ \enforced\let\Raise \tabl_table_Raise
+ \enforced\let\Lower \tabl_table_Lower
+ \enforced\let\BackSpace \tabl_table_BackSpace
+ \enforced\let\Vspace \tabl_table_Vspace
+ \enforced\let\OpenUp \tabl_table_OpenUp
+ \enforced\let\TableLeft \tabl_table_Left
+ \enforced\let\TableCenter\tabl_table_Center
+ \enforced\let\TableRight \tabl_table_Right
+\to \everytable
+
+%D \macros
+%D {inintable, ifsplittables}
+%D
+%D First we declare some variables. These show a bit what we are dealing with. First
+%D we introdoce some booleans that enable us, inside as well as outside this module,
+%D to determine in what mode we are.
+
+\newif\ifintable
+\newif\ifsplittables
+
+%D We show this feature in an example that also shows some of the basic table
+%D typesetting commands.
+%D
+%D \startbuffer
+%D \starttable[|||]
+%D \HL
+%D \VL first \VL second \VL\AR
+%D \HL
+%D \VL alfa \VL 1 \VL\AR
+%D \VL beta \VL 2 \VL\AR
+%D \VL gamma \VL 3 \VL\AR
+%D \HL
+%D \stoptable
+%D \stopbuffer
+%D
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D This table is specified as:
+%D
+%D \typebuffer
+%D
+%D This examples shows about the minimum of commands needed to typeset such a table.
+%D In this table, the \type {\AR} is automatically translated into the more
+%D primitive (but more verbose) commands \type {\SR}, \type {\FR}, \type {\MR} and
+%D \type {\LR} commands.
+%D
+%D \startbuffer
+%D \starttables[|||]
+%D \HL
+%D \VL first \VL second \VL\AR
+%D \HL
+%D \VL alfa \VL 1 \VL\AR
+%D \VL beta \VL 2 \VL\AR
+%D \VL gamma \VL 3 \VL\AR
+%D \HL
+%D \stoptables
+%D \stopbuffer
+%D
+%D \getbuffer
+%D
+%D Some simple color support is provided:
+%D
+%D \startbuffer
+%D \starttable[|c|c|]
+%D \HL
+%D \VL test \VL test \VL \SR
+%D \HL[green,5]
+%D \VL[red] test \VL test \VL \FR
+%D \VL test \VL[10,red] test \VL \MR
+%D \VL test \VL test \VL[10] \LR
+%D \HL
+%D \stoptable
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\installcorenamespace{tabletemplate}
+
+\immutable\def\m!TABLE{TABLE}
+
+%D We already saw that the table macros report errors and provide automatic spacing.
+%D These features can only be implemented by keeping track of the state, often the
+%D last command on a row.
+
+\newconstant\tableforcestate
+\newconstant\tableactionstate
+
+\setnewconstant\tableunknownstate 0
+
+\setnewconstant\tableseparaterowstate 1
+\setnewconstant\tablefirstrowstate 2
+\setnewconstant\tablemidrowstate 3
+\setnewconstant\tablelastrowstate 4
+\setnewconstant\tablerulestate 5
+%setnewconstant\tableskipstate 6
+%setnewconstant\tableautorowstate 7
+
+\setnewconstant\tableforcefirstrowstate 1
+\setnewconstant\tableforcelastrowstate 2
+
+\newconstant\tablerowfactor
+\newconstant\TABLEendofrowdepth
+\newconstant\TABLEendofrowheight
+\newconstant\TABLEcr
+\newconstant\tablerowzero
+\newconstant\TABLEn
+
+%D We store these states using \type {constants}'s and like most variables, these
+%D are global ones. When needed, especially when we flush the backgrounds, we can
+%D temporary disable the assignment.
+
+\newconditional\tableactionstatepermitted
+
+\def\tabl_table_set_action#1{\ifconditional\tableactionstatepermitted\global\tableactionstate#1\fi}
+\def\tabl_table_set_force #1{\ifconditional\tableactionstatepermitted\global\tableforcestate #1\fi}
+
+%D To give an impression of what the (well documented) source of \TABLE\ looks like,
+%D we first implement an alternative for the numeric keys. The quantity keys
+%D (\type{q} and \type{Q}) support the more european way of writing numbers:
+%D
+%D \startnarrower
+%D 100.000.000,00 instead of 100,000,000.00
+%D \stopnarrower
+%D
+%D The next table shows how to use these keys. We use braces instead of brackets because
+%D we need brackets to specify the format.
+%D
+%D \starttyping
+%D \starttable{|q[00,000]|Q[00,00]|}
+%D \HL
+%D \VL -1,2 \VL 12,35 \VL\FR
+%D \VL 11,203 \VL 2,4 \VL\LR
+%D \HL
+%D \stoptable
+%D \stoptyping
+%D
+%D Although a more efficient implementation is possible |<|we can for instance share
+%D common macros|>| we just adapt a copy of the numeric ones. To permit double
+%D loading of this module, we check for the existence of one of the macros.
+%D
+%D To be compatible with the tabulate environment, we also support the \type {l},
+%D \type {c} and \type {r} keys for paragraph entries.
+%D
+%D All commands that are executed between rows are to be put in \type {\noalign}. We
+%D can however not verify if we (that is \TABLE) does or did not enter this mode. A
+%D moderate dirty but useful trick is using our own alternative:\footnote{Once one
+%D has entered the stage of redefining \TEX\ primitives, such hacks become a second
+%D nature. However, redefining \type {\omit} and \type{\span} is not that easy.}
+
+%D We no longer need this in \LUAMETATEX:
+%D
+%D \starttyping
+%D \aliased\let\tablenoalign \noalign
+%D \def\starttablenoalign{\noalign\bgroup}
+%D \let\stoptablenoalign \egroup
+%D \stoptyping
+
+%D \macros
+%D {starttable}
+%D
+%D The rest of this module is not easy to comprehend, mainly because we have to take
+%D care of:
+%D
+%D \startitemize[packed]
+%D \item \type{\startitemize[template]}
+%D \item \type{\startitemize{template}}
+%D \item \type{\startitemize[predefined]}
+%D \stopitemize
+%D
+%D as well as:
+%D
+%D \startitemize[continue]
+%D \item restart after table break
+%D \stopitemize
+%D
+%D The official specification of the start command is:
+%D
+%D \showsetup{starttable}
+
+\newconditional\c_tabl_table_repeat_head
+\newconditional\c_tabl_table_repeat_tail
+
+\permanent\tolerant\protected\def\starttable[#1]#*[#2]% preamble optional-settings
+ {\bgroup
+ \ifarguments\or\or
+ \setupcurrenttables[#2]%
+ \fi
+ \edef\p_tabl_table_split{\directtablesparameter\c!split}%
+ \edef\p_tabl_table_frame{\directtablesparameter\c!frame}%
+ \ifx\p_tabl_table_split\v!auto
+ \ifinsidesplitfloat
+ \let\p_tabl_table_split\v!yes
+ \lettablesparameter\c!split\v!yes % might be used later, best make a proper mode
+ \fi
+ \fi
+ \ifx\p_tabl_table_split\v!yes
+ \enforced\let\stoptable\table_table_stop_g % not \protected as we look ahead
+ \expandafter\starttables
+ \orelse\ifx\p_tabl_table_split\v!repeat
+ \enforced\let\stoptable\table_table_stop_g % not \protected as we look ahead
+ \expandafter\starttables
+ \else
+ \enforced\let\stoptable\table_table_stop
+ \ifempty\p_tabl_table_frame
+ \ifinsidefloat\else\startbaselinecorrection\fi
+ \else
+ \startframedcontent[\p_tabl_table_frame]%
+ \fi
+ \postponenotes
+ \expandafter\tabl_table_first_stage
+ \fi[#1]}
+
+% We cannot define the stopper as \type {\protected} because lookahead in
+% alignments fail then, so we relax it and define it locally. Actually we
+% now can.
+
+\permanent\let\stoptable\relax
+
+\permanent\def\table_table_stop
+ {\tabl_tables_chuck_auto_row % before the tail, else noalign problem
+ \tabl_table_insert_tail
+ \noalign\bgroup
+ \glet\tabl_table_head\empty
+ \glet\tabl_table_tail\empty
+ \egroup
+ \tabl_table_finish
+ \ifempty\p_tabl_table_frame
+ \ifinsidefloat\else
+ \stopbaselinecorrection
+ \goodbreak % compensates all the nobreaks
+ \fi
+ \else
+ \stopframedcontent
+ \fi
+ \egroup}
+
+\permanent\def\table_table_stop_g
+ {\table_table_stop_s\egroup}
+
+%D Before we can grab the argument, we have to make sure that the \CATCODES\ are
+%D set. The first stage takes care of that.
+
+\def\tabl_table_first_stage
+ {\bgroup
+ \global\intabletrue
+ \tabl_table_second_stage}
+
+%D \macros
+%D {definetabletemplate}
+%D
+%D The complex (and main) start macro first takes care of the predefined case. Such
+%D a predefined setup looks like:
+%D
+%D \starttyping
+%D \definetabletemplate[test][|||]
+%D
+%D \starttable[test]
+%D \VL test \VL test \VL\AR
+%D \VL test \VL test \VL\AR
+%D \VL test \VL test \VL\AR
+%D \stoptable
+%D \stoptyping
+%D
+%D The implementation of the definition macro is not that complicated:
+
+\installcorenamespace{tablehead}
+\installcorenamespace{tabletail}
+
+\permanent\protected\def\definetabletemplate % to be redone
+ {\bgroup
+ \catcode\barasciicode\othercatcode
+ \tabl_table_define_template}
+
+\tolerant\def\tabl_table_define_template[#1]#*[#2]#*[#3]#*[#4]%
+ {\ifarguments\or\else
+ \setgvalue{\??tabletemplate#1}{\tabl_table_use_template{#2}{#3}{#4}}%
+ \fi
+ \egroup}
+
+\def\tabl_table_use_template#1#2#3%
+ {\gdef\tabl_table_head{\begincsname\??tablehead#2\endcsname}%
+ \gdef\tabl_table_tail{\begincsname\??tabletail#3\endcsname}%
+ \tabl_table_second_stage[#1]}
+
+%D The optional third and fourth arguments define which table head and tail to use.
+%D
+%D \starttyping
+%D \definetabletemplate[test][|||][before][after]
+%D \stoptyping
+%D
+%D This also means that one can define table heads and tails by name!
+%D
+%D \starttyping
+%D \starttablehead[before]
+%D \HL \VL first \VL second \VL \SR \HL
+%D \stoptablehead
+%D \stoptyping
+%D
+%D Templates defined this way get protected names, that cannot conflict with
+%D existing commands.
+%D
+%D \showsetup{definetabletemplate}
+%D
+%D The second half of the next macro prepares table
+%D splitting.
+
+\def\tabl_table_insert_head
+ {\noalign{\global\settrue\preventtablebreak\global\setfalse\hassometablehead}%
+ \tabl_table_head
+ \noalign{\global\setfalse\preventtablebreak}}
+
+\def\tabl_table_insert_tail
+ {\noalign{\global\settrue\preventtablebreak\global\setfalse\hassometabletail}%
+ \tabl_table_tail
+ \noalign{\global\setfalse\preventtablebreak}}
+
+% \def\doverysimpletableHL % todo
+% {\noalign{\normalexpanded{\noexpand\tabl_table_normal_full_rule\m_tabl_table_HLheight}}
+
+\let\tabl_table_restart\relax
+
+\def\tabl_table_restart_indeed#1%
+ {\gdef\tabl_table_restart{#1}%
+ \tabl_table_restart
+% \noalign{\globalpushmacro\simpletableHL\glet\simpletableHL\doverysimpletableHL}%
+ \tabl_table_insert_head
+ \ifsplittables \ifconditional \c_tabl_table_repeat_tail
+ \noalign{\goodbreak}%
+ \tabl_table_insert_tail
+ \noalign{\goodbreak}%
+ \fi \fi
+% \noalign{\globalpopmacro\simpletableHL}%
+ }
+
+\bgroup \catcode\barasciicode\othercatcode
+
+\gdef\tabl_table_second_stage[#1]% brr nested mess
+ {\bgroup
+ \tabl_table_use_bar
+ \global\setfalse\tableactionstatepermitted
+ \global\setfalse\hassometablehead
+ \global\setfalse\hassometabletail
+ \expanded{\doifelseinstring{|}{#1}}
+ {\xdef\tabl_table_restart{\noexpand\tabl_table_restart_indeed{\noexpand\tabl_table_third_stage{#1}}}}
+ {\ifcsname\??tabletemplate#1\endcsname
+ \gdef\tabl_table_restart{\csname\??tabletemplate#1\endcsname}%
+ \else
+ \gdef\tabl_table_restart{\tabl_table_restart_indeed{\begincsname#1\endcsname}}%
+ \fi}%
+ \egroup
+ \tabl_table_restart}
+
+\egroup
+
+%D The third stage involves a lot of (re)sets, which we will explain later.
+
+\appendtoks
+ \fixedspaces
+ \enforced\let\_\normalunderscore
+\to \everytable
+
+%D Now we can start the table.
+
+\newtoks\localtabledefinitions
+
+\mutable\let\currenttableformat\empty
+
+\def\tabl_table_third_stage#1%
+ {\global\settrue\tableactionstatepermitted
+ \tabl_table_set_action\tableunknownstate
+ \tabl_table_set_force\tableunknownstate
+ \tabl_table_resetVLvalues
+ \appendtoks\tabl_table_local_setups\to\everytable
+ \tabl_table_standard_begin[\ifsplittables u\else b\fi]%
+ \the\localtabledefinitions
+ \forgetall % added
+ \edef\currenttableformat{#1}%
+ \doifsomething\currenttableformat
+ {\tabl_tables_get_nofcolumns\currenttableformat
+ % more modern is to use catcode tables
+ \expandafter\tabl_table_begin_format\currenttableformat\doendtableformat}}
+
+\def\tabl_table_finish
+ {\tabl_tables_chuck_auto_row
+ \unskip\crcr
+ \tabl_table_normal_end
+ \global\intablefalse
+ \egroup}
+
+%D \macros
+%D {starttables}
+%D
+%D Split tables are specified using the plural form of the start and stop commands.
+%D
+%D \showsetup{starttables}
+%D
+%D For example:
+%D
+%D \starttyping
+%D \starttables[|||]
+%D \HL
+%D \VL element \VL atom weight \VL\AR
+%D \HL
+%D \VL ....... \VL ........... \VL\AR
+%D \VL ....... \VL ........... \VL\AR
+%D \HL
+%D \stoptables
+%D \stoptyping
+
+\newbox\tablecontentbox
+
+\permanent\protected\def\starttables
+ {\bgroup
+ \enforced\let\stoptables\table_table_stop_s
+ \splittablestrue
+ \edef\p_tabl_table_split{\directtablesparameter\c!split}%
+ \ifx\p_tabl_table_split\v!repeat
+ \settrue\c_tabl_table_repeat_head
+ \settrue\c_tabl_table_repeat_tail
+ \else
+ \setfalse\c_tabl_table_repeat_head
+ \setfalse\c_tabl_table_repeat_tail
+ \fi
+ \flushnotes
+ \setbox\tablecontentbox\vbox\bgroup
+ \forgetall
+ \tabl_table_first_stage}
+
+\aliased\let\stoptables\relax % needed for \noalign
+
+\def\table_table_stop_s % not \protected as we need the lookahead (brrr)
+ {\tabl_tables_chuck_auto_row % AM: before the tail, else noalign problem
+ \ifconditional\c_tabl_table_repeat_tail\else\tabl_table_insert_tail\fi
+ \tabl_table_finish
+ \egroup
+ \dontcomplain
+ \tabl_table_split_box\tablecontentbox
+ \glet\tabl_table_head\empty % new here
+ \glet\tabl_table_tail\empty % new here
+ \flushnotes
+ \egroup}
+
+\def\tabl_table_split_box#1%
+ {\resettsplit
+ \c_split_minimum_free_lines\plustwo
+ \d_split_minimum_free_space\zeropoint
+ \setbox\b_split_content\box#1%
+ \ifconditional\c_tabl_table_repeat_head \ifconditional\hassometablehead
+ \setbox\b_split_head\vsplit\b_split_content to \lineheight
+ \setbox\b_split_head\vbox{\unvbox\b_split_head}% \vpack ?
+ \fi \fi
+ \ifconditional\c_tabl_table_repeat_tail \ifconditional\hassometabletail
+ \setbox\b_split_tail\vsplit\b_split_content to \lineheight
+ \setbox\b_split_tail\vbox{\unvbox\b_split_tail}% \vpack ?
+ \fi \fi
+ \ifinsidefloat\else
+ \t_split_before_result{\startbaselinecorrection}%
+ \t_split_after_result {\stopbaselinecorrection}%
+ \fi
+ \handletsplit}
+
+%D When the table in the previous example is split across pages, only the first gets
+%D a head. We could have said something like:
+%D
+%D \starttyping
+%D \starttablehead
+%D \HL
+%D \VL element \VL atom weight \VL\AR
+%D \HL
+%D \stoptablehead
+%D
+%D \starttabletail
+%D \HL
+%D \stoptabletail
+%D
+%D \starttables[|||]
+%D \VL ....... \VL ........... \VL\AR
+%D \VL ....... \VL ........... \VL\AR
+%D \stoptables
+%D \stoptyping
+%D
+%D This time each split table gets a head line and ends with a rule. Keep in mind
+%D that such heads also apply to the unbroken ones and should be defined local
+%D (grouped) if needed. The rather complicated definition below is due to the fact
+%D that the stopcondition is interface language dependant.
+
+\let\tabl_table_head\empty % needs checking
+\let\tabl_table_tail\empty % needs checking
+
+\letvalue{\e!start\v!tablehead}\relax % todo: frozen, but we use a grabber
+\letvalue{\e!stop \v!tablehead}\relax % todo: frozen, but we use a grabber
+\letvalue{\e!start\v!tabletail}\relax % todo: frozen, but we use a grabber
+\letvalue{\e!stop \v!tabletail}\relax % todo: frozen, but we use a grabber
+
+%D The second argument is a dummy one, by scanning for it, we get rid of
+%D interfering spaces.
+
+\newconditional\preventtablebreak
+\newconditional\hassometablehead
+\newconditional\hassometabletail
+
+% \def\tabl_table_set_head[#1][#2]#3\end{\setvalue{\??tablehead#1}{\noalign{\global\settrue\hassometablehead}#3}}
+% \def\tabl_table_set_tail[#1][#2]#3\end{\setvalue{\??tabletail#1}{\noalign{\global\settrue\hassometabletail}#3}}
+
+\permanent\protected\def\settablehead{\dodoubleempty\tabl_table_set_head} % todo: use pickup
+\permanent\protected\def\settabletail{\dodoubleempty\tabl_table_set_tail} % todo: use pickup
+
+\def\tabl_table_set_head[#1][#2]#3\end
+ {\gdef\tabl_table_head{\begincsname\??tablehead#1\endcsname}% new
+ \defcsname\??tablehead#1\endcsname{\noalign{\global\settrue\hassometablehead}#3}}
+
+\def\tabl_table_set_tail[#1][#2]#3\end
+ {\gdef\tabl_table_tail{\begincsname\??tabletail#1\endcsname}% new
+ \defcsname\??tabletail#1\endcsname{\noalign{\global\settrue\hassometabletail}#3}}
+
+\normalexpanded {
+ \aliased\letcsname\e!stop\v!tablehead\endcsname\relax
+ \aliased\letcsname\e!stop\v!tabletail\endcsname\relax
+}
+
+\normalexpanded {
+ \permanent\protected\def\csname\e!start\v!tablehead\endcsname#1\csname\e!stop\v!tablehead\endcsname{\settablehead#1\noexpand\end}
+ \permanent\protected\def\csname\e!start\v!tabletail\endcsname#1\csname\e!stop\v!tabletail\endcsname{\settabletail#1\noexpand\end}
+}
+
+%D Redundant \type{\HL}'s are removed automatically, so mid||lines can be used
+%D without problems.
+%D
+%D The order of the next macros is more or less random. First we implement error
+%D recovery. Errors are reported to the screen and log file as well as visualized in
+%D the table in teletype.
+
+\def\tabl_table_finish_row
+ {\crcr
+ \noalign\bgroup
+ \nobreak
+ \tabl_table_set_action\tableunknownstate
+ \glet\tabl_tables_check_auto_row\empty
+ \glet\tabl_tables_chuck_auto_row\empty
+ \global\currenttablecolumn\zerocount
+ \egroup}
+
+%D Next we enter the more complicated area of column and row switching. I won't go
+%D into much detail from now on, but just mention the general principles.
+%D
+%D \startitemize[3*ruim]
+%D \sym{\type{\SR}} end a separate row (between rules)
+%D \sym{\type{\FR}} end a first row (after a rule)
+%D \sym{\type{\MR}} end a mid row (between text lines)
+%D \sym{\type{\LR}} end a last row (before a rule)
+%D \stopitemize
+%D
+%D and best of all:
+%D
+%D \startitemize[continue]
+%D \sym{\type{\AR}} end a row with automatic spacing
+%D \stopitemize
+%D
+%D As far as possible, we report confusing situations. In most cases one can use
+%D \type{\AR}, which transfigurates itself into one of the other types.
+%D
+%D \starttyping
+%D \starttable[||]
+%D \HL
+%D \VL a separate row \VL\SR
+%D \HL
+%D \VL a first row \VL\FR
+%D \VL a mid row \VL\MR
+%D \VL a last row \VL\LR
+%D \HL
+%D \stoptable
+%D \stoptyping
+%D
+%D In this example we could have used \type{\AR} without problems.
+%D
+%D Color or gray scale backgrounds precede the content. They are passed over
+%D horizontal (division) lines when needed. Errors in the color template are traced
+%D elsewhere. Here we only check for inconsistent spacing. Due to the way \TEX\
+%D handles alignments, we cannot automate spacing for colored rows and columns.
+
+\tablerowzero\zerocount
+
+\appendtoks
+ \enforced\let\SR\tabl_table_SR
+ \enforced\let\FR\tabl_table_FR
+ \enforced\let\MR\tabl_table_MR
+ \enforced\let\LR\tabl_table_LR
+ \enforced\let\AR\tabl_table_AR
+\to \localtabledefinitions
+
+\protected\def\tabl_table_SR
+ {\ifnum\tableactionstate=\tablefirstrowstate
+ \writestatus\m!TABLE{change \string\SR\space into \string\MR/\string\LR}%
+ \orelse\ifnum\tableactionstate=\tablemidrowstate
+ \writestatus\m!TABLE{change \string\SR\space into \string\MR/\string\LR}%
+ \orelse\ifnum\tableactionstate=\tablemidrowstate
+ \writestatus\m!TABLE{change \string\SR\space into \string\MR/\string\LR}%
+ \fi
+ \tabl_table_end_row_indeed\tableseparaterowstate\tablerowfactor\tablerowfactor}
+
+\protected\def\tabl_table_FR
+ {\ifnum\tableactionstate=\tablemidrowstate
+ \writestatus\m!TABLE{change \string\FR\space into \string\MR/\string\LR}%
+ \orelse\ifnum\tableactionstate=\tablelastrowstate
+ \writestatus\m!TABLE{change \string\FR\space into \string\MR/\string\LR}%
+ \fi
+ \tabl_table_end_row_indeed\tablefirstrowstate\tablerowfactor\tablerowzero}
+
+\protected\def\tabl_table_MR
+ {\ifnum\tableactionstate=\tablerulestate
+ \writestatus\m!TABLE{change \string\MR\space into \string\FR/\string\SR}%
+ \orelse\ifnum\tableactionstate=\tablelastrowstate
+ \writestatus\m!TABLE{change \string\MR\space into \string\FR}%
+ \fi
+ \tabl_table_end_row_indeed\tablemidrowstate00}
+
+\protected\def\tabl_table_LR
+ {\ifnum\tableactionstate=\tablerulestate
+ \writestatus\m!TABLE{change \string\LR\space into \string\FR/\string\SR}%
+ \fi
+ \tabl_table_end_row_indeed\tablelastrowstate\tablerowzero\tablerowfactor}
+
+%D \macros
+%D {ifcheckTABLEcolums}
+%D
+%D The next macros handle the actual row ending. This macro also take care of space
+%D corrections due to table splitting when \type{\MR} and collegues are used. When
+%D tracing is enabled, the corrections as well as the values used to determine the
+%D available space are shown (in color). By default checking is off.
+
+\def\tabl_table_end_row_indeed#1#2#3%
+ {\tabl_table_set_action#1%
+ \ifcase#1\relax
+ % unknown
+ \or
+ \tabl_tables_end_line\SR\SR\tablerowfactor\tablerowfactor
+ \or
+ \tabl_tables_end_line\FR\FR\tablerowfactor\tablerowzero
+ \or
+ \ifnum\tableforcestate=\tableforcelastrowstate
+ \tabl_tables_end_line\MR\LR\tablerowzero\tablerowfactor
+ \orelse\ifnum\tableforcestate=\tableforcefirstrowstate
+ \tabl_tables_end_line\MR\FR\tablerowfactor\tablerowzero
+ \else
+ \tabl_tables_end_line\MR\MR\tablerowzero\tablerowzero
+ \fi
+ \or
+ \tabl_tables_end_line\LR\LR\tablerowzero\tablerowfactor
+ \fi
+ \noalign\bgroup
+ \tabl_table_set_force\tableunknownstate
+ \global\currenttablecolumn\zerocount
+ \ifconditional\preventtablebreak
+ \nobreak
+ \else
+ \goodbreak
+ \fi
+ \egroup}
+
+%D Handling \type{\AR} is postponed till the next row. The check takes care of
+%D the first and mid rows, the chuck macro |<|how about that name|>| handles
+%D the last row.
+
+\protected\def\tabl_table_AR
+ {\glet\tabl_tables_check_auto_row\tabl_tables_check_auto_row_indeed
+ \glet\tabl_tables_chuck_auto_row\tabl_tables_chuck_auto_row_indeed}
+
+\let\tabl_tables_check_auto_row\empty
+\let\tabl_tables_chuck_auto_row\empty
+
+\def\tabl_tables_check_auto_row_indeed
+ {\glet\tabl_tables_check_auto_row\empty
+ \ifnum\tableactionstate=\tablerulestate \FR\orelse
+ \ifnum\tableactionstate=\tableunknownstate\FR\else
+ \MR\fi}
+
+\def\tabl_tables_chuck_auto_row_indeed
+ {\glet\tabl_tables_check_auto_row\empty
+ \glet\tabl_tables_chuck_auto_row\empty
+ \ifnum\tableactionstate=\tablerulestate \SR\orelse
+ \ifnum\tableactionstate=\tableunknownstate\SR\else
+ \LR\fi}
+
+%D When a table is split, we also add a tail and when present we repeat the table
+%D head. If a gets split indeed, the spacing before and after a horizontal rule is
+%D corrected according to what we expect.
+
+\def\tabl_tables_end_line#1#2#3#4%
+ {\ifx#1#2\else
+ \writestatus\m!TABLE{\string#1\space changed into \string#2}%
+ \fi
+ \expandafter\tabl_table_normal_line_format#3#4\crcr % \crcr nodig ?
+ \noalign{\nobreak\global\settrue\tableactionstatepermitted}}
+
+%D In order to prevent (as good as possible) alignment overflow and therefore \TEX\
+%D error messages, we check the maximum number of columns. We keep track of the
+%D current column and maximum column by means of two \COUNTERS. Keep in mind that
+%D the number of \type{|}'s and \type{\VL}'s or alike is always one more than the
+%D number of columns.
+
+\newcount\currenttablecolumn
+
+% DWhile defining this macro we change the \CATCODE\ of \type{|}. When counting the
+% Dbars, we use a non active representation of the bar, simply because we cannot be
+% Dsure if the bar is active or not.\footnote{Normally it is, but \TABLE\ changes
+% Dthe catcode when needed.}
+
+\bgroup
+ \catcode\barasciicode\othercatcode \permanent\gdef\tabl_table_bar{|}
+ \catcode\barasciicode\activecatcode\gdef\tabl_table_use_bar{\enforced\let|\tabl_table_bar}
+\egroup
+
+\bgroup \catcode\barasciicode\othercatcode
+
+\gdef\tabl_tables_get_nofcolumns#1% todo: also divert this to lua as with tabulate
+ {\bgroup
+ \cleanupfeatures % needed !
+ \tabl_table_use_bar
+ \egroup}
+
+\egroup
+
+%D \startitemize[3*ruim]
+%D \sym{\type{\VL}} a vertical line
+%D \sym{\type{\VC}} a vertical colored line
+%D \sym{\type{\HL}} a horizontal line
+%D \sym{\type{\HC}} a horizontal colored line
+%D \stopitemize
+
+\newcount\c_tabl_table_vrule_thickness_factor
+\newcount\c_tabl_table_hrule_thickness_factor
+\newcount\c_tabl_table_drule_span
+
+\let\m_tabl_table_vrule_color\empty
+\let\m_tabl_table_hrule_color\empty
+
+\appendtoks
+ \enforced\let\VL\tabl_table_VL
+ \enforced\let\VC\tabl_table_VC
+ \enforced\let\HL\tabl_table_HL
+ \enforced\let\HC\tabl_table_HC
+ \enforced\let\VS\tabl_table_VS
+ \enforced\let\VD\tabl_table_VD
+ \enforced\let\VT\tabl_table_VT
+ \enforced\let\VN\tabl_table_VN
+\to \localtabledefinitions
+
+\def\tabl_table_resetVLvalues
+ {\global\currenttablecolumn\zerocount}
+
+\def\tabl_table_vrulecommand#1% global assignments
+ {\doifelsenumber{#1}
+ {\global\c_tabl_table_vrule_thickness_factor#1\relax
+ \global\multiply\c_tabl_table_vrule_thickness_factor\m_tabl_table_VLwidth\relax}
+ {\xdef\m_tabl_table_vrule_color{#1}}}
+
+\permanent\tolerant\protected\def\tabl_table_VL[#1]%
+ {\tabl_tables_check_auto_row
+ \global\advance\currenttablecolumn\plusone
+ \glet\m_tabl_table_vrule_color\empty
+ \global\c_tabl_table_vrule_thickness_factor\m_tabl_table_VLwidth\relax
+ \iftok{#1}\emptytoks\else
+ \rawprocesscommalist[#1]\tabl_table_vrulecommand
+ \fi
+ \tabl_table_normal_line_complex_bar}% \relax breaks \use
+
+\permanent\let\tabl_table_VC\tabl_table_VL % for mojca
+
+% \starttable[|||]
+% \HL
+% \VL test \VS test \VL \FR
+% \VL test \VD test \VL \MR
+% \VL test \VT test \VL \LR
+% \HL
+% \stoptable
+
+\permanent\protected\def\tabl_table_VS {\VN1}
+\permanent\protected\def\tabl_table_VD {\VN2}
+\permanent\protected\def\tabl_table_VT {\VN3}
+\permanent\protected\def\tabl_table_VN#1{\global\c_tabl_table_n_of_vrules#1\relax\VL}
+
+\def\tabl_table_hrulecommand#1% global assignments
+ {\doifelsenumber{#1}
+ {\global\c_tabl_table_hrule_thickness_factor#1\relax
+ \global\multiply\c_tabl_table_hrule_thickness_factor\m_tabl_table_HLheight\relax}
+ {\xdef\m_tabl_table_hrule_color{#1}}}
+
+\permanent\tolerant\protected\def\tabl_table_HL[#1]%
+ {\tabl_tables_chuck_auto_row
+ \tabl_table_finish_row
+ \noalign\bgroup
+ \nobreak
+ \ifnum\tableactionstate=\tablerulestate
+ \writestatus\m!TABLE{skipping \string\HL}% \statusmessage
+ \else
+ \ifnum\tableactionstate=\tablemidrowstate
+ \writestatus\m!TABLE{change \string\MR\space into \string\LR/\string\SR}%
+ \orelse\ifnum\tableactionstate=\tablefirstrowstate
+ \writestatus\m!TABLE{change \string\MR\space into \string\SR}%
+ \fi
+ \bgroup
+ \global\c_tabl_table_hrule_thickness_factor\m_tabl_table_HLheight\relax
+ \ifparameter#1\or
+ \glet\m_tabl_table_hrule_color\empty
+ \rawprocesscommalist[#1]\tabl_table_hrulecommand
+ \ifempty\m_tabl_table_hrule_color\else
+ \switchtocolor[\m_tabl_table_hrule_color]%
+ \fi
+ \fi
+ \tabl_table_normal_full_rule
+ \egroup
+ \tabl_table_account_width
+ \fi
+ \tabl_table_set_action\tablerulestate
+ \nobreak
+ \egroup}
+
+\aliased\let\tabl_table_HC\tabl_table_HL % for mojca
+
+%D \startitemize[3*ruim]
+%D \sym{\type{\NL}} a vertical skip
+%D \sym{\type{\NR}} goto the next row
+%D \sym{\type{\NC}} goto the next column
+%D \sym{\type{\FC}} a first column
+%D \sym{\type{\MC}} a mid column
+%D \sym{\type{\LC}} a last column
+%D \stopitemize
+
+% \starttable[|||]
+% \VL text \VL text \VL \AR
+% \TB[small]
+% \VL text \VL text \VL \AR
+% \TB[4*big]
+% \VL text \VL text \VL \AR
+% \stoptable
+
+% n+1 uitleggen
+
+\appendtoks
+ \enforced\let\TB\tabl_table_TB
+ \enforced\let\NL\tabl_table_NL % old
+ \enforced\let\NR\tabl_table_NR
+ \enforced\let\NC\tabl_table_NC
+ \enforced\let\FC\tabl_table_NC
+ \enforced\let\MC\tabl_table_NC
+ \enforced\let\LC\tabl_table_NC
+\to \localtabledefinitions
+
+\permanent\tolerant\protected\def\tabl_table_TB[#1]%
+ {\tabl_tables_chuck_auto_row
+ \tabl_table_finish_row
+ \noalign\bgroup
+ \blank[\iftok{#1}\emptytoks\directtablesparameter\c!NL\else#1\fi]%
+ \nobreak
+ \egroup}
+
+\aliased\let\tabl_table_NL\tabl_table_TB
+
+\protected\def\tabl_table_NR
+ {\global\currenttablecolumn\zerocount
+ \tabl_table_normal_line_ending
+ \noalign\bgroup
+ \nobreak
+ \tabl_table_set_action\tableunknownstate
+ \egroup}
+
+\protected\def\tabl_table_NC
+ {\tabl_tables_check_auto_row
+ \global\advance\currenttablecolumn \plusone
+ \tabl_table_normal_no_bar}
+
+%D \startitemize[3*broad]
+%D \sym{\type{\DL}}
+%D \sym{\type{\DV}} (\type{\VD})
+%D \sym{\type{\DC}}
+%D \sym{\type{\DR}}
+%D \stopitemize
+
+\newconditional\c_tabl_table_is_division
+
+\appendtoks
+ \global\setfalse\c_tabl_table_is_division
+ \enforced\let\DL\tabl_table_DL
+ \enforced\let\DC\tabl_table_DC
+ \enforced\let\DV\tabl_table_DV
+ \enforced\let\DR\tabl_table_DR
+\to \localtabledefinitions
+
+\def\tabl_table_check_division
+ {\ifconditional\c_tabl_table_is_division\else
+ \tabl_tables_chuck_auto_row
+ \global\currenttablecolumn\zerocount
+ \global\settrue\c_tabl_table_is_division
+ \fi}
+
+\def\tabl_table_drulecommand#1% global assignments
+ {\doifelsenumber{#1}
+ {\ifcase\c_tabl_table_drule_span
+ \global\c_tabl_table_drule_span#1\relax
+ \else
+ \global\c_tabl_table_hrule_thickness_factor#1\relax
+ \global\multiply\c_tabl_table_hrule_thickness_factor\m_tabl_table_VLwidth\relax
+ \fi}
+ {\xdef\m_tabl_table_hrule_color{#1}}}
+
+\permanent\tolerant\protected\def\tabl_table_DL[#1]%
+ {\tabl_table_check_division
+ \ifnum\tableactionstate=\tablerulestate
+ \writestatus\m!TABLE{skipping \string\DL}%
+ \else
+ \ifnum\tableactionstate=\tablemidrowstate
+ \writestatus\m!TABLE{change \string\MR\space into \string\LR/\string\SR}%
+ \orelse\ifnum\tableactionstate=\tablefirstrowstate
+ \writestatus\m!TABLE{change \string\MR\space into \string\SR}%
+ \fi
+ \tabl_table_set_action\tableunknownstate
+ \global\c_tabl_table_hrule_thickness_factor\m_tabl_table_HLheight\relax
+ \global\c_tabl_table_drule_span\zerocount
+ \iftok{#1}\emptytoks\else
+ \glet\m_tabl_table_hrule_color\empty
+ \rawprocesscommalist[#1]\tabl_table_drulecommand
+ % \ifempty\m_tabl_table_hrule_color\else
+ % \switchtocolor[\m_tabl_table_hrule_color]% see *DL*
+ % \fi
+ \fi
+ \ifcase\c_tabl_table_drule_span
+ \global\advance\currenttablecolumn \plusone
+ \tabl_table_normal_single_rule
+ \or
+ \global\advance\currenttablecolumn \plustwo
+ \tabl_table_normal_single_rule
+ \else
+ \global\advance\currenttablecolumn \plusone
+ \tabl_table_normal_multi_rule
+ \fi
+ \fi}
+
+\permanent\protected\def\tabl_table_DV
+ {\tabl_table_DCV\tabl_table_normal_line_simple_bar}
+
+\permanent\protected\def\tabl_table_DC
+ {\tabl_table_DCV\tabl_table_normal_no_bar}
+
+\permanent\protected\def\tabl_table_DCV#1%
+ {\tabl_table_check_division
+ \tabl_tables_check_auto_row
+ \global\advance\currenttablecolumn \plusone
+ #1}
+
+\permanent\protected\def\tabl_table_DR
+ {\global\currenttablecolumn\zerocount % nog check
+ \tabl_table_normal_line_ending
+ \noalign\bgroup
+ \nobreak
+ \global\setfalse\c_tabl_table_is_division
+ \tabl_table_account_width % temporary solution
+ \tabl_table_set_action\tablerulestate
+ \egroup}
+
+\def\tabl_table_account_width
+ {\scratchdimen\d_tabl_table_line_thickness_unit}
+
+\permanent\def\tabl_table_TWO {\use\plustwo}
+\permanent\def\tabl_table_THREE{\use\plusthree}
+\permanent\def\tabl_table_FOUR {\use\plusfour}
+\permanent\def\tabl_table_FIVE {\use\plusfive}
+\permanent\def\tabl_table_SIX {\use\plussix}
+
+\aliased\let\LOW \relax
+\aliased\let\TWO \relax
+\aliased\let\THREE\relax
+\aliased\let\FOUR \relax
+\aliased\let\FIVE \relax
+\aliased\let\SIX \relax
+\aliased\let\SPAN \relax
+\aliased\let\REF \relax
+
+\appendtoks
+ \enforced\let\TWO \tabl_table_TWO
+ \enforced\let\THREE\tabl_table_THREE
+ \enforced\let\FOUR \tabl_table_FOUR
+ \enforced\let\FIVE \tabl_table_FIVE
+ \enforced\let\SIX \tabl_table_SIX
+ \enforced\let\SPAN \use
+ \enforced\let\REF \tabl_table_reformat
+\to \localtabledefinitions
+
+\installcorenamespace{tables}
+\installcorenamespace{tabledistance}
+\installcorenamespace{tablealign}
+
+\installsetuponlycommandhandler \??tables {tables} % some day we can have named tables
+
+\defcsname\??tabledistance\v!none \endcsname{\tabl_table_OpenUp00\enforced\def\LOW{\Lower6 }}
+\defcsname\??tabledistance\v!small \endcsname{\tabl_table_OpenUp00\enforced\def\LOW{\Lower6 }} % == baseline
+\defcsname\??tabledistance\v!medium\endcsname{\tabl_table_OpenUp11\enforced\def\LOW{\Lower7 }}
+\defcsname\??tabledistance\v!big \endcsname{\tabl_table_OpenUp22\enforced\def\LOW{\Lower8 }}
+
+\appendtoks
+ \expandnamespaceparameter\??tabledistance\directtablesparameter\c!distance\v!medium
+\to \localtabledefinitions
+
+\defcsname\??tablealign\v!right \endcsname{\def\tabl_table_paralignment{\raggedright}}
+\defcsname\??tablealign\v!left \endcsname{\def\tabl_table_paralignment{\raggedleft}}
+\defcsname\??tablealign\v!middle \endcsname{\def\tabl_table_paralignment{\raggedcenter}}
+\defcsname\??tablealign\s!unknown\endcsname{\def\tabl_table_paralignment{\notragged}}
+
+\appendtoks
+ \doifelse{\directtablesparameter\c!distance}\v!none
+ {\tablerowfactor\zerocount}
+ {\tablerowfactor\plustwo }%
+\to \localtabledefinitions
+
+\appendtoks
+ \expandnamespaceparameter\??tablealign\directtablesparameter\c!align\s!unknown
+ \assignalfadimension{\directtablesparameter\c!VL}\m_tabl_table_VLwidth 246%
+ \assignalfadimension{\directtablesparameter\c!HL}\m_tabl_table_HLheight246%
+\to \everysetuptables
+
+\def\tabl_table_local_setups
+ {\directtablesparameter\c!commands\relax
+ \usebodyfontparameter\directtablesparameter
+ \d_tabl_table_line_thickness_unit\dimexpr\directtablesparameter\c!rulethickness/\tablelinethicknessfactor\relax
+ \edef\p_tabl_table_height{\directtablesparameter\c!height}%
+ \edef\p_tabl_table_depth{\directtablesparameter\c!depth}%
+ \ifx\p_tabl_table_height\v!strut
+ \let\tablestrutheightfactor\tablestrutheightfactor
+ \else
+ \let\tablestrutheightfactor\p_tabl_table_height
+ \fi
+ \ifx\p_tabl_table_depth\v!strut
+ \let\tablestrutdepthfactor\tablestrutdepthfactor
+ \else
+ \let\tablestrutdepthfactor\p_tabl_table_depth
+ \fi
+ \edef\tablestrutheightfactor{\withoutpt\the\dimexpr10\dimexpr\tablestrutheightfactor\points}%
+ \edef\tablestrutdepthfactor {\withoutpt\the\dimexpr10\dimexpr\tablestrutdepthfactor \points}%
+ \d_tabl_table_strut_unit \dimexpr\normalbaselineskip/12\relax % 12 is default bodyfont
+ \d_tabl_table_kern_unit .5em\relax
+ \s_tabl_table_inter_column_space_unit.5em plus 1fil minus .25em\relax
+ \d_tabl_table_column_width_unit \d_tabl_table_kern_unit
+ \d_tabl_table_kern_unit \d_tabl_table_kern_unit}
+
+%D As one can see, we didn't only add color, but also more control over spacing.
+%D
+%D \startbuffer[a]
+%D \starttable[|c|]
+%D \HL
+%D \VL \strut test \VL \FR
+%D \VL \strut test \VL \MR
+%D \VL \strut test \VL \MR
+%D \VL \strut test \VL \LR
+%D \HL
+%D \stoptable
+%D \stopbuffer
+%D
+%D \startbuffer[b]
+%D \starttabulate[|c|]
+%D \HL
+%D \NC test \NC \NR
+%D \NC test \NC \NR
+%D \NC test \NC \NR
+%D \NC test \NC \NR
+%D \HL
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D In the next example, the first table is defined as:
+%D
+%D \typebuffer[a]
+%D
+%D and the second one as:
+%D
+%D \typebuffer[b]
+%D
+%D The first table is typeset using the default height and depth factors .8 and .4.
+%D The second table has both factors set to \type {strut}, and the third table shows
+%D what happens when we set the values to zero. The rightmost table is typeset using
+%D the tabulate environment.
+%D
+%D \startcombination[4*1]
+%D {$\vcenter{\getbuffer[a]}$}
+%D {\hbox{h=.8 d=.4}}
+%D {\setuptables[height=strut,depth=strut]$\vcenter{\getbuffer[a]}$}
+%D {\hbox{h=d=\type{strut}}}
+%D {\setuptables[height=0,depth=0]$\vcenter{\getbuffer[a]}$}
+%D {\hbox{h=d=0}}
+%D {$\vcenter{\getbuffer[b]}$}
+%D {\hbox{tabulate}}
+%D \stopcombination
+
+\setuptables
+ [\c!HL=\v!medium,
+ \c!VL=\v!medium,
+ \c!NL=\v!small,
+ \c!frame=,
+ \c!align=\v!right,
+ \c!depth=.40, % \v!strut
+ \c!height=.80, % \v!strut
+ \c!textwidth=,
+ \c!rulethickness=\linewidth,
+ \c!rulecolor=,
+ \c!distance=\v!medium,
+ \c!bodyfont=,
+ \c!commands=,
+ \c!background=,
+ \c!backgroundcolor=,
+ \c!split=\v!auto,
+ \c!openup=\zeropoint]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-tbl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-tbl.mkxl
new file mode 100644
index 00000000000..88cd791e488
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-tbl.mkxl
@@ -0,0 +1,2949 @@
+%D \module
+%D [ file=core-tbl,
+%D version=1998.11.03,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Text Flow Tabulation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / Tabulation}
+
+\unprotect
+
+\registerctxluafile{tabl-tbl}{} % experiment
+
+%D I can probably reimplement this using a \LUATEX\ combination but it does not pay
+%D of in development time. If I need something else I will write it from scratch
+%D anyway. This module looks a bit complex which is a consequence of it dealing with
+%D paragraphs being split over pages and that there are several passes over the data
+%D set. We can probably do some cleanup (combine/split).
+%D
+%D Caching the preamble does not save much (compared to other bits and pieces of
+%D \CONTEXT). There are not that many ways to deal with preambles and this is just
+%D one of them. The keys are somewhat similar to those of the \TABLE\ package.
+
+% |p2|p3| 2:3 -> spanning (maybe)
+%
+% In-text tabbing environment
+%
+% \starttabulate[| separated template] % eg [|l|p|] or [|l|p|p|]
+% \NC ... \NC ... \NC\NR
+% \stoptabulate
+%
+% with: two pass auto width calculation when no p-width
+% specified, even with multiple p's, see examples.
+%
+% TaBlE compatible specifications:
+%
+% l align column/paragraph left
+% r align column/paragraph right
+% c align column/paragraph center
+% p p(dimen) of automatisch als alleen p
+% w column width
+% f font#1
+% A {alignmentoptions}
+% B bold
+% I italic
+% S slanted
+% T type
+% R roman
+% m math
+% M display math
+% h hook (inner level or par lines)
+% b before (may be command#1)
+% a after
+% i i<n> skip left of column
+% j i<n> skip right of column
+% k i<n> skip around column
+% d digits (~)
+%
+% C [LMRT] {color} % T is text color
+%
+% | {color,n}
+%
+% s setups
+%
+% g g{char} align at char
+% . align at .
+% , align at ,
+%
+% Still to be done
+%
+% N math numbers (best hook into existing digits mechanism)
+% n numbers (best hook into existing digits mechanism)
+% Q math numbers (best hook into existing digits mechanism)
+% q numbers (best hook into existing digits mechanism)
+% ~ \hskip.5em
+% | check
+%
+% nesting
+%
+% 10 evt auto stack; dan wel andere signal dan void nodig
+%
+% present but not yet 100% ok
+%
+% \TL [width,color] bottom hrule
+% \FL [width,color] first hrule
+% \ML [width,color] mid hrule (with auto split)
+% \LL [width,color] bottom hrule
+% \BL [width,color] last hrule
+%
+% \HL [width,color] top rule
+% \VL [width,color]
+%
+% \CC \CL \CM \CR color
+%
+% \EQ \RQ \HQ equal (raw, hook)
+% \NC \RC \HC normal (raw, hook)
+%
+% \NR checked break
+% \NB no break
+%
+% \HR : rule with lineheight
+%
+% \autotabulaterule : with lineheight, not first/last
+% \autotabulateline : spaced, not first/last
+% \tabulaterule : with lineheight
+% \tabulateline : spaced
+%
+% tricky: align scans ahead, over # and expands ones before
+% while doing
+%
+% new:
+%
+% \starttabulate[|cg{.}|cg{,}|cg{,}|]
+% \NC period \NC comma \NC comma \NC\NR
+% \NG 100.000,00 \NG 100.000,00 \NG 100,00 \NC\NR
+% \NG 10.000,00 \NG 10.000,00 \NG 1000,00 \NC\NR
+% \NG 100,00 \NG 100,00 \NG 10,00 \NC\NR
+% \NG 10 \NG 10 \NG 0,00 \NC\NR
+% \stoptabulate
+%
+% \starttabulate[|c.|c,|c,|]
+% \NC period \NC comma \NC comma \NC\NR
+% \NG 100.000,00 \NG 100.000,00 \NG 100,00 \NC\NR
+% \NG 10.000,00 \NG 10.000,00 \NG 1000,00 \NC\NR
+% \NG 100,00 \NG 100,00 \NG 10,00 \NC\NR
+% \NG 10 \NG 10 \NG 0,00 \NC\NR
+% \stoptabulate
+
+% nice demo (for BG)
+%
+% \starttabulate[|r|b{$\star$}|ra{\percent}|b{=}|r|]
+% \NC 500 \NC \NC 60 \NC \NC 300 \NC \NR
+% \NC 500 \NC \NC 55 \NC \NC 275 \NC \NR
+% \NC 500 \NC \NC 50 \NC \NC 250 \NC \NR
+% \NC 500 \NC \NC 45 \NC \NC 225 \NC \NR
+% \NC 500 \NC \NC 40 \NC \NC 200 \NC \NR
+% \NC 500 \NC \NC 35 \NC \NC 175 \NC \NR
+% \NC 500 \NC \NC 30 \NC \NC 150 \NC \NR
+% \NC 500 \NC \NC 25 \NC \NC 125 \NC \NR
+% \NC 500 \NC \NC 20 \NC \NC 100 \NC \NR
+% \stoptabulate
+
+\newtoks \t_tabl_tabulate_preamble
+\newtoks \t_tabl_tabulate_before
+\newtoks \t_tabl_tabulate_after
+\newtoks \t_tabl_tabulate_bmath
+\newtoks \t_tabl_tabulate_emath
+\newtoks \t_tabl_tabulate_font
+\newtoks \t_tabl_tabulate_settings
+\newtoks \t_tabl_tabulate_dummy
+\newtoks \t_tabl_tabulate_every_row
+\newtoks \t_tabl_tabulate_every_after_row
+\newtoks \t_tabl_tabulate_every_real_row
+
+\newtoks \t_tabl_tabulate_initializers_first
+\newtoks \t_tabl_tabulate_initializers_second
+
+\newcount \c_tabl_tabulate_nofauto
+\newcount \c_tabl_tabulate_columns
+\newcount \c_tabl_tabulate_column
+\newcount \c_tabl_tabulate_plines_min
+\newcount \c_tabl_tabulate_plines_max
+\newcount \c_tabl_tabulate_max_colorcolumn
+\newcount \c_tabl_tabulate_max_vrulecolumn
+\newcount \c_tabl_tabulate_repeathead
+\newcount \c_tabl_tabulate_noflines
+\newcount \c_tabl_tabulate_totalnoflines
+\newcount \c_tabl_tabulate_minusnoflines
+\newcount \c_tabl_tabulate_align
+\newcount \c_tabl_tabulate_nofrealrows
+\newcount \c_tabl_tabulate_autocolor
+
+\newcount \c_tabl_tabulate_nofcolumns % set at the lua end by parser
+\newcount \c_tabl_tabulate_has_rule_spec_first % set at the lua end by parser (for the moment a count)
+\newcount \c_tabl_tabulate_has_rule_spec_last % set at the lua end by parser (for the moment a count)
+
+\newconditional \c_tabl_tabulate_nopbreak
+\newconditional \c_tabl_tabulate_firstflushed
+\newconditional \c_tabl_tabulate_equal
+\newconditional \c_tabl_tabulate_split \settrue\c_tabl_tabulate_split
+\newconditional \c_tabl_tabulate_automode
+\newconditional \c_tabl_tabulate_handlepbreak \settrue\c_tabl_tabulate_handlepbreak
+\newconditional \c_tabl_tabulate_autorulespacing \settrue\c_tabl_tabulate_autorulespacing
+\newconditional \c_tabl_tabulate_someamble
+\newconditional \c_tabl_tabulate_tolerant_break
+\newconditional \c_tabl_tabulate_splitoff_whitespace
+\newconditional \c_tabl_tabulate_pwidth_set
+\newconditional \c_tabl_tabulate_reshape
+
+\newdimen \d_tabl_tabulate_width_p
+\newdimen \d_tabl_tabulate_width_w
+\newdimen \d_tabl_tabulate_width
+\newdimen \d_tabl_tabulate_unit
+\newdimen \d_tabl_tabulate_height_p_max
+\newdimen \d_tabl_tabulate_vrulethickness_default
+\newdimen \d_tabl_tabulate_hrulethickness_default
+\newdimen \d_tabl_tabulate_vrulethickness
+\newdimen \d_tabl_tabulate_hrulethickness % not used
+\newdimen \d_tabl_tabulate_vrulethickness_local
+\newdimen \d_tabl_tabulate_hrulethickness_local
+\newdimen \d_tabl_tabulate_indent
+\newdimen \d_tabl_tabulate_splitoff_betweenskip
+\newdimen \d_tabl_tabulate_margin
+
+\newskip \s_tabl_tabulate_pre
+\newskip \s_tabl_tabulate_post
+\newskip \s_tabl_tabulate_first
+\newskip \s_tabl_tabulate_last
+\newskip \s_tabl_tabulate_separator
+
+\newbox \b_tabl_tabulate
+
+\newconstant \c_tabl_tabulate_pass
+\newconstant \c_tabl_tabulate_type
+\newconstant \c_tabl_tabulate_kind % 1=strong 2=equals
+\newconstant \c_tabl_tabulate_splitlinemode \c_tabl_tabulate_splitlinemode\plusone
+\newconstant \c_tabl_tabulate_colorspan
+\newconstant \c_tabl_tabulate_localcolorspan
+\newconstant \c_tabl_tabulate_modus
+
+%newconditional \c_tabl_pre_is_set
+\newconditional \c_tabl_post_is_set
+
+\let \m_tabl_tabulate_separator_factor \empty % fraction
+
+\aliased\let\tabulatesplitlinemode\c_tabl_tabulate_splitlinemode % temp hack, we need an interface
+
+\newtoks \everytabulatepar % where used ?
+\newtoks \everytabulate % public ?
+
+\permanent\protected\def\tolerantTABLEbreaktrue{\settrue\c_tabl_tabulate_tolerant_break} % used in styles !
+
+\permanent\def\noftabulaterows{\number\c_tabl_tabulate_noflines} % handy for testing if a table is empty
+
+\installcorenamespace{tabulatebox}
+\installcorenamespace{tabulatesetup}
+\installcorenamespace{tabulatehook}
+\installcorenamespace{tabulatesplit}
+\installcorenamespace{tabulateseparator}
+\installcorenamespace{tabulatecolor}
+\installcorenamespace{tabulateheader}
+\installcorenamespace{tabulatealigning}
+\installcorenamespace{tabulatepreamble}
+\installcorenamespace{tabulatevrule}
+
+\installcorenamespace{tabulatehead}
+\installcorenamespace{tabulatefoot}
+\installcorenamespace{tabulatenext}
+
+\prependtoks
+ \global\c_tabl_tabulate_nofrealrows\zerocount
+\to \t_tabl_tabulate_initializers_first
+
+\prependtoks
+ \global\c_tabl_tabulate_nofrealrows\zerocount
+\to \t_tabl_tabulate_initializers_second
+
+\prependtoks
+ \global\advance\c_tabl_tabulate_nofrealrows\plusone
+\to \t_tabl_tabulate_every_real_row
+
+\permanent\def\b_tabl_tabulate_current#1%
+ {\csname\??tabulatebox\number#1\endcsname} % beware, a synonym
+
+\def\tabl_tabulate_initialize_boxes#1%
+ {\scratchcounter#1\relax
+ \tabl_tabulate_initialize_boxes_step}
+
+\def\tabl_tabulate_initialize_boxes_step
+ {\ifnum\scratchcounter>\zerocount
+ \tabl_tabulate_initialize_box\scratchcounter
+ \advance\scratchcounter\minusone
+ \expandafter\tabl_tabulate_initialize_boxes_step
+ \fi}
+
+\def\tabl_tabulate_initialize_box#1% also used elsewhere
+ {\ifcsname\??tabulatebox\number#1\endcsname
+ \tabl_tabulate_initialize_box_yes
+ \else
+ \tabl_tabulate_initialize_box_nop#1%
+ \fi}
+
+\def\tabl_tabulate_initialize_box_yes {\global \setbox\lastnamedcs\emptybox}
+\def\tabl_tabulate_initialize_box_nop#1{\expandafter\newbox\csname\??tabulatebox\number#1\endcsname}
+
+\tabl_tabulate_initialize_boxes{16} % not really needed
+
+% 0 = NC column next EQ equal column
+% 1 = RC column raw RQ equal column raw
+% 2 = HC column hook HQ equal column hook
+
+% [|lg{.}|] => \NG 12.34 \NC
+
+\def\tabl_tabulate_nobreak_inject_tracer
+ {\red % maybe use the fast color switcher here
+ \hrule\s!height.5\linewidth\s!depth.5\linewidth
+ \par
+ \kern-\linewidth
+ \tabl_tabulate_break_no}
+
+\installtextracker
+ {tables.tabulate.breaks}
+ {\let\tabl_tabulate_break_no_tracer\tabl_tabulate_nobreak_inject_tracer}
+ {\let\tabl_tabulate_break_no_tracer\donothing}
+
+\let\tabl_tabulate_break_no_tracer\donothing
+
+\def\tabl_tabulate_nobreak_inject_indeed
+ {\tabl_tabulate_break_no
+ \tabl_tabulate_break_no_tracer}
+
+\def\tabl_tabulate_nobreak_inject
+ {\noalign{\tabl_tabulate_nobreak_inject_indeed}}
+
+\protected\def\tabl_tabulate_hook_check
+ {\ifnum\c_tabl_tabulate_type<\plustwo
+ \glet\tabl_tabulate_hook\tabl_tabulate_hook_nop
+ \else
+ \glet\tabl_tabulate_hook\tabl_tabulate_hook_yes
+ \fi}
+
+\protected\def\tabl_tabulate_setups_check
+ {\begincsname\??tabulatesetup\the\c_tabl_tabulate_column\endcsname}
+
+\protected\def\tabl_tabulate_entry_before{\ignorespaces\tabl_tabulate_hook}
+\protected\def\tabl_tabulate_entry_after {\unskip\unskip\ifmmode\else\endgraf\fi}
+
+\protected\def\tabl_tabulate_shaped_par_begin
+ {\dowithnextboxcs\tabl_tabulate_shaped_par_finish\vbox\bgroup}
+
+\def\tabl_tabulate_shaped_par_finish
+ {\clf_doreshapeframedbox\nextbox\relax
+ \ifvmode\unvbox\else\box\fi\nextbox}
+
+\let\tabl_tabulate_shaped_par_end\egroup
+
+\ifdefined\dotagtabulatecell \else \aliased\let\dotagtabulatecell \relax \fi
+\ifdefined\dotagtabulatesignal \else \aliased\let\dotagtabulatesignal\relax \fi
+
+\protected\def\tabl_tabulate_check_local_color_first#1#2%
+ {\relax}
+
+\protected\def\tabl_tabulate_check_local_color_second#1#2%
+ {\relax
+ \ifempty\m_tabl_tabulate_color_local
+ \xdef\m_tabl_tabulate_color{#1}%
+ \else
+ \glet\m_tabl_tabulate_color\m_tabl_tabulate_color_local
+ \glet\m_tabl_tabulate_color_local\empty
+ \fi
+ \ifcase\c_tabl_tabulate_localcolorspan
+ \global\c_tabl_tabulate_colorspan#2\relax
+ \else
+ \global\c_tabl_tabulate_colorspan\c_tabl_tabulate_localcolorspan
+ \global\c_tabl_tabulate_localcolorspan\zerocount
+ \fi}
+
+\protected\def\tabl_tabulate_check_local_vrule_thickness#1%
+ {\relax
+ \ifcase\d_tabl_tabulate_vrulethickness_local
+ \global\d_tabl_tabulate_vrulethickness#1\relax
+ \else
+ \global\d_tabl_tabulate_vrulethickness\d_tabl_tabulate_vrulethickness_local
+ \global\d_tabl_tabulate_vrulethickness_local\zeropoint
+ \fi}
+
+\protected\def\tabl_tabulate_check_local_vrule_color_first#1%
+ {\relax}
+
+\protected\def\tabl_tabulate_check_local_vrule_color_second#1%
+ {\relax
+ \ifempty\m_tabl_tabulate_vrule_color_local
+ \xdef\m_tabl_tabulate_vrule_color{#1}%
+ \else
+ \glet\m_tabl_tabulate_vrule_color\m_tabl_tabulate_vrule_color_local
+ \glet\m_tabl_tabulate_vrule_color_local\empty
+ \fi}
+
+\let\tabl_tabulate_check_local_color \gobbletwoarguments
+\let\tabl_tabulate_check_local_vrule_color\gobbleoneargument
+
+\appendtoks
+ \let\tabl_tabulate_check_local_color \tabl_tabulate_check_local_color_first
+ \let\tabl_tabulate_check_local_vrule_color\tabl_tabulate_check_local_vrule_color_first
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \let\tabl_tabulate_check_local_color \tabl_tabulate_check_local_color_second
+ \let\tabl_tabulate_check_local_vrule_color\tabl_tabulate_check_local_vrule_color_second
+\to \t_tabl_tabulate_initializers_second
+
+% \protected % we can expand this one
+\def\tabl_tabulate_inject_pre_skip#1%
+ {\ifdim#1>\zeropoint
+ \kern#1\relax % was \hskip
+ \orelse\ifnum\c_tabl_tabulate_column=\zerocount
+ \ifconditional\c_tabl_tabulate_autorulespacing
+ \ifcase\c_tabl_tabulate_has_rule_spec_first\else
+ \kern\s_tabl_tabulate_first\relax % was \hskip
+ \fi
+ \fi
+ \fi}
+
+% \protected % we can expand this one
+\def\tabl_tabulate_inject_post_skip#1#2%
+ {\ifdim#1>\zeropoint
+ \kern\ifnum\c_tabl_tabulate_columns=\c_tabl_tabulate_nofcolumns#2\else#1\fi\relax
+ \orelse\ifnum\c_tabl_tabulate_columns=\c_tabl_tabulate_nofcolumns
+ \ifconditional\c_tabl_tabulate_autorulespacing
+ \ifcase\c_tabl_tabulate_has_rule_spec_last\else
+ \kern\s_tabl_tabulate_last\relax % was \hskip
+ \fi
+ \fi
+ \fi}
+
+\let\tabl_tabulate_hook_b\donothing
+\let\tabl_tabulate_hook_e\donothing
+
+\let\tabl_tabulate_hook_g\donothing
+
+\def\tabl_tabulate_set_preamble_step#1#2% only makes sense for many tabulates
+ {\etoksapp\t_tabl_tabulate_preamble{%
+ \tabl_tabulate_check_local_vrule_thickness\constantdimenargument\d_tabl_tabulate_vrulethickness
+ \tabl_tabulate_check_local_vrule_color\constantemptyargument\m_tabl_tabulate_vrule_color
+ \tabl_tabulate_check_local_color\constantemptyargument\m_tabl_tabulate_color\constantnumberargument\c_tabl_tabulate_colorspan
+ \tabl_tabulate_color_side_right
+ \aligntab
+ \tabl_tabulate_column_vrule_inject
+ \tabl_tabulate_color_side_left
+ \tabl_tabulate_inject_pre_skip{\the\dimexpr\s_tabl_tabulate_pre}% get rid of plus
+ \alignmark\alignmark
+ \aligntab
+ \tabl_tabulate_color_side_both
+ \global\c_tabl_tabulate_colorspan\zerocount
+ \global\c_tabl_tabulate_column\constantnumber\c_tabl_tabulate_columns
+ \tabl_tabulate_hook_g
+ \tabl_tabulate_setups_check % unexpandable
+ \tabl_tabulate_hook_check % unexpandable
+ \ifzeropt\d_tabl_tabulate_width
+ \ifcase\c_tabl_tabulate_modus\else
+ \settrue\c_tabl_tabulate_automode
+ \fi
+ \else
+ \ifcase\c_tabl_tabulate_modus
+ \hbox to
+ \else
+ \hsize
+ \fi
+ \the\d_tabl_tabulate_width
+ \fi
+ \bgroup
+ \tabl_tabulate_bbskip
+ \bgroup % we cannot combine the if because a cell may have only one ##
+ \tabl_tabulate_hook_b
+ \c_tabl_tabulate_align\constantnumber\c_tabl_tabulate_align % needed in tag passing
+ \ifempty\m_tabl_tabulate_alignment \else
+ \spac_align_use_now{\m_tabl_tabulate_alignment}%
+ \fi
+ \noexpand\dostarttagged\noexpand\t!tabulatecell\noexpand\empty
+ \noexpand\dotagtabulatecell
+ \noexpand#1%
+ \ifconditional\c_tabl_tabulate_reshape
+ \tabl_tabulate_shaped_par_begin
+ \fi
+ \dotagtabulatesignal % empty cells .. todo (can be removed as soon as build)
+ \noexpand\ifnum\noexpand\c_tabl_tabulate_type=\plusone\noexpand\else
+ \the\t_tabl_tabulate_bmath % maybe later? can interfere with char 0
+ \the\t_tabl_tabulate_font
+ \the\t_tabl_tabulate_settings
+ \the\t_tabl_tabulate_before
+ \ifempty\m_tabl_tabulate_text_color
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\colo_helpers_direct_activate
+ \fi\m_tabl_tabulate_text_color
+ \noexpand\fi
+ % grouping needs to be outside macros (or expandable), nice test
+ % example \NC \string \aligntab \NC which will fail otherwise (mk)
+ \bgroup
+ \tabl_tabulate_entry_before
+ \alignmark\alignmark
+ \tabl_tabulate_entry_after
+ \egroup
+ \noexpand\ifnum\noexpand\c_tabl_tabulate_type=\plusone\noexpand\else
+ \the\t_tabl_tabulate_after
+ \the\t_tabl_tabulate_emath
+ \noexpand\fi
+ \ifconditional\c_tabl_tabulate_reshape
+ \tabl_tabulate_shaped_par_end
+ \fi
+ \noexpand#2%
+ \tabl_tabulate_hook_e
+ \egroup
+ \egroup
+ \aligntab
+ \noexpand\dostoptagged
+ \tabl_tabulate_inject_post_skip
+ {\the\ifconditional\c_tabl_post_is_set\s_tabl_tabulate_post\else\s_tabl_tabulate_last\fi}%
+ {\the\s_tabl_tabulate_post}%
+ \alignmark\alignmark
+ }%
+ \toksapp\t_tabl_tabulate_dummy{\NC}%
+ \s_tabl_tabulate_pre.5\d_tabl_tabulate_unit\relax
+ \ifnum\c_tabl_tabulate_columns<\numexpr\c_tabl_tabulate_nofcolumns-\plusone\relax
+ \s_tabl_tabulate_post\s_tabl_tabulate_pre
+ \else
+ \s_tabl_tabulate_post\zeropoint
+ \fi
+ %\let\gettabulateexit\dogettabulateexit % still needed ?
+ \d_tabl_tabulate_width\zeropoint
+ %setfalse\c_tabl_pre_is_set
+ \setfalse\c_tabl_post_is_set}
+
+\permanent\protected\def\installtabulatepreambleoption#1#2%
+ {\defcsname\??tabulatepreamble\string#1\endcsname{#2}}%
+
+\installtabulatepreambleoption{x}{\c_tabl_tabulate_align\zerocount
+ \tabl_tabulate_set_preamble} % internal
+\installtabulatepreambleoption{l}{\c_tabl_tabulate_align\plusone
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{r}{\c_tabl_tabulate_align\plustwo
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{c}{\c_tabl_tabulate_align\plusthree
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{p}{\tabl_tabulate_set_paragraph}
+\installtabulatepreambleoption{s}{\tabl_tabulate_set_setups}
+\installtabulatepreambleoption{w}{\tabl_tabulate_set_width}
+\installtabulatepreambleoption{f}{\tabl_tabulate_set_font}
+\installtabulatepreambleoption{B}{\t_tabl_tabulate_font{\bf}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{I}{\t_tabl_tabulate_font{\it}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{S}{\t_tabl_tabulate_font{\sl}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{T}{\t_tabl_tabulate_font{\tt}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{R}{\t_tabl_tabulate_font{\rm}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{m}{\t_tabl_tabulate_bmath{\normalstartimath}%
+ \t_tabl_tabulate_emath{\normalstopimath}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{M}{\t_tabl_tabulate_bmath{\normalstartimath\displaystyle}%
+ \t_tabl_tabulate_emath{\normalstopimath}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{h}{\tabl_tabulate_set_hook}
+\installtabulatepreambleoption{b}{\tabl_tabulate_set_before}
+\installtabulatepreambleoption{a}{\tabl_tabulate_set_after}
+\installtabulatepreambleoption{i}{\tabl_tabulate_set_preskip}
+\installtabulatepreambleoption{j}{\tabl_tabulate_set_posskip}
+\installtabulatepreambleoption{k}{\tabl_tabulate_set_preposskip}
+\installtabulatepreambleoption{e}{\toksapp\t_tabl_tabulate_settings{\global\settrue\c_tabl_tabulate_equal}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{g}{\tabl_tabulate_set_align}
+\installtabulatepreambleoption{.}{\tabl_tabulate_set_align.}
+\installtabulatepreambleoption{,}{\tabl_tabulate_set_align,}
+\installtabulatepreambleoption{C}{\tabl_tabulate_set_color_span}
+\installtabulatepreambleoption{d}{\toksapp\t_tabl_tabulate_settings{\fixedspaces}%
+ \tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{ }{\tabl_tabulate_set_preamble}
+\installtabulatepreambleoption{A}{\tabl_tabulate_set_alignment}
+
+%D We no longer deal with \type {~} here but map it onto \type {d} instead. Of
+%D course we could prefix a key with \type {\meaning} instead, which works ok (and
+%D is needed in order to pseudo expand \type {\next}, but is ugly at the same time.
+%D The type {d} stands for digitspace.
+
+%D \starttyping
+%D \installtabulatepreambleoption{~}{...} % see 'd'
+%D \stoptyping
+%D
+%D Also, as there is always a key, we no longer do some after assigment or future
+%D let but just pick up the key.
+
+\installtabulatepreambleoption\relax
+ {} % finished
+
+\def\tabl_tabulate_set_preamble#1%
+ {\ifcsname\??tabulatepreamble\string#1\endcsname
+ \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument
+ \else
+ \expandafter\tabl_tabulate_set_preamble_nop
+ \fi{#1}}
+
+\def\tabl_tabulate_set_preamble_yes#1%
+ {\csname\??tabulatepreamble\string#1\expandafter\endcsname}
+
+\def\tabl_tabulate_set_preamble_nop#1%
+ {\writestatus{tabulate}{unknown preamble key: #1}%
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_preskip#1%
+ {%settrue\c_tabl_pre_is_set
+ \doifelsenumber{#1}%
+ {\s_tabl_tabulate_pre#1\d_tabl_tabulate_unit\tabl_tabulate_set_preamble }%
+ {\s_tabl_tabulate_pre.5\d_tabl_tabulate_unit\tabl_tabulate_set_preamble#1}}
+
+\def\tabl_tabulate_set_posskip#1%
+ {\settrue\c_tabl_post_is_set
+ \doifelsenumber{#1}%
+ {\s_tabl_tabulate_post#1\d_tabl_tabulate_unit\tabl_tabulate_set_preamble }%
+ {\s_tabl_tabulate_post.5\d_tabl_tabulate_unit\tabl_tabulate_set_preamble#1}}
+
+\def\tabl_tabulate_set_preposskip#1%
+ {%settrue\c_tabl_pre_is_set
+ \settrue\c_tabl_post_is_set
+ \doifelsenumber{#1}%
+ {\s_tabl_tabulate_pre#1\d_tabl_tabulate_unit\s_tabl_tabulate_post\s_tabl_tabulate_pre\tabl_tabulate_set_preamble }%
+ {\s_tabl_tabulate_pre.5\d_tabl_tabulate_unit\s_tabl_tabulate_post\s_tabl_tabulate_pre\tabl_tabulate_set_preamble#1}}
+
+\def\tabl_tabulate_set_setups#1%
+ {\defcsname\??tabulatesetup\the\c_tabl_tabulate_columns\endcsname{\setups[#1]}%
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_hook#1%
+ {\defcsname\??tabulatehook\the\c_tabl_tabulate_columns\endcsname{#1}%
+ \tabl_tabulate_set_preamble}
+
+% begin of character align plugin
+
+\newconditional\c_tabl_auto_align_mode % reset later
+
+\def\tabl_tabulate_hook_g % partly expanded
+ {\ifconditional\c_tabl_auto_align_mode
+ \signalcharacteralign\c_tabl_tabulate_column{\c_tabl_tabulate_noflines+\plusone}%
+ \typo_charalign_adapt_font
+ \fi}
+
+\def\tabl_tabulate_set_align#1%
+ {\global\settrue\c_tabl_auto_align_mode
+ \setcharacteralign\c_tabl_tabulate_columns{#1}%
+ \tabl_tabulate_set_preamble}
+
+% end of character align plugin
+
+\def\tabl_tabulate_set_before#1%
+ {\t_tabl_tabulate_before{#1}%
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_after#1%
+ {\t_tabl_tabulate_after{#1}%
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_font#1%
+ {\t_tabl_tabulate_font{#1}%
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_pickup_width
+ {\doifelsenextparenthesis\tabl_tabulate_set_width_indeed\tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_width
+ {\setfalse\c_tabl_tabulate_pwidth_set
+ \c_tabl_tabulate_modus\zerocount
+ \tabl_tabulate_pickup_width}
+
+\def\tabl_tabulate_set_alignment#1%
+ {\edef\m_tabl_tabulate_alignment{#1}%
+ \spac_align_use_later\m_tabl_tabulate_alignment
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_paragraph
+ {\doifelsenextparenthesis
+ {\c_tabl_tabulate_modus\plusone
+ \settrue\c_tabl_tabulate_pwidth_set
+ \tabl_tabulate_pickup_width}
+ {\c_tabl_tabulate_modus\plustwo
+ \setfalse\c_tabl_tabulate_pwidth_set
+ \tabl_tabulate_set_preamble}}
+
+% \startbuffer
+% \toplinebox{\framed[width=3cm,height=2cm]{tufte}}
+% \stopbuffer
+% \starttabulate[|p(fixed)|p|]
+% \dorecurse{100}{\NC \getbuffer \NC test \par test \par \NC \NR}
+% \stoptabulate
+% \starttabulate[|p(fit)|p|]
+% \dorecurse{100}{\NC \getbuffer \NC test \par test \par \NC \NR}
+% \stoptabulate
+
+\def\tabl_tabulate_set_width_indeed(#1)%
+ {\processallactionsinset % can be made faster
+ [#1]%
+ [ \v!fit=>\c_tabl_tabulate_modus\plusthree,
+ \v!fixed=>\c_tabl_tabulate_modus\plusthree
+ \settrue\c_tabl_tabulate_nopbreak,
+ \v!auto=>\c_tabl_tabulate_modus\plusthree
+ \settrue\c_tabl_tabulate_reshape,
+ \s!unknown=>\d_tabl_tabulate_width#1\relax]%
+ \ifconditional\c_tabl_tabulate_pwidth_set
+ \global\advance\d_tabl_tabulate_width_p\d_tabl_tabulate_width % accumulated parwidth
+ \fi
+ \tabl_tabulate_set_preamble}
+
+% faster but seldom used
+%
+% \installcorenamespace{tabulatewidth}
+%
+% \setvalue{\??tabulatewidth\v!fit }{\c_tabl_tabulate_modus\plusthree}
+% \setvalue{\??tabulatewidth\v!fixed}{\c_tabl_tabulate_modus\plusthree\settrue\c_tabl_tabulate_nopbreak}
+% \setvalue{\??tabulatewidth\v!auto }{\c_tabl_tabulate_modus\plusthree\settrue\c_tabl_tabulate_reshape}
+%
+% \def\tabl_tabulate_set_width_step#1%
+% {\ifcsname\??tabulatewidth#1\endcsname
+% \lastnamedcs
+% \else
+% \d_tabl_tabulate_width#1\relax
+% \fi}
+%
+% \def\tabl_tabulate_set_width_indeed(#1)%
+% {\rawprocesscommacommand[#1]\tabl_tabulate_set_width_step
+% \ifconditional\c_tabl_tabulate_pwidth_set
+% \global\advance\d_tabl_tabulate_width_p\d_tabl_tabulate_width % accumulated parwidth
+% \fi
+% \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_raggedright {\ifnum\c_tabl_tabulate_type=\plusone \else\raggedright \fi}
+\def\tabl_tabulate_set_raggedcenter{\ifnum\c_tabl_tabulate_type=\plusone \else\raggedcenter\fi}
+\def\tabl_tabulate_set_raggedleft {\ifnum\c_tabl_tabulate_type=\plusone \else\raggedleft \fi}
+\def\tabl_tabulate_set_notragged {\ifnum\c_tabl_tabulate_type=\plusone \else\notragged \fi}
+\def\tabl_tabulate_set_hss {\ifnum\c_tabl_tabulate_type=\plusone \else\hss \fi} % never change this to a fill
+
+\def\tabl_tabulate_bskip_raggedright {\tabl_tabulate_bskip\tabl_tabulate_set_raggedright }
+\def\tabl_tabulate_bskip_raggedleft {\tabl_tabulate_bskip\tabl_tabulate_set_raggedleft }
+\def\tabl_tabulate_bskip_raggedcenter{\tabl_tabulate_bskip\tabl_tabulate_set_raggedcenter}
+
+\def\tabl_tabulate_set_width_normal
+ {\ifcase\c_tabl_tabulate_align\relax
+ \tabl_tabulate_set_preamble_step\empty \tabl_tabulate_set_hss \or
+ \tabl_tabulate_set_preamble_step\empty \tabl_tabulate_set_hss \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_set_hss\empty \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_set_hss\tabl_tabulate_set_hss \fi}
+
+\def\tabl_tabulate_set_width_fixed
+ {\ifcase\c_tabl_tabulate_align\relax
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip \tabl_tabulate_eskip \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip_raggedright \tabl_tabulate_eskip \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip_raggedleft \tabl_tabulate_eskip \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip_raggedcenter\tabl_tabulate_eskip \fi}
+
+\def\tabl_tabulate_set_width_auto
+ {\global\advance\c_tabl_tabulate_nofauto\plusone
+ \ifcase\c_tabl_tabulate_align\relax
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip \tabl_tabulate_eskip \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip_raggedright \tabl_tabulate_eskip \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip_raggedleft \tabl_tabulate_eskip \or
+ \tabl_tabulate_set_preamble_step\tabl_tabulate_bskip_raggedcenter\tabl_tabulate_eskip \fi}
+
+\def\tabl_tabulate_set_width_simple
+ {\tabl_tabulate_set_preamble_step\tabl_tabulate_xbskip\tabl_tabulate_xeskip}
+
+% \def\tabl_tabulate_set_color_span#1#2%
+% {\xdef\m_tabl_tabulate_color{#2}%
+% \global\c_tabl_tabulate_colorspan\if#1L\plusone\orelse\if#1M\plustwo\orelse\if#1R\plusthree\else\zerocount\fi\relax
+% \tabl_tabulate_set_preamble}
+
+\installcorenamespace{tabulatecolorspec}
+
+\defcsname\??tabulatecolorspec C\endcsname#1{\xdef\m_tabl_tabulate_color {#1}\global\c_tabl_tabulate_colorspan\zerocount}
+\defcsname\??tabulatecolorspec L\endcsname#1{\xdef\m_tabl_tabulate_color {#1}\global\c_tabl_tabulate_colorspan\plusone }
+\defcsname\??tabulatecolorspec M\endcsname#1{\xdef\m_tabl_tabulate_color {#1}\global\c_tabl_tabulate_colorspan\plustwo }
+\defcsname\??tabulatecolorspec R\endcsname#1{\xdef\m_tabl_tabulate_color {#1}\global\c_tabl_tabulate_colorspan\plusthree}
+\defcsname\??tabulatecolorspec T\endcsname#1{\xdef\m_tabl_tabulate_text_color{#1}}
+
+\def\tabl_tabulate_set_color_span#1#2%
+ {\csname\??tabulatecolorspec#1\endcsname{#2}%
+ \tabl_tabulate_set_preamble}
+
+\def\tabl_tabulate_set_vrule_command#1%
+ {\doifelsenumber{#1}
+ {\global\d_tabl_tabulate_vrulethickness#1\d_tabl_tabulate_vrulethickness_default}
+ {\xdef\m_tabl_tabulate_vrule_color{#1}}}
+
+\permanent\protected\def\tabl_tabulate_set_entry#1#2% rulespec template
+ {\c_tabl_tabulate_align\v_tabl_tabulate_align
+ \c_tabl_tabulate_modus\zerocount
+ \setfalse\c_tabl_tabulate_pwidth_set
+ \setfalse\c_tabl_tabulate_reshape
+ %setfalse\c_tabl_pre_is_set
+ \setfalse\c_tabl_post_is_set
+ \t_tabl_tabulate_before\emptytoks
+ \t_tabl_tabulate_after\emptytoks
+ \t_tabl_tabulate_bmath\emptytoks
+ \t_tabl_tabulate_emath\emptytoks
+ \t_tabl_tabulate_font\emptytoks
+ \t_tabl_tabulate_settings\emptytoks
+ \glet\m_tabl_tabulate_alignment\empty
+ \glet\m_tabl_tabulate_color\empty
+ \glet\m_tabl_tabulate_text_color\empty
+ \glet\m_tabl_tabulate_vrule_color\empty
+ \global\c_tabl_tabulate_colorspan\zerocount
+ \global\setfalse\c_tabl_auto_align_mode
+ \global\advance\c_tabl_tabulate_columns\plusone
+ \letcsname\??tabulatesetup\the\c_tabl_tabulate_columns\endcsname\donothing % here ?
+ \iftok{#1}\emptytoks
+ \global\d_tabl_tabulate_vrulethickness\zeropoint
+ \else
+ \global\d_tabl_tabulate_vrulethickness\d_tabl_tabulate_vrulethickness_default
+ % the lmtx raw processor handles {} like the normal one so we need to prune
+ \rawprocesscommalist[#1]\tabl_tabulate_set_vrule_command
+ \fi
+ \tabl_tabulate_set_preamble#2\relax\relax % permits i without n
+ \ifcase\c_tabl_tabulate_modus\relax
+ \tabl_tabulate_set_width_normal
+ \or % fixed width
+ \tabl_tabulate_set_width_fixed
+ \or % auto width
+ \tabl_tabulate_set_width_auto
+ \or % simple
+ \tabl_tabulate_set_width_simple
+ \fi}
+
+\permanent\protected\def\tabl_tabulate_set_last_entry#1% rulespec
+ {\glet\m_tabl_tabulate_color\empty
+ \glet\m_tabl_tabulate_vrule_color\empty
+ \iftok{#1}\emptytoks
+ \global\d_tabl_tabulate_vrulethickness\zeropoint
+ \else
+ \global\d_tabl_tabulate_vrulethickness\d_tabl_tabulate_vrulethickness_default
+ \rawprocesscommalist[#1]\tabl_tabulate_set_vrule_command
+ \fi
+ \etoksapp\t_tabl_tabulate_preamble{%
+ \tabl_tabulate_check_local_vrule_thickness\constantdimenargument\d_tabl_tabulate_vrulethickness
+ \tabl_tabulate_check_local_vrule_color\constantemptyargument\m_tabl_tabulate_vrule_color
+ \tabl_tabulate_column_vrule_inject}}
+
+\aliased\let\settabulateentry \tabl_tabulate_set_entry % used at the lua end
+\aliased\let\settabulatelastentry\tabl_tabulate_set_last_entry % used at the lua end
+
+\def\tabl_tabulate_normalize_splitline
+ {\ifcase\c_tabl_tabulate_splitlinemode
+ % nothing
+ \or
+ \ht\b_tabl_tabulate\strutht
+ \dp\b_tabl_tabulate\strutdp
+ \or
+ \ifdim\ht\b_tabl_tabulate<\strutht
+ \ht\b_tabl_tabulate\strutht
+ \fi
+ \ifdim\dp\b_tabl_tabulate<\strutdp
+ \dp\b_tabl_tabulate\strutdp
+ \fi
+ \fi}
+
+\def\tabl_tabulate_whitespace
+ {\ifdim\d_tabl_tabulate_splitoff_betweenskip>\zeropoint
+ \vskip\d_tabl_tabulate_splitoff_betweenskip
+ \global\d_tabl_tabulate_splitoff_betweenskip\zeropoint
+ \fi}
+
+\def\tabl_tabulate_check_whitespace
+ {\setbox\scratchbox\vpack
+ {\splitdiscards
+ \unskip
+ \ifdim\lastskip>\d_tabl_tabulate_splitoff_betweenskip
+ \global\d_tabl_tabulate_splitoff_betweenskip\lastskip
+ \fi}}
+
+\installtexdirective
+ {tabulate.linenumbers}
+ {\def\tabl_tabulate_check_linenumbers{\page_postprocessors_linenumbers_deepbox\b_tabl_tabulate}}
+ {\let\tabl_tabulate_check_linenumbers\relax}
+
+\let\tabl_tabulate_check_linenumbers\relax
+
+\def\tabl_tabulate_splitoff_box
+ {\dontcomplain
+ \global\setbox\b_tabl_tabulate\vsplit\b_tabl_tabulate_current\c_tabl_tabulate_column to \lineheight % % % global ? % % %
+ \setbox\b_tabl_tabulate\vbox
+ {\unvbox\b_tabl_tabulate}%
+ \ifconditional\c_tabl_tabulate_splitoff_whitespace
+ \tabl_tabulate_check_whitespace
+ \fi
+ \tabl_tabulate_color_repeat % needs to end up in a cell
+ \setbox\b_tabl_tabulate\hpack to \wd\b_tabl_tabulate
+ {\hss\tabl_tabulate_hook_yes{\box\b_tabl_tabulate}\hss}%
+ \tabl_tabulate_normalize_splitline
+ \tabl_tabulate_check_linenumbers
+ \box\b_tabl_tabulate}
+
+\protected\def\tabl_tabulate_hook_nop
+ {}
+
+\let\tabl_tabulate_hook\tabl_tabulate_hook_nop
+
+\def\tabl_tabulate_hook_yes{\begincsname\??tabulatehook\the\c_tabl_tabulate_column\endcsname}
+
+\def\tabl_tabulate_pheight_reset
+ {\global\c_tabl_tabulate_plines_min\plusone
+ \ifdim\d_tabl_tabulate_height_p_max>\zeropoint
+ \getnoflines\d_tabl_tabulate_height_p_max
+ \global\c_tabl_tabulate_plines_max\noflines
+ \else
+ \global\c_tabl_tabulate_plines_max\zerocount
+ \fi
+ \global\d_tabl_tabulate_height_p_max\zeropoint}
+
+\def\tabl_tabulate_pheight_set
+ {\scratchdimen\ht\b_tabl_tabulate_current\c_tabl_tabulate_column\relax
+ \ifdim\scratchdimen>\d_tabl_tabulate_height_p_max
+ \global\d_tabl_tabulate_height_p_max\scratchdimen
+ \fi}
+
+\def\tabl_tabulate_pbreak_inject
+ {\ifconditional\c_tabl_tabulate_handlepbreak
+ \ifconditional\c_tabl_tabulate_nopbreak
+ \tabl_tabulate_nobreak_inject
+ \orelse\ifnum\c_tabl_tabulate_plines_max>\plusone
+ \ifnum\c_tabl_tabulate_plines_min=\plusone
+ \tabl_tabulate_nobreak_inject
+ \fi
+ \global\advance\c_tabl_tabulate_plines_min\plusone
+ \ifnum\c_tabl_tabulate_plines_min=\c_tabl_tabulate_plines_max\relax
+ \tabl_tabulate_nobreak_inject
+ \fi
+ \fi
+ \fi}
+
+\def\tabl_tabulate_pbreak_check
+ {\noalign\bgroup
+ \tabl_tabulate_pbreak_inject
+ \ifconditional\c_tabl_tabulate_splitoff_whitespace
+ \tabl_tabulate_whitespace
+ \fi
+ \egroup}
+
+%D \startbuffer
+%D \starttabulate[|c|p|p|]
+%D \NC \bf Alpha \NC \bf Beta \NC \bf Gamma \NC\NR
+%D \NC 1 \NC right indeed \NC definitely wrong \NC\NR
+%D \NC 2 \NC \thinrules[n=3] \NC \thinrules[n=3] \NC\NR
+%D \NC 3 \NC oh yes \NC simply no \NC\NR
+%D \NC 4 \NC very true \NC as false as can be \NC\NR
+%D \NC 5 \NC \thinrules[n=5] \NC \thinrules[n=5] \NC\NR
+%D \NC 6 \NC \thinrules[n=3] \NC \thinrules[n=4] \NC\NR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer {\tracetabulatetrue\getbuffer}
+%D
+%D \startbuffer
+%D \starttabulate[|c|p|p|]
+%D \NC \bf Alpha \NC \bf Beta \NC \bf Gamma \NC\NR
+%D \NC 1 \NC right indeed \NC definitely wrong \NC\NR
+%D \NC 2 \NC oh yes \NC simply no \NC\NR
+%D \NC 3 \NC very true \NC as false as can be \NC\NR
+%D \NC 4 \NC the whole truth \NC but the truth \NC\NR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer {\tracetabulatetrue\getbuffer}
+
+%D Because we want to be compatible we use an indirect way to implement the
+%D definers. The next examples demonstrate the difference:
+%D
+%D \starttyping
+%D \definetabulate[test][|l|c|r|]
+%D \definetabulate[test][two][|r|c|l|]
+%D
+%D \definetabulation[more][format={|l|c|r|}]
+%D \definetabulation[more:two][format={|r|c|l|}]
+%D
+%D \starttest
+%D \NC t \NC t \NC t \NC \NR
+%D \NC te \NC te \NC te \NC \NR
+%D \NC tes \NC tes \NC tes \NC \NR
+%D \NC test \NC test \NC test \NC \NR
+%D \stoptest
+%D
+%D \starttest[two]
+%D \NC t \NC t \NC t \NC \NR
+%D \NC te \NC te \NC te \NC \NR
+%D \NC tes \NC tes \NC tes \NC \NR
+%D \NC test \NC test \NC test \NC \NR
+%D \stoptest
+%D \startmore
+%D \NC t \NC t \NC t \NC \NR
+%D \NC te \NC te \NC te \NC \NR
+%D \NC tes \NC tes \NC tes \NC \NR
+%D \NC test \NC test \NC test \NC \NR
+%D \stopmore
+%D
+%D \startmore[two]
+%D \NC t \NC t \NC t \NC \NR
+%D \NC te \NC te \NC te \NC \NR
+%D \NC tes \NC tes \NC tes \NC \NR
+%D \NC test \NC test \NC test \NC \NR
+%D \stopmore
+%D \stoptyping
+
+\installcorenamespace {tabulation}
+
+\installcommandhandler \??tabulation {tabulation} \??tabulation
+
+\setuptabulation
+ [\c!unit=1em,
+ EQ={:},
+ \c!format={|l|p|},
+ \c!frame=\v!off,
+ %\c!bodyfont=,
+ \c!rule=\v!normal,
+ %\c!rulecolor=,
+ \c!rulethickness=\linewidth,
+ %\c!inner=,
+ \c!before=\blank,
+ \c!after=\blank,
+ \c!distance={\v!depth,\v!medium},
+ \c!align=\v!normal,
+ \c!margin=\!!zeropoint,
+ \c!split=\v!auto,
+ \c!header=\v!yes,
+ %\c!title=,
+ \c!indenting=\v!no]
+
+\permanent\tolerant\protected\def\definetabulate[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ % ignore
+ \or
+ % [tag]
+ \definetabulation[#1][\c!format={|l|p|},\s!check=]%
+ \or
+ % [tag] [template]
+ \definetabulation[#1][\c!format={#2},\s!check=]%
+ \or
+ % [tag] [sub] [template]
+ \ifcsname\namedtabulationhash{#1}\s!check\endcsname \else
+ \definetabulation[#1][\c!format={#3},\s!check=]%
+ \fi
+ \definetabulation[#1:#2][#1][\c!format={#3},\s!check=]%
+ \fi}
+
+\permanent\tolerant\protected\def\setuptabulate[#1]#*[#2]#*[#3]%
+ {\ifarguments
+ % ignore
+ \or
+ % [tag]
+ \setuptabulation[#1]%
+ \or
+ % [tag] [settings]
+ \setuptabulation[#1][#2]%
+ \or
+ % [tag] [sub] [settings]
+ \setuptabulation[#1:#2][#3]%
+ \fi}
+
+\appendtoks
+ \enforced\permanent\protected\edefcsname\e!start \currenttabulation\endcsname{\tabl_start_defined[\currenttabulation]}%
+ \enforced\aliased \letcsname \e!stop \currenttabulation\endcsname\relax
+ \enforced\aliased \letcsname \??tabulatehead\currenttabulation\endcsname\empty
+ \enforced\aliased \letcsname \??tabulatefoot\currenttabulation\endcsname\empty
+\to \everydefinetabulation
+
+\aliased \let\tabulateparameter\tabulationparameter % will stay for a while
+\permanent\def\currenttabulate {\currenttabulation} % will stay for a while
+
+% Here begins the implementation.
+
+\let\tabl_tabulate_insert_head\empty
+\let\tabl_tabulate_insert_body\empty
+\let\tabl_tabulate_insert_foot\empty
+
+\def\tabl_tabulate_insert_head_content
+ {\noalign{\global\settrue\c_tabl_tabulate_someamble}%
+ \begincsname\??tabulatehead\currenttabulation\endcsname
+ \noalign{\global\setfalse\c_tabl_tabulate_someamble}}%
+
+\def\tabl_tabulate_insert_foot_content
+ {\noalign{\global\settrue\c_tabl_tabulate_someamble}%
+ \begincsname\??tabulatefoot\currenttabulation\endcsname
+ \noalign{\global\setfalse\c_tabl_tabulate_someamble}}%
+
+\def\tabl_tabulate_check_full_content % - needed, else confusion with \c!header
+ {\ifcsname\??tabulatehead\currenttabulation\endcsname
+ %\expandafter\ifempty\csname\??tabulatehead\currenttabulation\endcsname
+ %\expandafter\ifempty\lastnamedcs
+ \ifempty\lastnamedcs
+ \let\tabl_tabulate_insert_head\empty
+ \else
+ \let\tabl_tabulate_insert_head\tabl_tabulate_insert_head_content
+ \fi
+ \else
+ \let\tabl_tabulate_insert_head\empty
+ \fi
+ \ifcsname\??tabulatefoot\currenttabulation\endcsname
+ %\expandafter\ifempty\csname\??tabulatefoot\currenttabulation\endcsname
+ %\expandafter\ifempty\lastnamedcs
+ \ifempty\lastnamedcs
+ \let\tabl_tabulate_insert_foot\empty
+ \else
+ \let\tabl_tabulate_insert_foot\tabl_tabulate_insert_foot_content
+ \fi
+ \else
+ \let\tabl_tabulate_insert_foot\empty
+ \fi}
+
+\def\tabl_tabulate_insert_content
+ {\tabl_tabulate_insert_head
+ \ifcase\c_tabl_tabulate_repeathead \else
+ \noalign{\penalty\zerocount}% added 7/5/2014 WS mail
+ \fi
+ \tabl_tabulate_insert_body
+ \tabl_tabulate_insert_foot
+ \tabl_tabulate_remove_funny_line}
+
+\def\tabl_tabulate_remove_funny_line
+ {\ifhmode
+ \strut\crcr
+ \noalign{\kern-\lineheight}%
+ \fi}
+
+% todo: make footer synonym to tail
+
+\permanent\protected\defcsname\e!start\v!tabulatehead\endcsname{\doifelsenextoptionalcs\tabl_tabulate_start_head_yes\tabl_tabulate_start_head_nop}
+\permanent\protected\defcsname\e!start\v!tabulatetail\endcsname{\doifelsenextoptionalcs\tabl_tabulate_start_foot_yes\tabl_tabulate_start_foot_nop}
+
+\let\m_tabl_tabulate_data\empty
+
+\def\tabl_tabulate_start_head_yes[#1]%
+ {\processcontent{\e!stop\v!tabulatehead}\m_tabl_tabulate_data{\letvalue{\??tabulatehead#1}\m_tabl_tabulate_data}}
+
+\def\tabl_tabulate_start_foot_yes[#1]%
+ {\processcontent{\e!stop\v!tabulatetail}\m_tabl_tabulate_data{\letvalue{\??tabulatefoot#1}\m_tabl_tabulate_data}}
+
+\def\tabl_tabulate_start_head_nop{\tabl_tabulate_start_head_yes[\v!tabulate]}
+\def\tabl_tabulate_start_foot_nop{\tabl_tabulate_start_foot_yes[\v!tabulate]}
+
+\protected\def\tabl_start_defined[#1]%
+ {\bgroup
+ \edef\currenttabulationparent{#1}%
+ \let\currenttabulation\currenttabulationparent
+ \iftok{\tabulationparameter\c!format}{\v!none}%
+ % this is special case: we need to define the generic english
+ % \starttabulate in other interfaces as well
+ \lettabulationparameter\c!format\tabl_default_format
+ \expandafter\tabl_start_regular
+ \else
+ \expandafter\tabl_start_defined_indeed
+ \fi}
+
+\tolerant\def\tabl_start_defined_indeed[#1]#*[#2]%
+ {\ifarguments\or
+ \ifhastok={#1}%
+ \setuptabulation[\currenttabulation][#1]%
+ \else
+ \edef\currenttabulation{\currenttabulation:#1}%
+ \fi
+ \else
+ \edef\currenttabulation{\currenttabulation:#1}%
+ \setuptabulation[\currenttabulation][#2]%
+ \fi
+ \tabl_tabulate_start_building}
+
+% \definetabulate[\v!tabulate][|l|p|] % we need to get rid of this one
+
+\def\tabl_default_format{|l|p|} % actually format is always set
+
+\permanent\protected\setuvalue{\e!start\v!tabulate}%
+ {\bgroup % whole thing
+ \let\currenttabulationparent\empty
+ \tabl_start_regular}
+
+% \tolerant\protected\def\tabl_start_regular[#1]#*[#2]% [format] | [settings] | [format] [settings] | [settings] [format]
+% {\let\currenttabulation\currenttabulationparent
+% \iftok{#1}\emptytoks
+% \ifhastok={#2}\relax
+% \setupcurrenttabulation[#2]%
+% \fi
+% \orelse\ifhastok={#1}\relax
+% \iftok{#2}\emptytoks\else
+% \settabulationparameter\c!format{#2}%
+% \fi
+% \setupcurrenttabulation[#1]%
+% \else
+% \settabulationparameter\c!format{#1}%
+% \ifhastok={#2}\relax
+% \setupcurrenttabulation[#2]%
+% \fi
+% \fi
+% \tabl_tabulate_start_building}
+
+\tolerant\protected\def\tabl_start_regular[#1]#*[#2]% [format] | [settings] | [format] [settings] | [settings] [format]
+ {\let\currenttabulation\currenttabulationparent
+ \iftok{#1}\emptytoks
+ \ifhastok={#2}\relax
+ \setupcurrenttabulation[#2]%
+ \fi
+ \orelse\ifhastok={#1}\relax
+ \iftok{#2}\emptytoks\else
+ \settabulationparameter\c!format{#2}%
+ \fi
+ \setupcurrenttabulation[#1]%
+ \else
+ \iftok{#1}\emptytoks\else
+ \settabulationparameter\c!format{#1}%
+ \fi
+ \ifhastok={#2}\relax
+ \setupcurrenttabulation[#2]%
+ \fi
+ \fi
+ \tabl_tabulate_start_building}
+
+\permanent\letcsname\e!stop\v!tabulate \endcsname\relax
+\permanent\letcsname\e!stop\v!tabulatehead\endcsname\relax
+\permanent\letcsname\e!stop\v!tabulatetail\endcsname\relax
+
+\permanent\protected\def\tabl_tabulate_start_ignore % todo when we go frozen
+ {\em Nested tabulate is not (yet) supported.\relax
+ \expandafter\gobbleuntil\csname\ifconditional\c_tabl_generic stoptabulate\else\e!stop\v!tabulate\fi\endcsname}
+
+\appendtoks
+ \enforced\letvalue{\e!start\v!tabulate}\tabl_tabulate_start_ignore % only the main one
+\to \everytabulate
+
+\defcsname\??tabulatesplit\v!yes \endcsname{\settrue\c_tabl_tabulate_split}
+\defcsname\??tabulatesplit\v!repeat\endcsname{\settrue\c_tabl_tabulate_split}
+\defcsname\??tabulatesplit\v!no \endcsname{\setfalse\c_tabl_tabulate_split}
+\defcsname\??tabulatesplit\v!auto \endcsname{\ifinsidefloat\ifinsidesplitfloat\else\setfalse\c_tabl_tabulate_split\fi\fi}
+
+% todo: spacing around tabulate when bodyfont is set
+
+% \let\tabl_tabulate_inside_before \relax
+% \let\tabl_tabulate_inside_after \relax
+% \let\tabl_tabulate_inside_inbetween\relax
+%
+% \def\tabl_tabulate_outside_before
+% {\whitespace
+% \tabulationparameter\c!before}
+%
+% \def\tabl_tabulate_outside_after
+% {\tabulationparameter\c!after}
+
+% \showboxes
+%
+% \startcombination
+% {\insidefloattrue \starttabulate[|||] \NC test \NC test \NC \NR \stoptabulate} {}
+% {\insidefloattrue \starttabulate[|||] \NC test \NC test \NC \NR \stoptabulate} {}
+% \stopcombination
+%
+% \startcombination
+% {\vbox{\starttabulate[|||] \NC test \NC test \NC \NR \stoptabulate}} {}
+% {\vbox{\starttabulate[|||] \NC test \NC test \NC \NR \stoptabulate}} {}
+% \stopcombination
+%
+% \startcombination
+% {\starttabulate[|||] \NC test \NC test \NC \NR \stoptabulate} {}
+% {\starttabulate[|||] \NC test \NC test \NC \NR \stoptabulate} {}
+% \stopcombination
+
+\let\tabl_tabulate_inside_after \relax
+\let\tabl_tabulate_outside_after \relax
+\let\tabl_tabulate_inside_inbetween \relax
+\let\tabl_tabulate_outside_inbetween\relax
+
+\protected\def\tabl_tabulate_inside_before
+ {\ifhmode\par\fi
+ \ifhmode
+ \ifinsidesplitfloat
+ \let\tabl_tabulate_inside_after\relax
+ \else
+ \vbox\bgroup
+ \let\tabl_tabulate_inside_after\egroup
+ \fi
+ \else
+ \let\tabl_tabulate_inside_after\relax
+ \fi}
+
+\protected\def\tabl_tabulate_outside_before
+ {\ifhmode\par\fi
+ \ifhmode
+ \vbox\bgroup
+ \let\tabl_tabulate_outside_after \egroup
+ \let\tabl_tabulate_outside_inbetween\relax
+ \orelse\ifinner
+ \let\tabl_tabulate_outside_after \relax
+ \let\tabl_tabulate_outside_inbetween\relax
+ \else
+ \whitespace
+ \tabulationparameter\c!before
+ \relax
+ \let\tabl_tabulate_outside_after \tabl_tabulate_outside_after_indeed
+ \let\tabl_tabulate_outside_inbetween\tabl_tabulate_outside_inbetween_indeed
+ \fi}
+
+\def\tabl_tabulate_outside_after_indeed
+ {\tabulationparameter\c!after}
+
+\def\tabl_tabulate_outside_inbetween_indeed
+ {\doifempty{\tabulationparameter\c!after}
+ {\vskip\strutdp
+ \verticalstrut
+ \vskip-\struttotal}}
+
+\def\tabl_tabulate_inside_inbetween % needs checking
+ {\doifempty{\tabulationparameter\c!after}
+ {\vskip\strutdp
+ \verticalstrut
+ \vskip-\struttotal}}
+
+\protected\def\tabl_tabulate_start_building
+ {\ifinsidefloat
+ \tabl_tabulate_inside_before
+ \else
+ \tabl_tabulate_outside_before
+ \fi
+ \bgroup % settings
+ %
+ \t_tabl_tabulate_preamble\emptytoks
+ \t_tabl_tabulate_dummy \emptytoks
+ %
+ \resetcharacteralign
+ %
+ \edef\p_distance {\tabulationparameter\c!distance}%
+ \edef\p_align {\tabulationparameter\c!align}%
+ \edef\p_line {\tabulationparameter\c!rule}%
+ \edef\p_rulecolor {\tabulationparameter\c!rulecolor}%
+ \edef\p_rulethickness{\tabulationparameter\c!rulethickness}%
+ \edef\p_bodyfont {\tabulationparameter\c!bodyfont}%
+ \edef\p_indenting {\tabulationparameter\c!indenting}%
+ \edef\p_keeptogether {\tabulationparameter\c!keeptogether}%
+ \edef\p_blank {\tabulationparameter\c!blank}%
+ %
+ \ifx\p_keeptogether\v!no
+ \settrue \c_tabl_tabulate_tolerant_break
+ %\setfalse\c_tabl_tabulate_handlepbreak
+ \else
+ \setfalse\c_tabl_tabulate_tolerant_break
+ %\settrue \c_tabl_tabulate_handlepbreak
+ \fi
+ %
+ \settrue\c_tabl_tabulate_split
+ \begincsname\??tabulatesplit\tabulationparameter\c!split\endcsname
+ %
+ \let\m_tabl_tabulate_blank_default\p_blank
+ %
+ \d_tabl_tabulate_unit\tabulationparameter\c!unit
+ \d_tabl_tabulate_margin\tabulationparameter\c!margin
+ \let\m_tabl_tabulate_vrule_color_default\p_rulecolor
+ \let\m_tabl_tabulate_hrule_color_default\p_rulecolor
+ \d_tabl_tabulate_vrulethickness_default\p_rulethickness
+ \d_tabl_tabulate_hrulethickness_default\p_rulethickness
+ \ifempty\p_bodyfont\else
+ \switchtobodyfont[\p_bodyfont]%
+ \fi
+ \postponenotes % new, to be tested / will be configurable
+ \widowpenalty\zerocount % otherwise lines are not broken
+ \clubpenalty \zerocount % but overlap in funny ways
+ \the\everytabulate
+ \tabulationparameter\c!inner
+ \d_tabl_tabulate_indent\dimexpr\leftskip+\hangindent\ifx\p_indenting\v!yes+\parindent\fi\relax
+ \global\c_tabl_tabulate_column\zerocount
+ \processcontent % stoptabulate needs to match the frozen one
+ {\ifconditional\c_tabl_generic stoptabulate\else\e!stop\ifempty\currenttabulationparent\v!tabulate\else\currenttabulationparent\fi\fi}
+ \tabl_tabulate_insert_body
+ \tabl_tabulate_process}
+
+\permanent\protected\def\tabulateEQ
+ {\ifconditional\c_tabl_tabulate_firstflushed\else
+ \dostarttaggedchained\t!ignore\empty\empty
+ \dostarttagged\t!ignore\empty
+ \tabulationparameter{EQ}%
+ \dostoptagged
+ \dostoptagged
+ \fi
+ \global\setfalse\c_tabl_tabulate_equal}
+
+% The next ones will be token registers
+
+\let\tabulatenormalpos\relax % hooks, todo
+\let\tabulateequalpos \relax % hooks, todo
+
+% color columns
+
+\let\m_tabl_tabulate_color_previous \empty
+\let\m_tabl_tabulate_color \empty
+\let\m_tabl_tabulate_text_color \empty
+\let\m_tabl_tabulate_color_local \empty
+\let\m_tabl_tabulate_vrule_color \empty
+\let\m_tabl_tabulate_vrule_color_local \empty
+\let\m_tabl_tabulate_vrule_color_default\empty % used local
+\let\m_tabl_tabulate_hrule_color_default\empty % used local
+\let\m_tabl_tabulate_blank_default \empty
+
+\appendtoks
+ \glet \m_tabl_tabulate_color_previous \empty
+ \glet \m_tabl_tabulate_color \empty
+ \glet \m_tabl_tabulate_text_color \empty
+ \glet \m_tabl_tabulate_color_local \empty
+ \glet \m_tabl_tabulate_vrule_color \empty
+ \glet \m_tabl_tabulate_vrule_color_local \empty
+ \global\d_tabl_tabulate_vrulethickness_local\zeropoint
+\to \t_tabl_tabulate_every_row
+
+\protected\def\tabl_tabulate_color_side_right_second
+ {\ifempty\m_tabl_tabulate_color_previous \else
+ \tabl_tabulate_color_set\m_tabl_tabulate_color_previous
+ \glet\m_tabl_tabulate_color_previous\empty
+ \fi}
+
+\protected\def\tabl_tabulate_color_side_left_second
+ {\ifempty\m_tabl_tabulate_color \else
+ \ifcase\c_tabl_tabulate_colorspan
+ \or
+ \tabl_tabulate_color_set\m_tabl_tabulate_color
+ \or
+ \tabl_tabulate_color_set\m_tabl_tabulate_color
+ \fi
+ \fi}
+
+\protected\def\tabl_tabulate_color_side_both_second
+ {\ifempty\m_tabl_tabulate_color \else
+ \tabl_tabulate_color_set\m_tabl_tabulate_color
+ \ifcase\c_tabl_tabulate_colorspan
+ %\glet\m_tabl_tabulate_color_previous\empty
+ \or
+ \glet\m_tabl_tabulate_color_previous\empty
+ \or
+ \glet\m_tabl_tabulate_color_previous\m_tabl_tabulate_color
+ \or
+ \glet\m_tabl_tabulate_color_previous\m_tabl_tabulate_color
+ \fi
+ \fi}
+
+\let\tabl_tabulate_color_side_right \relax
+\let\tabl_tabulate_color_side_left \relax
+\let\tabl_tabulate_color_side_both \relax
+
+\appendtoks
+ \let\tabl_tabulate_color_side_right\tabl_tabulate_color_side_right_second
+ \let\tabl_tabulate_color_side_left \tabl_tabulate_color_side_left_second
+ \let\tabl_tabulate_color_side_both \tabl_tabulate_color_side_both_second
+\to \t_tabl_tabulate_initializers_second
+
+\def\tabl_tabulate_set_color_column#1% overloaded / todo
+ {\unskip
+ \doifelsefastoptionalcheck{\tabl_tabulate_set_color_column_yes#1}{\tabl_tabulate_set_color_column_nop#1}}
+
+\def\tabl_tabulate_set_color_column_nop
+ {\tabl_tabulate_column_normal\zerocount}
+
+\def\tabl_tabulate_set_color_column_yes#1[#2]%
+ {\xdef\m_tabl_tabulate_color_local{#2}%
+ \tabl_tabulate_column_normal\zerocount#1}
+
+% normal columns:
+
+\def\tabl_tabulate_column_normal#1#2%
+ {\unskip
+ \aligntab
+ \ifconditional\c_tabl_tabulate_equal\tabulateequalpos\else\tabulatenormalpos\fi
+ \ifnum\c_tabl_tabulate_column>\c_tabl_tabulate_max_vrulecolumn\else
+ \tabl_tabulate_column_vrule_setup
+ \fi
+ \aligntab
+ \global\c_tabl_tabulate_kind#1%
+ \global\c_tabl_tabulate_type#2%
+ \aligntab}
+
+% equal columns
+
+\def\tabl_tabulate_column_equal#1#2%
+ {\unskip
+ \aligntab
+ \tabulateequalpos
+ \aligntab
+ \global\c_tabl_tabulate_kind#1%
+ \global\c_tabl_tabulate_type#2%
+ \aligntab}
+
+% ruled columns
+
+\def\tabl_tabulate_column_vruled_preset
+ {\glet\m_tabl_tabulate_vrule_color_local\m_tabl_tabulate_vrule_color_default
+ \global\d_tabl_tabulate_vrulethickness_local\d_tabl_tabulate_vrulethickness_default}
+
+\def\tabl_tabulate_column_vruled#1#2%
+ {\unskip % 0-n
+ %\ifnum\c_tabl_tabulate_column=\plusone
+ % \global\c_tabl_tabulate_has_rule_spec_first\plusone
+ %\orelse\ifnum\c_tabl_tabulate_column=\c_tabl_tabulate_nofcolumns
+ % \global\c_tabl_tabulate_has_rule_spec_last\plusone
+ %\fi
+ \ifnum\c_tabl_tabulate_column>\c_tabl_tabulate_max_vrulecolumn
+ \global\c_tabl_tabulate_max_vrulecolumn\c_tabl_tabulate_column
+ \fi
+ \doifelsefastoptionalcheck{\tabl_tabulate_column_vruled_yes#1#2}{\tabl_tabulate_column_vruled_nop#1#2}}
+
+\def\tabl_tabulate_column_vrule_setup
+ {\begincsname\??tabulatevrule\the\c_tabl_tabulate_column\endcsname}
+
+\def\tabl_tabulate_column_vruled_nop
+ {\gletcsname\??tabulatevrule\the\c_tabl_tabulate_column\endcsname\tabl_tabulate_column_vruled_preset
+ \tabl_tabulate_column_normal}
+
+\def\tabl_tabulate_column_vruled_step#1%
+ {\doifelsenumber{#1}%
+ {\global\d_tabl_tabulate_vrulethickness_local#1\d_tabl_tabulate_vrulethickness_default}
+ {\xdef\m_tabl_tabulate_vrule_color_local{#1}}}
+
+\def\tabl_tabulate_column_vruled_yes#1#2[#3]%
+ {\gdefcsname\??tabulatevrule\the\c_tabl_tabulate_column\endcsname
+ {\tabl_tabulate_column_vruled_preset
+ \rawprocesscommalist[#3]\tabl_tabulate_column_vruled_step}%
+ \tabl_tabulate_column_normal#1#2}
+
+\def\tabl_tabulate_vrule_reset
+ {\ifcase\c_tabl_tabulate_max_vrulecolumn\else
+ \tabl_tabulate_vrule_reset_indeed
+ \fi}
+
+\def\tabl_tabulate_vrule_reset_indeed
+ {\dofastloopcs\c_tabl_tabulate_max_vrulecolumn\tabl_tabulate_vrule_reset_step
+ \global\c_tabl_tabulate_max_vrulecolumn\zerocount}
+
+\def\tabl_tabulate_vrule_reset_step % undefined or relax
+ {\gletcsname\??tabulatevrule\the\fastloopindex\endcsname\undefined}
+
+\appendtoks
+ \tabl_tabulate_vrule_reset
+\to \t_tabl_tabulate_every_after_row
+
+\def\tabl_tabulate_column_vruled_normal
+ {\vrule\s!width\d_tabl_tabulate_vrulethickness\relax}
+
+\def\tabl_tabulate_column_vruled_colored
+ {\dousecolorparameter\m_tabl_tabulate_vrule_color
+ \vrule\s!width\d_tabl_tabulate_vrulethickness\relax}
+
+\protected\def\tabl_tabulate_column_vrule_inject_first
+ {\ifcase\d_tabl_tabulate_vrulethickness\else
+ \tabl_tabulate_column_vruled_normal % could be a skip instead
+ \fi
+ \global\d_tabl_tabulate_vrulethickness\zeropoint} % nils second one
+
+\protected\def\tabl_tabulate_column_vrule_inject_second
+ {\ifcase\d_tabl_tabulate_vrulethickness\else
+ \ifempty\m_tabl_tabulate_vrule_color
+ \tabl_tabulate_column_vruled_normal
+ \else
+ \tabl_tabulate_column_vruled_colored
+ \fi
+ \fi
+ \global\d_tabl_tabulate_vrulethickness\zeropoint} % nils second one
+
+\let\tabl_tabulate_column_vrule_inject\relax
+
+\appendtoks
+ \let\tabl_tabulate_column_vrule_inject\tabl_tabulate_column_vrule_inject_first
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \let\tabl_tabulate_column_vrule_inject\tabl_tabulate_column_vrule_inject_second
+\to \t_tabl_tabulate_initializers_second
+
+% auto columns
+
+\def\tabl_tabulate_column_inject_auto
+ {\tabl_tabulate_column_normal\zerocount\zerocount
+ \ifnum\c_tabl_tabulate_column>\c_tabl_tabulate_columns\relax
+ \expandafter\NR
+ \else
+ \expandafter\ignorespaces % interferes with the more tricky hooks
+ \fi}
+
+\protected\def\tabl_tabulate_set_quick#1% see \startlegend \startgiven (for the moment still public)
+ {\enforced\let#1\tabl_tabulate_column_inject_auto
+ \enforced\let\\\tabl_tabulate_column_inject_auto} % brrr, will go
+
+\defcsname\??tabulateseparator\v!blank \endcsname{\s_tabl_tabulate_separator\bigskipamount}
+\defcsname\??tabulateseparator\v!depth \endcsname{\s_tabl_tabulate_separator\strutdp}
+\defcsname\??tabulateseparator\v!small \endcsname{\def\m_tabl_tabulate_separator_factor{.25}}
+\defcsname\??tabulateseparator\v!medium\endcsname{\def\m_tabl_tabulate_separator_factor{.5}}
+\defcsname\??tabulateseparator\v!big \endcsname{}
+\defcsname\??tabulateseparator\v!none \endcsname{\s_tabl_tabulate_separator\zeropoint\let\m_tabl_tabulate_separator_factor\zerocount}
+\defcsname\??tabulateseparator\v!grid \endcsname{\s_tabl_tabulate_separator\zeropoint\let\m_tabl_tabulate_separator_factor\zerocount}
+
+\def\tabl_tabulate_column_rule_separator_step#1%
+ {\ifcsname\??tabulateseparator#1\endcsname
+ \lastnamedcs
+ \else
+ \s_tabl_tabulate_separator#1\relax
+ \fi}
+
+\def\tabl_tabulate_column_rule_separator_inject % can be sped up (will do when used frequently)
+ {\bgroup
+ \s_tabl_tabulate_separator\strutdp
+ \ifempty\p_distance\else
+ \let\m_tabl_tabulate_separator_factor\plusone
+ \processcommacommand[\p_distance]\tabl_tabulate_column_rule_separator_step
+ \s_tabl_tabulate_separator\m_tabl_tabulate_separator_factor\s_tabl_tabulate_separator
+ \fi
+ % someamble: footer or header: unfortunately a skip can trigger a page break (weird
+ % as we have lots of nobreaks)
+ % \ifconditional\c_tabl_tabulate_someamble\kern\else\vskip\fi\s_tabl_tabulate_separator % new
+ % \directvspacing{\the\s_tabl_tabulate_separator}% new
+ \directvskip\s_tabl_tabulate_separator
+ \egroup}
+
+\let\m_tabl_tabulate_hrule_color\empty
+
+\def\tabl_tabulate_hrule_preset_step#1%
+ {\doifelsenumber{#1}%
+ {\global\d_tabl_tabulate_hrulethickness_local#1\d_tabl_tabulate_hrulethickness_default}
+ {\xdef\m_tabl_tabulate_hrule_color{#1}}}
+
+\def\tabl_tabulate_hrule_preset[#1]%
+ {\glet\m_tabl_tabulate_hrule_color\m_tabl_tabulate_hrule_color_default
+ \global\d_tabl_tabulate_hrulethickness_local\d_tabl_tabulate_hrulethickness_default
+ \iftok{#1}\emptytoks\else\rawprocesscommalist[#1]\tabl_tabulate_hrule_preset_step\fi}
+
+\def\tabl_tabulate_hrule_inject_normal
+ {\autorule
+ \s!height.5\d_tabl_tabulate_hrulethickness_local
+ \s!depth .5\d_tabl_tabulate_hrulethickness_local
+ \s!left \d_tabl_tabulate_indent
+ \relax}
+
+\def\tabl_tabulate_hrule_inject_colored
+ {\dousecolorparameter\m_tabl_tabulate_hrule_color
+ \tabl_tabulate_hrule_inject_normal}
+
+\protected\def\tabl_tabulate_hrule_inject_first
+ {\ifcase\d_tabl_tabulate_hrulethickness_local\else
+ \tabl_tabulate_hrule_inject_normal
+ \fi}
+
+\protected\def\tabl_tabulate_hrule_inject_second
+ {\ifcase\d_tabl_tabulate_hrulethickness_local\else
+ \ifempty\m_tabl_tabulate_hrule_color
+ \tabl_tabulate_hrule_inject_normal
+ \else
+ \tabl_tabulate_hrule_inject_colored
+ \fi
+ \fi}
+
+\let\tabl_tabulate_hrule_inject\relax
+
+\appendtoks
+ \let\tabl_tabulate_hrule_inject\tabl_tabulate_hrule_inject_first
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \let\tabl_tabulate_hrule_inject\tabl_tabulate_hrule_inject_second
+\to \t_tabl_tabulate_initializers_second
+
+%D Color:
+
+% \starttabulate[||p||]
+% \NC test \NC test \NC test \NC \NR
+% \NC test \CC[green] \input tufte \CC[yellow] test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \CC[blue] test \CC[red] test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \CC[gray] test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \CC[blue] test \NC test \NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \NC test \CC[magenta] test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \CC[cyan] \dorecurse{10}{\input ward }\NC test \NC \NR
+% \NC test \NC test \NC test \NC \NR
+% \NC test \CC[yellow] test \NC test \NC \NR
+% \stoptabulate
+
+\protected\def\tabl_tabulate_color_set#1% we could store the attributes at the cost of a lua call
+ {\begingroup
+ \clf_enablebackgroundalign % was \node_backgrounds_align_initialize
+ \glet\tabl_tabulate_color_repeat\tabl_tabulate_color_repeat_second
+ \ifnum\c_tabl_tabulate_column>\c_tabl_tabulate_max_colorcolumn
+ \global\c_tabl_tabulate_max_colorcolumn\c_tabl_tabulate_column
+ \fi
+ \xdefcsname\??tabulatecolor\the\c_tabl_tabulate_column\endcsname{#1}%
+ \hpack \thealignbackgroundcolorattr{#1}{}% pack ?
+ \endgroup}
+
+\def\tabl_tabulate_color_repeat_second % for split off lines
+ {\begingroup
+ \scratchcounter\numexpr\c_tabl_tabulate_column-\plusone\relax % ugly !
+ \ifcsname\??tabulatecolor\the\scratchcounter\endcsname
+ % \hbox \thealignbackgroundcolorattr{\csname\??tabulatecolor\the\scratchcounter\endcsname}{}% pack ?
+ \hpack \expandafter\thealignbackgroundcolorattr\expandafter{\lastnamedcs}{}% pack ?
+ \fi
+ \endgroup}
+
+\let\tabl_tabulate_color_repeat\relax
+
+\appendtoks
+ \let\tabl_tabulate_color_repeat\relax
+\to \everytabulate
+
+\def\tabl_tabulate_color_reset
+ {\ifcase\c_tabl_tabulate_max_colorcolumn\else
+ \tabl_tabulate_color_reset_indeed
+ \fi}
+
+% \def\tabl_tabulate_color_reset_indeed
+% {\dorecurse\c_tabl_tabulate_max_colorcolumn{\letgvalue{\??tabulatecolor\recurselevel}\undefined}} % slow
+
+\def\tabl_tabulate_color_reset_indeed
+ {\dofastloopcs\c_tabl_tabulate_max_colorcolumn\tabl_tabulate_color_reset_step}
+
+\def\tabl_tabulate_color_reset_step % undefined or empty?
+ {\gletcsname\??tabulatecolor\number\fastloopindex\endcsname\undefined}
+
+\appendtoks
+ \tabl_tabulate_color_reset
+\to \t_tabl_tabulate_every_after_row
+
+% \def\tabl_tabulate_register_par_options_indeed
+% {\iftrialtypesetting \else
+% \registerparoptions
+% \ifinsidefloat
+% % that is, an unbreakable one
+% \glet\tabl_tabulate_register_par_options\empty
+% \else
+% % unsafe in crossing pages, at each b...
+% % \glet\tabl_tabulate_register_par_options\empty
+% \fi
+% \fi}
+%
+% \appendtoks
+% \glet\tabl_tabulate_register_par_options\tabl_tabulate_register_par_options_indeed
+% \to \everytabulate
+
+\def\tabl_tabulate_register_par_options_first % maybe track here if needed
+ {}
+
+\def\tabl_tabulate_register_par_options_second
+ {\registerparoptions
+ \ifinsidefloat
+ % that is, an unbreakable one
+ \glet\tabl_tabulate_register_par_options\empty
+ \else
+ % unsafe in crossing pages, at each b...
+ % \glet\tabl_tabulate_register_par_options\empty
+ \fi}
+
+\let\tabl_tabulate_register_par_options\relax
+
+\appendtoks
+ \let\tabl_tabulate_register_par_options\tabl_tabulate_register_par_options_first
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \let\tabl_tabulate_register_par_options\tabl_tabulate_register_par_options_second
+\to \t_tabl_tabulate_initializers_second
+
+\appendtoks
+ \tabl_tabulate_register_par_options
+\to \t_tabl_tabulate_every_row
+
+\def\tabl_tabulate_flush_indent_indeed
+ {\hbox to \d_tabl_tabulate_indent % pack ?
+ {% we now have a local hsize, and since we want to
+ % register positional info (i.e. real hsizes) we
+ % need to reconstitute the original hsize
+ \advance\hsize\d_tabl_tabulate_indent
+ % this is indeed rather messy and took a few hours
+ % to dis/uncover
+ \the\t_tabl_tabulate_every_row
+ \hss}}
+
+\def\tabl_tabulate_flush_indent
+ {\ifnum\c_tabl_tabulate_column=\zerocount
+ \tabl_tabulate_flush_indent_indeed
+ \fi}
+
+\def\tabl_tabulate_digits{\digits}
+
+%D Beware, we cannot use \type {\protected} on \type {\HL} cum suis, since \TEX's
+%D hard coded noalign lookahead fails on it! I mistakenly added this for a while.
+
+\defcsname\??tabulatealigning\v!normal\endcsname{0}
+\defcsname\??tabulatealigning\v!right \endcsname{1}
+\defcsname\??tabulatealigning\v!left \endcsname{2}
+\defcsname\??tabulatealigning\v!middle\endcsname{3}
+
+\defcsname\??tabulateheader\v!repeat\endcsname{\plusone}
+\defcsname\??tabulateheader\v!text \endcsname{\plustwo}
+
+\protected\def\tabl_tabulate_bskip_first {\setbox\b_tabl_tabulate\vbox\bgroup\glet\tabl_tabulate_hook\tabl_tabulate_hook_nop}
+\protected\def\tabl_tabulate_eskip_first {\par\egroup\glet\tabl_tabulate_hook\tabl_tabulate_hook_yes}
+\protected\def\tabl_tabulate_xbskip_first{\hpack\bgroup\vbox\bgroup\glet\tabl_tabulate_hook\tabl_tabulate_hook_nop}
+\protected\def\tabl_tabulate_xeskip_first{\par\egroup\egroup\glet\tabl_tabulate_hook\tabl_tabulate_hook_yes}
+
+\let\tabl_tabulate_bbskip\relax
+\let\tabl_tabulate_eeskip\relax
+\let\tabl_tabulate_bskip \relax
+\let\tabl_tabulate_eskip \relax
+\let\tabl_tabulate_xbskip\relax
+\let\tabl_tabulate_xeskip\relax
+
+\appendtoks
+ \let\tabl_tabulate_bbskip\relax
+ %\let\tabl_tabulate_eeskip\relax % adapted by bskip
+ \let\tabl_tabulate_bskip \tabl_tabulate_bskip_first
+ \let\tabl_tabulate_eskip \tabl_tabulate_eskip_first
+ \let\tabl_tabulate_xbskip\tabl_tabulate_xbskip_first
+ \let\tabl_tabulate_xeskip\tabl_tabulate_xeskip_first
+\to \t_tabl_tabulate_initializers_first
+
+\def\tabl_tabulate_baselinecorrection % keep an eye on this one
+ {\def\dobaselinecorrection{\vskip\dimexpr-\prevdepth+\strutdp+\strutdp\relax}% todo: mkiv
+ \baselinecorrection}
+
+% some hack to prevent an allowbreak ... actually we could set up a system then
+% that is dealt with atthe lua end in the skip handler: turn penalties with attributes
+% values into other penalties that get removed
+
+\installcorenamespace{tabulatenobreak}
+
+\def\tabl_tabulate_break_allow{\directvpenalty\zerocount}
+\def\tabl_tabulate_break_maybe{\directvpenalty\zerocount}
+\def\tabl_tabulate_break_no {\directvpenalty\plustenthousand} % ,order:2}}
+
+\let\tabl_tabulate_break_state_set \relax
+\let\tabl_tabulate_break_state_reset \relax
+\let\tabl_tabulate_break_state_allowbreak\tabl_tabulate_break_maybe
+
+% so far
+
+\permanent\protected\def\tabl_tabulate_VL_first{\tabl_tabulate_column_vruled\zerocount\zerocount}
+\permanent\protected\def\tabl_tabulate_NC_first{\tabl_tabulate_column_normal\zerocount\zerocount}
+\permanent\protected\def\tabl_tabulate_RC_first{\tabl_tabulate_column_normal\zerocount\plusone}
+\permanent\protected\def\tabl_tabulate_HC_first{\tabl_tabulate_column_normal\zerocount\plustwo}
+\permanent\protected\def\tabl_tabulate_EQ_first{\tabl_tabulate_column_equal \plustwo \zerocount}
+\permanent\protected\def\tabl_tabulate_RQ_first{\tabl_tabulate_column_equal \zerocount\plusone}
+\permanent\protected\def\tabl_tabulate_HQ_first{\tabl_tabulate_column_equal \zerocount\plustwo}
+
+\permanent\protected\def\tabl_tabulate_NG_first{\tabl_tabulate_column_normal\zerocount\zerocount}
+\permanent\protected\def\tabl_tabulate_NN_first{\tabl_tabulate_column_normal\zerocount\zerocount\tabl_tabulate_digits} % new, undocumented, test first
+\permanent\protected\def\tabl_tabulate_ND_first{\tabl_tabulate_column_normal\zerocount\zerocount\tabl_tabulate_digits} % same, for old times sake
+
+\permanent\protected\def\tabl_tabulate_NR_first {\tabl_tabulate_NR_common\conditionaltrue \tabl_tabulate_check_penalties} % next row
+\permanent\protected\def\tabl_tabulate_NB_first {\tabl_tabulate_NR_common\conditionaltrue \tabl_tabulate_nobreak_inject } % next row no break
+
+\permanent\protected\def\tabl_tabulate_NR_second{\tabl_tabulate_NR_common\conditionalfalse\tabl_tabulate_check_penalties} % next row
+\permanent\protected\def\tabl_tabulate_NB_second{\tabl_tabulate_NR_common\conditionalfalse\tabl_tabulate_nobreak_inject } % next row no break
+
+\permanent\protected\def\tabl_tabulate_CC_first{\global\c_tabl_tabulate_localcolorspan\zerocount\tabl_tabulate_set_color_column\zerocount}
+\permanent\protected\def\tabl_tabulate_CL_first{\global\c_tabl_tabulate_localcolorspan\plusone \tabl_tabulate_set_color_column\zerocount}
+\permanent\protected\def\tabl_tabulate_CM_first{\global\c_tabl_tabulate_localcolorspan\plustwo \tabl_tabulate_set_color_column\zerocount}
+\permanent\protected\def\tabl_tabulate_CR_first{\global\c_tabl_tabulate_localcolorspan\plusthree\tabl_tabulate_set_color_column\zerocount}
+
+%D Sort of special:
+%D
+%D \startbuffer
+%D \startitemize[n]
+%D \starttabulate[|||||]
+%D \NC p \NC \itemtag \NC q \NC r \NC \NR
+%D \NC p \NC \itemtag \NC q \NC r \NC \NR
+%D \NC p \NC \itemtag \NC q \NC r \NC \NR
+%D \NC p \NC \itemtag \NC q \NC r \NC \NR
+%D \stoptabulate
+%D \stopitemize
+%D
+%D \startitemize[n]
+%D \starttabulate[|||||]
+%D \NI b \NC c \NC d \NC \NR
+%D \NC a \NI c \NC d \NC \NR
+%D \NC a \NC b \NI d \NC \NR
+%D \NC a \NC b \NC c \NI \NR
+%D \stoptabulate
+%D \stopitemize
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\tolerant\protected\def\tabl_tabulate_NI_first[#1]{\NC\itemtag[#1]\NC}
+
+%D The following shortcut is handy for tables where one needs bold headers:
+
+\permanent\protected\def\tabl_tabulate_BC_first
+ {\tabl_tabulate_column_normal\plusone\zerocount
+ \let\fontstyle\globalfontstyle
+ \bf}
+
+\appendtoks
+ \enforced\let\VL\tabl_tabulate_VL_first
+ \enforced\let\NC\tabl_tabulate_NC_first
+ \enforced\let\BC\tabl_tabulate_BC_first
+ \enforced\let\RC\tabl_tabulate_RC_first
+ \enforced\let\HC\tabl_tabulate_HC_first
+ \enforced\let\EQ\tabl_tabulate_EQ_first
+ \enforced\let\RQ\tabl_tabulate_RQ_first
+ \enforced\let\HQ\tabl_tabulate_HQ_first
+ \enforced\let\NG\tabl_tabulate_NG_first
+ \enforced\let\NN\tabl_tabulate_NN_first
+ \enforced\let\ND\tabl_tabulate_ND_first
+ \enforced\let\NR\tabl_tabulate_NR_first
+ \enforced\let\NB\tabl_tabulate_NB_first
+ \enforced\let\CC\tabl_tabulate_CC_first
+ \enforced\let\CL\tabl_tabulate_CL_first
+ \enforced\let\CM\tabl_tabulate_CM_first
+ \enforced\let\CR\tabl_tabulate_CR_first
+ \enforced\let\NI\tabl_tabulate_NI_first
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \enforced\let\NR\tabl_tabulate_NR_second
+ \enforced\let\NB\tabl_tabulate_NB_second
+\to \t_tabl_tabulate_initializers_second
+
+\appendtoks
+ \enforced\let\SR\NR
+ \enforced\let\FR\NR
+ \enforced\let\MR\NR
+ \enforced\let\LR\NR
+ \enforced\let\AR\NR
+\to \t_tabl_tabulate_initializers_first
+
+\permanent\protected\def\tabl_tabulate_NR_common#1#2%
+ {\global\advance\c_tabl_tabulate_noflines\plusone
+ \global\setfalse\c_tabl_tabulate_firstflushed
+ \global\setfalse\c_tabl_tabulate_equal
+ \global\c_tabl_tabulate_column\zerocount
+ \ifconditional#1\relax
+ \tabl_tabulate_break_state_reset
+ \fi
+ \tabl_tabulate_pheight_reset
+ \unskip\unskip\crcr\tabl_tabulate_flush_collected
+ % can we omit the next one in the first run? probably
+ \noalign{\the\t_tabl_tabulate_every_after_row#2}}
+
+\def\tabl_tabulate_check_penalties
+ {\ifconditional\c_tabl_tabulate_tolerant_break\else
+ \ifnum\c_tabl_tabulate_totalnoflines=\plusone
+ % \tabl_tabulate_break_allow
+ \else
+ \ifconditional\c_tabl_tabulate_someamble \ifcase\c_tabl_tabulate_repeathead \else
+ \tabl_tabulate_break_allow
+ \fi \fi
+ \ifnum\c_tabl_tabulate_noflines=\plusone
+ \tabl_tabulate_nobreak_inject
+ \orelse\ifnum\c_tabl_tabulate_noflines=\c_tabl_tabulate_minusnoflines
+ \ifnum\c_tabl_tabulate_plines_max<\plustwo
+ \tabl_tabulate_nobreak_inject
+ \else
+ \tabl_tabulate_break_allow % needed with pbreak prevention
+ \fi
+ \else
+ \tabl_tabulate_break_state_allowbreak
+ \fi
+ \fi
+ \fi
+ \global\setfalse\c_tabl_tabulate_firstflushed}
+
+\protected\def\tabl_tabulate_bbskip_second_split_yes
+ {\ifvoid\b_tabl_tabulate_current\c_tabl_tabulate_column
+ \ifempty\tabl_tabulate_flush_collected_indeed\else
+ \setbox0\hbox
+ \fi
+ \fi}
+
+\protected\def\tabl_tabulate_eskip_second
+ {\par\egroup
+ \tabl_tabulate_pheight_set
+ \glet\tabl_tabulate_hook\tabl_tabulate_hook_yes
+ \tabl_tabulate_splitoff_box}
+
+\protected\def\tabl_tabulate_bskip_second_split_yes
+ {\ifvoid\b_tabl_tabulate_current\c_tabl_tabulate_column
+ % first line
+ \global\setbox\b_tabl_tabulate_current\c_tabl_tabulate_column\vbox
+ \bgroup
+ \glet\tabl_tabulate_hook\tabl_tabulate_hook_nop
+ \ifconditional\c_tabl_tabulate_automode\hsize\d_tabl_tabulate_width\fi
+ % \begstrut % interferes with pre-\pars
+ % evt: \appendtoks\begstrut\to\everypar
+ \ignorespaces
+ \let\tabl_tabulate_eskip\tabl_tabulate_eskip_second
+ \else
+ % successive lines
+ \let\tabl_tabulate_eskip\empty
+ \dontcomplain
+ \glet\tabl_tabulate_hook\tabl_tabulate_hook_yes
+ \expandafter\tabl_tabulate_splitoff_box
+ \fi}
+
+\protected\def\tabl_tabulate_xbskip_second{\tabl_tabulate_bskip}
+\protected\def\tabl_tabulate_xeskip_second{\tabl_tabulate_eskip}
+
+\protected\def\tabl_tabulate_flush_second_indeed
+ {\glet\tabl_tabulate_flush_collected_indeed\empty
+ \global\c_tabl_tabulate_column\zerocount
+ \tabl_tabulate_pbreak_check
+ \dofastloopcs\c_tabl_tabulate_columns\tabl_tabulate_flush_second_step
+ \global\settrue\c_tabl_tabulate_firstflushed}
+
+\protected\def\tabl_tabulate_flush_second_step
+ {\ifvoid\b_tabl_tabulate_current\fastloopindex\else
+ \gdef\tabl_tabulate_flush_collected_indeed{\the\t_tabl_tabulate_dummy}%
+ \fi}
+
+\def\tabl_tabulate_flush_second
+ {\noalign{\tabl_tabulate_flush_second_indeed}%
+ \tabl_tabulate_flush_collected_indeed}
+
+\protected\def\tabl_tabulate_bskip_second_split_nop
+ {\vtop\bgroup
+ \ifconditional\c_tabl_tabulate_automode\hsize\d_tabl_tabulate_width\fi
+ % \begstrut % interferes with pre-\pars
+ % evt: \appendtoks\begstrut\to\everypar
+ \ignorespaces}
+
+\protected\def\tabl_tabulate_eskip_second_split_nop % vertical strut added august 2003
+ {\par\verticalstrut
+ \vskip-\struttotal
+ \egroup}
+
+% \let\tabl_tabulate_eskip \relax % adapted by bskip
+% \let\tabl_tabulate_eeskip\relax % adapted by bskip
+
+\appendtoks
+ \let\tabl_tabulate_xbskip\tabl_tabulate_xbskip_second
+ \let\tabl_tabulate_xeskip\tabl_tabulate_xeskip_second
+ \ifconditional\c_tabl_tabulate_split
+ \let\tabl_tabulate_bskip \tabl_tabulate_bskip_second_split_yes
+ \let\tabl_tabulate_bbskip\tabl_tabulate_bbskip_second_split_yes
+ \else
+ \let\tabl_tabulate_bskip \tabl_tabulate_bskip_second_split_nop
+ \let\tabl_tabulate_eskip \tabl_tabulate_eskip_second_split_nop
+ \fi
+\to \t_tabl_tabulate_initializers_second
+
+% see ***
+%
+% \enabletrackers[nodes.page_vspacing]
+% \starttext
+% \starttabulate[||] \dorecurse{100}{\NC Eins \NC \NR \HL} \stoptabulate
+% \stoptext
+
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_XX_none [#1]{\noalign{\tabl_tabulate_break_state_set}}
+
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_FL_second[#1]{\noalign{\tabl_tabulate_hrule_preset[#1]\tabl_tabulate_FL_second_indeed}}
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_ML_second[#1]{\noalign{\tabl_tabulate_hrule_preset[#1]\tabl_tabulate_ML_second_indeed}}
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_LL_second[#1]{\noalign{\tabl_tabulate_hrule_preset[#1]\tabl_tabulate_LL_second_indeed}}
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_TL_second[#1]{\noalign{\tabl_tabulate_hrule_preset[#1]\tabl_tabulate_TL_second_indeed}}
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_BL_second[#1]{\noalign{\tabl_tabulate_hrule_preset[#1]\tabl_tabulate_BL_second_indeed}}
+
+\protected\def\tabl_tabulate_FL_second_indeed
+ {\ifinsidefloat\else
+ \doifempty{\tabulationparameter\c!before}\tabl_tabulate_baselinecorrection % no expansion
+ \fi
+ \tabl_tabulate_hrule_inject
+ \tabl_tabulate_nobreak_inject
+ \tabl_tabulate_column_rule_separator_inject
+ \prevdepth\strutdp
+ \tabl_tabulate_nobreak_inject}
+
+\def\spac_vspacing_no_topskip % use grouped
+ {\c_attr_skipcategory\plusten}
+
+\protected\def\tabl_tabulate_ML_second_indeed
+ {\tabl_tabulate_break_no
+ \tabl_tabulate_column_rule_separator_inject
+ \tabl_tabulate_break_no
+ \tabl_tabulate_hrule_inject
+ \vskip-\p_rulethickness\relax
+ \begingroup
+ \spac_vspacing_no_topskip
+ \tabl_tabulate_hrule_inject
+ \endgroup
+ \tabl_tabulate_break_no
+ \tabl_tabulate_column_rule_separator_inject}
+
+\protected\def\tabl_tabulate_LL_second_indeed
+ {\tabl_tabulate_nobreak_inject
+ \tabl_tabulate_column_rule_separator_inject
+ \tabl_tabulate_nobreak_inject
+ \tabl_tabulate_hrule_inject
+ \ifinsidefloat
+ \tabl_tabulate_inside_inbetween
+ \else
+ \tabl_tabulate_outside_inbetween
+ \fi}
+
+\protected\def\tabl_tabulate_TL_second_indeed
+ {\tabl_tabulate_nobreak_inject
+ \tabl_tabulate_column_rule_separator_inject
+ \tabl_tabulate_nobreak_inject
+ \tabl_tabulate_hrule_inject
+ \tabl_tabulate_nobreak_inject
+ \tabl_tabulate_column_rule_separator_inject
+ %\prevdepth\strutdp % todo, might differ between TL and BL
+ \tabl_tabulate_nobreak_inject}
+
+\let\tabl_tabulate_BL_second_indeed\tabl_tabulate_TL_second_indeed
+
+\permanent\def\tabl_tabulate_HL_second
+ {\csname
+ \ifnum\c_tabl_tabulate_noflines=\zerocount F\orelse
+ \ifnum\c_tabl_tabulate_noflines=\c_tabl_tabulate_totalnoflines L\else
+ M\fi
+ L\endcsname}
+
+\appendtoks
+ \enforced\let\FL\tabl_tabulate_XX_none
+ \enforced\let\ML\tabl_tabulate_XX_none
+ \enforced\let\LL\tabl_tabulate_XX_none
+ \enforced\let\TL\tabl_tabulate_XX_none
+ \enforced\let\BL\tabl_tabulate_XX_none
+ \enforced\let\HL\tabl_tabulate_XX_none
+ \enforced\let\HR\tabl_tabulate_XX_none
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \enforced\let\FL\tabl_tabulate_FL_second
+ \enforced\let\ML\tabl_tabulate_ML_second
+ \enforced\let\LL\tabl_tabulate_LL_second
+ \enforced\let\TL\tabl_tabulate_TL_second
+ \enforced\let\BL\tabl_tabulate_BL_second
+ \enforced\let\HL\tabl_tabulate_HL_second
+ \enforced\let\HR\tabl_tabulate_HL_second
+\to \t_tabl_tabulate_initializers_second
+
+% \dorecurse{10}{
+% \starttabulate[|l|]
+% \FL
+% \NC first line, bound to next rule \NC \NR
+% \TL
+% \NC bound to previous rule \NC \NR
+% \NC some line \NC \NR
+% \NC some line \NC \NR
+% \NC some line \NC \NR
+% \NC bound to next rule \NC \NR
+% \ML
+% \NC bound to previous rule \NC \NR
+% \NC bound to next rule \NC \NR
+% \BL
+% \NC last line, bound to previous rule \NC \NR
+% \LL
+% \stoptabulate
+% }
+
+% This needs checking:
+
+\permanent\def\tabulaterule {\HR} % a rule with lineheight
+\permanent\def\tabulateline {\HL} % just a spaced rule
+\permanent\def\tabulateautorule{\HR}%
+\permanent\def\tabulateautoline{\HL} % no longer different (to be looked into)
+
+%D When support for vertical rules we needed a way to pick up the specification for
+%D the final rule and a \type {|{}} interface was chosen. As a result parsing had to
+%D become more complex and I was not in the mood for messing up the code too much.
+%D Therefore from now on the preamble is split by \LUA. There are definitely more
+%D places where we can use \LUA\ code (for instance in alignment of numbers. The
+%D repeat parser is replace at the \LUA\ end as well.
+
+\let\tabl_tabulate_flush_collected \empty
+\let\tabl_tabulate_flush_collected_indeed\empty
+
+\let\v_tabl_tabulate_align\!!zerocount
+
+\def\tabl_tabulate_check_side_float % new per 29-07-2016
+ {\ifdefined\page_sides_check_floats_indeed
+ \page_sides_check_floats_indeed
+ \ifdim\hangindent>\zeropoint
+ \advance\d_tabl_tabulate_indent\hangindent
+ \fi
+ \fi}
+
+\def\tabl_tabulate_set_local_hsize
+ {\setlocalhsize
+ \hsize\localhsize}
+
+% test case for pre/post spacing:
+%
+% \ruledvbox{\starttabulate[|l|] \FL\NC xxx \NC \NR\LL\stoptabulate}
+% \ruledvbox{\starttabulate[|l|l|]\FL\NC xxx \NC xxx \NC \NR\LL\stoptabulate}
+% \ruledvbox{\starttabulate[|l|] \NC xxx \NC \NR \stoptabulate}
+% \ruledvbox{\starttabulate[|lj8|] \NC xxx \NC \NR \stoptabulate}
+% \ruledvbox{\starttabulate[|k8|] \NC xxx \NC \NR \stoptabulate}
+% \ruledvbox{\starttabulate[|l|l|] \NC xxx \NC xxx \NC \NR \stoptabulate}
+
+\def\tabl_tabulate_process
+ {\c_tabl_tabulate_pass\plusone
+ \tabl_tabulate_check_full_content
+ \edef\v_tabl_tabulate_align{\ifcsname\??tabulatealigning\p_align\endcsname\lastnamedcs\else0\fi}%
+ \s_tabl_tabulate_first.5\d_tabl_tabulate_unit
+ \s_tabl_tabulate_last\s_tabl_tabulate_first
+ \s_tabl_tabulate_pre\zeropoint
+ \s_tabl_tabulate_post\zeropoint
+ %setfalse\c_tabl_pre_is_set
+ \setfalse\c_tabl_post_is_set
+ \global\c_tabl_tabulate_columns\zerocount
+ \global\c_tabl_tabulate_nofauto\zerocount
+ \global\c_tabl_tabulate_noflines\zerocount
+ \c_tabl_tabulate_totalnoflines\zerocount
+ \c_tabl_tabulate_minusnoflines\zerocount
+ \global\d_tabl_tabulate_width_p\zeropoint
+ \global\d_tabl_tabulate_width_w\zeropoint
+ \global\setfalse\c_tabl_tabulate_equal
+ \tabl_tabulate_pheight_reset
+ \tabskip\zeropoint
+ \ifinsidesplitfloat
+ \donetrue
+ \orelse\ifinsidefloat
+ \donefalse
+ \else
+ \donetrue
+ \fi
+ \global\c_tabl_tabulate_repeathead
+ \ifdone
+ \ifcsname\??tabulateheader\tabulationparameter\c!header\endcsname
+ \lastnamedcs
+ \else
+ \zerocount
+ \fi
+ \else
+ \zerocount
+ \fi
+ %
+ \the\t_tabl_tabulate_initializers_first % collect more here
+ %
+ \glet\tabl_tabulate_flush_collected\empty
+ \ifdim\d_tabl_tabulate_margin>\zeropoint
+ \t_tabl_tabulate_preamble
+ {\aligntab
+ \tabl_tabulate_flush_indent
+% \global\advance\c_tabl_tabulate_noflines\plusone
+ \strut
+ \alignmark\alignmark
+ \tabskip\d_tabl_tabulate_margin
+ \strut
+ \aligntab
+ \alignmark\alignmark
+ \tabskip\zeropoint}%
+ \else
+ \t_tabl_tabulate_preamble
+ {\aligntab
+ \tabl_tabulate_flush_indent
+% \global\advance\c_tabl_tabulate_noflines\plusone
+ \strut
+ \alignmark\alignmark
+ \aligntab
+ \alignmark\alignmark
+ \tabskip\zeropoint}%
+ \fi
+ \d_tabl_tabulate_width\zeropoint
+ % these counters are set at the lua end
+ \c_tabl_tabulate_nofcolumns \zerocount
+ \c_tabl_tabulate_has_rule_spec_first\zerocount
+ \c_tabl_tabulate_has_rule_spec_last \zerocount
+ \clf_presettabulate{\detokenizedtabulationparameter\c!format}%
+ %
+ % \edef\totaltabulatecolumns{\the\numexpr3*\c_tabl_tabulate_columns+\plusfour}%
+ \d_tabl_tabulate_width\zeropoint
+ \tabl_tabulate_initialize_boxes\c_tabl_tabulate_columns
+ \toksapp\t_tabl_tabulate_preamble{%
+ \aligntab\alignmark\alignmark
+ \global\advance\c_tabl_tabulate_column\plusone % maybe just set it already
+ }%
+ \toksapp\t_tabl_tabulate_dummy{%
+ \NC\unskip\unskip\crcr\tabl_tabulate_flush_collected % no count
+ }%
+ \global\c_tabl_tabulate_column\zerocount
+ \tabl_tabulate_pheight_reset
+ \glet\tabl_tabulate_hook\tabl_tabulate_hook_yes
+ \ifx\p_indenting\v!no
+ \forgetparindent
+ \fi
+ \ifinsidefloat
+ \d_tabl_tabulate_indent\zeropoint
+ \else
+ \tabl_tabulate_check_side_float
+ \tabl_tabulate_set_local_hsize
+ \fi
+ \dontcomplain
+ \forgetall % hm, interference with preceding \forgetparindent probably bug, to be solved
+ \everypar\everytabulatepar
+ \setbox\scratchbox\vbox % outside \if because of line counting
+ {\notesenabledfalse
+ \d_tabl_tabulate_indent\zeropoint
+ \settrialtypesetting % very important
+ \anch_backgrounds_text_level_start
+ \expandafter\halign\expandafter{\the\t_tabl_tabulate_preamble\crcr\tabl_tabulate_insert_content\crcr}}%
+ \anch_backgrounds_text_level_stop
+ \ifcase\c_anch_backgrounds_text_state\else
+ \global\settrue\tablehaspositions
+ \fi
+ \ifnum\c_tabl_tabulate_nofauto>\zerocount
+ % so, even if the natural size is larger, in the final run, we force the calculated width
+ \d_tabl_tabulate_width\dimexpr\hsize-\wd\scratchbox-\d_tabl_tabulate_width_p-\d_tabl_tabulate_width_w\relax
+ \ifnum\c_tabl_tabulate_nofauto>\zerocount
+ \divide\d_tabl_tabulate_width \c_tabl_tabulate_nofauto\relax
+ \fi
+ \fi
+ \setbox\scratchbox\emptybox % free memory
+ \ifconditional\c_tabl_tabulate_split
+ \splittopskip\strutht
+ \glet\tabl_tabulate_flush_collected_indeed\empty
+ \glet\tabl_tabulate_flush_collected\tabl_tabulate_flush_second
+ \fi
+ \c_tabl_tabulate_totalnoflines\c_tabl_tabulate_noflines
+ \c_tabl_tabulate_minusnoflines\numexpr\c_tabl_tabulate_noflines+\minusone\relax
+ \global\c_tabl_tabulate_noflines\zerocount
+ %
+ \c_tabl_tabulate_pass\plustwo % final pass
+ \the\t_tabl_tabulate_initializers_second % collect more here
+ %
+ \ifx\p_line\v!line
+ \enforced\let\HL\HR
+ \enforced\let\tabulateautoline\tabulateautorule
+ \enforced\let\tabulateline \tabulaterule
+ \fi
+ %
+ \ifcase\c_tabl_tabulate_repeathead
+ \ifinsidesplitfloat
+ \global\setbox\b_tabl_tabulate\vbox \bgroup
+ \else
+ \startframedcontent[\tabulationparameter\c!frame]%
+ \fi
+ \else
+ \global\setbox\b_tabl_tabulate\vbox \bgroup
+ \fi
+ %
+ \dostarttaggedchained\t!tabulate\empty\??tabulation
+ \dostarttagged\t!tabulaterow\empty
+ \setfalse\inhibitmargindata % new per 2012.06.13 ... really needed
+% % \everycr\expandafter{\the\everycr\noalign{\the\t_tabl_tabulate_every_real_row}\dostoptagged\dostarttagged\t!tabulaterow\empty}%
+ \toksapp\everycr{\noalign{\the\t_tabl_tabulate_every_real_row\dostoptagged\dostarttagged\t!tabulaterow\empty}}%
+ \expandafter\halign\expandafter{\the\t_tabl_tabulate_preamble\crcr\tabl_tabulate_insert_content\crcr}%
+ \dostoptagged
+ \dostoptagged
+ \ifhmode\par\prevdepth\strutdp\fi % nog eens beter, temporary hack
+ \ifx\p_distance\v!grid
+ \vskip-\strutdp % experimental tm-prikkels
+ \fi
+ %
+ \ifcase\c_tabl_tabulate_repeathead
+ \ifinsidesplitfloat
+ \egroup % box
+ \egroup % settings
+ \tabl_split_box\b_tabl_tabulate
+ \else
+ \stopframedcontent
+ \egroup
+ \fi
+ \else
+ \egroup % box
+ \egroup % settings
+ \tabl_split_box\b_tabl_tabulate
+ \fi
+ %
+ \ifinsidefloat
+ \tabl_tabulate_inside_after
+ \else
+ \tabl_tabulate_outside_after
+ \fi
+ \egroup} % whole thing
+
+% \egroup
+
+% \setuptabulate[split=yes,header=text,title=Vervolg van Tabel]
+%
+% % \starttabulatehead
+% % \NC test \NC hans\NC \NR
+% % \stoptabulatehead
+%
+% \starttabulate
+% \NC test \NC \input tufte \relax \NC \NR
+% \NC test \NC \input knuth \relax \NC \NR
+% \NC test \NC \input knuth \relax \NC \NR
+% \NC test \NC \input tufte \relax \NC \NR
+% \NC test \NC \input tufte \relax \NC \NR
+% \NC test \NC \input tufte \relax \NC \NR
+% \stoptabulate
+
+\def\tabl_split_box#1% #1 <> 0/2 / derived from the one in core-ntb.tex
+ {\ifinsidesplitfloat
+ \tabl_split_box_indeed#1%
+ \orelse\ifinsidefloat
+ \unvbox#1%
+ \else
+ \tabl_split_box_indeed#1%
+ \fi}
+
+\def\tabl_split_box_indeed#1%
+ {\resettsplit
+ \c_split_minimum_free_lines\plustwo
+ \d_split_minimum_free_space\zeropoint
+ \setbox\b_split_content\box#1%
+ \ifcase\c_tabl_tabulate_repeathead\or
+ \setbox\b_split_head\vsplit\b_split_content to \lineheight
+ \setbox\b_split_head\vbox{\unvbox\b_split_head}%
+ \or
+ \setbox\b_split_head\vbox{\hbox{\strut\tabulationparameter\c!title}}%
+ \fi
+ \handletsplit}
+
+%D \starttyping
+%D \setuptabulate[split=no,rule=line]
+%D
+%D \starttabulate
+%D \NC tufte \NC \input tufte \NC \NR \tabulateautorule
+%D \NC tufte \NC \input tufte \NC \NR \tabulateautorule
+%D \NC tufte \NC \input tufte \NC \NR \tabulateautorule
+%D \NC tufte \NC \input tufte \NC \NR \tabulateautorule
+%D \NC tufte \NC \input tufte \NC \NR \tabulateautorule
+%D \NC tufte \NC \input tufte \NC \NR \tabulateautorule
+%D \stoptabulate
+%D \stoptyping
+
+%D Spacing:
+%
+% \starttabulate
+% \NC text \NC text \NC \NR
+% \TB[small]
+% \NC text \NC text \NC \NR
+% \TB[4*big]
+% \NC text \NC text \NC \NR
+% \stoptabulate
+
+\permanent\tolerant\noaligned\protected\def\tabl_tabulate_TB[#1]%
+ {\noalign\bgroup
+ \unless\iftok{#1}\emptytoks
+ \blank[#1]
+ \orelse\ifempty\m_tabl_tabulate_blank_default
+ \blank
+ \else
+ \blank[\m_tabl_tabulate_blank_default]%
+ \fi
+ \egroup}
+
+\appendtoks
+ \enforced\let\TB\tabl_tabulate_TB
+\to \everytabulate
+
+% %D Between alignment lines certain rules apply, and even a simple test can mess
+% %D up a table, which is why we have a special test facilityL
+% %D
+% %D \startbuffer
+% %D \starttabulate[|l|p|]
+% %D \NC 1test \NC test \NC \NR
+% %D \tableifelse{\doifelse{a}{a}}{\NC Xtest \NC test \NC \NR}{}%
+% %D \stoptabulate
+% %D \stopbuffer
+% %D
+% %D \typebuffer \getbuffer
+%
+% \def\tableifelse#1% should be tabulatenoalign then
+% {\tablenoalign
+% {#1%
+% {\aftergroup \firstoftwoarguments}%
+% {\aftergroup\secondoftwoarguments}}}
+%
+% \def\tableiftextelse#1{\tableifelse{\doiftextelse{#1}}}
+
+%D Some new trickery:
+%D
+%D \startbuffer
+%D \settrue\c_tabl_tabulate_splitoff_whitespace
+%D
+%D \starttabulate[|p(2cm)|p(2cm)|p(2cm)|]
+%D \NC test 1a \NC test 2a \NC test 3a
+%D \par
+%D test 3b \NC \NR
+%D \NC test 1a \NC test 2a \NC test 3a
+%D \blank[line]
+%D test 3b \NC \NR
+%D \NC test 1a \NC test 2a \NC test 3a
+%D \blank[halfline]
+%D test 3b
+%D \blank[halfline]
+%D test 3c \NC \NR
+%D \NC \blank \NC \blank \NC \blank \NC \NR
+%D \NC test 1a \NC test 2a \NC test 3a
+%D \blank[halfline]
+%D test 3b
+%D \blank[halfline]
+%D test 3c \NC \NR
+%D \NC \blank \NC \blank \NC \NC \NR
+%D \NC test 1a
+%D \par
+%D test 1b
+%D \par
+%D test 1b \NC test 2a
+%D \par
+%D test 2b
+%D \par
+%D test 2b \NC test 3a \NC \NR
+%D \NC test 1a
+%D \blank
+%D test 1b
+%D \par
+%D test 1b \NC test 2a
+%D \par
+%D test 2b
+%D \blank
+%D test 2b \NC test 3a \NC \NR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \start \getbuffer \stop
+
+% \starttabulatie[|mc|]
+% \NC \digits{100.000,00} \NC\NR
+% \NC \digits{@10.000,00} \NC\NR
+% \NC \digits{@@@.100,00} \NC\NR
+% \NC \digits{@@@.@10,@@} \NC\NR
+% \NC \digits{@@@.@@1,@@} \NC\NR
+% \stoptabulatie
+%
+% \starttabulatie[|mc|]
+% \ND 100.000,00 \NC\NR
+% \ND @10.000,00 \NC\NR
+% \ND @@@.100,00 \NC\NR
+% \ND @@@.@10,@@ \NC\NR
+% \ND @@@.@@1,@@ \NC\NR
+% \stoptabulatie
+%
+% \starttabulatie[|c|]
+% \ND $100.000,00$ \NC\NR
+% \ND $@10.000,00$ \NC\NR
+% \ND $@@@.100,00$ \NC\NR
+% \ND $@@@.@10,@@$ \NC\NR
+% \ND $@@@.@@1,@@$ \NC\NR
+% \stoptabulatie
+%
+% \starttabulatie[|c|]
+% \NC $\digits 100.000,00 $ \NC\NR
+% \NC $\digits @10.000,00 $ \NC\NR
+% \NC $\digits @@@.100,00 $ \NC\NR
+% \NC $\digits @@@.@10,@@ $ \NC\NR
+% \NC $\digits @@@.@@1,@@ $ \NC\NR
+% \stoptabulatie
+%
+% \starttabulatie[|c|]
+% \NC \digits $100.000,00$ \NC\NR
+% \NC \digits $@10.000,00$ \NC\NR
+% \NC \digits $@@@.100,00$ \NC\NR
+% \NC \digits $@@@.@10,@@$ \NC\NR
+% \NC \digits $@@@.@@1,@@$ \NC\NR
+% \stoptabulatie
+
+%D Predefined categories (moved from core-mis):
+
+\mutable\let\leg \relax % column separators
+\mutable\let\fact\relax % column separators
+
+\definetabulate
+ [\v!legend]
+ [|emj1|i1|mR|]
+
+\setuptabulate
+ [\v!legend]
+ [\c!unit=.75em,\c!inner=\tabl_tabulate_set_quick\leg,EQ={=}]
+
+\definetabulate
+ [\v!legend][\v!two]
+ [|emj1|emk1|i1|mR|]
+
+\definetabulate
+ [\v!fact]
+ [|R|ecmj1|i1mR|]
+
+\setuptabulate
+ [\v!fact]
+ [\c!unit=.75em,\c!inner=\tabl_tabulate_set_quick\fact,EQ={=}]
+
+%D Another example:
+%D
+%D \starttyping
+%D \definetabulate
+%D [whatever]
+%D [|l|r|]
+%D
+%D \definetabulate
+%D [whatever][else]
+%D [|l|c|r|]
+%D
+%D \startwhatever
+%D \NC l \NC r \NC \NR
+%D \NC left \NC right \NC \NR
+%D \stopwhatever
+%D
+%D \startwhatever[else]
+%D \NC l \NC m \NC r \NC \NR
+%D \NC left \NC middle \NC right \NC \NR
+%D \stopwhatever
+%D
+%D \startwhatever[else][format={|c|c|c|c|}]
+%D \NC l \NC m \NC m \NC r \NC \NR
+%D \NC left \NC middle \NC middle \NC right \NC \NR
+%D \stopwhatever
+%D \stoptyping
+
+%D This is needed because we sometimes use the english command in tracing macros. In
+%D fact, most detailed tracing macros that are done with \LUA\ only work in the
+%D english interface anyway.
+
+% \definetabulate[tabulate] \setuptabulate[tabulate][\c!format=\v!none] % so no \v! here
+
+\newconditional\c_tabl_generic
+
+\pushoverloadmode
+
+\permanent\protected\defcsname starttabulate\endcsname
+ {\bgroup % whole thing
+ \settrue\c_tabl_generic
+ \let\currenttabulationparent\empty
+ \tabl_start_regular}
+
+\permanent\letcsname stoptabulate\endcsname\relax % testcase cvs-002.tex
+
+\popoverloadmode
+
+%D The following helpers are just there because we also have them at the \LUA\ end:
+%D
+%D \startbuffer
+%D \starttabulate[|l|c|r|]
+%D \tabulaterow {a,b,c}
+%D \tabulaterowbold{aa,bb,cc}
+%D \tabulaterowtype{aaa,bbb,ccc}
+%D \tabulaterowtyp {aaaa,bbbb,cccc}
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\def\tabl_tabulate_compact_row#1#2%
+ {\NC\tabl_tabulate_compact_step#1#2,\end,}
+
+\def\tabl_tabulate_compact_step#1#2#3,%
+ {\ifx#2\end
+ \NR
+ \expandafter\gobbleoneargument
+ \else
+ #1{#2#3}\NC
+ \expandafter\tabl_tabulate_compact_step
+ \fi#1}
+
+\permanent\protected\def\tabulaterow {\tabl_tabulate_compact_row\relax}
+\permanent\protected\def\tabulaterowbold{\tabl_tabulate_compact_row\bold}
+\permanent\protected\def\tabulaterowtype{\tabl_tabulate_compact_row\type}
+\permanent\protected\def\tabulaterowtyp {\tabl_tabulate_compact_row\typ}
+
+%D Here we plug in a row background feature. As we only have support for
+%D \type {frame=name} we can use these variables.
+%D
+%D \starttyping
+%D \startuseMPgraphic{foo}
+%D fill unitsquare
+%D xyscaled (RuleWidth,RuleHeight+RuleDepth) enlarged (ExHeight/4,ExHeight/8)
+%D randomized ExHeight
+%D shifted (-ExHeight/8,ExHeight/16)
+%D withcolor RuleColor ;
+%D \stopuseMPgraphic
+%D
+%D \setuptabulate % wel only have frame=name so we can use these:
+%D [background=foo,
+%D backgroundcolor=darkred,
+%D foregroundcolor=white]
+%D
+%D \definelinefiller[foo][mp=foo,color=darkgreen]
+%D \definelinefiller[bar][mp=foo,color=darkred]
+%D
+%D \starttabulate[|||]
+%D \DB foo \BC bar \BC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \stoptabulate
+%D
+%D \starttabulate[|||]
+%D \PB foo \BC bar \BC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \stoptabulate
+%D
+%D \starttabulate[|||]
+%D \FB[bar] foo \BC bar \BC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \stoptabulate
+%D
+%D \startnarrower
+%D \starttabulate[|||]
+%D \DB foo \DB bar \BC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \stoptabulate
+%D \stopnarrower
+%D
+%D \starttabulate[|||]
+%D \BC foo \BC bar \BC \NR
+%D \NL[magenta] foo \NC bar \NC \NR
+%D \NL[yellow] foo \NC bar \NC \NR
+%D \NL[cyan] foo \NC bar \NC \NR
+%D \NL[gray] foo \NC bar \NC \NR
+%D \stoptabulate
+%D
+%D \starttabulate
+%D \NL[red] foo \NC bar \NC \NR
+%D \NL[green] foo \NL[red] bar \NC \NR
+%D \NC foo \NC bar \NC \NR
+%D \NL[blue] foo \NC \input tufte \NC \NR
+%D \NL[gray] foo \NC bar \NC \NR
+%D \NL[yellow] foo \NC bar \NC \NR
+%D \stoptabulate
+%D \stoptyping
+
+% \setuptabulate
+% [\c!background=,
+% \c!backgroundcolor=,
+% \c!foregroundcolor=,
+% \c!foregroundstyle=]
+
+\let\m_table_current_row_background \empty
+\let\m_table_current_row_background_default \empty
+\let\m_table_current_row_background_filler \empty
+\let\m_table_current_row_background_defaultfiller\empty
+\let\m_table_current_row_background_auto \empty
+
+\protected\def\tabl_register_row_background#1%
+ {\xdef\m_table_current_row_background{#1}}
+
+\protected\def\tabl_register_row_background_filler#1%
+ {\xdef\m_table_current_row_background_filler{#1}}
+
+\protected\def\tabl_synchronize_row_background
+ {\iftrialtypesetting\else
+ \ifempty\m_table_current_row_background_filler
+ \ifempty\m_table_current_row_background
+ % nothing
+ \tabl_synchronize_row_background_dummy
+ \else
+ \tabl_synchronize_row_background_indeed\m_table_current_row_background
+ \fi
+ \else
+ \tabl_synchronize_row_background_filler_indeed\m_table_current_row_background_filler
+ \fi
+ \fi}
+
+\protected\def\tabl_synchronize_row_background_dummy
+ {\iftrialtypesetting\else
+ \begingroup
+ %\clf_setbackgroundrowdata\numexpr\c_tabl_tabulate_nofrealrows+\minusone\relax\zerocount\zeropoint
+ \clf_setbackgroundrowdata\c_tabl_tabulate_nofrealrows\zerocount\zeropoint
+ \endgroup
+ \fi}
+
+\protected\def\tabl_synchronize_row_background_indeed#1%
+ {\iftrialtypesetting\else
+ \begingroup
+ \clf_enablebackgroundalign % can be moved into \clf_setbackgroundrowdata
+ \dousecolorparameter{#1}%
+ \setbox\scratchbox\hpack{}%
+ %\clf_setbackgroundrowdata\numexpr\c_tabl_tabulate_nofrealrows+\minusone\relax\scratchbox\d_tabl_tabulate_indent
+ \clf_setbackgroundrowdata\c_tabl_tabulate_nofrealrows\scratchbox\d_tabl_tabulate_indent
+ \endgroup
+ \fi}
+
+\protected\def\tabl_synchronize_row_background_filler_indeed#1%
+ {\iftrialtypesetting\else
+ \begingroup
+ \clf_enablebackgroundalign % can be moved into \clf_setbackgroundrowdata
+ \node_linefiller_set{#1}%
+ \setbox\scratchbox\hpack{}%
+ %\clf_setbackgroundrowdata\numexpr\c_tabl_tabulate_nofrealrows+\minusone\relax\scratchbox\d_tabl_tabulate_indent
+ \clf_setbackgroundrowdata\c_tabl_tabulate_nofrealrows\scratchbox\d_tabl_tabulate_indent
+ \endgroup
+ \fi}
+
+\appendtoks
+ \glet\m_table_current_row_background\empty
+ \glet\m_table_current_row_background_filler\empty
+ \global\c_tabl_tabulate_nofrealrows\zerocount
+ \global\c_tabl_tabulate_autocolor\zerocount
+ \clf_resetbackgroundrowdata
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \glet\m_table_current_row_background\empty
+ \glet\m_table_current_row_background_filler\empty
+ \global\c_tabl_tabulate_nofrealrows\zerocount
+ \global\c_tabl_tabulate_autocolor\zerocount
+ \clf_resetbackgroundrowdata
+\to \t_tabl_tabulate_initializers_second
+
+\appendtoks
+ \tabl_synchronize_row_background
+\to \t_tabl_tabulate_every_real_row
+
+\appendtoks
+ \glet\m_table_current_row_background\empty
+ \glet\m_table_current_row_background_filler\empty
+\to \t_tabl_tabulate_every_after_row
+
+\permanent\protected\def\tabl_tabulate_NL_first[#1]%
+ {\tabl_tabulate_column_normal\zerocount\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background{#1}%
+ \fi
+ \ignorespaces}
+
+\pushoverloadmode
+
+ % redefinition needs checking
+
+ \permanent\overloaded\protected\def\tabl_tabulate_ND_first
+ {\tabl_tabulate_column_normal\zerocount\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background\m_table_current_row_background_default
+ \fi
+ \ignorespaces}
+
+\popoverloadmode
+
+\permanent\protected\def\tabl_tabulate_LB_first[#1]%
+ {\tabl_tabulate_column_normal\plusone\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background{#1}%
+ \fi
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \ignorespaces}
+
+\permanent\protected\def\tabl_tabulate_DB_first
+ {\tabl_tabulate_column_normal\plusone\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background\m_table_current_row_background_default
+ \fi
+ \enforced\let\fontstyle\globalfontstyle
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \ignorespaces}
+
+\permanent\protected\def\tabl_tabulate_NF_first[#1]%
+ {\tabl_tabulate_column_normal\zerocount\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background_filler{#1}%
+ \fi
+ \ignorespaces}
+
+\permanent\protected\def\tabl_tabulate_NP_first
+ {\tabl_tabulate_column_normal\zerocount\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background_filler\m_table_current_row_background_default_filler
+ \fi
+ \ignorespaces}
+
+\permanent\protected\def\tabl_tabulate_FB_first[#1]%
+ {\tabl_tabulate_column_normal\plusone\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background_filler{#1}%
+ \fi
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \ignorespaces}
+
+\permanent\protected\def\tabl_tabulate_PB_first
+ {\tabl_tabulate_column_normal\plusone\zerocount\relax
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_register_row_background_filler\m_table_current_row_background_default_filler
+ \fi
+ \enforced\let\fontstyle\globalfontstyle
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \ignorespaces}
+
+\pushoverloadmode
+
+ \permanent\overloaded\protected\def\tabl_tabulate_BC_first
+ {\tabl_tabulate_column_normal\plusone\zerocount
+ \enforced\let\fontstyle\globalfontstyle
+ \ifempty\m_table_current_row_background
+ \ifempty\m_table_current_row_background_filler
+ \usetabulationstyleandcolor\c!headstyle\c!headcolor
+ \else
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \fi
+ \else
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \fi}
+
+\popoverloadmode
+
+\permanent\protected\def\tabl_tabulate_A_first
+ {\global\advance\c_tabl_tabulate_autocolor\plusone
+ \edef\m_table_current_row_background_auto{\tabulateparameter{\c!backgroundcolor:\number\c_tabl_tabulate_autocolor}}%
+ \ifempty\m_table_current_row_background_auto
+ \global\c_tabl_tabulate_autocolor\plusone
+ \edef\m_table_current_row_background_auto{\tabulateparameter{\c!backgroundcolor:\number\c_tabl_tabulate_autocolor}}%
+ \fi
+ \ifempty\m_table_current_row_background_auto
+ \let\m_table_current_row_background_auto\empty % \m_table_current_row_background_default
+ \fi
+ \tabl_register_row_background{\m_table_current_row_background_auto}}
+
+\permanent\protected\def\tabl_tabulate_NA_first
+ {\tabl_tabulate_column_normal\zerocount\zerocount\relax
+ \iftrialtypesetting\else
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_tabulate_A_first
+ \fi
+ \fi
+ \ignorespaces}
+
+\permanent\protected\def\tabl_tabulate_BA_first
+ {\tabl_tabulate_column_normal\plusone\zerocount\relax
+ \iftrialtypesetting\else
+ \ifcase\c_tabl_tabulate_column\or
+ \tabl_tabulate_A_first
+ \fi
+ \fi
+ \usetabulationstyleandcolor\c!foregroundstyle\c!foregroundcolor
+ \ignorespaces}
+
+\appendtoks
+ \enforced\let\NL\tabl_tabulate_NL_first % NC with Line
+ \enforced\let\ND\tabl_tabulate_ND_first % NC with Default Line
+ \enforced\let\LB\tabl_tabulate_LB_first % BC with Line
+ \enforced\let\DB\tabl_tabulate_DB_first % BC with Default Line
+ \enforced\let\NF\tabl_tabulate_NF_first % NC with Filler
+ \enforced\let\NP\tabl_tabulate_NP_first % NC with Predefined Filler
+ \enforced\let\FB\tabl_tabulate_FB_first % BC with Filler
+ \enforced\let\PB\tabl_tabulate_PB_first % BC with Predefined Filler
+ \enforced\let\NA\tabl_tabulate_NA_first % NC with Auto Line
+ \enforced\let\BA\tabl_tabulate_BA_first % NC with Auto Line
+\to \t_tabl_tabulate_initializers_first
+
+\appendtoks
+ \edef\m_table_current_row_background_default {\tabulateparameter\c!backgroundcolor}%
+ \edef\m_table_current_row_background_default_filler{\tabulateparameter\c!background}%
+ \let \m_table_current_row_background_auto \empty
+\to \everytabulate
+
+\setuptabulate
+ [\c!headcolor=,
+ \c!headstyle=\bf,
+ \c!backgroundcolor=\tabulationparameter\c!rulecolor,
+ \c!foregroundcolor=,
+ \c!foregroundstyle=\tabulationparameter\c!headstyle]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-tsp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/tabl-tsp.mkxl
new file mode 100644
index 00000000000..5f01e7eb4ae
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-tsp.mkxl
@@ -0,0 +1,580 @@
+%D \module
+%D [ file=tabl-tsp,
+%D version=2000.10.20,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Splitting,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / Splitting}
+
+%D The code in this file is moved here from other places and needs a mkiv cleanup.
+%D As it mostly targets at tables the code lives in the tabl and page namespaces.
+
+% work in progress
+
+\unprotect
+
+%D Although the name resembles floats, and therefore this should be a page module,
+%D we decided to make it core functionality because the table code depends on it.
+%D Othrwise there would be too much overloading afterwards involved. Actually, the
+%D float part is rather generic and not that related to floats.
+
+% \splitfloat [settings] {\placetable[optional args]{test}} {content}
+
+%D When \type {inbetween} is made empty instead of the default \type {\page}, we
+%D will get delayed flushing and text may continue below the graphic.
+%D
+%D \starttyping
+%D \dorecurse{2}{\input tufte }
+%D
+%D \splitfloat[lines=auto,inbetween=]
+%D {\placetable{\dorecurse{5}{test\recurselevel\endgraf}}}
+%D {\bTABLE[split=yes]
+%D \bTR \bTD 11 \eTD \bTD \input tufte \eTD \eTR
+%D \bTR \bTD 12 \eTD \bTD \input zapf \eTD \eTR
+%D \bTR \bTD 13 \eTD \bTD \input bryson \eTD \eTR
+%D \bTR \bTD 14 \eTD \bTD test \eTD \eTR
+%D \bTR \bTD 21 \eTD \bTD \input tufte \eTD \eTR
+%D \bTR \bTD 22 \eTD \bTD \input zapf \eTD \eTR
+%D \bTR \bTD 23 \eTD \bTD \input bryson \eTD \eTR
+%D \bTR \bTD 24 \eTD \bTD test \eTD \eTR
+%D \bTR \bTD 31 \eTD \bTD \input tufte \eTD \eTR
+%D \bTR \bTD 32 \eTD \bTD \input zapf \eTD \eTR
+%D \bTR \bTD 33 \eTD \bTD \input bryson \eTD \eTR
+%D \bTR \bTD 34 \eTD \bTD test \eTD \eTR
+%D \eTABLE}
+%D
+%D \dorecurse{10}{\input tufte }
+%D \stoptyping
+
+\installcorenamespace{floatsplitting}
+
+\installdirectcommandhandler \??floatsplitting {floatsplitting} % \??floatsplitting
+
+\setupfloatsplitting
+ [\c!conversion=\v!character, % \v!romannumerals
+ \c!lines=3,
+ \c!before=,
+ \c!inbetween=\page,
+ \c!after=]
+
+\newconditional\splitfloatfirstdone
+\newconditional\somenextsplitofffloat
+\newconditional\splitfloatdone
+\newconditional\onlyonesplitofffloat \settrue\onlyonesplitofffloat
+
+\newif \ifinsidesplitfloat % will become conditional
+
+\newcount \noffloatssplits
+\newtoks \everysplitfloatsetup
+
+\mutable\let \extrasplitfloatlines \!!zerocount
+\mutable\let \splitfloatfinalizer \relax
+\mutable\let \splitfloatcommand \empty
+
+\mutable\let \floatcaptionsuffix \empty
+
+\permanent\tolerant\protected\def\splitfloat[#1]#:#2% nog dubbele refs
+ {\bgroup
+ \global\setfalse\splitfloatdone
+ \aftergroup\page_split_float_check
+ \insidefloattrue
+ \insidesplitfloattrue
+ \setupcurrentfloatsplitting[#1]%
+ \global\noffloatssplits\zerocount
+ \let\floatcaptionsuffix\page_split_float_suffix
+ \edef\extrasplitfloatlines{\floatsplittingparameter\c!lines}%
+ \the\everysplitfloatsetup
+ \def\splitfloatcommand{#2}%
+ \global\settrue \onlyonesplitofffloat
+ \global\setfalse\somenextsplitofffloat
+ \page_floats_push_saved
+ \floatsplittingparameter\c!before
+ \let\next} % \bgroup
+
+\protected\def\page_split_float_suffix
+ {\begingroup
+ \usefloatsplittingstyleandcolor\c!style\c!color % only the suffix
+ \convertnumber{\floatsplittingparameter\c!conversion}\noffloatssplits
+ \endgroup}
+
+\protected\def\page_split_float_check
+ {\ifconditional\splitfloatdone
+ \splitfloatfinalizer % a weird place (could interfere with flushing)
+ \else
+ \blank
+ \begingroup
+ \tttf \dontleavehmode \getmessage\m!floatblocks{13}\empty
+ \endgroup
+ \blank
+ \showmessage\m!floatblocks{13}\empty
+ \fi}
+
+\def\page_split_float_process % nextbox
+ {\ifinsidesplitfloat
+ \expandafter\page_split_float_process_yes
+ \else
+ \expandafter\page_split_float_process_nop
+ \fi}
+
+\def\page_split_float_process_yes
+ {\dowithnextboxcs\page_split_float_process_finish\vbox}
+
+\def\page_split_float_process_finish
+ {\forgetall
+ \dontcomplain
+ \global\settrue\splitfloatdone
+ % \nodelocationmode\zerocount % bypass auto-renumbering
+ \global\advance\noffloatssplits\plusone
+ \ifcase\noffloatssplits\relax \or
+ \ifconditional\onlyonesplitofffloat
+ \let\floatcaptionsuffix\empty
+ \fi
+ \fi
+ \bgroup
+ \ifconditional\somenextsplitofffloat
+ \notesenabledfalse % best here, experimental, brrr; test with note in caption
+ \fi
+ \splitfloatcommand{\box\nextbox}%
+ \egroup
+ \ifconditional\somenextsplitofffloat
+ \edef\p_inbetween{\floatsplittingparameter\c!inbetween}%
+ \ifempty\p_inbetween
+ \ifconditional\splitfloatfirstdone\else\page\fi
+ \else
+ \p_inbetween
+ \fi
+ \else
+ \floatsplittingparameter\c!after
+ \page_floats_pop_saved
+ \page_floats_flush_saved
+ \fi
+ \global\settrue\splitfloatfirstdone}
+
+\def\page_split_float_process_nop
+ {\dowithnextboxcs\page_split_float_process_nop_finish\vbox}
+
+\def\page_split_float_process_nop_finish
+ {\forgetall
+ \dontcomplain
+ \box\nextbox % maybe an option to unvbox
+ \global\settrue\splitfloatfirstdone}
+
+\def\page_split_float_check_content#1% box
+ {\ifinsidesplitfloat
+ % \ifdim\ht#1=\zeropoint % funny: \ifcase does not check for overflow
+ \ifcase\ht#1\relax
+ \global\setfalse\somenextsplitofffloat
+ \else
+ \global\settrue \somenextsplitofffloat
+ \global\setfalse\onlyonesplitofffloat
+ \fi
+ \fi}
+
+\def\page_split_float_check_caption#1% depends on page-flt .. pretty messy
+ {\edef\extrasplitfloatlines{\extrasplitfloatlines}%
+ \ifx\extrasplitfloatlines\v!auto
+ \bgroup
+ \forcelocalfloats
+ \setuplocalfloats[\c!before=,\c!after=,\c!inbetween=]%
+ % This controls samepage resetting too but it also messes up the numbering
+ % so I need another fix.
+% \settrialtypesetting
+ \splitfloatcommand{\hbox to #1{\strut}}% dummy line
+% \resettrialtypesetting
+ \setbox\scratchbox\vbox{\flushlocalfloats}% \vpack ?
+ \getnoflines{\ht\scratchbox}%
+ \resetlocalfloats
+ \advance\noflines\minusone % compensate dummy line
+ \normalexpanded{\egroup\noexpand\edef\noexpand\extrasplitfloatlines{\the\noflines}}%
+ \global\settrue\usesamefloatnumber
+ \else
+ \doifelsenumber\extrasplitfloatlines\donothing{\def\extrasplitfloatlines{1}}%
+ \fi}
+
+\permanent\protected\def\doifnotinsidesplitfloat
+ {\ifinsidesplitfloat
+ \expandafter\gobbleoneargument
+ \fi}
+
+%D Table splitter, on top of previous code:
+
+% todo: keep tail to rest, so we need a lookahead
+
+\newbox\b_split_content
+\newbox\b_split_result
+\newbox\b_split_head
+\newbox\b_split_next
+\newbox\b_split_tail
+
+\newtoks\t_split_before_result
+\newtoks\t_split_after_result
+\newtoks\t_split_before
+\newtoks\t_split_inbetween
+\newtoks\t_split_after
+
+\newtoks\everyresettsplit
+
+\newcount\c_split_minimum_free_lines
+
+\newdimen\d_split_minimum_free_space
+\newdimen\d_split_available_height
+\newdimen\d_split_inbetween_height
+
+\newconditional\c_tabl_split_done
+\newconditional\c_tabl_split_head
+\newconditional\c_tabl_split_full
+
+\newconditional\tabl_split_forced_page
+
+\appendtoks
+ \c_split_minimum_free_lines\zerocount
+ \d_split_minimum_free_space\zeropoint
+ \setbox\b_split_content \emptyvbox
+ \setbox\b_split_result \emptyvbox
+ \setbox\b_split_head \emptyvbox
+ \setbox\b_split_next \emptyvbox
+ \setbox\b_split_tail \emptyvbox
+ \t_split_before_result \emptytoks
+ \t_split_after_result \emptytoks
+ \t_split_inbetween \emptytoks
+ \t_split_before \emptytoks
+ \t_split_after \emptytoks
+ \let\postprocesstsplit \donothing
+\to \everyresettsplit
+
+\mutable\let\postprocesstsplit\donothing
+
+\permanent\protected\def\resettsplit
+ {\the\everyresettsplit}
+
+\resettsplit
+
+\mutable\def\tsplitdirectwidth{\hsize}
+
+\permanent\protected\def\handletsplit
+ {\page_split_float_check_caption{\wd\b_split_content}%
+ \global\setfalse\splitfloatfirstdone
+ \testpagesync % new, sync, but still tricky
+ [\number\c_split_minimum_free_lines]
+ [\dimexpr\d_split_minimum_free_space+\extrasplitfloatlines\lineheight\relax]%
+ \setbox\scratchbox\vbox{\the\t_split_inbetween}%
+ \d_split_inbetween_height\htdp\scratchbox
+ \setfalse\c_tabl_split_done
+ \doloop\tabl_split_loop_body
+ \global\setfalse\usesamefloatnumber % new, prevent next increment
+ \global\setfalse\splitfloatfirstdone} % we can use this one for tests
+
+\def\tabl_split_loop_body
+ {\ifinsidecolumns
+ % brrr, assumes empty columns
+ \global\setfalse\splitfloatfirstdone
+ \d_split_available_height\textheight
+ \settrue\c_tabl_split_full
+ \else
+ \ifconditional\splitfloatfirstdone
+ \d_split_available_height\textheight
+ \settrue\c_tabl_split_full
+ \orelse\ifdim\pagegoal<\maxdimen
+ \d_split_available_height\dimexpr\pagegoal-\pagetotal\relax
+ \setfalse\c_tabl_split_full
+ \else
+ \d_split_available_height\textheight
+ \settrue\c_tabl_split_full
+ \fi
+ \fi
+ \d_split_available_height \dimexpr
+ \d_split_available_height
+ -\d_split_inbetween_height
+ -\d_split_minimum_free_space
+ -\extrasplitfloatlines\lineheight
+ \relax
+ \ifdim\htdp\b_split_tail>\zeropoint
+ \advance\d_split_available_height-\htdp\b_split_tail
+ \fi
+ \setbox\b_split_result\vbox
+ {\ifdim\ht\b_split_head>\zeropoint
+ \unvcopy\b_split_head
+ \the\t_split_inbetween
+ \fi}%
+ \ifconditional\c_tabl_split_done \else
+ \ifdim\ht\b_split_next>\zeropoint
+ \setbox\b_split_head\box\b_split_next
+ \fi
+ \fi
+ \settrue\c_tabl_split_done
+ \ifdim\ht\b_split_result>\zeropoint
+ \settrue\c_tabl_split_head % table head
+ \else
+ \setfalse\c_tabl_split_head % no tablehead
+ \fi
+ \splittopskip\zeropoint
+ \doloop % inner loop
+ {\setbox\scratchbox\vsplit\b_split_content to \onepoint % \lineheight
+ \setbox\scratchbox\vbox % \vpack
+ {\unvbox\scratchbox
+ \setbox\scratchbox\vbox % \vpack
+ {\splitdiscards
+ \ifnum\lastpenalty>-\plustenthousand\else
+ % so that \bTR[before=\page] works
+ \global\settrue\tabl_split_forced_page
+ \fi}}%
+ \ifconditional\tabl_split_forced_page
+ \global\setfalse\tabl_split_forced_page
+ \setbox\b_split_result\vbox
+ {\unvbox\b_split_result
+ \the\t_split_inbetween
+ \unvbox\scratchbox}%
+ \exitloop
+ \orelse\ifdim\dimexpr\d_split_available_height-\htdp\scratchbox-\htdp\b_split_result\relax>\zeropoint
+ \setbox\b_split_result\vbox
+ {\unvbox\b_split_result
+ \the\t_split_inbetween
+ \unvbox\scratchbox}%
+ \ifvoid\b_split_content \exitloop \fi
+ \orelse\ifconditional\c_tabl_split_head
+ % we only have a tablehead so far
+ \setbox\b_split_result\vbox{\unvbox\b_split_result\unvbox\scratchbox}% \vpack
+ \exitloop
+ \orelse\ifconditional\c_tabl_split_full
+ % we have text height available, but the (one) cell is too
+ % large to fit, so, in order to avoid loops/deadcycles we do:
+ \setbox\b_split_result\vbox
+ {\unvbox\b_split_result
+ \the\t_split_inbetween
+ \unvbox\scratchbox}%
+ \exitloop
+ \else
+ \setbox\b_split_content\vbox
+ {\unvbox\scratchbox
+ \the\t_split_inbetween
+ \ifvoid\b_split_content\else\unvbox\b_split_content\fi}%
+ \exitloop
+ \fi
+ \setfalse\c_tabl_split_head
+ \setfalse\c_tabl_split_full}%
+ \postprocesstsplit
+ \page_split_float_check_content\b_split_content
+ \ifvoid\b_split_content
+ \setbox\b_split_result\vbox
+ {\unvbox\b_split_result
+ \the\t_split_inbetween
+ \unvcopy\b_split_tail}%
+ \page_split_float_process{\the\t_split_before_result\box\b_split_result\the\t_split_after_result}%
+ \doifnotinsidesplitfloat{\the\t_split_after}%
+ \endgraf
+ \exitloop
+ \else
+ % hack
+ \ifdim\pagegoal<\maxdimen
+ \pagegoal\dimexpr\pagegoal+\lineheight\relax % etex
+ \fi
+ % brrr
+ \ifdim\ht\b_split_result>\zeropoint
+ \setbox\b_split_result\vbox
+ {\unvbox\b_split_result
+ \the\t_split_inbetween
+ \unvcopy\b_split_tail}%
+ \page_split_float_process{\the\t_split_before_result\box\b_split_result\the\t_split_after_result}%
+ \doifnotinsidesplitfloat{\the\t_split_after}%
+ \endgraf
+ \global\settrue\usesamefloatnumber % new, prevent next increment
+ \fi
+ \ifinsidecolumns
+ \goodbreak % was \doifnotinsidesplitfloat\goodbreak
+ \else
+ \page % was \doifnotinsidesplitfloat\page
+ \fi
+ \fi}
+
+%D The next one assumes that the split takes place elsewhere. This is used in
+%D xtables.
+
+\aliased\let\resetdirecttsplit\resettsplit
+
+\mutable\let\tsplitdirectsplitter\relax
+
+\permanent\protected\def\handledirecttsplit
+ {\page_split_float_check_caption{\tsplitdirectwidth}%
+ \global\setfalse\splitfloatfirstdone
+ \testpagesync % new, sync, but still tricky
+ [\number\c_split_minimum_free_lines]
+ [\dimexpr\d_split_minimum_free_space+\extrasplitfloatlines\lineheight\relax]%
+ \doloop\tabl_split_direct_loop_body
+ \global\setfalse\usesamefloatnumber % new, prevent next increment
+ \global\setfalse\splitfloatfirstdone} % we can use this one for tests
+
+\def\tabl_split_direct_loop_body
+ {\ifinsidecolumns
+ \global\setfalse\splitfloatfirstdone
+ \d_split_available_height\textheight
+ \orelse\ifconditional\splitfloatfirstdone
+ \d_split_available_height\textheight
+ \orelse\ifdim\pagegoal<\maxdimen
+ \d_split_available_height\dimexpr\pagegoal-\pagetotal\relax
+ \else
+ \d_split_available_height\textheight
+ \fi
+ \d_split_available_height\dimexpr
+ \d_split_available_height
+ -\d_split_minimum_free_space
+ -\extrasplitfloatlines\lineheight
+ \relax
+ \tsplitdirectsplitter\d_split_available_height % also sets state
+ \ifdim\ht\b_split_result>\zeropoint
+ \ifconditional\somenextsplitofffloat
+ \global\setfalse\onlyonesplitofffloat
+ \fi
+ \ifdim\pagegoal<\maxdimen
+ \pagegoal\dimexpr\pagegoal+\lineheight\relax % etex
+ \fi
+ \page_split_float_process{\the\t_split_before_result\box\b_split_result\the\t_split_after_result}%
+ \global\settrue\usesamefloatnumber % new, prevent next increment
+ \endgraf
+ \ifconditional\somenextsplitofffloat
+ \ifinsidecolumns
+ \goodbreak
+ \else
+ \page
+ \fi
+ \fi
+ \global\settrue\splitfloatfirstdone
+ \orelse\ifconditional\somenextsplitofffloat
+ \ifinsidecolumns
+ \goodbreak
+ \else
+ \page % no room
+ \fi
+ \else
+ \exitloop
+ \fi}
+
+%D Maybe handy:
+%D
+%D \starttyping
+%D \splitfloat
+%D {\placefigure{some caption}}
+%D {\startsplittext
+%D \typefile[option=TEX,before=,after=]{oeps.tex}
+%D \stopsplittext}
+%D \stoptyping
+
+\permanent\def\handlesplittext#1%
+ {\setbox\b_split_result\vbox
+ {\vsplit\b_split_content to \dimexpr#1-\lineheight\relax}}
+
+\permanent\protected\def\startsplittext
+ {\begingroup
+ \resettsplit
+ \c_split_minimum_free_lines\zerocount
+ \d_split_minimum_free_space\zeropoint
+ \let\extrasplitfloatlines \!!plusone
+ \let\tsplitdirectsplitter \handlesplittext
+ \setbox\b_split_content\vbox\bgroup
+ \insidefloattrue}
+
+\permanent\protected\def\stopsplittext
+ {\egroup
+ \handledirecttsplit
+ \endgroup}
+
+\protect \endinput
+
+% test cases
+
+% \setupTABLE[split=repeat]
+%
+% \input tufte \endgraf
+% \splitfloat[lines=11]
+% {\placetable{\dorecurse{10}{test\recurselevel\endgraf}}}
+% {\bTABLE\dorecurse{100}{\bTR \bTD test \eTD \eTR}\eTABLE}
+% \input tufte \page
+%
+% \input tufte \endgraf
+% \splitfloat[lines=0]
+% {}
+% {\bTABLE\dorecurse{100}{\bTR \bTD test \eTD \eTR}\eTABLE}
+% \input tufte \endgraf \page
+%
+% \input tufte \endgraf
+% \bTABLE\dorecurse{100}{\bTR \bTD test \eTD \eTR}\eTABLE
+% \input tufte \page
+
+% \setuptabulate[split=yes]
+%
+% \input tufte \endgraf
+% \splitfloat[lines=11]
+% {\placetable{\dorecurse{10}{test\recurselevel\endgraf}}}
+% {\starttabulate\dorecurse{200}{\NC test \NC test \NC \NR}\stoptabulate}
+% \input tufte \page
+%
+% \input tufte \endgraf
+% \splitfloat[lines=0]
+% {}
+% {\starttabulate\dorecurse{200}{\NC test \NC test \NC \NR}\stoptabulate}
+% \input tufte \page
+%
+% \input tufte \endgraf
+% \starttabulate\dorecurse{200}{\NC test \NC test \NC \NR}\stoptabulate
+% \input tufte \page
+
+% \setuptables[split=yes]
+%
+% \newtoks\TestToks
+%
+% \TestToks\emptytoks
+% \appendtoks\starttablehead\to\TestToks
+% \dorecurse{3}{\appendtoks\VL head \VL head \VL \SR\to\TestToks}
+% \appendtoks\stoptablehead\to\TestToks
+% \appendtoks\starttabletail\to\TestToks
+% \dorecurse{3}{\appendtoks\VL tail \VL tail \VL \SR\to\TestToks}
+% \appendtoks\stoptabletail\to\TestToks
+% \appendtoks\starttables[|c|c|]\to\TestToks
+% \dorecurse{100}{\appendtoks\VL test \VL test \VL \SR\to\TestToks}
+% \appendtoks\stoptables\to\TestToks
+%
+% \input tufte \endgraf
+% \splitfloat[lines=auto] % [lines=11]
+% {\placetable{\dorecurse{10}{test\recurselevel\endgraf}}}
+% {\the\TestToks}
+% \input tufte \page
+%
+% \input tufte \endgraf
+% \splitfloat[lines=0]
+% {}
+% {\the\TestToks}
+% \input tufte \page
+%
+% \input tufte \endgraf
+% \the\TestToks
+% \input tufte \page
+%
+% multiple floats
+%
+% \starttext
+% \dorecurse{3}{\input tufte } \endgraf
+% \dorecurse{5}{\placefigure{}{\framed[height=.5\textheight]{}}}
+% \splitfloat[lines=auto,inbetween=]
+% {\placetable{\dorecurse{5}{test\recurselevel\endgraf}}}
+% {\bTABLE[split=yes]
+% \bTR \bTD 11 \eTD \bTD \input tufte \eTD \eTR
+% \bTR \bTD 12 \eTD \bTD \input zapf \eTD \eTR
+% \bTR \bTD 13 \eTD \bTD \input bryson \eTD \eTR
+% \bTR \bTD 14 \eTD \bTD test \eTD \eTR
+% \bTR \bTD 21 \eTD \bTD \input tufte \eTD \eTR
+% \bTR \bTD 22 \eTD \bTD \input zapf \eTD \eTR
+% \bTR \bTD 23 \eTD \bTD \input bryson \eTD \eTR
+% \bTR \bTD 24 \eTD \bTD test \eTD \eTR
+% \bTR \bTD 31 \eTD \bTD \input tufte \eTD \eTR
+% \bTR \bTD 32 \eTD \bTD \input zapf \eTD \eTR
+% \bTR \bTD 33 \eTD \bTD \input bryson \eTD \eTR
+% \bTR \bTD 34 \eTD \bTD test \eTD \eTR
+% \eTABLE}
+% \dorecurse{10}{\input tufte }
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-xnt.mklx b/Master/texmf-dist/tex/context/base/mkxl/tabl-xnt.mklx
new file mode 100644
index 00000000000..f2a41c500fe
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-xnt.mklx
@@ -0,0 +1,142 @@
+% macros=mkvi
+
+%D \module
+%D [ file=tabl-xtb,
+%D version=2011.10.28,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Natural to Xtreme Tables,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / Natural to Xtreme Tables}
+
+\unprotect
+
+%D Don't expect wonders but it might come in handy:
+%D
+%D \starttyping
+%D \mapTABLEtoxtable
+%D \restoreTABLEfromxtable
+%D \stoptyping
+%D
+%D We stick to a simple example:
+%D
+%D \startbuffer
+%D \bTABLE
+%D \bTR
+%D \bTD[width=1cm,background=color,backgroundcolor=red] one \eTD
+%D \bTD[width=2cm] two \eTD
+%D \eTR
+%D \bTR
+%D \bTD alpha \eTD
+%D \bTD beta \eTD
+%D \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startbuffer
+%D \startlinecorrection
+%D \getbuffer
+%D \stoplinecorrection
+%D
+%D \startlinecorrection
+%D \mapTABLEtoxtable \getbuffer
+%D \stoplinecorrection
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\pushoverloadmode % this file is runtime loaded
+
+\overloaded\permanent\let\tabl_x_normal_bTABLEhead\bTABLEhead
+\overloaded\permanent\let\tabl_x_normal_eTABLEhead\eTABLEhead
+\overloaded\permanent\let\tabl_x_normal_bTABLEnext\bTABLEnext
+\overloaded\permanent\let\tabl_x_normal_eTABLEnext\eTABLEnext
+\overloaded\permanent\let\tabl_x_normal_bTABLEfoot\bTABLEfoot
+\overloaded\permanent\let\tabl_x_normal_eTABLEfoot\eTABLEfoot
+\overloaded\permanent\let\tabl_x_normal_bTABLEbody\bTABLEbody
+\overloaded\permanent\let\tabl_x_normal_eTABLEbody\eTABLEbody
+\overloaded\permanent\let\tabl_x_normal_bTR \bTR
+\overloaded\permanent\let\tabl_x_normal_eTR \eTR
+\overloaded\permanent\let\tabl_x_normal_bTD \bTD
+\overloaded\permanent\let\tabl_x_normal_eTD \eTD
+\overloaded\permanent\let\tabl_x_normal_bTH \bTH
+\overloaded\permanent\let\tabl_x_normal_eTH \eTH
+\overloaded\permanent\let\tabl_x_normal_setupTABLE\setupTABLE
+\overloaded\permanent\let\tabl_x_normal_bTABLE \bTABLE
+\overloaded\permanent\let\tabl_x_normal_eTABLE \eTABLE
+
+\permanent\protected\def\restoreTABLEfromxtable
+ {\enforced\let\bTABLEhead\tabl_x_normal_bTABLEhead
+ \enforced\let\eTABLEhead\tabl_x_normal_eTABLEhead
+ \enforced\let\bTABLEnext\tabl_x_normal_bTABLEnext
+ \enforced\let\eTABLEnext\tabl_x_normal_eTABLEnext
+ \enforced\let\bTABLEfoot\tabl_x_normal_bTABLEfoot
+ \enforced\let\eTABLEfoot\tabl_x_normal_eTABLEfoot
+ \enforced\let\bTABLEbody\tabl_x_normal_bTABLEbody
+ \enforced\let\eTABLEbody\tabl_x_normal_eTABLEbody
+ \enforced\let\bTR \tabl_x_normal_bTR
+ \enforced\let\eTR \tabl_x_normal_eTR
+ \enforced\let\bTD \tabl_x_normal_bTD
+ \enforced\let\eTD \tabl_x_normal_eTD
+ \enforced\let\bTH \tabl_x_normal_bTH
+ \enforced\let\eTH \tabl_x_normal_eTH
+ \enforced\let\setupTABLE\tabl_x_normal_setupTABLE
+ \enforced\let\bTABLE \tabl_x_normal_bTABLE
+ \enforced\let\eTABLE \tabl_x_normal_eTABLE}
+
+\permanent\protected\def\mapTABLEtoxtable
+ {\enforced\let\bTABLEhead\startxtablehead
+ \enforced\let\eTABLEhead\stopxtablehead
+ \enforced\let\bTABLEnext\startxtablefoot
+ \enforced\let\eTABLEnext\stopxtablefoot
+ \enforced\let\bTABLEfoot\startxtablefoot
+ \enforced\let\eTABLEfoot\stopxtablefoot
+ \enforced\let\bTABLEbody\startxtablebody
+ \enforced\let\eTABLEbody\stopxtablebody
+ \enforced\let\bTR \startxrow
+ \enforced\let\eTR \stopxrow
+ \enforced\let\bTD \startxcell
+ \enforced\let\eTD \stopxcell
+ \enforced\let\bTH \tabl_x_TABLE_TH_start_cell
+ \enforced\let\eTH \tabl_x_TABLE_TH_stop_cell
+ \enforced\let\setupTABLE\tabl_x_TABLE_setup
+ \enforced\let\bTABLE \tabl_x_TABLE_start
+ \enforced\let\eTABLE \tabl_x_TABLE_stop}
+
+\popoverloadmode
+
+\permanent\protected\def\tabl_x_TABLE_TH_start_cell
+ {\startxcellgroup[\c!foregroundstyle=\v!bold]%
+ \startxcell}
+
+\permanent\protected\def\tabl_x_TABLE_TH_stop_cell
+ {\stopxcell
+ \stopxcellgroup}
+
+\permanent\protected\tolerant\def\tabl_x_TABLE_setup[#settings]%
+ {\ifarguments\or
+ %\ifparameter#1\or
+ \setupxtable[#settings]%
+ \fi}
+
+\permanent\protected\tolerant\def\tabl_x_TABLE_start[#settings]%
+ {\bgroup
+ \ifarguments\or
+ %\ifparameter#1\or
+ \tabl_x_prepare{#settings}%
+ \fi
+ \edef\tabl_x_current_buffer{\tabl_x_default_buffer}%
+ \buff_pickup\tabl_x_current_buffer{bTABLE}{eTABLE}\relax\tabl_x_process\zerocount}
+
+\permanent\protected\def\tabl_x_TABLE_stop
+ {\stopxtable}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.mklx b/Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.mklx
new file mode 100644
index 00000000000..b15d99c1221
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/tabl-xtb.mklx
@@ -0,0 +1,976 @@
+% macros=mkvi
+
+%D \module
+%D [ file=tabl-xtb,
+%D version=2011.10.26,
+%D title=\CONTEXT\ Table Macros,
+%D subtitle=Xtreme,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Table Macros / Xtreme}
+
+\registerctxluafile{tabl-xtb}{}
+
+% todo:
+%
+% - yes or no: foregroundstyle/color <- style/color
+% - template alignment
+% - maybe split horizontal (a la linetables)
+% - before/after and wrapping (linecorrection)
+% - maybe also some before/after commands
+% - maybe correction when non float usage
+% - tagging needs to be checked
+% - maybe only tag the box
+% - scale to fit
+%
+% - buffers permit verbatim but are not always handy
+
+%D This module started as an afternoon experiment and surprisingly could be
+%D mostly finished the same evening. Of course it builds upon existing
+%D functionality. The main reason for writing it is that we occasionally
+%D run into pretty large tables that take tens of pages and need to be split
+%D into floats. Speed is one issue there, avoiding to use vsplit is another.
+%D
+%D \starttyping
+%D \definextable [tag] | [tag][parent]
+%D \setupxtable [settings] | [tag][settings]
+%D
+%D \startxtable[tag|settings]
+%D \startxtablehead|\startxtablenext|\startxtablebody|\startxtablefoot
+%D \startxrowgroup[tag|settings]
+%D \startxrow[settings]
+%D \startxcellgroup[tag|settings]
+%D \startxcell[settings] ... \stopxcell
+%D \stopxcellgroup
+%D \stopxrow
+%D \startxrowgroup
+%D \stopxtablehead|\stopxtablenext|\stopxtablebody|\stopxtablefoot
+%D \stopxtable
+%D \stoptyping
+%D
+%D See xtables-001.tex etc for some examples.
+
+% We can avoid some checking by using the fastoptionalcheckcs helpers
+% instead of dosingleempty but the speed gain is neglectable.
+
+\unprotect
+
+% option=stretch : equal distribution
+% option={stretch,width} : proportional distribution
+% option={max} : prefer max over forced width/height
+%
+% cells: option=fixed : nils autostretch (not yet complete)
+
+% \setbox\scratchbox\hbox attr \taggedattribute \c_attr_tagged {...}
+
+\aliased\let\dotagxtablecell \relax % names will change
+\aliased\let\dotagxtablesignal\relax % names will change
+
+\appendtoks
+ \enforced\permanent\protected\def\dotagxtablecell
+ {\clf_settagtablecell
+ \numexpr\tablecellrows\relax
+ \numexpr\tablecellcolumns\relax
+ \numexpr\raggedstatus\relax}%
+ \enforced\permanent\protected\def\dotagxtablesignal
+ {\signalcharacter}% not used
+\to \everyenableelements
+
+\newdimen\d_tabl_x_width
+\newdimen\d_tabl_x_height
+\newdimen\d_tabl_x_depth % not used
+\newdimen\d_tabl_x_distance
+\newcount\c_tabl_x_nx
+\newcount\c_tabl_x_ny
+\newcount\c_tabl_x_mode
+\newbox \b_tabl_x
+\newcount\c_tabl_x_state % 0=empty 1=content 3=splitleft
+\newdimen\d_tabl_x_final_width
+\newcount\c_tabl_x_nesting
+\newcount\c_tabl_x_skip_mode % 1 = skip
+\newdimen\d_tabl_x_textwidth
+\newcount\c_tabl_x_swapped
+\newcount\c_tabl_x_swapped_max
+
+\let\m_tabl_x_swapped_settings\empty
+
+\aliased\let\currentxtablerow \clf_x_table_r
+\aliased\let\currentxtablecolumn\clf_x_table_c
+
+% \setupxtable[one][parent][a=b,c=d]
+% \setupxtable[one] [a=b,c=d]
+% \setupxtable [a=b,c=d]
+
+\installcorenamespace{xtable}
+\installcorenamespace{xtablecheck}
+\installcorenamespace{xtableswap}
+
+\installframedautocommandhandler \??xtable {xtable} \??xtable
+
+\appendtoks
+ \checkxtableparent % so we can deal with undefined settings, not that it's efficient
+\to \everysetupxtable
+
+\setupxtable[%
+ \c!nr=\plusone,
+ \c!nc=\plusone,
+ \c!nx=\plusone, % slow
+ \c!ny=\plusone, % slow
+ \c!align=\v!table, % {\v!flushleft,\v!broad,\v!high}, % just as \bTABLE .. \eTABLE
+ \c!frameoffset=.5\linewidth,
+ \c!backgroundoffset=\v!frame,
+ % \c!framecolor=\s!black,
+ % \c!foregroundstyle=\xtableparameter\c!style, % not clean, better capture elsewhere
+ % \c!foregroundcolor=\xtableparameter\c!color, % not clean, better capture elsewhere
+ % \c!bodyfont=,
+ \c!width=\v!fit,
+ \c!height=\v!fit,
+ \c!maxwidth=8\emwidth,
+ \c!autowidth=\v!yes, % controls framed
+ \c!rulethickness=\linewidth,
+ \c!strut=\v!yes,
+ \c!autostrut=\v!no,
+ \c!split=\v!auto, % a number will take that many lines
+ \c!splitoffset=\zeropoint, % extra space taken
+ \c!aligncharacter=\v!no,
+ \c!alignmentcharacter={,},
+ \c!alignmentleftsample=,
+ \c!alignmentrightsample=,
+ \c!alignmentleftwidth=\zeropoint,
+ \c!alignmentrightwidth=\zeropoint,
+ % \c!option=, % \v!stretch {\v!stretch,\v!width}
+ % \c!footer=,
+ % \c!header=,
+ \c!spaceinbetween=,
+ \c!textwidth=\v!local, % was \hsize,
+ \c!textheight=\vsize, % used for vertical spread
+ \c!distance=\zeropoint, % individual column
+ \c!columndistance=\zeropoint, % each column (whole table)
+ \c!leftmargindistance=\zeropoint, % whole table
+ \c!rightmargindistance=\zeropoint,% whole table
+]
+
+\def\tabl_x_default_buffer{x_table_\number\c_tabl_x_nesting}
+\let\tabl_x_current_buffer\empty
+
+\permanent\protected\def\startxtable{\tabl_x_start_table}
+
+\protected\tolerant\def\tabl_x_start_table[#settings]% maybe two arguments: [tag][settings] | [tag] | [settings]
+ {\bgroup
+ \tabl_x_prepare{#settings}%
+ \edef\tabl_x_current_buffer{\tabl_x_default_buffer}%
+ \buff_pickup{\tabl_x_current_buffer}{startxtable}{stopxtable}\relax\tabl_x_process\zerocount}
+
+% \permanent\protected\aliased\startxtable\tabl_x_start_table
+
+\aliased\permanent\let\stopxtable\relax
+
+% These direct buffers can be somewhat faster but it's probably neglectable.
+% Anyway, no nesting is supported as we then need to catch (e.g.) rows and keep
+% track of nesting and have a more complex redefinition of nested instanced
+% \unknown\ it's not worth the trouble. Only use them when you really need them and
+% use the embeddedxtable command when nesting them. Implementing nesting would be
+% slower than not using direct buffers.
+
+\permanent\tolerant\protected\def\processxtablebuffer[#name]%
+ {\bgroup
+ \let\tabl_x_start_table\tabl_x_process_buffer
+ \edef\tabl_x_current_buffer{#name}%
+ \tabl_x_get_buffer % settings
+ \tabl_x_process}
+
+\protected\tolerant\def\tabl_x_start_ignore[#settings]%
+ {}
+
+\protected\tolerant\def\tabl_x_process_buffer[#settings]%
+ {\tabl_x_prepare{#settings}%
+ \let\tabl_x_start_table\tabl_x_start_ignore
+ \gobbleuntil\stopxtable} % nested xtables are not supported,
+
+%D A bonus: you have to use the following construct inside a macro or
+%D direct buffer.
+
+\permanent\tolerant\protected\def\startembeddedxtable[#settings]#:#content\stopembeddedxtable
+ {\tabl_x_prepare{#settings}%
+ \clf_assignbuffer{embedded_x_table}{\detokenize{#content}}\catcodetable\relax
+ \bgroup
+ \let\tabl_x_start_table\tabl_x_process_buffer
+ \edef\tabl_x_current_buffer{embedded_x_table}%
+ \tabl_x_process}
+
+\aliased\permanent\let\stopembeddedxtable\relax
+
+%D We can also define xtables.
+
+\appendtoks
+ \permanent\setuevalue{\e!start\currentxtable}{\tabl_x_start_named[\currentxtable]}%
+ \permanent\setuevalue{\e!stop \currentxtable}{\tabl_x_stop_named}%
+\to \everydefinextable
+
+\protected\def\tabl_x_start_named[#tag]#spacer[#settings]%
+ {\bgroup
+ \edef\currentxtable{#tag}%
+ \advance\c_tabl_x_nesting\plusone
+ \dostarttaggedchained\t!table\empty\??xtable
+ \setupcurrentxtable[#settings]%
+ \tabl_x_check_textwidth
+ %\forgetall % else whitespace mess
+ \edef\tabl_x_current_buffer{\tabl_x_default_buffer}%
+ \normalexpanded{\buff_pickup{\tabl_x_current_buffer}{\e!start\currentxtable}{\e!stop\currentxtable}\relax\tabl_x_process\zerocount}}
+
+\protected\def\tabl_x_stop_named
+ {}
+
+%D Now we come to processing:
+
+\protected\def\tabl_x_check_textwidth
+ {\edef\p_textwidth{\xtableparameter\c!textwidth}%
+ \ifx\p_textwidth\v!local
+ \d_tabl_x_textwidth\availablehsize
+ \else
+ \d_tabl_x_textwidth\p_textwidth
+ \fi}
+
+\newtoks\everypreparextable
+
+\protected\def\tabl_x_prepare#settings%
+ {\advance\c_tabl_x_nesting\plusone
+ \dostarttaggedchained\t!table\empty\??xtable
+ \tabl_x_set_checked{#settings}%
+ \tabl_x_check_textwidth
+ \the\everypreparextable
+ }% else whitespace mess
+
+\def\tabl_x_get_buffer
+ {\clf_gettexbuffer{\tabl_x_current_buffer}}
+
+\let\tabl_x_start_row_yes \relax
+\let\tabl_x_start_row_nop \relax
+\let\tabl_x_stop_row \relax
+\let\tabl_x_start_cell_yes\relax
+\let\tabl_x_start_cell_nop\relax
+\let\tabl_x_stop_cell \relax
+
+\newtoks\t_table_x_cleanup
+
+\protected\def\tabl_x_process
+ {\begingroup % *
+ \forgetall % moved here
+ \dontcomplain % for the moment here till we figure out where we get the overflow
+ \usebodyfontparameter\xtableparameter
+ \setbox\scratchbox\vbox
+ {\doifsomething{\xtableparameter\c!spaceinbetween}{\blank[\xtableparameter\c!spaceinbetween]}}%
+ \clf_x_table_create
+ option {\xtableparameter\c!option}%
+ textwidth \d_tabl_x_textwidth
+ textheight \dimexpr\xtableparameter\c!textheight\relax
+ maxwidth \dimexpr\xtableparameter\c!maxwidth\relax
+ lineheight \openlineheight
+ columndistance \dimexpr\xtableparameter\c!columndistance\relax
+ leftmargindistance \dimexpr\xtableparameter\c!leftmargindistance\relax
+ rightmargindistance \dimexpr\xtableparameter\c!rightmargindistance\relax
+ rowdistance \ht\scratchbox
+ header {\xtableparameter\c!header}%
+ footer {\xtableparameter\c!footer}%
+ \relax
+ %
+ \letxtableparameter\c!option\empty
+ % not so nice but needed as we use this in the setup
+ \linewidth\xtableparameter\c!rulethickness\relax
+ % so we freeze it
+ \c_tabl_x_swapped_max\zerocount
+ \begingroup
+ \let\tabl_x_start_row_yes \tabl_x_start_row_reflow_width_yes
+ \let\tabl_x_start_row_nop \tabl_x_start_row_reflow_width_nop
+ \let\tabl_x_stop_row \tabl_x_stop_row_reflow_width
+ \let\tabl_x_start_cell_yes\tabl_x_start_cell_reflow_width_yes
+ \let\tabl_x_start_cell_nop\tabl_x_start_cell_reflow_width_nop
+ \let\tabl_x_stop_cell \tabl_x_stop_cell_reflow_width
+ \settrialtypesetting
+ \tabl_x_get_buffer
+ \ifcase\c_tabl_x_swapped_max
+ \else
+ \tabl_x_flush_swapped
+ \fi
+ \clf_x_table_reflow_width
+ \endgroup
+ \begingroup
+ \let\tabl_x_start_row_yes \tabl_x_start_row_reflow_height_yes
+ \let\tabl_x_start_row_nop \tabl_x_start_row_reflow_height_nop
+ \let\tabl_x_stop_row \tabl_x_stop_row_reflow_height
+ \let\tabl_x_start_cell_yes\tabl_x_start_cell_reflow_height_yes
+ \let\tabl_x_start_cell_nop\tabl_x_start_cell_reflow_height_nop
+ \let\tabl_x_stop_cell \tabl_x_stop_cell_reflow_height
+ \settrialtypesetting
+ \ifcase\c_tabl_x_swapped_max
+ \tabl_x_get_buffer
+ \else
+ \tabl_x_flush_swapped
+ \fi
+ \clf_x_table_reflow_height
+ \endgroup
+ \begingroup
+ \let\tabl_x_start_row_yes \tabl_x_start_row_construct_yes
+ \let\tabl_x_start_row_nop \tabl_x_start_row_construct_nop
+ \let\tabl_x_stop_row \tabl_x_stop_row_construct
+ \let\tabl_x_start_cell_yes\tabl_x_start_cell_construct_yes
+ \let\tabl_x_start_cell_nop\tabl_x_start_cell_construct_nop
+ \let\tabl_x_stop_cell \tabl_x_stop_cell_construct
+ \ifcase\c_tabl_x_swapped_max
+ \tabl_x_get_buffer
+ \else
+ \tabl_x_flush_swapped
+ \fi
+ \clf_x_table_construct
+ \endgroup
+ \endgroup % *
+ \ifinsidesplitfloat
+ \tabl_x_flush_float_split
+ \orelse\ifinsidefloat
+ \tabl_x_flush_float_normal
+ \else
+ \tabl_x_flush_text_checked
+ \fi
+ \clf_x_table_cleanup
+ \dostoptagged
+ \resetbuffer[\tabl_x_current_buffer]%
+ \resetcharacteralign
+ \the\t_table_x_cleanup
+ \egroup}
+
+% text flow split modes
+
+\installcorenamespace{xtableflushsplit}
+
+\protected\def\tabl_x_flush_text_checked
+ {\expandnamespaceparameter\??xtableflushsplit\xtableparameter\c!split\v!no}
+
+% in text flow: headers and footers only once
+
+\setvalue{\??xtableflushsplit\v!yes}%
+ {\clf_x_table_flush
+ method {\v!split}%
+ \relax}
+
+% in text flow: headers and footers only once
+
+\setvalue{\??xtableflushsplit\v!no}%
+ {% \noindent % gives extra line after table
+ % \noindentation % messes up the next indentation
+ % \dontleavehmode % no leftskip
+ \kern\zeropoint % yet another guess
+ \ignorespaces
+ \clf_x_table_flush
+ method {\v!normal}%
+ \relax
+ \removeunwantedspaces}
+
+% in text flow: headers and footers get repeated
+
+\setvalue{\??xtableflushsplit\v!repeat}%
+ {\doloop
+ {\clf_x_table_flush
+ method {\v!split}%
+ height \ifdim\pagegoal=\maxdimen\textheight\else\pagegoal\fi
+ \relax
+ \ifcase\c_tabl_x_state
+ \exitloop
+ \else
+ \page
+ \fi}}
+
+% \setvalue{\??xtableflushsplit\v!setups}%
+% {\directsetup{xtable:split:user}}
+%
+% \startsetups[xtable:split:user]
+% \doloop {
+% \xtablesplitflush % uses \xtablesplitvsize (a macro)
+% \ifcase\xtablesplitstate
+% \exitloop
+% \else
+% \page
+% \fi
+% }
+% \stopsetups
+%
+% \protected\def\xtablesplitflush
+% {\clf_x_table_flush
+% method {\v!split}%
+% height \dimexpr\xtablesplitvsize\relax
+% \relax}
+%
+% \def\xtablesplitvsize
+% {\ifdim\pagegoal=\maxdimen\textheight\else\pagegoal\fi}
+%
+% \let\xtablesplitstate\c_tabl_x_state
+
+\mutable\let\extraxtablesplitheight\zeropoint % might disappear so don't depend on it
+
+\protected\def\tabl_x_flush_float_normal
+ {\clf_x_table_flush
+ method {\v!normal}%
+ \relax}
+
+\protected\def\tabl_x_flush_float_split
+ {\resetdirecttsplit
+ \edef\extrasplitfloatlines{\xtableparameter\c!split}%
+ \d_split_minimum_free_space\dimexpr\extraxtablesplitheight+\xtableparameter\c!splitoffset\relax
+ %\c_split_minimum_free_lines\plustwo % not needed here as we're precise enough
+ \let\tsplitdirectsplitter\tabl_x_split_splitter
+ \let\tsplitdirectwidth \d_tabl_x_final_width
+ \handledirecttsplit}
+
+\protected\def\tabl_x_split_splitter#height%
+ {\setbox\b_split_result\vbox
+ {\clf_x_table_flush
+ method {\v!split}%
+ height \dimexpr#height\relax
+ \relax}%
+ \ifcase\c_tabl_x_state
+ \global\setfalse\somenextsplitofffloat
+ \else
+ \global\settrue \somenextsplitofffloat
+ \fi}
+
+% \permanent\protected\def\startxrow
+% {\begingroup
+% \doifelsenextoptionalcs\tabl_x_start_row_yes\tabl_x_start_row_nop}
+
+% \permanent\protected\def\stopxrow
+% {\tabl_x_stop_row
+% \endgroup}
+
+\protected\def\tabl_x_start_row_reflow_width_yes[#settings]%
+ {\setupcurrentxtable[#settings]%
+ \clf_x_table_next_row}
+
+\protected\def\tabl_x_start_row_reflow_width_nop
+ {\clf_x_table_next_row}
+
+\protected\def\tabl_x_stop_row_reflow_width
+ {}
+
+\let\tabl_x_start_row_reflow_height_yes\tabl_x_start_row_reflow_width_yes
+\let\tabl_x_start_row_reflow_height_nop\tabl_x_start_row_reflow_width_nop
+\let\tabl_x_stop_row_reflow_height \tabl_x_stop_row_reflow_width
+
+\protected\def\tabl_x_start_row_construct_yes[#settings]%
+ {\setupcurrentxtable[#settings]%
+ \dostarttagged\t!tablerow\empty
+ \clf_x_table_next_row_option{\xtableparameter\c!samepage}}
+
+\protected\def\tabl_x_start_row_construct_nop
+ {\dostarttagged\t!tablerow\empty
+ \clf_x_table_next_row}
+
+\protected\def\tabl_x_stop_row_construct
+ {\clf_x_table_finish_row
+ \dostoptagged}
+
+\permanent\protected\def\dummyxcell
+ {\begingroup
+ \enforced\let\inheritedxtableframed\relax
+ \tabl_x_start_cell_nop
+ \tabl_x_stop_cell
+ \endgroup}
+
+\def\tabl_x_setup_character_align
+ {\edef\p_left {\directxtableparameter\c!alignmentleftsample}%
+ \edef\p_right{\directxtableparameter\c!alignmentrightsample}%
+ \ifempty\p_left
+ \scratchdimenone\dimexpr\directxtableparameter\c!alignmentleftwidth\relax
+ \else
+ \setbox\scratchbox\hbox{\p_left}%
+ \scratchdimenone\wd\scratchbox
+ \fi
+ \ifempty\p_right
+ \scratchdimentwo\dimexpr\directxtableparameter\c!alignmentrightwidth\relax
+ \else
+ \setbox\scratchbox\hbox{\p_right}%
+ \scratchdimentwo\wd\scratchbox
+ \fi
+ \clf_setcharacteraligndetail
+ \clf_x_table_c
+ {\directxtableparameter\c!alignmentcharacter}%
+ \scratchdimenone
+ \scratchdimentwo
+ \relax}
+
+\newtoks\t_tabl_x_every_cell
+
+% \appendtoks
+% \inhibitblank % already in framed
+% \to \t_tabl_x_every_cell
+
+\appendtoks
+ \edef\p_characteralign{\directxtableparameter\c!aligncharacter}%
+ \ifx\p_characteralign\v!yes
+ \ifcase\clf_x_table_r\or
+ \tabl_x_setup_character_align
+ \fi
+ \signalcharacteralign\clf_x_table_c\clf_x_table_r
+ \fi
+\to \t_tabl_x_every_cell
+
+\protected\def\tabl_x_begin_of_cell
+ {\the\t_tabl_x_every_cell
+ \everypar{\delayedbegstrut}}
+
+\protected\def\tabl_x_end_of_cell
+ {\ifhmode
+ \delayedendstrut
+ \par
+ \else
+ \par
+ \ifdim\prevdepth<\zeropoint % =-1000pt ?
+ \vskip-\strutdp
+ \else
+ \removebottomthings
+ \fi
+ \fi}
+
+% For historic reasons we support both nx/nc and ny/nr : maybe nx/ny becomes
+% obsolete some day. The let as well as the direct speed things up a bit. We
+% could also consider a \defaultxtableparameter.
+%
+% \c_tabl_x_nx\defaultxtableparameter\c!nc{\defaultxtableparameter\c!nx\plusone}
+% \c_tabl_x_ny\defaultxtableparameter\c!nr{\defaultxtableparameter\c!ny\plusone}
+%
+% Although this becomes kind of messy. It saves already time that we only check
+% for it when we have settings.
+
+% \def\tabl_x_set_hsize
+% {\hsize.25\maxdimen} % let's be reasonable
+
+% \def\tabl_x_set_hsize
+% {\edef\p_width{\xtableparameter\c!width}%
+% \ifempty\p_width
+% \hsize.25\maxdimen % is this really needed
+% \fi}
+
+\let\tabl_x_set_hsize\relax
+
+\protected\def\tabl_x_start_cell_reflow_width_yes[#settings]%
+ {\setbox\b_tabl_x\hpack\bgroup
+ \ifnum\c_tabl_x_nesting>\plusone
+ \letxtableparameter\c!width \v!fit % overloads given width
+ \letxtableparameter\c!height\v!fit % overloads given height
+ \fi
+ %
+ \letxtableparameter\c!nx\plusone
+ \letxtableparameter\c!ny\plusone
+ \letxtableparameter\c!nc\plusone
+ \letxtableparameter\c!nr\plusone
+ %
+ \setupcurrentxtable[#settings]%
+ %
+ \c_tabl_x_nx\directxtableparameter\c!nc\relax
+ \c_tabl_x_ny\directxtableparameter\c!nr\relax
+ \ifnum\c_tabl_x_nx=\plusone
+ \c_tabl_x_nx\directxtableparameter\c!nx\relax
+ \fi
+ \ifnum\c_tabl_x_ny=\plusone
+ \c_tabl_x_ny\directxtableparameter\c!ny\relax
+ \fi
+ %
+ \d_tabl_x_distance\xtableparameter\c!distance\relax
+ \clf_x_table_init_reflow_width_option{\xtableparameter\c!option}%
+ \inheritedxtableframed\bgroup
+ \tabl_x_begin_of_cell
+ \tabl_x_set_hsize}
+
+\protected\def\tabl_x_start_cell_reflow_width_nop
+ {\setbox\b_tabl_x\hpack\bgroup
+ \ifnum\c_tabl_x_nesting>\plusone
+ \letxtableparameter\c!width \v!fit % overloads given width
+ \letxtableparameter\c!height\v!fit % overloads given height
+ \fi
+ \c_tabl_x_nx\plusone
+ \c_tabl_x_ny\plusone
+ \d_tabl_x_distance\xtableparameter\c!distance\relax
+ \clf_x_table_init_reflow_width
+ \inheritedxtableframed\bgroup
+ \tabl_x_begin_of_cell
+ \tabl_x_set_hsize}
+
+\protected\def\tabl_x_stop_cell_reflow_width
+ {\tabl_x_end_of_cell
+ \egroup
+ \egroup
+ \clf_x_table_set_reflow_width}
+
+\protected\def\tabl_x_start_cell_reflow_height_yes[#settings]%
+ {\setbox\b_tabl_x\hpack\bgroup
+ \clf_x_table_init_reflow_height
+ \ifcase\c_tabl_x_skip_mode % can be sped up
+ \ifnum\c_tabl_x_nesting>\plusone
+ \letxtableparameter\c!height\v!fit % overloads given height
+ \fi
+ \setupcurrentxtable[#settings]%
+ \relax
+ \letxtableparameter\c!width\d_tabl_x_width % overloads given width
+ \inheritedxtableframed\bgroup
+ \tabl_x_begin_of_cell
+ \fi}
+
+\protected\def\tabl_x_start_cell_reflow_height_nop
+ {\setbox\b_tabl_x\hpack\bgroup
+ \clf_x_table_init_reflow_height
+ \ifcase\c_tabl_x_skip_mode % can be sped up
+ \ifnum\c_tabl_x_nesting>\plusone
+ \letxtableparameter\c!height\v!fit % overloads given height
+ \fi
+ \relax
+ \letxtableparameter\c!width\d_tabl_x_width % overloads given width
+ \inheritedxtableframed\bgroup
+ \tabl_x_begin_of_cell
+ \fi}
+
+\protected\def\tabl_x_stop_cell_reflow_height
+ {\ifcase\c_tabl_x_skip_mode
+ \tabl_x_end_of_cell
+ \egroup
+ \fi
+ \egroup
+ \clf_x_table_set_reflow_height}
+
+\protected\def\tabl_x_start_cell_construct_yes[#settings]%
+ {\dostarttagged\t!tablecell\empty % can't we just tag the box
+ \setbox\b_tabl_x\hpack\bgroup
+ \setupcurrentxtable[#settings]%
+ \letxtableparameter\c!width \d_tabl_x_width % overloads given width
+ \letxtableparameter\c!height\d_tabl_x_height % overloads given height
+ \clf_x_table_init_construct
+ \inheritedxtableframed\bgroup
+ \tabl_x_begin_of_cell
+ \dotagxtablecell}
+
+\protected\def\tabl_x_start_cell_construct_nop
+ {\dostarttagged\t!tablecell\empty % can't we just tag the box
+ \setbox\b_tabl_x\hpack\bgroup
+ \letxtableparameter\c!width \d_tabl_x_width % overloads given width
+ \letxtableparameter\c!height\d_tabl_x_height % overloads given height (commenting it ... nice option)
+ \clf_x_table_init_construct
+ \inheritedxtableframed\bgroup
+ \tabl_x_begin_of_cell
+ \dotagxtablecell}
+
+\protected\def\tabl_x_stop_cell_construct
+ {\tabl_x_end_of_cell
+ \egroup
+ \dotagxtablesignal % harmless spot
+ \egroup
+ \clf_x_table_set_construct
+ \dostoptagged}
+
+% \permanent\tolerant\protected\def\startxcellgroup[#settings]%
+% {\begingroup
+% \ifarguments\or
+% \tabl_x_set_checked{#settings}%
+% \fi}
+%
+% \permanent\protected\def\stopxcellgroup
+% {\endgroup}
+%
+% \permanent\tolerant\protected\def\startxrowgroup[#settings]%
+% {\begingroup
+% \ifarguments\or
+% \tabl_x_set_checked{#settings}%
+% \fi}
+%
+% \permanent\protected\def\stopxrowgroup
+% {\dostoptagged
+% \endgroup}
+
+\protected\def\tabl_x_set_checked#settings%
+ {\ifcsname\namedxtablehash{#settings}\s!parent\endcsname
+ \edef\currentxtable{#settings}%
+ \else
+ \setupcurrentxtable[#settings]%
+ \fi}
+
+\permanent\protected\def\startxtablehead{\begingroup\c_tabl_x_mode\plusone \tabl_x_start_partition}
+\permanent\protected\def\startxtablefoot{\begingroup\c_tabl_x_mode\plustwo \tabl_x_start_partition}
+\permanent\protected\def\startxtablenext{\begingroup\c_tabl_x_mode\plusthree\tabl_x_start_partition}
+\permanent\protected\def\startxtablebody{\begingroup\c_tabl_x_mode\plusfour \tabl_x_start_partition}
+
+\permanent\tolerant\protected\def\tabl_x_start_partition[#settings]#spacer%
+ {\ifarguments\or
+ \tabl_x_set_checked{#settings}%
+ \fi}
+
+\permanent\protected\def\tabl_x_stop_partition
+ {\endgroup}
+
+\aliased\let\stopxtablehead\tabl_x_stop_partition
+\aliased\let\stopxtablefoot\tabl_x_stop_partition
+\aliased\let\stopxtablenext\tabl_x_stop_partition
+\aliased\let\stopxtablebody\tabl_x_stop_partition
+
+%D This is an experiment! Beware: you can create loops by using nested
+%D references to already chained settings.
+%D
+%D \startbuffer
+%D \setupxtable[suffix][align=middle,foregroundcolor=red]
+%D \setupxtable[blabla][foregroundstyle=slanted]
+%D \setupxtable[crap] [foregroundcolor=blue]
+%D \setupxtable[bold] [crap][foregroundstyle=bold]
+%D
+%D \startxtable[frame=off]
+%D \startxtablehead
+%D \startxrow[bold]
+%D \startxcell[suffix] a 0 \stopxcell
+%D \startxcell[blabla] a 1 \stopxcell
+%D \startxcell a 2 \stopxcell
+%D \stopxrow
+%D \stopxtablehead
+%D \startxtablebody
+%D \startxrow \startxcell[suffix][ny=2] a 1 \stopxcell \startxcell b 1 \stopxcell \startxcell c 1 \stopxcell \stopxrow
+%D \startxrow \startxcell b 2 \stopxcell \startxcell c 2 \stopxcell \stopxrow
+%D \startxrow \startxcell[suffix] a 3 \stopxcell \startxcell b 3 \stopxcell \startxcell c 3 \stopxcell \stopxrow
+%D \startxrow \startxcell[suffix] a 4 \stopxcell \startxcell b 4 \stopxcell \startxcell c 4 \stopxcell \stopxrow
+%D \startxrow \startxcell[suffix] a 5 \stopxcell \startxcell b 5 \stopxcell \startxcell c 5 \stopxcell \stopxrow
+%D \stopxtablebody
+%D \stopxtable
+%D \stopbuffer
+%D
+%D \typebuffer \placetable{}{\getbuffer}
+
+\appendtoks
+ \letcsname\??xtablecheck\currentxtable\endcsname\relax % faster than checking parent
+\to \everysetupxtable
+
+% \definefontfamily[newtimes][serif][TeX Gyre Termes]
+% \setupxtable[newtimes][foregroundstyle=\newtimes]
+% \startxcell[newtimes] ...\stopxcell
+% \startxcell[foregroundstyle=\newtimes] ...\stopxcell
+
+% % \ifcsname\namedxtablehash{#tag}\s!parent\endcsname
+% % \ifcsname\??xtablecheck#tag\endcsname
+% \ifcsname\??xtablecheck\detokenize\expandafter{\normalexpanded{#tag}}\endcsname % two times slower on keywords
+% \expandafter\whatever % but more tolerant for tricky key=value
+% \else
+% \expandafter\whatever
+% \fi[#tag]
+
+% groups
+
+\permanent\protected\def\startxgroup
+ {\begingroup
+ \doifelsenextoptionalcs\tabl_x_start_group_delayed_one\relax}
+
+\permanent\protected\def\stopxgroup
+ {\endgroup}
+
+\protected\def\tabl_x_start_group_delayed_one[#tag]%
+ {\ifcsname\??xtablecheck\detokenize\expandafter{\normalexpanded{#tag}}\endcsname
+ \expandafter\tabl_x_start_group_delayed_two
+ \else
+ \expandafter\setupcurrentxtable
+ \fi[#tag]}
+
+\protected\def\tabl_x_start_group_delayed_two[#tag]%
+ {\ifempty\currentxtable \else
+ \chaintocurrentxtable{#tag}%
+ \fi
+ \edef\currentxtable{#tag}%
+ \doifelsenextoptionalcs\setupcurrentxtable\relax}
+
+\aliased\let\startxrowgroup \startxgroup
+\aliased\let\stopxrowgroup \stopxgroup
+\aliased\let\startxcellgroup\startxgroup
+\aliased\let\stopxcellgroup \stopxgroup
+
+% cells (maybe also check for 1 etc but it becomes messy)
+
+\permanent\protected\def\startxcell
+ {\begingroup
+ \doifelsenextoptionalcs\tabl_x_start_cell_delayed_one\tabl_x_start_cell_nop}
+
+\protected\def\tabl_x_start_cell_delayed_one[#tag]%
+ {\ifcsname\??xtablecheck\detokenize\expandafter{\normalexpanded{#tag}}\endcsname
+ \expandafter\tabl_x_start_cell_delayed_two
+ \else
+ \expandafter\tabl_x_start_cell_yes
+ \fi[#tag]}
+
+\tolerant\protected\def\tabl_x_start_cell_delayed_two[#tag]#spacer%
+ {\ifempty\currentxtable \else
+ \chaintocurrentxtable{#tag}%
+ \fi
+ \edef\currentxtable{#tag}%
+ \doifelsenextoptionalcs\tabl_x_start_cell_yes\tabl_x_start_cell_nop}
+
+\permanent\protected\def\stopxcell
+ {\tabl_x_stop_cell
+ \endgroup}
+
+% rows
+
+\permanent\protected\def\startxrow
+ {\begingroup
+ \doifelsenextoptionalcs\tabl_x_start_row_delayed_one\tabl_x_start_row_nop}
+
+\protected\def\tabl_x_start_row_delayed_one[#tag]%
+ {\ifcsname\??xtablecheck\detokenize\expandafter{\normalexpanded{#tag}}\endcsname
+ \expandafter\tabl_x_start_row_delayed_two
+ \else
+ \expandafter\tabl_x_start_row_yes
+ \fi[#tag]}
+
+\permanent\protected\def\tabl_x_start_row_delayed_two[#tag]%
+ {\ifempty\currentxtable \else
+ \chaintocurrentxtable{#tag}%
+ \fi
+ \edef\currentxtable{#tag}%
+ \doifelsenextoptionalcs\tabl_x_start_row_yes\tabl_x_start_row_nop}
+
+\permanent\protected\def\stopxrow
+ {\tabl_x_stop_row
+ \endgroup}
+
+%D A bonus, not advertised but some like it this way:
+
+\protected\def\tabl_x_nc
+ {\startxrow
+ \enforced\let\NC\tabl_x_nc_next
+ \enforced\let\NR\tabl_x_nr
+ \startxcell}
+
+\protected\def\tabl_x_nc_next
+ {\stopxcell
+ \startxcell}
+
+\protected\def\tabl_x_nr
+ {\stopxcell
+ \stopxrow
+ \enforced\let\NC\tabl_x_nc}
+
+\appendtoks
+ \enforced\let\NC\tabl_x_nc
+ \enforced\let\NR\tabl_x_nr
+\to \everypreparextable
+
+%D Another bonus, suggested by Taco at the 2018 \CONTEXT\ meeting.
+
+\protected\def\tabl_x_c_cell_start#settings%
+ {\begingroup
+ \tabl_x_set_checked{#settings}%
+ \doifelsenextoptionalcs\tabl_x_start_cell_yes\tabl_x_start_cell_nop}
+
+\protected\def\tabl_x_c_cell_stop
+ {\tabl_x_stop_cell
+ \endgroup}
+
+% \protected\def\dummyxcell
+% {\tabl_x_start_cell_nop
+% \tabl_x_stop_cell}
+
+\mutable\let\t_tabl_x_swapped\relax
+
+\def\tabl_x_flush_swapped
+ {\dorecurse\c_tabl_x_swapped_max
+ {\expandafter
+ \startxrow
+ \the\csname\??xtableswap##1\endcsname\relax
+ \stopxrow}}
+
+\def\tabl_x_collect_allocate
+ {\expandafter\newtoks\csname\??xtableswap\number\c_tabl_x_swapped\endcsname
+ \expandafter\let\expandafter\t_tabl_x_swapped\csname\??xtableswap\number\c_tabl_x_swapped\endcsname}
+
+\def\tabl_x_collect_advance
+ {\global\advance\c_tabl_x_swapped\plusone
+ \ifnum\c_tabl_x_swapped>\c_tabl_x_swapped_max
+ \global\c_tabl_x_swapped_max\c_tabl_x_swapped
+ \fi
+ \mutable\expandafter\let\expandafter\t_tabl_x_swapped\csname\??xtableswap\number\c_tabl_x_swapped\endcsname
+ \ifrelax\t_tabl_x_swapped
+ \tabl_x_collect_allocate
+ \fi}
+
+\protected\def\tabl_x_collect_cell_start
+ {\doifelsenextoptionalcs
+ \tabl_x_collect_cell_start_yes
+ \tabl_x_collect_cell_start_nop}
+
+\def\tabl_x_collect_cell_start_nop#content\stopxcell
+ {\tabl_x_collect_advance
+ \ifempty\m_tabl_x_swapped_settings
+ \gtoksapp\t_tabl_x_swapped{\tabl_x_c_cell_start{}#content\tabl_x_c_cell_stop}%
+ \else
+ \gtoksapp\t_tabl_x_swapped\expandafter{\expandafter\tabl_x_c_cell_start\expandafter{\m_tabl_x_swapped_settings}#content\tabl_x_c_cell_stop}%
+ \fi}
+
+\def\tabl_x_collect_cell_start_yes[#settings]#content\stopxcell
+ {\tabl_x_collect_advance
+ \ifempty\m_tabl_x_swapped_settings
+ \gtoksapp\t_tabl_x_swapped{\tabl_x_c_cell_start{}[#settings]#content\tabl_x_c_cell_stop}%
+ \else
+ \gtoksapp\t_tabl_x_swapped\expandafter{\expandafter\tabl_x_c_cell_start\expandafter{\m_tabl_x_swapped_settings}[#settings]#content\tabl_x_c_cell_stop}%
+ \fi
+ \getdummyparameters[\c!ny=1,#settings]%
+ \scratchcounter\numexpr\dummyparameter\c!ny-\plusone\relax
+ \ifcase\scratchcounter\else
+ \dorecurse\scratchcounter\tabl_x_collect_advance
+ \fi}
+
+\permanent\tolerant\protected\def\startxcolumn[#settings]#spacer%
+ {\begingroup
+ \global\c_tabl_x_swapped\zerocount
+ \enforced\let\startxcell\tabl_x_collect_cell_start
+ \enforced\let\stopxcell \relax
+ \xdef\m_tabl_x_swapped_settings{#settings}}
+
+\permanent\protected\def\stopxcolumn
+ {\endgroup}
+
+\appendtoks
+ \dorecurse\c_tabl_x_swapped_max
+ {\global\csname\??xtableswap\number#1\endcsname\emptytoks}%
+\to \t_table_x_cleanup
+
+%D \startbuffer
+%D \setupxtable[one][foregroundcolor=red]
+%D \setupxtable[two][foregroundcolor=blue]
+%D
+%D \startlinecorrection
+%D \startxtable
+%D \startxrow[one]
+%D \startxcell[width=5cm] Row 1, Column 1 \stopxcell
+%D \startxcell Row 1, Column 2 \stopxcell
+%D \startxcell Row 1, Column 3 \stopxcell
+%D \stopxrow
+%D \startxrow[two]
+%D \startxcell Row 2, Column 1 \stopxcell
+%D \startxcell Row 2, Column 2 \stopxcell
+%D \startxcell Row 2, Column 3 \stopxcell
+%D \stopxrow
+%D \stopxtable
+%D \stoplinecorrection
+%D
+%D \startlinecorrection
+%D \startxtable
+%D \startxcolumn[one]
+%D \startxcell[width=5cm] Row 1, Column 1 \stopxcell
+%D \startxcell Row 1, Column 2 \stopxcell
+%D \startxcell Row 1, Column 3 \stopxcell
+%D \stopxcolumn
+%D \startxcolumn[two]
+%D \startxcell Row 2, Column 1 \stopxcell
+%D \startxcell Row 2, Column 2 \stopxcell
+%D \startxcell Row 2, Column 3 \stopxcell
+%D \stopxcolumn
+%D \stopxtable
+%D \stoplinecorrection
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/task-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/task-ini.mkxl
new file mode 100644
index 00000000000..77e4fa98540
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/task-ini.mkxl
@@ -0,0 +1,22 @@
+%D \module
+%D [ file=task-ini,
+%D version=2007.06.06,
+%D title=\CONTEXT\ Task Handler,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Task Handler / initialization}
+
+\unprotect
+
+%D Maybe we will make things configureable (speed up and such).
+
+\registerctxluafile{task-ini}{}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/toks-aux.lmt b/Master/texmf-dist/tex/context/base/mkxl/toks-aux.lmt
new file mode 100644
index 00000000000..03f4dc2c106
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/toks-aux.lmt
@@ -0,0 +1,183 @@
+if not modules then modules = { } end modules ['toks-aux'] = {
+ version = 1.001,
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, tostring = type, tostring
+local max = math.max
+local formatters, gsub, char = string.formatters, string.gsub, string.char
+local concat = table.concat
+
+local function flags(t)
+ if type(t) == "string" then
+ t = token.create(t)
+ end
+ local r = { }
+ if t.frozen then r[#r+1] = "frozen" end
+ if t.permanent then r[#r+1] = "permanent" end
+ if t.immutable then r[#r+1] = "immutable" end
+ if t.primitive then r[#r+1] = "primitive" end
+ if t.mutable then r[#r+1] = "mutable" end
+ if t.noaligned then r[#r+1] = "noaligned" end
+ if t.instance then r[#r+1] = "instance" end
+ if t.tolerant then r[#r+1] = "tolerant" end
+ if t.protected then r[#r+1] = "protected" end
+ return r
+end
+
+tokens.flags= flags
+
+interfaces.implement {
+ name = "showluatokens",
+ public = true,
+ protected = true,
+ actions = function()
+ local f0 = formatters["%s: %s"]
+ local nl = logs.newline
+ local wr = logs.writer
+ local t = token.peek_next() -- local t = token.scan_next() token.put_back(t)
+ local n = ""
+ local w = ""
+ local c = t.cmdname
+ if c == "left_brace" then
+ w = "given token list"
+ t = token.scan_toks(false)
+ elseif c == "register_toks" then
+ token.scan_next()
+ w = "token register"
+ n = t.csname or t.index
+ t = tex.gettoks(n,true)
+ elseif c == "internal_toks" then
+ token.scan_next()
+ w = "internal token variable"
+ n = t.csname or t.index
+ t = tex.gettoks(n,true)
+ else
+ n = token.scan_csname()
+ local r = flags(t)
+ local m = token.get_meaning(n,true)
+ r[#r+1] = "control sequence"
+ if type(m) == "table" then
+ t = m
+ else
+ t = { t }
+ end
+ w = concat(r, " ")
+ end
+ wr(f0(w,n))
+ nl()
+ if type(t) == "table" then
+ local w1 = 4
+ local w2 = 1
+ local w3 = 3
+ local w4 = 3
+ for i=1,#t do
+ local ti = t[i]
+ w1 = max(w1,#tostring(ti.id))
+ w2 = max(w2,#tostring(ti.command))
+ w3 = max(w3,#tostring(ti.index))
+ w4 = max(w4,#ti.cmdname)
+ end
+ local f1 = formatters["%" .. w1 .. "i %" .. w2 .. "i %" .. w3 .. "i %-" .. w4 .. "s %s"]
+ local f2 = formatters["%" .. w1 .. "i %" .. w2 .. "i %" .. w3 .. "i %-" .. w4 .. "s"]
+ local f3 = formatters["%" .. w1 .. "i %" .. w2 .. "i %" .. w3 .. "i %-" .. w4 .. "s %C"]
+ for i=1,#t do
+ local ti = t[i]
+ local cs = ti.csname
+ local id = ti.id
+ local ix = ti.index
+ local cd = ti.command
+ local cn = ti.cmdname
+ if cn == "prefix" and not cs then
+ cs = "always enforced"
+ end
+ cn = gsub(cn,"_"," ")
+ if cs then
+ wr(f1(id,cd,ix,cn,cs))
+ elseif cn == "letter" or cn == "other char" then
+ wr(f3(id,cd,ix,cn,ix))
+ elseif cn == "match" then
+ local s
+ if ix == 32 then s = "optional spacer" -- space
+ elseif ix == 42 then s = "skip spaces" -- *
+ elseif ix == 43 then s = "keep braces" -- +
+ elseif ix == 45 then s = "thrash" -- -
+ elseif ix == 47 then s = "prune" -- /
+ elseif ix == 58 then s = "continue" -- :
+ elseif ix == 59 then s = "quit" -- ;
+ elseif ix == 61 then s = "mandate braces" -- =
+ elseif ix == 94 then s = "keep spaces" -- ^
+ elseif ix == 95 then s = "keep mandate braces" -- _
+ else s = "argument " .. char(ix)
+ end
+ wr(f1(id,cd,ix,cn,s))
+ else
+ wr(f2(id,cd,ix,cn))
+ if cn == "end match" then
+ wr("--------------")
+ end
+ end
+ end
+ nl()
+ end
+ end
+}
+
+-- For the moment here, will move to initex only (also see node-ini.lua); we need
+-- to actually store these.
+
+local groupcodes = { }
+local glyphoptioncodes = { }
+local hyphenationcodes = { }
+local frozenparcodes = { }
+local flagcodes = { }
+local normalizecodes = { }
+
+for k, v in next, tex.getgroupvalues() do
+ groupcodes[k] = gsub(v,"[_ ]","")
+end
+for k, v in next, tex.gethyphenationvalues() do
+ hyphenationcodes[k] = gsub(v,"[_ ]","")
+end
+for k, v in next, tex.getglyphoptionvalues() do
+ glyphoptioncodes[k] = gsub(v,"[_ ]","")
+end
+for k, v in next, tex.getfrozenparvalues() do
+ frozenparcodes[k] = gsub(v,"[_ ]","")
+end
+for k, v in next, tex.getflagvalues() do
+ flagcodes[k] = gsub(v,"[_ ]","")
+end
+for k, v in next, tex.getnormalizevalues() do
+ normalizecodes[k] = gsub(v,"[_ ]","")
+end
+
+
+if environment.initex then
+
+ local texintegerdef = tex.integerdef
+
+ for k, v in next, groupcodes do texintegerdef(v .. "groupcode", k,"immutable") end
+ for k, v in next, glyphoptioncodes do texintegerdef(v .. "code", k,"immutable") end
+ for k, v in next, hyphenationcodes do texintegerdef(v .. "hyphenationmodecode",k,"immutable") end
+ for k, v in next, frozenparcodes do texintegerdef("frozen" .. v .. "code", k,"immutable") end
+ for k, v in next, flagcodes do texintegerdef(v .. "flagcode", k,"immutable") end
+ for k, v in next, normalizecodes do texintegerdef(v .. "code", k,"immutable") end
+
+end
+
+groupcodes = utilities.storage.allocate(table.swapped(groupcodes, groupcodes))
+glyphoptioncodes = utilities.storage.allocate(table.swapped(glyphoptioncodes,glyphoptioncodes))
+hyphenationcodes = utilities.storage.allocate(table.swapped(hyphenationcodes,hyphenationcodes))
+frozenparcodes = utilities.storage.allocate(table.swapped(frozenparcodes, frozenparcodes))
+flagcodes = utilities.storage.allocate(table.swapped(flagcodes, flagcodes))
+normalizecodes = utilities.storage.allocate(table.swapped(normalizecodes, normalizecodes))
+
+tex.groupcodes = groupcodes
+tex.glyphoptioncodes = glyphoptioncodes
+tex.hyphenationcodes = hyphenationcodes
+tex.frozenparcodes = frozenparcodes
+tex.flagcodes = flagcodes
+tex.normalizecodes = normalizecodes
diff --git a/Master/texmf-dist/tex/context/base/mkxl/toks-aux.mkxl b/Master/texmf-dist/tex/context/base/mkxl/toks-aux.mkxl
new file mode 100644
index 00000000000..4563014ecfc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/toks-aux.mkxl
@@ -0,0 +1,68 @@
+%D \module
+%D [ file=toks-aux,
+%D version=2018.11.29,
+%D title=\CONTEXT\ Token Support,
+%D subtitle=Helpers,
+%D author=Wolfgang Schuster,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Token Support / Helpers}
+
+\unprotect
+
+%D The older \type {\scantokens} overload has been replace by a more modern one (in
+%D \LMTX):
+%D
+%D \starttyping
+%D \protected\def\whatever#1[#2](#3)\relax{oeps #1 and #2 and #3 done ## error}
+%D \scratchtoks{foo \framed{\red 123}456}
+%D
+%D \showluatokens{123\what}
+%D \showluatokens\whatever
+%D \showluatokens\framed
+%D
+%D \showluatokens\scratchtoks
+%D \showluatokens\everypar
+%D \stoptyping
+
+\registerctxluafile{toks-aux}{autosuffix}
+
+\installcorenamespace {tokenlist}
+
+\permanent\protected\def\definetokenlist[#1]%
+ {\ifcsname\??tokenlist#1\endcsname
+ \global\lastnamedcs\emptytoks
+ \else
+ \expandafter\newtoks\csname\??tokenlist#1\endcsname
+ \fi}
+
+\permanent\protected\def\starttokenlist[#1]#2\stoptokenlist
+ {\ifcsname\??tokenlist#1\endcsname \else
+ \expandafter\newtoks\csname\??tokenlist#1\endcsname
+ \fi
+ \toksapp\lastnamedcs{#2}}
+
+\aliased\let\stoptokenlist\relax
+
+\permanent\def\gettokenlist[#1]%
+ {\ifcsname\??tokenlist#1\endcsname
+ \the\lastnamedcs
+ \fi}
+
+\permanent\protected\def\settokenlist[#1]#2%
+ {\ifcsname\??tokenlist#1\endcsname \else
+ \expandafter\newtoks\csname\??tokenlist#1\endcsname
+ \fi
+ \toksapp\lastnamedcs{#2}}
+
+\permanent\protected\def\resettokenlist[#1]%
+ {\ifcsname\??tokenlist#1\endcsname
+ \lastnamedcs\emptytoks
+ \fi}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/toks-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/toks-ini.lmt
new file mode 100644
index 00000000000..4b2d1faccf5
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/toks-ini.lmt
@@ -0,0 +1,332 @@
+if not modules then modules = { } end modules ['toks-ini'] = {
+ version = 1.001,
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+tokens = tokens or { }
+
+local tokens = tokens
+local token = token -- the built in one
+local next = next
+local tonumber = tonumber
+local tostring = tostring
+local utfchar = utf.char
+local char = string.char
+local printtable = table.print
+local concat = table.concat
+local format = string.format
+
+local commands = token.getcommandvalues()
+local values = token.getfunctionvalues()
+values.dimen = values.dimension
+values.count = values.integer
+tokens.values = utilities.storage.allocate(table.swapped(values, values))
+tokens.commands = utilities.storage.allocate(table.swapped(commands,commands))
+
+local scan_toks = token.scan_toks
+local scan_string = token.scan_string
+local scan_argument = token.scan_argument
+local scan_delimited = token.scan_delimited
+local scan_tokenlist = token.scan_tokenlist or scan_string
+local scan_integer = token.scan_integer or token.scan_int
+local scan_cardinal = token.scan_cardinal
+local scan_code = token.scan_code
+local scan_token_code = token.scan_token_code
+local scan_dimen = token.scan_dimen
+local scan_glue = token.scan_glue
+local scan_skip = token.scan_skip
+local scan_keyword = token.scan_keyword
+local scan_keyword_cs = token.scan_keyword_cs or scan_keyword
+local scan_token = token.scan_token
+local scan_box = token.scan_box
+local scan_word = token.scan_word
+local scan_letters = token.scan_letters or scan_word -- lmtx
+local scan_key = token.scan_key
+local scan_value = token.scan_value
+local scan_char = token.scan_char
+local scan_number = token.scan_number -- not defined
+local scan_csname = token.scan_csname
+local scan_real = token.scan_real
+local scan_float = token.scan_float
+local scan_luanumber = token.scan_luanumber or scan_float -- only lmtx
+local scan_luainteger = token.scan_luainteger or scan_integer -- only lmtx
+local scan_luacardinal = token.scan_luacardinal or scan_cardinal -- only lmtx
+
+local scan_integer_argument = token.scan_integer_argument
+local scan_dimen_argument = token.scan_dimen_argument
+
+-- todo: gobble_integer -- might go away
+-- todo: gobble_dimension -- might go away
+
+local set_macro = token.set_macro
+local set_char = token.set_char
+local set_lua = token.set_lua
+
+local create_token = token.create
+local new_token = token.new
+local is_defined = token.is_defined
+local is_token = token.is_token
+
+tokens.new = new_token
+tokens.create = create_token
+tokens.istoken = is_token
+tokens.isdefined = is_defined
+tokens.defined = is_defined
+
+tokens.getinteger = token.get_integer
+tokens.setinteger = token.set_integer
+
+local bits = {
+ escape = 0x00000001, -- 2^00
+ begingroup = 0x00000002, -- 2^01
+ endgroup = 0x00000004, -- 2^02
+ mathshift = 0x00000008, -- 2^03
+ alignment = 0x00000010, -- 2^04
+ endofline = 0x00000020, -- 2^05
+ parameter = 0x00000040, -- 2^06
+ superscript = 0x00000080, -- 2^07
+ subscript = 0x00000100, -- 2^08
+ ignore = 0x00000200, -- 2^09
+ space = 0x00000400, -- 2^10 -- 1024
+ letter = 0x00000800, -- 2^11
+ other = 0x00001000, -- 2^12
+ active = 0x00002000, -- 2^13
+ comment = 0x00004000, -- 2^14
+ invalid = 0x00008000, -- 2^15
+ --
+ character = 0x00001800, -- 2^11 + 2^12
+ whitespace = 0x00002400, -- 2^13 + 2^10 -- / needs more checking
+ --
+ open = 0x00000402, -- 2^10 + 2^01 -- space + begingroup
+ close = 0x00000404, -- 2^10 + 2^02 -- space + endgroup
+}
+
+-- for k, v in next, bits do bits[v] = k end
+
+tokens.bits = bits
+
+-- words are space or \relax terminated and the trailing space is gobbled; a word
+-- can contain any non-space letter/other (see archive for implementation in lua)
+
+if not scan_number then
+
+ scan_number = function(base)
+ local s = scan_word()
+ if not s then
+ return nil
+ elseif base then
+ return tonumber(s,base)
+ else
+ return tonumber(s)
+ end
+ end
+
+end
+
+local function scan_boolean()
+ local kw = scan_word()
+ if kw == "true" then
+ return true
+ elseif kw == "false" then
+ return false
+ else
+ return nil
+ end
+end
+
+local function scan_verbatim()
+ return scan_argument(false)
+end
+
+if not scan_box then
+
+ local scan_list = token.scan_list
+ local put_next = token.put_next
+
+ scan_box = function(s)
+ if s == "hbox" or s == "vbox" or s == "vtop" then
+ put_next(create_token(s))
+ end
+ return scan_list()
+ end
+
+ token.scan_box = scan_box
+
+end
+
+tokens.scanners = { -- these expand
+ token = scan_token,
+ toks = scan_toks,
+ tokens = scan_toks,
+ box = scan_box,
+ hbox = function() return scan_box("hbox") end,
+ vbox = function() return scan_box("vbox") end,
+ vtop = function() return scan_box("vtop") end,
+ dimen = scan_dimen,
+ dimension = scan_dimen,
+ glue = scan_glue,
+ gluevalues = function() return scan_glue(false,false,true) end,
+ gluespec = scan_skip,
+ integer = scan_integer,
+ cardinal = scan_cardinal,
+ real = scan_real,
+ float = scan_float,
+ luanumber = scan_luanumber,
+ luainteger = scan_luainteger,
+ luacardinal = scan_luacardinal,
+ count = scan_integer,
+ string = scan_string,
+ argument = scan_argument,
+ delimited = scan_delimited,
+ tokenlist = scan_tokenlist,
+ verbatim = scan_verbatim, -- detokenize
+ code = scan_code,
+ tokencode = scan_token_code,
+ word = scan_word,
+ letters = scan_letters,
+ key = scan_key,
+ value = scan_value,
+ char = scan_char,
+ number = scan_number,
+ boolean = scan_boolean,
+ keyword = scan_keyword,
+ keywordcs = scan_keyword_cs,
+ csname = scan_csname,
+
+ next = token.scan_next,
+ nextexpanded = token.scan_next_expanded,
+
+ peek = token.peek_next,
+ peekexpanded = token.peek_next_expanded,
+ peekchar = token.peek_next_char,
+
+ skip = token.skip_next,
+ skipexpanded = token.skip_next_expanded,
+
+ cmdchr = token.scan_cmdchr,
+ cmdchrexpanded = token.scan_cmdchr_expanded,
+
+ ischar = token.is_next_char,
+
+ integerargument = scan_integer_argument,
+ dimenargument = scan_dimen_argument,
+}
+
+tokens.getters = { -- these don't expand
+ meaning = token.get_meaning,
+ macro = token.get_macro,
+ token = token.scan_next or token.get_next, -- not here, use scanners.next or token
+ cstoken = token.get_cstoken,
+ count = tex.getcount,
+ dimen = tex.getdimen,
+ skip = tex.getglue,
+ glue = tex.getglue,
+ skip = tex.getmuglue,
+ glue = tex.getmuglue,
+ box = tex.getbox,
+}
+
+tokens.setters = {
+ macro = set_macro,
+ char = set_char,
+ lua = set_lua,
+ count = tex.setcount,
+ dimen = tex.setdimen,
+ skip = tex.setglue,
+ glue = tex.setglue,
+ skip = tex.setmuglue,
+ glue = tex.setmuglue,
+ box = tex.setbox,
+}
+
+token.accessors = {
+ command = token.get_command,
+ cmd = token.get_command,
+ cmdname = token.get_cmdname,
+ name = token.get_cmdname,
+ csname = token.get_csname,
+ index = token.get_index,
+ active = token.get_active,
+ frozen = token.get_frozen,
+ protected = token.get_protected,
+ expandable = token.get_protected,
+ user = token.get_user,
+ cmdchrcs = token.get_cmdchrcs,
+ active = token.get_active,
+ range = token.get_range,
+}
+
+-- static int run_scan_token(lua_State * L)
+-- {
+-- saved_tex_scanner texstate;
+-- save_tex_scanner(texstate);
+-- get_x_token();
+-- make_new_token(L, cur_cmd, cur_chr, cur_cs);
+-- unsave_tex_scanner(texstate);
+-- return 1;
+-- }
+--
+-- static int run_get_future(lua_State * L)
+-- {
+-- /* saved_tex_scanner texstate; */
+-- /* save_tex_scanner(texstate); */
+-- get_token();
+-- make_new_token(L, cur_cmd, cur_chr, cur_cs);
+-- back_input();
+-- /* unsave_tex_scanner(texstate); */
+-- return 1;
+-- }
+
+if setinspector then
+
+ local simple = { letter = "letter", other_char = "other" }
+
+ local astable = function(t)
+ if t and is_token(t) then
+ local cmdname = t.cmdname
+ local simple = simple[cmdname]
+ if simple then
+ return {
+ id = t.id,
+ category = simple,
+ character = utfchar(t.index) or nil,
+ }
+ else
+ return {
+ id = t.id,
+ command = t.command,
+ index = t.index,
+ csname = t.csname,
+ cmdname = cmdname,
+ active = t.active,
+ expandable = t.expandable,
+ frozen = t.frozen,
+ tolerant = t.tolerant,
+ protected = t.protected,
+ primitive = t.primitive,
+ permanent = t.permanent,
+ noaligned = t.noaligned,
+ instance = t.instance,
+ immutable = t.imutable,
+ user = t.user,
+ }
+ end
+ end
+ end
+
+ tokens.astable = astable
+
+ setinspector("token",function(v) local t = astable(v) if t then printtable(t,tostring(v)) return true end end)
+
+end
+
+tokens.cache = table.setmetatableindex(function(t,k)
+ if not is_defined(k) then
+ set_macro(k,"","global")
+ end
+ local v = create_token(k)
+ t[k] = v
+ return v
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/toks-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/toks-ini.mkxl
new file mode 100644
index 00000000000..c7d7bceb384
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/toks-ini.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=toks-ini,
+%D version=2007.03.03,
+%D title=\CONTEXT\ Token Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Token Support / Initialization}
+
+\unprotect
+
+\registerctxluafile{toks-ini}{autosuffix}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/toks-scn.lmt b/Master/texmf-dist/tex/context/base/mkxl/toks-scn.lmt
new file mode 100644
index 00000000000..73eedbba789
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/toks-scn.lmt
@@ -0,0 +1,582 @@
+if not modules then modules = { } end modules ['toks-scn'] = {
+ version = 1.001,
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Writing this kind of code (and completing the newtoken code base) is fun. I did
+-- so with the brilliant film music from The Girl with the Dragon Tattoo running in a
+-- loop in the background (three cd's by Trent Reznor and Atticus Ross). An alien
+-- feeling helps with alien code.
+
+-- todo: more \let's at the tex end
+
+local type, next, tostring, tonumber = type, next, tostring, tonumber
+
+local formatters = string.formatters
+local concat = table.concat
+
+local scanners = tokens.scanners
+local tokenbits = tokens.bits
+
+local scanstring = scanners.string
+local scanargument = scanners.argument
+local scandelimited = scanners.delimited
+local scanverbatim = scanners.verbatim
+local scantokenlist = scanners.tokenlist
+local scantoks = scanners.toks
+local scaninteger = scanners.integer
+local scancardinal = scanners.cardinal
+local scannumber = scanners.number
+local scankeyword = scanners.keyword
+local scankeywordcs = scanners.keywordcs
+local scanword = scanners.word
+local scanletters = scanners.letters
+local scankey = scanners.key
+local scancode = scanners.code
+local scanboolean = scanners.boolean
+local scandimen = scanners.dimen
+local scanglue = scanners.glue
+local scangluevalues = scanners.gluevalues
+local scangluespec = scanners.gluespec
+local scancsname = scanners.csname
+local scanintegerargument = scanners.integerargument
+local scandimenargument = scanners.dimenargument
+
+local todimen = number.todimen
+local toboolean = toboolean
+
+local lpegmatch = lpeg.match
+local p_unquoted = lpeg.Cs(lpeg.patterns.unquoted)
+
+local trace_compile = false trackers.register("tokens.compile", function(v) trace_compile = v end)
+local report_compile = logs.reporter("tokens","compile")
+local report_scan = logs.reporter("tokens","scan")
+
+local open = tokenbits.open
+local close = tokenbits.close
+
+local function scanopen()
+ while true do
+ local c = scancode(open)
+ if c == 123 then
+ return true
+ -- elseif c ~= 32 then
+ elseif not c then
+ return
+ end
+ end
+end
+
+local function scanclose()
+ while true do
+ local c = scancode(close)
+ if c == 125 then
+ return true
+ -- elseif c ~= 32 then
+ elseif not c then
+ return
+ end
+ end
+end
+
+scanners.scanopen = scanopen
+scanners.scanclose = scanclose
+
+local function scanlist()
+ local wrapped = scanopen()
+ local list = { }
+ local size = 0
+ while true do
+ local entry = scanstring()
+ if entry then
+ size = size + 1
+ list[size] = entry
+ else
+ break
+ end
+ end
+ if wrapped then
+ scanclose()
+ end
+ return list
+end
+
+local function scanconditional()
+ local kw = scanword()
+ if kw == "true" then
+ return true
+ end
+ if kw == "false" then
+ return false
+ end
+ local c = scaninteger()
+ if c then
+ return c == 0 -- with a conditional 0=true
+ end
+ return nil
+end
+
+local function scantable(t,data)
+ if not data then
+ data = { }
+ end
+ if t then
+ local wrapped = scanopen()
+ while true do
+ local key = scanword(true)
+ if key then
+ local get = t[key]
+ if get then
+ data[key] = get()
+ else
+ -- catch all we can get
+ end
+ else
+ break
+ end
+ end
+ if wrapped then
+ scanclose()
+ end
+ end
+ return data
+end
+
+function tokens.constant(s)
+ if type(s) == "string" then
+ return "'" .. s .. "'"
+ else
+ return s
+ end
+end
+
+scanners.list = scanlist
+scanners.table = scantable
+scanners.conditional = scanconditional
+
+function scanners.whd()
+ local width, height, depth
+ while true do
+ if scankeyword("width") then
+ width = scandimen()
+ elseif scankeyword("height") then
+ height = scandimen()
+ elseif scankeyword("depth") then
+ depth = scandimen()
+ else
+ break
+ end
+ end
+ if width or height or depth then
+ return width or 0, height or 0, depth or 0
+ else
+ -- we inherit
+ end
+end
+
+local l = utf.byte("[")
+local r = utf.byte("]")
+
+local function scanbracketed()
+ local s = scandelimited(l, r)
+ if s then
+ return s
+ else
+ local readstate = status.getreadstate()
+ report_scan("missing argument in line %i of %a", readstate.linenumber, readstate.filename)
+ return ""
+ end
+end
+
+local function scanoptional()
+ return scandelimited(l, r) or ""
+end
+
+local function scanbracketedasis()
+ return scandelimited(l, r, false)
+end
+
+local function scanargumentasis()
+ return scanargument(false)
+end
+
+scanners.bracketed = scanbracketed
+scanners.optional = scanoptional
+scanners.bracketedasis = scanbracketedasis
+scanners.argumentasis = scanargumentasis
+
+local shortcuts = {
+ tokens = tokens,
+ bits = tokenbits,
+ open = open,
+ close = close,
+ scanners = scanners,
+ scanstring = scanstring,
+ scanargument = scanargument,
+ scanverbatim = scanverbatim,
+ scantokenlist = scantokenlist,
+ scantoks = scantoks,
+ scaninteger = scaninteger,
+ scancardinal = scancardinal,
+ scannumber = scannumber,
+ scantable = scantable, -- not directly useable
+ scankeyword = scankeyword,
+ scankeywordcs = scankeywordcs,
+ scanword = scanword,
+ scanletters = scanletters,
+ -- scankey = scankey,
+ scancode = scancode,
+ scanboolean = scanboolean,
+ scanglue = scanglue, -- list
+ scangluespec = scangluespec,
+ scangluevalues = scangluevalues,
+ scandimen = scandimen,
+ scandimension = scandimen,
+ scanbox = scanners.box,
+ scanhbox = scanners.hbox,
+ scanvbox = scanners.vbox,
+ scanvtop = scanners.vtop,
+ scanconditional = scanconditional,
+ scanopen = scanopen,
+ scanclose = scanclose,
+ scanlist = scanlist,
+ scancsname = scancsname,
+ scandelimited = scandelimited, -- not directly useable
+ scanbracketed = scanbracketed,
+ scanoptional = scanoptional,
+ scanbracketedasis = scanbracketedasis,
+ scanargumentasis = scanargumentasis,
+ scanintegerargument = scanintegerargument,
+ scandimenargument = scandimenargument,
+ todimen = todimen,
+ tonumber = tonumber,
+ tostring = tostring,
+ toboolean = toboolean,
+ inspect = inspect,
+ report = report_scan,
+}
+
+tokens.shortcuts = shortcuts
+
+local load = load
+local dump = string.dump
+
+local function loadstripped(code)
+ return load(code,nil,nil,shortcuts)
+ -- return load(dump(load(code),true),nil,nil,shortcuts)
+end
+
+tokens.converters = {
+ tonumber = "tonumber",
+ tostring = "tostring",
+ toboolean = "toboolean",
+ todimen = "todimen",
+ toglue = "todimen",
+}
+
+-- We could just pickup a keyword but then we really need to make sure that no number
+-- follows it when that is the assignment and adding an optional = defeats the gain
+-- in speed. Currently we have sources with no spaces (\startcontextdefinitioncode
+-- ...) so it fails there.
+--
+-- Another drawback is that we then need to use { } instead of ending with \relax (as
+-- we can do now) but that is no big deal. It's just that I then need to check the TeX
+-- end. More pain than gain and a bit risky too. Using scanletters works better, but
+-- the gain is only some 10 percent but if we don't have keywords with numbers it might
+-- make sense in the end, some day.
+
+local f_if = formatters[ " if scankeywordcs('%s') then data['%s'] = scan%s()"]
+local f_elseif = formatters[" elseif scankeywordcs('%s') then data['%s'] = scan%s()"]
+
+----- f_if_x = formatters[ " if not data['%s'] and scankeywordcs('%s') then data['%s'] = scan%s()"]
+----- f_elseif_x = formatters[" elseif not data['%s'] and scankeywordcs('%s') then data['%s'] = scan%s()"]
+
+----- f_if = formatters[" local key = scanletters() if key == '' then break elseif key == '%s' then data['%s'] = scan%s()"]
+----- f_elseif = formatters[" elseif key == '%s' then data['%s'] = scan%s()"]
+
+local f_local = formatters["local scan%s = scanners.%s"]
+local f_scan = formatters["scan%s()"]
+local f_shortcut = formatters["local %s = scanners.converters.%s"]
+
+local f_if_c = formatters[ " if scankeywordcs('%s') then data['%s'] = %s(scan%s())"]
+local f_elseif_c = formatters[" elseif scankeywordcs('%s') then data['%s'] = %s(scan%s())"]
+local f_scan_c = formatters["%s(scan%s())"]
+
+-- see above
+
+----- f_if_c = formatters[" local key = scanletters() if key == '' then break elseif key == '%s' then data['%s'] = %s(scan%s())"]
+----- f_elseif_c = formatters[" elseif k == '%s' then data['%s'] = %s(scan%s())"]
+
+local f_any = formatters[" else local key = scanword(true) if key then data[key] = scan%s() else break end end"]
+local f_any_c = formatters[" else local key = scanword(true) if key then data[key] = %s(scan%s()) else break end end"]
+local s_done = " else break end"
+
+local f_any_all = formatters[" local key = scanword(true) if key then data[key] = scan%s() else break end"]
+local f_any_all_c= formatters[" local key = scanword(true) if key then data[key] = %s(scan%s()) else break end"]
+
+local f_table = formatters["%\nt\nreturn function()\n local data = { }\n%s\n return %s\nend\n"]
+local f_sequence = formatters["%\nt\n%\nt\n%\nt\nreturn function()\n return %s\nend\n"]
+local f_singular = formatters["%\nt\n%\nt\n\nreturn function(%s)\n return %s\nend\n"]
+local f_simple = formatters["%\nt\nreturn function()\n return %s\nend\n"]
+local f_string = formatters["%q"]
+local f_action_f = formatters["action%s(%s)"]
+local f_action_s = formatters["local action%s = tokens._action[%s]"]
+local f_nested = formatters["local function scan%s()\n local data = { }\n%s\n return data\nend\n"]
+
+local f_check = formatters[ [[
+ local wrapped = scanopen()
+ while true do
+ ]] .. "%\nt\n" .. [[
+ %s
+ end
+ if wrapped then
+ scanclose()
+ end
+]] ]
+
+-- using these shortcuts saves temporary small tables (okay, it looks uglier)
+
+local presets = {
+ ["1 string" ] = { "string" },
+ ["2 strings"] = { "string", "string" },
+ ["3 strings"] = { "string", "string", "string" },
+ ["4 strings"] = { "string", "string", "string", "string" },
+ ["5 strings"] = { "string", "string", "string", "string", "string" },
+ ["6 strings"] = { "string", "string", "string", "string", "string", "string" },
+ ["7 strings"] = { "string", "string", "string", "string", "string", "string", "string" },
+ ["8 strings"] = { "string", "string", "string", "string", "string", "string", "string", "string" },
+
+ ["1 argument" ] = { "argument" },
+ ["2 arguments"] = { "argument", "argument" },
+ ["3 arguments"] = { "argument", "argument", "argument" },
+ ["4 arguments"] = { "argument", "argument", "argument", "argument" },
+}
+
+tokens.presets = presets
+
+function tokens.compile(specification)
+ local f = { }
+ local n = 0
+ local c = { }
+ local t = specification.arguments or specification
+ local a = specification.actions or nil
+ if type(a) == "function" then
+ a = { a }
+ end
+ local code
+ local args
+ local function compile(t,nested)
+ local done = s_done
+ local r = { }
+ local m = 0
+ for i=1,#t do
+ local ti = t[i]
+ if ti == "*" and i == 1 then
+ done = f_any_all("string")
+ else
+ local t1 = ti[1]
+ local t2 = ti[2] or "string"
+ if type(t2) == "table" then
+ n = n + 1
+ f[n] = compile(t2,n)
+ t2 = n
+ end
+ local t3 = ti[3]
+ if type(t3) == "function" then
+ -- todo: also create shortcut
+ elseif t3 then
+ c[t3] = f_shortcut(t3,t3)
+ if t1 == "*" then
+ if i == 1 then
+ done = f_any_all_c(t3,t2)
+ break
+ else
+ done = f_any_c(t3,t2)
+ end
+ else
+ m = m + 1
+ r[m] = (m > 1 and f_elseif_c or f_if_c)(t1,t1,t3,t2)
+ end
+ else
+ if t1 == "*" then
+ if i == 1 then
+ done = f_any_all(t2)
+ break
+ else
+ done = f_any(t2)
+ end
+ else
+ m = m + 1
+ r[m] = (m > 1 and f_elseif or f_if )(t1,t1,t2)
+ -- r[m] = (m > 1 and f_elseif_x or f_if_x)(t1,t1,t1,t2)
+ end
+ end
+ end
+ end
+ local c = f_check(r,done)
+ if nested then
+ return f_nested(nested,c)
+ else
+ return c
+ end
+ end
+ local p = t and presets[t] -- already done in implement
+ if p then
+ t = p
+ end
+ local tt = type(t)
+ if tt == "string" then
+ if a then
+ local s = lpegmatch(p_unquoted,t)
+ if s and t ~= s then
+ code = t
+ else
+ code = f_scan(t)
+ end
+ tokens._action = a
+ for i=1,#a do
+ code = f_action_f(i,code)
+ n = n + 1
+ f[n] = f_action_s(i,i)
+ end
+ code = f_simple(f,code)
+ else
+ return scanners[t]
+ end
+ elseif tt ~= "table" then
+ return
+ elseif #t == 1 then
+ local ti = t[1]
+ if type(ti) == "table" then
+ ti = compile(ti)
+ code = "data"
+ if a then
+ tokens._action = a
+ for i=1,#a do
+ code = f_action_f(i,code)
+ n = n + 1
+ f[n] = f_action_s(i,i)
+ end
+ end
+ code = f_table(f,ti,code)
+ elseif a then
+ code = f_scan(ti)
+ tokens._action = a
+ for i=1,#a do
+ code = f_action_f(i,code)
+ n = n + 1
+ f[n] = f_action_s(i,i)
+ end
+ code = f_simple(f,code)
+ else
+ return scanners[ti]
+ end
+ elseif #t == 0 then
+ if specification.usage == "value" then
+ code = "b"
+ args = "_,b"
+ else
+ code = ""
+ args = ""
+ end
+ if a then
+ tokens._action = a
+ for i=1,#a do
+ code = f_action_f(i,code)
+ n = n + 1
+ f[n] = f_action_s(i,i)
+ end
+ end
+ code = f_singular(c,f,args,code)
+ else
+ local r = { }
+ local p = { }
+ local m = 0
+ for i=1,#t do
+ local ti = t[i]
+ local tt = type(ti)
+ if tt == "table" then
+ if ti[1] == "_constant_" then
+ local v = ti[2]
+ if type(v) == "string" then
+ r[i] = f_string(v)
+ else
+ r[i] = tostring(v)
+ end
+ else
+ m = m + 1
+ p[m] = compile(ti,100+m)
+ r[i] = f_scan(100+m)
+ end
+ elseif tt == "number" then
+ r[i] = tostring(ti)
+ elseif tt == "boolean" then
+ r[i] = tostring(ti)
+ else
+ local s = lpegmatch(p_unquoted,ti)
+ if s and ti ~= s then
+ r[i] = ti -- a string, given as "'foo'" or '"foo"'
+ elseif scanners[ti] then
+ r[i] = f_scan(ti)
+ else
+ report_compile("unknown scanner %a",ti)
+ r[i] = ti
+ end
+ end
+ end
+ code = concat(r,",")
+ if a then
+ tokens._action = a
+ for i=1,#a do
+ code = f_action_f(i,code)
+ n = n + 1
+ f[n] = f_action_s(i,i)
+ end
+ end
+ code = f_sequence(c,f,p,code)
+ end
+ if not code then
+ return
+ end
+ if trace_compile then
+ report_compile("code: %s",code)
+ end
+ local code, message = loadstripped(code)
+ if code then
+ code = code() -- sets action
+ else
+ report_compile("error in code: %s",code)
+ report_compile("error message: %s",message)
+ end
+ if a then
+ tokens._action = nil
+ end
+ if code then
+ return code
+ end
+end
+
+-- local fetch = tokens.compile {
+-- "string",
+-- "string",
+-- {
+-- { "data", "string" },
+-- { "tab", "string" },
+-- { "method", "string" },
+-- { "foo", {
+-- { "method", "integer" },
+-- { "compact", "number" },
+-- { "nature" },
+-- { "*" }, -- any key
+-- } },
+-- { "compact", "string", "tonumber" },
+-- { "nature", "boolean" },
+-- { "escape", "string" },
+-- { "escape" },
+-- },
+-- "boolean",
+-- }
+--
+-- os.exit()
diff --git a/Master/texmf-dist/tex/context/base/mkxl/toks-scn.mkxl b/Master/texmf-dist/tex/context/base/mkxl/toks-scn.mkxl
new file mode 100644
index 00000000000..e8da7063d25
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/toks-scn.mkxl
@@ -0,0 +1,23 @@
+%D \module
+%D [ file=toks-ini,
+%D version=2007.03.03,
+%D title=\CONTEXT\ Token Support,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Token Support / Scanners}
+
+\unprotect
+
+\registerctxluafile{toks-scn}{}
+\registerctxluafile{cldf-scn}{}
+\registerctxluafile{cldf-stp}{}
+\registerctxluafile{cldf-lmt}{}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-ctx.mkxl b/Master/texmf-dist/tex/context/base/mkxl/trac-ctx.mkxl
new file mode 100644
index 00000000000..656a7a9aa9b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-ctx.mkxl
@@ -0,0 +1,58 @@
+%D \module
+%D [ file=trac-ctx,
+%D version=2012.07.13,
+%D title=\CONTEXT\ Tracing Macros,
+%D subtitle=TeX Trackers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Tracing Macros / TeX Trackers}
+
+\registerctxluafile{trac-ctx}{}
+
+\unprotect
+
+\permanent\protected\def\installtextracker #1#2#3{\clf_installtextracker {#1}{\detokenize{#2}}{\detokenize{#3}}}
+\permanent\protected\def\installtexdirective #1#2#3{\clf_installtexdirective {#1}{\detokenize{#2}}{\detokenize{#3}}}
+\permanent\protected\def\installtexexperiment#1#2#3{\clf_installtexexperiment{#1}{\detokenize{#2}}{\detokenize{#3}}}
+
+\appendtoks
+ \clf_initializetextrackers
+ \clf_initializetexdirectives
+ \clf_initializetexexperiments
+\to \everyjob
+
+\protect \endinput
+
+% this is one option:
+%
+% \newconditional\c_math_virtual_tx
+% \newconditional\c_math_virtual_px
+% \newconditional\c_math_virtual_lm
+%
+% \installtexdirective {fonts.math.virtual.tx} {\settrue \c_math_virtual_tx} {\setfalse\c_math_virtual_tx}
+% \installtexdirective {fonts.math.virtual.px} {\settrue \c_math_virtual_px} {\setfalse\c_math_virtual_px}
+% \installtexdirective {fonts.math.virtual.lm} {\settrue \c_math_virtual_lm} {\setfalse\c_math_virtual_lm}
+%
+% but as it makes the typescript look ugly we wil use:
+%
+% \installtexdirective {fonts.math.virtual.tx} {\enablemode[fonts.math.virtual.tx]} {\disablemode[fonts.math.virtual.tx]}
+% \installtexdirective {fonts.math.virtual.px} {\enablemode[fonts.math.virtual.px]} {\disablemode[fonts.math.virtual.px]}
+% \installtexdirective {fonts.math.virtual.lm} {\enablemode[fonts.math.virtual.lm]} {\disablemode[fonts.math.virtual.lm]}
+%
+% which could be:
+%
+% \def\mode_f_m_v_tx{fonts.math.virtual.tx}
+% \def\mode_f_m_v_px{fonts.math.virtual.px}
+% \def\mode_f_m_v_lm{fonts.math.virtual.lm}
+%
+% \installtexdirective \mode_f_m_v_tx {\enablemode[\mode_f_m_v_tx]} {\disablemode[\mode_f_m_v_tx]}
+% \installtexdirective \mode_f_m_v_px {\enablemode[\mode_f_m_v_px]} {\disablemode[\mode_f_m_v_px]}
+% \installtexdirective \mode_f_m_v_lm {\enablemode[\mode_f_m_v_lm]} {\disablemode[\mode_f_m_v_lm]}
+%
+% alas .. we don't want to define this in the core
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-deb.lmt b/Master/texmf-dist/tex/context/base/mkxl/trac-deb.lmt
new file mode 100644
index 00000000000..8ff12410528
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-deb.lmt
@@ -0,0 +1,417 @@
+if not modules then modules = { } end modules ['trac-deb'] = {
+ version = 1.001,
+ comment = "companion to trac-deb.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is an old mechanism, a result of some experiments in the early days of
+-- luatex and mkiv, but still nice anyway. I will clean it up in lmtx.
+
+local status = status
+
+local tonumber, tostring, type = tonumber, tostring, type
+local format, concat, match, find, gsub = string.format, table.concat, string.match, string.find, string.gsub
+
+local report_nl = logs.newline
+local report_str = logs.writer
+
+tracers = tracers or { }
+local tracers = tracers
+
+local implement = interfaces.implement
+
+local ioflush = io.flush
+local ioread = io.read
+local writenl = texio.write_nl
+local write = texio.write
+
+local runlocal = tex.runlocal
+local terminaldata = false
+local context = context
+
+implement {
+ name = "fetchterminaldata",
+ actions = function()
+ context(terminaldata)
+ end,
+}
+
+function texio.terminal()
+ writenl("\n" .. "entering interactive mode, use \\quit to abort reading" .."\n\n")
+ while true do
+ write(">")
+ ioflush()
+ terminaldata = ioread()
+ if terminaldata == "\\quit" then
+ terminaldata = false
+ break
+ else
+ runlocal("t_syst_terminal_data",nil,nil,true) -- obeymode
+ end
+ end
+end
+
+implement {
+ name = "readfromterminal",
+ public = true,
+ protected = true,
+ actions = texio.terminal,
+}
+
+local savedluaerror = nil
+local usescitelexer = nil
+local quitonerror = true
+
+local function errorreporter(luaerror)
+ local category = luaerror and "lua error" or "tex error"
+ local report = logs.reporter(category)
+ logs.enable(category)
+ return report
+end
+
+function tracers.showlines(filename,linenumber,offset,luaerrorline)
+ local data = io.loaddata(filename)
+ if not data or data == "" then
+ local hash = url.hashed(filename)
+ if not hash.noscheme then
+ local ok, d, n = resolvers.loaders.byscheme(hash.scheme,filename)
+ if ok and n > 0 then
+ data = d
+ end
+ end
+ end
+ local scite = usescitelexer and require("util-sci")
+ if scite then
+ return utilities.scite.tohtml(data,"tex",linenumber or true,false)
+ else
+ local lines = data and string.splitlines(data)
+ if lines and #lines > 0 then
+ if luaerrorline and luaerrorline > 0 then
+ -- lua error: linenumber points to last line
+ local start = "\\startluacode"
+ local stop = "\\stopluacode"
+ local n = linenumber
+ for i=n,1,-1 do
+ local line = lines[i]
+ if not line then
+ break
+ elseif find(line,start) then
+ n = i + luaerrorline - 1
+ if n <= linenumber then
+ linenumber = n
+ end
+ break
+ end
+ end
+ end
+ offset = tonumber(offset) or 10
+ linenumber = tonumber(linenumber) or 10
+ local start = math.max(linenumber - offset,1)
+ local stop = math.min(linenumber + offset,#lines)
+ if stop > #lines then
+ return "<linenumber past end of file>"
+ else
+ local result, fmt = { }, "%" .. #tostring(stop) .. "d %s %s"
+ for n=start,stop do
+ result[#result+1] = format(fmt,n,n == linenumber and ">>" or " ",lines[n])
+ end
+ return concat(result,"\n")
+ end
+ else
+ return "<empty file>"
+ end
+ end
+end
+
+-- todo: last tex error has ! prepended
+-- todo: some nested errors have two line numbers
+-- todo: collect errorcontext in string (after code cleanup)
+-- todo: have a separate status.lualinenumber
+-- todo: \starttext bla \blank[foo] bla \stoptext
+
+local nop = function() end
+local resetmessages = status.resetmessages or nop
+
+local function processerror(offset,errortype)
+ local readstate = status.readstate
+ local filename = readstate.filename
+ local linenumber = readstate.linenumber
+ local skiplinenumber = readstate.skiplinenumber
+ local errorstate = status.errorstate
+ local lastcontext = errorstate.errorcontext
+ local lasttexerror = errorstate.error or "?"
+ local lastluaerror = errorstate.luaerror or "?" -- lasttexerror
+ local luaerrorline = match(lastluaerror,[[lua%]?:.-(%d+)]]) or (lastluaerror and find(lastluaerror,"?:0:",1,true) and 0)
+ local lastmpserror = match(lasttexerror,[[^.-mp%serror:%s*(.*)$]])
+ resetmessages()
+ lastluaerror = gsub(lastluaerror,"%[\\directlua%]","[ctxlua]")
+ tracers.printerror {
+ filename = filename,
+ linenumber = linenumber,
+ skiplinenumber = skiplinenumber,
+ offset = tonumber(offset) or 10,
+ lasttexerror = lasttexerror,
+ lastmpserror = lastmpserror,
+ lastluaerror = lastluaerror, -- can be the same as lasttexerror
+ luaerrorline = luaerrorline,
+ lastcontext = lastcontext,
+ lasttexhelp = tex.gethelptext and tex.gethelptext() or nil,
+ errortype = errortype,
+ }
+ if job and type(job.disablesave) == "function" then
+ job.disablesave()
+ end
+end
+
+directives.register("system.quitonerror",function(v)
+ quitonerror = toboolean(v)
+end)
+
+directives.register("system.usescitelexer",function(v)
+ usescitelexer = toboolean(v)
+end)
+
+local busy = false
+
+function tracers.printerror(specification)
+ if not busy then
+ busy = true
+ local errorvalues = table.swapped(tex.geterrorvalues())
+ local filename = specification.filename
+ local linenumber = specification.linenumber
+ local lasttexerror = specification.lasttexerror
+ local lastmpserror = specification.lastmpserror
+ local lastluaerror = specification.lastluaerror
+ local lastcontext = specification.lastcontext
+ local luaerrorline = specification.luaerrorline
+ local errortype = specification.errortype
+ local offset = specification.offset
+ local endoffile = specification.endoffile
+ local report = errorreporter(luaerrorline)
+ if errortype == errorvalues.warning then
+ report("warning error: %s",lasttexerror or "-")
+ busy = false;
+ return;
+ elseif errortype == errorvalues.eof then
+ report("runaway error: %s",lasttexerror or "-")
+ if not quitonerror and texio.terminal then
+ texio.terminal() -- not well tested
+ end
+ elseif errortype == errorvalues.condition then
+ linenumber = specification.skiplinenumber
+ if linenumber > 0 then
+ report("condition error on line %s in file %s: %s",linenumber,filename,lasttexerror)
+ report_nl()
+ report_str(tracers.showlines(filename,linenumber,offset,tonumber(luaerrorline)))
+ report_nl()
+ else
+ report("runaway condition error: %s",lasttexerror or "-")
+ end
+ quitonerror = true
+ elseif not filename then
+ report("fuzzy error:")
+ report(" tex: %s",lasttexerror or "-")
+ report(" lua: %s",lastluaerror or "-")
+ report(" mps: %s",lastmpserror or "-")
+ elseif type(filename) == "number" then
+ report("error on line %s of filehandle %s: %s ...",linenumber,lasttexerror)
+ else
+ report_nl()
+ if luaerrorline then
+ if linenumber == 0 or not filename or filename == "" then
+ print("\nfatal lua error:\n\n",lastluaerror,"\n")
+ luatex.abort()
+ return
+ else
+ report("lua error on line %s in file %s:\n\n%s",linenumber,filename,lastluaerror)
+ end
+ elseif lastmpserror then
+ report("mp error on line %s in file %s:\n\n%s",linenumber,filename,lastmpserror)
+ else
+ report("tex error on line %s in file %s: %s",linenumber,filename,lasttexerror)
+ if lastcontext then
+ report_nl()
+ report_str(lastcontext)
+ report_nl()
+ else
+ report_nl()
+ -- tex.showcontext()
+ end
+ if lastluaerror and not match(lastluaerror,"^%s*[%?]*%s*$") then
+ print("\nlua error:\n\n",lastluaerror,"\n")
+ quitonerror = true
+ end
+ end
+ report_nl()
+ report_str(tracers.showlines(filename,linenumber,offset,tonumber(luaerrorline)))
+ report_nl()
+ end
+ local errname = file.addsuffix(tex.jobname .. "-error","log")
+ if quitonerror then
+ table.save(errname,specification)
+ local help = specification.lasttexhelp
+ if help and #help > 0 then
+ report_nl()
+ report_str(help)
+ report_nl()
+ report_nl()
+ end
+ luatex.abort()
+ end
+ busy = false
+ end
+end
+
+luatex.wrapup(function() os.remove(file.addsuffix(tex.jobname .. "-error","log")) end)
+
+local function processwarning(offset)
+ local warningstate = status.warningstate
+ local lastwarning = warningstate.warning or "?"
+ local lastlocation = warningstate.warningtag or "?"
+ resetmessages()
+ tracers.printwarning {
+ lastwarning = lastwarning,
+ lastlocation = lastlocation,
+ }
+end
+
+function tracers.printwarning(specification)
+ logs.report("luatex warning","%s: %s",specification.lastlocation,specification.lastwarning)
+end
+
+directives.register("system.errorcontext", function(v)
+ local register = callback.register
+ if v then
+ register('show_error_message', nop)
+ register('show_warning_message', function() processwarning(v) end)
+ register('intercept_lua_error', function() processerror(v) end)
+ register('intercept_tex_error', function(mode,eof) processerror(v,eof) return mode end)
+ else
+ register('show_error_message', nil)
+ register('show_warning_message', nil)
+ register('intercept_lua_error', nil)
+ register('intercept_tex_error', nil)
+ end
+end)
+
+-- this might move
+
+lmx = lmx or { }
+
+lmx.htmfile = function(name) return environment.jobname .. "-status.html" end
+lmx.lmxfile = function(name) return resolvers.findfile(name,'tex') end
+
+local function reportback(lmxname,default,variables)
+ if lmxname == false then
+ return variables
+ else
+ local name = lmx.show(type(lmxname) == "string" and lmxname or default,variables)
+ if name then
+ logs.report("context report","file: %s",name)
+ end
+ end
+end
+
+local function showerror(lmxname)
+ local readstate = status.readstate()
+ local filename = readstate.filename
+ local linenumber = tonumber(readstate.linenumber) or 0
+ local errorcontext = ""
+ if not filename then
+ filename = status.iocodes[readstate.iocode]
+ errorcontext = 'error in filename'
+ else
+ errorcontext = tracers.showlines(filename,linenumber,offset)
+ end
+ local variables = {
+ ['title'] = 'ConTeXt Error Information',
+ ['errormessage'] = status.errorstatus.error or "?",
+ ['linenumber'] = linenumber,
+ ['color-background-one'] = lmx.get('color-background-yellow'),
+ ['color-background-two'] = lmx.get('color-background-purple'),
+ ['filename'] = filename,
+ ['errorcontext'] = errorcontext,
+ }
+ reportback(lmxname,"context-error.lmx",variables)
+ luatex.abort()
+end
+
+lmx.showerror = showerror
+
+function lmx.overloaderror(v)
+ if v == "scite" then
+ usescitelexer = true
+ end
+ callback.register('show_error_message', function() showerror() end)
+ callback.register('intercept_lua_error', function() showerror() end)
+ callback.register('intercept_tex_error', function() showerror() end)
+end
+
+directives.register("system.showerror", lmx.overloaderror)
+
+-- local debugger = utilities.debugger
+--
+-- local function trace_calls(n)
+-- debugger.enable()
+-- luatex.registerstopactions(function()
+-- debugger.disable()
+-- debugger.savestats(tex.jobname .. "-luacalls.log",tonumber(n))
+-- end)
+-- trace_calls = function() end
+-- end
+--
+-- directives.register("system.tracecalls", function(n)
+-- trace_calls(n)
+-- end) -- indirect is needed for nilling
+
+-- Obsolete ... not that usefull as normally one runs from an editor and
+-- when run unattended it makes no sense either.
+
+-- local editor = [[scite "-open:%filename%" -goto:%linenumber%]]
+--
+-- directives.register("system.editor",function(v)
+-- editor = v
+-- end)
+--
+-- callback.register("call_edit",function(filename,linenumber)
+-- if editor then
+-- editor = gsub(editor,"%%s",filename)
+-- editor = gsub(editor,"%%d",linenumber)
+-- editor = gsub(editor,"%%filename%%",filename)
+-- editor = gsub(editor,"%%linenumber%%",linenumber)
+-- logs.report("system","starting editor: %s",editor)
+-- os.execute(editor)
+-- end
+-- end)
+
+implement { name = "showtrackers", public = true, protected = true, actions = trackers.show }
+implement { name = "enabletrackers", public = true, protected = true, actions = trackers.enable, arguments = "optional" }
+implement { name = "disabletrackers", public = true, protected = true, actions = trackers.disable, arguments = "optional" }
+implement { name = "resettrackers", public = true, protected = true, actions = trackers.reset }
+
+implement { name = "showdirectives", public = true, protected = true, actions = directives.show }
+implement { name = "enabledirectives", public = true, protected = true, actions = directives.enable, arguments = "optional" }
+implement { name = "disabledirectives", public = true, protected = true, actions = directives.disable, arguments = "optional" }
+
+implement { name = "showexperiments", public = true, protected = true, actions = experiments.show }
+implement { name = "enableexperiments", public = true, protected = true, actions = experiments.enable, arguments = "optional" }
+implement { name = "disableexperiments", public = true, protected = true, actions = experiments.disable, arguments = "optional" }
+
+implement { name = "overloaderror", public = true, protected = true, actions = lmx.overloaderror }
+implement { name = "showlogcategories", public = true, protected = true, actions = logs.show }
+
+local debugger = utilities.debugger
+
+directives.register("system.profile",function(n)
+ luatex.registerstopactions(function()
+ debugger.disable()
+ debugger.savestats("luatex-profile.log",tonumber(n) or 0)
+ report_nl()
+ logs.report("system","profiler stopped, log saved in %a","luatex-profile.log")
+ report_nl()
+ end)
+ logs.report("system","profiler started")
+ debugger.enable()
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-deb.mkxl b/Master/texmf-dist/tex/context/base/mkxl/trac-deb.mkxl
new file mode 100644
index 00000000000..0eab24d2940
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-deb.mkxl
@@ -0,0 +1,45 @@
+%D \module
+%D [ file=trac-deb,
+%D version=2005.11.06,
+%D title=\CONTEXT\ Tracing Macros,
+%D subtitle=Debugger,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Tracing Macros / Debugger}
+
+%registerctxluafile{trac-lmx}{}
+\registerctxluafile{trac-deb}{autosuffix}
+
+\unprotect
+
+% \showtrackers % defined at the lua end
+% \enabletrackers [#1] % defined at the lua end
+% \disabletrackers [#1] % defined at the lua end
+% \resettrackers % defined at the lua end
+%
+% \showdirectives % defined at the lua end
+% \enabledirectives [#1] % defined at the lua end
+% \disabledirectives [#1] % defined at the lua end
+%
+% \showexperiments % defined at the lua end
+% \enableexperiments [#1] % defined at the lua end
+% \disableexperiments[#1] % defined at the lua end
+%
+% \overloaderror % defined at the lua end
+%
+% \showlogcategories % defined at the lua end
+
+% \enabledirectives[system.showerror]
+
+\newtoks \t_syst_terminal_data
+
+%t_syst_terminal_data {\beginlocalcontrol\clf_fetchterminaldata\endlocalcontrol}
+\t_syst_terminal_data {\clf_fetchterminaldata}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-inf.lmt b/Master/texmf-dist/tex/context/base/mkxl/trac-inf.lmt
new file mode 100644
index 00000000000..ebcd21e07af
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-inf.lmt
@@ -0,0 +1,425 @@
+if not modules then modules = { } end modules ['trac-inf'] = {
+ version = 1.001,
+ comment = "companion to trac-inf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- As we want to protect the global tables, we no longer store the timing
+-- in the tables themselves but in a hidden timers table so that we don't
+-- get warnings about assignments. This is more efficient than using rawset
+-- and rawget.
+
+local type, tonumber, select = type, tonumber, select
+local format, lower, find, gsub, rep = string.format, string.lower, string.find, string.gsub, string.rep
+local concat = table.concat
+local clock = os.gettimeofday or os.clock -- should go in environment
+
+local setmetatableindex = table.setmetatableindex
+local serialize = table.serialize
+local formatters = string.formatters
+
+statistics = statistics or { }
+local statistics = statistics
+
+statistics.enable = true
+statistics.threshold = 0.01
+
+local statusinfo, n, registered, timers = { }, 0, { }, { }
+
+setmetatableindex(timers,function(t,k)
+ local v = { timing = 0, loadtime = 0, offset = 0 }
+ t[k] = v
+ return v
+end)
+
+local function hastiming(instance)
+ return instance and timers[instance]
+end
+
+local function resettiming(instance)
+ timers[instance or "notimer"] = { timing = 0, loadtime = 0, offset = 0 }
+end
+
+local ticks = clock
+local seconds = function(n) return n or 0 end
+
+ticks = lua.getpreciseticks
+seconds = lua.getpreciseseconds
+
+local function starttiming(instance,reset)
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing
+ if reset then
+ it = 0
+ timer.loadtime = 0
+ end
+ if it == 0 then
+ timer.starttime = ticks()
+ if not timer.loadtime then
+ timer.loadtime = 0
+ end
+ end
+ timer.timing = it + 1
+end
+
+local function stoptiming(instance)
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing
+ if it > 1 then
+ timer.timing = it - 1
+ else
+ local starttime = timer.starttime
+ if starttime and starttime > 0 then
+ local stoptime = ticks()
+ local loadtime = stoptime - starttime
+ timer.stoptime = stoptime
+ timer.loadtime = timer.loadtime + loadtime
+ timer.timing = 0
+ timer.starttime = 0
+ end
+ end
+end
+
+local function benchmarktimer(instance)
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing
+ if it > 1 then
+ timer.timing = it - 1
+ else
+ local starttime = timer.starttime
+ if starttime and starttime > 0 then
+ timer.offset = ticks() - starttime
+ else
+ timer.offset = 0
+ end
+ end
+end
+
+local function elapsed(instance)
+ if type(instance) == "number" then
+ return instance
+ else
+ local timer = timers[instance or "notimer"]
+ return timer and seconds(timer.loadtime - 2*(timer.offset or 0)) or 0
+ end
+end
+
+local function currenttime(instance)
+ if type(instance) == "number" then
+ return instance
+ else
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing
+ if it > 1 then
+ -- whatever
+ else
+ local starttime = timer.starttime
+ if starttime and starttime > 0 then
+ return seconds(timer.loadtime + ticks() - starttime - 2*(timer.offset or 0))
+ end
+ end
+ return 0
+ end
+end
+
+local function elapsedtime(instance)
+ return format("%0.3f",elapsed(instance))
+end
+
+local function elapsedindeed(instance)
+ return elapsed(instance) > statistics.threshold
+end
+
+local function elapsedseconds(instance,rest) -- returns nil if 0 seconds
+ if elapsedindeed(instance) then
+ return format("%0.3f seconds %s", elapsed(instance),rest or "")
+ end
+end
+
+statistics.hastiming = hastiming
+statistics.resettiming = resettiming
+statistics.starttiming = starttiming
+statistics.stoptiming = stoptiming
+statistics.currenttime = currenttime
+statistics.elapsed = elapsed
+statistics.elapsedtime = elapsedtime
+statistics.elapsedindeed = elapsedindeed
+statistics.elapsedseconds = elapsedseconds
+statistics.benchmarktimer = benchmarktimer
+
+-- general function .. we might split this module
+
+function statistics.register(tag,fnc)
+ if statistics.enable and type(fnc) == "function" then
+ -- second load can overload:
+ local rt = registered[tag] or (#statusinfo + 1)
+ statusinfo[rt] = { tag, fnc }
+ registered[tag] = rt
+ if #tag > n then n = #tag end
+ end
+end
+
+local report = logs.reporter("mkiv lua stats")
+
+function statistics.show()
+ if statistics.enable then
+ -- this code will move
+ local register = statistics.register
+ register("used platform", function()
+ return format("%s, type: %s, binary subtree: %s",
+ os.platform or "unknown",os.type or "unknown", environment.texos or "unknown")
+ end)
+ register("used engine", function()
+ return format("%s version: %s, functionality level: %s, format id: %s, compiler: %s",
+ LUATEXENGINE, LUATEXVERSION, LUATEXFUNCTIONALITY, LUATEXFORMATID, status.used_compiler)
+ end)
+ register("tex properties", function()
+ local t = status.gethashstate()
+ local l = status.getlookupstate()
+ local m = status.gettexstate()
+ return format("%s hash slots used of %s, %s control sequences, approximate memory usage: %i MB",
+ t.top, t.max, l.ptr, m.approximate // (1024 * 1024))
+ end)
+ register("callbacks", statistics.callbacks)
+ -- so far
+ register("lua properties",function()
+ -- local hash = lua.gethashchars()
+ local mask = load([[τεχ = 1]]) and "utf" or "ascii"
+ -- return format("engine: %s %s, used memory: %s, hash chars: min(%i,40), symbol mask: %s (%s)",
+ return format("engine: %s %s, used memory: %s, symbol mask: %s (%s)",
+ "lua", LUAVERSION, statistics.memused(), mask, mask == "utf" and "τεχ" or "tex")
+ end)
+ register("runtime",statistics.runtime)
+ logs.newline() -- initial newline
+ for i=1,#statusinfo do
+ local s = statusinfo[i]
+ local r = s[2]()
+ if r then
+ report("%s: %s",s[1],r)
+ end
+ end
+ -- logs.newline() -- final newline
+ statistics.enable = false
+ end
+end
+
+function statistics.memused() -- no math.round yet -)
+ local round = math.round or math.floor
+ local luastate = status.getluastate()
+ return format("%s MB, ctx: %s MB, max: %s MB",
+ round(collectgarbage("count")//1024),
+ round(luastate.state_bytes//1048576),
+ luastate.state_bytes_max and round(luastate.state_bytes_max//1048576) or "unknown"
+ )
+end
+
+starttiming(statistics)
+
+function statistics.formatruntime(runtime) -- indirect so it can be overloaded and
+ return format("%s seconds", runtime) -- indeed that happens in cure-uti.lua
+end
+
+function statistics.runtime()
+ stoptiming(statistics)
+ -- stoptiming(statistics) -- somehow we can start the timer twice, but where
+ local runtime = lua.getruntime and lua.getruntime() or elapsedtime(statistics)
+ return statistics.formatruntime(runtime)
+end
+
+local report = logs.reporter("system")
+
+function statistics.timed(action,all)
+ starttiming("run")
+ action()
+ stoptiming("run")
+ local runtime = tonumber(elapsedtime("run"))
+ if all then
+ local alltime = tonumber(lua.getruntime and lua.getruntime() or elapsedtime(statistics))
+ if alltime and alltime > 0 then
+ report("total runtime: %0.3f seconds of %0.3f seconds",runtime,alltime)
+ return
+ end
+ end
+ report("total runtime: %0.3f seconds",runtime)
+end
+
+-- goodie
+
+function statistics.tracefunction(base,tag,...)
+ for i=1,select("#",...) do
+ local name = select(i,...)
+ local stat = { }
+ local func = base[name]
+ setmetatableindex(stat,function(t,k) t[k] = 0 return 0 end)
+ base[name] = function(n,k,v) stat[k] = stat[k] + 1 return func(n,k,v) end
+ statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end)
+ end
+end
+
+-- now here
+
+status.iocodes = setmetatableindex(tex.getiovalues(), function() return "unknown" end)
+
+local report = logs.reporter("system")
+
+function statistics.showusage(when)
+ local s = status.list()
+ local c = status.getcallbackstate() -- status.callbacks
+ local m = mplib.getcallbackstate()
+ local b = backends.getcallbackstate()
+ --
+ local estatus = s.expandstate
+ local tstatus = s.texstate
+ local lstatus = s.luastate
+ local rstatus = s.readstate
+ --
+ local pstatus = logs.private.getpagetiming()
+ --
+ local iocode = status.iocodes[rstatus.iocode]
+ --
+ local mpinstances, mpmemory = metapost.getstatistics(true)
+ --
+ report("")
+ if when == "finish" then
+ report("status after finishing run")
+ else
+ report("status after shipping out page %s",tex.getcount("realpageno"))
+ end
+ report("")
+ local list = {
+ "stringstate", "poolstate", "hashstate", "lookupstate",
+ "nodestate", "extrastate", "tokenstate",
+ "bufferstate", "inputstate", "filestate",
+ "neststate", "parameterstate", "savestate",
+ "fontstate", "languagestate", "markstate", "sparsestate",
+ }
+ local fields = { "max", "min", "set", "stp", false, "mem", "all", false, "ini", "ptr", "top" }
+ local line = rep("-",190)
+ do
+ local t = { }
+ for i=1,#list do
+ t[i] = gsub(list[i],"state","")
+ end
+ report("%w%s",2,line)
+ report("%w%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s",5,unpack(t))
+ report("%w%s",2,line)
+ end
+ for i=1,#fields do
+ local f = fields[i]
+ if f then
+ local t = { }
+ for i=1,#list do
+ local n = s[list[i]][f]
+ t[i] = n < 0 and formatters["%w"](11) or formatters["%11i"](n)
+ end
+ report(" %3s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s",f,unpack(t))
+ else
+ report("")
+ end
+ end
+ report("%w%s",2,line)
+ report("")
+ report(" current input type : %s", iocode)
+ if iocode == "file" then
+ report(" current file name : %s", rstatus.filename or "")
+ report(" current line number : %s", rstatus.linenumber)
+ end
+ report("")
+ report(" approximate memory : %s (%s MB)", tstatus.approximate, tstatus.approximate // 1048576)
+ report("")
+ report(" expansion depth : min: %s, max: %s, set: %s, top: %s", estatus.min, estatus.max, estatus.set, estatus.top)
+ report("")
+ report(" luabytecode registers : %s", lstatus.bytecodes)
+ report(" luabytecode bytes : %s (%s MB)", lstatus.bytecode_bytes, lstatus.bytecode_bytes // 1048576)
+ report(" luastate bytes now : %s (%s MB)", lstatus.state_bytes, lstatus.state_bytes // 1048576)
+ report(" luastate bytes max : %s (%s MB)", lstatus.state_bytes_max, lstatus.state_bytes_max // 1048576)
+ report("")
+ report(" file callbacks : %s", c.file)
+ report(" saved callbacks : %s", c.saved)
+ report(" direct callbacks : %s", c.direct)
+ report(" function callbacks : %s", c["function"])
+ report(" value callbacks : %s", c.value)
+ report(" message callbacks : %s", c.message)
+ report(" bytecode callbacks : %s", c.bytecode)
+ report("")
+ report(" mp instances : %s", mpinstances)
+ report(" mp estimated memory : %s (%s MB)", mpmemory, mpmemory // 1048576)
+ report(" mp file callbacks : %s", m.file)
+ report(" mp text callbacks : %s", m.text)
+ report(" mp script callbacks : %s", m.script)
+ report(" mp log callbacks : %s", m.log)
+ report("")
+ report(" total callbacks : %s", c.count)
+ report(" mp total callbacks : %s", m.count)
+ report(" backend callbacks : %s", b.count)
+ report("")
+ report(" page numbers : realpage %s, userpage %s, subpage %s",pstatus.page.real,pstatus.page.user,pstatus.page.sub)
+ report(" page timing : total %0.03f, page %0.03f, average %0.03f",pstatus.time.elapsed,pstatus.time.page,pstatus.time.average)
+ report("")
+end
+
+function statistics.showmemory(when)
+ local s = status.list()
+ --
+ report("")
+ report("memory configuration")
+ report("")
+ local list = {
+ "stringstate", "poolstate", "hashstate", "lookupstate",
+ "nodestate", "tokenstate",
+ "bufferstate", "inputstate", "filestate",
+ "neststate", "parameterstate", "savestate",
+ "fontstate", "languagestate", "markstate",
+ }
+ local fields = { "max", "min", "set", "stp" }
+ local line = rep("-",168)
+ do
+ local t = { }
+ for i=1,#list do
+ t[i] = gsub(list[i],"state","")
+ end
+ report("%w%s",2,line)
+ report("%w%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s",5,unpack(t))
+ report("%w%s",2,line)
+ end
+ for i=1,#fields do
+ local f = fields[i]
+ if f then
+ local t = { }
+ for i=1,#list do
+ local n = s[list[i]][f]
+ t[i] = n < 0 and formatters["%w"](11) or formatters["%11i"](n)
+ end
+ report(" %3s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s",f,unpack(t))
+ else
+ report("")
+ end
+ end
+ report("%w%s",2,line)
+ report("")
+end
+
+local registered = false
+local enabled = false
+
+trackers.register("system.usage", function(v)
+ if v and not registered then
+ logs.private.enablepagetiming()
+ if v ~= "summary" then
+ luatex.registerpageactions(function()
+ if enabled then
+ statistics.showusage("page")
+ end
+ end)
+ end
+ luatex.registerstopactions(function()
+ if enabled then
+ statistics.showusage("finish")
+ end
+ end)
+ registered = true
+ end
+ enabled = v
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-jus.mkxl b/Master/texmf-dist/tex/context/base/mkxl/trac-jus.mkxl
new file mode 100644
index 00000000000..3375e5ee76f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-jus.mkxl
@@ -0,0 +1,24 @@
+%D \module
+%D [ file=trac-jus,
+%D version=2012.11.27,
+%D title=\CONTEXT\ Tracing Macros,
+%D subtitle=Justification,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Tracing Macros / Justification}
+
+\registerctxluafile{trac-jus}{}
+
+\unprotect
+
+\definesystemattribute [justification] [public]
+
+\permanent\protected\def\showjustification{\clf_showjustification} % currently no argument (default 1), todo: public implementor
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-tex.mkxl b/Master/texmf-dist/tex/context/base/mkxl/trac-tex.mkxl
new file mode 100644
index 00000000000..13c1a047f76
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-tex.mkxl
@@ -0,0 +1,70 @@
+%D \module
+%D [ file=trac-tex,
+%D version=2009.07.20,
+%D title=\CONTEXT\ Tracking Macros,
+%D subtitle=\TEX,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Tracking Macros / TeX}
+
+\registerctxluafile{trac-tex}{}
+
+%D All tracing flags at the \TEX\ end will be redone this way so
+%D that we have a similar mechanism for \TEX\ and \LUA. Also, the
+%D currently used if's might become conditionals.
+
+\unprotect
+
+% \def\enabletextracerwhatever {...}
+% \def\disabletextracerwhatever{...}
+
+\mutable\let\alltextracers\empty % so that we can report available tracers
+
+\permanent\protected\def\installtextracer#1%
+ {\addtocommalist{#1}\alltextracers}
+
+\permanent\protected\def\enabletextracers [#1]{\processcommalist[#1]\syst_textracers_enable }
+\permanent\protected\def\disabletextracers[#1]{\processcommalist[#1]\syst_textracers_disable}
+
+\def\syst_textracers_enable #1{\begincsname enabletracer#1\endcsname}
+\def\syst_textracers_disable#1{\begincsname disabletracer#1\endcsname}
+
+%D The next one is for Taco, although we can use directives as well which is the
+%D preferred way!
+
+\permanent\protected\def\nomkivstatistics{\enabledirectives[system.nostatistics]}
+
+%D This is not really a tracker but for decades it lived in my \type {cont-loc}
+%D file. I moved it here because I wanted someone else to use it. This macro is not
+%D really useful for users. I kept the \LUA\ variant in \type {cont-loc.mkiv}.
+
+\installcorenamespace{profilemacrocount}
+\installcorenamespace{profilemacromacro}
+
+\newtoks\t_syst_profile
+
+\appendtoks
+ \the\t_syst_profile
+\to \everystoptext
+
+\permanent\protected\def\profilemacro#1% an oldie, but modernized a bit
+ {\edef\p_name{\csstring#1}%
+ \ifcsname\??profilemacrocount\p_name\endcsname \else
+ \expandafter\newcount\csname\??profilemacrocount\p_name\endcsname
+ \letcsname\??profilemacromacro\p_name\endcsname#1%
+ \xtoksapp\t_syst_profile
+ {\writestatus
+ {profile}%
+ {\string#1: \noexpand\the\csname\??profilemacrocount\p_name\endcsname}}%
+ \enforced\protected\xdef#1%
+ {\global\advance\csname\??profilemacrocount\p_name\endcsname\plusone
+ \expandafter\noexpand\csname\??profilemacromacro\p_name\endcsname}%
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-tim.lmt b/Master/texmf-dist/tex/context/base/mkxl/trac-tim.lmt
new file mode 100644
index 00000000000..fcba7f1325a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-tim.lmt
@@ -0,0 +1,178 @@
+if not modules then modules = { } end modules ['trac-tim'] = {
+ version = 1.001,
+ comment = "companion to m-timing.tex",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, gsub = string.format, string.gsub
+local concat, sort = table.concat, table.sort
+local next, tonumber = next, tonumber
+
+moduledata = moduledata or { }
+local progress = moduledata.progress or { }
+moduledata.progress = progress
+
+local report_timing = logs.reporter("timing")
+
+if not nodes then nodes = { } end -- when loaded in mtxrun
+
+progress.defaultfilename = ((tex and tex.jobname) or "whatever") .. "-luatex-progress"
+
+-- storage
+
+function progress.store()
+ nodes.snapshots.takesample()
+end
+
+function progress.save(name)
+ local filename = (name or progress.defaultfilename) .. ".lut"
+ report_timing("saving data in %a",filename)
+ table.save(filename,nodes.snapshots.getsamples())
+ nodes.snapshots.resetsamples()
+end
+
+-- conversion
+
+local processed = { }
+
+local function convert(name)
+ name = name ~= "" and name or progress.defaultfilename
+ if not processed[name] then
+ local pages = 0
+ local names = { }
+ local top = { }
+ local bot = { }
+ local siz = { }
+ local paths = { }
+ local data = table.load(name .. ".lut")
+ if data then
+ pages = #data
+ if pages > 1 then
+
+ local factor = 100
+
+ local function path(tag,subtag,tagname)
+ local tagname = tag .. ": " .. subtag
+ local b, t, s = nil, nil, { }
+ for k=1,#data do
+ local v = data[k][tag]
+ v = v and v[subtag]
+ if v then
+ if type(v) == "table" then
+ set[tagname] = tonumber(v.set)
+ v = tonumber(v.top)
+ else
+ v = tonumber(v)
+ end
+ if v then
+ if b then
+ if v > t then t = v end
+ if v < b then b = v end
+ else
+ t = v
+ b = v
+ end
+ else
+ v = 0
+ end
+ else
+ v = 0
+ end
+ s[k] = v
+ end
+ if not b then
+ -- safeguard against updates
+ b = 0
+ t = 0
+ end
+ top[tagname] = gsub(format("%.3f",t),"%.000$","")
+ bot[tagname] = gsub(format("%.3f",b),"%.000$","")
+ local delta = t - b
+ if delta == 0 then
+ delta = 1
+ else
+ delta = factor/delta
+ end
+ for k=1,#s do
+ s[k] = format("(%.3f,%.3f)",k,(s[k]-b)*delta)
+ end
+ paths[tagname] = concat(s,"--")
+ return tagname
+ end
+
+ local function collect(category)
+ if data[1][category] then
+ local keys = { }
+ for k=1,#data do
+ for k, v in next, data[k][category] do
+ keys[k] = true
+ end
+ end
+ for k=1,#data do
+ local m = data[k][category]
+ for k, v in next, keys do
+ if not m[k] then m[k] = 0 end
+ end
+ end
+ for k in next, keys do
+ names[#names+1] = path(category,k)
+ end
+ end
+ end
+
+ collect("nodes")
+ collect("stock")
+ collect("memories")
+ collect("variables")
+ collect("texvariables")
+ collect("luavariables")
+ collect("texcallbacks")
+ collect("mpcallbacks")
+ collect("backendcallbacks")
+
+ pages = pages - 1 -- hm
+ end
+ end
+
+ sort(names)
+
+ processed[name] = {
+ names = names,
+ top = top,
+ bot = bot,
+ set = set,
+ pages = pages,
+ paths = paths,
+ }
+ end
+ return processed[name]
+end
+
+progress.convert = convert
+
+function progress.set(name,tag)
+ return convert(name).set[tag] or 0
+end
+
+function progress.bot(name,tag)
+ return convert(name).bot[tag] or 0
+end
+
+function progress.top(name,tag)
+ return convert(name).top[tag] or 0
+end
+
+function progress.pages(name,tag)
+ return convert(name).pages or 0
+end
+
+function progress.path(name,tag)
+ return convert(name).paths[tag] or "origin"
+end
+
+function progress.names(name)
+ return convert(name).names or { }
+end
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-vis.lmt b/Master/texmf-dist/tex/context/base/mkxl/trac-vis.lmt
new file mode 100644
index 00000000000..44723e38fad
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-vis.lmt
@@ -0,0 +1,1757 @@
+if not modules then modules = { } end modules ['trac-vis'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to trac-vis.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local node, nodes, attributes, tex = node, nodes, attributes, tex
+local type, tonumber, next, rawget = type, tonumber, next, rawget
+local gmatch = string.gmatch
+local formatters = string.formatters
+local round = math.round
+
+-- This module started out in the early days of mkiv and luatex with visualizing
+-- kerns related to fonts. In the process of cleaning up the visual debugger code it
+-- made sense to integrate some other code that I had laying around and replace the
+-- old supp-vis debugging code. As only a subset of the old visual debugger makes
+-- sense it has become a different implementation. Soms of the m-visual
+-- functionality will also be ported. The code is rather trivial. The caching is not
+-- really needed but saves upto 50% of the time needed to add visualization. Of
+-- course the overall runtime is larger because of color and layer processing in the
+-- backend (can be times as much) so the runtime is somewhat larger with full
+-- visualization enabled. In practice this will never happen unless one is demoing.
+
+-- todo: global switch (so no attributes)
+-- todo: maybe also xoffset, yoffset of glyph
+-- todo: inline concat (more efficient)
+-- todo: tags can also be numbers (just add to hash)
+-- todo: make a lmtx variant (a few more efficient fetchers)
+
+local nodecodes = nodes.nodecodes
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local setboth = nuts.setboth
+local setlink = nuts.setlink
+local setlist = nuts.setlist
+local setsubtype = nuts.setsubtype
+local setattr = nuts.setattr
+local setwidth = nuts.setwidth
+local setshift = nuts.setshift
+
+local getid = nuts.getid
+local getfont = nuts.getfont
+local getattr = nuts.getattr
+local getsubtype = nuts.getsubtype
+local getbox = nuts.getbox
+local getlist = nuts.getlist
+local getnext = nuts.getnext
+local getboth = nuts.getboth
+local getwhd = nuts.getwhd
+local getkern = nuts.getkern
+local getwidth = nuts.getwidth
+local getdepth = nuts.getdepth
+local getexpansion = nuts.getexpansion
+local getstate = nuts.getstate
+local getoffsets = nuts.getoffsets
+
+local isglyph = nuts.isglyph
+
+local hpack_nodes = nuts.hpack
+local vpack_nodes = nuts.vpack
+local copy_list = nuts.copy_list
+local copy_node = nuts.copy_node
+local flush_node_list = nuts.flush_list
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local apply_to_nodes = nuts.apply
+
+local hpack_string = nuts.typesetters.tohpack
+
+local texgetattribute = tex.getattribute
+local texsetattribute = tex.setattribute
+
+local setmetatableindex = table.setmetatableindex
+
+local unsetvalue = attributes.unsetvalue
+
+local current_font = font.current
+
+local fonthashes = fonts.hashes
+local chardata = fonthashes.characters
+local exheights = fonthashes.exheights
+local emwidths = fonthashes.emwidths
+local pt_factor = number.dimenfactors.pt
+
+local nodepool = nuts.pool
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+local new_hlist = nodepool.hlist
+local new_vlist = nodepool.vlist
+
+local tracers = nodes.tracers
+local visualizers = nodes.visualizers
+
+local setcolor = tracers.colors.set
+local setlistcolor = tracers.colors.setlist
+local settransparency = tracers.transparencies.set
+local setlisttransparency = tracers.transparencies.setlist
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local a_visual = attributes.private("visual")
+local a_layer = attributes.private("viewerlayer")
+
+local band = bit32.band
+local bor = bit32.bor
+
+local enableaction = nodes.tasks.enableaction
+
+-- local trace_hbox
+-- local trace_vbox
+-- local trace_vtop
+-- local trace_kern
+-- local trace_glue
+-- local trace_penalty
+-- local trace_fontkern
+-- local trace_strut
+-- local trace_whatsit
+-- local trace_user
+-- local trace_math
+-- local trace_italic
+-- local trace_discretionary
+-- local trace_expansion
+-- local trace_line
+-- local trace_space
+
+local report_visualize = logs.reporter("visualize")
+
+local modes = {
+ hbox = 0x000001,
+ vbox = 0x000002,
+ vtop = 0x000004,
+ kern = 0x000008,
+ glue = 0x000010,
+ penalty = 0x000020,
+ fontkern = 0x000040,
+ strut = 0x000080,
+ whatsit = 0x000100,
+ glyph = 0x000200,
+ simple = 0x000400,
+ simplehbox = 0x000401,
+ simplevbox = 0x000402,
+ simplevtop = 0x000404,
+ user = 0x000800,
+ math = 0x001000,
+ italic = 0x002000,
+ origin = 0x004000,
+ discretionary = 0x008000,
+ expansion = 0x010000,
+ line = 0x020000,
+ space = 0x040000,
+ depth = 0x080000,
+ marginkern = 0x100000,
+ mathlistkern = 0x200000,
+ dir = 0x400000,
+ par = 0x800000,
+}
+
+local usedfont, exheight, emwidth
+local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_marginkern, l_mathlistkern, l_italic, l_origin, l_discretionary, l_expansion, l_line, l_space, l_depth,
+ l_dir, l_whatsit
+
+local enabled = false
+local layers = { }
+
+local preset_boxes = modes.hbox + modes.vbox + modes.vtop + modes.origin
+local preset_makeup = preset_boxes
+ + modes.kern + modes.glue + modes.penalty
+local preset_all = preset_makeup
+ + modes.fontkern + modes.marginkern + modes.mathlistkern
+ + modes.whatsit + modes.glyph + modes.user + modes.math
+ + modes.dir + modes.whatsit
+
+function visualizers.setfont(id)
+ usedfont = id or current_font()
+ exheight = exheights[usedfont]
+ emwidth = emwidths[usedfont]
+end
+
+-- we can preset a bunch of bits
+
+local userrule -- bah, not yet defined: todo, delayed(nuts.rules,"userrule")
+local outlinerule -- bah, not yet defined: todo, delayed(nuts.rules,"userrule")
+local emptyrule -- bah, not yet defined: todo, delayed(nuts.rules,"userrule")
+
+local function initialize()
+ --
+ if not usedfont then
+ -- we use a narrow monospaced font -- infofont ?
+ visualizers.setfont(fonts.definers.define { name = "lmmonoltcond10regular", size = tex.sp("4pt") })
+ end
+ --
+ for mode, value in next, modes do
+ local tag = formatters["v_%s"](mode)
+ attributes.viewerlayers.define {
+ tag = tag,
+ title = formatters["visualizer %s"](mode),
+ visible = "start",
+ editable = "yes",
+ printable = "yes"
+ }
+ layers[mode] = attributes.viewerlayers.register(tag,true)
+ end
+ l_hbox = layers.hbox
+ l_vbox = layers.vbox
+ l_vtop = layers.vtop
+ l_glue = layers.glue
+ l_kern = layers.kern
+ l_penalty = layers.penalty
+ l_fontkern = layers.fontkern
+ l_strut = layers.strut
+ l_whatsit = layers.whatsit
+ l_glyph = layers.glyph
+ l_user = layers.user
+ l_math = layers.math
+ l_italic = layers.italic
+ l_marginkern = layers.marginkern
+ l_mathlistkern = layers.mathlistkern
+ l_origin = layers.origin
+ l_discretionary = layers.discretionary
+ l_expansion = layers.expansion
+ l_line = layers.line
+ l_space = layers.space
+ l_depth = layers.depth
+ l_dir = layers.dir
+ l_par = layers.par
+ --
+ if not userrule then
+ userrule = nuts.rules.userrule
+ end
+ --
+ if not outlinerule then
+ outlinerule = nuts.pool.outlinerule
+ end
+ --
+ if not emptyrule then
+ emptyrule = nuts.pool.emptyrule
+ end
+ initialize = false
+end
+
+local function enable()
+ if initialize then
+ initialize()
+ end
+ enableaction("shipouts","nodes.visualizers.handler")
+ report_visualize("enabled")
+ enabled = true
+ tex.setcount("global","c_syst_visualizers_state",1) -- so that we can optimize at the tex end
+end
+
+local function setvisual(n,a,what,list) -- this will become more efficient when we have the bit lib linked in
+ if not n or n == "reset" then
+ return unsetvalue
+ elseif n == true or n == "makeup" then
+ if not a or a == 0 or a == unsetvalue then
+ a = preset_makeup
+ else
+ a = bor(a,preset_makeup)
+ end
+ elseif n == "boxes" then
+ if not a or a == 0 or a == unsetvalue then
+ a = preset_boxes
+ else
+ a = bor(a,preset_boxes)
+ end
+ elseif n == "all" then
+ if what == false then
+ return unsetvalue
+ elseif not a or a == 0 or a == unsetvalue then
+ a = preset_all
+ else
+ a = bor(a,preset_all)
+ end
+ else
+ for s in gmatch(n,"[a-z]+") do
+ local m = modes[s]
+ if not m then
+ -- go on
+ elseif not a or a == 0 or a == unsetvalue then
+ a = m
+ else
+ a = bor(a,m)
+ end
+ end
+ end
+ if not a or a == 0 or a == unsetvalue then
+ return unsetvalue
+ elseif not enabled then -- must happen at runtime (as we don't store layers yet)
+ enable()
+ end
+ return a
+end
+
+function nuts.setvisual(n,mode)
+ setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true))
+end
+
+function nuts.setvisuals(n,mode) -- currently the same
+ setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true,true))
+end
+
+-- fast setters
+
+do
+
+ local cached = setmetatableindex(function(t,k)
+ if k == true then
+ return texgetattribute(a_visual)
+ elseif not k then
+ t[k] = unsetvalue
+ return unsetvalue
+ else
+ local v = setvisual(k)
+ t[k] = v
+ return v
+ end
+ end)
+
+ -- local function applyvisuals(n,mode)
+ -- local a = cached[mode]
+ -- apply_to_nodes(n,function(n) setattr(n,a_visual,a) end)
+ -- end
+
+ local a = unsetvalue
+
+ local f = function(n) setattr(n,a_visual,a) end
+
+ local function applyvisuals(n,mode)
+ a = cached[mode]
+ apply_to_nodes(n,f)
+ end
+
+ nuts.applyvisuals = applyvisuals
+
+ function nodes.applyvisuals(n,mode)
+ applyvisuals(tonut(n),mode)
+ end
+
+ function visualizers.attribute(mode)
+ return cached[mode]
+ end
+
+ visualizers.attributes = cached
+
+end
+
+function nuts.copyvisual(n,m)
+ setattr(n,a_visual,getattr(m,a_visual))
+end
+
+function visualizers.setvisual(n)
+ texsetattribute(a_visual,setvisual(n,texgetattribute(a_visual)))
+end
+
+function visualizers.setlayer(n)
+ texsetattribute(a_layer,layers[n] or unsetvalue)
+end
+
+local function set(mode,v)
+ texsetattribute(a_visual,setvisual(mode,texgetattribute(a_visual),v))
+end
+
+for mode, value in next, modes do
+ trackers.register(formatters["visualizers.%s"](mode), function(v) set(mode,v) end)
+end
+
+local fraction = 10
+local normalize = true
+
+trackers .register("visualizers.reset", function(v) set("reset", v) end)
+trackers .register("visualizers.all", function(v) set("all", v) end)
+trackers .register("visualizers.makeup", function(v) set("makeup",v) end)
+trackers .register("visualizers.boxes", function(v) set("boxes", v) end)
+directives.register("visualizers.fraction", function(v) fraction = (v and tonumber(v)) or (v == "more" and 5) or 10 end)
+
+local c_positive = "trace:b"
+local c_negative = "trace:r"
+local c_zero = "trace:g"
+local c_text = "trace:s"
+local c_space = "trace:y"
+local c_space_x = "trace:m"
+local c_skip_a = "trace:c"
+local c_skip_b = "trace:m"
+local c_glyph = "trace:o"
+local c_ligature = "trace:s"
+local c_white = "trace:w"
+----- c_math = "trace:s"
+----- c_origin = "trace:o"
+----- c_discretionary = "trace:d"
+----- c_expansion = "trace:o"
+local c_depth = "trace:o"
+local c_indent = "trace:s"
+
+local c_positive_d = "trace:db"
+local c_negative_d = "trace:dr"
+local c_zero_d = "trace:dg"
+local c_text_d = "trace:ds"
+local c_space_d = "trace:dy"
+local c_space_x_d = "trace:dm"
+local c_skip_a_d = "trace:dc"
+local c_skip_b_d = "trace:dm"
+local c_glyph_d = "trace:do"
+local c_ligature_d = "trace:ds"
+local c_white_d = "trace:dw"
+local c_math_d = "trace:dr"
+local c_origin_d = "trace:do"
+local c_discretionary_d = "trace:dd"
+----- c_expansion_d = "trace:do"
+----- c_depth_d = "trace:do"
+----- c_indent_d = "trace:ds"
+
+-- we can just paste verbatim together .. no typesetting needed
+
+local function sometext(str,layer,color,textcolor,lap,variant)
+ local text = hpack_string(str,usedfont)
+ local size = getwidth(text)
+ local rule = new_rule(size,2*exheight,exheight/2)
+ local kern = new_kern(-size)
+ if color then
+ setcolor(rule,color)
+ end
+ if textcolor then
+ setlistcolor(getlist(text),textcolor)
+ end
+ local info = setlink(rule,kern,text)
+ setlisttransparency(info,c_zero)
+ info = hpack_nodes(info)
+ local width = getwidth(info)
+ if variant then
+ nuts.setoffsets(info,0,variant*exheight)
+ end
+ if lap then
+ info = new_hlist(setlink(new_kern(-width),info))
+ else
+ info = new_hlist(info) -- a bit overkill: double wrapped
+ end
+ if layer then
+ setattr(info,a_layer,layer)
+ end
+ return info, width
+end
+
+local function someblob(str,layer,color,textcolor,width)
+ local text = hpack_string(str,usedfont)
+ local size = getwidth(text)
+ local rule = new_rule(width,2*exheight,exheight/2)
+ local kern = new_kern(-width + (width-size)/2)
+ if color then
+ setcolor(rule,color)
+ end
+ if textcolor then
+ setlistcolor(getlist(text),textcolor)
+ end
+ local info = setlink(rule,kern,text)
+ setlisttransparency(info,c_zero)
+ info = hpack_nodes(info)
+ local width = getwidth(info)
+ info = new_hlist(info)
+ if layer then
+ setattr(info,a_layer,layer)
+ end
+ return info, width
+end
+
+local caches = setmetatableindex("table")
+
+local fontkern, italickern, marginkern, mathlistkern do
+
+ local f_cache = caches["fontkern"]
+ local i_cache = caches["italickern"]
+ local m_cache = caches["marginkern"]
+ local l_cache = caches["mathlistkern"]
+
+ local function somekern(head,current,cache,color,layer)
+ local width = getkern(current)
+ local extra = getexpansion(current)
+ local kern = width + extra
+ local info = cache[kern]
+ if not info then
+ local text = hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont)
+ local rule = new_rule(emwidth/fraction,6*exheight,2*exheight)
+ local list = getlist(text)
+ if kern > 0 then
+ setlistcolor(list,c_positive_d)
+ elseif kern < 0 then
+ setlistcolor(list,c_negative_d)
+ else
+ setlistcolor(list,c_zero_d)
+ end
+ setlisttransparency(list,color)
+ setcolor(rule,color)
+ settransparency(rule,color)
+ setshift(text,-5 * exheight)
+ info = new_hlist(setlink(rule,text))
+ setattr(info,a_layer,layer)
+ f_cache[kern] = info
+ end
+ head = insert_node_before(head,current,copy_list(info))
+ return head, current
+ end
+
+ fontkern = function(head,current)
+ return somekern(head,current,f_cache,c_text_d,l_fontkern)
+ end
+
+ italickern = function(head,current)
+ return somekern(head,current,i_cache,c_glyph_d,l_italic)
+ end
+
+ marginkern = function(head,current)
+ return somekern(head,current,m_cache,c_glyph_d,l_marginkern)
+ end
+
+ mathlistkern = function(head,current)
+ return somekern(head,current,l_cache,c_glyph_d,l_mathlistkern)
+ end
+
+end
+
+local glyphexpansion do
+
+ local f_cache = caches["glyphexpansion"]
+
+ glyphexpansion = function(head,current)
+ local extra = getexpansion(current)
+ if extra and extra ~= 0 then
+ extra = extra / 1000
+ local info = f_cache[extra]
+ if not info then
+ local text = hpack_string(round(extra),usedfont)
+ local rule = new_rule(emwidth/fraction,exheight,2*exheight)
+ local list = getlist(text)
+ if extra > 0 then
+ setlistcolor(list,c_positive_d)
+ elseif extra < 0 then
+ setlistcolor(list,c_negative_d)
+ end
+ setlisttransparency(list,c_text_d)
+ setcolor(rule,c_text_d)
+ settransparency(rule,c_text_d)
+ setshift(text,1.5 * exheight)
+ info = new_hlist(setlink(rule,text))
+ setattr(info,a_layer,l_expansion)
+ f_cache[extra] = info
+ end
+ head = insert_node_before(head,current,copy_list(info))
+ return head, current
+ end
+ return head, current
+ end
+
+end
+
+local kernexpansion do
+
+ local f_cache = caches["kernexpansion"]
+
+ -- in mkiv we actually need to reconstruct but let's not do that now
+
+ kernexpansion = function(head,current)
+ local extra = getexpansion(current)
+ if extra ~= 0 then
+ extra = extra / 1000
+ local info = f_cache[extra]
+ if not info then
+ local text = hpack_string(round(extra),usedfont)
+ local rule = new_rule(emwidth/fraction,exheight,4*exheight)
+ local list = getlist(text)
+ if extra > 0 then
+ setlistcolor(list,c_positive_d)
+ elseif extra < 0 then
+ setlistcolor(list,c_negative_d)
+ end
+ setlisttransparency(list,c_text_d)
+ setcolor(rule,c_text_d)
+ settransparency(rule,c_text_d)
+ setshift(text,3.5 * exheight)
+ info = new_hlist(setlink(rule,text))
+ setattr(info,a_layer,l_expansion)
+ f_cache[extra] = info
+ end
+ head = insert_node_before(head,current,copy_list(info))
+ return head, current
+ end
+ return head, current
+ end
+
+end
+
+local whatsit do
+
+ local whatsitcodes = nodes.whatsitcodes
+ local w_cache = caches["whatsit"]
+
+ local tags = {
+ [whatsitcodes.open] = "OPN",
+ [whatsitcodes.write] = "WRI",
+ [whatsitcodes.close] = "CLS",
+ -- [whatsitcodes.special] = "SPE",
+ [whatsitcodes.latelua] = "LUA",
+ [whatsitcodes.savepos] = "POS",
+ [whatsitcodes.userdefined] = "USR",
+ [whatsitcodes.literal] = "LIT",
+ [whatsitcodes.setmatrix] = "MAT",
+ [whatsitcodes.save] = "SAV",
+ [whatsitcodes.restore] = "RES",
+ [whatsitcodes.startscaling] = "+SCA",
+ [whatsitcodes.stopscaling] = "-SCA",
+ [whatsitcodes.startrotation] = "+ROT",
+ [whatsitcodes.stoprotation] = "-ROT",
+ [whatsitcodes.startmirroring] = "+MIR",
+ [whatsitcodes.stopmirroring] = "-MIR",
+ [whatsitcodes.startclipping] = "+CLP",
+ [whatsitcodes.stopclipping] = "-CLP",
+ [whatsitcodes.startmatrix] = "+MAT",
+ [whatsitcodes.stopmatrix] = "-MAT",
+ [whatsitcodes.setstate] = "SET", -- can't happen because these are added after visualizing
+ }
+
+ whatsit = function(head,current)
+ local what = getsubtype(current)
+ local info = w_cache[what]
+ if info then
+ -- print("hit whatsit")
+ else
+ info = sometext(formatters["W:%s"](tags[what] or what),usedfont,nil,c_white)
+ setattr(info,a_layer,l_whatsit)
+ w_cache[what] = info
+ end
+ head, current = insert_node_after(head,current,copy_list(info))
+ return head, current
+ end
+
+end
+
+local dir, par do
+
+ local dircodes = nodes.dircodes
+ local dirvalues = nodes.dirvalues
+
+ local cancel_code = dircodes.cancel
+ local l2r_code = dirvalues.l2r
+ local r2l_code = dirvalues.r2l
+
+ local d_cache = caches["dir"]
+
+ local getdirection = nuts.getdirection
+
+ local tags = {
+ l2r = "L2R",
+ r2l = "R2L",
+ cancel = "CAN",
+ par = "PAR",
+ }
+
+ par = function(head,current)
+ local what = "par" -- getsubtype(current)
+ local info = d_cache[what]
+ if info then
+ -- print("hit par")
+ else
+ info = sometext(formatters["L:%s"](what),usedfont,nil,c_white)
+ setattr(info,a_layer,l_dir)
+ d_cache[what] = info
+ end
+ return head, current
+ end
+
+ dir = function(head,current)
+ local what = getsubtype(current)
+ if what == cancelcode then
+ what = "cancel"
+ elseif getdirection(current) == r2l_code then
+ what = "r2l"
+ else
+ what = "l2r"
+ end
+ local info = d_cache[what]
+ if info then
+ -- print("hit dir")
+ else
+ info = sometext(formatters["D:%s"](what),usedfont,nil,c_white)
+ setattr(info,a_layer,l_dir)
+ d_cache[what] = info
+ end
+ return head, current
+ end
+
+end
+
+local user do
+
+ local u_cache = caches["user"]
+
+ user = function(head,current)
+ local what = getsubtype(current)
+ local info = u_cache[what]
+ if info then
+ -- print("hit user")
+ else
+ info = sometext(formatters["U:%s"](what),usedfont)
+ setattr(info,a_layer,l_user)
+ u_cache[what] = info
+ end
+ head, current = insert_node_after(head,current,copy_list(info))
+ return head, current
+ end
+
+end
+
+local math do
+
+ local mathcodes = nodes.mathcodes
+ local m_cache = {
+ beginmath = caches["bmath"],
+ endmath = caches["emath"],
+ }
+ local tags = {
+ beginmath = "B",
+ endmath = "E",
+ }
+
+ math = function(head,current)
+ local what = getsubtype(current)
+ local tag = mathcodes[what]
+ local skip = getkern(current) + getwidth(current) -- surround
+ local info = m_cache[tag][skip]
+ if info then
+ -- print("hit math")
+ else
+ local text, width = sometext(formatters["M:%s"](tag and tags[tag] or what),usedfont,nil,c_math_d)
+ local rule = new_rule(skip,-655360/fraction,2*655360/fraction)
+ setcolor(rule,c_math_d)
+ settransparency(rule,c_math_d)
+ setattr(rule,a_layer,l_math)
+ if tag == "beginmath" then
+ info = new_hlist(setlink(new_glue(-skip),rule,new_glue(-width),text))
+ else
+ info = new_hlist(setlink(new_glue(-skip),rule,new_glue(-skip),text))
+ end
+ setattr(info,a_layer,l_math)
+ m_cache[tag][skip] = info
+ end
+ head, current = insert_node_after(head,current,copy_list(info))
+ return head, current
+ end
+
+end
+
+local ruleddepth do
+
+ ruleddepth = function(current,wd,ht,dp)
+ local wd, ht, dp = getwhd(current)
+ if dp ~= 0 then
+ local rule = new_rule(wd,0,dp)
+ setcolor(rule,c_depth)
+ settransparency(rule,c_zero)
+ setattr(rule,a_layer,l_depth)
+ setlist(current,setlink(rule,new_kern(-wd),getlist(current)))
+ end
+ end
+
+end
+
+local ruledbox do
+
+ local b_cache = caches["box"]
+ local o_cache = caches["origin"]
+
+ local getshift = nuts.getshift
+
+ setmetatableindex(o_cache,function(t,size)
+ local rule = new_rule(2*size,size,size)
+ local origin = hpack_nodes(rule)
+ setcolor(rule,c_origin_d)
+ settransparency(rule,c_origin_d)
+ setattr(rule,a_layer,l_origin)
+ t[size] = origin
+ return origin
+ end)
+
+ ruledbox = function(head,current,vertical,layer,what,simple,previous,trace_origin,parent)
+ local wd, ht, dp = getwhd(current)
+ local force_origin = wd == 0 or (dp + ht) == 0
+ local shift = getshift(current)
+ local next = getnext(current)
+ local prev = previous
+ setboth(current)
+ local linewidth = emwidth/fraction
+ local size = 2*linewidth
+ local this
+ if not simple then
+ this = b_cache[what]
+ if not this then
+ local text = hpack_string(what,usedfont)
+ this = setlink(new_kern(-getwidth(text)),text)
+ setlisttransparency(this,c_text)
+ this = new_hlist(this)
+ b_cache[what] = this
+ end
+ end
+ -- we need to trigger the right mode (else sometimes no whatits)
+ local info = setlink(
+ this and copy_list(this) or nil,
+ (force_origin and emptyrule(wd,ht,dp)) -- we accept some overhead
+ or (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth))
+ or userrule {
+ width = wd,
+ height = ht,
+ depth = dp,
+ line = linewidth,
+ type = "box",
+ dashed = 3*size,
+ }
+ )
+ --
+ setlisttransparency(info,c_text)
+ info = new_hlist(info) -- important
+ --
+ setattr(info,a_layer,layer)
+ if vertical then
+ if not force_origin and shift == 0 then
+ info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info)
+ elseif trace_origin or force_origin then
+ local size = 2*size
+ local origin = o_cache[size]
+ origin = copy_list(origin)
+ if getid(parent) == vlist_code then
+ setshift(origin,-shift)
+ info = setlink(current,new_kern(-size),origin,new_kern(-size-dp),info)
+ else
+ -- todo .. i need an example
+ info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info)
+ end
+ setshift(current,0)
+ else
+ info = setlink(current,new_dp ~= 0 and new_kern(-dp) or nil,info)
+ setshift(current,0)
+ end
+ info = new_vlist(info,wd,ht,dp,shift)
+ else
+ if not force_origin and shift == 0 then
+ info = setlink(current,new_kern(-wd),info)
+ elseif trace_origin or force_origin then
+ local size = 2*size
+ local origin = o_cache[size]
+ origin = copy_list(origin)
+ if getid(parent) == vlist_code then
+ info = setlink(current,new_kern(-wd-size-shift),origin,new_kern(-size+shift),info)
+ else
+ setshift(origin,-shift)
+ info = setlink(current,new_kern(-wd-size),origin,new_kern(-size),info)
+ end
+ setshift(current,0)
+ else
+ info = setlink(current,new_kern(-wd),info)
+ setshift(current,0)
+ end
+ info = new_hlist(info,wd,ht,dp,shift)
+ end
+ if next then
+ setlink(info,next)
+ end
+ if prev and prev > 0 then
+ setlink(prev,info)
+ end
+ if head == current then
+ return info, info
+ else
+ return head, info
+ end
+ -- else
+ -- return head, current
+ -- end
+ end
+
+end
+
+local ruledglyph do
+
+ -- see boundingbox feature .. maybe a pdf stream is more efficient, after all we
+ -- have a frozen color anyway or i need a more detailed cache .. below is a more
+ -- texie approach
+
+ ruledglyph = function(head,current,previous) -- wrong for vertical glyphs
+ local wd = getwidth(current)
+ if wd ~= 0 then
+ local wd, ht, dp = getwhd(current)
+ local next = getnext(current)
+ local prev = previous
+ setboth(current)
+ local linewidth = emwidth/(2*fraction)
+ local x_offset, y_offset, l_margin, r_margin, raise = getoffsets(current)
+ local info = setlink((dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule {
+ width = wd,
+ height = ht,
+ depth = dp,
+ line = linewidth,
+ type = "box",
+ },new_kern(-wd))
+ local c, f = isglyph(current)
+ local char = chardata[f][c]
+ if char and type(char.unicode) == "table" then -- hackery test
+ setlistcolor(info,c_ligature)
+ setlisttransparency(info,c_ligature_d)
+ else
+ setlistcolor(info,c_glyph)
+ setlisttransparency(info,c_glyph_d)
+ end
+ info = new_hlist(info)
+ setattr(info,a_layer,l_glyph)
+ local info = setlink(current,new_kern(-wd),info)
+ info = hpack_nodes(info)
+ setwidth(info,wd)
+ if next then
+ setlink(info,next)
+ end
+ if prev then
+ setlink(prev,info)
+ end
+ if head == current then
+ return info, info
+ else
+ return head, info
+ end
+ else
+ return head, current
+ end
+ end
+
+ function visualizers.setruledglyph(f)
+ ruledglyph = f or ruledglyph
+ end
+
+end
+
+local ruledglue do
+
+ local effectiveglue = nuts.effective_glue
+
+ local gluecodes = nodes.gluecodes
+
+ local userskip_code = gluecodes.userskip
+ local spaceskip_code = gluecodes.spaceskip
+ local xspaceskip_code = gluecodes.xspaceskip
+ local zerospaceskip_code = gluecodes.zerospaceskip or gluecodes.userskip
+ -- local keepskip_code = gluecodes.keepskip or gluecodes.userskip
+ local leftskip_code = gluecodes.leftskip
+ local rightskip_code = gluecodes.rightskip
+ local lefthangskip_code = gluecodes.lefthangskip
+ local righthangskip_code = gluecodes.righthangskip
+ local parfillleftskip_code = gluecodes.parfillleftskip or parfillskip_code
+ local parfillrightskip_code = gluecodes.parfillrightskip or parfillskip_code
+ local indentskip_code = gluecodes.indentskip
+ local correctionskip_code = gluecodes.correctionskip
+
+ local g_cache_v = caches["vglue"]
+ local g_cache_h = caches["hglue"]
+ local g_cache_ls = caches["leftskip"]
+ local g_cache_rs = caches["rightskip"]
+ local g_cache_lh = caches["lefthang"]
+ local g_cache_rh = caches["righthang"]
+ local g_cache_lp = caches["parfillleftskip"]
+ local g_cache_rp = caches["parfillrightskip"]
+ local g_cache_is = caches["indentskip"]
+ local g_cache_cs = caches["correctionskip"]
+
+ local tags = {
+ -- [userskip_code] = "US",
+ [gluecodes.lineskip] = "LI",
+ [gluecodes.baselineskip] = "BS",
+ [gluecodes.parskip] = "PS",
+ [gluecodes.abovedisplayskip] = "DA",
+ [gluecodes.belowdisplayskip] = "DB",
+ [gluecodes.abovedisplayshortskip] = "SA",
+ [gluecodes.belowdisplayshortskip] = "SB",
+ [gluecodes.topskip] = "TS",
+ [gluecodes.splittopskip] = "ST",
+ [gluecodes.tabskip] = "AS",
+ [gluecodes.thinmuskip] = "MS",
+ [gluecodes.medmuskip] = "MM",
+ [gluecodes.thickmuskip] = "ML",
+ [gluecodes.intermathskip] = "IM",
+ [gluecodes.keepskip or 99] = "KS",
+ [gluecodes.mathskip] = "MT",
+ [gluecodes.leaders] = "NL",
+ [gluecodes.cleaders] = "CL",
+ [gluecodes.xleaders] = "XL",
+ [gluecodes.gleaders] = "GL",
+ -- true = "VS",
+ -- false = "HS",
+ [leftskip_code] = "LS",
+ [rightskip_code] = "RS",
+ [lefthangskip_code] = "LH",
+ [righthangskip_code] = "RH",
+ [spaceskip_code] = "SP",
+ [xspaceskip_code] = "XS",
+ [zerospaceskip_code] = "ZS",
+ [parfillleftskip_code] = "PL",
+ [parfillrightskip_code] = "PR",
+ [indentskip_code] = "IN",
+ [correctionskip_code] = "CS",
+ }
+
+ local stags = {
+ [lefthangskip_code] = 0.5,
+ [righthangskip_code] = 0.5,
+ [leftskip_code] = -2,
+ [rightskip_code] = -2,
+ [parfillleftskip_code] = -0.75,
+ [parfillrightskip_code] = -0.75,
+ }
+
+ -- we sometimes pass previous as we can have issues in math (not watertight for all)
+
+ ruledglue = function(head,current,vertical,parent)
+ local subtype = getsubtype(current)
+ local width = effectiveglue(current,parent)
+ local stag = normalize and stags[subtype]
+ local amount = formatters["%s:%0.3f"](tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)
+ local info = (vertical and g_cache_v or g_cache_h)[amount]
+ if info then
+ -- print("glue hit")
+ else
+ if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then
+ info = sometext(amount,l_glue,c_space)
+ elseif subtype == userskip_code then
+ if width > 0 then
+ info = sometext(amount,l_glue,c_positive)
+ elseif width < 0 then
+ info = sometext(amount,l_glue,c_negative)
+ else
+ info = sometext(amount,l_glue,c_zero)
+ end
+ elseif subtype == indentskip_code or subtype == correctionskip_code then
+ info = sometext(amount,l_glue,c_indent)
+ elseif subtype == leftskip_code then
+ info = sometext(amount,l_glue,normalize and c_space or c_skip_a,false,true,stag)
+ elseif subtype == rightskip_code then
+ info = sometext(amount,l_glue,normalize and c_space or c_skip_a,false,false,stag)
+ elseif subtype == lefthangskip_code then
+ info = sometext(amount,l_glue,normalize and c_space or c_skip_b,false,true,stag)
+ elseif subtype == righthangskip_code then
+ info = sometext(amount,l_glue,normalize and c_space or c_skip_b,false,false,stag)
+ elseif subtype == parfillleftskip_code then
+ info = sometext(amount,l_glue,c_indent,false,true,stag)
+ elseif subtype == parfillrightskip_code then
+ info = sometext(amount,l_glue,c_indent,false,false,stag)
+ else
+ info = sometext(amount,l_glue,c_skip_b)
+ end
+ (vertical and g_cache_v or g_cache_h)[amount] = info
+ end
+ ::PICKIP::
+ info = copy_list(info)
+ if vertical then
+ info = vpack_nodes(info)
+ end
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ end
+
+ -- ruledspace = function(head,current,parent)
+ -- local subtype = getsubtype(current)
+ -- if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then
+ -- local width = effectiveglue(current,parent)
+ -- local amount = formatters["%s:%0.3f"](tags[subtype] or "HS",width*pt_factor)
+ -- local info = g_cache_h[amount]
+ -- if info then
+ -- -- print("space hit")
+ -- else
+ -- info = sometext(amount,l_glue,c_space)
+ -- g_cache_h[amount] = info
+ -- end
+ -- info = copy_list(info)
+ -- head, current = insert_node_before(head,current,info)
+ -- return head, getnext(current)
+ -- else
+ -- return head, current
+ -- end
+ -- end
+
+ local g_cache_s = caches["space"]
+ local g_cache_x = caches["xspace"]
+
+ ruledspace = function(head,current,parent)
+ local subtype = getsubtype(current)
+ if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then -- not yet all space
+ local width = effectiveglue(current,parent)
+ local info
+ if subtype == spaceskip_code then
+ info = g_cache_s[width]
+ if not info then
+ info = someblob("SP",l_glue,c_space,nil,width)
+ g_cache_s[width] = info
+ end
+ else
+ info = g_cache_x[width]
+ if not info then
+ info = someblob("XS",l_glue,c_space_x,nil,width)
+ g_cache_x[width] = info
+ end
+ end
+ info = copy_list(info)
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ else
+ return head, current
+ end
+ end
+
+end
+
+local ruledkern do
+
+ local k_cache_v = caches["vkern"]
+ local k_cache_h = caches["hkern"]
+
+ ruledkern = function(head,current,vertical,mk)
+ local kern = getkern(current)
+ local cache = vertical and k_cache_v or k_cache_h
+ local info = cache[kern]
+ if not info then
+ local amount = formatters["%s:%0.3f"](vertical and "VK" or (mk and "MK") or "HK",kern*pt_factor)
+ if kern > 0 then
+ info = sometext(amount,l_kern,c_positive)
+ elseif kern < 0 then
+ info = sometext(amount,l_kern,c_negative)
+ else
+ info = sometext(amount,l_kern,c_zero)
+ end
+ cache[kern] = info
+ end
+ info = copy_list(info)
+ if vertical then
+ info = vpack_nodes(info)
+ end
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ end
+
+end
+
+local ruleditalic do
+
+ local i_cache = caches["italic"]
+
+ ruleditalic = function(head,current)
+ local kern = getkern(current)
+ local info = i_cache[kern]
+ if not info then
+ local amount = formatters["%s:%0.3f"]("IC",kern*pt_factor)
+ if kern > 0 then
+ info = sometext(amount,l_kern,c_positive)
+ elseif kern < 0 then
+ info = sometext(amount,l_kern,c_negative)
+ else
+ info = sometext(amount,l_kern,c_zero)
+ end
+ i_cache[kern] = info
+ end
+ info = copy_list(info)
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ end
+
+end
+
+local ruledmarginkern do
+
+ local m_cache = caches["marginkern"]
+
+ ruledmarginkern = function(head,current)
+ local kern = getkern(current)
+ local info = m_cache[kern]
+ if not info then
+ local amount = formatters["%s:%0.3f"]("MK",kern*pt_factor)
+ if kern > 0 then
+ info = sometext(amount,l_marginkern,c_positive)
+ elseif kern < 0 then
+ info = sometext(amount,l_marginkern,c_negative)
+ else
+ info = sometext(amount,l_marginkern,c_zero)
+ end
+ m_cache[kern] = info
+ end
+ info = copy_list(info)
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ end
+
+end
+
+local ruledmathlistkern do
+
+ local l_cache = caches["mathlistkern"]
+
+ ruledmathlistkern = function(head,current)
+ local kern = getkern(current)
+ local info = l_cache[kern]
+ if not info then
+ local amount = formatters["%s:%0.3f"]("LK",kern*pt_factor)
+ if kern > 0 then
+ info = sometext(amount,l_mathlistkern,c_positive)
+ elseif kern < 0 then
+ info = sometext(amount,l_mathlistkern,c_negative)
+ else
+ info = sometext(amount,l_mathlistkern,c_zero)
+ end
+ l_cache[kern] = info
+ end
+ info = copy_list(info)
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ end
+
+end
+
+local ruleddiscretionary do
+
+ local d_cache = caches["discretionary"]
+
+ ruleddiscretionary = function(head,current)
+ local d = d_cache[true]
+ if not the_discretionary then
+ local rule = new_rule(4*emwidth/fraction,4*exheight,exheight)
+ local kern = new_kern(-2*emwidth/fraction)
+ setlink(kern,rule)
+ setcolor(rule,c_discretionary_d)
+ settransparency(rule,c_discretionary_d)
+ setattr(rule,a_layer,l_discretionary)
+ d = new_hlist(kern)
+ d_cache[true] = d
+ end
+ insert_node_after(head,current,copy_list(d))
+ return head, current
+ end
+
+end
+
+local ruledpenalty do
+
+ local p_cache_v = caches["vpenalty"]
+ local p_cache_h = caches["hpenalty"]
+
+ local raisepenalties = false
+
+ local getpenalty = nuts.getpenalty
+
+ directives.register("visualizers.raisepenalties",function(v) raisepenalties = v end)
+
+ ruledpenalty = function(head,current,vertical)
+ local penalty = getpenalty(current)
+ local info = (vertical and p_cache_v or p_cache_h)[penalty]
+ if info then
+ -- print("penalty hit")
+ else
+ local amount = formatters["%s:%s"](vertical and "VP" or "HP",penalty)
+ if penalty > 0 then
+ info = sometext(amount,l_penalty,c_positive)
+ elseif penalty < 0 then
+ info = sometext(amount,l_penalty,c_negative)
+ else
+ info = sometext(amount,l_penalty,c_zero)
+ end
+ (vertical and p_cache_v or p_cache_h)[penalty] = info
+ end
+ info = copy_list(info)
+ if vertical then
+ info = vpack_nodes(info)
+ elseif raisepenalties then
+ setshift(info,-65536*4)
+ end
+ head, current = insert_node_before(head,current,info)
+ return head, getnext(current)
+ end
+
+end
+
+do
+
+ local disc_code = nodecodes.disc
+ local kern_code = nodecodes.kern
+ local glyph_code = nodecodes.glyph
+ local glue_code = nodecodes.glue
+ local penalty_code = nodecodes.penalty
+ local whatsit_code = nodecodes.whatsit
+ local user_code = nodecodes.user
+ local math_code = nodecodes.math
+ local hlist_code = nodecodes.hlist
+ local vlist_code = nodecodes.vlist
+ local marginkern_code = nodecodes.marginkern
+ local mathlistkern_code = nodecodes.mathlistkern
+ local dir_code = nodecodes.dir
+ local par_code = nodecodes.par
+
+ local kerncodes = nodes.kerncodes
+ local fontkern_code = kerncodes.fontkern
+ local italickern_code = kerncodes.italiccorrection
+ local leftmarginkern_code = kerncodes.leftmarginkern
+ local rightmarginkern_code = kerncodes.rightmarginkern
+ local mathlistkern_code = kerncodes.mathlistkern
+ ----- userkern_code = kerncodes.userkern
+
+ local listcodes = nodes.listcodes
+ local linelist_code = listcodes.line
+
+ local vtop_package_state = 3 -- todo: symbolic
+
+ local getleader = nuts.getleader
+ local getdisc = nuts.getdisc
+
+ local setleader = nuts.setleader
+ local setdisc = nuts.setdisc
+
+ local cache
+
+ local function visualize(head,vertical,forced,parent)
+ local trace_hbox = false
+ local trace_vbox = false
+ local trace_vtop = false
+ local trace_kern = false
+ local trace_glue = false
+ local trace_penalty = false
+ local trace_fontkern = false
+ local trace_strut = false
+ local trace_whatsit = false
+ local trace_glyph = false
+ local trace_simple = false
+ local trace_user = false
+ local trace_math = false
+ local trace_italic = false
+ local trace_origin = false
+ local trace_discretionary = false
+ local trace_expansion = false
+ local trace_line = false
+ local trace_space = false
+ local trace_depth = false
+ local trace_dir = false
+ local trace_par = false
+ local current = head
+ local previous = nil
+ local attr = unsetvalue
+ local prev_trace_fontkern = nil
+ local prev_trace_italic = nil
+ local prev_trace_marginkern = nil
+-- local prev_trace_mathlist = nil
+ local prev_trace_expansion = nil
+
+ while current do
+ local id = getid(current)
+ local a = forced or getattr(current,a_visual) or unsetvalue
+ local subtype
+ if a ~= attr then
+ prev_trace_fontkern = trace_fontkern
+ prev_trace_italic = trace_italic
+ prev_trace_marginkern = trace_marginkern
+-- prev_trace_mathlistkern = trace_mathlistkern
+ prev_trace_expansion = trace_expansion
+ attr = a
+ if a == unsetvalue then
+ trace_hbox = false
+ trace_vbox = false
+ trace_vtop = false
+ trace_kern = false
+ trace_glue = false
+ trace_penalty = false
+ trace_fontkern = false
+ trace_strut = false
+ trace_whatsit = false
+ trace_glyph = false
+ trace_simple = false
+ trace_user = false
+ trace_math = false
+ trace_italic = false
+ trace_origin = false
+ trace_discretionary = false
+ trace_expansion = false
+ trace_line = false
+ trace_space = false
+ trace_depth = false
+ trace_marginkern = false
+ trace_mathlistkern = false
+ trace_dir = false
+ trace_par = false
+ if id == kern_code then
+ goto kern
+ else
+ goto list
+ end
+ else -- dead slow:
+ -- cache[a]()
+ trace_hbox = band(a,0x000001) ~= 0
+ trace_vbox = band(a,0x000002) ~= 0
+ trace_vtop = band(a,0x000004) ~= 0
+ trace_kern = band(a,0x000008) ~= 0
+ trace_glue = band(a,0x000010) ~= 0
+ trace_penalty = band(a,0x000020) ~= 0
+ trace_fontkern = band(a,0x000040) ~= 0
+ trace_strut = band(a,0x000080) ~= 0
+ trace_whatsit = band(a,0x000100) ~= 0
+ trace_glyph = band(a,0x000200) ~= 0
+ trace_simple = band(a,0x000400) ~= 0
+ trace_user = band(a,0x000800) ~= 0
+ trace_math = band(a,0x001000) ~= 0
+ trace_italic = band(a,0x002000) ~= 0
+ trace_origin = band(a,0x004000) ~= 0
+ trace_discretionary = band(a,0x008000) ~= 0
+ trace_expansion = band(a,0x010000) ~= 0
+ trace_line = band(a,0x020000) ~= 0
+ trace_space = band(a,0x040000) ~= 0
+ trace_depth = band(a,0x080000) ~= 0
+ trace_marginkern = band(a,0x100000) ~= 0
+ trace_mathlistkern = band(a,0x200000) ~= 0
+ trace_dir = band(a,0x400000) ~= 0
+ trace_par = band(a,0x800000) ~= 0
+ end
+ elseif a == unsetvalue then
+ goto list
+ end
+ if trace_strut then
+ setattr(current,a_layer,l_strut)
+ elseif id == glyph_code then
+ if trace_glyph then
+ head, current = ruledglyph(head,current,previous)
+ end
+ if trace_expansion then
+ head, current = glyphexpansion(head,current)
+ end
+ elseif id == disc_code then
+ if trace_discretionary then
+ head, current = ruleddiscretionary(head,current)
+ end
+ local pre, post, replace = getdisc(current)
+ if pre then
+ pre = visualize(pre,false,a,parent)
+ end
+ if post then
+ post = visualize(post,false,a,parent)
+ end
+ if replace then
+ replace = visualize(replace,false,a,parent)
+ end
+ setdisc(current,pre,post,replace)
+ elseif id == kern_code then
+ goto kern
+ elseif id == glue_code then
+ local content = getleader(current)
+ if content then
+ setleader(current,visualize(content,false,nil,parent))
+ elseif trace_glue then
+ head, current = ruledglue(head,current,vertical,parent)
+ elseif trace_space then
+ head, current = ruledspace(head,current,parent)
+ end
+ elseif id == penalty_code then
+ if trace_penalty then
+ head, current = ruledpenalty(head,current,vertical)
+ end
+ elseif id == hlist_code or id == vlist_code then
+ goto list
+ elseif id == whatsit_code then
+ if trace_whatsit then
+ head, current = whatsit(head,current)
+ end
+ elseif id == user_code then
+ if trace_user then
+ head, current = user(head,current)
+ end
+ elseif id == math_code then
+ if trace_math then
+ head, current = math(head,current)
+ end
+ elseif id == marginkern_code then
+ if trace_kern then
+ head, current = ruledkern(head,current,vertical,true)
+ end
+ elseif id == dir_code then
+ if trace_dir then
+ head, current = dir(head,current)
+ end
+ elseif id == par_code then
+ if trace_par then
+ head, current = par(head,current)
+ end
+ end
+ goto next
+ ::kern::
+ subtype = getsubtype(current)
+ if subtype == fontkern_code then
+ if trace_fontkern or prev_trace_fontkern then
+ head, current = fontkern(head,current)
+ end
+ if trace_expansion or prev_trace_expansion then
+ head, current = kernexpansion(head,current)
+ end
+ elseif subtype == italickern_code then
+ if trace_italic or prev_trace_italic then
+ head, current = italickern(head,current)
+ elseif trace_kern then
+ head, current = ruleditalic(head,current)
+ end
+ elseif subtype == leftmarginkern_code or subtype == rightmarginkern_code then
+ if trace_marginkern or prev_trace_marginkern then
+ head, current = marginkern(head,current)
+ elseif trace_kern then
+ head, current = ruledmarginkern(head,current)
+ end
+ elseif subtype == mathlistkern_code then
+ if trace_mathlist then -- or prev_trace_mathlist then
+ head, current = mathlistkern(head,current)
+ elseif trace_kern then
+ head, current = ruledmathlistkern(head,current)
+ end
+ else
+ if trace_kern then
+ head, current = ruledkern(head,current,vertical)
+ end
+ end
+ goto next;
+ ::list::
+ if id == hlist_code then
+ local content = getlist(current)
+ if content then
+ setlist(current,visualize(content,false,nil,current))
+ end
+ if trace_depth then
+ ruleddepth(current)
+ end
+ if trace_line and getsubtype(current) == linelist_code then
+ head, current = ruledbox(head,current,false,l_line,"L__",trace_simple,previous,trace_origin,parent)
+ elseif trace_hbox then
+ head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent)
+ end
+ elseif id == vlist_code then
+ local content = getlist(current)
+ local isvtop = getstate(current) == vtop_package_state
+ local tag = nil
+ local layer = nil
+ if content then
+ setlist(current,visualize(content,true,nil,current))
+ end
+ if trace_vtop then
+ if isvtop then
+ tag = "_T_"
+ layer = l_vtop
+ elseif trace_vbox then
+ tag = "__V"
+ layer = l_vbox
+ end
+ elseif trace_vbox then
+ if not isvtop then
+ tag = "__V"
+ layer = l_vbox
+ end
+ end
+ if tag then
+ head, current = ruledbox(head,current,true,layer,tag,trace_simple,previous,trace_origin,parent)
+ end
+ end
+ ::next::
+ previous = current
+ current = getnext(current)
+ end
+ return head
+ end
+
+ local function cleanup()
+ for tag, cache in next, caches do
+ for k, v in next, cache do
+ flush_node_list(v)
+ end
+ end
+ cleanup = function()
+ report_visualize("error, duplicate cleanup")
+ end
+ end
+
+ luatex.registerstopactions(cleanup)
+
+ function visualizers.handler(head)
+ if usedfont then
+ starttiming(visualizers)
+ head = visualize(head,true)
+ stoptiming(visualizers)
+ return head, true
+ else
+ return head, false
+ end
+ end
+
+ function visualizers.box(n)
+ if usedfont then
+ starttiming(visualizers)
+ local box = getbox(n)
+ if box then
+ setlist(box,visualize(getlist(box),getid(box) == vlist_code))
+ end
+ stoptiming(visualizers)
+ return head, true
+ else
+ return head, false
+ end
+ end
+
+end
+
+do
+
+ local hlist_code = nodecodes.hlist
+ local vlist_code = nodecodes.vlist
+ local nextnode = nuts.traversers.node
+
+ local last = nil
+ local used = nil
+
+ local mark = {
+ "trace:1", "trace:2", "trace:3",
+ "trace:4", "trace:5", "trace:6",
+ "trace:7",
+ }
+
+ local function markfonts(list)
+ for n, id in nextnode, list do
+ if id == glyph_code then
+ local font = getfont(n)
+ local okay = used[font]
+ if not okay then
+ last = last + 1
+ okay = mark[last]
+ used[font] = okay
+ end
+ setcolor(n,okay)
+ elseif id == hlist_code or id == vlist_code then
+ markfonts(getlist(n))
+ end
+ end
+ end
+
+ function visualizers.markfonts(list)
+ last, used = 0, { }
+ markfonts(type(n) == "number" and getlist(getbox(n)) or n)
+ end
+
+end
+
+statistics.register("visualization time",function()
+ if enabled then
+ -- cleanup() -- in case we don't don't do it each time
+ return formatters["%s seconds"](statistics.elapsedtime(visualizers))
+ end
+end)
+
+-- interface
+
+do
+
+ local implement = interfaces.implement
+
+ implement {
+ name = "setvisual",
+ arguments = "string",
+ actions = visualizers.setvisual
+ }
+
+ implement {
+ name = "setvisuals",
+ arguments = "string",
+ actions = visualizers.setvisual
+ }
+
+ implement {
+ name = "getvisual",
+ arguments = "string",
+ actions = { setvisual, context }
+ }
+
+ implement {
+ name = "setvisuallayer",
+ arguments = "string",
+ actions = visualizers.setlayer
+ }
+
+ implement {
+ name = "markvisualfonts",
+ arguments = "integer",
+ actions = visualizers.markfonts
+ }
+
+ implement {
+ name = "setvisualfont",
+ arguments = "integer",
+ actions = visualizers.setfont
+ }
+
+end
+
+-- Here for now:
+
+do
+
+ local function make(str,forecolor,rulecolor,layer)
+ if initialize then
+ initialize()
+ end
+ local rule = new_rule(emwidth/fraction,exheight,4*exheight)
+ setcolor(rule,rulecolor)
+ settransparency(rule,rulecolor)
+ local info
+ if str == "" then
+ info = new_hlist(rule)
+ else
+ local text = hpack_string(str,usedfont)
+ local list = getlist(text)
+ setlistcolor(list,textcolor)
+ setlisttransparency(list,textcolor)
+ setshift(text,3.5 * exheight)
+ info = new_hlist(setlink(rule,text))
+ end
+ setattr(info,a_layer,layer)
+ return info
+ end
+
+ function visualizers.register(name,textcolor,rulecolor)
+ if rawget(layers,name) then
+ -- message
+ return
+ end
+ local cache = caches[name]
+ local layer = layers[name]
+ if not textcolor then
+ textcolor = c_text_d
+ end
+ if not rulecolor then
+ rulecolor = c_origin_d
+ end
+ return function(str)
+ if not str then
+ str = ""
+ end
+ local info = cache[str]
+ if not info then
+ info = make(str,textcolor,rulecolor,layer)
+ cache[str] = info
+ end
+ return copy_node(info)
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/trac-vis.mkxl b/Master/texmf-dist/tex/context/base/mkxl/trac-vis.mkxl
new file mode 100644
index 00000000000..dd69e0bb8d3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/trac-vis.mkxl
@@ -0,0 +1,219 @@
+%D \module
+%D [ file=trac-vis, % replaces supp-vis plus some s-* modules
+%D version=2012.06.23, % 1996.10.21,
+%D title=\CONTEXT\ Tracing Macros,
+%D subtitle=Visualization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D The old visual debugger dates from 1996 and started out as joke. In practice
+%D it's not used that often although the \type {\ruledhbox} cum suis macros come
+%D in handy when writing code. In the process of luafication some additional
+%D tracing options were tested, for instance showing font kerns. As part of the
+%D \MKIV\ cleanup the decision was made to reimplement, reintegrate and reconsider
+%D all these features. The old code is gone and the new code will get extended
+%D when needed. We still provide placeholders for some old visualization commands
+%D but they might go away.
+%D
+%D Control over what gets visualized happens with the tracker command:
+%D
+%D \enabletrackers[visualizers.*]
+%D
+%D Possible values are: \type {fontkern}, \type {kern}, \type {glue}, \type
+%D {penalty}, \type {hbox}, \type {vbox}, \type {all}, \type {reset}, \type
+%D {makeup}, \type {whatsit}, \type{glyph}.
+%D
+%D In due time some special visualzation of math will move here as well.
+
+\writestatus{loading}{ConTeXt Tracing Macros / Visualization}
+
+\registerctxluafile{trac-vis}{autosuffix,optimize}
+
+\unprotect
+
+\newconstant\c_syst_visualizers_state
+\newtoks \t_syst_visualizers_optimize
+
+% \definesystemattribute[visual][public,global] % already defined
+
+% no, but can become an option:
+%
+% \appendtoks
+% \c_attr_visual\attributeunsetvalue
+% \to \everybeforepagebody
+
+%D We only provide box visualizers as they can come in handy for testing
+%D macros. In due time we will move some of the m-visual code here too.
+
+\aliased\let\syst_visualizers_hbox \hbox
+\aliased\let\syst_visualizers_vbox \vbox
+\aliased\let\syst_visualizers_vtop \vtop
+\aliased\let\syst_visualizers_vcenter\vcenter
+\aliased\let\syst_visualizers_hpack \hpack
+\aliased\let\syst_visualizers_vpack \vpack
+\aliased\let\syst_visualizers_tpack \tpack
+
+\permanent\protected\def\ruledhbox {\syst_visualizers_hbox attr \visualattribute \clf_getvisual{simplehbox} }
+\permanent\protected\def\ruledhpack {\syst_visualizers_hpack attr \visualattribute \clf_getvisual{simplehbox} }
+\permanent\protected\def\ruledvbox {\syst_visualizers_vbox attr \visualattribute \clf_getvisual{simplevbox} }
+\permanent\protected\def\ruledvpack {\syst_visualizers_vpack attr \visualattribute \clf_getvisual{simplevbox} }
+\permanent\protected\def\ruledvtop {\syst_visualizers_vtop attr \visualattribute \clf_getvisual{simplevtop} }
+\permanent\protected\def\ruledtpack {\syst_visualizers_tpack attr \visualattribute \clf_getvisual{simplevtop} }
+\permanent\protected\def\ruledvcenter{\syst_visualizers_vcenter attr \visualattribute \clf_getvisual{simplevbox} }
+\permanent\protected\def\ruledmbox #1{\ruledhbox{\startimath#1\stopimath}}
+
+\appendtoks
+ \ifcase\c_syst_visualizers_state\else
+ \syst_visualizers_speedup
+ \fi
+\to \everyshipout
+
+\appendtoks
+ \glet\syst_visualizers_speedup\relax
+ \global\t_syst_visualizers_optimize\emptytoks
+\to \t_syst_visualizers_optimize
+
+\def\syst_visualizers_speedup{\the\t_syst_visualizers_optimize}
+
+\appendtoks
+% \enforced\global\protected\edef\ruledhbox {\syst_visualizers_hbox attr \visualattribute \number\clf_getvisual{simplehbox} }
+% \enforced\global\protected\edef\ruledhpack {\syst_visualizers_hpack attr \visualattribute \number\clf_getvisual{simplehbox} }
+% \enforced\global\protected\edef\ruledvbox {\syst_visualizers_vbox attr \visualattribute \number\clf_getvisual{simplevbox} }
+% \enforced\global\protected\edef\ruledvpack {\syst_visualizers_vpack attr \visualattribute \number\clf_getvisual{simplevbox} }
+% \enforced\global\protected\edef\ruledvtop {\syst_visualizers_vtop attr \visualattribute \number\clf_getvisual{simplevtop} }
+% \enforced\global\protected\edef\ruledtpack {\syst_visualizers_tpack attr \visualattribute \number\clf_getvisual{simplevtop} }
+% \enforced\global\protected\edef\ruledvcenter{\syst_visualizers_vcenter attr \visualattribute \number\clf_getvisual{simplevbox} }
+\to \t_syst_visualizers_optimize
+
+\permanent\tolerant\protected\def\showmakeup[#1]%
+ {\ifarguments
+ \clf_setvisual{makeup}%
+ \else
+ \clf_setvisual{#1}%
+ \fi}
+
+\permanent\protected\def\showallmakeup {\clf_setvisual{all}\showstruts}
+\permanent\protected\def\showboxes {\clf_setvisual{boxes}}
+\permanent\protected\def\showglyphs {\clf_setvisual{glyph}}
+\permanent\protected\def\showfontkerns {\clf_setvisual{fontkern}}
+\permanent\protected\def\showfontitalics {\clf_setvisual{italic}}
+\permanent\protected\def\showglyphdata {\showglyphs\showfontkerns\showfontitalics}
+\permanent\protected\def\showfontexpansion{\clf_setvisual{expansion}}
+
+\permanent\tolerant\protected\def\setvisualizerfont[#1]% somename at 4pt
+ {\begingroup
+ \iftok{#1}\emptytoks
+ \definedfont[Mono at 4pt]%
+ \else
+ \definedfont[#1]%
+ \fi
+ \clf_setvisualfont\fontid\font
+ \endgroup}
+
+\permanent\protected\def\resetvisualizers
+ {\c_attr_visual\attributeunsetvalue}
+
+%D New (these might also be a visualizers):
+
+% \definecolor[f:r:t][a=1,t=.25,r=1]
+% \definecolor[f:g:t][a=1,t=.25,g=1]
+% \definecolor[f:b:t][a=1,t=.25,b=1]
+% \definecolor[f:c:t][a=1,t=.25,c=1]
+% \definecolor[f:m:t][a=1,t=.25,m=1]
+% \definecolor[f:y:t][a=1,t=.25,y=1]
+% \definecolor[f:k:t][a=1,t=.25,s=0]
+
+\permanent\protected\def\filledhboxr{\node_backgrounds_boxes_add\hbox[f:r:t]}
+\permanent\protected\def\filledhboxg{\node_backgrounds_boxes_add\hbox[f:g:t]}
+\permanent\protected\def\filledhboxb{\node_backgrounds_boxes_add\hbox[f:b:t]}
+\permanent\protected\def\filledhboxc{\node_backgrounds_boxes_add\hbox[f:c:t]}
+\permanent\protected\def\filledhboxm{\node_backgrounds_boxes_add\hbox[f:m:t]}
+\permanent\protected\def\filledhboxy{\node_backgrounds_boxes_add\hbox[f:y:t]}
+\permanent\protected\def\filledhboxk{\node_backgrounds_boxes_add\hbox[f:k:t]}
+
+%D Overload:
+
+\protected\def\spac_struts_vide_hbox
+ {\hbox attr \visualattribute \clf_getvisual{strut} }
+
+\appendtoks
+ \normalexpanded{\protected\xdef\spac_struts_vide_hbox\expandafter{\spac_struts_vide_hbox}}%
+\to \t_syst_visualizers_optimize
+
+%D For manuals:
+
+\permanent\protected\def\markfonts#1%
+ {\dontleavehmode
+ \begingroup
+ \setbox\scratchbox\hbox{\getbuffer}%
+ \clf_markvisualfonts\scratchbox
+ \unhbox\scratchbox
+ \endgroup}
+
+% As demo:
+%
+% \startbuffer
+% \hbox {\raise1cm\hbox{raised} normal \lower1cm\hbox{lowered}}
+% \blank
+% \moveleft1cm\hbox{left}
+% \blank
+% \hbox{normal}
+% \blank
+% \moveright1cm\hbox{right}
+% \blank
+% \stopbuffer
+%
+% \start \showboxes \getbuffer \stop \blank[4*big] \getbuffer
+%
+% \startbuffer
+% \moveleft1cm\vbox{left}
+% \blank
+% \vbox{normal}
+% \blank
+% \moveright1cm\vbox{right}
+% \blank
+% \stopbuffer
+%
+% \start \showboxes \getbuffer \stop \blank[4*big] \getbuffer
+
+%D We keep this one:
+
+\permanent\protected\def\dontinterfere
+ {\reseteverypar
+ \parindent\zeropoint
+ \parskip \zeropoint
+ \leftskip \zeropoint
+ \rightskip\zeropoint
+ \relax}
+
+%D We keep these for a while:
+
+% \aliased\let\ruledhss \hss
+% \aliased\let\ruledhfil \hfil
+% \aliased\let\ruledhfill \hfill
+% \aliased\let\ruledhfilll \hfilll
+% \aliased\let\ruledhfilneg \hfilneg
+% \aliased\let\ruledhfillneg \hfillneg
+% \aliased\let\ruledhfilllneg \hfilllneg
+% \aliased\let\ruledvss \vss
+% \aliased\let\ruledvfil \vfil
+% \aliased\let\ruledvfill \vfill
+% \aliased\let\ruledvfilll \vfilll
+% \aliased\let\ruledvfilneg \vfilneg
+% \aliased\let\ruledvfillneg \vfillneg
+% \aliased\let\ruledvfilllneg \vfilllneg
+% \aliased\let\ruledhskip \hskip
+% \aliased\let\ruledvskip \vskip
+% \aliased\let\ruledkern \kern
+% \aliased\let\ruledhglue \hglue
+% \aliased\let\ruledvglue \vglue
+% \aliased\let\ruledmkern \mkern
+% \aliased\let\ruledmskip \mskip
+% \aliased\let\ruledpenalty \penalty
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-def.mkxl b/Master/texmf-dist/tex/context/base/mkxl/type-def.mkxl
new file mode 100644
index 00000000000..a80b4f7bdf3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-def.mkxl
@@ -0,0 +1,48 @@
+%D \module
+%D [ file=type-def,
+%D version=2005.02.04,
+%D title=\CONTEXT\ Typescript Macros,
+%D subtitle=Default Definitions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\starttypescriptcollection[simplification]
+
+\starttypescript [\s!serif] [\s!simple] [\s!name]
+ \definefontsynonym [\s!Serif] [\s!Simple]
+ \definefontsynonym [\s!SerifBold] [\s!Serif]
+ \definefontsynonym [\s!SerifItalic] [\s!Serif]
+ \definefontsynonym [\s!SerifSlanted] [\s!Serif]
+ \definefontsynonym [\s!SerifBoldItalic] [\s!Serif]
+ \definefontsynonym [\s!SerifBoldSlanted] [\s!Serif]
+ \definefontsynonym [\s!SerifCaps] [\s!Serif]
+\stoptypescript
+
+\starttypescript [\s!sans] [\s!simple] [\s!name]
+ \definefontsynonym [\s!Sans] [\s!Simple]
+ \definefontsynonym [\s!SansBold] [\s!Sans]
+ \definefontsynonym [\s!SansItalic] [\s!Sans]
+ \definefontsynonym [\s!SansSlanted] [\s!Sans]
+ \definefontsynonym [\s!SansBoldItalic] [\s!Sans]
+ \definefontsynonym [\s!SansBoldSlanted] [\s!Sans]
+ \definefontsynonym [\s!SansCaps] [\s!Sans]
+\stoptypescript
+
+\starttypescript [\s!mono] [\s!simple] [\s!name]
+ \definefontsynonym [\s!Mono] [\s!Simple]
+ \definefontsynonym [\s!MonoBold] [\s!Mono]
+ \definefontsynonym [\s!MonoItalic] [\s!Mono]
+ \definefontsynonym [\s!MonoSlanted] [\s!Mono]
+ \definefontsynonym [\s!MonoBoldItalic] [\s!Mono]
+ \definefontsynonym [\s!MonoBoldSlanted] [\s!Mono]
+ \definefontsynonym [\s!MonoCaps] [\s!Mono]
+\stoptypescript
+
+\stoptypescriptcollection
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-fbk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/type-fbk.mkxl
new file mode 100644
index 00000000000..4ac38178754
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-fbk.mkxl
@@ -0,0 +1,70 @@
+%D \module
+%D [ file=type-sfbk,
+%D version=2005.02.04, % moved from ini/def/set
+%D title=\CONTEXT\ Typescript Macros,
+%D subtitle=Fallbacks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\definetypescriptprefix [\s!serif] [\s!Serif]
+\definetypescriptprefix [\s!sans] [\s!Sans]
+\definetypescriptprefix [\s!mono] [\s!Mono]
+
+\definefontsynonym [\s!DefaultFont] [\s!file:lmmonoproplt10-regular]
+
+\startsetups [\s!font:\s!fallback:\s!serif]
+ \definefontsynonym[\s!Serif] [\s!DefaultFont]
+ \definefontsynonym[\s!SerifBold] [\s!Serif]
+ \definefontsynonym[\s!SerifItalic] [\s!Serif]
+ \definefontsynonym[\s!SerifSlanted] [\s!SerifItalic]
+ \definefontsynonym[\s!SerifBoldItalic] [\s!Serif]
+ \definefontsynonym[\s!SerifBoldSlanted][\s!SerifBoldItalic]
+ \definefontsynonym[\s!SerifCaps] [\s!Serif] [\s!features=\s!smallcaps]
+\stopsetups
+
+\startsetups [\s!font:\s!fallback:\s!sans]
+ \definefontsynonym[\s!Sans] [\s!DefaultFont]
+ \definefontsynonym[\s!SansBold] [\s!Sans]
+ \definefontsynonym[\s!SansItalic] [\s!Sans]
+ \definefontsynonym[\s!SansSlanted] [\s!SansItalic]
+ \definefontsynonym[\s!SansBoldItalic] [\s!Sans]
+ \definefontsynonym[\s!SansBoldSlanted][\s!SansBoldItalic]
+ \definefontsynonym[\s!SansCaps] [\s!Sans] [\s!features=\s!smallcaps]
+\stopsetups
+
+\startsetups [\s!font:\s!fallback:\s!mono]
+ \definefontsynonym[\s!Mono] [\s!DefaultFont]
+ \definefontsynonym[\s!MonoBold] [\s!Mono]
+ \definefontsynonym[\s!MonoItalic] [\s!Mono]
+ \definefontsynonym[\s!MonoSlanted] [\s!MonoItalic]
+ \definefontsynonym[\s!MonoBoldItalic] [\s!Mono]
+ \definefontsynonym[\s!MonoBoldSlanted][\s!MonoBoldItalic]
+ \definefontsynonym[\s!MonoCaps] [\s!Mono] [\s!features=\s!smallcaps]
+\stopsetups
+
+\starttypescript [\s!serif,\s!sans,\s!mono] [\s!fallback]
+ \setups[\s!font:\s!fallback:\typescriptone]
+\stoptypescript
+
+\startsetups [\s!font:allbold:\s!serif]
+ \definefontsynonym[\s!Serif] [\s!SerifBold]
+ \definefontsynonym[\s!SerifItalic] [\s!SerifBoldItalic]
+ \definefontsynonym[\s!SerifSlanted][\s!SerifBoldSlanted]
+ %definefontsynonym[\s!SerifCaps] [\s!SerifBold]
+\stopsetups
+
+\startsetups [\s!font:allbold:\s!sans]
+ \definefontsynonym[\s!Sans] [\s!SansBold]
+ \definefontsynonym[\s!SansItalic] [\s!SansBoldItalic]
+ \definefontsynonym[\s!SansSlanted][\s!SansBoldSlanted]
+ %definefontsynonym[\s!SansCaps] [\s!SansBold]
+\stopsetups
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/type-ini.lmt
new file mode 100644
index 00000000000..23d5096e2ff
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-ini.lmt
@@ -0,0 +1,45 @@
+if not modules then modules = { } end modules ['type-ini'] = {
+ version = 1.001,
+ comment = "companion to type-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local lpegmatch, P, Cs = lpeg.match, lpeg.P, lpeg.Cs
+
+local context = context
+local implement = interfaces.implement
+local uselibrary = resolvers.uselibrary
+local p_strip = Cs((P("type-") * (P("imp-")^0))^0/"" * P(1)^0)
+local report = logs.reporter("fonts","typescripts")
+
+local function action(name,foundname)
+ context.loadfoundtypescriptfile(name,foundname)
+end
+
+local patterns = {
+ "type-imp-%s.mkxl",
+ "type-imp-%s.mkiv",
+ "type-imp-%s.tex"
+}
+
+local function failure(name)
+ if name ~= "loc" then
+ report("unknown library %a",name)
+ end
+end
+
+implement {
+ name = "loadtypescriptfile",
+ arguments = "string",
+ actions = function(name) -- a more specific name
+ uselibrary {
+ name = lpegmatch(p_strip,name) or name,
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = false, -- will become true
+ }
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-ini.mklx b/Master/texmf-dist/tex/context/base/mkxl/type-ini.mklx
new file mode 100644
index 00000000000..bba2d433b20
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-ini.mklx
@@ -0,0 +1,613 @@
+%D \module
+%D [ file=type-ini,
+%D version=2001.03.05,
+%D title=\CONTEXT\ Typescript Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typescript Macros / Initialization}
+
+\registerctxluafile{type-ini}{autosuffix}
+
+%D The default fontclass is empty. We could demand always using fontclasses, and
+%D then make the calling macros simplier (always assume fontclass) but apart from
+%D downward compatibility issues, it would make global, class spanning definitions a
+%D pain. Some day we will introduce a default class.
+%D
+%D The \type {type-ini} and \type {font-ini} modules come as a pair and have mutual
+%D dependencies.
+%D
+%D At some point we will only store in memory so some code can go away.
+
+\unprotect
+
+\newcount \c_font_typescripts_n_of_preloaded
+\newconditional\c_font_typescripts_quit
+\newtoks \c_font_typescripts_document
+\newconditional\c_font_typescripts_preload
+\newconditional\c_font_typescripts_first_pass \settrue\c_font_typescripts_first_pass
+
+\newif \iftypescriptfound % will become a mode
+\newif \iftracetypescripts
+
+\newtoks \everybeforedefinetypeface
+\newtoks \everyafterdefinetypeface
+
+\mutable\let\typescriptfiles \empty
+\mutable\let\currenttypescripts\empty
+\mutable\let\currenttypefile \empty
+
+\installmacrostack\currenttypefile
+
+\mutable\let\typescriptone \empty % public, used in typescripts
+\mutable\let\typescripttwo \empty % public, used in typescripts
+\mutable\let\typescriptthree\empty % public, used in typescripts
+
+\installmacrostack\typescriptone
+\installmacrostack\typescripttwo
+\installmacrostack\typescriptthree
+
+\mutable\let\fontclassstyle \empty
+
+\installmacrostack\fontclassstyle
+
+\let\m_font_typescripts_one \empty
+\let\m_font_typescripts_two \empty
+\let\m_font_typescripts_three\empty
+\let\m_font_typescripts_check\empty
+\let\m_font_typescripts_match\empty
+
+\installmacrostack\m_font_typescripts_one
+\installmacrostack\m_font_typescripts_two
+\installmacrostack\m_font_typescripts_three
+
+\mutable\let\t_font_typescripts\relax % uses as synonym
+
+\installcorenamespace{typescriptcache}
+\installcorenamespace{typescriptfiles}
+\installcorenamespace{typescriptonce}
+\installcorenamespace{typescriptsynonyms}
+\installcorenamespace{typescriptprefix}
+\installcorenamespace{typescriptinheritances}
+\installcorenamespace{typescriptdefaultstyles}
+\installcorenamespace{typescriptrelatives}
+
+\definesystemvariable{ts} % TypeScript
+
+% tricky ... here we push/pop ... so \let
+
+\mutable\let\typescriptmethod\plusone % 1: empty==all==true 2: empty==false
+\mutable\let\typescriptstate \plustwo % 1: process 2: store
+
+\installmacrostack\typescriptmethod
+\installmacrostack\typescriptstate
+
+%D When these are loaded it is possible to get spaces but the whole idea is to do
+%D that loading in vertical mode. Adding \type {\pushendofline} and \type
+%D {\popendofline} is an option but not watertight as it can still interfere, for
+%D instance work comments and leading spaces. Also, in horizontal mode it is likely
+%D that one is also grouped and loading bodyfonts grouped is a real bad idea.
+%D Ignoring all spaces is also not a solution. So, our verdict is: no catch.
+
+\permanent\protected\def\starttypescriptcollection % redefined on the fly
+ {\dosingleempty\font_typescripts_collection_start}
+
+\def\font_typescripts_collection_start[#tag]%
+ {}
+
+\permanent\let\stoptypescriptcollection\relax
+
+\permanent\protected\def\usetypescriptfile[#filename]%
+ {\ifcstok{#filename}\v!reset
+ \let\typescriptfiles\empty
+ \else
+ \splitfilename{#filename}%
+ \addtocommalist\splitoffbase\typescriptfiles
+ \fi}
+
+\installmacrostack\stoptypescript
+
+% \permanent\protected\def\usetypescript {\dotripleempty\font_typescripts_use_one}
+% \permanent\protected\def\usetypescriptexact{\dotripleempty\font_typescripts_use_two}
+%
+% \def\font_typescripts_use_one{\let\typescriptmethod\plusone\font_typescripts_use}
+% \def\font_typescripts_use_two{\let\typescriptmethod\plustwo\font_typescripts_use}
+%
+% \protected\def\font_typescripts_use[#one][#two][#three]%
+
+% todo: just pass the method ... also further on then .. first test
+
+\def\font_typescripts_use_one{\let\typescriptmethod\plusone\font_typescripts_use}
+\def\font_typescripts_use_two{\let\typescriptmethod\plustwo\font_typescripts_use}
+
+\permanent\protected\def\usetypescript {\font_typescripts_use_one}
+\permanent\protected\def\usetypescriptexact{\font_typescripts_use_two}
+
+\tolerant\protected\def\font_typescripts_use[#one]#spacer[#two]#spacer[#three]%
+ {\push_macro_m_font_typescripts_one
+ \push_macro_m_font_typescripts_two
+ \push_macro_m_font_typescripts_three
+ \edef\m_font_typescripts_one {\truetypescript{#one}}%
+ \edef\m_font_typescripts_two {\truetypescript{#two}}%
+ \edef\m_font_typescripts_three{\truetypescript{#three}}%
+ \push_macro_typescriptone
+ \push_macro_typescripttwo
+ \push_macro_typescriptthree
+ \push_macro_typescriptmethod
+ \push_macro_typescriptstate
+ \push_macro_stoptypescript
+ \typescriptfoundfalse
+ \let\typescriptstate\plusone % why
+ \iftracetypescripts
+ \writestatus\m!fonts{request: [\m_font_typescripts_one] [\m_font_typescripts_two] [\m_font_typescripts_three]}%
+ \fi
+ \ifhmode
+ \font_typescripts_use_inline
+ \else
+ \font_typescripts_use_display
+ \fi
+ \setfalse\c_font_typescripts_first_pass
+ \pop_macro_stoptypescript
+ \pop_macro_typescriptstate
+ \pop_macro_typescriptmethod
+ \pop_macro_typescriptthree
+ \pop_macro_typescripttwo
+ \pop_macro_typescriptone
+ \pop_macro_m_font_typescripts_three
+ \pop_macro_m_font_typescripts_two
+ \pop_macro_m_font_typescripts_one}
+
+\def\font_typescripts_use_display
+ {\processcommacommand[\typescriptfiles]\font_typescripts_load_file
+ \the\c_font_typescripts_document}
+
+\let\font_typescripts_use_inline\font_typescripts_use_display
+
+\permanent\protected\def\preloadtypescripts
+ {\ifproductionrun\settrue\c_font_typescripts_preload\fi}
+
+\prependtoks
+ \preloadtypescripts
+\to \everyjob
+
+\permanent\protected\def\loadtypescriptfile[#1]%
+ {\push_macro_typescriptstate
+ \let\typescriptstate\plustwo % assumes 2 at the outer level
+ \clf_loadtypescriptfile{#1}%
+ \pop_macro_typescriptstate}
+
+\permanent\protected\def\loadfoundtypescriptfile#1#2% name foundname / not a user command
+ {\startreadingfile
+ \unprotect
+ \pushendofline
+ \input{#2}%
+ \popendofline
+ \protect
+ \stopreadingfile}
+
+\permanent\protected\def\quittypescriptscanning
+ {\settrue\c_font_typescripts_quit} % public
+
+\permanent\protected\def\font_typescripts_start_store#definitions\stoptypescript
+ {\global\advance\c_font_typescripts_n_of_preloaded\plusone
+ \gdefcsname\??typescriptcache\the\c_font_typescripts_n_of_preloaded\endcsname
+ {\starttypescript#definitions\stoptypescript}%
+ \gtoksapp\t_font_typescripts\expandafter
+ {\csname\??typescriptcache\the\c_font_typescripts_n_of_preloaded\endcsname}}
+
+\permanent\protected\def\font_typescripts_collection_start_store#definitions\stoptypescriptcollection
+ {\global\advance\c_font_typescripts_n_of_preloaded\plusone
+ \gdefcsname\??typescriptcache\the\c_font_typescripts_n_of_preloaded\endcsname
+ {\starttypescriptcollection#definitions\stoptypescriptcollection}%
+ \gtoksapp\t_font_typescripts\expandafter
+ {\csname\??typescriptcache\the\c_font_typescripts_n_of_preloaded\endcsname}}
+
+\def\font_typescripts_load_file#filename%
+ {\setfalse\c_font_typescripts_quit
+ \push_macro_currenttypefile
+ \def\currenttypefile{#filename}%
+ \ifconditional\c_font_typescripts_preload
+ \font_typescript_process_typescript_file_and_store
+ \else
+ \font_typescript_process_typescript_file
+ \fi
+ \pop_macro_currenttypefile
+ \ifconditional\c_font_typescripts_quit
+ \quitcommalist
+ \setfalse\c_font_typescripts_quit
+ \fi}
+
+\def\font_typescript_process_typescript_file_and_store
+ {\expandafter\let\expandafter\t_font_typescripts\csname\??typescriptfiles\currenttypefile\endcsname
+ \ifrelax\t_font_typescripts
+ \font_typescript_process_typescript_store_indeed
+ \fi
+ \the\t_font_typescripts}
+
+\def\font_typescript_process_typescript_store_indeed
+ {\newtoks\t_font_typescripts % is \relaxed elsewhere
+ \begingroup
+ \enforced\let\starttypescript \font_typescripts_start_store
+ \enforced\let\starttypescriptcollection\font_typescripts_collection_start_store
+ \font_typescript_process_typescript_file
+ \endgroup
+ \letcsname\??typescriptfiles\currenttypefile\endcsname\t_font_typescripts}
+
+\def\font_typescript_process_typescript_file
+ {\clf_loadtypescriptfile{\currenttypefile}}
+
+\permanent\tolerant\protected\def\usetypescriptonce[#one]#spacer[#two]#spacer[#three]%
+ {\ifcsname\??typescriptonce#one:#two:#three\endcsname
+ \writestatus\m!fonts{once (#one) (#two) (#three)}%
+ \else
+ \letcsname\??typescriptonce#one:#two:#three\endcsname\relax
+ \font_typescripts_use[#one][#two][#three]%
+ \fi}
+
+% \definetypescriptsynonym[lbr][cmr]
+
+\permanent\tolerant\protected\def\definetypescriptsynonym[#name]#spacer[#synonym]%
+ {\ifarguments\or\or\edefcsname\??typescriptsynonyms#name\endcsname{#synonym}\fi}
+
+\permanent\def\truetypescript#name% recursive so no \lastnamedcs
+ {\ifcsname\??typescriptsynonyms#name\endcsname
+ \expandafter\truetypescript\lastnamedcs
+ \else
+ #name%
+ \fi}
+
+% script [serif] [default] [size]
+% script [serif] [computer-modern] [size]
+% script [serif] [computer-modern] [ec]
+% script [serif] [computer-modern] [name]
+% script [serif] [computer-modern] [special]
+
+\prependtoks
+ \settrue\c_font_typescripts_first_pass
+\to \everyjob
+
+\permanent\protected\def\starttypescript
+ {\ifcase\typescriptstate
+ % 0 = skip
+ \expandafter\font_typescripts_start_gobble
+ \or
+ % 1 = process
+ \expandafter\font_typescripts_start_process
+ \or
+ % 2 = store
+ \expandafter\font_typescripts_start_document
+ \else
+ % ? = skip
+ \expandafter\font_typescripts_start_gobble
+ \fi}
+
+\def\font_typescripts_start_gobble#ignore\stoptypescript{}
+
+\def\font_typescripts_start_document#definitions\stoptypescript
+ {\toksapp\c_font_typescripts_document{\starttypescript#definitions\stoptypescript}}
+
+\def\font_typescripts_start_process % could be a faster \doifelsenextoptionalif needed
+ {\let\typescriptone \m_font_typescripts_one
+ \let\typescripttwo \m_font_typescripts_two
+ \let\typescriptthree\m_font_typescripts_three
+ \let\m_font_typescripts_match\empty
+ \doifelsenextoptionalcs\font_typescripts_start_process_one\font_typescripts_start_process_all}
+
+\def\font_typescripts_start_process_all % could be a \let
+ {\ifconditional\c_font_typescripts_first_pass
+ \expandafter\font_typescripts_start_process_indeed
+ \else
+ % skip this since it may do unwanted resets, like
+ % setting symbolic font names to unknown, especially
+ % in run time user type scripts
+ \expandafter\font_typescripts_start_gobble
+ \fi}
+
+\def\font_typescripts_show_match
+ {\writestatus\m!fonts{match:\ifrelax\currenttypefile\space *\fi \m_font_typescripts_match}}
+
+\def\font_typescripts_start_process_yes
+ {\ifdone
+ \typescriptfoundtrue
+ \iftracetypescripts\font_typescripts_show_match\fi
+ \expandafter\font_typescripts_start_process_indeed
+ \else
+ \expandafter\font_typescripts_start_gobble
+ \fi}
+
+\def\font_typescripts_start_process_one
+ {\font_typescripts_check\m_font_typescripts_one\typescriptone\font_typescripts_start_process_again_one}
+
+\def\font_typescripts_start_process_two
+ {\font_typescripts_check\m_font_typescripts_two\typescripttwo\font_typescripts_start_process_again_two}
+
+\def\font_typescripts_start_process_three
+ {\font_typescripts_check\m_font_typescripts_three\typescriptthree\font_typescripts_start_process_again_three}
+
+\def\font_typescripts_start_process_again_one
+ {\doifelsenextoptionalcs\font_typescripts_start_process_two\font_typescripts_start_process_yes}
+
+\def\font_typescripts_start_process_again_two
+ {\doifelsenextoptionalcs\font_typescripts_start_process_three\font_typescripts_start_process_yes}
+
+\let\font_typescripts_start_process_again_three\font_typescripts_start_process_yes
+
+\def\font_typescripts_start_process_indeed
+ {\push_macro_fontclass}
+
+\permanent\protected\def\stoptypescript
+ {\pop_macro_fontclass}
+
+\def\font_typescripts_check#asked#target#followup[#value]% script use value next
+ {\donefalse
+ \edef\m_font_typescripts_check{#value}%
+ \ifempty\m_font_typescripts_check % no longer needed / met
+ \ifcase\typescriptmethod\or\donetrue\fi
+ \orelse\ifx#asked\s!all
+ \donetrue
+ \orelse\ifx\m_font_typescripts_check\s!all
+ \donetrue
+ \orelse\ifx#asked\m_font_typescripts_check % saves 10% trace so probably faster too
+ \donetrue
+ \let#target\m_font_typescripts_check
+ \else
+ \doifelsecommon\m_font_typescripts_check#asked\donetrue\donefalse
+ \ifdone
+ \let#target\commalistelement
+ \fi
+ \fi
+ \ifdone
+ \iftracetypescripts\extendtypescriptmatch\fi
+ \expandafter#followup%
+ \else
+ \expandafter\font_typescripts_start_gobble
+ \fi}
+
+\permanent\protected\def\extendtypescriptmatch
+ {\edef\m_font_typescripts_match{\m_font_typescripts_match\space[\m_font_typescripts_check]}}
+
+%D Map files will go away in \LUATEX, but till that happens we use stripped down
+%D support for loading them.
+
+\permanent\tolerant\protected\def\loadmapfile [#filename]{\clf_loadmapfile{#filename}}
+\permanent\tolerant\protected\def\loadmapline [#kind]#spacer[#data]{\clf_loadmapline{#kind}{#data}}
+\permanent \protected\def\forgetmapfiles {\clf_resetmapfiles}
+
+% \prependtoks
+% \loadmapfile[mkiv-base.map]% can't we preload this one?
+% \to \everystarttext
+
+%D A handy shortcut:
+
+% \definetypescriptprefix[serif][Serif]
+% \definetypescriptprefix[sans] [Sans]
+% \definetypescriptprefix[mono] [Mono]
+%
+% \starttypescript [serif,sans,mono] [handling,hanging,hz] [pure,normal,hz,quality]
+% \setupfontsynonym [\typescriptprefix\typescriptone] [handling=\typescriptthree]
+% \stoptypescript
+
+\permanent\tolerant\protected\def\definetypescriptprefix[#name]#spacer[#prefix]%
+ {\gdefcsname\??typescriptprefix#name\endcsname{#prefix}} % made global
+
+\permanent\def\typescriptprefix#name%
+ %{\ifcsname\??typescriptprefix#name\endcsname\csname\??typescriptprefix#name\endcsname\else#name\fi}
+ {\ifcsname\??typescriptprefix#name\endcsname\lastnamedcs\else#name\fi}
+
+% defining typefaces:
+%
+% \definetypeface [joke] [rm]
+% \definetypeface [joke] [rm] [settings]
+% \definetypeface [joke] [rm] [serif] [lucida]
+% \definetypeface [joke] [rm] [serif] [lucida] [size]
+% \definetypeface [joke] [rm] [serif] [lucida] [size] [settings]
+% \definetypeface [joke] [specification]
+
+\appendtoks
+ \font_helpers_reset_fontclass_math_families\fontclass
+\to \everybeforedefinetypeface
+
+%D This hooks into the font switcher:
+
+\settrue\autotypescripts
+
+\protected\def\fonts_helpers_try_currentfontclass#typeface%
+ {\ifconditional\autotypescripts
+ \usetypescript[#typeface]%
+ \ifcsname\??fontclassyes#typeface\endcsname
+ \edef\fontclass{#typeface}%
+ \else
+ \iftracetypescripts\writestatus\m!fonts{auto load typescript file 1: [#typeface]}\fi
+ \usetypescriptfile[#typeface]%
+ \usetypescript[#typeface]%
+ \ifcsname\??fontclassyes#typeface\endcsname
+ \edef\fontclass{#typeface}%
+ \else
+ % todo: message
+ \letcsname\??fontclassnop#typeface\endcsname\empty
+ \fi
+ \fi
+ \else
+ % todo: message
+ \letcsname\??fontclassnop#typeface\endcsname\empty
+ \fi}
+
+%D Now we define:
+
+\permanent\tolerant\protected\def\definetypeface[#name]#spacer[#style]#spacer[#fontshape]#spacer[#fontname]#spacer[#fontsize]#spacer[#settings]%
+ {\ifarguments
+ \expandafter\font_typefaces_define_e
+ \or
+ \expandafter\font_typefaces_define_d
+ \or
+ \expandafter\font_typefaces_define_d
+ \or
+ \expandafter\font_typefaces_define_c
+ \or
+ \expandafter\font_typefaces_define_b
+ \or
+ \expandafter\font_typefaces_define_a
+ \or
+ \expandafter\font_typefaces_define_a
+ \fi[#name][#style][#fontshape][#fontname][#fontsize][#settings]}
+
+\def\font_typefaces_define_e[#name][#style][#fontshape][#fontname][#fontsize][#settings]%
+ {}
+
+\def\font_typefaces_define_a[#name][#style][#fontshape][#fontname][#fontsize][#settings]%
+ {\iftracetypescripts\writestatus\m!fonts{define: [#name] [#style] [#fontshape] [#fontname]}\fi
+ \font_typefaces_define_indeed[#name][#style]%
+ \font_typefaces_defining_start{#name}{#style}{#settings}%
+ \font_typescripts_use_one[#fontshape][#fontname][\s!name]% [\s!name,\s!default]%
+ \iftypescriptfound
+ % we're okay
+ \orelse\ifconditional\autotypescripts
+ \iftracetypescripts\writestatus\m!fonts{auto load typescript file 2: [#fontname]}\fi
+ \usetypescriptfile[#fontname]%
+ \font_typescripts_use_one[#fontshape][#fontname][\s!name]% [\s!name,\s!default]%
+ \fi
+ \font_typescripts_use_one[#fontshape][#fontsize][\s!size]%
+ \font_typefaces_defining_stop}
+
+\def\font_typefaces_define_b[#name][#style][#fontshape][#fontname][#discard][#discard]%
+ {\font_typefaces_define_a[#name][#style][#fontshape][#fontname][\s!default][#discard]}
+
+\def\font_typefaces_define_c[#name][#style][#discard][#discard][#discard][#discard]%
+ {\font_typefaces_define_indeed[#name][#style]}
+
+\def\font_typefaces_define_d[#name][#specification][#discard][#discard][#discard][#discard]% use definitions in lfg file
+ {\clf_definetypeface{#name}{#specification}}
+
+\def\font_typefaces_define_indeed[#name][#style]% saveguard against redefinition
+ {\doifsomething{#name}
+ {\ifcsname\??typescriptdefaultstyles#name\endcsname \else
+ \registerfontclass{#name}%
+ \gdefcsname\??typescriptdefaultstyles#name\endcsname{#style}%
+ \fi
+ \ifcsname#name\endcsname \else
+ \protected\frozen\instance\gdefcsname#name\endcsname{\switchtotypeface[#name][#style]}%
+ \fi}}
+
+\def\font_typefaces_defining_start#name#style#settings%
+ {\let\m_ts_rscale \!!plusone % as we push/pop
+ \let\m_ts_features \empty
+ \let\m_ts_fallbacks \empty
+ \let\m_ts_goodies \empty
+ \let\m_ts_direction \empty
+ \let\m_ts_designsize\empty
+ \geteparameters[m_ts_][#settings]% todo raw ... no need for key interpretation
+ \push_macro_fontclass
+ \push_macro_fontclassstyle
+ \setcurrentfontclass{#name}%
+ \savefontclassparameters{#style}\m_ts_rscale\m_ts_features\m_ts_fallbacks\m_ts_goodies\m_ts_designsize\m_ts_direction
+ \the\everybeforedefinetypeface}
+
+\permanent\def\tsvar#key#default% undocumented and unofficial
+ {\expandafter\ifempty\csname\??ts#key\endcsname
+ #default%
+ \else
+ \csname\??ts#key\endcsname
+ \fi}
+
+\def\font_typefaces_defining_stop
+ {\the\everyafterdefinetypeface
+ \pop_macro_fontclassstyle
+ \pop_macro_fontclass}
+
+\permanent\protected\def\dofastdefinetypeface#name#style#fontshape#fontsize#settings% called from the lua end (via case d)
+ {\font_typefaces_define_indeed[#name][#style]%
+ \font_typefaces_defining_start{#name}{#style}{#settings}%
+ \font_typescripts_use_one[#fontshape][#fontsize][\s!size]%
+ \font_typefaces_defining_stop}
+
+\permanent\tolerant\protected\def\setuptypeface[#class]#spacer[#settings]%
+ {\ifarguments
+ \or
+ \setcurrentfontclass{#class}%
+ \let\globalfontclass\fontclass
+ \ifempty\fontclass
+ \setupbodyfont[\s!rm]%
+ \orelse\ifcsname\??typescriptdefaultstyles\fontclass\endcsname
+ \expandafter\setupbodyfont\expandafter[\lastnamedcs]%
+ \else
+ \setupbodyfont[\s!rm]%
+ \fi
+ \or
+ \setcurrentfontclass{#class}%
+ \let\globalfontclass\fontclass
+ \setupbodyfont[#settings]%
+ \fi
+ \ifmmode\mr\else\tf\fi} % needed ?
+
+\permanent\tolerant\protected\def\switchtotypeface[#class]#spacer[#settings]%
+ {\ifarguments
+ \or
+ \setcurrentfontclass{#class}%
+ \let\globalfontclass\globalfontclass
+ \ifempty\fontclass
+ \switchtobodyfont[\s!rm]%
+ \orelse\ifcsname\??typescriptdefaultstyles\fontclass\endcsname
+ \expandafter\switchtobodyfont\expandafter[\lastnamedcs]%
+ \else
+ \switchtobodyfont[\s!rm]%
+ \fi
+ \or
+ \setcurrentfontclass{#class}%
+ \let\globalfontclass\globalfontclass
+ \switchtobodyfont[#settings]%
+ \fi
+ \ifmmode\mr\else\tf\fi} % needed ?
+
+%D For Taco:
+%D
+%D \starttyping
+%D \inherittypeface[palatino][rm][postscript]
+%D \inherittypeface[palatino][rm][\fontclass]
+%D \inherittypeface[palatino][rm] % == \fontclass
+%D \inherittypeface[palatino] % == [rm,ss,tt,mm]
+%D \stoptyping
+
+\permanent\tolerant\protected\def\inherittypeface[#name]#spacer[#styles]#spacer[#parentclass]%
+ {\iftok{#styles}\emptytoks
+ \inherittypeface[#name][\s!rm,\s!ss,\s!tt,\s!mm][\fontclass]%
+ \orelse\iftok{#name}{#parentclass}\else
+ \glet\font_typescripts_inherit_check\font_typescripts_inherit_check_indeed
+ \def\font_typescripts_inherit_check_step#style{\edefcsname\??typescriptinheritances#name:#style\endcsname{#parentclass}}%
+ \processcommalist[#styles]\font_typescripts_inherit_check_step
+ \fi}
+
+\let\font_typescripts_inherit_check_step\relax
+
+%D This hooks into the font mechanism with:
+
+\def\font_typescripts_inherit_check_indeed#name% called often
+ {\ifcsname\??typescriptinheritances\fontclass:#name\endcsname
+ %\expandafter\let\expandafter\fontclass\csname\??typescriptinheritances\fontclass:#name\endcsname
+ \expandafter\let\expandafter\fontclass\lastnamedcs
+ \fi}
+
+\let\font_typescripts_inherit_check\gobbleoneargument
+
+\def\v_font_string_d % default fontstyle (expands to \s!Serif in font-ini)
+ {\expandafter\ifx\csname\??typescriptdefaultstyles\fontclass\endcsname\s!rm \s!Serif \else % no \orelse !
+ \expandafter\ifx\csname\??typescriptdefaultstyles\fontclass\endcsname\s!ss \s!Sans \else % no \orelse !
+ \expandafter\ifx\csname\??typescriptdefaultstyles\fontclass\endcsname\s!tt \s!Mono \else % no \orelse !
+ \s!Serif \fi\fi\fi}
+
+\protected\def\font_helpers_set_fontstyle_of_fontclass
+ {\ifempty\fontclass
+ \let\fontstyle\s!rm
+ \orelse\ifcsname\??typescriptdefaultstyles\fontclass\endcsname
+ %\edef\fontstyle{\csname\??typescriptdefaultstyles\fontclass\endcsname}%
+ \edef\fontstyle{\lastnamedcs}%
+ \else
+ \let\fontstyle\s!rm
+ \fi}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-lua.mkxl b/Master/texmf-dist/tex/context/base/mkxl/type-lua.mkxl
new file mode 100644
index 00000000000..fac6c3867a8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-lua.mkxl
@@ -0,0 +1,86 @@
+%D \module
+%D [ file=type-lua,
+%D version=2009.11.27,
+%D title=\CONTEXT\ Typescript Macros,
+%D subtitle=\MKIV\ goodies,
+%D author=Hans Hagen \& Wolfgang Schuster,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\starttypescriptcollection [luatex]
+
+\starttypescript [specserif] [\s!all] [\s!name]
+ \definefontsynonym [\s!Serif] [spec:\cleanfontname\typescripttwo-\s!normal-\s!normal] [\s!features=\s!default]
+ \definefontsynonym [\s!SerifItalic] [spec:\cleanfontname\typescripttwo-\s!normal-\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SerifSlanted] [spec:\cleanfontname\typescripttwo-\s!normal-\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SerifBold] [spec:\cleanfontname\typescripttwo-\s!bold -\s!normal] [\s!features=\s!default]
+ \definefontsynonym [\s!SerifBoldItalic] [spec:\cleanfontname\typescripttwo-\s!bold -\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SerifBoldSlanted] [spec:\cleanfontname\typescripttwo-\s!bold -\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SerifCaps] [spec:\cleanfontname\typescripttwo-\s!normal-\s!normal] [\s!features=\s!smallcaps]
+\stoptypescript
+
+\starttypescript [specsans] [\s!all] [\s!name]
+ \definefontsynonym [\s!Sans] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!normal] [\s!features=\s!default]
+ \definefontsynonym [\s!SansItalic] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SansSlanted] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SansBold] [\s!spec:\cleanfontname\typescripttwo-\s!bold -\s!normal] [\s!features=\s!default]
+ \definefontsynonym [\s!SansBoldItalic] [\s!spec:\cleanfontname\typescripttwo-\s!bold -\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SansBoldSlanted] [\s!spec:\cleanfontname\typescripttwo-\s!bold -\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!SansCaps] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!normal] [\s!features=\s!smallcaps]
+\stoptypescript
+
+\starttypescript [specmono] [\s!all] [\s!name]
+ \definefontsynonym [\s!Mono] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!normal] [\s!features=\s!default]
+ \definefontsynonym [\s!MonoItalic] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!MonoSlanted] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!MonoBold] [\s!spec:\cleanfontname\typescripttwo-\s!bold -\s!normal] [\s!features=\s!default]
+ \definefontsynonym [\s!MonoBoldItalic] [\s!spec:\cleanfontname\typescripttwo-\s!bold -\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!MonoBoldSlanted] [\s!spec:\cleanfontname\typescripttwo-\s!bold -\s!italic] [\s!features=\s!default]
+ \definefontsynonym [\s!MonoCaps] [\s!spec:\cleanfontname\typescripttwo-\s!normal-\s!normal] [\s!features=\s!smallcaps]
+\stoptypescript
+
+% we could combine these in the normal serif settings
+
+\starttypescript [specserif] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!rm] [\s!default]
+\stoptypescript
+
+\starttypescript [specsans] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!ss] [\s!default]
+\stoptypescript
+
+\starttypescript [specmono] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!tt] [\s!default]
+\stoptypescript
+
+\starttypescript [specserif] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!rm] [\s!default]
+\stoptypescript
+
+\starttypescript [specsans] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!ss] [\s!default]
+\stoptypescript
+
+\starttypescript [specmono] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!tt] [\s!default]
+\stoptypescript
+
+\stoptypescriptcollection
+
+\endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-set.mkxl b/Master/texmf-dist/tex/context/base/mkxl/type-set.mkxl
new file mode 100644
index 00000000000..4a22b59a50a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-set.mkxl
@@ -0,0 +1,139 @@
+%D \module
+%D [ file=type-set,
+%D version=2005.02.04, % moved from ini/def
+%D title=\CONTEXT\ Typescript Macros,
+%D subtitle=Default Settings,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+\ifnum\typescriptstate=\plustwo
+
+ % we store some typescripts in the format
+
+ % \loadmarkfile{type-def}
+ % \loadmarkfile{type-lua}
+ % \loadmarkfile{type-siz}
+ % \loadmarkfile{type-fbk}
+ % \loadmarkfile{type-imp-latinmodern}
+
+ % but ... let's make sure that newlines are gone
+
+ % beware: these are low level calls, so not user commands !
+
+ \loadfoundtypescriptfile\empty{type-def.mkxl}
+ \loadfoundtypescriptfile\empty{type-lua.mkxl}
+ \loadfoundtypescriptfile\empty{type-siz.mkxl}
+ \loadfoundtypescriptfile\empty{type-fbk.mkxl}
+ \loadfoundtypescriptfile\empty{type-imp-latinmodern.mkiv}
+
+ % and not:
+
+ % \loadtypescriptfile[def] % meant for local usage only
+ % \loadtypescriptfile[fbk] % meant for local usage only
+ % \loadtypescriptfile[lua] % meant for local usage only
+ % \loadtypescriptfile[siz] % meant for local usage only
+ % \usetypescriptfile[latinmodern]
+
+ \usetypescriptfile[loc]
+
+ % We want these names preset but they conflict with later loads so from now on
+ % one has to define a proper typeface if they are needed.
+ %
+ % \usetypescript[serif,sans,mono,math] [latin-modern]
+
+\else
+
+ \usetypescriptfile[def]
+ \usetypescriptfile[fbk]
+ \usetypescriptfile[lua]
+ \usetypescriptfile[siz]
+ \usetypescriptfile[latinmodern]
+ \usetypescriptfile[loc]
+
+\fi
+
+\definefilesynonym [type-imp-latin-modern.mkiv] [type-imp-latinmodern.mkiv]
+\definefilesynonym [type-imp-modern-latin.mkiv] [type-imp-modernlatin.mkiv]
+
+\definefilesynonym [type-imp-lucida.mkiv] [type-imp-lucida-typeone.mkiv]
+\definefilesynonym [type-imp-lucidaot.mkiv] [type-imp-lucida-opentype.mkiv]
+\definefilesynonym [type-imp-lucidadk.mkiv] [type-imp-lucida-opentype.mkiv]
+
+\definefilesynonym [type-imp-dejavu-condensed.mkiv] [type-imp-dejavu.mkiv]
+
+\definefilesynonym [type-imp-palatino.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-courier.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-avantgarde.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-helvetica.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-chancery.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-bookman.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-schoolbook.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-times.mkiv] [type-imp-texgyre.mkiv]
+
+\definefilesynonym [type-imp-cursor.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-adventor.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-heros.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-chorus.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-bonum.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-schola.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-pagella.mkiv] [type-imp-texgyre.mkiv]
+\definefilesynonym [type-imp-termes.mkiv] [type-imp-texgyre.mkiv]
+
+\definefilesynonym [type-imp-mscoretimes.mkiv] [type-imp-mscore.mkiv]
+\definefilesynonym [type-imp-mscorearial.mkiv] [type-imp-mscore.mkiv]
+\definefilesynonym [type-imp-mscorecourier.mkiv] [type-imp-mscore.mkiv]
+\definefilesynonym [type-imp-mscoreverdana.mkiv] [type-imp-mscore.mkiv]
+
+\definefilesynonym [type-imp-candara.mkiv] [type-imp-cleartype.mkiv]
+\definefilesynonym [type-imp-consolas.mkiv] [type-imp-cleartype.mkiv]
+\definefilesynonym [type-imp-constantia.mkiv] [type-imp-cleartype.mkiv]
+\definefilesynonym [type-imp-corbel.mkiv] [type-imp-cleartype.mkiv]
+\definefilesynonym [type-imp-calibri.mkiv] [type-imp-cleartype.mkiv]
+
+\definefilesynonym [type-imp-antykwa-poltawskiego.mkiv] [type-imp-antykwapoltawskiego.mkiv]
+
+%definefilesynonym [type-imp-mac.mkiv] [type-imp-osx.mkiv]
+%definefilesynonym [type-imp-win.mkiv] [type-imp-mscore.mkiv]
+
+\definefilesynonym [type-imp-eulernovum.mkiv] [type-imp-euler.mkiv]
+\definefilesynonym [type-imp-eulernova.mkiv] [type-imp-euler.mkiv]
+
+\definefilesynonym [type-imp-euler-with-pagella.mkiv] [type-imp-euler.mkiv]
+\definefilesynonym [type-imp-pagella-with-euler.mkiv] [type-imp-euler.mkiv]
+
+\definefilesynonym [type-imp-mdbch.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mdugm.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mdput.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mdici.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mdpgd.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mdpus.mkiv] [type-imp-mathdesign.mkiv]
+
+\definefilesynonym [type-imp-mathdesignch.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesigngm.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesignut.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesignci.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesigngd.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesignus.mkiv] [type-imp-mathdesign.mkiv]
+
+\definefilesynonym [type-imp-mathdesigncharter.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesigngaramond.mkiv] [type-imp-mathdesign.mkiv]
+\definefilesynonym [type-imp-mathdesignutopia.mkiv] [type-imp-mathdesign.mkiv]
+
+\definefilesynonym [type-imp-cows.mkiv] [type-imp-koeielettersot.mkiv]
+\definefilesynonym [type-imp-sheep.mkiv] [type-imp-koeielettersot.mkiv]
+\definefilesynonym [type-imp-coloredcows.mkiv] [type-imp-koeielettersot.mkiv]
+\definefilesynonym [type-imp-coloredsheep.mkiv] [type-imp-koeielettersot.mkiv]
+\definefilesynonym [type-imp-koeieletters.mkiv] [type-imp-koeielettersot.mkiv]
+
+\definefilesynonym [type-imp-stixtwo.mkiv] [type-imp-stix.mkiv]
+
+\definefilesynonym [type-imp-ibmplex.mkiv] [type-imp-plex.mkiv]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/type-siz.mkxl b/Master/texmf-dist/tex/context/base/mkxl/type-siz.mkxl
new file mode 100644
index 00000000000..d4c90b18229
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/type-siz.mkxl
@@ -0,0 +1,169 @@
+%D \module
+%D [ file=type-siz,
+%D version=2001.04.12,
+%D title=\CONTEXT\ Typescript Macros,
+%D subtitle=Sizing scripts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+% beware: no \s! before keys as these are interpreted
+
+\starttypescriptcollection[\s!size]
+
+\starttypescript [\s!serif] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!rm] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!sans] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!ss] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!mono] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!tt] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!math] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!mm] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!handwriting] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!hw] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!calligraphy] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!cg] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!casual] [\s!default] [\s!size]
+ \definebodyfont
+ [4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt,17.3pt]
+ [\s!rm] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!all] [\s!dtp] [\s!size]
+
+ \definebodyfontenvironment
+ [4pt]
+ [\s!text=4pt,\s!script=4pt,\s!scriptscript=4pt,
+ \s!x=4pt,\s!xx=4pt,\v!big=6pt,\v!small=4pt]
+
+ \definebodyfontenvironment
+ [5pt]
+ [\s!text=5pt,\s!script=5pt,\s!scriptscript=5pt,
+ \s!x=5pt,\s!xx=5pt,\v!big=7pt,\v!small=5pt]
+
+ \definebodyfontenvironment
+ [6pt]
+ [\s!text=6pt,\s!script=5pt,\s!scriptscript=5pt,
+ \s!x=5pt,\s!xx=5pt,\v!big=8pt,\v!small=5pt]
+
+ \definebodyfontenvironment
+ [7pt]
+ [\s!text=7pt,\s!script=6pt,\s!scriptscript=5pt,
+ \s!x=6pt,\s!xx=5pt,\v!big=9pt,\v!small=5pt]
+
+ \definebodyfontenvironment
+ [8pt]
+ [\s!text=8pt,\s!script=6pt,\s!scriptscript=5pt,
+ \s!x=6pt,\s!xx=5pt,\v!big=10pt,\v!small=6pt]
+
+ \definebodyfontenvironment
+ [9pt]
+ [\s!text=9pt,\s!script=7pt,\s!scriptscript=5pt,
+ \s!x=7pt,\s!xx=5pt,\v!big=11pt,\v!small=7pt]
+
+ \definebodyfontenvironment
+ [10pt]
+ [\s!text=10pt,\s!script=7pt,\s!scriptscript=5pt,
+ \s!x=8pt,\s!xx=6pt,\v!big=12pt,\v!small=8pt]
+
+ \definebodyfontenvironment
+ [11pt]
+ [\s!text=11pt,\s!script=8pt,\s!scriptscript=6pt,
+ \s!x=9pt,\s!xx=7pt,\v!big=13pt,\v!small=9pt]
+
+ \definebodyfontenvironment
+ [12pt]
+ [\s!text=12pt,\s!script=9pt,\s!scriptscript=7pt,
+ \s!x=10pt,\s!xx=8pt,\v!big=14pt,\v!small=10pt]
+
+ \definebodyfontenvironment
+ [13pt]
+ [\s!text=13pt,\s!script=10pt,\s!scriptscript=8pt,
+ \s!x=11pt,\s!xx=9pt,\v!big=16pt,\v!small=11pt]
+
+ \definebodyfontenvironment
+ [14pt]
+ [\s!text=14pt,\s!script=10pt,\s!scriptscript=7pt,
+ \s!x=12pt,\s!xx=10pt,\v!big=16pt,\v!small=12pt]
+
+ \definebodyfontenvironment
+ [16pt]
+ [\s!text=16pt,\s!script=12pt,\s!scriptscript=9pt,
+ \s!x=13pt,\s!xx=11pt,\v!big=18pt,\v!small=14pt]
+
+ % suboptimal math sizes
+
+ \definebodyfontenvironment
+ [18pt]
+ [\s!text=18pt,\s!script=16pt,\s!scriptscript=14pt,
+ \s!x=16pt,\s!xx=14pt,\v!big=22pt,\v!small=16pt]
+
+ \definebodyfontenvironment
+ [22pt]
+ [\s!text=22pt,\s!script=22pt,\s!scriptscript=18pt,
+ \s!x=18pt,\s!xx=16pt,\v!big=28pt,\v!small=18pt]
+
+ \definebodyfontenvironment
+ [28pt]
+ [\s!text=28pt,\s!script=22pt,\s!scriptscript=18pt,
+ \s!x=22pt,\s!xx=18pt,\v!big=28pt,\v!small=22pt]
+
+\stoptypescript
+
+\starttypescript [\s!serif] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!rm] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!sans] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!ss] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!mono] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!tt] [\s!default]
+\stoptypescript
+
+\starttypescript [\s!math] [\s!dtp] [\s!size]
+ \definebodyfont
+ [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,13pt,14pt,16pt,18pt,22pt,28pt]
+ [\s!mm] [\s!default]
+\stoptypescript
+
+\stoptypescriptcollection
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-bld.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-bld.lmt
new file mode 100644
index 00000000000..599106ccd61
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-bld.lmt
@@ -0,0 +1,414 @@
+if modules then modules = { } end modules ['typo-bld'] = { -- was node-par
+ version = 1.001,
+ comment = "companion to typo-bld.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- no need for nuts in the one-line demo (that might move anyway)
+
+local insert, remove = table.insert, table.remove
+
+builders = builders or { }
+local builders = builders
+
+builders.paragraphs = builders.paragraphs or { }
+local parbuilders = builders.paragraphs
+
+parbuilders.constructors = parbuilders.constructors or { }
+local constructors = parbuilders.constructors
+
+constructors.names = constructors.names or { }
+local names = constructors.names
+
+constructors.numbers = constructors.numbers or { }
+local numbers = constructors.numbers
+
+constructors.methods = constructors.methods or { }
+local methods = constructors.methods
+
+local a_parbuilder = attributes.numbers['parbuilder'] or 999 -- why 999
+constructors.attribute = a_parbuilder
+
+local unsetvalue = attributes.unsetvalue
+local texsetattribute = tex.setattribute
+local texnest = tex.nest
+local texlists = tex.lists
+
+local texget = tex.get
+local texset = tex.set
+
+local texgetdimen = tex.getdimen
+
+local nodes = nodes
+local nodeidstostring = nodes.idstostring
+local nodepool = nodes.pool
+local new_baselineskip = nodepool.baselineskip
+local new_lineskip = nodepool.lineskip
+local insert_node_before = nodes.insert_before
+local hpack_node = nodes.hpack
+
+local nuts = nodes.nuts
+local tonode = nodes.tonode
+local tonut = nodes.tonut
+local count_nodes = nuts.countall
+local getattr = nuts.getattr
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+local registercallback = callbacks.register
+
+storage.register("builders/paragraphs/constructors/names", names, "builders.paragraphs.constructors.names")
+storage.register("builders/paragraphs/constructors/numbers", numbers, "builders.paragraphs.constructors.numbers")
+
+local trace_page_builder = false trackers.register("builders.page", function(v) trace_page_builder = v end)
+local trace_vbox_builder = false trackers.register("builders.vbox", function(v) trace_vbox_builder = v end)
+local trace_post_builder = false trackers.register("builders.post", function(v) trace_post_builder = v end)
+
+local report_page_builder = logs.reporter("builders","page")
+local report_vbox_builder = logs.reporter("builders","vbox")
+local report_par_builder = logs.reporter("builders","par")
+
+local mainconstructor = nil -- not stored in format
+local nofconstructors = 0
+local stack = { }
+
+function constructors.define(name)
+ nofconstructors = nofconstructors + 1
+ names[nofconstructors] = name
+ numbers[name] = nofconstructors
+end
+
+function constructors.set(name) --- will go
+ if name then
+ mainconstructor = numbers[name] or unsetvalue
+ else
+ mainconstructor = stack[#stack] or unsetvalue
+ end
+ texsetattribute(a_parbuilder,mainconstructor)
+ if mainconstructor ~= unsetvalue then
+ constructors.enable()
+ end
+end
+
+function constructors.start(name)
+ local number = numbers[name]
+ insert(stack,number)
+ mainconstructor = number or unsetvalue
+ texsetattribute(a_parbuilder,mainconstructor)
+ if mainconstructor ~= unsetvalue then
+ constructors.enable()
+ end
+ -- report_par_builder("start %a",name)
+end
+
+function constructors.stop()
+ remove(stack)
+ mainconstructor = stack[#stack] or unsetvalue
+ texsetattribute(a_parbuilder,mainconstructor)
+ if mainconstructor == unsetvalue then
+ constructors.disable()
+ end
+ -- report_par_builder("stop")
+end
+
+-- return values:
+--
+-- true : tex will break itself
+-- false : idem but dangerous
+-- head : list of valid vmode nodes with last being hlist
+
+function constructors.handler(head,followed_by_display)
+ if type(head) == "boolean" then
+ return head
+ else
+ local attribute = getattr(head,a_parbuilder) -- or mainconstructor
+ if attribute then
+ local method = names[attribute]
+ if method then
+ local handler = methods[method]
+ if handler then
+ return handler(head,followed_by_display)
+ else
+ report_par_builder("contructor method %a is not defined",tostring(method))
+ return true -- let tex break
+ end
+ end
+ end
+ return true -- let tex break
+ end
+end
+
+-- just for testing
+
+function constructors.methods.default(head,followed_by_display)
+ return true -- let tex break
+end
+
+-- also for testing (now also surrounding spacing done)
+
+function parbuilders.constructors.methods.oneline(head,followed_by_display)
+ -- when needed we will turn this into a helper
+ local t = texnest[texnest.ptr]
+ local h = hpack_node(head)
+ local d = texget("baselineskip",false) - t.prevdepth - h.height
+ t.prevdepth = h.depth
+ t.prevgraf = 1
+ if d < texget("lineskiplimit") then
+ return insert_node_before(h,h,new_lineskip(texget("lineskip",false))) -- no stretch etc
+ else
+ return insert_node_before(h,h,new_baselineskip(d))
+ end
+end
+
+-- It makes no sense to have a sequence here as we already have
+-- pre and post hooks and only one parbuilder makes sense, so no:
+--
+-- local actions = nodes.tasks.actions("parbuilders")
+--
+-- yet ... maybe some day.
+
+local actions = constructors.handler
+local enabled = false
+
+local function processor(head,followed_by_display)
+ -- todo: not again in otr so we need to flag
+ if enabled then
+ starttiming(parbuilders)
+ head = tonut(head)
+ head = actions(head,followed_by_display)
+ head = tonode(head)
+ stoptiming(parbuilders)
+ return head
+ else
+ return true -- let tex do the work
+ end
+end
+
+function constructors.enable () enabled = true end
+function constructors.disable() enabled = false end
+
+registercallback('linebreak_filter', processor, "breaking paragraps into lines")
+
+statistics.register("linebreak processing time", function()
+ return statistics.elapsedseconds(parbuilders)
+end)
+
+-- todo: move from nodes.builders to builders
+
+nodes.builders = nodes.builder or { }
+local builders = nodes.builders
+
+local vboxactions = nodes.tasks.actions("vboxbuilders")
+
+function builders.vpack_filter(head,groupcode,size,packtype,maxdepth,direction)
+ local done = false
+ if head then
+ starttiming(builders)
+ head = tonut(head)
+ if trace_vbox_builder then
+ local before = count_nodes(head)
+ head, done = vboxactions(head,groupcode,size,packtype,maxdepth,direction)
+ local after = count_nodes(head)
+ nodes.processors.tracer("vpack",head,groupcode,before,after,done)
+ else
+ head, done = vboxactions(head,groupcode)
+ end
+ head = tonode(head)
+ stoptiming(builders)
+ end
+ return head, done
+end
+
+-- This one is special in the sense that it has no head and we operate on the mlv. Also,
+-- we need to do the vspacing last as it removes items from the mvl.
+
+local pageactions = nodes.tasks.actions("mvlbuilders")
+----- lineactions = nodes.tasks.actions("linebuilders")
+
+local function report(groupcode,head)
+ report_page_builder("trigger: %s",groupcode)
+ report_page_builder(" vsize : %p",texget("vsize"))
+ report_page_builder(" pagegoal : %p",texget("pagegoal"))
+ report_page_builder(" pagetotal: %p",texget("pagetotal"))
+ report_page_builder(" list : %s",head and nodeidstostring(head) or "<empty>")
+end
+
+-- check why box is called before after_linebreak .. maybe make categories and
+-- call 'm less
+
+-- this will be split into contribute_filter for these 4 so at some point
+-- the check can go away
+
+-- Todo: contrib_head can be any head (kind of) not per se the page one so maybe I will
+-- intercept that in the engine with page_contribute_head or so.
+
+function builders.buildpage_filter(groupcode)
+ local head = texlists.contribute_head
+ if head then
+ local done = false
+ -- called quite often ... maybe time to remove timing
+ starttiming(builders)
+ if trace_page_builder then
+ report(groupcode,head)
+ end
+ head, done = pageactions(head,groupcode)
+ stoptiming(builders)
+ -- -- doesn't work here (not passed on?)
+ -- texset("pagegoal,texget("vsize") - texgetdimen("d_page_floats_inserted_top") - texgetdimen("d_page_floats_inserted_bottom")
+ texlists.contribute_head = head or nil -- needs checking
+ -- tex.setlist("contribute_head",head,head and nodes.tail(head))
+ return done and head or true -- no return value needed
+ else
+ -- happens quite often
+ if trace_page_builder then
+ report(groupcode)
+ end
+-- return nil, false -- no return value needed
+ return nil
+ end
+end
+
+registercallback('vpack_filter', builders.vpack_filter, "vertical spacing etc")
+registercallback('buildpage_filter', builders.buildpage_filter, "vertical spacing etc (mvl)")
+
+statistics.register("v-node processing time", function()
+ return statistics.elapsedseconds(builders)
+end)
+
+local implement = interfaces.implement
+
+implement { name = "defineparbuilder", actions = constructors.define, arguments = "string" }
+implement { name = "setparbuilder", actions = constructors.set, arguments = "string" }
+implement { name = "startparbuilder", actions = constructors.start, arguments = "string" }
+implement { name = "stopparbuilder", actions = constructors.stop }
+implement { name = "enableparbuilder", actions = constructors.enable }
+implement { name = "disableparbuilder", actions = constructors.disable }
+
+-- Here are some tracers:
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local setcolor = nodes.tracers.colors.set
+local listtoutf = nodes.listtoutf
+local new_kern = nuts.pool.kern
+local new_rule = nuts.pool.rule
+local hpack = nuts.hpack
+local getheight = nuts.getheight
+local getdepth = nuts.getdepth
+local getdirection = nuts.getdirection
+local getlist = nuts.getlist
+local setwidth = nuts.setwidth
+local setdirection = nuts.setdirection
+local setlink = nuts.setlink
+local tonode = nuts.tonode
+
+local list = { }
+
+local report_quality = logs.reporter("pack quality")
+
+-- overflow|badness w h d dir
+
+local function vpack_quality(how,n,detail,first,last,filename)
+ if last <= 0 then
+ report_quality("%s vbox",how)
+ elseif first > 0 and first < last then
+ report_quality("%s vbox at line %i - %i in file %a",how,first,last,filename or "?")
+ else
+ report_quality("%s vbox at line %i in file %a",how,filename or "?",last)
+ end
+ list[#list+1] = { "hbox", how, filename, first, last, how }
+end
+
+trackers.register("builders.vpack.quality",function(v)
+ registercallback("vpack_quality",v and vpack_quality or nil,"check vpack quality")
+end)
+
+local report = false
+local show = false
+
+local function hpack_quality(how,detail,n,first,last,filename)
+ n = tonut(n)
+ if report then
+ local str = listtoutf(getlist(n),"",true,nil,true)
+ if last <= 0 then
+ report_quality("%s hbox: %s",how,str)
+ elseif first > 0 and first < last then
+ report_quality("%s hbox at line %i - %i in file %a: %s",how,first,last,filename or "?",str)
+ else
+ report_quality("%s hbox at line %i in file %a: %s",how,last,filename or "?",str)
+ end
+ list[#list+1] = { "hbox", how, filename, first, last, str }
+ end
+ if show then
+ local width = 2*65536
+ local height = getheight(n)
+ local depth = getdepth(n)
+ local direction = getdirection(n)
+ if height < 4*65526 then
+ height = 4*65526
+ end
+ if depth < 2*65526 then
+ depth = 2*65526
+ end
+ local rule = new_rule(width,height,depth)
+ setdirection(rule,direction)
+ if how == "overfull" then
+ setcolor(rule,"red")
+ local kern = new_kern(-detail)
+ setlink(kern,rule)
+ rule = kern
+ elseif how == "underfull" then
+ setcolor(rule,"blue")
+ elseif how == "loose" then
+ setcolor(rule,"magenta")
+ elseif how == "tight" then
+ setcolor(rule,"cyan")
+ end
+ rule = hpack(rule)
+ setwidth(rule,0)
+ setdirection(rule,direction)
+ return tonode(rule) -- can be a nut
+ end
+end
+
+trackers.register("builders.hpack.quality",function(v)
+ report = v
+ registercallback("hpack_quality",(report or show) and hpack_quality or nil,"check hpack quality")
+end)
+
+trackers.register("builders.hpack.overflow",function(v)
+ show = v
+ registercallback("hpack_quality",(report or show) and hpack_quality or nil,"check hpack quality")
+end)
+
+statistics.register("quality reports", function()
+ local n = #list
+ if n > 0 then
+ local t = table.setmetatableindex("number")
+ local fw = 0
+ local hw = 0
+ for i=1,n do
+ local f = list[i][1]
+ local h = list[i][2]
+ if #f > fw then
+ fw = #f
+ end
+ if #h > hw then
+ hw = #h
+ end
+ t[h] = t[h] + 1
+ end
+ logs.startfilelogging(report_quality)
+ for i=1,n do
+ local l = list[i]
+ report_quality("%-" .. fw .. "s [%04i - %04i] : %-" .. hw .. "s %s : %s",file.basename(l[3]),l[4],l[5],l[2],l[1],l[6])
+ end
+ logs.stopfilelogging()
+ report_quality()
+ report_quality("%i entries added to the log file : %s",n,table.sequenced(t))
+ report_quality()
+ end
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-bld.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-bld.mkxl
new file mode 100644
index 00000000000..ff1f569cb86
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-bld.mkxl
@@ -0,0 +1,62 @@
+%D \module
+%D [ file=typo-bld, % was node-par,
+%D version=2008.09.30,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Paragraph Building,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Paragraph Building}
+
+%D This is very experimental, undocumented, subjected to changes, etc. just as
+%D the underlying interfaces. But at least it's cleaned as part of the status-mkiv
+%D cleanup.
+
+% \enabletrackers[builders.hpack.overflow]
+% \enabletrackers[builders.hpack.quality]
+% \enabletrackers[builders.vpack.quality]
+
+% \startparbuilder[default]
+% \input tufte \par
+% \startparbuilder[oneline]
+% \input tufte \par
+% \stopparbuilder
+% \input tufte \par
+% \stopparbuilder
+%
+% \startparbuilder[oneline]
+% \dorecurse{100}{\input ward \par}
+% \stopparbuilder
+
+\unprotect
+
+\registerctxluafile{typo-bld}{autosuffix}
+
+\definesystemattribute[parbuilder][public]
+
+\installcorenamespace {parbuilder}
+
+\permanent\protected\def\defineparbuilder [#1]{\clf_defineparbuilder{#1}}
+\permanent\protected\def\setmainparbuilder[#1]{\clf_setparbuilder{#1}}
+
+\permanent\protected\def\startparbuilder [#1]{\ifhmode\par\fi\clf_startparbuilder{#1}}
+\permanent\protected\def\stopparbuilder {\ifhmode\par\fi\clf_stopparbuilder}
+
+%D There is no high level interface because after all implementing a linebreaker
+%D is not something that the average user will do.
+
+\defineparbuilder[default] % just for testing
+\defineparbuilder[oneline] % just for testing
+\defineparbuilder[basic] % just for testing
+
+%D This hooks in otr so we need to pickup.
+
+\permanent\protected\def\enableparbuilders {\clf_enableparbuilder } % can be public implementor
+\permanent\protected\def\disableparbuilders{\clf_disableparbuilder} % can be public implementor
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt
new file mode 100644
index 00000000000..8d2fcd147b7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt
@@ -0,0 +1,498 @@
+if not modules then modules = { } end modules ['typo-brk'] = {
+ version = 1.001,
+ comment = "companion to typo-brk.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this code dates from the beginning and is kind of experimental; it
+-- will be optimized and improved soon
+
+local next, type, tonumber, tostring = next, type, tonumber, tostring
+local utfbyte, utfchar = utf.byte, utf.char
+local format = string.format
+
+local trace_breakpoints = false trackers.register("typesetters.breakpoints", function(v) trace_breakpoints = v end)
+
+local report_breakpoints = logs.reporter("typesetting","breakpoints")
+
+local nodes, node = nodes, node
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getsubtype = nuts.getsubtype
+local getfont = nuts.getfont
+local getid = nuts.getid
+local getattrlist = nuts.getattrlist
+local getattr = nuts.getattr
+local getlanguage = nuts.getlanguage
+local isglyph = nuts.isglyph
+
+local setattr = nuts.setattr
+local setattrlist = nuts.setattrlist
+local setlink = nuts.setlink
+local setchar = nuts.setchar
+local setdisc = nuts.setdisc
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local setboth = nuts.setboth
+local setsubtype = nuts.setsubtype
+
+local copy_node = nuts.copy_node
+local copy_node_list = nuts.copy_list
+local flush_node = nuts.flush_node
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+local end_of_math = nuts.end_of_math
+local find_attribute = nuts.find_attribute
+local unset_attributes = nuts.unset_attributes
+
+local tonodes = nuts.tonodes
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local nodepool = nuts.pool
+local enableaction = nodes.tasks.enableaction
+
+local v_reset = interfaces.variables.reset
+local v_yes = interfaces.variables.yes
+
+local implement = interfaces.implement
+
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+local new_disc = nodepool.disc
+local new_wordboundary = nodepool.wordboundary
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local kern_code = nodecodes.kern
+local math_code = nodecodes.math
+
+local fontkern_code = kerncodes.fontkern
+local italickern_code = kerncodes.italiccorrection
+
+local is_letter = characters.is_letter
+
+local typesetters = typesetters
+
+typesetters.breakpoints = typesetters.breakpoints or {}
+local breakpoints = typesetters.breakpoints
+
+breakpoints.mapping = breakpoints.mapping or { }
+breakpoints.numbers = breakpoints.numbers or { }
+
+breakpoints.methods = breakpoints.methods or { }
+local methods = breakpoints.methods
+
+local a_breakpoints = attributes.private("breakpoint")
+
+storage.register("typesetters/breakpoints/mapping", breakpoints.mapping, "typesetters.breakpoints.mapping")
+
+local mapping = breakpoints.mapping
+local numbers = breakpoints.mapping
+
+for i=1,#mapping do
+ local m = mapping[i]
+ numbers[m.name] = m
+end
+
+-- this needs a cleanup ... maybe make all of them disc nodes
+
+-- todo: use boundaries
+
+local function insert_break(head,start,stop,before,after,kern)
+ if not kern then
+ local p = new_penalty(before)
+ local g = new_glue()
+ setattrlist(p,start)
+ setattrlist(g,start)
+ insert_node_before(head,start,p)
+ insert_node_before(head,start,g)
+ end
+ local p = new_penalty(after)
+ local g = new_glue()
+ setattrlist(p,start)
+ setattrlist(g,start)
+ insert_node_after(head,stop,g)
+ insert_node_after(head,stop,p)
+end
+
+methods[1] = function(head,start,stop,settings,kern)
+ local p, n = getboth(stop)
+ if p and n then
+ insert_break(head,start,stop,10000,0,kern)
+ end
+ return head, stop
+end
+
+methods[6] = function(head,start,stop,settings,kern)
+ local p = getprev(start)
+ local n = getnext(stop)
+ if p and n then
+ if kern then
+ insert_break(head,start,stop,10000,0,kern)
+ else
+ local l = new_wordboundary()
+ local d = new_disc()
+ local r = new_wordboundary()
+ setattrlist(d,start) -- otherwise basemode is forced and we crash
+ setlink(p,l,d,r,n)
+ if start == stop then
+ setboth(start)
+ setdisc(d,start,nil,copy_node(start))
+ else
+ setprev(start)
+ setnext(stop)
+ setdisc(d,start,nil,copy_node_list(start))
+ end
+ stop = r
+ end
+ end
+ return head, stop
+end
+
+methods[2] = function(head,start) -- ( => (-
+ local p, n = getboth(start)
+ if p and n then
+ local replace
+ head, start, replace = remove_node(head,start)
+ local post = copy_node(replace)
+ local hyphen = copy_node(post)
+ setchar(hyphen,languages.prehyphenchar(getlanguage(post)))
+ setlink(post,hyphen)
+ head, start = insert_node_before(head,start,new_disc(nil,post,replace))
+ setattrlist(start,replace)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[3] = function(head,start) -- ) => -)
+ local p, n = getboth(start)
+ if p and n then
+ local replace
+ head, start, replace = remove_node(head,start)
+ local pre = copy_node(replace)
+ local hyphen = copy_node(pre)
+ setchar(hyphen,languages.prehyphenchar(getlanguage(pre)))
+ setlink(hyphen,pre)
+ head, start = insert_node_before(head,start,new_disc(hyphen,nil,replace)) -- so not pre !
+ setattrlist(start,tmp)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[4] = function(head,start) -- - => - - -
+ local p, n = getboth(start)
+ if p and n then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ setattrlist(start,tmp)
+ setdisc(start,copy_node(tmp),copy_node(tmp),tmp)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[5] = function(head,start,stop,settings) -- x => p q r
+ local p, n = getboth(start)
+ if p and n then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ local attr = getattrlist(tmp)
+ local font = getfont(tmp)
+ local left = settings.left
+ local right = settings.right
+ local middle = settings.middle
+ if left then
+ left = tonodes(tostring(left),font,attr)
+ end
+ if right then
+ right = tonodes(tostring(right),font,attr)
+ end
+ if middle then
+ middle = tonodes(tostring(middle),font,attr)
+ end
+ setdisc(start,left,right,middle)
+ setattrlist(start,attr)
+ flush_node(tmp)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+-- we know we have a limited set
+-- what if characters are replaced by the font handler
+-- do we need to go into disc nodes (or do it as first step but then we need a pre/post font handler)
+
+function breakpoints.handler(head)
+ local _, current = find_attribute(head, a_breakpoints)
+ if current then
+ local done = false
+ local attr = nil
+ local map = nil
+ local current = head
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local a = getattr(current,a_breakpoints)
+ if a and a > 0 then
+ if a ~= attr then
+ local data = mapping[a]
+ if data then
+ map = data.characters
+ else
+ map = nil
+ end
+ attr = a
+ end
+ if map then
+ local cmap = map[char]
+ if cmap then
+ -- for now we collect but when found ok we can move the handler here
+ -- although it saves nothing in terms of performance
+ local lang = getlanguage(current)
+ local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[languages.numbers[lang]] or cmap[""])
+ if smap then
+ local skip = smap.skip
+ local start = current
+ local stop = current
+ current = getnext(current)
+ if skip then
+ while current do
+ local c = isglyph(current)
+ if c == char then
+ stop = current
+ current = getnext(current)
+ else
+ break
+ end
+ end
+ end
+ local d = { start, stop, cmap, smap, char }
+ if done then
+ done[#done+1] = d
+ else
+ done = { d }
+ end
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ elseif id == math_code then
+ attr = nil
+ current = end_of_math(current)
+ if current then
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ end
+ if not done then
+ return head
+ end
+ -- we have hits
+ -- local numbers = languages.numbers
+ for i=1,#done do
+ local data = done[i]
+ local start = data[1]
+ local stop = data[2]
+ local cmap = data[3]
+ local smap = data[4]
+ -- we do a sanity check for language
+ -- local lang = getlanguage(start)
+ -- local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[numbers[lang]] or cmap[""])
+ -- if smap then
+ local nleft = smap.nleft
+ local cleft = 0
+ local prev = getprev(start)
+ local kern = nil
+ while prev and nleft ~= cleft do
+ local char, id = isglyph(prev)
+ if char then
+ if not is_letter[char] then
+ cleft = -1
+ break
+ end
+ cleft = cleft + 1
+ prev = getprev(prev)
+ elseif id == kern_code then
+ local s = getsubtype(prev)
+ if s == fontkern_code or s == italickern_code then
+ if cleft == 0 then
+ kern = prev
+ prev = getprev(prev)
+ else
+ break
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if nleft == cleft then
+ local nright = smap.nright
+ local cright = 0
+ local next = getnext(stop) -- getnext(start)
+ while next and nright ~= cright do
+ local char, id = isglyph(next)
+ if char then
+ if not is_letter[char] then
+ cright = -1
+ break
+ end
+ if cright == 1 and cmap[char] then
+ -- let's not make it too messy
+ break
+ end
+ cright = cright + 1
+ next = getnext(next)
+ elseif id == kern_code then
+ local s = getsubtype(next)
+ if s == fontkern_code or s == italickern_code then
+ if cleft == 0 then
+ next = getnext(next)
+ else
+ break
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if nright == cright then
+ local method = methods[smap.type]
+ if method then
+ head, start = method(head,start,stop,smap,kern)
+ end
+ end
+ -- end
+ end
+ end
+ if done then
+-- unset_attributes(a_breakpoints, head)
+ end
+ end
+ return head
+end
+
+local enabled = false
+
+function breakpoints.define(name)
+ local data = numbers[name]
+ if data then
+ -- error
+ else
+ local number = #mapping + 1
+ local data = {
+ name = name,
+ number = number,
+ characters = { },
+ }
+ mapping[number] = data
+ numbers[name] = data
+ end
+end
+
+function breakpoints.setreplacement(name,char,language,settings)
+ char = utfbyte(char)
+ local data = numbers[name]
+ if data then
+ local characters = data.characters
+ local cmap = characters[char]
+ if not cmap then
+ cmap = { }
+ characters[char] = cmap
+ end
+ local left, right, middle = settings.left, settings.right, settings.middle
+ cmap[language or ""] = {
+ type = tonumber(settings.type) or 1,
+ nleft = tonumber(settings.nleft) or 1,
+ nright = tonumber(settings.nright) or 1,
+ left = left ~= "" and left or nil,
+ right = right ~= "" and right or nil,
+ middle = middle ~= "" and middle or nil,
+ skip = settings.range == v_yes,
+ } -- was { type or 1, before or 1, after or 1 }
+ end
+end
+
+function breakpoints.set(n)
+ if n == v_reset then
+ n = unsetvalue
+ else
+ n = mapping[n]
+ if not n then
+ n = unsetvalue
+ else
+ if not enabled then
+ if trace_breakpoints then
+ report_breakpoints("enabling breakpoints handler")
+ end
+ enableaction("processors","typesetters.breakpoints.handler")
+ end
+ n = n.number
+ end
+ end
+ texsetattribute(a_breakpoints,n)
+end
+
+-- interface
+
+implement {
+ name = "definebreakpoints",
+ actions = breakpoints.define,
+ arguments = "string"
+}
+
+implement {
+ name = "definebreakpoint",
+ actions = breakpoints.setreplacement,
+ arguments = {
+ "string",
+ "string",
+ "string",
+ {
+ { "type", "integer" },
+ { "nleft", "integer" },
+ { "nright", "integer" },
+ { "right" },
+ { "left" },
+ { "middle" },
+ { "range" },
+ }
+ }
+}
+
+implement {
+ name = "setbreakpoints",
+ actions = breakpoints.set,
+ arguments = "string"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-brk.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-brk.mkxl
new file mode 100644
index 00000000000..47e5da886e2
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-brk.mkxl
@@ -0,0 +1,94 @@
+%D \module
+%D [ file=typo-brk,
+%D version=2009.03.27, % code moved from core-spa.mkiv
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Breakpoints,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Breakpoints}
+
+\unprotect
+
+% hm, we cannot prebuild lists, font dependent
+%
+% -- type nleft nright language left right middle
+%
+% -- we might eventually stick to only method 5
+
+\registerctxluafile{typo-brk}{autosuffix}
+
+\definesystemattribute[breakpoint][public,global]
+
+% see below: \exhyphenchar \minusone % we use a different order than base tex, so we really need this
+
+\permanent\tolerant\protected\def\definebreakpoints[#1]%
+ {\clf_definebreakpoints{#1}} % todo: public implementor
+
+\permanent\tolerant\protected\def\definebreakpoint[#1]#*[#2]#*[#3]% name char settings
+ {\begingroup
+ \getdummyparameters
+ [\c!type=\plusone,\c!nleft=\plusthree,\c!nright=\plusthree,%
+ \s!language=,\c!left=,\c!right=,\c!middle=,\c!range=\v!no,%
+ #3]%
+ \clf_definebreakpoint
+ {#1}%
+ {#2}%
+ {\reallanguagetag{\directdummyparameter\s!language}}%
+ {% maybe deal with #3 at the lua end
+ type \directdummyparameter\c!type
+ nleft \directdummyparameter\c!nleft
+ nright \directdummyparameter\c!nright
+ right {\directdummyparameter\c!right}%
+ left {\directdummyparameter\c!left}%
+ middle {\directdummyparameter\c!middle}%
+ range {\directdummyparameter\c!range}%
+ }%
+ \relax
+ \endgroup}
+
+\permanent\protected\def\setbreakpoints[#1]%
+ {\exhyphenchar\minusone % we use a different order than base tex, so we really need this
+ \clf_setbreakpoints{#1}}
+
+\permanent\protected\def\resetbreakpoints
+ {\exhyphenchar\hyphenasciicode % 2020.03.05
+ \c_attr_breakpoint\attributeunsetvalue}
+
+\definebreakpoints[compound]
+
+% 1: simple break
+% 6: simple break but skip same
+
+\definebreakpoint [compound] [+] [\c!nleft=3,\c!nright=3,\c!type=1] % middle=+,left=,right=
+%definebreakpoint [compound] [-] [\c!nleft=3,\c!nright=3,\c!type=1,\c!range=\v!yes] % middle=+,left=,right=
+\definebreakpoint [compound] [-] [\c!nleft=3,\c!nright=3,\c!type=6,\c!range=\v!yes] % middle=+,left=,right=
+\definebreakpoint [compound] [/] [\c!nleft=3,\c!nright=3,\c!type=1] % middle=+,left=,right=
+\definebreakpoint [compound] [(] [\c!nleft=3,\c!nright=3,\c!type=2] % type=5,middle=(,left=(-,right=
+\definebreakpoint [compound] [)] [\c!nleft=3,\c!nright=3,\c!type=3] % type=5,middle=),left=,right=-)
+
+\aliased\let\installbreakpoint\definebreakpoint % for a while
+
+% \start \hsize 1.5cm \setbreakpoints[compound]
+% \definebreakpoint [compound] [-] [nleft=3,nright=3,type=5,left=,right={-},middle={-}]
+% \definebreakpoint [compound] [(] [nleft=3,nright=3,type=5,left=,right={(-},middle={(}]
+% \definebreakpoint [compound] [)] [nleft=3,nright=3,type=5,left={-)},right=,middle={)}]
+% composed-word\par composed(word)\par
+% \stop
+
+% \mainlanguage[czech]
+% \definebreakpoint [compound] [\number`-] [language=cs,nleft=3,nright=3,type=4]
+% \setbreakpoints[compound]
+% \start \hsize 1mm test-test \par \stop
+
+%D Maybe some day default:
+%
+% \setbreakpoints[compound]
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-cap.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-cap.lmt
new file mode 100644
index 00000000000..67acb2535c3
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-cap.lmt
@@ -0,0 +1,458 @@
+if not modules then modules = { } end modules ['typo-cap'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to typo-cap.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- see typo-cap.lua for a word traverser variant
+
+local next, type, tonumber = next, type, tonumber
+local format, insert = string.format, table.insert
+local div, getrandom, random = math.div, utilities.randomizer.get, math.random
+
+local trace_casing = false trackers.register("typesetters.casing", function(v) trace_casing = v end)
+
+local report_casing = logs.reporter("typesetting","casing")
+
+local nodes, node = nodes, node
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getattr = nuts.getattr
+local getfont = nuts.getfont
+local getsubtype = nuts.getsubtype
+local getchar = nuts.getchar
+local isglyph = nuts.isglyph
+local getdisc = nuts.getdisc
+
+local setchar = nuts.setchar
+local setfont = nuts.setfont
+local setscales = nuts.setscales
+
+local copy_node = nuts.copy
+local end_of_math = nuts.end_of_math
+local insert_after = nuts.insert_after
+local find_attribute = nuts.find_attribute
+local unset_attributes = nuts.unset_attributes
+
+local nextglyph = nuts.traversers.glyph
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local disc_code = nodecodes.disc
+local math_code = nodecodes.math
+
+local fontkern_code = kerncodes.fontkern
+
+local enableaction = nodes.tasks.enableaction
+
+local newkern = nuts.pool.kern
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontchar = fonthashes.characters
+
+local currentfont = font.current
+
+local variables = interfaces.variables
+local v_reset = variables.reset
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+local texgetcount = tex.getcount
+
+typesetters = typesetters or { }
+local typesetters = typesetters
+
+typesetters.cases = typesetters.cases or { }
+local cases = typesetters.cases
+
+cases.actions = { }
+local actions = cases.actions
+local a_cases = attributes.private("case")
+
+local run = 0 -- a trick to make neighbouring ranges work
+local blocked = { }
+
+local fontstate = { } -- this will become something generic
+
+local function set(tag,font)
+ if run < 0x7F then
+ run = run + 1
+ else
+ run = 1
+ end
+ local a = (font << 16)
+ + (tag << 8)
+ + (run << 0)
+ blocked[a] = false
+ -- it makes sense to fetch them all at once
+ fontstate[a] = {
+ texgetcount("glyphscale"),
+ texgetcount("glyphxscale"),
+ texgetcount("glyphyscale"),
+ texgetcount("glyphdatafield")
+ }
+ return a
+end
+
+local function get(a)
+ return
+ (a >> 8) & 0x00FF, -- tag
+ (a >> 16) & 0xFFFF, -- font
+ (a >> 0) & 0x00FF -- run
+end
+
+-- a previous implementation used char(0) as placeholder for the larger font, so we needed
+-- to remove it before it can do further harm ... that was too tricky as we use char 0 for
+-- other cases too
+--
+-- we could do the whole glyph run here (till no more attributes match) but then we end up
+-- with more code .. maybe i will clean this up anyway as the lastfont hack is somewhat ugly
+-- ... on the other hand, we need to deal with cases like:
+--
+-- \WORD {far too \Word{many \WORD{more \word{pushed} in between} useless} words}
+
+local uccodes = characters.uccodes
+local lccodes = characters.lccodes
+local categories = characters.categories
+
+-- true false true == mixed
+
+local function replacer(start,codes)
+ local char, fnt = isglyph(start)
+ local dc = codes[char]
+ if dc then
+ local ifc = fontchar[fnt]
+ if type(dc) == "table" then
+ for i=1,#dc do
+ if not ifc[dc[i]] then
+ return start, false
+ end
+ end
+ for i=#dc,1,-1 do
+ local chr = dc[i]
+ if i == 1 then
+ setchar(start,chr)
+ else
+ local g = copy_node(start)
+ setchar(g,chr)
+ insert_after(start,start,g)
+ end
+ end
+ elseif ifc[dc] then
+ setchar(start,dc)
+ end
+ end
+ return start
+end
+
+local registered, n = { }, 0
+
+local function register(name,f)
+ if type(f) == "function" then
+ n = n + 1
+ actions[n] = f
+ registered[name] = n
+ return n
+ else
+ local n = registered[f]
+ registered[name] = n
+ return n
+ end
+end
+
+cases.register = register
+
+local function WORD(start,attr,lastfont,n,count,where,first)
+ lastfont[n] = false
+ return replacer(first or start,uccodes)
+end
+
+local function word(start,attr,lastfont,n,count,where,first)
+ lastfont[n] = false
+ return replacer(first or start,lccodes)
+end
+
+local function Words(start,attr,lastfont,n,count,where,first) -- looks quite complex
+ if where == "post" then
+ return
+ end
+ if count == 1 and where ~= "post" then
+ replacer(first or start,uccodes)
+ return start, true
+ else
+ return start, true
+ end
+end
+
+local function Word(start,attr,lastfont,n,count,where,first)
+ blocked[attr] = true
+ return Words(start,attr,lastfont,n,count,where,first)
+end
+
+local function camel(start,attr,lastfont,n,count,where,first)
+ word(start,attr,lastfont,n,count,where,first)
+ Words(start,attr,lastfont,n,count,where,first)
+ return start, true
+end
+
+local function mixed(start,attr,lastfont,n,count,where,first)
+ if where == "post" then
+ return
+ end
+ local used = first or start
+ local char = getchar(used)
+ local dc = uccodes[char]
+ if not dc then
+ -- quit
+ elseif dc == char then
+ local lfa = lastfont[n]
+ if lfa then
+ local s = fontstate[attr]
+ setfont(used,lfa)
+ if s then
+ setscales(used, s[1], s[2], s[3])
+ end
+ end
+ else
+ replacer(used,uccodes)
+ end
+ return start, true
+end
+
+local function Capital(start,attr,lastfont,n,count,where,first,once) -- 3
+ local used = first or start
+ if count == 1 and where ~= "post" then
+ local lfa = lastfont[n]
+ if lfa then
+ local dc = uccodes[getchar(used)]
+ if dc then
+ local s = fontstate[attr]
+ setfont(used,lfa)
+ if s then
+ setscales(used, s[1], s[2], s[3])
+ end
+ end
+ end
+ end
+ local s, c = replacer(first or start,uccodes)
+ if once then
+ lastfont[n] = false -- here
+ end
+ return start, c
+end
+
+local function capital(start,attr,lastfont,n,where,count,first,count) -- 4
+ return Capital(start,attr,lastfont,n,where,count,first,true)
+end
+
+local function none(start,attr,lastfont,n,count,where,first)
+ return start, true
+end
+
+local function randomized(start,attr,lastfont,n,count,where,first)
+ local used = first or start
+ local char = getchar(used)
+ local font = getfont(used)
+ local tfm = fontchar[font]
+ lastfont[n] = false
+ local kind = categories[char]
+ if kind == "lu" then
+ while true do
+ local n = getrandom("capital lu",0x41,0x5A)
+ if tfm[n] then -- this also intercepts tables
+ setchar(used,n)
+ return start
+ end
+ end
+ elseif kind == "ll" then
+ while true do
+ local n = getrandom("capital ll",0x61,0x7A)
+ if tfm[n] then -- this also intercepts tables
+ setchar(used,n)
+ return start
+ end
+ end
+ end
+ return start
+end
+
+register(variables.WORD, WORD) -- 1
+register(variables.word, word) -- 2
+register(variables.Word, Word) -- 3
+register(variables.Words, Words) -- 4
+register(variables.capital,capital) -- 5
+register(variables.Capital,Capital) -- 6
+register(variables.none, none) -- 7 (dummy)
+register(variables.random, randomized) -- 8
+register(variables.mixed, mixed) -- 9
+register(variables.camel, camel) -- 10
+
+register(variables.cap, variables.capital) -- clone
+register(variables.Cap, variables.Capital) -- clone
+
+function cases.handler(head)
+ local _, start = find_attribute(head, a_cases)
+ if start then
+ local lastfont = { }
+ local lastattr = nil
+ local count = 0
+ local done = false
+ while start do -- while because start can jump ahead
+ local id = getid(start)
+ if id == glyph_code then
+ local attr = getattr(start,a_cases)
+ if attr and attr > 0 and not blocked[attr] then
+ if attr ~= lastattr then
+ lastattr = attr
+ count = 1
+ else
+ count = count + 1
+ end
+ local n, id, m = get(attr)
+ if lastfont[n] == nil then
+ lastfont[n] = id
+ end
+ local action = actions[n] -- map back to low number
+ if action then
+ local quit
+ start, quit = action(start,attr,lastfont,n,count)
+ if trace_casing then
+ report_casing("case trigger %a, instance %a, fontid %a, result %a",n,m,id,quit and "-" or "+")
+ end
+ elseif trace_casing then
+ report_casing("unknown case trigger %a",n)
+ end
+ done = true
+ end
+ elseif id == disc_code then
+ local attr = getattr(start,a_cases)
+ if attr and attr > 0 and not blocked[attr] then
+ if attr ~= lastattr then
+ lastattr = attr
+ count = 0
+ end
+ local n, id, m = get(attr)
+ if lastfont[n] == nil then
+ lastfont[n] = id
+ end
+ local action = actions[n] -- map back to low number
+ if action then
+ local pre, post, replace = getdisc(start)
+ if replace then
+ local cnt = count
+ for g in nextglyph, replace do
+ cnt = cnt + 1
+ getattr(g,a_cases)
+ local h, quit = action(start,attr,lastfont,n,cnt,"replace",g)
+ if quit then
+ break
+ end
+ end
+ end
+ if pre then
+ local cnt = count
+ for g in nextglyph, pre do
+ cnt = cnt + 1
+ getattr(g,a_cases)
+ local h, quit = action(start,attr,lastfont,n,cnt,"pre",g)
+ if quit then
+ break
+ end
+ end
+ end
+ if post then
+ local cnt = count
+ for g in nextglyph, post do
+ cnt = cnt + 1
+ getattr(g,a_cases)
+ local h, quit = action(start,attr,lastfont,n,cnt,"post",g)
+ if quit then
+ break
+ end
+ end
+ end
+ end
+ count = count + 1
+ done = true
+ end
+ else
+ if id == math_code then
+ start = end_of_math(start)
+ end
+ count = 0
+ end
+ if start then
+ start = getnext(start)
+ end
+ end
+ if done then
+ -- unset_attributes(a_cases,head)
+ end
+ end
+ return head
+end
+
+local enabled = false
+
+function cases.set(n,id)
+ if n == v_reset then
+ n = unsetvalue
+ else
+ n = registered[n] or tonumber(n)
+ if n then
+ if not enabled then
+ enableaction("processors","typesetters.cases.handler")
+ if trace_casing then
+ report_casing("enabling case handler")
+ end
+ enabled = true
+ end
+ n = set(n,id or currentfont())
+ else
+ n = unsetvalue
+ end
+ end
+ texsetattribute(a_cases,n)
+ -- return n -- bonus
+end
+
+-- interface
+
+interfaces.implement {
+ name = "setcharactercasing",
+ -- public = true,
+ -- protected = true,
+ actions = cases.set,
+ arguments = { "argument" }
+}
+
+-- An example of a special plug, see type-imp-punk for usage.
+
+cases.register("randomvariant", function(start)
+ local char, fnt = isglyph(start)
+ local data = fontchar[fnt][char]
+ if data then
+ local variants = data.variants
+ if variants then
+ local n = #variants
+ local i = getrandom("variant",1,n+1)
+ if i > n then
+ -- we keep the original
+ else
+ setchar(start,variants[i])
+ end
+ return start, true
+ end
+ end
+ return start, false
+end)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-cap.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-cap.mkxl
new file mode 100644
index 00000000000..bab69e130e7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-cap.mkxl
@@ -0,0 +1,271 @@
+%D \module
+%D [ file=typo-cap,
+%D version=2009.03.27, % code moved from core-spa.mkiv
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Capping,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Caps}
+
+\unprotect
+
+%D Maybe we need a more clever system: either command or style mode etc. so
+%D that we can avoid the grouped mess in a simple style switch.
+
+% \registerctxluafile{typo-cap}{autosuffix,optimize}
+\registerctxluafile{typo-cap}{autosuffix}
+
+% \definesystemattribute[case][public] % already predefined
+
+%D \macros
+%D {setupcapitals}
+%D
+%D By default we use pseudo small caps in titles. This can be set up with:
+%D
+%D \showsetup{setupcapitals}
+
+\installcorenamespace{capitals}
+
+\installcommandhandler \??capitals {capitals} \??capitals
+
+%D Beware, these are not really defines (yet).
+
+\definecapitals[\v!WORD] % all upper
+\definecapitals[\v!capital] % one upper + font
+\definecapitals[\v!Capital] % some upper + font
+\definecapitals[\v!mixed] % UpperCase
+\definecapitals[\v!Word] % one upper + font
+\definecapitals[\v!Words] % some upper
+\definecapitals[\v!camel] % lowers first
+\definecapitals[\v!word][\c!style=] % nothing
+
+%D \macros
+%D {Word, Words, WORD, WORDS}
+%D
+%D This is probably not the right place to present the next set of macros.
+%D
+%D \starttyping
+%D \Word {far too many words}
+%D \Words{far too many words}
+%D \WORD {far too many words}
+%D \WORDS{far too many words}
+%D \stoptyping
+%D
+%D \typebuffer
+%D
+%D This calls result in:
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D \showsetup{Word}
+%D \showsetup{Words}
+%D \showsetup{WORD}
+%D \showsetup{WORDS}
+
+% test \WORD{test TEST \TeX} test
+% test \word{test TEST \TeX} test
+% test \Word{test TEST \TeX} test
+
+\permanent\protected\def\setcharactercasing[#1]{\clf_setcharactercasing{#1}} % can be public implementor
+
+\protected\def\typo_capitale_WORD {\clf_setcharactercasing\v!WORD }
+\protected\def\typo_capitale_word {\clf_setcharactercasing\v!word }
+\protected\def\typo_capitale_Word {\clf_setcharactercasing\v!Word }
+\protected\def\typo_capitale_Words{\clf_setcharactercasing\v!Words}
+\protected\def\typo_capitale_camel{\clf_setcharactercasing\v!camel}
+
+\pushoverloadmode
+
+\permanent\protected\def\WORD {\triggergroupedcommandcs\typo_capitale_WORD }
+\permanent\protected\def\word {\triggergroupedcommandcs\typo_capitale_word }
+\permanent\protected\def\Word {\triggergroupedcommandcs\typo_capitale_Word }
+\permanent\protected\def\Words{\triggergroupedcommandcs\typo_capitale_Words}
+\permanent\protected\def\camel{\triggergroupedcommandcs\typo_capitale_camel}
+
+\aliased\let\WORDS\WORD
+\aliased\let\words\word
+
+\popoverloadmode
+
+%D \macros
+%D {kap,KAP,Kap,Kaps,nokap,userealcaps,usepseudocaps}
+%D
+%D We already introduced \type {\cap} as way to capitalize words. This command comes
+%D in several versions:
+%D
+%D \startbuffer
+%D \cap {let's put on a \cap{cap}}
+%D \cap {let's put on a \nocap{cap}}
+%D \CAP {let's put on a \\{cap}}
+%D \Cap {let's put on a \\{cap}}
+%D \Caps{let's put on a cap}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Note the use of \type {\nocap}, \type {\\} and the nested \type {\cap}.
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+%D
+%D These macros show te main reason why we introduced the smaller \type {\tx} and
+%D \type {\txx}.
+%D
+%D \starttyping
+%D \cap\romannumerals{1995}
+%D \stoptyping
+%D
+%D This at first sight unusual capitilization is completely legal.
+%D
+%D \showsetup{smallcapped}
+%D \showsetup{notsmallcapped}
+%D \showsetup{CAPPED}
+%D \showsetup{SmallCapped}
+%D \showsetup{SmallCaps}
+%D
+%D The difference between pseudo and real caps is demonstrated below:
+%D
+%D \startbuffer
+%D \usepseudocaps \cap{Hans Hagen}
+%D \userealcaps \cap{Hans Hagen}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \getbuffer
+%D
+%D The \type {\bgroup} trickery below is needed because of \type {\groupedcommand}.
+
+\newconditional\c_typo_capitals_pseudo
+
+\permanent\protected\def\usepseudocaps{\settrue \c_typo_capitals_pseudo}
+\permanent\protected\def\userealcaps {\setfalse\c_typo_capitals_pseudo}
+
+\usepseudocaps
+
+% we use char0 as placeholder for the larger font
+%
+% here we keep the \groupedcommand
+
+\def\typo_capitals_set_fake#1%
+ {\edef\currentcapitals{#1}%
+ \clf_setcharactercasing\currentcapitals
+ \usecapitalsstyleparameter\c!style}
+
+\def\typo_capitals_set_real#1%
+ {\edef\currentcapitals{#1}%
+ \sc
+ \clf_setcharactercasing\currentcapitals}
+
+\permanent\protected\def\pseudosmallcapped{\triggergroupedcommandcs\font_style_pseudosmallcapped}
+\permanent\protected\def\pseudoSmallcapped{\triggergroupedcommandcs\font_style_pseudoSmallcapped}
+\permanent\protected\def\pseudoSmallCapped{\triggergroupedcommandcs\font_style_pseudoSmallCapped}
+\permanent\protected\def\pseudoMixedCapped{\triggergroupedcommandcs\font_style_pseudoMixedCapped}
+
+\permanent\protected\def\realsmallcapped {\triggergroupedcommandcs\font_style_realsmallcapped}
+\permanent\protected\def\realSmallcapped {\triggergroupedcommandcs\font_style_realSmallcapped}
+\permanent\protected\def\realSmallCapped {\triggergroupedcommandcs\font_style_realSmallCapped}
+
+\permanent\protected\def\notsmallcapped {\triggergroupedcommandcs\font_style_notsmallcapped}
+
+\protected\def\font_style_pseudosmallcapped{\typo_capitals_set_fake\v!WORD } % all upper
+\protected\def\font_style_pseudoSmallcapped{\typo_capitals_set_fake\v!capital} % one upper + font
+\protected\def\font_style_pseudoSmallCapped{\typo_capitals_set_fake\v!Capital} % some upper + font
+\protected\def\font_style_pseudoMixedCapped{\typo_capitals_set_fake\v!mixed }
+
+\protected\def\font_style_realsmallcapped {\typo_capitals_set_real\v!WORD } % all lower
+\protected\def\font_style_realSmallcapped {\typo_capitals_set_real\v!Word } % one upper + font
+\protected\def\font_style_realSmallCapped {\typo_capitals_set_real\v!Words } % some upper
+
+\protected\def\font_style_notsmallcapped {\typo_capitals_set_fake\v!word }
+
+\protected\def\typo_capitals_smallcaps
+ {\ifconditional\c_typo_capitals_pseudo
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\permanent\protected\def\smallcapped{\typo_capitals_smallcaps\pseudosmallcapped\realsmallcapped}
+\permanent\protected\def\Smallcapped{\typo_capitals_smallcaps\pseudoSmallcapped\realSmallcapped}
+\permanent\protected\def\SmallCapped{\typo_capitals_smallcaps\pseudoSmallCapped\realSmallCapped}
+
+\permanent\protected\def\font_style_smallcapped{\typo_capitals_smallcaps\font_style_pseudosmallcapped\font_style_realsmallcapped}
+\permanent\protected\def\font_style_Smallcapped{\typo_capitals_smallcaps\font_style_pseudoSmallcapped\font_style_realSmallcapped}
+\permanent\protected\def\font_style_SmallCapped{\typo_capitals_smallcaps\font_style_pseudoSmallCapped\font_style_realSmallCapped}
+
+\permanent\protected\def\autocap{\ifmmode\expandafter\normalcap\else\expandafter\smallcapped\fi}
+
+\prependtoks
+ \enforced\let\normalcap\cap % mathmode cap
+\to \everydump
+
+\appendtoks
+ \enforced\let\cap\autocap
+\to \everydump
+
+\ifdefined\kap\else \aliased\let\kap\cap \fi % for old times sake
+
+\aliased\let\Caps\SmallCapped % for old times sake
+
+\aliased\let\mixedcaps\pseudoMixedCapped
+
+\aliased\let\normalsmallcapped\smallcapped
+\aliased\let\normalWORD \WORD
+\aliased\let\normalword \word
+
+\aliased\let\font_style_normalsmallcapped\font_style_smallcapped
+\aliased\let\font_style_normalWORD \WORD
+\aliased\let\font_style_normalword \word
+
+\appendtoks
+ \ifempty\currentcapitals
+ \doifelse{\directcapitalsparameter\c!title}\v!yes
+ {\definealternativestyle[\v!capital ][\font_style_normalsmallcapped][\font_style_normalsmallcapped]%
+ \definealternativestyle[\v!smallcaps][\setsmallcaps][\setsmallcaps]}
+ {\definealternativestyle[\v!capital ][\font_style_normalsmallcapped][\font_style_normalWORD]%
+ \definealternativestyle[\v!smallcaps][\setsmallcaps][\font_style_normalWORD]}%
+ \doifelse{\directcapitalsparameter\s!sc}\v!yes
+ \userealcaps
+ \usepseudocaps
+ \fi
+\to \everysetupcapitals
+
+\aliased\let\uppercased\normalWORD
+\aliased\let\lowercased\normalword
+
+\setupcapitals
+ [\c!title=\v!yes,
+ \c!style=\tx,
+ \s!sc=\v!no] % no \c!sc any longer
+
+\definefont
+ [MixedCaps]
+ [CurrentFont*default cp 1.2\exheight]
+
+\setupcapitals
+ [\v!mixed]
+ [\c!style=MixedCaps]
+
+% \definestartstop is not yet in available at core-spa time
+%
+% \startrandomized \input tufte \stoprandomized
+%
+% \definestartstop[randomized][\c!before=\dosetattribute{case}{8},\c!after=]
+
+% \protected\def\randomizetext{\groupedcommand{\c_attr_case\pluseight}{}}
+
+\permanent\protected\def\randomizetext{\triggergroupedcommand{\c_attr_case\pluseight}}
+
+\definestartstop[randomized][\c!before=\dosetattribute{case}{8},\c!after=]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-chr.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-chr.lmt
new file mode 100644
index 00000000000..bb11f54a64c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-chr.lmt
@@ -0,0 +1,291 @@
+if not modules then modules = { } end modules ['typo-chr'] = {
+ version = 1.001,
+ comment = "companion to typo-bld.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- A historic intermediate version can be found in the mkiv variant. The code
+-- can probably be improved but performance etc is not really an issue here.
+
+local insert, remove = table.insert, table.remove
+
+local context = context
+local ctx_doifelse = commands.doifelse
+
+local nodecodes = nodes.nodecodes
+local boundarycodes = nodes.boundarycodes
+local subtypes = nodes.subtypes
+
+local glyph_code = nodecodes.glyph
+local par_code = nodecodes.par
+local boundary_code = nodecodes.boundary
+
+local wordboundary_code = boundarycodes.word
+
+local texgetnest = tex.getnest -- to be used
+local texsetcount = tex.setcount
+
+local flush_node = nodes.flush_node
+local flush_list = nodes.flush_list
+
+local settexattribute = tex.setattribute
+local punctuation = characters.is_punctuation
+
+local variables = interfaces.variables
+local v_all = variables.all
+local v_reset = variables.reset
+
+local stack = { }
+
+local a_marked = attributes.numbers['marked']
+local lastmarked = 0
+local marked = {
+ [v_all] = 1,
+ [""] = 1,
+ [v_reset] = attributes.unsetvalue,
+}
+
+local function pickup()
+ local list = texgetnest()
+ if list then
+ local tail = list.tail
+ if tail and tail.id == glyph_code and punctuation[tail.char] then
+ local prev = tail.prev
+ list.tail = prev
+ if prev then
+ prev.next = nil
+ end
+ list.tail = prev
+ tail.prev = nil
+ return tail
+ end
+ end
+end
+
+local actions = {
+ remove = function(specification)
+ local n = pickup()
+ if n then
+ flush_node(n)
+ end
+ end,
+ push = function(specification)
+ local n = pickup()
+ if n then
+ insert(stack,n or false)
+ end
+ end,
+ pop = function(specification)
+ local n = remove(stack)
+ if n then
+ context(n)
+ end
+ end,
+}
+
+local function pickuppunctuation(specification)
+ local action = actions[specification.action or "remove"]
+ if action then
+ action(specification)
+ end
+end
+
+-- I played with nested marked content but it makes no sense and gives
+-- complex code. Also, it's never needed so why bother.
+
+local function pickup(head,tail,str)
+ local attr = marked[str]
+ local last = tail
+ if last[a_marked] == attr then
+ local first = last
+ while true do
+ local prev = first.prev
+ if prev and prev[a_marked] == attr then
+ if prev.id == par_code then -- and start_of_par(prev)
+ break
+ else
+ first = prev
+ end
+ else
+ break
+ end
+ end
+ return first, last
+ end
+end
+
+local function found(str)
+ local list = texgetnest()
+ if list then
+ local tail = list.tail
+ return tail and tail[a_marked] == marked[str]
+ end
+end
+
+local actions = {
+ remove = function(specification)
+ local list = texgetnest()
+ if list then
+ local head = list.head
+ local tail = list.tail
+ local first, last = pickup(head,tail,specification.mark)
+ if first then
+ if first == head then
+ list.head = nil
+ list.tail = nil
+ else
+ local prev = first.prev
+ list.tail = prev
+ prev.next = nil
+ end
+ flush_list(first)
+ end
+ end
+ end,
+}
+
+local function pickupmarkedcontent(specification)
+ local action = actions[specification.action or "remove"]
+ if action then
+ action(specification)
+ end
+end
+
+local function markcontent(str)
+ local currentmarked = marked[str or v_all]
+ if not currentmarked then
+ lastmarked = lastmarked + 1
+ currentmarked = lastmarked
+ marked[str] = currentmarked
+ end
+ settexattribute(a_marked,currentmarked)
+end
+
+interfaces.implement {
+ name = "pickuppunctuation",
+ actions = pickuppunctuation,
+ arguments = {
+ {
+ { "action" }
+ }
+ }
+}
+
+interfaces.implement {
+ name = "pickupmarkedcontent",
+ actions = pickupmarkedcontent,
+ arguments = {
+ {
+ { "action" },
+ { "mark" }
+ }
+ }
+}
+
+interfaces.implement {
+ name = "markcontent",
+ actions = markcontent,
+ arguments = "string",
+}
+
+interfaces.implement {
+ name = "doifelsemarkedcontent",
+ actions = function(str) ctx_doifelse(found(str)) end,
+ arguments = "string",
+}
+
+-- We just put these here.
+
+interfaces.implement {
+ name = "lastnodeidstring",
+ public = true,
+ actions = function()
+ local list = texgetnest() -- "top"
+ local okay = false
+ if list then
+ local tail = list.tail
+ if tail then
+ okay = nodecodes[tail.id]
+ end
+ end
+ context(okay or "")
+ end,
+}
+
+-- local t_lastnodeid = token.create("c_syst_last_node_id")
+--
+-- interfaces.implement {
+-- name = "lastnodeid",
+-- public = true,
+-- actions = function()
+-- ...
+-- tex.setcount("c_syst_last_node_id",okay)
+-- context.sprint(t_lastnodeid)
+-- end,
+-- }
+
+-- not needed in lmtx ...
+
+interfaces.implement {
+ name = "lastnodeid",
+ actions = function()
+ local list = texgetnest() -- "top"
+ local okay = -1
+ if list then
+ local tail = list.tail
+ if tail then
+ okay = tail.id
+ end
+ end
+ texsetcount("c_syst_last_node_id",okay)
+ end,
+}
+
+interfaces.implement {
+ name = "lastnodesubtypestring",
+ public = true,
+ actions = function()
+ local list = texgetnest() -- "top"
+ local okay = false
+ if list then
+ local tail = list.tail
+ if head then
+ okay = subtypes[tail.id][tail.subtype]
+ end
+ end
+ context(okay or "")
+ end,
+}
+
+local function lastnodeequals(id,subtype)
+ local list = texgetnest() -- "top"
+ local okay = false
+ if list then
+ local tail = list.tail
+ if tail then
+ local i = tail.id
+ okay = i == id or i == nodecodes[id]
+ if subtype then
+ local s = tail.subtype
+ okay = s == subtype or s == subtypes[i][subtype]
+ end
+ end
+ end
+ ctx_doifelse(okay)
+end
+
+interfaces.implement {
+ name = "lastnodeequals",
+ arguments = "2 strings",
+ actions = lastnodeequals,
+}
+
+interfaces.implement {
+ name = "atwordboundary",
+ actions = function()
+ lastnodeequals(boundary_code,wordboundary_code)
+ end,
+}
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-chr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-chr.mkxl
new file mode 100644
index 00000000000..1c459cfcf35
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-chr.mkxl
@@ -0,0 +1,87 @@
+%D \module
+%D [ file=typo-chr,
+%D version=2015.01.01, % or about that time
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Cleaning Up Mess,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Characters}
+
+\unprotect
+
+%D This is a very experimental feature, mostly for Alan and me to play with in
+%D publication hell.
+%D
+%D \startbuffer
+%D before, after\par
+%D before,\removepunctuation after\par
+%D before\markcontent[gone]{\darkred gone}\removemarkedcontent[gone]after\par
+%D before\markcontent[kept]{\darkgreen kept}\removemarkedcontent[gone]after\par
+%D \markcontent[gone]{\darkred gone}\removemarkedcontent[gone]after\par
+%D \markcontent[kept]{\darkgreen kept}\removemarkedcontent[gone]after\par
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer\blank
+%D
+%D This feature is paragraph based and is only to be used for small snippets of text,
+%D for instance when combining bit and pieces where keeping a state is complex compared
+%D to cleaning up unwanted stuff.
+
+\registerctxluafile{typo-chr}{autosuffix}
+
+\definesystemattribute[marked][public]
+
+\permanent\protected\def\removepunctuation{\clf_pickuppunctuation action{remove}\relax}
+\permanent\protected\def\pushpunctuation {\clf_pickuppunctuation action{push}\relax}
+\permanent\protected\def\poppunctuation {\clf_pickuppunctuation action{pop}\relax}
+
+\permanent\tolerant\protected\def\markcontent[#1]#:#2%
+ {\dontleavehmode
+ \bgroup
+ \clf_markcontent{#1}%
+ \bgroup
+ #2% double grouping makes aftergroups work ok
+ \egroup
+ \egroup}
+
+\permanent\tolerant\protected\def\startmarkedcontent[#1]%
+ {\dontleavehmode
+ \bgroup
+ \clf_markcontent{#1}%
+ % double grouping makes aftergroups work ok
+ \bgroup}
+
+\permanent\protected\def\stopmarkedcontent
+ {\egroup
+ \egroup}
+
+\permanent\tolerant\protected\def\removemarkedcontent[#1]%
+ {\clf_pickupmarkedcontent action{remove}mark{#1}\relax}
+
+\permanent\protected\def\doifelsemarkedcontent#1%
+ {\clf_doifelsemarkedcontent{#1}}
+
+%D A few helpers (put here for convenience):
+%D
+%D \starttyping
+%D test test\doifelselastnode{boundary}{word}{YES}{NOP}test
+%D test test\wordboundary \doifelselastnode{boundary}{word}{YES}{NOP}test
+%D test test\wordboundary \doifelseatwordboundary{YES}{NOP}test
+%D test test \lastnodeidstring test
+%D test test\lastnodeidstring test
+%D test test\number\lastnodeid test
+%D \stoptyping
+
+\newcount\c_syst_last_node_id
+
+\permanent\protected\def\doifelselastnode {\clf_lastnodeequals} % can be public implementors
+\permanent\protected\def\doifelseatwordboundary{\clf_atwordboundary} % can be public implementors
+\permanent\protected\def\lastnodeid {\clf_lastnodeid\c_syst_last_node_id} % can be public implementors
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-cln.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-cln.mkxl
new file mode 100644
index 00000000000..84fc1d2353b
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-cln.mkxl
@@ -0,0 +1,32 @@
+%D \module
+%D [ file=typo-cln,
+%D version=2011.02.11,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Cleaning,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Cleaning}
+
+\unprotect
+
+\registerctxluafile{typo-cln}{}
+
+\definesystemattribute[cleaner][public]
+
+%D Currently there is no interface.
+%D
+%D 1: Autocap first character of a line
+
+\permanent\protected\def\setcharactercleaning[#1]{\clf_setcharactercleaning{#1}} % also accepts reset, todo: public implementor
+
+% \appendtoks
+% \c_attr_cleaner\attributeunsetvalue
+% \to \everyforgetall
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-del.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-del.mkxl
new file mode 100644
index 00000000000..8e17ea82b3f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-del.mkxl
@@ -0,0 +1,925 @@
+%D \module
+%D [ file=typo-del, % moved from core-mis,
+%D version=20110112,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Delimited Content,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% todo: textstyle|color for inline \quotation etc
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Delimited Content}
+
+\unprotect
+
+\ifdefined\dotagsetdelimitedsymbol \else \aliased\let\dotagsetdelimitedsymbol \gobbleoneargument \fi
+\ifdefined\dotagsetsubsentencesymbol \else \aliased\let\dotagsetsubsentencesymbol\gobbleoneargument \fi
+
+% THIS IS OBSOLETE:
+
+\installcorenamespace{hyphenmarksign} % let's not waste a setuphandler (yet)
+
+\permanent\permanent\protected\def\setuphyphenmark[#1]% sign=normal|wide
+ {\getdummyparameters[#1]%
+ \expandnamespaceparameter\??hyphenmarksign\dummyparameter\c!sign\v!normal}
+
+\setvalue{\??hyphenmarksign\v!normal}%
+ {\enforced\let\textmodehyphen\normalhyphen
+ \enforced\let\textmodehyphendiscretionary\normalhyphendiscretionary}
+
+\setvalue{\??hyphenmarksign\v!wide}%
+ {\enforced\let\textmodehyphen\composedhyphen
+ \enforced\let\textmodehyphendiscretionary\composedhyphendiscretionary}
+
+\setuphyphenmark[\c!sign=\v!wide]
+
+\definesymbol[\c!lefthyphen] [\languageparameter\c!lefthyphen]
+\definesymbol[\c!righthyphen] [\languageparameter\c!righthyphen]
+\definesymbol[\c!hyphen] [\languageparameter\c!hyphen]
+
+\permanent\protected\def\normalhyphen
+ {\hbox{\directsymbol\empty\c!hyphen}}
+
+\permanent\protected\def\composedhyphen
+ {\hbox{\directsymbol\empty\c!compoundhyphen}}
+
+\permanent\protected\def\normalhyphendiscretionary
+ {\discretionary
+ {\hbox{\directsymbol\empty\c!righthyphen}}
+ {\hbox{\directsymbol\empty\c!lefthyphen}}
+ {\hbox{\directsymbol\empty\c!hyphen}}}
+
+\permanent\protected\def\composedhyphendiscretionary
+ {\discretionary
+ {\hbox{\directsymbol\empty\c!rightcompoundhyphen}}
+ {\hbox{\directsymbol\empty\c!leftcompoundhyphen}}
+ {\hbox{\directsymbol\empty\c!compoundhyphen}}}
+
+\aliased\let\textmodehyphen \composedhyphen
+\aliased\let\textmodehyphendiscretionary\composedhyphendiscretionary
+
+\definesymbol[\c!leftcompoundhyphen] [\languageparameter\c!leftcompoundhyphen]
+\definesymbol[\c!rightcompoundhyphen] [\languageparameter\c!rightcompoundhyphen]
+\definesymbol[\c!compoundhyphen] [\languageparameter\c!compoundhyphen]
+
+% TILL HERE
+
+\setnewconstant\boundarycharactermode\plusone
+
+% old: skip symbol skip
+% new: bound skip symbol skip bound
+
+\permanent\protected\def\midboundarycharacter#1#2%
+ {\ifcase\boundarycharactermode
+ \or
+ \removeunwantedspaces
+ \wordboundary
+ \hskip\hspaceamount\currentusedlanguage{#2}%
+ \usedlanguageparameter#1%
+ \hskip\hspaceamount\currentusedlanguage{#2}%
+ \wordboundary
+ \ignorespaces
+ \or
+ \usedlanguageparameter#1%
+ \fi
+ \boundarycharactermode\plusone}
+
+% old: symbol nobreak skip
+% new: symbol nobreak skip wordboundary
+
+\permanent\protected\def\leftboundarycharacter#1#2%
+ {\ifcase\boundarycharactermode
+ \or
+ \usedlanguageparameter#1%
+ \nobreak
+ \hskip\hspaceamount\currentusedlanguage{#2}% why not a kern
+ \wordboundary
+ \or
+ \usedlanguageparameter#1%
+ \fi
+ \boundarycharactermode\plusone}
+
+% old: preword skip symbol
+% new: bound nobreak skip symbol
+
+\permanent\protected\def\rightboundarycharacter#1#2%
+ {\ifcase\boundarycharactermode
+ \or
+ \wordboundary
+ \nobreak
+ \hskip\hspaceamount\currentusedlanguage{#2}% why not a kern
+ \usedlanguageparameter#1%
+ \or
+ \usedlanguageparameter#1%
+ \fi
+ \boundarycharactermode\plusone}
+
+\definehspace [sentence] [\zeropoint]
+\definehspace [intersentence] [.250\emwidth]
+
+\definesymbol
+ [\c!midsentence]
+ [\midboundarycharacter\c!midsentence{sentence}]
+
+\definesymbol
+ [\c!leftsentence]
+ [\leftboundarycharacter\c!leftsentence{sentence}]
+
+\definesymbol
+ [\c!rightsentence]
+ [\rightboundarycharacter\c!rightsentence{sentence}]
+
+\definesymbol
+ [\c!leftsubsentence]
+ [\leftboundarycharacter\c!leftsubsentence{sentence}]
+
+\definesymbol
+ [\c!rightsubsentence]
+ [\rightboundarycharacter\c!rightsubsentence{sentence}]
+
+\newsignal\d_typo_subsentence_signal
+\newcount \c_typo_subsentence_nesting
+
+\mutable\let\beforesubsentence\donothing % maybe just a parameter
+\mutable\let\aftersubsentence \donothing % maybe just a parameter
+
+% todo: make this language option
+%
+% \def\beforesubsentence{\removeunwantedspaces}
+% \def\aftersubsentence {\ignorespaces}
+
+\newconditional\c_typo_subsentence_cleanup \settrue\c_typo_subsentence_cleanup
+
+\def\typo_subsentence_cleanup_start
+ {\ifconditional\c_typo_subsentence_cleanup
+ \expandafter\ignorespaces
+ \fi}
+
+\def\typo_subsentence_cleanup_stop
+ {\ifconditional\c_typo_subsentence_cleanup
+ \removeunwantedspaces
+ \fi}
+
+\permanent\protected\def\midsentence
+ {\dostarttagged\t!subsentencesymbol\empty
+ \dotagsetsubsentencesymbol\s!middle
+ \symbol[\c!midsentence]%
+ \dostoptagged}
+
+\permanent\protected\def\beginofsubsentence
+ {\beforesubsentence
+ \ifdim\lastkern=\d_typo_subsentence_signal
+ \unskip
+ \kern\hspaceamount\currentusedlanguage{intersentence}%
+ \fi
+ \global\advance\c_typo_subsentence_nesting\plusone
+ \ifnum\c_typo_subsentence_nesting=\plusone
+ \dontleavehmode
+ \fi
+ \dostarttagged\t!subsentence\empty % no chain
+ \dostarttagged\t!subsentencesymbol\empty
+ \dotagsetsubsentencesymbol\s!left
+ \symbol[\ifodd\c_typo_subsentence_nesting\c!leftsentence\else\c!leftsubsentence\fi]%
+ \dostoptagged
+ \dostarttagged\t!subsentencecontent\empty
+ \typo_subsentence_cleanup_start}
+
+\permanent\protected\def\endofsubsentence % relax prevents space gobbling
+ {\typo_subsentence_cleanup_stop
+ \dostoptagged
+ \dostarttagged\t!subsentencesymbol\empty
+ \dotagsetsubsentencesymbol\s!right
+ \symbol[\ifodd\c_typo_subsentence_nesting\c!rightsentence\else\c!rightsubsentence\fi]%
+ \dostoptagged
+ \dostoptagged
+ \global\advance\c_typo_subsentence_nesting\minusone
+ \unskip
+ \kern\d_typo_subsentence_signal\relax
+ \aftersubsentence}
+
+\permanent\protected\def\beginofsubsentencespacing % relax prevents space gobbling
+ {\kern\d_typo_subsentence_signal\relax}% \ignorespaces}
+
+\permanent\protected\def\endofsubsentencespacing
+ {\ifdim\lastkern=\d_typo_subsentence_signal
+ \unskip
+ \hskip\hspaceamount\currentusedlanguage{intersentence}%
+ % no good, actually language dependent:
+ % \ignorespaces
+ \else
+ \unskip
+ \fi}
+
+%D \startbuffer
+%D test |<|test |<|test|>| test|>| test \par
+%D test|<|test|<|test|>|test|>|test \par
+%D test |<||<|test|>||>| test \par
+%D test \directdiscretionary{<}test\directdiscretionary{>} test \par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \getbuffer
+
+\permanent\protected\def\startsubsentence{\beginofsubsentence\wordboundary\beginofsubsentencespacing\wordboundary\typo_subsentence_cleanup_start}
+\permanent\protected\def\stopsubsentence {\typo_subsentence_cleanup_stop\wordboundary\endofsubsentencespacing\wordboundary\endofsubsentence}
+\permanent\protected\def\subsentence {\groupedcommandcs\startsubsentence\stopsubsentence}
+\permanent\protected\def\midsubsentence {\typo_subsentence_cleanup_start\wordboundary\midsentence\wordboundary\typo_subsentence_cleanup_stop}
+
+\definehspace [quotation] [\zeropoint]
+\definehspace [interquotation] [.125em]
+
+%definehspace [quote] [\zeropoint]
+%definehspace [speech] [\zeropoint]
+
+\definehspace [quote] [\hspaceamount\currentusedlanguage{quotation}]
+\definehspace [speech] [\hspaceamount\currentusedlanguage{quotation}]
+
+\definesymbol
+ [\c!leftquotation]
+ [\leftboundarycharacter\c!leftquotation{quotation}]
+
+\definesymbol
+ [\c!rightquotation]
+ [\rightboundarycharacter\c!rightquotation{quotation}]
+
+\definesymbol
+ [\c!nextleftquotation]
+ [\rightboundarycharacter\c!leftquotation{quotation}]
+
+\definesymbol
+ [\c!nextrightquotation]
+ [\leftboundarycharacter\c!rightquotation{quotation}]
+
+\definesymbol
+ [\c!leftquote]
+ [\leftboundarycharacter\c!leftquote{quote}]
+
+\definesymbol
+ [\c!rightquote]
+ [\rightboundarycharacter\c!rightquote{quote}]
+
+\definesymbol
+ [\c!leftspeech]
+ [\leftboundarycharacter\c!leftspeech{speech}]
+
+\definesymbol
+ [\c!rightspeech]
+ [\rightboundarycharacter\c!rightspeech{speech}]
+
+\definesymbol
+ [\c!middlespeech]
+ [\leftboundarycharacter\c!middlespeech{speech}]
+
+\appendtoks
+ \enforced\permanent\def\quotation#1{"#1"}%
+ \enforced\permanent\def\quote #1{'#1'}%
+\to \everysimplifycommands
+
+%D The next features was so desperately needed by Giuseppe Bilotta that he made a
+%D module for it. Since this is a typical example of core functionality, I decided
+%D to extend the low level quotation macros in such a way that a speech feature
+%D could be build on top of it. The speech opening and closing symbols are defined
+%D per language. Italian is an example of a language that has them set.
+
+\newsignal\d_typo_delimited_signal
+
+\mutable\let\currentdelimitedtext \s!unknown
+\mutable\let\currentparentdelimitedtext\currentdelimitedtext
+
+\installglobalmacrostack\currentdelimitedtext
+
+\mutable\let\delimitedtextlevel\!!zerocount
+
+\permanent\def\c_typo_delimited_nesting{\csname\??delimitedtextlevel\currentparentdelimitedtext\endcsname}
+
+% the \setlanguageparameter macro sets but we are ungrouped .. only used here
+%
+% \currentusedlanguage
+% \usedlanguageparameter
+
+%D The optional argument can be a language, a narrower spec, or a outer:inner language
+%D specification.
+%D
+%D \starttabulate
+%D \NC [en] \NC {\tttf en} \quotation[en] {{\tttf <en=\currentlanguage>} something french} \NC \NR
+%D \NC [fr] \NC {\tttf en} \quotation[fr] {{\tttf <fr=\currentlanguage>} something french} \NC \NR
+%D \NC [fr:] \NC {\tttf fr} \quotation[fr:] {{\tttf <en=\currentlanguage>} something french} \NC \NR
+%D \NC [:fr] \NC {\tttf en} \quotation[:fr] {{\tttf <fr=\currentlanguage>} something french} \NC \NR
+%D \NC [fr:fr] \NC {\tttf fr} \quotation[fr:fr]{{\tttf <fr=\currentlanguage>} something french} \NC \NR
+%D \NC [en:fr] \NC {\tttf en} \quotation[en:fr]{{\tttf <fr=\currentlanguage>} something french} \NC \NR
+%D \NC [fr:en] \NC {\tttf fr} \quotation[fr:en]{{\tttf <en=\currentlanguage>} something french} \NC \NR
+%D \stoptabulate
+
+\mutable\let\currentdelimitedlanguage\empty
+\mutable\let\innerdelimitedlanguage \empty
+\mutable\let\outerdelimitedlanguage \empty
+
+\installglobalmacrostack\currentdelimitedlanguage
+
+\def\typo_delimited_set_language_nop
+ {\setusedlanguage{\delimitedtextparameter\c!language}}
+
+\def\typo_delimited_set_language_yes
+ {\doiflanguageelse\m_delimited_argument
+ \typo_delimited_set_language_yes_a
+ {\doifelseinstring:\m_delimited_argument
+ \typo_delimited_set_language_yes_b
+ \typo_delimited_set_language_nop}}
+
+\def\typo_delimited_set_language_yes_b
+ {\splitatcolon\m_delimited_argument\outerdelimitedlanguage\innerdelimitedlanguage
+ \ifempty\outerdelimitedlanguage
+ \typo_delimited_set_language_nop
+ \else
+ \doiflanguageelse\outerdelimitedlanguage
+ {\setusedlanguage\outerdelimitedlanguage}%
+ \typo_delimited_set_language_nop
+ \fi
+ \ifempty\innerdelimitedlanguage\else
+ \doiflanguageelse\innerdelimitedlanguage
+ {\let\currentdelimitedlanguage\innerdelimitedlanguage}%
+ \donothing
+ \fi
+ \let\m_delimited_argument\empty}
+
+\def\typo_delimited_set_language_yes_a
+ {\let\currentdelimitedlanguage\m_delimited_argument
+ \let\m_delimited_argument\empty}
+
+\def\typo_delimited_push#1#2%
+ {\push_macro_currentdelimitedtext % can we combine these two
+ \push_macro_currentdelimitedlanguage % the language used for hyphenation
+ \edef\currentdelimitedtext{#1}%
+ \edef\m_delimited_argument{#2}%
+ \ifempty\m_delimited_argument
+ \typo_delimited_set_language_nop
+ \else
+ \typo_delimited_set_language_yes
+ \fi
+ \let\currentparentdelimitedtext\currentdelimitedtext
+ \global\advance\c_typo_delimited_nesting\plusone
+ \edef\delimitedtextlevel{\number\c_typo_delimited_nesting}%
+ \normalexpanded{\chaintocurrentdelimitedtext{\currentparentdelimitedtext:\delimitedtextlevel}}%
+ \edef\currentdelimitedtext{\currentparentdelimitedtext:\delimitedtextlevel}}
+
+\def\typo_delimited_pop
+ {\global\advance\c_typo_delimited_nesting\minusone
+ \pop_macro_currentdelimitedlanguage
+ \pop_macro_currentdelimitedtext}
+
+\installcorenamespace{delimitedtext}
+\installcorenamespace{delimitedtextlevel}
+
+\installcommandhandler \??delimitedtext {delimitedtext} \??delimitedtext
+
+\appendtoks
+ \expandafter\newcount\csname\??delimitedtextlevel\currentdelimitedtext\endcsname
+ \frozen\instance\setuevalue{\currentdelimitedtext }{\delimitedtext[\currentdelimitedtext]}%
+ \frozen\instance\setuevalue{\e!start\currentdelimitedtext}{\startdelimitedtext[\currentdelimitedtext]}%
+ \frozen\instance\setuevalue{\e!stop \currentdelimitedtext}{\stopdelimitedtext}%
+\to \everydefinedelimitedtext
+
+\setupdelimitedtext
+ [\c!location=\v!margin, % \v!text \v!paragraph
+ \c!spacebefore=,
+ \c!spaceafter=\delimitedtextparameter\c!spacebefore,
+ \c!style=,
+ \c!color=,
+ \c!leftmargin=\zeropoint,
+ \c!rightmargin=\delimitedtextparameter\c!leftmargin,
+ \c!indentnext=\v!yes,
+ \c!before=,
+ \c!after=,
+ \c!left=,
+ \c!right=,
+ %\c!level=0,
+ \c!method=,
+ %\c!language=\v!local,
+ \c!repeat=\v!no]
+
+\def\typo_delimited_repeat_indeed
+ {\relax\ifcase\delimitedtextlevel\else
+ \typo_delimited_handle_middle\c!middle
+ \fi}
+
+\let\typo_delimited_repeat\relax
+
+\permanent\tolerant\protected\def\startdelimitedtext[#1]#*[#2]%
+ {\begingroup
+ \typo_delimited_push{#1}{#2}%
+ \dostarttaggedchained\t!delimitedblock\currentdelimitedtext\??delimitedtext
+ \edef\p_delimited_method{\delimitedtextparameter\c!method}%
+ \ifx\p_delimited_method\v!font
+ \expandafter\typo_delimited_start_font
+ \else
+ \expandafter\typo_delimited_start_other
+ \fi}
+
+\let\typo_delimited_stop\relax % hooks into \everypar
+
+\def\typo_delimited_start_font
+ {\let\typo_delimited_stop\typo_delimitedtexts_finish_font
+ \dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!left
+ \delimitedtextparameter\c!left
+ \dostoptagged
+ \ignorespaces}
+
+\newconditional\c_typo_delimited_repeating
+
+\def\typo_delimited_start_other
+ {\edef\p_delimited_repeat{\delimitedtextparameter\c!repeat}%
+ \ifx\p_delimited_repeat\v!yes
+ \let\typo_delimited_repeat\typo_delimited_repeat_indeed
+ \else
+ \let\typo_delimited_repeat\relax
+ \fi
+ \setfalse\c_typo_delimited_repeating
+ \edef\p_delimited_location{\delimitedtextparameter\c!location}%
+ \ifx\p_delimited_location\v!paragraph
+ \expandafter\typo_delimited_start_par
+ \orelse\ifx\p_delimited_location\v!margin
+ \expandafter\typo_delimited_start_par
+ \else
+ \expandafter\typo_delimited_start_txt
+ \fi}
+
+\def\typo_delimitedtexts_finish_font
+ {\removeunwantedspaces % again ?
+ \dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!right
+ \delimitedtextparameter\c!right
+ \dostoptagged}
+
+\def\typo_delimited_show_language_indeed#1#2%
+ {\begingroup
+ \infofont
+ \setbox\scratchbox\hpack{\lower\strutht\hbox to \zeropoint{\darkred#1\currentlanguage:\currentdelimitedlanguage#2}}%
+ \vsmashbox\scratchbox
+ \box\scratchbox
+ \endgroup}
+
+\let\typo_delimited_show_language\gobbletwoarguments
+
+\installtextracker{delimited.language}
+ {\let\typo_delimited_show_language\typo_delimited_show_language_indeed}
+ {\let\typo_delimited_show_language\gobbletwoarguments}
+
+\def\typo_delimited_start_content
+ {\dostarttagged\t!delimitedcontent\empty
+ \begingroup
+ \douselanguageparameter\currentdelimitedlanguage
+ \typo_delimited_show_language<\hss
+ \ignorespaces}
+
+\def\typo_delimited_stop_content
+ {\removeunwantedspaces
+ \removelastskip % redundant
+ \typo_delimited_show_language\hss<%
+ \endgroup
+ \dostoptagged}
+
+\tolerant\def\typo_delimited_start_par[#1]%
+ {\let\typo_delimited_stop\typo_delimited_stop_par
+ \edef\p_delimited_spacebefore{\delimitedtextparameter\c!spacebefore}%
+ \ifempty\p_delimited_spacebefore \else
+ \blank[\p_delimited_spacebefore]%
+ \fi
+ \delimitedtextparameter\c!before
+ \edef\m_delimited_argument{#1}%
+ \ifempty\m_delimited_argument
+ \let\m_delimited_argument\m_delimited_argument
+ \fi
+ \ifempty\m_delimited_argument
+ \endgraf
+ \doadaptleftskip {\delimitedtextparameter\c!leftmargin}%
+ \doadaptrightskip{\delimitedtextparameter\c!rightmargin}%
+ \let\typo_delimited_stop_par_indeed\endgraf
+ \else % backward compatible direct directive
+ \startnarrower[\m_delimited_argument]%
+ \let\typo_delimited_stop_par_indeed\stopnarrower
+ \fi
+ % so far
+ \push_macro_checkindentation
+ \useindentingparameter\delimitedtextparameter
+ %
+ \begingroup
+ \usedelimitedtextstyleandcolor\c!style\c!color
+ %
+ \begingroup
+ \edef\p_delimited_left {\delimitedtextparameter{\c!left}}%
+ \edef\p_delimited_right {\delimitedtextparameter{\c!right}}%
+ \edef\p_delimited_nextleft {\delimitedtextparameter{\c!nextleft}}%
+ \edef\p_delimited_nextright{\delimitedtextparameter{\c!nextright}}%
+ %
+ \leftdelimitedtextmark
+ %
+ \setnextleftdelimitedtextmark
+ \setnextrightdelimitedtextmark
+ %
+ \typo_delimited_start_content}
+
+\let\typo_delimited_stop_par_indeed\endgraf
+
+\def\typo_delimited_stop_par
+ {\typo_delimited_stop_content
+ \rightdelimitedtextmark
+ \rightdelimitedtextmark
+ \carryoverpar\endgroup
+ \endgraf
+ \endgroup
+ \pop_macro_checkindentation
+ \typo_delimited_stop_par_indeed
+ \delimitedtextparameter\c!after
+ \edef\p_delimited_spaceafter{\delimitedtextparameter\c!spaceafter}%
+ \ifempty\p_delimited_spaceafter \else
+ \blank[\p_delimited_spaceafter]%
+ \fi
+ \useindentnextparameter\delimitedtextparameter
+ \aftergroup\dorechecknextindentation}% AM: This was missing!
+
+\def\typo_delimited_start_txt
+ {\let\typo_delimited_stop\typo_delimited_stop_txt
+ \begingroup
+ \usedelimitedtextstyleandcolor\c!style\c!color
+ \typo_delimited_handle_left\c!left
+ \typo_delimited_start_content}
+
+\def\typo_delimited_stop_txt
+ {\typo_delimited_stop_content
+ \typo_delimited_handle_right\c!right
+ \endgroup}
+
+\permanent\protected\def\stopdelimitedtext
+ {\typo_delimited_stop
+ \dostoptagged
+ \typo_delimited_pop
+ \endgroup}
+
+\permanent\tolerant\protected\def\delimitedtext[#1]#*[#2]%
+ {\dontleavehmode % following ones can be omited
+ \typo_delimited_push{#1}{#2}%
+ \edef\p_delimited_method{\delimitedtextparameter\c!method}%
+ \ifx\p_delimited_method\v!font
+ \expandafter\typo_delimited_fontdriven
+ \else
+ \expandafter\typo_delimited_other
+ \fi}
+
+\def\typo_delimited_other
+ {\edef\p_delimited_location{\delimitedtextparameter\c!location}%
+ \ifx\p_delimited_location\v!paragraph
+ \expandafter\typo_delimited_par
+ \orelse\ifx\p_delimited_location\v!margin
+ \expandafter\typo_delimited_par
+ \else
+ \expandafter\typo_delimited_txt
+ \fi}
+
+% shortcuts
+
+\permanent\protected\def\startdelimited{\startdelimitedtext}
+\permanent\protected\def\stopdelimited {\stopdelimitedtext} % no let, dynamically assigned
+\permanent \def\delimited {\delimitedtext}
+
+% todo: \dostarttagged\t!nothing\empty % for left/right boxes
+
+%D We have 4 different location and symbol handlers (two pairs):
+%D
+%D \starttyping
+%D \input tufte \startquotation \input tufte \stopquotation
+%D
+%D \setupdelimitedtext
+%D [quotation]
+%D [nextleft=right,
+%D nextright=left]
+%D
+%D \input tufte \startquotation \input tufte \stopquotation
+%D
+%D \setupdelimitedtext
+%D [quotation]
+%D [nextleft={\symbol[nextleftquotation]},
+%D nextright={\symbol[nextrightquotation]}]
+%D
+%D \input tufte \startquotation \input tufte \stopquotation
+%D \stoptyping
+
+\permanent\protected\def\setnextleftdelimitedtextmark
+ {\ifempty\p_delimited_nextleft
+ % nothing
+ \orelse\ifx\p_delimited_nextleft\v!left
+ \typo_delimited_nextleft_symbol\p_delimited_left
+ \orelse\ifx\p_delimited_nextleft\v!right
+ \typo_delimited_nextleft_symbol\p_delimited_right
+ \else
+ \typo_delimited_nextleft_symbol\p_delimited_nextleft
+ \fi}
+
+\permanent\protected\def\setnextrightdelimitedtextmark
+ {\ifempty\p_delimited_nextright
+ % nothing
+ \orelse\ifx\p_delimited_nextright\v!right
+ \typo_delimited_nextright_symbol\p_delimited_right
+ \orelse\ifx\p_delimited_nextright\v!left
+ \typo_delimited_nextright_symbol\p_delimited_left
+ \else
+ \typo_delimited_nextright_symbol\p_delimited_nextright
+ \fi}
+
+\permanent\protected\def\leftdelimitedtextmark
+ {\ifempty\p_delimited_left
+ % nothing
+ \else
+ \typo_delimited_left_symbol\p_delimited_left
+ \fi}
+
+\permanent\protected\def\rightdelimitedtextmark
+ {\ifempty\p_delimited_right
+ % nothing
+ \else
+ \typo_delimited_right_symbol\p_delimited_right
+ \fi}
+
+\def\typo_delimited_left_symbol#1%
+ {\dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!left
+ \setbox\scratchbox\hbox{\usedelimitedtextstyleandcolor\c!symstyle\c!symcolor#1}%
+ \dontleavehmode
+ \edef\p_delimited_margin{\delimitedtextparameter\c!location}%
+ \ifx\p_delimited_margin\v!margin
+ \hskip-\wd\scratchbox
+ \fi
+ \box\scratchbox
+ \dostoptagged}
+
+\def\typo_delimited_right_symbol#1%
+ {\dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!right
+ \hsmash{\usedelimitedtextstyleandcolor\c!symstyle\c!symcolor#1}%
+ \dostoptagged}
+
+\def\typo_delimited_nextleft_symbol#1%
+ {\let\typo_delimited_reset_next_symbol\typo_delimited_reset_next_symbol_indeed
+ \localleftbox\bgroup
+ \enforced\swapmacros\leftboundarycharacter\rightboundarycharacter
+ \boundarycharactermode\plusone
+ \typo_delimited_left_symbol#1%
+ \egroup}
+
+\def\typo_delimited_nextright_symbol#1%
+ {\let\typo_delimited_reset_next_symbol\typo_delimited_reset_next_symbol_indeed
+ \localrightbox\bgroup
+ \enforced\swapmacros\leftboundarycharacter\rightboundarycharacter
+ \boundarycharactermode\plusone
+ \typo_delimited_right_symbol#1%
+ \egroup}
+
+\protected\def\typo_delimited_reset_next_symbol_indeed
+ {\localleftbox {}%
+ \localrightbox{}}%
+
+\let\typo_delimited_reset_next_symbol\relax
+
+\appendtoks
+ \typo_delimited_reset_next_symbol
+\to \everyforgetall
+
+% \starttext
+% \hyphenatedword{groepsvrijstellingsverordeningen}\par
+% \hyphenatedword{\quote{groepsvrijstellingsverordeningen}}\par
+% \dorecurse{100}{\hskip300pt\hskip\recurselevel pt test \quote{xxx xxxx}.\par}
+% \page \setuppapersize[A5][A4]
+% \quotation {overly beautiful pusillanimous sesquipedalian
+% longwinded} test test test test test test test test test test test
+% test test test test test test test test test test test test test
+% test test test test test test test test test test test test test
+% test test test test test test test test test test test test test
+% test test test
+% \stoptext
+
+% We have no real test case for this and it's broken already for a while,
+% even in \MKII. Maybe we should to this in \LUA. Only Italian has the
+% middlespeech parameter set.
+
+\def\typo_delimited_handle_middle#1% special case
+ {\ifconditional\c_typo_delimited_repeating
+ \begingroup
+ \usedelimitedtextstyleandcolor\c!symstyle\c!symcolor
+ \setbox\scratchbox\hbox{\delimitedtextparameter#1}%
+ \ifdim\wd\scratchbox>\zeropoint
+ \ifdim\lastkern=\d_typo_delimited_signal
+ \unkern
+ \hskip\hspaceamount\currentusedlanguage{interquotation}%
+ \else % maybe an option:
+ %\edef\p_delimited_margin{\delimitedtextparameter\c!location}%
+ %\ifx\p_delimited_margin\v!margin
+ % \hskip-\wd\scratchbox
+ %\fi
+ \fi
+ \strut % new, needed below
+ \dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!middle
+ \delimitedtextparameter#1% unhbox\scratchbox
+ \dostoptagged
+ % \penalty\plustenthousand % else overfull boxes, but that's better than dangling periods
+ \kern\d_typo_delimited_signal % +- \prewordbreak
+ \fi
+ \endgroup
+ \else
+ \settrue\c_typo_delimited_repeating
+ \fi}
+
+\def\typo_delimited_handle_left#1%
+ {\begingroup
+ \usedelimitedtextstyleandcolor\c!symstyle\c!symcolor
+ \setbox\scratchbox\hbox{\delimitedtextparameter#1}%
+ \ifdim\wd\scratchbox>\zeropoint
+ \ifdim\lastkern=\d_typo_delimited_signal
+ \unkern
+ \hskip\hspaceamount\currentusedlanguage{interquotation}%
+ \orelse\ifdim\lastskip=\d_typo_delimited_signal
+ \unskip
+ \hskip\hspaceamount\currentusedlanguage{interquotation}%
+ \fi
+ % \strut % new, needed below
+ % \ifhmode % else funny pagebeaks
+ % \penalty\plustenthousand
+ % \hskip\zeropoint % == \prewordbreak
+ % \fi
+ \strut % new, needed below
+ \dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!left
+ \delimitedtextparameter#1% unhbox\scratchbox
+ \dostoptagged
+ \penalty\plustenthousand % new per 2013-03-09 WS mailing list
+ \hskip\d_typo_delimited_signal % +- \prewordbreak
+ \fi
+ \endgroup}
+
+\def\typo_delimited_handle_right#1%
+ {\begingroup
+ \usedelimitedtextstyleandcolor\c!symstyle\c!symcolor
+ \setbox\scratchbox\hbox{\delimitedtextparameter#1}%
+ \ifdim\wd\scratchbox>\zeropoint
+ \ifdim\lastkern=\d_typo_delimited_signal
+ \unkern
+ \penalty\plustenthousand
+ \hskip\hspaceamount\currentusedlanguage{interquotation}%
+ \orelse\ifdim\lastskip=\d_typo_delimited_signal
+ \unskip
+ \penalty\plustenthousand
+ \hskip\hspaceamount\currentusedlanguage{interquotation}%
+ \fi
+ \ifhmode % else funny pagebeaks
+ \penalty\plustenthousand
+ \hskip\zeropoint % == \prewordbreak
+ \fi
+ \strut % new, needed below
+ \dostarttagged\t!delimitedsymbol\empty
+ \dotagsetdelimitedsymbol\s!right
+ \delimitedtextparameter#1% unhbox\scratchbox
+ \dostoptagged
+ \kern\d_typo_delimited_signal % +- \prewordbreak
+ \fi
+ \endgroup}
+
+\protected\def\typo_delimited_par
+ {\groupedcommand
+ {\dostarttaggedchained\t!delimited\currentdelimitedtext\??delimitedtext % block?
+ \usedelimitedtextstyleandcolor\c!style\c!color
+ \typo_delimited_handle_left\c!left
+ \typo_delimited_start_content}
+ {\typo_delimited_stop_content
+ \typo_delimited_handle_right\c!right
+ \removelastskip % hm
+ \dostoptagged
+ \typo_delimited_pop}}
+
+\protected\def\typo_delimited_txt
+ {\edef\p_left_right{\delimitedtextparameter\c!left\delimitedtextparameter\c!right}%
+ \ifempty\p_left_right
+ \expandafter\typo_delimited_attributed
+ \else
+ \expandafter\typo_delimited_quoted
+ \fi}
+
+\def\typo_delimited_quoted
+ {\dontleavehmode
+ \begingroup
+ \dostarttaggedchained\t!delimited\currentdelimitedtext\??delimitedtext
+ \typo_delimited_handle_left\c!left
+ \usedelimitedtextstyleandcolor\c!style\c!color
+ \typo_delimited_start_content
+ \bgroup
+ \aftergroup\typo_delimited_quoted_e
+ \let\next=}
+
+\def\typo_delimited_quoted_e
+ {\typo_delimited_stop_content
+ \typo_delimited_handle_right\c!right
+ \removelastskip % ?
+ \dostoptagged
+ \typo_delimited_pop
+ \endgroup}
+
+\def\typo_delimited_attributed
+ {\dontleavehmode
+ \begingroup
+ \dostarttaggedchained\t!delimited\currentdelimitedtext\??delimitedtext
+ \usedelimitedtextstyleandcolor\c!style\c!color
+ \typo_delimited_start_content
+ \bgroup
+ \aftergroup\typo_delimited_attributed_e
+ \let\next=}
+
+\def\typo_delimited_attributed_e
+ {\typo_delimited_stop_content
+ \dostoptagged
+ \typo_delimited_pop
+ \endgroup}
+
+\def\typo_delimited_fontdriven
+ {\dontleavehmode
+ \begingroup
+ \dostarttaggedchained\t!delimited\currentdelimitedtext\??delimitedtext
+ \usedlanguageparameter{\c!left\currentparentdelimitedtext}% was: \currentdelimitedtext
+ \usedelimitedtextstyleandcolor\c!style\c!color
+ \typo_delimited_start_content
+ \bgroup
+ \aftergroup\typo_delimited_fontdriven_e
+ \let\next=}
+
+\def\typo_delimited_fontdriven_e
+ {\typo_delimited_stop_content
+ \usedlanguageparameter{\c!right\currentparentdelimitedtext}% was: \currentdelimitedtext
+ \dostoptagged
+ \typo_delimited_pop
+ \endgroup}
+
+% testcase for nesting:
+%
+% \quotation{... \quotation{...} ...}
+% \startquotation ... \startquotation... \quotation{...} \stopquotation\space ...\stopquotation
+% \setupdelimitedtext[quotation][1][left=(,right=)]
+% \setupdelimitedtext[quotation][2][left={[},right={]}]
+% \setupdelimitedtext[quotation][3][left=\{,right=\}]
+% \quotation{... \quotation{...} ...}
+% \startquotation ... \startquotation... \quotation{...} \stopquotation\space ...\stopquotation
+
+\definedelimitedtext
+ [\v!quotation]
+ [\c!left={\symbol[\c!leftquotation]},
+ \c!right={\symbol[\c!rightquotation]},
+ \c!leftmargin=\v!standard]
+
+\definedelimitedtext
+ [\v!quote][\v!quotation]
+
+\setupdelimitedtext
+ [\v!quote]
+ [\c!location=\v!text,
+ \c!left={\symbol[\c!leftquote]},
+ \c!right={\symbol[\c!rightquote]}]
+
+\definedelimitedtext
+ [\v!blockquote][\v!quotation]
+
+\setupdelimitedtext
+ [\v!blockquote]
+ [\c!left=,
+ \c!right=]
+
+\definedelimitedtext
+ [\v!speech][\v!quotation]
+
+\setupdelimitedtext
+ [\v!speech]
+ [\c!repeat=\v!yes,
+ \c!left={\symbol[\c!leftspeech]},
+ \c!middle={\symbol[\c!middlespeech]},
+ \c!right={\symbol[\c!rightspeech]}]
+
+\definedelimitedtext
+ [\v!aside]
+ [\c!left={\symbol[\c!leftsentence]},
+ \c!right={\symbol[\c!rightsentence]}]
+
+% how do we call an tight quote
+%
+% \definedelimitedtext
+% [\v!quotation][\v!quotation]
+%
+% \setupdelimitedtext
+% [\v!quotation]
+% [\c!indentnext=\v!no,
+% \c!spacebefore=\v!nowhite]
+
+\permanent\protected\def\setupquotation{\setupdelimitedtext[\v!quotation]}
+\permanent\protected\def\setupquote {\setupdelimitedtext[\v!quote]}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-dig.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-dig.lmt
new file mode 100644
index 00000000000..7f6663db98a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-dig.lmt
@@ -0,0 +1,181 @@
+if not modules then modules = { } end modules ['typo-dig'] = {
+ version = 1.001,
+ optimize = true,
+ comment = "companion to typo-dig.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- we might consider doing this after the otf pass because now osf do not work
+-- out well in node mode.
+
+local next, type, tonumber = next, type, tonumber
+local format, insert = string.format, table.insert
+local round, div = math.round, math.div
+
+local trace_digits = false trackers.register("typesetters.digits", function(v) trace_digits = v end)
+
+local report_digits = logs.reporter("typesetting","digits")
+
+local nodes, node = nodes, node
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getwidth = nuts.getwidth
+local isglyph = nuts.isglyph
+local getattr = nuts.getattr
+
+local setlink = nuts.setlink
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+
+local hpack_node = nuts.hpack
+local traverse_id = nuts.traverse_id
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local find_attribute = nuts.find_attribute
+local unset_attributes = nuts.unset_attributes
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+
+local nodepool = nuts.pool
+local enableaction = nodes.tasks.enableaction
+
+local new_glue = nodepool.glue
+
+local fonthashes = fonts.hashes
+local chardata = fonthashes.characters
+
+local v_reset = interfaces.variables.reset
+
+local charbase = characters.data
+local getdigitwidth = fonts.helpers.getdigitwidth
+
+typesetters = typesetters or { }
+local typesetters = typesetters
+
+typesetters.digits = typesetters.digits or { }
+local digits = typesetters.digits
+
+digits.actions = { }
+local actions = digits.actions
+
+local a_digits = attributes.private("digits")
+
+-- at some point we can manipulate the glyph node so then i need
+-- to rewrite this then
+
+function nodes.aligned(head,start,stop,width,how)
+ if how == "flushright" or how == "middle" then
+ head, start = insert_node_before(head,start,new_glue(0,65536,65536))
+ end
+ if how == "flushleft" or how == "middle" then
+ head, stop = insert_node_after(head,stop,new_glue(0,65536,65536))
+ end
+ local prv = getprev(start)
+ local nxt = getnext(stop)
+ setprev(start)
+ setnext(stop)
+ local packed = hpack_node(start,width,"exactly") -- no directional mess here, just lr
+ if prv then
+ setlink(prv,packed)
+ end
+ if nxt then
+ setlink(packed,nxt)
+ end
+ if getprev(packed) then
+ return head, packed
+ else
+ return packed, packed
+ end
+end
+
+actions[1] = function(head,start,attr)
+ local char, font = isglyph(start)
+ local unic = chardata[font][char].unicode or char
+ if charbase[unic].category == "nd" then -- ignore unic tables
+ local oldwidth = getwidth(start)
+ local newwidth = getdigitwidth(font)
+ if newwidth ~= oldwidth then
+ if trace_digits then
+ report_digits("digit trigger %a, instance %a, char %C, unicode %U, delta %s",
+ attr%100,div(attr,100),char,unic,newwidth-oldwidth)
+ end
+ head, start = nodes.aligned(head,start,start,newwidth,"middle")
+ return head, start
+ end
+ end
+ return head, start
+end
+
+function digits.handler(head)
+ local _, start = find_attribute(head, a_digits)
+ if start then
+ local current = head
+ while current do
+ if getid(current) == glyph_code then
+ local attr = getattr(current,a_digits)
+ if attr and attr > 0 then
+ local action = actions[attr%100] -- map back to low number
+ if action then
+ head, current = action(head,current,attr)
+ elseif trace_digits then
+ report_digits("unknown digit trigger %a",attr)
+ end
+ done = true
+ end
+ end
+ if current then
+ current = getnext(current)
+ end
+ end
+ if done then
+ unset_attributes(a_digits, head)
+ end
+ end
+ return head
+end
+
+local m, enabled = 0, false -- a trick to make neighbouring ranges work
+
+function digits.set(n) -- number or 'reset'
+ if n == v_reset then
+ n = unsetvalue
+ else
+ n = tonumber(n)
+ if n then
+ if not enabled then
+ enableaction("processors","typesetters.digits.handler")
+ if trace_digits then
+ report_digits("enabling digit handler")
+ end
+ enabled = true
+ end
+ if m == 100 then
+ m = 1
+ else
+ m = m + 1
+ end
+ n = m * 100 + n
+ else
+ n = unsetvalue
+ end
+ end
+ texsetattribute(a_digits,n)
+end
+
+-- interface
+
+interfaces.implement {
+ name = "setdigitsmanipulation",
+ actions = digits.set,
+ arguments = "string"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-dig.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-dig.mkxl
new file mode 100644
index 00000000000..308ccb66e61
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-dig.mkxl
@@ -0,0 +1,58 @@
+%D \module
+%D [ file=typo-dig,
+%D version=2010.01.18,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Digits,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Digits}
+
+\unprotect
+
+\registerctxluafile{typo-dig}{autosuffix}
+
+\definesystemattribute[digits][public]
+
+%D This could as well be a dynamic font feature but why bother.
+%D
+%D \macros
+%D {\equaldigits}
+%D
+%D \startbuffer
+%D test test \ruledhbox{123} test test\par
+%D test test \ruledhbox{\equaldigits{123}} test test\par
+%D test test \equaldigits{123} test test\par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This calls result in:
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+\permanent\protected\def\setdigitsmanipulation[#1]% can be public implementor
+ {\clf_setdigitsmanipulation{#1}}
+
+\permanent\protected\def\resetdigitsmanipulation
+ {\c_attr_digits\attributeunsetvalue}
+
+\appendtoks
+ \resetdigitsmanipulation
+\to \everyresettypesetting
+
+\permanent\protected\def\equaldigits{\groupedcommand{\setdigitsmanipulation[\number\plusone]}{}}
+\permanent\protected\def\dummydigit {\hphantom {\setdigitsmanipulation[\number\plusone]0}}
+
+\appendtoks
+ \resetdigitsmanipulation
+\to \everyinitializeverbatim
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-dir.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-dir.mkxl
new file mode 100644
index 00000000000..8439ed342d0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-dir.mkxl
@@ -0,0 +1,205 @@
+%D \module
+%D [ file=typo-dir,
+%D version=2009.03.27, % code moved from core-spa.mkiv
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Directions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Directions}
+
+%D At some point we might default to method 'two' but first I need to make it more
+%D efficient (and provide some options). I also want to have some basic tracing.
+
+\unprotect
+
+\registerctxluafile{typo-dir}{optimize}
+\registerctxluafile{typo-dha}{}
+%registerctxluafile{typo-dua}{}
+%registerctxluafile{typo-dub}{}
+\registerctxluafile{typo-duc}{autosuffix}
+
+\definesystemattribute[directions][public,pickup]
+
+\installcorenamespace{directions}
+\installcorenamespace{directionsbidimode}
+
+% plural as we can have a combination but maybe better singular
+
+\installsimplecommandhandler \??directions {directions} \??directions % no \define... yet
+
+\immutable\edef\lefttorightmark{\normalUchar"200E} \aliased\let\lrm\lefttorightmark % expandable
+\immutable\edef\righttoleftmark{\normalUchar"200F} \aliased\let\rlm\righttoleftmark % expandable
+
+\permanent\protected\def\setdirection[#1]% todo: symbolic names
+ {\clf_setdirection#1\relax}
+
+\permanent\protected\def\resetdirection
+ {\c_attr_directions\attributeunsetvalue}
+
+\newconstant\directionsbidimode % this one might become pivate
+
+% \setupdirections[bidi=global,method=default]
+% \setupdirections[bidi=global,method=one]
+% \setupdirections[bidi=global,method=two]
+% \setupdirections[bidi=global,method=two,fences=no]
+
+% maybe use chardefs
+
+\mutable\let\currentbidimode\empty
+\mutable\let\currentbidistamp\empty
+
+\def\typo_dir_get_mode
+ {\def\currentbidimode{\clf_getbidimode % test edef
+ scope {\directionsparameter\c!bidi}%
+ method {\directionsparameter\c!method}%
+ fences {\directionsparameter\c!fences}%
+ }%
+ \gletcsname\??directionsbidimode\currentbidistamp\endcsname\currentbidimode}
+
+\appendtoks
+ \edef\p_bidi{\directionsparameter\c!bidi}%
+ \edef\currentbidistamp
+ {\p_bidi
+ :\directionsparameter\c!method
+ :\directionsparameter\c!fences}%
+ \expandafter\let\expandafter\currentbidimode\csname\??directionsbidimode\currentbidistamp\endcsname
+ \ifrelax\currentbidimode
+ \typo_dir_get_mode
+ \fi
+ \directionsbidimode\currentbidimode\relax
+ \ifcase\directionsbidimode
+ \resetdirection
+ \else
+ \setdirection[\number\directionsbidimode]%
+ \fi
+ \ifx\p_bidi\v!global
+ \pickupdirectionsattribute % pagebuilder
+ \else
+ \forgetdirectionsattribute
+ \fi
+\to \everysetupdirections
+
+\appendtoks
+ \edef\p_option{\directionsparameter\c!break}% name can change
+ \bitwiseflip\normalizelinemode\ifx\p_option\v!both\else-\fi\breakafterdircode
+\to \everysetupdirections
+
+% bidi: local=obey grouping, global=ignore grouping (unicode has no grouping)
+
+\setupdirections % maybe start/stop
+ [\c!bidi=\v!off,
+ \c!method=\v!default,
+ \c!break=\v!both, % experimental value, maybe \v!no will be default (bad name too)
+ \c!fences=\v!yes]
+
+\permanent\protected\edef\bidilre{\normalUchar"202A}
+\permanent\protected\edef\bidirle{\normalUchar"202B}
+\permanent\protected\edef\bidipop{\normalUchar"202C}
+\permanent\protected\edef\bidilro{\normalUchar"202D}
+\permanent\protected\edef\bidirlo{\normalUchar"202E}
+
+\permanent\protected\def\dirlre{\ifcase\directionsbidimode\or\bidilre\or\textdirection\directionlefttoright\fi}
+\permanent\protected\def\dirrle{\ifcase\directionsbidimode\or\bidirle\or\textdirection\directionrighttoleft\fi}
+\permanent\protected\def\dirlro{\ifcase\directionsbidimode\or\bidilro\or\setdirection[3]\fi}
+\permanent\protected\def\dirrlo{\ifcase\directionsbidimode\or\bidirlo\or\setdirection[4]\fi}
+
+% for the moment: \setdirection[\plusone]
+
+\definecolor[bidi:left:original] [r=.6]
+\definecolor[bidi:left:reversed] [g=.6]
+\definecolor[bidi:right:original][b=.6]
+\definecolor[bidi:right:reversed][r=.6,g=.6]
+\definecolor[bidi:mirrored] [r=.6,b=.6]
+
+\protect \endinput
+
+% bidi test
+
+% \definefontfeature
+% [arab]
+% [mode=node,language=dflt,script=arab,
+% init=yes,medi=yes,fina=yes,isol=yes,
+% liga=yes,dlig=yes,rlig=yes,clig=yes,
+% mark=yes,mkmk=yes,kern=yes,curs=yes]
+%
+% \font\Arabic=arabtype*arab at 20pt
+%
+% \def\LATIN{LATIN} {\setdirection[1]} % enable this
+% \def\ARAB {عربي}
+%
+% \startluacode
+% function documentdata.split_tokens(str)
+% for s in str:bytes() do
+% context.sprint(tex.ctxcatcodes,string.format("\\hbox{\\char %s}",s))
+% end
+% end
+% \stopluacode
+%
+% \protected\def\biditest#1#2#3% font text raw
+% {\dontleavehmode\hbox
+% {\framed[offset=overlay]{\tttf#2}\quad
+% \enabletrackers[typesetters.directions]%
+% \framed[offset=overlay]{#1#3}\quad
+% \disabletrackers[typesetters.directions]%
+% \tttf\ctxlua{documentdata.split_tokens([[\detokenize{#3}]])}}}
+%
+% \startbuffer[bidi-sample]
+% \biditest\Arabic{LATIN BARA}{\lefttoright\relax \LATIN\ \ARAB}\par
+% \biditest\Arabic{BARA LATIN}{\righttoleft\relax \LATIN\ \ARAB}\par
+% \biditest\Arabic{LATIN ARAB}{\lefttoright{\bidilro \LATIN\ \ARAB}}\par % right -> left
+% \biditest\Arabic{LATIN ARAB}{\righttoleft{\bidilro \LATIN\ \ARAB}}\par % right -> left
+% \biditest\Arabic{BARA NITAL}{\lefttoright{\bidirlo \LATIN\ \ARAB}}\par % left -> right
+% \biditest\Arabic{BARA NITAL}{\righttoleft{\bidirlo \LATIN\ \ARAB}}\par % left -> right
+% \stopbuffer
+%
+% \startbuffer[bidi-sample]
+% \biditest\Arabic{LATIN BARA}{\lefttoright\relax \LATIN\ \ARAB}\par
+% \biditest\Arabic{BARA LATIN}{\righttoleft\relax \LATIN\ \ARAB}\par
+% \biditest\Arabic{LATIN ARAB}{\lefttoright\bidilro \LATIN\ \ARAB}\par % right -> left
+% \biditest\Arabic{LATIN ARAB}{\righttoleft\bidilro \LATIN\ \ARAB}\par % right -> left
+% \biditest\Arabic{BARA NITAL}{\lefttoright\bidirlo \LATIN\ \ARAB}\par % left -> right
+% \biditest\Arabic{BARA NITAL}{\righttoleft\bidirlo \LATIN\ \ARAB}\par % left -> right
+% \stopbuffer
+%
+% \startbuffer[bidi-setup]
+% \setupdirections[bidi=off]
+% \stopbuffer
+%
+% {\typebuffer[bidi-setup] \getbuffer[bidi-setup] \getbuffer[bidi-sample]}
+%
+% \startbuffer[bidi-setup]
+% \setupdirections[bidi=global]
+% \stopbuffer
+%
+% {\typebuffer[bidi-setup] \getbuffer[bidi-setup] \getbuffer[bidi-sample]}
+%
+% \startbuffer[bidi-setup]
+% \setupdirections[bidi=local]
+% \stopbuffer
+%
+% {\typebuffer[bidi-setup] \getbuffer[bidi-setup] \getbuffer[bidi-sample]}
+%
+% \startbuffer[bidi-sample]
+% \setupdirections[bidi=global]
+%
+% \hbox{\righttoleft\arabicfont (0001)}\par
+% \dontleavehmode\hbox{\righttoleft\arabicfont (0002)}\par
+% {\righttoleft\arabicfont (0003)\par}
+% {\righttoleft\arabicfont (0004)}\par
+% \dontleavehmode{\righttoleft\arabicfont (0005)\par}
+% \dontleavehmode{\righttoleft\arabicfont (0006)}\par
+% \rtlhbox{\arabicfont (0007)}\par
+% \ltrhbox{\arabicfont (0008)}\par
+% \dontleavehmode\rtlhbox{\arabicfont (0009)}\par
+% \dontleavehmode\ltrhbox{\arabicfont (0010)}\par
+% \stopsetups
+%
+% {\typebuffer[bidi-sample] \getbuffer[bidi-sample]}
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-drp.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-drp.lmt
new file mode 100644
index 00000000000..2c53cc111a9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-drp.lmt
@@ -0,0 +1,341 @@
+if not modules then modules = { } end modules ['typo-drp'] = {
+ version = 1.001,
+ comment = "companion to typo-drp.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This one is sensitive for order (e.g. when combined with first line
+-- processing.
+
+-- todo: use isglyph
+
+local tonumber, type, next = tonumber, type, next
+local ceil = math.ceil
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local trace_initials = false trackers.register("typesetters.initials", function(v) trace_initials = v end)
+local report_initials = logs.reporter("nodes","initials")
+
+local initials = typesetters.paragraphs or { }
+typesetters.initials = initials or { }
+
+local nodes = nodes
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+local disableaction = tasks.disableaction
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getchar = nuts.getchar
+local getid = nuts.getid
+local getattr = nuts.getattr
+local getwhd = nuts.getwhd
+
+local getprop = nuts.getprop
+local setprop = nuts.setprop
+
+local setlink = nuts.setlink
+local setprev = nuts.setprev
+local setnext = nuts.setnext
+local setfont = nuts.setfont
+local setchar = nuts.setchar
+local setwhd = nuts.setwhd
+local setkern = nuts.setkern
+local setoffsets = nuts.setoffsets
+local setglyphdata = nuts.setglyphdata
+
+local hpack_nodes = nuts.hpack
+
+local nodecodes = nodes.nodecodes
+
+local nodepool = nuts.pool
+local new_kern = nodepool.kern
+
+local insert_before = nuts.insert_before
+local insert_after = nuts.insert_after
+local remove_node = nuts.remove
+local start_of_par = nuts.start_of_par
+
+local nextnode = nuts.traversers.node
+local nextglyph = nuts.traversers.glyph
+
+local setcoloring = nuts.colors.set
+
+local variables = interfaces.variables
+local v_default = variables.default
+local v_margin = variables.margin
+local v_auto = variables.auto
+local v_first = variables.first
+local v_last = variables.last
+
+local texget = tex.get
+local texset = tex.set
+local unsetvalue = attributes.unsetvalue
+
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local glue_code = nodecodes.glue
+local kern_code = nodecodes.kern
+local par_code = nodecodes.par
+
+local actions = { }
+initials.actions = actions
+
+local a_initial = attributes.private("initial")
+
+local category = characters.category
+
+local function set(par,specification)
+ enableaction("processors","typesetters.initials.handler")
+ if trace_initials then
+ report_initials("enabling initials")
+ end
+ setprop(par,a_initial,specification)
+end
+
+function initials.set(specification)
+ nuts.setparproperty(set,specification)
+end
+
+interfaces.implement {
+ name = "setinitial",
+ actions = initials.set,
+ arguments = {
+ {
+ { "location" },
+ { "enabled", "boolean" },
+ { "method" },
+ { "distance" ,"dimen" },
+ { "hoffset" ,"dimen" },
+ { "voffset" ,"dimen" },
+ { "font", "integer" },
+ { "dynamic", "integer" },
+ { "ca", "integer" },
+ { "ma", "integer" },
+ { "ta", "integer" },
+ { "n", "integer" },
+ { "m", "integer" },
+ }
+ }
+}
+
+-- todo: prevent linebreak .. but normally a initial ends up at the top of
+-- a page so this has a low priority
+
+actions[v_default] = function(head,setting)
+ -- begin of par
+ local first = getnext(head)
+ local indent = false
+ -- parbox .. needs to be set at 0
+ if first and getid(first) == hlist_code then
+ first = getnext(first)
+ indent = true
+ end
+ -- we need to skip over kerns and glues (signals)
+ while first and getid(first) ~= glyph_code do
+ first = getnext(first)
+ end
+ if first and getid(first) == glyph_code then
+ local ma = setting.ma or 0
+ local ca = setting.ca
+ local ta = setting.ta
+ local last = first
+ local distance = setting.distance or 0
+ local voffset = setting.voffset or 0
+ local hoffset = setting.hoffset or 0
+ local parindent = texget("parindent")
+ local baseline = texget("baselineskip",false)
+ local lines = tonumber(setting.n) or 0
+ local dynamic = setting.dynamic
+ local font = setting.font
+ local method = settings_to_hash(setting.method)
+ local length = tonumber(setting.m) or 1
+ --
+ -- 1 char | n chars | skip first quote | ignore punct | keep punct
+ --
+ if getattr(first,a_initial) then
+ for current in nextnode, getnext(first) do
+ if getattr(current,a_initial) then
+ last = current
+ else
+ break
+ end
+ end
+ elseif method[v_auto] then
+ local char = getchar(first)
+ local kind = category(char)
+ if kind == "po" or kind == "pi" then
+ if method[v_first] then
+ -- remove quote etc before initial
+ local next = getnext(first)
+ if not next then
+ -- don't start with a quote or so
+ return head
+ end
+ last = nil
+ for current in nextglyph, next do
+ head, first = remove_node(head,first,true)
+ first = current
+ last = first
+ break
+ end
+ if not last then
+ -- no following glyph or so
+ return head
+ end
+ else
+ -- keep quote etc with initial
+ local next = getnext(first)
+ if not next then
+ -- don't start with a quote or so
+ return head
+ end
+ for current in nextglyph, next do
+ last = current
+ break
+ end
+ if last == first then
+ return head
+ end
+ end
+ elseif kind == "pf" then
+ -- error: final quote
+ else
+ -- okay
+ end
+ -- maybe also: get all A. B. etc
+ local next = getnext(first)
+ if next then
+ for current, char in nextglyph, next do
+ local kind = category(char)
+ if kind == "po" then
+ if method[v_last] then
+ -- remove period etc after initial
+ remove_node(head,current,true)
+ else
+ -- keep period etc with initial
+ last = current
+ end
+ end
+ break
+ end
+ end
+ else
+ for current in nextglyph, first do
+ last = current
+ if length <= 1 then
+ break
+ else
+ length = length - 1
+ end
+ end
+ end
+ local current = first
+ while true do
+ local id = getid(current)
+ if id == kern_code then
+ setkern(current,0)
+ elseif id == glyph_code then
+ local next = getnext(current)
+ if font then
+ setfont(current,font)
+ end
+ if dynamic > 0 then
+ setglyphdata(current,dynamic)
+ end
+ setcoloring(ma,ca,ta)
+ end
+ if current == last then
+ break
+ else
+ current = getnext(current)
+ end
+ end
+ -- We pack so that successive handling cannot touch the dropped cap. Packaging
+ -- in a hlist is also needed because we cannot locally adapt e.g. parindent (not
+ -- yet stored in with par).
+ local prev = getprev(first)
+ local next = getnext(last)
+ --
+ setprev(first)
+ setnext(last)
+ local dropper = hpack_nodes(first)
+ local width, height, depth = getwhd(dropper)
+ setwhd(dropper,0,0,0)
+ --
+ setlink(prev,dropper)
+ setlink(dropper,next)
+ --
+ if next then
+ local current = next
+ while current do
+ local id = getid(current)
+ if id == glue_code or id == kern_code then
+ local next = getnext(current)
+ -- remove_node(current,current,true) -- created an invalid next link and dangling remains
+ remove_node(head,current,true)
+ current = next
+ else
+ break
+ end
+ end
+ end
+ --
+ local hoffset = width + hoffset + distance + (indent and parindent or 0)
+ for current in nextglyph, first do
+ setoffsets(current,-hoffset,-voffset) -- no longer - height here
+ if current == last then
+ break
+ end
+ end
+ --
+ first = dropper
+ --
+ if setting.location == v_margin then
+ -- okay
+ else
+ if lines == 0 then -- safeguard, not too precise
+ lines = ceil((height+voffset) / baseline)
+ end
+ -- We cannot set parshape yet ... when we can I'll add a slope
+ -- option (positive and negative, in emwidth).
+ local hangafter = - lines
+ local hangindent = width + distance
+ if trace_initials then
+ report_initials("setting hangafter to %i and hangindent to %p",hangafter,hangindent)
+ end
+ texset("hangafter",hangafter,true)
+ texset("hangindent",hangindent,true)
+ end
+ if indent then
+ insert_after(first,first,new_kern(-parindent))
+ end
+ end
+ return head
+end
+
+-- we can count ... when all done, we can disable ...
+
+function initials.handler(head)
+ if getid(head) == par_code and start_of_par(head) then
+ local settings = getprop(head,a_initial)
+ if settings then
+ disableaction("processors","typesetters.initials.handler")
+ local alternative = settings.alternative or v_default
+ local action = actions[alternative] or actions[v_default]
+ if action then
+ if trace_initials then
+ report_initials("processing initials, alternative %a",alternative)
+ end
+ return action(head,settings)
+ end
+ end
+ end
+ return head
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-drp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-drp.mkxl
new file mode 100644
index 00000000000..94e08c9b013
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-drp.mkxl
@@ -0,0 +1,129 @@
+%D \module
+%D [ file=typo-drp, % was typo-par
+%D version=2011.10.27,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Initials,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Initials}
+
+%D This will change ... par specific attribute set and such.
+
+\unprotect
+
+\registerctxluafile{typo-drp}{autosuffix}
+
+\definesystemattribute[initial][public]
+
+\appendtoks
+ \c_attr_initial\attributeunsetvalue
+\to \everyforgetall
+
+%D For the moment here: dropped caps redone. In addition to the parameters shown in
+%D the example you can use the \type {style} and more low level \type {font} keys to
+%D set up the font where the first one wins. If you don't like the behaviour you can
+%D always roll out your own command.
+%D
+%D \starttyping
+%D \placeinitial \input ward \par \input ward \par \placeinitial \input tufte
+%D
+%D \setupinitial
+%D [location=text,
+%D n=2,
+%D color=darkred,
+%D distance=-1em,
+%D hoffset=1em,
+%D voffset=-3ex,
+%D before=\blank]
+%D
+%D \placeinitial \input ward \par \input ward \placeinitial \input tufte
+%D \stoptyping
+
+% todo: angle (once we can set parshape at the Lua end)
+
+\installcorenamespace{initial}
+
+\installcommandhandler \??initial {initial} \??initial
+
+\definemeasure[initial:n][\lineheight*\initialparameter\c!n - \strutdp]
+
+\setupinitial
+ [\c!location=\v!text,
+ \c!n=3,
+ \c!m=1,
+ \c!method=\v!none,
+ % \c!font=Bold sa 4,
+ % \c!font=Bold ht \measure{initial:n},
+ \c!font=Bold cp \measure{initial:n},
+ \c!distance=.125\emwidth,
+ \c!hoffset=\zeropoint,
+ \c!voffset=\v!line, % \dimexp\lineheight*\initialparameter\c!n-\lineheight\relax]
+ \c!style=,
+ \c!color=,
+ \c!before=\blank]
+
+\permanent\tolerant\protected\def\placeinitial[#1]% old command
+ {\par
+ \namedinitialparameter{#1}\c!before
+ \edef\typo_initial_handle{\typo_initial_handle_indeed{#1}{}}}
+
+\permanent\tolerant\protected\def\setinitial[#1]#*[#2]%
+ {\edef\typo_initial_handle{\typo_initial_handle_indeed{#1}{#2}}}
+
+\protected\def\typo_initial_handle_indeed#1#2%
+ {\dontleavehmode
+ \begingroup
+ \edef\currentinitial{#1}%
+ \setupcurrentinitial[#2]%
+ \resetfontfeature % might be needed in more places
+ \doifelsenothing{\initialparameter\c!style}
+ {\definedfont[\initialparameter\c!font]}
+ {\useinitialstyleparameter\c!style}%
+ \useinitialcolorparameter\c!color
+ \edef\p_voffset{\initialparameter\c!voffset}%
+ \scratchcounter \initialparameter\c!n\relax
+ \scratchvoffset\dimexpr\ifx\p_voffset\v!line\scratchcounter\lineheight-\lineheight\else\p_voffset\fi\relax
+ \clf_setinitial
+ location {\initialparameter\c!location}%
+ enabled true\space
+ n \scratchcounter
+ m \numexpr\initialparameter\c!m\relax
+ method {\initialparameter\c!method}%
+ distance \dimexpr\initialparameter\c!distance\relax
+ hoffset \dimexpr\initialparameter\c!hoffset\relax
+ voffset \scratchvoffset
+ ma \c_attr_colormodel
+ ca \c_attr_color
+ ta \c_attr_transparency
+ font \fontid\font
+ dynamic \font_dynamic_value % it's a bit over the top to support this here
+ \relax
+ \c_attr_initial\plusone
+ \initialparameter\c!text
+ \endgroup
+ \glet\typo_initial_handle\relax}
+
+\let\typo_initial_handle\relax
+
+% \setupbodyfont[dejavu,9pt]
+%
+% \startbuffer
+% \setinitial[two] D. E. Knuth \ignorespaces\input knuth \par
+% \setinitial[two] Knuth \ignorespaces\input knuth \par
+% \setinitial[two] \quotation{D. E. Knuth} \ignorespaces\input knuth \par
+% \setinitial[two] \quotation {Knuth} \ignorespaces\input knuth \par
+% \setinitial[two] [text={D.E. Knuth}] \ignorespaces\input knuth \par
+% \setinitial[two] [m=4] D. E. Knuth \ignorespaces\input knuth \par
+% \stopbuffer
+%
+% \type{m=2} \start \defineinitial[two][m=2,method=none] \getbuffer \page \stop
+% \type{m=1,method=auto} \start \defineinitial[two][m=1,method=auto] \getbuffer \page \stop
+% \type{m=1,method={auto,first,last}} \start \defineinitial[two][m=1,method={first,auto,last}] \getbuffer \page \stop
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-duc.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-duc.lmt
new file mode 100644
index 00000000000..07ae140e867
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-duc.lmt
@@ -0,0 +1,1048 @@
+if not modules then modules = { } end modules ['typo-duc'] = {
+ version = 1.001,
+ comment = "companion to typo-dir.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ comment = "Unicode bidi (sort of) variant c",
+}
+
+-- This is a follow up on typo-uda which itself is a follow up on t-bidi by Khaled Hosny which
+-- in turn is based on minibidi.c from Arabeyes. This is a further optimizations, as well as
+-- an update on some recent unicode bidi developments. There is (and will) also be more control
+-- added. As a consequence this module is somewhat slower than its precursor which itself is
+-- slower than the one-pass bidi handler. This is also a playground and I might add some plugin
+-- support. However, in the meantime performance got a bit better and this third variant is again
+-- some 10% faster than the second variant.
+
+-- todo (cf html):
+--
+-- normal The element does not offer a additional level of embedding with respect to the bidirectional algorithm. For inline elements implicit reordering works across element boundaries.
+-- embed If the element is inline, this value opens an additional level of embedding with respect to the bidirectional algorithm. The direction of this embedding level is given by the direction property.
+-- bidi-override For inline elements this creates an override. For block container elements this creates an override for inline-level descendants not within another block container element. This means that inside the element, reordering is strictly in sequence according to the direction property; the implicit part of the bidirectional algorithm is ignored.
+-- isolate This keyword indicates that the element's container directionality should be calculated without considering the content of this element. The element is therefore isolated from its siblings. When applying its bidirectional-resolution algorithm, its container element treats it as one or several U+FFFC Object Replacement Character, i.e. like an image.
+-- isolate-override This keyword applies the isolation behavior of the isolate keyword to the surrounding content and the override behavior o f the bidi-override keyword to the inner content.
+-- plaintext This keyword makes the elements directionality calculated without considering its parent bidirectional state or the value of the direction property. The directionality is calculated using the P2 and P3 rules of the Unicode Bidirectional Algorithm.
+-- This value allows to display data which has already formatted using a tool following the Unicode Bidirectional Algorithm.
+--
+-- todo: check for introduced errors
+-- todo: reuse list, we have size, so we can just change values (and auto allocate when not there)
+-- todo: reuse the stack
+-- todo: no need for a max check
+-- todo: collapse bound similar ranges (not ok yet)
+-- todo: combine some sweeps
+-- todo: removing is not needed when we inject at the same spot (only chnage the dir property)
+-- todo: isolated runs (isolating runs are similar to bidi=local in the basic analyzer)
+
+-- todo: check unicode addenda (from the draft):
+--
+-- Added support for canonical equivalents in BD16.
+-- Changed logic in N0 to not check forwards for context in the case of enclosed text opposite the embedding direction.
+-- Major extension of the algorithm to allow for the implementation of directional isolates and the introduction of new isolate-related values to the Bidi_Class property.
+-- Adds BD8, BD9, BD10, BD11, BD12, BD13, BD14, BD15, and BD16, Sections 2.4 and 2.5, and Rules X5a, X5b, X5c and X6a.
+-- Extensively revises Section 3.3.2, Explicit Levels and Directions and its existing X rules to formalize the algorithm for matching a PDF with the embedding or override initiator whose scope it terminates.
+-- Moves Rules X9 and X10 into a separate new Section 3.3.3, Preparations for Implicit Processing.
+-- Modifies Rule X10 to make the isolating run sequence the unit to which subsequent rules are applied.
+-- Modifies Rule W1 to change an NSM preceded by an isolate initiator or PDI into ON.
+-- Adds Rule N0 and makes other changes to Section 3.3.5, Resolving Neutral and Isolate Formatting Types to resolve bracket pairs to the same level.
+
+local insert, remove, unpack, concat = table.insert, table.remove, table.unpack, table.concat
+local utfchar = utf.char
+local setmetatable = setmetatable
+local formatters = string.formatters
+
+local directiondata = characters.directions
+local mirrordata = characters.mirrors
+local textclassdata = characters.textclasses
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+----- getchar = nuts.getchar
+local getattr = nuts.getattr
+local getprop = nuts.getprop
+local getdirection = nuts.getdirection
+local isnextchar = nuts.isnextchar -- we do this before the font handler
+
+local setprop = nuts.setprop
+local setchar = nuts.setchar
+local setdirection = nuts.setdirection
+local setattrlist = nuts.setattrlist
+
+local properties = nodes.properties.data
+
+local remove_node = nuts.remove
+local insert_node_after = nuts.insert_after
+local insert_node_before = nuts.insert_before
+local start_of_par = nuts.start_of_par
+
+local nodepool = nuts.pool
+local new_direction = nodepool.direction
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local math_code = nodecodes.math
+local dir_code = nodecodes.dir
+local par_code = nodecodes.par
+local penalty_code = nodecodes.penalty
+
+local parfillskip_code = gluecodes.parfillskip
+local parfillleftskip_code = gluecodes.parfillleftskip
+
+local dirvalues = nodes.dirvalues
+local lefttoright_code = dirvalues.lefttoright
+local righttoleft_code = dirvalues.righttoleft
+
+local maximum_stack = 0xFF
+
+local a_directions = attributes.private('directions')
+
+local directions = typesetters.directions
+local setcolor = directions.setcolor
+local getfences = directions.getfences
+
+local remove_controls = true directives.register("typesetters.directions.removecontrols",function(v) remove_controls = v end)
+----- analyze_fences = true directives.register("typesetters.directions.analyzefences", function(v) analyze_fences = v end)
+
+local report_directions = logs.reporter("typesetting","directions three")
+
+local trace_directions = false trackers.register("typesetters.directions", function(v) trace_directions = v end)
+local trace_details = false trackers.register("typesetters.directions.details", function(v) trace_details = v end)
+local trace_list = false trackers.register("typesetters.directions.list", function(v) trace_list = v end)
+
+-- strong (old):
+--
+-- l : left to right
+-- r : right to left
+-- lro : left to right override
+-- rlo : left to left override
+-- lre : left to right embedding
+-- rle : left to left embedding
+-- al : right to legt arabic (esp punctuation issues)
+--
+-- weak:
+--
+-- en : english number
+-- es : english number separator
+-- et : english number terminator
+-- an : arabic number
+-- cs : common number separator
+-- nsm : nonspacing mark
+-- bn : boundary neutral
+--
+-- neutral:
+--
+-- b : paragraph separator
+-- s : segment separator
+-- ws : whitespace
+-- on : other neutrals
+--
+-- interesting: this is indeed better (and more what we expect i.e. we already use this split
+-- in the old original (also these isolates)
+--
+-- strong (new):
+--
+-- l : left to right
+-- r : right to left
+-- al : right to left arabic (esp punctuation issues)
+--
+-- explicit: (new)
+--
+-- lro : left to right override
+-- rlo : left to left override
+-- lre : left to right embedding
+-- rle : left to left embedding
+-- pdf : pop dir format
+-- lri : left to right isolate
+-- rli : left to left isolate
+-- fsi : first string isolate
+-- pdi : pop directional isolate
+
+local whitespace = {
+ lre = true,
+ rle = true,
+ lro = true,
+ rlo = true,
+ pdf = true,
+ bn = true,
+ ws = true,
+}
+
+local b_s_ws_on = {
+ b = true,
+ s = true,
+ ws = true,
+ on = true
+}
+
+-- tracing
+
+local function show_list(list,size,what)
+ local what = what or "direction"
+ local joiner = utfchar(0x200C)
+ local result = { }
+ for i=1,size do
+ local entry = list[i]
+ local character = entry.char
+ local direction = entry[what]
+ if character == 0xFFFC then
+ local first = entry.id
+ local last = entry.last
+ local skip = entry.skip
+ if last then
+ result[i] = formatters["%-3s:%s %s..%s (%i)"](direction,joiner,nodecodes[first],nodecodes[last],skip or 0)
+ else
+ result[i] = formatters["%-3s:%s %s (%i)"](direction,joiner,nodecodes[first],skip or 0)
+ end
+ elseif character >= 0x202A and character <= 0x202C then
+ result[i] = formatters["%-3s:%s %U"](direction,joiner,character)
+ else
+ result[i] = formatters["%-3s:%s %c %U"](direction,joiner,character,character)
+ end
+ end
+ return concat(result,joiner .. " | " .. joiner)
+end
+
+-- preparation
+
+local function show_done(list,size)
+ local joiner = utfchar(0x200C)
+ local result = { }
+ local format = formatters["<%s>"]
+ for i=1,size do
+ local entry = list[i]
+ local character = entry.char
+ local begindir = entry.begindir
+ local enddir = entry.enddir
+ if begindir then
+ result[#result+1] = format(begindir)
+ end
+ if entry.remove then
+ -- continue
+ elseif character == 0xFFFC then
+ result[#result+1] = format("?")
+ elseif character == 0x0020 then
+ result[#result+1] = format(" ")
+ elseif character >= 0x202A and character <= 0x202C then
+ result[#result+1] = format(entry.original)
+ else
+ result[#result+1] = utfchar(character)
+ end
+ if enddir then
+ result[#result+1] = format(enddir)
+ end
+ end
+ return concat(result,joiner)
+end
+
+-- keeping the list and overwriting doesn't save much runtime, only a few percent
+-- char is only used for mirror, so in fact we can as well only store it for
+-- glyphs only
+--
+-- tracking what direction is used and skipping tests is not faster (extra kind of
+-- compensates gain)
+
+local mt_space = { __index = { char = 0x0020, direction = "ws", original = "ws", level = 0, skip = 0 } }
+local mt_lre = { __index = { char = 0x202A, direction = "lre", original = "lre", level = 0, skip = 0 } }
+local mt_rle = { __index = { char = 0x202B, direction = "rle", original = "rle", level = 0, skip = 0 } }
+local mt_pdf = { __index = { char = 0x202C, direction = "pdf", original = "pdf", level = 0, skip = 0 } }
+local mt_object = { __index = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = 0 } }
+
+local stack = table.setmetatableindex("table") -- shared
+local list = { } -- shared
+
+-- instead of skip we can just have slots filled with 'skip'
+
+local function build_list(head,where)
+ -- P1
+ local current = head
+ local size = 0
+ while current do
+ size = size + 1
+ -- local id = getid(current)
+ local nxt, chr, id = isnextchar(current)
+ local p = properties[current]
+ if p and p.directions then
+ -- tricky as dirs can be injected in between
+ local skip = 0
+ local last = id
+ -- current = getnext(current)
+ current = nxt
+ while current do
+ local id = getid(current)
+ local p = properties[current]
+ if p and p.directions then
+ skip = skip + 1
+ last = id
+ current = getnext(current)
+ else
+ break
+ end
+ end
+ if id == last then -- the start id
+ list[size] = setmetatable({ skip = skip, id = id },mt_object)
+ else
+ list[size] = setmetatable({ skip = skip, id = id, last = last },mt_object)
+ end
+ -- elseif id == glyph_code then
+ elseif chr then
+ -- local chr = getchar(current)
+ local dir = directiondata[chr]
+ -- could also be a metatable
+ list[size] = { char = chr, direction = dir, original = dir, level = 0 }
+ -- current = getnext(current)
+ current = nxt
+ -- if not list[dir] then list[dir] = true end -- not faster when we check for usage
+ elseif id == glue_code then -- and how about kern
+ list[size] = setmetatable({ },mt_space)
+ -- current = getnext(current)
+ current = nxt
+ elseif id == dir_code then
+ local dir, pop = getdirection(current)
+ if dir == lefttoright_code then
+ list[size] = setmetatable({ },pop and mt_pdf or mt_lre)
+ elseif dir == righttoleft_code then
+ list[size] = setmetatable({ },pop and mt_pdf or mt_rle)
+ else
+ list[size] = setmetatable({ id = id },mt_object)
+ end
+ -- current = getnext(current)
+ current = nxt
+ elseif id == math_code then
+ local skip = 0
+ -- current = getnext(current)
+ current = nxt
+ while getid(current) ~= math_code do
+ skip = skip + 1
+ current = getnext(current)
+ end
+ skip = skip + 1
+ current = getnext(current)
+ list[size] = setmetatable({ id = id, skip = skip },mt_object)
+ else -- disc_code: we assume that these are the same as the surrounding
+ local skip = 0
+ local last = id
+ -- current = getnext(current)
+ current = nxt
+ while n do
+ local id = getid(current)
+ if id ~= glyph_code and id ~= glue_code and id ~= dir_code then
+ skip = skip + 1
+ last = id
+ current = getnext(current)
+ else
+ break
+ end
+ end
+ if id == last then -- the start id
+ list[size] = setmetatable({ id = id, skip = skip },mt_object)
+ else
+ list[size] = setmetatable({ id = id, skip = skip, last = last },mt_object)
+ end
+ end
+ end
+ return list, size
+end
+
+-- new
+
+-- we could support ( ] and [ ) and such ...
+
+-- ש ) ל ( א 0-0
+-- ש ( ל ] א 0-0
+-- ש ( ל ) א 2-4
+-- ש ( ל [ א ) כ ] 2-6
+-- ש ( ל ] א ) כ 2-6
+-- ש ( ל ) א ) כ 2-4
+-- ש ( ל ( א ) כ 4-6
+-- ש ( ל ( א ) כ ) 2-8,4-6
+-- ש ( ל [ א ] כ ) 2-8,4-6
+
+local fencestack = table.setmetatableindex("table")
+
+local function resolve_fences(list,size,start,limit)
+ -- N0: funny effects, not always better, so it's an option
+ local nofstack = 0
+ for i=start,limit do
+ local entry = list[i]
+ if entry.direction == "on" then
+ local char = entry.char
+ local mirror = mirrordata[char]
+ if mirror then
+ local class = textclassdata[char]
+ entry.mirror = mirror
+ entry.class = class
+ if class == "open" then
+ nofstack = nofstack + 1
+ local stacktop = fencestack[nofstack]
+ stacktop[1] = mirror
+ stacktop[2] = i
+ elseif nofstack == 0 then
+ -- skip
+ elseif class == "close" then
+ while nofstack > 0 do
+ local stacktop = fencestack[nofstack]
+ if stacktop[1] == char then
+ local open = stacktop[2]
+ local close = i
+ list[open ].paired = close
+ list[close].paired = open
+ break
+ else
+ -- do we mirror or not
+ end
+ nofstack = nofstack - 1
+ end
+ end
+ end
+ end
+ end
+end
+
+-- local function test_fences(str)
+-- local list = { }
+-- for s in string.gmatch(str,".") do
+-- local b = utf.byte(s)
+-- list[#list+1] = { c = s, char = b, direction = directiondata[b] }
+-- end
+-- resolve_fences(list,#list,1,#size)
+-- inspect(list)
+-- end
+--
+-- test_fences("a(b)c(d)e(f(g)h)i")
+-- test_fences("a(b[c)d]")
+
+-- the action
+
+local function get_baselevel(head,list,size,direction)
+ if direction == lefttoright_code or direction == righttoleft_code then
+ return direction, true
+ elseif getid(head) == par_code and start_of_par(head) then
+ direction = getdirection(head)
+ if direction == lefttoright_code or direction == righttoleft_code then
+ return direction, true
+ end
+ end
+ -- P2, P3
+ for i=1,size do
+ local entry = list[i]
+ local direction = entry.direction
+ if direction == "r" or direction == "al" then -- and an ?
+ return righttoleft_code, true
+ elseif direction == "l" then
+ return lefttoright_code, true
+ end
+ end
+ return lefttoright_code, false
+end
+
+local function resolve_explicit(list,size,baselevel)
+-- if list.rle or list.lre or list.rlo or list.lro then
+ -- X1
+ local level = baselevel
+ local override = "on"
+ local nofstack = 0
+ for i=1,size do
+ local entry = list[i]
+ local direction = entry.direction
+ -- X2
+ if direction == "rle" then
+ if nofstack < maximum_stack then
+ nofstack = nofstack + 1
+ local stacktop = stack[nofstack]
+ stacktop[1] = level
+ stacktop[2] = override
+ level = level + (level % 2 == 1 and 2 or 1) -- least_greater_odd(level)
+ override = "on"
+ entry.level = level
+ entry.direction = "bn"
+ entry.remove = true
+ elseif trace_directions then
+ report_directions("stack overflow at position %a with direction %a",i,direction)
+ end
+ -- X3
+ elseif direction == "lre" then
+ if nofstack < maximum_stack then
+ nofstack = nofstack + 1
+ local stacktop = stack[nofstack]
+ stacktop[1] = level
+ stacktop[2] = override
+ level = level + (level % 2 == 1 and 1 or 2) -- least_greater_even(level)
+ override = "on"
+ entry.level = level
+ entry.direction = "bn"
+ entry.remove = true
+ elseif trace_directions then
+ report_directions("stack overflow at position %a with direction %a",i,direction)
+ end
+ -- X4
+ elseif direction == "rlo" then
+ if nofstack < maximum_stack then
+ nofstack = nofstack + 1
+ local stacktop = stack[nofstack]
+ stacktop[1] = level
+ stacktop[2] = override
+ level = level + (level % 2 == 1 and 2 or 1) -- least_greater_odd(level)
+ override = "r"
+ entry.level = level
+ entry.direction = "bn"
+ entry.remove = true
+ elseif trace_directions then
+ report_directions("stack overflow at position %a with direction %a",i,direction)
+ end
+ -- X5
+ elseif direction == "lro" then
+ if nofstack < maximum_stack then
+ nofstack = nofstack + 1
+ local stacktop = stack[nofstack]
+ stacktop[1] = level
+ stacktop[2] = override
+ level = level + (level % 2 == 1 and 1 or 2) -- least_greater_even(level)
+ override = "l"
+ entry.level = level
+ entry.direction = "bn"
+ entry.remove = true
+ elseif trace_directions then
+ report_directions("stack overflow at position %a with direction %a",i,direction)
+ end
+ -- X7
+ elseif direction == "pdf" then
+ if nofstack > 0 then
+ local stacktop = stack[nofstack]
+ level = stacktop[1]
+ override = stacktop[2]
+ nofstack = nofstack - 1
+ entry.level = level
+ entry.direction = "bn"
+ entry.remove = true
+ elseif trace_directions then
+ report_directions("stack underflow at position %a with direction %a",
+ i, direction)
+ else
+ report_directions("stack underflow at position %a with direction %a: %s",
+ i, direction, show_list(list,size))
+ end
+ -- X6
+ else
+ entry.level = level
+ if override ~= "on" then
+ entry.direction = override
+ end
+ end
+ end
+ -- X8 (reset states and overrides after paragraph)
+end
+
+local function resolve_weak(list,size,start,limit,orderbefore,orderafter)
+ -- W1: non spacing marks get the direction of the previous character
+-- if list.nsm then
+ for i=start,limit do
+ local entry = list[i]
+ if entry.direction == "nsm" then
+ if i == start then
+ entry.direction = orderbefore
+ else
+ entry.direction = list[i-1].direction
+ end
+ end
+ end
+-- end
+ -- W2: mess with numbers and arabic
+-- if list.en then
+ for i=start,limit do
+ local entry = list[i]
+ if entry.direction == "en" then
+ for j=i-1,start,-1 do
+ local prev = list[j]
+ local direction = prev.direction
+ if direction == "al" then
+ entry.direction = "an"
+ break
+ elseif direction == "r" or direction == "l" then
+ break
+ end
+ end
+ end
+ end
+-- end
+ -- W3
+-- if list.al then
+ for i=start,limit do
+ local entry = list[i]
+ if entry.direction == "al" then
+ entry.direction = "r"
+ end
+ end
+-- end
+ -- W4: make separators number
+-- if list.es or list.cs then
+ -- skip
+ if false then
+ for i=start+1,limit-1 do
+ local entry = list[i]
+ local direction = entry.direction
+ if direction == "es" then
+ if list[i-1].direction == "en" and list[i+1].direction == "en" then
+ entry.direction = "en"
+ end
+ elseif direction == "cs" then
+ local prevdirection = list[i-1].direction
+ if prevdirection == "en" then
+ if list[i+1].direction == "en" then
+ entry.direction = "en"
+ end
+ elseif prevdirection == "an" and list[i+1].direction == "an" then
+ entry.direction = "an"
+ end
+ end
+ end
+ else -- only more efficient when we have es/cs
+ local runner = start + 2
+ if runner <= limit then
+ local before = list[start]
+ local entry = list[start + 1]
+ local after = list[runner]
+ while after do
+ local direction = entry.direction
+ if direction == "es" then
+ if before.direction == "en" and after.direction == "en" then
+ entry.direction = "en"
+ end
+ elseif direction == "cs" then
+ local prevdirection = before.direction
+ if prevdirection == "en" then
+ if after.direction == "en" then
+ entry.direction = "en"
+ end
+ elseif prevdirection == "an" and after.direction == "an" then
+ entry.direction = "an"
+ end
+ end
+ before = current
+ current = after
+ after = list[runner]
+ runner = runner + 1
+ end
+ end
+ end
+-- end
+ -- W5
+-- if list.et then
+ local i = start
+ while i <= limit do
+ if list[i].direction == "et" then
+ local runstart = i
+ local runlimit = runstart
+ for i=runstart,limit do
+ if list[i].direction == "et" then
+ runlimit = i
+ else
+ break
+ end
+ end
+ local rundirection = runstart == start and sor or list[runstart-1].direction
+ if rundirection ~= "en" then
+ rundirection = runlimit == limit and orderafter or list[runlimit+1].direction
+ end
+ if rundirection == "en" then
+ for j=runstart,runlimit do
+ list[j].direction = "en"
+ end
+ end
+ i = runlimit
+ end
+ i = i + 1
+ end
+-- end
+ -- W6
+-- if list.es or list.cs or list.et then
+ for i=start,limit do
+ local entry = list[i]
+ local direction = entry.direction
+ if direction == "es" or direction == "et" or direction == "cs" then
+ entry.direction = "on"
+ end
+ end
+-- end
+ -- W7
+ for i=start,limit do
+ local entry = list[i]
+ if entry.direction == "en" then
+ local prev_strong = orderbefore
+ for j=i-1,start,-1 do
+ local direction = list[j].direction
+ if direction == "l" or direction == "r" then
+ prev_strong = direction
+ break
+ end
+ end
+ if prev_strong == "l" then
+ entry.direction = "l"
+ end
+ end
+ end
+end
+
+local function resolve_neutral(list,size,start,limit,orderbefore,orderafter)
+ -- N1, N2
+ for i=start,limit do
+ local entry = list[i]
+ if b_s_ws_on[entry.direction] then
+ -- this needs checking
+ local leading_direction, trailing_direction, resolved_direction
+ local runstart = i
+ local runlimit = runstart
+-- for j=runstart,limit do
+ for j=runstart+1,limit do
+ if b_s_ws_on[list[j].direction] then
+-- runstart = j
+ runlimit = j
+ else
+ break
+ end
+ end
+ if runstart == start then
+ leading_direction = orderbefore
+ else
+ leading_direction = list[runstart-1].direction
+ if leading_direction == "en" or leading_direction == "an" then
+ leading_direction = "r"
+ end
+ end
+ if runlimit == limit then
+ trailing_direction = orderafter
+ else
+ trailing_direction = list[runlimit+1].direction
+ if trailing_direction == "en" or trailing_direction == "an" then
+ trailing_direction = "r"
+ end
+ end
+ if leading_direction == trailing_direction then
+ -- N1
+ resolved_direction = leading_direction
+ else
+ -- N2 / does the weird period
+ resolved_direction = entry.level % 2 == 1 and "r" or "l"
+ end
+ for j=runstart,runlimit do
+ list[j].direction = resolved_direction
+ end
+ i = runlimit
+ end
+ i = i + 1
+ end
+end
+
+local function resolve_implicit(list,size,start,limit,orderbefore,orderafter,baselevel)
+ for i=start,limit do
+ local entry = list[i]
+ local level = entry.level
+ local direction = entry.direction
+ if level % 2 ~= 1 then -- even
+ -- I1
+ if direction == "r" then
+ entry.level = level + 1
+ elseif direction == "an" or direction == "en" then
+ entry.level = level + 2
+ end
+ else
+ -- I2
+ if direction == "l" or direction == "en" or direction == "an" then
+ entry.level = level + 1
+ end
+ end
+ end
+end
+
+local function resolve_levels(list,size,baselevel,analyze_fences)
+ -- X10
+ local start = 1
+ while start < size do
+ local level = list[start].level
+ local limit = start + 1
+ while limit < size and list[limit].level == level do
+ limit = limit + 1
+ end
+ local prev_level = start == 1 and baselevel or list[start-1].level
+ local next_level = limit == size and baselevel or list[limit+1].level
+ local orderbefore = (level > prev_level and level or prev_level) % 2 == 1 and "r" or "l"
+ local orderafter = (level > next_level and level or next_level) % 2 == 1 and "r" or "l"
+ -- W1 .. W7
+ resolve_weak(list,size,start,limit,orderbefore,orderafter)
+ -- N0
+ if analyze_fences then
+ resolve_fences(list,size,start,limit)
+ end
+ -- N1 .. N2
+ resolve_neutral(list,size,start,limit,orderbefore,orderafter)
+ -- I1 .. I2
+ resolve_implicit(list,size,start,limit,orderbefore,orderafter,baselevel)
+ start = limit
+ end
+ -- L1
+ for i=1,size do
+ local entry = list[i]
+ local direction = entry.original
+ -- (1)
+ if direction == "s" or direction == "b" then
+ entry.level = baselevel
+ -- (2)
+ for j=i-1,1,-1 do
+ local entry = list[j]
+ if whitespace[entry.original] then
+ entry.level = baselevel
+ else
+ break
+ end
+ end
+ end
+ end
+ -- (3)
+ for i=size,1,-1 do
+ local entry = list[i]
+ if whitespace[entry.original] then
+ entry.level = baselevel
+ else
+ break
+ end
+ end
+ -- L4
+ if analyze_fences then
+ for i=1,size do
+ local entry = list[i]
+ if entry.level % 2 == 1 then -- odd(entry.level)
+ if entry.mirror and not entry.paired then
+ entry.mirror = false
+ end
+ -- okay
+ elseif entry.mirror then
+ entry.mirror = false
+ end
+ end
+ else
+ for i=1,size do
+ local entry = list[i]
+ if entry.level % 2 == 1 then -- odd(entry.level)
+ local mirror = mirrordata[entry.char]
+ if mirror then
+ entry.mirror = mirror
+ end
+ end
+ end
+ end
+end
+
+local stack = { }
+
+local function insert_dir_points(list,size)
+ -- L2, but no actual reversion is done, we simply annotate where
+ -- begindir/endddir node will be inserted.
+ local maxlevel = 0
+ local toggle = true
+ for i=1,size do
+ local level = list[i].level
+ if level > maxlevel then
+ maxlevel = level
+ end
+ end
+ for level=0,maxlevel do
+ local started -- = false
+ local begindir -- = nil
+ local enddir -- = nil
+ local prev -- = nil
+ if toggle then
+ begindir = lefttoright_code
+ enddir = lefttoright_code
+ toggle = false
+ else
+ begindir = righttoleft_code
+ enddir = righttoleft_code
+ toggle = true
+ end
+ for i=1,size do
+ local entry = list[i]
+ if entry.level >= level then
+ if not started then
+ entry.begindir = begindir
+ started = true
+ end
+ else
+ if started then
+ prev.enddir = enddir
+ started = false
+ end
+ end
+ prev = entry
+ end
+ end
+ -- make sure to close the run at end of line
+ local last = list[size]
+ if not last.enddir then
+ local n = 0
+ for i=1,size do
+ local entry = list[i]
+ local e = entry.enddir
+ local b = entry.begindir
+ if e then
+ n = n - 1
+ end
+ if b then
+ n = n + 1
+ stack[n] = b
+ end
+ end
+ if n > 0 then
+ if trace_list and n > 1 then
+ report_directions("unbalanced list")
+ end
+ last.enddir = stack[n]
+ end
+ end
+end
+
+-- We flag nodes that can be skipped when we see them again but because whatever
+-- mechanism can inject dir nodes that then are not flagged, we don't flag dir
+-- nodes that we inject here.
+
+local function apply_to_list(list,size,head,pardir)
+ local index = 1
+ local current = head
+ if trace_list then
+ report_directions("start run")
+ end
+ while current do
+ if index > size then
+ report_directions("fatal error, size mismatch")
+ break
+ end
+ local id = getid(current) -- we can better store the id in list[index]
+ local entry = list[index]
+ local begindir = entry.begindir
+ local enddir = entry.enddir
+ local p = properties[current]
+ if p then
+ p.directions = true
+ else
+ properties[current] = { directions = true }
+ end
+ if id == glyph_code then
+ local mirror = entry.mirror
+ if mirror then
+ setchar(current,mirror)
+ end
+ if trace_directions then
+ local direction = entry.direction
+ if trace_list then
+ local original = entry.original
+ local char = entry.char
+ local level = entry.level
+ if direction == original then
+ report_directions("%2i : %C : %s",level,char,direction)
+ else
+ report_directions("%2i : %C : %s -> %s",level,char,original,direction)
+ end
+ end
+ setcolor(current,direction,false,mirror)
+ end
+ elseif id == hlist_code or id == vlist_code then
+ setdirection(current,pardir) -- is this really needed?
+ elseif id == glue_code then
+ -- Maybe I should also fix dua and dub but on the other hand ... why?
+ if enddir and getsubtype(current) == parfillskip_code then
+ -- insert the last enddir before \parfillskip glue
+ local c = current
+ local p = getprev(c)
+ if p and getid(p) == glue_code and getsubtype(p) == parfillleftskip_code then
+ c = p
+ p = getprev(c)
+ end
+ if p and getid(p) == penalty_code then -- linepenalty
+ c = p
+ end
+ -- there is always a par nodes so head will stay
+ head = insert_node_before(head,c,new_direction(enddir,true))
+ enddir = false
+ end
+ elseif begindir then
+ if id == par_code and start_of_par(current) then
+ -- par should always be the 1st node
+ head, current = insert_node_after(head,current,new_direction(begindir))
+ begindir = nil
+ end
+ end
+ if begindir then
+ head = insert_node_before(head,current,new_direction(begindir))
+ end
+ local skip = entry.skip
+ if skip and skip > 0 then
+ for i=1,skip do
+ current = getnext(current)
+ local p = properties[current]
+ if p then
+ p.directions = true
+ else
+ properties[current] = { directions = true }
+ end
+ end
+ end
+ if enddir then
+ head, current = insert_node_after(head,current,new_direction(enddir,true))
+ end
+ if not entry.remove then
+ current = getnext(current)
+ elseif remove_controls then
+ -- X9
+ head, current = remove_node(head,current,true)
+ else
+ current = getnext(current)
+ end
+ index = index + 1
+ end
+ if trace_list then
+ report_directions("stop run")
+ end
+ return head
+end
+
+-- If needed we can optimize for only_one. There is no need to do anything
+-- when it's not a glyph. Otherwise we only need to check mirror and apply
+-- directions when it's different from the surrounding. Paragraphs always
+-- have more than one node. Actually, we only enter this function when we
+-- do have a glyph!
+
+local function process(head,direction,only_one,where)
+ -- for the moment a whole paragraph property
+ local attr = getattr(head,a_directions)
+ local analyze_fences = getfences(attr)
+ --
+ local list, size = build_list(head,where)
+ local baselevel, dirfound = get_baselevel(head,list,size,direction)
+ if trace_details then
+ report_directions("analyze: baselevel %a",baselevel == righttoleft_code and "r2l" or "l2r")
+ report_directions("before : %s",show_list(list,size,"original"))
+ end
+ resolve_explicit(list,size,baselevel)
+ resolve_levels(list,size,baselevel,analyze_fences)
+ insert_dir_points(list,size)
+ if trace_details then
+ report_directions("after : %s",show_list(list,size,"direction"))
+ report_directions("result : %s",show_done(list,size))
+ end
+ return apply_to_list(list,size,head,baselevel)
+end
+
+local variables = interfaces.variables
+
+directions.installhandler(variables.one, process) -- for old times sake
+directions.installhandler(variables.two, process) -- for old times sake
+directions.installhandler(variables.three, process) -- for old times sake
+directions.installhandler(variables.unicode,process)
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-fkr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-fkr.mkxl
new file mode 100644
index 00000000000..f56294dc057
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-fkr.mkxl
@@ -0,0 +1,38 @@
+%D \module
+%D [ file=typo-fkr,
+%D version=2016.10.10,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Additional Font Kerning,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Additional Font Kerning}
+
+\registerctxluafile{typo-fkr}{}
+
+\definesystemattribute[extrafontkern][public]
+
+\unprotect
+
+% none : not across fonts (but still within)
+% min : min value across fonts
+% max : max value across fonts
+% mixed : mean value across fonts
+% reset : disable
+
+\permanent\protected\def\setextrafontkerns[#1]% % can be public implementor
+ {\clf_setextrafontkerns{#1}}
+
+\permanent\protected\def\resetextrafontkerns
+ {\c_attr_extrafontkern\attributeunsetvalue}
+
+\appendtoks
+ \resetextrafontkerns
+\to \everyresettypesetting
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt
new file mode 100644
index 00000000000..fa507df4b1a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt
@@ -0,0 +1,406 @@
+if not modules then modules = { } end modules ['typo-fln'] = {
+ version = 1.001,
+ comment = "companion to typo-fln.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- When I ran into the following experimental code again, I figured that it dated
+-- from the early days of mkiv, so I updates it a bit to fit into todays context.
+-- In the process I might have messed up things. For instance we had a diffent
+-- wrapper then using head and tail.
+
+-- todo: only letters (no punctuation)
+-- todo: nuts
+
+local trace_firstlines = false trackers.register("typesetters.firstlines", function(v) trace_firstlines = v end)
+local report_firstlines = logs.reporter("nodes","firstlines")
+
+typesetters.firstlines = typesetters.firstlines or { }
+local firstlines = typesetters.firstlines
+
+local nodes = nodes
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+local disableaction = tasks.disableaction
+
+local context = context
+local implement = interfaces.implement
+
+local nuts = nodes.nuts
+local tonode = nuts.tonode
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local setboth = nuts.setboth
+local getid = nuts.getid
+local getwidth = nuts.getwidth
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getattr = nuts.getattr
+local getbox = nuts.getbox
+local getdisc = nuts.getdisc
+local setdisc = nuts.setdisc
+local setlink = nuts.setlink
+local setfont = nuts.setfont
+local setglyphdata = nuts.setglyphdata
+local getprop = nuts.getprop
+local setprop = nuts.setprop
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local par_code = nodecodes.par
+
+local spaceskip_code = nodes.gluecodes.spaceskip
+
+local nextglyph = nuts.traversers.glyph
+local nextdisc = nuts.traversers.disc
+
+local flush_node_list = nuts.flush_list
+local flush_node = nuts.flush_node
+local copy_node_list = nuts.copy_list
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+local getdimensions = nuts.dimensions
+local hpack_node_list = nuts.hpack
+local start_of_par = nuts.start_of_par
+
+local setcoloring = nuts.colors.set
+
+local nodepool = nuts.pool
+local newpenalty = nodepool.penalty
+local newkern = nodepool.kern
+local tracerrule = nodes.tracers.pool.nuts.rule
+
+local actions = { }
+firstlines.actions = actions
+
+local a_firstline = attributes.private('firstline')
+
+local texget = tex.get
+
+local variables = interfaces.variables
+local v_default = variables.default
+local v_line = variables.line
+local v_word = variables.word
+
+local function set(par,specification)
+ enableaction("processors","typesetters.firstlines.handler")
+ if trace_firstlines then
+ report_firstlines("enabling firstlines")
+ end
+ setprop(par,a_firstline,specification)
+end
+
+function firstlines.set(specification)
+ nuts.setparproperty(set,specification)
+end
+
+implement {
+ name = "setfirstline",
+ actions = firstlines.set,
+ arguments = {
+ {
+ { "alternative" },
+ { "font", "integer" },
+ { "dynamic", "integer" },
+ { "ma", "integer" },
+ { "ca", "integer" },
+ { "ta", "integer" },
+ { "n", "integer" },
+ }
+ }
+}
+
+actions[v_line] = function(head,setting)
+ local dynamic = setting.dynamic
+ local font = setting.font
+ local noflines = setting.n or 1
+ local ma = setting.ma or 0
+ local ca = setting.ca
+ local ta = setting.ta
+ local hangafter = texget("hangafter")
+ local hangindent = texget("hangindent")
+ local parindent = texget("parindent")
+ local nofchars = 0
+ local n = 0
+ local temp = copy_node_list(head)
+ local linebreaks = { }
+
+ local set = function(head)
+ for g in nextglyph, head do
+ if dynamic > 0 then
+ setglyphdata(g,dynamic)
+ end
+ setfont(g,font)
+ end
+ end
+
+ set(temp)
+
+ for g in nextdisc, temp do
+ local pre, post, replace = getdisc(g)
+ if pre then
+ set(pre)
+ end
+ if post then
+ set(post)
+ end
+ if replace then
+ set(replace)
+ end
+ end
+
+ local start = temp
+ local list = temp
+ local prev = temp
+ for i=1,noflines do
+ local hsize = texget("hsize") - texget("leftskip",false) - texget("rightskip",false)
+ if i == 1 then
+ hsize = hsize - parindent
+ end
+ if i <= - hangafter then
+ hsize = hsize - hangindent
+ end
+
+ local function list_dimensions(list,start)
+ local temp = copy_node_list(list,start)
+ temp = nodes.handlers.characters(temp)
+ temp = nodes.injections.handler(temp)
+ -- temp = typesetters.fontkerns.handler(temp) -- maybe when enabled
+ -- nodes.handlers.protectglyphs(temp) -- not needed as we discard
+ -- temp = typesetters.spacings.handler(temp) -- maybe when enabled
+ -- temp = typesetters.kerns.handler(temp) -- maybe when enabled
+ -- temp = typesetters.cases.handler(temp) -- maybe when enabled
+ local width = getdimensions(temp)
+ flush_node_list(temp)
+ return width
+ end
+
+ local function try(extra)
+ local width = list_dimensions(list,start)
+ if extra then
+ width = width + list_dimensions(extra)
+ end
+ -- report_firstlines("line length: %p, progression: %p, text: %s",hsize,width,nodes.listtoutf(list,nil,nil,start))
+ if width > hsize then
+ list = prev
+ return true
+ else
+ linebreaks[i] = n
+ prev = start
+ nofchars = n
+ end
+ end
+
+ while start do
+ local id = getid(start)
+ if id == glyph_code then
+ -- go on
+ elseif id == disc_code then
+ -- this could be an option
+ n = n + 1
+ local pre, post, replace = getdisc(start)
+ if pre and try(pre) then
+ break
+ elseif replace and try(replace) then
+ break
+ end
+ elseif id == kern_code then -- todo: fontkern
+ -- this could be an option
+ elseif id == glue_code then
+ n = n + 1
+ if try() then
+ break
+ end
+ end
+ start = getnext(start)
+ end
+ if not linebreaks[i] then
+ linebreaks[i] = n
+ end
+ end
+
+ flush_node_list(temp)
+
+ local start = head
+ local n = 0
+
+ local function update(start)
+ if dynamic > 0 then
+ setglyphdata(start,dynamic)
+ end
+ setfont(start,font)
+ setcoloring(ma,ca,ta)
+ end
+
+ for i=1,noflines do
+ local linebreak = linebreaks[i]
+ while start and n < nofchars do
+ local id = getid(start)
+ local ok = false
+ if id == glyph_code then
+ update(start)
+ elseif id == disc_code then
+ n = n + 1
+ local disc = start
+ local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+ if linebreak == n then
+ local p, n = getboth(start)
+ if pre then
+ for current in nextglyph, pre do
+ update(current)
+ end
+ setlink(pretail,n)
+ setlink(p,pre)
+ start = pretail
+ pre = nil
+ else
+ setlink(p,n)
+ start = p
+ end
+ if post then
+ local p, n = getboth(start)
+ setlink(posttail,n)
+ setlink(start,post)
+ post = nil
+ end
+ else
+ local p, n = getboth(start)
+ if replace then
+ for current in nextglyph, replace do
+ update(current)
+ end
+ setlink(replacetail,n)
+ setlink(p,replace)
+ start = replacetail
+ replace = nil
+ else
+ setlink(p,n)
+ start = p
+ end
+ end
+ setdisc(disc,pre,post,replace)
+ flush_node(disc)
+ elseif id == glue_code then
+ n = n + 1
+ if linebreak ~= n then
+ head = insert_node_before(head,start,newpenalty(10000)) -- nobreak
+ end
+ end
+ local next = getnext(start)
+ if linebreak == n then
+ if start ~= head then
+ local where = id == glue_code and getprev(start) or start
+ if trace_firstlines then
+ head, where = insert_node_after(head,where,newpenalty(10000)) -- nobreak
+ head, where = insert_node_after(head,where,newkern(-65536))
+ head, where = insert_node_after(head,where,tracerrule(65536,4*65536,2*65536,"darkblue"))
+ end
+ head, where = insert_node_after(head,where,newpenalty(-10000)) -- break
+ end
+ start = next
+ break
+ end
+ start = next
+ end
+ end
+
+ return head
+end
+
+actions[v_word] = function(head,setting)
+ -- local attribute = fonts.specifiers.contextnumber(setting.feature) -- was experimental
+ local dynamic = setting.dynamic
+ local font = setting.font
+ local words = 0
+ local nofwords = setting.n or 1
+ local start = head
+ local ok = false
+ local ma = setting.ma or 0
+ local ca = setting.ca
+ local ta = setting.ta
+ while start do
+ local id = getid(start)
+ -- todo: delete disc nodes
+ if id == glyph_code then
+ if not ok then
+ words = words + 1
+ ok = true
+ end
+ setcoloring(ma,ca,ta)
+ if dynamic > 0 then
+ setglyphdata(start,dynamic)
+ end
+ setfont(start,font)
+ elseif id == disc_code then
+ -- continue
+ elseif id == kern_code then -- todo: fontkern
+ -- continue
+ else
+ ok = false
+ if words == nofwords then
+ break
+ end
+ end
+ start = getnext(start)
+ end
+ return head
+end
+
+actions[v_default] = actions[v_line]
+
+function firstlines.handler(head)
+ if getid(head) == par_code and start_of_par(head) then
+ local settings = getprop(head,a_firstline)
+ if settings then
+ disableaction("processors","typesetters.firstlines.handler")
+ local alternative = settings.alternative or v_default
+ local action = actions[alternative] or actions[v_default]
+ if action then
+ if trace_firstlines then
+ report_firstlines("processing firstlines, alternative %a",alternative)
+ end
+ return action(head,settings)
+ end
+ end
+ end
+ return head
+end
+
+-- goodie
+
+local function applytofirstcharacter(box,what)
+ local tbox = getbox(box) -- assumes hlist
+ local list = getlist(tbox)
+ local done = nil
+ for n in nextglyph, list do
+ list = remove_node(list,n)
+ done = n
+ break
+ end
+ if done then
+ setlist(tbox,list)
+ local kind = type(what)
+ if kind == "string" then
+ context[what](tonode(done))
+ elseif kind == "function" then
+ what(done)
+ else
+ -- error
+ end
+ end
+end
+
+implement {
+ name = "applytofirstcharacter",
+ actions = applytofirstcharacter,
+ arguments = { "integer", "string" }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-fln.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-fln.mkxl
new file mode 100644
index 00000000000..3ef688bb55a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-fln.mkxl
@@ -0,0 +1,109 @@
+%D \module
+%D [ file=typo-fln,
+%D version=2013.08.22, % comes from the few years older m-newstuff
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=First Lines,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / First Lines}
+
+%D I had this code laying around for a while \unknown\ probably as a side effect
+%D of cleaning up the supp-fun modules. There is probably room for improvement
+%D and more features.
+
+% \setupbodyfont[pagella]
+%
+% \starttext
+%
+% \setupindenting[medium,yes]
+%
+% \definefirstline
+% [smallcaps]
+% [alternative=line,
+% color=darkred,
+% style=\setfontfeature{smallcaps}]
+%
+% \setfirstline[smallcaps] \input tufte \par
+% \setfirstline[smallcaps] \input ward \par
+% \setfirstline[smallcaps] \input knuth \par
+% \setfirstline[smallcaps] \input bryson \par
+%
+% \definefirstline
+% [smallcaps]
+% [alternative=word,
+% color=darkblue,
+% style=bold,
+% n=2]
+%
+% \setfirstline[smallcaps] \input tufte \par
+% \setfirstline[smallcaps] \input ward \par
+% \setfirstline[smallcaps] \input knuth \par
+% \setfirstline[smallcaps] \input bryson \par
+%
+% \stoptext
+
+\unprotect
+
+\registerctxluafile{typo-fln}{autosuffix}
+
+\definesystemattribute[firstline][public]
+
+\appendtoks
+ \c_attr_firstline\attributeunsetvalue
+\to \everyforgetall
+
+\installcorenamespace {firstline}
+
+\installcommandhandler \??firstline {firstline} \??firstline
+
+\setupfirstline
+ [\c!alternative=\v!line,
+ %\c!style=,
+ %\c!color=,
+ \c!n=1]
+
+\permanent\tolerant\protected\def\setfirstline[#1]%
+ {\edef\typo_firstline_handle{\typo_firstline_handle_indeed{#1}}}
+
+\protected\def\typo_firstline_handle_indeed#1%
+ {\dontleavehmode
+ \begingroup
+ \edef\currentfirstline{#1}%
+ \usefirstlinestyleandcolor\c!style\c!color
+ \clf_setfirstline
+ alternative {\firstlineparameter\c!alternative}%
+ ma \c_attr_colormodel
+ ca \c_attr_color
+ ta \c_attr_transparency
+ n \numexpr\firstlineparameter\c!n\relax
+ font \fontid\font
+ dynamic \font_dynamic_value
+ \relax
+% \kern\zeropoint % we need a node
+% \hskip\zeropoint\s!plus\emwidth\relax % can be an option
+ \endgroup
+ \glet\typo_firstline_handle\relax}
+
+\let\typo_firstline_handle\relax
+
+% goodie, question on list
+%
+% \defineframed[ChapterFramed][location=low,background=color,backgroundcolor=red,frame=off]
+%
+% \setuphead[chapter][deeptextcommand=\applytofirstcharacter\ChapterFramed]
+
+\permanent\protected\def\applytofirstcharacter#1%
+ {\begingroup
+ \dowithnextbox
+ {\clf_applytofirstcharacter\nextbox{\csstring#1}%
+ \unhbox\nextbox
+ \endgroup}%
+ \hbox}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-ini.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-ini.lmt
new file mode 100644
index 00000000000..c45d2966436
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-ini.lmt
@@ -0,0 +1,11 @@
+if not modules then modules = { } end modules ['typo-ini'] = {
+ version = 1.001,
+ comment = "companion to typo-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- nothing yet
+
+typesetters = typesetters or { }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-ini.mkxl
new file mode 100644
index 00000000000..3c081d8ca40
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-ini.mkxl
@@ -0,0 +1,49 @@
+%D \module
+%D [ file=typo-ini,
+%D version=2000.16.09,
+%D title=\CONTEXT\ Typographic Macros,
+%D subtitle=Initialization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Since \CONTEXT\ is dealing with typographics, isn't
+%D September 2000 a bit late to start writing this module? It
+%D may seem so, but since more and more languages are
+%D supported, we think it is time to isolate language specific
+%D typographic extensions in modules. The first language that
+%D demands this is Chinese, and more will follow.
+
+\writestatus{loading}{ConTeXt Typographic Macros / Initialization}
+
+\registerctxluafile{typo-ini}{autosuffix}
+
+\unprotect
+
+% This will be done differently:
+%
+% %D \macros
+% %D {ifvertical}
+% %D
+% %D The following switch can be used to signal macros that they
+% %D should adapt their behaviour.
+%
+% \newif\ifvertical % maybe also ifreverse
+%
+% %D \macros
+% %D {vhbox}
+% %D
+% %D A stupid but useful macro.
+%
+% \def\vhbox{\ifvertical\vbox\else\hbox\fi}
+
+%D \macros
+%D {everyresettypesetting}
+
+\newtoks\everyresettypesetting
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-inj.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-inj.mkxl
new file mode 100644
index 00000000000..f615014b04f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-inj.mkxl
@@ -0,0 +1,75 @@
+%D \module
+%D [ file=typo-inj,
+%D version=2014.10.13,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Triggering Actions,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Triggering Actions}
+
+%D This is a sort of escape from too automatic typesetting of lists. I have
+%D been thinking fo a more generic injector for instance based on tags (as we
+%D already index each element) but thi sis sort of fuzzy because the number of a
+%D tag is not always incremented before we check for it. Also, registers and lists
+%D are among the few candidates that cannot be controlled directly by putting
+%D something in the input. So,m for the moment I stick to this mechanism but
+%D future versions of \CONTEXT\ might do it differently. Compatibility is not much
+%D of an issue here as this mechanism is only to be used in final production runs.
+
+\unprotect
+
+\registerctxluafile{typo-inj}{}
+
+% todo: no need in trialmode
+
+%D \showinjector
+%D
+%D \setinjector[register][3][\column]
+%D \setinjector[list] [2][{\blank[3*big]}]
+%D
+%D \starttext
+%D \placelist[section][criterium=text]
+%D \blank[3*big]
+%D \placeregister[index][criterium=text]
+%D \page
+%D \startsection[title=Alpha] first \index{first} \stopsection
+%D \startsection[title=Beta] second \index{second} \stopsection
+%D \startsection[title=Gamma] third \index{third} \stopsection
+%D \startsection[title=Delta] fourth \index{fourth} \stopsection
+%D \stoptext
+
+\permanent \protected\def\resetinjector [#1]{\clf_resetinjector{#1}}
+\permanent \protected\def\markinjector [#1]{\dontleavehmode\clf_markinjector{#1}}
+\permanent\tolerant\protected\def\checkinjector [#1]#*[#2]{\clf_checkinjector{#1}{#2}}
+\permanent \protected\def\checknextinjector [#1]{\clf_checkinjector{#1}{\v!next}}
+\permanent \protected\def\checkpreviousinjector [#1]{\clf_checkinjector{#1}{\v!previous}}
+%permanent \protected\def\checknextinjector [#1]{\clf_checknextinjector{#1}}
+%permanent \protected\def\checkpreviousinjector [#1]{\clf_checkpreviousinjector{#1}}
+\permanent\tolerant\protected\def\setinjector [#1]#*[#2]#*[#3]{\clf_setinjector{#1}{#2}{#3}}
+\permanent\tolerant\protected\def\showinjector [#1]{\clf_showinjector{#1}}
+
+\permanent\protected\def\domarkinjector#1#2% called at the lua end
+ {\dontleavehmode\llap{\infofont\ifcase#1\else\red\fi<#2>\quad}}
+
+% low level definers .. we could have \injectors_mark and \injectors_check and then
+% use \v!list instead of \s!list
+
+\permanent\protected\def\doinstallinjector#1%
+ {\letcsname typo_injectors_mark_#1\endcsname\donothing
+ \letcsname typo_injectors_check_#1\endcsname\donothing}
+
+\permanent\protected\def\doactivateinjector#1% used at lua end
+ {\protected\defcsname typo_injectors_mark_#1\endcsname{\dontleavehmode\noexpand\clf_markinjector{#1}}%
+ \protected\defcsname typo_injectors_check_#1\endcsname{\noexpand\clf_checkinjector{#1}}}
+
+\permanent\protected\def\dotestinjector#1% only for testing outside unprotect
+ {\begincsname typo_injectors_check_#1\endcsname
+ \begincsname typo_injectors_mark_#1\endcsname}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt
new file mode 100644
index 00000000000..4350053b367
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-itc.lmt
@@ -0,0 +1,703 @@
+if not modules then modules = { } end modules ['typo-itc'] = {
+ version = 1.001,
+ comment = "companion to typo-itc.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tonumber = tonumber
+
+local trace_italics = false trackers.register("typesetters.italics", function(v) trace_italics = v end)
+
+local report_italics = logs.reporter("nodes","italics")
+
+local threshold = 0.5 trackers.register("typesetters.threshold", function(v) threshold = v == true and 0.5 or tonumber(v) end)
+
+typesetters.italics = typesetters.italics or { }
+local italics = typesetters.italics
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local disc_code = nodecodes.disc
+local math_code = nodecodes.math
+
+local enableaction = nodes.tasks.enableaction
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+local getprev = nuts.getprev
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getchar = nuts.getchar
+local getdisc = nuts.getdisc
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getattrlist = nuts.getattrlist
+local setattrlist = nuts.setattrlist
+local setdisc = nuts.setdisc
+local isglyph = nuts.isglyph
+local setkern = nuts.setkern
+local getkern = nuts.getkern
+local getheight = nuts.getheight
+
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+local end_of_math = nuts.end_of_math
+
+local texgetattribute = tex.getattribute
+local texsetattribute = tex.setattribute
+local a_italics = attributes.private("italics")
+local a_mathitalics = attributes.private("mathitalics")
+
+local unsetvalue = attributes.unsetvalue
+
+local new_correction_kern = nodepool.italickern
+local new_correction_glue = nodepool.glue
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local italicsdata = fonthashes.italics
+local exheights = fonthashes.exheights
+local chardata = fonthashes.characters
+
+local is_punctuation = characters.is_punctuation
+
+local implement = interfaces.implement
+
+local forcedvariant = false
+
+function typesetters.italics.forcevariant(variant)
+ forcedvariant = variant
+end
+
+-- We use the same key as the tex font handler. So, if a valua has already be set, we
+-- use that one.
+
+local function setitalicinfont(font,char)
+ local tfmdata = fontdata[font]
+ local character = tfmdata.characters[char]
+ if character then
+ local italic = character.italic
+ if not italic then
+ local autoitalicamount = tfmdata.properties.autoitalicamount or 0
+ if autoitalicamount ~= 0 then
+ local description = tfmdata.descriptions[char]
+ if description then
+ italic = description.italic
+ if not italic then
+ local boundingbox = description.boundingbox
+ italic = boundingbox[3] - description.width + autoitalicamount
+ if italic < 0 then -- < 0 indicates no overshoot or a very small auto italic
+ italic = 0
+ end
+ end
+ if italic ~= 0 then
+ italic = italic * tfmdata.parameters.hfactor
+ end
+ end
+ end
+ if trace_italics then
+ report_italics("setting italic correction of %C of font %a to %p",char,font,italic)
+ end
+ if not italic then
+ italic = 0
+ end
+ character.italic = italic
+ end
+ return italic
+ else
+ return 0
+ end
+end
+
+-- todo: clear attribute
+
+local function okay(data,current,font,prevchar,previtalic,char,what)
+ if data then
+ if trace_italics then
+ report_italics("ignoring %p between %s italic %C and italic %C",previtalic,what,prevchar,char)
+ end
+ return false
+ end
+ if threshold then
+ -- if getid(current) == glyph_code then
+ while current and getid(current) ~= glyph_code do
+ current = getprev(current)
+ end
+ if current then
+ local ht = getheight(current)
+ local ex = exheights[font]
+ local th = threshold * ex
+ if ht <= th then
+ if trace_italics then
+ report_italics("ignoring correction between %s italic %C and regular %C, height %p less than threshold %p",prevchar,what,char,ht,th)
+ end
+ return false
+ end
+ else
+ -- maybe backtrack to glyph
+ end
+ end
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and regular %C",previtalic,what,prevchar,char)
+ end
+ return true
+end
+
+-- maybe: with_attributes(current,n) :
+--
+-- local function correction_kern(kern,n)
+-- return with_attributes(new_correction_kern(kern),n)
+-- end
+
+local function correction_kern(kern,n)
+ local k = new_correction_kern(kern)
+ if n then
+-- local a = getattrlist(n)
+-- if a then -- maybe not
+-- setattrlist(k,a) -- can be a marked content (border case)
+-- end
+ setattrlist(k,n) -- can be a marked content (border case)
+ end
+ return k
+end
+
+local function correction_glue(glue,n)
+ local g = new_correction_glue(glue)
+ if n then
+-- local a = getattrlist(n)
+-- if a then -- maybe not
+-- setattrlist(g,a) -- can be a marked content (border case)
+-- end
+ setattrlist(g,n) -- can be a marked content (border case)
+ end
+ return g
+end
+
+local mathokay = false
+local textokay = false
+local enablemath = false
+local enabletext = false
+
+local function domath(head,current)
+ current = end_of_math(current)
+ local next = getnext(current)
+ if next then
+ local char, id = isglyph(next)
+ if char then
+ -- we can have an old font where italic correction has been applied
+ -- or a new one where it hasn't been done
+ local kern = getprev(current)
+ if kern and getid(kern) == kern_code then
+ local glyph = getprev(kern)
+ if glyph and getid(glyph) == glyph_code then
+ -- [math: <glyph><kern>]<glyph> : we remove the correction when we have
+ -- punctuation
+ if is_punctuation[char] then
+ local a = getattr(glyph,a_mathitalics)
+ if a and (a < 100 or a > 100) then
+ if a > 100 then
+ a = a - 100
+ else
+ a = a + 100
+ end
+ local i = getkern(kern)
+ local c, f = isglyph(glyph)
+ if getheight(next) < 1.25*exheights[f] then
+ if i == 0 then
+ if trace_italics then
+ report_italics("%s italic %p between math %C and punctuation %C","ignoring",i,c,char)
+ end
+ else
+ if trace_italics then
+ report_italics("%s italic between math %C and punctuation %C","removing",i,c,char)
+ end
+ setkern(kern,0) -- or maybe a small value or half the ic
+ end
+ elseif i == 0 then
+ local d = chardata[f][c]
+ local i = d.italic
+ if i == 0 then
+ if trace_italics then
+ report_italics("%s italic %p between math %C and punctuation %C","ignoring",i,c,char)
+ end
+ else
+ setkern(kern,i)
+ if trace_italics then
+ report_italics("%s italic %p between math %C and punctuation %C","setting",i,c,char)
+ end
+ end
+ elseif trace_italics then
+ report_italics("%s italic %p between math %C and punctuation %C","keeping",k,c,char)
+ end
+ end
+ end
+ end
+ else
+ local glyph = kern
+ if glyph and getid(glyph) == glyph_code then
+ -- [math: <glyph>]<glyph> : we add the correction when we have
+ -- no punctuation
+ if not is_punctuation[char] then
+ local a = getattr(glyph,a_mathitalics)
+ if a and (a < 100 or a > 100) then
+ if a > 100 then
+ a = a - 100
+ else
+ a = a + 100
+ end
+ if trace_italics then
+ report_italics("%s italic %p between math %C and non punctuation %C","adding",a,getchar(glyph),char)
+ end
+ insert_node_after(head,glyph,correction_kern(a,glyph))
+ end
+ end
+ end
+ end
+ end
+ end
+ return current
+end
+
+local function mathhandler(head)
+ local current = head
+ while current do
+ if getid(current) == math_code then
+ current = domath(head,current)
+ end
+ current = getnext(current)
+ end
+ return head
+end
+
+local function texthandler(head)
+
+ local prev = nil
+ local prevchar = nil
+ local prevhead = head
+ local previtalic = 0
+ local previnserted = nil
+
+ local pre = nil
+ local pretail = nil
+
+ local post = nil
+ local posttail = nil
+ local postchar = nil
+ local posthead = nil
+ local postitalic = 0
+ local postinserted = nil
+
+ local replace = nil
+ local replacetail = nil
+ local replacechar = nil
+ local replacehead = nil
+ local replaceitalic = 0
+ local replaceinserted = nil
+
+ local current = prevhead
+ local lastfont = nil
+ local lastattr = nil
+
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local font = id
+ local data = italicsdata[font]
+ if font ~= lastfont then
+ if previtalic ~= 0 then
+ if okay(data,current,font,prevchar,previtalic,char,"glyph") then
+ insert_node_after(prevhead,prev,correction_kern(previtalic,current))
+ end
+ elseif previnserted and data then
+ if trace_italics then
+ report_italics("deleting last correction before %s %C",char,"glyph")
+ end
+ remove_node(prevhead,previnserted,true)
+ else
+ --
+ if replaceitalic ~= 0 then
+ if okay(data,replace,font,replacechar,replaceitalic,char,"replace") then
+ insert_node_after(replacehead,replace,correction_kern(replaceitalic,current))
+ end
+ replaceitalic = 0
+ elseif replaceinserted and data then
+ if trace_italics then
+ report_italics("deleting last correction before %s %C","replace",char)
+ end
+ remove_node(replacehead,replaceinserted,true)
+ end
+ --
+ if postitalic ~= 0 then
+ if okay(data,post,font,postchar,postitalic,char,"post") then
+ insert_node_after(posthead,post,correction_kern(postitalic,current))
+ end
+ postitalic = 0
+ elseif postinserted and data then
+ if trace_italics then
+ report_italics("deleting last correction before %s %C","post",char)
+ end
+ remove_node(posthead,postinserted,true)
+ end
+ end
+ --
+ lastfont = font
+ end
+ if data then
+ local attr = forcedvariant or getattr(current,a_italics)
+ if attr and attr > 0 then
+ local cd = data[char]
+ if not cd then
+ -- this really can happen
+ previtalic = 0
+ else
+ previtalic = cd.italic
+ if not previtalic then
+ previtalic = setitalicinfont(font,char) -- calculated once
+ -- previtalic = 0
+ end
+ if previtalic ~= 0 then
+ lastfont = font
+ lastattr = attr
+ prev = current
+ -- prevhead = head
+ prevchar = char
+ end
+ end
+ else
+ previtalic = 0
+ end
+ else
+ previtalic = 0
+ end
+ previnserted = nil
+ replaceinserted = nil
+ postinserted = nil
+ elseif id == disc_code then
+ previnserted = nil
+ previtalic = 0
+ replaceinserted = nil
+ replaceitalic = 0
+ postinserted = nil
+ postitalic = 0
+ updated = false
+ replacefont = nil
+ postfont = nil
+ pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
+ if replace then
+ local current = replacetail
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local font = id
+ if font ~= lastfont then
+ local data = italicsdata[font]
+ if data then
+ local attr = forcedvariant or getattr(current,a_italics)
+ if attr and attr > 0 then
+ local cd = data[char]
+ if not cd then
+ -- this really can happen
+ replaceitalic = 0
+ else
+ replaceitalic = cd.italic
+ if not replaceitalic then
+ replaceitalic = setitalicinfont(font,char) -- calculated once
+ -- replaceitalic = 0
+ end
+ if replaceitalic ~= 0 then
+ lastfont = font
+ lastattr = attr
+ replacechar = char
+ replacehead = replace
+ updated = true
+ end
+ end
+ end
+ end
+ replacefont = font
+ end
+ break
+ else
+ current = getprev(current)
+ end
+ end
+ end
+ if post then
+ local current = posttail
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local font = id
+ if font ~= lastfont then
+ local data = italicsdata[font]
+ if data then
+ local attr = forcedvariant or getattr(current,a_italics)
+ if attr and attr > 0 then
+ local cd = data[char]
+ if not cd then
+ -- this really can happen
+ -- postitalic = 0
+ else
+ postitalic = cd.italic
+ if not postitalic then
+ postitalic = setitalicinfont(font,char) -- calculated once
+ -- postitalic = 0
+ end
+ if postitalic ~= 0 then
+ lastfont = font
+ lastattr = attr
+ postchar = char
+ posthead = post
+ updated = true
+ end
+ end
+ end
+ end
+ postfont = font
+ end
+ break
+ else
+ current = getprev(current)
+ end
+ end
+ end
+ if replacefont or postfont then
+ lastfont = replacefont or postfont
+ end
+ if updated then
+ setdisc(current,pre,post,replace)
+ end
+ elseif id == kern_code then -- how about fontkern ?
+ previnserted = nil
+ previtalic = 0
+ replaceinserted = nil
+ replaceitalic = 0
+ postinserted = nil
+ postitalic = 0
+ elseif id == glue_code then
+ if previtalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and glue",previtalic,"glyph",prevchar)
+ end
+ previnserted = correction_glue(previtalic,current) -- maybe just add ? else problem with penalties
+ previtalic = 0
+ insert_node_after(prevhead,prev,previnserted)
+ else
+ if replaceitalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and glue",replaceitalic,"replace",replacechar)
+ end
+ replaceinserted = correction_kern(replaceitalic,current) -- needs to be a kern
+ replaceitalic = 0
+ insert_node_after(replacehead,replace,replaceinserted)
+ end
+ if postitalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and glue",postitalic,"post",postchar)
+ end
+ postinserted = correction_kern(postitalic,current) -- needs to be a kern
+ postitalic = 0
+ insert_node_after(posthead,post,postinserted)
+ end
+ end
+ elseif id == math_code then
+ -- is this still needed ... the current engine implementation has been redone
+ previnserted = nil
+ previtalic = 0
+ replaceinserted = nil
+ replaceitalic = 0
+ postinserted = nil
+ postitalic = 0
+ if mathokay then
+ current = domath(head,current)
+ else
+ current = end_of_math(current)
+ end
+ else
+ if previtalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and whatever",previtalic,"glyph",prevchar)
+ end
+ insert_node_after(prevhead,prev,correction_kern(previtalic,current))
+ previnserted = nil
+ previtalic = 0
+ replaceinserted = nil
+ replaceitalic = 0
+ postinserted = nil
+ postitalic = 0
+ else
+ if replaceitalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and whatever",replaceitalic,"replace",replacechar)
+ end
+ insert_node_after(replacehead,replace,correction_kern(replaceitalic,current))
+ previnserted = nil
+ previtalic = 0
+ replaceinserted = nil
+ replaceitalic = 0
+ postinserted = nil
+ postitalic = 0
+ end
+ if postitalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and whatever",postitalic,"post",postchar)
+ end
+ insert_node_after(posthead,post,correction_kern(postitalic,current))
+ previnserted = nil
+ previtalic = 0
+ replaceinserted = nil
+ replaceitalic = 0
+ postinserted = nil
+ postitalic = 0
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ if lastattr and lastattr > 1 then -- more control is needed here
+ if previtalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and end of list",previtalic,"glyph",prevchar)
+ end
+ insert_node_after(prevhead,prev,correction_kern(previtalic,current))
+ else
+ if replaceitalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and end of list",replaceitalic,"replace",replacechar)
+ end
+ insert_node_after(replacehead,replace,correction_kern(replaceitalic,current))
+ end
+ if postitalic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between %s italic %C and end of list",postitalic,"post",postchar)
+ end
+ insert_node_after(posthead,post,correction_kern(postitalic,current))
+ end
+ end
+ end
+ return head
+end
+
+function italics.handler(head)
+ if textokay then
+ return texthandler(head)
+ elseif mathokay then
+ return mathhandler(head)
+ else
+ return head, false
+ end
+end
+
+enabletext = function()
+ enableaction("processors","typesetters.italics.handler")
+ if trace_italics then
+ report_italics("enabling text/text italics")
+ end
+ enabletext = false
+ textokay = true
+end
+
+enablemath = function()
+ enableaction("processors","typesetters.italics.handler")
+ if trace_italics then
+ report_italics("enabling math/text italics")
+ end
+ enablemath = false
+ mathokay = true
+end
+
+function italics.enabletext()
+ if enabletext then
+ enabletext()
+ end
+end
+
+function italics.enablemath()
+ if enablemath then
+ enablemath()
+ end
+end
+
+function italics.set(n)
+ if enabletext then
+ enabletext()
+ end
+ if n == variables.reset then
+ texsetattribute(a_italics,unsetvalue)
+ else
+ texsetattribute(a_italics,tonumber(n) or unsetvalue)
+ end
+end
+
+function italics.reset()
+ texsetattribute(a_italics,unsetvalue)
+end
+
+implement {
+ name = "setitaliccorrection",
+ actions = italics.set,
+ arguments = "string"
+}
+
+implement {
+ name = "resetitaliccorrection",
+ actions = italics.reset,
+}
+
+local variables = interfaces.variables
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local function setupitaliccorrection(option) -- no grouping !
+ if enabletext then
+ enabletext()
+ end
+ local options = settings_to_hash(option)
+ local variant = unsetvalue
+ if options[variables.text] then
+ variant = 1
+ elseif options[variables.always] then
+ variant = 2
+ end
+ -- maybe also keywords for threshold
+ if options[variables.global] then
+ forcedvariant = variant
+ texsetattribute(a_italics,unsetvalue)
+ else
+ forcedvariant = false
+ texsetattribute(a_italics,variant)
+ end
+ if trace_italics then
+ report_italics("forcing %a, variant %a",forcedvariant or "-",variant ~= unsetvalue and variant)
+ end
+end
+
+implement {
+ name = "setupitaliccorrection",
+ actions = setupitaliccorrection,
+ arguments = "string"
+}
+
+-- for manuals:
+
+local stack = { }
+
+implement {
+ name = "pushitaliccorrection",
+ actions = function()
+ table.insert(stack,{forcedvariant, texgetattribute(a_italics) })
+ end
+}
+
+implement {
+ name = "popitaliccorrection",
+ actions = function()
+ local top = table.remove(stack)
+ forcedvariant = top[1]
+ texsetattribute(a_italics,top[2])
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-itc.mklx b/Master/texmf-dist/tex/context/base/mkxl/typo-itc.mklx
new file mode 100644
index 00000000000..67753efd50c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-itc.mklx
@@ -0,0 +1,56 @@
+%D \module
+%D [ file=typo-itc,
+%D version=2011.10.08,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Italic Correction,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Italics Corrections}
+
+\unprotect
+
+%D The brave might try:
+%D
+%D \starttyping
+%D \definefontfeature[default][default][itlc=yes,textitalics=yes]
+%D \setupitaliccorrection[global,always]
+%D \stoptyping
+
+\registerctxluafile{typo-itc}{autosuffix}
+
+\definesystemattribute[italics] [public]
+\definesystemattribute[mathitalics][public]
+
+% \let\/=/
+% \let\emphasiscorrection\donothing
+
+\permanent\protected\def\italiccorrection
+ {\ifnum\c_attr_italics<\plusone\normalitaliccorrection\fi}
+
+\pushoverloadmode
+ \aliased\let\/\italiccorrection % replaces the primitive
+\popoverloadmode
+
+% 1 = end of word
+% 2 = end of word and end of a list
+
+\permanent\protected\def\setitaliccorrection [#code]{\clf_setitaliccorrection#code\relax} % todo: public implementor
+\permanent\protected\def\resetitaliccorrection {\clf_resetitaliccorrection} % todo: public implementor
+\permanent\protected\def\setupitaliccorrection[#settings]{\clf_setupitaliccorrection{#settings}} % todo: public implementor
+
+% global : no attributes, just always (faster and less memory)
+% text : only text
+% always : text and boxes
+% none : -
+
+\appendtoks
+ \c_attr_italics\attributeunsetvalue
+\to \everymathematics
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-itm.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-itm.mkxl
new file mode 100644
index 00000000000..7ae1b732f42
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-itm.mkxl
@@ -0,0 +1,273 @@
+%D \module
+%D [ file=typo-itm, % comes from core-mis
+%D version=2012.06.28,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Item Lists,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Macros / Item Lists}
+
+\unprotect
+
+%D This is a real old mechanism that we once used for questionaries. As it is documented
+%D we keep it around. A more modern implementation would probably use another approach
+%D but I don't want to spend too much time on it now. There are a couple of changes:
+%D
+%D \startitemize
+%D \startitem textwidth sets the combined width \stopitem
+%D \startitem width sets the symbolwidth \stopitem
+%D \startitem alternative is used instead of location \stopitem
+%D \stopitemize
+%D
+%D \startbuffer
+%D \items[alternative=left]{a,b,c}
+%D \items[alternative=left,align=middle,textalign=flushright,distance=1em]{a,b,c}
+%D \items[alternative=right]{a,b,c}
+%D \items[alternative=inmargin]{a,b,c}
+%D \items[alternative=top]{a,b,c}
+%D \items[alternative=bottom]{a,b,c}
+%D \items[alternative=bottom,align=flushleft,textstyle=bold,color=red,textcolor=green]{a,b,c}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D This renders as:
+%D
+%D \getbuffer
+
+\installcorenamespace{items}
+\installcorenamespace{itemsalternatives}
+\installcorenamespace{itemshorizontal}
+\installcorenamespace{itemsvertical}
+
+\installcommandhandler \??items {items} \??items
+
+\setupitems
+ [\c!alternative=\v!left,
+ \c!symbol=5,
+ \c!textwidth=\availablehsize,
+ \c!align=\v!middle,
+ \c!textalign=\v!flushleft,
+ \c!distance=\zeropoint,
+ %\c!n=,
+ \c!before=\blank,
+ \c!inbetween={\blank[\v!medium]},
+ \c!after=\blank]
+
+\newcount\c_typo_items_n
+\newcount\c_typo_items_m
+\newdimen\d_typo_items_text_width
+\newdimen\d_typo_items_symbol_width
+\newdimen\d_typo_items_distance
+\newbox \b_typo_items_symbols
+\newbox \b_typo_items_texts
+
+\permanent\tolerant\protected\def\items[#1]#:#2%
+ {\bgroup
+ \setupitems[#1]%
+ \edef\p_typo_items_alternative{\itemsparameter\c!alternative}%
+ \ifcsname\??itemsalternatives\p_typo_items_alternative\endcsname \else
+ \let\p_typo_items_alternative\v!left
+ \fi
+ \let\currentitems\p_typo_items_alternative
+ \setupcurrentitems[#1]%
+ %
+ \edef\p_typo_items_textwidth{\itemsparameter\c!textwidth}%
+ \ifempty\p_typo_items_textwidth
+ \d_typo_items_text_width\availablehsize
+ \else
+ \d_typo_items_text_width\p_typo_items_textwidth\relax
+ \fi
+ %
+ \edef\p_typo_items_width{\itemsparameter\c!width}%
+ \ifempty\p_typo_items_width
+ \d_typo_items_symbol_width1.5\emwidth
+ \else
+ \d_typo_items_symbol_width\p_typo_items_width\relax
+ \fi
+ %
+ \edef\p_typo_items_distance{\itemsparameter\c!distance}%
+ \ifempty\p_typo_items_distance
+ \d_typo_items_distance\zeropoint
+ \else
+ \d_typo_items_distance\p_typo_items_distance\relax
+ \fi
+ %
+ \edef\p_typo_items_symbol{\itemsparameter\c!symbol}%
+ \ifempty\p_typo_items_symbol
+ \let\m_typo_items_symbol\firstofoneargument
+ \orelse\ifx\p_typo_items_symbol\v!none
+ \let\p_typo_items_symbol\empty
+ \let\m_typo_items_symbol\firstofoneargument
+ \else
+ \doifelseconversiondefined\p_typo_items_symbol
+ {\def\m_typo_items_symbol{\convertnumber\p_typo_items_symbol}}
+ {\doifelsesymboldefined\p_typo_items_symbol
+ {\def\m_typo_items_symbol{\symbol[\p_typo_items_symbol]\gobbleoneargument}}
+ {\let\m_typo_items_symbol\firstofoneargument}}%
+ \fi
+ %
+ \edef\p_typo_items_align{\itemsparameter\c!align}%
+ \edef\p_typo_items_textalign{\itemsparameter\c!textalign}%
+ %
+ \edef\p_typo_items_n{\itemsparameter\c!n}%
+ \ifempty\p_typo_items_n
+ \getcommalistsize[#2]%
+ \c_typo_items_n\commalistsize\relax % \commalistcounter
+ \else
+ \c_typo_items_n\p_typo_items_n\relax
+ \fi
+ %
+ \parindent\zeropoint
+ \dontcomplain
+ %
+ \itemsparameter\c!before
+ \csname\??itemsalternatives\p_typo_items_alternative\endcsname{#2}%
+ \itemsparameter\c!after
+ \egroup}
+
+% rendering
+
+\let\typo_items_item\relax
+\let\typo_items_make\relax
+
+\def\typo_items_item_horizontal
+ {\advance\c_typo_items_m\plusone
+ \csname\??itemshorizontal
+ \ifcsname\??itemshorizontal\p_typo_items_align\endcsname
+ \p_typo_items_align
+ \else
+ \s!unknown
+ \fi
+ \endcsname}
+
+\def\typo_items_item_vertical
+ {\advance\c_typo_items_m\plusone
+ \csname\??itemsvertical
+ \ifcsname\??itemsvertical\p_typo_items_align\endcsname
+ \p_typo_items_align
+ \else
+ \s!unknown
+ \fi
+ \endcsname}
+
+\defcsname\??itemshorizontal\v!margin\endcsname#1%
+ {\ifnum\c_typo_items_m=\plusone\hss\else\hfill\fi
+ \strut#1%
+ \ifnum\c_typo_items_m=\c_typo_items_n\hss\else\hfill\fi}
+
+\defcsname\??itemshorizontal\s!unknown\endcsname
+ {\simplealignedbox\scratchwidth\m_typo_items_align}
+
+\defcsname\??itemsvertical\s!unknown\endcsname
+ {\simplealignedbox\scratchwidth\m_typo_items_align}
+
+\def\typo_items_make_horizontal#1%
+ {\divide\scratchwidth\c_typo_items_n
+ \hbox{#1}}
+
+\def\typo_items_make_vertical#1%
+ {\vbox{#1}}
+
+\def\typo_items_construct_items_boxes#1%
+ {\setbox\b_typo_items_texts\hbox
+ {\c_typo_items_m\zerocount
+ \let\m_typo_items_align\p_typo_items_textalign
+ \scratchwidth\d_typo_items_text_width
+ \useitemsstyleandcolor\c!textstyle\c!textcolor
+ \typo_items_make{\processcommalist[#1]\typo_items_item}}%
+ \ifempty\p_typo_items_symbol
+ \setbox\b_typo_items_symbols\emptyhbox
+ \else
+ \setbox\b_typo_items_symbols\hbox
+ {\c_typo_items_m\zerocount
+ \let\m_typo_items_align\p_typo_items_align
+ \scratchwidth\d_typo_items_symbol_width
+ \useitemsstyleandcolor\c!style\c!color
+ \typo_items_make{\dorecurse\c_typo_items_n{\typo_items_item{\strut\m_typo_items_symbol\recurselevel}}}}%
+ \fi}
+
+% alternatives:
+
+\defineitems[\v!top][\c!width=\d_typo_items_text_width,\c!textalign=\itemsparameter\c!align]
+
+\setvalue{\??itemsalternatives\v!top}#1%
+ {\let\typo_items_make\typo_items_make_horizontal
+ \let\typo_items_item\typo_items_item_horizontal
+ \typo_items_construct_items_boxes{#1}%
+ \noindent\vbox\bgroup
+ \forgetall
+ \ifvoid\b_typo_items_symbols \else
+ \box\b_typo_items_symbols
+ \itemsparameter\c!inbetween
+ \nointerlineskip
+ \fi
+ \box\b_typo_items_texts\
+ \egroup}
+
+\defineitems[\v!bottom][\c!width=\d_typo_items_text_width,\c!textalign=\itemsparameter\c!align]
+
+\setvalue{\??itemsalternatives\v!bottom}#1%
+ {\let\typo_items_make\typo_items_make_horizontal
+ \let\typo_items_item\typo_items_item_horizontal
+ \typo_items_construct_items_boxes{#1}%
+ \noindent\vbox\bgroup
+ \forgetall
+ \box\b_typo_items_texts
+ \ifvoid\b_typo_items_symbols \else
+ \itemsparameter\c!inbetween
+ \nointerlineskip
+ \box\b_typo_items_symbols
+ \fi
+ \egroup}
+
+\defineitems[\v!inmargin][\c!width=1.5\emwidth,\c!align=\v!flushright,\c!distance=\leftmargindistance]
+
+\setvalue{\??itemsalternatives\v!inmargin}#1%
+ {\let\typo_items_make\typo_items_make_vertical
+ \let\typo_items_item\typo_items_item_vertical
+ \typo_items_construct_items_boxes{#1}%
+ \noindent\hbox\bgroup
+ \ifvoid\b_typo_items_symbols \else
+ \llap{\box\b_typo_items_symbols\hskip\d_typo_items_distance}%
+ \fi
+ \box\b_typo_items_texts
+ \egroup}
+
+\defineitems[\v!left][\c!width=1.5\emwidth,\c!align=\v!flushleft]
+
+\setvalue{\??itemsalternatives\v!left}#1%
+ {\let\typo_items_make\typo_items_make_vertical
+ \let\typo_items_item\typo_items_item_vertical
+ \advance\d_typo_items_text_width-\dimexpr\d_typo_items_symbol_width+\d_typo_items_distance\relax
+ \typo_items_construct_items_boxes{#1}%
+ \noindent\hbox\bgroup
+ \ifvoid\b_typo_items_symbols \else
+ \box\b_typo_items_symbols
+ \kern\d_typo_items_distance
+ \fi
+ \box\b_typo_items_texts
+ \egroup}
+
+\defineitems[\v!right][\c!width=1.5\emwidth,\c!align=\v!flushright]
+
+\setvalue{\??itemsalternatives\v!right}#1%
+ {\let\typo_items_make\typo_items_make_vertical
+ \let\typo_items_item\typo_items_item_vertical
+ \advance\d_typo_items_text_width-\dimexpr\d_typo_items_symbol_width+\d_typo_items_distance\relax
+ \typo_items_construct_items_boxes{#1}%
+ \noindent\hbox\bgroup
+ \box\b_typo_items_texts
+ \ifvoid\b_typo_items_symbols \else
+ \kern\d_typo_items_distance
+ \box\b_typo_items_symbols
+ \fi
+ \egroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-krn.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-krn.lmt
new file mode 100644
index 00000000000..a8ccedccf44
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-krn.lmt
@@ -0,0 +1,630 @@
+if not modules then modules = { } end modules ['typo-krn'] = {
+ version = 1.001,
+ comment = "companion to typo-krn.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- glue is still somewhat suboptimal
+-- components: better split on tounicode
+--
+-- maybe ignore when properties[n].injections.cursivex (or mark)
+
+local next, type, tonumber = next, type, tonumber
+
+local nodes = nodes
+local fonts = fonts
+
+local enableaction = nodes.tasks.enableaction
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+-- check what is used
+
+local find_node_tail = nuts.tail
+local flush_node = nuts.flush_node
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local end_of_math = nuts.end_of_math
+local copy_node = nuts.copy
+local find_attribute = nuts.find_attribute
+local unset_attributes = nuts.unset_attributes
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getfont = nuts.getfont
+local getsubtype = nuts.getsubtype
+local getchar = nuts.getchar
+local getdisc = nuts.getdisc
+local getglue = nuts.getglue
+local getkern = nuts.getkern
+local getglyphdata = nuts.getglyphdata
+
+local isglyph = nuts.isglyph
+
+local setfield = nuts.setfield
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local setlink = nuts.setlink
+local setdisc = nuts.setdisc
+local setglue = nuts.setglue
+local setkern = nuts.setkern
+local setchar = nuts.setchar
+local setglue = nuts.setglue -- todo
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+local gluecodes = nodes.gluecodes
+local disccodes = nodes.disccodes
+local listcodes = nodes.listcodes
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local math_code = nodecodes.math
+
+local boxlist_code = listcodes.box
+local unknownlist_code = listcodes.unknown
+
+local discretionarydisc_code = disccodes.discretionary
+local automaticdisc_code = disccodes.automatic
+
+local fontkern_code = kerncodes.fontkern
+local userkern_code = kerncodes.userkern
+
+local userskip_code = gluecodes.userskip
+local spaceskip_code = gluecodes.spaceskip
+local xspaceskip_code = gluecodes.xspaceskip
+
+local fonthashes = fonts.hashes
+local chardata = fonthashes.characters
+local quaddata = fonthashes.quads
+local markdata = fonthashes.marks
+local fontproperties = fonthashes.properties
+local fontdescriptions = fonthashes.descriptions
+local fontfeatures = fonthashes.features
+
+local tracers = nodes.tracers
+local setcolor = tracers.colors.set
+local resetcolor = tracers.colors.reset
+
+local v_max = interfaces.variables.max
+local v_auto = interfaces.variables.auto
+
+typesetters = typesetters or { }
+local typesetters = typesetters
+
+local kerns = typesetters.kerns or { }
+typesetters.kerns = kerns
+
+local report = logs.reporter("kerns")
+local trace_ligatures = false trackers.register("typesetters.kerns.ligatures", function(v) trace_ligatures = v end)
+local trace_ligatures_d = false trackers.register("typesetters.kerns.ligatures.details", function(v) trace_ligatures_d = v end)
+
+kerns.mapping = kerns.mapping or { }
+kerns.factors = kerns.factors or { }
+local a_kerns = attributes.private("kern")
+
+local contextsetups = fonts.specifiers.contextsetups
+
+storage.register("typesetters/kerns/mapping", kerns.mapping, "typesetters.kerns.mapping")
+storage.register("typesetters/kerns/factors", kerns.factors, "typesetters.kerns.factors")
+
+local mapping = kerns.mapping
+local factors = kerns.factors
+
+-- one must use liga=no and mode=base and kern=yes
+-- use more helpers
+-- make sure it runs after all others
+-- there will be a width adaptor field in nodes so this will change
+-- todo: interchar kerns / disc nodes / can be made faster
+-- todo: use insert_before etc
+
+local gluefactor = 4 -- assumes quad = .5 enspace
+
+-- red : kept by dynamic feature
+-- green : kept by static feature
+-- blue : keep by goodie
+
+function kerns.keepligature(n) -- might become default
+ local f = getfont(n)
+ local a = getglyphdata(n) or 0
+ if trace_ligatures then
+ local c = getchar(n)
+ local d = fontdescriptions[f][c].name
+ if a > 0 and contextsetups[a].keepligatures == v_auto then
+ if trace_ligatures_d then
+ report("font %!font:name!, glyph %a, slot %X -> ligature %s, by %s feature %a",f,d,c,"kept","dynamic","keepligatures")
+ end
+ setcolor(n,"darkred")
+ return true
+ end
+ local k = fontfeatures[f].keepligatures
+ if k == v_auto then
+ if trace_ligatures_d then
+ report("font %!font:name!, glyph %a, slot %X -> ligature %s, by %s feature %a",f,d,c,"kept","static","keepligatures")
+ end
+ setcolor(n,"darkgreen")
+ return true
+ end
+ if not k then
+ if trace_ligatures_d then
+ report("font %!font:name!, glyph %a, slot %X -> ligature %s, by %s feature %a",f,d,c,"split","static","keepligatures")
+ end
+ resetcolor(n)
+ return false
+ end
+ local k = fontproperties[f].keptligatures
+ if not k then
+ report("font %!font:name!, glyph %a, slot %X -> ligature %s, %s goodie specification",f,d,c,"split","no")
+ resetcolor(n)
+ return false
+ end
+ if k and k[c] then
+ report("font %!font:name!, glyph %a, slot %X -> ligature %s, %s goodie specification",f,d,c,"kept","by")
+ setcolor(n,"darkblue")
+ return true
+ else
+ report("font %!font:name!, glyph %a, slot %X -> ligature %s, %s goodie specification",f,d,c,"split","by")
+ resetcolor(n)
+ return false
+ end
+ else
+ if a > 0 and contextsetups[a].keepligatures == v_auto then
+ return true
+ end
+ local k = fontfeatures[f].keepligatures
+ if k == v_auto then
+ return true
+ end
+ if not k then
+ return false
+ end
+ local k = fontproperties[f].keptligatures
+ if not k then
+ return false
+ end
+ if k and k[c] then
+ return true
+ end
+ end
+end
+
+-- can be optimized .. the prev thing .. but hardly worth the effort
+
+local function kern_injector(fillup,kern)
+ if fillup then
+ local g = new_glue(kern)
+ setfield(g,"stretch",kern)
+ setfield(g,"stretch_order",1)
+ return g
+ else
+ return new_kern(kern)
+ end
+end
+
+-- a simple list injector, no components and such .. just disable ligatures in
+-- kern mode .. maybe not even hyphenate ... anyway, the next one is for simple
+-- sublists .. beware: we can have char -1
+
+local function inject_begin(boundary,prev,keeptogether,krn,ok) -- prev is a glyph
+ local char, id = isglyph(boundary)
+ if id == kern_code then
+ if getsubtype(boundary) == fontkern_code then
+ local inject = true
+ if keeptogether then
+ local next = getnext(boundary)
+ if not next or (getid(next) == glyph_code and keeptogether(prev,next)) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setkern(boundary,getkern(boundary) + quaddata[getfont(prev)]*krn,userkern_code)
+ return boundary, true
+ end
+ end
+ elseif char then
+ if keeptogether and keeptogether(boundary,prev) then
+ -- keep 'm
+ else
+ local prevchar = isglyph(prev)
+ if prevchar and prevchar > 0 then
+ local font = getfont(boundary)
+ local data = chardata[font][prevchar]
+ local kerns = data and data.kerns
+ local kern = new_kern((kerns and kerns[char] or 0) + quaddata[font]*krn)
+ setlink(kern,boundary)
+ return kern, true
+ end
+ end
+ end
+ return boundary, ok
+end
+
+local function inject_end(boundary,next,keeptogether,krn,ok)
+ local tail = find_node_tail(boundary)
+ local char, id = isglyph(tail)
+ if id == kern_code then
+ if getsubtype(tail) == fontkern_code then
+ local inject = true
+ if keeptogether then
+ local prev = getprev(tail)
+ if getid(prev) == glyph_code and keeptogether(prev,two) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setkern(tail,getkern(tail) + quaddata[getfont(next)]*krn,userkern_code)
+ return boundary, true
+ end
+ end
+ elseif char then
+ if keeptogether and keeptogether(tail,two) then
+ -- keep 'm
+ else
+ local nextchar = isglyph(tail)
+ if nextchar and nextchar > 0 then
+ local font = getfont(tail)
+ local data = chardata[font][nextchar]
+ local kerns = data and data.kerns
+ local kern = (kerns and kerns[char] or 0) + quaddata[font]*krn
+ setlink(tail,new_kern(kern))
+ return boundary, true
+ end
+ end
+ end
+ return boundary, ok
+end
+
+local function process_list(head,keeptogether,krn,font,okay)
+ local start = head
+ local prev = nil
+ local pid = nil
+ local kern = 0
+ local mark = font and markdata[font]
+ while start do
+ local char, id = isglyph(start)
+ if char then
+ if not font then
+ font = id -- getfont(start)
+ mark = markdata[font]
+ kern = quaddata[font]*krn
+ end
+ if prev then
+ if mark[char] then
+ -- skip
+ elseif pid == kern_code then
+ if getsubtype(prev) == fontkern_code then
+ local inject = true
+ if keeptogether then
+ local prevprev = getprev(prev)
+ if getid(prevprev) == glyph_code and keeptogether(prevprev,start) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setkern(prev,getkern(prev) + kern,userkern_code)
+ okay = true
+ end
+ end
+ elseif pid == glyph_code then
+ if keeptogether and keeptogether(prev,start) then
+ -- keep 'm
+ else
+ local prevchar = getchar(prev)
+ local data = chardata[font][prevchar]
+ local kerns = data and data.kerns
+ -- if kerns then
+ -- print("it happens indeed, basemode kerns not yet injected")
+ -- end
+ insert_node_before(head,start,new_kern((kerns and kerns[char] or 0) + kern))
+ okay = true
+ end
+ end
+ end
+ end
+ if start then
+ prev = start
+ pid = id
+ start = getnext(start)
+ end
+ end
+ return head, okay, prev
+end
+
+local function closest_bound(b,get)
+ b = get(b)
+ if b and getid(b) ~= glue_code then
+ while b do
+ if not getattr(b,a_kerns) then
+ break
+ else
+ local c, f = isglyph(b)
+ if c then
+ return b, f
+ else
+ b = get(b)
+ end
+ end
+ end
+ end
+end
+
+function kerns.handler(head)
+ local _, start = find_attribute(head, a_kerns)
+ if start then
+ local lastfont = nil
+ local keepligature = kerns.keepligature
+ local keeptogether = kerns.keeptogether
+ local fillup = false
+ local bound = false
+ local prev = nil
+ local previd = nil
+ local prevchar = nil
+ local prevfont = nil
+ local prevmark = nil
+ local done = false
+ while start do
+ -- fontkerns don't get the attribute but they always sit between glyphs so
+ -- are always valid bound .. disc nodes also sometimes don't get them
+ local attr = getattr(start,a_kerns)
+ if attr and attr > 0 then
+ local char, id = isglyph(start)
+ local krn = mapping[attr]
+ if krn == v_max then
+ krn = .25
+ fillup = true
+ else
+ fillup = false
+ end
+ if not krn or krn == 0 then
+ bound = false
+ elseif char then -- id == glyph_code
+ local font = id -- more readable
+ local mark = markdata[font]
+ if keepligature and keepligature(start) then
+ -- keep 'm
+ else
+ -- beware, these are not kerned so we mighty need a kern only pass
+ -- maybe some day .. anyway, one should disable ligaturing
+ local data = chardata[font][char]
+ if data then
+ local unicode = data.unicode -- can be cached
+ if type(unicode) == "table" then
+ char = unicode[1]
+ local s = start
+ setchar(s,char)
+ for i=2,#unicode do
+ local n = copy_node(s)
+ if i == 2 then
+ setattr(n,a_kerns,attr) -- we took away the attr
+ end
+ setchar(n,unicode[i])
+ insert_node_after(head,s,n)
+ s = n
+ end
+ end
+ end
+ end
+ if not bound then
+ -- yet
+ elseif mark[char] then
+ -- skip
+ elseif previd == kern_code then
+ if getsubtype(prev) == fontkern_code then
+ local inject = true
+ if keeptogether then
+ if previd == glyph_code and keeptogether(prev,start) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setkern(prev,getkern(prev) + quaddata[font]*krn,userkern_code)
+ end
+ end
+ elseif previd == glyph_code then
+ if prevfont == font then
+ if keeptogether and keeptogether(prev,start) then
+ -- keep 'm
+ else
+ -- hm, only basemode ... will go away ...
+ local data = chardata[font][prevchar]
+ local kerns = data and data.kerns
+ local kern = (kerns and kerns[char] or 0) + quaddata[font]*krn
+ insert_node_before(head,start,kern_injector(fillup,kern))
+ end
+ else
+ insert_node_before(head,start,kern_injector(fillup,quaddata[font]*krn))
+ end
+ end
+ prev = start
+ prevchar = char
+ prevfont = font
+ prevmark = mark
+ previd = glyph_code -- id
+ bound = true
+ elseif id == disc_code then
+ local prev, next, pglyph, nglyph -- delayed till needed
+ local subtype = getsubtype(start)
+ -- if subtype == automaticdisc_code then
+ -- -- this is kind of special, as we have already injected the
+ -- -- previous kern
+ -- local prev = getprev(start)
+ -- local pglyph = prev and getid(prev) == glyph_code
+ -- languages.expand(start,pglyph and prev)
+ -- -- we can have a different start now
+ -- elseif subtype ~= discretionarydisc_code then
+ -- prev = getprev(start)
+ -- pglyph = prev and getid(prev) == glyph_code
+ -- languages.expand(start,pglyph and prev)
+ -- end
+ local pre, post, replace = getdisc(start)
+ local indeed = false
+ if pre then
+ local okay = false
+ if not prev then
+ prev = getprev(start)
+ pglyph = prev and getid(prev) == glyph_code
+ end
+ if pglyph then
+ pre, okay = inject_begin(pre,prev,keeptogether,krn,okay)
+ end
+ pre, okay = process_list(pre,keeptogether,krn,false,okay)
+ if okay then
+ indeed = true
+ end
+ end
+ if post then
+ local okay = false
+ if not next then
+ next = getnext(start)
+ nglyph = next and getid(next) == glyph_code
+ end
+ if nglyph then
+ post, okay = inject_end(post,next,keeptogether,krn,okay)
+ end
+ post, okay = process_list(post,keeptogether,krn,false,okay)
+ if okay then
+ indeed = true
+ end
+ end
+ if replace then
+ local okay = false
+ if not prev then
+ prev = getprev(start)
+ pglyph = prev and getid(prev) == glyph_code
+ end
+ if pglyph then
+ replace, okay = inject_begin(replace,prev,keeptogether,krn,okay)
+ end
+ if not next then
+ next = getnext(start)
+ nglyph = next and getid(next) == glyph_code
+ end
+ if nglyph then
+ replace, okay = inject_end(replace,next,keeptogether,krn,okay)
+ end
+ replace, okay = process_list(replace,keeptogether,krn,false,okay)
+ if okay then
+ indeed = true
+ end
+ elseif prevfont then
+ replace = new_kern(quaddata[prevfont]*krn)
+ indeed = true
+ end
+ if indeed then
+ setdisc(start,pre,post,replace)
+ end
+ bound = false
+ elseif id == kern_code then
+ bound = getsubtype(start) == fontkern_code
+ prev = start
+ previd = id
+ elseif id == glue_code then
+ local subtype = getsubtype(start)
+ if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then
+ local width, stretch, shrink, stretch_order, shrink_order = getglue(start)
+ if width > 0 then
+ local w = width + gluefactor * width * krn
+ stretch = stretch * w / width
+ shrink = shrink * w / width
+ if fillup then
+ stretch = 2 * stretch
+ shrink = 2 * shrink
+ stretch_order = 1
+ -- shrink_order = 1 ?
+ end
+ setglue(start,w,stretch,shrink,stretch_order,shrink_order)
+ end
+ end
+ bound = false
+ elseif id == hlist_code or id == vlist_code then
+ local subtype = getsubtype(start)
+ if subtype == unknownlist_code or subtype == boxlist_code then
+ -- special case
+ local b, f = closest_bound(start,getprev)
+ if b then
+ insert_node_before(head,start,kern_injector(fillup,quaddata[f]*krn))
+ end
+ local b, f = closest_bound(start,getnext)
+ if b then
+ insert_node_after(head,start,kern_injector(fillup,quaddata[f]*krn))
+ end
+ end
+ bound = false
+ elseif id == math_code then
+ start = end_of_math(start)
+ bound = false
+ end
+ if start then
+ start = getnext(start)
+ end
+ done = true
+ else
+ local id = getid(start)
+ if id == kern_code then
+ bound = getsubtype(start) == fontkern_code
+ prev = start
+ previd = id
+ start = getnext(start)
+ else
+ bound = false
+ start = getnext(start)
+ end
+ end
+ end
+ if done then
+ -- unset_attributes(a_kerns, head)
+ end
+ end
+ return head
+end
+
+local enabled = false
+
+function kerns.set(factor)
+ if factor ~= v_max then
+ factor = tonumber(factor) or 0
+ end
+ if factor == v_max or factor ~= 0 then
+ if not enabled then
+ enableaction("processors","typesetters.kerns.handler")
+ enabled = true
+ end
+ local a = factors[factor]
+ if not a then
+ a = #mapping + 1
+ factors[factors], mapping[a] = a, factor
+ end
+ factor = a
+ else
+ factor = unsetvalue
+ end
+ texsetattribute(a_kerns,factor)
+ return factor
+end
+
+-- interface
+
+interfaces.implement {
+ name = "setcharacterkerning",
+ actions = kerns.set,
+ arguments = "string"
+}
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-krn.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-krn.mkxl
new file mode 100644
index 00000000000..4c6aea256f8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-krn.mkxl
@@ -0,0 +1,143 @@
+%D \module
+%D [ file=typo-krn,
+%D version=2009.03.27, % code moved from core-spa.mkiv
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Spacing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Kerning}
+
+\unprotect
+
+\registerctxluafile{typo-krn}{autosuffix}
+
+\definesystemattribute[kern][public]
+
+% more
+%
+% {\setcharacterkerning[extrakerning]\input davis\relax}
+
+\installcorenamespace{characterkerning}
+
+\installcommandhandler \??characterkerning {characterkerning} \??characterkerning
+
+\permanent\protected\def\setcharacterkerning[#1]%
+ {\edef\currentcharacterkerning{#1}%
+ \ifx\currentcharacterkerning\s!reset
+ \resetcharacterkerning
+ \else
+ \typo_kerning_set
+ \fi}
+
+\def\typo_kerning_set
+ {\usecharacterkerningstyleandcolor\c!style\c!color % goodie, maybe also strut
+ \useaddfontfeatureparameter\characterkerningparameter
+ \clf_setcharacterkerning{\characterkerningparameter\c!factor}}
+
+\permanent\protected\def\resetcharacterkerning % fast one
+ {\c_attr_kern\attributeunsetvalue}
+
+\appendtoks
+ \resetcharacterkerning
+\to \everyresettypesetting
+
+\definecharacterkerning [extrakerning] [\c!factor=.125] % used in manuals
+
+%D Added after discussion on list (posted by WS, adapted abit by HH) \unknown\ this
+%D needs to be interfaced (\type {\v!kerncharacters}).
+%D
+%D \starttyping
+%D \setuphead[section][style=\sca,textstyle={\kerncharacters[.5]}] \section{Section}
+%D \stoptyping
+%D
+%D We could combine this with the previous definition command but then we always
+%D would get a command defined which is not beforehand a good idea.
+%D
+%D Here we need to keep the groupedcommand solution as it is used as modifier.
+
+\appendtoks
+ \frozen\instance\setuevalue{\currentcharacterkerning}%
+ {\doifelsenextoptional
+ {\typo_kerning_apply_yes{\currentcharacterkerning}}%
+ {\typo_kerning_apply_nop{\currentcharacterkerning}}}%
+\to \everydefinecharacterkerning
+
+\protected\def\typo_kerning_apply_yes#1[#2]%
+ {\groupedcommand{\typo_kerning_apply_yes_indeed{#1}{#2}}\donothing}
+
+\protected\def\typo_kerning_apply_nop#1%
+ {\groupedcommand{\typo_kerning_apply_nop_indeed{#1}}\donothing}
+
+\def\typo_kerning_apply_yes_indeed#1#2%
+ {\edef\currentcharacterkerning{#1}%
+ \setupcurrentcharacterkerning[\c!factor=#2]%
+ \typo_kerning_set}
+
+\def\typo_kerning_apply_nop_indeed#1%
+ {\edef\currentcharacterkerning{#1}%
+ \typo_kerning_set}
+
+\definecharacterkerning [\v!kerncharacters] [\c!factor=.125]
+\definecharacterkerning [\v!letterspacing ] [\v!kerncharacters] [\c!features=letterspacing]
+
+%D \macros
+%D {stretched}
+%D
+%D Stretching characters in a word is a sort of typographical murder. Nevertheless
+%D we support this manipulation for use in for instance titles.
+%D
+%D \starttyping
+%D \hbox to 5cm{\stretched{to the limit}}
+%D \stretched{to the limit}
+%D \stretched[width=10cm]{to the limit}
+%D \stoptyping
+%D
+%D \typebuffer
+%D
+%D or
+%D
+%D \startexample
+%D \getbuffer
+%D \stopexample
+%D
+%D \showsetup{stretched}
+%D
+%D This command replaces the old \MKII\ variant.
+
+\definecharacterkerning
+ [\v!stretched]
+ [\c!factor=\v!max,
+ \c!width=\availablehsize]
+
+\permanent\protected\def\setupstretched
+ {\setupcharacterkerning[\v!stretched]}
+
+\pushoverloadmode
+
+\enforced\permanent\tolerant\protected\def\stretched[#1]#:#2%
+ {\begingroup
+ \let\currentcharacterkerning\v!stretched
+ \ifparameter#1\or
+ \setupcurrentcharacterkerning[#1]%
+ \fi
+ \edef\p_width{\characterkerningparameter\c!width}%
+ \ifempty\p_width \else
+ \hbox to \p_width
+ \fi
+ \bgroup
+ \usecharacterkerningstyleandcolor\c!style\c!color
+ \useaddfontfeatureparameter\characterkerningparameter
+ \typo_kerning_set
+ #2%
+ \egroup
+ \endgroup}
+
+\popoverloadmode
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-lan.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-lan.mkxl
new file mode 100644
index 00000000000..9d137f5a561
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-lan.mkxl
@@ -0,0 +1,64 @@
+%D \module
+%D [ file=typo-lan,
+%D version=2013.03.22,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Language Goodies,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typography Macros / Languages}
+
+\unprotect
+
+\registerctxluafile{typo-lan}{}
+
+%D \macros
+%D {averagecharwidth, charwidthlanguage}
+%D
+%D This is a more \MKIV-ish variant of lang-frq.mkiv. The methods are gone as one
+%D doesn't need the tables for them. The main macro is \type {\averagecharwidth}
+%D which behaves like a dimension register.
+%D
+%D I finally decided to reimplement this as I needed it for a manual (which is often
+%D a reason for such a rewrite). With some inspiring Porcupine Tree in the
+%D background it's not the worst thing to do.
+
+\mutable\def\charwidthlanguage{\currentmainlanguage}
+
+\permanent\def\averagecharwidth {\dimexpr\clf_averagecharwidth{\charwidthlanguage}\scaledpoint\relax}
+\permanent\def\languagecharwidth#1{\dimexpr\clf_averagecharwidth{#1}\scaledpoint\relax}
+
+\protect
+
+\continueifinputfile{typo-lan.mkiv}
+
+\setuplayout[backspace=4cm]
+
+\showframe
+
+\starttext
+
+\startbuffer
+
+\mainlanguage[en] \hsize65\averagecharwidth \normalexpanded{\inleft{\the\hsize}} \input ward \par
+\mainlanguage[de] \hsize65\averagecharwidth \normalexpanded{\inleft{\the\hsize}} \input ward \par
+\mainlanguage[nl] \hsize65\averagecharwidth \normalexpanded{\inleft{\the\hsize}} \input ward \par
+
+\stopbuffer
+
+\getbuffer \blank
+
+\switchtobodyfont[pagella]
+
+\getbuffer \blank
+
+\switchtobodyfont[tt,8pt]
+
+\getbuffer
+
+\stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-lig.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-lig.mkxl
new file mode 100644
index 00000000000..e30774d8efc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-lig.mkxl
@@ -0,0 +1,23 @@
+%D \module
+%D [ file=typo-lig,
+%D version=2014.12.01,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Ligatures,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Ligatures}
+
+%D The original macro was mostly used for testing an attribute that blocks ligatures
+%D in the nodemode handler. In \LMTX\ things are done differently.
+
+\unprotect
+
+\permanent\protected\def\noligature{\dontleavehmode\groupedcommandcs\noligaturing\donothing}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-lin.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-lin.lmt
new file mode 100644
index 00000000000..86365c3ba03
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-lin.lmt
@@ -0,0 +1,402 @@
+if not modules then modules = { } end modules ['typo-lin'] = {
+ version = 1.001,
+ comment = "companion to typo-lin.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code. The idea is to create an anchor point in a line but there are
+-- some considerations:
+--
+-- * if we normalize in order to have more easy access later on, we need to normalize all
+-- lines and we cannot catch all without losing efficiency
+--
+-- * if we normalize too soon, we might have issues with changed properties later on
+--
+-- * if we normalize too late, we have no knowledge of the current hsize
+--
+-- * if we always create an anchor we also create unwanted overhead if it is not used later
+-- on (more nodes)
+--
+-- The question is: do we mind of at the first access an anchor is created? As we cannot know
+-- that now, I choose some middle ground but this might change. if we don't assume direct
+-- access but only helpers, we can decide later.
+--
+-- Because of right2left mess it makes sense to use helpers so that we only need to deal with
+-- this mess once, in helpers. The more abstraction there the better. And so, after a week of
+-- experimenting, yet another abstraction was introduced.
+--
+-- The danger of adding the anchor later is that we adapt the head and so the caller needs to
+-- check that ... real messy. On the other hand, we soldom traverse the line. And other
+-- mechanisms can push stuff in front too. Actually that alone can mess up analysis when we
+-- delay too much. So in the end we need to accept the slow down.
+--
+-- We only need to normalize the left side because when we mess around we keep the page stream
+-- order (and adding content to the right of the line is a no-go for tagged etc. For the same
+-- reason we don't use two left anchors (each side fo leftskip) because there can be stretch.
+-- But, maybe there are good reasons for having just that anchor (mostly for educational purposes
+-- I guess.)
+--
+-- At this stage the par node is no longer of any use so we remove it (each line has the
+-- direction attached). We might at some point also strip the disc nodes as they no longer serve
+-- a purpose but that can better be a helper. Anchoring left has advantage of keeping page
+-- stream.
+--
+-- This looks a bit messy but we want to keep the box as it is so \showboxes still visualizes as
+-- expected. Normally left and rightskips end up in the line while hangindents become shifts and
+-- hsize corrections. We could normalize this to a line with
+
+-- indent : hlist type 3
+-- hangindent : shift and width
+
+local type = type
+
+local trace_anchors = false trackers.register("paragraphs.anchors", function(v) trace_anchors = v end)
+
+local report = logs.reporter("anchors")
+
+local nuts = nodes.nuts
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local kern_code = nodecodes.kern
+local linelist_code = listcodes.line
+
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local nexthlist = nuts.traversers.hlist
+local insert_before = nuts.insert_before
+local insert_after = nuts.insert_after
+
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getid = nuts.getid
+local getboth = nuts.getboth
+local setlink = nuts.setlink
+local setkern = nuts.setkern
+local getkern = nuts.getkern
+local getdirection = nuts.getdirection
+local getshift = nuts.getshift
+local setshift = nuts.setshift
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+
+local getnormalizedline = nuts.getnormalizedline
+
+local setprop = nuts.setprop
+local getprop = nuts.rawprop -- getprop
+
+local effectiveglue = nuts.effective_glue
+
+local nodepool = nuts.pool
+local new_kern = nodepool.kern
+local new_hlist = nodepool.hlist
+local new_rule = nodepool.rule
+local new_glue = nodepool.glue
+
+local righttoleft_code = nodes.dirvalues.righttoleft
+
+local texgetcount = tex.getcount
+local setmetatableindex = table.setmetatableindex
+
+local jobpositions = job.positions
+local getposition = jobpositions.get
+local getreserved = jobpositions.getreserved
+
+local paragraphs = { }
+typesetters.paragraphs = paragraphs
+
+local noflines = 0
+
+local function finalize(prop,key) -- delayed calculations
+ local line = prop.line
+ local hsize = prop.hsize
+ local width = prop.width
+ local shift = getshift(line) -- dangerous as it can be vertical as well
+ local reverse = getdirection(line) == righttoleft_code or false
+ local pack = new_hlist()
+ local head = getlist(line)
+ local delta = 0
+ if reverse then
+ delta = - shift + (hsize - width)
+ else
+ delta = shift
+ end
+ local kern1 = new_kern(delta)
+ local kern2 = new_kern(-delta)
+ head = insert_before(head,head,kern1) -- setlink
+ head = insert_before(head,head,pack)
+ head = insert_before(head,head,kern2)
+ setlist(line,head)
+ local where = {
+ pack = pack,
+ head = nil,
+ tail = nil,
+ }
+ prop.where = where
+ prop.reverse = reverse
+ -- prop.shift = shift
+ setmetatableindex(prop,nil)
+ return prop[key]
+end
+
+local function normalize(line,islocal) -- assumes prestine lines, nothing pre/appended
+ local prop = getnormalizedline(line) -- we also can have "lineproperties" set but for a different porpose
+ local width = getwidth(line)
+ local hsize = islocal and width or tex.hsize
+ noflines = noflines + 1
+ if prop then
+ prop.width = width
+ prop.hsize = hsize
+ prop.line = line
+ prop.number = noflines
+ else
+ -- can be a vbox ... we could use a metatable with zeros
+ prop = {
+ width = width,
+ hsize = hsize,
+ leftskip = 0,
+ rightskip = 0,
+ lefthangskip = 0,
+ righthangskip = 0,
+ parfillleftskip = 0,
+ parfillrightskip = 0,
+ line = line,
+ number = noflines,
+ }
+ end
+ setprop(line,"line",prop)
+ setmetatableindex(prop,finalize)
+ return prop
+end
+
+function paragraphs.checkline(n)
+ return getprop(n,"line") or normalize(n,true)
+end
+
+-- print(nodes.idstostring(head))
+
+-- We do only basic positioning and leave compensation for directions and distances
+-- to the caller as that one knows the circumstances better.
+
+-- todo: only in mvl or explicitly, e.g. framed or so, not in all lines
+
+local function addtoline(n,list,option)
+ local line = getprop(n,"line")
+ if not line then
+ line = normalize(n,true)
+ end
+ if line then
+ if trace_anchors and not line.traced then
+ line.traced = true
+ local rule = new_rule(2*65536,2*65536,1*65536)
+ local list = insert_before(rule,rule,new_kern(-1*65536))
+ addtoline(n,list)
+ local rule = new_rule(2*65536,6*65536,-3*65536)
+ local list = insert_before(rule,rule,new_kern(-1*65536))
+ addtoline(n,list,"internal")
+ else
+ line.traced = true
+ end
+ local list = tonut(list)
+ local where = line.where
+ local head = where.head
+ local tail = where.tail
+ local blob = new_hlist(list)
+ local delta = 0
+ if option == "internal" then
+ if line.reverse then
+ delta = line.lefthangskip - line.leftskip - (line.hsize - line.width)
+ else
+ delta = line.lefthangskip + line.leftskip
+ end
+ end
+ -- always kerns, also when 0 so that we can adapt but we can optimize if needed
+ -- by keeping a hash as long as we use the shiftinline helper .. no need to
+ -- optimize now .. we can also decide to put each blob in a hlist
+ local kern = new_kern(delta)
+ if tail then
+ head, tail = insert_after(head,tail,kern)
+ else
+ head, tail = kern, kern
+ setlist(where.pack,head)
+ end
+ head, tail = insert_after(head,tail,blob)
+ local kern = new_kern(-delta)
+ head, tail = insert_after(head,tail,kern)
+ --
+ where.head = head
+ where.tail = tail
+ return line, blob
+ else
+ -- report("unknown anchor")
+ end
+end
+
+local function addanchortoline(n,anchor)
+ local line = type(n) ~= "table" and getprop(n,"line") or n
+ if not line then
+ line = normalize(n,true)
+ end
+ if line then
+ local anchor = tonut(anchor)
+ local where = line.where
+ if trace_anchors then
+ anchor = new_hlist(setlink(
+ anchor,
+ new_kern(-65536/4),
+ new_rule(65536/2,4*65536,4*65536),
+ new_kern(-65536/4-4*65536),
+ new_rule(8*65536,65536/4,65536/4)
+ ))
+ setwidth(anchor,0)
+ end
+ if where.tail then
+ local head = where.head
+ insert_before(head,head,anchor)
+ else
+ where.tail = anchor
+ end
+ setlist(where.pack,anchor)
+ where.head = anchor
+ return line
+ end
+end
+
+paragraphs.addtoline = addtoline
+paragraphs.addanchortoline = addanchortoline
+
+function paragraphs.moveinline(n,blob,dx,dy)
+ if not blob then
+ return
+ end
+ if not dx then
+ dx = 0
+ end
+ if not dy then
+ dy = 0
+ end
+ if dx ~= 0 or dy ~= 0 then
+ local line = type(n) ~= "table" and getprop(n,"line") or n
+ if line then
+ if dx ~= 0 then
+ local prev, next = getboth(blob)
+ if prev and getid(prev) == kern_code then
+ setkern(prev,getkern(prev) + dx)
+ end
+ if next and getid(next) == kern_code then
+ setkern(next,getkern(next) - dx)
+ end
+ end
+ if dy ~= 0 then
+ if getid(blob) == hlist_code then
+ setshift(blob,getshift(blob) + dy)
+ end
+ end
+ else
+ -- report("no line")
+ end
+ end
+end
+
+local latelua = nodepool.latelua
+local setposition = jobpositions.setspec
+
+local function setanchor(h_anchor)
+ return latelua {
+ action = setposition,
+ name = "md:h",
+ index = h_anchor,
+ value = { x = true, c = true },
+ }
+end
+
+-- needs to be adapted !
+
+function paragraphs.calculatedelta(n,width,delta,atleft,islocal,followshape,area)
+ local line = type(n) ~= "table" and getprop(n,"line") or n
+ if not line then
+ line = normalize(n,true)
+ end
+ local hmove = 0
+ if line then
+ local reverse = line.reverse
+ -- basic hsize based anchoring
+ if atleft then
+ if reverse then
+ -- delta = delta
+ else
+ delta = - delta - width
+ end
+ else
+ if reverse then
+ delta = - delta - width - line.hsize
+ else
+ delta = delta + line.hsize
+ end
+ end
+ if islocal then
+ -- relative to hsize with leftskip / rightskip compensation
+ if atleft then
+ if reverse then
+ delta = delta - line.leftskip
+ else
+ delta = delta + line.leftskip
+ end
+ else
+ if reverse then
+ delta = delta + line.rightskip
+ else
+ delta = delta - line.rightskip
+ end
+ end
+ if followshape then
+ -- shape compensation (compare to mkiv where we have shift!)
+ if atleft then
+ if reverse then
+ delta = delta + line.lefthangskip - line.hsize + line.width -- needs testing: + line.parfillleftskip
+ else
+ delta = delta + line.lefthangskip -- needs testing: - line.parfillleftskip
+ end
+ else
+ if reverse then
+ delta = delta + line.lefthangskip + line.parfillrightskip
+ else
+ delta = delta + line.lefthangskip - line.hsize + line.width - line.parfillrightskip
+ end
+ end
+ end
+ end
+ if area then
+ local number = line.number
+ if not line.hanchor then
+ addanchortoline(line,setanchor(number))
+ line.hanchor = true
+ end
+ local blob = getposition("md:h",number)
+ if blob then
+ local reference = getreserved(area,blob.c)
+ if reference then
+ hmove = (reference.x or 0) - (blob.x or 0)
+ if atleft then
+ if reverse then
+ hmove = hmove + (reference.w or 0)
+ else
+ -- hmove = hmove
+ end
+ else
+ if reverse then
+ hmove = hmove + line.hsize
+ else
+ hmove = hmove + (reference.w or 0) - line.hsize
+ end
+ end
+ end
+ end
+ end
+ end
+ return delta, hmove
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-lin.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-lin.mkxl
new file mode 100644
index 00000000000..6b142b32d19
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-lin.mkxl
@@ -0,0 +1,25 @@
+%D \module
+%D [ file=typo-lin,
+%D version=2015.08.01,
+%D title=\CONTEXT\ Typographic Macros,
+%D subtitle=Line Normalization,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typographic Macros / Normalization}
+
+%D Beware: the mvl callback is also called when a line is added to a vbox.
+
+\registerctxluafile{typo-lin}{autosuffix}
+
+\unprotect
+
+% nothing yet / experimental
+
+\protect \endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-mar.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-mar.lmt
new file mode 100644
index 00000000000..7e69162a900
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-mar.lmt
@@ -0,0 +1,995 @@
+if not modules then modules = { } end modules ['typo-mar'] = {
+ version = 1.001,
+ comment = "companion to typo-mar.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo:
+--
+-- * autoleft/right depending on available space (or distance to margin)
+-- * floating margin data, with close-to-call anchoring
+
+local format, validstring = string.format, string.valid
+local insert, remove, sortedkeys, fastcopy = table.insert, table.remove, table.sortedkeys, table.fastcopy
+local setmetatable, next, tonumber = setmetatable, next, tonumber
+local formatters = string.formatters
+local toboolean = toboolean
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local attributes = attributes
+local nodes = nodes
+local variables = variables
+local context = context
+
+local trace_margindata = false trackers.register("typesetters.margindata", function(v) trace_margindata = v end)
+local trace_marginstack = false trackers.register("typesetters.margindata.stack", function(v) trace_marginstack = v end)
+local trace_margingroup = false trackers.register("typesetters.margindata.group", function(v) trace_margingroup = v end)
+
+local report_margindata = logs.reporter("margindata")
+
+local tasks = nodes.tasks
+local prependaction = tasks.prependaction
+local disableaction = tasks.disableaction
+local enableaction = tasks.enableaction
+
+local variables = interfaces.variables
+
+local conditionals = tex.conditionals
+local systemmodes = tex.systemmodes
+
+local v_top = variables.top
+local v_depth = variables.depth
+local v_local = variables["local"]
+local v_global = variables["global"]
+local v_left = variables.left
+local v_right = variables.right
+local v_inner = variables.inner
+local v_outer = variables.outer
+local v_margin = variables.margin
+local v_edge = variables.edge
+local v_default = variables.default
+local v_normal = variables.normal
+local v_yes = variables.yes
+local v_continue = variables.continue
+local v_first = variables.first
+local v_text = variables.text
+local v_paragraph = variables.paragraph
+local v_line = variables.line
+
+local nuts = nodes.nuts
+local tonode = nuts.tonode
+
+local hpack_nodes = nuts.hpack
+local traverse_id = nuts.traverse_id
+local flush_node_list = nuts.flush_list
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getsubtype = nuts.getsubtype
+local getlist = nuts.getlist
+local getwhd = nuts.getwhd
+local setlist = nuts.setlist
+local setlink = nuts.setlink
+local getshift = nuts.getshift
+local setshift = nuts.setshift
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+local getheight = nuts.getheight
+
+local setattrlist = nuts.setattrlist
+
+local getbox = nuts.getbox
+local takebox = nuts.takebox
+
+local setprop = nuts.setprop
+local getprop = nuts.getprop
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+local whatsitcodes = nodes.whatsitcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local whatsit_code = nodecodes.whatsit
+local userdefined_code = whatsitcodes.userdefined
+
+local nodepool = nuts.pool
+
+local new_hlist = nodepool.hlist
+local new_usernode = nodepool.usernode
+local latelua = nodepool.latelua
+
+local texgetdimen = tex.getdimen
+local texgetcount = tex.getcount
+local texget = tex.get
+
+local isleftpage = layouts.status.isleftpage
+local registertogether = builders.paragraphs.registertogether
+
+local paragraphs = typesetters.paragraphs
+local addtoline = paragraphs.addtoline
+local moveinline = paragraphs.moveinline
+local calculatedelta = paragraphs.calculatedelta
+
+local a_linenumber = attributes.private('linenumber')
+
+local inline_mark = nodepool.userids["margins.inline"]
+
+local jobpositions = job.positions
+local getposition = jobpositions.get
+local setposition = jobpositions.set
+local getreserved = jobpositions.getreserved
+
+local margins = { }
+typesetters.margins = margins
+
+local locations = { v_left, v_right, v_inner, v_outer } -- order might change
+local categories = { }
+local displaystore = { } -- [category][location][scope]
+local inlinestore = { } -- [number]
+local nofsaved = 0
+local nofstored = 0
+local nofinlined = 0
+local nofdelayed = 0
+local nofinjected = 0
+local h_anchors = 0
+local v_anchors = 0
+
+local mt1 = {
+ __index = function(t,location)
+ local v = { [v_local] = { }, [v_global] = { } }
+ t[location] = v
+ return v
+ end
+}
+
+local mt2 = {
+ __index = function(stores,category)
+ categories[#categories+1] = category
+ local v = { }
+ setmetatable(v,mt1)
+ stores[category] = v
+ return v
+ end
+}
+
+setmetatable(displaystore,mt2)
+
+local defaults = {
+ __index = {
+ location = v_left,
+ align = v_normal, -- not used
+ method = "",
+ name = "",
+ threshold = 0, -- .25ex
+ margin = v_normal,
+ scope = v_global,
+ distance = 0,
+ hoffset = 0,
+ voffset = 0,
+ category = v_default,
+ line = 0,
+ vstack = 0,
+ dy = 0,
+ baseline = false,
+ inline = false,
+ leftskip = 0,
+ rightskip = 0,
+ option = { }
+ }
+}
+
+local enablelocal, enableglobal -- forward reference (delayed initialization)
+
+local function showstore(store,banner,location)
+ if next(store) then
+ for i, si in table.sortedpairs(store) do
+ local si =store[i]
+ report_margindata("%s: stored in %a at %s: %a => %s",banner,location,i,validstring(si.name,"no name"),nodes.toutf(getlist(si.box)))
+ end
+ else
+ report_margindata("%s: nothing stored in location %a",banner,location)
+ end
+end
+
+function margins.save(t)
+ setmetatable(t,defaults)
+ local content = takebox(t.number)
+ local location = t.location
+ local category = t.category
+ local inline = t.inline
+ local scope = t.scope
+ local name = t.name
+ local option = t.option
+ local stack = t.stack
+ if option then
+ option = settings_to_hash(option)
+ t.option = option
+ end
+ if not content then
+ report_margindata("ignoring empty margin data %a",location or "unknown")
+ return
+ end
+ setprop(content,"specialcontent","margindata")
+ local store
+ if inline then
+ store = inlinestore
+ else
+ store = displaystore[category][location]
+ if not store then
+ report_margindata("invalid location %a",location)
+ return
+ end
+ store = store[scope]
+ end
+ if not store then
+ report_margindata("invalid scope %a",scope)
+ return
+ end
+ if enablelocal and scope == v_local then
+ enablelocal()
+ if enableglobal then
+ enableglobal() -- is the fallback
+ end
+ elseif enableglobal and scope == v_global then
+ enableglobal()
+ end
+ nofsaved = nofsaved + 1
+ nofstored = nofstored + 1
+ if trace_marginstack then
+ showstore(store,"before",location)
+ end
+ if name and name ~= "" then
+ -- this can be used to overload
+ if inlinestore then -- todo: inline store has to be done differently (not sparse)
+ local t = sortedkeys(store) for j=#t,1,-1 do local i = t[j]
+ local si = store[i]
+ if si.name == name then
+ local s = remove(store,i)
+ flush_node_list(s.box)
+ end
+ end
+ else
+ for i=#store,1,-1 do
+ local si = store[i]
+ if si.name == name then
+ local s = remove(store,i)
+ flush_node_list(s.box)
+ end
+ end
+ end
+ if trace_marginstack then
+ showstore(store,"between",location)
+ end
+ end
+ if t.number then
+ local leftmargindistance = texgetdimen("naturalleftmargindistance")
+ local rightmargindistance = texgetdimen("naturalrightmargindistance")
+ local strutbox = getbox("strutbox")
+ local _, strutht, strutdp = getwhd(strutbox)
+ -- better make a new table and make t entry in t
+ t.box = content
+ t.n = nofsaved
+ -- used later (we will clean up this natural mess later)
+ -- nice is to make a special status table mechanism
+ t.strutheight = strutht
+ t.strutdepth = strutdp
+ -- beware: can be different from the applied one (we're not in forgetall)
+ t.leftskip = texget("leftskip",false)
+ t.rightskip = texget("rightskip",false)
+ --
+ t.leftmargindistance = leftmargindistance -- todo:layoutstatus table
+ t.rightmargindistance = rightmargindistance
+ t.leftedgedistance = texgetdimen("naturalleftedgedistance")
+ + texgetdimen("leftmarginwidth")
+ + leftmargindistance
+ t.rightedgedistance = texgetdimen("naturalrightedgedistance")
+ + texgetdimen("rightmarginwidth")
+ + rightmargindistance
+ t.lineheight = texgetdimen("lineheight")
+ --
+ -- t.realpageno = texgetcount("realpageno")
+ if inline then
+ local n = new_usernode(inline_mark,nofsaved)
+ setattrlist(n,true)
+ context(tonode(n)) -- or use a normal node
+ store[nofsaved] = t -- no insert
+ nofinlined = nofinlined + 1
+ else
+ insert(store,t)
+ end
+ end
+ if trace_marginstack then
+ showstore(store,"after",location)
+ end
+ if trace_margindata then
+ report_margindata("saved %a, location %a, scope %a, inline %a",nofsaved,location,scope,inline)
+ end
+end
+
+-- Actually it's an advantage to have them all anchored left (tags and such)
+-- we could keep them in store and flush in stage two but we might want to
+-- do more before that so we need the content to be there unless we can be
+-- sure that we flush this first which might not be the case in the future.
+--
+-- When the prototype inner/outer code that was part of this proved to be
+-- okay it was moved elsewhere.
+
+local function realign(current,candidate)
+ local location = candidate.location
+ local margin = candidate.margin
+ local hoffset = candidate.hoffset
+ local distance = candidate.distance
+ local hsize = candidate.hsize
+ local width = candidate.width
+ local align = candidate.align
+ local inline = candidate.inline
+ local anchor = candidate.anchor
+ local hook = candidate.hook
+ local scope = candidate.scope
+ local option = candidate.option
+ local reverse = hook.reverse
+ local atleft = true
+ local hmove = 0
+ local delta = 0
+ local leftpage = isleftpage()
+ local leftdelta = 0
+ local rightdelta = 0
+ local leftdistance = distance
+ local rightdistance = distance
+ --
+ if not anchor or anchor == "" then
+ anchor = v_text -- this has to become more clever: region:0|column:n|column
+ end
+ if margin == v_normal then
+ --
+ elseif margin == v_local then
+ leftdelta = - candidate.leftskip
+ rightdelta = candidate.rightskip
+ elseif margin == v_margin then
+ leftdistance = candidate.leftmargindistance
+ rightdistance = candidate.rightmargindistance
+ elseif margin == v_edge then
+ leftdistance = candidate.leftedgedistance
+ rightdistance = candidate.rightedgedistance
+ end
+ if leftpage then
+ leftdistance, rightdistance = rightdistance, leftdistance
+ end
+ if location == v_right then
+ atleft = false
+ elseif location == v_inner then
+ if leftpage then
+ atleft = false
+ end
+ elseif location == v_outer then
+ if not leftpage then
+ atleft = false
+ end
+ else
+ -- v_left
+ end
+
+ local islocal = scope == v_local
+ local area = (not islocal or option[v_text]) and anchor or nil
+
+ if atleft then
+ delta = hoffset + leftdelta + leftdistance
+ else
+ delta = hoffset + rightdelta + rightdistance
+ end
+
+ local delta, hmove = calculatedelta (
+ hook, -- the line
+ width, -- width of object
+ delta, -- offset
+ atleft,
+ islocal, -- islocal
+ option[v_paragraph], -- followshape
+ area -- relative to area
+ )
+
+ if hmove ~= 0 then
+ delta = delta + hmove
+ if trace_margindata then
+ report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,hmove)
+ end
+ else
+ if trace_margindata then
+ report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin)
+ end
+ end
+ moveinline(hook,candidate.node,delta)
+end
+
+local function realigned(current,candidate)
+ realign(current,candidate)
+ nofdelayed = nofdelayed - 1
+ setprop(current,"margindata",false)
+ return true
+end
+
+-- Stacking is done in two ways: the v_yes option stacks per paragraph (or line,
+-- depending on what gets by) and mostly concerns margin data dat got set at more or
+-- less the same time. The v_continue option uses position tracking and works on
+-- larger range. However, crossing pages is not part of it. Anyway, when you have
+-- such messed up margin data you'd better think twice.
+--
+-- The stacked table keeps track (per location) of the offsets (the v_yes case). This
+-- table gets saved when the v_continue case is active. We use a special variant
+-- of position tracking, after all we only need the page number and vertical position.
+
+local validstacknames = {
+ [v_left ] = v_left ,
+ [v_right] = v_right,
+ [v_inner] = v_inner,
+ [v_outer] = v_outer,
+}
+
+local cache = { }
+local stacked = { [v_yes] = { }, [v_continue] = { } }
+local anchors = { [v_yes] = { }, [v_continue] = { } }
+
+local function resetstacked(all)
+ stacked[v_yes] = { }
+ anchors[v_yes] = { }
+ if all then
+ stacked[v_continue] = { }
+ anchors[v_continue] = { }
+ end
+end
+
+-- anchors are only set for lines that have a note
+
+local function sa(specification) -- maybe l/r keys ipv left/right keys
+ local tag = specification.tag
+ local p = cache[tag]
+ if p then
+ if trace_marginstack then
+ report_margindata("updating anchor %a",tag)
+ end
+ p.p = true
+ p.y = true
+ -- maybe settobesaved first
+ setposition("md:v",tag,p)
+ cache[tag] = nil -- do this later, per page a cleanup
+ end
+end
+
+local function setanchor(v_anchor) -- freezes the global here
+ return latelua { action = sa, tag = v_anchor }
+end
+
+local function aa(specification) -- maybe l/r keys ipv left/right keys
+ local tag = specification.tag
+ local n = specification.n
+ local p = jobpositions.gettobesaved('md:v',tag)
+ if p then
+ if trace_marginstack then
+ report_margindata("updating injected %a",tag)
+ end
+ local pages = p.pages
+ if not pages then
+ pages = { }
+ p.pages = pages
+ end
+ pages[n] = texgetcount("realpageno")
+ elseif trace_marginstack then
+ report_margindata("not updating injected %a",tag)
+ end
+end
+
+local function addtoanchor(v_anchor,n) -- freezes the global here
+ return latelua { action = aa, tag = v_anchor, n = n }
+end
+
+local function markovershoot(current) -- todo: alleen als offset > line
+ v_anchors = v_anchors + 1
+ cache[v_anchors] = fastcopy(stacked)
+ local anchor = setanchor(v_anchors)
+ -- local list = hpack_nodes(setlink(anchor,getlist(current))) -- not ok, we need to retain width
+ -- local list = setlink(anchor,getlist(current)) -- why not this ... better play safe
+ local list = hpack_nodes(setlink(anchor,getlist(current)),getwidth(current),"exactly")--
+ if trace_marginstack then
+ report_margindata("marking anchor %a",v_anchors)
+ end
+ setlist(current,list)
+end
+
+local function inject(parent,head,candidate)
+ local box = candidate.box
+ if not box then
+ return head, nil, false -- we can have empty texts
+ end
+ local width, height, depth
+ = getwhd(box)
+ local shift = getshift(box)
+ local stack = candidate.stack
+ local stackname = candidate.stackname
+ local location = candidate.location
+ local method = candidate.method
+ local voffset = candidate.voffset
+ local line = candidate.line
+ local baseline = candidate.baseline
+ local strutheight = candidate.strutheight
+ local strutdepth = candidate.strutdepth
+ local inline = candidate.inline
+ local psubtype = getsubtype(parent)
+ -- This stackname is experimental and therefore undocumented and basically
+ -- unsupported. It was introduced when we needed to support overlapping
+ -- of different anchors.
+ if not stackname or stackname == "" then
+ stackname = location
+ else
+ stackname = validstacknames[stackname] or location
+ end
+ local isstacked = stack == v_continue or stack == v_yes
+ local offset = isstacked and stacked[stack][stackname]
+ local firstonstack = offset == false or offset == nil
+ nofinjected = nofinjected + 1
+ nofdelayed = nofdelayed + 1
+ -- yet untested
+ baseline = tonumber(baseline)
+ if not baseline then
+ baseline = toboolean(baseline)
+ end
+ --
+ if baseline == true then
+ baseline = false
+ else
+ baseline = tonumber(baseline)
+ if not baseline or baseline <= 0 then
+ -- in case we have a box of width 0 that is not analyzed
+ baseline = false -- strutheight -- actually a hack
+ end
+ end
+ candidate.width = width
+ candidate.hsize = getwidth(parent) -- we can also pass textwidth
+ candidate.psubtype = psubtype
+ candidate.stackname = stackname
+ if trace_margindata then
+ report_margindata("processing, index %s, height %p, depth %p, parent %a, method %a",candidate.n,height,depth,listcodes[psubtype],method)
+ end
+ -- Overlap detection is somewhat complex because we have display and inline
+ -- notes mixed as well as inner and outer positioning. We do need to
+ -- handle it in the stream because we also keep lines together so we keep
+ -- track of page numbers of notes.
+
+ if isstacked then
+ firstonstack = true
+ local anchor = getposition("md:v")
+ if anchor and (location == v_inner or location == v_outer) then
+ local pages = anchor.pages
+ if pages then
+ local page = pages[nofinjected]
+ if page then
+ if isleftpage(page) then
+ stackname = location == v_inner and v_right or v_left
+ else
+ stackname = location == v_inner and v_left or v_right
+ end
+ candidate.stackname = stackname
+ offset = stack and stack ~= "" and stacked[stack][stackname]
+ end
+ end
+ end
+ local current = v_anchors + 1
+ local previous = anchors[stack][stackname]
+ if trace_margindata then
+ report_margindata("anchor %i, offset so far %p",current,offset or 0)
+ end
+ local ap = anchor and anchor[previous]
+ local ac = anchor and anchor[current]
+ if not previous then
+ elseif previous == current then
+ firstonstack = false
+ elseif ap and ac and ap.p == ac.p then
+ local distance = (ap.y or 0) - (ac.y or 0)
+ if trace_margindata then
+ report_margindata("distance %p",distance)
+ end
+ if offset > distance then
+ -- we already overflow
+ offset = offset - distance
+ firstonstack = false
+ else
+ offset = 0
+ end
+ else
+ -- what to do
+ end
+ anchors[v_yes] [stackname] = current
+ anchors[v_continue][stackname] = current
+ if firstonstack then
+ offset = 0
+ end
+ offset = offset + candidate.dy -- always
+ shift = shift + offset
+ else
+ if firstonstack then
+ offset = 0
+ end
+ offset = offset + candidate.dy -- always
+ shift = shift + offset
+ end
+ -- Maybe we also need to patch offset when we apply methods, but how ...
+ -- This needs a bit of playing as it depends on the stack setting of the
+ -- following which we don't know yet ... so, consider stacking partially
+ -- experimental.
+ if method == v_top then
+ local delta = height - getheight(parent)
+ if trace_margindata then
+ report_margindata("top aligned by %p",delta)
+ end
+ if delta < candidate.threshold then -- often we need a negative threshold here
+ shift = shift + voffset + delta
+ end
+ elseif method == v_line then
+ local _, ph, pd = getwhd(parent)
+ if pd == 0 then
+ local delta = height - ph
+ if trace_margindata then
+ report_margindata("top aligned by %p (no depth)",delta)
+ end
+ if delta < candidate.threshold then -- often we need a negative threshold here
+ shift = shift + voffset + delta
+ end
+ end
+ elseif method == v_first then
+ if baseline then
+ shift = shift + voffset + height - baseline -- option
+ else
+ shift = shift + voffset -- normal
+ end
+ if trace_margindata then
+ report_margindata("first aligned")
+ end
+ elseif method == v_depth then
+ local delta = strutdepth
+ if trace_margindata then
+ report_margindata("depth aligned by %p",delta)
+ end
+ shift = shift + voffset + delta
+ elseif method == v_height then
+ local delta = - strutheight
+ if trace_margindata then
+ report_margindata("height aligned by %p",delta)
+ end
+ shift = shift + voffset + delta
+ elseif voffset ~= 0 then
+ if trace_margindata then
+ report_margindata("voffset %p applied",voffset)
+ end
+ shift = shift + voffset
+ end
+ -- -- --
+ if line ~= 0 then
+ local delta = line * candidate.lineheight
+ if trace_margindata then
+ report_margindata("offset %p applied to line %s",delta,line)
+ end
+ shift = shift + delta
+ offset = offset + delta
+ end
+ setshift(box,shift)
+ setwidth(box,0) -- not needed when wrapped
+ --
+ if isstacked then
+ setlink(box,addtoanchor(v_anchors,nofinjected))
+ box = new_hlist(box)
+ -- set height / depth ?
+ end
+ --
+ candidate.hook, candidate.node = addtoline(parent,box)
+ --
+ setprop(box,"margindata",candidate)
+ if trace_margindata then
+ report_margindata("injected, location %a, stack %a, shift %p",location,stackname,shift)
+ end
+ -- we need to add line etc to offset as well
+ offset = offset + depth
+ local room = {
+ height = height,
+ depth = offset,
+ slack = candidate.bottomspace, -- todo: 'depth' => strutdepth
+ lineheight = candidate.lineheight, -- only for tracing
+ stacked = inline and isstacked,
+ }
+ offset = offset + height
+ -- we need a restart ... when there is no overlap at all
+ stacked[v_yes] [stackname] = offset
+ stacked[v_continue][stackname] = offset
+ -- todo: if no real depth then zero
+ if trace_margindata then
+ report_margindata("status, offset %s",offset)
+ end
+ return getlist(parent), room, inline and isstacked or (stack == v_continue)
+end
+
+local function flushinline(parent,head)
+ local current = head
+ local done = false
+ local continue = false
+ local room, don, con, list
+ while current and nofinlined > 0 do
+ local id = getid(current)
+ if id == whatsit_code then
+ if getsubtype(current) == userdefined_code and getprop(current,"id") == inline_mark then
+ local n = getprop(current,"data")
+ local candidate = inlinestore[n]
+ if candidate then -- no vpack, as we want to realign
+ inlinestore[n] = nil
+ nofinlined = nofinlined - 1
+ head, room, con = inject(parent,head,candidate) -- maybe return applied offset
+ done = true
+ continue = continue or con
+ nofstored = nofstored - 1
+ if room and room.stacked then
+ -- for now we also check for inline+yes/continue, maybe someday no such check
+ -- will happen; we can assume most inlines are one line heigh; also this
+ -- together feature can become optional
+ registertogether(parent,room)
+ end
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ -- optional (but sometimes needed)
+ list, don, con = flushinline(current,getlist(current))
+ setlist(current,list)
+ continue = continue or con
+ done = done or don
+ end
+ current = getnext(current)
+ end
+ return head, done, continue
+end
+
+local function flushed(scope,parent) -- current is hlist
+ local head = getlist(parent)
+ local done = false
+ local continue = false
+ local room, con, don
+ for c=1,#categories do
+ local category = categories[c]
+ for l=1,#locations do
+ local location = locations[l]
+ local store = displaystore[category][location][scope]
+ if store then
+ while true do
+ local candidate = remove(store,1) -- brr, local stores are sparse
+ if candidate then -- no vpack, as we want to realign
+ head, room, con = inject(parent,head,candidate)
+ done = true
+ continue = continue or con
+ nofstored = nofstored - 1
+ if room then
+ registertogether(parent,room)
+ end
+ else
+ break
+ end
+ end
+ else
+ -- report_margindata("fatal error: invalid category %a",category or "?")
+ end
+ end
+ end
+ if nofinlined > 0 then
+ if done then
+ setlist(parent,head)
+ end
+ head, don, con = flushinline(parent,head)
+ continue = continue or con
+ done = done or don
+ end
+ if done then
+ local a = getattr(head,a_linenumber) -- hack .. we need a more decent critical attribute inheritance mechanism
+ if false then
+ local l = hpack_nodes(head,getwidth(parent),"exactly")
+ setlist(parent,l)
+ if a then
+ setattr(l,a_linenumber,a)
+ end
+ else
+ -- because packing messes up profiling
+ setlist(parent,head)
+ if a then
+ setattr(parent,a_linenumber,a)
+ end
+ end
+ end
+ return done, continue
+end
+
+-- only when group : vbox|vmode_par
+-- only when subtype : line, box (no indent alignment cell)
+
+local function handler(scope,head,group)
+ if nofstored > 0 then
+ if trace_margindata then
+ report_margindata("flushing stage one, stored %s, scope %s, delayed %s, group %a",nofstored,scope,nofdelayed,group)
+ end
+ local current = head
+ local done = false -- for tracing only
+ while current do
+ local id = getid(current)
+ if (id == vlist_code or id == hlist_code) and getprop(current,"margindata") == nil then
+ local don, continue = flushed(scope,current)
+ if don then
+ done = true
+ setprop(current,"margindata",false) -- signal to prevent duplicate processing
+ if continue then
+ markovershoot(current)
+ end
+ if nofstored <= 0 then
+ break
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ if trace_margindata then
+ if done then
+ report_margindata("flushing stage one, done, %s left",nofstored)
+ else
+ report_margindata("flushing stage one, nothing done, %s left",nofstored)
+ end
+ end
+ resetstacked()
+ end
+ return head
+end
+
+local trialtypesetting = context.trialtypesetting
+
+-- maybe change this to an action applied to the to be shipped out box (that is
+-- the mvl list in there so that we don't need to traverse global
+
+function margins.localhandler(head,group) -- sometimes group is "" which is weird
+
+ if trialtypesetting() then
+ return head
+ end
+
+ local inhibit = conditionals.inhibitmargindata
+ if inhibit then
+ if trace_margingroup then
+ report_margindata("ignored 3, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head
+ end
+ if nofstored > 0 then
+ return handler(v_local,head,group)
+ end
+ if trace_margingroup then
+ report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head
+end
+
+function margins.globalhandler(head,group) -- check group
+
+ if trialtypesetting() then
+ return head, false
+ end
+
+ local inhibit = conditionals.inhibitmargindata
+ if inhibit or nofstored == 0 then
+ if trace_margingroup then
+ report_margindata("ignored 1, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head
+ elseif group == "hmode_par" then
+ return handler(v_global,head,group)
+ elseif group == "vmode_par" then -- experiment (for alignments)
+ return handler(v_global,head,group)
+ -- this needs checking as we then get quite some one liners to process and
+ -- we cannot look ahead then:
+ elseif group == "box" then -- experiment (for alignments)
+ return handler(v_global,head,group)
+ elseif group == "alignment" then -- experiment (for alignments)
+ return handler(v_global,head,group)
+ else
+ if trace_margingroup then
+ report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head
+ end
+end
+
+local function finalhandler(head)
+ if nofdelayed > 0 then
+ local current = head
+ while current and nofdelayed > 0 do -- traverse_list
+ local id = getid(current)
+ if id == hlist_code then -- only lines?
+ local a = getprop(current,"margindata")
+ if not a then
+ finalhandler(getlist(current))
+ elseif realigned(current,a) then
+ if nofdelayed == 0 then
+ return head, true
+ end
+ end
+ elseif id == vlist_code then
+ finalhandler(getlist(current))
+ end
+ current = getnext(current)
+ end
+ end
+ return head
+end
+
+function margins.finalhandler(head)
+ if nofdelayed > 0 then
+ if trace_margindata then
+ report_margindata("flushing stage two, instore: %s, delayed: %s",nofstored,nofdelayed)
+ end
+ head = finalhandler(head)
+ resetstacked(nofdelayed==0)
+ else
+ resetstacked()
+ end
+ return head
+end
+
+-- Somehow the vbox builder (in combinations) gets pretty confused and decides to
+-- go horizontal. So this needs more testing.
+
+enablelocal = function()
+ enableaction("finalizers", "typesetters.margins.localhandler")
+ enableaction("shipouts", "typesetters.margins.finalhandler")
+ enablelocal = nil
+end
+
+enableglobal = function()
+ enableaction("mvlbuilders", "typesetters.margins.globalhandler")
+ enableaction("shipouts", "typesetters.margins.finalhandler")
+ enableglobal = nil
+end
+
+statistics.register("margin data", function()
+ if nofsaved > 0 then
+ return format("%s entries, %s pending",nofsaved,nofdelayed)
+ else
+ return nil
+ end
+end)
+
+interfaces.implement {
+ name = "savemargindata",
+ actions = margins.save,
+ arguments = {
+ {
+ { "location" },
+ { "method" },
+ { "category" },
+ { "name" },
+ { "scope" },
+ { "number", "integer" },
+ { "margin" },
+ { "distance", "dimen" },
+ { "hoffset", "dimen" },
+ { "voffset", "dimen" },
+ { "dy", "dimen" },
+ { "bottomspace", "dimen" },
+ { "baseline"}, -- dimen or string or
+ { "threshold", "dimen" },
+ { "inline", "boolean" },
+ { "anchor" },
+ -- { "leftskip", "dimen" },
+ -- { "rightskip", "dimen" },
+ { "align" },
+ { "option" },
+ { "line", "integer" },
+ { "index", "integer" },
+ { "stackname" },
+ { "stack" },
+ }
+ }
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-mar.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-mar.mkxl
new file mode 100644
index 00000000000..4862ee671ba
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-mar.mkxl
@@ -0,0 +1,478 @@
+% macros=mkvi
+
+%D \module
+%D [ file=typo-mar,
+%D version=2010.02.15, % was experimental code
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Margindata,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% maybe dontleavehmode when scope is local
+
+% todo: tags
+% todo: force inline with option (saves pos)
+% todo: margintitle (also less position then)
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Margindata}
+
+\unprotect
+
+%D This module has been on the agenda for a while. Actually, it is one of the things
+%D that I really need myself, for instance when rendering rather unpredictable
+%D (educational) tests encoded in XML. This module permits anchoring for instance
+%D item numbers and also overload them when they have subnumbers. In the future it
+%D might replace the current maginal note mechanism (that then will be just an
+%D instance).
+%D
+%D In spite of what might be expected, the more advanced \LUA\ based variant is upto
+%D twice as fast on simple entries. Also, we no longer need an extra pass to get
+%D inner and outer alignments in sync with the pagebuilder.
+
+\registerctxluafile{typo-mar}{autosuffix}
+
+%definesystemattribute[margindata] % only at the lua end
+
+%D In \MKII\ we have three categories and their historically meaning is as follows:
+%D
+%D marginlines: These are flushed relative to the start of a line and need to be
+%D invoked there.
+%D
+%D marginwords: These can be issued in the text flow and will migrate sidewards; in
+%D spite of the name, it can be a paragraph of text as well, but normally it's
+%D words.
+%D
+%D margintexts: These can be set beforehand and are flushed at the next paragraph of
+%D text (of header).
+%D
+%D In \MKIV\ we have further integrated the mechanism and we now have:
+%D
+%D margindata: This can be anything that needs to go into the margin. It can be
+%D anchored in the text or given beforehand in which case it gets flushed at the
+%D first occasion.
+%D
+%D margintext: This runs on top of margindata and the only difference is that it
+%D uses the framed mechanism for packaging.
+%D
+%D Stacking is done differently as is inner and outer alignment (in most cases more
+%D efficient). The functionality is mostly the same as in \MKII, but there are a few
+%D additions, like names entries, where later ones overload preceding not yet
+%D flushed ones. Data can get catagorized and is then treated as a group (e.g. when
+%D stacking is needed).
+%D
+%D The amount of \TEX\ code is less than in \MKII\ because we do all trickery in at
+%D the \LUA\ end. At the end of this file we define several commands, like \type
+%D {\inleftmargin} and \type {\inleft}. You can configure them individually or as a
+%D group. There is an inheritance model in place.
+%D
+%D The following notes will be stacked:
+%D
+%D \starttyping
+%D \ininner[line=2]{IM A}
+%D \ininner[stack=yes]{IM B}
+%D \ininner[stack=yes]{IM C}
+%D \stoptyping
+%D
+%D The distance between them is determined by \type {dy}:
+%D
+%D \starttyping
+%D \ininner[stack=yes,dy=2ex][frame=on] {IM A}
+%D \ininner[stack=yes,dy=2ex][frame=on] {IM B}
+%D \stoptyping
+%D
+%D There are several methods of vertical alignment.
+%D
+%D \starttyping
+%D \inmargin [method=first] [frame=on] {first\\second} \input ward \par
+%D \inmargin [method=first] [frame=on,offset=3pt] {first\\second} \input ward \par
+%D \inmargin [method=first,voffset=-3pt][frame=on,offset=3pt,rulethickness=3pt] {first\\second} \input ward \par
+%D \inmargin [method=first,voffset=-6pt][frame=on,offset=3pt,rulethickness=3pt] {first\\second} \input ward \par
+%D \stoptyping
+%D
+%D You sometimes need to combine \type {voffset} with \type {offset}. The first
+%D argument concerns the data, the second the framed. Not sharing the setup is on
+%D purpose: location, offset, alignment and other parameters might clash.
+
+\installcorenamespace{margindata}
+\installcorenamespace{marginframed}
+
+\installcommandhandler \??margindata {margindata} \??margindata
+\installframedcommandhandler \??marginframed {marginframed} \??marginframed
+
+\setupmargindata
+ [\c!location=\v!left,
+ % \c!align=,
+ % \c!method=,
+ \c!style=\v!bold,
+ \c!color=, % maybe \maintextcolor
+ % \c!name=,
+ % \c!category=,
+ \c!threshold=.25\exheight,
+ \c!margin=\v!normal,
+ \c!scope=\v!global,
+ \c!width=,
+ % \c!stack=,
+ % \c!stackname=,
+ % \c!option=, % \v!paragraph (follow shape)
+ \c!line=0,
+ \c!anchor=\v!text,
+ \c!bottomspace=\strutdepth, % slack used for keeptogether
+ \c!dy=\zeropoint,
+ \c!distance=\zeropoint,
+ \c!hoffset=\zeropoint,
+ \c!voffset=\zeropoint]
+
+\setupmarginframed % so, align should be set with the data command
+ [\c!strut=\v!yes, % so by default we scale the strut to the font !
+ \c!offset=\v!overlay,
+ \c!fr!analyze=\v!yes,
+ \c!frame=\v!off,
+ \c!width=\margindataparameter\c!width,
+ \c!align=\margindataparameter\c!align]
+
+\appendtoks
+ \frozen\instance\setuevalue\currentmargindata{\margindata[\currentmargindata]}%
+\to \everydefinemargindata
+
+\newconditional\inhibitmargindata % This one is used at the Lua end!
+\newtoks \everymargindatacontent % Later on we will set this one.
+
+\mutable\let\currentmargindatastrut \empty
+\mutable\let\currentmargindatawidth \empty
+\mutable\let\currentmarginfirstheight\empty
+\mutable\let\currentmarginreference \empty
+
+\appendtoks
+ \settrue\inhibitmargindata
+\to \everyforgetall
+
+\appendtoks
+ \forgetall
+ \tf
+ \resetallattributes % \deactivatecolor % needed, but maybe we should switch to maintextcolor: \onlyinheritmaintextcolor
+ \pickupattributes
+\to \everymargindatacontent
+
+% trialtypesetting: no need for margin stuff while trialing as
+% is has no dimensions
+
+\newcount\nofmargintexts
+\newcount\c_typo_margins_n
+
+\ifdefined\dotagmarginanchor \else \aliased\let\dotagmarginanchor\gobbleoneargument \fi
+\ifdefined\dotagmargintext \else \aliased\let\dotagmargintext \gobbleoneargument \fi
+
+\definepagestate[\s!margintext]
+
+\protected\def\typo_margins_data_synchronize
+ {\doforcedtrackpagestate\s!margintext\nofmargintexts % includes increment
+ \docheckpagestate\s!margintext\nofmargintexts
+ %\doifelserightpagestate\s!margintext\nofmargintexts\relax\relax
+ \realpageno\realpagestateno
+ \page_layouts_swap_margins}
+
+\permanent\protected\def\margindata
+ {\iftrialtypesetting
+ \expandafter\typo_margins_data_nop
+ \else
+ \expandafter\typo_margins_data_yes
+ \fi}
+
+\permanent\tolerant\protected\def\typo_margins_data_nop[#name]#spacer[#dataparameters]#spacer[#textparameters]#:#content%
+ {}
+
+\permanent\tolerant\protected\def\typo_margins_data_yes[#name]#spacer[#dataparameters]#spacer[#textparameters]#:#content%
+ {\setfalse\inhibitmargindata % flushing afterwards
+ \begingroup
+ %\settrue\inhibitmargindata % no flushing in here
+ \def\currentmargindata{#name}%
+ \let\currentmarginframed\currentmargindata
+ \ifparameter#dataparameters\or
+ \setupcurrentmargindata[#dataparameters]%
+ \fi
+ % \doifelsenothing{#content}\donefalse\donetrue
+ % \doifelsetext{#content}\donetrue\donefalse
+ \ifparameter#content\donefalse\or\donetrue\else\donefalse\fi
+ \global\advance\c_typo_margins_n\plusone
+ \ifdone
+ \edef\currentmarginreference{\margindataparameter\c!reference}%
+ \ifempty\currentmarginreference \else
+ \strc_references_set_page_only_destination_box_attribute\currentmarginreference\currentmarginreference
+ \fi
+ \edef\currentmargindatastrut{\margindataparameter\c!strut}%
+ \dostarttaggedchained\t!margintext\currentmargindata\??margindata
+ \dotagmargintext\c_typo_margins_n
+ \ifcsname\currentmarginframedhash\s!parent\endcsname
+ \setbox\nextbox\naturalhbox \currentmarginreference \bgroup
+ \the\everymargindatacontent
+ \usemargindatastyleandcolor\c!style\c!color
+ \setupcurrentmarginframed[\c!location=\v!normal,#textparameters]%
+ \typo_margins_data_synchronize
+ \inheritedmarginframedframed\bgroup
+ \ifempty\currentmargindatastrut \else
+ \synchronizestrut\currentmargindatastrut
+ \fi
+ \begstrut
+ \strc_references_flush_destination_nodes
+ \margindataparameter\c!command{#content}%
+ \endstrut
+ \egroup
+ \egroup
+ \edef\currentmarginfirstheight{\number\dimexpr\framedfirstheight}%
+ \else
+ \edef\currentmargindatawidth{\margindataparameter\c!width}%
+ \ifempty\currentmargindatawidth
+ \setbox\nextbox\naturalhbox \currentmarginreference \bgroup
+ \typo_margins_data_synchronize
+ \the\everymargindatacontent
+ \usemargindatastyleandcolor\c!style\c!color
+ \ifempty\currentmargindatastrut \else
+ \synchronizestrut\currentmargindatastrut
+ \fi
+ \begstrut
+ \strc_references_flush_destination_nodes
+ \margindataparameter\c!command{#content}%
+ \endstrut
+ \egroup
+ \let\currentmarginfirstheight\empty
+ \else
+ \setbox\nextbox\naturalhbox \currentmarginreference \bgroup
+ \typo_margins_data_synchronize
+ \dosetraggedcommand{\margindataparameter\c!align}%
+ \vtop \bgroup
+ \the\everymargindatacontent
+ \usemargindatastyleandcolor\c!style\c!color
+ \hsize\currentmargindatawidth
+ \raggedcommand
+ \ifempty\currentmargindatastrut \else
+ \synchronizestrut\currentmargindatastrut
+ \fi
+ \begstrut
+ \strc_references_flush_destination_nodes
+ \margindataparameter\c!command{#content}%
+ \endstrut
+ \egroup
+ \egroup
+ \edef\currentmarginfirstheight{true}%
+ \fi
+ \fi
+ \dostoptagged
+ \fi
+ \ifdone
+ \edef\p_anchor{\margindataparameter\c!anchor}%
+ \anch_positions_initialize % we use positions at the lua end
+ \dostarttagged\t!marginanchor\empty
+ \dotagmarginanchor\c_typo_margins_n
+ \clf_savemargindata
+ location {\margindataparameter\c!location}%
+ method {\margindataparameter\c!method}%
+ category {\margindataparameter\c!category}%
+ name {\margindataparameter\c!name}%
+ scope {\margindataparameter\c!scope}%
+ option {\margindataparameter\c!option}%
+ number \nextbox
+ margin {\margindataparameter\c!margin}% local normal margin edge
+ distance \dimexpr\margindataparameter\c!distance\relax
+ hoffset \dimexpr\margindataparameter\c!hoffset\relax
+ voffset \dimexpr\margindataparameter\c!voffset\relax
+ dy \dimexpr\margindataparameter\c!dy\relax
+ bottomspace \dimexpr\margindataparameter\c!bottomspace\relax
+ \ifempty\currentmarginfirstheight \else
+ baseline {\currentmarginfirstheight}%
+ \fi
+ threshold \dimexpr\margindataparameter\c!threshold\relax % overlap related, will change
+ \ifhmode
+ inline true %
+ \fi
+ anchor {\p_anchor\ifx\p_anchor\v!region:0\fi}% kind of a hack to force column anchoring (for now)
+ %
+ % we're not in forgetall
+ %
+ % \ifzeropt\leftskip \else
+ % leftskip \dimexpr\leftskip\relax
+ % \fi
+ % \ifzeropt\leftskip \else
+ % rightskip \dimexpr\rightskip\relax
+ % \fi
+ align {\margindataparameter\c!align}%
+ line \numexpr\margindataparameter\c!line\relax
+ stackname {\margindataparameter\c!stackname}%
+ stack {\margindataparameter\c!stack}%
+ index \c_typo_margins_n
+ \relax
+ \dostoptagged
+ \else
+ \clf_savemargindata
+ location {\margindataparameter\c!location}%
+ method {\margindataparameter\c!method}%
+ category {\margindataparameter\c!category}%
+ name {\margindataparameter\c!name}%
+ scope {\margindataparameter\c!scope}%
+ number \nextbox
+ \relax
+ \fi
+ \endgroup}
+
+%D Downward compatible hack:
+
+\appendtoks
+ \enforced\let\\\spaceorpar
+\to \everymargindatacontent
+
+%D Another one:
+
+% \installcorenamespace{oppositemargin}
+%
+% \letvalue{\??oppositemargin\v!left }\v!right
+% \letvalue{\??oppositemargin\v!right }\v!left
+% \letvalue{\??oppositemargin\v!inner }\v!outer
+% \letvalue{\??oppositemargin\v!outer }\v!inner
+% \letvalue{\??oppositemargin\v!normal}\v!normal
+%
+% \def\oppositemargin#1%
+% {\csname\??oppositemargin\ifcsname\??oppositemargin#1\endcsname#1\else\v!normal\fi\endcsname}
+
+%D Definitions:
+
+% common to lines and text
+
+\setupmargindata [\v!left ] [\c!method=\v!first,\c!location=\v!left ,\c!margin=\v!margin,\c!align=\v!flushright,\s!parent=\??margindata] % we could autoparent when no define yet
+\setupmargindata [\v!right] [\c!method=\v!first,\c!location=\v!right,\c!margin=\v!margin,\c!align=\v!flushleft, \s!parent=\??margindata]
+\setupmargindata [\v!outer] [\c!method=\v!first,\c!location=\v!outer,\c!margin=\v!margin,\c!align=\v!inner, \s!parent=\??margindata]
+\setupmargindata [\v!inner] [\c!method=\v!first,\c!location=\v!inner,\c!margin=\v!margin,\c!align=\v!outer, \s!parent=\??margindata]
+
+% lines
+
+\definemargindata [\v!inleftmargin] [\v!left ] [\c!margin=\v!margin,\c!width=\leftmarginwidth ,\c!style=,\c!color=]
+\definemargindata [\v!inrightmargin] [\v!right] [\c!margin=\v!margin,\c!width=\rightmarginwidth,\c!style=,\c!color=]
+\definemargindata [\v!inoutermargin] [\v!outer] [\c!margin=\v!margin,\c!width=\outermarginwidth,\c!style=,\c!color=]
+\definemargindata [\v!ininnermargin] [\v!inner] [\c!margin=\v!margin,\c!width=\innermarginwidth,\c!style=,\c!color=]
+
+\definemargindata [\v!inleftedge] [\v!left ] [\c!margin=\v!edge ,\c!width=\leftedgewidth ,\c!style=,\c!color=,\c!category=\v!edge]
+\definemargindata [\v!inrightedge] [\v!right] [\c!margin=\v!edge ,\c!width=\rightedgewidth ,\c!style=,\c!color=,\c!category=\v!edge]
+\definemargindata [\v!inouteredge] [\v!outer] [\c!margin=\v!edge ,\c!width=\outeredgewidth ,\c!style=,\c!color=,\c!category=\v!edge]
+\definemargindata [\v!ininneredge] [\v!inner] [\c!margin=\v!edge ,\c!width=\inneredgewidth ,\c!style=,\c!color=,\c!category=\v!edge]
+
+\definemargindata [\v!atleftmargin] [\v!left ] [\c!margin=\v!normal,\c!width=\leftmarginwidth ,\c!style=,\c!color=]
+\definemargindata [\v!atrightmargin] [\v!right] [\c!margin=\v!normal,\c!width=\rightmarginwidth,\c!style=,\c!color=]
+
+% text: \v!added
+
+\definemargindata [\v!inleft] [\v!left ] [\c!margin=\v!margin,\c!width=\leftmarginwidth ,\c!align=\v!flushright]
+\definemargindata [\v!inright] [\v!right] [\c!margin=\v!margin,\c!width=\rightmarginwidth,\c!align=\v!flushleft]
+\definemargindata [\v!inouter] [\v!outer] [\c!margin=\v!margin,\c!width=\outermarginwidth,\c!align=\v!inner]
+\definemargindata [\v!ininner] [\v!inner] [\c!margin=\v!margin,\c!width=\innermarginwidth,\c!align=\v!outer]
+
+% no longer auto auto-other
+
+\definemargindata [\v!inmargin] [\v!left] [\c!margin=\v!margin,\c!width=\leftmarginwidth, \c!align=\v!flushright]
+\definemargindata [\v!inother] [\v!right] [\c!margin=\v!margin,\c!width=\rightmarginwidth,\c!align=\v!flushleft]
+
+\definemargindata [\v!margintext] [\v!left] [\c!margin=\v!margin,\c!width=\leftmarginwidth, \c!align=\v!flushright,\c!stack=\v!yes]
+
+\setupmarginframed [\v!left ] [\c!method=\v!first,\c!align=\v!flushright,\s!parent=\??marginframed] % we could autoparent when no define yet
+\setupmarginframed [\v!right] [\c!method=\v!first,\c!align=\v!flushleft, \s!parent=\??marginframed]
+\setupmarginframed [\v!outer] [\c!method=\v!first,\c!align=\v!inner, \s!parent=\??marginframed]
+\setupmarginframed [\v!inner] [\c!method=\v!first,\c!align=\v!outer, \s!parent=\??marginframed]
+
+\definemarginframed [\v!inleft] [\v!left ]
+\definemarginframed [\v!inright] [\v!right]
+\definemarginframed [\v!inouter] [\v!outer]
+\definemarginframed [\v!ininner] [\v!inner]
+\definemarginframed [\v!inmargin] [\v!inleft]
+\definemarginframed [\v!inother] [\v!inright]
+
+\aliased\let\marginword \margintext
+\aliased\let\margintitle \margintext
+\aliased\let\inothermargin\inother % for old times sake
+
+%definemargindata [inouterextra] [\v!outer] [\c!margin=\v!edge,\c!location=\v!outer,\c!width=\outeredgewidth,\c!align=\v!outer,\c!category=\v!edge]
+%definemargindata [ininnerextra] [\v!inner] [\c!margin=\v!edge,\c!location=\v!inner,\c!width=\inneredgewidth,\c!align=\v!inner,\c!category=\v!edge]
+%
+%definemarginframed [inouterextra] [\v!outer]
+%definemarginframed [ininnerextra] [\v!inner]
+
+%D As we have more control we are not backward compatible although in
+%D practice it won't hurt that much. So, from now on use:
+%D
+%D \starttyping
+%D \definemargindata
+%D \setupmargindata
+%D \definemarginframed
+%D \setupmarginframed
+%D \stoptyping
+
+% The following sort of works okay but is to be avoided ... it will disappear from lmtx:
+
+\permanent\tolerant\protected\def\defineinmargin[#name]#spacer[#location]#spacer[#align]#spacer[#settings]% not completely compatible
+ {\ifarguments\else
+ \definemargindata[#name][\c!location=#location,\c!align=#align,#settings]%
+ \definemarginframed[#name][#location][\c!align=#align,#settings]%
+ \fi}
+
+\aliased\let\definemarginline\definemargindata % for old times sake
+\aliased\let\setupinmargin \setupmargindata % only partial (no framed), for old times sake
+
+% begin of experimental code (will move)
+%
+% \dosetanchor{x}test \dostarthanchoring{x}\llap{crap}\dostophanchoring{x}test test test
+% test \dostarthanchoring{text}\llap{crap}\dostophanchoring{text}test test test
+
+% \def\dosetanchor #1{\dontleavehmode\latelua{anchors.set("#1")}}
+% \def\doresetanchor #1{\dontleavehmode\latelua{anchors.reset("#1")}}
+% \def\doresetanchornow #1{\directlua{anchors.reset("#1")}}
+% \def\dostartanchoring #1{\dontleavehmode\latelua{anchors.startmove("#1")}}
+% \def\dostopanchoring #1{\dontleavehmode\latelua{anchors.stopmove("#1")}}
+% \def\dostarthanchoring#1{\dontleavehmode\latelua{anchors.startmove("#1","h")}}
+% \def\dostartvanchoring#1{\dontleavehmode\latelua{anchors.startmove("#1","v")}}
+% \let\dostophanchoring \dostopanchoring
+% \let\dostopvanchoring \dostopanchoring
+
+%D Here because in strc-ren we are too early:
+
+% % \definemargindata
+% % [margintext:chapter]
+% % [margintext:section]
+% %
+% % \defineheadalternative
+% % [margintext:chapter]
+% % [margintext]
+% % [margintext=margintext:chapter]
+% %
+% % \setuphead
+% % [chapter]
+% % [alternative=margintext:chapter]
+%
+% \setuphead
+% [chapter]
+% [alternative=margintext]
+
+\definemargindata
+ [\v!margintext:\v!section]
+ [\v!left]
+ [\c!margin=\v!margin,
+ \c!width=\leftmarginwidth,
+ \c!align=\v!flushright]
+
+\defineheadalternative
+ [\v!margintext]
+ [\c!alternative=\v!somewhere,
+ \c!margintext=\v!margintext:\v!section,
+ \c!renderingsetup=\??headrenderings:\v!margintext]
+
+\startsetups[\??headrenderings:\v!margintext]
+ \executeifdefined{\headalternativeparameter\c!margintext}\margintext {
+ \ifconditional\headshownumber
+ \headnumbercontent
+ \hskip\headnumberdistance
+ \fi
+ \headtextcontent
+ }
+\stopsetups
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-ovl.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-ovl.lmt
new file mode 100644
index 00000000000..5834f9ff06a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-ovl.lmt
@@ -0,0 +1,184 @@
+if not modules then modules = { } end modules ['typo-ovl'] = {
+ version = 1.001,
+ comment = "companion to typo-ovl.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is dubious code. If you needed it your source is probably bad. We only used
+-- in when we had to mark bad content but when cleaning up some project code I decided
+-- that it is easier to maintain in the distribution then in a project style. After all,
+-- we have hardly any private code. For convenience I hooked it into the existing
+-- replacement module (as it used the same code anyway). I did some cleanup.
+
+local next, type = next, type
+
+local context = context
+
+local nuts = nodes.nuts
+local tonut = nodes.tonut
+local tonode = nodes.tonode
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+
+local getnext = nuts.getnext
+local getid = nuts.getid
+local getdisc = nuts.getdisc
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getattrlist = nuts.getattrlist
+local setattrlist = nuts.setattrlist
+local getdata = nuts.getdata
+local setfont = nuts.setfont
+
+local nextnode = nuts.traversers.node
+
+local unsetvalue = attributes.unsetvalue
+local prvattribute = attributes.private
+
+local texgetbox = tex.getbox
+local currentfont = font.current
+
+local a_overloads = attributes.private("overloads")
+local n_overloads = 0
+local t_overloads = { }
+
+local overloaded = { }
+
+local function markasoverload(a)
+ local n = prvattribute(a)
+ if n then
+ overloaded[n] = a
+ end
+end
+
+attributes.markasoverload = markasoverload
+
+markasoverload("color")
+markasoverload("colormodel")
+markasoverload("transparency")
+markasoverload("case")
+markasoverload("negative")
+markasoverload("effect")
+markasoverload("ruled")
+markasoverload("shifted")
+markasoverload("kernchars")
+markasoverload("kern")
+markasoverload("noligature")
+markasoverload("viewerlayer")
+
+local function tooverloads(n)
+ local current = tonut(n)
+ local a = getattrlist(current)
+ local s = { }
+ while a do
+ local index, value = getdata(a)
+ local o = overloaded[n]
+ if o and value ~= unsetvalue then
+ -- It is actually very unlikely that we have unsetvalue.
+ s[index] = value
+ end
+ a = getnext(a)
+ end
+ return s
+end
+
+attributes.tooverloads = tooverloads
+
+function attributes.applyoverloads(specification,start,stop)
+ local start = tonut(start)
+ local processor = specification.processor
+ local overloads = specification.processor or getattr(start,a_overloads)
+ if overloads and overloads ~= unsetvalue then
+ overloads = t_overloads[overloads]
+ if not overloads then
+ return
+ end
+ else
+ return
+ end
+
+ local last = stop and tonut(stop)
+ local oldlist = nil
+ local newlist = nil
+ local newfont = overloads.font
+
+ local function apply(current)
+ local a = getattrlist(current)
+ if a == oldlist then
+ setattrlist(current,newlist)
+ else
+ oldlist = getattrlist(current)
+ for k, v in next, overloads do
+ if type(v) == "number" then
+ setattr(current,k,v)
+ else
+ -- can be: ["font"] = number
+ end
+ end
+ newlist = current -- getattrlist(current)
+ end
+ if newfont then
+ setfont(current,newfont)
+ end
+ end
+
+ for current, id in nextnode, start do
+ if id == glyph_code then
+ apply(current)
+ elseif id == disc_code then
+ apply(current)
+ if pre then
+ while pre do
+ if getid(pre) == glyph_code then
+ apply()
+ end
+ pre = getnext(pre)
+ end
+ end
+ if post then
+ while post do
+ if getid(post) == glyph_code then
+ apply()
+ end
+ post = getnext(post)
+ end
+ end
+ if replace then
+ while replace do
+ if getid(replace) == glyph_code then
+ apply()
+ end
+ replace = getnext(replace)
+ end
+ end
+ end
+ if current == last then
+ break
+ end
+ end
+end
+
+-- we assume the same highlight so we're global
+
+interfaces.implement {
+ name = "overloadsattribute",
+ arguments = { "string", "integer", "integer" },
+ actions = function(name,font,box)
+ local samplebox = texgetbox(box)
+ local sample = samplebox and samplebox.list
+ local overloads = sample and tooverloads(sample)
+ if overloads then
+ overloads.font = font > 0 and font or false
+ n_overloads = n_overloads + 1
+ t_overloads[n_overloads] = overloads
+ t_overloads[name] = overloads
+ context(n_overloads)
+ else
+ context(unsetvalue)
+ end
+ end
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-ovl.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-ovl.mkxl
new file mode 100644
index 00000000000..b3ba2e78388
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-ovl.mkxl
@@ -0,0 +1,116 @@
+%D \module
+%D [ file=lang-ovl,
+%D version=2016.02.03, % about that time (maybe earlier)
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Overloads,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Overloads}
+
+\unprotect
+
+%D See \LUA\ file for comments. I'll probably never use this code outside the
+%D project where it had some experimental use. (Occasionally we do some spell
+%D checking and suspicious word usage marking.) So there is not much documentation
+%D here. Keep in mind that overloading only makes sense for content that gets
+%D replaced as otherwise normal attributes make more sense. Using this otherwise
+%D is weird but one never knows what users come up with.
+
+\registerctxluafile{typo-ovl}{autosuffix}
+
+\definesystemattribute[overloads][public,global]
+
+\installcorenamespace{overloads}
+
+%D Watch how we abuse highlights.
+
+\newcount\c_typo_overload_font_id
+
+\protected\def\typo_overloads_define#1%
+ {\begingroup
+ % we pack so we avoid interference (and for sure don't process!)
+ \setbox\scratchbox\hpack\bgroup
+ \global\c_typo_overload_font_id\fontid\font
+ \resetallattributes % so we only change what gets set
+ \highlight[#1]%
+ \signalcharacter
+ \global\c_typo_overload_font_id
+ \ifnum\c_typo_overload_font_id=\fontid\font
+ \zerocount
+ \else
+ \fontid\font
+ \fi
+ \egroup
+ \global\expandafter\chardef\csname\??overloads#1\endcsname
+ \clf_overloadsattribute{#1}\c_typo_overload_font_id\scratchbox
+ \endgroup}
+
+\permanent\protected\def\setoverloads[#1]%
+ {\ifcsname\??overloads#1\endcsname\else
+ \typo_overloads_define{#1}%
+ \fi
+ \c_attr_overloads\csname\??overloads#1\endcsname}
+
+\permanent\protected\def\registeroverloads[#1]%
+ {\ifcsname\??overloads#1\endcsname\else
+ \typo_overloads_define{#1}%
+ \fi}
+
+\permanent\protected\def\resetoverloads
+ {\c_attr_overloads\attributeunsetvalue}
+
+%D We hook this into the replacement module, probably the only place where it makes
+%D sense. Here is an adapted version of an example:
+%D
+%D \starttyping
+%D \replaceword[basics][new] [old]
+%D \replaceword[basics][now] [hl3->never]
+%D \replaceword[basics][never][now]
+%D \replaceword[basics][heck] []
+%D
+%D \definehighlight[hl1][style={\setbar[underbar]\red\setcharactercasing[WORD]\bf}]
+%D \definehighlight[hl2][style={\setbar[overbar]\blue\setcharactercasing[Words]}]
+%D \definehighlight[hl3][style={\setbar[overstrike]\green\bs}]
+%D
+%D \registeroverloads[hl1]
+%D \registeroverloads[hl2]
+%D \registeroverloads[hl3]
+%D
+%D \start \setreplacements[basics][hl1] What the heck, it's now or never, isn't it new? \stop \par
+%D \start \setreplacements[basics][hl2] What the heck, it's now or never, isn't it new? \stop \par
+%D \start \setreplacements[basics][hl1] What the heck, it's now or never, isn't it new? \stop \par
+%D \start \setreplacements[basics] What the heck, it's now or never, isn't it new? \stop \par
+%D \stoptyping
+%D
+%D We used it to mark synonyms that should not be corrected by the editor. Sort of the
+%D reverse of not having a word in a vetted wordlist. A bit out of place in a typesetting
+%D system. If really needed we can support multiple words seperataed by spaces but I didn't
+%D need it. And \unknown\ fonts are supported but at fixed size!
+
+\ifdefined\setreplacements
+
+ \pushoverloadmode
+
+ \permanent\tolerant\protected\def\setreplacements[#1]#*[#2]%
+ {\clf_setreplacements{#1}%
+ \ifparameter#2\or
+ \setoverloads[#2]%
+ \else
+ \resetoverloads
+ \fi}
+
+ \popoverloadmode
+
+\else
+
+ % something weird
+
+\fi
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-pag.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-pag.lmt
new file mode 100644
index 00000000000..9f8a3a1f418
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-pag.lmt
@@ -0,0 +1,206 @@
+if not modules then modules = { } end modules ['typo-pag'] = {
+ version = 1.001,
+ comment = "companion to typo-pag.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+
+builders = builders or { }
+local builders = builders
+
+builders.paragraphs = builders.paragraphs or { }
+local parbuilders = builders.paragraphs
+
+local nodes = nodes
+local nodecodes = nodes.nodecodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local kern_code = nodecodes.kern
+local penalty_code = nodecodes.penalty
+
+local unsetvalue = attributes.unsetvalue
+local a_keeptogether = attributes.private("keeptogether")
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getattr = nuts.getattr
+local takeattr = nuts.takeattr
+local setattr = nuts.setattr
+local getwhd = nuts.getwhd
+local getkern = nuts.getkern
+local setpenalty = nuts.setpenalty
+local getwidth = nuts.getwidth
+local getdepth = nuts.getdepth
+
+local insert_node_after = nuts.insert_after
+local new_penalty = nuts.pool.penalty
+
+local trace_keeptogether = false
+local report_keeptogether = logs.reporter("parbuilders","keeptogether")
+
+local enableaction = nodes.tasks.enableaction
+
+local cache = { }
+local last = 0
+local enabled = false
+
+trackers.register("parbuilders.keeptogether", function(v) trace_keeptogether = v end)
+
+-- todo: also support lines = 3 etc (e.g. dropped caps) but how to set that
+-- when no hlists are there ?
+
+function parbuilders.registertogether(line,specification) -- might change
+ if not specification then
+ return
+ end
+ if not enabled then
+ enableaction("finalizers","builders.paragraphs.keeptogether")
+ end
+ local a = getattr(line,a_keeptogether)
+ local c = a and cache[a]
+ if c then
+ local height = specification.height
+ local depth = specification.depth
+ local slack = specification.slack
+ if height and height > c.height then
+ c.height = height
+ end
+ if depth and depth > c.depth then
+ c.depth = depth
+ end
+ if slack and slack > c.slack then
+ c.slack = slack
+ end
+ else
+ last = last + 1
+ cache[last] = specification
+ if not specification.height then
+ specification.height = 0
+ end
+ if not specification.depth then
+ specification.depth = 0
+ end
+ if not specification.slack then
+ specification.slack = 0
+ end
+ setattr(line,a_keeptogether,last)
+ end
+ if trace_keeptogether then
+ local a = a or last
+ local c = cache[a]
+ local noflines = specification.lineheight
+ local height = c.height
+ local depth = c.depth
+ local slack = c.slack
+ if not noflines or noflines == 0 then
+ noflines = "unknown"
+ else
+ noflines = math.round((height + depth - slack) / noflines)
+ end
+ report_keeptogether("registered, index %s, height %p, depth %p, slack %p, noflines %a",a,height,depth,slack,noflines)
+ end
+end
+
+local function keeptogether(start,a,specification)
+ local current = getnext(start)
+ local previous = start
+ local total = getdepth(previous)
+ local slack = specification.slack
+ local threshold = specification.depth - slack
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %p, threshold %p, slack %p","list",a,total,threshold,slack)
+ end
+ while current do
+ local id = getid(current)
+ if id == vlist_code or id == hlist_code then
+ local wd, ht, dp = getwhd(current)
+ total = total + ht + dp
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %p, threshold %p","list",a,total,threshold)
+ end
+ if total <= threshold then
+ if getid(previous) == penalty_code then
+ setpenalty(previous,10000)
+ else
+ insert_node_after(head,previous,new_penalty(10000))
+ end
+ else
+ break
+ end
+ elseif id == glue_code then
+ -- hm, breakpoint, maybe turn this into kern
+ total = total + getwidth(current)
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %p, threshold %p","glue",a,total,threshold)
+ end
+ if total <= threshold then
+ if getid(previous) == penalty_code then
+ setpenalty(previous,10000)
+ else
+ insert_node_after(head,previous,new_penalty(10000))
+ end
+ else
+ break
+ end
+ elseif id == kern_code then
+ total = total + getkern(current)
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %s, threshold %s","kern",a,total,threshold)
+ end
+ if total <= threshold then
+ if getid(previous) == penalty_code then
+ setpenalty(previous,10000)
+ else
+ insert_node_after(head,previous,new_penalty(10000))
+ end
+ else
+ break
+ end
+ elseif id == penalty_code then
+ if total <= threshold then
+ if getid(previous) == penalty_code then
+ setpenalty(previous,10000)
+ end
+ setpenalty(current,10000)
+ else
+ break
+ end
+ end
+ previous = current
+ current = getnext(current)
+ end
+end
+
+-- also look at first non glue/kern node e.g for a dropped caps
+
+-- todo: find_attribute and unset_attributes instead of takeattr
+
+function parbuilders.keeptogether(head)
+ local done = false -- can go
+ local current = head
+ while current do
+ if getid(current) == hlist_code then
+ local a = takeattr(current,a_keeptogether)
+ if a and a > 0 then
+ local specification = cache[a]
+ if specification then
+ keeptogether(current,a,specification)
+ -- this is tricky ... we need a better resetter, maybe some
+ -- injected latelua or a gc method on a property (interesting
+ -- experiment)
+ cache[a] = nil
+ done = true
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ return head, done
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-pag.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-pag.mkxl
new file mode 100644
index 00000000000..b34ff079b5a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-pag.mkxl
@@ -0,0 +1,20 @@
+%D \module
+%D [ file=typo-pag,
+%D version=2011.11.02,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Pages,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Pages}
+
+\unprotect
+
+\registerctxluafile{typo-pag}{autosuffix}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-par.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-par.lmt
new file mode 100644
index 00000000000..005d1b32ecb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-par.lmt
@@ -0,0 +1,96 @@
+if not modules then modules = { } end modules ['typo-par'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Just some experimental stuff .. trying to improve some ancient metafun manual
+-- hackery that has been on the angenda for too long already. Names might names
+-- anyway.
+
+local insert, remove = table.insert, table.remove
+
+local texget = tex.get
+local texset = tex.set
+local shiftparshape = tex.shiftparshape
+
+local sequencers = utilities.sequencers
+local appendaction = sequencers.appendaction
+local enableaction = sequencers.enableaction
+local disableaction = sequencers.disableaction
+
+local implement = interfaces.implement
+
+local stack = { }
+local top = nil
+local enabled = false
+
+local trace = false trackers.register("paragraphs.tweaks",function(v) trace = v end)
+
+local report = logs.reporter("paragraphs","tweaks")
+
+implement {
+ name = "pushparagraphtweak",
+ public = true,
+ protected = true,
+ arguments = "string",
+ actions = function(t)
+ insert(stack,top)
+ if not enabled then
+ if trace then
+ report("enabling")
+ end
+ enableaction("paragraphcontext","builders.checkparcontext")
+ enabled = true
+ end
+ top = t
+ end
+}
+
+implement {
+ name = "popparagraphtweak",
+ public = true,
+ protected = true,
+ actions = function()
+ top = remove(stack)
+ if enabled and not top then
+ if trace then
+ report("disabling")
+ end
+ disableaction("paragraphcontext","builders.checkparcontext")
+ enabled = false
+ end
+ end
+}
+
+function builders.checkparcontext(where)
+ if top and where == "normal" then
+ if top == "cycle" then
+ local s = texget("parshape",true)
+ if s then
+ local p = texget("prevgraf")
+ while p > s do
+ p = p - s
+ end
+ if trace then
+ report("cycling %i",s)
+ end
+ shiftparshape(p,true)
+ return true
+ end
+ elseif top == "shift" then
+ local s = texget("parshape",true)
+ if s then
+ if trace then
+ report("shifting %i", s)
+ end
+ shiftparshape(texget("prevgraf"))
+ return true
+ end
+ end
+ end
+end
+
+appendaction("paragraphcontext","system","builders.checkparcontext")
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-par.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-par.mkxl
new file mode 100644
index 00000000000..22450e76edb
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-par.mkxl
@@ -0,0 +1,60 @@
+%D \module
+%D [ file=typo-par,
+%D version=2008.09.30,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Paragraph Building,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Node Macros / Paragraph Building}
+
+%D This is very experimental, undocumented, subjected to changes, etc. just as
+%D the underlying interfaces. But at least it's cleaned as part of the status-mkiv
+%D cleanup.
+
+% \startparbuilder[basic]
+% \input tufte \par
+% \stopparbuilder
+
+\unprotect
+
+%registerctxluafile{node-ltp}{optimize}
+\registerctxluafile{node-ltp}{}
+\registerctxluafile{trac-par}{}
+\registerctxluafile{typo-par}{}
+
+%D Just a reminder:
+%D
+%D \starttyping
+%D \def\whatever#1{#1mm \dimexpr\hsize-#1mm\relax}
+%D
+%D \parshape
+%D 14
+%D options 1 % repeat
+%D \whatever{0} \whatever {2}\whatever {4}\whatever{6}\whatever{8}\whatever{10}\whatever{12}\whatever{14}
+%D \whatever{12}\whatever{10}\whatever{8}\whatever{6}\whatever {4}\whatever {2}
+%D \pushparagraphtweak {repeat}
+%D \dorecurse{10}{\samplefile{tufte} \samplefile{tufte} \par}
+%D \popparagraphtweak
+%D \page
+%D
+%D \dorecurse{2}{
+%D \parshape 4 5mm 125mm 0mm 120mm 5mm 125mm 0mm 120mm
+%D \pushparagraphtweak {repeat}
+%D \dorecurse{10}{
+%D verse line 1\crlf
+%D verse line 2\crlf
+%D verse line 3\crlf
+%D verse line 4\par
+%D }
+%D \popparagraphtweak
+%D \page
+%D }
+%D \stoptyping
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-plc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-plc.mkxl
new file mode 100644
index 00000000000..e6e90db73c8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-plc.mkxl
@@ -0,0 +1,39 @@
+%D \module
+%D [ file=typo-plc,
+%D version=2020.07.01,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Placeholders,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Placeholders}
+
+\unprotect
+
+\installcorenamespace {placeholder}
+
+\installcommandhandler \??placeholder {placeholder} \??placeholder
+
+\permanent\protected\def\placeholder[#1]%
+ {\begingroup
+ \edef\currentplaceholder{#1}%
+ \placeholderparameter\c!before
+ \useplaceholderstyleandcolor\c!style\c!color
+ \placeholderparameter\c!text
+ \placeholderparameter\c!after
+ \endgroup}
+
+\defineplaceholder
+ [todo]
+ %[\c!text={Still to be written!},
+ [\c!text=todo, % labeltext ?
+ \c!style=\v!bold,
+ \c!before=\blank,
+ \c!after=\blank]
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-pnc.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-pnc.mkxl
new file mode 100644
index 00000000000..b5b1a17d676
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-pnc.mkxl
@@ -0,0 +1,55 @@
+%D \module
+%D [ file=typo-pnc,
+%D version=2017.06.23, % experiment in project
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Punctuation,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Punctuation}
+
+\unprotect
+
+% In manuals we often have "foo e.g. bar" where we then have to compensate spaces
+% with "foo e.g.\ bar" so let's see if we can automate that and at the same time
+% inject spaces between the snippets. It gets boring writing this kind of code.
+
+\registerctxluafile{typo-pnc}{}
+
+\definesystemattribute[periodkern][public,global]
+
+\installcorenamespace{periodkerning}
+
+\installcommandhandler \??periodkerning {periodkerning} \??periodkerning
+
+\permanent\protected\def\setperiodkerning[#1]%
+ {\edef\currentperiodkerning{#1}%
+ \ifx\currentperiodkerning\s!reset
+ \resetperiodkerning
+ \else
+ \typo_periodkerning_set
+ \fi}
+
+\def\typo_periodkerning_set
+ {\clf_setperiodkerning{\periodkerningparameter\c!factor}}
+
+\permanent\protected\def\resetperiodkerning % fast one
+ {\c_attr_periodkern\attributeunsetvalue}
+
+\appendtoks
+ \resetperiodkerning
+\to \everyresettypesetting
+
+\defineperiodkerning [zerospaceperiods] [\c!factor=0]
+\defineperiodkerning [smallspaceperiods] [\c!factor=.25]
+\defineperiodkerning [halfspaceperiods] [\c!factor=.5]
+
+% If we ever need a command, see typo-krn.mkiv but it doesn't make much sense
+% to have it.
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-prc.mklx b/Master/texmf-dist/tex/context/base/mkxl/typo-prc.mklx
new file mode 100644
index 00000000000..9531949b8f4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-prc.mklx
@@ -0,0 +1,99 @@
+%D \module
+%D [ file=typo-prc,
+%D version=2008.10.20,
+%D title=\CONTEXT\ Structure Macros,
+%D subtitle=Processors,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Processors}
+
+%D For the moment manipulators are loaded here too, as they're in the same category
+%D as processors. This might change. (They are used in publications.)
+
+\registerctxluafile{typo-prc}{}
+\registerctxluafile{typo-man}{}
+
+\unprotect
+
+%D Processors are used when we cannot easily associate typesetting directives with
+%D (for instance) structural elements. Instead of ending up with numerous additional
+%D definitions we can group treatments in so called processors.
+%D
+%D An example of where processors can be used is in separator sets (these are
+%D related to typesetting numbers using structure).
+%D
+%D \starttyping
+%D \defineprocessor[demo][style=\bfb,color=red]
+%D \stoptyping
+%D
+%D This defines a processor named \type {demo}. Such a name ends up as prefix in for
+%D instance:
+%D
+%D \starttyping
+%D \defineseparatorset [demosep] [demo->!,demo->?,demo->!,demo->?] [demo->@]
+%D \stoptyping
+%D
+%D Here the \type {!} and \type {?} are just the seperator characters that end up
+%D between part, chapter, section, etc.\ numbers. The third argument defines the
+%D default. When a separator is inserted, the \type{demo} processor will be applied.
+%D Here the number will be separated by red slightly bigger than normal bold
+%D exclamation marks and questionmarks.
+%D
+%D Valid keys for defining a processor are \type {style}, \type {color}, \type
+%D {left}, \type {right}, and \type {command} (the given command takes one
+%D argument).
+
+\installcorenamespace{processor}
+\installcorenamespace{processorcheck}
+
+\installcommandhandler \??processor {processor} \??processor
+
+\appendtoks
+ \letcsname\??processorcheck\currentprocessor\endcsname\relax
+ \clf_registerstructureprocessor{\currentprocessor}% global, but it permits using processor that are yet undefined
+\to \everydefineprocessor
+
+%D The following command can be used by users but normally it will be invoked behind
+%D the screens. After all, processor prefixes need to be split off first. At the
+%D \LUA\ end we do check for a processor being registered anyway.
+
+\permanent\protected\def\applyprocessor#tag%
+ {\def\currentprocessor{#tag}%
+ \ifcsname\??processorcheck\currentprocessor\endcsname
+ \expandafter\typo_processor_apply
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\def\typo_processor_apply
+ {\edef\p_state{\processorparameter\c!state}%
+ \ifx\p_state\v!stop
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\typo_processor_apply_indeed
+ \fi}
+
+\def\typo_processor_apply_indeed#content%
+ {\begingroup
+ \useprocessorstyleandcolor\c!style\c!color
+ \processorparameter\c!left
+ \processorparameter\c!command{#content}%
+ \processorparameter\c!right
+ \endgroup}
+
+%D \startbuffer
+%D \defineprocessor[first] [style=bold]
+%D \defineprocessor[last] [first] [color=red]
+%D
+%D \applyprocessor{first}{hans} \applyprocessor{last}{hagen}
+%D \stopbuffer
+%D
+%D \typebuffer \start \blank \getbuffer \blank \stop
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-rep.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-rep.mkxl
new file mode 100644
index 00000000000..6e0a9a22ce0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-rep.mkxl
@@ -0,0 +1,42 @@
+%D \module
+%D [ file=typo-rep,
+%D version=2010.05.23,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Stripping,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Stripping}
+
+%D Here we implement an experimental stripper. I might cook up a few more
+%D variants when there is real demand. It might even go away.
+%D
+%D \starttyping
+%D \definefontfeature[default][default][mode=node,formatters=strip]
+%D
+%D \setcharacterstripping[1]
+%D \stoptyping
+
+\unprotect
+
+\registerctxluafile{typo-rep}{}
+
+\definesystemattribute[stripping][public]
+
+\permanent\protected\def\setcharacterstripping[#1]%
+ {\clf_setcharacterstripping{#1}}
+
+\permanent\protected\def\resetcharacterstripping
+ {\c_attr_stripping\attributeunsetvalue}
+
+% maybe .. this might disappear, but is handy for testing
+
+\permanent\protected\def\forcecharacterstripping % secret command
+ {\ctxlua{fonts.handlers.otf.features.register("formatters",true)}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-rub.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-rub.lmt
new file mode 100644
index 00000000000..82aa3c269df
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-rub.lmt
@@ -0,0 +1,397 @@
+if not modules then modules = { } end modules ['typo-rub'] = {
+ version = 1.001,
+ comment = "companion to typo-rub.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: recycle slots better
+-- todo: hoffset
+-- todo: auto-increase line height
+-- todo: only hpack when start <> stop
+
+-- A typical bit of afternoon hackery ... with some breaks for watching
+-- Ghost-Note on youtube (Robert Searight and Nate Werth) ... which expands
+-- my to-be-had cd/dvd list again.
+
+local lpegmatch = lpeg.match
+local utfcharacters = utf.characters
+local setmetatableindex = table.setmetatableindex
+
+local variables = interfaces.variables
+local implement = interfaces.implement
+
+local texsetattribute = tex.setattribute
+
+local v_flushleft = variables.flushleft
+local v_middle = variables.middle
+local v_flushright = variables.flushright
+local v_yes = variables.yes
+local v_no = variables.no
+local v_auto = variables.auto
+
+local nuts = nodes.nuts
+
+local getid = nuts.getid
+local getsubtype = nuts.getsubtype
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getnext = nuts.getnext
+local setnext = nuts.setnext
+local getprev = nuts.getprev
+local setprev = nuts.setprev
+local setlink = nuts.setlink
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local setshift = nuts.setshift
+local getwidth = nuts.getwidth
+local setwidth = nuts.setwidth
+
+local hpack = nuts.hpack
+local insert_after = nuts.insert_after
+local takebox = nuts.takebox
+
+local nextlist = nuts.traversers.list
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local penalty_code = nodecodes.penalty
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local par_code = nodecodes.par
+local dir_code = nodecodes.dir
+
+local kerncodes = nodes.kerncodes
+local fontkern_code = kerncodes.font
+
+local nodepool = nuts.pool
+local new_kern = nodepool.kern
+
+local setprop = nuts.setprop
+local getprop = nuts.getprop
+
+local enableaction = nodes.tasks.enableaction
+
+local nofrubies = 0
+local rubylist = { }
+
+local a_ruby = attributes.private("ruby")
+
+local rubies = { }
+typesetters.rubies = rubies
+
+local trace_rubies = false trackers.register("typesetters.rubies",function(v) trace_rubies = v end)
+local report_rubies = logs.reporter("rubies")
+
+do
+
+ local shared = nil
+ local splitter = lpeg.tsplitat("|")
+
+ local function enable()
+ enableaction("processors","typesetters.rubies.check")
+ enableaction("shipouts", "typesetters.rubies.attach")
+ enable = false
+ end
+
+ local ctx_setruby = context.core.setruby
+
+ local function ruby(settings)
+ local base = settings.base
+ local comment = settings.comment
+ shared = settings
+ local c = lpegmatch(splitter,comment)
+ if #c == 1 then
+ ctx_setruby(base,comment)
+ if trace_rubies then
+ report_rubies("- %s -> %s",base,comment)
+ end
+ else
+ local i = 0
+ for b in utfcharacters(base) do
+ i = i + 1
+ local r = c[i]
+ if r then
+ ctx_setruby(b,r)
+ if trace_rubies then
+ report_rubies("%i: %s -> %s",i,b,r)
+ end
+ else
+ ctx_setruby(b,"")
+ if trace_rubies then
+ report_rubies("%i: %s",i,b)
+ end
+ end
+ end
+ end
+ if enable then
+ enable()
+ end
+ end
+
+ local function startruby(settings)
+ shared = settings
+ if enable then
+ enable()
+ end
+ end
+
+ implement {
+ name = "ruby",
+ actions = ruby,
+ arguments = {
+ {
+ { "align" },
+ { "stretch" },
+ { "hoffset", "dimension" },
+ { "voffset", "dimension" },
+ { "comment" },
+ { "base" },
+ }
+ },
+ }
+
+ implement {
+ name = "startruby",
+ actions = startruby,
+ arguments = {
+ {
+ { "align" },
+ { "stretch" },
+ { "hoffset", "dimension" },
+ { "voffset", "dimension" },
+ }
+ },
+ }
+
+ local function setruby(n,m)
+ nofrubies = nofrubies + 1
+ local r = takebox(n)
+ rubylist[nofrubies] = setmetatableindex({
+ text = r,
+ width = getwidth(r),
+ basewidth = 0,
+ start = false,
+ stop = false,
+ }, shared)
+ texsetattribute(a_ruby,nofrubies)
+ end
+
+ implement {
+ name = "setruby",
+ actions = setruby,
+ arguments = "integer",
+ }
+
+end
+
+function rubies.check(head)
+ local current = head
+ local start = nil
+ local stop = nil
+ local found = nil
+
+ local function flush(where)
+ local r = rubylist[found]
+ if r then
+ local prev = getprev(start)
+ local next = getnext(stop)
+ setprev(start)
+ setnext(stop)
+ local h = hpack(start)
+ if start == head then
+ head = h
+ else
+ setlink(prev,h)
+ end
+ setlink(h,next)
+ local bwidth = getwidth(h)
+ local rwidth = r.width
+ r.basewidth = bwidth
+ r.start = start
+ r.stop = stop
+ setprop(h,"ruby",found)
+ if rwidth > bwidth then
+ -- ruby is wider
+ setwidth(h,rwidth)
+ end
+ end
+ end
+
+ while current do
+ local nx = getnext(current)
+ local id = getid(current)
+ if id == glyph_code then
+ local a = getattr(current,a_ruby)
+ if not a then
+ if found then
+ flush("flush 1")
+ found = nil
+ end
+ elseif a == found then
+ stop = current
+ else
+ if found then
+ flush("flush 2")
+ end
+ found = a
+ start = current
+ stop = current
+ end
+ -- go on
+ elseif id == kern_code and getsubtype(current,fontkern_code) then
+ -- go on
+ elseif found and id == disc_code then
+ -- go on (todo: look into disc)
+ elseif found then
+ flush("flush 3")
+ found = nil
+ end
+ current = nx
+ end
+ if found then
+ flush("flush 4")
+ end
+ return head, true -- no need for true
+end
+
+local attach
+
+local function whatever(current,list)
+ local a = getprop(current,"ruby")
+ if a then
+ local ruby = rubylist[a]
+ local align = ruby.align or v_middle
+ local stretch = ruby.stretch or v_no
+ local hoffset = ruby.hoffset or 0
+ local voffset = ruby.voffset or 0
+ local start = ruby.start
+ local stop = ruby.stop
+ local text = ruby.text
+ local rwidth = ruby.width
+ local bwidth = ruby.basewidth
+ local delta = rwidth - bwidth
+ setwidth(text,0)
+ if voffset ~= 0 then
+ setshift(text,voffset)
+ end
+ -- center them
+ if delta > 0 then
+ -- ruby is wider
+ if stretch == v_yes then
+ setlink(text,start)
+ while start and start ~= stop do
+ local s = nodepool.stretch()
+ local n = getnext(start)
+ setlink(start,s,n)
+ start = n
+ end
+ text = hpack(text,rwidth,"exactly")
+ else
+ local left = new_kern(delta/2)
+ local right = new_kern(delta/2)
+ setlink(text,left,start)
+ setlink(stop,right)
+ end
+ setlist(current,text)
+ elseif delta < 0 then
+ -- ruby is narrower
+ if align == v_auto then
+ local l = true
+ local c = getprev(current)
+ while c do
+ local id = getid(c)
+ if id == glue_code or id == penalty_code or id == kern_code then
+ -- go on
+ elseif id == hlist_code and getwidth(c) == 0 then
+ -- go on
+ elseif id == whatsit_code or id == par_code or id == dir_code then
+ -- go on
+ else
+ l = false
+ break
+ end
+ c = getprev(c)
+ end
+ local r = true
+ local c = getnext(current)
+ while c do
+ local id = getid(c)
+ if id == glue_code or id == penalty_code or id == kern_code then
+ -- go on
+ elseif id == hlist_code and getwidth(c) == 0 then
+ -- go on
+ else
+ r = false
+ break
+ end
+ c = getnext(c)
+ end
+ if l and not r then
+ align = v_flushleft
+ elseif r and not l then
+ align = v_flushright
+ else
+ align = v_middle
+ end
+ end
+ if align == v_flushleft then
+ setlink(text,start)
+ setlist(current,text)
+ elseif align == v_flushright then
+ local left = new_kern(-delta)
+ local right = new_kern(delta)
+ setlink(left,text,right,start)
+ setlist(current,left)
+ else
+ local left = new_kern(-delta/2)
+ local right = new_kern(delta/2)
+ setlink(left,text,right,start)
+ setlist(current,left)
+ end
+ else
+ setlink(text,start)
+ setlist(current,text)
+ end
+ setprop(current,"ruby",false)
+ rubylist[a] = nil
+ elseif list then
+ attach(list)
+ end
+end
+
+attach = function(head)
+ for current, id, subtype, list in nextlist, head do
+ if id == hlist_code or id == vlist_code then
+ whatever(current,list)
+ end
+ end
+ return head
+end
+
+rubies.attach = attach
+
+-- for now there is no need to be compact
+
+-- local data = { }
+-- rubies.data = data
+--
+-- function rubies.define(settings)
+-- data[#data+1] = settings
+-- return #data
+-- end
+--
+-- implement {
+-- name = "defineruby",
+-- actions = { rubies.define, context },
+-- arguments = {
+-- {
+-- { "align" },
+-- { "stretch" },
+-- }
+-- }
+-- }
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-rub.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-rub.mkxl
new file mode 100644
index 00000000000..b4b0cbc5ee8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-rub.mkxl
@@ -0,0 +1,173 @@
+%D \module
+%D [ file=typo-rub,
+%D version=2016.10.10,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Rubies,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D Experimental and unfinished.
+
+% todo: distance
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Rubies}
+
+\unprotect
+
+\registerctxluafile{typo-rub}{autosuffix}
+
+\definesystemattribute[ruby][public]
+
+\installcorenamespace {ruby}
+\installcorenamespace {rubyanalyze}
+\installcorenamespace {rubyplacement}
+\installcorenamespace {rubynumber}
+
+\installcommandhandler \??ruby {ruby} \??ruby
+
+% \appendtoks
+% \clf_defineruby
+% align {\rubyparameter\c!align}
+% stretch {\rubyparameter\c!stretch}
+% hoffset \rubyparameter\c!hoffset
+% voffset \rubyparameter\c!voffset
+% \relax
+% \to \everydefineruby
+
+\permanent\tolerant\protected\def\ruby[#1]#:#2#3%
+ {\dontleavehmode
+ \begingroup
+ \enforced\let\ruby\noruby
+ \edef\currentruby{#1}%
+ \edef\p_location{\rubyparameter\c!location}%
+ \enforced\let|\relax
+ \ifcsname\??rubyanalyze\p_location\endcsname
+ \expandafter\lastnamedcs\else\expandafter\typo_ruby_analyze
+ \fi{#2}{#3}%
+ \endgroup}
+
+\permanent\tolerant\protected\def\noruby[#1]#:#2#3%
+ {#2}
+
+\def\typo_ruby_analyze#1#2%
+ {\clf_ruby
+ base {#1}%
+ comment {#2}
+ \relax}
+
+\setvalue{\??rubyanalyze\v!top}#1#2%
+ {\clf_ruby
+ align {\rubyparameter\c!align}%
+ stretch {\rubyparameter\c!stretch}%
+ hoffset \rubyparameter\c!hoffset
+ voffset \rubyparameter\c!voffset
+ base {#1}%
+ comment {#2}
+ \relax}
+
+\setvalue{\??rubyplacement\v!top}#1#2%
+ {\setbox\scratchbox\hbox\bgroup
+ \userubystyleandcolor\c!style\c!color
+ #2%
+ \egroup
+ \clf_setruby \scratchbox
+ \relax
+ #1}
+
+\setvalue{\??rubyplacement\v!right}#1#2%
+ {#1%
+ \edef\p_distance{\rubyparameter\c!distance}%
+ \ifempty\p_distance\orelse\ifx\p_distance\v!none\else\hskip\p_distance\fi
+ \begingroup
+ \userubystyleandcolor\c!style\c!color
+ \rubyparameter\c!left#2\rubyparameter\c!right
+ \endgroup}
+
+\setvalue{\??rubyplacement\v!left}#1#2%
+ {\begingroup
+ \userubystyleandcolor\c!style\c!color
+ \rubyparameter\c!left#2\rubyparameter\c!right
+ \endgroup
+ \edef\p_distance{\rubyparameter\c!distance}%
+ \ifempty\p_distance\orelse\ifx\p_distance\v!none\else\hskip\p_distance\fi
+ #1}
+
+\permanent\protected\def\setruby#1#2%
+ {\begingroup
+ \ifcsname\??rubyplacement\p_location\endcsname
+ \lastnamedcs{#1}{#2}%
+ \else
+ #1%
+ \fi
+ \endgroup}
+
+\permanent\tolerant\protected\def\startruby[#1]%
+ {\dontleavehmode
+ \begingroup
+ \enforced\aliased\startruby\begingroup
+ \edef\currentruby{#1}%
+ \clf_startruby
+ align {\rubyparameter\c!align}%
+ stretch {\rubyparameter\c!stretch}%
+ hoffset \rubyparameter\c!hoffset
+ voffset \rubyparameter\c!voffset
+ \relax}
+
+\permanent\protected\def\stopruby
+ {\endgroup}
+
+\setupruby
+ [\c!style=\txx,
+ \c!location=\v!top,
+ \c!left=(,
+ \c!right=),
+ \c!distance=\zeropoint, % \v!none means no skip at all so no break either
+ \c!hoffset=\zeropoint,
+ \c!voffset=-2\exheight]
+
+%D Experiment too:
+
+\permanent\protected\def\numberedruby[#1]#2%
+ {\doifnotcounter{\??rubynumber#1}{\definecounter[\??rubynumber#1]\setcounter[\??rubynumber#1][1]}%
+ \ruby[#1]{#2}{\convertedcounter[\??rubynumber#1]}%
+ \incrementcounter[\??rubynumber#1]%
+ \relax}
+
+\protect \endinput
+
+% \usemodule[art-01]\setupbodyfont[dejavu,12pt]
+%
+% \defineruby[auto] [align=auto,color=darkred]
+% \defineruby[left] [align=flushleft,color=darkred]
+% \defineruby[right] [align=flushright,color=darkred]
+% \defineruby[spread][stretch=yes]
+%
+% \showframe \showglyphs \showfontkerns \setupinterlinespace[22pt]
+%
+% \starttext
+%
+% \startbuffer
+% \dorecurse{20}{\ruby{XYZ}{a|bc|d} }\par
+% \dorecurse{20}{\ruby{PQR}{p|q|r} }\par
+% \dorecurse{20}{\ruby{XYZ}{1|22|333} }\par
+% \dorecurse{20}{\ruby{XYZ}{111|222|333} }\par
+% \dorecurse{20}{\ruby{XYZ}{foobar} }\par
+% \dorecurse{20}{\ruby{XYZ}{fooledbar} }\par
+% \dorecurse{20}{\ruby[spread]{XYZ}{fooledbar} }\par
+% \dorecurse{20}{\ruby{extremely}{wide} }\par
+% \dorecurse{20}{\ruby{wide}{extremely} }\par
+% stopbuffer
+%
+% \testfeatureonce{1}{\start \setupinterlinespace[16pt] \setupruby[location=none] \getbuffer \stop \page}
+% \testfeatureonce{1}{\start \setupinterlinespace[16pt] \setupruby[location=right] \getbuffer \stop \page}
+% \testfeatureonce{1}{\start \setupinterlinespace[28pt] \setupruby[align=auto,color=darkred] \getbuffer \stop \page}
+% \testfeatureonce{1}{\start \setupinterlinespace[28pt] \setupruby[align=flushleft,color=darkgreen] \getbuffer \stop \page}
+% \testfeatureonce{1}{\start \setupinterlinespace[28pt] \setupruby[align=flushright,color=darkblue] \getbuffer \stop \page}
+% \testfeatureonce{1}{\start \setupinterlinespace[28pt] \setupruby[align=middle,color=darkyellow] \getbuffer \stop \page}
+%
+% \stoptext
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-scr.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-scr.mkxl
new file mode 100644
index 00000000000..bb67228cdc0
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-scr.mkxl
@@ -0,0 +1,266 @@
+%D \module
+%D [ file=typo-scr,
+%D version=2012.01.23, % very old ... 1995.10.10 .. moved from core-mis
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Scripts,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Scripts}
+
+\unprotect
+
+%D I addition to the commands implemented here, we have alternative commands
+%D \type {shiftup} and \type {shiftdown} that can work across paragraphs.
+
+%D \macros
+%D {low, high, definelow, definehigh, setuplow, setuphigh}
+%D
+%D Although \TEX\ is pretty well aware of super- and subscripts, its mechanism
+%D is mainly tuned for math mode. The next few commands take care of script
+%D texts both modes.
+%D
+%D \startbuffer
+%D The higher\high{one goes} the lower\low{one drops}, or\lohi{yes}{no}?
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \getbuffer
+%D
+%D Note the different placement of \type {\lohi}, where we need a bit more
+%D space. The implementation looks a bit fuzzy, since some \type {\fontdimen}'s
+%D are involved to determine the optimal placement.
+
+\installcorenamespace {low}
+\installcorenamespace {high}
+
+\installcommandhandler \??low {low} \??low
+\installcommandhandler \??high {high} \??high
+
+\setuplow [\c!style=\tx,\c!distance=\zeropoint,\c!down=.48\exheight] % historical
+\setuphigh[\c!style=\tx,\c!distance=\zeropoint,\c!up =.86\exheight] % values
+
+\appendtoks \frozen\protected\edefcsname\currentlow \endcsname{\typo_scripts_low [\currentlow ]}\to \everydefinelow
+\appendtoks \frozen\protected\edefcsname\currenthigh\endcsname{\typo_scripts_high[\currenthigh]}\to \everydefinehigh
+
+\protected\def\typo_scripts_low[#1]#2%
+ {\dontleavehmode
+ \begingroup
+ \edef\currentlow{#1}%
+ \kern\lowparameter\c!distance\relax
+ \setbox\scratchbox\runninghbox\bgroup
+ \lower\lowparameter\c!down\hbox\bgroup
+ \ifempty\fontsize
+ \ifmmode
+ \mr % no color yet
+ \else
+ \uselowstyleandcolor\c!style\c!color
+ \fi
+ \else
+ \uselowstyleandcolor\c!style\c!color
+ \fi
+ \dostarttagged\t!sub\currentlow
+ #2%
+ \dostoptagged
+ \egroup
+ \egroup
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox
+ \endgroup}
+
+\protected\def\typo_scripts_high[#1]#2%
+ {\dontleavehmode
+ \begingroup
+ \edef\currenthigh{#1}%
+ \kern\highparameter\c!distance\relax
+ \setbox\scratchbox\runninghbox\bgroup
+ \raise\highparameter\c!up\hbox\bgroup
+ \ifempty\fontsize
+ \ifmmode
+ \mr % no color yet
+ \else
+ \usehighstyleandcolor\c!style\c!color
+ \fi
+ \else
+ \usehighstyleandcolor\c!style\c!color
+ \fi
+ \dostarttagged\t!sup\currenthigh
+ #2%
+ \dostoptagged
+ \egroup
+ \egroup
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox
+ \endgroup}
+
+\permanent\protected\def\low {\typo_scripts_low []}
+\permanent\protected\def\high{\typo_scripts_high[]}
+
+%D \macros
+%D {lohi, hilo, definelohi, setuplohi}
+%D
+%D You can provide an optional keyword \type {left}, in which case the super and
+%D subscripts will be aligned in a way that permits placement at the left of a word
+%D (which means that it will be right aligned).
+%D
+%D \startbuffer
+%D \lohi {aha} {ah} test \lohi {aha} {ah} test
+%D \lohi [left] {aha} {ah} test \lohi [left] {aha} {ah} test
+%D \lohi {aha} {ah} test \lohi {aha} {ah} test
+%D \lohi [left] {aha} {ah}test \lohi [left] {aha} {ah} test
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \getbuffer
+
+\installcorenamespace {lowhigh}
+
+\installcommandhandler \??lowhigh {lowhigh} \??lowhigh
+
+\setuplowhigh
+ [\c!style=\tx,
+ \c!distance=.1\exheight, % these are
+ \c!up=.96\exheight, % historical
+ \c!down=.58\exheight] % values
+
+\appendtoks
+ \frozen\setuevalue\currentlowhigh{\typo_scripts_lowhigh[\currentlowhigh]}%
+\to \everydefinelowhigh
+
+\tolerant\protected\def\typo_scripts_lowhigh[#1]#*[#2]#:#3#4% todo: align .. [#1] is compatible hack
+ {\dontleavehmode
+ \runninghbox\bgroup
+ \edef\currentlowhigh{#1}%
+ \dostarttagged\t!subsup\currentlowhigh
+ \scratchdimen\lowhighparameter\c!distance\relax
+ \setbox\plusfour\hpack{\typo_scripts_lowhigh_low_high\lower\c!down\t!sub{#3}}%
+ \setbox\plussix \hpack{\typo_scripts_lowhigh_low_high\raise\c!up \t!sup{#4}}%
+ \edef\p_align{#2}%
+ \ifx\p_align\v!left
+ \ifdim\wd\plusfour<\wd\plussix
+ \setbox\plusfour\hpack to \wd\plussix {\hss\box\plusfour}%
+ \else
+ \setbox\plussix \hpack to \wd\plusfour{\hss\box\plussix }%
+ \fi
+ \fi
+ \ifdim\wd\plusfour<\wd\plussix
+ \wd\plusfour\zeropoint
+ \box\plusfour
+ \box\plussix
+ \else
+ \wd\plussix\zeropoint
+ \box\plussix
+ \box\plusfour
+ \fi
+ \dostoptagged
+ \egroup}
+
+\def\typo_scripts_lowhigh_low_high#1#2#3#4%
+ {\kern\scratchdimen
+ \setbox\scratchbox\hpack\bgroup
+ #1\lowhighparameter#2\hbox\bgroup
+ \ifempty\fontsize
+ \ifmmode
+ \mr % no color yet
+ \else
+ \uselowhighstyleandcolor\c!style\c!color
+ \fi
+ \else
+ \uselowhighstyleandcolor\c!style\c!color
+ \fi
+ \dostarttagged#3\empty
+ #4%
+ \dostoptagged
+ \egroup
+ \egroup
+ \ht\scratchbox\strutht
+ \dp\scratchbox\strutdp
+ \box\scratchbox}
+
+\permanent\tolerant\protected\def\typo_scripts_highlow[#1]#*[#2]#:#3#4%
+ {\typo_scripts_lowhigh[#1][#2]{#4}{#3}}
+
+\permanent\protected\def\lohi{\typo_scripts_lowhigh[]}
+\permanent\protected\def\hilo{\typo_scripts_highlow[]}
+
+%D \macros
+%D {lowmidhigh, definelowmidhigh, setuplowmidhigh}
+%D
+%D The previous command originally didn't have definers. These were introduced when
+%D the next showed up:
+%D
+%D \startbuffer
+%D \definelow [MyLow] [style=\txx]
+%D \definehigh [MyHigh] [style=\txx]
+%D \definelowhigh [MyLoHi] [style=\txx]
+%D \definelowmidhigh[MyLoMiHi][style=\txx]
+%D
+%D We have
+%D \ruledhbox{\low {L}} and \ruledhbox{\MyLow {L}} and
+%D \ruledhbox{\high {H}} and \ruledhbox{\MyHigh {H}} and
+%D \ruledhbox{\lohi {L}{H}} and \ruledhbox{\MyLoHi {L}{H}} and
+%D \ruledhbox{\lomihi{L}{M}{H}} and \ruledhbox{\MyLoMiHi{L}{M}{H}}.
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\installcorenamespace {lowmidhigh}
+
+\installcommandhandler \??lowmidhigh {lowmidhigh} \??lowmidhigh
+
+\setuplowmidhigh
+ [\c!style=\tx,
+ \c!up=.8\struthtdp,
+ \c!down=.8\struthtdp]
+
+\appendtoks
+ \frozen\setuevalue\currentlowmidhigh{\typo_scripts_lowmidhigh[\currentlowmidhigh]}%
+\to \everydefinelowmidhigh
+
+\protected\def\typo_scripts_lowmidhigh[#1]#2#3#4%
+ {\dontleavehmode
+ \runninghbox\bgroup
+ \edef\currentlowmidhigh{#1}%
+ \dostarttagged\t!subsup\currentlowmidhigh
+ \uselowmidhighstyleandcolor\c!style\c!color
+ \setstrut
+ \setbox\plustwo \hbox{\strut\dostarttagged\t!sub\empty#2\dostoptagged}%
+ \setbox\plusfour\hbox{\strut\dostarttagged\t!mid\empty#3\dostoptagged}% inefficient
+ \setbox\plussix \hbox{\strut\dostarttagged\t!sup\empty#4\dostoptagged}%
+ \scratchdimen \wd
+ \ifdim\wd\plustwo>\wd\plusfour
+ \ifdim\wd\plustwo>\wd\plussix
+ \plustwo
+ \else
+ \plussix
+ \fi
+ \else
+ \ifdim\wd\plusfour>\wd\plussix
+ \plusfour
+ \else
+ \plussix
+ \fi
+ \fi
+ \relax
+ \setbox\plustwo \hpack to \scratchdimen{\hss\lower\lowmidhighparameter\c!down\box\plustwo \hss}%
+ \setbox\plusfour\hpack to \scratchdimen{\hss \box\plusfour\hss}%
+ \setbox\plussix \hpack to \scratchdimen{\hss\raise\lowmidhighparameter\c!up \box\plussix \hss}%
+ \wd\plustwo \zeropoint
+ \wd\plusfour\zeropoint
+ \box\plusfour
+ \box\plustwo
+ \box\plussix
+ \dostoptagged
+ \egroup}
+
+\permanent\protected\def\lomihi {\typo_scripts_lowmidhigh[]}
+\permanent\protected\def\himilo#1#2#3{\typo_scripts_lowmidhigh[]{#3}{#2}{#1}}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-shp.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-shp.lmt
new file mode 100644
index 00000000000..30719c40e28
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-shp.lmt
@@ -0,0 +1,267 @@
+if not modules then modules = { } end modules ['meta-imp-txt'] = {
+ version = 1.001,
+ comment = "companion to meta-imp-txt.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+local setmetatableindex = table.setmetatableindex
+local settings_to_array = utilities.parsers.settings_to_array
+
+local texget = tex.get
+local texset = tex.set
+local texgetcount = tex.getcount
+local texgetglue = tex.getglue
+
+local expandmacro = token.expand_macro
+
+local implement = interfaces.implement
+
+local scan = mp.scan
+local scannumeric = scan.numeric
+local scaninteger = scan.integer
+local scanboolean = scan.boolean
+local scanstring = scan.string
+
+local bpfactor = number.dimenfactors.bp
+
+local context = context
+
+local parshapes = { }
+local properties = { }
+
+metapost.registerscript("setparshapeproperty", function()
+ local k = scanstring()
+ if k == "line" then
+ local entry = properties.shape[scannumeric()]
+ local indent = scannumeric() / bpfactor
+ local width = scannumeric() / bpfactor
+ entry[1] = indent
+ entry[2] = width
+ elseif k == "lines" then
+ properties.lines = scaninteger()
+ properties.shape = setmetatableindex(function(t,k)
+ local v = { 0, properties.width or 0 }
+ t[k] = v
+ return v
+ end)
+ elseif k == "first" then
+ properties[k] = scanboolean()
+ elseif k == "inspect" then
+ inspect(properties)
+ else
+ properties[k] = scannumeric() / bpfactor
+ end
+end)
+
+implement {
+ name = "setparagraphmetashape",
+ public = true,
+ protected = true,
+ arguments = { "optional", "optional" }, -- array
+ actions = function(list,options)
+ if list and list ~= "" then
+ list = settings_to_array(list) -- array
+ options = settings_to_array(options) -- array
+ if #list > 0 then
+ parshapes = { }
+ properties = { }
+ for i=1,#list do
+ properties = { }
+ parshapes[i] = properties
+ expandmacro("spac_shapes_calculate","{"..list[i].."}")
+ end
+ local t, n = { }, 0
+ for i=1,#parshapes do
+ local p = parshapes[i]
+ local s = p.shape
+ if s then
+ for i=1,(p.lines or #s) do
+ n = n + 1
+ t[n] = s[i]
+ end
+ end
+ end
+ if n > 0 then
+ for i=1,#options do
+ t[options[i]] = true
+ end
+ texset("parshape",t)
+ end
+ end
+ end
+ end
+}
+
+-- implement {
+-- name = "resetparagraphmetashape",
+-- public = true,
+-- protected = true,
+-- actions = function(list)
+-- parshapes = { }
+-- properties = { }
+-- end
+-- }
+
+implement {
+ name = "getshapeparameter",
+ public = true,
+ arguments = "string",
+ actions = function(name)
+ local index = texgetcount("shapetextindex")
+ local value = parshapes[index][name]
+ if type(value) == "boolean" then
+ value = value and 1 or 0
+ end
+ context(value or 0) -- so the first check, for "lines" is always ok
+ end
+}
+
+-- Another experiment: continuing parshapes with alternative definitions:
+--
+-- left d | right d | left d right d | both d | left d hsize d |
+-- copy n | reset | repeat | done
+
+do
+
+ local scanners = tokens.scanners
+ local scanword = scanners.word
+ local scandimen = scanners.dimen
+ local scanstring = scanners.string
+ local scancardinal = scanners.cardinal
+
+ implement {
+ name = "setparagraphshape",
+ protected = true,
+ actions = function()
+ local t = { }
+ local n = 0
+ local h = texget("hsize")
+ local a = 0
+ while true do
+ local key = scanword()
+ ::AGAIN::
+ if key == "left" then
+ local l = scandimen()
+ key = scanword()
+ if key == "right" then
+ n = n + 1 ; t[n] = { l, a + h - l - scandimen() }
+ elseif key == "hsize" then
+ n = n + 1 ; t[n] = { l, a + scandimen() }
+ else
+ n = n + 1 ; t[n] = { l, a + h }
+ goto AGAIN
+ end
+ elseif key == "right" then
+ n = n + 1 ; t[n] = { 0, a + h - scandimen() }
+ elseif key == "both" then
+ local b = scandimen()
+ n = n + 1 ; t[n] = { b, a + h - b - b }
+ elseif key == "copy" then
+ local c = scancardinal()
+ for i=1,c do
+ local m = n + 1
+ t[m] = t[n]
+ n = m
+ end
+ elseif key == "done" then
+ -- in case the user ended with "done"
+ scanword()
+ break
+ elseif key == "metapost" then
+ local list = settings_to_array(scanstring()) -- array
+ properties = { }
+ parshapes = { }
+ for i=1,#list do
+ properties = { }
+ parshapes[i] = properties
+ expandmacro("spac_shapes_calculate","{"..list[i].."}")
+ end
+ for i=1,#parshapes do
+ local p = parshapes[i]
+ local s = p.shape
+ if s then
+ for i=1,(p.lines or #s) do
+ local si = s[i]
+ n = n + 1 ; t[n] = { si[1], a + si[2] }
+ end
+ end
+ end
+ elseif key == "repeat" then
+ t["repeat"] = true
+ elseif key == "delete" then
+ local c = scancardinal()
+ for i=1,c do
+ if n > 0 then
+ t[n] = nil
+ n = n - 1
+ else
+ break
+ end
+ end
+ elseif key == "reset" then
+ n = n + 1 ; t[n] = { 0, a + h }
+ break
+ elseif key == "absolute" then
+ local s = scanword()
+ local l = texgetglue("leftskip")
+ local r = texgetglue("rightskip")
+ if s == "left" then
+ a = l
+ elseif s == "right" then
+ a = r
+ elseif s == "both" then
+ a = l + r
+ else
+ a = l + r
+ goto AGAIN
+ end
+ else
+ logs.report("system","bad key %a in paragraphshape",key)
+ break
+ end
+ end
+ texset("parshape",t)
+ end,
+ }
+
+ local NC = context.NC
+ local NR = context.NR
+ local VL = context.VL
+
+ implement {
+ name = "showparagraphshape",
+ protected = true,
+ public = true,
+ actions = function()
+ local p = texget("parshape")
+ if p then
+ -- only english interface (for now)
+ context.inleftmargin(
+ {
+ align = "flushright",
+ strut = "no",
+ width = "0pt",
+ -- voffset = "-\\lineheight"
+ }, function()
+ context.starttabulate {
+ before = "",
+ after = "",
+ unit = "2pt",
+ rulethickness = ".1pt",
+ format = "|rb{\\smallinfofont}|lb{\\smallinfofont}|"
+ }
+ for i=1,#p do
+ NC() context("%P",p[i][1])
+ VL() context("%P",p[i][2])
+ NC() NR()
+ end
+ context.stoptabulate()
+ end
+ )
+ end
+ end
+ }
+
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-shp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-shp.mkxl
new file mode 100644
index 00000000000..b74003068fa
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-shp.mkxl
@@ -0,0 +1,130 @@
+%D \module
+%D [ file=typo-shp,
+%D version=2021.02.27, % was meta-txt / meta-imp-txt / 2000.07.06
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Paragraph Shapes,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D I finally decided to move some of the code written in 2000 for the
+%D \METAFUN\ manual to its own core module. Of course it got adapted
+%D to the way \LMTX\ does things.
+
+\writestatus{loading}{ConTeXt Spacing Macros / Paragraph Shapes}
+
+\registerctxluafile{typo-shp}{autosuffix}
+
+\unprotect
+
+%D Something new (experimental and evolving):
+%D
+%D \starttyping
+%D \parshape
+%D 3
+%D options 1 % repeat
+%D 0cm 10cm 2cm 8cm 4cm 6cm
+%D lots of text
+%D \stoptyping
+%D
+%D \starttyping
+%D \parshape 4 5mm 125mm 0mm 120mm 5mm 125mm 0mm 120mm
+%D \pushparagraphtweak {repeat}
+%D verse line 1\crlf
+%D verse line 2\crlf
+%D verse line 3\crlf
+%D verse line 4\par
+%D etc
+%D \popparagraphtweak
+%D \stoptyping
+
+%D But we wrap this in a more abstract interface:
+
+\installcorenamespace {parshapes}
+
+\aliased\let\stopparagraphshape\relax
+
+\permanent\protected\def\startparagraphshape[#1]#2\stopparagraphshape
+ {\gdefcsname\??parshapes#1\endcsname{#2}} % global
+
+\permanent\protected\def\rawparagraphshape#1%
+ {\begincsname\??parshapes#1\endcsname}
+
+\permanent\protected\def\setparagraphshape[#1]%
+ {\ifcsname\??parshapes#1\endcsname
+ \expandafter\clf_setparagraphshape\lastnamedcs done\relax
+ \fi}
+
+\def\spac_shapes_calculate#1% called locally in \LUA
+ {\startMPcalculation \includeMPgraphic{#1} \stopMPcalculation}
+
+\permanent\protected\tolerant\def\startshapedparagraph[#1]% no grouping
+ {\begingroup
+ \getdummyparameters[\c!method=,\c!list=,\c!mp=,\c!repeat=,#1]%
+ \edef\p_mp {\dummyparameter\c!mp}%
+ \edef\p_repeat{\dummyparameter\c!repeat}%
+ \setlocalhsize
+ \normalexpanded
+ {\endgroup
+ \ifempty\p_mp
+ \setparagraphshape[\dummyparameter\c!list]%
+ \else
+ \setparagraphmetashape[\dummyparameter\c!mp][\ifx\p_repeat\v!yes repeat\fi]%
+ \fi
+ \pushparagraphtweak{\dummyparameter\c!method}\relax}}
+
+\permanent\protected\def\stopshapedparagraph
+ {\popparagraphtweak}
+
+%D As it is not much code we now put it here:
+
+\newbox \shapetextbox
+\newcount\shapetextindex
+
+\permanent\protected\def\startshapetext[#1]%
+ {\begingroup
+ \global\shapetextindex\zerocount
+ \global\setbox\shapetextbox\vbox\bgroup
+ \setparagraphmetashape[#1]%
+ \forgetall
+ \dontcomplain
+ \setuptolerance[\v!verytolerant,\v!stretch]% default
+ % \setuplayout[\c!grid=\v!yes]% goes wrong, we need a local one
+ \pushparagraphtweak {shift}}
+
+\permanent\protected\def\stopshapetext
+ {\popparagraphtweak
+ \egroup
+ \endgroup}
+
+\permanent\protected\def\getshapetext
+ {\vbox\bgroup
+ \forgetall
+ \dontcomplain
+ \global\advance\shapetextindex\plusone
+ \scratchcounter\getshapeparameter{lines}\relax
+ \ifnum\scratchcounter>\zerocount
+ \scratchwidth \getshapeparameter{width}\scaledpoint\relax
+ \scratchheight \getshapeparameter{height}\scaledpoint\relax
+ \setbox\scratchbox\vpack to \scratchheight
+ {\splittopskip\strutheight
+ \vskip\dimexpr\getshapeparameter{voffset}\scaledpoint\relax
+ \ifcase\numexpr\getshapeparameter{first}\relax\else
+ \vskip\lineheight
+ \fi
+ \hskip\dimexpr\getshapeparameter{hoffset}\scaledpoint\relax
+ \hpack{\vsplit\shapetextbox to \scratchcounter\lineheight}}%
+ \wd\scratchbox\scratchwidth
+ \ht\scratchbox\scratchheight
+ \dp\scratchbox\zeropoint
+ \box\scratchbox
+ \else
+ % what now
+ \fi
+ \egroup}
+
+\protect
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-spa.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-spa.lmt
new file mode 100644
index 00000000000..84dd0696d6c
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-spa.lmt
@@ -0,0 +1,255 @@
+if not modules then modules = { } end modules ['typo-spa'] = {
+ version = 1.001,
+ comment = "companion to typo-spa.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next, type = next, type
+
+local trace_spacing = false trackers.register("typesetters.spacing", function(v) trace_spacing = v end)
+
+local report_spacing = logs.reporter("typesetting","spacing")
+
+local nodes, fonts, node = nodes, fonts, node
+
+local fonthashes = fonts.hashes
+local quaddata = fonthashes.quads
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local v_reset = interfaces.variables.reset
+
+local nuts = nodes.nuts
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getattr = nuts.getattr
+local isglyph = nuts.isglyph
+
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+local end_of_math = nuts.end_of_math
+local unset_attributes = nuts.unset_attributes
+local find_attribute = nuts.find_attribute
+
+local nodepool = nuts.pool
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+
+local nodecodes = nodes.nodecodes
+local math_code = nodecodes.math
+
+local somespace = nodes.somespace
+local somepenalty = nodes.somepenalty
+
+local enableaction = nodes.tasks.enableaction
+
+typesetters = typesetters or { }
+local typesetters = typesetters
+
+typesetters.spacings = typesetters.spacings or { }
+local spacings = typesetters.spacings
+
+spacings.mapping = spacings.mapping or { }
+spacings.numbers = spacings.numbers or { }
+
+local a_spacings = attributes.private("spacing")
+
+storage.register("typesetters/spacings/mapping", spacings.mapping, "typesetters.spacings.mapping")
+
+local mapping = spacings.mapping
+local numbers = spacings.numbers
+
+for i=1,#mapping do
+ local m = mapping[i]
+ numbers[m.name] = m
+end
+
+-- todo cache lastattr
+
+function spacings.handler(head)
+ local _, start = find_attribute(head, a_spacings)
+ if start then
+ local done = false
+ -- head is always begin of par (whatsit), so we have at least two prev nodes
+ -- penalty followed by glue
+ while start do
+ local char, id = isglyph(start)
+ if char then
+ local attr = getattr(start,a_spacings)
+ if attr and attr > 0 then
+ local data = mapping[attr]
+ if data then
+ local map = data.characters[char]
+ if map then
+ local font = id
+ local left = map.left
+ local right = map.right
+ local alternative = map.alternative
+ local quad = quaddata[font]
+ local prev = getprev(start)
+ if left and left ~= 0 and prev then
+ local ok = false
+ local prevprev = getprev(prev)
+ if alternative == 1 then
+ local somespace = somespace(prev,true)
+ if somespace then
+ local somepenalty = somepenalty(prevprev,10000)
+ if somepenalty then
+ if trace_spacing then
+ report_spacing("removing penalty and space before %C (left)",char)
+ end
+ head = remove_node(head,prev,true)
+ head = remove_node(head,prevprev,true)
+ else
+ if trace_spacing then
+ report_spacing("removing space before %C (left)",char)
+ end
+ head = remove_node(head,prev,true)
+ end
+ end
+ ok = true
+ else
+ ok = not (somespace(prev,true) and somepenalty(prevprev,true)) or somespace(prev,true)
+ end
+ if ok then
+ if trace_spacing then
+ report_spacing("inserting penalty and space before %C (left)",char)
+ end
+ insert_node_before(head,start,new_penalty(10000))
+ insert_node_before(head,start,new_glue(left*quad))
+ end
+ end
+ local next = getnext(start)
+ if right and right ~= 0 and next then
+ local ok = false
+ local nextnext = getnext(next)
+ if alternative == 1 then
+ local somepenalty = somepenalty(next,10000)
+ if somepenalty then
+ local somespace = somespace(nextnext,true)
+ if somespace then
+ if trace_spacing then
+ report_spacing("removing penalty and space after %C right",char)
+ end
+ head = remove_node(head,next,true)
+ head = remove_node(head,nextnext,true)
+ end
+ else
+ local somespace = somespace(next,true)
+ if somespace then
+ if trace_spacing then
+ report_spacing("removing space after %C (right)", char)
+ end
+ head = remove_node(head,next,true)
+ end
+ end
+ ok = true
+ else
+ ok = not (somepenalty(next,10000) and somespace(nextnext,true)) or somespace(next,true)
+ end
+ if ok then
+ if trace_spacing then
+ report_spacing("inserting penalty and space after %C (right)",char)
+ end
+ insert_node_after(head,start,new_glue(right*quad))
+ insert_node_after(head,start,new_penalty(10000))
+ end
+ end
+ end
+ end
+ done = true
+ end
+ elseif id == math_code then
+ start = end_of_math(start) -- weird, can return nil .. no math end?
+ end
+ if start then
+ start = getnext(start)
+ end
+ end
+ if done then
+ -- unset_attributes(a_spacings,head)
+ end
+ end
+ return head
+end
+
+local enabled = false
+
+function spacings.define(name)
+ local data = numbers[name]
+ if data then
+ -- error
+ else
+ local number = #mapping + 1
+ local data = {
+ name = name,
+ number = number,
+ characters = { },
+ }
+ mapping[number] = data
+ numbers[name] = data
+ end
+end
+
+function spacings.setup(name,char,settings)
+ local data = numbers[name]
+ if not data then
+ -- error
+ else
+ data.characters[char] = settings
+ end
+end
+
+function spacings.set(name)
+ local n = unsetvalue
+ if name ~= v_reset then
+ local data = numbers[name]
+ if data then
+ if not enabled then
+ enableaction("processors","typesetters.spacings.handler")
+ enabled = true
+ end
+ n = data.number or unsetvalue
+ end
+ end
+ texsetattribute(a_spacings,n)
+end
+
+function spacings.reset()
+ texsetattribute(a_spacings,unsetvalue)
+end
+
+-- interface
+
+local implement = interfaces.implement
+
+implement {
+ name = "definecharacterspacing",
+ actions = spacings.define,
+ arguments = "string"
+}
+
+implement {
+ name = "setupcharacterspacing",
+ actions = spacings.setup,
+ arguments = {
+ "string",
+ "integer",
+ {
+ { "left", "number" },
+ { "right", "number" },
+ { "alternative", "integer" },
+ }
+ }
+}
+
+implement {
+ name = "setcharacterspacing",
+ actions = spacings.set,
+ arguments = "string"
+}
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-spa.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-spa.mkxl
new file mode 100644
index 00000000000..42825b02272
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-spa.mkxl
@@ -0,0 +1,78 @@
+%D \module
+%D [ file=typo-spa,
+%D version=2009.03.27, % code moved from cors-spa.mkiv
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Spacing,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Spacing}
+
+\unprotect
+
+\registerctxluafile{typo-spa}{autosuffix}
+
+\definesystemattribute[spacing][public]
+
+% experimental spacing
+%
+% test: oeps {\setcharacterspacing[frenchpunctuation]x: xx \bfd x: xx} oeps: test
+%
+% todo: page | text => pagebody or text only
+
+\installcorenamespace{characterspacing}
+
+\installsetuponlycommandhandler \??characterspacing {_p_characterspacing} % private
+
+\setup_p_characterspacing
+ [\c!left=0,
+ \c!right=0,
+ \c!alternative=0]
+
+\permanent\protected\def\definecharacterspacing[#1]%
+ {\clf_definecharacterspacing{#1}}
+
+\permanent\tolerant\protected\def\setupcharacterspacing[#1]#*[#2]#*[#3]% todo: #2 list
+ {\begingroup
+ \setupcurrent_p_characterspacing[#3]%
+ \clf_setupcharacterspacing
+ {#1}%
+ \numexpr#2\relax
+ {%
+ left \direct_p_characterspacingparameter\c!left\space
+ right \direct_p_characterspacingparameter\c!right\space
+ alternative \direct_p_characterspacingparameter\c!alternative
+ }%
+ \relax
+ \endgroup}
+
+\permanent\protected\def\setcharacterspacing[#1]% we can store the attribute if we want speed
+ {\clf_setcharacterspacing{#1}}
+
+\permanent\protected\def\resetcharacterspacing % fast one
+ {\c_attr_spacing\attributeunsetvalue}
+
+\appendtoks
+ \resetcharacterspacing
+\to \everyresettypesetting
+
+% \setcharacterspacing[frenchpunctuation]
+% «\type{bla}»\crlf « \type{bla}»\crlf
+% «bla »\crlf « bla»\crlf « bla »\crlf
+% bla: bla\crlf bla : bla
+
+\definecharacterspacing [frenchpunctuation] % name may change / unit is em
+
+\setupcharacterspacing [frenchpunctuation] ["003A] [\c!left =.25,\c!alternative=1] % : % strip preceding space(char)
+\setupcharacterspacing [frenchpunctuation] ["003B] [\c!left =.25,\c!alternative=1] % ; % strip preceding space(char)
+\setupcharacterspacing [frenchpunctuation] ["003F] [\c!left =.25,\c!alternative=1] % ? % strip preceding space(char)
+\setupcharacterspacing [frenchpunctuation] ["0021] [\c!left =.25,\c!alternative=1] % ! % strip preceding space(char)
+\setupcharacterspacing [frenchpunctuation] ["00AB] [\c!right=.25,\c!alternative=1] % guillemotleft/leftguillemot % strip following space(char)
+\setupcharacterspacing [frenchpunctuation] ["00BB] [\c!left =.25,\c!alternative=1] % guillemotright/rightguillemot % strip preceding space(char)
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-sus.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-sus.lmt
new file mode 100644
index 00000000000..2c6d9ea69c9
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-sus.lmt
@@ -0,0 +1,308 @@
+if not modules then modules = { } end modules ['typo-sus'] = {
+ version = 1.001,
+ comment = "companion to typo-sus.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next = next
+
+local punctuation = {
+ po = true,
+}
+
+local openquote = {
+ ps = true,
+ pi = true,
+}
+
+local closequote = {
+ pe = true,
+ pf = true,
+}
+
+local weird = {
+ lm = true,
+ no = true,
+}
+
+local categories = characters.categories
+
+local nodecodes = nodes.nodecodes
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local penalty_code = nodecodes.penalty
+local glue_code = nodecodes.glue
+local math_code = nodecodes.math
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local nuts = nodes.nuts
+
+local nextnode = nuts.traversers.node
+
+local getid = nuts.getid
+local getprev = nuts.getprev
+local getnext = nuts.getnext
+local getattr = nuts.getattr
+local getfont = nuts.getfont
+local getlist = nuts.getlist
+local getkern = nuts.getkern
+local getpenalty = nuts.getpenalty
+local getwidth = nuts.getwidth
+local getwhd = nuts.getwhd
+local isglyph = nuts.isglyph
+
+local setattr = nuts.setattr
+local setlist = nuts.setlist
+
+local setcolor = nodes.tracers.colors.set
+local insert_before = nuts.insert_before
+local insert_after = nuts.insert_after
+local end_of_math = nuts.end_of_math
+
+local nodepool = nuts.pool
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+local new_hlist = nodepool.hlist
+----- new_penalty = nodepool.penalty
+
+local a_characters = attributes.private("characters")
+local a_suspecting = attributes.private('suspecting')
+local a_suspect = attributes.private('suspect')
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+local enabled = false
+
+local enableaction = nodes.tasks.enableaction
+
+local threshold = 65536 / 4
+
+local function special(n)
+ if n then
+ local id = getid(n)
+ if id == kern_code then
+ return getkern(n) < threshold
+ elseif id == penalty_code then
+ return true
+ elseif id == glue_code then
+ return getwidth(n) < threshold
+ elseif id == hlist_code then
+ return getwidth(n) < threshold
+ end
+ else
+ return false
+ end
+end
+
+local function goback(current)
+ local prev = getprev(current)
+ while prev and special(prev) do
+ prev = getprev(prev)
+ end
+ if prev then
+ return prev, getid(prev)
+ end
+end
+
+local function goforward(current)
+ local next = getnext(current)
+ while next and special(next) do
+ next = getnext(next)
+ end
+ if next then
+ return next, getid(next)
+ end
+end
+
+-- We can cache rules if needed but there are not that many.
+
+local function mark(head,current,id,color)
+ if id == glue_code then
+ -- the glue can have stretch and/or shrink so the rule can overlap with the
+ -- following glyph .. no big deal as that one then sits on top of the rule
+ local width = getwidth(current)
+ local rule = new_rule(width)
+ local kern = new_kern(-width)
+ head = insert_before(head,current,rule)
+ head = insert_before(head,current,kern)
+ setcolor(rule,color)
+ -- elseif id == kern_code then
+ -- local width = getkern(current)
+ -- local rule = new_rule(width)
+ -- local kern = new_kern(-width)
+ -- head = insert_before(head,current,rule)
+ -- head = insert_before(head,current,kern)
+ -- setcolor(rule,color)
+ else
+ local width, height, depth = getwhd(current)
+ local extra = fonts.hashes.xheights[getfont(current)] / 2
+ local rule = new_rule(width,height+extra,depth+extra)
+ local hlist = new_hlist(rule)
+ head = insert_before(head,current,hlist)
+ setcolor(rule,color)
+ setcolor(current,"white")
+ end
+ return head, current
+end
+
+-- we can cache the font and skip ahead to next but it doesn't
+-- save enough time and it makes the code looks bad too ... after
+-- all, we seldom use this
+
+local colors = {
+ "darkred",
+ "darkgreen",
+ "darkblue",
+ "darkcyan",
+ "darkmagenta",
+ "darkyellow",
+ "darkgray",
+ "orange",
+}
+
+local found = 0
+
+function typesetters.marksuspects(head)
+ local current = head
+ local lastdone = nil
+ while current do
+ if getattr(current,a_suspecting) then
+ local char, id = isglyph(current)
+ if char then
+ local code = categories[char]
+ local done = false
+ if punctuation[code] then
+ local prev, pid = goback(current)
+ if prev and pid == glue_code then
+ done = 3 -- darkblue
+ elseif prev and pid == math_code then
+ done = 3 -- darkblue
+ else
+ local next, nid = goforward(current)
+ if next and nid ~= glue_code then
+ done = 3 -- darkblue
+ end
+ end
+ elseif openquote[code] then
+ local next, nid = goforward(current)
+ if next and nid == glue_code then
+ done = 1 -- darkred
+ end
+ elseif closequote[code] then
+ local prev, pid = goback(current)
+ if prev and pid == glue_code then
+ done = 1 -- darkred
+ end
+ elseif weird[code] then
+ done = 2 -- darkgreen
+ else
+ local prev, pid = goback(current)
+ if prev then
+ if pid == math_code then
+ done = 7-- darkgray
+ elseif pid == glyph_code and getfont(current) ~= getfont(prev) then
+ if lastdone ~= prev then
+ done = 2 -- darkgreen
+ end
+ end
+ end
+ if not done then
+ local next, nid = goforward(current)
+ if next then
+ if nid == math_code then
+ done = 7 -- darkgray
+ elseif nid == glyph_code and getfont(current) ~= getfont(next) then
+ if lastdone ~= prev then
+ done = 2 -- darkgreen
+ end
+ end
+ end
+ end
+ end
+ if done then
+ setattr(current,a_suspect,done)
+ lastdone = current
+ found = found + 1
+ end
+ current = getnext(current)
+ elseif id == math_code then
+ current = getnext(end_of_math(current))
+ elseif id == glue_code then
+ local a = getattr(current,a_characters)
+ if a then
+ local prev = getprev(current)
+ local prid = prev and getid(prev)
+ local done = false
+ if prid == penalty_code and getpenalty(prev) == 10000 then
+ done = 8 -- orange
+ else
+ done = 5 -- darkmagenta
+ end
+ if done then
+ setattr(current,a_suspect,done)
+ -- lastdone = current
+ found = found + 1
+ end
+ end
+ current = getnext(current)
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ end
+ return head
+end
+
+local function showsuspects(head)
+ -- we inject before so we're okay with a loop
+ for current, id, subtype in nextnode, head do
+ if id == glyph_code then
+ local a = getattr(current,a_suspect)
+ if a then
+ head, current = mark(head,current,id,colors[a])
+ end
+ elseif id == glue_code then
+ local a = getattr(current,a_suspect)
+ if a then
+ head, current = mark(head,current,id,colors[a])
+ end
+ elseif id == math_code then
+ current = end_of_math(current)
+ elseif id == hlist_code or id == vlist_code then
+ local list = getlist(current)
+ if list then
+ local l = showsuspects(list)
+ if l ~= list then
+ setlist(current,l)
+ end
+ end
+ end
+ end
+ return head
+end
+
+function typesetters.showsuspects(head)
+ if found > 0 then
+ return showsuspects(head)
+ else
+ return head
+ end
+end
+
+-- or maybe a directive
+
+trackers.register("typesetters.suspects",function(v)
+ texsetattribute(a_suspecting,v and 1 or unsetvalue)
+ if v and not enabled then
+ enableaction("processors","typesetters.marksuspects")
+ enableaction("shipouts", "typesetters.showsuspects")
+ enabled = true
+ end
+end)
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-sus.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-sus.mkxl
new file mode 100644
index 00000000000..7b62b8acac4
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-sus.mkxl
@@ -0,0 +1,51 @@
+%D \module
+%D [ file=typo-sus,
+%D version=2014.11.06,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Checking Suspects,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Checking Suspects}
+
+%D This is a rather special module, mostly needed by ourselves for
+%D projects where copy||editing is not that precise.
+
+\registerctxluafile{typo-sus}{}
+
+\permanent\protected\def\showsuspects{\enabletrackers[typesetters.suspects]}
+
+%D The suspicious spacing will be colored in the text. There can be false
+%D positives but this features is mostly used when proofreading. So, we
+%D don't worry too much about interference (and efficiency).
+%D
+%D \protected\def\showsample#1%
+%D {\NC \type{#1}%
+%D \NC \enabletrackers[typesetters.suspects]#1\disabletrackers[typesetters.spacing]%
+%D \NC \NR}
+%D
+%D \starttabulate[|||][before=,after=]
+%D \showsample{foo$x$}
+%D \showsample{$x$bar}
+%D \showsample{foo$x$bar}
+%D \showsample{$f+o+o$:}
+%D \showsample{;$f+o+o$}
+%D \showsample{; bar}
+%D \showsample{foo:bar}
+%D \showsample{\quote{ foo }}
+%D \showsample{\quote{bar }}
+%D \showsample{\quote{ bar}}
+%D \showsample{(foo )}
+%D \showsample{\{foo \}}
+%D \showsample{foo{\bf gnu}bar}
+%D \showsample{foo$x^2$bar}
+%D \showsample{foo\nobreakspace bar}
+%D \stoptabulate
+
+\endinput
+
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-tal.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-tal.lmt
new file mode 100644
index 00000000000..5c32e9ef9fc
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-tal.lmt
@@ -0,0 +1,439 @@
+if not modules then modules = { } end modules ['typo-tal'] = {
+ version = 1.001,
+ comment = "companion to typo-tal.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- I'll make it a bit more efficient and provide named instances too which is needed for
+-- nested tables.
+--
+-- Currently we have two methods: text and number with some downward compatible
+-- defaulting.
+
+-- We can speed up by saving the current fontcharacters[font] + lastfont.
+
+local next, type, tonumber = next, type, tonumber
+local div = math.div
+local utfbyte = utf.byte
+
+local splitmethod = utilities.parsers.splitmethod
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+
+local fontcharacters = fonts.hashes.characters
+----- unicodes = fonts.hashes.unicodes
+local categories = characters.categories -- nd
+
+local variables = interfaces.variables
+local v_text = variables.text
+local v_number = variables.number
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getid = nuts.getid
+local getfont = nuts.getfont
+local getchar = nuts.getchar
+local getattr = nuts.getattr
+local isglyph = nuts.isglyph
+
+local setattr = nuts.setattr
+local setchar = nuts.setchar
+
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local nextglyph = nuts.traversers.glyph
+local getdimensions = nuts.dimensions
+
+local setglue = nuts.setglue
+
+local nodepool = nuts.pool
+local new_kern = nodepool.kern
+
+local tracers = nodes.tracers
+local setcolor = tracers.colors.set
+local tracedrule = tracers.pool.nuts.rule
+
+local enableaction = nodes.tasks.enableaction
+
+local characteralign = { }
+typesetters.characteralign = characteralign
+
+local trace_split = false trackers.register("typesetters.characteralign", function(v) trace_split = true end)
+local report = logs.reporter("aligning")
+
+local a_characteralign = attributes.private("characteralign")
+local a_character = attributes.private("characters")
+
+local enabled = false
+
+local datasets = false
+
+local implement = interfaces.implement
+
+local comma = 0x002C
+local period = 0x002E
+local punctuationspace = 0x2008
+
+local validseparators = {
+ [comma] = true,
+ [period] = true,
+ [punctuationspace] = true,
+}
+
+local validsigns = {
+ [0x002B] = 0x002B, -- plus
+ [0x002D] = 0x2212, -- hyphen
+ [0x00B1] = 0x00B1, -- plusminus
+ [0x2212] = 0x2212, -- minus
+ [0x2213] = 0x2213, -- minusplus
+}
+
+-- If needed we can have more modes which then also means a faster simple handler
+-- for non numbers.
+
+local function setcharacteralign(column,separator,before,after)
+ if not enabled then
+ enableaction("processors","typesetters.characteralign.handler")
+ enabled = true
+ end
+ if not datasets then
+ datasets = { }
+ end
+ local dataset = datasets[column] -- we can use a metatable
+ if not dataset then
+ local method, token
+ if separator then
+ method, token = splitmethod(separator)
+ if method and token then
+ separator = utfbyte(token) or comma
+ else
+ separator = utfbyte(separator) or comma
+ method = validseparators[separator] and v_number or v_text
+ end
+ else
+ separator = comma
+ method = v_number
+ end
+ local before = tonumber(before) or 0
+ local after = tonumber(after) or 0
+ dataset = {
+ separator = separator,
+ list = { },
+ maxbefore = before,
+ maxafter = after,
+ predefined = before > 0 or after > 0,
+ collected = false,
+ method = method,
+ separators = validseparators,
+ signs = validsigns,
+ }
+ datasets[column] = dataset
+ used = true
+ end
+ return dataset
+end
+
+local function resetcharacteralign()
+ datasets = false
+end
+
+characteralign.setcharacteralign = setcharacteralign
+characteralign.resetcharacteralign = resetcharacteralign
+
+implement {
+ name = "setcharacteralign",
+ actions = setcharacteralign,
+ arguments = { "integer", "string" }
+}
+
+implement {
+ name = "setcharacteraligndetail",
+ actions = setcharacteralign,
+ arguments = { "integer", "string", "dimension", "dimension" }
+}
+
+implement {
+ name = "resetcharacteralign",
+ actions = resetcharacteralign
+}
+
+local function traced_kern(w)
+ return tracedrule(w,nil,nil,"darkgray")
+end
+
+function characteralign.handler(head,where)
+ if not datasets then
+ return head
+ end
+ local first
+ for n in nextglyph, head do
+ first = n
+ break
+ end
+ if not first then
+ return head
+ end
+ local a = getattr(first,a_characteralign)
+ if not a or a == 0 then
+ return head
+ end
+ local column = div(a,0xFFFF)
+ local row = a % 0xFFFF
+ local dataset = datasets and datasets[column] or setcharacteralign(column)
+ local separator = dataset.separator
+ local list = dataset.list
+ local b_start = nil
+ local b_stop = nil
+ local a_start = nil
+ local a_stop = nil
+ local c = nil
+ local current = first
+ local sign = nil
+ --
+ local validseparators = dataset.separators
+ local validsigns = dataset.signs
+ local method = dataset.method
+ -- we can think of constraints
+ if method == v_number then
+
+-- local function bothdigit(current) -- this could become a helper
+-- local prev, next = getboth(current)
+-- if next and prev and getid(next) == glyph_code and getid(prev) == glyph_code then
+-- local pchar = getchar(prev)
+-- local nchar = getchar(next)
+-- local pdata = fontcharacters[getfont(prev)][pchar]
+-- local ndata = fontcharacters[getfont(next)][nchar]
+-- local punicode = pdata and pdata.unicode or pchar -- we ignore tables
+-- local nunicode = ndata and ndata.unicode or nchar -- we ignore tables
+-- if punicode and nunicode and categories[punicode] == "nd" and categories[nunicode] == "nd" then
+-- return true
+-- end
+-- end
+-- end
+
+ local function bothdigit(current) -- this could become a helper
+ local prev, next = getboth(current)
+ if next and prev then
+ local char, font = isglyph(prev)
+ if char then
+ local data = fontcharacters[font][char]
+ if data and categories[data.unicode or char] == "nd" then -- we ignore tables
+ char, font = isglyph(next)
+ if char then
+ data = fontcharacters[font][char]
+ if data and categories[data.unicode or char] == "nd" then -- we ignore tables
+ return true
+ end
+ end
+ end
+ end
+ end
+ end
+
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local font = id --- nicer
+ local data = fontcharacters[font][char]
+ local unicode = data and data.unicode or char -- ignore tables
+ if not unicode then -- type(unicode) ~= "number"
+ -- no unicode so forget about it
+ elseif unicode == separator then
+ c = current
+ if trace_split then
+ setcolor(current,"darkred")
+ end
+ dataset.hasseparator = true
+ elseif categories[unicode] == "nd" or validseparators[unicode] then
+ if c then
+ if not a_start then
+ a_start = current
+ end
+ a_stop = current
+ if trace_split then
+ setcolor(current,validseparators[unicode] and "darkcyan" or "darkblue")
+ end
+ else
+ if not b_start then
+ if sign then
+ b_start = sign
+ local c, f = isglyph(sign)
+ local new = validsigns[c]
+ if char == new or not fontcharacters[f][new] then
+ if trace_split then
+ setcolor(sign,"darkyellow")
+ end
+ else
+ setchar(sign,new)
+ if trace_split then
+ setcolor(sign,"darkmagenta")
+ end
+ end
+ sign = nil
+ b_stop = current
+ else
+ b_start = current
+ b_stop = current
+ end
+ else
+ b_stop = current
+ end
+ if trace_split and current ~= sign then
+ setcolor(current,validseparators[unicode] and "darkcyan" or "darkblue")
+ end
+ end
+ elseif not b_start then
+ sign = validsigns[unicode] and current
+ -- if trace_split then
+ -- setcolor(current,"darkgreen")
+ -- end
+ end
+ elseif (b_start or a_start) and id == glue_code then
+ -- maybe only in number mode
+ -- somewhat inefficient
+ if bothdigit(current) then
+ local width = fontcharacters[getfont(b_start or a_start)][separator or period].width
+ setglue(current,width,0,0)
+ setattr(current,a_character,punctuationspace)
+ if a_start then
+ a_stop = current
+ elseif b_start then
+ b_stop = current
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ else
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local font = id -- nicer
+ -- local unicode = unicodes[font][char]
+ local unicode = fontcharacters[font][char].unicode or char -- ignore tables
+ if not unicode then
+ -- no unicode so forget about it
+ elseif unicode == separator then
+ c = current
+ if trace_split then
+ setcolor(current,"darkred")
+ end
+ dataset.hasseparator = true
+ else
+ if c then
+ if not a_start then
+ a_start = current
+ end
+ a_stop = current
+ if trace_split then
+ setcolor(current,"darkgreen")
+ end
+ else
+ if not b_start then
+ b_start = current
+ end
+ b_stop = current
+ if trace_split then
+ setcolor(current,"darkblue")
+ end
+ end
+ end
+ end
+ current = getnext(current)
+ end
+ end
+ local predefined = dataset.predefined
+ local before, after
+ if predefined then
+ before = b_start and getdimensions(b_start,getnext(b_stop)) or 0
+ after = a_start and getdimensions(a_start,getnext(a_stop)) or 0
+ else
+ local entry = list[row]
+ if entry then
+ before = entry.before or 0
+ after = entry.after or 0
+ else
+ before = b_start and getdimensions(b_start,getnext(b_stop)) or 0
+ after = a_start and getdimensions(a_start,getnext(a_stop)) or 0
+ list[row] = {
+ before = before,
+ after = after,
+ }
+ return head, true
+ end
+ if not dataset.collected then
+ -- print("[maxbefore] [maxafter]")
+ local maxbefore = 0
+ local maxafter = 0
+ for k, v in next, list do
+ local before = v.before
+ local after = v.after
+ if before and before > maxbefore then
+ maxbefore = before
+ end
+ if after and after > maxafter then
+ maxafter = after
+ end
+ end
+ dataset.maxbefore = maxbefore
+ dataset.maxafter = maxafter
+ dataset.collected = true
+ end
+ end
+ local maxbefore = dataset.maxbefore
+ local maxafter = dataset.maxafter
+ local new_kern = trace_split and traced_kern or new_kern
+ if b_start then
+ if before < maxbefore then
+ head = insert_node_before(head,b_start,new_kern(maxbefore-before))
+ end
+ if not c then
+ -- print("[before]")
+ if dataset.hasseparator then
+ local width = fontcharacters[getfont(b_start)][separator].width
+ insert_node_after(head,b_stop,new_kern(maxafter+width))
+ end
+ elseif a_start then
+ -- print("[before] [separator] [after]")
+ if after < maxafter then
+ insert_node_after(head,a_stop,new_kern(maxafter-after))
+ end
+ else
+ -- print("[before] [separator]")
+ if maxafter > 0 then
+ insert_node_after(head,c,new_kern(maxafter))
+ end
+ end
+ elseif a_start then
+ if c then
+ -- print("[separator] [after]")
+ if maxbefore > 0 then
+ head = insert_node_before(head,c,new_kern(maxbefore))
+ end
+ else
+ -- print("[after]")
+ local width = fontcharacters[getfont(b_stop)][separator].width
+ head = insert_node_before(head,a_start,new_kern(maxbefore+width))
+ end
+ if after < maxafter then
+ insert_node_after(head,a_stop,new_kern(maxafter-after))
+ end
+ elseif c then
+ -- print("[separator]")
+ if maxbefore > 0 then
+ head = insert_node_before(head,c,new_kern(maxbefore))
+ end
+ if maxafter > 0 then
+ insert_node_after(head,c,new_kern(maxafter))
+ end
+ end
+ return head
+end
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-tal.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-tal.mkxl
new file mode 100644
index 00000000000..d96d1348bb8
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-tal.mkxl
@@ -0,0 +1,303 @@
+%D \module
+%D [ file=typo-tal, % spac-cha (2012.06.08) supp-ali (2000.04.17)
+%D version=2013.10.04,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Character Alignment,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Character Alignment}
+
+%D This module replaces the \MKII\ character alignment code which hooked into
+%D table mechanisms but used parsing. In fact, this might be one of these cases
+%D where a \TEX\ based solution is faster, but a \LUA\ one a bit more robust.
+%D Anyway, as I had to fix something (to fit the newer table mechanisms) I
+%D decided to go the mixed route, a rather easy going effort in the aftermath of
+%D the 2013 \CONTEXT\ meeting.
+
+\unprotect
+
+\registerctxluafile{typo-tal}{autosuffix}
+
+\definesystemattribute[characteralign][public]
+
+%D This mechanism is mostly meant for tables:
+%D
+%D \startbuffer
+%D \starttabulate[|l|g{,}|r|]
+%D \NC test \NC 1.234.456,99 \NC \NC test \NR
+%D \NC test \NC 234.456,9 \NC \NC test \NR
+%D \NC test \NC 234.456 \NC \NC test \NR
+%D \NC test \NC 456 \NC \NC test \NR
+%D \NC test \NC \bf whatever \NC \NC test \NR
+%D \stoptabulate
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+% D \startbuffer
+% D \bTABLE
+% D \bTR \bTD[aligncharacter=yes] € 1,1 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 11,11 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 12\punctuationspace111,11 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 12 111,11 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 1.234.451,22222 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 234.451,2 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 234.451 \eTD \eTR
+% D \bTR \bTD[aligncharacter=yes] € 451 \eTD \eTR
+% D \bTR \bTD \bf some text \eTD \eTR
+% D \eTABLE
+% D \stopbuffer
+% D
+% D \typebuffer \blank \getbuffer \blank
+
+\permanent\protected\def\signalcharacteralign #1#2{\c_attr_characteralign\numexpr#1*\maxcardminusone+#2\relax} % 0xFFFF
+\permanent\protected\def\setcharacteralign #1#2{\clf_setcharacteralign#1{#2}}
+\permanent\protected\def\resetcharacteralign {\clf_resetcharacteralign}
+\permanent\protected\def\nocharacteralign {\c_attr_characteralign\attributeunsetvalue}
+\permanent\protected\def\setcharacteraligndetail#1#2#3#4{\clf_setcharacteraligndetail#1{#2}#3#4\relax}
+
+%D Mostly downward compatible:
+%D
+%D \startbuffer
+%D \startcharacteralign
+%D \checkcharacteralign{123.456,78}
+%D \checkcharacteralign{456}
+%D \checkcharacteralign{23.456}
+%D \checkcharacteralign{78,9}
+%D \stopcharacteralign
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+%D
+%D \startbuffer
+%D \startcharacteralign[leftsample=123.456,rightsample=00,character={,}]
+%D \checkcharacteralign{123.456,78}\par
+%D \checkcharacteralign {456}\par
+%D \checkcharacteralign {23.456}\par
+%D \checkcharacteralign {78,9}\par
+%D \checkcharacteralign {78}\par
+%D \stopcharacteralign
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+%D
+%D \startbuffer
+%D \startcharacteralign[leftwidth=123.456,rightwidth=00,character={,}]
+%D \checkcharacteralign{123.456,78}\par
+%D \checkcharacteralign {456}\par
+%D \checkcharacteralign {23.456}\par
+%D \checkcharacteralign {78,9}\par
+%D \checkcharacteralign {78}\par
+%D \stopcharacteralign
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+%D
+%D We have (currently) two modes: \type {text} and \type {number}. The handler tries
+%D to determine the mode automatically. When using periods and commas as separators
+%D the \type {number} mode is chosen. If you use for instance a \type {-} as
+%D separator, \type {text} is chosen, but you can enforce \type {number} with \type
+%D {number->-} (as with other mechanisms, the arrow indicates a method to apply).
+%D
+%D One can use \type {\nocharacteralign} to disable this mechanism, for instance in
+%D a table cell.
+
+\mutable\def\alignmentcharacter{,}
+
+\installcorenamespace{characteralign}
+
+\installparameterhandler\??characteralign {characteralign}
+\installsetuphandler \??characteralign {characteralign}
+
+\setupcharacteralign
+ [\c!leftwidth =\zeropoint,
+ \c!rightwidth =\zeropoint,
+ \c!leftsample =,
+ \c!rightsample=,
+ \c!character =\alignmentcharacter]
+
+\permanent\protected\def\typo_charalign_pass_one
+ {\advance\scratchcounter\plusone
+ \setbox\scratchbox\typo_charalign_pass}
+
+\permanent\protected\def\typo_charalign_pass_two
+ {\advance\scratchcounter\plusone
+ \typo_charalign_pass}
+
+\def\typo_charalign_pass
+ {\hbox\bgroup\signalcharacteralign\plusone\scratchcounter\let\next}
+
+\permanent\tolerant\protected\def\startcharacteralign[#1]%
+ {\doifelseassignment{#1}\typo_charalign_start_one\typo_charalign_start_two{#1}}
+
+\def\typo_charalign_start_one#1#2\stopcharacteralign
+ {\bgroup
+ % for now no instances
+ \setupcurrentcharacteralign[#1]%
+ \edef\p_left {\characteralignparameter\c!leftsample}%
+ \edef\p_right{\characteralignparameter\c!rightsample}%
+ \ifempty\p_left
+ \scratchdimenone\dimexpr\characteralignparameter\c!leftwidth\relax
+ \else
+ \setbox\scratchbox\hbox{\p_left}%
+ \scratchdimenone\wd\scratchbox
+ \fi
+ \ifempty\p_right
+ \scratchdimentwo\dimexpr\characteralignparameter\c!rightwidth\relax
+ \else
+ \setbox\scratchbox\hbox{\p_right}%
+ \scratchdimentwo\wd\scratchbox
+ \fi
+ \ifzeropt\scratchdimenone
+ \ifzeropt\scratchdimentwo
+ \donefalse
+ \else
+ \donetrue
+ \fi
+ \else
+ \donetrue
+ \fi
+ \edef\alignmentcharacter{\characteralignparameter\c!character}%
+ \ifdone
+ \clf_setcharacteraligndetail
+ \plusone
+ \alignmentcharacter
+ \scratchdimenone
+ \scratchdimentwo
+ \else
+ \clf_setcharacteralign
+ \plusone
+ \alignmentcharacter
+ \begingroup
+ \scratchcounter\zerocount
+ \enforced\let\checkcharacteralign\typo_charalign_pass_one
+ \settrialtypesetting
+ #2\relax
+ \endgroup
+ \fi
+ \begingroup
+ \scratchcounter\zerocount
+ \enforced\let\checkcharacteralign\typo_charalign_pass_two
+ #2\relax
+ \endgroup
+ \resetcharacteralign
+ \egroup}
+
+\def\typo_charalign_start_two#1#2\stopcharacteralign
+ {\bgroup
+ \edef\m_temp{#1}%
+ \ifempty\m_temp \else
+ \let\alignmentcharacter\m_temp
+ \fi
+ \clf_setcharacteralign
+ \plusone
+ \alignmentcharacter
+ \begingroup
+ \scratchcounter\zerocount
+ \enforced\let\checkcharacteralign\typo_charalign_pass_one
+ \settrialtypesetting
+ #2\relax
+ \endgroup
+ \begingroup
+ \scratchcounter\zerocount
+ \enforced\let\checkcharacteralign\typo_charalign_pass_two
+ #2\relax
+ \endgroup
+ \resetcharacteralign
+ \egroup}
+
+\aliased\let\stopcharacteralign \relax
+\aliased\let\checkcharacteralign\gobbleoneargument
+
+\permanent\protected\def\setfirstpasscharacteralign {\enforced\let\checkcharacteralign\gobbleoneargument}
+\permanent\protected\def\setsecondpasscharacteralign{\enforced\let\checkcharacteralign\firstofoneargument}
+
+%D We need fonts to provide tabular digits that is, the digits need to have the same
+%D width.
+%D
+%D \startbuffer
+%D \startbuffer[demo]
+%D \switchtobodyfont[pagella]
+%D \setupTABLE[column][1][alignmentcharacter=.,aligncharacter=yes]
+%D \bTABLE
+%D \bTR \bTD 11.111 \eTD \bTD 11.111 \eTD \eTR
+%D \bTR \bTD 2.2 \eTD \bTD 2.2 \eTD \eTR
+%D \bTR \bTD 444.444 \eTD \bTD 444.444 \eTD \eTR
+%D \eTABLE
+%D \stopbuffer
+%D
+%D \start inlinenumbers: \crlf \addfeature[inlinenumbers] \getbuffer \stop
+%D \start tabularnumbers: \crlf \addfeature[tabularnumbers] \getbuffer \stop
+%D \start oldstylenumbers:\crlf \addfeature[oldstylenumbers] \getbuffer \stop
+%D \stopbuffer
+%D
+%D \enabledirectives[typesetters.characteralign.autofont]
+%D \typebuffer \blank \getbuffer \blank
+%D \disabledirectives[typesetters.characteralign.autofont]
+%D \typebuffer \blank \getbuffer \blank
+%D \enabledirectives[typesetters.characteralign.autofont]
+
+\definefontfeature
+ [system:tabnum]
+ [tnum=yes,
+ lnum=no]
+
+\newconditional\c_tabl_ntb_char_align_auto_font \settrue\c_tabl_ntb_char_align_auto_font
+
+\installtexdirective % yes or no ?
+ {typesetters.characteralign.autofont}
+ {\settrue \c_tabl_ntb_char_align_auto_font}
+ {\setfalse\c_tabl_ntb_char_align_auto_font}
+
+\def\m_font_feature_auto_tabnum{system:tabnum}
+
+\protected\def\typo_charalign_adapt_font_indeed
+ {\let\m_font_feature_asked\m_font_feature_auto_tabnum
+ \font_feature_reset_add_indeed}
+
+\def\typo_charalign_adapt_font % slow but seldom used (expanded in preamble)
+ {\ifconditional\c_tabl_ntb_char_align_auto_font
+ \typo_charalign_adapt_font_indeed
+ \fi}
+
+%D Another example:
+%D
+%D \starttyping
+%D \setupTABLE[c][2][alignmentcharacter={number->,},aligncharacter=yes,align={flushleft}]
+%D \bTABLE
+%D \bTR \bTD 1 \eTD \bTD 125 cm \eTD \eTR
+%D \bTR \bTD 2 \eTD \bTD 1 125,80 cm \eTD \eTR
+%D \bTR \bTD 6 \eTD \bTD 1 125,80 $\pi^2$ \eTD \eTR
+%D \bTR \bTD 7 \eTD \bTD 129,3 \unit{square centimeter} \eTD \eTR
+%D \eTABLE
+%D
+%D \setupTABLE[c][2][alignmentcharacter={number->,},aligncharacter=yes,align={flushleft}]
+%D \bTABLE
+%D \bTR \bTD 1 \eTD \bTD 125 cm \eTD \eTR
+%D \bTR \bTD 2 \eTD \bTD 1 125,80 cm \eTD \eTR
+%D \bTR \bTD 6 \eTD \bTD 1 125,80 $\pi^2$ \eTD \eTR
+%D \bTR \bTD 7 \eTD \bTD 129,3 \unit{square centimeter} \eTD \eTR
+%D \eTABLE
+%D
+%D \setupTABLE[c][2][alignmentcharacter={number->,},aligncharacter=yes,align={middle}]
+%D \bTABLE
+%D \bTR \bTD 1 \eTD \bTD 125 \eTD \eTR
+%D \bTR \bTD 6 \eTD \bTD 1 125,80 \eTD \eTR
+%D \bTR \bTD 7 \eTD \bTD 129,3 \eTD \eTR
+%D \eTABLE
+%D
+%D \setupTABLE[c][2][alignmentcharacter={text->,},aligncharacter=yes,align={middle}]
+%D \bTABLE
+%D \bTR \bTD 1 \eTD \bTD 125 \eTD \eTR
+%D \bTR \bTD 6 \eTD \bTD 1 125,80 \eTD \eTR
+%D \bTR \bTD 7 \eTD \bTD 129,3 \eTD \eTR
+%D \eTABLE
+%D \stoptyping
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-txt.mklx b/Master/texmf-dist/tex/context/base/mkxl/typo-txt.mklx
new file mode 100644
index 00000000000..270cc94e028
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-txt.mklx
@@ -0,0 +1,231 @@
+%D \module
+%D [ file=typo-txt,
+%D version=2011.10.27,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Text Hacks,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+%D This module replaces the by now rather old supp-fun module.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Text Hacks}
+
+\unprotect
+
+% registerctxluafile{typo-txt}{}
+
+%D \macros
+%D {normalizefontheight,normalizefontwidth,normalizedfontsize}
+%D
+%D Next we introduce some font manipulation macros. When we want to typeset some
+%D text spread in a well defined area, it can be considered bad practice to
+%D manipulate character and word spacing. In such situations the next few macros can
+%D be of help:
+%D
+%D \starttyping
+%D \normalizefontheight \name {sample text} {height} {font}
+%D \normalizefontwidth \name {sample text} {width} {font}
+%D \stoptyping
+%D
+%D Consider for instance:
+%D
+%D \startbuffer[a]
+%D \normalizefontheight \TempFont {X} {2\baselineskip} {Serif}
+%D \stopbuffer
+%D
+%D \startbuffer[b]
+%D \ruledhbox{\TempFont To Be Or Not To Be}
+%D \stopbuffer
+%D
+%D \typebuffer[a,b] \getbuffer[a]
+%D
+%D This shows up as:
+%D
+%D \startlinecorrection
+%D \ruledhbox{\getbuffer[b]}
+%D \stoplinecorrection
+%D
+%D The horizontal counterpart is:
+%D
+%D \startbuffer[a]
+%D \normalizefontwidth \TempFont {This Line Fits} {\hsize} {Serif}
+%D \stopbuffer
+%D
+%D \startbuffer[b]
+%D \ruledhbox{\TempFont This Line Fits}
+%D \stopbuffer
+%D
+%D \typebuffer[a,b] \getbuffer[a]
+%D
+%D This gives:
+%D
+%D \startlinecorrection
+%D \ruledhbox{\getbuffer[b]}
+%D \stoplinecorrection
+%D
+%D The calculated font scale is avaliable in \type {\normalizedfontsize}.
+
+\newbox\b_typo_normalizers
+
+\def\typo_normalizers_size#axis#size%
+ {\dimexpr\ifdim#1\b_typo_normalizers=\zeropoint
+ \bodyfontsize
+ \else
+ \luaexpr{\number\dimexpr10pt\relax*\number\dimexpr#size\relax/\number#axis\b_typo_normalizers}\scaledpoint
+ \fi\relax}
+
+\def\typo_normalizers_font_at_size#axis#cs#text#size#font% avoid overflow by using lua
+ {\begingroup
+ \setbox\b_typo_normalizers\hbox{\definedfont[#font at 10pt]\settrialtypesetting#text}%
+ \normalexpanded{\endgroup\enforced\edef\noexpand\normalizedfontsize{\the\typo_normalizers_size{#axis}{#size}}}%
+ \definefont[\strippedcsname#cs][#font at \normalizedfontsize]}
+
+\protected\def\typo_normalizers_text_at_size#axis#font#size#text%
+ {\dontleavehmode
+ \begingroup
+ \setbox\b_typo_normalizers\hbox{\definedfont[#font at 10pt]\settrialtypesetting#text}%
+ \definedfont[#font at \the\typo_normalizers_size{#axis}{#size}]#text%
+ \endgroup}
+
+\mutable\def\normalizedfontsize{\bodyfontsize}
+
+\permanent\protected\def\normalizetextwidth {\typo_normalizers_text_at_size\wd}
+\permanent\protected\def\normalizetextheight{\typo_normalizers_text_at_size\ht}
+\permanent\protected\def\normalizetextdepth {\typo_normalizers_text_at_size\dp}
+\permanent\protected\def\normalizetextline {\typo_normalizers_text_at_size\htdp}
+
+\permanent\protected\def\normalizefontwidth {\typo_normalizers_font_at_size\wd}
+\permanent\protected\def\normalizefontheight{\typo_normalizers_font_at_size\ht}
+\permanent\protected\def\normalizefontdepth {\typo_normalizers_font_at_size\dp}
+\permanent\protected\def\normalizefontline {\typo_normalizers_font_at_size\htdp}
+
+\permanent\protected\def\widthspanningtext #text#size#specification{\hbox{\normalizefontwidth \temp{#text}{#size}{#specification}\temp#text}}
+\permanent\protected\def\heightspanningtext#text#size#specification{\hbox{\normalizefontheight\temp{#text}{#size}{#specification}\temp#text}}
+\permanent\protected\def\depthspanningtext #text#size#specification{\hbox{\normalizefontdepth \temp{#text}{#size}{#specification}\temp#text}}
+\permanent\protected\def\linespanningtext #text#size#specification{\hbox{\normalizefontline \temp{#text}{#size}{#specification}\temp#text}}
+
+%D Traditionally we use UpperCasedNames for this kind of functionality.
+
+\aliased\let\NormalizeFontHeight \normalizefontheight
+\aliased\let\NormalizeFontWidth \normalizefontwidth
+\aliased\let\NormalizeTextHeight \normalizetextheight
+\aliased\let\NormalizeTextWidth \normalizetextwidth
+
+\aliased\let\WidthSpanningText \widthspanningtext
+
+\permanent\def\TheNormalizedFontSize{\normalizedfontsize}
+
+%D \macros
+%D {vulgarfraction}
+%D
+%D This code is moved from \type {cor-mis.mkiv}. We show three versions. First
+%D the simple one using \type {\low} and \type {high}:
+%D
+%D \startbuffer
+%D \def\vfrac#1#2%
+%D {\hbox{\high{\tx#1\kern-.25em}/\low{\kern-.25em\tx#2}}}
+%D
+%D test \vfrac{1}{2} test \vfrac{123}{456} test
+%D \stopbuffer
+%D
+%D \typebuffer {\showmakeup\getbuffer}
+%D
+%D A better way to handle the kerning is the following, here we kind of assume
+%D that tye slash is symmetrical and has nearly zero width.
+%D
+%D \startbuffer
+%D \def\vfract#1#2%
+%D {\hbox{\high{\tx#1}\hbox to \zeropoint{\hss/\hss}\low{\tx#2}}}
+%D \stopbuffer
+%D
+%D \typebuffer {\showmakeup\getbuffer}
+%D
+%D The third and best alternative is the following:
+%D
+%D {\showmakeup\getbuffer}\crlf\getbuffer
+%D
+%D This time we measure the height of the \type {/} and shift over the maximum
+%D height and depths of this character and the fractional digits (we use 57 as
+%D sample). Here we combine all methods in one macros.
+
+\setnewconstant\vulgarfractionmethod\plusthree
+
+\definehspace[\v!vulgarfraction][.25em] % [.15em]
+\definesymbol[\v!vulgarfraction][/] % [\raise.2ex\hbox{/}]
+
+\permanent\protected\def\vulgarfraction#1#2%
+ {\dontleavehmode
+ \hbox
+ {\def\vulgarfraction{vulgarfraction}%
+ \ifcase\vulgarfractionmethod
+ #1\symbol[\v!vulgarfraction]#2%
+ \or
+ \high{\tx#1\kern-\hspaceamount\empty\v!vulgarfraction}%
+ \symbol[\vulgarfraction]%
+ \low {\kern-\hspaceamount\empty\v!vulgarfraction\tx#2}%
+ \or
+ \high{\tx#1}%
+ \hbox to \zeropoint{\hss\symbol[\v!vulgarfraction]\hss}%
+ \low{\tx#2}%
+ \or
+ \setbox0\hbox{\symbol[\vulgarfraction]}%
+ \setbox2\hbox{\txx57}%
+ \raise\ht0\hbox{\lower\ht2\hbox{\txx#1}}%
+ \hbox to \zeropoint{\hss\symbol[\v!vulgarfraction]\hss}%
+ \lower\dp0\hbox{\raise\dp2\hbox{\txx#2}}%
+ \fi}}
+
+\ifdefined\vfrac \else \let\vfrac\vulgarfraction \fi % \aliased ?
+
+%D \starttabulate[|l|l|]
+%D \HL
+%D \NC \bf method \NC \bf visualization \NC\NR
+%D \HL
+%D \NC 0 \NC \vulgarfractionmethod0 \vulgarfraction{1}{2} \NC\NR
+%D \NC 1 \NC \vulgarfractionmethod1 \vulgarfraction{1}{2} \NC\NR
+%D \NC 2 \NC \vulgarfractionmethod2 \vulgarfraction{1}{2} \NC\NR
+%D \NC 3 \NC \vulgarfractionmethod3 \vulgarfraction{1}{2} \NC\NR
+%D \HL
+%D \stoptabulate
+
+%D This is used in the beginners manual. One needs to set the font size to an
+%D acceptable value for this to work.
+
+\permanent\tolerant\protected\def\startnicelyfilledbox[#1]%
+ {\vbox\bgroup
+ \forgetall
+ \letdummyparameter\c!width \hsize
+ \letdummyparameter\c!height\vsize
+ \letdummyparameter\c!offset\exheight % we obey to the outer exheight
+ \letdummyparameter\c!strut \v!yes % we obey to the inner strut !
+ \getdummyparameters[#1]%
+ \scratchoffset\dummyparameter\c!offset\relax
+ \setbox\scratchbox\vbox to \dummyparameter\c!height \bgroup
+ \hsize\dummyparameter\c!width\relax
+ \emergencystretch10\scratchoffset
+ \parfillskip\zeropoint
+ \leftskip \scratchoffset
+ \rightskip\scratchoffset}
+
+\permanent\protected\def\stopnicelyfilledbox
+ {\egroup
+ \vbox to \dummyparameter\c!height \bgroup
+ \doifelse{\dummyparameter\c!strut}\v!yes
+ {\dorecurse{\boxlines\scratchbox}%
+ {\boxlineht\scratchbox\recurselevel\strutht
+ \boxlinedp\scratchbox\recurselevel\strutdp
+ \boxline\scratchbox\recurselevel
+ \vss}}%
+ {\dorecurse{\boxlines\scratchbox}%
+ {\boxline\scratchbox\recurselevel
+ \vss}}%
+ \unskip
+ \egroup
+ \egroup}
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-wrp.mkxl b/Master/texmf-dist/tex/context/base/mkxl/typo-wrp.mkxl
new file mode 100644
index 00000000000..b552c151aa7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-wrp.mkxl
@@ -0,0 +1,58 @@
+%D \module
+%D [ file=typo-wrp,
+%D version=2014.11.09,
+%D title=\CONTEXT\ Typesetting Macros,
+%D subtitle=Wrappers,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Typesetting Macros / Wrapping}
+
+\unprotect
+
+\registerctxluafile{typo-wrp}{}
+
+\permanent\protected\def\crlf
+ {\ifhmode
+ \expandafter\spac_crlf
+ \fi}
+
+\protected\def\spac_crlf
+ {\clf_enablecrlf % once
+ \unskip
+ \spac_crlf_placeholder
+ \ifcase\raggedstatus\hfil\or\or\or\hfil\fi
+ % in bad usage this can lead to: [break -10000] [wordboundary] [line(break)penalty] [parfillskip]
+ \break
+ \wordboundary
+ % which we then remove (maybe we should flag the wordboundary with an attribute but not now
+ \ignorespaces}
+
+\protected\def\spac_crlf_placeholder
+ {\strut}
+
+\protected\def\spac_crlf_placeholder_show
+ {\wordboundary
+ %\nobreak
+ \hpack to \zeropoint{\strut{\infofont\kern.25\emwidth}\lohi{\infofont CR}{\infofont LF}\hss}}
+
+\permanent\protected\def\settestcrlf
+ {\let\spac_crlf_placeholder\spac_crlf_placeholder_show}
+
+\permanent\protected\def\crlfplaceholder % for old times sake
+ {\spac_crlf_placeholder}
+
+\appendtoks
+ \let\spac_crlf_placeholder\empty
+\to \everysetnostrut
+
+\appendtoks
+ \let\spac_crlf\space
+\to \everysimplifycommands
+
+\protect \endinput
diff --git a/Master/texmf-dist/tex/context/base/mkxl/unic-ini.mkxl b/Master/texmf-dist/tex/context/base/mkxl/unic-ini.mkxl
new file mode 100644
index 00000000000..aedfe47ed2a
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/unic-ini.mkxl
@@ -0,0 +1,36 @@
+%D \module
+%D [ file=unic-ini,
+%D version=2002.12.03,
+%D title=\CONTEXT\ \UNICODE\ Support,
+%D subtitle=\UNICODE\ \& UTF-8 support,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{ConTeXt Unicode Support / Initialization}
+
+\registerctxluafile{unic-ini}{}
+
+\unprotect
+
+\permanent\def\unicodenumber #1{\the\numexpr#1\relax} % no lookahead
+\permanent\def\unicodehexnumber#1{\cldcontext{number.toevenhex(\number#1))}}
+
+%D \startbuffer
+%D \unicodechar{left square bracket}okay\unicodechar{right square bracket}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\permanent\def\unicodechar#1{\clf_unicodechar{#1}}
+
+\permanent\protected\def\unknownchar
+ {\dontleavehmode\hpack{\vrule\s!width.5\emwidth\s!height\exheight\s!depth\zeropoint}}
+
+% \ifdefined\zwnbsp\else \let\zwnbsp\relax \fi % zerowidthnonbreakablespace
+
+\protect \endinput